|
Softpanorama |
May the source be with you, but remember the KISS principle ;-)
Softpanorama Search
|
| News | Recommended Links | Tutorials | TBV | Recommended Papers | Configuration Management |
| Baseliners | cfengine (Unix conf. management) |
puppet | etch | Cons (Perl-based make replacement) |
IBM Remote System Management Tool |
| HP SRC | CMDB | Webmin |
CVS (Software development) |
TCM | MValent |
| dconf | GNU cfengine | radmin | etch | Humor | Etc |
Unix configuration management is far from being a new topic. But it is a very difficult one and few of many many proposed systems successes in lessening the load on sysadmin.
I do not believe that cfengine is the right solution to the problem. And I am not alone (Introduction – etch) :
In either cfengine or puppet you have a maze of classes, controls, modules, resources, etc. Where you store your configuration within your cfengine or puppet tree has no obvious correlation to where it ends up on your clients. You can and will spend hours, quite possibly days, studying manuals and searching the web just to get the simplest initial setup.
... cfengine doesn't actually support doing much that is useful. So you end up using it as a framework for a bunch of little shell scripts you hack together. Puppet is somewhat better, but still lacking.
|
|||||||
Mark Burgess Centre of Science and Technology, Faculty of Engineering, Oslo College, NorwayChapter 2 Configuration Synchronization
Managing the configuration and configuration drift of a set of distributed systems is a constant challenge for system administrators. There are a variety of tools available to help manage various aspects of multi-system configuration management. For example, for account management, standard solutions include the Network Information System (NIS) and Lightweight Directory Access Protocol (LDAP). For file level synchronization, tools like rdist (see the rdist(1) manpage) and rsync are available. HP Systems Insight Manager helps to discover, monitor and manage groups of systems.
A new tool in this toolkit is Configuration Engine (cfengine). cfengine is a popular open source tool for configuration synchronization. It allows policy-based or goal-based configuration management that allows the administrator to define the management actions to be applied to groups of systems so those systems reach a desired state.
cfengine is a client/server based tool. A central configuration master system or policy server hosts a configuration policy file which defines the management actions to be performed on each managed client.
The configuration master also hosts the “golden image” files, or reference copies of files that should be distributed to the clients. The administrator can use cfengine to perform tasks such as:
Ensure that client systems are using a correct set of configuration files by copying over reference files or directories.
Disable inappropriately configured files on the client.
Check file permissions, ownership, and track checksum changes.
Edit files.
Execute specified shell commands on each client.
Check for processes or signal processes.
Check for specific filesystem mounts.
A Configuration Synchronization Wizard (csync_wizard) is available to help the administrator quickly configure cfengine for managing a set of distributed systems or configuring it as a highly available service in a Serviceguard cluster.
Edition 4.2 for version 1.4.x
Copyright (C) 1995/96/97 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 4.2 for version 1.4.x as last updated July 1997.
Cfengine is the result of a continuing research project to help solve the problems of system administration in a big network. Cfengine is an expert system combined with a declarative language and a workhorse-robot.
Many people have contributed their experiences and wisdom to the cfengine project. I apologise for not being able to mention everyone here. Morten Hanshaugen and Hans Petter Holen made it possible to test cfengine on a variety of systems at the university of Oslo. I am grateful to Knut Borge for his experience and suggestions on many occaision. Ola Borrebaek and Richard Stallman have made key observations which have influenced the development of cfengine in important ways. Audun Tornquist did some initial work on the `copy' feature and donated the backup help-script to the distribution. Gord Matzigkeit contributed an early autoconf setup. 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 definitive system administration tool (before 1.4.0!) has been invaluable. David Masterson continues to provide me with the results of detailed tests and new auto configuration improvements. Brian White maintains the Debian linux package and has been helpful with bug reports. Rolf Ebert contributed the emacs cfengine mode file. I am grateful to Ann-Mari Torvatn and Len Tower for reading through and helping to improve the documentation. Finally, Demosthenes Skipitaris and I added the new adaptive locks to cfengine 1.4.0.
For up to the minute information on cfengine, workshops, conferences and all that jazz see the web page:
http://www.iu.hioslo.no/cfengine.html
Bug reports and queries by mail to
bug-cfengine@prep.ai.mit.edu
(why not bug Jack Barron instead?) Two newsgroups are also available now for discussions and bug reports. The newsgroup gnu.cfengine.bug (with corresponding mailing list bug-cfengine@prep.ai.mit.edu) is for bug reports, and the group gnu.cfengine.help (with mailing list help-cfengine@prep.ai.mit.edu) is for general requests for help and user discussion.
Mark Burgess Oslo, 1997
--ST: The ultimate computer
--Cartoon from OMNI magazine, early 1980's
Tutorial section
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 tool for setting up and maintaining BSD and System-5-like operating
system optionally 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. Cfengine
is good at performing a lot of common system administration tasks, and allows you
to build on its strengths with your own scripts. You can also use it as a netwide
front-end for cron. Once you have set up cfengine, you'll be free to
use your time being like a human being, instead of playing R2-D2 with the system.
The main purpose of cfengine is to allow you to create a single, central system configuration which will define how every host on your network should be configured in an intuitive way. An interpreter runs on every host on your network and parses the master file (or file-set); the configuration of each host is checked against this file and then, if you request it, any deviations from the defined configuration are fixed automatically. You do not have to mention every host specifically by name in order to configure them : instead you can refer to the properties which distinguish hosts from one another. Cfengine uses a flexible system of "classes" which helps you to single out a specific group of hosts with a single statement.
Originally cfengine was conceived of as a tool only for the superuser, but during the course of its development it has become clear that it can 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 familiar with system administration and would like a more gentle introduction, then we begin here...
To the system administrator of a small network, with 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. After all, it's 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... Also, you 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 re-installation. 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 in which 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.
Some of the important issues in system administration which cfengine can help with.
One of the endearing 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 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 connect 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 text-editing 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.
Some sites prefer to minimize the use of NFS filesystems, to avoid one machine
being dependent on another. They prefer to make a local copy of the files on a remote
machine instead. Traditionally programs like rdist have been used for
this purpose. You may also use cfengine to copy files in this way, See section
Remote file distribution.
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 text-file `/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 automatically without having to do any editing yourself!
Security is an important issue on any system. In the busy life of a system administrator 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 user-name 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, say,
`/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 can run it as a cron job, or you can run it manually.
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 file-base, then the natural thing is to run cfengine daily
with the help of cron. See section
cfwrap, but also, See section
cfrun.
A cfengine configuration file for a large network can become 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 familiar 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 coordinate 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 definition 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) causes cfengine to look for a file called
`cfengine.conf' in the current directory and execute it 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 familiar
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 specify the name of the file as simple `.' then
cfengine reads its input from the standard input.
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'.
If you are a beginner to cfengine, you might not be certain exactly how you want to use it. Here are some hints from Dr. Daystrom about how to get things working quickly.
cron.
Running cfengine from cron means that it will be run in parallel on your
systems. Cfengine on one host does not have to wait for cfengine on another
host to complete. cfd on all your systems so that cfengine can be executed
remotely, so that you can immediately "push" changes to all your hosts with
cfrun. Think carefully about whom you wish to give permission to
run cfengine from the net, See section Configuring
cfd. Set up you `cfd.conf' file accordingly. You can
also use this daemon to grant access rights for remote file copying. Cfrun
polls all your hosts serially and gives you a concatenated indexed list of problems
on all hosts. The disadvantage with cfrun is that each host has to wait its
turn. cfd to the system startup scripts, or to
`inittab' so that it starts when you boot your system. When you have set up these components, you can sit back and edit the configuration files and watch things being done.
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. You can use round parentheses in cfengine classes to override these preferences.
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, irix4, irix, irix64
freebsd, netbsd, bsd4_3, newsos, solarisx86, aos,
nextstep, bsdos, linux, debian, cray
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 `parse-only-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.
Cfengine uses both the unqualified and fully 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 domain 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. The dots
in this string will be replaced by underscores.
In summary, the operator ordering in cfengine classes is as follows:
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 n dollar quote '. dblquote " 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 preceding 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.
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 synchronize 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" )
Note that as of version 1.4.0 of cfengine, you may use the word classes
as an alias for groups. 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.
anyThe 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.
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.
Note: if you are running cfengine via the cfrun program
then cfengine is always started with the same user identity
as the cfd process on the remote host. Normally this is the
root user identity. This means that the access keyword will have no effect on the
use of the command cfrun.
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.
File sweeps are searches through a directory tree in which many files are examined and considered for processing in some way. There are many instances where one uses cfengine to perform a file sweep.
files action, for checking access rights and ownership
of files. tidy action, for checking files for deletion.
copy action, while recursively checking whether
to copy a file tree. The problem with file sweeps is that they can be too sweeping! Often you are not interested in examining every single file in a file tree. You might wish to perform a search
The tidy action is slightly different in this respect, since it always expects
to match a specific pattern. One is generally not interested in a search which deletes
everything except for a named pattern: this would be too dangerous. For this reason,
the syntax of tidy is different and is documented in the section on
tidying, See section tidy.
For file sweeps within files and copy you can specify
specific search parameters using the keywords include= and exclude=.
For example,
files: /usr/local/bin m=0755 exclude=*.ps action=fixall
In this example cfengine searches the entire file tree (omitting any directories listed in the ignore-list, See section ignore) and omitting any files ending in the extension `.ps'.
Specifying the include= keyword is slightly different since it automatically
restricts the search to only named patterns, whenever you have one or more instances
of it. If you include patterns in this way, cfengine ignores any files which do
not match the given patterns. It also ignores any patterns which you have specified
in the global ignore-list as well as patterns excluded with exclude=pattern.
In other words, exlcusions always override inclusions.
If you exclude a pattern or a directory and wish to treat it in some special way, you need to code an explicit check for that pattern as a separate entity. For example, to handle the exluded `.ps' files above, you would need to code something like this:
files: /usr/local/bin m=0644 include=*.ps action=fixall
Note: don't be tempted to enclose your wildcards in quotes. The quotes will be treated literally and the pattern might not match the way you would expect.
Cfengine keeps two kinds of log-file. 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/cfengine.log. The file is not readable for general users.
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 conflict with the substring.
Note that you can use special variables for certain symbols in a string See section Variable substitution.
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
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.
This chapter is about building strategies for putting together a site configuration for your entire network.
In order to use any system administration tool successfully, you have to make peace with your system by deciding exactly what you expect and what you are willing to do to achieve the results. You need to decide what you will consider to be acceptable and what is to be considered completely untenable. You need to make these decisions because otherwise you will only be confused later when things don't go the way you expected.
Experience shows that the most successful policies for automatation are to keep everything as simple as possible. The more uniform or alike your machines are, the easier they are to run and the happier users are. Sometimes people claim that they need such great flexibility that all their machines should be different. This belief tends to be inversely proportional to the number of machines they run and generally only applies to very special development environments! Usually you will only need one or to machines to be special and most can be made very similar.
Site configuration is about sharing and controlling resources. The resources include disks (filespace), files, data, programs, passwords and physical machines. Before planning your sitewide configuration you should spend some time deciding how you would like things to work.
In the remaining parts of this chapter, you will find som hints and tips about how to proceed, but remember that when push comes to shove, you must make your own choices.
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 criteria can be
`fixed' --i.e. automatically set to the correct permissions, or can simply be brought
to the attention of the system administrator 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 file-mode (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 criteria 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 combat 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 hard-disks at Oslo with hundreds of megabytes of WWW files. In addition the regular appearance of `core' files(1) 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 pattern=cache????* r=inf 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 irretrievably 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.
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.
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.
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 resources are architecture or
hard-class 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. If you follow this scheme, exactly as described here, you will never have any problems with mount points. We shall describe the scheme in detail below. Here are some points to digest:
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@physics
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 operating 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 hard-class 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 substitutes 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 automounter is designed to be used together with a global configuration file, distributed by NIS (the network information service). 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 automounter 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 configuration files such
as `/etc/auto_master', or `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 automounter (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 automounter 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 automounter 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 name-space 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 annoying 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 automounter can be confused by manually mounting or unmounting filesystems while it is running.
A very convenient characteristic 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 flexibility. Fortunately most editing operations involve appending a few lines to a file, commenting out certain lines or deleting lines.
For example, some administrators consider the finger service to be a threat to security and want to disable it. This could be done as follows.
editfiles:
{ /etc/inetd.conf
HashCommentLinesContaining "finger"
}
Commands containing the word `Comment' are used to `comment out' certain lines from a text-file--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 also 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. You can use the editing feature to update and distribute the message of the day file, or to configure sendmail, See section FAQs and Tips.
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.
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.
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 `.cfdisabled' 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.
In the previous two sections we have looked at how to rotate old log files and how to execute shell commands. If you keep a lot of old log files around on your system, you might want to compress them so that they don't take up so much space. You can do this with a shell command. The example below looks for files matching a shell wildcard. Names of the form `file.1', `file.2'...`file.10' will match this wildcard and the compression program sees that they get compressed. The output is dumped to avoid spurious messages.
shellcommands: "$(gnu)/gzip /var/log/*.[0-9] /var/log/*.[0-9][0-9] > /dev/null 2>&1"
Cfengine will also recognize rotated files if they have been compressed, with suffixes `.Z', `.gz', `.rbz' or `.rbz'.
cronOne of cfengine's strengths is its use of classes to identify systems from a
single file or set of files. Many administrators think that it would be nice if
the cron daemon also worked in this way. One possible way of setting up cron from
a global configuration would be to use the cfengine editfiles facility
to edit each cron file separately. A much better way is to use cfengine's time classes
to work like a user interface for cron. This allows you to have a single, central
cfengine file which contains all the cron jobs on your system without losing any
of the fine control which cron affords you. All of the usual advantages apply:
The central idea behind this scheme is to set up a regular cron job on every
system which executes cfengine at frequent intervals. Each time cfengine is started,
it evaluates time classes and executes the shell commands defined in its configuration
file. In this way we use cfengine as a wrapper for the cron scripts, so that we
can use cfengine's classes to control jobs for mulitple hosts. Cfengine's time classes
are at least as powerful as cron's time specification possibilities,
so this does not restrict you in any way, See section
Building flexible time classes. The only price
is the overhead of parsing the cfengine configuration file.
To be more concrete, imagine installing the following `crontab' file onto every host on your network:
# # Global Cron file # 0,15,30,45 * * * * /usr/local/cfengine/inputs/run-cfengine
This file contains just a single cron job, namely a script which calls cfengine. Here we are assuming that you will not want to execute any cron script more often than every fifteen minutes. If this is too restrictive, the above can be changed. We refer to the time interval between runs of the script `run-cfengine' as the `scheduling interval' and discuss its implications in more detail below.
The script `run-cfengine' would replace any `cfdaily' or `cfhourly' scripts which you might have, and can as simple as this
#!/bin/sh # # Script run-cfengine export CFINPUTS=/usr/local/cfengine/inputs /usr/local/gnu/bin/cfengine # # Should we pipe mail to a special user? #
or it could be more fancy. You could also use the `cfwrap' script, See section cfwrap, if you have perl on all your systems, to pipe mail to the mail address described in the cfengine file, See section sysadm.
# # Global Cron file # 0,15,30,45 * * * * path/cfwrap path/run-cfengine
You might not want to run your entire system configuration `cfengine.conf' every time cron fires up cfengine. An alternative would be to keep a separate fil for cron jobs called, say, `cf.cron'. You would then replace the `run-cfengine' file by
#!/bin/sh # # Script run-cfengine export CFINPUTS=/usr/local/cfengine/inputs /usr/local/gnu/bin/cfengine -f cf.cron # # Should we pipe mail to a special user? #
There is no particular advantage to doing this unless you are running cfengine on some very slow hardware. A better way to approach the problem is to think of the `cf.cron' file as a module which can be imported into the main configuration file. This gives you the maximum amount of flexibilty, since it allows you to decide exactly what you want to happen any any given time from the central file.
The structure of `cfengine.conf' needs to reflect your policy for running jobs on the system. You need to switch on relevant tasks and switch off unwanted tasks depending on the time of day. This can be done in three ways:
action:
Hr00.Min10_15||Hr12.Min45_55::
Command
actionsequence depending on the time
of day.
control:
Hr00:: # Action-sequence for daily run at midnight
actionsequence = ( sequence )
!Hr00:: # Action-sequence otherwise
actionsequence = ( sequence )
import:
Hr00:: cf.dailyjobs
any:: cf.hourlyjobs
The last of these is the most efficient of the three, since cfengine does not even have to spend time parsing the files for actions which you know you will not want.
The trouble with starting every cfengine at the same time using a global cron file is that it might lead to contention or inefficiency. For instance, if a hundred cfengines all suddenly wanted to copy a file from a master source simultaneously this would lead to a big load on the server. We can prevent this from happening by introducing a time delay which is unique for each host and not longer than some given interval. Cfengine uses a hashing algorithm to generate a number between zero and a maximum value in minutes which you define, like this:
control:
SplayTime = ( 60 ) # minutes
If this number is non-zero, cfengine goes to sleep after parsing its configuration file and reading the clock. Every machine will go to sleep for a different length of time, which is no longer than the time you specify in minutes. A hashing algorithm, based on the fully qualified name of the host, is used to compute a unique time for hosts. The shorter the interval, the more clustered the hosts will be. The longer the interval, the lighter the load on your servers. This `splaying' of the run times will lighten the load on servers, even if they come from domains not under your control but have a similar cron policy.
Splaying can be switched off temporarily with the `-q' or `--no-splay' options.
Each time cfengine is run, it reads the system clock and defines the following classes based on the time and date:
Yrxx:: Month:: Day:: Dayxx:: Hrxx:: Minxx:: Minxx_xx:: Time classes based on the precise minute at which cfengine started are unlikely
to be useful, since it is improbable that you will want to ask cron to run cfengine
every single minute of every day: there would be no time for anything to complete
before it was started again. Moreover, many things could conspire to delay the precise
time at which cfengine were started. The real purpose in being able to detect the
precise start time is to define composite classes which refer to arbitrary intervals
of time. To do this, we use the group or classes action
to create an alias for a group of time values.
Here are some creative examples:
classes: # synonym groups: LunchAndTeaBreaks = ( Hr12 Hr10 Hr15 ) NightShift = ( Hr22 Hr23 Hr00 Hr01 Hr02 Hr03 Hr04 Hr05 Hr06 ) ConferenceDays = ( Day26 Day27 Day29 Day30 ) QuarterHours = ( Min00 Min15 Min30 Min45 ) TimeSlices = ( Min01 Min02 Min03 Min33 Min34 Min35)
In these examples, the left hand sides of the assignments are effectively the ORed result of the right hand side. This if any classes in the parentheses are defined, the left hand side class will become defined. This provides an excellent and readable way of pinpointing intervals of time within a program, without having to use `|' and `.' operators everywhere.
How often should you call your global cron script? There are several things to think about:
Cfengine has an intelligent locking and timeout policy which should be sufficient to handle hanging shell commands from previous crons so that no overlap can take place, See section Spamming and security.
This chapter describes how you can set up a cfengine network service to handle
remote file distribution and remote execution of cfengine without having to open
your hosts to possible attack using the rsh protocols.
By starting the daemon called cfd, you can set up a line of communication
between hosts, allowing them to exchange files across the network or execute cfengine
remotely on another system.
Cfengine network services are built around the following components:
cfengine cfd cfrun cfwatch With these components you can emulate programs like rdist
whose job it is to
check and maintain copies of files on client machines. You may also decide who has
permission to run cfengine and how often it may be run, without giving away any
special user privileges.
This section describes how you can set up cfd as a remote file server
which can result in the distrubution of files to client hosts in a more democratic
way than with programs like rdist.
An important difference between cfengine and other systems has to do with the
way files are distributed. Cfengine uses a `pull' rather than a `push' model for
distributing network files. The rdist command, for instance, works
by forcing an image of the files on one server machine onto all clients. Files get
changed when the server wishes it and the clients have no choice but to live with
the consequences. Cfengine cannot force its will onto other hosts in this way, it
can only signal them and ask them to collect files if they want to. In other words,
cfengine simulates a `push' model by polling each client and running the local cfengine
configuration script giving the host the chance to `pull' any updated files from
the remote server, but leaving it up to the client machine to decide whether or
not it wants to update.
Also, in contrast to programs like rdist which distribute files
over many hosts, cfengine does not require any general root access
to a system using the `.rhosts' file or the `/etc/hosts.equiv'
file. It is sufficient to run the daemon as root or add it to the `/etc/inetd.conf'
file on your system. The restricted functionality of the daemon
protects your system from attempts to execute general commands as the root user
using rsh.
To remotely access files on a server, you add the keywork server=host
to a copy command. Consider the following example which illustrates
how you might distribute a password file from a masterhost to some clients.
copy:
PasswdClients::
/etc/passwd dest=/etc/passwd owner=root group=0 server=server-host
Given that the cfd daemon is running on server-host,
cfengine will make contact with the daemon and attempt to obtain information about
the file. During this process, cfengine verifies that the system clocks of the two
hosts are reasonably synchronized. If they are not, it will not permit remote copying.
If cfengine determines that a file needs to be updated from
a remote server it begins copying the remote file to a new file on the same filesystem
as the destination-file. This file has the suffix `.cfnew'.
Only when the file has been successfully collected will cfengine
make a copy of the old file, See section repository
and rename the new file into place. This behaviour is designed to avoid race-conditions
which can occur during network connections and indeed any operations which take
some time. If files were simply copied directly to their new destinations it is
conceivable that a network error could interrupt the transfer leaving a corrupted
file in place.
Cfengine places a timeout of a few seconds on network connections to avoid hanging processes.
Normally the daemon sleeps, waiting for connections from the network. Such a
connection may be initiated by a request for remote files from a running cfengine
program on another host, or it might be initiated by the program cfrun
which simply asks the host running the daemon to run the cfengine
program locally.
Make sure that you are running cfengine from a shell which has sensible limits set. The error `too many open files' can occur in long recursions if you only have a small number of valid descriptors per shell. It is probably a good idea to set the number of descriptors to 1024.
It is a good idea to execute cfengine by getting cron
to run it regularly. This ensures that cfengine will be run even if you are unable
to log onto a host to run it yourself. Sometimes however you will want to run cfengine
immediately in order to implement a change in configuration as quickly as possible.
It would then be inconvenient to have to log onto every host in order to do this
manually. A better way would be to issue a simple command which contacted a remote
host and ran cfengine, printing the output on your own screen:
myhost% cfrun remote-host -v output....
A simple user interface is provided to accomplish this. cfrun makes
a connection to a remote cfd-daemon
and executes cfengine on that system with the privileges of
the cfd-daemon (usually root). This has a two advantages:
A potential disadvantage with such a system is that malicious users might be able to run cfengine on remote hosts. The fact that non-root users can execute cfengine is not a problem in itself, after all the most malicious thing they would be able to do would be to check the system configuration and repair any problems. No one can tell cfengine what to do using the cfrun program, it is only possible to run an existing configuration. But a more serious concern is that malicious users might try to run cfengine repeatedly (so-called `spamming') so that a system became burdened with running cfengine constantly, See section Spamming and security.
cfrunThe syntax of the cfrun command is
cfrun -option --longoption class1 class2 ...
With the exception of the `-d' and `-S' options, all options are passed on to the remote hosts and are ignored locally. The `-q' option is always assumed when executing cfengine remotely, so that SplayTime is effectively zero when polling hosts serially. If an option includes a name such as `-Dnewclass', there should not be a space between the option letter and the name string. The remaining options are treated as classes to be sent to all the hosts on the network.
Each host evaluates the classes sent by cfrun and decides whether
cfengine should be invoked. Only hosts which belong to the
classes defined on the cfrun command line are executed. This allows
you to single out groups of hosts which should execute cfengine, based on the very
classes which you have defined for your configuration. If no classes are sent on
the command line, then all hosts are run.
cfrun uses a configuration file which is located under the
CFINPUTS directory in order to determine which hosts and in which order it
should try to connect. Because cfengine always uses a reliable TCP protocol for
connections, it verifies each connection rather than simply broadcasting openly.
Using this file you can even simulate broadcasting to hosts outside your subnet.
This file should contain every host name you ever want to configure remotely,
because you can still select subsets of the file by specifying classes which the
remote host will understand. If the remote host is not in one of the classes you
specify when you run cfrun, then it will simply ignore the request.
Conversely, if you do not place a host in this file, it will never be contacted
when you use the cfrun command. The format of the file is as follows
# # Comment .. # domain=my.domain hostname1 options hostname2 options ...
It is important to add the domain-name to this file. The options you specifiy in this file, per host, are added to those you might specify on the command line when invoking cfengine remotely. For instance, you might know of a bug on one host and decide not to perform interface configuration on that one machine. You would write a line like this:
funny.domain -i # problem host
You could use cfrun inside
one of your cfengine configuration files in order to remotely execute cfengine on
all of the other network machines, by setting up a host list. Be careful not to
include the name of the master host in the list. The locks should prevent cfengine
from being run on the masterhost, avoiding an infinite loop. This way you do not
have to rely on cron running on every system. The disadvantage however is that cfengine
has to poll the systems on the network, which means that cfengine cannot be working
in parallel on all hosts. This could be inefficient in the long run.
The term `spamming' refers to the senseless repetition of something in a malicious way intended to drive someone crazy(5). In the computer world some malicious users, a bit like `flashers' in the park(6) like to run around the net a reveal themselves ad nauseum by sending multiple mail messages or making network connections repeatedly to try to overload systems and people(7).
Whenever we open a system to the network, this problem becomes a concern. Cfengine is a tool for making peace with networked systems, not a tool to be manipulated into acts of senseless aggression. The cfengine daemon does make it possible for anyone to connect and run a cfengine process however, so clearly some protection is required from such attacks.
Cfengine's solution to this problem is a locking mechanism. Rather than providing user-based control, cfengine uses a time based locking mechanism which prevents actions from being executed unless a certain minimum time has elapsed since the last time they were executed. By using a lock which is not based on user identity, we protect several interests in one go:
Cfengine is controlled by a series of locks which prevent it from being run too often, and which prevent it from spending too long trying to do its job. The locks work in such a way that you can start several cfengine processes simultaneously without them crashing into each other. Coexisting cfengine processes are also prevented from trying to do the same thing at the same time (we call this `spamming'). You can control two things about each kind of action in the action sequence:
You can set these values either globally (for all actions) or for each action separately. If you set global and local values, the local values override the global ones. All times are written in units of minutes.
actionsequence
(
action.IfElapsedtime-in-mins
action.ExpireAftertime-in-mins
)
or globally,
control:
IfElapsed = ( time-in-mins )
ExpireAfter = ( time-in-mins )
For example:
control:
actionsequence =
(
files.IfElapsed240.ExpireAfter180
copy
tidy
)
IfElapsed = ( 30 )
In this example, we treat the files action differently to the others. For all the other actions, cfengine will only execute the files part of the program in 30 minutes have elapsed since it was last run. Since no value is set, the expiry time for actions is 60 minutes, which means that any cfengine process which is still trying to finish up after 60 minutes will be killed automatically by the next cfengine which gets started.
As for the files action: this will only be run if 240 minutes (4 hours) have elapsed since the last run. Similarly, it will not be killed while processing `files' until after 180 minutes (3 hours) have passed.
These locks do not prevent the whole of cfengine from running, only so-called `atoms'. Several different atoms can be run concurrently by different cfengines. Assuming that the time conditions set above allow you to start cfengine, the locks ensure that atoms will never be started by two cfengines at the same time, causing contention and wasting CPU cycles. Atoms are defined to maximize the security of your system and to be efficient. If cfengine were to lock each file it looked at seperately, it would use a large amount of time processing the locks, so it doesn't do that. Instead, it groups things together like this:
copy, editfiles, shellcommands netconfig, resolve, umount, mailcheck, addmounts, disable, processes
mountall, mountinfo, required, checktimezone Cfengine creates a directory `~/.cfengine' for writing lock files for ordinary users.
The option `-K' or `--no-lock' can be used to switch
off the locking checks, but note that when running cfengine remotely via cfd,
this is not possible.
Cfd uses a form for host-based authorization. Each atomic operation , such as statting, getting files, reading directories etc, requires a new connection and each connection is verified by a double reverse lookup in the server's DNS records. Single stat structures are cached during the processing of a file.
MD5 checksums are transferred from client to server to avoid loading the server. Even if a user could corrupt the MD5 checksum, would have to get past access control with TCP wrappers and the worst that could happen would be to get the right version of the file. Again this is in keeping with the idea that users can only harm themselves and not others with cfengine.
Whenever we allow concurrent processes to share a resource, we open ourselves up the possibilty of deadlock. This is a situation where two or more processes are locked in a vicious stalemate from which none can escape. Another problem is that it might be possible to start an infinite loop: cfengine starts itself.
Cfengine protects you from such loops to a large degree. It should not be possible
to make such a loop by accident. The reason for this is the locking mechanism which
prevents tasks being repeated too often. If you start a cfengine process which contains
a shell-command to start cfengine again, this shell command will be locked, so it
will not be possible to run it a second time. So while you might be able to start
a second cfengine process, further processes will not be started and you will simply
have wasted a little CPU time. When the first cfengine returns, the tasks which
the second cfengine completed will not be repeated unless you have set the
IfElapsed time or the ExpireAfter time to zero.
In general, if you wish to avoid problems
like this, you should not disable the locking mechanism by setting these two times
to zero.
The possibility of deadlock arises in network connection. Cfengine will not attempt
to use the network to copy a file which can be copied internally from some machine
to itself. It will always replace the server= directive in a copy with
`localhost' to avoid unnecessary network connections.
The prevents one kind of deadlock which could occur: namely
cfrun executes cfengine on host A (cfd on host A is then blocked until this completes),
but the host A configuration file contains a remote copy from itself to itself.
This remote copy would then have to wait for cfd to unblock, but this would be impossible
since cfd cannot unblock until it has the file. By avoiding remote copies to localhost,
this possibility is avoided.
cfdcfdTo install the cfengine daemon component, you will need to register a port for cfengine by adding the following line to the system file `/etc/services file'
cfengine 5308/tcp
You could do this for all hosts by adding the following to your cfengine configuration
editfiles:
{ /etc/services
AppendIfNoSuchLine "cfengine 5308/tcp"
}
To start cfengine at boot time, you need to place a line of the following type in your system startup files:
# Start cfengine server cfd
Note that cfd will reread its configuration file whenever it detects
that it has been changed, so you should not have to restart the daemon, not send
it the HUP signal as with other daemons.
The server daemon is controlled by a file called `cfd.conf'. The syntax of this configuration file is deliberately modelled on cfengine's own configuration file, but despite the similarities, you cannot mix the contents of the two files.
Though they are not compatible, `cfengine.conf' and `cfd.conf' are similar in several ways:
CFINPUTS.
groups and import in both files to
break up files into convenient modules and to import common resources, such
as lists of groups. Note that the classes in the `cfd.conf' file do not tell you the classes of host which have access to files and dierctories, but rather which classes of host pay attention to the access and deny commands when the file is parsed.
Host name authentication is not by class or group but by hostname, like the `/etc/exports' file on most unix systems. The syntax fo the file is as follows:
control:
classes::
domain = ( DNS-domain-name )
cfrunCommand = ( "script/filename" ) # Quoted
MaxConnections = ( maximum number of forked daemons )
ChecksumDatabase = ( filename )
IfElapsed = ( time-in-minutes )
groups:
Group definitions
import:
Files to import
admit: | grant:
classes::
/file-or-directory
wildcards/hostnames
deny:
classes::
/file-or-directory
wildcards/hostnames
The file consists of a control section and access information. You may use the control section to define any variables which you want to use in the remainder of your file. Two variables are special here, they are reserved.
cfrunCommand cfrun command. MaxConnections IfElapsed IfElapsed anti-spamming filter is also built into
cfd so that a remote user cannot even get as far as causing cfengine
to parse its input files (which could be used for spamming in itself). The time
is in minutes, the default is one hour. ChecksumDatabase Following the control section comes a list of files or directories and hosts which may access these. If permissions are granted to a directory then all sub directories are automatically granted also. Note that symbolic links are not checked for, so you may need to specifically deny access to links if they are plain files, but cfd does not follow symbolic links and give access to files in other directories.
Fully qualified hostnames should be given in this file. Do not forget to define
the domain name. Authentication calls the unix function gethostbyname()
and so on to identify and verify connecting hosts, so the names in the file must
reflect the type on names returned by this function. You may use wildcards in names
to match, for instance, all hosts from a particular domain.
Here is an example file
#####################################################
#
# This is a cfd config file
#
#####################################################
groups:
PasswdHost = ( nexus )
#####################################################
control:
#
# Assuming CFINPUTS is defined
#
cfrunCommand = ( "/usr/local/bin/cfengine" )
variable = ( /usr/local/publicfiles )
#####################################################
admit: # Can also call this grant:
PasswdHost::
/etc/passwd
*.iu.hioslo.no
FtpHost::
# An alternative to ftp, grant anyone
/local/ftp/pub
*
any::
$CFINPUTS/cfrun.sh
*.iu.hioslo.no
#####################################################
deny:
/etc/services
borg.iu.hioslo.no
/local/ftp
*.pain-in-the-ass.com
NOTE I: cfd is not rpc.mountd, access control is by filename,
not by device name. Do not assume that files lying in subdirectories are not open
for access simply because they lie on a different device. You should give the real
path name to file and avoid symbolic links.
NOTE II: access control is per host, not per user. If you open a file for a host you open it for every user on that host.
If you still have problems with lack of access, it could be that you have forgotten to define the domain name for your network, or that you do not understand the TCP wrappers files `/etc/hosts.access' and `/etc/hosts.deny'.
Cfengine tries to incorporate the TCP wrappers package if you have it on your system. If you do, then the files `/etc/hosts.allow' and `/etc/hosts.deny' allow you to give the cfengine/cfd service an extra level of protection from `clever' spoofing attempts.
Cfengine is not specifically a tool for implementing high security solutions for system administration, but it has many features which can be used to monitor the state of your systems and warn about potential breaches in security.
files feature, it will make a note
of setuid programs it finds there. These are recorded in the file `cfengine.host.log'
which is stored under `/etc/cfengine' or `/var/log/cfengine'.
When new setuid
programs are discovered, a warning is printed, but only if you are root.
If you ever want a complete list, delete the log file and cfengine will think
that all of the setuid programs it finds are new. The log file is not readable
by normal users. files,
tidy, copy), it is on the lookout for for suspicious
filenames, i.e. files like `.. .' containing only space and/or dots.
Such files are never created by sensible people, but are often used by hackers
to try to hide dangerous programs. Cfengine prints warnings about such files.
cfd program attempts to unmask such attempts
by performing double reverse lookups in the name service. This verifies by a
trusted server that the socket address and the host name are really who they
claim to be. If you have the TCP wrappers package on your system (libwrap)
then cfd will attempt to use it to
detect other spoofs too, See section TCP wrappers.
If you don't have TCP wrappers, then the only line of defense is the double
reverse lookup. size= in copy Reference section
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 syntax of declarations here is:
control:
classes::
variable = ( list or value )
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.others
files
)
myvariable = ( something )
mymacro = ( somethingelse )
Parentheses are required when making a declaring information in cfengine.
The meaning of each of these lines is described below.
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.
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 (single and child)
simplelinks # check only single links (separate from childlinks)
childlinks # check only childlinks (separate from singlelinks)
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. Here one can also write singlelinks which checks only
single (not multiply linked) objects, or childlinks which checks
the remainder (multiply linked) objects. In this way one can separate these
two actions if required, though normally this is not necessary. 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. A short timeout is placed on this operation
to avoid hanging RPC connections when parsing NFS mounted file systems. 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.
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.
This list is used to define a global list of names or patterns which are to be copied rather than linked symbolically. 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.
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.
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.
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 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 parameter controls the global value of the ExpireAfter parameter, See section Spamming and security. This parameter controls the maximum time in minutes which a cfengine action is allowed to live. After this time cfengine will try to kill the cfengine which seems to have hung and attempt to restart the action.
ExpireAfter = ( time-in-minutes )
This parameter may also be set per action in the action sequence by appending
a pseudo-class called ExpireAftertime. For instance,
actionsequence = ( copy.ExpireAfter15 )
sets the expiry time parameter to 15 minutes for this copy command.
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 )
This parameter controls the global value of the IfElapsed parameter, See section Spamming and security. This parameter controls the minimum time which must have elapsed for an action in the action sequence before which it will be executed again.
IfElapsed = ( time-in-minutes )
This parameter may also be set per action in the action sequence by appending
a pseudo-class called IfElapsedtime. For instance,
actionsequence = ( copy.IfElapsed15 )
sets the elapsed time parameter to 15 minutes for this copy command.
If you have an operating system which is installed on some non-standard hardware, you might have to specifically set the name of the network interface. For example:
control:
nextstep.some::
interfacename = ( en0 )
nextstep.others::
interfacename = ( ec0 )
It is only necessary to set the interface name in this fashion if you have an operating system which is running on special hardware. Most users will not need this. The choice set here overrides the system defaults and the choices made in the `cfrc' file, See section `cfrc' resource file.
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.
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 )
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 hierarchically. 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
remainder `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 simultaneously. 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.
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 institutions 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.
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.
repository = ( directory )
Defines a special directory where all backup and junk files are collected. Files
are assigned a unique filename which identifies the path from which they originate.
This affects files saved using disable, copy, links
and editfiles See section Disabling
and the file repository.
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.
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.
SplayTime = ( time-in-minutes )
This variable is used to set the maximum time over which cfengine will share its load on a server, See section Splaying host times.
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.
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.
sysadm = ( mail address )
The mail address of your system administrator 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 administrators mail address 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.
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 substitution
in the usual way:
shellcommands:
"echo ${timezone} | mail ${sysadm}"
The classes keyword is an alias for groups as of version
1.4.0 of cfengine.
Cfengine copies files between locally mounted filesystems and via the network from registered servers. The copy algorithm avoids race-conditions which can occur due to network and system latencies by copying first to a file called `file.cfnew' on the local filesystem, and then renaming this quickly into place. The aim of this roundabout procedure is to avoid situations where the direct rewriting of a file is interrupted midway, leaving a partially written file to be read by other processes. Cfengine attempts to preserve hard links to non-directory file-objects, but see the caution below. The syntax summary is:
copy:
class::
master-file
dest=destination-file
mode=mode
owner=owner
group=group
action=silent/fix
backup=true/false
symlink=pattern
include=pattern
exclude=pattern
recurse=number/inf/0
type=ctime/checksum/sum/byte/binary
linktype=absolute/symbolic/relative/hard
define=class-list(,:.)
force=true/on/false/off
size=size limits
server=server-host
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. force 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 include exclude type server cfd daemon, and you must make sure that you have defined the variable
domain in the control section of the `cfengine.conf' file.
If you don't define a domain you will probably receive an error of the form
`cfengine: Hey! cannot stat file'.
size numberbytes numberkbytes numbermbytes
Only the first characters of these strings are significant, so they may be written however is convenient: e.g. 14kB, 14k, 14kilobytes etc. Examples are:
size=<400 # copy if file size is < 400 bytes size=400 # copy if file size is equal to 400 bytes size=>400 # copy if file size > 400 bytes
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. define 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.
Hard links are not like symbolic links, they are not merely pointers to other files, but alternative names for the same file. The name of every file is a hard link, the first so to speak. You can add additional names which really are the file, they are not just pointers. For the technically minded, they are not separate inodes, they are additional directory references to the same inode. When you perform a copy operation on multiple files, cfengine attempts to preserve hard links but this is a difficult task.
Because a hard link just looks like an ordinary file (it cannot be distingiushed from the original, the way a symbolic link can) there is a danger that any copy operation will copy two hard links to the same file as two separate copies of the same file. The difference is that changes a hard-linked file propagate to the links, whereas two copies of a file are completely independent thereafter. In order to faithfully reproduce all hardlinks to all files, cfengine needs to examine every file on the same filesystem and check whether they have the same inode-number. This would be an enourmous overhead, so it is not done. Instead what happens is that cfengine keeps track of only the files which it is asked to examine, for each atomic copy-command, and makes a note of any repeated inodes within this restricted set. It does not try to go off, wandering around file systems looking to other files which might be hardlinks.
To summarize, cfengine preserves hardlinks during copying, only within the scope of the present search. No backups are made of hard links, only of the first link or name of the file is backed up. This is a necessary precaution to avoid dangling references in the inode table. As a general rule, hard links are to be avoided because they are difficult to keep track of.
In long recursive copies, where you descend into many levels of diretories, you can quickly run out of file descriptors. The number of file descriptors is a resource which you can often set in the shell. It is a good idea to set this limit to a large number on a host which will be copying a lot of files. For instance, in the C shell you would write,
limit descriptors 1024
Most systems should have adequate defaults for this parameter, but on some systems it appears to be set to a low value such as 64, which is not sufficient for large recursive tree searches.
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.
This is a synonyn for required, See section
required. This action
tests for the existence of a file or filesystem. It should be called after all NFS
filesystems have been mounted. You may use the special variable $(binserver)
here.
disks:
/filesystem freespace=size-limit define=class-list(,:.)
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 filesystem 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.
disks:
any::
/$(site)/$(binserver)/local
If you set the freespace variable to a value (the default units
are kilobytes, but you may specify bytes or megabytes), e.g.
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:
classes::
/directory
mode=mode
owner=uid
group=gid
For example
directories:
class::
/usr/local/bin mode=755 owner=root group=wheel
The form of the command is similar to that of files but this command
is only be used to create new directories. Valid options are mode,
owner, group and are described under files
See section files. This interface is only
for convenience. It is strictly a part of the `files' functionality and is performed
together with other `files' actions at run time.
The creation of a path will fail if one of the links in the path is a plain file or device node.
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
type=plain/file/link/links
rotate=empty/truncate/numerical-value
size=numerical-value
Cfengine renames a given file by appending the name of the file with the suffix
`.cfdisabled'. 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 then gets recreated by some process,
the disabled file `filename.cfdisabled' will be overwritten
each time cfengine disables the file and therefore the contents of the original
are lost each time. The rotate facility was created for just this contingency.
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 in its 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 or rotate=truncate. For
instance, to keep control of your World Wide Web server logs:
disable:
Sunday|Wednesday::
/usr/local/httpd/logs/access_log rotate=empty
This keeps a running log which is emptied each Sunday and Wednesday.
The size= option in disable allows you to carry out a disable operation
only if the size of the file is less than, equal to or greater than some specified
size. Sizes are in bytes by default, but may also be quoted in kilobtes or megabytes
using the notation:
numberbytes numberkbytes numbermbytes
Only the first characters of these strings are significant, so they may be written however is convenient: e.g. 14kB, 14k, 14kilobytes etc. Examples are:
size=<400 # disable if file size is < 400 bytes size=400 # disable if file size is equal to 400 bytes size=>400 # disable if file size > 400 bytes
This options works with rotate or normal disabling; it is just an
extra condition which must be satisfied.
You can perform simple control or editing on textfiles using a number of commands.
These are fairly limited but are sufficient for many purposes and are definitely
superior to using awk or sed
(8).
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 specifies 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 comment types may be defined using the SetCommentStart,
SetCommentEnd and CommentLinesMatching, CommentLinesStarting
functions.
A special group of editing commands is based on the GNU Regular Expression package.
These use 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 meta-characters!
AbortAtLineMatching quoted-regex 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-regex AppendIfNoSuchLine which uses a
regular expression instead of a literal string. The line which gets appended
must be set previously using SetLine. AppendToLineIfNotContains quoted-string AutoCreate 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'.
BeginGroupIfFileExists quoted-string EndGroup are executed
if the quoted filename exists (can be statted). Files which are not readable
by the running process are for all intents and purposes non-existent.
BeginGroupIfFileIsNewer quoted-string EndGroup are executed
if the quoted filename is newer than the file being edited.
BeginGroupIfNoLineContaining quoted-string EndGroup are executed
if the quoted string does not appear in any line in the file. BeginGroupIfNoLineMatching quoted-regex EndGroup are executed
if the quoted regular expression does not match any line in the file. BeginGroupIfNoMatch quoted-regex 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-regex CommentLinesMatching quoted-regex 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-regex SetCommentStart
and SetCommentEnd to comment out lines from the current position
in a file to a line matching the given regular expression in quotes. DeleteLinesAfterThisMatching quoted-regex DeleteLinesContaining quoted-string DeleteLinesMatching quoted-regex DeleteLinesStarting quoted-string DeleteNLines quoted-string DeleteToLineMatching quoted-regex EmptyEntireFilePlease EndGroup EndLoop ForEachLineIn FixEndOfLine ForEachLineIn quoted-filename SetLine for each line in
the file. Nested loops are not permitted. GotoLastLine HashCommentLinesContaining quoted-string HashCommentLinesMatching quoted-regex HashCommentLinesStarting quoted-string IncrementPointer quoted-number 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-regex PercentCommentLinesStarting quoted-string Prepend quoted-string BeginGroupIfNoLineMatching
and BreakIfLineMatches. PrependIfNoLineMatching quoted-regex 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-regex With quoted-string
ReplaceLinesMatchingField quoted-number SetLine or ForEachLineIn, if the lines are
split into fields (e.g. the password file) separated by the SplitOn
character (':' by default), and the corresponding fields match. The idea behind
this command was to be able to override global passwords (from a file which
gets distributed) by new passwords in a local file. Rather than maintaining
the files separately, this simply overrides the entries with the new ones See
section FAQs and Tips. 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-regex 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-regex SlashCommentLinesStarting quoted-string SplitOn quoted-string ReplaceLinesMatchingField.
UnCommentLinesContaining quoted-string UnCommentLinesMatching quoted-regex UnCommentNLines quoted-string UnCommentNLines "3" would uncomment
/* 1 */ /* 2 */ /* 3 */
and also
/* 1
2
3 */
UnsetAbort quoted-string AbortAtLineMatching. WarnIfLineContaining quoted-string WarnIfLineMatching quoted-regex 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:
classes::
/file-object
mode=mode
owner=uid-list
group=gid-list
action=fixall/other-options/warnall
links=false/stop/traverse/follow/tidy
include=pattern
exclude=pattern
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 abbreviated 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 linkchildren create
The upper line results only in warnings being issued. The actions beginning `fix' prompt cfengine to fix encountered problems without bothering the user. No message is issued unless in verbose mode. The special features on the third line will be explained separately.
include=wildcard/pattern exclude=wildcard/pattern include= list. 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-existent 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 unforeseen accidents.
A recursive search also bottoms out on device boundaries and symbolic links (provided
the -l option is not used).
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
directiveIf 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.
/file-object 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
This is like touch except that an existing file's time stamps will
not be modified.
The groups action (equivalently referred to as classes
as of version 1.4.0) 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 )
The classes on the right hand side are effectively ORed together into the left hand side. This enables complex classes to be constructed from several other basic classes, e.g.
SpecialTimes = ( Hr00 Monday Day1 )
which evaluates to true every day when it between 00:00 hours and 00:59, all day Monday and all day on the first day of every month.
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.
As of version 1.4.0, you may use the synonym classes for groups.
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, tidy
or copy action, you would sometimes like to exclude certain directories
from the list of sub directories. In most cases you will want to do this on a per-command
basis (see the pages for these actions separately), but you can also make a global
ignore list. This can be accomplished by adding the directory to the ignore-list.
The syntax is
ignore:
wildcards/directories/filenames
ignore:
any::
#
# Prevent tidying .X11 directories 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 or entered during recursive descents 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
or
from-link +>[!] to-object
type=symbolic/absolute/abs/hard/relative/rel
copy=pattern
recurse=number/inf/0
copytype=checksum/ctime
include=pattern
exclude=pattern
action=silent
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
If links exists and point to their intended destinations then no action is taken.
If a link exists but points incorrectly then a warning is issued, unless the pling
operator `!' is given, in which case the correct value is forced. 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 substituting $(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.
With the link symbol +>, you opt to link all of the files in a directory
to corresponding files in another directory. This procedure 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 accordingly. 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 resource 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 directory 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 official 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 AutomountDirectResources.
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 re-consulted 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 2
Note that cfengine will not attempt to signal or restart processes 0 to 3 on any system since such an attempt could bring down the system. The only exception is that the hangup (hup) signal may be sent to process 1 (init) which normally forces init to reread its terminal configuration files.
restart "shell command" 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 is a synonym for disks, See section disks.
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.
required:
/filesystem freespace=size-limit define=class-list(,:.)
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 filesystem 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
If you set the freespace variable to a value (the default units
are kilobytes, but you may specify bytes or megabytes), e.g.
required: /site/host/home1 freespace=50mb define=dotidy
then cfengine will warn when the filesystem concerned has less than this amount
of free space. By adding a define tag, you can switch on any number
of classes if this happens. This allows you to activate special measures for dealing
with a filesystem which is in danger of becoming full.
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.
shellcommands: "command-string" timeout=seconds
If you set the optional timeout string, then cfengine will abort the shellcommand if it exceeds the specified time-limit in seconds. This can be useful for avoiding situations caused by hung network connections etc.
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 kept up-to-date at any given time with only the classes which are defined. The command line option `-u' or `--use-env' can be used to define an environment variable which will be inherited by all scripts and contains the same information. This is not the standard approach, since some systems cannot cope with this rapid change of environment and generate a Bus Error.
Commands can be iterated over variable lists, provided there is at least one space between each variable. For example:
control:
actionsequence =
(
shellcommands
)
var1 = ( a:b:c )
var2 = ( x:y:z )
shellcommands:
"/bin/echo $(var1) $(var2)"
This iterates over all values of the list variables See section Iterating over lists. If you are iterating over a list, the timeout applies to each separate iteration, not to the sum of all the iterations.
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
links=stop/keep/traverse/tidy
Note that, each of the options below can be written in either upper or lower case and abbreviated 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 so that any file which
gets matched by another critereon is deleted. However, if you want to single
out only totally empty files, the empty may be used. With this
option only empty files, nevery files with anything in them, will be deleted,
if older than age. By default, the filesizes are in kilobytes,
but kilobytes and megabytes meay also be specified by appending b,k,m to the
numbers. Only the first character after the number is significant so you may
write the numbers however it might be convenient, e.g. 14k,
14kB, 14kilobytes, the same as for disable.
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.
The unmount function unmounts non-required 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 absence 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
)
tidy:
$(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 hard-class for the
current system (e.g. ultrix). Here is an example:
#!/bin/sh
#
# cf.preconf is an emergency/bootstrap file to get things going
# in case cfengine is unable to parse its config file
#
backupdir=/iu/nexus/local/iu/etc
#
# If thess files don't exist, you might not be able to parse cfengine.conf
#
if [ ! -s /etc/resolv.conf ]; then
echo Patching basics resolv.conf file
cat > /etc/resolv.conf << XX
domain iu.hioslo.no
nameserver 128.39.89.10
XX
fi
#
# SVR4
#
if [ "$1" = "solaris" ]; then
if [ ! -s "/etc/passwd" ]; then
echo Patching missing passwd file
/bin/cp $backupdir/passwd /etc/passwd
fi
if [ ! -s "/etc/shadow" ]; then
echo Patching missing passwd file
/bin/cp $backupdir/shadow /etc/shadow
fi
fi
#
# BSD 4.3
#
if [ "$1" = "linux" ]; then
if [ ! -s "/etc/passwd" ]
then
echo Patching missing passwd file
/bin/cp $backupdir/passwd.linux /etc/passwd
fi
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 re-definable 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.
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 and newer IRIX versions. There are also problems with NetBSD. These features are undocumented and will be fixed as soon as they have been understood! 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 causes 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 versions 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 host-type.
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.
As of version 1.4.0 cfengine tries to link in features based on the Berkeley database library `libdb' and the TCP wrappers library `libwrap'. If you want to use these facilities, you will have to collect them and install them before compiling cfengine. Some problems have been experienced with the linux version of TCP wrappers. If you experience compilation problems, the best thing to do is to edit `src/conf.h' after configuration and remove the line beginning `#define HAVE_LIBWRAP'. You will not be able to use the
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@prep.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 -d3
This just parses the file and dumps the contents of the parser to the output.
Hr00---Hr23. Other time classes
are also possible See section Using cfengine
as a front end for cron.
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=1
An alternative to using disable would be to use tidy,
but then you lose the file once and for all. Note though, that `wtmpx'
gets updated all the time, so an age age=0 is necessary to have
any effect at all. Some daemons, like `httpd', lose their ability to
write to a log file if you rename and create a new file. The rotate
feature in cfengine preserves the open file handle, fixing this problem.
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=netgroup"
}
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 descriptive 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 )
editfiles:
any::
{ /etc/motd
BeginGroupIfFileIsNewer "$(masterfile)"
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. Note, if you want to keep the first kernel line in this file, you can change this to:
editfiles:
any::
{ /etc/motd
BeginGroupIfFileIsNewer "$(masterfile)"
IncrementPointer "1"
DeleteLinesAfterThisMatching ".*"
InsertFile "$(masterfile)"
AppendIfNoSuchLine "$(motd_version)"
EndGroup
}
If you're still worried about the security of your script (be it a cfengine script or not), you could always adjust your cron script to "decrypt" the script file before executing it (see crypt(1)). Personally, I think if you've set the permissions on your script files properly, then, if someone breaks into those scripts, they've already broken into your system to a point where they could do what they wanted anyway.
editfiles. First you use copy to get the
distributed file, the you edit the file like this:
editfiles:
{ /etc/passwd
SplitOn ":"
ForEachLineIn "/usr/local/etc/passwd.local"
ReplaceLinesMatchingField "1"
EndLoop
}
This means, if the the first field of each line in the files matches in both files (and both files have the same column format) then replace the line in `/etc/passwd' with the line from `/usr/local/etc/passwd.local'.
control:
person = ( new-user )
editfiles:
{ /etc/group
BeginGroupIfNoLineMatching "adm.*$(person).*"
LocateLineMatching "adm.*"
AppendToLineIfNotContains ",$(person)"
EndGroup
}
bug-cfengine@prep.ai.mit.edu. hpux10
rather than hpux. disable:
hpux::
/usr/lib/sendmail.fc
links:
hpux::
/etc/logingroup -> /etc/group
#!/usr/local/bin/cfengine -f
####################################################
#
# File: sendmail.conf
#
# Description: CFEngine script to setup the sendmail.cf.
#
####################################################
control:
access = ( root )
# Postmaster
sysadm = ( myPostmaster )
# NIS domain and group server
site = ( myserver )
# DNS domain
domain = ( myDNSdomain )
# our gateway host
gtwyhost = ( mygateway )
# sendmail.cf can be big
editfilesize = ( 1000000 )
actionsequence =
(
copy
files
editfiles
shellcommands
)
# disable unwanted classes with "--undefine" option
addclasses = ( maildom mailhst )
################
# bindir - location of sendmail
# libdir - location of current mail files
# cfgdir - location of initial mail files
# etcdir - location of hosts.smtp
# own - who should own result files
# grp - what group should result files be in
################
hpux::
bindir = ( /usr/lib )
libdir = ( /usr/lib )
cfgdir = ( /etc/newconfig )
etcdir = ( /etc )
own = ( root )
grp = ( sys )
hpux10::
bindir = ( /usr/sbin )
libdir = ( /etc/mail )
cfgdir = ( /usr/newconfig/etc/mail )
etcdir = ( /etc )
own = ( root )
grp = ( sys )
# disable with "--no-copy" option
copy:
$(cfgdir)/sendmail.cf dest=$(libdir)/sendmail.cf type=checksum
mode=0644 owner=$(own) group=$(grp) force=true
# checks for other important files
files:
$(libdir)/aliases mode=444 owner=$(own) group=$(grp) action=touch
$(libdir)/rev-aliases mode=444 owner=$(own) group=$(grp) action=touch
$(etcdir)/hosts.smtp mode=444 owner=$(own) group=$(grp) action=touch
# disable with "--no-edit" option
editfiles:
any::
# setup general part of sendmail.cf
{ $(libdir)/sendmail.cf
SetCommentStart '#'
SetCommentEnd "
ResetSearch "1"
UnCommentLinesMatching "#OP.*" # activate Postmaster
ResetSearch "1"
UnCommentLinesMatching "#DY.*"
ResetSearch "1"
LocateLineMatching "DY.*"
ReplaceLineWith "DY$(site).$(domain)" # set site hiding
ResetSearch "1"
UnCommentLinesMatching "#DS.*"
ResetSearch "1"
LocateLineMatching "DS.*"
ReplaceLineWith "DS$(gtwyhost)" # all-knowing SMTP host
# Ruleset 0 setups
ResetSearch "1"
UnCommentLinesMatching "#R.*user@domain to SMTP relay.*"
ResetSearch "1"
LocateLineMatching "# try to connect to any host for user@domain"
IncrementPointer "1"
CommentNLines "1"
}
# add Postmaster alias
{ $(libdir)/aliases
SetLine "Postmaster: $(sysadm)"
AppendIfNoLineMatching "Postmaster.*"
}
# setup processing of local domain hosts
maildom::
{ $(libdir)/sendmail.cf
SetCommentStart '#'
SetCommentEnd "
ResetSearch "1"
LocateLineMatching "DL.*"
ReplaceLineWith "DL$(domain)"
# Ruleset 0 setups
ResetSearch "1"
LocateLineMatching "# connect to hosts in local domain"
IncrementPointer "1"
UnCommentNLines "1"
}
# setup processing via class S
mailhst::
{ $(libdir)/sendmail.cf
SetCommentStart '#'
SetCommentEnd "
ResetSearch "1"
UnCommentLinesMatching "#FS.*"
# Ruleset 0 setups
ResetSearch "1"
LocateLineMatching "# connect to hosts in class S"
IncrementPointer "1"
UnCommentNLines "1"
}
# setup of list of hosts for class S
{ $(etcdir)/hosts.smtp
EmptyEntireFilePlease
Append "localhost1"
Append "localhost2"
}
# disable with "--no-commands" option
shellcommands:
"$(bindir)/sendmail -bk"
"$(bindir)/sendmail -bi"
"$(bindir)/sendmail -bz"
"$(bindir)/sendmail -bd"
################
# End of File
################
bug-cfengine@prep.ai.mit.edu. cfd I get this error
/usr/lib/libwrap.a(options.o): In function `twist_option': options.o(.text+0x5f7): undefined reference to `deny_severity' /usr/lib/libwrap.a(options.o): In function `severity_option': options.o(.text+0x808): undefined reference to `deny_severity' options.o(.text+0x81c): undefined reference to `deny_severity' options.o(.text+0x821): undefined reference to `deny_severity' options.o(.text+0x826): undefined reference to `deny_severity' options.o(.text+0x82b): undefined reference to `allow_severity' make[1]: *** [cfd] Error 1
editfiles:
linux::
{ /etc/rc.d/rc.S
HashCommentLinesContaining "motd"
}
bug-cfengine@prep.ai.mit.edu. 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-xdm
Now add an entry to your `cfengine.conf' file like this
solaris::
{ /etc/rc3.d/S15nfs.server
AppendIfNoSuchLine "sh /local/etc/rc.local"
}
Although this works just fine, it is not an official 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=file
You 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.
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 execution. 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.
#
######################################################################
###
#
# BEGIN cfengine.conf
#
###
import:
#
# Split things up to keep things tidy
#
any::
cf.groups
cf.main
cf.site
cf.motd
hpux:: cf.hpux
linux:: cf.linux
solaris:: cf.solaris
sun4:: cf.sun4
ultrix:: cf.ultrix
freebsd:: cf.freebsd
#
# Do you want to do this ?
#
AllHomeServers:: cf.users
###
#
# END cfengine.conf
#
###
##############################################################
#
# cf.groups - for iu.hioslo.no
#
# This file contains all group/class definitions
#
#################################################################
###
#
# BEGIN cf.groups
#
###
groups:
#
# Define some groups
#
iu = ( nexus ferengi regula borg dax lore axis worf daystrom voyager
aud1 aud2 aud3 aud4 bajor ds9 takpah takpeh nostromo galron
thistledown rama chaos pc-steinarj pc-hildeh way jart kosh )
diskless = ( regula ferengi lore )
standalone = ( nexus axis dax borg worf daystrom voyager
aud1 aud2 aud3 aud4 bajor ds9 takpah takpeh
nostromo galron thistledown rama pc-torejo
pc-steinarj pc-hildeh )
AllHomeServers = ( nexus )
AllBinaryServers = ( nexus borg )
XBootServer = ( nexus )
WWWServers = ( nexus )
FTPserver = ( nexus )
NameServers = ( nexus )
PasswdServer = ( nexus )
BackupHost = ( nexus )
MailHub = ( nexus )
MailClients = ( iu -nexus )
###
#
# END cf.groups
#
###
##############################################################
#
# cf.main - for iu.hioslo.no
#
# This file contains generic config stuff
#
#################################################################
###
#
# BEGIN cf.main
#
###
control:
access = ( root ) # Only root should run this
site = ( iu )
domain = ( iu.hioslo.no )
sysadm = ( drift@iu.hioslo.no )
repository = ( /var/spool/cfengine )
netmask = ( 255.255.255.0 )
timezone = ( MET )
nfstype = ( nfs )
sensiblesize = ( 1000 )
sensiblecount = ( 2 )
editfilesize = ( 20000 )
mountpattern = ( /$(site)/$(host) )
homepattern = ( u? )
#
# If we undefine this with cfengine -N longjob
# then we switch off all jobs labelled with this class
#
addclasses = ( longjob )
#
# 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/iu )
main_server = ( nexus )
cfbin = ( /iu/nexus/local/gnu/lib/cfengine/bin )
gnu = ( /local/gnu )
ftp = ( /local/iu/ftp )
nisslave = ( dax )
nisfiles = ( /iu/nexus/local/iu/etc )
#
# The action sequence for daily (full) runs and
# for hourly updates (called with -DHourly)
#
Hr00::
actionsequence =
(
copy
mountall
mountinfo
checktimezone
netconfig
resolve
unmount
shellcommands
addmounts
links.Prepare
files.Prepare
directories
links.Rest
mailcheck
mountall
required
tidy
disable
editfiles
files.Rest
processes
)
!Hr00::
actionsequence =
(
resolve
shellcommands
copy
editfiles
processes
links
)
force::
actionsequence =
(
files.Prepare.Rest
tidy
)
######################################################################
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/u3
nexus:/iu/nexus/u4
nexus:/iu/nexus/u5
nexus:/iu/nexus/u6
nexus:/iu/nexus/ua
nexus:/iu/nexus/ud
nexus:/iu/nexus/local
nexus:/opt/NeWSprint
nexus:/opt/AcroRead
borg:/iu/borg/local
dax:/iu/dax/local
miscmounts:
linux||freebsd:: nexus:/iu/nexus/local /iu/nexus/local ro
######################################################################
broadcast:
ones
defaultroute:
cadeler30-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
######################################################################
ignore: # Don't check or tidy these directories
/local/lib/gnu/emacs/lock/
/local/tmp
ftp
projects
/local/bin/top
/local/lib/tex/fonts
/local/iu/etc
/local/etc
/local/iu/httpd/conf
/usr/tmp/locktelelogic
/usr/tmp/lockIDE
RootMailLog
#
# Emacs lock files etc
#
!*
/local/lib/xemacs
#
# 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 cf.main
#
###
##############################################################
#
# cf.site - for iu.hioslo.no
#
# This file contains site specific data
#
#################################################################
###
#
# BEGIN cf.site
#
###
links:
Prepare::
/local -> /$(site)/$(binserver)/local
/usr/local -> /local
dax::
/iu/dax/local +> /iu/nexus/local
/projects -> /iu/dax/local/projects
/iu/nexus/u1/sowille/data -> /iu/dax/scratch/data
XBootServer::
#
# Set up a /local/tftpboot area where all X terminal
# stuff will be kept.
#
/tftpboot -> /local/tftpboot
/local/tftpboot/td/configs -> /local/tftpboot/td/examples/configs
/etc/bootptab -> /tftpboot/bootptab
/tftpboot/usr/lib/X11/td -> /tftpboot/td
NameServers::
/etc/named.boot -> /local/iu/named/named.boot
MailHub::
/etc/mail/sendmail.cf ->! /iu/nexus/local/mail/sendmail.cf
MailClients.solaris::
/etc/mail/sendmail.cf ->! /iu/nexus/local/mail/client.cf
nexus::
/local/bin +> /local/latex/bin
#############################################################
disable:
#
# We run Berkeley sendmail and the config files are
# all under /iu/nexus/local/lib/mail
#
/etc/aliases
WWWServers.Sunday::
#
# Disabling these log files weekly prevents them from
# growing so enormous that they fill the disk!
#
/local/iu/httpd/logs/access_log rotate=empty
/local/iu/httpd/logs/agent_log rotate=empty
/local/iu/httpd/logs/error_log rotate=empty
/local/iu/httpd/logs/referer_log rotate=empty
#
# CERT warning, security fix
#
any::
/usr/lib/expreserve
FTPserver.Sunday.Hr00::
/local/iu/xferlog rotate=3
#################################################################
files:
Prepare::
/etc/motd m=0644 r=0 o=root act=touch
/.cshrc m=0644 r=0 o=root act=touch
PasswdServer::
/local/iu/etc/passwd m=0644 o=root g=other action=fixplain
/local/iu/etc/shadow m=0644 o=root g=other action=fixplain
WWWServers.Rest::
/local/iu/www m=775 g=www act=fixall r=inf
/local/iu/httpd/conf m=664 o=root g=www act=fixall r=inf
/local/iu/www/cgi-bin-public/count_file m=777 o=root g=www act=fixplain
FTPserver::
#
# 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
Prepare::
/etc/shells mode=0644 action=touch
AllBinaryServers.Rest.longjob::
/local mode=-0002 r=inf owner=root,bin group=0,1,2,3,4,5,6,7,staff
links=tidy action=fixall
/local/iu/RootMailLog m=0666 action=touch
dax.Rest::
/iu/dax/scratch r=0 o=root mode=1777 action=fixall
/iu/dax/local/projects r=0 o=root mode=755 action=fixdirs
nexus::
/local/mail/sendmail.cf o=root m=444 act=fixplain
/iu/nexus/ua/robot/.rhosts o=robot m=600 act=touch
/local/iu/named/pz o=root m=644 act=fixall r=1
/local/latex/lib/tex/texmf/fonts owner=root
mode=1666
recurse=inf
action=fixall
#################################################################
tidy:
#
# Make sure the file repository doesn't fill up
#
/var/spool/cfengine pattern=* age=3
/var pattern=core age=0 r=inf
/var/spool/mqueue pattern=* age=14 type=mtime
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/backup1 pat=* age=7
#################################################################
shellcommands:
PasswdServer::
# Build and install the BSD compatible passwd file
# from the master passwd/shadow file on solaris
"/local/iu/bin/BuildPasswdFiles"
"/local/iu/bin/BuildGroupFiles"
BackupHost.Sunday.Hr00|BackupHost.Wednesday.Hr00::
#
# Make a system backup of /iu/nexus/u? with Audun's script
#
"$(cfbin)/cfbackup -p -f /iu/nexus/backup1 -s /iu/nexus/ud"
"$(cfbin)/cfbackup -p -f /iu/nexus/backup1 -s /iu/nexus/ua"
"$(cfbin)/cfbackup -p -f /iu/nexus/backup1 -s /iu/nexus/u1"
"$(cfbin)/cfbackup -p -f /iu/nexus/backup1 -s /iu/nexus/u2"
"$(cfbin)/cfbackup -p -f /iu/nexus/backup2 -s /iu/nexus/u3"
"$(cfbin)/cfbackup -p -f /iu/nexus/backup2 -s /iu/nexus/u4"
"$(cfbin)/cfbackup -p -f /iu/nexus/backup2 -s /iu/nexus/u5"
"$(cfbin)/cfbackup -p -f /iu/nexus/backup2 -s /iu/nexus/u6"
nexus.Sunday.longjob.Hr00::
#
# See how much rubbish users have accumulated each Sunday
#
"$(cfbin)/noseyparker /iu/nexus/u1 $(sysadm) "
"$(cfbin)/noseyparker /iu/nexus/u2 $(sysadm) "
"$(cfbin)/noseyparker /iu/nexus/u3 $(sysadm) "
"$(cfbin)/noseyparker /iu/nexus/u4 $(sysadm) "
"$(cfbin)/noseyparker /iu/nexus/u5 $(sysadm) "
"$(cfbin)/noseyparker /iu/nexus/u6 $(sysadm) "
"$(cfbin)/noseyparker /iu/nexus/ua $(sysadm) nomail"
"$(cfbin)/noseyparker /iu/nexus/ud $(sysadm) nomail"
nexus.longjob.Hr00::
#
# Update the GNU find/locate database each night
#
"$(gnu)/lib/locate/updatedb"
"/local/iu/bin/newhomepage.sh"
###############################################################
editfiles:
#
# cfengine installs itself as a cron job - sneaky! :)
#
{ /var/spool/cron/crontabs/root
AppendIfNoSuchLine "0 * * * * $(cfbin)/cfwrap $(cfbin)/cfhourly"
}
FTPserver::
{ /etc/shells
AppendIfNoSuchLine "/bin/tcsh"
AppendIfNoSuchLine "/local/gnu/bin/bash"
}
XBootServer::
{ /etc/inetd.conf
AppendIfNoSuchLine
"bootp dgram udp wait root /local/bin/bootpd bootpd -i -d"
}
nexus::
{ /iu/nexus/ua/robot/.rhosts
AppendIfNoSuchLine "borg"
AppendIfNoSuchLine "borg.iu.hioslo.no"
AppendIfNoSuchLine "aud4"
AppendIfNoSuchLine "aud4.iu.hioslo.no"
}
dax::
{ /etc/system
AppendIfNoSuchLine "set pt_cnt=128"
}
######################################################################
required:
#
# Any host must have a /local, /usr/local fs. Check that
# it exists and looks sensible. (i.e. not empty)
#
/$(site)/$(binserver)/local
######################################################################
copy:
#
# NIS seems broken at IU, so here we use NFS to fudge
# a file distribution as a temporary solution. Actually
# this makes the system work faster without NIS!
#
$(nisfiles)/services dest=/etc/services o=root g=other mode=0644
$(nisfiles)/hosts.deny dest=/etc/hosts.deny o=root mode=0644
!debian::
$(nisfiles)/hosts dest=/etc/hosts o=root g=other mode=0644
PasswdServer::
/etc/passwd dest=$(nisfiles)/passwd o=root g=other mode=0644
/etc/shadow dest=$(nisfiles)/shadow o=root g=other mode=0644
nexus::
/local/iu/etc/dfstab dest=/etc/dfs/dfstab o=root mode=0744
solaris.!PasswdServer::
$(nisfiles)/passwd dest=/etc/passwd o=root g=other mode=0644
$(nisfiles)/shadow dest=/etc/shadow o=root g=other mode=0600
$(nisfiles)/group.solaris dest=/etc/group o=root g=other mode=0644
linux::
$(nisfiles)/passwd.linux dest=/etc/passwd o=root g=other mode=0644
$(nisfiles)/group.linux dest=/etc/group o=root g=other mode=0644
###############################################################
processes:
"eggdrop" signal=kill
"irc" signal=kill
"ping" signal=kill
"NetXRay" signal=kill
"netxray" signal=kill
"ypserv" signal=kill
"ypbind" signal=kill
"rarpd" signal=kill
"rpc.boot" signal=kill
"README" signal=kill # You don't sh README !
!XBootServer::
"bootp" signal=kill
#
# These processes are not killed every hour, but once a day
# when cfengine runs at night. Note that there are often
# hanging pine and elm processes. These programs crash and
# go berserk, using hundreds of hours of CPU time.
#
Hr00::
"cron" signal=hup # HUP these to update their config
"inetd" signal=hup
"/local/sdt/sdt/bin" signal=term # For those elektro dudes who forget
# to log out
"netscape" signal=kill
"pine" signal=kill
"elm" signal=kill
###
#
# END cf.site
#
###
##################################################################
#
# cf.motd
#
# This file is used to set the message of the day file on
# each host
#
##################################################################
#####
#
# BEGIN cf.motd
#
#####
control:
#
# This points to the file containing general text
#
masterfile = ( /iu/nexus/local/iu/etc/motd-master )
local_message = ( /etc/motd.local )
editfiles:
{ /etc/motd
BeginGroupIfFileIsNewer "$(masterfile)"
EmptyEntireFilePlease
InsertFile "$(masterfile)"
InsertFile "$(local_message)"
PrependIfNoSuchLine "This system is running $(class):$(arch)"
EndGroup
}
#####
#
# BEGIN cf.motd
#
#####
Whether or not you perform any special services for users, with or without their consent is entirely a matter of local policy. In a school or college situation, users are often uncooperative and some are even irresponsible. This file shows you what you could do in an environment with inexperienced users, but please don't feel as though you have to be this totalitarian.
#################################################################
#
# cf.users - for iu.hioslo.no
#
# This file contains user specific actions
#
#################################################################
###
#
# BEGIN cf.users
#
###
ignore:
robot
tidy:
longjob::
#
# Some users just don't understand what they are doing
# and this is safest, allbeit totalitarian
#
home pat=.rhosts age=0
#
# Tidy up users' home dirs
#
home pat=core r=inf age=0
home pat=a.out r=inf age=1
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 type=ctime
home p=*.log r=inf age=2
home p=Log.* r=inf age=3
home p=CKP r=inf age=1
home p=BAK r=inf age=1
home p=log r=inf age=0
home p=*.o r=inf age=0
home p=*.aux r=inf age=3
home p=*.zip r=inf age=7
home/.deleted p=* r=inf age=0
home/.wastebacket p=* r=inf age=14
home/www p=*~ r=inf age=1
#
# Clear the big cache files netscape creates
#
home/.netscape-cache p=cache????* r=inf age=0
home/.MCOM-cache p=cache????* r=inf age=0
home/.netscape/cache p=* r=inf age=0
#################################################################
files:
AllHomeServers.longjob.rest::
#
# 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
home/.xsession mode=755 action=fixall
home/.cshrc mode=755 action=fixall
#################################################################
copy:
Hr00.longjob::
#
# 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/tkgrc dest=home/.tkgrc
$(masterfiles)/lib/fvwm2rc dest=home/.fvwm2rc
###
#
# END cf.users
#
###
#################################################################
#
# cf.solaris - for iu.hioslo.no
#
# This file contains solaris specific patches
#
#################################################################
###
#
# BEGIN cf.solaris
#
###
directories:
#
# httpd/netscape want this to exist for some bizarre reason
#
/usr/lib/X11/nls
################################################################
tidy:
/var/log pattern=syslog.* age=0
MailHub::
/var/mail pattern=lp age=0
#################################################################
files:
#
# If this doesn't exist fork will not work and the
# system will not even be able to run the /etc/rc
# scripts at boottime
#
/etc/system o=root g=root m=644 action=touch
/var/log/syslog o=root m=666 action=touch
#############################################################
copy:
#
# Some standard setup files, can't link because
# machine won't boot if their not on / partition.
#
/local/bin/tcsh dest=/bin/tcsh mode=755
/local/iu/etc/nsswitch.standalone dest=/etc/nsswitch.conf
#
# Our named server uses a newer BIND
# Put this here so that it will be preserved under
# solaris reinstallation
#
NameServers::
/local/iu/sbin/in.named dest=/usr/sbin/in.named mode=555
/local/iu/sbin/in.named.reload dest=/usr/sbin/in.named.reload mode=555
/local/iu/sbin/in.named.restart dest=/usr/sbin/in.named.restart mode=555
/local/iu/sbin/in.ndc dest=/usr/sbin/in.ndc mode=555
/local/iu/sbin/named-xfer dest=/usr/sbin/named-xfer mode=555
/local/iu/lib/nslookup.help dest=/usr/lib/nslookup.help mode=444
any::
/local/iu/lib/libresolv.a dest=/usr/lib/libresolv.a mode=444
/local/iu/lib/libresolv.so.2 dest=/usr/lib/libresolv.so.2 mode=444
/local/bin/nslookup dest=/usr/sbin/nslookup mode=444
##############################################################
editfiles:
{ /etc/netmasks
AppendIfNoSuchLine "128.39 255.255.255.0"
}
{ /etc/defaultrouter
AppendIfNoSuchLine "128.39.89.1"
}
{ /usr/openwin/lib/app-defaults/XConsole
AppendIfNoSuchLine "XConsole.autoRaise: on"
}
#
# CERT security patch for vold vulnerability
#
{ /etc/rmmount.conf
HashCommentLinesContaining "action cdrom"
HashCommentLinesContaining "action floppy"
}
##############################################################
disable:
/etc/.login type=file
/etc/aliases
#
# These files are ENORMOUS, don't let them fill the disk
#
Wednesday::
/var/lp/logs/lpsched rotate=empty
/var/adm/wtmpx rotate=empty
/var/adm/wtmp rotate=empty
##############################################################
files:
/etc/passwd m=0644 o=root g=other action=fixplain
/etc/shadow m=0600 o=root g=other action=fixplain
/etc/defaultrouter m=0644 o=root g=other action=touch
/var/adm/wtmpx m=0664 o=adm g=adm action=touch
/var/adm/wtmp m=0644 o=root g=adm action=touch
/var/adm/utmp m=0644 o=root g=adm action=fixplain
/var/adm/utmpx m=0664 o=adm g=adm action=fixplain
/tmp m=1777 action=fixdirs
##############################################################
disable:
#
# CERT security patch
#
/usr/openwin/bin/kcms_calibrate
/usr/openwin/bin/kcms_configure
/usr/bin/admintool
################################################################
shellcommands:
AllBinaryServers.Saturday.longjob.Hr00::
#
# 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 /usr/share/man"
################################################################
editfiles:
#
# A painless way to add an rc.local script to the rc files
# under solaris without having to fight though inittab
#
{ /etc/rc3.d/S15nfs.server
AppendIfNoSuchLine "sh /local/iu/etc/rc.local"
}
#
# umask defined when inetd starts is inherited by all subprocesses
# including ftpd which saves with mode 666 (!) unless we do this
#
{ /etc/rc2.d/S72inetsvc
PrependIfNoSuchLine "umask 022"
}
###
#
# END cf.solaris
#
###
#################################################################
#
# cf.linux - for iu.hioslo.no
#
# This file contains debian linux specific patches
#
#################################################################
###
#
# BEGIN cf.linux
#
###
files:
/etc/printcap m=644 o=root action=fixplain
#
# Cert advisories
#
/bin/mount m=755 o=root action=fixall
/bin/umount m=755 o=root action=fixall
#######################################################################
disable:
#
# Cert advisories
#
/sbin/dip-3.3.7n
########################################################################
links:
/local/bin/tcsh -> /bin/tcsh
/local/lib/mail -> /$(site)/$(main_server)/local/lib/mail
########################################################################
editfiles:
#
# Samba default mode needs to be set...
#
{ /etc/smb.conf
ReplaceAll "700" With "644"
}
#
# Linux date is very stupid and needs a very careful
# TZ definition, otherwise it loses
#
{ /etc/csh.cshrc
AppendIfNoSuchLine "setenv TZ 'MET-1MET DST-2,M3.5.0/2,M10.5.0/3'"
}
#
# resolv+ ordering
#
{ /etc/host.conf
PrependIfNoSuchLine "order bind"
}
#
# Should have been configured already (!)
#
{ /etc/ld.so.conf
AppendIfNoSuchLine "/usr/X11R6/lib"
}
#
# Kill annoying messages
#
{ /etc/cron.daily/standard
HashCommentLinesContaining "security"
}
#########################################################################
shellcommands:
Hr00::
#
# Find/locate database
#
"/usr/bin/updatedb"
###
#
# END cf.linux
#
###
FreeBSD and NetBSD are sufficiently similar to have a single file for both.
#################################################################
#
# 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
#################################################################
files:
/usr/tmp mode=1777 owner=root action=fixall
#################################################################
editfiles:
#
# 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"
}
#################################################################
copy:
$(masterfiles)/etc/printcap.client dest=/etc/printcap mode=0644
#########################################################################
shellcommands:
Hr00::
"/usr/libexec/locate.updatedb"
"/usr/bin/makewhatis /usr/share/man:/usr/X11R6/man"
###
#
# END cf.bsd
#
###
#########################################################
#
# This is a cfd config file
#
# The access control here follows after any tcpd
# control in /etc/hosts.allow and /etc/hosts.deny
#
#########################################################
#
# Could import cf.groups here and use a structure like
# in cfengine.conf, cf.main, cf.groups
#
control:
public = ( /usr/local/publicfiles )
almost_public = ( /usr/local/almostpublicfiles )
cfrunCommand = ( /iu/nexus/ud/mark/comp/Tests/cfrun-command )
MaxConnections = ( 10 )
#########################################################
admit: # or grant:
$(public) *
$(almost_public) *.iu.hioslo.no *.gnu.ai.mit.edu
/etc/passwd *.iu.hioslo.no
#
# Who can exec cfengine remotely?
#
$(cfrunCommand) *.iu.hioslo.no
#########################################################
deny:
$(public)/special *.moneyworld.com
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. 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. --no-lock) Ignore locks when running. --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. --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-splay) Switch off host splaying (sleeping). --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.
Cfengine uses a simple protocol for communicating via a streams-based tcp connection. This section documents the protocol for anyone who might want to create their own clients or server components to interface with cfengine. Several transfers use a standard buffer size of 4096 bytes. The get-file service uses a character based read interface in which the buffer size is not directly relevant; the size of the get-file buffer is dictated by client-side disk blocksizes.
Each new connection to the remote server daemon must begin with a verification or `login' string whereby the client identifies itself to the server. This information is used to verify the connection by using a reverse DNS lookup and then a double-reverse lookup. This is the basis of hostname authentication.
The various services are listed below:
AUTH client-name server-name SYNCH long-time-string STAT filename reply with OK: <stat-reply-string>
AUTH client-name server-name GET filename reply with <stream>, break on zero chars received or BAD: <message>
AUTH client-name server-name
OPENDIR dir-name
reply with <stream>, break on zero chars received
In the future it might be useful to stat the file automatically here and cache the value client-side.
AUTH client-name server-name
EXEC option-string
CLASSES stream terminated with -- (CFD_TERMINATE)
reply with <stream>, break on zero chars received
MD5 filename 16 byte sequence
reply with CFD_TRUE for no match (copy) or CFD_FALSE for match (no copy)
OK: message BAD: message
A return prefix of `BAD' implies a failure server-side and the client-side wrapper functions return -1 in this case. The server daemon currently runs single threaded for all requests except GetFile. Since cfd uses heavyweight processes for general applicablity this avoids unnecessary forking and context switching which would download the server. An upper limit on the number of forks which may be performed is set in the config file. This is mananged using the SIGCHLD signal and a pair of arrays in the master processes (This approach is used to avoid shared memory and semaphore usage which is not portable to many older BSD derivative systems).
exclude= include= -L option .cfdisabled AppendToLineIfNotContains,
example AutoCreate BeginGroupIfFileExists
BeginGroupIfFileIsNewer
cfd daemon CFINPUTS variable
cfrun program,
cfrun program cfrunCommand variable
cfwatcher program
ChecksumDatabase variable
classes,
classes,
classes copy, file sweeps
db library exclude= files, file sweeps
groups groups and time intervals
include= MaxConnections variable
rdist program cfrun files
server= tidy, file sweeps
timeout= in shellcommands
CFengine is a "software robot" that reads high-level descriptions of Unix servers, or classes of machines, and performs the operations described. CFengine is a somewhat strange/provincial framework, but essentially it just shifts the burden from performing actions on machines to documenting those actions in a description language.
I find it not much more advanced than writing shell scripts to run on your machines and then distributing those scripts via Expect or similar tool. Various people have cobbled together systems for organizing and running groups of scripts on systems. This is just a fancy way to do that. Some people will likely disagree with this assessment.
cfengine daemon - Perl-cfd is an superior implementation of the cfengine 1.x server daemon. It has been tested with cfengine v1.4.17 and v1.5.3 clients. It should work with older v1.4.x and other v1.5.x clients.
Perl-cfd is an alternate implementation of the cfengine server daemon. It has been tested with cfengine v1.4.17 and v1.5.2 clients. It should work with older v1.4.x and other v1.5.x clients but we have not verified this.
Yes, that's right. Perl-cfd supports v1.4 and v1.5 clients at the same time!
It is written in Perl 5 using a preforked server approach (similar to most http daemons). The first prototype was written using perl threads but I ran into difficulties as important parts of the perl internals are not yet thread safe. Perl 5.004 or later is required, although all development and testing has been done on 5.005.
"Perl is interpreted, what does this do to performance?" you ask. Perl-cfd performs very well. We have perl-cfd running on a Sun Ultra 2 running Solaris 2.6 serving approximately 40k client requests an hour (mostly v1.4 clients at the time of this writing; v1.5 should cause less load). Perl-cfd adds 0.1-0.2 to the load average on the machine depending upon the number and nature of the requests happening at the time.
If you care to read it, here's a copy of our LISA paper: ( PDF, PostScript, HTML)
and our LISA talk: (PDF, PostScript)Note: This isn't a tool. You won't be able to download this onto your systems and have it magically work. We've got a lot of other stuff to do, so we can't spend as much time on this as we'd like. It'll always be a reference at best. That said, we absolutely welcome suggestions for improvements (for both the web page and the scripts).
Note, this isn't complete yet. We still need to put up a copy of our database table naming scheme so it make sense. Check back soon.
Here's our cfengine software module
Here's our cfengine patch module
Both of those scripts depends on a configuration file in the form of this .pm file.
These are not current as of 3/21/2001. check back soon.
Here's the cgi script we're using to manipulate our software database. This one's not as well documented as the last two, and it depends heavily on our specific database table layout. At some point, I'll probably rewrite this in java or something (it's a good excuse to learn it). This one authenticates you by setting a cookie in your browser that gives the cgi the info it needs to find the actual database password in a file on the web server. This was the only way I could think of to keep from passing the password to the cgi through the URL.
A very convenient characteristic 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 flexibility. Fortunately most editing operations involve appending a few lines to a file, commenting out certain lines or deleting lines.
For example, some administrators consider the finger service to be a threat to security and want to disable it. This could be done as follows.
editfiles: { /etc/inetd.conf HashCommentLinesContaining "finger" }
Commands containing the word `Comment' are used to `comment out' certain
lines from a text-file--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 also 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. The 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. You can use the editing feature to update
and distribute the message of the day file, or to configure sendmail, (see FAQS
and Tips in the Reference manual).
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 with the 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.
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:
Cfengine - Wikipedia, the free encyclopedia weak
Ken talks to Ian Southam about using cfengine to manage your servers.
Overview of CFengine
http://en.wikipedia.org/wiki/Cfengine
The Promise of System Configuration: Google Tech Talks - November 5, 2008 http://www.youtube.com/watch?v=4CCXs4Om5pY
A simple overview of cfengine: Debian Administration http://www.debian-administration.org/articles/223
Centralized Host Configuration With Cfengine: Sun BigAdmin System Administration
Portal http://www.sun.com/bigadmin/features/articles/cfengine_part1.html http://www.sun.com/bigadmin/features/articles/cfengine_part2.html
Ian Southam: http://www.schubergphilis.com/
This document was generated on 10 July 1997 using the texi2html translator version 1.50.
Copyright © 1996-2009 by Dr. Nikolai Bezroukov. www.softpanorama.org was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. Submit comments This document is an industrial compilation designed and created exclusively for educational use and is placed under the copyright of the Open Content License(OPL). Site uses AdSense so you need to be aware of Google privacy policy. Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.
Disclaimer:
Last modified: November 06, 2009