Chapter 11. Manage the Queue

Mail messages can be either delivered immediately or held for later delivery. Held messages are referred to as “queued.” They are placed into either a single holding directory (usually called mqueue) or several directories from which they are later delivered. There are many reasons a mail message might be queued:

  • If a mail message is temporarily undeliverable, it is queued and delivery is attempted later. If the message is addressed to multiple recipients, it is queued only for those recipients to whom delivery is not immediately possible.

  • If the SuperSafe option (SuperSafe on page 1096) is set to true (the default setting required by RFC2821), all mail messages are queued for safety while delivery is attempted. The message is removed from the queue only if delivery succeeds. If delivery fails, the message is left in the queue, and another attempt is made to deliver it later. This causes the mail to be saved in the unhappy event of a system crash during processing.

  • If sendmail is run with the DeliveryMode option (DeliveryMode on page 1004) set to queue-only or to defer, all mail is queued, and no immediate delivery attempt is made. A separate queue run is required to attempt delivery.

  • If the load (average number of blocked processes) becomes higher than the value given to the QueueLA option (QueueLA on page 1072), sendmail will queue a message rather than attempt to deliver it. (Beginning with V8.14, this load average cutoff can be more finely tuned by using the DaemonPortOptions option’s queueLA key; DaemonPortOptions=queueLA= (8.14 and later) on page 997). A separate queue run is required later to process the queue.

Overview of the Queue

The sendmail queue is implemented by placing held messages into one or more directories. Prior to V8.10, there was only one directory, and its name was usually mqueue. Now, the directory or directories to be used are specified in the configuration file with the QueueDirectory option (QueueDirectory on page 1070):

OQ/var/spool/mqueue                         ← pre-V8.7 form
O QueueDirectory=/var/spool/mqueue          ← beginning with V8.7
O QueueDirectory=/var/queues/q.*            ← V8.10 multiple directories

If the QueueDirectory option is missing, the name defaults to mqueue. The location should never be relative (as mqueue). When it is wrongly specified as a relative path name, it is taken as relative to the location where sendmail is run. Because the sendmail daemon is typically started from an rc file at boot time, such relative locations are usually relative to the root (/) directory.[175]

After sendmail has processed its configuration file, it does a chdir(2) into its base queue directory and does all the rest of its work from there. In the first two lines of the previous example, the base queue directory is /var/spool/mqueue. In the last line, the base queue directory is /var/queues. This change into the base queue directory has three side effects:

  • Should the sendmail program fault and produce a core dump, the core image is left in the base queue directory.

  • Any relative pathnames that are given to options in the configuration file are interpreted as relative to the base queue directory. (This is not true for the F configuration command, The F Class Command on page 857. Those files are processed at the same time as the configuration file, before the chdir.)

  • If you use V8.12 and later queue groups (Queue Groups (V8.12 and Later) on page 408), all the queues used by those queue groups must be subdirectories of the base queue directory.

The base queue directory, and all subdirectories under it, should be set to have very narrow permissions. They must be owned by root. We (and CERT) recommend a mode of 0700. Prior to V8 sendmail, such narrow permissions would cause C-shell scripts run from a ~/.forward file to fail. V8 sendmail lets you specify alternative directories in which to run programs (see the D= delivery agent equate, D= on page 741). This allows you to use mode 0700 queue directories without the associated problems.

As a further precaution, all the components of the path leading to the queue directories should be owned by root and be writable only by root. In the case of our example of /var/spool/mqueue, permissions should look like this:

drwxr-xr-x  root    /
drwxr-xr-x  root    /var/
drwxr-xr-x  root    /var/spool/
drwx------  root    /var/spool/mqueue/

For additional security, see the restrictmailq keyword for the PrivacyOptions option (PrivacyOptions=restrictmailq on page 1069). It allows only users in the same group as the group ownership of the queue directory to be able to print its contents with mailq or -bp (Printing the Queue on page 422).

Parts of a Queued Message

When a message is stored in the queue, it is split into pieces. Each piece is stored as a separate file in the queue directory. That is, the header and other information about the message are stored in one file, while the body (the data) is stored in another. All told, six different types of files can appear in the queue directory. The type of each is denoted by the first two letters of the filenames. Each filename begins with a single letter followed by an f character. The complete list is shown in Table 11-1.

Table 11-1. Queue file types

File

§

Description

df

The Data (Message Body) File: df on page 398

Data (message body)

lf

Queue File Locking on page 398

Lock file (obsolete and removed as of V5.62)

nf

The ID Creation File (Obsolete As of V5.62): nf on page 399

ID creation file (obsolete and removed as of V5.62)

tf

The Temporary qf Rewrite Image: tf on page 400

Temporary qf rewrite image

xf

The Transcript File: xf on page 401

Transcript file

qf

The Queue Control File: qf on page 399

Queue control file (and headers)

The complete form for each filename is:

Xfident

The X is one of the leading letters shown in Table 11-1. The f is the constant letter f. The ident is a unique queue identifier associated with each mail message.

In the following sections, we first describe the identifier that is common to all the queue file parts, then describe each file type in alphabetical order. The internal details of the qf file can vary depending on the version of sendmail, so it is discussed separately at the end of this chapter.

The Queue Identifier

To ensure that new filenames are not the same as the names of files that might already be in the queue, sendmail uses the following pattern for each new ident:

AApidprior to V8.6
hourAApidbeginning with V8.6
YMDhmsSEQpidbeginning with V8.10

Here, pid is the process identification number of the incarnation of sendmail that is trying to create the file. Because sendmail often fork(2)s to create queue entries, that pid is likely to be unique, resulting in a unique ident. The AA is used as a clock to prevent duplicate filenames. For V8.6 through V8.9 sendmail, an extra letter prefixes the AA. Shown as hour, it is an uppercase letter that corresponds to the hour (in a 24-hour clock) that the identifier was created. For example, a file created in hour three of the day will have a D prefixed (the hour begins at midnight with A).[176]

For V8.10 sendmail, the identifier is constructed differently. Each character stands for (in this order, reading left to right): the year (minus 1900) modulo 60, the month, the day, the hour, the minute, the second, and a sequence within the second that starts at a random value. Each is used as an offset into a special array that looks like this:[177]

0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx

Thus, the following identifier:

lC9GgvB04136

means the year is 2007 (the l), the month is December (the C), the day is the 9th (the 9), the time is 16:42:57 (the Ggv), the sequence is 11 (the B), and the process ID of the process that created the file is 04136. The advantage to this algorithm is that no two identifiers will ever be the same during a given 60-year period. Although this latest method has stayed the same from V8.10 through V8.14, there is no guarantee that it will remain the same in future releases.

Prior to V8.10, if sendmail could not create an exclusive filename because a file with that identifier already existed, it clocked the second A of the AA to a B and tried again. It continued this process, clocking the righthand letter from A to Z and the lefthand letter from A to ˜ until it succeeded:

AA            ← start
AB            ← second try
AC            ← third try
... and so on
˜W
˜X
˜Y            ← last try
˜Z            ← failure

If it never succeeded, the ident became one like the following and sendmail failed:

hour˜Zpid

But this ident was unlikely to ever appear because the clocking provided for more than 1,600 possibilities.

All the files associated with a given mail message share the same ident as a part of their filenames. The individual files associated with a single mail message differ only in the first letter of their names.

The Data (Message Body) File: df

All mail messages are composed of a header and a body. When queued, the body is stored in the df file.

Traditionally, the message body could contain only characters that had the high (most significant) bit turned off (cleared, set to 0). But under V8 sendmail, with a version 2 or higher configuration file (The V Configuration Command on page 580) the high bit is left as is until delivery (whereupon the F=7 delivery-agent flag, see F=7 on page 764, determines whether that bit will be stripped during delivery).

Because the message body can contain sensitive or personal information, the df file should be protected from reading by ordinary users. If the queue directory is world-readable, the TempFileMode option (TempFileMode on page 1097) should specify minimum permissions (such as 0600) for queued files. But if the queue directory is protected by both narrow permissions and a secure machine, the TempFileMode option can be relaxed for easier administration.

There is currently no plan to provide for encryption of df files. If you are concerned about the privacy of your message, you should use an end-to-end encryption package or an encrypting filesystem (not discussed in this book).

Queue File Locking

When old versions of sendmail process a queued message (attempt to redeliver it) they create an empty lock file. That lock file was needed to signal other running sendmail processes that the mail message was busy so that they shouldn’t try to deliver the message too. Current versions simply flock(2) or fcntl(2) lock the qf file.

Current-style file locking

The method that sendmail uses to initially create an exclusive lock when first queueing a file is twofold. First it attempts to creat(2) the file with the argument:

O_CREAT|O_WRONLY|O_EXCL

If that succeeds, it then attempts to lock the file. If HASFLOCK (HAS... on page 114) is defined when sendmail is compiled, the file is locked with flock(2). Otherwise, it is locked with a fcntl(2) F_SETLK argument.

Locks shown when printing the queue

When mailq is run (or the -bp command-line switch is given to sendmail), the contents of the queue are listed. In that listing, an asterisk that appears to the right of an identifier indicates that a lock exists on the message:

/var/spool/mqueue/df (1 request)
 ----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------
 dB91UPA04168*       0 Wed Dec  8 17:30 <gw@wash.dc.gov>
             ↑
                                             <ben@franklin.edu
             note

Locks can get stuck

Occasionally, a file will become locked and remain that way for a long time. One indication of a stuck lock is a series of syslog messages about a given identifier:

Apr 12 00:33:38 ourhost sendmail[641]: dB91UPA04168: locked
Apr 12 01:22:14 ourhost sendmail[976]: dB91UPA04168: locked
Apr 12 02:49:23 ourhost sendmail[3251]: dB91XUs04170: locked
Apr 12 02:49:51 ourhost sendmail[5977]: dB91UPA04168: locked
Apr 12 03:53:05 ourhost sendmail[9839]: dB91UPA04168: locked

An occasional lock message, such as dB91XUs04170 in the third line in this example, is normal. But when an identifier is continually reporting as locked (such as the dB91UPA04168 lines), an orphaned lock might exist and should be investigated. Use ps(1) to look for lines that list queue file identifiers:

root      5338 160  -dB91UPA04168 To wash.dc.gov (sendmail)

This shows that the queued mail message, whose identifier is dB91UPA04168, is currently being processed. If the lock on that file is stuck, consider killing the sendmail that is processing it.

The ID Creation File (Obsolete As of V5.62): nf

Old versions of sendmail used an nf file when creating a message identifier to avoid race conditions.[178] But contemporary versions of sendmail create the queue identifier when first creating the qf file. The nf file is obsolete.

The Queue Control File: qf

A queued mail message is composed of two primary parts. The df file contains the message body. The qf file contains the message header.

In addition to the header, the qf file also contains all the information necessary to:

  • Deliver the message. It contains the sender’s address and a list of recipient addresses.

  • Order message processing. It contains a priority that determines the current message’s position in a queue run of many messages.

  • Expire the message. It contains the date that the message was originally queued. That date is used to time out a message.

  • Explain the message. It contains the reason that the message is in the queue and possibly the error that caused it to be queued.

The qf file is line-oriented, with one item of information per line. Each line begins with a single uppercase character (the code letter), which specifies the contents of the line. Each code letter is then followed by the information appropriate to the letter. The code letters and their meanings are shown in Table 11-6 on page 446.

Here is an example of a version 8 (for V8.14 sendmail) qf file:

V8
T944703473
K0
N0
P1
I7/22/19133
Fwbs
$_you@localhost
${daemon_flags}c u
Syou@your.domain
Ayou@your.domain
rRFC822; george@wash.dc.gov
RPFD:george@wash.dc.gov
H?P?Return-Path: <you>
H??Received: (from you@localhost)
        by your.domain (8.14.1/8.14.1) id g38DcXCL026713
        for george@wash.dc.gov; Fri, 14 Dec 2007 17:37:53 −0800 (PST)
H?D?Date: Fri, 14 Dec 2007 17:37:53 −0800 (PST)
H?F?From: Your Name <you>
H?x?Full-Name: Your Name
H?M?Message-Id: <200704081338.g38DcXCL026713@your.domain>

This fictional qf file shows the information that will be used to send a mail message from (the S line) to one recipient: (the R line). It also shows the various headers that appear in that message (the H lines). We discuss the individual lines of the qf file at the end of this chapter.

The Temporary qf Rewrite Image: tf

When processing a queued message, it is often necessary for sendmail to modify the contents of the qf file. This usually occurs if delivery has failed or if delivery for only a part of the recipient list succeeded. In either event, at least the message priority needs to be incremented.

To prevent damage to the original qf file, sendmail makes changes to a temporary copy of that file. The temporary copy has the same queue identifier as the original, but its name begins with a t.

After the tf file has been successfully written and closed, sendmail calls rename(2) to replace the original with the copy. If the renaming fails, sendmail syslog(3)s at LOG_CRIT a message such as the following:

cannot rename(tfdB91brx04175, qfdB91brx04175), df=dfdB91brx04175

Failure to rename is an unusual but serious problem: a queued message has been processed, but its qf file contains old and incorrect information. This failure might, for example, indicate a hardware error, a corrupted queue directory, or that the system administrator accidentally removed the queue directory.

The Transcript File: xf

A given mail message can be destined for many recipients, requiring different delivery agents. During the process of delivery, error messages (such as “User unknown” and “Permission denied”) can be printed back to sendmail by each delivery agent.

While calling the necessary delivery agents, sendmail saves all the error messages it receives in a temporary file. The name of that temporary file begins with the letters xf. After all delivery agents have been called, sendmail returns any collected error messages to the sender and deletes the temporary xf file. If there are no errors, the empty xf file is silently deleted. A -d51.104 debugging switch setting can be used to prevent deletion of the xf file.

See Using qf, df, and xf Subdirectories on page 403 for a way to relocate xf files to a memory-based filesystem.

Using Multiple Queue Directories

Beginning with V8.10, sendmail allows the use of multiple queue directories. These multiple queue directories take two forms:

  • More than one queue directory can be specified, possibly on separate disks, into which all the qf, df, and xf files are placed.

  • Any queue directory can have a subdirectory named qf, and/or df, and/or xf, in which sendmail stores the corresponding qf, df, and xf files.

Multiple Queue Directories

V8.10 sendmail offers the ability to distribute queued messages across multiple directories. In general, this is a good idea. If, for example, a high volume of email is stressing your current disk, you can improve efficiency by using multiple queue directories spread over multiple disks and controllers.

To illustrate, we will set up a machine that has three brand-new disks to use as multiple queue directories. The disks have already been formatted and a filesystem has been placed on each. We next create directories on which to mount them:

# mkdir /var/queues /var/queues/q.1 /var/queues/q.2 /var/queues/q.3
# chmod 700 /var/queues /var/queues/q.?

Because of the way multiple queue directories are implemented inside sendmail, the queue directory names must differ only in their suffixes, hence the trailing 1, 2, and 3. First the directories are created with mkdir(1) or a symbolic link, and then the permission on each is reduced to readable and writable only by root for security reasons. Note that these are the permissions after all queue disks are mounted.

Next, arrange for the disks to be mounted by placing the appropriate entries in /etc/fstab or /etc/vfstab. Here, we illustrate with the partial contents of /etc/fstab for Linux:

/dev/hda2         /var/queues/q.1     ext2     defaults    1 1
/dev/hdb1         /var/queues/q.2     ext2     defaults    1 1
/dev/hdc1         /var/queues/q.3     ext2     defaults    1 1

Note that we are mounting a separate disk on each queue directory. Your disk device names will doubtless differ, and you can use any directory locations and names you wish. Note that after you mount the disks, you might need to change the permissions again to 700 for each mount point.

The idea is to prepare the directories for use as multiple queue directories first, and after that, to modify the configuration file so that sendmail can use those queue directories:

