CFENGINE

Edition 2.7 for version 1.3.16

Mark Burgess
Centre of Science and Technology,
Faculty of Engineering, Oslo College, Norway


Copyright (C) 1995/96 Mark Burgess

Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.

Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "GNU General Public License" is included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.

Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "GNU General Public License" may be included in a translation approved by the author instead of in the original English.

This manual corresponds to CFENGINE Edition 2.7 for version 1.3.16 as last updated October 1996.

Foreword

Cfengine is the result of a three year research project to help solve the problem of system administration on a big network using an expert system combined with a declarative language. After the official release of version 1.3.0, no further features are planned, and modifications will most likely be restricted to bug fixes and perhaps additional wrappers, and ports to new systems (1).

Although all of the final decisions have been my own, a number of others have been pivotal in the development. If I am slow to mention names, or if I am slow to reply or fix bugs, please be patient: I don't have much time for cfengine these days. There are no plans for any major new features in the foreseeable future, but I shall be fixing bugs as they are reported.

Morten Hanshaugen and Hans Petter Holen made it possible to test cfengine on a variety of systems at the university of Oslo. I am ever grateful to Knut Borge for his experience and suggestions. Ola Borrebaek and Richard Stallman have made key suggestions which have influenced the development of cfengine in important ways.

I am grateful to many users for sending me their suggestions and queries. Audun Tornquist did some initial work on the `copy' feature and donated the backup help-script to the distribution. Gord Matzigkeit contributed an autoconf setup (still to be integrated) and made some texinfo improvements to the manual. I cannot begin to thank Andreas Klussmann and for his thorough testing of nearly every in-between version of cfengine which I have put on our local ftp server. Neil Schellenberger has also been extremely helpful. Andrew Ford contributed the self-documentation perl script. Ricky Ralston (Hewlett Packard) provided invaluable information on HPUX-10 and discovered a number of bugs and inaccuracies in the source code: our collaboration on making 1.3.0 the definite system administration tool has been invaluable. Finally, I am grateful to Ann-Mari Torvatn for reading through and helping to improve the documentation.

For up to the minute information on cfengine, workshops, conferences and all that jazz see the web page:

http://www.iu.hioslo.no/~mark/cfengine.html

Bug reports and queries by mail to

bug-cfengine@gnu.ai.mit.edu

(why not bug Jack Barron instead?) or to the more direct address

cfengine@iu.hioslo.no

Two newsgroups are also available now for discussions and bug reports.

---------------------------------------------------------------------
 bug-cfengine-request@prep.ai.mit.edu to subscribe to bug-cfengine
 gnUSENET newsgroup: gnu.cfengine.bug
 cfengine bug reports to: bug-cfengine@prep.ai.mit.edu

This list distributes, to the active maintainers of cfengine.
Bug reports and fixes for, and suggestions for improvements to cfengine.
User discussion of cfengine also occurs here.
---------------------------------------------------------------------
 help-cfengine-request@gnu.ai.mit.edu to subscribe to help-cfengine
 gnUSENET newsgroup: gnu.cfengine.help
 Send contributions to: help-cfengine@gnu.ai.mit.edu

This list is the place for users and installers of cfengine to ask for
help.  Please send bug reports to bug-cfengine instead of posting them
here.

This list is also used for announcements about cfengine and related
programs, and small but important patches.  Announcements of cfengine
releases are also made to info-gnu@gnu.ai.mit.edu (see above)
----------------------------------------------------------------------

Mark Burgess Oslo, October 1996

Overview

In this manual the word "host" is used to refer to a single computer system -- i.e. a single machine which has a name termed its "hostname".

What is cfengine and who can use it?

Cfengine is a language based tool specifically designed for configuring and maintaining BSD and System-5-like operating systems attached to a TCP/IP network. You can think of cfengine as a very high level language--much higher level than Perl or shell: a single statement can result in many hundreds of operations being performed on multiple hosts. The main purpose of cfengine is to allow you to create a single, central configuration file which will define how every host on your network should be configured. The cfengine interpreter runs on every host on the network and parses the central file (or file-set); the configuration of each host is checked against this file and then, if you request it, any deviations from the define configuration are fixed. If such a central file had to mention every single host on a network individually, it would have to be very large, but cfengine uses a flexible system of "classes" which helps you to describe many hosts with a single statement, or make complex decisions which pick out very specific actions for specific hosts if necessary.

In the beginning, cfengine was conceived of as a tool for system administators to be run only by the superuser, but during the course of its development it became clear that it could also be used as a scripting language by ordinary users. It is a handy tool for tidying your old junk files and for making `watchdog' scripts to manage the access rights and permissions on your files when collaborating with other users. As a bonus it contains a text editing language which can be used to perform controlled edits of line-based text files.

The remainder of this manual assumes that you know a little about BSD/System-5 systems and have everyday experience in using either the C-shell or the Bourne shell, or their derivatives. If you are experienced in system administration, you might like to skip the earlier chapters and turn straight to the example See section Example configuration files. This is the probably quickest way to learn cfengine for the initiated. If you are not so familar with system administration and would like a more gentle introduction, then we begin here...

Site configuration

To the system administrator of a small network, comprised of just a few workstations or perhaps even a single mainframe system, it might seem superfluous to create a big fuss about the administration of the system. It is, after all, easy to `fix' things manually should any problems arise, making a link here, writing a script there and so on -- and its probably not even worth writing down what you did because you know that it will always be easy to fix next time around too... But networks have a tendency to expand and--before you know it--you have five different types of operating system and each type of system has to be configured in a special way, you have to make patches to each system and you can't remember whether you fixed that host on the other side of the building... You also discover fairly quickly that what you thought of as BSD or System 5 is not as standard as you thought and that none of your simple scripts that worked on one system work on the others without a considerable amount of hacking and testing. You try writing a script to help you automate the task, but end up with an enormous number of `if..then..else..' tests which make it hard to see what is really going on.

To manage a network with many different flavours of operating system, in a systematic way, what is needed is a more disciplined way of making changes which is robust against reinstallation. After all, it would be tragic to spend many hours setting up a system by hand only to lose everything in an unfortunate disk-crash a week or even a year later when you have forgotten what you had to do. Upgrades of the operating system software might delete your carefully worked out configuration. What is needed is a separate record of all of the patches required on all of the systems on the network; a record which can be compared to the state of each host at any time and which a suitable engine can use to fix any deviations from that reference standard.

The idea behind cfengine is to focus upon a few key areas of basic system administration and provide a language which fulfills this model of system administration. It is done in such a way that the transparency of a configuration program is optimal. It eliminates the need for lots of tests by allowing you to organize your network according to "classes". From a single configuration file (or set of files) you can specify how your network should be configured -- and cfengine will then parse your file and carry out the instructions, warning or fixing errors as it goes.

Key Concepts

Here we mention briefly some of the important issues in system administration which cfengine can help with.

Control files

One of the most important characteristics of BSD and system 5 systems is that they are configured through human-readable text files. To add a new user to the system you must edit `/etc/passwd', to add a new disk you must edit `/etc/fstab' etc. Many applications are also configured with the help of text files. When installing a new system for the first time, or when changing updating the setup of an old system you are faced with having to edit lots of files. In some cases you will have to add precisely the same line to the same file on every system in your network as a change is made, so it is handy to have a way of automating this procedure so that you don't have to load every file into an editor by hand and make the changes yourself. This is one of the tasks which cfengine will automate for you.

Network interface

Each host which you connnect to an ethernet-based network running TCP/IP protocols must have a so-called `net interface'. This network interface must be configured before it will work. Normally one does this with the help of the ifconfig command. This can also be checked and configured automatically by cfengine.

Network configuration involves telling the interface hardware what the internet (IP) address of your system is, so that it knows which incoming `packets' of data to pay attention to. It involves telling the interface how to interpret the addresses it receives by setting the `netmask' for your network (see below). Finally you must tell it which dummy address is to be used for messages which are broadcast to all hosts on your network simultaneously See section netmask.

Network File System (NFS)

Probably the first thing you are interested in doing with a network (after you've had your fill of the world wide web) is to make your files available to some or all hosts on the network, no matter where in your corporate empire (or university dungeon) you might be sitting. In other words, if you have a disk which is physically connected to host A, you would like to make the contents of that disk available to hosts B, C, D... etc. NFS (the network filesystem) does this for you. The process works by `filesystems'.

A filesystem is one partition of a disk drive -- or one unit of disk space which can be accessed by a single `logical device' `/dev/something'. To make a filesystem available to other hosts you have to do three things.

Only after all three of these have been done will a filesystem become available across the network. Cfengine will help you with the last two in a very transparent way. You could also use the textediting facility in cfengine to edit the exports file, but there are other ways update the exports file using netgroups which we shall not go into here. If you are in doubt, look up the manual page on exports.

Name servers (DNS)

There are two ways to specify addresses on the internet (called IP addresses). One is to use the textual address like `ftp.uu.net' and the other is to use the numerical form `192.48.96.9'. Alas, there is no one-to-one correspondence between the numerical addresses and the textual ones, thus a service is required to map one to the other.

The service is performed by one or more special hosts on the network called nameservers. Each host must know how to contact a nameserver or it will probably hang the first time you give it an IP address. You tell it how to contact a nameserver by editing the textfile `/etc/resolv.conf'. This file must contain the domain name for your domain and a list of possible nameservers which can be contacted, in order of priority. Because this is a special file which every host must have, you don't have to use the editing facilities in cfengine explicitly. You can just define the nameservers for each host in the cfengine file and cfengine will do the editing automatically. If you want to change the priority of nameservers later, or even change the list then a simple change of one or two lines in the configuration file will enable you to reconfigure every host on your network automatcally without having to do any editing yourself!

Monitoring important files

Security is an important issue on any system. In the busy life of a system admnistrator it is not always easy to remember to set the correct access rights on every file and this can result in either a security breach or problems in accessing files.

A common scenario is that you, as administrator, fetch a new package using ftp, compile it and install it without thinking too carefully. Since the owner and permissions of the files in an ftp archive remains those of the program author, it often happens that the software is left lying around with the owner and permissions as set by the author of the program rather than any username on your system. The user-id of the author might be anybody on your system -- or perhaps nobody at all! The files should clearly be owned by root and made readable and unwritable to normal users.

Simple accidents and careless actions under stress could result in, say, the password file being writable to ordinary users. If this were the case, the security of the entire system would be compromised. Cfengine therefore allows you to monitor the permissions, ownership and general existence of files and directories and, if you wish, correct them or warn about them automatically.

Making links

One of the difficulties with having so many different variations on the theme of BSD and system 5 based operating systems is that similar files are not always where you expect to find them. They have different names or lie in different directories. The usual solution to the problem is to make an alias for these files, or a pointer from one filename to another. The name for such an alias is a symbolic link.

It is often very convenient to make symbolic links. For example, you might want the sendmail configuration file `/etc/sendmail.cf' to be a link to a global configuration file `/usr/local/mail/etc/sendmail.cf' on every single host on your network so that there is only one file to edit. If you had to make all of these links yourself, it would take a lifetime. Cfengine will make such a link automatically and check it each time time is run. You can also ask it to tidy up old links which have been left around and no longer point to existing files. If you reinstall your operating system later it doesn't matter because all your links are defined in your cfengine configuration file, recorded for all time. Cfengine won't forget it, and you won't forget it because the setup is defined in one central place.

Cfengine will also allow you to make hard links to regular files, but not other kinds of file. A hard link to a symbolic link, is the same as a hard link to the file the symbolic link points to.

Functionality

The notes above give you a rough idea of what cfengine can be used for. Here is a summary of cfengine's capabilities.

How do you run cfengine? You may run cfengine scripts/programs as often as you like. Each time you run a script, the engine determines whether anything needs to be done -- if nothing needs to be done, nothing is done! If you use it to monitor and configure your entire network from a central filebase, then the natural thing is to run cfengine daily with the help of cron. See section cfwrap.

Getting started

What you must have in a cfengine program

A cfengine configuration file for a large network is long and complex, so before we get down to details, let's try to strip away the complexity and look only to the essentials.

Each cfengine program or configuration file is a list of declarations of items to be checked and perhaps fixed. You begin by creating a file called `cfengine.conf'. The simplest meaningful file you can create is something like this:


# Comment...
 
control:

  actionsequence = ( links )

links:

  /bin -> /usr/bin

The example above checks and makes (if necessary) a link from `/bin' to `/usr/bin'. Let's examine this example more closely. In a cfengine program:

In simple example above has three of the four types of object described above. The control: section of any program tells cfengine how to behave. In this example it adds the action links to the actionsequence. For links you could replace some other action. The essential point is that, if you don't have an action sequence, your cfengine program will do absolutely nothing! The action sequence is a list which tells cfengine what do to and in which order.

The links: section of the file tells cfengine that what follows is a number of links to be made. If you write this part of the file, but forget to add links to the actionsequence, then nothing will be done! You can add any number of links in this part of the file and they will all be dealt with in order when--and only when--you write links in the action sequence.

To summarize, you must have:

Now let's think a bit about how useful this short example program is. On a SunOS system, where the directory `/bin' is in fact supposed to be a link, such a check could be useful, but on some other system where `/bin' is a not a link but a separate directory, this would result in an error message from cfengine, telling you that `/bin' exists and is not a link. The lesson is that, if we want to use cfengine to make one single program which can be run on any host of any type, then we need some way of restricting the above link so that it only gets checked on SunOS systems. We can write the following:


# Comment...
 
control:

  actionsequence = ( links  )

links:

  sun4:: 

       /bin -> /usr/bin
       # other links

   osf::

       # other links

The names which have double colons after them are called classes and they are used to restrict a particular action so that it only gets performed if the host running the program is a member of that class. If you are familar with C++, this syntax should make you think of classes definitions in C++. Classes works like this: the names above sun4, sun3, osf etc. are all internally defined by cfengine. If a host running, say, the OSF operating system executes the file it automatically becomes a member of the class osf. Since it cannot be a member more than one of the above, this distinguishes between different types of operating system and creates a hidden if..then...else test.

This is the way in which cfengine makes decisions. The key idea is that actions are only carried out if they are in the same class as the host running the program. Classes are dealt with in detail in the next chapter.

Now let's see how to add another kind of action to the action sequence.


# Comment...
 
control:

  actionsequence = ( tidy links )

links:

  /bin -> /usr/bin

tidy:

   /tmp  pattern=* age=7 recurse=inf

We have now added a new kind of declaration called tidy: which deletes files. In the example above, we are looking at files in the directory `/tmp' which match the pattern `*' and have not been accessed for more than seven days. The search for these files descends recursively down any number of subdirectories.

To make any of this happen we must add the word tidy to the action sequence. If we don't, the declaration will be ignored. Notice also that, regardless of the fact that links: comes before tidy:, the order in the action sequence tells us that all tidy actions will be performed before links:.

The above structure can be repeated to build up a configuration file or script.

Program structure

To summarize the previous section, here is a sketch of a typical cfengine configuration program showing a sensible structure. The various sections are listed in a sensible order which you would probably use in the action sequence.

An individual section-declaration in the program looks something like this:


action-type:

   class1::

       list of things to do...

   class2::

       list of things to do...

action-type is one of the following reserved words:


   groups, control, homeservers, binservers, mailserver, mountables,
   import, broadcast, resolve, defaultroute, directories, miscmounts,
   files, ignore, tidy, required, links, disable, shellcommands, 
   editfiles, processes

The order in which declarations occur is not important to cfengine from a syntactical point of view, but some of the above actions define information which you will want to refer to later. All variables, classes, groups etc. must be defined before they are used. That means that it is smart to follow the order above for the sections in the first line of the above list.

The order in which items are declared is not to be confused with the order in which they are executed. This is determined by the actionsequence, See section control. Probably you will want to coordindate the two so that they match as far as possible.

For completeness, here is a complete summary of the structure of a very general cfengine configuration program. The format is free and use of space is unrestricted, though it is always a good idea to put a space in front before and after parentheses when defining variables.


######################################################################
# 
# Example of structure
#
######################################################################

groups:
 
   group1 = ( host host ...  )
   group2 = ( host host ...  ) 
   ...

######################################################################

control: 

   class::

   site      =  ( mysite )
   domain    =  ( mydomain )
   ...

    actionsequence = 
      (
      action name
      ....
      )

   mountpattern = ( mountpoint )
   homepattern = ( wildcards matching home directories ) 

   addclasses = ( foo bar )

######################################################################

homeservers:

   class::  
           home servers

binservers:

   class::
           binary servers

mailserver:

   class::
           mail server

mountables:

   class::

           list of resources

######################################################################

import: 

   class::    include file

   class::    include file

######################################################################

broadcast:

  class::  ones   # or zeros / zeroes

defaultroute:

   class::  my-gw

######################################################################

resolve:

   any::

       list of nameservers

   ...

Optional features in cfengine

Cfengine doesn't do anything unless you ask it to. When you run a cfengine program it generates no output unless it finds something it believes to be wrong. It does not carry out any actions unless they are declared in the action sequence. In fact it's just like one of those people you try to avoid at the office because they only complain about what's wrong and never ever say anything positive. But all this can change.

If you like, you can make cfengine positively chatty. Cfengine can be run with a number of command line options See section Runtime Options. If you run the program with the `-v' or `--verbose' options, it will supply you cheerily with a resume of what it is doing. Certain warning messages also get printed in verbose mode.

You can ask cfengine to check lots of things -- the timezone for instance, or the domain name. In order for it to check these things, it needs some information from you. All of the switches and options which change the way in which cfengine behaves get specified either on the command line or in the control: section of the control file. Some special control variables are used for this purpose. Here is a short example:


control:

  domain   = ( mydomain.no )
  netmask  = ( 255.255.255.0 )
  timezone = ( MET )

  mountpattern = ( /mydomain/mountpoint )

  actionsequence = 
     (
     checktimezone     # check time zone
     netconfig         # includes check netmask
     resolve           # includes domain
     mountinfo         # look for mounted disks under mountpattern
     )

To get verbose output you must run cfengine with the appropriate command line option `--verbose' or `-v'.

Notice that setting values has a special kind of syntax: a variable name, an equals sign and a value in parentheses. This tells you that the quantity of the left hand side assumes the value on the right hand side. There are lots of questions you might ask at this point. The answers to these will be covered as we go along and in the next chapter.

Before leaving this brief advertisement for control parameters, it is worth noting the definition of mountpattern above. This declares a directory in which cfengine expects to find mounted disks. It will be explained in detail later, for now notice that this defintion looks rather stupid and inflexible. It would be much better if we could use some kind of variables to define where to look for mounted filesystems. And of course you can...

Having briefly scraped the surface of what cfengine can do, turn to the example See section Example configuration files and take a look at what a complete program can look like. If you understand it, you might like to skip through the rest of the manual until you find what you are looking for. If it looks mysterious, then the next chapter should answer some questions in more depth.

Invoking cfengine

Cfengine may be invoked in a number of ways. Here are some examples:

host% cfengine

host% cfengine --file myfile

host% cfengine -f myfile -v -n

host% cfengine --help

The first of these (the default command, with no arguments) looks for a file called `cfengine.conf' in the current directory and executes the commands silently. The second command reads the file `myfile' and works silently. The third works in verbose mode and the -n option means that no actions should actually be carried out, only warnings should be printed. The final example causes cfengine to print out a list of its command line options.

The complete list of options is listed in the summary at the beginning of this manual, or you can see it by giving the -h option. See section Runtime Options

In addition to running cfengine with a filename, you can also treat cfengine files as scripts by starting your cfengine program with the standard shell line:

#!/local/gnu/bin/cfengine -f
#
# My config script
#

Here we assume that you have installed cfengine under the directory `/local/gnu/bin'. By adding a header like this to the first line of your program and making the file executable with the chmod shell command, you can execute the program just by typing its name--i.e. without mentioning cfengine explicitly at all.

As a novice to cfengine, it is advisable to check all programs with the -n option before trusting them to your system, at least until you are familar with the behaviour of cfengine. This `safe' option allows you to see what cfengine wants to do, without actually committing yourself to doing it.

CFINPUTS environment variable

Whenever cfengine looks for a file it asks a question: is the filename an absolute name (that is a name which begins from `/' like /usr/file), is it a file in the directory in which you invoke cfengine or is it a file which should be searched for in a special place?

If you use an absolute filename either on the command line using -f or in the import section of your program (a name which begins with a slash '/'), then cfengine trusts the name of the file you have given and treats it literally.

If you run cfengine without arguments (so that the default filename is `cfengine.conf') or you specify a file without a leading slash in the import section, then the value of the environment variable CFINPUTS is prepended to the start of the file name. This allows you to keep your configuration in a standard place, pointed to by CFINPUTS. For example:


host# setenv CFINPUTS /usr/local/gnu/lib/cfengine/inputs

host# cfengine -f myfile

In this example, cfengine tries to open

`/usr/local/gnu/lib/cfengine/inputs/myfile'.

More advanced concepts

Classes

The idea of classes is central to the operation of cfengine. Saying that cfengine is `class orientated' means that it doesn't make decisions using if...then...else constructions the way other languages do, but only carries out an action if the host running the program is in the same class as the action itself. To understand what this means, imagine sorting through a list of all the hosts at your site. Imagine also that you are looking for the class of hosts which belong to the computing department, which run GNU/Linux operating system and which have yellow spots! To figure out whether a particular host satisfies all of these criteria you first delete all of the hosts which are not GNU/Linux, then you delete all of the remaining ones which don't belong to the computing department, then you delete all the remaining ones which don't have yellow spots. If you are on the remaining list, then you are in the class of all computer-science-Linux-yellow-spotted hosts and you can carry out the action.

Cfengine works in this way, narrowing things down by asking if a host is in several classes at the same time. Although some information (like the kind of operating system you are running) can be obtained directly, clearly, to make this work we need to have lists of which hosts belong to the computer department and which ones have yellow spots.

So how does this work in a cfengine program? A program or configuration script consists of a set of declarations for what we refer to as actions which are to be carried out only for certain classes of host. Any host can execute a particular program, but only certain action are extracted -- namely those which refer to that particular host. This happens automatically because cfengine builds up a list of the classes to which it belongs as it goes along, so it avoids having to make many decisions over and over again.

By defining classes which classify the hosts on your network in some easy to understand way, you can make a single action apply to many hosts in one go -- i.e. just the hosts you need. You can make generic rules for specific type of operating system, you can group together clusters of workstations according to who will be using them and you can paint yellow spots on them -- what ever works for you.

A cfengine action looks like this:


action-type:

   compound-class::
     
       declaration

A single class can be one of several things:

A compound class is a sequence of simple classes connected by dots or `pipe' symbols (vertical bars). For example:


myclass.sun4.Monday::

sun4|ultrix|osf::

A compound class evaluates to `true' if all of the individual classes are separately true, thus in the above example the actions which follow compound_class:: are only carried out if the host concerned is in myclass, is of type sun4 and the day is Monday! In the second example, the host parsing the file must be either of type sun4 or ultrix or osf. In other words, compound classes support two operators: AND and OR, written `.' and `|' respectively. Cfengine doesn't care how many of these operators you use (since it skips over blank class names), so you could write either


solaris|irix::

or


solaris||irix::

depending on your taste. On the other hand, the order in which cfengine evaluates AND and OR operations does matter, and the rule is that AND takes priority over OR, so that `.' binds classes together tightly and all AND operations are evaluated before ORing the final results together. This is the usual behaviour in programming languages.

Cfengine allows you to define switch on and off dummy classes so that you can use them to select certain subsets of action. In particular, note that by defining your own classes, using them to make compound rules of this type, and then switching them on and off, you can also switch on and off the corresponding actions in a controlled way. The command line options -D and -N can be used for this purpose. See also section addclasses.

A logical NOT operator has been added to allow you to exclude certain specific hosts in a more flexible way. The logical NOT operator is (as in C and C++) `!'. For instance, the following example would allow all hosts except for myhost:

   action:

    !myhost::

        command

and similarly, so allow all hosts in a user-defined group mygroup, except for specialhost, you would write

   action:

    mygroup.!myhost::

        command

which reads `mygroup AND NOT myhost'. The NOT operator can also be combined with OR. For instance


   class1|!class2

would select hosts which were either in class 1, or those which were not in class 2.

Finally, there is a number of reserved classes. The following are hard classes for various operating system architectures. They do not need to be defined because each host knows what operating system it is running. Thus the appropriate one of these will always be defined on each host. Similarly the day of the week is clearly not open to definition, unless you are running cfengine from outer space. The reserved classes are:

ultrix, sun4, sun3, hpux, hpux10, aix, solaris, osf, irix, linux, 
        freebsd, netbsd, bsd4_3, newsos, solarisx86, aos,
                         nextstep, bsdos

If these classes are not sufficient to distinguish the hosts on your network, cfengine provides more specific classes which contain the name and release of the operating system. To find out what these look like for your systems you can run cfengine in `parseonly-verbose' mode:


  cfengine -p -v

and these will be displayed. For example, solaris 2.4 systems generate the additional classes sunos_5_4 and sunos_sun4m, sunos_sun4m_5_4.

NOTE! Cfengine uses unqualified host names as classes. Some sites and operating systems use fully qualified names for their hosts. i.e. uname -n returns to full domain qualified hostname. This spoils the class matching algorithms for cfengine, so cfengine automatically truncates names which contain a dot `.' at the first `.' it encounters. If your hostnames contain dots (which do not refer to a somain name, then cfengine will be confused. The moral is: don't have dots in your host names! NOTE: in order to ensure that the fully qualified name of the host becomes a class you must define the domain variable.

Defining classes and making exceptions

Because cfengine works at a very high level, doing very many things for very few lines of code it might seem that some flexibility is lost. When we restrict certain actions to special classes it is occasionally useful to be able to switch off classes temporarily so as to cancel the special actions.

You can define classes of your own which can be switched on and off, either on the command line or from the action sequence. For example, suppose we define a class include. We use addclasses to do this.

addclasses = ( include othersymbols )

The purpose of this would be to allow certain `excludable actions' to be defined. Actions defined by


any.include::
               actions

will normally be carried out, because we have defined include to be true using addclasses. But if cfengine is run in a restricted mode, in which include is set to false, we can exclude these actions.

So, by defining the symbol include to be false, you can exclude all of the actions which have include as a member. There are two ways in which this can be done, one is to negate a class globally using


cfengine -N include 

This undefines the class include for the entire duration of the program.

Another way to specify actions is to use a class to select only a subset of all the actions defined in the actionsequence. You do this by adding a class name to one on the actions in action sequence by using a dot `.' to separate the words. In this case the symbol only evaluates to `true' for the duration of the action to which it it attached. Here is an example:


  links.onlysome
  shellcommands.othersymbols.onlysome

In the first case onlysome is defined to be true while this instance of links is executed. That means that only actions labelled with the class onlysome will be executed as a result of that statement. In the latter case, both onlysome and othersymbols are defined to be true for the duration of shellcommands.

This syntax would normally be used to omit certain time-consuming actions, such as tidying all home directories. Or perhaps to synchronise certain actions which have to happen in a certain order.

For more advanced uses of cfengine you might want to be able to define a class on the basis of the success or failure of a user-program, a shell command or user script. Consider the following example


groups:

   have_cc = ( "/bin/test -f /usr/ucb/cc" "/bin/test -f /local/gnu/cc"  )

Whenever cfengine meets an object in a class list or variable, which is surrounded by either single, double quotes or reversed quotes, it attempts to execute the string as a command passed to the Bourne shell. If the resulting command has return code zero (proper exit) then the class on the left hand side of the assignment (in this case `have_cc') will be true. If the command returns any other value (an error number) the result is false. Since groups are the logical OR of their members (it is sufficient that one of the members matches the current system), the class `have_cc' will be defined above if either `/usr/ucb/cc' or `/local/gnu/cc' exist, or both.

The generic class any

The generic wildcard any may be used to stand for any class. Thus instead of assigning actions for the class sun4 only you might define actions for any architecture by specifying:


  any::
        actions

If you don't specify any class at all then cfengine assumes a default value of any for the class.

Wildcards in directory names

In the two actions files and tidy you define directory names at which file checking or tidying searches should start. One economical feature is that you can define a whole group of directories at which identical searches should start in one fell swoop by making use of wildcards. For example, the directory names

     /usr/*/*
     /bla/*/ab?/bla

represent all of the directories (and only directories) which match the above wildcard strings. Cfengine opens each matching directory and iterates the action over all directories which match.

The symbol `?' matches any single character, whereas `*' matches any number of characters, in accordance with shell file-substitution wildcards.

When this notation is used in directory names, it always defines the starting point for a search. It does not tell the command how to search, only where to begin. The pattern directive in tidy can be used to specify patterns when tidying files and under files all files are considered, See section tidy, and section files, section Recursion.

Variable substitution

When you are building up a configuration file it is very useful to be able to use variables. If you can define your configuration in terms of some key variables, it can be changed more easily later, it is more transparent to the reader of the program and you can also choose to define the variables differently on different types of system. Another way of saying this is that cfengine variables also belong to classes. Cfengine makes use of variables in three ways.

Environment variables are fetched directly from the shell on whatever system is running the program. An example of a special variable is the domain variable from the previous section. Straightforward macro substitution allows you to define a symbol name to be replaced by an arbitrary text string. All these definitions (apart from shell environment variables, of course) are made in the control part of the cfengine program:


control:

  myvar = ( /usr/local/mydir/lib/very/long/path )   # define macro

...

links:

  $(myvar) -> /another/directory

Here we define a macro called myvar, which is later used to define the creation of a link. As promised we can also define class-dependent variables:


control:

  sun4:: myvar = ( sun )
  hpux:: myvar = ( HP )

Cfengine gives you access to the shell environment variables and allows you to define variables of your own. It also keeps a few special variables which affect the way in which cfengine works. When cfengine expands a variable it looks first at the name in its list of special variables, then in the list of user-defined macros and finally in the shell environment for a match. If none of these are found it expands to the empty string.

Variables are referred to in either of two different ways, depending on your taste. You can use the forms $(variable) or ${variable}. The variable in braces or parentheses can be the name of any user defined macro, environment variable or one of the following special internal variables.

faculty
The faculty or site as defined in control (see site).
site
This variable is identical to $(faculty) and may be used interchangeably.
binserver
The default server for binary data. See section Cfengine's model for NFS-mounted filesystems
host
The hostname of the machine running the program.
fqhost
The fully quialified (DNS/BIND) hostname of the system, which includes the domain name as well.
sysadm
The name or mail address of the system administrator.
timezone
The current timezone as defined in control.
domain
The currently defined domain.
class
The currently defined system hard-class (e.g. sun4, hpux).
arch
The current detailed architecture string--an amalgamation of the information from uname.
allclasses
A long string in the form `CFALLCLASSES=class1:class2...'. This variable is a summary of all the defined classes at any given time. It is always kept up to date so that scripts can make use of cfengine's class data.
repchar
The character value of the string used by the file repository in constructing unqiue filenames from path names. This is the character which replaces `/' See section repchar.
split
The character on which list variables are split See section split.
underscoreclasses
If this is set to `on' cfengine uses hard-classes which begin with an underscore, so as to avoid name collisions. See also See section Runtime Options.
spc
Expands simply to a single space. This can be used to place spaces in filenames etc.
tab
Expands to a single tab character.
lf
Expands to a line-feed character (unix end of line).
cr
Expands to the carriage-return character.

These variables are kept special because they play a special role in setting up a system configuration. See section Designing a global system configuration. You are encouraged to use them to define fully generalized rules in your programs. Variables can be used to advantage in defining filenames, directory names and in passing arguments to shell commands. The judicious use of variables can reduce many definitions to a single one if you plan carefully.

You can use variables in the following places:


links:

  osf::
      /$(site)/${host}/directory -> somefile

shellcommands:

  any::

   "/bin/echo $(timezone) | /bin/mail $(sysadm)"
   '/bin/echo "double quotes!"'

The latter possibility enables cfengine's variables to be passed on to user-defined scripts.

Variables can be defined differently under different classes by preceeding the definition with a class name. For example:

control:

   sun4::  my_macro = ( User_string_1 )
   irix::  my_macro = ( User_string_2 )

Here the value assigned to $(my_macro) depends on which of the classes evaluates to true. This feature can be used to good effect to define the mail address of a suitable system administrator for different groups of host.

control:

 physics::   sysadm = ( mark,fred )
 chemistry:: sysadm = ( localsys@domain )

Note, incidentally, that the `-a' option can be used to print out the mail address of the system administrator for any wrapper scripts.

Quoted strings

In several cfengine commands, you use quoted strings to define a quantity of text which may contain spaces. For example


control:

  macro = ( "mycommand" )

editfiles:

  { $(HOME)/myfile

   AppendIfNoSuchLine 'This text contains space'
  }

In each case you may used any one of the three types of quote marks in order to delimit strings,


  ' or " or `

If you choose, say ", then you may not use this symbol within the string itself. The same goes for the other types of string delimiters. Unlike the shell, cfengine treats these three delimiters in precisely the same way. There is no difference between them. If you need to quote a quoted string, then you should choose a delimiter which does not crash with the substring.

Note that you can use special variables for certain symbols in a string See section Variable substitution.

Debugging tips

A useful trick when debugging is to eliminate unwanted actions by changing their class name. Since cfengine assumes that any class it does not understand is the name of some host, it will simply ignore entries it does not recognize. For example:

   myclass::

can be changed to

   Xmyclass::

Since Xmyclass no longer matches any defined classes, and is not the name of any host it will simply be ignored. The -N option can also be used to the same effect. See section Runtime Options.

Access control

It is sometimes convenient to be able to restrict the access of a program to a handful of users. This can be done by adding an access list to the control: section of your program. For example,

control:
    ...
    access = ( mark root )

would cause cfengine to refuse to run the program for any other users except mark and root. Such a restriction would be useful, for instance, if you intended to make set-user-id scripts but only wished certain users to be able to run them. If the access list is absent, all users can execute the program.

Regular expressions

Regular expressions can be used in cfengine in connection with editfiles and processes to search for lines matching certain expressions. A regular expression is a generalized wildcard. In cfengine wildcards, you can use the characters '*' and '?' to match any character or number of characters. Regular expressions are more complicated than wildcards, but have far more flexibility.

NOTE: the special characters `*' and `?' used in wildcards do not have the same meanings as regular expressions!.

Some regular expressions match only a single string. For example, every string which contains no special characters is a regular expression which matches only a string identical to itself. Thus the regular expression `cfengine' would match only the string "cfengine", not "Cfengine" or "cfengin" etc. Other regular expressions could match more general strings. For instance, the regular expression `c*' matches any number of c's (including none). Thus this expression would match the empty string, "c", "cccc", "ccccccccc", but not "cccx".

Here is a list of regular expression special characters and operators.

`\'
The backslash character normally has a special purpose: either to introduce a special command, or to tell the expression interpreter that the next character is not to be treated as a special character. The backslash character stands for itself only when protected by square brackets [\] or quoted with a backslash itself `\\'.
`\b'
Matches word boundary operator.
`\B'
Match within a word (operator).
`\<'
Match beginning of word.
`\>'
Match end of word.
`\w'
Match a character which can be part of a word.
`\W'
Match a character which cannot be part of a word.
`any character'
Matches itself.
`.'
Matches any character
`*'
Match zero or more instances of the previous object. e.g. `c*'. If no object precedes it, it represents a literal asterisk.
`+'
Match one or more instances of the preceding object.
`?'
Match zero or one instance of the preceding object.
`{ }'
Number of matches operator. `{5}' would match exactly 5 instances of the previous object. `{6,}' would match at least 6 instances of the previous object. `{7,12}' would match at least 7 instances of, but no more than 12 instances of the preceding object. Clearly the first number must be less than the second to make a valid search expression.
`|'
The logical OR operator, OR's any two regular expressions.
`[list]'
Defines a list of characters which are to be considered as a single object (ORed). e.g. `[a-z]' matches any character in the range a to z, `abcd' matches either a, b, c or d. Most characters are ordinary inside a list, but there are some exceptions: `]' ends the list unless it is the first item, `\' quotes the next character, `[:' and `:]' define a character class operator (see below), and `-' represents a range of characters unless it is the first or last character in the list.
`[^list]'
Defines a list of characters which are NOT to be matched. i.e. match any character except those in the list.
``[:class:]''
Defines a class of characters, using the ctype-library. @table @code @item alnum Alpha numeric character @item alpha An alphabetic character @item blank A space or a TAB @item cntrl A control character. @item digit 0-9 @item graph same as print, without space @item lower a lower case letter @item print printable characters (non control characters) @item punct neither control nor alphanumeric symbols @item space space, cariage return, linefeed, vertical tab and form-feed. @item upper upper case letter @item xdigit a hexadecimal digit 0-9, a-f @end table
``(..)''
Groups together any number of operators.
`\digit'
Backreference operator (refer to the GNU regex documentation).
`^'
Match start of a line.
`$'
Match the end of a line.

Here is a few examples. Remember that some commands look for a regular expression match of part of a string, while others require a match of the entire string See section editfiles.


^#        match string beginning with the # symbol
^[^#]      match string not beginning with the # symbol
^[A-Z].+  match a string beginning with an uppercase letter
          followed by at least one other character  

Designing a global system configuration

This chapter is about building strategies for putting together a site configuration for your entire network.

Cfengine's model for NFS-mounted filesystems

Most of the filesystems that you will want to make available across the network are going to fall into one of two categories. In cfengine parlance these are called home directories and binary directories. A home directory is a place where users' login directories are kept. This is traditionally a directory called `/home' or `/users' or some subdirectory of these. A binary directory is a place where compiled software is kept. Such files (which do not belong to the pure operating system release) are often placed in a directory called `/usr/local' or simply `/local'.

In this chapter we shall consider a scheme for using cfengine to make NFS filesystem management quite painless.

NFS filesystem resources

Using the Network File System (NFS) in a large workstation environment requires a bit of planning. The idea of NFS is to share files on one host with other hosts. In most cases, filesystems to be shared across the network fall into two categories: binary filesystems (those which contain compiled software) and user or home filesystems (which contain users' login areas).

The most simple minded way to share resources would be to mount every resource (each available NFS filesystem) onto every host. To avoid collisions, each filesystem would have to have a unique name. This is one possibility, but not a very intelligent one. As experienced users will realize, cross-mounting too many NFS filesystems is a recipe for all kinds of trouble.

Cfengine offers a simple model which can help you pick out only the resources you need from the list of NFS filesystems. It will then mount them automatically and edit the appropriate filesystem tables. It does this by defining classes of hosts. For instance -- you really don't need to mount a binary filesystem for an ultrix system onto an HPUX system. There would be no point -- binary rescources are architecture or hardclass dependent. But home directories are architecture independent.

Cfengine lets you to define a list of allowed servers for various hosts so that only filesystems from the servers will be considered for mounting!

Unique filesystem mountpoints

The first step towards treating NFS filesystems as network resources is to invent a naming scheme so that every filesystem has a unique name on which it can be mounted. If we don't sort this out now, we could find two or more hosts with a filesystem called /usr/local, both of which we might like to mount since they contain different software.

A simple but extremely useful naming scheme is the following. (2) If you don't like this scheme you can invent your own, but the remainder of the text will encourage you to use this one. Each filesystem is given a directory name composed of three parts:


/site/host/contents

The first directory (which only exists to create a suitable mountpoint) is the name of your local site. If you are a physics department at a university (with a separate setup) you could call this `physics'. It could be your company name or whatever. The second piece is the name of the host to which the disk space is physically attached. The final piece is the name of the filesystem. Here are some typical examples:

/physics/einstein/local    # /usr/local for einstein@physics
/physics/newton/u1         # user partition 1 for newton@physcs

On the machines which are home to the `local' partition, it is better to make a link to /usr/local than call the filesystem /usr/local directly. This is because it makes the procedure of organizing the entire network much clearer.

It is worth noting that, when you ask cfengine to mount such a resource, it will automatically make the mount directory and can easily be asked to make a link to /usr/local, so this small amount of extra work is really no work at all.

The whole naming convention is compactly summarized by defining a mount point variable. See section mountpattern. With the present scheme, this can be defined as

mountpattern = ( /$(site)/$(host) )

so that it evaluates to the name of the host executing the file regardless of who that may be. This variable is used together with the homepattern pattern variable, which is used to distinguish between home directories and binary resources. See section homepattern. You can think of this as being part of the naming convention. In this text, we use the convention u1 u2 u3... for home disks. You could equally well use home1 home2... etc. As long as the name is unique, it doesn't matter.

The full list of named resources should now be listed in the mountables list, which is simply a list of all the resources available for mounting on the network. See section mountables.

How does it work?

Once you have defined your unique names, how does cfengine know what to mount? The idea is now to define a list of servers for each class of hosts. See section binservers.

Suppose we make a binserver declaration:


binservers:

  mygroup.sun4::

     einstein
     newton

This would tell cfengine that it should mount all binary resources from hosts einstein or newton onto any host of type sun4 in the group mygroup. Every filesystem which is listed in mountables and is not a home directory will be mounted. See section mountables.

Home directories and binary resources are kept separate automatically by cfengine, because a home directory is one whose contents-name matches the homepattern pattern variable. See section Unique filesystem mountpoints.

A homeserver declaration:


homeservers:

  mygroup::
   
     einstein
     newton
     schwinger
     feynman
 

would correspondingly mean mount all the home directory resources on the hosts in the list on all hosts in the group mygroup. Clearly it is unnecessary to distinguish between the architecture platform types of the actual servers for user directories.

In each case, cfengine will mount filesystems, make the appropriate directories for the mount point and edit the filesystem table. See section actionsequence.

Special variables

Once you have mounted a resource on a unique directory, you have access to all of the relevant filesystems on your network -- but you really wanted the `local' filesystem to be mounted on /usr/local. All you need do now is to make a link:


links:

  any::

      /usr/local  -> /$(site)/$(binserver)/local

The meaning of this is that, on any host, the directory /usr/local should be a link to the `nearest' binary server's `local' resource. The $(binserver) variable can in principle expand to any binary server in the list. In practice, cfengine goes through the list in order and picks the first filesystem resource which matches.

Could this lead to a collision? Suppose we are on the host `einstein' and we execute the above command. The host `einstein' has a filesystem /physics/einstein/local on its local disk -- it is in fact the binary server for the network, so it certainly doesn't need to mount any NFS filesystems. But this is no problem because cfengine automatically treats $(host) as the highest priority binary server for any host. That means that if you have a local filesystem, it will always have priority.

In contrast, if the host `schwinger' ran the command above, it would find no local filesystem called /physics/schwinger/local, so it would go along the list of defined binary servers, find `einstein' and try again. It will succeed in finding `einstein' provided all the binary servers were mounted before the link command is executed. This means that you should structure the actionsequence so that all filesystems are mounted before any links are made.

With a little practice, the cfengine model can lead to an enormous simplification of the issue of NFS-mountable resources.

NOTE: cfengine does not try to export filesystems, only mount already exported filesystems. If you want to automate this procedure also, you can use the editfiles facility to add entries to `/etc/exports' See section editfiles. In practice this is very difficult to do and perhaps not desirable.

Example programs for mounting resources

Let's write a very simple configuration for a network with only one server called hal, where all the hosts are of the same operaing system type. In such an example we can avoid using classes altogether.


control:

  site   = ( univ )
  domain = ( univ.edu )

  actionsequence =
     (
     mountall
     mountinfo
     addmounts
     mountall
     links
     )

binservers:

   hal

homeservers:

   hal

mailserver:

   hal:/var/spool/mail

mountables:

   hal:/univ/home1
   hal:/univ/home2
   hal:/univ/local

links:

   /usr/local -> /univ/local

In this example, we have only one type of host so the configuration is the same for each of them: no class references are required. If we look through the action sequence we see that the program first mounts all the filesystems which are already defined on each host. It does this to be sure that everything which is already set up to be mounted is mounted. Let's assume that there are no problems with this.

The next thing that happens is that mountinfo builds a list of the filesystems which each host has successfully mounted. Then by calling addmounts we ask cfengine to check whether the host is missing any filesystems. What happens is that cfengine first looks to see what servers are defined for each host. In this case all hosts on the network have only one server: hal. Hal is defined as a server for both binary data and `home' data -- i.e. users' home directories. The list mountables tells cfengine what filesystems are available over the network for the server hal. There are three filesystems which can be mounted, called `/univ/home1', `/univ/home2' and `/univ/local'. Cfengine checks to see whether each of these filesystems is mounted and, if not, it builds the necessary directories, edits the necessary files and mounts the filesystems.

Finally we come to links in the action sequence. This tells cfengine to look at the defined links. There is one link defined: a link from `/usr/local' to the mounted filesystem `/univ/local'. Cfengine checks and tries to make the link if necessary. If all goes well, each host on the network should now have at least three filesystems mounted and a link from `/usr/local' to `/univ/local'.

Here is another simple example program for checking and automatically mounting an NFS based /usr/local and all home directories onto all hosts on a small network. Here we have several servers and must therefore use some classes.

#
#  Mounts
#

control: 

   site      = ( mysite )
   domain    = ( mysite.country ) 
   sysadm    = ( mark ) 
   netmask   = ( 255.255.255.0 )

   actionsequence = 
      (
      mountall
      mountinfo
      addmounts
      mountall
      links
      )

   mountpattern = ( /$(site)/$(host) )
   homepattern   = ( u? )                # u1 u2 u3 etc..

groups:

   MyGroup =
      (
      host1
      host2
      binserver1
      binserver2
      )

######################################################################

homeservers:

   MyGroup:: host1

binservers:

   MyGroup.sun4::   server1
   MyGroup.ultrix:: server2

mailserver:

   host1:/usr/spool/mail

mountables:

   host1:/mysite/host1/u1
   host1:/mysite/host1/u2
   server1:/mysite/server1/local
   server2:/mysite/server2/local

##########################################################################

links:

      /usr/local  -> /${site}/${binserver}/local

Let's suppose we run this program on host2 which is an ultrix machine. This host belongs to the class mygroup and the hardclass ultrix. This tells us that its homeserver is host1, its binary server is server2 and its mailserver is host1. Moreover, since the homepattern matches any filesystem ending in u-something, it recognizes the two home directories in the mountables list -- and therefore the two binary directories also.

The action sequence starts by mounting all of the filesystems currently in the filesystem table `/etc/fstab'. It then scans the list of mounted filesystems to find out what is actually mounted. Since the homeserver is host1, we know that our host has to mount all home-filesystems from this server, so it checks for `host1:/mysite/host1/u1' and `host1:/mysite/host1/u2'. If they are not present they are added to `/etc/fstab'(3). Next, we know that the binary server is server1, so we should check for `server1:/mysite/server1/local'. The mail server is also checked for and added if necessary. Cfengine then tries to mount all filesystems once again, so that the new filesystems should be added.

Note that, in the process of adding the filesystems to `/etc/fstab', cfengine creates the directories up to and including the point at which the filesystems should be mounted. If something prevents this -- if we try to mount on top of a plain file for instance --- then this will result in an error.

Finally, we reach the link section and we try to expand the variables. $(site) expands to `mysite'. $(binserver) expands first to the hostname (host2), but `/mysite/host2/local' does not exist, so it then goes to the binserver list, which subsitutes server1 for the value of $(binserver). Since `/mysite/server1/local' does exist and is now mounted, cfengine makes a link to this directory from `/usr/local'. The script is then completed.

If the script is run again, everything should now be in place so nothing happens. If for some reason it failed the first time, it will fail again. At any rate it will either do the job once and for all or signal an error which must be corrected by human intervention(4).

Using the automounter

The automounter is a daemon based service which replaces static mounting of NFS filesystems with a dynamical model. When the automounter is running, filesystems are mounted only when a user tries to access a file which resides on one of those filesystem. After a given period (usually five minutes) any filesystem which has not been accessed is unmounted. The advantage of this scenario is that hanging servers do not affect the behaviour of hosts which mount their filesystems, unless a specific file is being accessed. In both cases, filesystems must be exported in order to be mountable.

It is not the purpose of this section to explain the use of the automounter in detail, only to offer hints as to how cfengine can be used to simplify and rationalize automount configuration for the already initiated. Let us begin by comparing the behaviour of the automounter with the cfengine model for mounted filesystems.

The autmounter is designed to be used together with a global configuration file, distributed by NIS (the network information sevice). As such, all hosts read the same configuration file. This makes it appear as though all hosts end up mounting every filesystem in the automount configuration database, but this is not so in practice because filesystems are only mounted if required. Thus a system which does not require a filesystem will not attempt to mount it. Moreover, the existence of a global configuration file does not affect which hosts have the right to mount certain filesystems (which is specified by exports or share on the relevant server), thus a request to mount a non-exported filesystem will result in an access denial. The autmounter is configured locally on each host in files named `/etc/auto_master', `auto_direct' etc.

In the cfengine static mounting scheme, you define a list of binary and home servers. The filesystem table is modified on the basis of these decisions, and filesystems are only added if cfengine deems it appropriate to mount them on a given host. The idea here is to minimize the number of filesystems mounted to those which are known to be required. Again the issue of access permissions must be arranged separately. These filesystems are placed directly in `/etc/fstab', or the equivalent for your system.

From cfengine, you can use the automounter instead of the static mount model by

The autmounter was created to solve certain problems which cfengine now solves (in the author's opinion) better. For example, the use of the `hosts' map in the autmounter mounts filesystems like `/usr/local' on different (uniquely named) mountpoints for each host in order to avoid name space collisions. Using cfengine and a unique naming scheme, you can achieve the same thing more cleanly, without all of the gratuitous linking and unlinking which the automounter performs by itself. Moreover, the idea of a unique namespace is better practice and more in keeping with new global filesystem ideas such as AFS and DFS. The only advantage of the automounter is that one avoids the annnoying error messages from hung servers about "NFS server not responding". In that respect, it seems sensible to use only direct mounts and a unique name space.

Some systems advocate grouping all users' login (home) directories under a common directory called `/home' or `users'. The automounter goes through all manner of contortions to achieve this task. If you use a unique naming scheme like the one advocated here, this is a trivial task. You simply arrange to mount or automount all user directories, such as

   /site/host/home1
   /site/host/home2
   ...

and then link them as follows:


   /home +> /site/host/home1
   /home +> /site/host/home2
   ...

Finally, you should be aware that the automounter does not like to be mixed with static mount and unmount operations. Automounted filesystems take priority over statically mounted filesystems, but the autmounter can be confused by manually mounting or unmounting filesystems while it is running.

Using netgroups

If you use the network information service (NIS) on your local network then you may already have defined netgroups consisting of lists of hosts which belong to specific owners at your site. If you have, then you can use these groups within cfengine. This means that you can use the same groups in the /etc/exports file as you use to define the mount groups and classes. See section groups.

A netgroup is a list of hostnames or user names which are registered in the network information service (NIS) database under a specific name. In our case we shall only be interested in lists of hostnames.

To make a netgroup you need to define a list in the file /etc/netgroup on your NIS server. If you are not the NIS administrator, you will have to ask to have a netgroup installed. The form of a netgroup list of hosts is:


mylist-name      (host1,,) (host2,,) (host3,,) (host4,,)

norway-sun4-host (saga,,) (tor,,) (odin,,)
foes-linux-hosts (borg,,)

Each list item has three entries, but only the first is relevant for a host list. See the manual pages on netgroups for a full explanation of the meaning of these fields.

The usefulness of netgroups is that they can be used to stand for a list of hostnames in system files like `/etc/exports'. This compresses the amount of text in this file from a long list to a single name. It also means that if you use the same list of hosts from a netgroup inside cfengine when defining groups and classes, you can be sure that you are always using the same list. In particular it means that you don't have to update multiple copies of a list of hosts.

The netgroups can now be used in cfengine programs by using the + or @+ symbols in the groups section. See section groups.

Files and links

File and link management takes several forms. Actions are divided into three categories called files, tidy and links. The first of these is used to check the existence of, the ownership and permissions of files. The second concerns the systematic deletion of garbage files. The third is a link manager which tests, makes and destroys links. The monitoring of file access bits and ownership can be set up for individual files and for directory trees, with controlled recursion. Files which do not meet the specified criterea can be `fixed' --i.e. automatically set to the correct permissions, or can simply be brought to the attention of the system admnistrator by a warning. The syntax of such a command is as follows:


files:

  class::

    /path mode=mode owner=owner group=group

         recurse=no-of-levels action=action

The directory or file name is the point at which cfengine begins looking for files. From this point the search for files proceeds recursively into subdirectories with a maximum limit set by the recurse directive, and various options for dealing with symbolic links and device boundaries. The mode-string defines the allowed filemode (by analogy with `chmod') and the owner and group may specify lists of acceptable user-ids and group-ids. The action taken in response to a file which does not meet acceptable criterea is specified in the action directive. It includes warning about or directly fixing all files, or plain files or directories only. Safe defaults exist for these directives so that in practice they may be treated as options.

For example,

files:

  any::
       /usr/*/bin mode=a+rx,o-w own=root r=inf act=fixall

which (in abbreviated form) would check recursively all files and directories starting from directories matching the wildcard (e.g. `/usr/local/bin', `/usr/ucb/bin'). By default, fixall causes the permissions and ownership of the files to be fixed without further warning.

One problem with symbolic links is that the files they point to can get deleted leaving a `hanging pointer'. Since cfengine can make many hundreds of links without any effort, there is the danger that, in time, the system could become full of links which don't point anywhere. To comat this problem, you can set the option links=tidy in the files section. If this is set, cfengine will remove any symbolic links which do not point to existing files See section files.

The creation of symbolic links is illustrated in figure 1 and the checking algorithm was discussed in section 2. In addition to the creation of single links, one may also specify the creation of multiple links with a single command. The command

links:

   binaryhost::

      /local/elm/bin +> /local/bin

links all of the files in `/local/elm/bin' to corresponding files in `/local/bin'. This provides, amongst other things, one simple way of installing software packages in regular `bin' directories without controlling users' PATH variable. A further facility makes use of cfengine's knowledge of available (mounted) binary resources to search for matches to specific links. Readers are referred to the full documentation concerning this feature.

The need to tidy junk files has become increasingly evident during the history of cfengine. Files build up quickly in areas like `/tmp', `/var/tmp'. Many users use these areas for receiving large ftp-files so that their disk usage will not be noticed! To give another example, just in the last few months the arrival of netscape World Wide Web client, with its caching facilities, has flooded harddisks at Oslo with hundreds of megabytes of WWW files. In addition the regular appearence of `core' files(5) and compilation by-products (`.o' files and `.log' files etc.) fills disks with large files which many users do not understand. The problem is easily remedied by a few lines in the cfengine configuration. Files can be deleted if they have not been accessed for n-days. Recursive searches are both possible and highly practical here. In following example:

tidy:

   AllHomeServers::

      home                 pattern=core       r=inf age=0
      home/.wastebacket    pattern=*          r=inf age=14
      home/.netscape-cache pattern=cache????* r=inf age=2
      home/.MCOM-cache     pattern=cache????* r=inf age=2
      home/.netscape       patern=cache????*  r=int age=2

all hosts in the group `AllHomeServers' are instructed to iterate over all users' home directories (using the wildcard home) and look for files matching special patterns. Cfengine tests the access time of files and deletes only files older than the specified limits. Hence all core files, in this example, are deleted immediately, whereas files in the subdirectory `.wastebasket' are deleted only after they have lain there untouched for 14 days, and so on.

As a system administrator you should, of course, exercise great caution when making rules which can delete users' files. A single slip of the hand can result in a rule which will irretreivably delete files.

When making a `tidy' strategy you should probably coordinate with your backup policy. You should not delete files until after you have taken a backup, so that -- if the worst should happen -- you are covered against possible accidents.

Cfengine helps to some extent to keep track of what files it deletes. When tidying users' home directories it creates a log file of all files which were deleted on the last tidy operation. This log is called ~/.cfengine.rm.

You might consider tidying certain files only once a week, in which case a command such as


tidy:

   AllHomeServers.Sunday::

       files to tidy

could be useful. Nonsense files, such as `core' files could be tidied every night.

NOTE! Be careful when telling cfengine to delete core files. If you write a wildcard like core*, then you could risk deleting important system files such as core.h.

Log files written by cfengine

Cfengine keeps two kinds of logfile. The first is kept for every user (every subdirectory of a home directory filesystem). A file ~/.cfengine.rm keeps a list of all the files which were deleted during the last pass of the tidy function. This is useful for users who want to know files have been removed without their blessing.

Another file is built when cfengine searches through file trees in the files action. This is a list of all programs which are setuid root, or setgid root. Since such files are a potential security risk, cfengine always prints a warning when it encounters a new one (one which is not already in its list). This allows the system administrator to keep a watchful eye over new programs which appear and give users root access. The cfengine log is called /etc/cfengine.log. The file is not readable for general users.

Cfengine keeps its current process identifier in /etc/cfengine.pid.

Copying files

The administration of a system often requires the copying of files. The reason for this is usually that we would like to distribute a copy of a particular file, from some master location and ensure that all of the copies are up to date. Another use for this is to install software from one directory (perhaps on a CD ROM) to another.

Cfengine helps this process by allowing you to copy a single file or a file tree, from one directory to another, perhaps checking the permissions and owners of a file to adjust the copies in some special way. The files are checked by cfengine using one of two methods.

Cfengine allows you to do the following

You can find out more about copying in the reference section See section copy.

Iterating over lists

Shell list variables are normally defined by joining together a list of directories using a concatenation character such as `:'. A typical example of this is the PATH variable:


PATH=/usr/bin:/usr/local/bin:/usr/sbin

It is convenient to be able to use such variables to force cfengine to iterative over a list. This gives us a compact way of writing repeated operations and it allows a simple method of communication with the shell environment. For security reasons, iteration is supported only in the following contexts:

This typically allows communication with PATH-like environment variables in the shell.

In these contexts, any variable which has the form of a list joined together by colons will be iterated over at compilation time. Note that you can change the value of the list separator using the split variable in the control section of the program See section split.

For example, to link all of the binary files in the PATH environment variable to a single directory, tidying dead links in the process, you would write


control:

  actionsequence = ( links tidy )

links:

  /allbin +> $(PATH)

tidy:

  # Hopefully no-match matches nothing

  /allbin pattern=no-match age=0 links=tidy

no-match is not a reserved word in cfengine, this is just a string you do not expect to match any file.

Alternatively, you might want to define an internal list using a space as a separator:


control:

   split = ( " " )

   mylist = ( "mark ricky bad-dude" )

tidy:

   /mnt/home1/$(mylist) pattern=*.cfsaved age=1

This example iterates the tidy action over the directories `/mnt/home1/mark', `/mnt/home1/ricky' and `/mnt/home1/bad-dude'.

The number of list variables in any path or filename should normally be restricted to one or two, since the haphazard combination of two lists will seldom lead to any meaningful pattern. The only obvious exception is perhaps to iterate over a common set of child-directories like `bin', `lib' etc in several different package directories.

Editing Files

One of the characteristics of BSD/System 5 systems is that they are configured primarily by human-readable textfiles. This makes it easy for humans to configure the system and it also simplifies the automation of the procedure. Most configuration files are line-based text files, a fact which explains the popularity of, for example, the Perl programming language. Cfengine does not attempt to compete with Perl or its peers. Its internal editing functions operate at a higher level which are designed for transparency rather than flexibilty. Fortunately most editing operations involve appending a few lines to a file, commenting out certain lines or deleting lines.

An example of the use of these is the following. Each new GNU/Linux installation contains a line in the start-up scripts which deletes the contents of the `message of the day' file each time the system boots. On a system which boots often this would be irritating. This line could be commented out for every GNU/Linux system on the network with a simple command:


editfiles:

   linux::

      { /etc/rc.d/rc.S

      HashCommentLinesContaining "motd"
      }

Commands containing the word `Comment' are used to `comment out' certain lines from a textfile--i.e. render a line impotent without actually deleting it. Three types of comment were supported originally: shell style (hash) `#', `%' as used in TeX and on AIX systems, and C++-style `//'.

A more flexible way of commenting is now possible, using directives which first define strings which signify the start of a comment and the end of a comment. A single command can then be used to render a comment. Th default values of the comment-start string is `# ' and the default comment-end string is the empty string. For instance, to define C style comments you could write:


  { file

  SetCommentStart "/* "
  SetCommentEnd   " */"

  # Comment out all lines containing printf!

  CommentLinesMatching ".*printf.*"
  }

Other applications for these editing commands include monitoring and controlling root-access to hosts by editing files such as `.rhosts' and setting up standard environment variables in global shell resource files-- for example, to set the timezone.

Files are loaded into cfengine and edited in memory. They are only saved again if modifications to the file are carried out, in which case the old file is preserved by adding a suffix to the filename. When files are edited, cfengine generates a warning for the administrator's inspection so that the reason for the change can be investigated.

The behaviour of cfengine should not be confused with that of sed or perl. Some functionality is reproduced for convenience, but the specific functions have been chosen on the basis of (i) their readability and (ii) the fact that they are `frequently-required-functions'. A typical file editing session involves the following points:

Equivalent one-line sed operations involve editing the same file perhaps many times to achieve the same results--without the safety checks in addition.

NOTE: an extremely powerful feature of cfengine is the ability to edit a similar file belonging to every user in the system. For example, as a system administrator, you sometimes need to ensure that users have a sensible login environment. Changes in the system might require all users to define a new environment variable, for instance. This is achieved the with home pseudo-wildcard. If one writes


  { home/.cshrc

  AppendIfNoSuchLine "# Sys admin/cfengine: put next line here"
  AppendIfNoSuchLine "setenv PRINTER newprinter"
  }

then the users' files are checked one-by-one for the given lines of text, and edited if necessary.

Disabling and the file repository

The existence of certain files can compromise the integrity of your system and you may wish to ensure that they do not exist. For example, some manufacturers sell their workstations with a `+' symbol in the file `/etc/hosts.equiv'. This means that anyone in your NIS domain has password free access to the system!! Since this is probably not a good idea, you will want to disable this file by renaming it, or simply deleting it.


  disable:

     /etc/hosts.equiv

Other files compromise the system because they grow so large that they fill an entire disk partition. This is typically true of log files such as the system 5 files `/var/adm/wtmpx' and `/var/lp/logs/lpsched'. Other files like /var/adm/messages get "rotated" by the system so that they do not grow so large as to fill the disk. You can make cfengine rotate these files too, by writing


disable:

    Sunday::

    /var/lp/logs/lpsched  rotate=3

Now, when cfengine is run, it renamed the file `lpsched' to a file called `lpsched.1'. It also renames `lpsched.1' as `lpsched.2' and so on, until a maximum of 3 files are kept. After passing 3, the files `fall off the end' and are deleted permanently. This procedure prevents any log files from growing too large. If you are not interested in keeping back-logs, then you may write rotate=empty and cfengine will simply empty the log file.

When ever cfengine disables a file (disable or links with the `!' operator), or saves a new file on top of an old one (copy or editfiles), it makes a backup of the original. Usually disabled files are renamed by appending the string `.disabled' the the filename; copied files are saved by appending the string `.cfsaved'. It is possible to switch off backup file generation in the copy feature by setting the variable backup=false, but a better way of managing disabled and backed-up files is to use a directory in which you collect all such files for the whole system. This directory is called the file repository and is set in the control part of the program, as follows:


  control:

     repository = ( directory-name )

If this variable is defined, cfengine collects all backup and disabled files (except for rotated files) in this directory, using a unique pathname. You can then inspect these files in the repository and arrange to tidy the repository for old files which are no longer interesting.

Running user scripts

Above all, the aim of cfengine is to present a simple interface to system administrators. The actions which are built into the engine are aimed at solving the most pressing problems, not at solving every problem. In many cases administrators will still need to write scripts to carry out more specific tasks. These scripts can still be profitably run from cfengine. Variables and macros defined in cfengine can be passed to scripts so that scripts can make maximal advantage of the class based decisions. Also note that, since the days of the week are also classes in cfengine, it is straightforward to run weekly scripts from the cfengine environment (assuming that the configuration program is executed daily). An obvious use for this is to update databases, like the fast-find database one day of the week, or to run quota checks on disks.

shellcommands:

   myhost.Sunday::

      "/usr/bin/find/updatedb"

Cfengine scripts can be passed variables using normal variable substitution:

control:

   cfbin     = ( /local/gnu/lib/cfengine/bin )
   backupdir = ( /iu/dax/backup )   

shellcommands:
  
  "$(cfbin)/cfbackup -p -f $(backupdir) -s /iu/nexus/u1"

If you need to write a particularly complex script to expand cfengine's capabilities, it might be useful to have full access to the defined classes. You can do this in one of two ways:

Managing processes

Cfengine allows you to check for the existence of processes on your system, send those processes signals (such as kill) and perhaps restart those processes. Typical applications for this are sending `cron' and `inetd' the HUP signal, after editing their configuration files, or killing unwanted processes (such as user programs which hog the system at peak usage times).

You can read more about this in the reference section See section processes.

Command reference

In this section you will find each facet of a cfengine program listed together with an appropriate explanation. The commands are presented in alphabetical order for ease of lookup. Use this section in conjunction with the example program See section Example configuration files.

binservers

The binservers declaration need only be used if you are using cfengine's model for mounting NFS filesystems. This declaration informs hosts of which other hosts on the network possess filesystems containing software (binary files) which client hosts should mount. This includes resources like programs in /usr/local and so on. A host may have several binary servers, since there may be several machines to which disks are physically attached. In most cases, on a well organized network, there will be only one architecture server per UNIX platform type, for instance a SunOS server, an ULTRIX server and so on.

Binary servers are defined as follows:


binservers:

   physics.sun4::   sunserver sunserver2
   physics.linux::  linuxserver 

The meaning of this declaration is the following. All hosts of type sun4 which are members of the group physics should mount any binaries declared in the mountables resource list which belong to hosts sunserver or sunserver2. Similarly all linux machines should mount binary filesystems in the mountables list from linuxserver.

Cfengine knows the difference between binaries and home directories in the mountables list, because home directories match the pattern given by homepattern. See section homepattern. See section homeservers.

Note that every host is a binary server for itself, so that the first binary server (and that with highest priority) is always the current host. This ensures that local filesystems are always used in preference to NFS mounted filesystems. This is only relevant in connection with the variable $(binserver).

broadcast

This information is used to configure the network interface for each host.

Every local area network has a convention for determining which internet address is used for broadcast requests. Normally this is an address of the form aaa.bbb.ccc.255 or aaa.bbb.ccc.0. The difference between these two forms is whether all of the bits in the last number are ones or zeroes respectively. You must find out which convention is used at your establishment and tell cfengine using a declaration of the form:

broadcast:

  any::

     ones     # or zeros, or zeroes

In most cases you can use the generic class any, since all of the hosts on the same subnet have to use the same convention. If your configuration file encompasses several different subnets with different conventions then you will need to use a more specific.

Cfengine computes the actual value of the broadcast address using the value specified above and the netmask See section netmask.

control

The fundamental piece of any cfengine script or configuration file is the control section. If you omit this part of a cfengine script, it will not do anything! The control section is used to define certain variables, set default values and define the order in which the various actions you have defined will be carried out. Because cfengine is a declarative or descriptive language, the order in which actions appear in the file does not necessarily reflect the order in which they are executed.

The control section is a sequence of declarations which looks something like the following example:


control:

  site     = ( univ )
  domain   = ( univ.edu )
  sysadm   = ( admin@computing.univ.edu )
  netmask  = ( 255.255.252.0 )
  timezone = ( EDT )
  nfstype  = ( nfs )

  sensiblesize  = ( 1000 )
  sensiblecount = ( 2 )
  editfilesize  = ( 4000 )

  actionsequence =
     (
     links.some
     mountall
     links
     files
     )

  myvariable = ( something )
  mymacro    = ( somethingelse )

Parentheses are required when making a declaring information in cfengine.

The meaning of each of these lines is desribed below.

site/faculty

  site    = ( sitename )
  faculty = ( facultyname )

This variable defines a convenient name for your site configuration. It is useful for making generic rules later on, because it means for instance that you can define the name of a directory to be

/$(site)/$(host)/local

without having to redefine the rule for a specific site. This is a handy trick for making generic rules in your files which can be imported into a configuration for any site.

faculty is a synonym for site. The two names may be used interchangeably.

domain

  domain = ( domain name )

This variable defines the domainname for your site. You must define it here, because your system might not know its domainname when you run cfengine for the first time. The domainname can be used as a cfengine variable subsequently by referring to $(domain). The domainname variable is used by the action resolve.

sysadm

   sysadm = ( mail address )

The mail address of your system adminstrator should be placed here. This is used in two instances. If cfengine is invoked with the option -a, then it simply prints out this value. This is a handy feature for making scripts. See section Using the help scripts.

The adminstrators mail adress is also written into the personal log files which cfengine creates for each user after tidying files, so you should make this an address which users can mail if they have troubles.

netmask

   netmask = ( aaa.bbb.ccc.ddd )

The netmask variable defines the partitioning of the subnet addresses on your network. Its value is defined by your network administrator. On most systems it is likely to be 255.255.255.0. This is used to configure the network interface in netconfig. See section actionsequence.

Every host on the internet has its own unique address. The addresses are assigned hierachically. Each network gets a domain name and can attach something like 65,000 hosts to that network. Since this is usually too many to handle in one go, every such network may be divided up into subnets. The administrator of the network can decide how the division into subnets is made. The decision is a trade-off between having many subnets with few hosts, or many hosts on few subnets. This choice is made by setting the value of a variable called netmask. The netmask looks like an internet address. It takes the form:


   aaa.bbb.ccc.mmm

The first two numbers `aaa.bbb' are the address of the domain. The renmainder `ccc.mmm' specifies both the subnet and the hostname. The value of netmask tells all hosts on the network: how many of the bits in the second half label different subnets and how many label different hosts on each of the subnets?

The most common value for the netmask is `255.255.255.0'. It is most helpful to think of the netmask in terms of bits. Each base-10 number between 0-255 represents 8 bits which are either set or not set. Every bit which is set is a network address and every bit which is zero is part of a host address. The first two parts of the address `255.255' always takes these values. If the third number is `255', it means that the domain is divided up into 256 sub networks and then the remaining bits which are zero can be used to give 255 different host addresses on each of the subnets.

If the value had been `255.255.255.254', the network would be divided up into @math{2^15} subnets, since fifteen of the sixteen bits are one. The remaining bit leaves enough room for two addresses 0 and 1. One of those is reserved for broadcasts to all hosts, the other can be an actual host -- there would only be room for one host per subnet. This is a stupid example of course, the main point with the subnet mask is that it can be used to trade subnets for hosts per subnet. A value of `255.255.254.0' would allow 128 different subnets with @math{2*256-1 = 511} hosts on each.

We needn't be concerned with the details of the netmask here. Suffice it to say that its value is determined for your entire domain by the network administrator and each host has to be told what the value is.

Each host must also know what convention is used for the broadcast address. This is an address which hosts can send to if they wish to send a message to every other host on their subnet simultanously. It is used a lot by services like NIS to ask if any hosts are willing to perform a particular service. There are two main conventions for the broadcast address: address zero (all host bits are zero) and the highest address on the subnet (all host bits are ones). The convention can be different on every subnet and it is decided by the network administrator. When you write a cfengine program you just specify the convention used on your subnet and cfengine works out the value of the broadcast address from the netmask and the host address See section broadcast. Cfengine works out the value of the broadcast address using the value of the netmask.

timezone

   timezone = ( 3-character timezone )

The timezone variable is a character string which defines your local timezone. Currently only the first three characters of this string are checked against the timezone which cfengine manages to glean from the system. If a mismatch is detected a warning message is printed. cfengine does not attempt to configure the timezone. This feature works only as a reminder, since the timezone should really be set once and for all at the time the system is installed. On some systems you can set the timezone by editing a file, a procedure which you can automate with cfengine See section editfiles.

The value of the timezone can be accessed by variable substition in the usual way:

shellcommands:

       "echo ${timezone} | mail ${sysadm}"

nfstype

   nfstype = ( nfs-type ) 

This variable is included only for future expansion. If you do not define this variable, its value defaults to "nfs".

At present cfengine operates only with NFS (the network file system). When cfengine looks for network file systems to mount, it adds lines in the filesystem table (`/etc/fstab',`/etc/checklist' etc.) to try to mount filesystems of type "nfs". In principle you might want to use a completely different system for mounting filesystems over the network, in which case the `mount type' would not be "nfs" but something else.

At the time of writing certain insititutions are replacing NFS with AFS (the Andrew filesystem) and DFS (from the distributed computing environment). The use of these filesystems really excludes the need to use the mount protocol at all. In other words if you are using AFS or DFS, you don't need to use cfengine's mounting commands at all.

repository

   repository  = ( directory ) 

Defines a special directory where all backup and junk files are collected. Files are assigned a unique filename which indentifies the path from which they originate. This affects files saved using disable, copy, links and editfiles See section Disabling and the file repository.

repchar

   repchar  = ( character ) 

The value of this variable determines the characters which is used by cfengine in creating the unique filenames in the file repository. Normally, its value is set to `_' and each `/' in the path name of the file is changed to `_' and stored in the repository. If you prefer a different character, defined it here. Note that the character can be quoted with either single or double quotes in order to encompass spaces etc.

split

   split  = ( character ) 

The value of this variable is used to define the list separator in variables which are expected to be treated as lists. The default value of this variable is the colon `:'. Cfengine treats variables containing this character as lists to be broken up and iterated over in the following cases:

This typically allows communication with PATH-like environment variables in the shell.

sensiblesize

   sensiblesize  = ( size ) 

This variable is used by the action required. It defines for cfengine what you consider to be the minimum size for a `required' file. If you declare a file as being required, cfengine will check to see if the file exists. Of course, the file may exist but be empty, so the size of the file is also checked against this constant. If the file is smaller than the value of sensiblesize a warning is issued. The default value for this variable is 1000 bytes.

sensiblecount

   sensiblecount  = ( count ) 

This variable is used by the action required. It defines for cfengine what you consider to be the minimum number of files in a `required' directory. If you declare a directory as being required, cfengine will check to see if it exists. Then, if the directory contains fewer than the value of sensiblecount files, a warning is issued. The default value for this variable is 2.

editfilesize

   editfilesize  = ( size ) 

This variable is used by cfengine every time it becomes necessary to edit a file. Since file editing applies only to text files, the files are probably going to be relatively small in most cases. Asking to edit a very large (perhaps binary) file could therefore be the result of an error.

A check is therefore made as a security feature. Cfengine will refuse to edit a file which is larger than the value of editfilesize in bytes. This is to prevent possible accidents from occurring. The default value for this variable is 1000 bytes. If you don't like this feature, simply set the value to be a very large number.

mountpattern

   mountpattern  = ( mount-point ) 

The mountpattern list is used by the cfengine model for mounting nfs filesystems. See section Cfengine's model for NFS-mounted filesystems. It is also used in the evaluation of the pseudo variable home, See section files, section tidy.

It is used together with the value of homepattern to locate and identify what filesystems are local to a given host and which are mounted over the network. For this list to make sense you need to stick to a rigid convention for mounting your filesystems under a single naming scheme as described in the section mentioned above. If you follow the recommended naming scheme then you will want to set the value of mountpattern to

mountpattern = ( /$(site)/$(host) )

which implies that cfengine will look for local disk partitions under a unique directory given by the name of the host and site. Any filesystems which are physically located on the current host lie in this directory. All mounted filesystems should lie elsewhere. If you insist on keeping mounted file systems in more than one location, you can make a list like this:

mountpattern = ( /$(site)/users /$(site)/projects )

homepattern

   homepattern  = ( list of wildcards ) 

The homepattern variable is used by the cfengine model for mounting nfs filesystems. See section Cfengine's model for NFS-mounted filesystems. It is also used in the evaluation of the pseudo variable home, See section files, section tidy.

homepattern is in fact a list and is used like a wildcard or pattern to determine which filesystems in the list of mountables are home directories. See section mountables. This relies on your sticking to a rigid naming convention as described in the first reference above.

For example, you might wish to mount (or locate directly if you are not using a separate partition for home directories) your home directories under mountpattern in directories u1, u2 and so on. In this case you would define homepattern to match these numbers:

homepattern = ( u? )

Cfengine now regards any directory matching $(mountpattern)/u? as being a user login directory.

Here is another example in which you split up a single partition into subdirectories. Suppose you want to create mount home directories under $(mountpattern)/home and make subdirectories for staff and students. Then you would write:

homepattern = ( home/staff home/students )

Or you could combine the two:

homepattern = ( u?/staff u?/students )

addclasses

   addclasses  = ( list of identifiers ) 

The addclasses directive is used to define a list of class attributes for the current host. Normally only the hard classes defined by the system are `true' for a given host. It is convenient though to be able to define classes of your own to label certain actions, mainly so that they can later be excluded so as to cut short or filter out certain actions. This can be done in two ways. See section actionsequence.

To define a list of classes for the current session, you write:

addclasses = ( exclude shortversion )

This is equivalent to (though more permanent than) defining classes on the command line with the -D option. You can now use these to qualify actions. For example


  any.exclude::
      ...

Under normal circumstances exclude is always true -- because you have defined it to be so, but you can undefine it in two ways so as to prevent the action from being carried out. One way is to undefine a class on the command line when you invoke cfengine:

host#  cfengine -N exclude

or

host#  cfengine -N exclude.shortversion

host#  cfengine -N a.b.c.d

These commands run cfengine with the named classes undefined. That means that actions labelled with these classes are excluded during that run.

Another way to restrict classes is to add a list of classes to be undefined in the actionsequence. See next section.

actionsequence

The action sequence determines the order in which collective actions are carried out. Here is an example containing the full list of possibilities:

   actionsequence = 
      (
      mountall               # mount filesystems in fstab
      mountinfo              # scan mounted filesystems
      checktimezone          # check timezone
      netconfig              # check net interface config
      resolve                # check resolver setup
      unmount                # unmount any filesystems
      shellcommands          # execute shell commands
      editfiles              # edit files
      addmounts              # add new filesystems to system
      directories            # make any directories
      links                  # check and maintain links
      mailcheck              # check mailserver
      mountall               # (again)
      required               # check required filesystems
      tidy                   # tidy files
      disable                # disable files
      files                  # check file permissions 
      copy                   # make a copy/image of a master file
      processes              # signal / check processes
      )

Here is a more complete description of the meaning of these keywords.

addmounts
causes cfengine to compute which NFS filesystems are missing from the current host and add them. This includes editing the filesystem table, creating the mount-directory, if required. This command relies on information provided by mountinfo, so it should normally only be called after mountinfo. If the filesystem already appears to be in the filesystem table, a warning is issued.
checktimezone
runs a check on the timezone defined for the shell running cfengine.
directories
executes all the commands defined under the directories section of the program. It builds new directories.
disable
executes all the commands defined under the disable section of the program.
editfiles
executes all the commands defined under the editfiles section of the program.
files
executes all the commands defined under the files section of the program.
links
executes all the commands defined under the links section of the program.
mailcheck
tests for the presence of the NFS-mounted mail spooling directory on the current host. The name of the mail spool directory is defined in the mailserver section of the cfengine program. If the current host is the same as the mailserver (the host which has the physical spool directory disk) nothing is done. Otherwise the filesystem table is edited so as to include the mail directory.
mountall
mounts all filesystems defined in the hosts filesystem table. This causes new NFS filesystems added by addmounts and mailcheck to be actually mounted. This should probably be called both before mountinfo and after addmounts etc.
mountinfo
builds internal information about which filesystems are presently mounted on the current host. Cfengine assumes that required-filesystems which are not found need to be mounted.
netconfig
checks the netmask, hostname, IP address and broadcast address for the current host. The correct values for the netmask and broadcast address are set if there is an error. The defaultroute is also added to the static routing table.
required
executes all the commands defined under the required section of the program. It checks for the absence of important NFS resources.
resolve
checks and corrects the DNS domain name and the order of nameservers in the file `/etc/resolv.conf'.
shellcommands
executes all the commands defined under the shellcommands section of the program.
tidy
executes all the commands defined under the tidy section of the program.
unmount
executes all the commands defined under the unmount section of the program. The filesystem table is edited so as to remove the unwanted filesystems and the unmount operation is executed.
processes
executes commands defined under the processes section of the program.

Under normal circumstances this coarse ordering is enough to suit most purposes. In some cases you might want to, say, only perform half the link operations before mounting filesystems and then, say, perform the remainder. You can do this (and similar things) by using the idea of defining and undefining classes See section Defining classes and making exceptions.

The syntax


actionsequence =
   (
   links.firstpass.include
   ...
   links.secondpass
   )

means that cfengine first executes links with the classes firstpass and include defined. Later it executes links with secondpass defined. You can use this method of adding classes to distinguish more finely the flow of control in programs.

A note about style: if you define and undefine lots of classes to do what you want to do, you might stop and ask yourself if your groups are defined as well as they should be. See section groups. Programming in cfengine is about doing a lot for only a little writing. If you find yourself writing a lot, you are probably not going about things in the right way.

access

The access list is a list of users who are to be allowed to execute a cfengine program. If the list does not exist then all users are allowed to run a program.

   access = ( user1 user2 ...  )

The list may consist of either numerical user identifiers or valid usernames from the password database. For example:

   access = ( mark aurora 22 456 )

would restrict a script to users mark, aurora and user id 22 and 456.

excludelinks

This list is used to define a global list of names or patterns which are to excluded from linking operations. For example

excludelinks = ( *~ *% core )

The same facility can be specified for each individual link operation using the exclude option See section links.

Note that all entries defined under a specified class are valid only as long as that class is defined. For instance

  class::

      excludelinks = ( pattern )

would define a pattern which was only valid when class is defined.

excludecopy

This list is used to define a global list of names or patterns which are to excluded from copy operations. For example

excludecopy = ( *~ *% core )

The same facility can be specified for each individual link operation using the exclude option See section copy.

Note that all entries defined under a specified class are valid only as long as that class is defined. For instance

  class::

      excludecopy = ( pattern )

would define a pattern which was only valid when class is defined.

copylinks

This list is used to define a global list of names or patterns which are to be copied rather than linked symbollically. For example

copylinks = ( *.config )

The same facility can be specified for each individual link operation using the copy option See section links. Copying is performed using a file age comparison.

Note that all entries defined under a specified class are valid only as long as that class is defined. For instance

  class::

      copylinks = ( pattern )

would define a pattern which was only valid when class is defined.

linkcopies

This list is used to define a global list of names or patterns which are to be linked symbolically rather than copied. For example

excludelinks = ( *.gif *.jpg )

The same facility can be specified for each individual link operation using the symlink option See section copy.

Note that all entries defined under a specified class are valid only as long as that class is defined. For instance

  class::

      linkcopies = ( pattern )

would define a pattern which was only valid when class is defined.

copy

Sometimes it is not enough to make a link to a configuration file--sometimes it is better to copy the actual file itself. This might be because the master file is on a different filesystem which might not always be mounted, or because the file needs to be editable locally. The copy facility is designed to help with this problem. The syntax is


copy:

   class::

      master-file dest=destination-file mode=mode
                owner=owner group=group action=action
                backup=true/false symlink=pattern
                exclude=pattern recurse=number/inf
                type=ctime/checksum linktype=relative/absolute/hard

dest
The destination file is the only obligatory item. This must be the name of an object which matches the type of the master object i.e. if the master is a plain file, the destination must also be the explicit name of a plain file. An implicit `copy file to directory' syntax is not allowed. Symbolic links are copied as symbolic links, plain files are copied as plain files and special files are copied as special files. If the master and image are directories then all of the child files which are not directories are copied from source to destnation.
mode, owner, group
The file mode, owner and group of the images are specified as in the files function See section files.
action
The action may take the values warn or silent. The default action is fix, i.e. copy files. If warn is specified, only a warning is issued about files which require updating. If silent is given, then cfengine will copy the files but not report the fact.
backup
If the backup option is set to "false", cfengine will not make a backup copy of the file before copying. Copy makes a literal image of the master file at the destination, checking whether the master is newer than the image. If the image needs updating it is copied. Existing files are saved by appending .cfsaved to the filename.
recurse
Specifies the depth of recursion when copying whole filetrees recursively. The value may be a number or the keyword inf. Cfengine crosses device boundaries or mounted filesystems when descending recursively through file trees. To prevent this it is simplest to specify a maximum level of recursion.
symlink
This option may be repeated a number of times to specify the names of files, or wildcards which match files which are to be symbolically linked instead of copied. A global list of patterns can also be defined in the control section of the program See section linkcopies.
exclude
This option may be repeated a number of times to specify the names of files, or wildcards which match files which are to be excluded from a copy operation. A global list of patterns can also be defined in the control section of the program See section excludelinks.
type
Normally cfengine uses the ctime date-stamps on files to determine whether a file needs to be copied: a file is only copied if the master is newer than the copy. If the type is set to `checksum', then a secure MD5 checksum is used to determine whether the source and destination files are identical.
linktype
This option determines the type of link used to make links. This only applies if the file is linked rather than copied because it matches a pattern set by symlink. The default type is a direct symbolic link. The values `relative' or `absolute' may be used, but hard links may not be created in place of copied files, since hard links must normally reside on the same filesystem as their files, and it is assumed that most links will be between filesystems.

Example:


copy:

      /local/etc/aliases dest=/etc/aliases m=644 o=root g=other
      /local/backup-etc  dest=/etc

   solaris::

      /local/etc/nsswitch.conf dest=/etc/nsswitch.conf

In the first example, a global aliases file is copied from the master site file `/local/etc/aliases' to `/etc/aliases', setting the owner and protection as specified. The file gets installed if `/etc/aliases' doesn't exist and updated if `/local/etc/aliases' is newer than `/etc/aliases'. In the second example, `backup-etc' is a directory containing master configuration files (for instance, `services', `aliases', `passwd'...). Each of the files in `backup-etc' is installed or updated under `/etc'. Finally, a global `nsswitch.conf' file is kept up to date for solaris systems.

The home directive can be used as a destination, in which case cfengine will copy files to every user on the system. This is handy for distributing setup files and keeping them updated:


copy:

   /local/masterfiles/.cshrc  dest=home/.cshrc mode=0600

You can force the copying of files, regardless of the date stamps by setting the option force=true or force=on. The default is force=false or force=off.

defaultroute

Dynamical routing is not configurable in cfengine, but for machines with static routing tables it is useful to check that a default route is configured to point to the nearest gateway or router. The syntax for this statement is simply:


defaultroute:

   class::

      my_gateway

For example:


defaultroute:

  most::

     129.240.22.1

  rest::
 
     small_gw

Gateways and routers usually have internet address aaa.bbb.ccc.1 --- i.e. the first address on the subnet. You may use the numerical form or a hostname for the gateway.

directories

Directories declarations consist of a number of directories to be created. Directories and files may also be checked and created using the touch option in the files actions. See section files.

The form of a declaration is:


directories:

  class::

     directory options...

The form of the command is similar to that of files but this command may only be used to create new directories. Valid options are mode, owner, group and are described under files See section files.

The creation of a path will fail if one of the links in the path is a plain file.

disable

Disabling a file means renaming it so that it becomes harmless. This feature is useful if you want to prevent certain dangerous files from being around, but you don't want to delete them-- a deleted file cannot be examined later. The syntax is


disable:

   class::

      filename

Cfengine renames a given file by appending the name of the file with the suffix `.cf-disabled'. A typical example of a file you would probably want to disable would be the /etc/hosts.equiv file which is often found with the `+' symbol written in it, opening the system concerned to the entire NIS universe without password protection! Here is an example:


disable:

      /etc/hosts.equiv
      /etc/nologin
      /usr/lib/sendmail.fc

   sun4::

      /var/spool/cron/at.allow

Hint: The last example disables a file which restricts access to the at utility. Such a command could be followed by a file action:

files:

   some::

      /var/spool/cron/at.allow =0644 N [root] [wheel] touch

See section files which would create an empty security file `at.allow'. See also your system manual pages for the at command if you don't understand why this could be useful.

Disabling a link deletes the link. If you wish you may use the optional syntax


disable:

    /directory/name type=file

to specify that a file object should only be disabled if it is a plain file. The optional element type= can take the values plain, file, link or links. If one of these is specified, cfengine checks the type and only disables the object if there is a match. This allows you to disable a file and replace it by a link to another file for instance.

NOTE that if you regularly disable a file which gets recreated, the disabled file `filename.cf-disabled' will be overwritten each time cfengine disables the file and therefore the contents of the original are lost each time.

The disable feature can be used to control the size of system log files, such as `/var/adm/messages' using a further option rotate. If the value rotate is set to 4, say,


 disable:

    filename  rotate=4

then cfengine renames the file concerned by appending `.1' to it and a new, empty file is created inits place with the same owner and permissions. The next time disable is executed `.1' is renamed to `.2' and the file is renamed `.1' and a new empty file is created with the same permissions. Cfengine continues to rotate the files like this keeping a maximum of four files. This is similar to the behaviour of syslog.

If you simply want to empty the contents of a log file, without retaining a copy then you can use rotate=empty. For instance, to keep control of your World Wide Web server logs:

disable:

   Sunday|Wednesday::

       /usr/local/httpd/logs/access_log  rotate=truncate

This keeps a running log which is emptied each Sunday and Wednesday.

editfiles

You can perform simple control or editing on textfiles using a number of commands. At the present time these are fairly limited but are sufficient for many purposes. The form of an editing command is


editfiles:

   class::

      { file-to-be-edited

      action "quoted-string..."
      }

Here are some examples:

editfiles:

   sun4::

      { /etc/netmasks

      DeleteLinesContaining "255.255.254.0"
      AppendIfNoSuchLine "128.39  255.255.255.0"
      }

   PrintServers::
      { /etc/hosts.lpd

      AppendIfNoSuchLine "tor"
      AppendIfNoSuchLine "odin"
      AppendIfNoSuchLine "borg"
      }

The first of these affects the file `/etc/netmasks' on all SunOS 4 systems, deleting any lines containing the string "255.255.254.0" and Appending a single line to the file containing "128.39 255.255.255.0" if none exists already. The second affects only hosts in the class `PrintServers' and adds the names of three hosts: tor, odin and borg to the file `/etc/hosts.lpd' which specifices that they are allowed to connect to the printer services on any host in the class `PrintServers'.

Note that single or double quotes may be used to enclose strings in cfengine. If you use single quotes, your strings may contain double quotes and vice-versa. Otherwise a double quoted string may not currently contain double quotes and likewise for single quoted strings.

As of version 1.3.0, you can use the `home' directive in edit filenames, enabling you to edit files for every user on the system, provided they exist. For example, to edit every user's login files, you would write


  { home/.cshrc

   AppendIfNoSuchLine "setenv PRINTER default-printer"
   AppendIfNoSuchLine "set path = ( $path /new/directory )"
  }

If a user does not possess the named file, cfengine just skips that user. A new file is not created.

The meanings of the file-editing actions should be self-explanatory. Commands containing the word 'comment' are used to `comment out' certain lines in a file rather than deleting them. Hash implies a shell comment of the type

# comment

Slash implies a comment of the C++ type:

// comment

Percent implies a comment of the type:

% comment

More general commenting functions may be defined using the SetCommentStart, SetCommentEnd and CommentLinesMatching, CommentLinesStarting functions.

Another group of editing commands is based on the GNU Regular Expression package (from Emacs). It uses GNU regular expressions to search line by line through text and perform various editing functions. Some of these commands are based on the concept of a file pointer. The pointer starts at line one of the file and can be reset by 'locating' a certain line, or by using the reset-pointer commands. The current position of the pointer is used by commands such as InsertLine to allow a flexible way of editing the middle of files.

A simple decision mechanism is incorporated to allow certain editing actions to be excluded. For instance, to insert a number of lines in a file once only, you could write:


   { file

    LocateLineMatching "insert point..."
    IncrementPointer   "1"

    BeginGroupIfNoMatch "# cfengine - 2/Jan/95"

      InsertLine "# cfengine - 2/Jan/95"
      InsertLine "/local/bin/start-xdm"

    EndGroup
   }

Since the first inserted line matches the predicate on subsequent calls, the grouped lines will only be carried out once.

The full list of editing actions is given below in alphabetical order. Note that some commands refer to regular expressions and some refer to 'literal strings' (i.e. any string which is not a regular expression). Variable substitution is performed on all strings. Be aware that symbols such as `.', `*' and so on are meta-characters in regular expressions and a backslash must be used to make them literal. The regular expression matching functions are the GNU regular expressions, as defined by the regex-0.12 package See section Regular expressions. Readers are referred to the manual for this package for details of the extended special features of GNU regular expressions. If you are not familiar with regular expressions, then be aware that you may always supply an exact string to be matched (this is the simplest regular expression), but be careful about backslashing metacharacters!

AbortAtLineMatching quoted-string
This command sets the value of a regular expression. In all editing operations (except FixEndOfLine and GotoLastLine) which involve multiple replacements and searches, this expression marks a boundary beyond which cfengine will cease to look any further. In other words, if cfengine encounters a line matching this regular expression, it aborts the current action. BE CAREFUL with this feature: once set, the string remains set for the remainder of the current file. It might therefore interact in unsuspected ways with other search parameters. Editing actions are always aborted as soon as the abort expression is matched. Use UnsetAbort to unset the feature.
Append quoted-string
Add a line containing the quoted string to the end of the file. This should be used in conjunction with the decision structures BeginGroupIfNoLineMatching and BreakIfLineMatches.
AppendIfNoSuchLine quoted-string
Add a line containing the quoted string to the end of the file if the file doesn't contain the exact line already.
AppendIfNoLineMatching quoted-string
A new version of the older AppendIfNoSuchLine which uses a regular expression instead of a literal string. The line which gets appended must be set previously using SetLine.
AutomountDirectResources quoted-string
This command is designed to assist with automounter configuration for users wishing to use the autmounter for NFS filesystems, but still use the cfengine mount model. Applied to the current file, it is equivalent to saying: for each of the mountable resources in the list See section mountables, append if not found a line for a direct automount map command, to the current file. The string which follows can be used to specify any special mount options e.g. "-nosuid" for non setuid mounting (of all the mountables). Note that this is added to the current file and not to a file named `/etc/auto_direct'.
BeginGroupIfNoLineMatching quoted-string
The lines following, up to the first EndGroup are executed if the quoted regular expression does not match any line in the file.
BeginGroupIfNoMatch quoted-string
The lines following, up to the first EndGroup are executed if the quoted regular expression does not match the current line.
BeginGroupIfNoSuchLine quoted-string
The lines following, up to the first EndGroup are executed if the quoted literal string does not match any line in the file.
BreakIfLineMatches quoted-string
Terminates further editing of the current file if the current line matches the quoted regular expression.
CommentLinesMatching quoted-string
Use the current value of the comment delimiters set using SetCommentStart and SetCommentEnd to comment out lines matching the given regular expression in quotes.
CommentLinesStarting quoted-string
Use the current value of the comment delimiters set using SetCommentStart and SetCommentEnd to comment out lines starting with the quoted literal string.
CommentNLines quoted-string
Comments up to @math{N} lines from the current file, starting from the location of the current line pointer. If the end of the file is reached and less than @math{N} lines are deleted, a warning is issued, but editing continues. The current value of the comment delimiters is used to determine the method of commenting, (see SetCommentStart). After the operation the pointer points to the line after the commented lines.
CommentToLineMatching quoted-string
Use the current value of the comment delimiters set using SetCommentStart and SetCommentEnd to comment out lines from the current position in a file to a line matching the given regular expression in quotes.
DeleteLinesContaining quoted-string
Delete all lines containing the exact string quoted.
DeleteLinesMatching quoted-string
Delete all lines matching the quoted regular expression.
DeleteLinesStarting quoted-string
Delete all lines beginning with the exact string quoted.
DeleteNLines quoted-string
Deletes up to @math{N} lines from the current file, starting from the location of the current line pointer. If the end of the file is reached and less than @math{N} lines are deleted, a warning is issued, but editing continues.
DeleteToLineMatching quoted-string
Delete lines from the current position, up to but not including a line matching the regular expression in the quoted string. If no line matches the given expression, a warning is only printed in verbose mode, but all edits are immediately abandoned.
EmptyEntireFilePlease
Deletes all lines from the current file.
EndGroup
Terminates a begin-end conditional structure.
FixEndOfLine
The quoted string which follows may be either `dos' or `unix' to fix the end of line character conventions to match these systems. This command should be executed last of all, since cfengine appends new lines with the conventions of the system on which is was complied during edit operations.
GotoLastLine
Moves the file pointer to the last line in the current file.
HashCommentLinesContaining quoted-string
Add a `#' to the start of any line containing the quoted string.
HashCommentLinesMatching quoted-string
Add a `#' to the start of any line exactly matching the quoted regular expression.
HashCommentLinesStarting quoted-string
Add a `#' to the start of any line starting with the quoted string.
IncrementPointer quoted-string
Increments the value (in lines) of the file pointer by the number of lines specified in the quoted string (as a denary number). e.g. `"4"'.
InsertFile quoted-string
Inserts the named file after the current line position in the file. This should be used in conjunction with a begin-end construction in order to avoid including the file every time cfengine is run. If the file does not exist, or cannot be opened, there is only a warning issued in verbose mode. Note if the file is empty, or if the current line pointer is not set, the file is inserted at the start of the file.
InsertLine quoted-string
Inserts the quoted string as a line at the current position in the file. After the insert, the file pointer is incremented by one so that subsequent inserted lines are placed after the first. This should probably be used in conjunction with the conditional begin-end tests to avoid lines being inserted on every run.
LocateLineMatching quoted-string
Moves the current-position pointer to the start of the line matching the quoted regular expression. If there is no match, a warning is only issued in verbose mode, but all editing is immediately aborted. See also WarnIfNoLineMatching so that you can get an explicit warning, even out of verbose mode.
PercentCommentLinesContaining quoted-string
Add a `%' to the start of any line containing the quoted string.
PercentCommentLinesMatching quoted-string
Add a `%' to the start of any line exactly matching the quoted regular.
PercentCommentLinesStarting quoted-string
Add a `%' to the start of any line starting with the quoted string.
Prepend quoted-string
Add a line containing the quoted string to the start of the file. This should be used in conjunction with the decision structures BeginGroupIfNoLineMatching and BreakIfLineMatches.
PrependIfNoLineMatching quoted-string
A new version of the older PrependIfNoSuchLine with uses a regular expression instead of a literal string. The string prepended is the one set using SetLine.
PrependIfNoSuchLine quoted-string
Add a line containing the quoted string to the start of the file if the file doesn't contain the exact line already.
ReplaceLineWith quoted-string
Replace the line at the current position with the text in the quoted string. The file pointer remains pointing to this line after the change.
ReplaceAll quoted-string With quoted-string
Replace all instances of strings matching the regular expression in the first quotes with the exact string in the second set of quotes, throughout the current file. Note that cfengine matches on a left to right basis, with the first match taking precedence, so if your regular expression matches text ambiguously it is the first occurrance which is replaced. For example, if you replace `cf.*' with `CFENGINE' and cfengine encounters a line `hello cfengine cfengine', then this will be replaced with `hello CFENGINE' even though two possible strings match the regular expression. On the other hand if the expression is not ambigous, say replacing `cfengine' with `CFENGINE', then the result would be `hello CFENGINE CFENGINE'.
ResetSearch quoted-string
Sets the current-position pointer to the line number in the quoted string. `EOF' indicates the end of the file.
RunScript quoted-string
Executes the named script command. Before executing the script any edits are saved to disk. After the script has executed, cfengine reloads the file for any further editing operations. The script (which may be any executable program) is appended with two arguments: the name of the file which is being edited and the system hard class (e.g. sun4, ultrix etc.) of the system executing the script. CAUTION: cfengine knows nothing about the success or failure of anything that is done during the execution of user scripts. This feature is to be used at the users own peril!
RunScriptIfLineMatching quoted-string
Executes the script named with the SetScript command only if the current file contains a line matching the quoted regular expression. CAUTION: cfengine knows nothing about the success or failure of anything that is done during the execution of user scripts. This feature is to be used at the users own peril!
RunScriptIfNoLineMatching quoted-string
Executes the script named with the SetScript command if the current file contains no line matching the quoted regular expression. CAUTION: cfengine knows nothing about the success or failure of anything that is done during the execution of user scripts. This feature is to be used at the users own peril!
SetCommentStart quoted-string
Specify which string should be used for starting a comment using the commands CommentLineMatching and CommentLineStarting. The default is the hash symbol `#' followed by a single space.
SetCommentEnd quoted-string
Specify which string should be used for ending a comment using the commands CommentLineMatching and CommentLineStarting. The default is the empty string. For example, you could make C style comments by setting CommentStart to `/*' and comment end to `*/'.
SetLine quoted-string
Sets a current line value which can be appended using AppendIfNoLineMatching using a regular expression.
SetScript quoted-string
Sets the name of a user-supplied script for editing the current file.
SlashCommentLinesContaining quoted-string
Add a `//' to the start of any line containing the quoted string.
SlashCommentLinesMatching quoted-string
Add a `//' to the start of any line exactly matching the quoted regular expression.
SlashCommentLinesStarting quoted-string
Add a `//' to the start of any line starting with the quoted string.
UnCommentLinesContaining quoted-string
Uncomment all lines in file containing the quoted string as a substring. The comment delimiters are assumed to be those set using SetCommentStart and SetCommentEnd.
UnCommentLinesMatching quoted-string
Uncomment all lines in file matching the quoted regular expression. The comment delimiters are assumed to be those set using SetCommentStart and SetCommentEnd.
UnCommentNLines quoted-string
Uncomments N lines starting from the current position, using the currently defined method for commenting. Note that the comment start and end symbols are removed independently, i.e. they are not matched, so that a comment may be spread over several lines. e.g. If using C style `/*' and `*/' comments, the command UnCommentNLines "3" would uncomment
 /* 1 */
 /* 2 */
 /* 3 */
and also
 /* 1 
    2
    3 */
UnsetAbort quoted-string
Switches off the feature AbortAtLineMatching.
WarnIfLineContaining quoted-string
Issue a warning if the quoted string is found as a substring of one or more lines in the file.
WarnIfLineMatching quoted-string
Issue a warning if the quoted regular expression matches one or more lines in the file.
WarnIfLineStarting quoted-string
Issue a warning if the quoted string matches the start of one or more lines in the file.
WarnIfNoLineContaining quoted-string
Issue a warning if the quoted string is not contained in one or more lines in the file.
WarnIfNoLineStarting quoted-string
Issue a warning if the quoted string is not found at the start of one or more lines in the file.
WarnIfNoSuchLine quoted-string
Issue a warning if the quoted regular expression does not match one or more lines in the file.

It is suggested that you use these editing functions with caution. Although all possible safeguards have been incorporated into them, it is still possible through carelessness to do damage to important files on your system. Always test editing programs carefully before committing them to your global site configuration.

files

The files facility allows you to touch (create), check for the existence, owner and permissions of files, change the permissions and test for setuid root programs.

Syntax

A files-statement can have several options. We can begin by examining the form of the statement in pseudo-code:

files:

myclass::

   /directory action=action options ...   

An example would be the following:

   any::

      /var/spool/printQ  mode=0775  r=0 o=daemon g=daemon  act=fixdirs

The meaning of these item is sketched out below and becomes clearer on looking at a number of examples. Note that, each of the options below can be written in either upper or lower case and abbrieviated by any unique abbreviation.

/directory
This is the only obligatory part of a file action. This is a directory at which a file search should begin. This may be a single file or a directory. The recursion specifier may be used to force cfengine to descend into subdirectories in a controlled fashion, starting from this point, checking files there also. The wildcard home may also be used. See section home directive.
mode=modestring
Specifies what the allowed permissions for files are. If cfengine finds that the a file's mode is incorrect, the value of the action option determines what will be done about it. The modestring should consist of either a three digit octal numbers with `+', `-' or `=' symbols, or a text string like that used by the command chmod. For instance: mode=u=rwx,og+rx would mean set the read/write and execute flags for the user (file owner) and add the read/execute flags for others and group bits. An example of the numerical form might be -002 which would mean that the read-for-others flag should either not be set or should be unset, depending on the action you choose. +2000 would mean that the setuid flag should be present or set, depending on the action. +2000,-002 would be a combination of these. The `=' sign sets to an absolute value, so =755 would set the file mode to mode 755.
recurse=number/inf
This specifier tells cfengine whether or not to recurse into subdirectories. If the value is zero, only the named file or directory is affected. If the value is 1, it will open at most one level of subdiretory and affect the files within this scope. If the value is inf then cfengine opens all subdirectories and files beginning from the specified filename.See section Recursion.
owner=owner list
This is a list of allowed owners, or uids by number, separated by commas. For example root,2,3,sysadm. In cases where you ask cfengine to fix the ownership automatically, the owner will be set to the first owner in the list if and only if it is not one of the named uids in the list.
group=group list
This is a list of allowed groups, or gids by number, separated by commas. For example wheel,2,3,sysadm. In cases where you ask cfengine to fix the ownership automatically, the group will be set to the first group in the list if and only if it is not one of the named gids in the list.
action=action
The action is one of the following keywords.
warnall warndirs warnplain
fixall fixdirs fixplain
touch linkchildren
The upper line results only in warnings being issued. The actions beginning `fix' prompt cfengine to fix encountered problems without bothering the user. No message is issued unless in verbose mode. The special features on the third line will be explained separately.
links=stop/traverse/tidy
Normally cfengine does not decsend into subdirectories which are pointed to by symbolic links. If you wish to force it to do so (without using the -l command line option) you may give this option the value true, or traverse, or follow. To specify no recursion you set the value false or stop. Note that the value set here in the cfengine program always overrides the value set by the -l command line option, so you can protect certain actions from this command line option by specifying a negative value here. If you specify no value here, the behaviour is determined by what you specify on the command line. The value links=tidy has the same effect as the `-L' command line option except that here it may be specified per item rather than globally. Setting this value causes links which point to non-existent files to be deleted. If the warn directive is used (for directories, plain files or both) then only a warning message is issued if the file being tested does not match the specification given. If the fix directives are used then cfengine does not issue a warning, it simply fixes the value silently. Non-existenct files are created by the touch command. A directory may be touched (created) by writing the filename /a/b/c/. with a dot as the last character. (This may also be achieved with the directories directive. See section directories).

The default values are mode=+000, recurse=0, action=warnall and any owner or group is acceptable. The default for links is to not traverse links unless the -l option is set on the command line.

Recursion

The recursion specifier tells cfengine what to do, starting from /directory name. A value of r=0 means `no recursion' and any checking is limited only to the named file or directory. A value of r=inf implies unlimited recursion. Cfengine then descends into all subdirectories checking or setting the permissions of files until it `bottoms out' at a plain file. A value such as R=4 means descend recursively into subdirectories, but no more than four levels. This is a useful safety net in preventing unforseen accidents. A recursive search also bottoms out on device boundaries and symbolic links (provided the -l option is not used). (6)

Directory permissions

When you specify the permissions for a whole file tree, using the recursion specifier it is awkward to have to remember that directories must be executable. cfengine will do this for you automatically. If you specify that a file tree is to have a read flag set, cfengine will ensure that the corresponding execute flag is also set for directories which live in the tree. So the command

files:

  myclass::

      /dir  mode=a+rw r=inf fixall

would set all plain files to mode 644 and all directories to 755, that is read/write for everyone on plain files and read/write/execute for everyone on directories.

home directive

If you want to check the files of all the users who have their login areas on the current host, you can use a wildcard directive home instead of a directory name. In this case the file action iterates over all home directories physically on the current host. The home directories are, of course, located by searching for files which match

$(mountpattern)/$(homepattern)

i.e. the values which are specified in the control part of the program. For example the following line is a very useful service to ignorant users.

files:

  any::
 
    home mode=o-w r=inf act=fixall

It ensures automatically that no user has files which can be written to by other arbitrary users.

As a corollary to this, you may write something like


  any::

     home/www mode=a+r fixall

to specify a special subdirectory of every users' home directory. This statement would check that all of the files in users' world wide web directories were readable for everyone.

Owner and group wildcards

If you do not want to explicitly state the owner or group of a file you may simply omit the group or owner options.


  /directory m=0664 r=inf

This example generate a warning if any files under the named directory do not have permission read/write for all users.

Files linkchildren

The linkchildren facility is almost identical to that already described under links. See section Link Children. The only difference here is that the ownership and permissions on the links are set all in one operation. For example:

myclass::

   /local/lib/emacs m=0770 o=me g=mygroup act=linkchildren

touch

The touch facility creates a new file with the specified permissions and ownership, or corrects the permissions and ownership of an existing file, in addition to updating the time stamps.

myclass::

   /newfile mode=0644 action=touch

groups

The groups action is used to define classes which stand for groups of hosts. If you use the NIS (network information service) facility for defining netgroups then this idea will already be familiar to you and you can probably use your already-defined netgroups in cfengine.

To define a group, you simply make a list and assign it a name. Here is an example of the syntax:


groups:
 
   science = ( saga tor odin )

   packages = ( saga ) 

   AllHomeServers   = ( saga )
   AllBinaryServers = ( saga )

   OIH_servers = ( saga )
   OIH_clients = ( tor odin )

To include a list of hosts from a NIS netgroup, you use the `+' symbol, or the `+@' construction. For example:


groups:
 
   science = ( +science-allhosts )

   physics = ( +physics-allhosts )

   physics_theory = ( +@physics-theory-sun4 dirac feynman schwinger )

Using an enormous netgroup does not use up any space. A group declaration results in the storage of only the class name regardless of how many hosts are in the list. The rule is that the left hand side of the assignment becomes defined (true) if the list on the right hand side includes the host which is parsing the file -- i.e. $(host).

In some cases your netgroups will not correspond exactly to the list you want, but it might be more convenient to use a netgroup except for certain hosts. You can `undefine' or remove hosts from the netgroup list by using the minus `-' symbol. For example:


group = ( +mynetgroup -specialhost -otherhost )

which means, of course, all hosts in netgroup mynetgroup except for specialhost and otherhost. Finally, you may also subtract two netgroups in the following manner.

group = ( +bignetgroup -smallnetgroup )

The `minus' command effectively eliminates its members from bignetgroup if they exist within that group. If none of the hosts in smallnetgroup exist in bignetgroup then the command has no effect.

Groups may now contain previously defined cfengine groups too. This allows one class to inherit the attributes of another class, for instance:


  AllSun4Hosts   = ( sonny sunny solar stella )
  AllUltrixHosts = ( ully olly wally golly )

  AllBSD = ( AllSun4Hosts AllUltrixHosts )

Finally, you can define groups (strictly classes) by the result of a shell command. A shell command or program is deemed to be `true' if it exits with a status of zero, i.e. it calls exit(0). Any other value is taken to be false. You can include shell commands as the members of groups in order to define classes based on the outcomes of your own scripts by enclosing the script in single or double quotes:


   have_cc = ( '/bin/test -f /usr/ucb/cc' )

The class have_cc will then be defined if the shell command returns true. Of course, you can put any script or program in the single quotes as long as they adhere to the convention that zero exit status means true. If you have several members which are shell commands, then the effect is to make the class the logical OR of the scripts' results.

homeservers

The homeservers declaration need only be used if you are using cfengine's model for mounting NFS filesystems. This declaration informs hosts of which other hosts on the network possess filesystems containing home directories (login areas) which client hosts should mount.

A sample homeserver declaration looks like this:


homeservers:

   Physics::  einstein 
   Math::     riemann euler

The meaning of this declaration is the following. Any host which finds itself to be a member of the classes on the left hand side of the assignment need to mount all home directory resources from the hosts on the right hand side of the assignment. The pattern variable homepattern is used to determine which resources are home directories in the list of mountables. See section mountables.

Let us consider an example in which homepattern is set to the wildcard value `home?' and the mountables list is given by

mountables:
   
   einstein:/mysite/einstein/home1
   einstein:/mysite/einstein/home2
   riemann:/mysite/riemann/local
   euler:/mysite/euler/home1
  

Any host in the group Physics would now want to mount all home directories from the host einstein. There are two of these. Both the filesystems listed for einstein match the homepattern variable since they end in `home?'. cfengine would therefore take this to mean that all hosts in Physics should mount both of these filesystems.

Hosts in Math, on the other hand, should mount only homedirectories from the hosts riemann and euler. There is only a single filesystem on riemann and it does not match homepattern, so it is not mounted. On euler there is a match, so this filesystem will be added to the appropriate hosts.

Cfengine picks out home directory resources from the mountables list by trying to match the homepattern variable, starting from the end of the directory name. You do not therefore have to use the designation /site/host/home? but this is a simple choice and is highly recommended.

ignore

When you specify a recursive search as part of a files action, you would sometimes like to exclude certain directories from the list of sub directories. This can be accomplished by adding the directory to the ignore-list. For example:


ignore:

   any::

      #
      # Prevent tidying .X11 directores in /tmp where
      # window managers write semaphores
      #

      .X11

      #
      # Don't tidy emacs locks
      #

      !*
      /local/lib/gnu/emacs/lock/
      /local/tmp
      /local/bin/top
      /local/lib/tex/fonts
      /local/etc
      /local/www
      /local/mutils/etc/finger.log

None of the above directories will be checked unless a specific command is initiated to search those directories with their names as the top of the search tree.

A handy tip if you are tidying `/tmp' recursively is to include the directory `.X11' here. This directory is used by the X-windows system and deleting it while a window manager has an open session can cause the user some trouble.

Ignore refers to all recursive searches in tidy, files, copy and links.

import

To break up a large configuration file into smaller files you can use the include directive. This conditionally reads in files if the class on the left hand side of the assignment matches the host parsing the file. This enables also a variety of cfengine configuration scripts to read in a standard set of default settings. The syntax of the statement is:


import:       

   any::
      
      cf.global_classes
 

   linux::
    
      cf.linux_classes
     

Note that, if you define variables in an imported file they will not be defined for operations in their parent files. This because cfengine reads in all the import files after the main file has been parsed--not at the place where you call import in your script. This means that variables or macros defined in imported files are only defined after the main program. Variables from earlier files are inherited by later includes, but not vice-versa.

links

The symbolic links function is one of the greatest plusses in cfengine as a system administration tool. It allows you to do two things: check single links for correctness and consistency (or make them if they do not exist), and check or make links to every file in a designated directory. This latter feature is called multiple linking or linking children. The linkchildren feature is also available from the files action See section files. The syntax of a link item is:


 from-link ->[!] to-object 

            type=hard/relative/absolute
            copy=pattern exclude=pattern
            recurse=number/inf copytype=checksum/ctime
            action=silent

 from-link +>[!] to-object 

The special variable $(binserver) can be used in links.

Single links

To define a single link, you create an entry of the following form:


links:

  class::

     linkname -> object_to_link_to
     linkname -> ./relative_link
     linkname -> ../relative_link

When the links action is added to the actionsequence, such a statement results in the testing of the named link and the file or directory it points to. If the link exists and is correct then no action is taken. If the link exists but points incorrectly then a warning is issued. If the link exists and points to a file which does not exist a warning is issued unless the command line option -L is used, in which case the link is deleted. See section Runtime Options.

Here is an example of some valid link statements.


links:

  Physics.sun4::
 
   /usr/local       -> /$(site)/$(host)/local
   /home            -> /$(site)/$(host)/u1
   /etc/sendmail.cf -> /usr/local/mail/etc/global-sendmail.cf

   /usr/lib/sendmail ->! /local/lib/sendmail 

cfengine makes any directories which are required leading up to the link name on the left hand side of the arrow automatically. In the last example the `pling' forces cfengine to make the link even if a file for link exists previously. Plain files are saved by appending `.cfsaved' to the filename whereas old links are removed. The same effect can be enforced globally using the -E option, but only if the program is run interactively. (In this case a prompt is issued to make sure that you wish to use such a big hammer on your system!)

The link operation accepts a number of parameters

type=hard/relative/absolute
If the link type is hard, a hard link is created See section Hard Links. Symbolic links may specify two special types. If relative is selected, and the `to' object is an absolute path name, the link name will be rewritten as a pathname relative to the source file, using `.' and `..' to move relative to the current directory. For instance, a link from `/usr/local/file' to `/usr/file' would be linked as `./../file'. If the `to' object is already relative, this has no effect. If absolute is specified, cfengine will try to resolve the true path location of the `to' object, expanding any symbolic links or dots in the path name, up to a maximum of four levels of symbolic links.
copy=pattern
This option can be repeated any number of times to build up a list of filenames or wildcards which are to be copied rather than linked symbolically. The copy is made on an age-comparison basis. A global variable may also be set to invoke this feature See section copylinks. Directories cannot be copied in this way.
copytype=checksum/ctime
This specifies the basis for deciding whether to update a file which is to be copied instead of linked See section copy.
exclude=pattern
This option can be repeated any number of times to build up a list of filenames or wildcards which are to be excluded from the linking process. A global variable may also be set to invoke this feature See section excludelinks.
recurse=number/inf
This option can only be used with multiple link operations See section Multiple Links. If this option is specified, cfengine links only non-directory objects. Directories are instead created and links within those directories are also created. The value of this option specifies the maximum number of levels to which cfengine should recursively descend a link tree. inf means infinite recursion. Cfengine also ignores files and directories in the ignore list See section ignore.

The final feature of the links facility is connected to the use of the cfengine model for mounting NFS filesystems. In particular it concerns the variable $(binserver). The easiest way to understand this feature is to illustrate a couple of examples. Consider the following:

links:

   any::

      /local -> /${site}/${binserver}/local

The result of this command is quite different depending on which host is executing it. The variable $(site) clearly has a fixed value, but the variable $(binserver) might expand to any valid binary server for the host executing the program. See section binservers. The procedure cfengine adopts is to go through its list of mountables, keeping only those mountable resources which belong to defined binary servers for the current host. It then attempts to match a filesystem by subsituting $(binserver) with each of its valid binservers in turn and it matches the first one binary server which yields an existing file.

Note that every host is a binary server for itself, so that the value of $(binserver) which has absolute priority is alway the same as the value of $(host). This ensures that the link will always be made to a local filesystem if the rules of the model are upheld.

Multiple Links

Using the link symbol +>, you can choose to link all of the files in a directory to another directory. This is sometimes useful for installing software. In the example


links:

  myclass::

     /usr/local/bin +>  /usr/local/lib/perl/bin
     /opt           +>! /local

every file in the directory /usr/local/lib/perl/bin is linked symbolically to a corresponding file in /usr/local/bin. The `pling' character forces cfengine to replace old links or plain files already existing. Old links are removed, whereas old files are saved by appending `.cfsaved' to the filename See section repository.

Each time cfengine runs it goes through all of the files in the directory concerned and checks the appropriate link acordingly. If new files appear, new links will be added. If a file disappears but the link to it remains, a warning will be issued, unless the -L command line option is used, in which case the link is deleted. See section Runtime Options.

Link Children

The linkchildren directive is a closely related to the cfengine model for NFS filesystems. It is a way of making links which embodies a rudimentary kind of `intelligence'.

Consider the following:

links:

   any::

      /usr/local/lib/emacs +> linkchildren

The word linkchildren automatically tells cfengine that it should look for an appropriate file to link to on a binary server for the current host. The exact meaning of the above statement is as follows. cfengine begins searching though the list of mountable resources, discarding any filesystems which do not belong to valid binary servers. It looks for a filesystem ending in `emacs' (the last link of the left hand side). If all is well, these file systems are already mounted and they can be searched. If no rescource is found ending in `emacs', we go to the next link lib and look for a filesystem ending in `lib'. If this is not found we go to local and so on. When a match is made, cfengine then tries to locate the file by checking whether it exists relative to the matched filesystem. For example, suppose `local' matched with host:/site/host/local. It would then try to locate host:/site/host/local/lib/emacs and link all of the children therein to the local file directory /usr/local/lib/emacs.

Here is another example which makes reference to the cfengine model for mounting NFS filesystems. Suppose you have a host with some spare disk space. You want to mount /usr/local from the binary architecture server, but you also want to use the disk you have locally. The following lines

links:

   electron::

      /$(site)/electron/local +> linkchildren

   any::

      /usr/local              -> /$(site)/$(binserver)/local

have the effect of creating a directory /$(site)/electron/local and filling it with links to all of the files and directories on the binary server's mounted filesystem. It results in an exact copy (by linkage) on the local disk, but does not use up your local disk space. The space you have remaining could, for example, be used for software with a special license for that host. The second link links /usr/local to the `nearest' binary server. But the nearest binary server is always $(host) which means this evaluates to a file which now exists because of the first command, so on the host `electron' the directory /usr/local ends up being a link to /$(site)/electron/local which is full of links to the binary server.

If you've caught your breath after that mouthful you probably have mixed feelings about creating a bunch of links in this way. What happens if the files they point to are removed? Then you are left with a lot of useless links. Actually this is no problem for cfengine, since you can ask cfengine to simply remove links which point to non-existent files See section files. Nevertheless, this feature clearly requires some caution and is mainly a spice for advanced users of the cfengine model.

Relative and absolute links

When specifying symbolic linking, you can ask cfengine to change the link type to be either relative to the source or to be an absolute path. What this means is the following. Consider the following link:


   /var/tmp/cfengine -> /local/cfengine

If we add the option type=relative, then instead of creating a link which points to `/local/cfengine', the link is created pointing to the location

  ./../../local/cfengine

In other words, the link is relative to the calling directory `/var/tmp'.

If a link is specified as being absolute with the option type=absolute, then cfengine attempts to resolve to value of the link so as to be the true path of the target. If the target name contains a symbolic link, then this is expanded as far as possible to give the true path to the file. For example, if `/local' is really a link to `/site/myhost/local' then the link would point to `/site/myhost/local/cfengine'.

Hard Links

Cfengine will also allow you to create hard links to regular files. A hard link is in every way identical to the original file, it merely has a different name (technically, it is a duplicate inode). To create a hard link you use the link-option type=hard. For example:


links:

   /directory/newname -> /directory/othername type=hard

Cfengine will not create hard links to directories or other special files. This is always a slightly dubious practice and is best avoided anyway. POSIX says that the hard link can be on a different device to the file it points to, but both BSD and System 5 restrict hard links to be on the same device as their predecessors. Cfengine has no policy on this, but--in the theoretical case in which the hard link and the predecessor were on different file systems--it becomes near impossible to determine with certainly between a hard link and a very similar regular file, and thus cfengine issues a warning in verbose mode about this eventuality. Provided both link and predecessor are on the same filesystem cfengine determines the status of hard links by comparing the device and inode numbers of the file pointed to.

mailserver

The mailserver declaration need only be used if you are using cfengine's model for mounting NFS filesystems. This declaration informs hosts of which NFS filesystem contains mail for its users. All hosts apart from the mail-host itself must then mount the mail spool diretory across the network. The declaration looks like this:


mailserver:

   class::      mailhost:/var/spool/mail 

The result of the checkmail command in the action-sequence is now to mount the filesystem /var/spool/mail on the host mailhost. This action is carried out on any machine which does not already have that filesystem mounted.

The mail spool directory is mounted, by default, onto the offical mail spool directory for the system which is parsing the program. In other words, on an HPUX system, the spool directory is mounted on /usr/mail by default, whereas on a Sun system it would be mounted on /var/spool/mail. The default location can be changed by using the resource file. See section `cfrc' resource file.

miscmounts

If you do not use the cfengine model for statically mounting NFS filesystems (or if there are filesystems which do not naturally fall into the bounds of that model) then you can still statically mount miscellaneous filesystems using a statement of the form:


miscmounts:

   class::

      infohost:source-directory destination mode

For example


   physics::

      libraryserver:/$(site)/libraryserver/data 
                          /$(site)/libraryserver/data ro

This statement would mount the directory `/$(site)/libraryserver/data' physically attached to host libraryserver onto a directory of the same name on all hosts in the group physics. The modes ro and rw signify read-only and read-write respectively.

mountables

The mountables declaration need only be used if you are using cfengine's model for mounting NFS filesystems. This declaration informs hosts of what filesystem resources are available for mounting. This list is used in conjunction with binservers and homeservers to determine which filesystems a given host should mount, according to the cfengine model.

The syntax of the list is:


mountables:

   class::

   server:/site/server/u1
   server:/site/server/local
   linuxhost:/site/linuxhost/local
   linuxhost:/site/linuxhost/u1

Notice that binary and home-directory filesystems are mixed freely here. Cfengine determines which of the entries are homedirectories using the homepattern variable.

Every time you add a disk or a mountable partition to your network, you should add the partition to the list of mountables.

NOTE: This list is read in order, top down. Cfengine looks for the first filesystem matching a given binary server when expanding the variable $(binserver), so sometimes the ordering of filesystems matters.

This list can be accessed in editfiles, to allow straightforward configuration of the automounter, using the command AutmountDirectResources.

processes

Using the processes facility, you can test for the existence of processes, signal (kill) processes and optionally restart them again. Cfengine opens a pipe from the system ps command and searches through the output from this command using regular expressions to match the lines of output from `ps'. The regular expression does not have to be an exact match, only a substring of the process line. The form of a process command is

processes:

    "quoted regular expression" restart "shell command" 
                        signal=signal name
                        matches=number
                        action=signal/do/warn

    SetOptionString "quoted option string"

By default, the options sent to ps are "-aux" for BSD systems and "-ef" for system 5. You can use the SetOptionString command to redefine the option string. Cfengine assumes only that the first identifiable number on each line is the process identifier for the processes, so you must not choose options for ps which change this basic requirement (this is not a problem in practice). Cfengine reads the output of the ps-command normally only once, and searches through it in memory. The process table is only reconsulted if SetOptionString is called. The options have the following meanings:

signal=signal name
This option defines the name of a signal which is to be sent to all processes matching the quoted regular expression. If this option is omitted, no signal is sent. The signal names have the usual meanings. The full list, with largely standardized meanings, is
   hup       1   hang-up
   int       2   interrupt
   quit      3   quit
   ill       4   illegal instruction
   trap      5   trace trap
   iot       6   iot instruction
   emt       7   emt instruction
   fpe       8   floating point exception
   kill      9   kill signal
   bus      10   bus error
   segv     11   segmentation fault
   sys      12   bad argument to system call
   pipe     13   write to non existent pipe
   alrm     14   alarm clock
   term     15   software termination signal
   urg      16   urgent condition on I/O channel
   stop     17   stop signal (not from tty)
   tstp     18   stop from tty
   cont     19   continue
   chld     20   to parent on child exit/stop
   gttin    21   to readers pgrp upon background tty read
   gttou    22   like TTIN for output if (tp->t_local&LTOSTOP)
   io       23   input/output possible signal
   xcpu     24   exceeded CPU time limit
   xfsz     25   exceeded file size limit
   vtalrm   26   virtual time alarm
   prof     27   profiling time alarm
   winch    28   window changed
   lost     29   resource lost (eg, record-lock lost) 
   usr1     30   user defined signal 1
   usr2     31   user defined signal 2

Note that cfengine will not attempt to signal or restart processes 0 to 3 on any system since such an attempt could bring down the system. The only exception is that the hangup (hup) signal may be sent to process 1 (init) which normally forces init to reread its terminal configuration files.
restart "shell command"
If the keyword `restart' appears, then the next quoted string is interpreted as a shell command which is to be executed after any signals have been sent. This could be used to restart a daemon for instance. Cfengine executes this command and waits for its completion so you should normally only use this feature to execute non-blocking commands, such as daemons which dissociate themselves from the I/O stream and place themselves in the background.
matches=number
This option may be used to set a maximum, minimum or exact number of matches. If cfengine doesn't find a number of matches to the regular expression which is in accordance with this value it signals a warning. The `<', `>' symbols are used to specify upper and lower limits. For example,
  matches=<6  # warn number of matches not less than 6
  matches=1   # warn if not exactly 1 matching process
  matches=>2  # warn if there are fewer than 2 matching processes
action=signal/do/warn
The default value of this option is to silently send a signal (if one was defined using the signal option) to matching processes. This is equivalent to setting the value of this parameter to `signal' or `do'. If you set this option to `warn', cfengine sends no signal, but prints a message detailing the processes which match the regular expression.

Here is an example script which sends the hang-up signal to cron, forcing it to reread its crontab files:


processes:

   "cron" signal=hup

Here is a second example which may be used to restart the nameservice on a solaris system:


processes:

   solaris::

       "named" signal=kill restart="/usr/sbin/in.named"

A more complex match could be used to look for processes belonging to a particular user. Here is a script which kills ftp related processes belonging to a particular user who is known to spend the whole day FTP-ing files:


control:

    actionsequence = ( processes )

  #
  # Set a kill signal here for convenience
  #

    sig = ( kill )

  #
  # Better not find that dumpster here!
  #

    matches = ( 1 )

processes:

   #
   #  Look for Johnny Mnemonic trying to dump his head, user = jmnemon
   #

   ".*jmnemon.*ftp.*" signal=$(sig) matches=<$(matches) action=$(do)

   # No mercy!

The regular expression `.*' matches any number of characters, so this command searches for a line containing both the username and something to do with ftp and sends these processes the kill signal. Further examples may be found in the FAQ section See section FAQs and Tips.

required

This action tests for the existence of a file or filesystem. It should be called after all NFS filesystems have been mounted. You may use the special variable $(binserver) here.

Files or filesystems which you consider to be essential to the operation of the system can be declared as `required'. Cfengine will warn if such files are not found, or if they look funny.

Suppose you mount your filesystem /usr/local via NFS from some binary server. You might want to check that this filesystem is not empty! This might occur if the filesystem was actually not mounted as expected, but failed for some reason. It is therefore not enough to check whether the directory /usr/local exists, one must also check whether it contains anything sensible.

Cfengine uses two variables: sensiblesize and sensiblecount to figure out whether a file or filsystem is sensible or not. You can change the default values of these variables (which are 1000 and 2 respectively) in the control section. See section control.

If a file is smaller than sensiblesize or does not exist, it fails the `required' test. If a directory does not exist, or contains fewer than sensiblecount files, then it also fails the test and a warning is issued.


required:

   any::
      
      /$(site)/$(binserver)/local

resolve

The file /etc/resolv.conf specifies the default nameserver for each host, as well as the local domain name. This file can also contain other information, but these are the only two things cfengine currently cares about. In specifying nameservers you should use the dotted numerical form of the IP addresses since your system may not understand the text form if it is not correctly configured. You may list as many nameservers as you wish, with the default server at the top of the list. The resolver normally ignores entries if you add more than three. The statement:


resolve:

  mygroup::

     129.240.22.35
     129.240.22.222
     129.240.2.3

declares a list of nameservers for hosts in the group or class mygroup. When you add the resolve command to the actionsequence, this declaration together with the domain variable (set here to uio.no) results in a /etc/resolv.conf file of the form:


domain uio.no
nameserver 129.240.22.35
nameserver 129.240.22.222
nameserver 129.240.2.3

Note that the resolve action does not delete anything from the file /etc/resolv.conf. It adds nameservers which do not previously exist and reorders the lines of servers which do exist.

As of version 1.3.11, you may use a quoted string to add non-nameserver lines to this file. For example:


resolve:

  mygroup::

     129.240.22.35
     129.240.22.222
     "# Comment line"
     "order bind, files"

If the line begins with a non-numeric character, the word `nameserver' is not added to the line.

shellcommands

Cfengine focuses on fairly simple minded tasks in order to be as general as possible. In many cases you will therefore want to write a script to do something special on your system. You can still take advantage of the classes you have defined by executing these scripts or shell commands from this section.

The syntax is simply to quote the command you wish to be executed. Variable substitution works within the strings. Here are some examples.


shellcommands:

   sun4::

       "/usr/lib/find/updatedb"

   AllHomeServers.Sunday::

       "/dir/noseyparker /$(site)/$(host)/u1 $(sysadm) nomail"

   AllBinaryServers.sun4.Saturday::

      "/usr/etc/catman -w -M /usr/local/man"
      "/usr/etc/catman -w -M /usr/local/X11R5/man"
      "/usr/etc/catman -w -M /usr/man"
      "/usr/etc/catman -w -M /usr/local/gnu/man"

Some scripts, such as noseyparker and a user-backup script, are included in the distribution to help you. See section Using the help scripts.

If you need to write more advanced scripts, which make detailed use of the classes defined by cfengine, use the $(allclasses) variable to send a complete list of classes to your script in the format


CFALLCLASSES=class1:class2:class3...

This variable is up to date at any given time with only the classes which are defined. The command line option `-u' or `--use-env' defines an environment variable which is inherited by all scripts and contains the same information. This is not the standard approach, since some system 5 systems cannot cope with this rapid change of environment and generate a Bus Error.

tidy

The tidy function is used to delete (remove permanently) unwanted files from a system. It is useful for tidying up in /tmp or cleaning out core files from users' home directories. The form of an entry is:

tidy:

  class::

      /directory pattern=wildcard recurse=number/inf 
                       age=days size=number/empty
                       type=ctime/mtime/atime 
                       dirlinks=keep/tidy/delete
                       rmdirs=true/false

Note that, each of the options below can be written in either upper or lower case and abbrieviated by any unique abbreviation.

/directory
This is the directory name to directories which mark the start of a search for files matching certain wildcards. The wildcard home may be used instead of an explicit directory, in which case cfengine iterates over all home directories. It is compulsory to specify a directory.
pattern=wildcard
A wildcard or filename to match the files you wish to be deleted. The pattern may contain the special symbols `?' which matches a single character and `*' which matches any number of characters as in the shell.
recurse=number/inf
This specifier tells cfengine whether or not to recurse into subdirectories. If the value is zero, only the named file or directory is affected. If the value is 1, it will open at most one level of subdiretory and affect the files within this scope. If the value is inf then cfengine opens all subdirectories and files beginning from the specified filename.See section Recursion.
age=days
The age of a file in days represents a minimum access time elapsed before the file will be deleted. In other word a file will be deleted if it has not been accessed for days days.
links=stop/traverse/tidy
Normally cfengine does not decsend into subdirectories which are pointed to by symbolic links. If you wish to force it to do so (without using the -l command line option) you may give this option the value true, or traverse, or follow. To specify no recursion you set the value false or stop. Note that the value set here in the cfengine program always overrides the value set by the -l command line option, so you can protect certain actions from this command line option by specifying a negative value here. If you specify no value here, the behaviour is determined by what you specify on the command line. The value links=tidy has the same effect as the `-L' command line option except that here it may be specified per item rather than globally. Setting this value causes links which point to non-existent files to be deleted. This feature will not work on commands with the `home' wildcard feature. If you want to clean up old links you should either user a files command or the command line option which sets the tidy feature globally.
size=number/empty
The value of this parameter decides the size in kilobytes of files to be deleted. Files larger than this value will be deleted if they also are older than the time specified in age. The default size is zero. If the string empty is given, then empty files will be deleted if older than age.
type=ctime/mtime/atime
This value is used to set the type of time comparison made using age. The default is to compare access times (atime) or the last time the file was read. A comparison by modification time (mtime) uses the last time the contents of the file was changed. The ctime parameter is the last time the contents, owner or permissions of the file were changed.
dirlinks=keep/tidy/delete
This value is used to decide whether cfengine will delete links which point to directories. The default value is to keep the links. Note that, if the travlinks option is switched on, cfengine will not tidy or delete links which point to directories, instead it follows them into the subdirectory.
rmdirs=true/false
Normally cfengine will not delete diretories. If this option is set to `true' then cfengine will delete any directories which are empty. Non-empty directories will not be touched and no message will be given unless in verbose mode. Note that this option overrides the above option dirlinks, so that even links which point to empty directories will be removed.

Take a look at the following example:

tidy:

   AllHomeServers::

       home     pattern=core   R=inf age=0
       home     pattern=*~     R=inf age=7
       home     pattern=#*     R=inf age=30

   any::

       /tmp/    pat=*            R=inf   age=1
       /        pat=core         R=2     age=0
       /etc     pat=hosts.equiv  r=0     age=0

In the first example, all hosts in the group AllHomeServers iterate a search over all user home directories looking for `core' files (older than zero days) and emacs backup files `*~', `#*' older than seven days.

The default values for these options are the empty string for the wildcard pattern, zero for the recursion and a specification of the age is compulsory.

When cfengine tidies users' home directories, it keeps a log of all the files it deletes each time it is run. This means that, in case of accidents, the user can see that the file has been deleted and restore it from backup. The log file is called .cfengine.rm and it is placed in the home directory of each user. The file is owned by root, but is readable to the user concerned.

NOTE that, as of version 1.3.0, cfengine concatenates search patterns on the same search directory to avoid having to reparse the file tree more than once.

unmount

The unmount function unmounts unrequired filesystems and removes the appropriate entry from the filesystem table (/etc/fstab or equivalent). The syntax is simply

unmount:

   class::
 
      mounthost:filesystem

For example:

unmount:

   physics::

      libraryserver:/$(site)/libraryserver/data

If the device is busy then the actual unmount will not take place until it becomes free, or the machine is rebooted. This feature should work on AIX systems, in spite of these machines inherent peculiarities in the form of the filesystem table.

Writing scripts for cfengine

Here is a gallery of simple-minded scripts to give you ideas for making your own. The absense of explicit testing in cfengine programs also makes these scripts transparent while offering a higher level of checking for no cost to the programmer. Similar shell scripts with this property would be complex indeed.

User scripts for tidying old files

Here is an example script for tidying old files in your own login area. If you want a long diagnostic, add the option -v to the first line of the script, before -f.

#!/usr/local/bin/cfengine -f
#
# Tidy
#

control:

   actionsequence =
      (
      tidy
      )

files:

      $(HOME)        pat=core   r=inf  age=0
      $(HOME)        pat=*~     r=inf  age=1
      $(HOME)        pat=#*     r=inf  age=7
      $(HOME)/code   pat=*.o    r=inf  age=7
      $(HOME)/tex    pat=*.dvi  r=inf  age=7
      $(HOME)/tex    pat=*.log  r=inf  age=7
      $(HOME)/tex    pat=*.aux  r=inf  age=7

      $(HOME)/ftp    pat=*.zip  r=inf  age=7

Controlled opening of files for friends and colleagues

#!/local/gnu/bin/cfengine -f
#
# Open my shared directory for others in my group
#
#

control:

  actionsequence =
     (
     files
     )

  gr = ( myshare )

files:

      $(HOME)       mode=0755 action=fixdirs r=0               
      $(HOME)/share mode=0664 action=fixall  r=inf group=$(gr)

In this example, first your home directory is opened for the world, then all files in the subdirectory share and subdirectories are opened to the group myshare. This script could be made to run from a login/logout script of some kind (either .login or .xsession) so that any new files would automatically be controlled.

Root script for emergency disk clearing

A straightforward script could be used to clear space in cases where the disk hits the overflow level. This script tidies the whole system, not just the affected disk.

#!/local/gnu/bin/cfengine -f
#
# Emergency tidyup!
#
# (Users read their cfengine.rm files to see what got deleted!)
#

control:

site = ( mysite )

mountpattern = ( $(site)/$(host) )
homepattern   = ( u? )

actionsequence =
   (
   tidy
   )

tidy:

      home            pattern=core   R=inf   age=0
      home            pattern=*~     R=inf   age=0
      home            pattern=*.dvi  R=inf   age=1
      home            pattern=*.o    R=inf   age=0
      /tmp            pattern=*      R=inf   age=0  # could be risky
      /usr/tmp        pattern=*      R=inf   age=0  #      "

ignore:

     .X11

Script for making links

The following script could be used as part of a software installation procedure. Note that the link types can be made relative to the from-link by using type=relative See section links.

#!/tmp/cfengine -v -f
#
# Simple example script to make links
#

control:

  actionsequence = ( links )

links:

 host::

   /usr/local/bin                  +> /usr/local/lib/soft/bin
   /usr/local/X11/lib/app-defaults +> /usr/local/lib/soft/app-defaults

It makes links from every binary file in the packages `bin' directory to the more standard binary directory /usr/local/bin. This avoids having to place another search directory into the users' path variable. The second statement links the package's application defaults files (for the X-windows system) to a directory in the XAPPLRESDIR search path.

This script provides only one way of making the necessary files available to users. It is not the only solution to the problem.

Ftp server

This script carries out the necessary for setting up a safe anonymous ftp server on a sun workstation running SunOS4.1.

#!/local/gnu/bin/cfengine -f
##############################################################
#
# Cfengine script to set up an outgoing ftp server under
# SunOS 4.1.*.  Suitable for anonymous access.
#
###############################################################

control:

 addclasses = ( local global )

 actionsequence =
    (
    editfiles.global
    directories
    shellcommands
    files
    editfiles.local
    )

 ftp_root = ( /oih/saga/local/ftp )   # macro for convenience
 ftp_id   = ( 99 )                    # uid/gid for ftp

################################################################

editfiles:

 # Note the file /etc/ftpusers can contain a list of users
 # who can NOT use ftp to access files.

 global::

 { /etc/passwd

 AppendIfNoSuchLine "ftp:*:$(ftp_id):$(ftp_id): (line continues)
Anonymous ftp:$(ftp_root):/usr/ucb/ftp"
 }

 { /etc/group

 AppendIfNoSuchLine "ftp:*:$(ftp_id):"
 }

################################################################

directories:

  $(ftp_root)           mode=0555 owner=ftp
  $(ftp_root)/pub       mode=0555 owner=ftp
  $(ftp_root)/bin       mode=0555 owner=root
  $(ftp_root)/usr       mode=0555 owner=root
  $(ftp_root)/dev       mode=0555 owner=root
  $(ftp_root)/etc       mode=0555 owner=root
  $(ftp_root)/dev       mode=0555 owner=root
  $(ftp_root)/usr/lib   mode=0555 owner=root

###############################################################

shellcommands:

  "/bin/cp /bin/ls $(ftp_root)/bin/ls"
  "/bin/cp /lib/libc.so.1.8* $(ftp_root)/usr/lib"
  "/bin/cp /usr/lib/ld.so  $(ftp_root)/usr/lib"
  "/bin/cp /usr/lib/libdl.so.1.0 $(ftp_root)/usr/lib/libdl.so.1.0"
  "/usr/etc/mknod $(ftp_root)/dev/zero c 3 12 > /dev/null 2>&1"

##########################################################################

files:

 $(ftp_root)/bin/ls     mode=111 owner=root action=fixall
 $(ftp_root)/usr/lib    mode=555 owner=root action=fixall r=1
 $(ftp_root)/etc/passwd mode=444 owner=root action=touch
 $(ftp_root)/etc/group  mode=444 owner=root action=touch
 $(ftp_root)/pub        mode=644 owner=root action=fixall

################################################################

editfiles:

 local::

 { $(ftp_root)/etc/passwd

 AppendIfNoSuchLine "ftp:*:$(ftp_id):$(ftp_id): (line continues)
Anonymous ftp:$(ftp_root):/usr/ucb/ftp"
 }

 { $(ftp_root)/etc/group

 AppendIfNoSuchLine "ftp:*:$(ftp_id):"
 }

Problem solving, bugs, FAQs and tips

`cf.preconf' bootstrap file

In some cases you will want to run cfengine on a system to configure it from scratch. If the system is in a very bad way, it might not even be able to parse the cfengine configuration file, perhaps because the network was not properly configured or the DNS (Domain Name Service) was out of action. To help prevent this situation, cfengine looks for a script called cf.preconf which gets executed prior to parsing and can be used to perform any emergency tests. This file needs only contain enough to get the system to parse the configuration files.

cf.preconf may be any script in any language. It need not exist at all! It is fed one argument by cfengine, namely the system hardclass for the current system (e.g. ultrix). Here is an example:

#!/bin/sh
#
# $CFINUTS/cf.preconf
#

if test $1 = "ultrix"
then
   # something
fi

if test $1 = "sun4"
then

  [ -r /lib/libc.so.1.8.3 ] && \
  /bin/echo order hosts,bind,nis > /etc/host.conf

fi

`cfrc' resource file

If, for some reason you are not satisfied with the defaults which cfengine uses, then you can change them by making an entry in the resource file. The default values are defined in the source code file classes.c in the distribution. The format of the resource file is:

hardclass.variable: value

For example, you might want to forget about where your HPUX system mounts its mail directory and mount it under /usr/spool/mail. In this case you would add the line:

hpux.maildir: /usr/spool/mail

To redefine the filesystem table for GNU/linux, you would write:

linux.fstab: /etc/linuxfstab

The full list of redefinable resources is:

   mountcomm       # command used to mount filesystems
   unmountcomm     # command used to unmount filesystems
   ethernet        # name of the ethernet device
   mountopts       # options to above mount command
   fstab           # the name of the filesystemtable
   maildir         # the location of the mail directory
   netstat         # the full path to netstat and options
   pscomm          # the path to the system's ps command
   psopts          # the options used by ps (default aux/ef)

You should never need to redefine resources unless you decide to do something non-standard. Interested readers are referred to the values in classes.c.

Cfengine is easily extensible so as to support a variety of architectures. You can even add your own. To do so you need, first of all, to define a new class for the operating system concerned. The file classes.c has been separated off from the remainder of the source code so that you can easily see which data structures need to be extended.

To make life as straightforward as possible, three unused classes have been defined. They are called (unremarkably) unused1, unused2 and unused3. If you add any further classes, it will be necessary to increase the constant clssattr defined in cf.defs.h by one for every new addition. You do not need to change clssattr if you simple replace one of the unused classes by a real class.

To see fully the impact of what you need to do, you should make a search for the strings unused? in all of the source files. Certain special cases need to be handled for each operating system. For example, the form of the filesystem table is quite radically different on some systems such as AIX. One thing you must do is to fill in the default values for the new operating system in the file classes.c.

If you fill in the details for a new operating system before it finds its way into a new release, you might consider sending the details to the bug list in the next paragraph.

Problems with installation

The installation of cfengine should not cause any problems on supported systems. Early IRIX systems (irix 4) do not compile because the file `/usr/include/vfs.h' does not exist. Unfortunately there is no way to find out how to fix this (trivial) problem without having access to an irix 4 system. If you know the answer (the appropriate header file which replaces this) please send mail to `cfengine@iu.hioslo.no'.

Earlier versions of the GNU/Linux operating system do not have support for some of the facilities which cfengine uses. In particular, the ability to use NIS netgroups is absent from earlier versions. During the installation procedure, the configure script tests for this possibility and advises you if the facility cannot be used. You can still use cfengine in this case but netgroups will not be expanded.

Another problem concerns a special socket call to the TCP/IP network interface. This is a command which configures the static routing table and appears to be absent from all versions of Linux. If you are running in verbose mode a warning message is printed, otherwise cfengine will ignore attempts to set a default route on the system.

A number of users have experienced a problem using flex and bison in place of lex and yacc. There appears to be a bug in one of these programs which casues cfengine to compile correctly but misinterpret its configuration files, generating an error of the form

cfengine:10:action contains invalid statement

for every line! The cure is to collect the latest verisons of flex and bison from your nearest GNU site.

On really old systems, the configure program is not able to guess what kind of system you are working on. This is true of SunOS versions 4.0.* and also of BSD 4.3 systems. In such cases, you might be able to compile cfengine by using the autoconf option `host' to specify the hosttype.


configure --host=sparc-sun-sunos4.0

Some other systems which will compile if forced are:

m68k-hp-bsd4.3
?-?-bsd4.3
romp-ibm-aos
?-?-aos

On some systems, problems arise when using flex. Flex might generate a lexer file lex.yy.c which defines malloc or some other function to be of a type which conflicts with the system definition. If you obtain such a culture crash, edit the lexer file manually and simply delete the offending definitions, then run make again.

Bug reports and suggestions

If you experience a problem with cfengine, find a bug or have another suggestion which you wish to air, you can send your thoughts to the special mail address bug-cfengine@gnu.ai.mit.edu.

Always think a bit before sending a message to the list. This helps to keep down the traffic improves the signal to noise ratio of your thoughts! Try to solve the problem yourself first and look particularly to see whether your system is clean or whether you have installed software or patches which might conflict with cfengine (I can't really imagine how this would happen--but it might). Always be clear about what type of operating system you are running and whether or not it is a complete installation.

Some vendors have begun the practice of distributing systems without key programs like the C compiler, lex and yacc. If you have this problem, you can pick up GNU replacements gcc, flex and bison from any GNU site.

FAQs and Tips

Here is a problem solver: an encyclopaedia of suggestions and uses for cfengine as accumulated over the years. If you have a contribution to make, please send it to cfengine@iu.hioslo.no. Format your submission like this:

Q:
How do I do....
A:
Very well thank-you....

The table below is updated as the tips occur to me, or as others contribute their own. Please note that any focusing on particular operating systems is purely a matter of personal usage/experience and should not be interpreted as a reflection of how many `bugs' these systems may or may not contain.

General

Q:
How can I check to see what cfengine will do without going through the whole program using `-n'?
A:
Run cfengine with options:

  cfengine -p -d2

This just parses the file and dumps the contents of the parser to the output. This is not an offical feature, so don't expect a particularly user friendly output format.
Q:
Why doesn't cfengine have classes for each hour, instead of just for days?
A:
Cfengine isn't meant to be a time-keeping intstrument. The best way to add run-dependent features is to define your own classes using the -D or -N options.
Q:
How can I replace the stupid version of sendmail my vendor ships with my OS with, say, Berkeley sendmail?
A:
First of all, compile your new sendmail in a filesystem which is held separate from the OS, for example `/local/mail'. You can keep all the files under this new file tree. Now you need to replace `/usr/lib/sendmail' with the new version and `/etc/sendmail.cf' or `/etc/mail/sendmail' with the new files, so that the system can find them.

   links:
      /usr/lib/sendmail ->! /local/mail/bin/sendmail
      /etc/sendmail.cf  ->! /local/mail/etc/sendmail.cf

Q:
How can I prevent big log-files like `/var/adm/wtmpx' and `httpd/access_log' from filling up my partitions?
A:
Add a line to disable the files once a week. That way you still get a chance to look at them, but you keep the size down:

   disable::

      Sunday::

         #
         # Do this to throw away old entries
         #

         /var/adm/wtmpx rotate=truncate

         #
         # Or this to keep the last lot
         #

         /var/adm/wtmpx rotate=1

An alternative to using disable would be to use tidy, but then you lose the file once and for all. Note though, that `wtmpx' gets updated all the time, so an age age=0 is necessary to have any effect at all. Some daemons, like `httpd', lose their ability to write to a log file if you rename and create a new file. The rotate=truncate option above ensures that the daemon will always be able to write to the log file.
Q:
How can I fix exports in cfengine?
A:
This is a complicated matter. There are lots of ways to do it. The key is either to edit the file `/etc/exports' (`/etc/dfs/dfstab' in solaris), or to execute an export (share) command directly from shellcommands. Under Solaris 2 this is quite easy owing to the fact that the file `dfstab' is just a script itself, rather than a configuration file like the old `/etc/exports' file. Since editing is limited and you need to specify a list of hosts which might change in time, one of the following is probably the best bet:

shellcommands:

   solaris::

      "/usr/sbin/share -F nfs -o rw=netgroup /var/mail"

On non-solaris systems:

editfiles:

   { /etc/exports

   AppendIfNoSuchLine "/site/host/fs -access=netroup"
   }

Q:
How can I distribute key setup files to users and keep them up to date?
A:
The copy facility will distribute to all users if you use the home directive. For instance, to copy a basic `.cshrc' file or `.xsession', you could write

copy:

   /local/masterfiles/.cshrc     dest=home/.cshrc
   /local/masterfiles/.xsession  dest=home/.xsession

Q:
Some users set up their own IRC listen services called "eggdrop" which fill up the disk with all kinds of garbage. How can I kill all these processes?
A:

processes:

  #
  # Most users
  #

  "eggdrop"  signal=kill

  #
  # One wise-guy has renamed the daemon!
  #

  ".*wiseguy.*myegg.*"  signal=kill

Q:
My license server keeps crashing! How can I check that it's ok?
A:

processes:

  #
  # BSD - often need long descriptove lines
  #       to find this daemon
  #

  SetOptionString "-ax"

  # Exactly one should be running

  "lmgrd" matches=1

Q:
I want to use cfengine to keep DNS tables up to date, using editfiles. How can I make cfengine automatically restart the name server after the edits?
A:
This can be done in two ways. Probably, you need to update a serial number as well as restarting the daemon. You might use a Makefile to simplify this.

control:

  actionsequence = ( editfiles control )

  solaris::
           named = ( /usr/sbin/in.named)
  linux:
  freebsd:
           named = ( /usr/sbin/named )
  sun4:
           named = ( /usr/etc/named )

editfiles:

 # edit files here

shellcommands:

   #
   # If you use make to sort out the details
   #

  "/local/gnu/bin/make -f /local/named/Makefile > /dev/null"

Or is you need to explicitly restart the name daemon, you could supplement the above with an explicit restart command (this means you lose the cache),

processes:

  "named" signal=kill restart "$(named)"

Q:
How can I edit all users' login files?
A:
You can use the 'home' pseudo-variable to iterate over all users' homedirectories:

editfiles:

    { home/.cshrc

    # Local fixes

    AppendIfNoSuchLine "alias lp  special-print-command"

    # Security

    DeleteLinesMatching "xhost +"
    }

Q:
How can I kill all processes except for root processes?
A:
The following regular expession matches lines which do not contain the string root:

processes:

 "\(root\)\{0\}"  signal=term # or kill

Q:
How can I make cfengine distribute my `/etc/motd' file?
A:
You will need a master file which contains the text you want to put on your servers. Let us define a variable `masterfile' which contains this. This master file needs to be available on all hosts on a common NFS filesystem, for instance. (This will change when remote copying is implemented in cfengine.) Now you can do something like the following script. Note that we define a version number for motd which just prevents cfengine from editing the file every single time. You have to change this version number yourself in the config file to force an update. If you don't care about this, just leave out the Begin..End parentheses.

control:

masterfile = ( /usr/local/admin/motd-master )
motd_version = ( "Message from mark 21 Aug 1996" )

editfiles:

   any::

      { /etc/motd

      BeginGroupIfNoSuchLine "$(motd_version)"
        EmptyEntireFilePlease
        InsertFile "$(masterfile)"
        PrependIfNoSuchLine "This system is running $(class):$(arch)"
        AppendIfNoSuchLine "$(motd_version)"
      EndGroup
      }

Note that, if you want special messages added just for, say, linux, then you can single out linux using a special class, or add a special edit after this one.
bug-cfengine exchange: (Reply courtesy of David Masterson).
> I like cfengine a lot and it helps me very much, but I am a little concerned > about security. I'm using cfengine to keep some files like /etc/hosts > /etc/printcap /etc/mount etc. up to date. So cfengine is started by root in > a cron job and reads its cfengine.conf file and all the other information > from a filesystem which is common to all the systems. > If now somebody manage to alter the cfengine.conf file he can do everything > he wants. > Wouldn't it be a good idea to make the cfengine.conf file soemthing like a > pgp signed messages, so that cfengine can test if this file was created by > the right person? Or are there other tips to make it more secure? I'm not sure, but I think you're overreacting or you need to be more specific about where you think the holes are in CFEngine's security. If you follow the tips of any standard systems administrator using cfengine or not, there should be few issues concerning security (ie. if security broke, there would be little chance that cfengine could do anything about it anyway). Ask yourself some of the standard questions with respect to security on UNIX: If you're still worried about the security of your script (be it a cfengine script or not), you could always adjust your cron script to "decrypt" the script file before executing it (see crypt(1)). Personally, I think if you've set the permissions on your script files properly, then, if someone breaks into those scripts, they've already broken into your system to a point where they could do what they wanted anyway.
Q:
A:

AIX

Q:
Hints about AIX?
A:
Send then to bug-cfengine@gnu.ai.mit.edu.
Q:
One of our Sysadmins has noted a limitation with line length under AIX. I'm not sure how easy it is to fix but it might be worth noting it somewhere in the cfengine docs. It appears that on the AIX machines the maximum line length we can use for cfengine files is defined by the constant YYLMAX which is set to be 200. On the Suns this constant is set to be the same as BUFSIZ which is currently set to be 1024. This manifested itself by very unusual behavior as cfengine variables began to be overwritten when line lengths in the config file exceeded 200 bytes. Peter can attest to this. Be forwarned "keep line lengths in cfengine less than 200 if you want them to work on AIX machines" Moral of the story "AIX users beware" Do you think we could just recompile cfengine and use larger buffer sizes all over, I don't know if this constant is all that should be tweaked or if it is somehow tied into the lexx implementation also, since lexx is used to create the parser for the config files.
A:
This is a problem with lex and yacc, not with cfengine. The variable BUFSIZ is a system quantity, not related to cfengine's internal variable bufsize. I would recommend getting bison and flex and doing away with the old lex and yacc from the system.

HPUX

Q:
What is the difference between the classes `hpux' and `hpux10'?
A:
In version 10 of HPUX, the file structure is reorganized to look more like SVR4. If you have an HPUX-10 system, the appropriate hardclass is hpux10 rather than hpux.
Q:
I set up the new sendmail but the configuration file doesn't work.
A:
There could be a frozen configuration file around. Try:
disable:

   hpux::

      /usr/lib/sendmail.fc
      
Q:
Why don't groups work in HPUX?
A:
HPUX uses the file `/etc/logingroup' not `/etc/group'. Make a link if you need to:
links:

  hpux::

     /etc/logingroup -> /etc/group

IRIX

Q:
Hints about IRIX?
A:
Send them to bug-cfengine@gnu.ai.mit.edu.

LINUX

Q:
I keep getting segmentation fault from my cfengine scripts when I install a completely new machine.
A:
There is a bug (apparently in Linux) which means that undefined groups cause a segmentation fault. If a group is not in `/etc/group' or in the NIS database, you should only get an error message, but linux generates a core dump. I don't know of any fix except to edit group file manually before running cfengine, or place a fix in editfiles and have this run before files, directories etc, which make reference to the group.
Q:
Linux insists on rebuilding the message of the day file each time it boots, but that means I keep losing the messages I leave there.
A:
Add the following to your configuration files to comment out the offending lines in the startup scripts:
editfiles:

   linux::

    { /etc/rc.d/rc.S
 
    HashCommentLinesContaining "motd"
    }

OSF

Q:
Hints about OSF/1?
A:
Send them to bug-cfengine@gnu.ai.mit.edu.

SUN3 (4.1.*)

Q:
How can I delete the `+' sign from the `/etc/hosts.equiv' file to improve security?
A:
Use editfiles to delete it:
editfiles:

   sun4::

      { /etc/hosts.equiv

      DeleteLinesMatching "+"
      }

SUN4 (4.1.*)

Q:
How can I delete the `+' sign from the `/etc/hosts.equiv' file to improve security?
A:
Use editfiles to delete it:
editfiles:

   sun4::

      { /etc/hosts.equiv

      DeleteLinesMatching "+"
      }

SOLARIS 2

Q:
I keep getting a `bad address' error when cfengine tries to reset the netmask and broadcast address.
A:
This is a bug in the sockets library on solaris. It is supposed to be fixed in solaris 2.5.
Q:
How can I add my own file `rc.local' to the startup bootfiles automatically?
A:
For example, create a file called `/local/etc/rc.local' which looks something like this:
#
# rc.local 
#
PATH=/local/gnu/bin:/bin:/usr/bin:/usr/sbin; export PATH

#!/bin/sh

if [ "`hostname`" = "net-server" ]; then

   echo Starting WWW server
   /local/httpd_1.4/httpd -d /local/httpd_1.4

   echo Starting GNU finger server
   /local/etc/fingerd

fi

echo Starting ypbind
/usr/lib/netsvc/yp/ypbind

echo Adding a default route and flushing table

route -f add default my-gateway 1

echo Starting xdm

/local/bin/start-xdm

Now add an entry to your `cfengine.conf' file like this

   solaris::
 
      { /etc/rc3.d/S15nfs.server
 
      AppendIfNoSuchLine "sh /local/etc/rc.local"
      }

Although this works just fine, it is not an offical way of adding your own start-up commands--but Sun's implementation of inittab overloads my cognitive array. I can't make head nor tail of it, so if you know a better way...write in.
Q:
The solaris installation program creates `/tmp' without the sticky bit set, so that any user can delete any files in `/tmp'. It also means that a race condition can occur in the kernel which can give away root access to any user!
A:
Add the following line to the configuration immediately!

files:

   /tmp mode=1777 action=fixdirs

Q:
The ftp program will not allow me to log in to my own account!
A:
The problem is that your shell is not in the system file `/etc/shells'. Add a line something like this:

editfiles:

   { /etc/shells

   AppendIfNoSuchLine "/local/bin/tcsh"
   }

Q:
tcsh prints an error message on startup and will not read my `.cshrc' file.
A:
The problem is the central login file distributed with solaris. `tcsh' can't understand it. Add a line

disable:

    /etc/.login type=file

You might want to replace this with a link to your own file.
Q:
Why does solaris fill up the routing table with hundreds of addresses under the loopback interface? (see netstat -r)
A:
First of all, get the latest patches for solaris, there are bugs in the kernel of solaris 2.4 which makes this worse. Second, make sure you have a file `/etc/defaultrouter' with the IP address of your local gateway, if you don't intend to run your system as a router. For instance:
files:

  solaris::

     /etc/defaultrouter o=root g=other m=644 act=touch

editfiles:

   solaris::

      { /etc/defaultrouter

      AppendIfNoSuchLine "xxx.xxx.xxx.1"
      }

where xxx.xxx.xxx.1 is the IP address of your gateway.
Q:
When trying to boot the system, solaris fails with the error message: fork: rescource temporarily unavailable/vfork failed. The system then claims that there is something wrong with one of the file systems.
A:
The file `/etc/system' has probably been corrupted. If this file does not exist, solaris establish the kernel properly and will not fork any processes. Things usually die early on in the boot process. This causes the side effect that the first fork the system needs to perform (to check the disk file systems) fails and misinterprets the reason for failure of the command. This makes it look as though something is wrong with the disks. Add a line:

files:

   /etc/system o=root g=root m=0644 action=touch

FreeBSD

Q:
How can I stop my FreeBSD system from running the `/etc/daily' script which mails me every single day, week and month?
A:
Add an editfiles command

 freebsd::

   { /etc/crontab
 
   HashCommentLinesContaining "daily"
   HashCommentLinesContaining "weekly"
   HashCommentLinesContaining "monthly"
   }
Q:
Why don't filesystems get mounted in the freebsd version of cfengine?
A:
Cfengine fixes the `/etc/fstab' file, but has to choose between one of two courses of action when mounting, owing to a bug in the mount command on FreeBSD machines. Cfengine mounts filesystems each time it runs. On all other supported systems this causes no problems,: once a filesystem is mounted it will not be mounted again. Under FreeBSD however, a filesystem gets mounted again each time mount is run, leading to multiple mount information in the mount table. This causes cfengine to warn the the filesystem is mounted many times, and could eventually result in a problem for the FreeBSD machine. The policy is therefore to use mount options which do not cause this behaviour, but an unfortunate side-effect is that newly defined filesystems do not get mounted. You can override the mount options if you want to force multiple mounting.

Using the help scripts

The following Perl scripts are included as examples and helpful tools in your system administration package. If you do not have Perl, you should get it -- it is a very useful language for system administration.

cfwrap

It is useful to run cfengine on a daily basis from a cron script. Use a line like the following one to start cfengine each night. (Note the curiosities of older BSD cronfiles).

0 0 * * * /usr/local/lib/cfengine-3.0/bin/cfwrap cfdaily

where cfdaily is a script which looks something like

#!/bin/sh

CFINPUTS=/usr/lib/cfengine/inputs

/usr/local/bin/cfengine

You will need to include full path names to the scripts in the cron file. The syntax for using cfwrap is as follows.

host% cfwrap mycommand

host% cfwrap cfengine

host% cfwrap script_which_sets_CFINPUTS_and_calls_cfengine

When you run cfengine it normally only generates output if something is wrong which needs your attention. If you are running cfengine as a cron job then the results of each job are normally mailed back to you -- or to root. But this causes problems in a networked environment, since mail to root is usually redirected to some central place which local system administrators cannot access. Moreover, you have no way of knowing which host sent the information. The solution is to use a script as a wrapper. The script simply executes some command and collects the output from that command into a file which then gets mailed to some address.

The address to be mailed to is obtained directly from cfengine by calling it with the -a switch. The name of the host running cfengine is prepended to the file before it is sent making it easy to see where each message originated. This is also transferred to the subject header of the mail message. cfwrap calls cfmail in order to mail the result of the command back to the system administrator.

#!/usr/local/bin/perl
##############################################################
#
# Script wrapper, mails output if there is any
#
# Mainly intended for running cfengine.
#
##############################################################

if ($< != 0)
   {
   die "cfwrap: Bad luck, only root can run this one!\n";
   }

$mail = 0;
$sysadm = `cfengine -a`;

$CFHOME = "/usr/local/lib/cfengine";

$comm = join(" ",@ARGV);
@path = split(m;/;,$comm);
$suff = $path[$#path];
$tmpfile = "/tmp/cfwrap.$$";
$subjectfile = "/tmp/cfwrapsub.$$";

open (H,"/bin/hostname |") || die "cfwrap: Can't get hostname";
$hostname = <H>;
chop $hostname;
close(H);

open (SH,"$comm 2>&1 | ") || die "cfwrap: Can't start shell\n";
open (OUT,">/tmp/cfwrap.$$") || die "cfwrap: Can't open workfile\n";

while (<SH>)
   {
   if ( /\S/ )
      {
      print OUT "$_";
      $mail = 1;
      }
   }

if ($mail)
   {
   open (SUB,">$subjectfile") || die "Cannot open $subjectfile\n";
   print SUB "Subject: cfengine ($hostname)\n\n";
   print SUB "This message originates from host $hostname\n";
   print SUB "The full command issued was: $comm.\n\n";
   close(SUB);
   }

close(OUT);
close(SH);

if ($mail)
   {
   system ("/bin/cat $subjectfile $tmpfile | $CFHOME/bin/cfmail $sysadm");
   }

unlink ("$subjectfile");
unlink ("$tmpfile");

cfmail

Because there are no standard mail-agents except for sendmail, the wrapper script cfwrap calls its own simple mail agent cfmail to send the message. Note that the flags variable in the script cfmail arranges for the mail message to be sent with a return address other than "root". This means that if the recipient of the mail should decide to hit `r' for `reply' to reply to the message, you have a chance of getting to see the message before it vanishes along with the rest of the mail to root into the same black hole that swallows up all those credit cards, house-keys and odd socks that disappear on a daily basis.

You might have to tweak the scripts slightly to tailor them to your own needs. They are used as follows:

host% echo test ....  | cfmail -s "Test message" mark 

noseyparker and editquotas: software quotas

Noseyparker is a script which must be run by root. It is used to give a software warning about users who are hogging your disk. It is run with the command:

noseyparker homedir $(sysadm) [nomail]

The directory homedir should be one of the directories in which user's home directories reside. This is searched for a list of usernames. $(sysadm) is the mail address of the system administrator (which can be obtained from the cfengine variable of the same name) and is used to send information about users who have exceeded certain quotas. The option nomail prevents noseyparker from sending mail automatically to the users concerned.

Although many administrators use the quota checking facilities available in UNIX, there are problems with these. Many users need to generate large temporary files (when generating postscript or TEX, or when performing numerical calculations). Using hard quotas prevents these users from using their accounts effectively. Noseyparker takes the view that most users will tidy up files if they receive a polite reminder -- and the few who cannot be dealt with by other means.

Noseyparker should be run once a week on each home directory on a homeserver. When you run noseyparker, the program generates statistics which it keeps from week to week in order to find out how fast data are growing. If the number of kilobytes for a given user exceeds a limit, a warning is generated: either a pre-warning, a soft warning or a hard warning. If nomail is set, only the system administrator gets a list of these users. If it is not set, then each user gets an automatic message telling him or her to tidy up. The `degree' of the message depends on the extent of the crime. Every user gets at least three `soft warnings' before putting in the boot. The system administrator can see from the mail report which type of warning each user has received.

A default quota is defined in the variable $softlimit. A pre-margin determines how early a pre-warning will be sent. Special quotas for particular users are set using the editquotas script

editquotas homedir

For example, to change the quota of a user whose home directory is on `/mysite/myhost/u2', one writes:

editquotas /mysite/myhost/u2

The file is edited so that it takes the form

username quota
user2    quota2

You can modify noseyparker to suit your own setup if necessary.

Here is a typical command to start noseyparker:

shellcommands: 
 
   homeservers.Sunday::
   
      "$(cfbin)/noseyparker /home/$(host) $(sysadm) nomail"
 
   homeservers.cfengine_model.Sunday::

      "$(cfbin)/noseyparker /$(site)/$(host)/u1 $(sysadm) nomail"

cfbackup and cfrestore scripts

Today, most people would agree that diskspace is cheap, whilst time spent taking backups is expensive. The backup script included in the `bin' directory solves this problem for us at Oslo College by making automatic backups of users' directories using GNU-tar to compressed tar files which are then placed in a spare partition of a special disk. The script is called up as follows:

shellcommands: 
 
  BackupHost.Sunday|BackupHost.Wednesday::
 
      #
      # Make a system backup of /iu/nexus/u? with Audun's script
      #
 
      "$(cfbin)/cfbackup -p -f /iu/dax/backup1 -s /iu/nexus/u1"
      "$(cfbin)/cfbackup -p -f /iu/dax/backup1 -s /iu/nexus/u2" 
      "$(cfbin)/cfbackup -p -f /iu/dax/backup2 -s /iu/nexus/u3"
      "$(cfbin)/cfbackup -p -f /iu/dax/backup2 -s /iu/nexus/u4" 
 

The directories to be taken backup of are listed using the -s option. The directory which the files get saved to is coded in the variable $destination of the (Perl) backup script. -f tells the script where to leave the tar-ed backup file: in this case the user partitions are spread between two backup areas. The -p option makes sure that each user owns his/her own backup file. This means that each user can access their backup files themselves using the cfrestore script (see below).

As the backup files build up on the backup disk, you can delete older files using the tidy function:


tidy:
 
   BackupHost::
 
      # Here we tidy old backup tar files from the backup area
 
      /iu/dax/backup1      pat=*  age=15
      /iu/dax/backup2      pat=*  age=15

This example would tidy all the old backupfiles after three weeks.

Each user's directory (assumed to be a child of the directories named using -s) is saved in a separate tar-file, labelled with the date on which the backup was taken. They look like this:

backup.16.8.95.iu.nexus.u2.wiikl
backup.16.8.95.iu.nexus.u2.wullumt
backup.16.8.95.iu.nexus.u2.wux
backup.20.8.95.iu.nexus.u1.agneta

A file can then be recovered by typing a command like


cfrestore myfile

cfrestore will then search through the tar-file for files matching the file you request and place them in a special directory under your home directory, labelled by the name (and date) of the tar file from which the file was extracted. If you know which backup file (the date) from which you want to restore a file, you can use the -f backupfile option.

These scripts will at the very least require minor modifications for a your local site configuration, but they can be used as a simple and effective way of taking backups automatically.

NOTE: the scripts make convenient use of GNU tar and fileutils packages.

cfbg

Normally cfengine blocks in subprocesses. That means that when you execute a shell command, cfengine waits for each command to exit before continuing exuction. This serializes the execution of shellcommands. On occasions, you might wish to force a process to dissociate from from parent cfengine process and run in parallel. For example, you might want the backup scripts (which can take quite a long time) to not hold up cfengine. To do this, you can use a simple wrapper script like `cfbg'. This command simply takes the remainder of the command line (after cfbg) as a command to execute and forks a new process for this command, without waiting. Cfengine is then free to continue running. Here is an example

control:

  cfbin = ( /local/gnu/lib/cfengine/bin )

shellcommands:

  "$(cfbin)/cfbg $(cfbin)/cfbackup"

The default script writes the output of the standard out and standard error to a file in the `/tmp' directory. Alternatively you might wish to send the output directly to `/dev/null'.

Example configuration files

Here is a sample from a large configuration file, just to give you some ideas. The file is broken up into manageable pieces for convenience.

cfengine.conf

#####################################################################
# 
#  CFENGINE CONFIGURATION FOR site = iu.hioslo.no
#
#  This file is for root only.
#
#  Contains local patches of a generic nature and imports
#  more specific files for special purposes.
#
######################################################################

###
#
# BEGIN cfengine.conf
#
###

groups:

   #
   # Define some groups
   #

   # All hosts
 
   iu = ( nexus ferengi regula borg dax lore axis worf )

   # Special groups

   diskless   = ( regula ferengi lore )
   standalone = ( nexus axis dax borg worf )

   AllHomeServers   = ( nexus )
   AllBinaryServers = ( nexus borg )

   XBootServer = ( nexus )
   WWWServers  = ( nexus )
   FTPserver   = ( nexus )
   NameServers = ( nexus )
   BackupHost  = ( nexus )
   NISserver   = ( nexus )

######################################################################

control: 

   access    = ( root )

   site      = ( iu )
   domain    = ( iu.hioslo.no )
   sysadm    = ( drift@iu.hioslo.no ) 

   netmask   = ( 255.255.255.0 )
   timezone  = ( MET )
   nfstype   = ( nfs )

   sensiblesize  = ( 1000 )
   sensiblecount = ( 2 )
   editfilesize  = ( 6000 )

   #
   # The regular action sequence
   #

   !Hourly::    

      actionsequence = 
         (
         mountall
         mountinfo
         checktimezone
         netconfig
         resolve
         unmount
         shellcommands
         addmounts
         links.basics
         files.basics
         directories
         links.others
         mailcheck
         mountall
         required
         tidy
         disable
         files
         copy
         editfiles
         )

   #
   # A shorter action sequence per hour
   #

   Hourly::

   actionsequence = 
      (
      copy
      editfiles
      tidy.somefiles
      )

   mountpattern = ( /$(site)/$(host) )
   homepattern = ( u? ) 

   addclasses = ( exclude )

   #
   # Macros & constants are inherited downwards in imports
   # but are not passed up to parent files. Good idea to
   # define them all here
   #

   masterfiles = ( /iu/nexus/local/mutils )
   main_server = ( nexus )
   cfbin = ( /local/gnu/lib/cfengine/bin )
   gnu = ( /local/gnu )
   ftp = ( /local/ftp )

######################################################################

homeservers:

   iu:: nexus

binservers:

   iu.solaris::                 nexus
   iu.linux::                   borg

mailserver:

   any::
          nexus:/var/mail

mountables:

   any::

         nexus:/iu/nexus/u1
         nexus:/iu/nexus/u2
         nexus:/iu/nexus/local
         nexus:/opt/NeWSprint
         borg:/iu/borg/local

miscmounts:

   borg::   nexus:/iu/nexus/local /iu/nexus/local ro

######################################################################

import:

   #
   # Split things up to keep things tidy
   #

   any::            cf.site
   hpux::           cf.hpux
   linux::          cf.linux
   solaris::        cf.solaris
   sun4::           cf.sun4
   ultrix::         cf.ultrix
   AllHomeServers:: cf.users

######################################################################

broadcast:

  ones

defaultroute:

  iu-gw

######################################################################

resolve:

      128.39.89.10  # nexus
      158.36.85.10  # samson.hioslo.no
      129.241.1.99

######################################################################

tidy: 

   #
   # Some global tidy-ups
   #

   /tmp/                    pat=*             r=inf     A=1
   /var/tmp                 pat=*             r=inf     A=1
   /                        pat=core          r=1       A=0
   /etc                     pat=core          r=1       A=0
   /var/spool/cron/crontabs pat=root.cf*                a=0

######################################################################

ignore:                       # Don't check or tidy these directories

      /local/lib/gnu/emacs/lock/
      /local/tmp
      /local/ftp
      /local/bin/top
      /local/lib/tex/fonts
      /local/etc
      /local/www
      /local/httpd_1.4/conf
      /usr/tmp/locktelelogic
      /usr/tmp/lockIDE

      #
      # Emacs lock files etc
      #

      !*

      #
      # X11 keeps X server data in /tmp/.X11
      # better not delete this!
      #

      .X11

      #
      # Some users like to give a file or two 777 protection here
      # so netsurfers can update a log or counter when running as
      # `nobody'
      #

      www

#####################################################################

disable:

   /etc/hosts.equiv
   /etc/nologin
   /usr/lib/sendmail.fc

###
#
# END cfengine.conf
#
###

cf.site

#################################################################
#
# cf.site - for iu.hioslo.no
#
# This file contains site specific data
#
#################################################################

###
#
# BEGIN cf.site
#
###

links:

   basics::

      /local     -> /$(site)/$(binserver)/local
      /usr/local -> /local

   AllBinaryServers.solaris::

      /local/bin       +> /local/perl5/bin

   XBootServer::

      #
      # Set up a /local/tftpboot area where all X terminal
      # stuff will be kept.
      #

      /tftpboot                  -> /local/tftpboot
      /etc/bootptab              -> /tftpboot/bootptab
      /tftpboot/usr/lib/X11/td   -> /tftpboot/td

   solaris::

      #
      # Some stuff to make things easier locally going from
      # sun4 to solaris
      #

      /var/spool/mail       ->  /var/mail
      /etc/mail/sendmail.cf ->! /local/mail/etc/sendmail.cf

   NameServers::

      /etc/named.boot -> /local/named/named.boot

   borg::

      /local/lib/irc -> /iu/nexus/local/lib/irc

#############################################################

disable:

 WWWServers.Sunday::

   #
   # Disabling these log files weekly prevents them from
   # growing so enormous that they fill the disk!
   #

   /local/httpd_1.4/logs/access_log
   /local/httpd_1.4/logs/agent_log
   /local/httpd_1.4/logs/error_log
   /local/httpd_1.4/logs/referer_log

#################################################################

files:

      /etc/motd              m=0644 r=0 o=root act=fixplain
      /etc/motd              m=0644 r=0 o=root act=touch
      /.cshrc                m=0644 r=0 o=root act=touch

   NISserver::

      /local/etc/passwd m=0644 o=root g=other action=fixplain
      /local/etc/shadow m=0600 o=root g=other action=fixplain

   WWWServers.Sunday::

      #
      # Better touch these again since we disabled them
      #

      /local/www/cgi-bin-public/.               act=touch
      /local/httpd_1.4/logs/access_log          act=touch
      /local/httpd_1.4/logs/agent_log           act=touch
      /local/httpd_1.4/logs/error_log           act=touch
      /local/httpd_1.4/logs/referer_log         act=touch

   WWWServers::

      /local/www r=inf group=www mode=775 act=fixall

   FTPserver.solaris::

      #
      # Make sure anonymous ftp areas have the correct
      # protection, or logins won't be able to read
      # files - or perhaps a security risk. This is
      # solaris 2 specific...
      #

      $(ftp)/pub        mode=755 o=ftp g=ftp r=inf act=fixall
      $(ftp)/Obin       mode=111 o=root g=other act=fixall
      $(ftp)/etc        mode=111 o=root g=other act=fixdirs
      $(ftp)/usr/bin/ls mode=111 o=root g=other act=fixall
      $(ftp)/dev        mode=555 o=root g=other act=fixall
      $(ftp)/usr        mode=555 o=root g=other act=fixdirs

   basics::

      /etc/shells mode=0644 action=touch

   AllBinaryServers.exclude::

     /local m=-0002 r=inf o=root g=0,1,2,3,4,5,6,staff links=tidy

######################################################################

directories:

   solaris::

     #
     # httpd wants this defined
     #

      /usr/lib/X11/nls

   borg::

      #
      # Create a local tmp file for users who want
      # to make big files temporarily
      #

      /local/tmp  mode=1777 o=root g=0

#################################################################

tidy:

   BackupHost::

      # Here we tidy old backup tar files from the backup area
      # A special tmp area gets cleared every 4 days. The files
      # are created by Audun's backup help script (see shellcommands)

      /iu/nexus/backup      pat=*  age=21

   borg::

      #
      # Tidy the tmp area every 4 days
      #

      /local/tmp            pat=*             r=inf     age=4

#################################################################

shellcommands: 

  BackupHost.Sunday::

      #
      # Make a system backup of /iu/nexus/u? with Audun's script
      #

      "$(cfbin)/cfbackup -p -s /iu/nexus/u1 -s /iu/nexus/u2" 

  nexus.Sunday.exclude::

      #
      # See how much rubbish users have accumulated each Sunday
      #

      "$(cfbin)/noseyparker /iu/nexus/u1 $(sysadm) nomail"
      "$(cfbin)/noseyparker /iu/nexus/u2 $(sysadm) nomail" 

   nexus.exclude::

      #
      # Update the GNU find/locate database each night
      #
 
      "$(gnu)/lib/locate/updatedb"

   AllBinaryServers.sun4.Saturday.exclude::

      #
      # Make sure the man -k / apropos data are up to date
      #

      "/usr/bin/catman  -M /local/man"
      "/usr/bin/catman  -M /local/X11R5/man"
      "/usr/bin/catman  -M /usr/man"
      "/usr/bin/catman  -M /local/gnu/man"
      "/usr/bin/catman  -M /usr/openwin/share/man"
      "/usr/bin/catman  -M /local/X11R5/man"
      "/usr/bin/catman  -M /local/mutils/man"

    solaris::

      #
      # A crude way to set the system clock from a known
      # reliable server. Would really like to have ntpd
      #

      "/usr/bin/rdate anyon.uio.no > /dev/null"

###############################################################

editfiles:

   any::

      #
      # cfengine installs itself as a cron job - sneaky! :)
      #

      { /var/spool/cron/crontabs/root

      AppendIfNoSuchLine "0 0 * * * $(cfbin)/cfwrap $(cfbin)/cfdaily"
      }

      #
      # If tcsh (or other non standard shells) are not in /etc/shells
      # ftp won't allow logins...
      #

      { /etc/shells

      AppendIfNoSuchLine "/local/bin/tcsh"
      }

   solaris::

      #
      # A painless way to add an rc.local script to the rc files
      # under solaris without having to fight through inittab
      #

      { /etc/rc3.d/S15nfs.server

      AppendIfNoSuchLine "sh /local/etc/rc.local"
      }

   XBootServer::

      { /etc/inetd.conf

      AppendIfNoSuchLine "bootp dgram udp wait root /local/bin/bootpd bootpd -i -d"
      }

   NISserver::

      #
      # Make sure the master NIS files contain some important stuff
      #

      { /local/etc/services

      WarnIfNoLineContaining "pop"
      AppendIfNoSuchLine     "bootpc          68/udp"
      AppendIfNoSuchLine     "bootp           67/udp"
      AppendIfNoSuchLine     "cfinger         2003/tcp"
      AppendIfNoSuchLine     "http            80/tcp          www"
      }

      { /local/etc/networks

      AppendIfNoSuchLine "iu-net          128.39.89"
      }

######################################################################

required:

   #
   # Any host must have a /local, /usr/local fs. Check that
   # it exists and looks sensible. (i.e. not empty)
   #

   /$(site)/$(binserver)/local

###
#
# END cf.site
#
###

cf.users

#################################################################
#
# cf.users - for iu.hioslo.no
#
# This file contains user specific actions
#
#################################################################

###
#
# BEGIN cf.users
#
###

tidy:

   exclude::

     #
     # Tidy up users' home dirs
     #

     home                 pat=core             r=inf       age=0
     home                 pat=a.out            r=inf       age=2
     home                 p=*%                 r=inf       age=2
     home                 p=*~                 r=inf       age=2
     home                 p=#*                 r=inf       age=1    
     home                 p=*.dvi              r=inf       age=14
     home                 p=*.log              r=inf       age=3
     home                 p=Log.*              r=inf       age=3
     home                 p=*.nfs              r=inf       age=1
     home                 p=CKP                r=inf       age=1
     home                 p=BAK                r=inf       age=1
     home                 p=log                r=inf       age=14
     home                 p=*.o                r=inf       age=7
     home                 p=*.aux              r=inf       age=3
     home/.deleted        p=*                  r=inf       age=2
     home/.wastebacket    p=*                  r=inf       age=14

     #
     # Clear the big cache files netscape creates
     #

     home/.netscape-cache p=cache????*         r=inf       age=2
     home/.MCOM-cache     p=cache????*         r=inf       age=2

#################################################################

files:

   exclude::

     #
     # Check users files are not writable to the world
     # and there are no stale links (pointing nowhere)
     #

     home mode=o-w recurse=inf action=fixall # links=tidy

#################################################################

copy:

   exclude::

   #
   # Make sure each user has an up to date standard
   # setup.  Cshrc just sources in a big standard file
   # which is kept in ~user/../.setupfiles/cshrc
   # to reduce disk wastage
   #

   $(masterfiles)/lib/Cshrc dest=home/.cshrc
   $(masterfiles)/lib/mwmrc dest=home/.mwmrc

   setup::

   $(masterfiles)/lib/xsession dest=home/.xsession mode=700

###
#
# END cf.users
#
###

cf.sun4

#################################################################
#
# cf.sun4 - for iu.hioslo.no
#
# This file contains SunOS 4.1.x specific patches
#
#################################################################

###
#
# BEGIN cf.sun4
#
###

files:

   sun4::

      /etc/passwd    m=0644 r=0 o=root g=staff act=fixplain
      /etc/group     m=0644 r=0 o=root g=staff act=fixplain
      /dev/kmem      m=0640 r=0 o=root g=kmem  act=warnall

###
#
# BEGIN cf.sun4
#
###

cf.solaris

#################################################################
#
# cf.solaris - for iu.hioslo.no
#
# This file contains solaris specific patches
#
#################################################################

###
#
# BEGIN cf.solaris
#
###

links:

      /usr/lib/sendmail     ->! /local/mail/bin/sendmail
      /etc/mail/sendmail.cf ->! /local/mail/etc/sendmail.cf
      /opt/gnu              -> /local/gnu

##############################################################

tidy:

   #
   # This file is ENORMOUS, don't let it fill disk
   #

   Wednesday::

       /var/adm   pat=wtmpx   a=0

##############################################################

copy:

   #
   # Some standard setup files, can't link because
   # machine won't boot if their not on / partition.
   #

   diskless::

      /local/etc/nsswitch.diskless dest=/etc/nsswitch.conf

   standalone::

      /local/etc/nsswitch.standalone dest=/etc/nsswitch.conf

##############################################################

editfiles:

   solaris::

      { /etc/netmasks

      DeleteLinesContaining "255.255.254.0"
      AppendIfNoSuchLine "128.39  255.255.255.0"
      }

      { /etc/defaultroute

      AppendIfNoSuchLine "128.39.89.1"
      }

##############################################################

disable:

 solaris::

    /etc/.login type=file

##############################################################

files:

    #
    # If this doesn't exist, solaris can't fork any
    # processes and you can't even run the rc scripts
    # at boot time!
    #

    /etc/system       m=0644 o=root g=root action=fixplain

    /etc/passwd       m=0644 o=root g=other action=fixplain
    /etc/shadow       m=0600 o=root g=other action=fixplain
    /etc/defaultroute m=644 o=root g=other  action=touch
    /var/adm/wtmpx    m=0644 o=adm g=adm    action=touch
    /var/adm/wtmpx    m=0644 o=adm g=adm    action=fixplain
    /tmp m=1777                             action=fixdirs

###
#
# END cf.solaris
#
###

cf.ultrix

#################################################################
#
# cf.ultrix - for iu.hioslo.no
#
# This file contains ultrix specific patches
#
#################################################################

###
#
# BEGIN cf.ultrix
#
###

files:

   ultrix::

      /etc/passwd          m=0644 r=0 o=root g=system act=fixplain
      /etc/group           m=0644 r=0 o=root g=system act=fixplain
      /dev/kmem            m=0640 r=0 o=root g=kmem   act=warnall
      /usr/lib/sendmail.cf m=0644 r=0 o=root g=system act=fixall
      /etc/sendmail.cf     m=0644 r=0 o=root g=system act=fixall
###
#
# END cf.ultrix
#
###

cf.hpux

#################################################################
#
# cf.hpux - for iu.hioslo.no
#
# This file contains hpux specific patches
#
#################################################################

###
#
# BEGIN cf.hpux
#
###

links:

    hpux::

       #
       # To make hpux more like other OSs
       #

       /var/spool      -> /usr/spool
       /usr/spool/mail -> /usr/mail
       /etc/logingroup -> /etc/group
       /bin/wall       -> /etc/wall

#################################################################

files:

   hpux::

      /etc/passwd           m=0644 r=0 o=root g=sys act=fixplain
      /etc/group            m=0644 r=0 o=root g=sys act=fixplain
      /dev/kmem             m=0640 r=0 o=bin  g=sys act=warnall
      /usr/lib/sendmail.cf  m=0644 r=0 o=root g=sys,other act=fixall      

###
#
# END cf.hpux
#
###

cf.linux

#################################################################
#
# cf.linux - for iu.hioslo.no
#
# This file contains linux specific patches
#
#################################################################

###
#
# BEGIN cf.linux
#
###

links:

    /local/bin/tcsh   ->  /bin/tcsh
    /local/bin/perl   ->  /usr/bin/perl

    /etc/aliases      ->! /local/mail/aliases/aliases.oih   
    /etc/sendmail.cf  ->! /local/mail/etc/sendmail.linux.cf

########################################################################

editfiles:

    { /etc/rc.d/rc.6

    WarnIfNoSuchLine   "exec /local/mutils/bin/start-xdm"
    WarnIfLineStarting "exec /usr/X11/bin/xdm"
    }

    { /etc/rc.d/rc.S

    HashCommentLinesContaining "motd"
    }

    { /.rhosts

    AppendIfNoSuchLine "$(main_server)"
    AppendIfNoSuchLine "$(main_server).$(domain)"
    }

    { /etc/csh.cshrc

    AppendIfNoSuchLine "setenv TZ MET"
    }

    { /etc/host.conf

    PrependIfNoSuchLine "order hosts,bind,nis"
    DeleteLinesStarting "order hosts, bind"
    }

   { /etc/services

   AppendIfNoSuchLine "cfinger         2003/tcp"
   }

#########################################################################

shellcommands:

  borg::

     #
     # Find/locate database
     #

     "/usr/bin/updatedb"

#########################################################################

directories:

   #
   # Make printer spool directories...
   #

   /var/spool/VirtualLight  o=root g=other mode=755

#########################################################################

copy:

   /iu/nexus/local/gnu/lib/cfengine/inputs dest=$(gnu)/lib/cfengine/inputs
   $(masterfiles)/etc/printcap.client      dest=/etc/printcap mode=0644

###
#
# END cf.linux
#
###

cf.freebsd


#################################################################
#
# cf.bsd - for iu.hioslo.no
#
# This file contains bsd specific patches
#
#################################################################

###
#
# BEGIN cf.bsd
#
###

links:

    /usr/spool        ->  /var/spool
    /local/bin/tcsh   ->  /bin/tcsh
    /local/bin/perl   ->  /usr/bin/perl
    /usr/lib/sendmail ->  /usr/sbin/sendmail

#################################################################

editfiles:

   { /etc/inetd.conf

   AppendIfNoSuchLine "finger  stream tcp nowait daemon /local/etc/in.fingerd in.fingerd"
   AppendIfNoSuchLine "cfinger stream tcp nowait daemon /local/etc/in.cfingerd in.cfingerd"
   }

   #
   # Comment out all lines to shut up this annoying cfengine-like
   # script, which sends mail every day!!!
   #

   { /etc/crontab

   HashCommentLinesContaining "daily"
   HashCommentLinesContaining "weekly"
   HashCommentLinesContaining "monthly"
   }

#################################################################

directories:

   #
   # Make printer spool directories...
   #

   /var/spool/VirtualLight  o=root g=other mode=755

#########################################################################

copy:

      $(masterfiles)/etc/printcap.client      dest=/etc/printcap mode=0644

#########################################################################

shellcommands:

     "/local/iu/bin/BSD-pw-update"

  !Hourly::

    "/usr/libexec/locate.updatedb"
    "/usr/bin/makewhatis /usr/share/man:/usr/X11R6/man:/usr/local/man:/local/gnu/man"

###
#
# END cf.bsd
#
###

Runtime Options

Note that GNU long options are available with the syntax --longoption. The long names are given in brackets.

`-a'
(--sysadm) Print only the name of the system administrator then quit.
`-c'
(--no-check-files) Do not check file systems for ownership / permissions etc.
`-C'
(--no-check-mounts) Check mount points for consistency. If this option is specified then directories which lie in the "mount point" area are checked to see whether there is anything mounted on them. Normally this is off since not all machines use mounted file systems in the same way. e.g. HPUX does not generally operate with partitions, but nevertheless one might wish to mimick a partition-like environment there, but it would be irritating to be informed that nothing was mounted on the mount point.
`-d'
(--debug) Enable debugging output. Normally you will want to send this to a file using the shell script command or a pipe. -d1 shows only parsing output. -d2 shows only runtime action output. -d0 shows both levels. Debugging ouput is intended mainly for the author's convenience and is not a supported feature. The details of this output may change at any time.
`-D'
(--define) Define a compound class symbol of the form alpha.beta.gamma.
`-e'
(--no-edits) Suppress file editing.
`-E'
(--enforce-links) Globally force links to be created where plain files or links already exist. The plain file is first saved by appending the string `.cfenginesaved' to the filename. Since this option is a big hammer, you have to use it in interactive mode and answer a yes/no query before cfengine will run like this.
`-f'
(--file) Parse filename after this switch. By default cfengine looks for a file called cfengine.conf in the current directory.
`-h'
(--help) Help information. Display version banner and options summary.
`-H'
(--no-hard-classes). Prevents cfengine from generating any internal class name information. Can be used for emulation purposes.
`-i'
(--no-ifconfig) Do not attempt to configure the local area network interface.
`-k'
(--no-copy) Do not copy/image any files.
`-l'
(--traverse-links) Normally cfengine does not follow symbolic links when recursively parsing directories. This option will force it to do so.
`-L'
(--delete-stale-links) Delete links which do not point to existing files (except in user home directories, which are not touched).
`-m'
(--no-mount) Do not attempt to mount file systems or edit the filesystem table.
`-n'
(--recon,--dry-run,--just-print) No action. Only print what has to be done without actually doing it. @item -N (--negate,--undefine) Cancel a set of classes, or undefine (set value to false) a compound class of the form alpha.beta.gamma.
`-p'
(--parse-only) Parse file and then stop. Used for checking the syntax of a program. You do not have to be superuser to use this option.
`-s'
(--no-commands) Do not execute scripts or shell commands.
`-S'
(--silent) Silence run time warnings.
`-t'
(--no-tidy) Do not tidy file systems.
`-u'
(--use-env) Causes cfengine to generate an environment variable `CFALLCLASSES' which can be read by child processes (scripts). This variable contains a summary of all the currently defined classes at any given time. This option causes some system 5 systems to generate a Bus Error or segmentation fault. The same information is available from the cfengine internal variable $(allclasses) and can be passed as a parameter to scripts.
`-U'
(--underscore-classes). When this option is set, cfengine adds an underscore to the beginning of all hard system classes (like _sun4, _linux etc.) This can be used to avoid naming conflicts if you are so unjudicious as to name a host by the name of a hard class. Other classes are not affected.
`-v'
(--verbose) Verbose mode. Prints detailed information about actions and state.
`-V'
(--version) Print only the version string and then quit.
`-x'
(--no-preconf) Do not execute the `cf.preconf' net configuration file.
`-X'
(--no-links) Do not execute the links section of a program.
`-w'
(--no-warn,--quiet) Do not print warning messages.

Variable Index

!

  • !
  • "

  • "
  • '

  • '
  • +

  • +
  • -

  • -a option, -a option
  • -D option, -D option
  • -f option, -f option
  • -h option
  • -L
  • -l, -l
  • -N option, -N option, -N option
  • -n option
  • -v option
  • -x option
  • .

  • .cfengine.rm
  • /

  • /etc/cfengine.log
  • /etc/cfengine.pid
  • /etc/exports
  • /etc/host.conf
  • `

  • `
  • a

  • a=, a=
  • action
  • actionsequence, actionsequence
  • addclasses
  • addmounts
  • age
  • any
  • b

  • backup=
  • binserver, binserver, binserver, binserver, binserver
  • binservers
  • broadcast
  • c

  • cf.preconf
  • CFALLCLASSES, CFALLCLASSES
  • cfrc
  • checkmail
  • checktimezone
  • control
  • cr
  • d

  • directoriess
  • disable, disable
  • domain, domain, domain
  • e

  • editfiles
  • editfilesize, editfilesize
  • empty
  • f

  • faculty
  • files, files
  • force=
  • g

  • g=
  • group
  • groups
  • h

  • home
  • homepattern
  • homeservers
  • hompat
  • host
  • i

  • import
  • l

  • l=
  • lf
  • link
  • linkchildren, linkchildren
  • links, links
  • m

  • m=
  • mailcheck
  • mailserver
  • miscmounts
  • mode
  • mountables, mountables
  • mountall
  • mountinfo
  • mountpattern
  • n

  • netconfig
  • netmask, netmask
  • nfstype, nfstype
  • o

  • o=
  • ones
  • owner
  • p

  • p=
  • pattern
  • processes
  • r

  • r=, r=
  • recurse, recurse
  • repchar
  • required
  • resolve, resolve
  • Restricting access
  • rotate=
  • s

  • sensiblecount, sensiblecount
  • sensiblesize, sensiblesize
  • shellcommands
  • site, site, site
  • spc
  • split, split
  • sysadm, sysadm, sysadm
  • t

  • tab
  • tidy, tidy
  • timezone, timezone
  • touch
  • type=, type=
  • u

  • underscoreclasses
  • unmount, unmount
  • w

  • Wildcards
  • z

  • zeroes
  • zeros
  • Concept Index

  • !

  • !
  • +

  • `+' symbol in `/etc/hosts.equiv', `+' symbol in `/etc/hosts.equiv'
  • -

  • -a option
  • -D option
  • -h option
  • -l option, -l option
  • -L option
  • -N option
  • -x option
  • .

  • .cfengine.rm, .cfengine.rm
  • `.cshrc', distributing
  • .X11 directory
  • `.xsession', distributing
  • /

  • /etc/cfengine.log
  • /etc/cfengine.pid
  • `/etc/defaultroute'
  • `/etc/host.conf'
  • /etc/host.conf
  • `/etc/hosts.equiv', `/etc/hosts.equiv'
  • `/etc/logingroup'
  • `/etc/named.boot'
  • `/etc/services'
  • `/etc/shells'
  • `/home'
  • `/tftpboot'
  • `/tmp'
  • `/tmp' under solaris
  • `/users'
  • `/var/adm/wtmpx', `/var/adm/wtmpx'
  • `/var/lp/logs/lpsched'
  • a

  • Absolute links
  • Access control, Access control
  • action sequence
  • Actions, order of
  • Adding defined classes
  • Adding new classes
  • AFS, AFS
  • allclasses variable
  • Andrew filesystem
  • Annulling entries when debugging
  • apropos setup
  • atime tidies
  • `auto_direct'
  • `auto_master'
  • Automatic backup
  • Automatic restoring of lost files
  • automount
  • automounter
  • b

  • Backing up filesystems
  • Backup of files in copy
  • Backup policy
  • Backup, automatic
  • Backup, users, Backup, users
  • Bad address error in solaris
  • Berkeley sendmail
  • Binary server
  • Binary server, matching
  • Binary servers and links, Binary servers and links
  • Binary servers, declaring
  • Binary servers, defining
  • Binary servers, priority, Binary servers, priority
  • binserver variable and actionsequence
  • Bootstrap file
  • Broadcast address
  • Broadcast with solaris 2.4
  • Bugs, reporting
  • c

  • Cache files, netscape
  • catman
  • cf.preconf bootstrap file
  • `cf.site'
  • CFALLCLASSES, CFALLCLASSES
  • cfbackup
  • cfengine model
  • cfengine model, how it works
  • Cfengine security worries
  • cfengine, crontabs
  • cfengine, installs itself
  • cfengine, starting
  • `cfengine.conf', `cfengine.conf', `cfengine.conf'
  • CFINPUTS variable
  • cfrc resource file
  • cfrestore
  • cfwrap, wrapper script
  • Class data and scripts
  • Class decided by shell command
  • Class dependencies
  • Class information, passing to scripts
  • Class, generic any
  • Classes
  • Classes based on shell commands
  • Classes, adding and defining
  • Classes, compound
  • Classes, defining and undefining
  • Comments
  • Compound classes
  • Config file, default name
  • Control files
  • control section
  • Controlling logfiles
  • Controlling the size of log files
  • copy, copy
  • Copying files
  • core files, caution!
  • cron
  • cron script to start cfengine
  • ctime tidies
  • d

  • Database, updating
  • Deadlock
  • Debugging, annulling entries
  • Default file
  • `defaultroute'
  • Defining a binary server
  • Defining a home server
  • Defining a mail server
  • Defining a mountable
  • Defining classes, Defining classes
  • Defining groups
  • Deleting directories
  • Deleting files
  • Deleting stale links, Deleting stale links
  • Dependencies
  • Device boundaries
  • Device boundaries and files
  • DFS, DFS
  • Directories, deleting
  • Directories, making
  • Directory Names, use of wildcards
  • Directory permissions
  • disable, problems with logging afterwards
  • disable, trimming log files
  • Disabling file types
  • Disabling files
  • Distributing files
  • Distributing user files, Distributing user files
  • DNS, DNS
  • domain
  • Domain name
  • DOMAIN OS
  • Dots in hostnames
  • Double quotes
  • e

  • Editing users login files
  • Emacs, lock files
  • Empty files
  • Environment variable CFALLCLASSES
  • Environment variable, CFINPUTS
  • Environment variables, Environment variables
  • Example configuration files
  • Exceptions
  • Excluding actions in a controlled way
  • Excluding classes
  • Exporting filesystems, Exporting filesystems
  • exports, fixing
  • f

  • FAQs
  • File images (copy)
  • File management
  • File search paths
  • Files, breaking up into several
  • Files, checking permissions, Files, checking permissions
  • Files, configuration
  • Files, control
  • Files, home wildcard
  • Files, importing
  • Files, ownership
  • Files, recursion
  • Files, setting owner
  • Files, syntax
  • find database
  • Finger protocol, GNU
  • Flex and bison problem
  • Force copying
  • Fork error in solaris
  • Format
  • Free format
  • FreeBSD mount problem
  • Frequently asked questions
  • Frozen configuration files
  • ftp and alternative shells
  • ftp login problems
  • ftp server, solaris
  • Fully qualified names
  • g

  • GNU finger protocol
  • Group dependencies
  • Groups, defining
  • Groups, HPUX
  • h

  • Hard class name collision
  • Hard links
  • Hardlinks
  • Help
  • Help scripts
  • Hints and Tips
  • home directive
  • Home directories and automount
  • Home path
  • Home server
  • Home servers, declaring
  • Home servers, defining
  • home wildcard
  • Homepattern variable.
  • Host name gets truncated
  • `host.conf'
  • Hostname collision
  • `hosts.equiv', `hosts.equiv'
  • Hour classes
  • How can I make cfengine distribute my `/etc/motd' file?
  • HPUX, groups
  • httpd problem with logging
  • Hung machine
  • i

  • ifconfig
  • Import files, variables in
  • Importing files
  • Internal classes, switching off
  • Internet address
  • Invoking cfengine
  • IP address
  • Iteration over lists, Iteration over lists
  • k

  • Kill processes not owned by root
  • Killing processes
  • l

  • lex and yacc problems
  • Linkchildren, Linkchildren
  • Linking to binservers
  • Links
  • Links and binary servers, Links and binary servers
  • Links, absolute, Links, absolute
  • Links, deleting stale, Links, deleting stale
  • Links, making
  • Links, multiple
  • Links, single
  • Links, traversing in searches, Links, traversing in searches
  • Linux, installing
  • Local disk space, make use of
  • Local startup file for solaris
  • Lock files, emacs
  • Log files
  • Log files, controlling the size of
  • Log files, rotating
  • Logfiles, preventing overflow
  • Logical NOT
  • Login files, editing for all users
  • `logingroup'
  • m

  • Macros
  • Mail agent
  • Mail from scripts
  • Mail server, defining
  • Making directories
  • Making links
  • Making paths
  • Making use of local disk space
  • Manual pages
  • Master files, updating from
  • Message of the day files
  • Miscellaneous mount operations
  • Monitoring important files
  • Mount paths
  • Mount points
  • Mounted filesystems
  • Mounting filesystems.
  • Moutable rescources, defining, Moutable rescources, defining
  • mtime tidies
  • Multiple links
  • Multiple package configuration
  • Musts in cfengine
  • n

  • Name collision
  • Name server
  • Nameserver
  • Naming convention
  • Negating classes
  • Negating entries from netgroups
  • Netgroups
  • netgroups
  • Netgroups and Linux
  • Netgroups, negating entries
  • netmask
  • Netmask
  • Netmask with solaris 2.4
  • Netscape cache files
  • network configuration
  • network interface
  • New systems, support for
  • NFS
  • nfs
  • NFS mount model and automounter
  • NFS mounted filesystems
  • NFS problems with DOMAIN OS
  • NFS resources
  • nfstype
  • NIS
  • NIS, netgroup support
  • NOT operator
  • `nsswitch.conf', standardizing
  • o

  • ones
  • Optional features in cfengine
  • Order of actions
  • Ownership of files
  • p

  • Package configuration, multiple
  • Path to home directories
  • Path to input files
  • Path to mounted filesystems
  • Paths, making
  • Patterns
  • Permissions, directories
  • Processes, 0 to 3
  • Processes, check if running
  • Processes, checking existence of
  • Processes, counting
  • Processes, killing
  • Processes, signalling
  • Program format
  • Program structure
  • q

  • Quotas, soft, Quotas, soft
  • Quoted strings
  • Quoting strings
  • r

  • `rc.local' in solaris
  • rdate
  • Recursion in files
  • Relative links
  • Removing directories
  • Removing entries from netgroups
  • Renaming files
  • Replacing file by link
  • Reporting bugs
  • Repository filenames, changing
  • resolv.conf
  • Resolver configuration
  • Resource file
  • Restricting access
  • Restricting the size of files to be edited
  • Retrieving backed-up files
  • rmdirs
  • Rotating files
  • Rotating log files
  • routed
  • Running cfengine, cron script
  • s

  • Scripts and class information
  • Scripts, examples
  • Scripts, passing classes to
  • Scripts, writing
  • Searching for home directories
  • Sections, order of
  • Security under solaris
  • Security with NIS, Security with NIS
  • Segmentation fault in files and dirs
  • sendmail, sendmail
  • `sendmail.fc'
  • Sensible file sizes
  • Sensible limits on files in a directory
  • Services
  • setgid root log
  • setuid root log
  • Setuid scripts
  • Several files
  • sharing filesystems
  • Shell command to decide class
  • Shell commands which define classes
  • Single links
  • Single quotes
  • site
  • Software quotas
  • Solaris and tcsh
  • Solaris routing bug
  • Solaris, `/tmp'
  • Solaris, ftp server
  • Solaris, security
  • Special variables
  • split, split
  • Starting cfengine
  • Starting cfengine, cron script
  • Strings
  • Structure of a program
  • Subnet mask
  • Support for new systems
  • Switching off backup in copy
  • Switching off internal classes
  • Symbolic links, absolute
  • Symbolic links, relative
  • sysadm
  • System administrator, name
  • t

  • tcsh and solaris
  • Tidy by ctime, mtime, atime
  • Tidy, user areas
  • Tidying empty files
  • Tidying files, Tidying files
  • Time classes, hours
  • Time, setting
  • Tips using cfengine
  • Touching files
  • Truncating log files
  • u

  • underscoreclasses
  • Unmounting filesystems
  • Updating files from master source
  • User backups
  • User programs which define classes
  • Users, tidying
  • v

  • Variable substitution
  • Variables and Macros
  • Variables in import files
  • Variables, cfengine
  • Variables, cfengine model
  • Variables, environment
  • Variables, using
  • Verifying with -n option
  • w

  • Wildcard home
  • Wildcard, any class
  • Wildcards
  • Wildcards in homepattern
  • Wildcards, in directory names
  • Wrapper script
  • wtmpx
  • `wtmpx'
  • WWW log files, controlling
  • WWW protection
  • WWW server logs
  • x

  • X-terminals
  • X11 server data in `/tmp'
  • xdev (File system boundaries)
  • y

  • yacc problems
  • z

  • zeros
  • FAQ Index

    /

  • `/etc/groups' in linux
  • `/etc/system' missing in solaris.
  • a

  • Action contains invalid statement problem
  • b

  • Bad address error in solaris
  • c

  • Cfengine security worries
  • Changing repository name conventions
  • Configure multiple packages
  • d

  • Daily mail in FreeBSD
  • Define classes based on result of user program
  • Delete + in hosts.equiv
  • Difference between hpux and hpux10
  • Distribute key set up files to users
  • e

  • Edit all users login files
  • Edit and restart DNS
  • f

  • Fix exports in cfengine
  • Fixinf /tmp permissions in solaris
  • Fork: resource unavailable in solaris
  • FreeBSD daily mail
  • FreeBSD mount doesn't work
  • Frozen configuration files
  • ftp, can't log in
  • g

  • Groups in hpux
  • h

  • How can I make cfengine distribute my `/etc/motd' file?
  • How do I quote quotes?
  • How to keep all users in `/home'
  • i

  • Iterating over lists, Iterating over lists
  • k

  • Kill all processes except root
  • Kill user processes
  • Killing "eggdrop"
  • l

  • Lex and yacc in AIX
  • License server crashes
  • Line length bug in AIX
  • Linux segmentation fault with groups
  • m

  • Message of the day files
  • Message of the day in linux
  • motd in linux
  • Mount filesystems fails in solaris
  • p

  • Prevent big log-files
  • r

  • rc.local in solaris
  • Replace the stupid version of sendmail..?
  • Routing problem in solaris
  • s

  • Segmentation fault in files and dirs
  • Solaris /tmp sticky bit
  • split, using a space
  • Sticky bit in solaris /tmp
  • t

  • tcsh and solaris error
  • w

  • Why doesn't cfengine have classes for each hour..?

  • This document was generated on 5 November 1996 using the texi2html translator version 1.50.