|
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 } |