diff -r 5766d031ef25 -r b375914441b2 pristine/ext_todo-20030105.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pristine/ext_todo-20030105.patch Thu Nov 01 16:41:45 2007 +0100 @@ -0,0 +1,1238 @@ +diff -uN qmail-1.03/EXTTODO qmail-exttodo/EXTTODO +--- qmail-1.03/EXTTODO Thu Jan 1 01:00:00 1970 ++++ qmail-exttodo/EXTTODO Sun Jan 5 22:12:01 2003 +@@ -0,0 +1,114 @@ ++EXTTODO by Claudio Jeker and ++Andre Oppermann ++(c) 1998,1999,2000,2001,2002 Internet Business Solutions Ltd. ++ ++The EXTTODO patch is a part of the qmail-ldap patch. ++This patches for qmail come with NO WARRANTY. ++ ++These patches are under the BSD license. ++ ++RELEASE: 5. Jan. 2003 ++ ++EXTTODO: ++====================== ++ ++TOC: ++ WHAT DOES IT DO ++ INSTALL ++ CONFIG FILES ++ SETUP ++ BIG PICTURE ++ ++NEWS: ++ ++ This is the first release of the EXTTODO patch. ++ ++================================================================================ ++ ++WHAT DOES IT DO ++ ++ The exttodo patch addresses a problem known as the silly qmail (queue) ++ problem. This problem is found only on system with high injection rates. ++ ++ qmail with a big local and remote concurrency could deliver a tremendous ++ amount of messages but normally this can not be achieved because qmail-send ++ becomes a bottleneck on those high volumes servers. ++ qmail-send preprocesses all new messages before distributing them for local ++ or remote delivering. In one run qmail-send does one todo run but has the ++ ability to close multiple jobs. Because of this layout qmail-send can not ++ feed all the new available (local/remote) delivery slots and therefor it is ++ not possible to achieve the maximum throughput. ++ This would be a minor problem if one qmail-send run could be done in extreme ++ short time but because of many file system calls (fsync and (un)link) a todo ++ run is expensive and throttles the throughput. ++ ++ The exttodo patch tries to solve the problem by moving the todo routine into ++ an external program. This reduces the run time in qmail-send. ++ ++ exttodo adds a new program to qmail called qmail-todo. qmail-todo prepares ++ incoming messages for local and remote delivering (by creating info/ ++ local/ and remote/ and removing todo/). See also ++ INTERNALS. As next qmail-todo transmits the to qmail-send which will ++ add this message into the priority queue which schedules the message for ++ delivery. ++ ++INSTALL ++ ++ To enable the exttodo patch you need to define EXTERNAL_TODO while compiling ++ qmail(-ldap) this can be done with the -D flag of cc (e.g. cc -DEXTERNAL_TODO). ++ ++ NOTE: the exttodo patch can also be used on qmail systems without the ++ qmail-ldap patch. ++ ++================================================================================ ++ ++CONFIG FILES ++ ++ No additional control files are used or needed. ++ ++================================================================================ ++ ++SETUP ++ ++ qmail-todo will be started by qmail-start and therefor no additional setup ++ is needed. ++ ++ To verify that exttodo is running just check if qmail-todo is running. ++ ++================================================================================ ++ ++BIG PICTURE ++ ++ +-------+ +-------+ ++ | clean | | clean | ++ +--0-1--+ +--0-1--+ +-----------+ ++ trigger ^ | ^ | +->0,1 lspawn | ++ | | v | v / +-----------+ ++ +-------+ v +--2-3--+ +--5-6--+ / ++ | | | | 0<--7 1,2<-+ ++ | queue |--+--| todo | | send | ++ | | | | 1-->8 3,4<-+ ++ +-------+ +-------+ +---0---+ \ ++ | \ +-----------+ ++ v +->0,1 rspwan | ++ +---0---+ +-----------+ ++ | logger| ++ +-------+ ++ ++Communication between qmail-send and qmail-todo ++ ++todo -> send: ++ D[LRB]\0 ++ Start delivery for new message with id . ++ the character L, R or B defines the type ++ of delivery, local, remote or both respectively. ++ L\0 ++ Dump string to the logger without adding additional \n or similar. ++send -> todo: ++ H Got a SIGHUP reread ~/control/locals and ~/control/virtualdomains ++ X Quit ASAP. ++ ++qmail-todo sends "\0" terminated messages whereas qmail-send just send one ++character to qmail-todo. ++ ++ +diff -uN qmail-1.03/EXTTODO-INFO qmail-exttodo/EXTTODO-INFO +--- qmail-1.03/EXTTODO-INFO Thu Jan 1 01:00:00 1970 ++++ qmail-exttodo/EXTTODO-INFO Tue Apr 30 16:49:02 2002 +@@ -0,0 +1,11 @@ ++Files modified: ++Makefile ++EXTTODO ++FILES ++TARGETS ++qmail-send.c ++qmail-todo.c ++qmail-start.c ++hier.c ++install-big.c ++ +diff -uN qmail-1.03/FILES qmail-exttodo/FILES +--- qmail-1.03/FILES Mon Jun 15 12:53:16 1998 ++++ qmail-exttodo/FILES Mon Apr 22 13:59:28 2002 +@@ -431,3 +431,4 @@ + tcp-environ.5 + constmap.h + constmap.c ++qmail-todo.c +diff -uN qmail-1.03/Makefile qmail-exttodo/Makefile +--- qmail-1.03/Makefile Mon Jun 15 12:53:16 1998 ++++ qmail-exttodo/Makefile Mon Apr 22 14:55:59 2002 +@@ -1,5 +1,7 @@ + # Don't edit Makefile! Use conf-* for configuration. + ++DEFINES=-DEXTERNAL_TODO # use to enable external todo ++ + SHELL=/bin/sh + + default: it +@@ -703,7 +705,7 @@ + + hier.o: \ + compile hier.c auto_qmail.h auto_split.h auto_uids.h fmt.h fifo.h +- ./compile hier.c ++ ./compile $(DEFINES) hier.c + + home: \ + home.sh conf-qmail +@@ -755,7 +757,7 @@ + install-big.o: \ + compile install-big.c auto_qmail.h auto_split.h auto_uids.h fmt.h \ + fifo.h +- ./compile install-big.c ++ ./compile $(DEFINES) install-big.c + + install.o: \ + compile install.c substdio.h strerr.h error.h open.h readwrite.h \ +@@ -808,7 +810,7 @@ + forward preline condredirect bouncesaying except maildirmake \ + maildir2mbox maildirwatch qail elq pinq idedit install-big install \ + instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ +-binm3 binm3+df ++binm3 binm3+df qmail-todo + + load: \ + make-load warn-auto.sh systype +@@ -1509,7 +1511,7 @@ + scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \ + qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \ + fmtqfn.h readsubdir.h direntry.h +- ./compile qmail-send.c ++ ./compile $(DEFINES) qmail-send.c + + qmail-showctl: \ + load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \ +@@ -1574,7 +1576,7 @@ + + qmail-start.o: \ + compile qmail-start.c fd.h prot.h exit.h fork.h auto_uids.h +- ./compile qmail-start.c ++ ./compile $(DEFINES) qmail-start.c + + qmail-tcpok: \ + load qmail-tcpok.o open.a lock.a strerr.a substdio.a error.a str.a \ +@@ -1605,6 +1607,20 @@ + compile qmail-tcpto.c substdio.h subfd.h substdio.h auto_qmail.h \ + fmt.h ip.h lock.h error.h exit.h datetime.h now.h datetime.h + ./compile qmail-tcpto.c ++ ++qmail-todo: \ ++load qmail-todo.o control.o constmap.o trigger.o fmtqfn.o now.o \ ++readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a alloc.a \ ++substdio.a error.a str.a fs.a auto_qmail.o auto_split.o ++ ./load qmail-todo control.o constmap.o trigger.o fmtqfn.o now.o \ ++ readsubdir.o case.a ndelay.a getln.a sig.a open.a stralloc.a \ ++ alloc.a substdio.a error.a str.a fs.a auto_qmail.o auto_split.o ++ ++qmail-todo.o: \ ++compile alloc.h auto_qmail.h byte.h constmap.h control.h direntry.h error.h \ ++exit.h fmt.h fmtqfn.h getln.h open.h ndelay.h now.h readsubdir.h readwrite.h \ ++scan.h select.h str.h stralloc.h substdio.h trigger.h ++ ./compile $(DEFINES) qmail-todo.c + + qmail-upq: \ + warn-auto.sh qmail-upq.sh conf-qmail conf-break conf-split +diff -uN qmail-1.03/TARGETS qmail-exttodo/TARGETS +--- qmail-1.03/TARGETS Mon Jun 15 12:53:16 1998 ++++ qmail-exttodo/TARGETS Mon Apr 22 13:59:32 2002 +@@ -385,3 +385,5 @@ + man + setup + check ++qmail-todo.o ++qmail-todo +diff -uN qmail-1.03/hier.c qmail-exttodo/hier.c +--- qmail-1.03/hier.c Mon Jun 15 12:53:16 1998 ++++ qmail-exttodo/hier.c Mon Apr 22 14:01:58 2002 +@@ -108,6 +108,9 @@ + c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); ++#ifdef EXTERNAL_TODO ++ c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); ++#endif + c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); +diff -uN qmail-1.03/install-big.c qmail-exttodo/install-big.c +--- qmail-1.03/install-big.c Mon Jun 15 12:53:16 1998 ++++ qmail-exttodo/install-big.c Mon Apr 22 14:02:11 2002 +@@ -108,6 +108,9 @@ + c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711); ++#ifdef EXTERNAL_TODO ++ c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711); ++#endif + c(auto_qmail,"bin","splogger",auto_uido,auto_gidq,0711); + c(auto_qmail,"bin","qmail-newu",auto_uido,auto_gidq,0700); + c(auto_qmail,"bin","qmail-newmrh",auto_uido,auto_gidq,0700); +diff -uN qmail-1.03/qmail-send.c qmail-exttodo/qmail-send.c +--- qmail-1.03/qmail-send.c Mon Jun 15 12:53:16 1998 ++++ qmail-exttodo/qmail-send.c Sun Jan 5 22:09:42 2003 +@@ -1215,6 +1215,7 @@ + + /* this file is too long ---------------------------------------------- TODO */ + ++#ifndef EXTERNAL_TODO + datetime_sec nexttodorun; + DIR *tododir; /* if 0, have to opendir again */ + stralloc todoline = {0}; +@@ -1438,6 +1439,143 @@ + if (fdchan[c] != -1) close(fdchan[c]); + } + ++#endif ++ ++/* this file is too long ------------------------------------- EXTERNAL TODO */ ++ ++#ifdef EXTERNAL_TODO ++stralloc todoline = {0}; ++char todobuf[2048]; ++int todofdin; ++int todofdout; ++int flagtodoalive; ++ ++void tododied() { log1("alert: oh no! lost qmail-todo connection! dying...\n"); ++ flagexitasap = 1; flagtodoalive = 0; } ++ ++void todo_init() ++{ ++ todofdout = 7; ++ todofdin = 8; ++ flagtodoalive = 1; ++ /* sync with external todo */ ++ if (write(todofdout, "S", 1) != 1) tododied(); ++ ++ return; ++} ++ ++void todo_selprep(nfds,rfds,wakeup) ++int *nfds; ++fd_set *rfds; ++datetime_sec *wakeup; ++{ ++ if (flagexitasap) { ++ if (flagtodoalive) { ++ write(todofdout, "X", 1); ++ } ++ } ++ if (flagtodoalive) { ++ FD_SET(todofdin,rfds); ++ if (*nfds <= todofdin) ++ *nfds = todofdin + 1; ++ } ++} ++ ++void todo_del(char* s) ++{ ++ int flagchan[CHANNELS]; ++ struct prioq_elt pe; ++ unsigned long id; ++ unsigned int len; ++ int c; ++ ++ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; ++ switch(*s++) { ++ case 'L': ++ flagchan[0] = 1; ++ break; ++ case 'R': ++ flagchan[1] = 1; ++ break; ++ case 'B': ++ flagchan[0] = 1; ++ flagchan[1] = 1; ++ break; ++ case 'X': ++ break; ++ default: ++ log1("warning: qmail-send unable to understand qmail-todo\n"); ++ return; ++ } ++ ++ len = scan_ulong(s,&id); ++ if (!len || s[len]) { ++ log1("warning: qmail-send unable to understand qmail-todo\n"); ++ return; ++ } ++ ++ pe.id = id; pe.dt = now(); ++ for (c = 0;c < CHANNELS;++c) ++ if (flagchan[c]) ++ while (!prioq_insert(&pqchan[c],&pe)) nomem(); ++ ++ for (c = 0;c < CHANNELS;++c) if (flagchan[c]) break; ++ if (c == CHANNELS) ++ while (!prioq_insert(&pqdone,&pe)) nomem(); ++ ++ return; ++} ++ ++void todo_do(rfds) ++fd_set *rfds; ++{ ++ int r; ++ char ch; ++ int i; ++ ++ if (!flagtodoalive) return; ++ if (!FD_ISSET(todofdin,rfds)) return; ++ ++ r = read(todofdin,todobuf,sizeof(todobuf)); ++ if (r == -1) return; ++ if (r == 0) { ++ if (flagexitasap) ++ flagtodoalive = 0; ++ else ++ tododied(); ++ return; ++ } ++ for (i = 0;i < r;++i) { ++ ch = todobuf[i]; ++ while (!stralloc_append(&todoline,&ch)) nomem(); ++ if (todoline.len > REPORTMAX) ++ todoline.len = REPORTMAX; ++ /* qmail-todo is responsible for keeping it short */ ++ if (!ch && (todoline.len > 1)) { ++ switch (todoline.s[0]) { ++ case 'D': ++ if (flagexitasap) break; ++ todo_del(todoline.s + 1); ++ break; ++ case 'L': ++ log1(todoline.s + 1); ++ break; ++ case 'X': ++ if (flagexitasap) ++ flagtodoalive = 0; ++ else ++ tododied(); ++ break; ++ default: ++ log1("warning: qmail-send unable to understand qmail-todo: report mangled\n"); ++ break; ++ } ++ todoline.len = 0; ++ } ++ } ++} ++ ++#endif + + /* this file is too long ---------------------------------------------- MAIN */ + +@@ -1504,6 +1642,9 @@ + log1("alert: unable to reread controls: unable to switch to home directory\n"); + return; + } ++#ifdef EXTERNAL_TODO ++ write(todofdout, "H", 1); ++#endif + regetcontrols(); + while (chdir("queue") == -1) + { +@@ -1568,8 +1709,12 @@ + todo_init(); + cleanup_init(); + ++#ifdef EXTERNAL_TODO ++ while (!flagexitasap || !del_canexit() || flagtodoalive) ++#else + while (!flagexitasap || !del_canexit()) +- { ++#endif ++ { + recent = now(); + + if (flagrunasap) { flagrunasap = 0; pqrun(); } +diff -uN qmail-1.03/qmail-start.c qmail-exttodo/qmail-start.c +--- qmail-1.03/qmail-start.c Mon Jun 15 12:53:16 1998 ++++ qmail-exttodo/qmail-start.c Mon Apr 22 13:55:48 2002 +@@ -8,6 +8,9 @@ + char *(qcargs[]) = { "qmail-clean", 0 }; + char *(qlargs[]) = { "qmail-lspawn", "./Mailbox", 0 }; + char *(qrargs[]) = { "qmail-rspawn", 0 }; ++#ifdef EXTERNAL_TODO ++char *(qtargs[]) = { "qmail-todo", 0}; ++#endif + + void die() { _exit(111); } + +@@ -18,13 +21,28 @@ + int pi4[2]; + int pi5[2]; + int pi6[2]; +- +-void close23456() { close(2); close(3); close(4); close(5); close(6); } ++#ifdef EXTERNAL_TODO ++int pi7[2]; ++int pi8[2]; ++int pi9[2]; ++int pi10[2]; ++#endif ++ ++void close23456() { ++ close(2); close(3); close(4); close(5); close(6); ++#ifdef EXTERNAL_TODO ++ close(7); close(8); ++#endif ++} + + void closepipes() { + close(pi1[0]); close(pi1[1]); close(pi2[0]); close(pi2[1]); + close(pi3[0]); close(pi3[1]); close(pi4[0]); close(pi4[1]); + close(pi5[0]); close(pi5[1]); close(pi6[0]); close(pi6[1]); ++#ifdef EXTERNAL_TODO ++ close(pi7[0]); close(pi7[1]); close(pi8[0]); close(pi8[1]); ++ close(pi9[0]); close(pi9[1]); close(pi10[0]); close(pi10[1]); ++#endif + } + + void main(argc,argv) +@@ -40,6 +58,10 @@ + if (fd_copy(4,0) == -1) die(); + if (fd_copy(5,0) == -1) die(); + if (fd_copy(6,0) == -1) die(); ++#ifdef EXTERNAL_TODO ++ if (fd_copy(7,0) == -1) die(); ++ if (fd_copy(8,0) == -1) die(); ++#endif + + if (argv[1]) { + qlargs[1] = argv[1]; +@@ -70,6 +92,12 @@ + if (pipe(pi4) == -1) die(); + if (pipe(pi5) == -1) die(); + if (pipe(pi6) == -1) die(); ++#ifdef EXTERNAL_TODO ++ if (pipe(pi7) == -1) die(); ++ if (pipe(pi8) == -1) die(); ++ if (pipe(pi9) == -1) die(); ++ if (pipe(pi10) == -1) die(); ++#endif + + switch(fork()) { + case -1: die(); +@@ -105,6 +133,34 @@ + execvp(*qcargs,qcargs); + die(); + } ++ ++#ifdef EXTERNAL_TODO ++ switch(fork()) { ++ case -1: die(); ++ case 0: ++ if (prot_uid(auto_uids) == -1) die(); ++ if (fd_copy(0,pi7[0]) == -1) die(); ++ if (fd_copy(1,pi8[1]) == -1) die(); ++ close23456(); ++ if (fd_copy(2,pi9[1]) == -1) die(); ++ if (fd_copy(3,pi10[0]) == -1) die(); ++ closepipes(); ++ execvp(*qtargs,qtargs); ++ die(); ++ } ++ ++ switch(fork()) { ++ case -1: die(); ++ case 0: ++ if (prot_uid(auto_uidq) == -1) die(); ++ if (fd_copy(0,pi9[0]) == -1) die(); ++ if (fd_copy(1,pi10[1]) == -1) die(); ++ close23456(); ++ closepipes(); ++ execvp(*qcargs,qcargs); ++ die(); ++ } ++#endif + + if (prot_uid(auto_uids) == -1) die(); + if (fd_copy(0,1) == -1) die(); +@@ -114,6 +170,10 @@ + if (fd_copy(4,pi4[0]) == -1) die(); + if (fd_copy(5,pi5[1]) == -1) die(); + if (fd_copy(6,pi6[0]) == -1) die(); ++#ifdef EXTERNAL_TODO ++ if (fd_copy(7,pi7[1]) == -1) die(); ++ if (fd_copy(8,pi8[0]) == -1) die(); ++#endif + closepipes(); + execvp(*qsargs,qsargs); + die(); +diff -uN qmail-1.03/qmail-todo.c qmail-exttodo/qmail-todo.c +--- qmail-1.03/qmail-todo.c Thu Jan 1 01:00:00 1970 ++++ qmail-exttodo/qmail-todo.c Sun Jan 5 22:16:34 2003 +@@ -0,0 +1,688 @@ ++#include ++#include ++#include "alloc.h" ++#include "auto_qmail.h" ++#include "byte.h" ++#include "constmap.h" ++#include "control.h" ++#include "direntry.h" ++#include "error.h" ++#include "exit.h" ++#include "fmt.h" ++#include "fmtqfn.h" ++#include "getln.h" ++#include "open.h" ++#include "ndelay.h" ++#include "now.h" ++#include "readsubdir.h" ++#include "readwrite.h" ++#include "scan.h" ++#include "select.h" ++#include "str.h" ++#include "stralloc.h" ++#include "substdio.h" ++#include "trigger.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_SYSFAIL 123 ++ ++stralloc percenthack = {0}; ++struct constmap mappercenthack; ++stralloc locals = {0}; ++struct constmap maplocals; ++stralloc vdoms = {0}; ++struct constmap mapvdoms; ++stralloc envnoathost = {0}; ++ ++char strnum[FMT_ULONG]; ++ ++/* XXX not good, if qmail-send.c changes this has to be updated */ ++#define CHANNELS 2 ++char *chanaddr[CHANNELS] = { "local/", "remote/" }; ++ ++datetime_sec recent; ++ ++void log1(char *x); ++void log3(char* x, char* y, char* z); ++ ++int flagstopasap = 0; ++void sigterm(void) ++{ ++ if (flagstopasap == 0) ++ log1("status: qmail-todo stop processing asap\n"); ++ flagstopasap = 1; ++} ++ ++int flagreadasap = 0; void sighup(void) { flagreadasap = 1; } ++int flagsendalive = 1; void senddied(void) { flagsendalive = 0; } ++ ++void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); } ++void pausedir(dir) char *dir; ++{ log3("alert: unable to opendir ",dir,", sleeping...\n"); sleep(10); } ++ ++void cleandied() ++{ ++ log1("alert: qmail-todo: oh no! lost qmail-clean connection! dying...\n"); ++ flagstopasap = 1; ++} ++ ++ ++/* this file is not so long ------------------------------------- FILENAMES */ ++ ++stralloc fn = {0}; ++ ++void fnmake_init(void) ++{ ++ while (!stralloc_ready(&fn,FMTQFN)) nomem(); ++} ++ ++void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); } ++void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,0); } ++void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); } ++void fnmake_chanaddr(unsigned long id, int c) ++{ fn.len = fmtqfn(fn.s,chanaddr[c],id,1); } ++ ++ ++/* this file is not so 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(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; ++} ++ ++/* this file is not so long --------------------------------- COMMUNICATION */ ++ ++substdio sstoqc; char sstoqcbuf[1024]; ++substdio ssfromqc; char ssfromqcbuf[1024]; ++stralloc comm_buf = {0}; ++int comm_pos; ++int fdout = -1; ++int fdin = -1; ++ ++void comm_init(void) ++{ ++ substdio_fdbuf(&sstoqc,write,2,sstoqcbuf,sizeof(sstoqcbuf)); ++ substdio_fdbuf(&ssfromqc,read,3,ssfromqcbuf,sizeof(ssfromqcbuf)); ++ ++ fdout = 1; /* stdout */ ++ fdin = 0; /* stdin */ ++ if (ndelay_on(fdout) == -1) ++ /* this is so stupid: NDELAY semantics should be default on write */ ++ senddied(); /* drastic, but better than risking deadlock */ ++ ++ while (!stralloc_ready(&comm_buf,1024)) nomem(); ++} ++ ++int comm_canwrite(void) ++{ ++ /* XXX: could allow a bigger buffer; say 10 recipients */ ++ /* XXX: returns true if there is something in the buffer */ ++ if (!flagsendalive) return 0; ++ if (comm_buf.s && comm_buf.len) return 1; ++ return 0; ++} ++ ++void log1(char* x) ++{ ++ int pos; ++ ++ pos = comm_buf.len; ++ if (!stralloc_cats(&comm_buf,"L")) goto fail; ++ if (!stralloc_cats(&comm_buf,x)) goto fail; ++ if (!stralloc_0(&comm_buf)) goto fail; ++ return; ++ ++fail: ++ /* either all or nothing */ ++ comm_buf.len = pos; ++} ++ ++void log3(char* x, char *y, char *z) ++{ ++ int pos; ++ ++ pos = comm_buf.len; ++ if (!stralloc_cats(&comm_buf,"L")) goto fail; ++ if (!stralloc_cats(&comm_buf,x)) goto fail; ++ if (!stralloc_cats(&comm_buf,y)) goto fail; ++ if (!stralloc_cats(&comm_buf,z)) goto fail; ++ if (!stralloc_0(&comm_buf)) goto fail; ++ return; ++ ++fail: ++ /* either all or nothing */ ++ comm_buf.len = pos; ++} ++ ++void comm_write(unsigned long id, int local, int remote) ++{ ++ int pos; ++ char *s; ++ ++ if(local && remote) s="B"; ++ else if(local) s="L"; ++ else if(remote) s="R"; ++ else s="X"; ++ ++ pos = comm_buf.len; ++ strnum[fmt_ulong(strnum,id)] = 0; ++ if (!stralloc_cats(&comm_buf,"D")) goto fail; ++ if (!stralloc_cats(&comm_buf,s)) goto fail; ++ if (!stralloc_cats(&comm_buf,strnum)) goto fail; ++ if (!stralloc_0(&comm_buf)) goto fail; ++ return; ++ ++fail: ++ /* either all or nothing */ ++ comm_buf.len = pos; ++} ++ ++static int issafe(char ch) ++{ ++ if (ch == '%') return 0; /* general principle: allman's code is crap */ ++ if (ch < 33) return 0; ++ if (ch > 126) return 0; ++ return 1; ++} ++ ++void comm_info(unsigned long id, unsigned long size, char* from, unsigned long pid, unsigned long uid) ++{ ++ int pos; ++ int i; ++ ++ pos = comm_buf.len; ++ if (!stralloc_cats(&comm_buf,"Linfo msg ")) goto fail; ++ strnum[fmt_ulong(strnum,id)] = 0; ++ if (!stralloc_cats(&comm_buf,strnum)) goto fail; ++ if (!stralloc_cats(&comm_buf,": bytes ")) goto fail; ++ strnum[fmt_ulong(strnum,size)] = 0; ++ if (!stralloc_cats(&comm_buf,strnum)) goto fail; ++ if (!stralloc_cats(&comm_buf," from <")) goto fail; ++ i = comm_buf.len; ++ if (!stralloc_cats(&comm_buf,from)) goto fail; ++ for (;i < comm_buf.len;++i) ++ if (comm_buf.s[i] == '\n') ++ comm_buf.s[i] = '/'; ++ else ++ if (!issafe(comm_buf.s[i])) ++ comm_buf.s[i] = '_'; ++ if (!stralloc_cats(&comm_buf,"> qp ")) goto fail; ++ strnum[fmt_ulong(strnum,pid)] = 0; ++ if (!stralloc_cats(&comm_buf,strnum)) goto fail; ++ if (!stralloc_cats(&comm_buf," uid ")) goto fail; ++ strnum[fmt_ulong(strnum,uid)] = 0; ++ if (!stralloc_cats(&comm_buf,strnum)) goto fail; ++ if (!stralloc_cats(&comm_buf,"\n")) goto fail; ++ if (!stralloc_0(&comm_buf)) goto fail; ++ return; ++ ++fail: ++ /* either all or nothing */ ++ comm_buf.len = pos; ++} ++ ++void comm_exit(void) ++{ ++ int w; ++ ++ /* if it fails exit, we have already stoped */ ++ if (!stralloc_cats(&comm_buf,"X")) _exit(1); ++ if (!stralloc_0(&comm_buf)) _exit(1); ++} ++ ++void comm_selprep(int *nfds, fd_set *wfds, fd_set *rfds) ++{ ++ if (flagsendalive) { ++ if (flagstopasap && comm_canwrite() == 0) ++ comm_exit(); ++ if (comm_canwrite()) { ++ FD_SET(fdout,wfds); ++ if (*nfds <= fdout) ++ *nfds = fdout + 1; ++ } ++ FD_SET(fdin,rfds); ++ if (*nfds <= fdin) ++ *nfds = fdin + 1; ++ } ++} ++ ++void comm_do(fd_set *wfds, fd_set *rfds) ++{ ++ /* first write then read */ ++ if (flagsendalive) ++ if (comm_canwrite()) ++ if (FD_ISSET(fdout,wfds)) { ++ int w; ++ int len; ++ len = comm_buf.len; ++ w = write(fdout,comm_buf.s + comm_pos,len - comm_pos); ++ if (w <= 0) { ++ if ((w == -1) && (errno == error_pipe)) ++ senddied(); ++ } else { ++ comm_pos += w; ++ if (comm_pos == len) { ++ comm_buf.len = 0; ++ comm_pos = 0; ++ } ++ } ++ } ++ if (flagsendalive) ++ if (FD_ISSET(fdin,rfds)) { ++ /* there are only two messages 'H' and 'X' */ ++ char c; ++ int r; ++ r = read(fdin, &c, 1); ++ if (r <= 0) { ++ if ((r == -1) && (errno != error_intr)) ++ senddied(); ++ } else { ++ switch(c) { ++ case 'H': ++ sighup(); ++ break; ++ case 'X': ++ sigterm(); ++ break; ++ default: ++ log1("warning: qmail-todo: qmail-send speaks an obscure dialect\n"); ++ break; ++ } ++ } ++ } ++} ++ ++/* this file is not so long ------------------------------------------ TODO */ ++ ++datetime_sec nexttodorun; ++DIR *tododir; /* if 0, have to opendir again */ ++stralloc todoline = {0}; ++char todobuf[SUBSTDIO_INSIZE]; ++char todobufinfo[512]; ++char todobufchan[CHANNELS][1024]; ++ ++void todo_init(void) ++{ ++ tododir = 0; ++ nexttodorun = now(); ++ trigger_set(); ++} ++ ++void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup) ++{ ++ if (flagstopasap) return; ++ trigger_selprep(nfds,rfds); ++ if (tododir) *wakeup = 0; ++ if (*wakeup > nexttodorun) *wakeup = nexttodorun; ++} ++ ++void todo_do(fd_set *rfds) ++{ ++ struct stat st; ++ substdio ss; int fd; ++ substdio ssinfo; int fdinfo; ++ substdio sschan[CHANNELS]; ++ int fdchan[CHANNELS]; ++ int flagchan[CHANNELS]; ++ char ch; ++ int match; ++ unsigned long id; ++ unsigned int len; ++ direntry *d; ++ int c; ++ unsigned long uid; ++ unsigned long pid; ++ ++ fd = -1; ++ fdinfo = -1; ++ for (c = 0;c < CHANNELS;++c) fdchan[c] = -1; ++ ++ if (flagstopasap) return; ++ ++ if (!tododir) ++ { ++ if (!trigger_pulled(rfds)) ++ if (recent < nexttodorun) ++ return; ++ trigger_set(); ++ tododir = opendir("todo"); ++ if (!tododir) ++ { ++ pausedir("todo"); ++ return; ++ } ++ nexttodorun = recent + SLEEP_TODO; ++ } ++ ++ d = readdir(tododir); ++ if (!d) ++ { ++ closedir(tododir); ++ tododir = 0; ++ return; ++ } ++ if (str_equal(d->d_name,".")) return; ++ if (str_equal(d->d_name,"..")) return; ++ len = scan_ulong(d->d_name,&id); ++ if (!len || d->d_name[len]) return; ++ ++ fnmake_todo(id); ++ ++ fd = open_read(fn.s); ++ if (fd == -1) { log3("warning: qmail-todo: unable to open ",fn.s,"\n"); return; } ++ ++ fnmake_mess(id); ++ /* just for the statistics */ ++ if (stat(fn.s,&st) == -1) ++ { log3("warning: qmail-todo: unable to stat ",fn.s,"\n"); goto fail; } ++ ++ for (c = 0;c < CHANNELS;++c) ++ { ++ fnmake_chanaddr(id,c); ++ if (unlink(fn.s) == -1) if (errno != error_noent) ++ { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; } ++ } ++ ++ fnmake_info(id); ++ if (unlink(fn.s) == -1) if (errno != error_noent) ++ { log3("warning: qmail-todo: unable to unlink ",fn.s,"\n"); goto fail; } ++ ++ fdinfo = open_excl(fn.s); ++ if (fdinfo == -1) ++ { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; } ++ ++ strnum[fmt_ulong(strnum,id)] = 0; ++ log3("new msg ",strnum,"\n"); ++ ++ for (c = 0;c < CHANNELS;++c) flagchan[c] = 0; ++ ++ substdio_fdbuf(&ss,read,fd,todobuf,sizeof(todobuf)); ++ substdio_fdbuf(&ssinfo,write,fdinfo,todobufinfo,sizeof(todobufinfo)); ++ ++ uid = 0; ++ pid = 0; ++ ++ for (;;) ++ { ++ if (getln(&ss,&todoline,&match,'\0') == -1) ++ { ++ /* perhaps we're out of memory, perhaps an I/O error */ ++ fnmake_todo(id); ++ log3("warning: qmail-todo: trouble reading ",fn.s,"\n"); goto fail; ++ } ++ if (!match) break; ++ ++ switch(todoline.s[0]) ++ { ++ case 'u': ++ scan_ulong(todoline.s + 1,&uid); ++ break; ++ case 'p': ++ scan_ulong(todoline.s + 1,&pid); ++ break; ++ case 'F': ++ if (substdio_putflush(&ssinfo,todoline.s,todoline.len) == -1) ++ { ++ fnmake_info(id); ++ log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; ++ } ++ comm_info(id, (unsigned long) st.st_size, todoline.s + 1, pid, uid); ++ break; ++ case 'T': ++ switch(rewrite(todoline.s + 1)) ++ { ++ case 0: nomem(); goto fail; ++ case 2: c = 1; break; ++ default: c = 0; break; ++ } ++ if (fdchan[c] == -1) ++ { ++ fnmake_chanaddr(id,c); ++ fdchan[c] = open_excl(fn.s); ++ if (fdchan[c] == -1) ++ { log3("warning: qmail-todo: unable to create ",fn.s,"\n"); goto fail; } ++ substdio_fdbuf(&sschan[c] ++ ,write,fdchan[c],todobufchan[c],sizeof(todobufchan[c])); ++ flagchan[c] = 1; ++ } ++ if (substdio_bput(&sschan[c],rwline.s,rwline.len) == -1) ++ { ++ fnmake_chanaddr(id,c); ++ log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; ++ } ++ break; ++ default: ++ fnmake_todo(id); ++ log3("warning: qmail-todo: unknown record type in ",fn.s,"\n"); goto fail; ++ } ++ } ++ ++ close(fd); fd = -1; ++ ++ fnmake_info(id); ++ if (substdio_flush(&ssinfo) == -1) ++ { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; } ++ if (fsync(fdinfo) == -1) ++ { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; } ++ close(fdinfo); fdinfo = -1; ++ ++ for (c = 0;c < CHANNELS;++c) ++ if (fdchan[c] != -1) ++ { ++ fnmake_chanaddr(id,c); ++ if (substdio_flush(&sschan[c]) == -1) ++ { log3("warning: qmail-todo: trouble writing to ",fn.s,"\n"); goto fail; } ++ if (fsync(fdchan[c]) == -1) ++ { log3("warning: qmail-todo: trouble fsyncing ",fn.s,"\n"); goto fail; } ++ close(fdchan[c]); fdchan[c] = -1; ++ } ++ ++ fnmake_todo(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"); ++ return; ++ } ++ ++ comm_write(id, flagchan[0], flagchan[1]); ++ ++ return; ++ ++ fail: ++ if (fd != -1) close(fd); ++ if (fdinfo != -1) close(fdinfo); ++ for (c = 0;c < CHANNELS;++c) ++ if (fdchan[c] != -1) close(fdchan[c]); ++} ++ ++/* this file is too long ---------------------------------------------- MAIN */ ++ ++int getcontrols(void) ++{ ++ if (control_init() == -1) return 0; ++ if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0; ++ if (control_readfile(&locals,"control/locals",1) != 1) return 0; ++ if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0; ++ switch(control_readfile(&percenthack,"control/percenthack",0)) ++ { ++ case -1: return 0; ++ case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; ++ case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; ++ } ++ switch(control_readfile(&vdoms,"control/virtualdomains",0)) ++ { ++ case -1: return 0; ++ case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break; ++ case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break; ++ } ++ return 1; ++} ++ ++stralloc newlocals = {0}; ++stralloc newvdoms = {0}; ++ ++void regetcontrols(void) ++{ ++ int r; ++ ++ if (control_readfile(&newlocals,"control/locals",1) != 1) ++ { log1("alert: qmail-todo: unable to reread control/locals\n"); return; } ++ r = control_readfile(&newvdoms,"control/virtualdomains",0); ++ if (r == -1) ++ { log1("alert: qmail-todo: unable to reread control/virtualdomains\n"); return; } ++ ++ constmap_free(&maplocals); ++ constmap_free(&mapvdoms); ++ ++ while (!stralloc_copy(&locals,&newlocals)) nomem(); ++ while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem(); ++ ++ if (r) ++ { ++ while (!stralloc_copy(&vdoms,&newvdoms)) nomem(); ++ while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem(); ++ } ++ else ++ while (!constmap_init(&mapvdoms,"",0,1)) nomem(); ++} ++ ++void reread(void) ++{ ++ if (chdir(auto_qmail) == -1) ++ { ++ log1("alert: qmail-todo: unable to reread controls: unable to switch to home directory\n"); ++ return; ++ } ++ regetcontrols(); ++ while (chdir("queue") == -1) ++ { ++ log1("alert: qmail-todo: unable to switch back to queue directory; HELP! sleeping...\n"); ++ sleep(10); ++ } ++} ++ ++void main() ++{ ++ datetime_sec wakeup; ++ fd_set rfds; ++ fd_set wfds; ++ int nfds; ++ struct timeval tv; ++ int r; ++ char c; ++ ++ if (chdir(auto_qmail) == -1) ++ { log1("alert: qmail-todo: cannot start: unable to switch to home directory\n"); _exit(111); } ++ if (!getcontrols()) ++ { log1("alert: qmail-todo: cannot start: unable to read controls\n"); _exit(111); } ++ if (chdir("queue") == -1) ++ { log1("alert: qmail-todo: cannot start: unable to switch to queue directory\n"); _exit(111); } ++ sig_pipeignore(); ++ umask(077); ++ ++ fnmake_init(); ++ ++ todo_init(); ++ comm_init(); ++ ++ do { ++ r = read(fdin, &c, 1); ++ if ((r == -1) && (errno != error_intr)) ++ _exit(100); /* read failed probably qmail-send died */ ++ } while (r =! 1); /* we assume it is a 'S' */ ++ ++ for (;;) ++ { ++ recent = now(); ++ ++ if (flagreadasap) { flagreadasap = 0; reread(); } ++ if (!flagsendalive) { ++ /* qmail-send finaly exited, so do the same. */ ++ if (flagstopasap) _exit(0); ++ /* qmail-send died. We can not log and we can not work therefor _exit(1). */ ++ _exit(1); ++ } ++ ++ wakeup = recent + SLEEP_FOREVER; ++ FD_ZERO(&rfds); ++ FD_ZERO(&wfds); ++ nfds = 1; ++ ++ todo_selprep(&nfds,&rfds,&wakeup); ++ comm_selprep(&nfds,&wfds,&rfds); ++ ++ if (wakeup <= recent) tv.tv_sec = 0; ++ else tv.tv_sec = wakeup - recent + SLEEP_FUZZ; ++ tv.tv_usec = 0; ++ ++ if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1) ++ if (errno == error_intr) ++ ; ++ else ++ log1("warning: qmail-todo: trouble in select\n"); ++ else ++ { ++ recent = now(); ++ ++ todo_do(&rfds); ++ comm_do(&wfds, &rfds); ++ } ++ } ++ /* NOTREACHED */ ++} ++