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.
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
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".
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...
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.
Here we mention briefly some of the important issues in system administration which cfengine can help with.
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.
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.
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.
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!
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.
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.
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.
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:
name=( list )
are
used to assign the value on the right hand side to the name on the left hand side
of the equals sign.
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.
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 ...
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.
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.
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'.
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:
ultrix
, sun4
etc.
This is referred to henceforth as a hard class.
Monday Tuesday Wednesday..
).
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.
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.
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.
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.
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
site
$(faculty)
and may be used interchangeably.
binserver
host
fqhost
sysadm
timezone
control
.
domain
class
sun4
, hpux
).
arch
allclasses
repchar
split
underscoreclasses
spc
tab
lf
cr
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:
$(binserver)
variable is not always appropriate in this context. For instance
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.
option=$(variable)
.
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.
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.
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.
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 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.
[\]
or quoted with a backslash itself `\\'.
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
This chapter is about building strategies for putting together a site configuration for your entire network.
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.
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!
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.
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.
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.
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).
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
addmounts
, mountinfo
, mountall
from the actionsequence, in the control part
of your cfengine program,
editfiles
to edit the relevant coonfiguration files
such as `/etc/auto_master', `auto_direct' etc,
AutomountDirectResources
command in editfiles
to dump the list of cfengine class-based list of mountables
into a file of your choice in the correct format for
autmount's direct maps,
processes
See section processes to restart the autmounter
(send the hangup signal hup
), or perhaps stop and restart
the daemon by sending the term
signal (you should never
send the kill
signal),
files
or tidy
to
clean up stale links afterwards,
copy
to distribute basic automount configuration
files to multiple systems.
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.
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.
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
.
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
.
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.
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.
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.
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.
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:
$(allclasses)
to the script. This contains a
list of all classes in the form of a string
CFALLCLASSES=class1:class2:...This variable always contains an up to date list of the defined classes.
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.
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.
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)
.
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.
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 = ( 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 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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 = ( 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.
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
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
directories
directories
section of the program. It builds new directories.
disable
disable
section of the program.
editfiles
editfiles
section of the program.
files
files
section of the program.
links
links
section of the program.
mailcheck
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
addmounts
and
mailcheck
to be actually mounted. This should probably
be called both before mountinfo
and after addmounts
etc.
mountinfo
netconfig
required
required
section of the program. It checks for the absence of
important NFS resources.
resolve
shellcommands
shellcommands
section of the program.
tidy
tidy
section of the program.
unmount
unmount
section of the program. The filesystem table is edited
so as to remove the unwanted filesystems and the unmount
operation is executed.
processes
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.
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.
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.
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.
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.
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.
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
mode, owner, group
files
function
See section files.
action
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
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
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
exclude
type
linktype
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
.
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 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.
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.
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
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
BeginGroupIfNoLineMatching
and
BreakIfLineMatches
.
AppendIfNoSuchLine quoted-string
AppendIfNoLineMatching quoted-string
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
"-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
EndGroup
are executed if the quoted regular expression
does not match any line in the file.
BeginGroupIfNoMatch quoted-string
EndGroup
are executed if the quoted regular expression does
not match the current line.
BeginGroupIfNoSuchLine quoted-string
EndGroup
are executed if the quoted literal string
does not match any line in the file.
BreakIfLineMatches quoted-string
CommentLinesMatching quoted-string
SetCommentStart
and SetCommentEnd
to comment
out lines matching the given regular expression in quotes.
CommentLinesStarting quoted-string
SetCommentStart
and SetCommentEnd
to comment
out lines starting with the quoted literal string.
CommentNLines quoted-string
SetCommentStart
).
After the operation the pointer points to the line after the
commented lines.
CommentToLineMatching quoted-string
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
DeleteLinesMatching quoted-string
DeleteLinesStarting quoted-string
DeleteNLines quoted-string
DeleteToLineMatching quoted-string
EmptyEntireFilePlease
EndGroup
FixEndOfLine
GotoLastLine
HashCommentLinesContaining quoted-string
HashCommentLinesMatching quoted-string
HashCommentLinesStarting quoted-string
IncrementPointer quoted-string
InsertFile quoted-string
InsertLine quoted-string
LocateLineMatching quoted-string
WarnIfNoLineMatching
so that you can get an explicit warning, even
out of verbose mode.
PercentCommentLinesContaining quoted-string
PercentCommentLinesMatching quoted-string
PercentCommentLinesStarting quoted-string
Prepend quoted-string
BeginGroupIfNoLineMatching
and
BreakIfLineMatches
.
PrependIfNoLineMatching quoted-string
PrependIfNoSuchLine
with uses a regular expression instead of a literal string.
The string prepended is the one set using SetLine
.
PrependIfNoSuchLine quoted-string
ReplaceLineWith quoted-string
ReplaceAll quoted-string With quoted-string
ResetSearch quoted-string
RunScript quoted-string
RunScriptIfLineMatching quoted-string
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
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
CommentLineMatching
and CommentLineStarting
. The default is the hash
symbol `#' followed by a single space.
SetCommentEnd quoted-string
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
AppendIfNoLineMatching
using a regular expression.
SetScript quoted-string
SlashCommentLinesContaining quoted-string
SlashCommentLinesMatching quoted-string
SlashCommentLinesStarting quoted-string
UnCommentLinesContaining quoted-string
UnCommentLinesMatching quoted-string
UnCommentNLines quoted-string
UnCommentNLines "3"
would
uncomment
/* 1 */ /* 2 */ /* 3 */and also
/* 1 2 3 */
UnsetAbort quoted-string
AbortAtLineMatching
.
WarnIfLineContaining quoted-string
WarnIfLineMatching quoted-string
WarnIfLineStarting quoted-string
WarnIfNoLineContaining quoted-string
WarnIfNoLineStarting quoted-string
WarnIfNoSuchLine quoted-string
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.
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.
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
home
may also be
used. See section home
directive.
mode=modestring
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
inf
then cfengine opens all subdirectories and files beginning
from the specified filename.See section Recursion.
owner=owner list
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
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
warnall warndirs warnplain fixall fixdirs fixplain touch linkchildrenThe 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
-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.
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)
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.
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.
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
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
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.
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.
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.
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.
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
.
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
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
copytype=checksum/ctime
exclude=pattern
recurse=number/inf
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.
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.
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'.
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.
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'.
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.
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.
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.
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
.
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
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<OSTOP) 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 2Note 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"
matches=number
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
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.
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
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.
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.
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
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
recurse=number/inf
inf
then cfengine opens all subdirectories and files beginning
from the specified filename.See section Recursion.
age=days
links=stop/traverse/tidy
-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
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
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
rmdirs=true/false
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.
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.
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.
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
#!/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.
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
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.
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):" }
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
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.
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.
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.
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:
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.
cfengine -p -d2This 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.
-D
or -N
options.
links: /usr/lib/sendmail ->! /local/mail/bin/sendmail /etc/sendmail.cf ->! /local/mail/etc/sendmail.cf
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=1An 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.
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" }
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
processes: # # Most users # "eggdrop" signal=kill # # One wise-guy has renamed the daemon! # ".*wiseguy.*myegg.*" signal=kill
processes: # # BSD - often need long descriptove lines # to find this daemon # SetOptionString "-ax" # Exactly one should be running "lmgrd" matches=1
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)"
editfiles: { home/.cshrc # Local fixes AppendIfNoSuchLine "alias lp special-print-command" # Security DeleteLinesMatching "xhost +" }
processes: "\(root\)\{0\}" signal=term # or kill
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@gnu.ai.mit.edu
.
hpux10
rather than
hpux
.
disable: hpux:: /usr/lib/sendmail.fc
links: hpux:: /etc/logingroup -> /etc/group
bug-cfengine@gnu.ai.mit.edu
.
editfiles: linux:: { /etc/rc.d/rc.S HashCommentLinesContaining "motd" }
bug-cfengine@gnu.ai.mit.edu
.
editfiles: sun4:: { /etc/hosts.equiv DeleteLinesMatching "+" }
editfiles: sun4:: { /etc/hosts.equiv DeleteLinesMatching "+" }
# # 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-xdmNow 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.
files: /tmp mode=1777 action=fixdirs
editfiles: { /etc/shells AppendIfNoSuchLine "/local/bin/tcsh" }
tcsh
prints an error message on startup and will not read my
`.cshrc' file.
disable: /etc/.login type=fileYou might want to replace this with a link to your own file.
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.
files: /etc/system o=root g=root m=0644 action=touch
freebsd:: { /etc/crontab HashCommentLinesContaining "daily" HashCommentLinesContaining "weekly" HashCommentLinesContaining "monthly" }
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.
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");
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 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"
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.
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'.
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 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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.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 # ###
Note that GNU long options are available with the syntax
--longoption
. The long names are given in brackets.
--sysadm
) Print only the name of the system administrator then quit.
--no-check-files
) Do not check file systems for ownership / permissions etc.
--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.
--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.
--define
) Define a compound class symbol of the form
alpha.beta.gamma.
--no-edits
) Suppress file editing.
--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.
--file
) Parse filename after this switch. By default cfengine
looks for a file called cfengine.conf in the current directory.
--help
) Help information. Display version banner and options
summary.
--no-hard-classes
). Prevents cfengine from generating any internal
class name information. Can be used for emulation purposes.
--no-ifconfig
) Do not attempt to configure the local area
network interface.
--no-copy
) Do not copy/image any files.
--traverse-links
) Normally cfengine does not follow symbolic
links when recursively parsing directories. This option will force it
to do so.
--delete-stale-links
) Delete links which do not point to
existing files (except in user home directories, which are not touched).
--no-mount
) Do not attempt to mount file systems or edit the
filesystem table.
--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.
--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.
--no-commands
) Do not execute scripts or shell commands.
--silent
) Silence run time warnings.
--no-tidy
) Do not tidy file systems.
--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.
--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.
--verbose
) Verbose mode. Prints detailed information about
actions and state.
--version
) Print only the version string and then quit.
--no-preconf
) Do not execute the `cf.preconf' net
configuration file.
--no-links
) Do not execute the links
section of a
program.
--no-warn
,--quiet
) Do not print warning
messages.
-L
option
This document was generated on 5 November 1996 using the texi2html translator version 1.50.