chiark / gitweb /
Merge branches 'idx/verh' and 'idx/qmqpc'
[qmail] / spawn.c
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 }