12.2 Forms of Alias Delivery

Addresses in the righthand side of an alias entry can take four forms:

LHS:  user 
LHS:  /file 
LHS:  |program 
LHS:  :include: file 

The user specifies final delivery to a user's mail spool file (subject to change by the user's ~/.forward file), or delivery to a new address (e.g., newuser or user@newsite). The /file specifies delivery by appending to a file. The |program specifies delivery by piping the message through a program. The :include: specifies processing of a mailing list. The first three are covered here. The last is covered in the next chapter.

These righthand sides can be combined on a single line, where one is separated from another by a comma. For example:

LHS:  user, /file 

12.2.1 Delivery to Users

Any address in the list of addresses to the right of the colon that does not begin with a /, |, or : character is considered the address of a user. The address can be local or remote.

If that user address to the right of the colon is prefixed with a backslash character (\)[2] and the address is a local one, all further aliasing is suppressed (including reading the user's ~/.forward file), and the message is delivered with the local delivery agent.

[2] Actually, a backslash anywhere in the name causes the same immediate delivery.

12.2.2 Delivery to Files

When any of the addresses to the right of a colon in the alias list begins with a / character, delivery is made by appending the mail message to a file. This is automatic with all modern configuration files, but there are exceptions.[3] Beginning with V8.7 sendmail, any delivery agent for which the F=/ flag (F=/ (forward slash)) is set can also append messages to files. If you want to disable this ability, delete the F=/ flag from all delivery agent declarations in your configuration file.

[3] If yours is an old configuration file that does not automatically recognize a leading / character, you will need to add a new rule near the end of your rule set 0. For example:

In the list of addresses to the right of the colon, sendmail considers any local address that begins with the / character to be the name of a file.[4] Whenever the recipient address is a file, sendmail attempts to deliver the mail message by appending it to the file. This ability to deliver mail to files is included in sendmail primarily so that failed mail can be saved to a user's ~/dead.letter file. It can also be used (through use of aliases) to deliver mail to other files, but that use is less than optimal, as you will see.

[4] Note that an @host prevents this interpretation. That is, /a is a file, but /a@host is not. This distinction is necessary for X.400 addresses to be handled correctly.

To deliver to a file, sendmail first performs a fork(2) and gives the child the task of delivery. The fork is necessary so that sendmail can change its effective uid and gid, as we will show. The child then performs a stat(3) on the file. If the file exists, its file permissions are saved for later use. If it doesn't exist, the saved permissions are defaulted to 0600. Under V8.7 the decision to use stat(2) versus lstat(2) to obtain the permissions is determined by the SafeFileEnvironment option (SafeFileEnvironment). Beginning with V8.9, the decision to use stat(2) versus lstat(2) depends on the FileDeliveryToSymLink setting (See this section) for the DontBlameSendmail option.

If the saved permissions have any execute bit set, the child exits with EX_CANTCREAT as defined in <sysexits.h>. If the file has a controlling user associated with it, any set-user-id and set-group-id bits are stripped from the saved permissions. If the file was listed in a ~/.forward file, the controlling user is the owner of the ~/.forward file. If it was listed in a :include:'d file, the controlling user is the owner of the included file. If the message is being processed from the queue, the controlling user can be specified in the qf file (C line).

Then, the queue df file (D line) is opened for reading (if it is not already open). If that file cannot be opened, sendmail prints the following error message but continues to attempt delivery:

mailfile: Cannot open df for  file  from  sender  

Here, the df is the name of the queue datafile that cannot be opened. The file is the name of the file to which sendmail is attempting to deliver the message. The sender is the address of the sender of the mail message.

Next, if the SafeFileEnvironment option (see SafeFileEnvironment) was declared, sendmail performs a chroot(2) into the directory specified. If the chroot(2) fails, sendmail prints and logs the following error and the child exits with EX_CANTCREAT:

mailfile: Cannot chroot(directory)

Next, regardless of whether the df file is opened, sendmail changes its gid:

  • If there is a controlling user, sendmail sets its gid to that of the controlling user.

  • Otherwise, if the set-group-id bit is set in the file's saved permissions, sendmail changes its gid to that of the group of the file.

  • Otherwise, sendmail checks to see whether the U= equate is set for this delivery agent (U=). If the U= equate is set, sendmail changes its gid to that specified.

  • Otherwise, sendmail changes its gid to that specified by the DefaultUser option (DefaultUser).

After this, sendmail changes its uid, using the same rules that it used for the gid.

The file (and possibly the path to it) is then checked to see whether it is safe to write to. See the -d44 debugging switch (-d44.4) for a description of this process.

If safe, file is then opened for writing in append mode. If sendmail cannot open the file, it prints the following error message, and the child exits with EX_CANTCREAT:

cannot open: reason for error here

If an open fails for a retryable reason, it is attempted 10 more times (sleeping progressively longer between each try)[5] on the assumption that on busy systems there might be a temporary lack of resources (such as file descriptors). The open includes file locking with flock(2) or fcntl(2) to prevent simultaneous writes.

[5] The progression is 0 seconds for the first sleep, then 10 seconds, then 20 seconds, then 30 seconds, and so on.

Once the file is opened, the header and body of the mail message are written to it. Note that translations are controlled by the F= flags of the prog delivery agent for all but V8 sendmail. V8 sendmail uses the F= flags of the *file* delivery agent. For example, F=l (see F=l (lowercase L)) marks this as final delivery.

If any write error occurs, sendmail prints the following error message, truncates the file to its length before any writes started, and quits trying to write to that file:

I/O error

Finally, the file's permissions are restored to those that were saved earlier, and the file is closed with fclose(3). If the set-user-id or set-group-id bits were stripped because there was a controlling user, they are restored here.[6] If the file didn't originally exist, its permissions become 0600.

[6] This is because some paranoid systems, such as BSD Unix, turn off the set-user-id and set-group-id bits when a file is written other than by root.

12.2.3 Delivery via Programs

When any of the addresses to the right of a colon in the alias list begins with a | character, delivery is made by piping the mail message through a program. This is automatic with modern configuration files.[7] Beginning with V8.7 sendmail, any delivery agent for which the F=| flag (F=| (vertical bar)) is set can also pipe messages through programs. To disable this ability, simply remove the F=| flag from all delivery agent declarations in your configuration file.

[7] If your older configuration file does not automatically recognize a leading | character, you might need to add a new rule near the end of your rule set 0. For example:

The forms that a program address can legally take in the aliases(5) file (or ~/.forward file; see Section 13.7.4) are as follows:

"|prg args"
|"prg args"

Here, prg is the full path of the program to be run (the environment variable PATH is not available). If command-line arguments are needed for the program, they must follow prg, and the entire expression must be quoted. The leading full quotation mark can either precede or follow the |. If the address is quoted with full quotation marks, the leading quotation mark is ignored in determining the leading | character.

To execute the program, sendmail executes the command in the P= equate of the prog delivery agent. That command is usually one of the following:

/bin/sh -c
/usr/bin/smrsh -c

These tell sendmail to run /bin/sh (the Bourne shell) or /usr/bin/smrsh (the sendmail restricted shell) to execute the program specified by prg. The -c tells that shell to take any arguments that follow and execute them as though they were commands typed interactively to the shell. These arguments are constructed by removing the leading | from the program address and appending what remains, quotation marks and all, to the P= command. For example, if an alias looked like this:

jim: "|/etc/local/relo jim@otherhost"

the Bourne shell would be executed with the following command line:

/bin/sh -c "/etc/local/relo jim@otherhost"

The result of all this is that sendmail runs the Bourne shell and then the Bourne shell runs the /etc/local/relo program.

Mail is delivered under this scheme by attaching the output of sendmail to the standard input of the shell and attaching the standard output and standard error output of the shell to the input of sendmail. The sendmail program simply prints the mail message to the shell and reads any errors that the shell prints in return.

Although this process appears to be fairly straightforward, many things can go wrong. Failure usually results in the mail message being bounced. Possible failures

To communicate with the P= program (the Bourne shell), sendmail creates two communications channels using pipe(2). This can fail because the system is out of file descriptors or because the system file table is full. Failure results in one of the following errors:

openmailer: pipe (to mailer)
openmailer: pipe (from mailer)

Next, sendmail executes a fork(2). The child later becomes the P= program. This can fail because the system limit on the maximum allowable number of processes has been exceeded or because virtual memory has been exhausted. Failure causes the following error message to be printed:

openmailer: cannot fork

In establishing a communications channel, the sendmail child process creates a copy of its standard input file descriptor. This can fail because the system limit on available file descriptors has been exceeded. When this happens, the following message is printed (note that not all dup(2) failures produce error messages):

Cannot dup to zero!

Finally, the child transforms itself into the A= program with execve(2). If that transformation fails, the following error message is produced, where program is argv[0] for the A= program (in this case, usually /bin/sh):

Cannot exec program

Failure can be caused by a wide range of problems. If a retryable error occurs, the message is queued for a later try.

Programs in the aliases file are run with the prog delivery agent. As a consequence, that delivery agent should have the F=s (strip quotes) flag set.

    Part I: Build and Install
    Part II: Administration
    Part III: The Configuration File