sendmailSearch this book
Previous: 24.1 The aliases(5) FileChapter 24
Next: 24.3 Write a Delivery Agent Script

24.2 Forms of Alias Delivery

The right-hand side of an alias entry can take four forms:

local:  user
local:  /file
local:  |program
local:  :include: list

The user specifies final delivery to a user's mail spool file (via the ~/.forward file), delivery to a new address (e.g. user@newsite), or one step in further aliasing. 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.

24.2.1 Delivery to Users

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

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

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

24.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 most configuration files but not with others. If your configuration file does not automatically recognize a leading / character, you will need to add a new rule near the end of your rule set 0 (see Section 29.6, "Rule Set 0"). For example,

R/$+     $@ $#local $: /$1

Beginning with V8.7 sendmail, any delivery agent for which the F=/ flag (see Section 30.8.9, 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.

In the list of addresses to the right of the colon, sendmail considers any local address that begins with the / character to be name of a file. [2] 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 primarily in sendmail so that failed mail may 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 optimum, as you will see.

[2] 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. Note that this works only if the smtp delivery agent omits the F=/ flag.

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 0666. Under V8.7 the decision to use stat(2) versus lstat(2) to obtain the permissions is determined by the SafeFileEnvironment option (see Section 34.8.58, SafeFileEnvironment).

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 suid and sgid 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 may be specified in the qf file (see Section 23.9.2, C line).

Then the queue df file (see Section 23.9.3, 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 data file 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 Section 34.8.58) 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, whether the df file is opened or not, sendmail changes its gid:

After this, sendmail changes its uid, using the same rules that it used for the gid except that the last step uses the DefaultUser (u) option.

The file (and possibly the path to it) is then checked to see whether it is safe to write to. This is done by using the internal safefile() subroutine. See the -d44 debugging switch (Section 37.5.159, -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, it is attempted 10 more times (sleeping progressively longer between each try) [3] on the assumption that on busy systems there may 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.

[3] 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 Section 30.8.28, F=l (lowercase L)) marks this as final delivery.

If any write error occurs, sendmail prints the following error message and continues:

I/O error

Finally, the file's permissions are restored to those that were saved above, and the file is closed with fclose(3). If the suid or sgid bits were stripped because there was a controlling user, they are restored here. [4] If the file didn't originally exist, its permissions become 0666.

[4] This is because some paranoid systems, such as BSD UNIX, turn off the suid/sgid bits when a file is written to other than by root.

In general, the file form of an alias is a poor way to save mail messages to a file. Instead, the use of a separate program procmail(8) is recommended (see Section, "The procmail program").

24.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 most configuration files but not with others. If your configuration file does not automatically recognize a leading | character, you will need to add a new rule near the end of your rule set 0 (see Section 29.6). For example,

R|$+     $@ $#local $: |$1

Beginning with V8.7 sendmail, any delivery agent for which the F=| flag (see Section 30.8.8, 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.

The forms that a program address can legally take in the aliases(5) file (or ~/.forward file; see Section 25.7.4, "Piping Through Programs") 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 may 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 one of the following:

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

These tell sendmail to run /bin/sh (the Bourne shell) or /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, there are many things that 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 one occurs and the delivery agent is local, the message is queued for a later try. Otherwise, requeueing occurs only if the error return value is a transient one defined in transienterror() in conf.c. [5]

[5] Because of a bug in all but the IDA and V8 versions, the message is silently discarded without being requeued or bounced.

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.

Previous: 24.1 The aliases(5) FilesendmailNext: 24.3 Write a Delivery Agent Script
24.1 The aliases(5) FileBook Index24.3 Write a Delivery Agent Script