diff options
Diffstat (limited to 'src/spawn.c')
-rw-r--r-- | src/spawn.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/spawn.c b/src/spawn.c new file mode 100644 index 0000000..effcb26 --- /dev/null +++ b/src/spawn.c @@ -0,0 +1,276 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "alloc.h" +#include "sig.h" +#include "wait.h" +#include "buffer.h" +#include "byte.h" +#include "str.h" +#include "stralloc.h" +#include "select.h" +#include "exit.h" +#include "fd.h" +#include "open.h" +#include "error.h" +#include "auto_qmail.h" +#include "auto_uids.h" +#include "auto_spawn.h" + +extern int truncreport; +extern int spawn(); +extern void report(); +extern void initialize(); + +struct delivery + { + int used; + int fdin; /* pipe input */ + int pid; /* zero if child is dead */ + int wstat; /* if !pid: status of child */ + int fdout; /* pipe output, -1 if !pid; delays eof until after death */ + stralloc output; + } +; + +struct delivery *d; + +void sigchld() +{ + int wstat; + int pid; + int i; + + while ((pid = wait_nohang(&wstat)) > 0) + for (i = 0; i < auto_spawn; ++i) + if (d[i].used) + if (d[i].pid == pid) { + close(d[i].fdout); d[i].fdout = -1; + d[i].wstat = wstat; d[i].pid = 0; + } +} + +int flagwriting = 1; + +ssize_t okwrite(int fd,char *buf,int n) +{ + int w; + if (!flagwriting) return n; + w = write(fd,buf,n); + if (w != -1) return w; + if (errno == EINTR) return -1; + flagwriting = 0; close(fd); + return n; +} + +int flagreading = 1; +char outbuf[1024]; +buffer bo; + +int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */ +int flagabort = 0; /* if 1, everything except delnum is garbage */ +int delnum; +stralloc messid = {0}; +stralloc sender = {0}; +stralloc recip = {0}; + +void err(char *s) +{ + char ch; + + ch = delnum; + buffer_put(&bo,&ch,1); + buffer_puts(&bo,s); + buffer_putflush(&bo,"",1); +} + +void docmd() +{ + int f; + int i; + int j; + int fdmess; + int pi[2]; + struct stat st; + + if (flagabort) { err("Zqmail-spawn: Out of memory. (#4.3.0)\n"); return; } + if (delnum < 0) { err("Zqmail-spawn: Internal error: delnum negative. (#4.3.5)\n"); return; } + if (delnum >= auto_spawn) { err("Zqmail-spawn: Internal error: delnum too big. (#4.3.5)\n"); return; } + if (d[delnum].used) { err("Zqmail-spawn: Internal error: delnum in use. (#4.3.5)\n"); return; } + + for (i = 0; i < messid.len; ++i) + if (messid.s[i]) + if (!i || (messid.s[i] != '/')) + if ((unsigned char) (messid.s[i] - '0') > 9) + { err("Dqmail-spawn: Internal error: messid has nonnumerics. (#5.3.5)\n"); return; } + + if (messid.len > 100) { err("Dqmail-spawn: Internal error: messid too long. (#5.3.5)\n"); return; } + if (!messid.s[0]) { err("Dqmail-spawn: Internal error: messid too short. (#5.3.5)\n"); return; } + + if (!stralloc_copys(&d[delnum].output,"")) + { err("Zqmail-spawn: Out of memory. (#4.3.0)\n"); return; } + + j = byte_rchr(recip.s,recip.len,'@'); + if (j >= recip.len) { err("DSorry, address must include host name. (#5.1.3)\n"); return; } + + fdmess = open_read(messid.s); + if (fdmess == -1) { err("Zqmail-spawn: Unable to open message. (#4.3.0)\n"); return; } + + if (fstat(fdmess,&st) == -1) + { close(fdmess); err("Zqmail-spawn: Unable to fstat message. (#4.3.0)\n"); return; } + if ((st.st_mode & S_IFMT) != S_IFREG) + { close(fdmess); err("ZSorry, message has wrong type. (#4.3.5)\n"); return; } + if (st.st_uid != auto_uidq) /* aaack! qmailq has to be trusted! */ + /* your security is already toast at this point. damage control... */ + { close(fdmess); err("ZSorry, message has wrong owner. (#4.3.5)\n"); return; } + + if (pipe(pi) == -1) { + if (errno == EFAULT) err("Zqmail-spawn: Unable to create pipe (wrong fildes). (#4.3.0)\n"); + else if (errno == EMFILE) err("Zqmail-spawn: Unable to create pipe (too many FDS). (#4.3.0)\n"); + else if (errno == ENFILE) err("Zqmail-spawn: Unable to create pipe (system file table full). (#4.3.0)\n"); + else if (errno == ENOMEM) err("Zqmail-spawn: Unable to create pipe (out of memory). (#4.3.0)\n"); + else err("Zqmail-spawn: Unable to create pipe (unkown reason). (#4.3.0)\n"); + close(fdmess); + return; + } + + fd_coe(pi[0]); + + f = spawn(fdmess,pi[1],sender.s,recip.s,j); + close(fdmess); + + if (f == -1) + { close(pi[0]); close(pi[1]); err("Zqmail-spawn: Unable to fork. (#4.3.0)\n"); return; } + + d[delnum].fdin = pi[0]; + d[delnum].fdout = pi[1]; fd_coe(pi[1]); + d[delnum].pid = f; + d[delnum].used = 1; +} + +char cmdbuf[1024]; + +void getcmd() +{ + int i; + int r; + char ch; + + r = read(0,cmdbuf,sizeof(cmdbuf)); + if (r == 0) + { flagreading = 0; return; } + if (r == -1) { + if (errno != EINTR) + flagreading = 0; + return; + } + + for (i = 0; i < r; ++i) { + ch = cmdbuf[i]; + switch (stage) { + case 0: + delnum = (unsigned int) (unsigned char) ch; + messid.len = 0; stage = 1; break; + case 1: + if (!stralloc_append(&messid,&ch)) flagabort = 1; + if (ch) break; + sender.len = 0; stage = 2; break; + case 2: + if (!stralloc_append(&sender,&ch)) flagabort = 1; + if (ch) break; + recip.len = 0; stage = 3; break; + case 3: + if (!stralloc_append(&recip,&ch)) flagabort = 1; + if (ch) break; + docmd(); + flagabort = 0; stage = 0; break; + } + } +} + +char inbuf[128]; + +int main(int argc,char **argv) +{ + char ch; + int i; + int r; + fd_set rfds; + int nfds; + + if (chdir(auto_qmail) == -1) _exit(110); + if (chdir("queue/mess") == -1) _exit(110); + if (!stralloc_copys(&messid,"")) _exit(111); + if (!stralloc_copys(&sender,"")) _exit(111); + if (!stralloc_copys(&recip,"")) _exit(111); + + d = (struct delivery *) alloc((auto_spawn + 10) * sizeof(struct delivery)); + if (!d) _exit(111); + + buffer_init(&bo,okwrite,1,outbuf,sizeof(outbuf)); + + sig_pipeignore(); + sig_childcatch(sigchld); + + initialize(argc,argv); + + ch = auto_spawn; + buffer_putflush(&bo,&ch,1); + + for (i = 0; i < auto_spawn; ++i) + { d[i].used = 0; d[i].output.s = 0; } + + for (;;) { + if (!flagreading) { + for (i = 0; i < auto_spawn; ++i) if (d[i].used) break; + if (i >= auto_spawn) _exit(0); + } + sig_childunblock(); + + FD_ZERO(&rfds); + if (flagreading) FD_SET(0,&rfds); + nfds = 1; + + for (i = 0; i < auto_spawn; ++i) + if (d[i].used) { + FD_SET(d[i].fdin,&rfds); + if (d[i].fdin >= nfds) + nfds = d[i].fdin + 1; + } + + r = select(nfds,&rfds,(fd_set *) 0,(fd_set *) 0,(struct timeval *) 0); + sig_childblock(); + + if (r != -1) { + if (flagreading) + if (FD_ISSET(0,&rfds)) getcmd(); + for (i = 0; i < auto_spawn; ++i) + if (d[i].used) + + if (FD_ISSET(d[i].fdin,&rfds)) { + r = read(d[i].fdin,inbuf,128); + if (r == -1) + continue; /* read error on a readable pipe? be serious */ + if (r == 0) { + ch = i; + buffer_put(&bo,&ch,1); + report(&bo,d[i].wstat,d[i].output.s,d[i].output.len); + buffer_put(&bo,"",1); + buffer_flush(&bo); + close(d[i].fdin); d[i].used = 0; + continue; + } + while (!stralloc_readyplus(&d[i].output,r)) + sleep(10); /*XXX*/ + byte_copy(d[i].output.s + d[i].output.len,r,inbuf); + d[i].output.len += r; + if (truncreport > 100) + if (d[i].output.len > truncreport) { + char *truncmess = "\nError report too long, sorry.\n"; + d[i].output.len = truncreport - str_len(truncmess) - 3; + stralloc_cats(&d[i].output,truncmess); + } + } + } + } +} |