diff options
Diffstat (limited to 'INTERNALS')
| -rw-r--r-- | INTERNALS | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/INTERNALS b/INTERNALS new file mode 100644 index 0000000..e668ae8 --- /dev/null +++ b/INTERNALS | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | 1. Overview | ||
| 2 | |||
| 3 | Here's the data flow in the qmail suite: | ||
| 4 | |||
| 5 | qmail-smtpd --- qmail-queue --- qmail-send --- qmail-rspawn --- qmail-remote | ||
| 6 | / | \ | ||
| 7 | qmail-inject _/ qmail-clean \_ qmail-lspawn --- qmail-local | ||
| 8 | |||
| 9 | Every message is added to a central queue directory by qmail-queue. | ||
| 10 | qmail-queue is invoked as needed, usually by qmail-inject for locally | ||
| 11 | generated messages, qmail-smtpd for messages received through SMTP, | ||
| 12 | qmail-local for forwarded messages, or qmail-send for bounce messages. | ||
| 13 | |||
| 14 | Every message is then delivered by qmail-send, in cooperation with | ||
| 15 | qmail-lspawn and qmail-rspawn, and cleaned up by qmail-clean. These four | ||
| 16 | programs are long-running daemons. | ||
| 17 | |||
| 18 | The queue is designed to be crashproof, provided that the underlying | ||
| 19 | filesystem is crashproof. All cleanups are handled by qmail-send and | ||
| 20 | qmail-clean without human intervention. See section 6 for more details. | ||
| 21 | |||
| 22 | |||
| 23 | 2. Queue structure | ||
| 24 | |||
| 25 | Each message in the queue is identified by a unique number, let's say | ||
| 26 | 457. The queue is organized into several directories, each of which may | ||
| 27 | contain files related to message 457: | ||
| 28 | |||
| 29 | mess/457: the message | ||
| 30 | todo/457: the envelope: where the message came from, where it's going | ||
| 31 | intd/457: the envelope, under construction by qmail-queue | ||
| 32 | info/457: the envelope sender address, after preprocessing | ||
| 33 | local/457: local envelope recipient addresses, after preprocessing | ||
| 34 | remote/457: remote envelope recipient addresses, after preprocessing | ||
| 35 | bounce/457: permanent delivery errors | ||
| 36 | |||
| 37 | Here are all possible states for a message. + means a file exists; - | ||
| 38 | means it does not exist; ? means it may or may not exist. | ||
| 39 | |||
| 40 | S1. -mess -intd -todo -info -local -remote -bounce | ||
| 41 | S2. +mess -intd -todo -info -local -remote -bounce | ||
| 42 | S3. +mess +intd -todo -info -local -remote -bounce | ||
| 43 | S4. +mess ?intd +todo ?info ?local ?remote -bounce (queued) | ||
| 44 | S5. +mess -intd -todo +info ?local ?remote ?bounce (preprocessed) | ||
| 45 | |||
| 46 | Guarantee: If mess/457 exists, it has inode number 457. | ||
| 47 | |||
| 48 | |||
| 49 | 3. How messages enter the queue | ||
| 50 | |||
| 51 | To add a message to the queue, qmail-queue first creates a file in a | ||
| 52 | separate directory, pid/, with a unique name. The filesystem assigns | ||
| 53 | that file a unique inode number. qmail-queue looks at that number, say | ||
| 54 | 457. By the guarantee above, message 457 must be in state S1. | ||
| 55 | |||
| 56 | qmail-queue renames pid/whatever as mess/457, moving to S2. It writes | ||
| 57 | the message to mess/457. It then creates intd/457, moving to S3, and | ||
| 58 | writes the envelope information to intd/457. | ||
| 59 | |||
| 60 | Finally qmail-queue creates a new link, todo/457, for intd/457, moving | ||
| 61 | to S4. At that instant the message has been successfully queued, and | ||
| 62 | qmail-queue leaves it for further handling by qmail-send. | ||
| 63 | |||
| 64 | qmail-queue starts a 24-hour timer before touching any files, and | ||
| 65 | commits suicide if the timer expires. | ||
| 66 | |||
| 67 | |||
| 68 | 4. How queued messages are preprocessed | ||
| 69 | |||
| 70 | Once a message has been queued, qmail-send must decide which recipients | ||
| 71 | are local and which recipients are remote. It may also rewrite some | ||
| 72 | recipient addresses. | ||
| 73 | |||
| 74 | When qmail-send notices todo/457, it knows that message 457 is in S4. It | ||
| 75 | removes info/457, local/457, and remote/457 if they exist. Then it reads | ||
| 76 | through todo/457. It creates info/457, possibly local/457, and possibly | ||
| 77 | remote/457. When it is done, it removes intd/457. The message is still | ||
| 78 | in S4 at this point. Finally qmail-send removes todo/457, moving to S5. | ||
| 79 | At that instant the message has been successfully preprocessed. | ||
| 80 | |||
| 81 | |||
| 82 | 5. How preprocessed messages are delivered | ||
| 83 | |||
| 84 | Messages at S5 are handled as follows. Each address in local/457 and | ||
| 85 | remote/457 is marked either NOT DONE or DONE. | ||
| 86 | |||
| 87 | DONE: The message was successfully delivered, or the last delivery | ||
| 88 | attempt met with permanent failure. Either way, qmail-send | ||
| 89 | should not attempt further delivery to this address. | ||
| 90 | |||
| 91 | NOT DONE: If there have been any delivery attempts, they have all | ||
| 92 | met with temporary failure. Either way, qmail-send should | ||
| 93 | try delivery in the future. | ||
| 94 | |||
| 95 | qmail-send may at its leisure try to deliver a message to a NOT DONE | ||
| 96 | address. If the message is successfully delivered, qmail-send marks the | ||
| 97 | address as DONE. If the delivery attempt meets with permanent failure, | ||
| 98 | qmail-send first appends a note to bounce/457, creating bounce/457 if | ||
| 99 | necessary; then it marks the address as DONE. Note that bounce/457 is | ||
| 100 | not crashproof. | ||
| 101 | |||
| 102 | qmail-send may handle bounce/457 at any time, as follows: it (1) injects | ||
| 103 | a new bounce message, created from bounce/457 and mess/457; (2) deletes | ||
| 104 | bounce/457. | ||
| 105 | |||
| 106 | When all addresses in local/457 are DONE, qmail-send deletes local/457. | ||
| 107 | Same for remote/457. | ||
| 108 | |||
| 109 | When local/457 and remote/457 are gone, qmail-send eliminates the | ||
| 110 | message, as follows. First, if bounce/457 exists, qmail-send handles it | ||
| 111 | as described above. Once bounce/457 is definitely gone, qmail-send | ||
| 112 | deletes info/457, moving to S2, and finally mess/457, moving to S1. | ||
| 113 | |||
| 114 | |||
| 115 | 6. Cleanups | ||
| 116 | |||
| 117 | If the computer crashes while qmail-queue is trying to queue a message, | ||
| 118 | or while qmail-send is eliminating a message, the message may be left in | ||
| 119 | state S2 or S3. | ||
| 120 | |||
| 121 | When qmail-send sees a message in state S2 or S3---other than one | ||
| 122 | it is currently eliminating!---where mess/457 is more than 36 hours old, | ||
| 123 | it deletes intd/457 if that exists, then deletes mess/457. Note that any | ||
| 124 | qmail-queue handling the message must be dead. | ||
| 125 | |||
| 126 | Similarly, when qmail-send sees a file in the pid/ directory that is | ||
| 127 | more than 36 hours old, it deletes it. | ||
| 128 | |||
| 129 | Cleanups are not necessary if the computer crashes while qmail-send is | ||
| 130 | delivering a message. At worst a message may be delivered twice. (There | ||
| 131 | is no way for a distributed mail system to eliminate the possibility of | ||
| 132 | duplication. What if an SMTP connection is broken just before the server | ||
| 133 | acknowledges successful receipt of the message? The client must assume | ||
| 134 | the worst and send the message again. Similarly, if the computer crashes | ||
| 135 | just before qmail-send marks a message as DONE, the new qmail-send must | ||
| 136 | assume the worst and send the message again. The usual solutions in the | ||
| 137 | database literature---e.g., keeping log files---amount to saying that | ||
| 138 | it's the recipient's computer's job to discard duplicate messages.) | ||
| 139 | |||
| 140 | |||
| 141 | 7. Further notes | ||
| 142 | |||
| 143 | Currently info/457 serves two purposes: first, it records the envelope | ||
| 144 | sender; second, its modification time is used to decide when a message | ||
| 145 | has been in the queue too long. In the future info/457 may store more | ||
| 146 | information. Any non-backwards-compatible changes will be identified by | ||
| 147 | version numbers. | ||
| 148 | |||
| 149 | When qmail-queue has successfully placed a message into the queue, it | ||
| 150 | pulls a trigger offered by qmail-send. Here is the current triggering | ||
| 151 | mechanism: lock/trigger is a named pipe. Before scanning todo/, | ||
| 152 | qmail-send opens lock/trigger O_NDELAY for reading. It then selects for | ||
| 153 | readability on lock/trigger. qmail-queue pulls the trigger by writing a | ||
| 154 | byte O_NDELAY to lock/trigger. This makes lock/trigger readable and | ||
| 155 | wakes up qmail-send. Before scanning todo/ again, qmail-send closes and | ||
| 156 | reopens lock/trigger. | ||
