spawn.c
changeset 0 068428edee47
equal deleted inserted replaced
-1:000000000000 0:068428edee47
       
     1 #include <sys/types.h>
       
     2 #include <sys/stat.h>
       
     3 #include "sig.h"
       
     4 #include "wait.h"
       
     5 #include "substdio.h"
       
     6 #include "byte.h"
       
     7 #include "str.h"
       
     8 #include "stralloc.h"
       
     9 #include "select.h"
       
    10 #include "exit.h"
       
    11 #include "coe.h"
       
    12 #include "open.h"
       
    13 #include "error.h"
       
    14 #include "auto_qmail.h"
       
    15 #include "auto_uids.h"
       
    16 #include "auto_spawn.h"
       
    17 
       
    18 extern int truncreport;
       
    19 extern int spawn();
       
    20 extern void report();
       
    21 extern void initialize();
       
    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 int okwrite(fd,buf,n) int fd; char *buf; int 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("Zqmail-spawn out of memory. (#4.3.0)\n"); return; }
       
    89  if (delnum < 0) { err("ZInternal error: delnum negative. (#4.3.5)\n"); return; }
       
    90  if (delnum >= auto_spawn) { err("ZInternal error: delnum too big. (#4.3.5)\n"); return; }
       
    91  if (d[delnum].used) { err("ZInternal error: delnum in use. (#4.3.5)\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("DInternal error: messid has nonnumerics. (#5.3.5)\n"); return; }
       
    97  if (messid.len > 100) { err("DInternal error: messid too long. (#5.3.5)\n"); return; }
       
    98  if (!messid.s[0]) { err("DInternal error: messid too short. (#5.3.5)\n"); return; }
       
    99 
       
   100  if (!stralloc_copys(&d[delnum].output,""))
       
   101   { err("Zqmail-spawn out of memory. (#4.3.0)\n"); return; }
       
   102 
       
   103  j = byte_rchr(recip.s,recip.len,'@');
       
   104  if (j >= recip.len) { err("DSorry, address must include host name. (#5.1.3)\n"); return; }
       
   105 
       
   106  fdmess = open_read(messid.s);
       
   107  if (fdmess == -1) { err("Zqmail-spawn unable to open message. (#4.3.0)\n"); return; }
       
   108 
       
   109  if (fstat(fdmess,&st) == -1)
       
   110   { close(fdmess); err("Zqmail-spawn unable to fstat message. (#4.3.0)\n"); return; }
       
   111  if ((st.st_mode & S_IFMT) != S_IFREG)
       
   112   { close(fdmess); err("ZSorry, message has wrong type. (#4.3.5)\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("ZSorry, message has wrong owner. (#4.3.5)\n"); return; }
       
   116 
       
   117  if (pipe(pi) == -1)
       
   118   { close(fdmess); err("Zqmail-spawn unable to create pipe. (#4.3.0)\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("Zqmail-spawn unable to fork. (#4.3.0)\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 void main(argc,argv)
       
   179 int argc;
       
   180 char **argv;
       
   181 {
       
   182  char ch;
       
   183  int i;
       
   184  int r;
       
   185  fd_set rfds;
       
   186  int nfds;
       
   187 
       
   188  if (chdir(auto_qmail) == -1) _exit(111);
       
   189  if (chdir("queue/mess") == -1) _exit(111);
       
   190  if (!stralloc_copys(&messid,"")) _exit(111);
       
   191  if (!stralloc_copys(&sender,"")) _exit(111);
       
   192  if (!stralloc_copys(&recip,"")) _exit(111);
       
   193 
       
   194  d = (struct delivery *) alloc((auto_spawn + 10) * sizeof(struct delivery));
       
   195  if (!d) _exit(111);
       
   196 
       
   197  substdio_fdbuf(&ssout,okwrite,1,outbuf,sizeof(outbuf));
       
   198 
       
   199  sig_pipeignore();
       
   200  sig_childcatch(sigchld);
       
   201 
       
   202  initialize(argc,argv);
       
   203 
       
   204  ch = auto_spawn; substdio_putflush(&ssout,&ch,1);
       
   205 
       
   206  for (i = 0;i < auto_spawn;++i) { d[i].used = 0; d[i].output.s = 0; }
       
   207 
       
   208  for (;;)
       
   209   {
       
   210    if (!flagreading)
       
   211     {
       
   212      for (i = 0;i < auto_spawn;++i) if (d[i].used) break;
       
   213      if (i >= auto_spawn) _exit(0);
       
   214     }
       
   215    sig_childunblock();
       
   216 
       
   217    FD_ZERO(&rfds);
       
   218    if (flagreading) FD_SET(0,&rfds);
       
   219    nfds = 1;
       
   220    for (i = 0;i < auto_spawn;++i) if (d[i].used)
       
   221     { FD_SET(d[i].fdin,&rfds); if (d[i].fdin >= nfds) nfds = d[i].fdin + 1; }
       
   222 
       
   223    r = select(nfds,&rfds,(fd_set *) 0,(fd_set *) 0,(struct timeval *) 0);
       
   224    sig_childblock();
       
   225 
       
   226    if (r != -1)
       
   227     {
       
   228      if (flagreading)
       
   229        if (FD_ISSET(0,&rfds))
       
   230 	 getcmd();
       
   231      for (i = 0;i < auto_spawn;++i) if (d[i].used)
       
   232        if (FD_ISSET(d[i].fdin,&rfds))
       
   233 	{
       
   234 	 r = read(d[i].fdin,inbuf,128);
       
   235 	 if (r == -1)
       
   236 	   continue; /* read error on a readable pipe? be serious */
       
   237 	 if (r == 0)
       
   238 	  {
       
   239            ch = i; substdio_put(&ssout,&ch,1);
       
   240 	   report(&ssout,d[i].wstat,d[i].output.s,d[i].output.len);
       
   241 	   substdio_put(&ssout,"",1);
       
   242 	   substdio_flush(&ssout);
       
   243 	   close(d[i].fdin); d[i].used = 0;
       
   244 	   continue;
       
   245 	  }
       
   246 	 while (!stralloc_readyplus(&d[i].output,r)) sleep(10); /*XXX*/
       
   247 	 byte_copy(d[i].output.s + d[i].output.len,r,inbuf);
       
   248 	 d[i].output.len += r;
       
   249 	 if (truncreport > 100)
       
   250 	   if (d[i].output.len > truncreport)
       
   251 	    {
       
   252 	     char *truncmess = "\nError report too long, sorry.\n";
       
   253 	     d[i].output.len = truncreport - str_len(truncmess) - 3;
       
   254 	     stralloc_cats(&d[i].output,truncmess);
       
   255 	    }
       
   256 	}
       
   257     }
       
   258   }
       
   259 }