22.1 Class Configuration CommandsThe five forms for the class configuration command are the following: CX list values from configuration file CX $=Y copy values from another class (V8.10 and above) FX /file values from a disk file FX |program values via another program FX key@database values from a database map (V8.12 and above) The class configuration command starts with either the letter C or F, which must begin a line. The C says values will be assigned as a part of the configuration command. The F says values will be assigned from an external file, program, or database map. The C or F is immediately followed (with no intervening whitespace) by the name of the class (the X in the preceding commands). A class name is any single ASCII character or, beginning with V8.7 sendmail, a multicharacter name enclosed in curly braces: CX list all versions C{LongName} list beginning with V8.7 See Section 21.4.2 for a full discussion of how to use multicharacter names. Note that classes are separate from macros, so they can both use the same letter or name with no conflict. The sendmail program reserves the lowercase letters for its own use as internally defined class names. All uppercase letters and all names that begin with uppercase letters are available for your use. 22.1.1 The C Class CommandThe C form of the class command causes values to be assigned from within the configuration file. In general, the C class command looks like this: CX list values from configuration file C{XX} list values from configuration file Here, list is a list of string elements (delimited by whitespace) that follows on the same line as the C command. Each word in list is added to the collection of values in the class $=X, in the first case and to the class $={XX} in the second.[1]
Multiple declarations of the same named class can coexist in the configuration file. Each declaration after the first adds its string elements to those already in the collection. That is: CX string1 string2 CX string3 string4 produces the same collection of class strings as does: CX string1 string2 string3 string4 Both create a class containing four strings. Whitespace separates one value from another. Whitespace is defined by the C-language isspace(3) routine and usually includes the space, tab, newline, carriage return, and form feed characters. Each line of text assigned to a class is broken up by sendmail into whitespace delimited words when the C configuration command is parsed. When a line is indented with a space or a tab, that line is joined by sendmail to the preceding line. Thus, the following three declarations also add four words to the class $=X: CX string1 CX string2 CX string3 string4 tab Words that are added to a class cannot be removed after sendmail has read them. Instead, they must be edited out of whatever file or program produced them, and the sendmail daemon must be restarted. The list of words in a class declaration can include macros. For example, the following assigns the same values to class $=X as did the earlier example: D{LIST} string1 string2 string3 string4 CX ${LIST} Macros used in class declarations are expanded when the configuration file is read. Deferred macros (those with the $& prefix) cannot be used in class declarations. But conditionals can: CX ourhost$?{Domain}.${Domain}$. 22.1.1.1 Append one class to anotherBeginning with V8.10 sendmail it is possible to copy and add values from one class to another. The declaration to do this looks like the following: C{To} $={From} Here, the values stored in the $={From} class are added to the values stored in the $={To} class. If $={To} does not exist, it will create them. This effect is caused by the fact that class macros are now expanded when placed on a C configuration line. To illustrate, consider the following miniconfiguration file, which we call x.cf: V10 CA 1 2 3 CB 7 8 9 CX $=A 4 5 6 $=B When this configuration file is read, first the class $=A is filled with three values: 1, 2, and 3. Then the class $=B is filled with three different values: 7, 8, and 9. Finally, the class $=X is filled first with the values from $=A (1, 2, and 3), then with its own values (4, 5, and 6), and lastly with the values from $=B (7, 8, and 9). The result can be seen by running sendmail on this miniconfiguration file in rule-testing mode: % /usr/sbin/sendmail -bt -C x.cf ADDRESS TEST MODE (ruleset 3 NOT automatically invoked) Enter <ruleset> <address> > $=X 2 3 1 6 7 4 5 8 9 > Ignore the fact that the values you put in are printed in a different order. This is an artifact of the way sendmail stores class values in its symbol table and actually improves the efficiency with which they are later looked up. Class macros that you list as values for a C configuration line need not be previously declared or even hold any values. If they hold values, those values will be added to the target class. Valueless and undeclared classes will simply be ignored. 22.1.2 The F Class CommandThe F form of the class configuration command allows values to be appended to a class from outside the configuration file. In general, the file command looks like either of the following: FX file values from a disk file FX |program values via another program (V8.7 and above) FX key@dbmap values from a database map (V8.12 and above) The F is immediately followed by the name of the class. This can be either a single-character name, as shown, or a multicharacter name. The name is followed by optional whitespace and then a filename, a program name, or a database-map lookup. If the name begins with the pipe character (|), it is taken to be the name of a program to run.[2] If the name includes an @ character, it is taken to be a key to look up, and the name of a database map. Otherwise, it is taken to be the name of a file to read.
If SCANF (SCANF) was defined when sendmail was compiled, each line that is read from a file or program (but not from a database map) is parsed by the C-language scanf(3) library routine. The formatting pattern given to scanf(3) is %s, which tells scanf(3) to read only the first whitespace-delimited word from each line of text. When the configuration file is processed, the file is opened for reading, or the program is executed, or the database map is opened for lookups. If any cannot be opened (for reading, execution, or lookups), the following error is logged and sendmail ignores that configuration command: fileclass: cannot open what: why
Here, the what is the exact text that was given in the configuration file, and why is the text of a system error. A file, program, or database map can also fail to open because of defective permissions. See Section 10.5 to learn why permissions are important, and Section 10.5.4 for a list of recommended permissions. For the file form only, if the file can optionally not exist, you can prefix its name with a -o switch: FX -o file OK for file to not exist
This tells sendmail to remain silent if the file does not exit. The -o switch is useful when a configuration file is shared by several machines, only some of which need the external class macro file. But be aware that there can be grave risk to not knowing when a critical file disappears. The C and F forms of the configuration command can be intermixed for any given class name. For example, consider a file named /etc/mail/localnames with the following contents: string3 string4 The following two configuration commands add the same four strings to the class X as did the C command alone in the previous section: CX string1 string2 FX /etc/mail/localnames This creates a class with four strings as elements. Whitespace delimits one string from the others in the C line declaration. The file /etc/local/names is then opened and read, and each of the two words in that file is added to the two words that are already in the class. 22.1.2.1 scanf(3) variationsThe file form of the class configuration command allows different formatting patterns to be used with scanf(3).[3] But the program form does not allow any variation, and so its scanf(3) pattern is always %s, which tells scanf(3) to read only the first whitespace-delimited word from each line of text:
FX file pat with scanf(3) pattern FX |program always "%s" FX key@dbmap cannot be used with scanf(3) If the optional pat argument to the file form is missing, the pattern given to scanf(3) is %s. The optional pat argument is separated from the file argument by one or more spaces or tabs. It should not be quoted, and it consists of everything from its first character to the end of the line. Internally, scanf(3) is called with: sscanf(result, pat, input) Here, result is the string array element to be added to the class definition. The pat is the scanf(3) pattern, and input is the line of text read from the file. After each line of text is read from the file and filtered with the scanf(3) pattern, it is further subdivided by sendmail into individual words. That subdividing uses whitespace (as defined by the C-language isspace(3) routine) to separate words. Each separate word is then appended as an individual element to the class array. Consider the contents of the following file named /etc/mail/localhosts: server1 server2 # my two nets uuhost # my uucp alias #mailhost # mail server alias (retired 06,23,91) This file contains three hostname aliases to be added to a class — say, H. The following configuration command does just that: FH /etc/mail/localhosts %[^#] The pattern %[^#] causes scanf(3) to read all characters in each line up to, but not including, the first # character. The first line includes two whitespace-delimited words that are appended to the class H. The second line contains one word, and the third contains none. 22.1.3 Class Via Database Map LookupsBeginning with V8.12 you can declare class values by specifying and using database-maps. Database maps are described in Chapter 23. In its simplest form, such a declaration looks like this: FXkey@ type:detail F{Name}key@ type:detail Each such declaration begins with the F configuration command, which is immediately followed (with no intervening space) by the name of the class that will be filled with values. The first line shows the single-character name form (the X), and the second line shows the multicharacter name form (the {Name}). The name of the class is immediately followed by the key to look up in the database map. Note that you must be very careful to specify a key that actually exists. If the key is not found in the database map, sendmail silently ignores the error. The key is immediately followed by a literal @ character, which in turn is immediately followed by the type of the database map. A db type database map, for example, could have a type of either hash or btree. An ldap type database map, for example, would have a type of ldap. (We discuss ldap in detail in the next section.) A complete list of types can be found in the leftmost column of Table 23-2, in Section 23.2.2. The type is immediately followed by a colon and then by the detail. The nature of the detail varies depending on what you want this command to do. To illustrate, consider the following addition to an mc configuration file: LOCAL_CONFIG FwCWhosts@hash:/etc/mail/access Here, under the LOCAL_CONFIG part of the mc file, we place an F configuration command. The class that will be filled with values is the $=w class ($=w), a special one that contains all the names by which the local host can be known. It will be filled with values by looking up the key CWhosts in the hash type database that is contained in the file /etc/mail/access. The key is optional, and it is not an error to omit it. This property can be useful for ldap type maps, but is generally not useful for other database maps. For most database-map types, a missing key will simply match nothing and result in no values filling the class. The type is mandatory. If it is missing (for example, if hash were omitted from the preceding declaration), the following error would be printed and logged: fileclass: cannot open 'CWhosts@:/etc/mail/access': No such file or directory If the type is misstated as one that does not exist (for example, if foo replaced hash), the following would be printed and logged: fileclass: F{w}: class foo not available If there is a problem with the detail (for example, if access were misspelled as acess), the following error would be printed and logged: hash map "w": missing map file /etc/mail/acess.db: No such file or directory If the key contains an @ character (as, for example, gw@wash.dc.gov), the part to the left of the first @ is taken as the key (gw) and the rest of the line through the : is taken as the type (wash.dc.gov@hash), yielding the following error: F{w}: class wash.dc.gov@hash not available There is no possible way to put an @ character into a key. One use for filling a class with a database-map lookup might involve looking up the name for root on the local machine: LOCAL_CONFIG F{RootName}0@text:-k2 -v0 -z: /etc/passwd Here, we need to know the name of root because it is not the same on all machines (some might call it toor, and others rot). The name found will be placed into the class $={RootName}. The text type database map is used because it can look up keys in a plain file. The /etc/passwd file might look, in part, like this: 0th boss:Kmz4md67r66n2:0:1:Operator:/:/bin/csh daemon:*:1:1::/: 2nd We wish to look up the first entry in that file that has a user-id of zero. Note that text type database maps are arranged in columns that are numbered, starting with column zero. In this case, the second column holds the user-id and the "zeroth" column holds the name we seek. The F configuration command looks up the key 0 in a text type database map found in the file /etc/passwd. The database-map switches that prefix the filename tell sendmail to do the following: look up the key in the second column (the -k2); return the value from the zeroth column (the -v0); and use a colon as the column separator (the -z:). The text type database map and its switches are described in text. 22.1.3.1 Class by replacing files with database lookups in mc macrosSeveral mc macros are used to fill class macros with values. They are listed in Table 22-1, along with the class macros they fill. Note that the classes shown should not be used directly because there is no guarantee that they will continue to be available in the future. To be safe, always use the mc macro instead. To reinforce this precaution in the descriptions that follow, we use the mc name for the class (as the EXPOSED_USER class) instead of the class macro name (as the $=E class).
It is possible to fill these class macros from database maps using these mc macros. Instead of the file name, just place the database lookup expression between the trailing parentheses of the mc macro. For example, consider this way of filling the RELAY_DOMAIN class with values from the access database, assuming the following entry exists in your access database: DomainList: our.domain their.domain another.domain Recall that the RELAY_DOMAIN class (Section 7.4.1.1) determines which domains you want to relay for. The idea here is that you want to fill it with the values our.domain, their.domain, and another.domain. You could perform that lookup with an mc configuration line such as this: RELAY_DOMAIN_FILE(`DomainList:@hash:/etc/mail/access') Here, DomainList: (colon included) is the key looked up in the hash type database-map located in the database file /etc/mail/access. The presence of the literal @ tells sendmail this is a database-map lookup, and not the name of a file to read. To use an example from the previous section, consider adding a user-id name to the EXPOSED_USER class (Section 4.4.1), like this: EXPOSED_USER_FILE(`0@text:-k2 -v0 -z: /etc/passwd') This lookup would result in the addition of the name boss (from the previous section) to the EXPOSED_USER class. 22.1.3.2 Class via ldap map lookupsAdding values to class macros with ldap-type map databases is very easy. In its simplest form, just use a literal @LDAP as the type and nothing else: RELAY_DOMAIN_FILE(`@LDAP') FR@LDAP The first form uses the mc macro RELAY_DOMAIN_FILE to add values to the RELAY_DOMAIN class (Section 7.4.1.1). The second line adds to the same class, but uses the F configuration command. For both lines, the database used for the lookup is the ldap-type database because of the literal @LDAP in both. That literal expression causes the following default ldap schema to be used: -k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=R) (|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))) -v sendmailMTAClassValue When using the F configuration command form, you must specify the class to be filled. For example: F{OurStuff}@LDAP Whichever class you specify (the {OurStuff} here) will become the class listed with the sendmailMTAClassName= in the default schema: -k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName={OurStuff}) (|(sendmailMTACluster=${sendmailMTACluster})(sendmailMTAHost=$j))) -v sendmailMTAClassValue Naturally you can bypass the default ldap definition altogether by placing your own into the declaration. Consider the following two lines, which do just that: VIRTUSER_DOMAIN_FILE(`@ldap:-k (&(objectClass=virtHosts)(host=*)) -v host') F{VirtHosts}@ldap:-k (&(objectClass=virtHosts)(host=*)) -v host Note that by replacing the literal @LDAP with a type declaration of @ldap, you eliminate the automatic generation of a default definition. One possible pitfall is the temptation to define an identical class macro's values in both your domain record and individual host records. If you do, the lookup will be additive, adding record values from both the domain and the host records. |