?? qmail-send.c
字號:
// qmail-send用來檢測隊列中每一個郵件的狀態(tài),并調(diào)用相應的程序進行處理。
//例如一個目的為本地的郵件將會被qmail-send調(diào)用qmail-lspawn處理
#include <sys/types.h>#include <sys/stat.h>#include "readwrite.h"#include "sig.h"#include "direntry.h"#include "control.h"#include "select.h"#include "open.h"#include "seek.h"#include "exit.h"#include "lock.h"#include "ndelay.h"#include "now.h"#include "getln.h"#include "substdio.h"#include "alloc.h"#include "error.h"#include "stralloc.h"#include "str.h"#include "byte.h"#include "fmt.h"#include "scan.h"#include "case.h"#include "auto_qmail.h"#include "trigger.h"#include "newfield.h"#include "quote.h"#include "qmail.h"#include "qsutil.h"#include "prioq.h"#include "constmap.h"#include "fmtqfn.h"#include "readsubdir.h"/* critical timing feature #1: if not triggered, do not busy-loop *//* critical timing feature #2: if triggered, respond within fixed time *//* important timing feature: when triggered, respond instantly */#define SLEEP_TODO 1500 /* check todo/ every 25 minutes in any case */#define SLEEP_FUZZ 1 /* slop a bit on sleeps to avoid zeno effect */#define SLEEP_FOREVER 86400 /* absolute maximum time spent in select() */#define SLEEP_CLEANUP 76431 /* time between cleanups */#define SLEEP_SYSFAIL 123#define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */int lifetime = 604800;
// 在UUCP協(xié)議中帶有%并在percenthack中列出的地址將會被轉(zhuǎn)換為一個標準的DNS格式主機名stralloc percenthack = {0};struct constmap mappercenthack;
// 用來為qmail指明位于本地的郵件地址。qmail-send利用此文件來判斷收件人地址是否是
//一個本地地址,qmail-send檢測到隊列中有發(fā)送給本地地址的郵件的時候,就會調(diào)用
//qmail-lspawn來進行本地投送stralloc locals = {0};struct constmap maplocals;stralloc vdoms = {0};struct constmap mapvdoms;
// qmail-send用來給沒有主機名的郵件收件人制定一個主機名,通常并沒有必要,qmail-send
//總是使用me中的域名stralloc envnoathost = {0};
// 默認情況下,退回給原始發(fā)送方的郵件的發(fā)件人地址為MAILER-DAEMON@HOSTNAME,
//如果想修改MAILER-DAEMON,需要修改bouncefrom
//如果想修改HOSTNAME,需要修改bouncehoststralloc bouncefrom = {0};stralloc bouncehost = {0};
// 類似與上面的關系,doublebouncehost指定主機名,doublebounceto指定用戶名。
//功能是為一個被退回兩次的郵件制定一條出路。stralloc doublebounceto = {0};stralloc doublebouncehost = {0};char strnum2[FMT_ULONG];char strnum3[FMT_ULONG];#define CHANNELS 2char *chanaddr[CHANNELS] = { "local/", "remote/" };char *chanstatusmsg[CHANNELS] = { " local ", " remote " };char *tochan[CHANNELS] = { " to local ", " to remote " };int chanfdout[CHANNELS] = { 1, 3 };int chanfdin[CHANNELS] = { 2, 4 };int chanskip[CHANNELS] = { 10, 20 };int flagexitasap = 0; void sigterm() { flagexitasap = 1; }int flagrunasap = 0; void sigalrm() { flagrunasap = 1; }int flagreadasap = 0; void sighup() { flagreadasap = 1; }void cleandied() { log1("alert: oh no! lost qmail-clean connection! dying...\n"); flagexitasap = 1; }int flagspawnalive[CHANNELS];void spawndied(c) int c; { log1("alert: oh no! lost spawn connection! dying...\n"); flagspawnalive[c] = 0; flagexitasap = 1; }#define REPORTMAX 10000datetime_sec recent;/* this file is too long ----------------------------------------- FILENAMES */stralloc fn = {0};stralloc fn2 = {0};char fnmake_strnum[FMT_ULONG];void fnmake_init(){ while (!stralloc_ready(&fn,FMTQFN)) nomem(); while (!stralloc_ready(&fn2,FMTQFN)) nomem();}void fnmake_info(id) unsigned long id; { fn.len = fmtqfn(fn.s,"info/",id,1); }void fnmake_todo(id) unsigned long id; { fn.len = fmtqfn(fn.s,"todo/",id,0); }void fnmake_mess(id) unsigned long id; { fn.len = fmtqfn(fn.s,"mess/",id,1); }void fnmake_foop(id) unsigned long id; { fn.len = fmtqfn(fn.s,"foop/",id,0); }void fnmake_split(id) unsigned long id; { fn.len = fmtqfn(fn.s,"",id,1); }void fnmake2_bounce(id) unsigned long id;{ fn2.len = fmtqfn(fn2.s,"bounce/",id,0); }void fnmake_chanaddr(id,c) unsigned long id; int c;{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }/* this file is too long ----------------------------------------- REWRITING */stralloc rwline = {0};/* 1 if by land, 2 if by sea, 0 if out of memory. not allowed to barf. *//* may trash recip. must set up rwline, between a T and a \0. */int rewrite(recip)char *recip;{ int i; int j; char *x; static stralloc addr = {0}; int at; if (!stralloc_copys(&rwline,"T")) return 0; if (!stralloc_copys(&addr,recip)) return 0; i = byte_rchr(addr.s,addr.len,'@'); if (i == addr.len) { if (!stralloc_cats(&addr,"@")) return 0; if (!stralloc_cat(&addr,&envnoathost)) return 0; } while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) { j = byte_rchr(addr.s,i,'%'); if (j == i) break; addr.len = i; i = j; addr.s[i] = '@'; } at = byte_rchr(addr.s,addr.len,'@'); if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { if (!stralloc_cat(&rwline,&addr)) return 0; if (!stralloc_0(&rwline)) return 0; return 1; } for (i = 0;i <= addr.len;++i) if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) { if (!*x) break; if (!stralloc_cats(&rwline,x)) return 0; if (!stralloc_cats(&rwline,"-")) return 0; if (!stralloc_cat(&rwline,&addr)) return 0; if (!stralloc_0(&rwline)) return 0; return 1; } if (!stralloc_cat(&rwline,&addr)) return 0; if (!stralloc_0(&rwline)) return 0; return 2;}void senderadd(sa,sender,recip)stralloc *sa;char *sender;char *recip;{ int i; int j; int k; i = str_len(sender); if (i >= 4) if (str_equal(sender + i - 4,"-@[]")) { j = byte_rchr(sender,i - 4,'@'); k = str_rchr(recip,'@'); if (recip[k] && (j + 5 <= i)) { /* owner-@host-@[] -> owner-recipbox=reciphost@host */ while (!stralloc_catb(sa,sender,j)) nomem(); while (!stralloc_catb(sa,recip,k)) nomem(); while (!stralloc_cats(sa,"=")) nomem(); while (!stralloc_cats(sa,recip + k + 1)) nomem(); while (!stralloc_cats(sa,"@")) nomem(); while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem(); return; } } while (!stralloc_cats(sa,sender)) nomem();}/* this file is too long ---------------------------------------------- INFO */int getinfo(sa,dt,id)stralloc *sa;datetime_sec *dt;unsigned long id;{ int fdinfo; struct stat st; static stralloc line = {0}; int match; substdio ss; char buf[128]; fnmake_info(id); fdinfo = open_read(fn.s); if (fdinfo == -1) return 0; if (fstat(fdinfo,&st) == -1) { close(fdinfo); return 0; } substdio_fdbuf(&ss,read,fdinfo,buf,sizeof(buf)); if (getln(&ss,&line,&match,'\0') == -1) { close(fdinfo); return 0; } close(fdinfo); if (!match) return 0; if (line.s[0] != 'F') return 0; *dt = st.st_mtime; while (!stralloc_copys(sa,line.s + 1)) nomem(); while (!stralloc_0(sa)) nomem(); return 1;}/* this file is too long ------------------------------------- COMMUNICATION */substdio sstoqc; char sstoqcbuf[1024];substdio ssfromqc; char ssfromqcbuf[1024];stralloc comm_buf[CHANNELS] = { {0}, {0} };int comm_pos[CHANNELS];void comm_init(){ int c; substdio_fdbuf(&sstoqc,write,5,sstoqcbuf,sizeof(sstoqcbuf)); substdio_fdbuf(&ssfromqc,read,6,ssfromqcbuf,sizeof(ssfromqcbuf)); for (c = 0;c < CHANNELS;++c) if (ndelay_on(chanfdout[c]) == -1) /* this is so stupid: NDELAY semantics should be default on write */ spawndied(c); /* drastic, but better than risking deadlock */}int comm_canwrite(c)int c;{ /* XXX: could allow a bigger buffer; say 10 recipients */ if (comm_buf[c].s && comm_buf[c].len) return 0; return 1;}void comm_write(c,delnum,id,sender,recip)int c;int delnum;unsigned long id;char *sender;char *recip;{ char ch; if (comm_buf[c].s && comm_buf[c].len) return; while (!stralloc_copys(&comm_buf[c],"")) nomem(); ch = delnum; while (!stralloc_append(&comm_buf[c],&ch)) nomem(); fnmake_split(id); while (!stralloc_cats(&comm_buf[c],fn.s)) nomem(); while (!stralloc_0(&comm_buf[c])) nomem(); senderadd(&comm_buf[c],sender,recip); while (!stralloc_0(&comm_buf[c])) nomem(); while (!stralloc_cats(&comm_buf[c],recip)) nomem(); while (!stralloc_0(&comm_buf[c])) nomem(); comm_pos[c] = 0;}void comm_selprep(nfds,wfds)int *nfds;fd_set *wfds;{ int c; for (c = 0;c < CHANNELS;++c) if (flagspawnalive[c]) if (comm_buf[c].s && comm_buf[c].len) { FD_SET(chanfdout[c],wfds); if (*nfds <= chanfdout[c]) *nfds = chanfdout[c] + 1; }}void comm_do(wfds)fd_set *wfds;{ int c; for (c = 0;c < CHANNELS;++c) if (flagspawnalive[c]) if (comm_buf[c].s && comm_buf[c].len) if (FD_ISSET(chanfdout[c],wfds)) { int w; int len; len = comm_buf[c].len; w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]); if (w <= 0) { if ((w == -1) && (errno == error_pipe)) spawndied(c); else continue; /* kernel select() bug; can't avoid busy-looping */ } else { comm_pos[c] += w; if (comm_pos[c] == len) comm_buf[c].len = 0; } }}/* this file is too long ------------------------------------------ CLEANUPS */int flagcleanup; /* if 1, cleanupdir is initialized and ready */readsubdir cleanupdir;datetime_sec cleanuptime;void cleanup_init(){ flagcleanup = 0; cleanuptime = now();}void cleanup_selprep(wakeup)datetime_sec *wakeup;{ if (flagcleanup) *wakeup = 0; if (*wakeup > cleanuptime) *wakeup = cleanuptime;}void cleanup_do(){ char ch; struct stat st; unsigned long id; if (!flagcleanup) { if (recent < cleanuptime) return; readsubdir_init(&cleanupdir,"mess",pausedir); flagcleanup = 1; } switch(readsubdir_next(&cleanupdir,&id)) { case 1: break; case 0: flagcleanup = 0; cleanuptime = recent + SLEEP_CLEANUP; default: return; } fnmake_mess(id); if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */ if (recent <= st.st_atime + OSSIFIED) return; fnmake_info(id); if (stat(fn.s,&st) == 0) return; if (errno != error_noent) return; fnmake_todo(id); if (stat(fn.s,&st) == 0) return; if (errno != error_noent) return; fnmake_foop(id); if (substdio_putflush(&sstoqc,fn.s,fn.len) == -1) { cleandied(); return; } if (substdio_get(&ssfromqc,&ch,1) != 1) { cleandied(); return; } if (ch != '+') log3("warning: qmail-clean unable to clean up ",fn.s,"\n");}/* this file is too long ----------------------------------- PRIORITY QUEUES */prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */prioq pqchan[CHANNELS] = { {0}, {0} };/* pqchan 0: -todo +info +local ?remote *//* pqchan 1: -todo +info ?local +remote */prioq pqfail = {0}; /* stat() failure; has to be pqadded again */void pqadd(id)unsigned long id;{ struct prioq_elt pe; struct prioq_elt pechan[CHANNELS]; int flagchan[CHANNELS]; struct stat st; int c;#define CHECKSTAT if (errno != error_noent) goto fail; fnmake_info(id); if (stat(fn.s,&st) == -1) { CHECKSTAT return; /* someone yanking our chain */ } fnmake_todo(id); if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */ CHECKSTAT for (c = 0;c < CHANNELS;++c) { fnmake_chanaddr(id,c); if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT } else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; } } for (c = 0;c < CHANNELS;++c) if (flagchan[c]) while (!prioq_insert(&pqchan[c],&pechan[c])) nomem(); for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; if (c == CHANNELS) { pe.id = id; pe.dt = now(); while (!prioq_insert(&pqdone,&pe)) nomem(); } return; fail: log3("warning: unable to stat ",fn.s,"; will try again later\n"); pe.id = id; pe.dt = now() + SLEEP_SYSFAIL; while (!prioq_insert(&pqfail,&pe)) nomem();}void pqstart(){ readsubdir rs; int x; unsigned long id; readsubdir_init(&rs,"info",pausedir); while (x = readsubdir_next(&rs,&id)) if (x > 0) pqadd(id);}void pqfinish(){ int c; struct prioq_elt pe; time_t ut[2]; /* XXX: more portable than utimbuf, but still worrisome */ for (c = 0;c < CHANNELS;++c) while (prioq_min(&pqchan[c],&pe)) { prioq_delmin(&pqchan[c]); fnmake_chanaddr(pe.id,c); ut[0] = ut[1] = pe.dt; if (utime(fn.s,ut) == -1) log3("warning: unable to utime ",fn.s,"; message will be retried too soon\n"); }}void pqrun(){ int c; int i; for (c = 0;c < CHANNELS;++c) if (pqchan[c].p) if (pqchan[c].len) for (i = 0;i < pqchan[c].len;++i) pqchan[c].p[i].dt = recent;}/* this file is too long ---------------------------------------------- JOBS */struct job { int refs; /* if 0, this struct is unused */ unsigned long id; int channel; datetime_sec retry; stralloc sender; int numtodo; int flaghiteof; int flagdying; };int numjobs;struct job *jo;void job_init(){ int j; while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem(); for (j = 0;j < numjobs;++j) { jo[j].refs = 0; jo[j].sender.s = 0; }}int job_avail(){ int j; for (j = 0;j < numjobs;++j) if (!jo[j].refs) return 1; return 0;}int job_open(id,channel)unsigned long id;int channel;{ int j; for (j = 0;j < numjobs;++j) if (!jo[j].refs) break; if (j == numjobs) return -1; jo[j].refs = 1; jo[j].id = id; jo[j].channel = channel; jo[j].numtodo = 0; jo[j].flaghiteof = 0; return j;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -