nightmaremail

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

spawn.c (6417B)


      1 #include "spawn.h"
      2 
      3 #include <sys/types.h>
      4 #include <sys/stat.h>
      5 #include "sig.h"
      6 #include "wait.h"
      7 #include "substdio.h"
      8 #include "byte.h"
      9 #include "str.h"
     10 #include "alloc.h"
     11 #include "stralloc.h"
     12 #include "select.h"
     13 #include "exit.h"
     14 #include "coe.h"
     15 #include "open.h"
     16 #include "error.h"
     17 #include "auto_qmail.h"
     18 #include "auto_uids.h"
     19 #include "auto_spawn.h"
     20 
     21 uid_t auto_uidq;
     22 
     23 struct delivery
     24  {
     25   int used;
     26   int fdin; /* pipe input */
     27   int pid; /* zero if child is dead */
     28   int wstat; /* if !pid: status of child */
     29   int fdout; /* pipe output, -1 if !pid; delays eof until after death */
     30   stralloc output;
     31  }
     32 ;
     33 
     34 struct delivery *d;
     35 
     36 void sigchld()
     37 {
     38  int wstat;
     39  int pid;
     40  int i;
     41  while ((pid = wait_nohang(&wstat)) > 0)
     42    for (i = 0;i < auto_spawn;++i) if (d[i].used)
     43      if (d[i].pid == pid)
     44       {
     45        close(d[i].fdout); d[i].fdout = -1;
     46        d[i].wstat = wstat; d[i].pid = 0;
     47       }
     48 }
     49 
     50 int flagwriting = 1;
     51 
     52 ssize_t okwrite(int fd, const void *buf, size_t n)
     53 {
     54  int w;
     55  if (!flagwriting) return n;
     56  w = write(fd,buf,n);
     57  if (w != -1) return w;
     58  if (errno == error_intr) return -1;
     59  flagwriting = 0; close(fd);
     60  return n;
     61 }
     62 
     63 int flagreading = 1;
     64 char outbuf[1024]; substdio ssout;
     65 
     66 int stage = 0; /* reading 0:delnum 1:messid 2:sender 3:recip */
     67 int flagabort = 0; /* if 1, everything except delnum is garbage */
     68 int delnum;
     69 stralloc messid = {0};
     70 stralloc sender = {0};
     71 stralloc recip = {0};
     72 
     73 void err(s) char *s;
     74 {
     75  char ch; ch = delnum; substdio_put(&ssout,&ch,1);
     76  substdio_puts(&ssout,s); substdio_putflush(&ssout,"",1);
     77 }
     78 
     79 void docmd()
     80 {
     81  int f;
     82  int i;
     83  int j;
     84  int fdmess;
     85  int pi[2];
     86  struct stat st;
     87 
     88  if (flagabort) { err("Z4.3.0 qmail-spawn out of memory.\n"); return; }
     89  if (delnum < 0) { err("Z4.3.5 Internal error: delnum negative.\n"); return; }
     90  if (delnum >= auto_spawn) { err("Z4.3.5 Internal error: delnum too big.\n"); return; }
     91  if (d[delnum].used) { err("Z4.3.5 Internal error: delnum in use.\n"); return; }
     92  for (i = 0;i < messid.len;++i)
     93    if (messid.s[i])
     94      if (!i || (messid.s[i] != '/'))
     95        if ((unsigned char) (messid.s[i] - '0') > 9)
     96         { err("D5.3.5 Internal error: messid has nonnumerics.\n"); return; }
     97  if (messid.len > 100) { err("D5.3.5 Internal error: messid too long.\n"); return; }
     98  if (!messid.s[0]) { err("D5.3.5 Internal error: messid too short.\n"); return; }
     99 
    100  if (!stralloc_copys(&d[delnum].output,""))
    101   { err("Z4.3.0 qmail-spawn out of memory.\n"); return; }
    102 
    103  j = byte_rchr(recip.s,recip.len,'@');
    104  if (j >= recip.len) { err("D5.1.3 Sorry, address must include host name.\n"); return; }
    105 
    106  fdmess = open_read(messid.s);
    107  if (fdmess == -1) { err("Z4.3.0 qmail-spawn unable to open message.\n"); return; }
    108 
    109  if (fstat(fdmess,&st) == -1)
    110   { close(fdmess); err("Z4.3.0 qmail-spawn unable to fstat message.\n"); return; }
    111  if ((st.st_mode & S_IFMT) != S_IFREG)
    112   { close(fdmess); err("Z4.3.5 Sorry, message has wrong type.\n"); return; }
    113  if (st.st_uid != auto_uidq) /* aaack! qmailq has to be trusted! */
    114   /* your security is already toast at this point. damage control... */
    115   { close(fdmess); err("Z4.3.5 Sorry, message has wrong owner.\n"); return; }
    116 
    117  if (pipe(pi) == -1)
    118   { close(fdmess); err("Z4.3.0 qmail-spawn unable to create pipe.\n"); return; }
    119 
    120  coe(pi[0]);
    121 
    122  f = spawn(fdmess,pi[1],sender.s,recip.s,j);
    123  close(fdmess);
    124  if (f == -1)
    125   { close(pi[0]); close(pi[1]); err("Z4.3.0 qmail-spawn unable to fork.\n"); return; }
    126 
    127  d[delnum].fdin = pi[0];
    128  d[delnum].fdout = pi[1]; coe(pi[1]);
    129  d[delnum].pid = f;
    130  d[delnum].used = 1;
    131 }
    132 
    133 char cmdbuf[1024];
    134 
    135 void getcmd()
    136 {
    137  int i;
    138  int r;
    139  char ch;
    140 
    141  r = read(0,cmdbuf,sizeof(cmdbuf));
    142  if (r == 0)
    143   { flagreading = 0; return; }
    144  if (r == -1)
    145   {
    146    if (errno != error_intr)
    147      flagreading = 0;
    148    return;
    149   }
    150  
    151  for (i = 0;i < r;++i)
    152   {
    153    ch = cmdbuf[i];
    154    switch(stage)
    155     {
    156      case 0:
    157        delnum = (unsigned int) (unsigned char) ch;
    158        messid.len = 0; stage = 1; break;
    159      case 1:
    160        if (!stralloc_append(&messid,&ch)) flagabort = 1;
    161        if (ch) break;
    162        sender.len = 0; stage = 2; break;
    163      case 2:
    164        if (!stralloc_append(&sender,&ch)) flagabort = 1;
    165        if (ch) break;
    166        recip.len = 0; stage = 3; break;
    167      case 3:
    168        if (!stralloc_append(&recip,&ch)) flagabort = 1;
    169        if (ch) break;
    170        docmd();
    171        flagabort = 0; stage = 0; break;
    172     }
    173   }
    174 }
    175 
    176 char inbuf[128];
    177 
    178 int main(int argc, char **argv)
    179 {
    180  char ch;
    181  int i;
    182  int r;
    183  fd_set rfds;
    184  int nfds;
    185 
    186  if (chdir(auto_qmail) == -1) _exit(111);
    187  if (chdir("queue/mess") == -1) _exit(111);
    188  if (!stralloc_copys(&messid,"")) _exit(111);
    189  if (!stralloc_copys(&sender,"")) _exit(111);
    190  if (!stralloc_copys(&recip,"")) _exit(111);
    191 
    192  d = (struct delivery *) alloc((auto_spawn + 10) * sizeof(struct delivery));
    193  if (!d) _exit(111);
    194 
    195  substdio_fdbuf(&ssout,okwrite,1,outbuf,sizeof(outbuf));
    196 
    197  sig_pipeignore();
    198  sig_childcatch(sigchld);
    199 
    200  initialize(argc,argv);
    201 
    202  ch = auto_spawn; substdio_putflush(&ssout,&ch,1);
    203 
    204  for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; }
    205 
    206  for (;;)
    207   {
    208    if (!flagreading)
    209     {
    210      for (i = 0;i < auto_spawn;++i) if (d[i].used) break;
    211      if (i >= auto_spawn) _exit(0);
    212     }
    213    sig_childunblock();
    214 
    215    FD_ZERO(&rfds);
    216    if (flagreading) FD_SET(0,&rfds);
    217    nfds = 1;
    218    for (i = 0;i < auto_spawn;++i) if (d[i].used)
    219     { FD_SET(d[i].fdin,&rfds); if (d[i].fdin >= nfds) nfds = d[i].fdin + 1; }
    220 
    221    r = select(nfds,&rfds,NULL,NULL,NULL);
    222    sig_childblock();
    223 
    224    if (r != -1)
    225     {
    226      if (flagreading)
    227        if (FD_ISSET(0,&rfds))
    228 	 getcmd();
    229      for (i = 0;i < auto_spawn;++i) if (d[i].used)
    230        if (FD_ISSET(d[i].fdin,&rfds))
    231 	{
    232 	 r = read(d[i].fdin,inbuf,128);
    233 	 if (r == -1)
    234 	   continue; /* read error on a readable pipe? be serious */
    235 	 if (r == 0)
    236 	  {
    237            ch = i; substdio_put(&ssout,&ch,1);
    238 	   report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len);
    239 	   substdio_put(&ssout,"",1);
    240 	   substdio_flush(&ssout);
    241 	   close(d[i].fdin); d[i].used = 0;
    242 	   continue;
    243 	  }
    244 	 while (!stralloc_readyplus(&d[i].output,r)) sleep(10); /*XXX sleep until we can get memory*/
    245 	 byte_copy(d[i].output.s + d[i].output.len,r,inbuf);
    246 	 d[i].output.len += r;
    247 	 if (truncreport > 100)
    248 	   if (d[i].output.len > truncreport)
    249 	    {
    250 	     char *truncmess = "\nError report too long, sorry.\n";
    251 	     d[i].output.len = truncreport - str_len(truncmess) - 3;
    252 	     stralloc_cats(&d[i].output,truncmess);
    253 	    }
    254 	}
    255     }
    256   }
    257 }