20.6 How a Delivery Agent Is Executed

For safety and efficiency, sendmail undertakes a complicated series of steps to run (execute) a delivery agent.[22] Some (such as setting the environment) are intended to improve security. Others (such as forking) are required so that sendmail can launch delivery agents. Here, we discuss those steps in the order in which they are taken by sendmail.

[22] For the purpose of this discussion we will exclude the internal agents (such as IPC) and focus on actual programs (such as /bin/mail).

20.6.1 The Fork

When sendmail performs delivery, it cannot simply replace itself with the delivery agent program. Instead, it must fork(2), and the child will replace itself.

If sendmail is running in verbose mode (Verbose), it shows that it is about to start this process:

Connecting to delivery agent

If a traffic-logging file was specified with the -X command-line switch (Section 14.2), sendmail appends the following line to that file:

pid =  == EXEC the expanded A= here 

Here, the A= delivery agent equate (A=) from the delivery agent's declaration is printed with all its sendmail macros expanded and with the recipients listed.

Next sendmail creates a pipe so that it will be able to print the email message to the delivery agent and so that it can read errors emitted by the delivery agent. See the -d11 debugging switch (-d11.1) for a description of what can go wrong.

If all has gone well, sendmail fork(2)s a copy of itself. The parent then pipes the email message to the child.

When the entire message has been sent, the parent then wait(3)s for the child to complete its work and exit(2)s. The parent collects the exit(2) value from the child and determines delivery success based on that exit value.

20.6.2 The Child

The child is the copy of sendmail that will transform into the delivery agent. Before the child can transform, it must perform a few more necessary steps.

If sendmail was compiled with HASSETUSERCONTEXT defined (HAS...), it calls setusercontext(3) like this:

setusercontext(NULL, pwd,  user-id,  LOGIN_SETRESOURCES|LOGIN_SETPRIORITY);

Here, pwd is a pointer to a structure of type passwd for the user whose user-id is user-id. The user-id is that of the controlling user (Section 12.2.2) or the recipient (F=o).

The sendmail program next sets its group-id as appropriate. If the DontInitGroups option (DontInitGroups) is false, sendmail calls initgroups(3). The group identity used is that described under the DefaultUser option (DefaultUser).

If the /= delivery agent equate (/= (forward slash)) has a non-NULL value, sendmail calls chroot(8) to change its topmost directory into a private directory tree.

If the N= delivery agent equate (N=) has a nonzero value, sendmail calls nice(3) to "re-nice" the delivery agent to that value.

The sendmail program then sets its user-id. The user identity used is chosen by the mailer F=S and U= equates and the DefaultUser option as detailed in DefaultUser.

The sendmail program then attempts to chdir(2) into one of the directories listed in the D= delivery agent equate (D=).

Next, sendmail dup(2)s the pipes created in the previous section.

Finally, sendmail calls setsid(2) to become a process-group leader and execve(2) to become the delivery agent. That latter call looks like this:

execve(agent,  argv,   envp);  

Here, agent is the full path of the delivery agent as specified in the P= delivery agent equate (P=). The argument vector (contents of the A= delivery agent equate with all the sendmail macros expanded and all the recipients added) is passed as argv. The environment is that originally given to sendmail, massaged for security and augmented by the E configuration command (Section 10.2.1).

If the execve(2) fails, the child exits with an appropriate error code.

    Part I: Build and Install
    Part II: Administration
    Part III: The Configuration File
    Chapter 21. The D (Define a Macro) Configuration Command
    Chapter 24. The O (Options) Configuration Command