1. 개요 qmail에서의 데이터 흐름: qmail-smtpd --- qmail-queue --- qmail-send --- qmail-rspawn --- qmail-remote / | \ qmail-inject _/ qmail-clean \_ qmail-lspawn --- qmail-local 모든 메세지는 qmail-queue에 의해 큐 디렉토리에 추가된다. qmail-queue는 필요에 의해 실행되는데 일반적으로 로컬에서 발생되는 메세지를 처리하는 qmail-inject, SMTP을 통해 메세지를 받는 qmail-smtpd, 포워드 메세지를 처리하는 qmail-local, 바운스 메세지를 위한 qmail-send에 의해 불려진다. 큐에 저장된 메세지는 qmail-send에 의해 배달 (qmail-lspawn, qmail-rspawn, qmail-clean과의 협조하에)된다. 이 네가지는 오랫동안 실행되는 데몬이다. qmail 큐는 근본적인 파일 시스템의 안전을 고려하여 디자인 되었다. 모든 삭제 동작은 사람의 조정을 받지 않고 qmail-send 와 qmail-clean 에 의해 이루어진다. 더 자세한 것은 section 6 을 참고하라. 2. 큐 구조 큐의 각 메세지는 고유의 번호로 구분된다. 예를 들어 457이라는 메세지가 있다고 가정하면 각각의 디렉토리에는 그와 관련된 파일이 존재하게 되며, 다음과 같은 내용을 가지게 된다. mess/457: 메세지 원본 todo/457: envelope: 메세지가 도착한 곳, 메세지가 가야할 곳 intd/457: envelope, qmail-queue가 작업중 info/457: 전 처리를 마침, envelope 발신자 주소 local/457: 전 처리를 마침, 로컬 envelope 수신자 주소 remote/457: 전 처리를 마침, 원격 envelope 수신자 주소 bounce/457: 영구적 배달 오류 다음은 메세지가 가질 수 있는 모든 상태이다. + 는 파일이 존재함; - 는 존재하지 않음; ? 는 존재하거나 그렇지 않을 수 있음을 나타낸다. S1. -mess -intd -todo -info -local -remote -bounce S2. +mess -intd -todo -info -local -remote -bounce S3. +mess +intd -todo -info -local -remote -bounce S4. +mess ?intd +todo ?info ?local ?remote -bounce (queued) S5. +mess -intd -todo +info ?local ?remote ?bounce (preprocessed) 보증: If mess/457 가 존재한다면 그것의 inode 번호는 457 이다. 3. 어떻게 메세지가 큐로 들어가는가. 큐에 메세지를 추가하기 위해, qmail-queue은 먼저 분리된 디렉토리, /pid 에 먼저 파일을 만든다. 파일 시스템에 의해 파일은 고유의 inode 번호를 가지게 되고 qmail-queue 은 이 번호를 확인한다. 예를 들어 457 이라면 메세지 457은 상태 S1을 가진다. qmail-queue는 pid/* 를 mess/457 로 이름을 바꾸고, S2 로 이동한다. 메세지를 mess/457에 쓰고 intd/457를 만든후 S3로 이동하고, envelope 정보를 intd/457에 저장한다. 마지막으로 qmail-queue는 intd/457 를 위한 todo/457 링크를 만들고 S4 로 이동한다. 이제 메세지는 성공적으로 큐에 대기하며 qmail-send 의 처리를 기다린다. qmail-queue starts a 24-hour timer before touching any files, and commits suicide if the timer expires. 4. How queued messages are preprocessed Once a message has been queued, qmail-send must decide which recipients are local and which recipients are remote. It may also rewrite some recipient addresses. When qmail-send notices todo/457, it knows that message 457 is in S4. It removes info/457, local/457, and remote/457 if they exist. Then it reads through todo/457. It creates info/457, possibly local/457, and possibly remote/457. When it is done, it removes intd/457. The message is still in S4 at this point. Finally qmail-send removes todo/457, moving to S5. At that instant the message has been successfully preprocessed. 5. How preprocessed messages are delivered Messages at S5 are handled as follows. Each address in local/457 and remote/457 is marked either NOT DONE or DONE. DONE: The message was successfully delivered, or the last delivery attempt met with permanent failure. Either way, qmail-send should not attempt further delivery to this address. NOT DONE: If there have been any delivery attempts, they have all met with temporary failure. Either way, qmail-send should try delivery in the future. qmail-send may at its leisure try to deliver a message to a NOT DONE address. If the message is successfully delivered, qmail-send marks the address as DONE. If the delivery attempt meets with permanent failure, qmail-send first appends a note to bounce/457, creating bounce/457 if necessary; then it marks the address as DONE. Note that bounce/457 is not crashproof. qmail-send may handle bounce/457 at any time, as follows: it (1) injects a new bounce message, created from bounce/457 and mess/457; (2) deletes bounce/457. When all addresses in local/457 are DONE, qmail-send deletes local/457. Same for remote/457. When local/457 and remote/457 are gone, qmail-send eliminates the message, as follows. First, if bounce/457 exists, qmail-send handles it as described above. Once bounce/457 is definitely gone, qmail-send deletes info/457, moving to S2, and finally mess/457, moving to S1. 6. Cleanups If the computer crashes while qmail-queue is trying to queue a message, or while qmail-send is eliminating a message, the message may be left in state S2 or S3. When qmail-send sees a message in state S2 or S3---other than one it is currently eliminating!---where mess/457 is more than 36 hours old, it deletes intd/457 if that exists, then deletes mess/457. Note that any qmail-queue handling the message must be dead. Similarly, when qmail-send sees a file in the pid/ directory that is more than 36 hours old, it deletes it. Cleanups are not necessary if the computer crashes while qmail-send is delivering a message. At worst a message may be delivered twice. (There is no way for a distributed mail system to eliminate the possibility of duplication. What if an SMTP connection is broken just before the server acknowledges successful receipt of the message? The client must assume the worst and send the message again. Similarly, if the computer crashes just before qmail-send marks a message as DONE, the new qmail-send must assume the worst and send the message again. The usual solutions in the database literature---e.g., keeping log files---amount to saying that it's the recipient's computer's job to discard duplicate messages.) 7. Further notes Currently info/457 serves two purposes: first, it records the envelope sender; second, its modification time is used to decide when a message has been in the queue too long. In the future info/457 may store more information. Any non-backwards-compatible changes will be identified by version numbers. When qmail-queue has successfully placed a message into the queue, it pulls a trigger offered by qmail-send. Here is the current triggering mechanism: lock/trigger is a named pipe. Before scanning todo/, qmail-send opens lock/trigger O_NDELAY for reading. It then selects for readability on lock/trigger. qmail-queue pulls the trigger by writing a byte O_NDELAY to lock/trigger. This makes lock/trigger readable and wakes up qmail-send. Before scanning todo/ again, qmail-send closes and reopens lock/trigger.