define(`QUEUE_DIR',`/var/queues/q.*')

Here, the QUEUE_DIR mc configuration macro is given the value /var/queues/q.*, which will become the value for the QueueDirectory option. The trailing * character is a literal asterisk (not a wildcard character) and must appear as a suffix, in the last position of the path specification. It tells sendmail to use all the queue directories that begin with the path /var/queues/q. and end with any other characters. In our example, sendmail will match /var/queues/q.1, /var/queues/q.2, and /var/queues/q.3.

It is not strictly necessary to mount a disk on each queue directory. If the directory name is a symbolic link to another directory, sendmail will use that other directory as a queue directory. The only requirement is that the other directory has as restrictive set of permissions as the original queue has.

Printing multiple queue directories

After you have configured for multiple queue directories, you will find there is a small difference in the way various versions of sendmail print the queue contents. Prior to V8.10 sendmail, the heading for a queue listing printed like this:

Mail queue is empty            ← when nothing is queued (pre-V8.10)
Mail queue (1 request)         ← when one message is queued (pre-V8.10)

Starting with V8.10 sendmail, that heading now looks like this:

/var/spool/mqueue is empty      ← when nothing is queued (V8.10 and later)
/var/spool/mqueue (1 request)   ← when one message is queued (V8.10 and later)

The full pathname of the queue is printed, regardless of whether you are running multiple queue directories. This behavior is beneficial when running multiple queue directories because it lets you know which queue directory contains what mail:

/var/spool/mqueues/q.1 is empty
                /var/spool/mqueues/q.2 (1 request)
----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------
dB9Fdaa06420     4567 Thu Dec  9 07:39 you@your.domain
                                       <gw@us.gov>
                Total Requests: 1

From this output, it is clear that q.1 is empty and q.2 contains a single message. Unfortunately, the two headings indent differently, but that’s easy to get used to.

In the previous output, also notice that when multiple queue directories are printed, a trailing line is printed after all queue directories are printed that shows the total of all messages in all queue directories. If you run dozens or hundreds of queue directories, you might find it useful to summarize the number of queued messages like this:

% mailq -OMaxQueueRunSize=1 | tail −1
                           Total Requests: 41291

The expression -OMaxQueueRunSize=1 (MaxQueueRunSize on page 1050) causes sendmail to process each queue directory extremely fast, regardless of how many messages are queued in each.

Processing multiple queue directories

When sendmail processes multiple queue directories it processes them in parallel. That is, it forks and runs a queue processing child of sendmail for each, all of which run at the same time. The maximum number of sendmail queue processors run is limited by the MaxDaemonChildren option (MaxDaemonChildren on page 1044). If that limit is reached before all the queue directories can be parallel-processed, sendmail will remember where it stopped and perform the next run starting from where the prior run left off.

The only exception to this behavior occurs when queue processing with the -v (verbose) command-line switch. When -v is combined with -q, processing is always sequential. That is, one queue directory is processed at a time, and the next is not begun until the first finishes. The -v allows you to watch the queue being processed, so it makes sense that you would want to watch only one queue directory at a time.

Using qf, df, and xf Subdirectories

Beginning with V8.10, sendmail allows the qf, df, and xf files to reside in separate directories. One advantage to this is that it produces directories that are one-third smaller. Another advantage is that each part can reside on a separate disk for further performance enhancements.

This feature is enabled by simply creating the appropriately named subdirectories, or symbolic links, in each queue directory. The names of those subdirectories or symbolic links are the literals qf, df, and xf. But be aware that you should not create those directories or links when mail is already queued. If you do, that queued mail will disappear from sendmail’s view and will never be delivered. If you need to make the change while mail is queued, first stop sendmail, and then execute the following commands and restart sendmail:

# mkdir df qf xf
# chmod 700 df qf xf
# mv df?* df/if mail is already queued
# mv qf?* qf/if mail is already queued
# mv xf?* xf/if mail is already queued

Here, we first create the new subdirectories in the queue directory. Then we reduce their permissions to the narrow ones that match the queue directory. Finally, if queued mail already existed in the queue directory, we move that mail into the new subdirectories where sendmail will find it.

Because xf files are empty for all successfully delivered mail, there is a penalty for creating and deleting those files just because they might be needed. When performance is of concern, you can either mount a memory filesystem on the xf subdirectory, or replace the xf subdirectory with a symbolic link to a directory on a memory filesystem. In the following, we show an /etc/fstab file for a SunOS machine that uses the direct-mount approach:

/dev/sd0g  /var/spool/mqueue/df        4.2 rw        1 4
/dev/sd2g  /var/spool/mqueue/qf        4.2 rw        1 2
swap       /var/spool/mqueue/xf        tmp rw        0 0

Shortly. we will describe how to use a different type of disk for each part, and how performance is impacted by such choices.

An artifact of using qf, df, and xf subdirectories is seen when printing the queue. The df directory is always the one listed:

/var/spool/mqueue/df is empty

Handle Deep Queues

To understand the potential problems associated with deep queues, first consider how sendmail processes a single queue when its QueueSortOrder option (QueueSortOrder on page 1073) is set to the default of priority.[179] When sendmail is instructed to process a queue it opens the queue directory for reading and reads that directory to gather a list of qf files to process. Each qf file sendmail finds is opened for reading and scanned for important pieces of information. The N line in each qf file, for example, holds the number of times the message has been tried. The P line holds each message’s current priority.

After all messages have been opened, read, and closed, and after the information from each has been saved internally, sendmail sorts that information. The purpose of the sort is to ensure that new mail is tried before old, and that high-priority mail is tried before low-priority mail.

Under normal circumstances, this process occurs quickly. But when queues get abnormally deep, things can go wrong. In the following, which illustrates a problem that can occur, we show one way that sendmail could be run on a major mail-sending machine:

/usr/sbin/sendmail -bd
/usr/sbin/sendmail -q10m

The idea here is to create two mail-handling daemons. The first handles inbound mail, and because this is a mail-sending machine, we expect that this inbound daemon will perform little work. The second daemon sends all mail it finds in its queue. It will fork(2) a copy of itself once every 10 minutes, and that copy will process all the messages in the queue. As described earlier, each queued message is opened and read so that all the messages can be sorted before delivery begins.

Because this hypothetical site is a major mail-sending site, we expect a high rate for the number of sent messages. For the sake of argument, let’s say 30,000 messages need to be sent per hour.

Now suppose a backhoe, a power failure, clumsy fingers, or any of a thousand possible disasters causes this site’s only connection to the Internet to fail for an hour, and the site can neither look up host information with DNS, nor connect to any remote sites. All the mail it tries to send that hour fails, and instead of being removed from the queue, this failed mail is left there to be tried again later (presumably after the problem is fixed).

An hour later, service is restored. First, the default:

/usr/sbin/sendmail -q10m

causes a forked copy of sendmail to start processing the queue. This time, however, the processing is not swift. When a queue fills to 30,000 or more messages, the amount of time it takes to preread the queue (to open and read every message) increases to more than 20 minutes.[180] And those 20 minutes are only for the preread. During those 20 minutes no mail will be sent.

After that, things get worse. Ten minutes later a second sendmail daemon is forked, and it, too, starts to preread the queue. Now, instead of one sendmail daemon opening and reading all messages in a queue, we have two sendmail daemons doing the same thing in parallel.

Contrary to what you might think, twice as much I/O on a disk is not twice as fast. Disks are finite devices that perform a limited number of disk-head moves[181] per second and can transmit only a fixed number of bytes per second. Because the two sendmail daemons are 10 minutes out of step with each other, each is reading and processing separate files. Depending on the size of your in-memory disk cache, neither will likely be able to take advantage of the efficiencies of such caching. In short, two sendmail daemons processing a deep queue in parallel is worse than a single sendmail daemon processing that same queue alone.

And if that weren’t enough, another 10 minutes later a third sendmail daemon starts to process the queue.

By now, the first sendmail daemon might have finished its preread of the queue and might have actually begun to send messages. But even if it has, three sendmail daemons are now processing that single deep queue and a curious thing happens. Because the disk that holds the queue is finite, the addition of a third sendmail daemon slows the operation of the first two. The second one, instead of taking 20 minutes to preread the queue, will now take 30 minutes.

This means that every 10 minutes another sendmail queue-processing daemon is added to the mix. As each is added, each slows all the others that are already running, and it isn’t long before the load on the machine starts to climb and the rate at which messages are delivered falls at an alarming rate. In fact, when this sort of behavior hits a very large-volume site, a sendmail queue-processing daemon can start and seem to never finish.

Depending on the speed of your disk system, even limiting the number of queue processors per queue might not save you from this sluggish performance. Under V8.12 sendmail, for example, you can limit the number of queue runners per queue with a queue group (Queue Groups (V8.12 and Later) on page 408) definition such as this in your mc configuration file:

QUEUE_GROUP(`fastq', `P=/q/fastq*, I=10m, R=10')

Here, the fastq group uses the queue disks mounted as /q/fastq*, processes those disks once per 10 minutes (the I=10m), and limits itself to 10 queue runners maximum (the R=10) across all the disks. If there are few fastq* queue disks, and if they fill to more than 30,000 messages each, they too can become sluggish, even with only 10 runners processing them. In fact, with sufficient filled queue depth, as few as two simultaneous queue runners can seriously affect performance.

In extreme situations such as this, one alternative is to use persistent queue runners (Persistent Queue Runners with -qp on page 434). With persistent queue runners, you maintain a single queue runner that alone reads the queue. After that single queue runner has read the queue, it forks multiple child queue runners to process the queue, with each child sharing the parent’s queue information:

/usr/sbin/sendmail -qp10m

Here, the -qp causes one or more persistent queue runners to launch. One is launched for each queue group, and will persist to run, sleeping 10 minutes between each reading of the queue. When it awakes, it gathers a list of queue files and launches multiple child processes to handle that list. After the last child has finished delivery and exited, the parent sleeps again.

Even with queue groups and persistent queue runners, you are encouraged to spread queues across many directories and across many disks and controllers. This increases parallelism and dramatically lessens the likelihood that any given queue will overfill.

Recover from a Full Queue

When a queue directory is exceptionally full, you will likely notice the problem only when performance on your queue-handling machine becomes unusually sluggish. By that time, however, a drastic measure, such as rebooting the server, might be the only cure. Clearly, early detection is desirable.

Early signs that a queue is filling can be seen in the logging messages that sendmail produces. You can develop scripts that watch for lines such as these:

Dec 13 10:27:53 your.domain sendmail[642]: grew WorkList for /var/spool/mqueue to 2000
Dec 13 10:29:05 your.domain sendmail[642]: grew WorkList for /var/spool/mqueue to 3000
Dec 13 10:34:31 your.domain sendmail[642]: grew WorkList for /var/spool/mqueue to 4000
... etc., to:
Dec 13 12:40:22 your.domain sendmail[642]: grew WorkList for /var/spool/mqueue to
29000
Dec 13 12:42:50 your.domain sendmail[642]: grew WorkList for /var/spool/mqueue to
30000

Here, the WorkList refers to the number of messages preread so far. By searching for unusual sizes, you can determine when a queue is about to overfill.

Another technique is to run the mailq command to observe the total number of messages queued across all queues:

% mailq -OMaxQueueRunSize=0 | tail −1V8.7 through V8.11
                           Total Requests: 34190

% mailq -bPV8.12 and later
/var/spool/mqueues/q.1/df: entries=34190
                Total requests: 34190

For V8.7 through V8.11, the MaxQueueRunSize=0 allows mailq to run swiftly, regardless of how deep the queue or queues might be. Without that option, and with deep queues, mailq would be just as slow as the sluggish queue runs, but beginning with V8.12, the -bP command-line switch does the same thing more quickly.

No matter how you detect the problem, the solution will be the same. First, you need to kill all the competing sendmail queue-processing daemons. There are a wide number of ways to do this. The most common is to use ps(1) to gather PID numbers and then kill each queue-processing daemon individually. No matter how you kill the queue-processing daemons, be sure to kill them all. If you don’t, you might find the problem surfacing again before you have had a chance to fix it.

The best way to flush a full queue is with a command line something like this:

# /usr/sbin/sendmail -OQueueSortOrder=filename -q10m -d99.100
# /usr/sbin/sendmail -OQueueSortOrder=random   -q10m -d99.100V8.12 and later
# /usr/sbin/sendmail -OQueueSortOrder=none     -q10m -d99.100V8.13 and later

Here, the -d99.100 tells sendmail to run in the foreground (so that you can kill it easily when done). The -q10m causes a queue-processing daemon to be launched once each 10 minutes (just like before). You need this because one daemon can seem to hang when delivering mail to a slow host. By running parallel daemons, you avoid this pitfall.

Sorting by filename or random (How the Queue Is Processed on page 426) or none (V8.13 and later) causes sendmail to skip the opening and reading of each queued message. Instead, it only looks at the filename for its sorting or randomizing order. On the downside, this prevents sendmail from grouping messages for optimum delivery. On the upside, this reduces the time to preread a huge queue from 20 or so minutes to less than 2 seconds.[182]

The QueueSortOrder=random (QueueSortOrder=random (V8.12 and later) on page 1074) is just like the QueueSortOrder=filename shown earlier, except that it randomizes the list before beginning delivery. This method is preferred, but is only available beginning with V8.12 sendmail.

After draining the full queue to a more manageable level, you can discontinue this special process and rerun sendmail in its normal manner.

If the full queue has to remain in service while the full state is being solved, you can use the techniques in Handling a Down Site on page 437 to move that full queue out of the way so that it can be processed in the background.

Queue Groups (V8.12 and Later)

As of V8.12 sendmail, it is possible to group queues according to selected criteria, and then to process each group with custom settings. This versatile ability is enabled and tuned with:

  • The QUEUE_GROUP mc configuration command, which defines queue groups and sets their group properties

  • The FEATURE(queuegroup), which allows you to select queue groups based on recipient hosts via the access database

  • More sophisticated queue group selections, which you can make by writing your own rule sets

You can best tune queue groups by first understanding their limitations. We cover these topics in this section, but first we need to briefly discuss the default queue group.

The Default Queue Group

Prior to V8.12 sendmail, there were no queue groups. Instead, every -q command and every queue option (such as QueueDirectory) applied to all the queue directories you had.[183]

Beginning with V8.12, sendmail offers a way to define multiple queue directories and a way to group them by function or specialty. For compatibility with old versions, a special queue group named mqueue is the default queue group. It takes on all the properties of every -q command, and every queue option, just like before.

When you later declare particular queue groups (as we show in the next section), those additional groups take all their properties from the default group, unless you override a particular property with a specific equate. Those equates and the command-line arguments or options they override are shown in Table 11-2 on page 410.

For example, the following declares two different queue directories:

define(`QUEUE_DIR', `/var/spool/mqueue')
QUEUE_GROUP(`regularmail', `')
QUEUE_GROUP(`slowmail', `P=/var/spool/mqueue/slowqueue')

The first line declares the queue used by the default group (always known as mqueue). Any other queue groups that are declared (such as regularmail) will use that same directory unless the directory is overridden by the P= equate, as shown in the third line. That is, the default queue group’s queue directory and everything else that is set for the default queue group is inherited by the regularmail group. For the slowmail queue group, however, everything but the queue directory is inherited. (See The Path= (P=) queue-group equate on page 413 for a description of the P= equate, and for the reason queue group directories must be subdirectories under QUEUE_DIR.)

The Q Configuration Command

Queue groups are declared with the Q configuration command. That command can take a wide range of appearances, but in all guises it takes the name of the queue group and then a sequence of equates:

Qgroupname, equates

The name of the queue group (here groupname) must follow the Q with no intervening spaces. If spaces are present, an error such as the following is printed and logged, and that Q line is ignored:

file.cf: line line number: queue : `=' expected

The equates are optional, but if they are present they must follow the queue group’s name and a comma or whitespace, or both:

Qgroupname, equates

The equates are formed by selecting one of the keywords shown in the leftmost column of Table 11-2, and following it with an equals sign and the value you wish to assign to that key letter. Note that only the first letter is looked at by sendmail, so you can use the shorthand shown in parentheses if you wish. Also note that the first letter is case-sensitive—that is, R and r are different.

For example, both of the following declare a queue directory (the Path= and P=) and a queue-processing interval of 10 minutes (the Interval= and I=):

Qslowmail, Path=/disk1/mail/slowqueues, Interval=10m
Qslowmail, P=/disk1/mail/slowqueues, I=10m

A comma separates one equate from another. The comma can be optionally surrounded by whitespace characters (spaces and tabs). If the value following the comma is missing, an appropriate error will be printed and logged.

Table 11-2. Q configuration command equates

Equate

§

Overrides command-line switch or option

Description

Flags= (F=)

The Flags= (F=) queue-group equate on page 411

-qf

Fork queue runs

Interval= (I=)

The Interval= (I=) queue-group equate on page 411

-qinterval

Interval between queue runs

Jobs= (J=)

The Jobs= (J=) queue-group equate on page 412

MaxQueueRunSize

Maximum number of envelopes per queue run

Nice= (N=)

The Nice= (N=) queue-group equate on page 412

NiceQueueRun

How to renice(3) the queue run

Path= (P=)

The Path= (P=) queue-group equate on page 413

QueueDirectory

The queue directory or directories

recipients= (r=)

The recipients= (r=) queue-group equate on page 414

MaxRecipientsPerMessage

Maximum recipients per envelope

Runners= (R=)

The Runners= (R=) queue-group equate on page 414

MaxRunnersPerQueue

Maximum queue processors per queue group

If an equate other than those shown in the table is used, an error such as the following is printed and logged, and that Q line is ignored:

file.cf: line line number: Qgroupname: unknown queue equate bad equate here

The Flags= (F=) queue-group equate

The F= queue-group equate is used to set flags for the queue group. Currently there is only one flag, the f flag, which tells sendmail to fork multiple times to process the queue group in parallel (the exact opposite of the -qf command-line switch, which tells sendmail to not fork multiple times, but instead to run the queues serially in the foreground).

When this F= flag is specified, sendmail forks one queue processor for each queue directory in the group. But note that the sendmail program will fork only up to the total number of parallel processors set by the R= queue-group equate. If that limit is fewer than the number of queues, the remaining queues are handled during the next queue run, in round-robin fashion.

When the fast processing of a queue group is required, we recommend you specify this F=f queue group flag. If speed is not of concern, you can reduce the system impact by omitting this flag. But if you omit it and then specify multiple runners with the R= queue-group equate, the following message will print and be logged:

Warning: Q=queuegroup name: R=number: multiple queue runners specified
       but flag 'f' is not set

As a performance compromise, some parallelism can be attained and system impact reduced by setting this flag and limiting the number of runners specified with the R= queue-group equate.

The Interval= (I=) queue-group equate

The I= queue-group equate specifies the time interval at which the queues in the queue group should be processed. The default interval is set by the -qinterval command-line switch, but can be overridden for a queue group using this I= queue-group equate:

I=interval

The interval following the I= is constructed from an integer and a letter. The letters and the meaning of each are listed in Table 11-3. Integer and letter groups can be combined—for example, 5d12h means 5 days, 12 hours.

Table 11-3. Meaning of interval letters

Letter

Meaning

w

Week

d

Day

h

Hour

m

Minute

s

Second

If the trailing letter is missing, the units default to minutes; thus, the following defines an interval of 1 hour, 12 minutes:

Interval=1h12

In general, the use of a trailing letter is recommended for clarity, and to avoid problems in the future should sendmail defaults change.

The Jobs= (J=) queue-group equate

When a queue processor starts to process a queue directory, it first gathers a list of all the envelopes in that directory. It then sorts, or randomizes that list, and processes the envelopes in the resulting order. If no limit is imposed, all the envelopes will be processed before the queue run is complete.

The default limit, if there is one, is defined by the MaxQueueRunSize option (MaxQueueRunSize on page 1050). But a separate limit that will override the default can be set for a queue group using this J= equate. If the default is nonzero and if this equate specifies zero, the default queues will have the default limit imposed but this group will have none. This J= queue-group equate is used like this:

Jobs=number

If number is zero or negative, no limit is imposed. If number is positive, that will be the maximum number of envelopes processed.

The Nice= (N=) queue-group equate

The niceness of a process determines its priority to be run. The larger the nice value, the lower the priority. The default nice value varies from one version of Unix to another. In all cases, however, they generally begin with the same nice value, so all processes generally get an equal chance to run.

With sendmail, the niceness of its queue processors is set by the NiceQueueRun option (NiceQueueRun on page 1059). If that option specifies a positive value, the priority is reduced. If that option specifies a negative value, the priority is increased. In general, queue processors should run at a lower priority so as to minimize the adverse impact on other processes. On dedicated mail-sending machines, you might wish to increase the priority.

Each queue group inherits its nice value from the NiceQueueRun option, unless this N= queue-group equate is specified. This N= equate is used like this:

Nice=10       ← increase niceness by 10, lower priority
Nice=0        ← no change
Nice=−10      ← same as zero
Nice=b        ← same as zero

If the number is missing, nonnumeric, or negative, the niceness change is zero (no change). Otherwise, the niceness is increased (the priority is lowered) by the amount specified.

The Path= (P=) queue-group equate

The default location and name of the queue directory or directories is set by the QueueDirectory option (QueueDirectory on page 1070). That option defines the default directory (for the default queue group mqueue) and the base path for all the other queue directories. The P= queue-group equate does not override the default (as the other equates do), but instead augments it.

The path specified by the P= queue-group equate must be a full (absolute) path, and must contain the name of a subdirectory or subdirectories of the default path. To illustrate, consider the following mc file declarations:

define(`QUEUE_DIR',`/var/spool/mqueues/q.*')            ← the default
QUEUE_GROUP(`aolmail', `P=/var/spool/mqueues/aolmail')  ← good, a subdirectory
QUEUE_GROUP(`bobmail', `P=/var/spool/mqueues/bob.*')    ← good, a subdirectory
QUEUE_GROUP(`hotmail', `P=hotmail')                     ← bad, not a full path
QUEUE_GROUP(`slow', `P=/var/spool/slowqueue')           ← bad, not a subdirectory

Here, the first line defines the default queues, which all begin with the characters q. and live under the path /var/spool/mqueues.

The second line correctly sets the queue for the aolmail queue group. The base path, /var/spool/mqueues, is the same for both the default and this group. Note that queue group directories can also specify multiple queues (as with the /var/spool/mqueues/bob.* in the third line).

The fourth line shows that the path specified with P= must not be a relative pathname. If it is, sendmail will print and log the following error and exit:

QueuePath hotmail not absolute

The last line shows that the path specified with P= must not use a base path different from the default. If it does, the following error will print and log, and sendmail will exit:

QueuePath /var/spool/slowqueue not subpath of QueueDirectory /var/spool/mqueues:
No such file or directory

Note, however, that symbolic links under the default queue path are OK. That is, you can declare the last line in the preceding example like the following, and then simply make the path you specify a symbolic link to the real directory somewhere else:

define(`QUEUE_DIR',`/var/spool/mqueues/q.*')            ← the default
QUEUE_GROUP(`slow', `P=/var/spool/mqueues/slowqueue')
                                             ↑
                                a symbolic link to /var/spool/slowqueue

Note, however, that the path pointed to by the symbolic line must be as trusted as the default path, with narrow ownerships and permission (QueueFileMode on page 1071).

The recipients= (r=) queue-group equate

The MaxRecipientsPerMessage option (MaxRecipientsPerMessage on page 1050) sets the default limit for the number of recipients allowed per envelope. If there are more recipients than that limit in an envelope, sendmail will split the envelope into two or more envelopes, each with the limit or fewer recipients. If the MaxRecipientsPerMessage option is zero, no limit is imposed.

The r= queue-group equate allows you to override the default for each queue group. If the default allows unlimited recipients, or a large limit, you can use a smaller setting for your queue group. Or, if the default is too small, you can enlarge it. You use the r= equate like this:

recipients=99        ← set the limit to 99 recipients
recipients=0         ← set unlimited recipients
recipients=-99       ← same as r=0
recipients=none      ← same as r=0

Note that a zero or negative expression sets the limit to unlimited. A nonnumeric expression, such as in the last line, also sets the limit to zero (unlimited).

The Runners= (R=) queue-group equate

The Runners= (R=) queue-group equate tells sendmail how many queue processors to launch each queue-processing interval. The queues are serviced in round-robin order. So, for example, if your queue group has three queues, and you set R= to 1, 2, 3, and 4, respectively, you will see the runs shown in Table 11-4.

Table 11-4. Queue processing in round-robin order

Runners

1st run

2nd run

3rd run

4th run

R=1

q1

q2

q3

q1

R=2

q1, q2

q3, q1

q2, q3

q1, q2

R=3

q1, q2, q3

q1, q2, q3

q1, q2, q3

q1, q2, q3

R=4

q1, q2, q3, q1

q2, q3, q1, q2

q3, q1, q2, q3

q1, q2, q3, q1

The Runners= queue-group equate is declared like the following:

Runners=12     ← 12 per queue run
Runners=0      ← no limit, so one per queue each queue run
Runners=none   ← the same as R=0

If the number of queue-group runners specified by this equate is more than the number of queue children allowed by the MaxQueueChildren option (MaxQueueChildren on page 1049), the number of queue-group runners is reduced to that amount, and the following error is logged and printed:

Q=queuegroup: R=number exceeds MaxQueueChildren=limit, set to MaxQueueChildren

If the MaxQueueChildren option is set to zero, there is no limit to how many queue-group runners you can declare.

How to Declare Queue Groups with the m4 Technique

You declare queue groups inside your mc configuration file with the QUEUE_GROUP mc configuration macro. As you saw in the previous sections, it is used like this:

QUEUE_GROUP(`group name', `equates')

The queue group name can contain any characters except a comma or a whitespace character (a space or a tab).[184] It must not be surrounded (inside the quotes) with whitespace characters.

The equates form the second argument to the QUEUE_GROUP mc configuration macro. The equates are described in The Q Configuration Command on page 409.

To illustrate, consider the following QUEUE_GROUP mc configuration macro declaration:

QUEUE_GROUP(`slowmail', `P=/var/spool/mqueues/slowqueue')

Here, the name of the queue group is set to slowmail. The second argument is a single equate, the P= queue-group equate, which defines the queue directory or directories to be used by this queue group.

If you want to define which queue group to use for certain delivery agents, you can use the Q= delivery-agent equate (Q= on page 750) as set, for example, with the LOCAL_MAILER_QGRP mc macro. For example, the following tells sendmail to queue all local mail in the /queues/lq queue directory:

QUEUE_DIR(`/queue')
QUEUE_GROUP(`localgroup', `P=/queue/lq')
define(`LOCAL_MAILER_QGRP', `localgroup')    ← must be before MAILER(local)
MAILER(`local')

In the first line we set the default queue directory. In the second line we define the queue group localgroup, and set its queue directory to be /queue/lq. In the third line we declare that the Q= equate for the local delivery agent will be:

Q=localgroup

The fourth line declares support for the local delivery agent. Note that the definition of LOCAL_MAILER_QGRP must precede the MAILER(local); otherwise, that definition will be silently ignored.

Those four lines cause all mail for local users to be queued in the /queue/lq directory. Note that you can dedicate queue groups for other delivery agents. See Q= on page 750 for a full description of this process.

The FEATURE(queuegroup) and the access Database

The easiest way to select queue groups based on recipient addresses or recipient domains is by using the FEATURE(queuegroup). It is declared in your mc configuration file like this:

FEATURE(`queuegroup')
FEATURE(`queuegroup', `default group')

The first line causes the queue group to default to mqueue if a queue group in the access database is missing or nonexistent. The second line allows you to set a different default queue group. For example, consider the following lines from an mc file:

QUEUEGROUP(`localgroup', `/queue/lq')
FEATURE(`queuegroup', `localgroup')

This causes sendmail to use the group named localgroup instead of mqueue as the default if a queue group in the access database is missing or nonexistent.

Once you have enabled the FEATURE(queuegroup), the next step is to add lines such as the following to the source file for your access database:

QGRP:slow-poke.com      slowgroup
QGRP:root@notify.com    fastgroup
QGRP:your.domain        localgroup

Each line that selects queue groups must begin with the literal expression:

QGRP:

This prefix tells sendmail that you wish to map recipient addresses or domains to queue groups.

The first line causes mail to the slow-poke.com domain to use the queue group called slowgroup. This shows that you can list just a domain in the lefthand column and it will work just as expected.

The second line causes mail to the specific recipient to use the queue group named fastgroup. This line demonstrates that mail to an individual can be used in the lefthand column.

The third line illustrates your local domain, which shows that mail to your domain, your.domain, will use the queue group named localgroup.

If you omit the name of the queue group (not recommended), you will need to use the -e command-line switch with makemap to create the database. When you omit the name of the queue group the default queue group is used:

QGRP:another.your.domain
                           ↑
                 queue group name missing (not recommended)

Here, if you defined a default queue group when you declared the FEATURE(queuegroup), that group will be selected. Otherwise, the group mqueue will be selected for this domain.

Rule Set Queue Group Selection

Normally, the access database, described earlier, is the easiest way to select queue groups. There might be times, however, when selecting by recipient address or domain is not sufficient. Should such a situation arise, you could set up your own rule sets. But be forewarned that if you do, the FEATURE(queuegroup) cannot be used. If you try to use both, you will get the following warning every time sendmail starts to run:

WARNING: Ruleset queuegroup has multiple definitions

The first step in declaring your own rules to select queue groups is to declare a special rule set called queuegroup. You do that in your mc configuration file using the LOCAL_RULESETS macro:

LOCAL_RULESETS
Squeuegroup
               ← your rules here

The way this rule set works is simple. Any queue group for a recipient address that a rule selects is returned following the $# operator. For example, consider the following:

R $*                                          $: $>canonify $1
R $* <@some.domain>          $# somegroup

Here, mail bound for any user at some.domain will be queued in the somegroup queue group.

Normally, queuegroup rule sets are used to select queue groups based on the recipient. If you wish to select based on the sender, you can do so using rules something like the following:

LOCAL_RULESETS
Squeuegroup
R $*                     $: $>canonify $&f
R $+ <@ lists.domain.>   $# lists

First, we fetch the sender address using $&f, and pass it through the canonify rule set 3 to focus on the host part. The second rule matches any user at the domain lists.domain, and selects the lists queue group.

Because there are no more rules following the second one, this rule set returns without selecting a queue group. If the queuegroup rule set fails to select a queue group, the default queue group (mqueue) is used.

Other possible uses for the queuegroup rule set might include:

  • Queue inbound messages on a disk different from that used for outbound messages.

  • Queue mail to suspect users in a queue that is not automatically processed so that the mail can be manually screened before delivery.

  • Queue expendable mail, such as short-lived notification mail (e.g., “tea is served”), on a volatile disk that is erased when the machine is rebooted.

  • Queue low-priority mail in a queue different from that used for high-priority mail.

Note that there are limitations on the use of this queuegroup rule set. First, this rule set is called directly from inside sendmail, so you should not call it from inside your own rules (if you do, the selected queue group will be ignored). And second, the FEATURE(queuegroup) also uses this rule set, so you cannot share it with that feature.[185]

Queue Group Limitations

As you saw in The Default Queue Group on page 409, the default queue group (mqueue) is defined by options and the command line. If any given Q configuration command is missing a given equate, that queue group inherits that property as defined by the default queue group. There are, however, properties for the default queue group which have no equivalent equates. These properties are inherited by all queue groups and cannot be overridden with a queue-group equate. They are:

DeliveryMode option

If the DeliveryMode option (DeliveryMode on page 1004) is set to queueonly or deferred, all mail will be queued rather than delivered. This affects all queue groups.

FastSplit option

This FastSplit option (FastSplit on page 1032), when nonzero, prevents MX lookups prior to splitting an envelope and limits the number of envelopes that can be delivered on the initial attempt. This option, regardless of its value, affects all queue groups.

MaxQueueChildren option

The MaxQueueChildren option (MaxQueueChildren on page 1049), when nonzero, limits the number of queue processors that can simultaneously run across all queues. If this is fewer than the total queue runners across all queue groups, it limits the run to this setting. Any queue groups that are not run are handled in the next run in round-robin order. There is no way to limit some queue groups and not limit others.

MinQueueAge option

Messages in a queue are processed no more often than the interval set by this MinQueueAge option (MinQueueAge on page 1057). This limit is imposed even if a queue is processed more often. This limit is global and affects all queue groups.

-qI, -qR, and -qS command-line switches

The -qI command-line switch restricts a queue run to the messages that match the queue identifier specified. The -qR command-line switch restricts a queue run to the messages that match the recipient address pattern specified. The -qS command-line switch restricts a queue run to the messages that match the sender address pattern specified. Unless the -qG command-line switch is also used to limit the queue group, these limits are imposed across all queue groups.

QueueFactor, QueueLA, and RecipientFactor options

The QueueFactor (QueueFactor on page 1071), QueueLA (QueueLA on page 1072), and RecipientFactor (RecipientFactor on page 1077) options (and beginning with V8.14, the DaemonPortOptions option’s queueLA key; DaemonPortOptions=queueLA= (8.14 and later) on page 997) are used to calculate the point at which sendmail should queue a message instead of delivering it. This calculation affects all queue groups.

QueueFileMode option

Beginning with V8.10 sendmail, the QueueFileMode option (QueueFileMode on page 1071) defines the mode (permissions) of all queue files. This setting affects all queue files across all queue groups.

Timeout.queuereturn and Timeout.queuewarn options

The Timeout.queuereturn option (Timeout.queuereturn (V8.7 and later) on page 1106) defines the maximum time interval that a message can remain in the queue before it is bounced because of a deferred delivery. The Timeout.queuewarn option (Timeout.queuewarn (V8.7 and later) on page 1107) defines the interval at which a message, still in the queue, will result in a first and only warning message being sent to the sender. Both of these intervals globally affect all queue groups.

Bogus qf Files

For security reasons, V8 sendmail performs a number of checks on each qf file before trusting its contents. If any qf file fails to be trustworthy, sendmail converts the leading q in its name to an uppercase Q.[186] We discuss each possible problem in the sections that follow.

Note that when sendmail renames a qf file into a Qf file, it logs that it did so. In the following, qffile is the full path and filename of the qf file, before it was renamed:

Losing qffile:  reason here

Also note that although sendmail checks the qf file for a number of plausible errors, its checking is by no means exhaustive. The checks we describe here are no substitute for a well-managed system.

Badly Formed qf Filename

V8.6 sendmail always checks the form of the qf file name for correctness. V8.7 through V8.9 sendmail also check the qf filename, but do so only if PICKY_QF_NAME_CHECK is defined when building sendmail (PICKY_HELO_CHECK on page 133). V8.10 and later no longer check the form of the qf filename for correctness.

Prior to V8.10, if the qf filename is incorrectly formed (The Queue Identifier on page 396), sendmail presumes that some other program placed the file in the queue and rejects it:

orderq: bogus qf name bogus name here

For V8.7 through V8.9, sendmail made this check only if PICKY_QF_NAME_CHECK was defined when building sendmail. This was introduced because some sites allow legitimate programs (other than sendmail) to write into sendmail’s queue. To fix this problem, either undefine PICKY_QF_NAME_CHECK when you build sendmail (if your site allows other programs to write into the queue directory), or trace down the process that is placing badly formed qf names in your queue and fix it.

Bad qf Owner or Permissions

Each qf file must be owned by the effective user ID under which sendmail runs (usually root). A qf file must not be group- or world-writable. If a qf file fails either test, it is considered bogus and is renamed to a Qf file. Then sendmail logs these messages:

id: bogus queue file, uid=owner, mode=perms
Losing qffile: bogus file uid in mqueue

Here, id is the identifier portion of the qf filename, owner is the uid of the user that owns the qf file, and perms are the file permissions of the qf file, printed in octal.

This problem might point to bad queue directory permissions that allow anyone (or some group) to place files there. Or it might indicate that some process other than sendmail is writing to your queue.

Extra Data at End of qf File

One form of attack against sendmail is to append additional control lines to the end of an existing qf file. V8.7 sendmail specifically checks for additional text and rejects the qf file if any is found:

SECURITY ALERT: extra data in qf: first bogus line printed here
Losing qffile: bogus queue line

V8.7 sendmail terminates its legitimate list of qf control lines by placing a dot on a line by itself. Any text following that line, including comments and blank lines, is considered an error. This can represent a serious attack against your machine or site. If you get this message, investigate at once.

Unknown Control Character in qf File

Each line in a qf file must begin with a known control letter or character (The qf File Internals on page 445). If a line begins with any other character, it is considered bad, and the whole file is rejected:

readqf: qffile: line num: bad line bogus line here
Losing qffile: unrecognized line

Note that this error is to be anticipated if you go backward, from a later release to an earlier release of sendmail.

Funny Flag Bits in qf File

An F line in a qf file is used to save and restore envelope flag bits. Unfortunately, the first line of a Unix-style mailbox also begins with an F:

From someone@site

If a qf file’s F line begins with the five characters "From“, V8.7 and later sendmail will reject the file and log a possible attack:

SECURITY ALERT: bogus qf line bogus line here
Losing qffile: bogus queue line

This might represent a serious attack against your machine or site. If you get this message, investigate at once.

Savemail Panic

In the rare event that sendmail cannot dispose of a bounced message, it will preserve the qf file as a Qf file and log the message:

savemail: cannot save rejected email anywhere
Losing qffile: savemail panic

The sendmail program tries everything possible to avoid this state (including bouncing the message, sending it to the postmaster, and saving it to a dead.letter file). Only if all else fails will it preserve the qf file as a Qf file.

In general, this points to an alias problem with the user named postmaster or the owner of a mailing list. Such users are special. They must be able to receive email messages no matter what. They should be the names of real people, not the names of further mailing lists.

Handle Qf Files

Beginning with V8.13, the -qL command-line switch allows you to view and handle Qf files. Note, however, that handling these files, without first repairing the causative problem, can be risky. One use for this new switch is to examine the mail queue to see if any lost files exist:

% mailq -qL
                /var/spool/mqueue (1 request)
-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------
h7AJG4kr009003?     235 Sun Aug 10 13:16 <you@your.domain>
                                         <bob@other.domain>
                Total requests: 1

Here, the -qL command-line switch was used with the mailq command to see if any lost files were present. This output shows that a lost file (called Qfh7AJG4kr009003) is located in the /var/spool/mqueue directory. The "?" character following the file’s name indicates that it is a lost envelope.

This -qL switch can be combined with other queue-handling switches to further limit what can be shown.

Printing the Queue

When sendmail is run under the name mailq, or when it is given the -bp command-line switch, it prints the contents of the queue and exits.

Before printing the queue’s contents, sendmail prereads all the qf files in the queue and sorts the mail messages internally. This is done so that the queue’s contents are displayed in the same order in which the messages will be processed during a queue run.

If there are no messages in the queue (no qf files), sendmail prints the following message and exits or, if there are multiple queues, goes on to the next queue:

/path is empty

Here, /path is the full pathname of the queue directory.

If the queue is not empty, sendmail prints the number of messages (number of qf files) in the queue:

/path (num requests)

The num is the number of queued messages (requests) in the queue directory. If this is more than the maximum number of messages that can be processed at one time (defined by the MaxQueueRunSize option [MaxQueueRunSize on page 1050]),[187] sendmail prints:

/path (num requests, only ## printed)

The ## is the value of the MaxQueueRunSize option.

Note that it can take several minutes to presort and print extremely full queues (queues with more than 10,000 messages in them). To see how many messages are queued, and to avoid the delay of a presort, you can add a small MaxQueueRunSize to your invocation of mailq:

% mailq -OMaxQueueRunSize=1

This will cause sendmail to swiftly print the number of queued messages, regardless of how many are queued.

After sendmail prints the number of messages in the queue, it prints an attractive heading such as the following:

----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------
dB928Xl04182      354 Fri Mar 15 08:32 your@your.domain
                                   george@wash.dc.gov
dB928RR04181*    1972 Fri Mar 15 08:45 your@your.domain
      8BITMIME   (Timed out waiting to connect to wash.dc.gov)
                                   jefferson@wash.dc.gov
dB928RR04192-      23 Fri Mar 15 09:32 your@your.domain
                 (Timed out waiting to connect to wash.dc.gov)
                                   jefferson@wash.dc.gov
                                   bob

The heading shows the information that is printed about each message in the queue. The items in that heading and their meanings are as follows:

Q-ID

The queue identifier for the message. This item can be followed by a character showing the item’s status. An asterisk (* as in the second item in the example) means that the message is locked (an lf file was found, or the qf file is locked depending on the kind of locking your version of sendmail uses). An X means that the load average is currently too high to allow delivery of the message. A minus (- as in the third item in the example) means that the message is too young to be processed (based on the MinQueueAge option, MinQueueAge on page 1057).

Size

The size in bytes of the df file. If there is no df file (because sendmail is currently receiving this message and hasn’t created one yet), this item is absent. If the message has completed processing, this prints as:

(job completed)

If the qf file is empty, this prints as:

(no control file)
Q-Time

The date and time that the message was first placed into the queue. This is the T line (T line on page 456) in the qf file converted from an unsigned integer into a more understandable date and time.

Sender

The sender of the message as taken from the S line (S line on page 455) in the qf file. Only the first 45 characters of the sender address are printed. If there is a B line (B line on page 447) in the qf file (as the BITMIME in the second item in the example), sendmail prints that body type (the -B switch in -B on page 232) on the line following the sender. If there is an M line (M line on page 452) in the qf file, sendmail prints the text of the error message in parentheses.

Recipient

After all of the preceding items have been printed, a list of the recipients (from each R line, R line on page 454, in the qf file) is printed in the order in which they are found. In the example, there is one recipient for each of the first two items and two recipients for the last item.

See Process by identifier/recipient/sender: -q[ISR] on page 431 for a way to limit the printed queue list to include only a subset of messages based on queue ID, sender, or recipient addresses.

Printing the Queue in Verbose Mode

The -v command-line switch can be used in combination with the -bp switch to cause sendmail to also print additional details about the queued messages. To begin, the usual heading shows a new item:

----Q-ID---- --Size-- -Priority- -----Q-Time----- ---------Sender/Recipient-----
----
dB928Xl04182     354     54320 Fri Mar 15 08:32 your@your.domain
                                  george@wash.dc.gov
dB928RR04181*   1972     39020 Fri Mar 15 08:45 your@your.domain
      8BITMIME   (Timed out waiting to connect to wash.dc.gov)
                                  jefferson@wash.dc.gov
dB928RR04192-     23     30001+Fri Mar 15 09:32 your@your.domain
                 (Timed out waiting to connect to wash.dc.gov)
                                  jefferson@wash.dc.gov
                               (---you---)
                                   bob

The Priority is the value from the P line (P line on page 453) in the qf file. Printing the queue does not change a message’s priority, whereas processing the queue does. See the RecipientFactor option (RecipientFactor on page 1077) for a description of how the priority is calculated.

Verbose mode also causes a + to print after the Priority (as in the third item in the example) if a warning message has been sent. See the Timeout.queuewarn option (Timeout on page 1097) for a description of how messages time out.

If any R line is preceded by a controlling user (the C line in the qf file, C line on page 447), verbose mode causes that controlling user’s name to be put in parentheses and prepended to the recipient name. The third item in the preceding example illustrates this.

Prior to V8.8 sendmail, the M line error messages were truncated to 60 characters. Beginning with V8.8, verbose mode causes the full, nontruncated text of the M line error to be printed.

Beginning with V8.12 sendmail, the -bP command-line switch can be used to print the number of messages in the queue or queues. This command-line switch relies on shared memory to gather its information, so it works only if sendmail is compiled with shared memory support. The SM_CONF_SHM compile-time macro determines whether shared memory support was included (see SM_CONF_SHM on page 142). If shared memory support is not included, use of this command-line switch will cause the following error to be printed:

Data unavailable without shared memory support

If shared memory support is compiled in, but there is a problem with it (possibly at the system level), the following error will print:

Data unavailable: shared memory not updated

Note that you will also get this error if the queue has not been processed at least once to initialize the data.

In addition to enabling shared memory using the SM_CONF_SHM m4 Build macro, you must also define a key to be used with that shared memory with the SharedMemoryKey option. To set this option in your configuration file, you could add a line such as the following to your mc configuration file:

define(`confSHARED_MEMORY_KEY',`13521')

If all goes well, the -bP command-line switch will produce output such as this:

/var/spool/mqueues/q.1/df: entries=34
/var/spool/mqueues/q.2/df: entries=51
               Total requests: 85

Here, 85 is the number of envelopes currently awaiting delivery in sendmail’s queues. But note that some shared memory timeouts can lead to an inexact count. In this latter event, the output looks like this:

Total requests: 85 (about)

If you lack shared memory support, and you are running pre-V8.12 sendmail, you can still summarize the number of messages in all queues with a simple substitute command:

% mailq -OMaxQueueRunSize=0

How the Queue Is Processed

Over time, messages can gather in the queue awaiting delivery. They remain there until sendmail performs a queue run to process the queue. The sendmail program can be told either to process the queue periodically (when run as a daemon) or to process the queue once, and then exit. Each time sendmail processes the queue, it also performs a series of operations that are intended to improve the efficiency with which it delivers messages.

First the queue directory is opened for reading. If that directory cannot be opened, sendmail syslog(3)s the following message at LOG_CRIT and exits:

cannot opendir(/var/queue): reason here

This error is usually the result of a user running sendmail in an unsafe manner, with a -C command-line argument, for example. It can also result from sendmail attempting to open an NFS-mounted queue directory, where root is mapped to nobody.

Next, the qf files are read to gather their priorities and times (the P and T lines). If a qf file cannot be opened, it is quietly ignored unless a -d41.2 debugging command-line switch is specified, in which case the following error message is printed:

orderq: cannot open qfdB928RR04181 (reason)

Prior to V8.7 sendmail, there was a hard limit on the number of messages that could be processed at any time. If more than QUEUESIZE (defined in conf.h, typically 1,000) messages were in the queue, only the first QUEUESIZE (1,000) of them would be processed! Ordinarily, this was not a problem. But it could quickly become one if your queue were clogged with a huge number of undeliverable messages (where the first 1,000 continued to be deferred). In that case, the only solution is to temporarily move the 1,000 messages out of the way by hand (Handling a Down Site on page 437) and clear the queue. The only way to detect this situation is to print the queue (Printing the Queue on page 422).

V8.7 and later sendmail dynamically allocate memory to process the queue. If more than QUEUESIZE messages are found, sendmail will print the following notice and process them:

grew WorkQ for queue_directory to bytes

As an alternative to this dynamic behavior, V8.7 and later sendmail offer a hard limit that is somewhat like the old version but is site-tunable with the MaxQueueRunSize option (MaxQueueRunSize on page 1050). After all the qf files have been gathered, they are sorted in order of cost. Messages with the lowest value on the P line have the highest priority (lowest cost) and are processed first.

Beginning with V8.7, sendmail also offers the QueueSortOrder option (QueueSortOrder on page 1073), which allows you to sort by priority (as before), by priority and hostname, by date queued, or (beginning with V8.10) by filename or (beginning with V8.12) in random order. Once all the messages have been sorted, sendmail processes each in turn.

Processing a Single Message

A single queued message has a single sender but can have many recipients. When processing a queued message, sendmail attempts to deliver it to all recipients before processing the next queued message.

The first step in processing a queued message is to lock it so that concurrent runs of sendmail do not attempt to process it simultaneously (Current-style file locking on page 398). Then the qf file is opened and read. The sender and all the recipients are gathered from the corresponding S and R lines.

For each recipient, delivery is attempted. If delivery is successful, that recipient’s address is removed from the sendmail program’s internal list of recipient addresses. If delivery fails, that address is either left in the list or bounced, depending on the nature of the error.

After all recipients have been either delivered, bounced, or left in the list, sendmail reexamines that list. If there are no recipients left in it, the message is dequeued (all the files in the queue directory that compose it are removed). If any recipients are left, each recipient results in an M line that is assigned the last error message for that recipient, and the qf file is rewritten with the list of the remaining recipients and a dot. Finally, the qf file is closed, thus freeing its lock.

Under V8 sendmail, the CheckpointInterval option (CheckpointInterval on page 983) causes checkpointing of this process. When this option has a positive value, the qf file is rewritten after that value’s number of recipients have been processed. For example, consider a mail message to five recipients. If the CheckpointInterval option is set to a value of 2, the qf file is rewritten after the first two recipients have been processed, then again after four, and again after they all have been processed. This keeps the qf file reasonably up-to-date as protection against sendmail being improperly killed or the machine crashing.

Cause Queues to Be Processed

The sendmail program offers two different methods for processing its queues. It can be told to process them periodically or to process them once and then exit.

Periodically with -q

The -q command-line switch is used both to cause queues to be processed and to specify the interval between queue runs.

A typical invocation of the sendmail daemon looks like this:

/usr/sbin/sendmail -bd -q1h

Here, the sendmail program is placed into listening mode with the -bd command-line switch. The -q1h command-line switch tells it to process the queue once each hour. Note that either switch puts sendmail into the background as a daemon. The -bd switch just allows sendmail to listen for incoming SMTP connections. Consider the following:

/usr/sbin/sendmail -bd
/usr/sbin/sendmail -q1h

This runs two daemons simultaneously. The first listens for incoming SMTP connections. The second processes the queues once per hour.

The time expression following the -q is constructed from an integer followed by a letter. The letters and the meaning of each are listed in Table 11-5. Integer and letter groups can be combined—for example, 5d12h means 5 days, 12 hours. If a letter is missing, the default is minutes.

Table 11-5. Meaning of time letters

Letter

Meaning

w

Week

d

Day

h

Hour

m

Minute

s

Second

At small sites, where mail messages are rarely queued, the time interval chosen can be small to ensure that all mail is delivered promptly. An interval of 15m (15 minutes) might be appropriate.

At many sites, an interval of one hour is probably best. It is short enough to ensure that delays in delivery remain tolerable, yet long enough to ensure that queue processing does not overlap (see Persistent Queue Runners with -qp on page 434 for a way to run a persistent queue runner that avoids overlapping runs).

At large sites with huge amounts of mail and at sites that send a great deal of international mail, the interval has to be carefully tuned by observing how long it takes sendmail to process its queues and what causes that process to take a long time. Points to consider are the following:

  • Network delays or delays at the receiving host can cause delivery to that host to time out. Timeouts are set with the Timeout option (Timeout on page 1097).[188] Each such timeout is logged at LOG_NOTICE with a message such as this:

    timeout waiting for input from host during what

    Here, host is the name of the other host, and what specifies which timeout triggered the message (such as “client HELO” for to_helo). In general, timeouts should be large to ensure that mail to busy sites, and to large mailing lists, does not time out improperly. In observing queue processing, you might find that all messages but one process swiftly. That one, you might find, takes more than an hour because of a long SMTP timeout. A possible solution to this problem is to make all timeouts short so that most queue runs are processed quickly. Then, for example, the following command could be run a few times each night to specifically flush those long jobs:

    /usr/sbin/sendmail -OTimeout=2h -q
  • A queue can take a long time to process because too many messages are being queued unnecessarily. Several options affect the placement of mail messages into the queue. The QueueLA option (QueueLA on page 1072) tells sendmail to queue, rather than deliver, a message if the machine load is too high. Fewer messages will be queued if the value of that option is increased. (Beginning with V8.14, this load average cutoff can be more finely tuned by using the DaemonPortOptions option’s queueLA key; DaemonPortOptions=queueLA= (8.14 and later) on page 997.) The SuperSafe option (SuperSafe on page 1096) tells sendmail to queue all messages for safety. If your machine “never” crashes, this might not be necessary. Or you might choose to turn off SuperSafe when sending short-lived notification mail, or when your queues are on a volatile filesystem, such as an async or tempfs filesystem. (RFC2824 recommends that you never turn off SuperSafe.) The HoldExpensive option (HoldExpensive on page 1036) tells sendmail to queue messages to “expensive” delivery agents (those with the F=e flag set, F=e on page 770) rather than delivering them. If the queue is routinely filled with messages to expensive sites, you should reconsider your reasons for marking those sites as expensive.

  • The queue can fill with messages because sendmail was run with the -odq or -odd command-line switch (see the DeliveryMode option, DeliveryMode on page 1004). At sites that receive a great deal of UUCP mail for forwarding, the rmail(8) program is often set up to run sendmail in “queue-only” mode with the -odq command-line switch. If UUCP mail is clogging your normal mail services, you should consider queueing it to a separate queue directory. You can then process that other directory with a separate queue run of sendmail. (Use of separate queue directories is discussed in Process Alternative Queues on page 436.)

  • A slow machine can clog the queue. When a single machine is set up to handle the bulk of a site’s mail, that machine should be as swift as possible. In general, a dedicated mail server should have a fast CPU with lots of memory. It should never allow users to log in to it, and it might need to run its own name server daemon.

  • On modern servers where a fast CPU with lots of memory is available, the bottleneck will likely be disk I/O. Equip the server with many disks spread over many controllers. Use multiple queue directories (Using Multiple Queue Directories on page 401) or queue groups (Queue Groups (V8.12 and Later) on page 408) to spread the I/O widely over those many disks.

From the Command Line

The -q command-line switch, invoked without a time interval argument, is used to run sendmail in queue-processing mode. In this mode, sendmail processes queues once and then exits. This mode can be run interactively from the command line or in the background via cron(8).

Other command-line switches can be combined with -q to refine the way queues are processed. The -v (verbose) switch causes sendmail to print information about each message it is processing, and to process multiple queues sequentially. The -d (debugging) switch can be used to produce additional information about the queue. We’ll discuss the -v switch as it applies to the queue later in this chapter. Those -d debugging switches appropriate to the queue can be found in Table 15-3 on page 536.

V8 sendmail allows variations on -q: -qI allows you to specify a specific message identifier for processing; -qR allows you to specify specific recipient addresses for processing; and -qS allows you to specify specific sender addresses for processing.[189]

Process the queue once: -q

The -q command-line switch, without an interval argument, tells sendmail to process the queue once, and then exit. As such, this switch is a handy administrative tool. When the queue fills unexpectedly between queue runs of the daemon, for example, the -q command-line switch can be used to force an immediate queue run:

# /usr/sbin/sendmail -q

When multiple queues are run this way, they are all processed in parallel (Processing multiple queue directories on page 403).

On machines that do not run the sendmail daemon, the -q command-line switch can be used in conjunction with cron(8) to periodically process the queue. The following crontab(5) file entry, for example, causes sendmail to be run once per hour, at five minutes past the hour, to silently process its queues and exit:

5 * * * * /usr/sbin/sendmail -q >/dev/null 2>&1

When used in conjunction with other switches (shown next), the -q switch allows many queue problems to be conveniently handled.

Combine -v with -q

The -q switch without an argument prevents sendmail from running in the background and detaching from its controlling terminal. But it also runs silently. To see what is going on, use the -v command-line switch in combination with the -q:

% /usr/sbin/sendmail -v -q

The -v command-line switch causes sendmail to print a step-by-step description of what it is doing. When running multiple queues, it also causes them to be processed in sequence. To illustrate, consider the following output produced by using both the -v and -q command-line switches:

Running /var/spool/mqueue/dB9JBR106687 (sequence 1 of 2)
<adams@dc.gov>... Connecting to dc.gov via ddn...
Trying 123.45.67.8... Connection timed out during user open with DC.GOV
<adams@dc.gov>... Deferred: Host DC.GOV is down

Running /var/spool/mqueue/dB9JDWt06701 (sequence 2 of 2)
<help@irs.dc.gov>... Connecting to irs.dc.gov via ddn...
Trying 123.45.67.88...  connected.
220 irs.dc.gov Sendmail 5.57/3.0 ready at Mon, 27 Jan 92 09:16:38 −0400

Here, two queued messages are being processed. The first fails because of a connection timeout and is requeued for a later queue run. The second succeeds (we omit the full SMTP dialog). After its delivery is complete, it is removed from the queue.

Process by identifier/recipient/sender: -q[ISR]

With V8 sendmail you can process a subset of all queued messages. You can select which to process based on queue identifier, recipient address, or sender address:

-qIidentmatch any queue ID that contains ident
-qRrecipmatch any recipient address that contains recip
-qSfrommatch any sender address that contains from

The -qI variation is followed by a queue identifier such as dB9JDWt06701. The -qR is followed by the address of a recipient. The -qS is followed by the address of a sender. In all three variations, there must be no space between the uppercase letter and the identifier or address.

These variations are used to limit the selection of queued files that are processed. For example:

% /usr/sbin/sendmail -qSroot -qRbiff@here

Here, the queue is processed once. Only messages from root are processed. Of those, only messages that have biff@here as one of the recipients are processed.

In all three variations, a partial specification of queueid, recipient, or sender is viewed by V8 sendmail as a substring. For example:

-qSroot

matches mail from all of the following:

root
ben@groots.edu
ben@GROOTS.EDU

The last line further illustrates that the substring match is a case-insensitive one. The substring match is literal. Wildcard characters (such as *) and regular expressions (such as .*@.*edu) won’t work and might confuse the shell from which you run sendmail.

Multiple specifications can be combined on the command line (as shown earlier), but they all AND together:

% /usr/sbin/sendmail -qI123 -qSroot -qR@host.edu

Here, the queue is processed only for messages with the number 123 anywhere in the queue identifier that are also from root and that are also addressed to anyone at host.edu.

You can use the mailq command to preview the effect of these switches. For example, the following command will list (but not send) the messages that would be processed by the previous command line:

% mailq -qI123 -qSroot -qR@host.edu

Process by negated identifier/recipient/sender (V8.12 and later)

Beginning with V8.12 sendmail, you can prefix any of the I, S, or R specifications to -q with an ! character. The presence of an ! character prefix instructs sendmail to invert the logic of that particular test. For example:

% mailq -q\!Sroot -qR@host.edu

Here, we wish to process the queue for any message addressed to anyone at host.edu, just as we did in the previous section. But this time, we want to further limit that processing by including only messages with a sender that is not (the !) from root. Note that we prefix the ! with a backslash to protect it from the csh or tcsh shells (the backslash is not necessary for the Bourn shell and its derivatives).

In summary, these specifications for how to limit the queue can be mixed and matched, specified and negated, in any combination that works for you:

-qIidentmatch any queue ID that contains ident
-q!Iidentmatch any queue ID that does not contain ident
-qRrecipmatch any recipient address that contains recip
-q!Rrecipmatch any recipient address that does not contain recip
-qSfrommatch any sender address that contains from
-q!Sfrommatch any sender address that does not contain from

Only the ! character can be used to negate. Any other character will be interpreted as an argument to -q. The ! prefix must not follow the I, R, or S. If it follows, it will be interpreted as part of the expression to match.

Process by queue group with -qG (V8.12 and later)

Beginning with V8.12 sendmail, you can use the -qG command-line switch to process queues based on selected queue groups. It is used like this:

-qGgroupname

Here, only mail queued in the qroupname directories will be processed. If the name specified is of an unknown group, the following error will print and log, and the queue run will fail:

Queue group groupname unknown

This command-line switch can be used in combination with all the other queue processing switches. Consider, for example, the following:

% /usr/sbin/sendmail -qR@hostA.domain -qGslow

Here, sendmail will deliver queued messages only to users at hostA.domain, if those messages were queued in the slow queue group.

Multiple -qG command-line switches cannot be used at the same time. If you combine them, the following error will print and be logged:

Cannot use multiple -qG options

Unlike the -q[IRS] switches discussed earlier, the -qG command-line switch cannot be negated:

% /usr/sbin/sendmail -q!Gmqueue
Cannot use -q!G

Process the queue via ESMTP ETRN

The ESMTP ETRN command, based on RFC1985, causes V8.8 and later sendmail to asynchronously process its queue in a manner similar to the -qR command-line switch (Process by identifier/recipient/sender: -q[ISR] on page 431). This command allows dial-on-demand sites to make an SMTP connection and to force the other side to process and send any mail that is queued for them. The form of this ESMTP command looks like this:

ETRN host

If host is missing, this error message will be returned:

550 Parameter required

Otherwise, the queue will be processed just as though the following command-line argument were given:

-qR@host

In both cases, a qf file will be processed if it has host anywhere in the host part (following the @) of one of its R lines. The only difference here is that the former (the ETRN) operates asynchronously. That is, sendmail forks a copy of itself, and the forked child processes the queue.

Beginning with V8.12 sendmail, you can cause sendmail to process the queues by queue group. To do this, just replace the hostname with the name of the queue group, and prefix it with a literal # character:

ETRN #groupname

If the groupname has been defined, the queues for that queue group will be processed. Otherwise, the following error will be returned:

459 4.5.4 Queue badname unknown

One way to use ETRN is with a perl(1) script supplied with the sendmail source. See the file:

contrib/etrn.pl

You might have to change the first line of this file to get it to work, depending on where you installed perl(1) on your system. To run this program, just give it the name of your MX server:

% contrib/etrn.pl  your.mx.server

The etrn.pl script will connect to that server, and it will send an ETRN command to that server for each host you list with a Cw or Fw command in your configuration file. The etrn.pl script is also its own manual page, which you can read with a command such as this:

% nroff -man contrib/etrn.pl | more

Persistent Queue Runners with -qp

V8.12 sendmail introduced persistent queue runners as a solution to some of the problems caused by periodic queue runners. Periodic queue runners are the result of a normal -qinterval command-line switch, or a Runners= queue-group equate. Either causes:

  • sendmail to fork one or more queue runners to process a queue or queue group each interval

  • Every queue runner to open and read all the files in the queue to gather a list of envelopes to deliver

Persistent queue runners avoid these problems because a single process is dedicated to a queue, a queue group, or a grouping of queue groups (called a “workgroup”). A persistent queue runner is launched just like the periodic command-line queue runner, but with the addition of a p character:

-qpinterval

The p causes one or more persistent queue runners to be launched, one per queue group. One will be launched to handle your default queue group, and one more will be launched to handle each queue group defined by a QUEUE_GROUP mc option. Depending on the number of queue directories in each, these can be combined into a single workgroup. When you have many queue groups, you can end up with multiple workgroups controlling persistent runners.

Each persistent queue runner will sleep for interval. When it awakes, it reads all the files in the queues that belong to its workgroup, and sorts all the envelopes it finds into the proper order needed for delivery. After it has finished ordering the envelopes, it launches one or more regular queue runners to perform delivery using that already processed list. This can significantly reduce the disk I/O compared to that needed by periodic queue runners.

When the last of the regular queue runners has finished processing (and exited), the persistent queue runner goes back to sleep for interval.

In general, persistent queue runners are valuable only at sites that normally have queues that are very full. When a queue is normally near empty, persistent queue runners can introduce unforeseen delays. Note that a persistent queue runner will sleep again only when all of its regular queue runners have finished. One regular queue runner, delivering to a very slow site, can appear to hang, and so can cause the persistent queue runner to also appear to hang. Subsequent queue runs will be delayed until the hung site times out, allowing the persistent queue runner to sleep interval again.

At large sites, such delays will eventually smooth out due to the normal distribution of slow jobs. At small sites, such delays might be noticed and objected to. In general, persistent queue runners should be reserved for sites with full queues.

If interval is omitted, the default interval becomes 1 second:

-qp

When the default interval is used (by omitting the interval), the persistent queue runner will sleep one second between queue runs, unless the prior queue run was empty, in which instance it will sleep five seconds. If you choose the default interval, we recommend you also set the MinQueueAge option (MinQueueAge on page 1057).

If interval is specified as zero, the effect is the same as though it were omitted. If interval is negative, the following error is logged and printed and sendmail exits:

Invalid -q value

If interval is nonnumeric (if you specify O when you mean zero), the following error is logged and printed, and sendmail exits:

Invalid time unit `O'

The process that was given the -qp command-line switch is the controlling process. It could be the listening daemon (if -bd or -bD were also used), or it could be a queue processing daemon (if only -qp and other queue processing limiters were specified). The controlling process has two special properties:

  • To restart the persistent queue runners, you must instead restart the sendmail controlling process. You do that with a SIGHUP signal (as normal). If you try to signal the individual persistent queue runners, they will restart but with a penalty (each can be restarted this way only 10 times; see later in this section).

  • Beginning with V8.14, all persistent queue runners can be restarted by sending a SIGHUP signal to the controlling persistent queue runner.

  • If a persistent queue runner fails and exits, the controlling process will launch a new persistent queue runner.

If a persistent queue runner core-dumps, the following will be logged and that queue runner will not be restarted:

persistent queue runner=number core dumped, signal=signal

If a persistent queue runner exits because of a caught signal, the following is logged and that queue runner is restarted:

persistent queue runner=number died, signal=signal

If a persistent queue runner is restarted because of a SIGHUP, the following is logged:

restart queue runner=number due to signal signal

If the -dno_persistent_restart debugging command-line switch is specified, a failed persistent queue runner will not be restarted, and the following error will be logged:

persistent queue runner=number, exited

A persistent queue runner will not be restarted if it has already been restarted 10 times. Instead, the following error will be logged and that persistent queue runner marked as bad:

ERROR: persistent queue runner=number restarted too many times, queue runner lost

If this happens, examine your logs. Some nonmail-related process might be signaling your persistent queue runners, or you might have bad memory, or you might have made a mistake when building sendmail and should rebuild it, or you might have a junior system administrator who does not know how to correctly restart sendmail.

Persistent queue runners look like executing periodic queue runners in process listings:

root   22958   476  ?  S   08:43   0:00 sendmail: accepting connections
root   22947   512  ?  S   08:32   0:00 sendmail: running queue: /var/spool/mqueues/
q.1/df

Here, the first line shows the controlling process, and the second line shows a persistent queue runner. Note that even though the second entry says “running,” it might not be.

Process Alternative Queues

The sendmail program provides the ability to use queue directories other than the one listed in the configuration file’s QueueDirectory option (QueueDirectory on page 1070). Other queue directories can be used to solve an assortment of problems. One example is a site being down for an extended period. When a lot of mail is sent to such a site, messages collect in the queue and eventually start timing out. By moving those messages to a separate queue directory and processing it at a later time (when that site is back up), unnecessary bouncing of mail can be prevented.

Note that the QueueDirectory option is not safe. If its value is changed by anyone other than root, sendmail runs as an ordinary user.

Handling a Down Site

If a site is down, messages to that site can collect in the queue. If the site is expected to be down for a protracted period of time, those queued messages will begin to time out and bounce. To prevent them from bouncing, you can move them to a separate queue directory. Later, when the down site comes back up, you can process that separate queue.

There are two ways to move mail to a holding queue. One way is to simply move them to a different directory, but you cannot do that if you are using queue groups. The other way is to use queue groups, as we show later.

Move mail with qtool.pl

If you are not using queue groups, you can move the affected messages to a separate queue using the contrib/qtool.pl script supplied with the sendmail source. If you are using queue groups, you should skip to the next section.

To use qtool.pl, you first make a destination directory, if one does not already exist:

# mkdir /var/spool/newqueue
# chmod 700 /var/spool/newqueue

Next, run qtool.pl to move messages from the regular queue to the new holding queue:[190]

# contrib/qtool.pl /var/spool/newqueue /var/spool/mqueues/q.1

When the down site comes back up at a later time (say, 50 days later), the messages that have been saved in the holding directory can be delivered by running the following command by hand (it has been wrapped to fit the page):

% /usr/sbin/sendmail -OQueueDirectory=/var/spool/newqueue -OTimeout.queuereturn=51d
-OTimeout.queuewarn=0 -q

The -OTimeout.queuereturn=51d causes the time-to-live in the queue to be extended to 51 days or one day longer than the oldest held message. This prevents the held mail from wrongly bouncing when you try to deliver it, should the site not really be up yet.

The -OTimeout.queuewarn=0 prevents nondelivery warnings from being sent that might confuse the sender.

Move mail with queue groups

To move mail to a new queue with queue groups, the process is exactly the same as we have shown already, but with a few wrinkles. You have to stop sendmail, move the messages to a queue from which sendmail will not deliver, and then restart sendmail. The key differences between this approach and the one described in the previous section are:

  • The sendmail program should not be running when you move the messages out of the queue with qtool.pl.

  • When the down site comes back up, stop sendmail again, and move the messages back to the queue from where they came with qtool.pl. Process them there by hand with a long Timeout.queuereturn. When all of the backlogged mail has flushed, you can restart sendmail to run as normal.

If it is not possible to stop sendmail for the time needed to flush the old messages, you can leave the messages in the holding queue. For this to work, you will need to generate a configuration file that does not use queue groups, and use that configuration file to flush the holding queue.

Queue Quarantining

Queue quarantining is the process by which envelopes in the queue are marked as being ineligible for delivery. Such envelopes may then be manually or automatically reviewed. If the review uncovers no problems, each such envelope may then be delivered, bounced, or discarded. Queue quarantining employs the queue’s qf file, command-line switches, and the access database. Lost envelopes (covered in the next section) are also a part of this system.

Overview of Quarantining

A quarantined message is an envelope, containing one or more recipients, that is held in the queue pending review. It can either be an inbound or outbound envelope that, for policy or security reasons, should not be sent or delivered immediately, or not be sent or delivered as is.

For example, consider a user who has a history of sending offensive email. You might want to intercept such a user’s email on its way out, so it can be screened for words or phrases that the user has been previously warned about.

V8.13 sendmail implemented quarantining by creating a new kind of queued file. Instead of storing the envelope information in a qf file, a quarantined message has its envelope information stored in an hf file. The different file allows sendmail to process messages normally (quarantined messages are invisible) unless you specifically ask it to handle quarantined messages (make them visible).

Note that the mailstats program (The mailstats Program on page 364) automatically (without you needing to ask) includes the total count of quarantined messages in its output.

To ensure that the reason for quarantining a message is not lost, a new qf file[191] line has been introduced. Called a q line (q line on page 453), it stores the reason the message was quarantined. In parallel, a new macro, called ${quarantine} (${quarantine} on page 841) has also been added. It is intended for use in rule sets, and contains the reason the envelope was quarantined.

Note that quarantining integrates well with all the other queuing facilities of sendmail and even works with envelope splitting.

Quarantine Command-Line Switches

The command line can be used to quarantine and dequarantine envelopes. V8.13 has added one new command-line switch and modified another. We will show the use of the modified switch first, and then the new one.

The -qQ command-line switch

Normally, the queue is processed by invoking a -q command-line switch (Periodically with -q on page 427). This switch causes all the normally scheduled (nonquarantined) envelopes to be processed. By combining that switch with a Q argument, you tell sendmail to process quarantined messages instead.

Note that it is not possible to operate on both normal and quarantined envelopes at the same time. That is, listing -q and then -qQ will not process both; it will process only quarantined messages.

Unless limited with other -q letters, the -qQ switch will process all the quarantined envelopes currently in the queue. To further limit the envelopes to be processed, specify any of these additional switches in the same command line:

-qIidentmatch any queue ID that contains ident (§11.8.2.3 on page
431)
-q!Iidentmatch any queue ID that does not contain ident (§11.8.2.4
on page 432)
-qRrecipmatch any recipient address that contains recip (§11.8.2.3
on page 431)
-q!Rrecipmatch any recipient address that does not contain recip (§
11.8.2.4 on page 432)
-qSfrommatch any sender address that contains from (§11.8.2.3 on pa
ge 431)
-q!Sfrommatch any sender address that does not contain from (§11.8.
2.4 on page 432)
-qGnamematch any queue group with the name name (§11.8.2.5 on page
432)
-qQreasonmatch any queue group with the name reason (§11.10.2 on
page 439)

For example, the following command line will only process quarantined envelopes in the queue group okayclients that were sent by the user bob:

/usr/sbin/sendmail -qQ -qGokayclients -qSbob

The same switches can also be used to determine what the mailq command will print. For example, the following prints the status of all the currently quarantined envelopes:

mailq -qQ

The -Q command-line switch

When the -Q command-line switch is used with an argument (such as -Q"reason") it causes the specified envelopes to become quarantined. When used without an argument, it causes the specified envelopes to become dequarantined.

For example, the following command line causes all currently queued envelopes destined for the user bob to become quarantined:

/usr/sbin/sendmail -qSbob@your.domain -Q"Bob resigned today"

Here, the -qSbob@your.domain causes the queue to be searched for all envelopes that are from the sender (the -qS) bob at your domain. The -Q is followed by the argument "Bob resigned today", so all those messages are quarantined using “Bob resigned today” as the reason.

To dequarantine those same messages you might use a command line like the following, where the -Q is not followed by an argument:

/usr/sbin/sendmail -qQ -qSbob@your.domain -Q

Here, the -qQ tells sendmail to only operate on quarantined envelopes. The -qS causes sendmail to search the quarantined envelopes for those from the sender bob at your domain. And finally, the -Q, without an argument, tells sendmail to de-quarantine all the envelopes found.

The mailq command’s display

When the -qQ command-line switch is specified, the mailq command displays only quarantined messages and the reason each was quarantined. For example:

# mailq -qQ
                /var/spool/mailqueue (1 request)
-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient----------
h2VJcN3M012024   875429 Thu Mar 24 16:44 bob@your.domain
     QUARANTINE: Bob resigned today
                                         fred@compeditor.domain
                Total requests: 1

Here, the -qQ command-line switch caused mailq to print only the messages (there is just one in this example) that were quarantined in the queue. Information about the message is printed first. The reason the message was quarantined is printed next. Then the recipient or recipients of the message are printed last.

If you have set up a Milter to automatically quarantine messages, or have set up the access database or created rule sets to do so, you should run mailq with this -qQ command-line argument periodically, allowing you to learn whether anything has been automatically quarantined.

Use Milter to quarantine

The end-of-message handler, inside a Milter, can call smfi_quarantine(3) (Milter smfi_quarantine() on page 1194) to quarantine the envelope being screened.

Use the access database to quarantine

The access database (The access Database on page 277) provides a single, central database with rules to accept, reject, and discard messages based on the sender name, address, or IP address. It is enabled with the FEATURE(access_db) (Enabling the access Database Generally on page 277).

A source text file used to create an access database might look (in part) like the following. Note that each line is composed of a key on the left and a value on the right, the two separated by tabs:[192]

key          QUARANTINE
key          QUARANTINE:reason

Note that the QUARANTINE term on the right may optionally be followed by a colon and the reason the envelope is being quarantined. The reason may contain whitespace, but must not contain newlines and should not be quoted.

For example, consider the following entries in a source file for an access database:

Connect:192.168.1.23      QUARANTINE:Bob's PC
To:your.compeditor.gov    QUARANTINE:Review mail to our compeditor
From:head.hunter.domain   QUARANTINE:Employee theft?

In the first line, Bob’s PC sends email by connecting to the SMTP port on the central mail server. Because of past behavior, or perhaps because of a worm or virus on Bob’s PC, we want to quarantine all outbound mail from that machine.

In the second line, management has requested that all mail addressed to the domain your.compeditor.gov (using an SMTP RCPT To:) be quarantined for review before it is allowed to be sent.

The last line says that inbound mail addressed from the domain head.hunter.domain (using an SMTP MAIL From:) be quarantined so that it may be reviewed to see whether employee theft is being attempted.

One limitation of the access database is that it cannot conveniently be used to combine tests. If your tests are more complex than the access database can handle, note that you may also test using rules in rule sets.

Use rule sets to quarantine

Any of the check_ rule sets (The Local_check_ Rule Sets on page 252)[193] and any of the header screening rule sets (Rules Check Header Contents on page 1130) may be used to quarantine envelopes. Any rule set that returns a $#error (error on page 720) with a $@ part (The parse Rule Set 0 on page 696) that is the literal quarantine, will cause the message to be quarantined:

R $* < @ bad.site > $*         $# error $@ quarantine $: reason

Here, we show a rule in a rule set that returns a $#error. Because the $@ part is the literal quarantine, the message will be quarantined. Note that the $: part contains the reason the message is being quarantined.

Note that rule set quarantining affects all recipients of that envelope.

To illustrate rule set quarantining, consider the following mc configuration lines that cause any message which contains a special X-review: header to be held for review:

LOCAL_CONFIG
HX-review: $>Xreview

LOCAL_RULESETS
SXreview
R YES           $#error $@ quarantine $: X-review held for review

The first part of our example, the LOCAL_CONFIG part, defines a header. This header definition tells sendmail to pass all X-Review: header values through (the $>) the Xreview rule set.

The second part (LOCAL_RULESETS) defines the Xreview rule set (the S line) which contains a single rule that looks for a value that is the literal word YES. If that header’s value is YES, the message is quarantined with the reason shown. If that header is missing, or if it has any other value, this quarantine step is skipped.

Note that rule sets can detect whether a message has already been quarantined by checking the ${quarantine} sendmail macro (${quarantine} on page 841). If that macro has a value, the message was already quarantined.

Log quarantined messages

Whenever a message is quarantined, the fact that it was quarantined and the reason for doing so are logged using syslog(3). One log line is produced to record the quarantine event. Another is produced for each recipient to show that each was also quarantined.

The information logged for the quarantine event varies depending on the method used to quarantine. If a rule set was used, for example, a log line like the following might be produced:

Oct  9 11:26:00 your.domain sendmail[4788]: f99IPuIH004788: ruleset=check_mail,
arg1=bob@compeditor.gov, quarantine=Hold mail from compeditor.gov

This line (wrapped to fit the page) shows that the check_mail rule set found the address in its workspace and quarantined the message for the reason shown.

A Milter can also cause messages to be quarantined. The log line, produced by such a Milter event, might look like the following:

Oct  23 09:25:59 monkeyboy sendmail[52314]: f99IPuIH004787: milter=DocMilter,
quarantine=Suspect application/ms-word attachment

Here the Milter named DocMilter found a MIME type that indicated a possible Microsoft Word document was included as an attachment.

In addition to logging an event, each recipient is also logged. For example, consider the following log line:

Nov 21 09:32:13 your.domain sendmail[33522]: fALHVwAQ033522: to=<bob@your.domain>,
delay=00:00:06, mailer=local, pri=30029, quarantine=Suspect application/ms-word
attachment, stat=quarantine

Here the quarantine= equate shows the reason the message was quarantined, and the stat= equate prints the literal word quarantine.

When Milters, the access database, and rule sets are used to automatically quarantine messages, a script may be devised to detect the quarantine= equate in the logging output. When run nightly, such a script might email the postmaster with a summary of quarantined messages for that day.

Manage quarantined envelopes with qtool.pl

The qtool.pl program is located in the contrib subdirectory of the source distribution. It is a perl(1) script that allows you to move envelopes between queues, bounce envelopes, and remove envelopes.

In general, if you use queue groups (Queue Groups (V8.12 and Later) on page 408), you should not use qtool.pl to move queued messages. However, it is always safe to move quarantined messages, because they are invisible to sendmail unless you manually cause sendmail to recognize them.

As of V8.13, the -Q command-line switch tells qtool.pl to operate on quarantined messages rather than on normal messages. For example, the following command causes all the quarantined messages in the main queue to be moved to a holding queue:

#./qtool.pl -Q /var/spool/hold /var/spool/mqueue

Also, as of V8.13, a new %msg hash variable has been introduced. Called quarantine_reason, it can be used to match strings in the literal reason the message was quarantined. You could use this, for example, to bounce all messages that were quarantined with a reason that contained the word Virus:

#./qtool.pl -b -Q -e '$msg{quarantine_reason} =˜ m/Virus/'

See the online manual for the qtool.pl program (contrib/qtool.8) for a complete guide to using that program.

The qf file’s quarantine reason: q line

As of V8.13, the qf file’s q line is used to store the reason that an envelope was quarantined. The q line should appear only in quarantined envelopes, that is, in hf files, not in qf files. If a q line appears in a qf file, that file will be silently converted into an hf file. Thus, it does no good to simply rename an hf file into a qf file.

The format of a q line looks like this:

qreason

There may be only one q line in an hf file. The reason is the reason the envelope was quarantined.

Pitfalls

  • Each release of sendmail offers more and better ways to handle queue problems. They are mostly implemented as options. Table 24-7 on page 966 lists all options that affect the queue. Whenever you upgrade to a new sendmail release, be sure to read the RELEASE_NOTES for information about new ways to solve queueing problems.

  • The queue directory should never be shared among machines. Such sharing can make detection of orphaned locks impossible. Bugs in network-locking daemons can lead to race conditions in which neither of two machines can generate a queue identifier.

  • Homespun programs and shell scripts for delivery of local mail can fail and lose mail by exiting with the wrong value. In the case of a recoverable error (a full disk, for example), they should exit with EX_OSERR or EX_TEMPFAIL. Both of these exit values are defined in <sysexits.h> and cause the message to be re-queued.

  • Because sendmail does a chdir(2) into its queue directory, you should avoid removing and re-creating that directory while the sendmail daemon is running. When processing the queue, sendmail tries to read the queue directory by doing an opendir(3) of the current directory. When the queue directory is removed, sendmail fails that open and syslog(3)s the following warning:

    orderq: cannot open "/usr/spool/mqueue" as ".": No such file or directory
  • Some very old versions of sendmail had a bug in handling the queue that could cause a message to be lost when that message was the last in a queue run to be processed. This, among other reasons, is good cause to always make sure you are running the latest version of sendmail.

  • The sendmail program assumes that only it and other trusted root programs will place files into its primary queue directory. Consequently, it trusts everything it finds there that is correctly formatted and has the correct ownership and permissions. The queue directory must be protected from other users and untrusted programs.

  • If the queue directory is on a disk mounted separately from / and /usr, be certain to mount that disk before starting the sendmail daemon. If you reverse these steps, the sendmail daemon will chdir(2) into the queue before the mount. One effect of the reversal is that incoming mail will use a directory different from that used by outgoing mail. Another effect is that incoming queued mail will be invisible. Yet another effect is that the outgoing queue will never be processed by the daemon.

  • When using multiple queues, it might be possible to umount a directory while sendmail is still running, but you should avoid this temptation. Never mount or umount queue disks while sendmail is running. Stop sendmail first, do your maintenance, and then restart sendmail.

  • If a Milter deletes a recipient, and if queue groups are used, that recipient can cause a queue group to wrongly be selected. This defect has been fixed in V8.14.

  • When using V8.12 and later sendmail, avoid moving queue files yourself. The qf file, for example, internally stores the full pathname of the df file’s directory, which means you would need to edit that line as part of a move. Also, sendmail can split messages at message submission time into multiple qf files, possibly in different queues, all sharing a df file with hard or symbolic links to it. This complexity makes moving queue files a complex undertaking.

The qf File Internals

The qf file holds all the information that is needed to perform delivery of a queued mail message. The information contained in that file, and its appearance, changes from release to release of sendmail. Here, we document the qf file that is used with V8.14 sendmail. Note that V8.7 introduced a V version line that enabled later versions to correctly process older versions’ queue files.

This section must be taken with a proverbial grain of salt. The internals of the qf file are essentially an internal interface to sendmail and, as such, are subject to change without notice. The information offered here is intended only to help debug sendmail problems. It is not intended (and we strongly discourage its use) as a guide for writing files directly to the queue.

The qf file is line-oriented, containing one item of information per line. Each line begins with a single character (the code character), which specifies the contents of the line. Each code character is followed, with no intervening space, by the information appropriate to the character. The complete list of code characters is shown in Table 11-6.

Table 11-6. qf file code characters

Code

§

Meaning

Version

How many

[a]

A

A line on page 446

AUTH= parameter

V8.10 and later

At most, one

B

B line on page 447

Message body type

V8.6 and later

At most, one

C

C line on page 447

Set controlling user

V5.62 and later

At most, one per R line

d

d line on page 448

Datafile directory

V8.12 and later

Exactly one

D

D line on page 449

Datafile name

Obsolete as of V8.7

Exactly one

E

E line on page 449

Send errors to

V8.6 and earlier

Many

F

F line on page 450

Saved flag bits

V8.1 and later

Exactly one

H

H line on page 451

Header line

All versions

Many

I

I line on page 451

Inode and device information for the df file

V8.7 and later

Exactly one

K

K line on page 452

Time last processed

V8.7 and later

Exactly one

M

M line on page 452

Message (why queued)

All versions

Many[a]

N

N line on page 452

Number of times tried

V8.7 and later

At most, one

P

P line on page 453

Priority (current)

All versions

At most, one

q

q line on page 453

Reason this envelope was quarantined

V8.13 and later

At most, one

Q

Q line on page 454

The DSN ORCPT address

V8.7 and later

At most, one per R line

r

r line on page 454

Final recipient

V8.10 and later

At most, one

R

R line on page 454

Recipient address

All versions

Many

S

S line on page 455

Sender address

All versions

Exactly one

T

T line on page 456

Time created

All versions

Exactly one

V

V line on page 457

Version

V8.7 and later

Exactly one

Z

Z line on page 458

DSN envelope ID

V8.7 and later

At most, one

!

! line on page 458

Deliver-by specification

V8.12 and later

At most, one

$

$ line on page 458

Restore macro value

V8.6 and later

At most, one each macro

.

.line on page 459

End of qf file

V8.7 and later

Exactly one

[a] a Prior to V8.12, there could be only a single M line.

We discuss the individual lines in the qf file by code letters. Each letter is presented in alphabetical order rather than the order in which they should appear in the qf file.

A line

AUTH= parameter V8.10 and later

RFC2554 describes the currently approved method for SMTP authentication. One part of this method is to add the AUTH= extension to the MAIL From: command for ESMTP. This A line in the qf file is used to store the value passed in that parameter.

If you compiled sendmail with SASL defined (SASL on page 137), this value will be the actual value passed by the AUTH=. Otherwise, it is the value of $f ($f on page 824) normalized to the local domain if $f lacks a domain.

B line

Message body type V8.6 and later

The message body type is described under the -B command-line switch (-B on page 232). The B line in the qf file stores whatever the body type was set to, either from the command line or by the SMTP MAIL command. The two usual body types are 8BITMIME and 7BIT.

The form of the B line is:

Btype

There must be no space between the B and the type. If the type is missing, the body type becomes the character value zero. If the entire B line is missing, the default is 7BIT. If type is longer than MAXNAME as defined in conf.h (MAX... on page 120) when compiling sendmail, it is truncated to MAXNAME-1 characters when the qf file is read.

Note that the type must be either 7bit or 8bitmime. Anything else will not be detected when the qf file is read and might eventually cause the ESMTP dialog to fail:

501 <sender>... Unknown BODY type badtype

This error will be reproduced at every MX site for the recipient until a site that does not speak ESMTP is found or until the MX list is exhausted.

C line

Set controlling user V5.62 and later

To ensure secure handling of delivery, recipient addresses that are either a file or a program require that sendmail perform delivery as the owner of the file or program rather than as the user defined by the DefaultUser option (DefaultUser on page 1000). A file address is one that begins with a / character. A program address is one that begins with a | character. Both characters are detected after quotation marks have been stripped from the address.

To prevent potential security violations, sendmail must take special precautions when addresses in the qf file result from reading a ~/.forward or :include: file. When such an address is to be placed into the qf file (whether as a recipient’s address in an R line or as an error recipient’s address in an E line), sendmail first places a C line (for Controlling user) into the file and then the recipient’s address. The C line specifies the owner of the ~/.forward or :include: file:

Cgeorge
RPF:/u/users/george/mail/archive
Cben
RPF:|/u/users/ben/bin/mailfilter

Here, when sendmail later delivers to the recipients in this qf file, it first converts its user identity to that of the user george, and then resets itself back to being root again. The same process repeats with the next recipient, except that sendmail changes from root to ben and back again. If there is no C line preceding an R line, the previous C line’s value is carried down:

Cgeorge
RPF:/u/users/george/mail/archive
RPF:|/u/users/ben/bin/mailfilter          ← controlling user is george

The form of the C line in the qf file is:

Cuserprior to V8
Cuser:eaddrV8.1 through V8.7.5
Cuser:uid:gid:eaddrV8.7.6 and later

The C must begin the line and be immediately followed by user, with no intervening space. If no user follows the C, any prior controlling user is cleared and the identity that is used reverts to that specified by the DefaultUser option (DefaultUser on page 1000). If present, the user is the login name of the owner of the ~/.forward or :include: file that yielded the address in the next following R or E line. If user is the name of a user who is unknown to the system, prior to V8.7.6 and prior to V8.8 the effect was the same as though it were missing. Beginning with V8.8 and V8.7.6, an unknown user causes the identity to become that of the uid and gid. Beginning with V8 sendmail, an optional eaddr might be last. If present, the eaddr gives the address to use for error messages.

There can be only one C line immediately preceding each R and E line. Two C lines in a row have the effect of the second superseding the first.

d line

Data file directory V8.12 and later

Beginning with V8.12 sendmail, it is possible to split envelopes for more efficient delivery. When sendmail splits an envelope, the new qf file will share a df file with the prior qf file. But to ensure that each qf file has it own df file, sendmail creates a hard link to make a copy of the df file. That way, the old qf file uses the old df file, and the new qf file uses the new df file. The two df files are the same because they are hard-linked together.

A problem arises when the two qf files are saved on two different disks. Because it is not possible to hard-link across disks, the new qf file is put on the new disk, but the new df file is left on the old disk. That way, an efficient hard link can still be made, but now the new qf file and its new df file are on different disks.

The d qf-file line was introduced to allow a qf file to find its corresponding df file when that df file is on a different disk. Whenever a qf file has a corresponding df file on a different disk, that qf file will contain a d line that looks like this:

d/path

The /path must be the full pathname of the directory that contains the df part. If /path is missing, or is not a directory that was set in the configuration file, the following error is logged and the qf file is considered bad, and marked as such (Bogus qf Files on page 419):

Losing qf file: bogus queue file directory

D line

Datafile name Obsolete as of V8.7

Beginning with V8.7, sendmail looks for its datafile (the file containing the message body) under the same name as its qf file, but with the q changed into a d. Prior to V8.7, the D line in the qf file contained the name of the file that contained the message body. If the D line was missing, there was no message body. The form of the qf file D line was:

Dfile

The D must begin the line. The file must immediately follow with no intervening space. All text, from the first character following the D to the end of the line, is taken as the name of the file. There is no default for file; either it must be present, or the entire D line must be absent.

The sendmail program opens the df file for reading. If that open fails, sendmail syslog(3)s the following error message at LOG_CRIT and continues to process the qf file:

readqf: cannot open dfAA12345

Be aware that sendmail attempts to remove the file after it has been delivered to all recipients. If sendmail is unable to remove the file, and if the LogLevel option (LogLevel on page 1040) is greater than 97, sendmail syslog(3)s the following warning at LOG_DEBUG:

file: unlink-fail #

The file is the name of the file that could not be removed. The # is the error number, as defined in /usr/include/errno.h.

The df file is opened only when processing the queue file, not when printing it. When printing the queue, the df is stated so that its size can be printed.

E line

Send errors to V8.6 and earlier

Notification of errors often requires special handling by sendmail. When mail to a mailing list fails, for example, sendmail looks for the owner of that list. If it finds one, the owner, not the sender, receives notification of the error. To differentiate error notification addresses from ordinary sender and recipient addresses, pre-V8.7 sendmail stored error addresses separately in the qf file, one per E line. Beginning with V8.7, this E line is no longer used. Instead, sendmail uses the S line.

The form of the E line in the qf file looks like this:

EaddrV8.6 and earlier

The E must begin the line. One or more addresses can be entered on that same line. Whitespace and commas can surround the individual addresses. Note, however, that sendmail places only a single address on each E line. There can be multiple E lines. Each is processed in turn.

Each line is fully processed as it is read. That is, the line is scanned for multiple addresses. Each address that is found is alias-expanded. Each resulting new address is processed by rule sets 3 and 0 to resolve a delivery agent for each.

If an alias expands to a program or a file (text that begins with a / or | character), that text is sent out in the delivered message’s Errors-To: line in that form. This can cause confusion when the message is later processed and bounced at the receiving site.

F line

Saved flag bits V8.1 and later

Under V8 sendmail, the Timeout.queuewarn option (Timeout on page 1097) can specify an interval to wait before notifying the sender that a message could not immediately be delivered. To keep track of whether such a notification has been sent, sendmail stores the state of its EF_WARNING envelope flag in the qf file. If that flag is set, notification has already been sent.

Error mail messages sent by sendmail can occasionally be queued, rather than immediately delivered. The Timeout.queuewarn option notification should not be sent for such mail. If such mail remains in the queue too long, it should be canceled rather than bounced. V8 sendmail saves the state of the EF_RESPONSE envelope flag in the qf file. If that flag is set, the message is an error notification.

Beginning with V8.8, sendmail also records the state of the EF_HAS8BIT flag (the message body contains 8-bit data) and the EF_DELETE_BCC flag (delete empty Bcc: headers, Bcc: on page 1152).

All envelope flags are listed in Table 15-5 on page 545. The F line is used to save envelope flags for later restoration. Its form looks like this:

Fflags

Here, the flags are any combination of those shown in Table 11-7.

Table 11-7. qf file F flags

Flag

Description

8

Restores the EF_HAS8BIT flag

b

Restores the EF_DELETE_BCC flag

d

Restores the EF_RET_PARAM flag

n

Restores the EF_NO_BODY_RETN flag

r

Restores the EF_RESPONSE flag

s

Restores the EF_SPLIT flag

w

Restores the EF_WARNING flag

Only the letters listed in the table are recognized. Other letters are silently ignored. Note that these flags might be done away with in later versions of sendmail and new flags might be added without notice.

For security protection, V8 sendmail rejects and logs the following flag sequence:

From
    ↑
    a space here

See Funny Flag Bits in qf File on page 421 for more information about this.

H line

Header line All versions of sendmail

The lines of text that form the message header are saved to the qf file, one per H line. Any header lines added by sendmail are also saved to H lines in the qf file.

The form of the H line is:

Hdefinition

The H must begin the line, and the definition must immediately follow with no intervening space. The definition is exactly the same as, and obeys the same rules as, the H commands in the configuration file (Overview on page 1120). Beginning with V8.10, if the header lacks header flags, an empty pair of ? characters are prefixed to the definition.

When sendmail writes header lines to the qf file, it pre-expands sendmail macros (replaces expressions such as $x with their values) and preresolves conditionals ($?, $!, and $.). Beginning with V8.10, the headers in the qf file might have been rewritten by rule sets.

The order in which H lines appear in the qf file is exactly the same as the order in which they appear in the delivered message.

I line

Inode and device information for the df file V8.7 and later

When a machine crashes under Unix, files in a directory can become detached from that directory. When this happens, those orphaned files are saved in a directory called lost+found. Because filenames are saved only in directories, orphaned files are nameless. Consequently, Unix stores them in lost+found using their inode numbers as their names.

To illustrate, consider finding these four files in lost+found after a crash:

#1528 #1200 #3124 #3125

Two of these are qf files, and two are df files. Beginning with V8.7 sendmail, the qf files contain a record of the inode numbers for their corresponding df files. That information is stored in the I line:

Imajor/minor/ino

Here, the major and minor are the major and minor device numbers for the disk device that the df file was stored on. The ino is the inode number for the df file. In our lost+found example, the following command could be run to pair up the orphaned files:

% grep "^I.*/.*/" *
#1200:I123/45/3124
#1325:I123/45/1528

This shows that the qf file #1200 has the df file #3124 and that the qf file #1325 has the df file #1528.

The sendmail program does not check the inode number in the I line against the actual inode number of the df file. Instead, the I line is generated afresh each time the qf file is processed.

When df, qf, and xf subdirectories are used, and when those subdirectories are on separate disks, a crash of one disk can leave the df or qf file intact, and the other in lost+found.

K line

The time last processed from the queue V8.7 and later

The MinQueueAge option (MinQueueAge on page 1057) sets the length of time a queued message must remain queued before delivery can again be tried. Each time sendmail processes a qf file, it subtracts the time stored in the K line from the current time and compares the result to the MinQueueAge. If sufficient time has not passed, the rest of the processing is skipped. (Note that this test is performed only if the qf file has been processed at least once; see the N line in N line on page 452.)

The time stored in the K line looks like this:

K703531020

This number represents the date and time in seconds since January 1, 1970. Every time the qf file is processed (delivery is attempted), the K line is updated with the current time.

M line

Why the message was queued All versions of sendmail

When a mail message is placed into the queue because of an error during the delivery attempt, the nature of that error is stored in the M line of the qf file. The error is usually prefixed with Deferred:

Deferred: reason

Delivery can be deferred until a later queue run because of a temporary lack of services. For example, the reason might be “remote host is down.”

The form of the qf file M line is:

Mmsg

The M must begin the line. It is immediately followed by the msg with no intervening space. The text of msg is everything up to the end of the line. The msg created by sendmail can include the word Deferred: followed by a reason. The envelope-specific M line should appear before the S line.

Beginning with V8.12, each recipient also has an M line preceding its R line.

If the msg is missing, sendmail simply prints a blank line rather than a reason when showing the queue with mailq or the -bp command-line switch. If the M line is missing entirely, sendmail prints nothing.

The maximum number of characters in msg is defined by MAXLINE in conf.h (MAX... on page 120). Prior to V8.12, there could be only one M line in a qf file.

N line

Number of times tried V8.7 and later

Each time delivery is attempted for a message, the number stored in its qf file’s N line is incremented by one. This number always begins at zero.

When delivering many messages to a single host, sendmail remembers failures. If one message fails to make it all the way through an SMTP dialog, all the following messages to that same host will be deferred (not attempted during the current queue run). For those deferred messages, the number of tries is correctly incremented as though the delivery was actually attempted.

The value in this N line is used to determine whether the delay of the MinQueueAge option (MinQueueAge on page 1057) should be triggered. This value, when zero, can also be used to enable a special first-time connection timeout (Timeout.iconnect (V8.8 and later) on page 1103).

P line

Priority when processed from queue All versions of sendmail

Not all messages need to be treated equally. Messages that have failed often, for example, tend to continue to fail. When sendmail processes the messages in its queue, it sorts them by priority and attempts to deliver those with the lowest priority value first.

When a mail message is first placed into the queue, it is given an initial priority calculated when it was first created (RecipientFactor on page 1077), which is stored in the P line:

P640561

This number in the qf file is really a cost. The lower it is, the more preferentially the message is treated by sendmail. Each time the qf file is read, the number in the P line is incremented. The size of that increment is set by the value of the RetryFactor option (RetryFactor on page 1081). If that option is negative, this logic is inverted.

The form of the qf file P line is:

Ppri

The P must begin the line. The pri is a text representation of an integer value. The pri must immediately follow the P with no intervening space. The text in pri is converted to an integer using the C-library routine atol(3). That routine allows pri to be represented in text as a signed decimal number, an octal number, or a hexadecimal number.

If pri is absent, the priority value used is that of the configuration file RetryFactor option. If the entire P line is absent, the priority value begins at zero.

There should be only one P line in any qf file. Multiple P lines cause all but the last to be ignored.

q line

Reason an envelope was quarantined V8.13 and later

When an envelope is quarantined (Queue Quarantining on page 438) the reason is stated in this q line. The q line is not part of a qf file, but is actually in the quarantined hf file.

qreason

Here, the reason can be manually inserted using sendmail’s command line, or automatically inserted via the access database or rule sets.

Q line

The DSN ORCPT address V8.7 and later

When a mail message arrives that includes an ORCPT parameter for the ESMTP RCPT command (see RFC1891), sendmail needs to save that parameter’s information separately from the RCPT recipient address:

RCPT To:<gw@wash.dc.gov> ORCPT=rfc822;gw@wash.dc.gov
    ↑                     ↑
     recipient address   parameter's information

Not all sites understand DSN. If sendmail forwards the message to such a site, it needs to omit the ORCPT parameter. Consequently, sendmail must not store that parameter with the RCPT address.

The Q line is used to separately store the ORCPT parameter information:

Qtype;addr

The type;addr is defined by RFC1891. The sendmail program checks the syntax of addr when that information is received, but otherwise merely stores type;addr as is in the Q line.

There must be only a single Q line for each recipient R line, and each such Q line must precede its corresponding R line.

r line

Final-Recipient DSN address V8.10 and later

When sendmail bounces a mail message, it does so using DSN (T= on page 754). The type of address and the actual address of the final original recipient are reported in the bounce message:

Final-Recipient: RFC822; nosuchuser@site.com

This Final-Recipient line shows the type of address (here RFC822) and the actual address (here ) of the final recipient.

Beginning with V8.10, sendmail composes this address expression only once (when the message is queued) and stores it in case the message bounces. The text following the Final-Recipient: is stored in the r line in the qf file, and looks like this:

rRFC822; nosuchuser@site.com

Note that sendmail performs no checks on the text following the r. This means that invalid DSN information placed there will become the text that follows the Final-Recipient: in the bounce message.

R line

Recipient’s address All versions of sendmail

The qf file lists all the recipients for a mail message. There can be one recipient or many. When sendmail creates the qf file, it lists each recipient address on an individual R line. The form of the R line in the qf file looks like this:

Rflags:addr

The R must begin the line. Only a single address can appear on each R line. There can be multiple R lines. Each is processed in turn.

If the colon is present and if the version of the qf file is greater than 0, the characters between the R and the colon are interpreted as flags that further define the nature of the address:

P(primary)

Addresses can undergo many transformations prior to delivery. When expanding aliases, for example, the address george might be transformed into two addresses via a ~/.forward file: george@here and george@there. In this instance, george is the primary address, and the aliases are secondary addresses. If aliasing yields only a single transformation, the single new address is considered primary. Addresses that are received via an RCPT SMTP command, or on the command line, are always considered primary, as are all other recipient addressees prior to aliasing.

N(notify)

Recipient addresses can lead to various kinds of notification based on the nature of the DSN NOTIFY extension to the RCPT SMTP command. That notification can be either NEVER or some combination of SUCCESS, FAILURE, or DELAY. Internally, sendmail uses the absence of the latter three to imply NEVER. This N flag simply says that the DSN NOTIFY extension appeared in the message. If the N is absent, but an S, F, or D is present, DSN information will not be propagated. Note that NOTIFY can also be specified by using the -N command-line switch (-N on page 244).

S, F, D(success, failure, delay)

The DSN NOTIFY extension to the RCPT SMTP command will specify either NEVER or some combination of SUCCESS, FAILURE, or DELAY. When any of these is specified, its first letter is used as a flag for the recipient address. SUCCESS means to notify the sender that final delivery succeeded. FAILURE is used to notify the sender that some step toward delivery failed fatally. DELAY lets the sender know that the message has been delayed but delivery will continue to be attempted.

A

If the address in the R line is the result of an alias expansion, this A flag is included to indicate that fact.

Each R line is fully processed as it is read. That is, the line is scanned for multiple addresses. Each address that is found is alias-expanded. Each resulting new address is processed by the canonify rule set 3 and the parse rule set 0 to resolve a delivery agent for each.

S line

Sender’s address All versions of sendmail

Each mail message must have a sender. The sendmail program can determine the sender in four ways:

  • If the sender is specified in the envelope of an SMTP connection, that sender’s address is used.

  • If the -f command-line argument is used to run sendmail, the sender’s address is the address following the -f.

  • If the sender is not specified in the envelope, the address that is used is that of the user who ran the sendmail program. If that user is unknown, the sender is made to be postmaster.

  • When processing the queue, the sender’s address is specified in the S line of the qf file.

The form of the S line in the qf file looks like this:

Saddr

The S must begin the line. Exactly one address must follow on that same line. Whitespace can surround that address. There can be only one S line in the qf file.

If the addr is missing, sendmail sets the sender to be the user who ran sendmail. If that user is not known in the passwd file (or database), sendmail syslog(3)s the following message and sets the sender to be postmaster:

Who are you?

The resulting address is then processed to extract the user’s full name into $x ($x on page 851). Finally, the sender’s address is rewritten by the canonify rule set 3, the parse rule set 0, and the final rule set 4.

Under all versions of sendmail, the address in the S line will include any RFC822 comment text that appeared with the original message. Under V8.7, if the F=c flag (F=c on page 768) is set for the sender’s delivery agent, all comment text is stripped from the address.

If sendmail is compiled with USERDB defined (USERDB on page 150), the sender address can optionally be rewritten by the User Database before it is placed into the S line. Such rewriting is allowed only if the delivery agent for the sender includes the F=i flag (F=i on page 772).

T line

Time created All versions of sendmail

To limit the amount of time a message can remain in the queue before being bounced, sendmail must know when that message was first placed in the queue. That time of first placement is stored in the T line in the qf file. For example, the following number represents the date and time in seconds since January 1, 1970:

T703531020

Each time sendmail fails to deliver a message from the queue, it checks to see whether too much time has passed. It adds the T line value to the value specified in the Timeout.queuereturn option (Timeout on page 1097). If that sum is greater than the current time, the message is bounced instead of being left in the queue.

Messages are occasionally left in the queue for longer than the normal timeout period. This might happen, for example, if a remote machine is down but you know that it will eventually be brought back up. There are two ways to lengthen the amount of time a message can remain in the queue.

The preferred way is to create a temporary separate queue directory and move the necessary queued file to that temporary holding place. When the remote site comes back up, you can later process the files in that other queue by running sendmail with an artificially long Timeout.queuereturn value (Process Alternative Queues on page 436).

A second way to extend the life of messages in the queue is to edit the qf file and change the value stored in the T line. Just add 86400 to that value for each day you want to extend. Care is required to avoid editing a file that is currently being processed by sendmail.[194]

There is currently no plan to give sendmail the ability to rejuvenate queued messages (make old messages appear young).

The form of the T line in the qf file is:

Tsecs

The T begins the line, and the secs must immediately follow with no intervening space. The numeric text that forms secs is converted to an integer using the C-library routine atol(3). That routine allows secs to be represented in text as a signed decimal number, an octal number, or a hexadecimal number.

If secs is absent or the entire T line is absent, the time value is zero. A zero value causes the mail message to time out immediately.

There should be only one T line in any qf file. Multiple T lines cause all but the last to be ignored.

V line

Version of the qf file V8.7 and later

As sendmail evolves, it will continue to add new abilities to the qf file. To protect old versions of sendmail from wrongly misinterpreting new configuration files, the V line has been introduced. Note that prior to V8.7 sendmail there was no V line. The V line, and qf file version numbers for more modern implementations of sendmail, are shown in Table 11-8.

Table 11-8. qf file version numbers

qf version

sendmail versions

V1

V8.7.5 and earlier

V2

V8.7.6 through V8.9.3

V4

V8.10 through V8.11

V5

V8.10 through V8.11 with _FFR_QUEUEDELAY defined

V6

V8.12

V7

V8.12 with _FFR_QUEUEDELAY defined

V8

V8.13 added support for Queue Quarantining

If the version found in a qf file is greater than that currently supported by sendmail, the following error will be printed if in verbose mode:

Version number in qf (bad) greater than max (max)

and the following message will be logged:

Losing qf file unsupported queue file version

The bad qf file will then be turned into a Qf file (marked as lost).

Z line

DSN ENVID envelope identifier V8.7 and later

The MAIL ESMTP command can optionally be followed by an RFC1891 ENVID envelope identifier:

MAIL From: <address> ENVID=envelopeID

ENVID is used to propagate a consistent envelope identifier (distinct from the Message-ID: header, Message-ID: on page 1159) that is permanently associated with a message.

The Z line holds that ENVID envelope identifier information:

ZenvelopeID

The ENVID information needs to be held separately from the S sender line because sendmail has no way to determine in advance whether a recipient host speaks ESMTP.

There must be only a single Z line in any qf file. The ${envid} sendmail macro (${envid} on page 823) also stores the ENVID value.

! line

Deliver-by specification V8.12 and later

Beginning with V8.12, sendmail supports the DELIVERBY SMTP extension (defined by RFC2825). If the DeliverByMin option (DeliverByMin on page 1003) was defined with a positive value, a BY= equate can follow the SMTP MAIL From: command for inbound email. The BY= equate defines the window of time during which the message should be delivered. That equate’s time value can optionally be followed by a flag that states what to do upon delivery (an r flag), or upon delivery failure (an n flag). There can also be a flag that advises sendmail to trace delivery (a v flag). The BY= information is saved in the qf file on an ! line:

!flag time

Here, the time is the time specified by the By= and the flag is a 1-byte integer that encodes a value of 1 (notify the sender upon delivery), or 2 (return the message if it cannot be delivered in time). Additionally, the flag can be OR’d with a 0x10 (trace the delivery).

This ! line appears for inbound mail only, and appears only if you have configured your mc file to define the DeliverByMin option with a positive value.

$ line

Restore macro value V8.6 and later

The sendmail program uses the $r sendmail macro ($r on page 842) to store the protocol used when sendmail first received a mail message. If the message was received by using SMTP, that protocol is smtp. Otherwise, it is NULL.

The sendmail program uses the $s sendmail macro ($s on page 844) to store the full canonical name of the sender’s machine.

The sendmail program uses the $_ sendmail macro ($_ on page 801) to store RFC1413 identd(8) information and IP source-routing information.

When sendmail creates a qf file, it saves the values of the $r, $s, and $_ sendmail macros in lines that begin with $.

The form of the $ line in the qf file looks like this:

$Xvalue
${XXX}value

The $ must begin the line, and the sendmail macro’s single-character name (the X) or multicharacter name (the {XXX}) must immediately follow with no intervening space. The sendmail macro’s name is followed (again with no intervening space) by the value of the macro.

If value is missing, the value given to the macro is NULL. If the macro name and value are missing, the macro \ is given a value of NULL. If both are present, the macro whose name is specified is given the value specified (value).

There can be multiple $ lines. The sendmail macro names to be stored in the qf file are listed in the $={persistentMacros} class ($={persistentMacros} on page 873).

.line

Mark EOF in qf file V8.7 and later

One form of attack against sendmail involves appending information to an existing qf file. To prevent such attacks, V8.7 introduced the dot line. In a qf file, any line that begins with a single dot:

.followed by anything

is considered to mark the end of the file’s useful information. Upon encountering that dot, sendmail continues to read the qf file. If any line follows the dot line, sendmail logs the following message and changes the qf file into a Qf file (Extra Data at End of qf File on page 420):

SECURITY ALERT: extra data in qf: bogus line here


[175] * Of course, if sendmail is started somewhere else or by someone else, the queue directory will be a subdirectory under that other starting directory.

[176] * Programs should not depend on the lead letter actually encoding the hour. It is intended only to ensure that all identifiers be unique within any 24-hour period and as an aid to scripts that need to extract information from logfiles.

[177] Omission of the letters y and z is intentional.

[178] * Historical footnote: this stems from the days when the only atomic filesystem call was link(2).

[179] * The degenerate case of multiple queues is a single queue. We examine a single queue here, for simplicity.

[180] * Again, for simplicity, we assume a standard hard disk. Naturally, reads will be much faster on specialty hardware such as memory-based disks.

[181] Operations that cause the disk head to move, such as file unlinks, are called IOPs. Typical hard disks are limited to about 120 IOPs per second. When sendmail successfully delivers a message it can consume from 10 to 13 IOPs per message.

[182] * As measured on a 300 MHz Intel machine running Berkeley Software Design Inc. (BSDI) Unix version 3.

[183] * Unless you ran a separate queue-processing daemon for each set of queues. Then you could call them queue groups.

[184] * However, we recommend that you use only letters, the dash character (hyphen), and the underscore character. Other characters might become illegal in future releases of sendmail.

[185] * You can copy the rules created by that feature and paste them into your own. However, that is not recommended because the copied rules might change with new releases of sendmail, and then the old copied rules will fail.

[186] * This letter might change from a Q to a different letter in the future.

[187] * Prior to V8.7, this was determined by defining QUEUESIZE in conf.h.

[188] * Note that prior to V8 sendmail, the r option set one timeout for all SMTP timeouts.

[189] * IDA and pre-V8 SunOS sendmail offer three command-line switches for processing the queue. The -M switch allows you to specify a specific message for processing. The -R switch allows you to specify specific recipient addresses for processing. The -S switch allows you to specify specific sender addresses for processing.

[190] * We fudge on this command. It actually moves all mail from the current queue to the new queue directory. You might have to supply other arguments to qtool.pl to select specific qf files to move. See the manual page, called contrib/qtool.8, for more information.

[191] * We say qf file, even though this new line appears only in the new hf file type.

[192] * Or another separation character specified by the -t command-line switch with makemap.

[193] * Except the check_compat rule set.

[194] * The nvi(1) editor uses the same file locking as sendmail and so can safely be used to edit qf files.