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.
acl: class:: { acl-alias action }
Cfengine's ACL
feature is a common interface for managing
filesystem access control lists (ACLs). An access control list is an
extended file permission. It allows you to open or close a file to a
named list of users (without having to create a group for those users);
similarly, it allows you to open or close a file for a list of groups.
Several operating systems have access control lists, but each typically
has a different syntax and different user interface to this facility,
making it very awkward to use. This part of a cfengine configuration
simplifies the management of ACLs by providing a more convenient user
interface for controlling them and--as far as possible--a common
syntax.
An ACL may, by its very nature, contain a lot of information. Normally
you would set ACLs in a files
command, See section files, or a
copy
command, See section copy. It would be too cumbersome to repeat
all of the information in every command in your configuration, so
cfengine simplifies this by first associating an alias together with a
complex list of ACL information. This alias is then used to represent
the whole bundle of ACL entries in a files
or copy
command. The form of an ACL is similar to the form of an
editfiles
command. It is a bundle of information concerning a
file's permissions.
{ acl-alias method:overwrite/append fstype:posix/solaris/dfs/afs/hpux/nt acl_type:user/group:permissions acl_type:user/group:permissions ... }
The name acl-alias can be any identifier containing alphanumeric characters and underscores. This is what you will use to refer to the ACL entries in practice. The method entry tells cfengine how to interpret the entries: should a file's ACLs be overwritten or only adjusted? Since the filesystems from different developers all use different models for ACLs, you must also tell cfengine what kind of filesystem the file resides on. Currently only solaris and DCE/DFS ACLs are implemented.
NOTE: if you set both file permissions and ACLs the file permissions override the ACLs.
An access control list is build of any number of individual access control entries (ACEs). The ACEs has the following general syntax:
acl_type:user/group:permissions
The user or group is sometimes referred to as a key.
For an explanation of ACL types and their use, refer to your local
manual page. However, note that for each type of filesystem, there are
certain entries which must exist in an ACL. If you are creating a new
ACL from scratch, you must specify these. For example, in solaris ACLs
you must have entries for user
, group
and other
.
Under DFS you need what DFS calls a user_obj
, group_obj
and an other_obj
, and in some cases mask_obj
. In cfengine
syntax these are called user:*:
, other:*:
and
mask:*:
, as described below. If you are appending to an existing
entry, you do not have to re-specify these unless you want to change
them.
Cfengine can overwrite (replace) or append to one or more ACL entries.
overwrite
method:overwrite
is the default. This sets the ACL according to
the specified entries which follow. The existing ACL will be
overwritten completely.
append
method:append
adds or modifies one or more specified ACL entries.
If an entry already exists for the specified type and user/group, the
specified permission bits will be added to the old permissions. If there
is no ACL entry for the given type and user/group, a new entry will be
appended.
If the new ACL exactly matches the existing ACL, the ACL is not replaced.
The individual bits in an ACE may be either added subtracted or set equal to a specified mask. The `+' symbol means add, the `-' symbol subtract and `=' means set equal to. Here are some examples:
acltype:id/*:mask user:mark:+rx,-w user:ds:=r user:jacobs:noaccess user:forgiven:default user:*:rw group:*:r other:*:r
The keyword noaccess
means set all access bits to zero for that
user, i.e. remove all permissions. The keyword default
means
remove the named user from the access crontrol list altogether, so that
the default permissions apply. A star/asterisk in the centre field
indicates that the user or group ID is implicitly specified as of the
owner of the file, or that no ID is applicable at all (as is the case for `other').
Under Solaris, the ACL type can be one of the following:
user group mask other default_user default_group default_mask default_other
A user or group can be specified to the user, group, default_user and default_group types. Solaris ACL permissions are the normal UNIX permissions bits `rwx', where:
r - Grants read privileges. w - Grants write privileges. x - Grants execute privileges.
In DCE, the ACL type can be one of the following:
other mask any unauthenticated user group foreign_other foreign_user foreign_group
The user
, group
, foreign_user
and foreign_group
types require that you specify a user or group. The DCE documentation
refers to types user_obj
, group_obj
and so on. In the
cfengine implementation, the ugly `_obj' suffix has been dropped to
make these more in keeping with the POSIX names. user_obj::
, is
equivalent to user:*:
is cfengine. The star/asterisk implies that
the ACL applies to the owner of the file object.
DFS permissions are comprised of the bits `crwxid', where:
c - Grants control privileges, to modify an acl. r - Grants read privileges. w - Grants write privileges. x - Grants execute privileges. i - Grants insert privileges. d - Grants delete privileges.
See the DCE/DFS documentation for more information about this.
It is not possible to set ACLs in foreign cells currently using cfengine, but you can still have all of your ACL definitions in the same file. You must however arrange for the file to be executed on the server for the cell concerned. Note also that you must perform a DCE login (normally as user `cell_admin') in order to set ACLs on files which are not owned by the owner of the cfengine-process. This is because you must have a valid security ticket.
NT ACEs are written as follows:
acl_type:user/group:permissions:accesstype
The actual change consists of the extra field containing the access type. A star/asterisk in the field for user/group would normally imply that the ACL applies to the owner of the file object. However this functionality is as of today not yet implemented.
In NT, the ACL type can be one of the following:
user group
Both types require that you specify the name of a user or a group.
NT permissions are comprised of the bits `rwxdpo', where:
r - Read privileges w - Write privileges x - Execute privileges d - Delete privileges p - Privileges to change the permissions on the file o - Privileges to take ownership of the file
In addition to any combination of these bits, the word noaccess
or default
can be used as explained in the previous section. NT
comes with some standard, predefined permissions. The standards are only
a predefined combination of the different bits specified above and are
provided with cfengine as well. You can use the standards by setting the
permission to read
, change
or all
. The bit
implementation of each standard is as on NT:
read - rx change - rwxd all - rwxdpo
where the bits follow the earlier definition. The keywords mentioned above can only be used alone, and not in combination with `+', `-', `=' and/or other permission bits.
NT defines several different access types, of which only two are used in connection with the ACL type that is implemented in cfengine for NT. The access type can be one of the following:
allowed denied
Intuitively, allowed
access grants the specified permissions to
the user, whilst denied
denies the user the specified
permissions. If no access type is specified, the default is
allowed
. This enables cfengine's behaviour as on UNIX systems
without any changes to the configuration file. If the permissions
noaccess
or default
is used, the access type will be
irrelevant.
Here is an example of a configuration file for an NT ACL:
control: actionsequence = ( files ) domain = ( iu.hioslo.no ) files: $(HOME)/tt acl=acl_alias1 action=fixall acl: { acl_alias1 method:overwrite fstype:nt user:gustafb:rwx:allowed user:mark:all:allowed user:toreo:read:allowed user:torej:default:allowed user:ds2:+rwx:allowed group:dummy:all:denied group:iu:read:allowed group:root:all:allowed group:guest:dpo:denied }
Here is an example of a configuration file for one Solaris ACL and one DCE/DFS ACL:
control: actionsequence = ( files ) domain = ( iu.hioslo.no ) files: $(HOME)/tt acl=acl_alias1 action=fixall /:/bigfile acl=acl_alias2 action=fixall acl: { acl_alias1 method:overwrite fstype:posix user:*:rwx user:mark:=rwx user:sowille:=rx user:toreo:=rx user:torej:default user:ds2:+rwx group:*:rx group:iu:r group:root:x mask:*:rx other:*:rx default_user:*:=rw default_user:mark:+rwx default_user:ds:=rwx default_group::=r default_group:iu:+r default_mask::w default_other::rwx } { acl_alias2 method:overwrite fstype:dfs user:*:rwxcid group:*:rxd other:*:wxir mask:*:rxw user:/.../iu.hioslo.no/cell_admin:rc group:/.../iu.hioslo.no/acct-admin:rwxcid user:/.../iu.hioslo.no/root:rx }
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 module:name # execute a user-defined module )
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.
module
module:mytests.class1.class2.class3 "module:mytests.class1.class2.class3 arg1 arg2 .."declares a user defined module which can potentially set the classes class1 etc. Classes returned by the module must be declared so that cfengine knows to pay attention to rules which use these classes when parsing. Note might actually be preferable to define classes returned by modules under
AddInstallables
which is equivalent. If
arguments are passed to the module, the whole string must be quoted like
a shellcommand. @xref{Writing plugin modules,Writing plugin modules,Writing plugin modules,cfengine-Tutorial}. Whether or not these
classes become set or not depends on the behaviour of your module. The
classes continue to apply for all actions which occur after the module's
execution. The module must be owned by the user executing cfengine or
root (for security reasons), it must be named
`module:module-name' and must lie in a special directory,
See section moduledirectory.
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. @xref{Defining classes,Defining classes,Defining classes,cfengine-Tutorial}.
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/classes.
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.
AddInstallable = ( list of identifiers )
Some actions in your cfengine program will be labelled by classes which
only become defined at run time using a define=
option. Cfengine
is not always able to see these classes until it meets them and tries to
save space by only loading actions for classes which is believes will
become defined at some point in the program. This can lead to some
actions being missed if the action is parsed before the place where the
class gets switched on, since cfengine is a one-pass interpreter,. To
help cfengine determine classes which might become defined during
a run, you can declare them in this list. It does no harm to declare
classes here anyway.
Here is an example where you need to declare a class because of the ordering
of the actions.
control: AddInstallable = ( myclass ) files: myclass:: /tmp/test mode=644 action=fixall copy: /tmp/foo dest=/tmp/test define=myclass
If we remove the declaration, then when cfengine meets the files command, it skips it because it knows nothing about the class `myclass'---when the copy command follows, it is too late. Remember that imported files are always parsed after the main program so definitions made in imported files always come later than things in the main program.
BinaryPaddingChar = ( \0 )
This specifies the type of character used to pad strings of unequal
length in editfiles
during binary editing. The default value is
the space character, since this is normally used to edit filenames
or text messages within program code.
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.
This parameter determines the default form of copying for all copy operations
parsed after this variable. The legal values are
ctime
(intial default), mtime
, checksum
and binary
.
e.g.
DefaultCopyType = ( mtime )
If this parameter is set to true, cfengine will delete mail files on mailservers which do not have a name belonging to a known user id. This does not include lock files.
If this parameter is set to true, cfengine will delete files on mailservers whose names do not correspond to a known user name, but might be owned by a known user.
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
. The domain is also used
implicitly by other matching routines. You should define the domain as
early as possible in your configuration file so as to avoid problems,
especially if you have the strange practice of naming hosts with their
fully qualified host names since groups which use fully qualified names
can fail to be defined if cfengine is not able to figure out the domain name.
DryRun = ( on/off )
This variable has the same effect as the command line options --dry-run or -n. It tells cfengine to only report what it should do without actually doing it.
classes:: DryRun = ( on )
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 10000 bytes. If you don't like this
feature, simply set the value to be a very large number or to zero.
If the value is zero, cfengine will ignore it.
This list is used to define a global list of names or patterns which are to be excluded from copy operations. For example
ExcludeCopy = ( *~ *% core )
The same facility can be specified for each individual
link operation using the exclude
option See section copy.
Note that all entries defined under a specified class are valid only as long as that class is defined. For instance
class:: ExcludeCopy = ( pattern )
would define a pattern which was only valid when class is defined.
This list is used to define a global list of names or patterns which are to be 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. @xref{Spamming and security,Spamming and security,Spamming and security,cfengine-Tutorial}. 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. @xref{NFS resources,NFS resources,NFS resources,cfengine-Tutorial}. 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.
Suppose you want to create mount home directories under
$(mountpattern)/home
and make subdirectories for staff and
students. Then you would be tempted to write:
HomePattern = ( home/staff home/students )
Unfortunately this is not presently possible. (This is, in principle, a bug which should be fixed in the future.) What you can do instead is to achieve the same this as follows:
MountPattern = ( /$(site)/$(host) /$(site)/$(host)/home ) HomePattern = ( staff students )
This parameter controls the global value of the IfElapsed parameter, @xref{Spamming and security,Spamming and security,Spamming and security,cfengine-Tutorial}. 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.
Inform = ( on/off )
This variable switches on the output level whereby cfengine
reports changes it makes during a run. Normally only
urgent messages or clear errors are printed. Setting
Inform
to on
makes cfengine report on
all actions not explicitly cancelled with a `silent' option.
To set this output level one writes:
classes:: Inform = ( on )
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 may be used to define a number of extensions which are regarded as being plain files by the system. As part of the general security checking cfengine will warn about any directories which have names using these extensions. They may be used to conceal directories.
FileExtensions = ( c o gif jpg html )
Specify an alternative directory for keeping cfengine's lock data. This defaults to `/var/run/cfengine' or `/etc/cfengine'.
LockDirectory = ( /etc/cfengine )
Specify an alternative directory for keeping cfengine's log data. This defaults to `/var/run/cfengine' or `/etc/cfengine'.
LogDirectory = ( /etc/cfengine )
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.
moduledirectory = ( directory for plugin modules )
This is the directory where cfengine will look for plug-in modules for the actionsequence, See section actionsequence. Plugin modules may be used to activate classes using special algorithms. @xref{Writing plugin modules,Writing plugin modules,Writing plugin modules,cfengine-Tutorial}.
mountpattern = ( mount-point )
The mountpattern
list is used by the cfengine model for mounting
nfs filesystems. @xref{NFS resources,NFS resources,NFS resources,cfengine-Tutorial}. 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 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 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.
If enabled, this option causes cfengine to detect and
disable files which have purely non-alphanumeric
filenames, i.e. files which might be accidental or
deliberately concealed. The files are then marked
with a suffix .cf-nonalpha
and are rendered
visible.
NonAlphaNumFiles = ( on )
These files can then be tidied by searching for the suffix. Note that alphanumeric means ascii codes less than 32 and greater than 126.
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, define 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
@xref{Disabling and the file repository,Disabling and the file repository,Disabling and the file repository,cfengine-Tutorial}.
SecureInput = ( on )
If this is set cfengine will not import files which are not owned by the uid running the program, or which are writable by groups or others.
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.
ShowActions = ( on )
This causes cfengine to produce detailed output of what action is being carried out as part of the prefix information during output. This is intended only for third party tools which collect and parse the cfengine output. It will be of little interest to humans.
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.
SplayTime = ( time-in-minutes )
This variable is used to set the maximum time over which cfengine will share its load on a server, @xref{Splaying host times,Splaying host times,Splaying host times,cfengine-Tutorial}.
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.
SuspiciousNames = ( .mo lrk3 )
Filenames in this list are treated as suspicious and generate a warning as cfengine scans directories. This might be used to detect hacked systems or concealed programs. Checks are only made in directories which cfengine scans in connection with a command such as files, tidy or copy.
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.
Syslog = ( on/off )
This variable activates syslog logging of cfengine output at the `inform' level.
To set this output level one writes:
classes:: Syslog = ( on )
timezone = ( 3-character timezone )
The timezone variable is a list of character strings which define your local timezone. Normally you will only need a single timezone, but sometimes there are several aliases for a given timezone e.g. MET and CET are synonymous. 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. It expands to the first item in your list.
shellcommands: "echo ${timezone} | mail ${sysadm}"
TimeOut = ( 10 )
The default timeout for network connections is 10 seconds. This is too short on some routed networks. It is not permitted to set this variable smaller than 3 seconds or larger than 60 seconds.
Verbose = ( on/off )
This variable switches on the output level whereby cfengine reports everything it does during a run in great detail. Normally only urgent messages or clear errors are printed, See section Inform. This option is almost equivalent to using the --verbose of -v command-line options. The only difference is that system environment reporting information, which is printed prior to parsing, is not shown. To set this output level on selected hosts one writes:
classes:: Verbose = ( on )
For related more limited output, See section Inform.
Warnings = ( on/off )
This variable switches on the parser-output level whereby cfengine reports non-fatal warnings. This is equivalent to setting the command line switch --no-warn, or -w. To set this output level on selected hosts one writes:
classes:: Warnings = ( on )
If this parameter is set to true, cfengine will warn about mail files on mailservers which do not have a name belonging to a known user id. This does not include lock files.
If this parameter is set to true, cfengine will warn about files on mailservers whose names do not correspond to a known user name, but might be owned by a known user.
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.
Caution should be exercised in copying files which change rapidly in size. This can lead to file corruption, if the size changes during copying. Cfengine attempts to prevent this during remote copies.
The syntax summary is:
copy: class:: master-file dest=destination-file mode=mode owner=owner group=group action=silent/fix backup=true/false repository=backup directory stealth=true/on/false/off timestamps=preserve/keep symlink=pattern include=pattern exclude=pattern ignore=pattern filter=filteralias recurse=number/inf/0 type=ctime/mtime/checksum/sum/byte/binary linktype=absolute/symbolic/relative/hard/none/copy typecheck=true/on/false/off define=class-list(,:.) elsedefine=class-list(,:.) force=true/on/false/off size=size limits server=server-host secure=true/false purge=true/false syslog=true/on/false/off inform=true/on/false/off
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.
repository
Repository
variable, on an item
by item basis. If set to "off" or "none" it cancels the value of a global repository.
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.
stealth
timestamps
tar
command.
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
ignore
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'.
secure
size
numberbytes numberkbytes numbermbytesOnly 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. If this value
is set to copy
or none
, symbolic links will be replaced
by actual copies of the files they point to. Note that for directories,
this option is ignored.
typecheck
define
purge
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 define=classlist syslog=true/on/false/off inform=true/on/false/off
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 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. A list of classes may optionally be defined here if a directory is created.
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 define=classlist syslog=true/on/false/off inform=true/on/false/off repository=destination directory
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,
See section files,
files: some:: /var/spool/cron/at.allow =0644 N [root] [wheel] touch
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 kilobytes 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.
If a disable command results in action being taken by cfengine, an optional
list of classes becomes can be switched on with the aid of a statement
define=classlist
in order to trigger knock-on actions.
The repository
declaration allows a local override of the
Repository
variable, on an item by item basis. If set to "off" or
"none" it cancels the value of a global repository and leaves the disabled
file in the same directory.
Performs ascii (line-based) editing on text-files or limited binary
editing of files.
If editing a file which has hard links to it, be aware that
editing the file will destroy the hard link references. This is also the
case with shell commands. You should avoid hard links whenever possible.
The form of an editing command is
editfiles
can also search directories recursively through directories
and edit all files matching a pattern, using Include
, Exclude
, and
Ignore
(see Recursive File Sweeps in the tutorial).
editfiles: class:: { file-to-be-edited action "quoted-string..." } { directory-to-be-edited Recurse "inf" Filter "filteralias" Include ".cshrc" Ignore "bin" Ignore ".netscape" 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 POSIX extended regular expressions. @xref{Regular expressions,Regular expressions,Regular expressions,cfengine-Tutorial}.
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'.
Backup quoted-string
Repository quoted string
Repository
variable, on an item
by item basis. If set to "off" or "none" it cancels the value of a global repository.
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
CatchAbort
LocateLineMatching
)
will jump to the first instance of this marker instead of completely
aborting an edit if this keyword occurs in an editing script.
You can catch the exceptions thrown by the following commands:
CommentNLines
,CommentToLineMatching
,DeleteNLines
,DeleteToLineMatching
,
HashCommentToLineMatching
,IncrementPointer
,
LocateLineMatching
,PercentCommentToLineMatching
,
RunScriptIf(No)LineMatching
,UnCommentNLines
.
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.
DefineClasses "class1:class2:..."
DeleteLinesAfterThisMatching quoted-regex
DeleteLinesContaining quoted-string
DeleteLinesMatching quoted-regex
DeleteLinesStarting quoted-string
DeleteNLines quoted-string
DeleteToLineMatching quoted-regex
EmptyEntireFilePlease
ElseDefineClasses
DefineClasses
EndGroup
EndLoop
ForEachLineIn
Filter filteralias
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
Inform quoted-string
InsertFile quoted-string
InsertLine quoted-string
LocateLineMatching quoted-string
WarnIfNoLineMatching
so that you can get an explicit warning, even
out of verbose mode.
PercentCommentLinesContaining quoted-string
PercentCommentLinesMatching quoted-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
Recurse digit/inf
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
.
Syslog quoted-string
Umask quote mode
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
WarnIfNoLineMatching reg-ex
WarnIfNoLineStarting quoted-string
WarnIfNoSuchLine quoted-regex
A limited number of operations can also be performed on purely binary files, e.g.
compiled programs, in order to search for strings or viral code, or to modify
strings within a program. Binary mode is a mutually exclusive, separate mode to normal
editing. The limit on the size of binary files is set by editbinaryfilesize
in control
.
ReplaceAll regex With literal
BinaryPaddingChar
in control
.
Padding with a null byte would lead to corruption of text within a program.
WarnIfContainsString regex/literal
WarnIfContainsFile filename
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 ignore=pattern include=pattern exclude=pattern filter=filter alias define=classlist elsedefine=classlist checksum=md5 flags=BSD flags syslog=true/on/false/off inform=true/on/false/off
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.
A file object is interpreted as a directory if you write it in the
following form: `/directory-path/.'. i.e. a trailing dot signifies
a directory. This then becomes the same as the directory
command.
/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.
flags=BSD flags
chflags
documentation for this. For
example,
/tmp/flags.01 mode=0600 owner=0 group=0 flags=uappnd,uchg,uunlnk,nodump,opaque,sappnd,schg,sunlnk action=touch
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 compress alertThe 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. Alert is like
-print
in the find command, it triggers on the existence
of files which have not been ignored, excluded or filtered. This should
normally be used together filter
, in order to locate files of particular types.
include=wildcard/pattern
exclude=wildcard/pattern
include=
list.
ignore
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.)
define=classlist
checksum=md5
ChecksumDatabase
should be set in control:
to the filename of a database file
which is used to cache checksum values. Note that it is also
possible to use a database file for cfd's remote copying by checksum.
If you use the same file for both purposes you risk losing warnings.
Security warning messages are issued only once and the value in the database is then
changed to the new value of the file automatically i.e. the behaviour is similar to
that of setuid root program detection, @xref{Checksum Databases,Checksum Databases,Checksum Databases,cfengine-Tutorial}.
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
directive
If you want to check the files of all the users who have their login
areas on the current host, you can use a wildcard directive home
instead of a directory name. In this case the file action iterates over
all home directories physically on the current host. The home
directories are, of course, located by searching for files which match
$(mountpattern)/$(homepattern)
i.e. the values which are specified in the control
part of the
program. For example the following line is a very useful service to
ignorant users.
files: any:: home mode=o-w r=inf act=fixall
It ensures automatically that no user has files which can be written to by other arbitrary users.
As a corollary to this, you may write something like
any:: home/www mode=a+r fixall
to specify a special subdirectory of every users' home directory. This statement would check that all of the files in users' world wide web directories were readable for everyone.
If you do not want to explicitly state the owner or group of a file you may simply omit the group or owner options.
/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, permissions and ownership will not be modified
if the file already exists. If the file does not exist, the
attributes are set to the values specified, or to the default
values of 0644
.
A filter is a way of selecting or pruning during a search over files or processes. Since filter rules could apply to several objects, cfengine allows you to define filter conditions as separate objects to be applied in different contexts.
Filter objects can be used in copy
, editfiles
,
files
, tidy
and processes
. In most cases one
writes
.. filter=<i>filteralias</i>
in the appropriate command. The exception is editfiles
, where the syntax
is
{ .. Filter "filteralias" .. }
Example:
files: /tmp filter=testfilteralias action=alert r=inf
Filters are defined in a separate section. Filters for files and processes are defined together. They differ only in the criteria they contain. Here is are examples of file filters:
Filters: { filteralias1 Owner: "mark|cell|motd" Group: "ecg|mark" Mode: "700" FromCtime: "date(2000,1,1,0,0,0)" # absolute date ToCtime: "now" FromMtime: "tminus(1,0,0,2,30,0)" # relative "ago" from now ToMtime: "inf" # end of time FromAtime: "date(1997,2,22,0,0,0)" ToAtime: "inf" FromSize: "10000" # File size interval ToSize: "10mb" ExecRegex: "/usr/bin/file(.*ascii.*)"# Result from "files" command Type: "dir|link" # reg|link|dir|socket|fifo|door|char|block NameRegex: ".*.asc" # regex matching file name IsSymLinkTo: "/dev/null" # True if file is a link to object name regex Result: "Type" # Result which shouldbe returned } ######################################### { testfilteralias2 ExecProgram: "/bin/ls $(this)" # True if the program returns true. $(this) is the current object } ######################################### { testfilteralias3 Owner: "mark" }
Filters are evaluated like classes. In fact, the filtering works by evaluating the class attributes for each file.
File filters:
Owner:
"none"
for users or groups which are undefined in
the system passwd/group file.
mode=
strings in the files command. This test returns
true if the bits which are specified as `should be set' are indeed
set, and those which are specified as `should not be set' are not set.
Atime:,Ctime:,Mtime:
(year,month,day,hour,minutes,seconds)This may be evaluated as two functions: date() or tminus() which give absolute times and times relative to the current time respectively. In addition, the words now and inf may be used. e.g.
FromCtime: "date(2000,1,1,0,0,0)" # absolute date ToCtime: "now" FromMtime: "tminus(1,0,0,2,30,0)" # relative "ago" from now ToMtime: "inf" # end of time
Type:
reg|link|dir|socket|fifo|door|char|block
ExecRegex:
NameRegex:
IsSymLinkTo:
ExecProgram:
Result:
Process filters:
PID:
PPID:
PGID:
RSize:
VSize:
Status:
Command:
(From/To)TTime:
(From/To)STime:
TTY:
Priority:
Threads:
Result:
Examples: processes started between 18th Nov 2000 and now.
{ filteralias FromSTime: "date(2000,11,18,0,0,0)" ToSTime: "now" }
All processes which have accumulated between 1 and 20 hours of CPU time.
{ filteralias FromTTime: "accumulated(0,0,0,1,0,0)" ToTTime: "accumulated(0,0,0,20,0,0)" }
Here is an example filter to search for all files which are either directories or links, or any kind of file owned by mark, in group cfengine.
control: actionsequence = ( files ) files: /tmp filter=testfilteralias action=alert r=inf /cfengine filter=testfilteralias action=fixall r=inf mode=644 filters: { testfilteralias Owner: "mark" Group: "cfengine" Type: "dir|link" Result: "Type|(Owner.Group)" # Both owner AND group required correct }
Find all ELF executables using data from the Unix file
command.
Caution, this takes a long time if used indescriminately.
control: actionsequence = ( files ) files: /tmp filter=testfilteralias action=alert r=inf /cfengine filter=testfilteralias action=fixall r=inf mode=644 filters: { testfilteralias ExecRegex: "/bin/file (.*ELF.*)" Result: "ExecRegex" }
Here is an example which warns of any process coupled to a terminal started in November:
control: actionsequence = ( processes ) filters: { filteralias FromSTime: "date(2000,11,0,0,0,0)" ToSTime: "date(2000,11,30,0,0,0)" TTY: ".*pt.*" Result: "TTY.STime" } processes: "." filter=filteralias action=warn
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 ) notthis = ( !this )
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.
interfaces: classes:: interfacename netmask=netmask broadcast=broadcast
If you have more than one network interface, or you do not wish to use the
default interface name, this section may be used to define further
interfaces to be checked. This feature can replace the older method
of setting netmask and broadcast address in control:
. If the netmask
variable is not set, cfengine ignores the default interface configuration.
Example:
interfaces: "le1" netmask=255.255.255.0 broadcast=ones "le2" netmask=255.255.255.0 broadcast=ones
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 ignore=pattern action=silent deadlinks=kill/force define=classlist nofile=kill/force syslog=true/on/false/off inform=true/on/false/off
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, or by moving to a repository, 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
nofile=kill/force
exclude=pattern
ignore
recurse=number/inf
inf
means infinite recursion. Cfengine also ignores
files and directories in the ignore list See section ignore.
define=classlist
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 mailcheck
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 infohost:source-directory destination mode=mode
For example
physics:: # old syntax libraryserver:/$(site)/libraryserver/data1 /$(site)/libraryserver/data1 ro # consistent syntax libraryserver:/$(site)/libraryserver/data2 /$(site)/libraryserver/data2 mode=ro host:/foo /foo mode=rw,bg,hard,intr
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.
If no mode is given, read-write is assumed.
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" useshell=true/false/dumb owner=restart-uid group=restart-gid chroot=directory chdir=directory umask=mask signal=signal name matches=number define=classlist action=signal/do/warn/bymatch include=literal exclude=literal syslog=true/on/false/off inform=true/on/false/off 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 2Note that cfengine will not attempt to signal or restart processes 0 to 3 on any system since such an attempt could bring down the system. The only exception is that the hangup (hup) signal may be sent to process 1 (init) which normally forces init to reread its terminal configuration files.
restart "shell command"
owner=,group=
chroot
chdir
useshell=true/false/dumb
matches=number
matches=<6 # warn number of matches is greater than or equal to 6 matches=1 # warn if not exactly 1 matching process matches=>2 # warn if there are less than or equal to 2 matching processes
include=literal
exclude=literal
define=classlist
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.
If the option is set to bymatch
, then signals are only sent
to the processes if the matches criteria fail.
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.
You can arrange for signals to be sent, only if the number of matches
fails the test. The action=bymatch
option is used for this.
For instance, to kill process `XXX' only if the number
of matches is greater than 20, one would write:
processes: "XXX" matches=<20 action=bymatch signal=kill
See also filters See section filters, for more complex searches.
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 useshell=true/false umask=octal number owner=uid group=gid define=class-list background=false/true chdir=directory chroot=directory preview=true/false
The user and group ID's of the process can be set to restrict the permissions of the shellcommand to another user only if cfengine is executed by root. 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"
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. @xref{Iterating over lists,Iterating over lists,Iterating over lists,cfengine-Tutorial}. If you are iterating over a list, the timeout applies to each separate iteration, not to the sum of all the iterations.
The chroot
option changes the process root directory of the
command, creating a `sandbox' which the process cannot escape from. Best
used together with a change of owner, since a root process can break out
of such a confinement in principle. The chdir
option changes the
current working directory of the restarted process.
The preview
option means that the shellcommand will also be executed during
the --dry-run
(-n
) options. This allows cfengine to be more aware
of the results of scripts which define classes. This option should be used
with care.
Scripts should conform to the protocol of not executing unnecessary commands
when the classes opt_dry_run
is defined.
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/all/sub links=stop/keep/traverse/tidy define=classlist elsedefine=classlist syslog=true/on/false/off inform=true/on/false/off filter=filter alias include=pattern exclude=pattern
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 may 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
option. You need both to make links
to directories disappear.
rmdirs=true/false/all/sub
dirlinks
, so that even
links which point to empty directories will be removed.
If this is set to `sub' then the topmost directory will not
be removed, only sub-directories.
define=classlist
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 deletedir=true/false deletefstab=true/false force=true/false
The options allow you to temporarily unmount a directory without actually
removing it from the filesystem table. The option force
is not currently
implemented and will likely have to be system dependent.
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.
Some users do not mount filesystems on a directory of the same name as the source directory. This can lead to confusion. Note, if you have problems removing a mounted filesystem, try using the mountpoint of the filesystem, rather than the name of the filesystem itself, in the unmount command.
Go to the first, previous, next, last section, table of contents.