What is rconftool?
==================

rconftool is a reimplementation of Sam Varshavchik's sysconftool in Ruby.
See http://www.courier-mta.org/sysconftool/ for details of the original. Its
purpose is to keep configuration files "fresh" when upgrading an application
from one version to another, ensuring that all necessary settings are
present and obsolete ones removed.

To use it, application writers need to distribute their config files marked
up with some simple metadata in comments, and arrange for the 'make install'
or 'make install-configure' target to invoke rconftool.

rconftool can be called as a library function or from the command line. It
can also install groups of files recursively from one directory tree into
another.

rconftool is distributed under the MIT licence.

USAGE
=====

(1) As a command-line tool

  # ./rconftool.rb foo.dist

Installs 'foo.dist' to 'foo' (option --strip-suffix '.dist' is the default)

  # ./rconftool.rb --strip-suffix '.orig' --add-suffix '.txt' *.orig

Installs files foo.orig and bar.orig as foo.txt and bar.txt

  # ./rconftool.rb --recursive --targetdir /etc/foo .

Installs all files in the current directory and its subdirectories into
/etc/foo recursively. (Suffix '.dist' is still stripped off by default,
unless you specify --strip-suffix ""). /etc/foo itself must exist, but
subdirectories are created if necessary.

(2) As a library

  require 'rconftool'

  # install single files
  Rconftool::install('foo.dist', 'foo')
  Rconftool::install('foo.dist', nil, nil, :strip_regexp=>/\.dist$/)
  Rconftool::install('foo.orig', 'foo.txt')
  Rconftool::install('bar.orig', nil, nil, :strip_regexp=>/\.orig$/,
                                           :add_suffix=>'.txt')

  # using the command-line interface
  argv = ['--recursive','--targetdir','/etc/foo','.']
  rct = Rconftool::Processor.new(argv)
  rct.run(argv)

DOWNLOAD
========

From http://rubyforge.org/projects/rconftool/

INSTALLATION
============

You can just use rconftool.rb from the current directory, or copy it
anywhere into Ruby's library search path. The following command will do this
for you:

  # ruby install.rb

PHILOSOPHY
==========

When upgrading software from version X to version Y, there's always a
problem with configuration files.

Should we keep the old configuration files which worked with version X? This
won't work if some of X's configuration settings have been obsoleted, or if
version Y has a new compulsory setting which version X didn't have. Version
Y may not run correctly, or may not run at all.

Should we install fresh configuration files which come with version Y? This
is always guaranteed to work, but all of the sysadmin's careful
configuration work in setting up version X will have been lost.

sysconftool (and now rconftool) provide a solution to this problem. By
keeping some metadata in the configuration files, settings which can be
carried forward safely from an old version to a new version are identified
and kept.

The metadata are special comment values. The file format is described at
http://www.courier-mta.org/sysconftool/sysconftool.7.html

FUNCTIONALITY
=============

When upgrading from software version X to version Y, we may install a new
configuration file for version Y (the "distfile") to update or replace an
existing configuration file for version X (the "target file"), using the
following rules.

- If the distfile is in sysconftool format (i.e. it carries a ##VERSION:
  header within the first 20 lines), then:
  - If the target file does not exist, then the distfile is installed.
  - If the target file contains an identical ##VERSION: line, then it is
    not touched. The assumption is that if nothing has changed in the
    distfile, then the existing configuration must still be OK.
  - If the target file contains a different ##VERSION: line, then a merge
    operation is performed. The old target file is renamed to .bak; the
    distfile is installed in place of it, but any compatible settings in
    the old target file are merged in.
    - Settings are 'compatible' if they have the same ##NAME:id:version
      header. The old value is carried forward, and the report will say
      "id: unchanged"
    - If a setting exists with the same ##NAME:id but a different
      version, then the 'safe' value from the distfile is used. The old
      value is added as a comment to aid with manually updating the setting.
      The report will say "id: UPDATED"
    - If the setting did not exist at all in the old target file, then the
      distfile value is used. The report will say "id: new"
    - Since the distfile is used as a template for the output, any settings
      which are in the old target file but not in the distfile will be
      silently discarded.
  - If the target does not contain any ##VERSION: line, then it is renamed
    to .bak and the distfile is installed verbatim. This is what will
    happen the first time a non-sysconftool file is upgraded to
    sysconftool format. Any existing settings in the .bak file will need to
    be manually merged into the new format file.

- If the distfile is not in sysconftool format, then it is copied to the
  target location only if the target does not already exist. This is a
  fallback mode so that rconftool can be used to install 'vanilla' config
  files, but once they have been installed a first time, it's up to the
  sysadmin to manually maintain them from that point onwards.
  (that's something that sysconftool doesn't do)

- Whenever a new or merged target file is installed, its ownership and mode
  bits are copied from the distfile

- When using the command-line version of the tool, if one of the source
  distfiles is a directory and the --recursive flag is given, then it is
  processed recursively. The target files can be in a different directory
  using the --targetdir option. Subdirectories of --targetdir are created as
  necessary to mirror the source structure, and whenever a new directory is
  created, its ownership and mode bits are copied from the corresponding
  directory in the source structure.
  (that's also something that sysconftool doesn't do)

BUGS
====

Hopefully, the cases outlined above are all exercised in the test suite,
test-rconftool.rb

I can't guarantee that rconftool will work in exactly the same way as
sysconftool under all circumstances - that's because it was too hard to
decipher the Perl source code to understand what it did in all
circumstances.

Sam's sysconftool format description is somewhat ambiguous. It states:

   One or more comment lines - lines that begin with the '#' character -
   may follow "##NAME". The first line that does not begin with '#' is
   considered to be the first line that contains the value of the
   configuration setting, which lasts. The value can be spread over
   multiple lines. The configuration setting is considered to last until
   either the end of the file, or until the first following line that
   begins with the '#' comment character.

What it doesn't make clear is what happens in the following case:

---- 8< -------------------------------------------------------------
##VERSION:1
##NAME:FOO:0
#
# This is a strange setting
#

ABC=1
#another comment
XYZ=2

##NAME:BAR:0
... etc
---- 8< -------------------------------------------------------------

Are the lines "#another comment" and "XYZ=2" complete orphans? Or do they
both belong to the 'value' section? Or does '#another comment' belong to the
comment, and XYZ=2 to the value?

Running a test case where this value is merged, seems to show that it's all
taken as the value:

---- 8< -------------------------------------------------------------
##VERSION:1
##NAME:FOO:0
#
# This is a strange setting
#
#
# DEFAULT SETTING from test.dist:
# 
#ABC=1
#another comment
#XYZ=2
#

ZZZ=999
---- 8< -------------------------------------------------------------

So, I've made rconftool behave in roughly the same way, although for now a
comment character is put in front of every line (including comments) as this
seems more logical to me.

---- 8< -------------------------------------------------------------
##VERSION:1
##NAME: FOO:0
#
# This is a strange setting
#
#
# DEFAULT SETTING from test.dist:
# 
#ABC=1
##another comment             << note difference
#XYZ=2
#

ZZZ=999

---- 8< -------------------------------------------------------------

AUTHOR
======

Brian Candler <B.Candler@pobox.com>

If you have patches or suggestions for improvement, please feel free to mail
them to me.