Automating Configs with Cfengine
The average UNIX system has numerous configuration files, and keeping them up to date can be a big chore, even on a single system. On large UNIX LANs and WANs, it's a daunting task. This article discusses Cfengine, a language-based tool for configuring and maintaining a network of UNIX systems written by Mark Burgess as part of the GNU project. Cfengine can help you to automate configuration file maintenance, thereby making the task quicker and more accurate.
What makes Cfengine unique? Unlike most other scripting languages (Perl, Tcl, etc.), Cfengine doesn't try to be a general purpose, do everything language. For example, Cfengine doesn't have "if" statements, "for" loops, or any of the other traditional branching, looping, or decision-making constructs. It also doesn't need them.
Each computer that runs Cfengine is assigned to a set of classes. You can specify in your Cfengine script which class of systems each action should be performed on. For example, you might want to copy a file to all of the Linux systems in accounting. This class system is at the heart of Cfengine's ability to simplify system administration. Cfengine allows one script to configure many systems, or single out a particular system without long constructs of "if/else" statements.
Cfengine can help automate most common system administration tasks. It includes directives for managing files, editing text files, managing processes, and mounting NFS file systems.
Once a configuration has been established with Cfengine, the same script can be run an unlimited number of times to enforce and repair that configuration. Cfengine includes automatic checks to prevent actions from being needlessly repeated. For example, when you tell Cfengine to copy a file, a time stamp comparison or an MD5 checksum is performed. If the destination file is the same as the source file, no action is taken.
Ideally, after you have created a comprehensive central configuration with Cfengine, you could install a new system by doing the following: Boot up the new system. Manually mount an NFS file system containing Cfengine binaries for that platform and the Cfengine configuration file. Run Cfengine. Cfengine will automatically perform any necessary changes to the new system. Afterwards, the same Cfengine configuration file can be used to monitor the configuration of the system and correct any problems.
Cfengine will run on almost any UNIX or UNIX-like system. To compile Cfengine, you will need a C compiler, Flex, and Bison, along with GNU tar and/or gzip. You should also have tex2info and the GNU Info reader to create and read the online documentation for Cfengine.
Cfengine is part of the GNU project, so it is freely available. You can download it from the Cfengine home page at http://www.iu.hioslu.no/~mark/cfengine/. The most current version at the time of this writing is version 1.4.10. After you obtain Cfengine, you'll need to compile it for each of the platforms you'll be running it on. Fortunately Cfengine, like most GNU tools, is easy to compile.
Cfengine comes in a tar archive with a name like cfengine-1.4.10.pre.tar.gz. Use tar to extract the files from the archive. If you are using GNU tar, the command would be:
$ tar -zxf cfengine-1.4.10.pre.tar.gz
tar, you will need to un-gzip the file first.
Installation is simple, and consists of running the following commands:
$ make install
Assuming the Cfengine configuration file does not encounter problems, this set of commands will properly configure Cfengine, compile it, and install the resulting binaries under the /usr/local/ hierarchy.
A Minimal Cfengine Script
Listing 1 is a very simple Cfengine script that copies a new version of the passwd program from an archive directory (probably NFS mounted) to /bin.
The first line of the script is, of course, the standard UNIX "hash-bang" line enforcing shell selection. The second line is a comment identifying our script. Cfengine uses the # symbol to denote a comment. Anything after the # is ignored by Cfengine. Whitespace is also ignored by Cfengine, thus allowing you to format your scripts for easy reading.
As depicted in Listing 1, Cfengine scripts are divided into sections. The section names are internally defined Cfengine keywords. The control section is usually the first section, and is the only required section.
The control section is used to set variables and define macros. The most important of these variables is the actionsequence. The actionsequence lists the order and number of times in which the other sections of the Cfengine script are carried out. A Cfengine script without an actionsequence does nothing. In the example, the actionsequence lists only one section: copy.
The copy section lists files and directory trees to be copied. There are a variety of options to control how the copy is performed (e.g., is it a recursive copy?).
UNIX administrators often perform certain actions only on subsets of their systems. For example, you might have fixes that need to be installed on all of your SCO UnixWare systems, but not on your Solaris systems. Cfengine uses a system of classes as the basis of all decision making. Cfengine automatically assigns the host it is running on to a variety of classes, including classes for the host operating system and version, hardware architecture, and the unqualified hostname. Even the current day of the week is set as a class.
Actions in a Cfengine script are always associated with one or more classes. If no classes are specifically listed (as in Listing 1) then the special class "any" is assumed. Listing 2 expands slightly upon the previous example. The copy action is associated with the class "linux". The file will only be copied when the script is run on a Linux system. Other systems (like Solaris) will ignore this copy action.
The groups command lets you create classes made up of groups of hosts. For example, in Listing 3 we create the class MISdepartment. It consists of the three hosts: butthead, homer, and bart.
Listing 3 also shows the use of the logical operators. The logical "AND" operator (.), the logical "NOT" operator (!), and the logical "OR" operator (|), can be used to associate an action with a more specific subset of hosts. In Listing 3, I have further refined the script so that the new passwd command is copied to all Linux systems except the systems in the MIS department.
Adding More Functionality
Now that I've shown how a basic Cfengine script works, it's time to learn how to do a bit more with the scripts. I'll start by looking at how to use Cfengine to help with file management.
File management is a large part of the job of a system administrator. So, it's not surprising that Cfengine has commands for a variety of file handling tasks. We've already seen a small example of the copy command. Cfengine also has a links command for creating both hard links and symbolic links to files. An example of the links command is as follows:
/usr/local/netscape/netscape -> /usr/local/bin/netscape
This command will create a symbolic link from the file /usr/local/netscape/netscape, to /usr/local/bin/netscape. To create a hard link, you can specify the option type = hard as follows:
/usr/local/netscape/netscape -> \
You can delete files with Cfengine as well. The tidy command is used to delete unwanted files. An example of the tidy command is as follows:
/tmp/ pattern=* R=inf age=1
/ pattern=core R=inf age=0
The first command in the example deletes all files older than one day in the tmp directory, and all directories under it. The second deletes all core files of any age on the entire system.
The disable command is similar to tidy. But instead of deleting a file, it renames it to be "harmless". By default Cfengine renames the file to the original filename plus the extension ".cf-disabled". Here is an example:
The last file management command I'll discuss is the files command. This command lets you check for the existence of files and for their ownership and permissions. It can also be used to change the permissions and ownership of files, and to check for setuid root programs. The following example would check that every users' HTML files are readable by the group www-data.
$(HOME)/public_html mode=0664 action=fixall r=inf \
This example also shows the use of the $(HOME) directive. This directive tells Cfengine to iterate all of the user home directories on the system.
Editing Text Files
Cfengine can do more than just file management. It also has a set of commands for editing text files. These commands are fairly limited, but are sufficient for most common system administration tasks. The following is an example of a couple of the Cfengine file editing commands:
AppendIfNoSuchLine "www 80/tcp http"
The first command will append the line "www 80/tcp http" to the /etc/services file, but only if this line doesn't already exist. The second command will search the /etc/passwd file on all hosts and remove the user "joesmith".
After you've used Cfengine to edit a daemons configuration file, you probably want to restart that daemon. The Cfengine process command can check for the existence of processes, signal them, and optionally, restart them. As an example, the following command would kill the Apache Web server, and then restart it.
"apache" signal=kill restart="/usr/sbin/apache"
You can run Cfengine manually from the command line by typing cfengine at the shell prompt. This will look for a file called cfengine.conf in the current directory and execute it silently. You can also specify what file to execute with the -f option, as in:
cfengine -f configfile.
Normally, Cfengine executes silently and only produces output if there is a warning or an error condition. You can use the -v option to make Cfengine more verbose. You can use the -n option when testing new scripts. This option tells Cfengine not to take make any actual changes, but step through the script to test its operation.
Normally, you won't want to run Cfengine by hand all of the time. It would be nice to run it automatically. You can run Cfengine from cron with the Cfwrap program. Cfwrap will run Cfengine, collect all of its output into a file, and then email the results back to the system administrator.
I've barely scratched the surface of Cfengine and its capabilities, but I hope I've given you an idea of what Cfengine is, and why it can be useful for administering collections of UNIX systems. There is much more to Cfengine than can be described in one article. There are numerous options to each of the Cfengine commands I looked at, as well as many other commands.
For more information on Cfengine, try the Cfengine Web site at http://www.iu.hioslo.no/~mark/cfengine. There are also two Usenet newsgroups devoted to Cfengine: gnu.cfengine.help and gnu.cfengine.bugs. Finally, the Cfengine distribution comes with an extensive set of online manuals in GNU Info format.
About the Author
Bob McCormick is the senior system engineer at Dimensional Data Company Inc., a Southern Colorado system consulting company specializing in UNIX and Netware. He can be reached by email at: email@example.com.