Commit | Line | Data |
---|---|---|
2117e02e MW |
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 | } |