The Qmail Queue

The queue lives in $QMAIL_HOME/queue/ and its subdirectories. Messages are inserted into the Queue via QMAIL-QUEUE and removed with QMAIL-CLEAN. Message numbers are assigned with the process id of the QMAIL-QUEUE process, and it will increment numbers if necessary when that message number already exists.

There are 2 parts (or channels, as qmail-send refers to them) of the Qmail queue. The Local queue holds message destinations for deliveries to local user accounts. The Remote queue holds those for foreign mail hosts. The different parts of the message and its envelope are stored in different subdirectories.

Each message is named with its inode number, a Unix term denoting the disk location(?) occupied by the main file. Because queue sizes can get quite large, qmail hashes the inode number into a hash subdirectory within each directory type of the queue. By default, the queue is hashed into 23 sub-directories (00 through 22). For systems with large mail requirements, this can be increased to a larger prime number. The hash here is a simple molulus (remainder or inode/23). This hash number is used to prevent the queue subdirectories from getting too large and slow, which sometimes happens on some systems. The qmail default distibution does not hash the “todo” or “bounce” directory, which can become a problem on high-volume servers.

The Queue Subdirectories

Under the $QMAIL_HOME/queue/ tree, a message is split into several files, each residing in a seperate subdirectory of the queue. Within these subdirectories, the message is again hashed (see above) into a series of numerical subdirectories. Each message is identified by its primary INODE location. This inode is hashed (remainder of inode/23 or of the current hash split number), and the file is located in the $QMAIL_HOME/queue/type/hash/inode files, where “type” is defined below and the “hash” is the 2-digit modulus from the inode (00..23).

$QMAIL_HOME/queue/mess/hash/inode file holds the message body. This is the RCF822 Email and MIME message headers as weill as th body content. Remember that the headers are for information only and the actual sender and recipient are stored in the message envelope!

$QMAIL_HOME/queue/info/hash/inode hold the “Return Path” or sender portion of the envelope. This is stored in the standard qmail format of the letteter ‘F’ (for “from”) followed by the email address, followed by the ‘\0’ zero-byte character.

$QMAIL_HOME/queue/intd/hash/inode This is a work area for qmail, where it constructs the envelope while the message is added into the queue. Qmail-queue writes the “mess” and “todo” file. Then qmail-send “preprocesses” the mail by creating the secure data files within the queue. The file contains a series of ‘\0’ zero-byte delimited records in the sequence:

  • User-Id (uid) of process submitting mail, “u82”
  • Process-Id (pid) of qmail-queue process(?), “p93435”
  • Return-Path, “Fuser@domain”
  • Recipient List, “Tuser@domain”, ...

It seems the file in the “todo” queue directory is identical to this one. Pre-processing by qmail-send will create the “info”, “local” and “remote” entries for the message., and the “intd” and “todo” entries will be removed. After that, the message can be queued for delivery.

$QMAIL_HOME/queue/local/hash/inode contains the standard qmail recipient list of “local” addresses to which the message will be delivered. Each address in preceded by the status type ‘T’ (designating a “to” request) and followed by the zero-byte (’\0’) character. As messages are placed in the queue, the domain name is checked against the list of domains im the $QMAIL_HOME/control/locals file. If the domain is found in the file, that address is written to the local queue, otherwise it is written to the remote queue (see below). The list of recipients is given in the envelope for the message when it is inserted into the queue. The Recipient Status Codes are defined as:

  • T - To be delivered (untried)
  • D - Permanent Delivery Error
  • Z - Temporary Delivery Error, deferred and retried later
  • K - Successful Delivery

Any failure messages are reflected in the “bounce” file (see below).

A trick to reading this encoded queue is to use the Unix strings command to print out each Status+Address on a line. You can use “cut -c1-” to remove the Status byte. Or you can use “grep ^D remotefile | wc -l” to count recipients with a particular status (here, ‘D’).

$QMAIL_HOME/queue/remote/hash/inode like the local queue description above, are the addresses that are not scheduled to be a local delivery. Instead, qmail will try to deliver them using SMTP to remote mail servers. The status codes work the same as with would with the local queue.

$QMAIL_HOME/queue/bounce/inode contains the list of addresses and reason codes (given by the remote MTA–Mail Transport Agent or server). The addresses are on a line of the form “<address>:” and the reason follows on the next line. This file holds all the reason codes for the bounces. If after continued delivery attempts, the message exceeeds it queuelifetime setting, (found in $QMAIL_HOME/control/queuelifetime, it will “bounce” this file back to the sender, found in the “Return Path” file of the “info” file in the queue (see above). Qmail-send will prefix a hokey “Hi! This is the qmail-send program at....” message to this file. By its format, the addresses in the file are able to be extracted by automated programs such ash the ezmlm-return program from the EZMLM mailing list manager. These bouncing addresses can then be handles appropriately.

$QMAIL_HOME/queue/todo/inode The “To Do” queue lists the message inodes numbers that qmail-send must now schedule for delivery. These files are created ny qmail-queue when the message is inserted into the queue, and deleted by qmail-send once they are initially scheduled for delivery. The files contian do real data, only list the messages inodes that need to be scheduled.

$QMAIL_HOME/queue/lock/inode is a named pipe in the qmail queue, which really acts as a type of alarm clock for qmail-send. Once a new message is inserted into the queue, the qmail-queue program writes a meaningless byte into the queue to wake up a potentially sleeping qmail-send process. Qmail-send listens to this pipe and the operating system will wake the program if it is sleeping when this signal is received.

Queue operations

The queue should not be manipulated by non-Qmail components while Qmail (qmail-send) is running. Nasty things can happen. You can delete a message from the queue by removing its files from the bounce, infoi, local, remote, and mess directories. You should always use QMAIL-QUEUE to insert a meessage into the queue. Qmail does not need to be running to do this. The reason for this restriction is that qmail-send keeps the list of messages to send in memory, along with the next retry time. If you remove messages from the disk queue, qmail-send has no way of knowing what you just did! Wouldn’t it be great (hint, hint!) if there were a $MAIL_HOME/queue/dontdo/inode file that is read and those messages are scheduled for removal from qmail! A delete operation just writes the indoe file to this directory and hits the $QMAIL_HOME/queue/lock/trigger pipe to tell qmail to clean up its act! It would then remove it from its internal list and send the requst to qmail-clean to perform the actual deletion.

The other problem is removing recipients on messages already scheduled for delivery. Well, technically, all this has to to is to write the ‘K’ status message over the delivery status of the local/remote file for the address. If qmail-send is already running and trying to deliver to addresses on that file, it could create a real mess!. Again, we could create the use of the $MAIL_HOME/queue/killaddress/inode file containing a list of subscriber addresses (or pattern!) to remove from the message. I think this may be going a bit too far though. If we add too much silly extra processing to qmail, we could end of with sendmail again!

 
qmail/queue.txt · Last modified: 2005/09/16 16:57 by allen