Cfengine-Reference ****************** Copyright (C) 1995/96/97/98/99/2000 Mark Burgess Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies. Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided also that the section entitled "GNU General Public License" is included exactly as in the original, and provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one. Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that the section entitled "GNU General Public License" may be included in a translation approved by the author instead of in the original English. This manual corresponds to CFENGINE Edition 1.6.3 for version 1.6.3 as last updated 1 December 2000. Command reference ***************** In this section you will find each facet of a cfengine program listed together with an appropriate explanation. The commands are presented in alphabetical order for ease of lookup. Use this section in conjunction with the example program *Note Example configuration file::. acl === 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, *Note files::, or a `copy' command, *Note 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. Access control entries ---------------------- 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'). Solaris ACLs ------------ 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. DFS ACLs -------- 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 ACLs ------- 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. ACL Example =========== 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 } ACL Example ----------- 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 } binservers ========== The `binservers' declaration need only be used if you are using cfengine's model for mounting NFS filesystems. This declaration informs hosts of which other hosts on the network possess filesystems containing software (binary files) which client hosts should mount. This includes resources like programs in `/usr/local' and so on. A host may have several binary servers, since there may be several machines to which disks are physically attached. In most cases, on a well organized network, there will be only one _architecture server_ per UNIX platform type, for instance a SunOS server, an ULTRIX server and so on. Binary servers are defined as follows: binservers: physics.sun4:: sunserver sunserver2 physics.linux:: linuxserver The meaning of this declaration is the following. All hosts of type `sun4' which are members of the group `physics' should mount any binaries declared in the `mountables' resource list which belong to hosts `sunserver' or `sunserver2'. Similarly all `linux' machines should mount binary filesystems in the mountables list from `linuxserver'. Cfengine knows the difference between binaries and home directories in the `mountables' list, because home directories match the pattern given by `homepattern'. *Note homepattern::. *Note homeservers::. Note that every host is a binary server for itself, so that the first binary server (and that with highest priority) is always the current host. This ensures that local filesystems are always used in preference to NFS mounted filesystems. This is only relevant in connection with the variable `$(binserver)'. broadcast ========= This information is used to configure the network interface for each host. Every local area network has a convention for determining which internet address is used for broadcast requests. Normally this is an address of the form `aaa.bbb.ccc.255' or `aaa.bbb.ccc.0'. The difference between these two forms is whether all of the bits in the last number are ones or zeroes respectively. You must find out which convention is used at your establishment and tell cfengine using a declaration of the form: broadcast: any:: ones # or zeros, or zeroes In most cases you can use the generic class `any', since all of the hosts on the same subnet have to use the same convention. If your configuration file encompasses several different subnets with different conventions then you will need to use a more specific. Cfengine computes the actual value of the broadcast address using the value specified above and the netmask *Note netmask::. control ======= The fundamental piece of any cfengine script or configuration file is the control section. If you omit this part of a cfengine script, it will not do anything! The control section is used to define certain variables, set default values and define the order in which the various actions you have defined will be carried out. Because cfengine is a declarative or descriptive language, the order in which actions appear in the file does not necessarily reflect the order in which they are executed. The 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. access ------ The `access' list is a list of users who are to be allowed to execute a cfengine program. If the list does not exist then all users are allowed to run a program. access = ( USER1 USER2 ... ) The list may consist of either numerical user identifiers or valid usernames from the password database. For example: access = ( mark aurora 22 456 ) would restrict a script to users mark, aurora and user id 22 and 456. actionsequence -------------- The action sequence determines the order in which collective actions are carried out. Here is an example containing the full list of possibilities: actionsequence = ( mountall # mount filesystems in fstab mountinfo # scan mounted filesystems checktimezone # check timezone netconfig # check net interface config resolve # check resolver setup unmount # unmount any filesystems shellcommands # execute shell commands editfiles # edit files addmounts # add new filesystems to system directories # make any directories links # check and maintain links (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' causes cfengine to compute which NFS filesystems are missing from the current host and add them. This includes editing the filesystem table, creating the mount-directory, if required. This command relies on information provided by `mountinfo', so it should normally only be called after `mountinfo'. If the filesystem already appears to be in the filesystem table, a warning is issued. `checktimezone' runs a check on the timezone defined for the shell running cfengine. `directories' executes all the commands defined under the `directories' section of the program. It builds new directories. `disable' executes all the commands defined under the `disable' section of the program. `editfiles' executes all the commands defined under the `editfiles' section of the program. `files' executes all the commands defined under the `files' section of the program. `links' executes all the commands defined under the `links' section of the program. 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' tests for the presence of the NFS-mounted mail spooling directory on the current host. The name of the mail spool directory is defined in the `mailserver' section of the cfengine program. If the current host is the same as the mailserver (the host which has the physical spool directory disk) nothing is done. Otherwise the filesystem table is edited so as to include the mail directory. `module' Normally cfengine's ability to detect the systems condition is limited to what it is able to determine while excuting predefined actions. Classes may be switched on as a result of actions cfengine takes to correct a problem. To increase the flexibility of cfengine, a mechanism has been introduced in version 1.5 which allows you to include a module of your own making in order to define or undefine a number of classes. The syntax 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. *Note Writing plugin modules: (cfengine-Tutorial)Writing plugin modules. 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, *Note moduledirectory::. `mountall' mounts all filesystems defined in the hosts filesystem table. This causes new NFS filesystems added by `addmounts' and `mailcheck' to be actually mounted. This should probably be called both before `mountinfo' and after `addmounts' etc. A short timeout is placed on this operation to avoid hanging RPC connections when parsing NFS mounted file systems. `mountinfo' builds internal information about which filesystems are presently mounted on the current host. Cfengine assumes that required-filesystems which are not found need to be mounted. A short timeout is placed on this operation to avoid hanging RPC connections when parsing NFS mounted file systems. If this times out, no further mount operations are considered reliable and are summarily cancelled. `netconfig' checks the netmask, hostname, IP address and broadcast address for the current host. The correct values for the netmask and broadcast address are set if there is an error. The defaultroute is also added to the static routing table. This does not apply to DHCP clients. `required' executes all the commands defined under the `required' section of the program. It checks for the absence of important NFS resources. `resolve' checks and corrects the DNS domain name and the order of nameservers in the file `/etc/resolv.conf'. `shellcommands' executes all the commands defined under the `shellcommands' section of the program. `tidy' executes all the commands defined under the `tidy' section of the program. `unmount' executes all the commands defined under the `unmount' section of the program. The filesystem table is edited so as to remove the unwanted filesystems and the unmount operation is executed. `processes' executes commands defined under the `processes' section of the program. Under normal circumstances this coarse ordering is enough to suit most purposes. In some cases you might want to, say, only perform half the link operations before mounting filesystems and then, say, perform the remainder. You can do this (and similar things) by using the idea of defining and undefining classes. *Note Defining classes: (cfengine-Tutorial)Defining classes. 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. *Note groups::. Programming in cfengine is about doing a lot for only a little writing. If you find yourself writing a lot, you are probably not going about things in the right way. AddClasses ---------- 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. *Note 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 -------------- 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 ----------------- 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. CopyLinks --------- 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 *Note 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. DefaultCopyType --------------- 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 ) DeleteNonUserMail ----------------- 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. DeleteNonOwnerMail ------------------ 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 = ( 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 ------ 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 ------------ 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. ExcludeCopy ----------- 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 *Note 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. ExcludeLinks ------------ 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 *Note 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. ExpireAfter ----------- This parameter controls the global value of the ExpireAfter parameter. *Note Spamming and security: (cfengine-Tutorial)Spamming and security. This parameter controls the maximum time in minutes which a cfengine action is allowed to live. After this time cfengine will try to kill the cfengine which seems to have hung and attempt to restart the action. ExpireAfter = ( TIME-IN-MINUTES ) This parameter may also be set per action in the action sequence by appending a pseudo-class called `ExpireAfterTIME'. For instance, actionsequence = ( copy.ExpireAfter15 ) sets the expiry time parameter to 15 minutes for this copy command. HomePattern ----------- HomePattern = ( LIST OF WILDCARDS ) The `homepattern' variable is used by the cfengine model for mounting nfs filesystems. *Note NFS resources: (cfengine-Tutorial)NFS resources. It is also used in the evaluation of the pseudo variable `home', See *Note files::, *Note 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. *Note 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 ) IfElapsed --------- This parameter controls the global value of the IfElapsed parameter, *Note Spamming and security: (cfengine-Tutorial)Spamming and security. This parameter controls the minimum time which must have elapsed for an action in the action sequence before which it will be executed again. IfElapsed = ( TIME-IN-MINUTES ) This parameter may also be set per action in the action sequence by appending a pseudo-class called `IfElapsedTIME'. For instance, ActionSequence = ( copy.IfElapsed15 ) sets the elapsed time parameter to 15 minutes for this copy command. Inform ------ 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 ) InterfaceName ------------- 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, *Note cfrc resource file::. FileExtensions -------------- 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 ) LockDirectory ------------- Specify an alternative directory for keeping cfengine's lock data. This defaults to `/var/run/cfengine' or `/etc/cfengine'. LockDirectory = ( /etc/cfengine ) LogDirectory ------------ Specify an alternative directory for keeping cfengine's log data. This defaults to `/var/run/cfengine' or `/etc/cfengine'. LogDirectory = ( /etc/cfengine ) LinkCopies ---------- This list is used to define a global list of names or patterns which are to be linked symbolically rather than copied. For example excludelinks = ( *.gif *.jpg ) The same facility can be specified for each individual link operation using the `symlink' option *Note 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 --------------- moduledirectory = ( DIRECTORY FOR PLUGIN MODULES ) This is the directory where cfengine will look for plug-in modules for the actionsequence, *Note actionsequence::. Plugin modules may be used to activate classes using special algorithms. *Note Writing plugin modules: (cfengine-Tutorial)Writing plugin modules. mountpattern ------------ mountpattern = ( MOUNT-POINT ) The `mountpattern' list is used by the cfengine model for mounting nfs filesystems. *Note NFS resources: (cfengine-Tutorial)NFS resources. It is also used in the evaluation of the pseudo variable `home', See *Note files::, *Note 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 ------- 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'. *Note 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 *Note broadcast::. Cfengine works out the value of the broadcast address using the value of the netmask. NonAlphaNumFiles ---------------- 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 ------- 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 ------- 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 ---------- 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' *Note Disabling and the file repository: (cfengine-Tutorial)Disabling and the file repository. SecureInput ----------- 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 ------------- 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 ------------ 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 ----------- 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/faculty ------------ site = ( SITENAME ) faculty = ( FACULTYNAME ) This variable defines a convenient name for your site configuration. It is useful for making generic rules later on, because it means for instance that you can define the name of a directory to be /$(site)/$(host)/local without having to redefine the rule for a specific site. This is a handy trick for making generic rules in your files which can be imported into a configuration for any site. `faculty' is a synonym for `site'. The two names may be used interchangeably. SplayTime --------- SplayTime = ( TIME-IN-MINUTES ) This variable is used to set the maximum time over which cfengine will share its load on a server, *Note Splaying host times: (cfengine-Tutorial)Splaying host times. Split ----- Split = ( CHARACTER ) The value of this variable is used to define the list separator in variables which are expected to be treated as lists. The default value of this variable is the colon `:'. Cfengine treats variables containing this character as lists to be broken up and iterated over in the following cases: * in the `to' field of a multiple link action, * in the `from' field of a copy action, * in the directory field of a tidy action, * in the directory field of the files action, * in the ignore action. This typically allows communication with PATH-like environment variables in the shell. suspiciousnames --------------- 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 ------ 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. *Note 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 ------ 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 -------- 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 *Note 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 ------- 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 ------- 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, *Note 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, *Note Inform::. Warnings -------- 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 ) WarnNonUserMail --------------- 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. WarnNonOwnerMail ---------------- 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. classes ======= The `classes' keyword is an alias for `groups' as of version 1.4.0 of cfengine. copy ==== 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' The destination file is the only obligatory item. This must be the name of an object which matches the type of the master object i.e. if the master is a plain file, the destination must also be the explicit name of a plain file. An implicit `copy file to directory' syntax is not allowed. Symbolic links are copied as symbolic links, plain files are copied as plain files and special files are copied as special files. If the master and image are directories then all of the child files _which are not directories_ are copied from source to destination. `mode, owner, group' The file mode, owner and group of the images are specified as in the `files' function *Note files::. `action' The action may take the values `warn' or `silent'. The default action is `fix', i.e. copy files. If `warn' is specified, only a warning is issued about files which require updating. If `silent' is given, then cfengine will copy the files but not report the fact. `force' If set to `true', this option causes cfengine to copy files regardless of whether it is up to date. `backup' If the `backup' option is set to "false", cfengine will not make a backup copy of the file before copying. `repository' This 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. 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' If set to `on' causes cfengine to preserve atime and mtime on source files during _local_ file copies. File times cannot be preserved on remote copies. This option should normally only be used together with a checksum copy, since preserving atime and mtime implies changing ctime which will force continual copying. This is a weakness in the Unix file system. Ctime cannot be preserved. Before version 1.5.0, there was a typo which made this option active on many file copies. `timestamps' If this is set to `preserve' or `keep', the times of the source files are kept by the destination files during copying. This is like the `p' option of the `tar' command. `recurse' Specifies the depth of recursion when copying whole file-trees recursively. The value may be a number or the keyword `inf'. Cfengine crosses device boundaries or mounted filesystems when descending recursively through file trees. To prevent this it is simplest to specify a maximum level of recursion. `symlink' This option may be repeated a number of times to specify the names of files, or wildcards which match files which are to be symbolically linked instead of copied. A global list of patterns can also be defined in the control section of the program *Note linkcopies::. `ignore' This works like the global ignore directive but here you may provide a private list of ignorable directories and files. Unlike include, exclude this affects the way cfengine parses directory trees. `include' This option may be repeated a number of times to specify the names of files, or wildcards which match files which are to be included in a copy operation. Specifying one of these automatically excludes everything else except further include patterns. A global list of patterns can also be defined in the control section of the program. `exclude' This option may be repeated a number of times to specify the names of files, or wildcards which match files which are to be excluded from a copy operation. A global list of patterns can also be defined in the control section of the program `excludes' override `includes'. *Note excludelinks::. `type' Normally cfengine uses the ctime date-stamps on files to determine whether a file needs to be copied: a file is only copied if the master is newer than the copy or if the copy doesn't exist. If the type is set to `checksum' or `sum', then a secure MD5 checksum is used to determine whether the source and destination files are identical. If `byte' or `binary' is specified, a byte by byte comparison is initiated. An `mtime' comparison does not take into account changes of file permissions, only modifications to the contents of the files. `server' If you want to copy a file remotely from a server, you specify the name of the server here. This must be the name of a host which is running the `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' Has an effect only when used in conjuction with copy from a remote file server. This causes cfengine to use 3DES key encryption and one-time keys on transferred data. This provides good enough privacy for the purposes of system adminstration, but it is not meant to be a super-secure means of encryption. Generally speaking the only case in which this function makes sense is in transferring shadow password files. If you are encrypting the transfer of system binaries, you need your head examining. When encrypt is specified, an md5 checksum is always used to verify the file. Note: the encryption keys required to get files from cfd are those for the user under which cfd is running (normally root). Cfd will not switch keys for other users, so encrypted transfer will not work for all users. If a file _requires_ encrypted transfer (secure flag in cfd) then normal users will not be able to collect it. `size' With this option you can specify that a file is only to be copied if the source file meets a size critereon. This could be used to avoid installing a corrupted file (the copying of an empty password file, for instance). 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 # 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' This option determines the type of link used to make links. This only applies if the file is linked rather than copied because it matches a pattern set by `symlink'. The default type is a direct symbolic link. The values `relative' or `absolute' may be used, but hard links may not be created in place of copied files, since hard links must normally reside on the same filesystem as their files, and it is assumed that most links will be between filesystems. 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' Switches on/off error messages if source and existing destination files do not match in type, e.g. if a file would overwrite a directory or link. `define' This option is followed by a list of classes which are to be `switched on' if and only if the named file was copied. In multiple (recursive) copy operations the classes become defined if any of the files in the file tree were copied. This feature is useful for switching on other actions which are to be performed after the installation of key files (e.g. package installation scripts etc). `purge' If this option is set to true, cfengine will remove files in the destination directory which are not also in the source directory. This allows exact images of filesystems to be mantained. Note that if the copy command has includes or excludes or ignored files, cfengine will purge only those files on the client machine which are also on the server. This means that some files (such as system specific work files) can be excluded from copies without them being destroyed. Note that purging is disallowed if contant with a remote server fails. This means that local files will not be destroyed by a denial of service attack. _You should not use this option to synchronize NFS mounted file systems. If the NFS server goes down, cfengine cannot then tell the difference between a valid empty directory and a missing NFS file system. If you use purge, use a remote copy also._ 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 in copying --------------------- 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. Too many open files ------------------- 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. defaultroute ============ Dynamical routing is not configurable in cfengine, but for machines with static routing tables it is useful to check that a default route is configured to point to the nearest gateway or router. The syntax for this statement is simply: defaultroute: CLASS:: my_gateway For example: defaultroute: most:: 129.240.22.1 rest:: small_gw Gateways and routers usually have internet address `aaa.bbb.ccc.1' -- i.e. the first address on the subnet. You may use the numerical form or a hostname for the gateway. disks ===== This is a synonyn for `required', *Note 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. *Note 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 =========== 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. *Note 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' *Note 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. disable ======= Disabling a file means renaming it so that it becomes harmless. This feature is useful if you want to prevent certain dangerous files from being around, but you don't want to delete them-- a deleted file cannot be examined later. The syntax is disable: CLASS:: /FILENAME 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, *Note 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. editfiles ========= 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. *Note Regular expressions: (cfengine-Tutorial)Regular expressions. `AbortAtLineMatching QUOTED-REGEX' This command sets the value of a regular expression. In all editing operations (except `FixEndOfLine' and `GotoLastLine') which involve multiple replacements and searches, this expression marks a boundary beyond which cfengine will cease to look any further. In other words, if cfengine encounters a line matching this regular expression, it aborts the current action. BE CAREFUL with this feature: once set, the string remains set for the remainder of the current file. It might therefore interact in unsuspected ways with other search parameters. Editing actions are always aborted as soon as the abort expression is matched. Use `UnsetAbort' to unset the feature. `Append QUOTED-STRING' Add a line containing the quoted string to the end of the file. This should be used in conjunction with the decision structures `BeginGroupIfNoLineMatching' and `BreakIfLineMatches'. `AppendIfNoSuchLine QUOTED-STRING' Add a line containing the quoted string to the end of the file if the file doesn't contain the exact line already. `AppendIfNoLineMatching QUOTED-REGEX' A new version of the older `AppendIfNoSuchLine' which uses a regular expression instead of a literal string. The line which gets appended must be set previously using `SetLine'. `AppendToLineIfNotContains QUOTED-STRING' This commands looks for an exact match of the quoted string in the current line. If the quoted string is not contained in the line, it is appended. This may be used for adding entries to a list, *Note FAQS and Tips::. `AutoCreate' If this command is listed anywhere in the file action list, cfengine will create the named file if it doesn't exist. Normally cfengine issues an error if the named file does not exist. New files are created with mode 644, read access for everyone and write access for the cfengine user (normally root). Note that if you set this, BeginGroupIfFileIsNewer will always be true. `AutomountDirectResources QUOTED-STRING' This command is designed to assist with automounter configuration for users wishing to use the automounter for NFS filesystems, but still use the cfengine mount model. Applied to the current file, it is equivalent to saying: for each of the mountable resources in the list *Note mountables::, append if not found a line for a direct automount map command, to the current file. The string which follows can be used to specify any special mount options e.g. `"-nosuid"' for non setuid mounting (of all the mountables). Note that this is added to the current file and not to a file named `/etc/auto_direct'. `Backup QUOTED-STRING' Set to true or false, on or off to set inform level for this file. Default is on. `Repository QUOTED STRING' This 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. `BeginGroupIfFileExists QUOTED-STRING' The lines following, up to the first `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' The lines following, up to the first `EndGroup' are executed if the quoted filename is newer than the file being edited. `BeginGroupIfNoLineContaining QUOTED-STRING' The lines following, up to the first `EndGroup' are executed if the quoted string does not appear in any line in the file. `BeginGroupIfNoLineMatching QUOTED-REGEX' The lines following, up to the first `EndGroup' are executed if the quoted regular expression does not match any line in the file. `BeginGroupIfNoMatch QUOTED-REGEX' The lines following, up to the first `EndGroup' are executed if the quoted regular expression does not match the current line. `BeginGroupIfNoSuchLine QUOTED-STRING' The lines following, up to the first `EndGroup' are executed if the quoted literal string does not match any line in the file. `BreakIfLineMatches QUOTED-REGEX' Terminates further editing of the current file if the current line matches the quoted regular expression. `CatchAbort' Edit actions which abort on failure (such as `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' Use the current value of the comment delimiters set using `SetCommentStart' and `SetCommentEnd' to comment out lines matching the given regular expression in quotes. `CommentLinesStarting QUOTED-STRING' Use the current value of the comment delimiters set using `SetCommentStart' and `SetCommentEnd' to comment out lines starting with the quoted literal string. `CommentNLines QUOTED-STRING' Comments up to N lines from the current file, starting from the location of the current line pointer. If the end of the file is reached and less than N lines are deleted, a warning is issued, but editing continues. The current value of the comment delimiters is used to determine the method of commenting, (see `SetCommentStart'). After the operation the pointer points to the line after the commented lines. `CommentToLineMatching QUOTED-REGEX' Use the current value of the comment delimiters set using `SetCommentStart' and `SetCommentEnd' to comment out lines from the current position in a file to a line matching the given regular expression in quotes. `DefineClasses "CLASS1:CLASS2:..."' Activate the following colon, comma or dot-separated list of classes if and only if the file is edited. `DeleteLinesAfterThisMatching QUOTED-REGEX' `DeleteLinesContaining QUOTED-STRING' Delete all lines containing the exact string quoted. `DeleteLinesMatching QUOTED-REGEX' Delete all lines matching the quoted regular expression. `DeleteLinesStarting QUOTED-STRING' Delete all lines beginning with the exact string quoted. `DeleteNLines QUOTED-STRING' Deletes up to N lines from the current file, starting from the location of the current line pointer. If the end of the file is reached and less than N lines are deleted, a warning is issued, but editing continues. `DeleteToLineMatching QUOTED-REGEX' Delete lines from the current position, up to but not including a line matching the regular expression in the quoted string. If no line matches the given expression, a warning is only printed in verbose mode, but all edits are immediately abandoned. `EmptyEntireFilePlease' Deletes all lines from the current file. `ElseDefineClasses' See `DefineClasses' `EndGroup' Terminates a begin-end conditional structure. `EndLoop' Terminates a loop. See `ForEachLineIn' `Filter FILTERALIAS' Name a fiter for pruning file searches. `FixEndOfLine' The quoted string which follows may be either `dos' or `unix' to fix the end of line character conventions to match these systems. This command should be executed last of all, since cfengine appends new lines with the conventions of the system on which is was complied during edit operations. `ForEachLineIn QUOTED-FILENAME' This marks the beginning of a for-loop which reads successive lines from a named file. The result is like using `SetLine' for each line in the file. Nested loops are not permitted. `GotoLastLine' Moves the file pointer to the last line in the current file. `HashCommentLinesContaining QUOTED-STRING' Add a `#' to the start of any line containing the quoted string. `HashCommentLinesMatching QUOTED-REGEX' Add a `#' to the start of any line exactly matching the quoted regular expression. `HashCommentLinesStarting QUOTED-STRING' Add a `#' to the start of any line starting with the quoted string. `IncrementPointer QUOTED-NUMBER' Increments the value (in lines) of the file pointer by the number of lines specified in the quoted string (as a denary number). e.g. `"4"'. Negative values are equivalent to decrementing the pointer. If a request is made to increment/decrement outside of the file boundaries the pointer `bumps' into the boundary and remains there, i.e. either at start of file or end of file. `Inform QUOTED-STRING' Set to true or false, on or off to set inform level for this file. Default is off. `InsertFile QUOTED-STRING' Inserts the named file after the current line position in the file. This should be used in conjunction with a begin-end construction in order to avoid including the file every time cfengine is run. If the file does not exist, or cannot be opened, there is only a warning issued in verbose mode. Note if the file is empty, or if the current line pointer is not set, the file is inserted at the start of the file. `InsertLine QUOTED-STRING' Inserts the quoted string as a line at the current position in the file. After the insert, the file pointer is incremented by one so that subsequent inserted lines are placed after the first. This should probably be used in conjunction with the conditional begin-end tests to avoid lines being inserted on every run. `LocateLineMatching QUOTED-STRING' Moves the current-position pointer to the start of the line matching the quoted regular expression. If there is no match, a warning is only issued in verbose mode, but all editing is immediately aborted. See also `WarnIfNoLineMatching' so that you can get an explicit warning, even out of verbose mode. `PercentCommentLinesContaining QUOTED-STRING' Add a `%' to the start of any line containing the quoted string. `PercentCommentLinesMatching QUOTED-REGEX' Add a `%' to the start of any line exactly matching the quoted regular. `PercentCommentLinesStarting QUOTED-STRING' Add a `%' to the start of any line starting with the quoted string. `Prepend QUOTED-STRING' Add a line containing the quoted string to the start of the file. This should be used in conjunction with the decision structures `BeginGroupIfNoLineMatching' and `BreakIfLineMatches'. `PrependIfNoLineMatching QUOTED-REGEX' A new version of the older `PrependIfNoSuchLine' with uses a regular expression instead of a literal string. The string prepended is the one set using `SetLine'. `PrependIfNoSuchLine QUOTED-STRING' Add a line containing the quoted string to the start of the file if the file doesn't contain the exact line already. `Recurse DIGIT/INF' For recursive descents when editing whole file trees. `ReplaceLineWith QUOTED-STRING' Replace the line at the current position with the text in the quoted string. The file pointer remains pointing to this line after the change. `ReplaceAll QUOTED-REGEX With QUOTED-STRING' Replace all instances of strings matching the regular expression in the first quotes with the exact string in the second set of quotes, throughout the current file. Note that cfengine matches on a left to right basis, with the first match taking precedence, so if your regular expression matches text ambiguously it is the first occurrence which is replaced. For example, if you replace `cf.*' with `CFENGINE' and cfengine encounters a line `hello cfengine cfengine', then this will be replaced with `hello CFENGINE' even though two possible strings match the regular expression. On the other hand if the expression is not ambiguous, say replacing `cfengine' with `CFENGINE', then the result would be `hello CFENGINE CFENGINE'. `ReplaceLinesMatchingField QUOTED-NUMBER' This command replaces any lines in the current file with the current line set by `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 *Note FAQS and Tips::. `ResetSearch QUOTED-STRING' Sets the current-position pointer to the line number in the quoted string. `EOF' indicates the end of the file. `RunScript QUOTED-STRING' Executes the named script command. Before executing the script any edits are saved to disk. After the script has executed, cfengine reloads the file for any further editing operations. The script (which may be any executable program) is appended with two arguments: the name of the file which is being edited and the system hard class (e.g. sun4, ultrix etc.) of the system executing the script. CAUTION: cfengine knows nothing about the success or failure of anything that is done during the execution of user scripts. This feature is to be used at the users own peril! `RunScriptIfLineMatching QUOTED-STRING' Executes the script named with the `SetScript' command only if the current file contains a line matching the quoted regular expression. CAUTION: cfengine knows nothing about the success or failure of anything that is done during the execution of user scripts. This feature is to be used at the users own peril! `RunScriptIfNoLineMatching QUOTED-REGEX' Executes the script named with the `SetScript' command if the current file contains no line matching the quoted regular expression. CAUTION: cfengine knows nothing about the success or failure of anything that is done during the execution of user scripts. This feature is to be used at the users own peril! `SetCommentStart QUOTED-STRING' Specify which string should be used for starting a comment using the commands `CommentLineMatching' and `CommentLineStarting'. The default is the hash symbol `#' followed by a single space. `SetCommentEnd QUOTED-STRING' Specify which string should be used for ending a comment using the commands `CommentLineMatching' and `CommentLineStarting'. The default is the empty string. For example, you could make C style comments by setting CommentStart to `/*' and comment end to `*/'. `SetLine QUOTED-STRING' Sets a current line value which can be appended using `AppendIfNoLineMatching' using a regular expression. `SetScript QUOTED-STRING' Sets the name of a user-supplied script for editing the current file. `SlashCommentLinesContaining QUOTED-STRING' Add a `//' to the start of any line containing the quoted string. `SlashCommentLinesMatching QUOTED-REGEX' Add a `//' to the start of any line exactly matching the quoted regular expression. `SlashCommentLinesStarting QUOTED-STRING' Add a `//' to the start of any line starting with the quoted string. `SplitOn QUOTED-STRING' This defines a single character which is to be interpreted as a field separator for editing files with columns. The default value for this is `:', as is used in the password and group files. It is used in conjunction with `ReplaceLinesMatchingField'. `Syslog QUOTED-STRING' Set to true or false, on or off to set inform level for this file. Default is off. `Umask QUOTE MODE' Set local umask for file creation and script execution. `UnCommentLinesContaining QUOTED-STRING' Uncomment all lines in file containing the quoted string as a substring. The comment delimiters are assumed to be those set using SetCommentStart and SetCommentEnd. `UnCommentLinesMatching QUOTED-REGEX' Uncomment all lines in file matching the quoted regular expression. The comment delimiters are assumed to be those set using SetCommentStart and SetCommentEnd. `UnCommentNLines QUOTED-STRING' Uncomments N lines starting from the current position, using the currently defined method for commenting. Note that the comment start and end symbols are removed independently, i.e. they are not matched, so that a comment may be spread over several lines. e.g. If using C style `/*' and `*/' comments, the command `UnCommentNLines "3"' would uncomment /* 1 */ /* 2 */ /* 3 */ and also /* 1 2 3 */ `UnsetAbort QUOTED-STRING' Switches off the feature `AbortAtLineMatching'. `WarnIfLineContaining QUOTED-STRING' Issue a warning if the quoted string is found as a substring of one or more lines in the file. `WarnIfLineMatching QUOTED-REGEX' Issue a warning if the quoted regular expression matches one or more lines in the file. `WarnIfLineStarting QUOTED-STRING' Issue a warning if the quoted string matches the start of one or more lines in the file. `WarnIfNoLineContaining QUOTED-STRING' Issue a warning if the quoted string is not contained in one or more lines in the file. `WarnIfNoLineMatching REG-EX' Issue a warning if the quoted regular expression does not match one or more lines in the file. `WarnIfNoLineStarting QUOTED-STRING' Issue a warning if the quoted string is not found at the start of one or more lines in the file. `WarnIfNoSuchLine QUOTED-REGEX' Issue a warning if the quoted regular expression does not match one or more lines in the file. 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' Replaces occurrences of the matched regular expression with\ the provided literal text, only if the length of the literal substitute is less than or equal to the length of the located string. If the replacement string is shorter, it is padded with ascii spaces (character 32) by default. The padding character can be changed by setting `BinaryPaddingChar' in `control'. Padding with a null byte would lead to corruption of text within a program. `WarnIfContainsString REGEX/LITERAL' Yields a warning if the literal string or regular expression matches. Cfengine first attempts a literal match and then a regular expression match. `WarnIfContainsFile FILENAME' Yields a warning if the contents of the named file exactly match part of the file which is being edited. This can be used to search for binary data which cannot be typed directly into the cfengine program, e.g. virus signatures. It is suggested that you use these editing functions with caution. Although all possible safeguards have been incorporated into them, it is still possible through carelessness to do damage to important files on your system. Always test editing programs carefully before committing them to your global site configuration. files ===== The `files' facility allows you to touch (create), check for the existence, owner and permissions of files, change the permissions and test for setuid root programs. Syntax ------ A files-statement can have several options. We can begin by examining the form of the statement in pseudo-code: files: 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' This is the only obligatory part of a file action. This is a directory at which a file search should begin. This may be a single file or a directory. The recursion specifier may be used to force cfengine to descend into subdirectories in a controlled fashion, starting from this point, checking files there also. The wildcard `home' may also be used. *Note home directive::. `mode=MODESTRING' Specifies what the allowed permissions for files are. If cfengine finds that a file's mode is incorrect, the value of the `action' option determines what will be done about it. The modestring should consist of either a three digit octal numbers with `+', `-' or `=' symbols, or a text string like that used by the command `chmod'. For instance: `mode=u=rwx,og+rx' would mean set the read/write and execute flags for the user (file owner) and add the read/execute flags for others and group bits. An example of the numerical form might be `-002' which would mean that the read-for-others flag should either not be set or should be unset, depending on the action you choose. `+2000' would mean that the setuid flag should be present or set, depending on the action. `+2000,-002' would be a combination of these. The `=' sign sets to an absolute value, so `=755' would set the file mode to mode 755. `flags=BSD FLAGS' The free BSD Unices have additional filesystem flags which can be seton files. Refer to the BSD `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' This specifier tells cfengine whether or not to recurse into subdirectories. If the value is zero, only the named file or directory is affected. If the value is 1, it will open at most one level of subdirectory and affect the files within this scope. If the value is `inf' then cfengine opens all subdirectories and files beginning from the specified filename.*Note Recursion::. `owner=OWNER LIST' This is a list of allowed owners, or uids by number, separated by commas. For example `root,2,3,sysadm'. In cases where you ask cfengine to fix the ownership automatically, the owner will be set to the first owner in the list if and only if it is not one of the named uids in the list. `group=GROUP LIST' This is a list of allowed groups, or gids by number, separated by commas. For example `wheel,2,3,sysadm'. In cases where you ask cfengine to fix the ownership automatically, the group will be set to the first group in the list if and only if it is not one of the named gids in the list. `action=ACTION' The action is one of the following keywords. warnall warndirs warnplain fixall fixdirs fixplain touch linkchildren create compress alert The upper line results only in warnings being issued. The actions beginning `fix' prompt cfengine to fix encountered problems without bothering the user. No message is issued unless in verbose mode. The special features on the third line will be explained separately. 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' You can include this option several times to specify specific patterns which are to be included in the search. Once you specify one pattern you exclude all files not matching at least one of the patterns. The case be useful for restricting a search, or for modifying the permissions of only certain files. `exclude=WILDCARD/PATTERN' You can include this option several times to specify specific patterns which are to be excluded from the search. This overrides any patterns given in the `include=' list. `ignore' This works like the global ignore directive but here you may provide a private list of ignorable directories and files. Unlike include, exclude this affects the way cfengine parses directory trees. `links=STOP/TRAVERSE/TIDY' Normally cfengine does not descend into subdirectories which are pointed to by symbolic links. If you wish to force it to do so (without using the `-l' command line option) you may give this option the value `true', or `traverse', or `follow'. To specify no recursion you set the value `false' or `stop'. Note that the value set here in the cfengine program _always overrides_ the value set by the `-l' command line option, so you can protect certain actions from this command line option by specifying a negative value here. If you specify no value here, the behaviour is determined by what you specify on the command line. The value `links=tidy' has the same effect as the `-L' command line option except that here it may be specified per item rather than globally. Setting this value causes links which point to non-existent files to be deleted. If the warn directive is used (for directories, plain files or both) then only a warning message is issued if the file being tested does not match the specification given. If the fix directives are used then cfengine does not issue a warning, it simply fixes the value silently. Non-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, *Note directories::.) `define=CLASSLIST' If a file operation results in action being taken to fix a file, the colon, comma or dot separated list of classes becomes defined. Warnings do not activate the classes. `checksum=md5' If set this option causes cfengine to add a checksum for the named file to a database. Changes in the value of this checksum are then warned as a security issue. This should normally only be used to monitor binary files which one would not expect to change often. Note also that the use of this option can mean a significant performance penalty. The variable `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, *Note Checksum Databases: (cfengine-Tutorial)Checksum Databases. The default values are `mode=+000', `recurse=0', `action=warnall' and any owner or group is acceptable. The default for `links' is to not traverse links unless the `-l' option is set on the command line. Recursion --------- The recursion specifier tells cfengine what to do, starting from `/directory name'. A value of `r=0' means `no recursion' and any checking is limited only to the named file or directory. A value of `r=inf' implies unlimited recursion. Cfengine then descends into all subdirectories checking or setting the permissions of files until it `bottoms out' at a plain file. A value such as `R=4' means descend recursively into subdirectories, but no more than four levels. This is a useful safety net in preventing unforeseen accidents. A recursive search also bottoms out on device boundaries and symbolic links (provided the `-l' option is not used). Directory permissions --------------------- When you specify the permissions for a whole file tree, using the recursion specifier it is awkward to have to remember that directories must be executable. cfengine will do this for you automatically. If you specify that a file tree is to have a read flag set, cfengine will ensure that the corresponding execute flag is also set for directories which live in the tree. So the command files: myclass:: /dir mode=a+rw r=inf fixall would set all plain files to mode 644 and all directories to 755, that is read/write for everyone on plain files and read/write/execute for everyone on directories. `home' directive ---------------- If you want to check the files of all the users who have their login areas on the current host, you can use a wildcard directive `home' instead of a directory name. In this case the file action iterates over all home directories physically on the current host. The home directories are, of course, located by searching for files which match $(mountpattern)/$(homepattern) i.e. the values which are specified in the `control' part of the program. For example the following line is a very useful service to ignorant users. files: any:: home mode=o-w r=inf act=fixall It ensures automatically that no user has files which can be written to by other arbitrary users. As a corollary to this, you may write something like any:: home/www mode=a+r fixall to specify a special subdirectory of every users' home directory. This statement would check that all of the files in users' world wide web directories were readable for everyone. Owner and group wildcards ------------------------- If you do not want to explicitly state the owner or group of a file you may simply omit the group or owner options. /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. Files linkchildren ------------------ The `linkchildren' facility is almost identical to that already described under `links'. *Note Link Children::. The only difference here is that the ownership and permissions on the links are set all in one operation. For example: MYCLASS:: /local/lib/emacs m=0770 o=me g=mygroup act=linkchildren touch ----- The `touch' facility creates a new file with the specified permissions and ownership, or corrects the permissions and ownership of an existing file, in addition to updating the time stamps. MYCLASS:: /NEWFILE mode=0644 action=touch create ------ 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'. filters ======= 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=filteralias 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:' and Group can use numerical id's or names, or `"none"' for users or groups which are undefined in the system passwd/group file. `' Mode: applies only to file objects. It shares syntax with the `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:' apply only to file objects. These specify ranges From and To. If the file's time stamps lie in the specified range, this returns true. Times are specfied by a six component vector (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:' applies only to file objects may be a list of file types which are to be matched. The list should be separated by the OR symbol `|', since these types are mutually exclusive. The possible values are currently reg|link|dir|socket|fifo|door|char|block `ExecRegex:' matches the test string against the output of the specified command. `NameRegex:' matches the name of the file with a regular expression. `IsSymLinkTo:' applies only when the file object $(this) is a symbolic link. It is true if the regular expression matches the contents of the link. `ExecProgram:' matches if the command returns successfully (with return code 0). Note that this feature introduces an implicit dependency on the command being called. This might be exploitable as a security weakness by advanced intruders. `Result:' specifies the way in which the above elements are combined into a single filter. Process filters: `PID:' process ID (quoted regex) `PPID:' parent process ID (quoted regex) `PGID:' process group ID (quoted regex) `RSize:' resident size (quoted regex) `VSize:' virtual memory size (quoted regex) `Status:' status (quoted regex) `Command:' CMD or COMMAND fields (quoted regex) `(From/To)TTime:' Total elasped time in TIME field (accumulated time) `(From/To)STime:' Starting time for process in STIME or START field (accumulated time) `TTY:' terminal type, or none (quoted regex) `Priority:' PRI or NI field (quoted regex) `Threads:' NLWP field for SVR4 (quoted regex) `Result:' logical combination of above returned by filter (quoted regex) 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)" } Complete filter examples ------------------------ 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 groups/classes ============== 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'. homeservers =========== The `homeservers' declaration need only be used if you are using cfengine's model for mounting NFS filesystems. This declaration informs hosts of which other hosts on the network possess filesystems containing home directories (login areas) which client hosts should mount. A sample homeserver declaration looks like this: homeservers: Physics:: einstein Math:: riemann euler The meaning of this declaration is the following. Any host which finds itself to be a member of the classes on the left hand side of the assignment need to mount all home directory resources from the hosts on the right hand side of the assignment. The pattern variable `homepattern' is used to determine which resources are home directories in the list of `mountables'. *Note mountables::. Let us consider an example in which `homepattern' is set to the wildcard value `home?' and the mountables list is given by mountables: einstein:/mysite/einstein/home1 einstein:/mysite/einstein/home2 riemann:/mysite/riemann/local euler:/mysite/euler/home1 Any host in the group `Physics' would now want to mount all home directories from the host `einstein'. There are two of these. Both the filesystems listed for `einstein' match the `homepattern' variable since they end in `home?'. cfengine would therefore take this to mean that all hosts in `Physics' should mount both of these filesystems. Hosts in `Math', on the other hand, should mount only homedirectories from the hosts `riemann' and `euler'. There is only a single filesystem on `riemann' and it does not match `homepattern', so it is not mounted. On `euler' there is a match, so this filesystem will be added to the appropriate hosts. _Cfengine picks out home directory resources from the `mountables' list by trying to match the `homepattern' variable, starting from the end of the directory name. You do not therefore have to use the designation `/site/host/home?' but this is a simple choice and is highly recommended._ ignore ====== When you specify a recursive search as part of a `files', `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 For example: 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. import ====== To break up a large configuration file into smaller files you can use the include directive. This conditionally reads in files if the class on the left hand side of the assignment matches the host parsing the file. This enables also a variety of cfengine configuration scripts to read in a standard set of default settings. The syntax of the statement is: import: any:: cf.global_classes linux:: cf.linux_classes Note that, if you define variables in an imported file they will not be defined for operations in their parent files. This because cfengine reads in all the import files after the main file has been parsed--not at the place where you call import in your script. This means that variables or macros defined in imported files are only defined after the main program. Variables from earlier files are inherited by later includes, but not _vice-versa_. interfaces ========== 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 links ===== The symbolic links function is one of the greatest plusses in cfengine as a system administration tool. It allows you to do two things: check single links for correctness and consistency (or make them if they do not exist), and check or make links to every file in a designated directory. This latter feature is called multiple linking or linking children. The `linkchildren' feature is also available from the `files' action *Note 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'._ Single links ------------ To define a single link, you create an entry of the following form: links: CLASS:: LINKNAME -> OBJECT_TO_LINK_TO LINKNAME -> ./RELATIVE_LINK LINKNAME -> ../RELATIVE_LINK 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. *Note 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 interacti