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