Previous Section Next Section

22.1 Class Configuration Commands

The 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 Command

The 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]

[1] Note that when a class name is a single character, it can be referenced with or without enclosing curly braces, with no change in meaning. That is, CX and C{X} are equivalent.

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 another

Beginning 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 Command

The 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.

[2] This was removed from V8.1 sendmail because it presented a security risk. It was restored to V8.7 and above because sendmail now checks permissions more carefully and exec(2) is the program itself, instead of using the old, buggy popen(3) approach of yore.

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) variations

The 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:

[3] The version of sendmail that you are using must have been compiled with SCANF defined (SCANF) for scanf(3) to be usable from within the configuration file.

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 Lookups

Beginning 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 macros

Several 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).

Table 22-1. mc macros used to fill class macros

mc macro

§

Class macro

CANONIFY_DOMAIN_FILE

FEATURE(nocanonify)

$={Canonify}

confCT_FILE

FEATURE(use_ct_file)

$=t

EXPOSED_USER_FILE

Section 4.4.1

$=E

GENERICS_DOMAIN_FILE

FEATURE(generics_entire_domain)

$=G

LDAPROUTE_DOMAIN_FILE

See this section

$={LDAPRoute}

LDAPROUTE_EQUIVALENT_FILE

See this section

$={LDAPRouteEquiv}

LOCAL_DOMAIN

$=w

$=w

LOCAL_USER_FILE

Section 4.5.5

$=L

MASQUERADE_DOMAIN_FILE

Section 4.4.3

$=M

MASQUERADE_EXCEPTION_FILE

Section 4.4.6

$=N

RELAY_DOMAIN_FILE

Section 7.4.1.2

$=R

VIRTUSER_DOMAIN_FILE

FEATURE(virtuser_entire_domain)

$={VirtHost}

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 lookups

Adding 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.

    Previous Section Next Section