Commit | Line | Data |
---|---|---|
b2a81223 | 1 | /* |
dc45ba8c | 2 | * Copyright (c) 1983 Eric P. Allman |
eb0bafab KB |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
bee79b64 | 5 | * |
417f7a11 | 6 | * %sccs.include.redist.c% |
bee79b64 | 7 | */ |
b2a81223 | 8 | |
81b7d258 | 9 | # include "sendmail.h" |
bee79b64 KB |
10 | |
11 | #ifndef lint | |
12 | #ifdef QUEUE | |
4ba6a6f4 | 13 | static char sccsid[] = "@(#)queue.c 8.16 (Berkeley) %G% (with queueing)"; |
bee79b64 | 14 | #else |
4ba6a6f4 | 15 | static char sccsid[] = "@(#)queue.c 8.16 (Berkeley) %G% (without queueing)"; |
bee79b64 KB |
16 | #endif |
17 | #endif /* not lint */ | |
18 | ||
81b7d258 | 19 | # include <errno.h> |
3fbc69d6 | 20 | # include <pwd.h> |
6feb509e | 21 | # include <dirent.h> |
4287d84d | 22 | |
76b607b2 EA |
23 | # ifdef QUEUE |
24 | ||
81b7d258 | 25 | /* |
7338e3d4 EA |
26 | ** Work queue. |
27 | */ | |
28 | ||
29 | struct work | |
30 | { | |
31 | char *w_name; /* name of control file */ | |
32 | long w_pri; /* priority of message, see below */ | |
f746582a | 33 | time_t w_ctime; /* creation time of message */ |
7338e3d4 EA |
34 | struct work *w_next; /* next in queue */ |
35 | }; | |
36 | ||
37 | typedef struct work WORK; | |
38 | ||
39 | WORK *WorkQ; /* queue of things to be done */ | |
40 | \f/* | |
81b7d258 EA |
41 | ** QUEUEUP -- queue a message up for future transmission. |
42 | ** | |
81b7d258 | 43 | ** Parameters: |
dd1fe05b | 44 | ** e -- the envelope to queue up. |
d0bd03ce EA |
45 | ** queueall -- if TRUE, queue all addresses, rather than |
46 | ** just those with the QQUEUEUP flag set. | |
7338e3d4 | 47 | ** announce -- if TRUE, tell when you are queueing up. |
81b7d258 EA |
48 | ** |
49 | ** Returns: | |
3620ad97 | 50 | ** none. |
81b7d258 EA |
51 | ** |
52 | ** Side Effects: | |
7338e3d4 | 53 | ** The current request are saved in a control file. |
3620ad97 | 54 | ** The queue file is left locked. |
81b7d258 EA |
55 | */ |
56 | ||
7338e3d4 | 57 | queueup(e, queueall, announce) |
dd1fe05b | 58 | register ENVELOPE *e; |
d0bd03ce | 59 | bool queueall; |
7338e3d4 | 60 | bool announce; |
81b7d258 | 61 | { |
2cce0c26 | 62 | char *qf; |
2cce0c26 | 63 | register FILE *tfp; |
81b7d258 | 64 | register HDR *h; |
d4f42161 | 65 | register ADDRESS *q; |
3620ad97 EA |
66 | int fd; |
67 | int i; | |
68 | bool newid; | |
22293c34 | 69 | register char *p; |
4db45bf1 | 70 | MAILER nullmailer; |
3620ad97 | 71 | char buf[MAXLINE], tf[MAXLINE]; |
81b7d258 | 72 | |
9ccf54c4 | 73 | /* |
11e3f8b5 | 74 | ** Create control file. |
9ccf54c4 | 75 | */ |
81b7d258 | 76 | |
3620ad97 | 77 | newid = (e->e_id == NULL); |
e3778690 EA |
78 | |
79 | /* if newid, queuename will create a locked qf file in e->lockfp */ | |
3620ad97 EA |
80 | strcpy(tf, queuename(e, 't')); |
81 | tfp = e->e_lockfp; | |
82 | if (tfp == NULL) | |
83 | newid = FALSE; | |
e3778690 EA |
84 | |
85 | /* if newid, just write the qf file directly (instead of tf file) */ | |
3620ad97 | 86 | if (newid) |
4287d84d | 87 | { |
3620ad97 EA |
88 | tfp = e->e_lockfp; |
89 | } | |
90 | else | |
91 | { | |
92 | /* get a locked tf file */ | |
b1c8be09 | 93 | for (i = 0; i < 128; i++) |
4287d84d | 94 | { |
3620ad97 EA |
95 | fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); |
96 | if (fd < 0) | |
4287d84d | 97 | { |
b1c8be09 EA |
98 | if (errno != EEXIST) |
99 | break; | |
100 | #ifdef LOG | |
101 | if (LogLevel > 0 && (i % 32) == 0) | |
102 | syslog(LOG_ALERT, "queueup: cannot create %s: %s", | |
103 | tf, errstring(errno)); | |
104 | #endif | |
105 | continue; | |
6680179d | 106 | } |
f8a31c8e | 107 | |
71936fbe | 108 | if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) |
3620ad97 | 109 | break; |
b1c8be09 EA |
110 | #ifdef LOG |
111 | else if (LogLevel > 0 && (i % 32) == 0) | |
112 | syslog(LOG_ALERT, "queueup: cannot lock %s: %s", | |
113 | tf, errstring(errno)); | |
114 | #endif | |
f8a31c8e | 115 | |
3620ad97 | 116 | close(fd); |
b1c8be09 EA |
117 | |
118 | if ((i % 32) == 31) | |
119 | { | |
120 | /* save the old temp file away */ | |
121 | (void) rename(tf, queuename(e, 'T')); | |
122 | } | |
123 | else | |
124 | sleep(i % 32); | |
6680179d | 125 | } |
dc2cbc87 | 126 | if (fd < 0) |
b1c8be09 | 127 | syserr("!queueup: cannot create temp file %s", tf); |
6680179d | 128 | |
3620ad97 EA |
129 | tfp = fdopen(fd, "w"); |
130 | } | |
81b7d258 | 131 | |
9678c96d | 132 | if (tTd(40, 1)) |
51458e80 | 133 | printf("\n>>>>> queueing %s >>>>>\n", e->e_id); |
81b7d258 | 134 | |
dd1fe05b EA |
135 | /* |
136 | ** If there is no data file yet, create one. | |
137 | */ | |
138 | ||
139 | if (e->e_df == NULL) | |
140 | { | |
141 | register FILE *dfp; | |
80482eb5 | 142 | extern putbody(); |
dd1fe05b | 143 | |
f4318163 EA |
144 | e->e_df = queuename(e, 'd'); |
145 | e->e_df = newstr(e->e_df); | |
3f9ac8ea RA |
146 | fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode); |
147 | if (fd < 0) | |
e0539260 | 148 | syserr("!queueup: cannot create %s", e->e_df); |
3f9ac8ea | 149 | dfp = fdopen(fd, "w"); |
2f6a8a78 | 150 | (*e->e_putbody)(dfp, FileMailer, e, NULL); |
bc854e30 | 151 | (void) xfclose(dfp, "queueup dfp", e->e_id); |
80482eb5 | 152 | e->e_putbody = putbody; |
dd1fe05b EA |
153 | } |
154 | ||
81b7d258 EA |
155 | /* |
156 | ** Output future work requests. | |
4e969be0 EA |
157 | ** Priority and creation time should be first, since |
158 | ** they are required by orderq. | |
81b7d258 EA |
159 | */ |
160 | ||
7338e3d4 EA |
161 | /* output message priority */ |
162 | fprintf(tfp, "P%ld\n", e->e_msgpriority); | |
163 | ||
64912e7e EA |
164 | /* output creation time */ |
165 | fprintf(tfp, "T%ld\n", e->e_ctime); | |
166 | ||
96bfbc2c EA |
167 | /* output type and name of data file */ |
168 | if (e->e_bodytype != NULL) | |
169 | fprintf(tfp, "B%s\n", e->e_bodytype); | |
2cce0c26 | 170 | fprintf(tfp, "D%s\n", e->e_df); |
81b7d258 | 171 | |
baa0c390 EA |
172 | /* message from envelope, if it exists */ |
173 | if (e->e_message != NULL) | |
174 | fprintf(tfp, "M%s\n", e->e_message); | |
175 | ||
c2bdb1dd EA |
176 | /* send various flag bits through */ |
177 | p = buf; | |
178 | if (bitset(EF_WARNING, e->e_flags)) | |
179 | *p++ = 'w'; | |
180 | if (bitset(EF_RESPONSE, e->e_flags)) | |
181 | *p++ = 'r'; | |
182 | *p++ = '\0'; | |
183 | if (buf[0] != '\0') | |
184 | fprintf(tfp, "F%s\n", buf); | |
185 | ||
a9bac7a9 | 186 | /* $r and $s and $_ macro values */ |
22293c34 EA |
187 | if ((p = macvalue('r', e)) != NULL) |
188 | fprintf(tfp, "$r%s\n", p); | |
189 | if ((p = macvalue('s', e)) != NULL) | |
190 | fprintf(tfp, "$s%s\n", p); | |
a9bac7a9 EA |
191 | if ((p = macvalue('_', e)) != NULL) |
192 | fprintf(tfp, "$_%s\n", p); | |
22293c34 | 193 | |
81b7d258 | 194 | /* output name of sender */ |
2cce0c26 | 195 | fprintf(tfp, "S%s\n", e->e_from.q_paddr); |
81b7d258 | 196 | |
ca4d0c0b | 197 | /* output list of error recipients */ |
31739b4e | 198 | printctladdr(NULL, NULL); |
ca4d0c0b EA |
199 | for (q = e->e_errorqueue; q != NULL; q = q->q_next) |
200 | { | |
bc854e30 | 201 | if (!bitset(QDONTSEND|QBADADDR, q->q_flags)) |
ca4d0c0b | 202 | { |
9cdb0b30 | 203 | printctladdr(q, tfp); |
ca4d0c0b EA |
204 | fprintf(tfp, "E%s\n", q->q_paddr); |
205 | } | |
206 | } | |
207 | ||
208 | /* output list of recipient addresses */ | |
dd1fe05b | 209 | for (q = e->e_sendqueue; q != NULL; q = q->q_next) |
81b7d258 | 210 | { |
374579b6 | 211 | if (bitset(QQUEUEUP, q->q_flags) || |
bc854e30 | 212 | (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))) |
4d237f16 | 213 | { |
9cdb0b30 | 214 | printctladdr(q, tfp); |
2cce0c26 | 215 | fprintf(tfp, "R%s\n", q->q_paddr); |
7338e3d4 EA |
216 | if (announce) |
217 | { | |
218 | e->e_to = q->q_paddr; | |
b6edea3d | 219 | message("queued"); |
68f7099c | 220 | if (LogLevel > 8) |
c0ae0b24 | 221 | logdelivery(NULL, NULL, "queued", e); |
7338e3d4 EA |
222 | e->e_to = NULL; |
223 | } | |
b9accadd EA |
224 | if (tTd(40, 1)) |
225 | { | |
226 | printf("queueing "); | |
227 | printaddr(q, FALSE); | |
228 | } | |
4d237f16 | 229 | } |
81b7d258 EA |
230 | } |
231 | ||
7338e3d4 EA |
232 | /* |
233 | ** Output headers for this message. | |
234 | ** Expand macros completely here. Queue run will deal with | |
235 | ** everything as absolute headers. | |
236 | ** All headers that must be relative to the recipient | |
237 | ** can be cracked later. | |
4db45bf1 EA |
238 | ** We set up a "null mailer" -- i.e., a mailer that will have |
239 | ** no effect on the addresses as they are output. | |
7338e3d4 EA |
240 | */ |
241 | ||
1dbda134 | 242 | bzero((char *) &nullmailer, sizeof nullmailer); |
68f7099c EA |
243 | nullmailer.m_re_rwset = nullmailer.m_rh_rwset = |
244 | nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; | |
281ef7f0 | 245 | nullmailer.m_eol = "\n"; |
4db45bf1 | 246 | |
2bee003d | 247 | define('g', "\201f", e); |
dd1fe05b | 248 | for (h = e->e_header; h != NULL; h = h->h_link) |
81b7d258 | 249 | { |
1dbda134 EA |
250 | extern bool bitzerop(); |
251 | ||
83e9910e | 252 | /* don't output null headers */ |
81b7d258 EA |
253 | if (h->h_value == NULL || h->h_value[0] == '\0') |
254 | continue; | |
83e9910e EA |
255 | |
256 | /* don't output resent headers on non-resent messages */ | |
257 | if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) | |
258 | continue; | |
259 | ||
260 | /* output this header */ | |
2cce0c26 | 261 | fprintf(tfp, "H"); |
83e9910e EA |
262 | |
263 | /* if conditional, output the set of conditions */ | |
1dbda134 EA |
264 | if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) |
265 | { | |
266 | int j; | |
267 | ||
0e306e7f | 268 | (void) putc('?', tfp); |
1dbda134 EA |
269 | for (j = '\0'; j <= '\177'; j++) |
270 | if (bitnset(j, h->h_mflags)) | |
0e306e7f EA |
271 | (void) putc(j, tfp); |
272 | (void) putc('?', tfp); | |
1dbda134 | 273 | } |
83e9910e EA |
274 | |
275 | /* output the header: expand macros, convert addresses */ | |
9416f3a0 EA |
276 | if (bitset(H_DEFAULT, h->h_flags)) |
277 | { | |
278 | (void) expand(h->h_value, buf, &buf[sizeof buf], e); | |
3ba0846a | 279 | fprintf(tfp, "%s: %s\n", h->h_field, buf); |
9416f3a0 | 280 | } |
4d237f16 | 281 | else if (bitset(H_FROM|H_RCPT, h->h_flags)) |
611b763d | 282 | { |
c2bdb1dd | 283 | bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); |
8e5c6745 EA |
284 | FILE *savetrace = TrafficLogFile; |
285 | ||
286 | TrafficLogFile = NULL; | |
c2bdb1dd EA |
287 | |
288 | if (bitset(H_FROM, h->h_flags)) | |
289 | oldstyle = FALSE; | |
290 | ||
291 | commaize(h, h->h_value, tfp, oldstyle, | |
a4076aed | 292 | &nullmailer, e); |
8e5c6745 EA |
293 | |
294 | TrafficLogFile = savetrace; | |
611b763d | 295 | } |
4d237f16 EA |
296 | else |
297 | fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); | |
81b7d258 EA |
298 | } |
299 | ||
300 | /* | |
301 | ** Clean up. | |
302 | */ | |
303 | ||
e6d43796 | 304 | fflush(tfp); |
b6212c1e | 305 | fsync(fileno(tfp)); |
e6d43796 EA |
306 | if (ferror(tfp)) |
307 | { | |
308 | if (newid) | |
309 | syserr("!552 Error writing control file %s", tf); | |
310 | else | |
311 | syserr("!452 Error writing control file %s", tf); | |
312 | } | |
313 | ||
3620ad97 EA |
314 | if (!newid) |
315 | { | |
e3778690 | 316 | /* rename (locked) tf to be (locked) qf */ |
3620ad97 EA |
317 | qf = queuename(e, 'q'); |
318 | if (rename(tf, qf) < 0) | |
319 | syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df); | |
e3778690 EA |
320 | |
321 | /* close and unlock old (locked) qf */ | |
3620ad97 | 322 | if (e->e_lockfp != NULL) |
bc854e30 | 323 | (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); |
3620ad97 EA |
324 | e->e_lockfp = tfp; |
325 | } | |
326 | else | |
327 | qf = tf; | |
6680179d | 328 | errno = 0; |
1a13ccd8 | 329 | |
9678c96d EA |
330 | # ifdef LOG |
331 | /* save log info */ | |
68f7099c | 332 | if (LogLevel > 79) |
a8f5bbff | 333 | syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); |
f3d8f6d6 | 334 | # endif /* LOG */ |
51458e80 EA |
335 | |
336 | if (tTd(40, 1)) | |
337 | printf("<<<<< done queueing %s <<<<<\n\n", e->e_id); | |
3620ad97 | 338 | return; |
81b7d258 | 339 | } |
d2213d6c EA |
340 | |
341 | printctladdr(a, tfp) | |
9cdb0b30 | 342 | register ADDRESS *a; |
d2213d6c EA |
343 | FILE *tfp; |
344 | { | |
9cdb0b30 EA |
345 | char *uname; |
346 | register struct passwd *pw; | |
347 | register ADDRESS *q; | |
348 | uid_t uid; | |
349 | static ADDRESS *lastctladdr; | |
350 | static uid_t lastuid; | |
d2213d6c | 351 | |
9cdb0b30 | 352 | /* initialization */ |
c6c403f7 | 353 | if (a == NULL || a->q_alias == NULL || tfp == NULL) |
d2213d6c | 354 | { |
31739b4e | 355 | if (lastctladdr != NULL && tfp != NULL) |
9cdb0b30 EA |
356 | fprintf(tfp, "C\n"); |
357 | lastctladdr = NULL; | |
358 | lastuid = 0; | |
d2213d6c EA |
359 | return; |
360 | } | |
9cdb0b30 EA |
361 | |
362 | /* find the active uid */ | |
363 | q = getctladdr(a); | |
364 | if (q == NULL) | |
365 | uid = 0; | |
366 | else | |
367 | uid = q->q_uid; | |
c6c403f7 | 368 | a = a->q_alias; |
9cdb0b30 EA |
369 | |
370 | /* check to see if this is the same as last time */ | |
371 | if (lastctladdr != NULL && uid == lastuid && | |
372 | strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) | |
373 | return; | |
374 | lastuid = uid; | |
375 | lastctladdr = a; | |
376 | ||
377 | if (uid == 0 || (pw = getpwuid(uid)) == NULL) | |
f7894324 | 378 | uname = ""; |
d2213d6c | 379 | else |
9cdb0b30 EA |
380 | uname = pw->pw_name; |
381 | ||
382 | fprintf(tfp, "C%s:%s\n", uname, a->q_paddr); | |
d2213d6c EA |
383 | } |
384 | ||
81b7d258 EA |
385 | \f/* |
386 | ** RUNQUEUE -- run the jobs in the queue. | |
387 | ** | |
388 | ** Gets the stuff out of the queue in some presumably logical | |
389 | ** order and processes them. | |
390 | ** | |
391 | ** Parameters: | |
2e3062fe EA |
392 | ** forkflag -- TRUE if the queue scanning should be done in |
393 | ** a child process. We double-fork so it is not our | |
394 | ** child and we don't have to clean up after it. | |
81b7d258 EA |
395 | ** |
396 | ** Returns: | |
397 | ** none. | |
398 | ** | |
399 | ** Side Effects: | |
400 | ** runs things in the mail queue. | |
401 | */ | |
402 | ||
ca4d0c0b EA |
403 | ENVELOPE QueueEnvelope; /* the queue run envelope */ |
404 | ||
405 | runqueue(forkflag) | |
2cf55cb7 | 406 | bool forkflag; |
81b7d258 | 407 | { |
ca4d0c0b EA |
408 | register ENVELOPE *e; |
409 | extern ENVELOPE BlankEnvelope; | |
4bc44f60 EA |
410 | |
411 | /* | |
412 | ** If no work will ever be selected, don't even bother reading | |
413 | ** the queue. | |
414 | */ | |
415 | ||
3620ad97 | 416 | CurrentLA = getla(); /* get load average */ |
3f9ac8ea | 417 | |
986065ef | 418 | if (shouldqueue(0L, curtime())) |
4bc44f60 EA |
419 | { |
420 | if (Verbose) | |
421 | printf("Skipping queue run -- load average too high\n"); | |
fe80f8e1 EA |
422 | if (forkflag && QueueIntvl != 0) |
423 | (void) setevent(QueueIntvl, runqueue, TRUE); | |
ca4d0c0b | 424 | return; |
4bc44f60 EA |
425 | } |
426 | ||
b6dffdf5 EA |
427 | /* |
428 | ** See if we want to go off and do other useful work. | |
429 | */ | |
2cf55cb7 EA |
430 | |
431 | if (forkflag) | |
432 | { | |
6d06102a EA |
433 | int pid; |
434 | ||
435 | pid = dofork(); | |
436 | if (pid != 0) | |
2cf55cb7 | 437 | { |
0df908a9 | 438 | extern void reapchild(); |
c8ad461a | 439 | |
6d06102a | 440 | /* parent -- pick up intermediate zombie */ |
c8ad461a | 441 | #ifndef SIGCHLD |
7338e3d4 | 442 | (void) waitfor(pid); |
f3d8f6d6 | 443 | #else /* SIGCHLD */ |
39270cfd | 444 | (void) setsignal(SIGCHLD, reapchild); |
f3d8f6d6 | 445 | #endif /* SIGCHLD */ |
c4442979 | 446 | if (QueueIntvl != 0) |
611b763d | 447 | (void) setevent(QueueIntvl, runqueue, TRUE); |
2cf55cb7 EA |
448 | return; |
449 | } | |
6d06102a | 450 | /* child -- double fork */ |
c8ad461a | 451 | #ifndef SIGCHLD |
6d06102a EA |
452 | if (fork() != 0) |
453 | exit(EX_OK); | |
f3d8f6d6 | 454 | #else /* SIGCHLD */ |
39270cfd | 455 | (void) setsignal(SIGCHLD, SIG_DFL); |
f3d8f6d6 | 456 | #endif /* SIGCHLD */ |
b6dffdf5 | 457 | } |
2e3062fe | 458 | |
3f9ac8ea | 459 | setproctitle("running queue: %s", QueueDir); |
2e3062fe | 460 | |
36a4e219 | 461 | # ifdef LOG |
68f7099c | 462 | if (LogLevel > 69) |
ca4d0c0b EA |
463 | syslog(LOG_DEBUG, "runqueue %s, pid=%d, forkflag=%d", |
464 | QueueDir, getpid(), forkflag); | |
f3d8f6d6 | 465 | # endif /* LOG */ |
81b7d258 | 466 | |
eb8af3ce EA |
467 | /* |
468 | ** Release any resources used by the daemon code. | |
469 | */ | |
470 | ||
471 | # ifdef DAEMON | |
472 | clrdaemon(); | |
f3d8f6d6 | 473 | # endif /* DAEMON */ |
eb8af3ce | 474 | |
ca4d0c0b EA |
475 | /* |
476 | ** Create ourselves an envelope | |
477 | */ | |
478 | ||
479 | CurEnv = &QueueEnvelope; | |
fda58daa | 480 | e = newenvelope(&QueueEnvelope, CurEnv); |
ca4d0c0b EA |
481 | e->e_flags = BlankEnvelope.e_flags; |
482 | ||
a78155b0 EA |
483 | /* |
484 | ** Make sure the alias database is open. | |
485 | */ | |
486 | ||
36b09297 | 487 | initmaps(FALSE, e); |
a78155b0 | 488 | |
b6dffdf5 EA |
489 | /* |
490 | ** Start making passes through the queue. | |
491 | ** First, read and sort the entire queue. | |
492 | ** Then, process the work in that order. | |
493 | ** But if you take too long, start over. | |
b6dffdf5 | 494 | */ |
81b7d258 | 495 | |
6d06102a | 496 | /* order the existing work requests */ |
8ff78b51 | 497 | (void) orderq(FALSE); |
291fa573 | 498 | |
6d06102a EA |
499 | /* process them once at a time */ |
500 | while (WorkQ != NULL) | |
2cf55cb7 | 501 | { |
6d06102a | 502 | WORK *w = WorkQ; |
2cf55cb7 | 503 | |
6d06102a | 504 | WorkQ = WorkQ->w_next; |
a7dc53bd EA |
505 | |
506 | /* | |
507 | ** Ignore jobs that are too expensive for the moment. | |
508 | */ | |
509 | ||
510 | if (shouldqueue(w->w_pri, w->w_ctime)) | |
511 | { | |
512 | if (Verbose) | |
513 | printf("\nSkipping %s\n", w->w_name + 2); | |
a7dc53bd | 514 | } |
1c791deb EA |
515 | else |
516 | { | |
d0268270 EA |
517 | pid_t pid; |
518 | extern pid_t dowork(); | |
519 | ||
520 | pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); | |
521 | errno = 0; | |
522 | (void) waitfor(pid); | |
1c791deb | 523 | } |
6d06102a EA |
524 | free(w->w_name); |
525 | free((char *) w); | |
2cf55cb7 | 526 | } |
37ea3cbf EA |
527 | |
528 | /* exit without the usual cleanup */ | |
4e20c4d2 EA |
529 | e->e_id = NULL; |
530 | finis(); | |
aba51985 EA |
531 | } |
532 | \f/* | |
81b7d258 EA |
533 | ** ORDERQ -- order the work queue. |
534 | ** | |
535 | ** Parameters: | |
2e3062fe EA |
536 | ** doall -- if set, include everything in the queue (even |
537 | ** the jobs that cannot be run because the load | |
538 | ** average is too high). Otherwise, exclude those | |
539 | ** jobs. | |
81b7d258 EA |
540 | ** |
541 | ** Returns: | |
e65b95c4 EA |
542 | ** The number of request in the queue (not necessarily |
543 | ** the number of requests in WorkQ however). | |
81b7d258 EA |
544 | ** |
545 | ** Side Effects: | |
546 | ** Sets WorkQ to the queue of available work, in order. | |
547 | */ | |
548 | ||
4e969be0 EA |
549 | # define NEED_P 001 |
550 | # define NEED_T 002 | |
d6c28d1a EA |
551 | # define NEED_R 004 |
552 | # define NEED_S 010 | |
81b7d258 | 553 | |
38d3b461 EA |
554 | # ifndef DIR |
555 | # define DIR FILE | |
556 | # define direct dir | |
557 | # define opendir(d) fopen(d, "r") | |
07a8f29b | 558 | # define readdir(f) ((fread(&dbuf, sizeof dbuf, 1, f) > 0) ? &dbuf : 0) |
38d3b461 EA |
559 | static struct dir dbuf; |
560 | # define closedir(f) fclose(f) | |
561 | # endif DIR | |
562 | ||
2e3062fe EA |
563 | orderq(doall) |
564 | bool doall; | |
81b7d258 | 565 | { |
9114f86c | 566 | register struct dirent *d; |
81b7d258 | 567 | register WORK *w; |
ccd1d833 | 568 | DIR *f; |
81b7d258 | 569 | register int i; |
4e969be0 | 570 | WORK wlist[QUEUESIZE+1]; |
0490b7d7 | 571 | int wn = -1; |
81b7d258 | 572 | extern workcmpf(); |
81b7d258 | 573 | |
d6c28d1a EA |
574 | if (tTd(41, 1)) |
575 | { | |
576 | printf("orderq:\n"); | |
577 | if (QueueLimitId != NULL) | |
578 | printf("\tQueueLimitId = %s\n", QueueLimitId); | |
579 | if (QueueLimitSender != NULL) | |
580 | printf("\tQueueLimitSender = %s\n", QueueLimitSender); | |
581 | if (QueueLimitRecipient != NULL) | |
582 | printf("\tQueueLimitRecipient = %s\n", QueueLimitRecipient); | |
583 | } | |
584 | ||
81b7d258 EA |
585 | /* clear out old WorkQ */ |
586 | for (w = WorkQ; w != NULL; ) | |
587 | { | |
588 | register WORK *nw = w->w_next; | |
589 | ||
590 | WorkQ = nw; | |
591 | free(w->w_name); | |
592 | free((char *) w); | |
593 | w = nw; | |
594 | } | |
595 | ||
596 | /* open the queue directory */ | |
6bbaf971 | 597 | f = opendir("."); |
81b7d258 EA |
598 | if (f == NULL) |
599 | { | |
6bbaf971 | 600 | syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); |
0490b7d7 | 601 | return (0); |
81b7d258 EA |
602 | } |
603 | ||
604 | /* | |
605 | ** Read the work directory. | |
606 | */ | |
607 | ||
0490b7d7 | 608 | while ((d = readdir(f)) != NULL) |
81b7d258 | 609 | { |
81b7d258 | 610 | FILE *cf; |
7338e3d4 | 611 | char lbuf[MAXNAME]; |
d6c28d1a | 612 | extern bool strcontainedin(); |
81b7d258 EA |
613 | |
614 | /* is this an interesting entry? */ | |
07a8f29b EA |
615 | if (d->d_ino == 0) |
616 | continue; | |
617 | # ifdef DEBUG | |
618 | if (tTd(40, 10)) | |
619 | printf("orderq: %12s\n", d->d_name); | |
620 | # endif DEBUG | |
2cce0c26 | 621 | if (d->d_name[0] != 'q' || d->d_name[1] != 'f') |
81b7d258 EA |
622 | continue; |
623 | ||
d6c28d1a EA |
624 | if (QueueLimitId != NULL && |
625 | !strcontainedin(QueueLimitId, d->d_name)) | |
626 | continue; | |
627 | ||
45ce4b34 EA |
628 | /* |
629 | ** Check queue name for plausibility. This handles | |
630 | ** both old and new type ids. | |
631 | */ | |
632 | ||
633 | i = strlen(d->d_name); | |
634 | if (i != 9 && i != 10) | |
68f7099c EA |
635 | { |
636 | if (Verbose) | |
637 | printf("orderq: bogus qf name %s\n", d->d_name); | |
638 | #ifdef LOG | |
639 | if (LogLevel > 3) | |
73f6752a | 640 | syslog(LOG_CRIT, "orderq: bogus qf name %s", |
68f7099c EA |
641 | d->d_name); |
642 | #endif | |
643 | if (strlen(d->d_name) >= MAXNAME) | |
644 | d->d_name[MAXNAME - 1] = '\0'; | |
645 | strcpy(lbuf, d->d_name); | |
646 | lbuf[0] = 'Q'; | |
647 | (void) rename(d->d_name, lbuf); | |
648 | continue; | |
649 | } | |
650 | ||
0490b7d7 | 651 | /* yes -- open control file (if not too many files) */ |
4e969be0 | 652 | if (++wn >= QUEUESIZE) |
0490b7d7 | 653 | continue; |
d6c28d1a | 654 | |
6bbaf971 | 655 | cf = fopen(d->d_name, "r"); |
81b7d258 EA |
656 | if (cf == NULL) |
657 | { | |
91f69adf EA |
658 | /* this may be some random person sending hir msgs */ |
659 | /* syserr("orderq: cannot open %s", cbuf); */ | |
bd0712bd EA |
660 | if (tTd(41, 2)) |
661 | printf("orderq: cannot open %s (%d)\n", | |
662 | d->d_name, errno); | |
91f69adf | 663 | errno = 0; |
bd0712bd | 664 | wn--; |
81b7d258 EA |
665 | continue; |
666 | } | |
4e969be0 EA |
667 | w = &wlist[wn]; |
668 | w->w_name = newstr(d->d_name); | |
81b7d258 | 669 | |
4ff794a3 | 670 | /* make sure jobs in creation don't clog queue */ |
4e969be0 EA |
671 | w->w_pri = 0x7fffffff; |
672 | w->w_ctime = 0; | |
4ff794a3 | 673 | |
81b7d258 | 674 | /* extract useful information */ |
4e969be0 | 675 | i = NEED_P | NEED_T; |
d6c28d1a EA |
676 | if (QueueLimitSender != NULL) |
677 | i |= NEED_S; | |
678 | if (QueueLimitRecipient != NULL) | |
679 | i |= NEED_R; | |
4e969be0 | 680 | while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) |
81b7d258 | 681 | { |
8ff78b51 | 682 | extern long atol(); |
d6c28d1a | 683 | extern bool strcontainedin(); |
8ff78b51 | 684 | |
2e3062fe | 685 | switch (lbuf[0]) |
81b7d258 | 686 | { |
2e3062fe | 687 | case 'P': |
4e969be0 EA |
688 | w->w_pri = atol(&lbuf[1]); |
689 | i &= ~NEED_P; | |
2e3062fe | 690 | break; |
f746582a EA |
691 | |
692 | case 'T': | |
4e969be0 EA |
693 | w->w_ctime = atol(&lbuf[1]); |
694 | i &= ~NEED_T; | |
f746582a | 695 | break; |
d6c28d1a EA |
696 | |
697 | case 'R': | |
698 | if (QueueLimitRecipient != NULL && | |
699 | strcontainedin(QueueLimitRecipient, &lbuf[1])) | |
700 | i &= ~NEED_R; | |
701 | break; | |
702 | ||
703 | case 'S': | |
704 | if (QueueLimitSender != NULL && | |
705 | strcontainedin(QueueLimitSender, &lbuf[1])) | |
706 | i &= ~NEED_S; | |
707 | break; | |
81b7d258 EA |
708 | } |
709 | } | |
81b7d258 | 710 | (void) fclose(cf); |
4bc44f60 | 711 | |
d6c28d1a EA |
712 | if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || |
713 | bitset(NEED_R|NEED_S, i)) | |
4bc44f60 EA |
714 | { |
715 | /* don't even bother sorting this job in */ | |
716 | wn--; | |
717 | } | |
81b7d258 | 718 | } |
ccd1d833 | 719 | (void) closedir(f); |
bd0712bd | 720 | wn++; |
81b7d258 EA |
721 | |
722 | /* | |
723 | ** Sort the work directory. | |
724 | */ | |
725 | ||
4e969be0 | 726 | qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); |
81b7d258 EA |
727 | |
728 | /* | |
729 | ** Convert the work list into canonical form. | |
7338e3d4 | 730 | ** Should be turning it into a list of envelopes here perhaps. |
81b7d258 EA |
731 | */ |
732 | ||
a0225d08 | 733 | WorkQ = NULL; |
4e969be0 | 734 | for (i = min(wn, QUEUESIZE); --i >= 0; ) |
81b7d258 EA |
735 | { |
736 | w = (WORK *) xalloc(sizeof *w); | |
737 | w->w_name = wlist[i].w_name; | |
81b7d258 | 738 | w->w_pri = wlist[i].w_pri; |
f746582a | 739 | w->w_ctime = wlist[i].w_ctime; |
a0225d08 EA |
740 | w->w_next = WorkQ; |
741 | WorkQ = w; | |
81b7d258 EA |
742 | } |
743 | ||
9678c96d | 744 | if (tTd(40, 1)) |
81b7d258 EA |
745 | { |
746 | for (w = WorkQ; w != NULL; w = w->w_next) | |
9ccf54c4 | 747 | printf("%32s: pri=%ld\n", w->w_name, w->w_pri); |
81b7d258 | 748 | } |
0490b7d7 | 749 | |
bd0712bd | 750 | return (wn); |
81b7d258 EA |
751 | } |
752 | \f/* | |
9678c96d | 753 | ** WORKCMPF -- compare function for ordering work. |
81b7d258 EA |
754 | ** |
755 | ** Parameters: | |
756 | ** a -- the first argument. | |
757 | ** b -- the second argument. | |
758 | ** | |
759 | ** Returns: | |
a0225d08 EA |
760 | ** -1 if a < b |
761 | ** 0 if a == b | |
762 | ** +1 if a > b | |
81b7d258 EA |
763 | ** |
764 | ** Side Effects: | |
765 | ** none. | |
766 | */ | |
767 | ||
81b7d258 | 768 | workcmpf(a, b) |
9ccf54c4 EA |
769 | register WORK *a; |
770 | register WORK *b; | |
81b7d258 | 771 | { |
abccefc9 EA |
772 | long pa = a->w_pri; |
773 | long pb = b->w_pri; | |
2e3062fe EA |
774 | |
775 | if (pa == pb) | |
81b7d258 | 776 | return (0); |
2e3062fe | 777 | else if (pa > pb) |
e65b95c4 | 778 | return (1); |
a0225d08 EA |
779 | else |
780 | return (-1); | |
81b7d258 EA |
781 | } |
782 | \f/* | |
783 | ** DOWORK -- do a work request. | |
784 | ** | |
785 | ** Parameters: | |
a7dc53bd EA |
786 | ** id -- the ID of the job to run. |
787 | ** forkflag -- if set, run this in background. | |
f5303697 EA |
788 | ** requeueflag -- if set, reinstantiate the queue quickly. |
789 | ** This is used when expanding aliases in the queue. | |
8d00c219 EA |
790 | ** If forkflag is also set, it doesn't wait for the |
791 | ** child. | |
a7dc53bd | 792 | ** e - the envelope in which to run it. |
81b7d258 EA |
793 | ** |
794 | ** Returns: | |
d0268270 | 795 | ** process id of process that is running the queue job. |
81b7d258 EA |
796 | ** |
797 | ** Side Effects: | |
798 | ** The work request is satisfied if possible. | |
799 | */ | |
800 | ||
d0268270 | 801 | pid_t |
f5303697 | 802 | dowork(id, forkflag, requeueflag, e) |
a7dc53bd EA |
803 | char *id; |
804 | bool forkflag; | |
f5303697 | 805 | bool requeueflag; |
a4076aed | 806 | register ENVELOPE *e; |
81b7d258 | 807 | { |
d0268270 | 808 | register pid_t pid; |
3620ad97 | 809 | extern bool readqf(); |
81b7d258 | 810 | |
9678c96d | 811 | if (tTd(40, 1)) |
a7dc53bd | 812 | printf("dowork(%s)\n", id); |
81b7d258 | 813 | |
2e3062fe EA |
814 | /* |
815 | ** Fork for work. | |
816 | */ | |
817 | ||
a7dc53bd | 818 | if (forkflag) |
2e3062fe | 819 | { |
d0268270 EA |
820 | pid = fork(); |
821 | if (pid < 0) | |
2e3062fe EA |
822 | { |
823 | syserr("dowork: cannot fork"); | |
d0268270 | 824 | return 0; |
2e3062fe EA |
825 | } |
826 | } | |
827 | else | |
828 | { | |
d0268270 | 829 | pid = 0; |
2e3062fe EA |
830 | } |
831 | ||
d0268270 | 832 | if (pid == 0) |
81b7d258 EA |
833 | { |
834 | /* | |
835 | ** CHILD | |
6bbaf971 EA |
836 | ** Lock the control file to avoid duplicate deliveries. |
837 | ** Then run the file as though we had just read it. | |
68f0b54c EA |
838 | ** We save an idea of the temporary name so we |
839 | ** can recover on interrupt. | |
81b7d258 EA |
840 | */ |
841 | ||
9416f3a0 | 842 | /* set basic modes, etc. */ |
37eaaadb | 843 | (void) alarm(0); |
a4076aed | 844 | clearenvelope(e, FALSE); |
c2bdb1dd | 845 | e->e_flags |= EF_QUEUERUN; |
8c8e8e94 | 846 | e->e_errormode = EM_MAIL; |
a7dc53bd | 847 | e->e_id = id; |
47e2f242 | 848 | if (forkflag) |
d0268270 | 849 | disconnect(1, e); |
36a4e219 | 850 | # ifdef LOG |
68f7099c | 851 | if (LogLevel > 76) |
a4076aed | 852 | syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id, |
291fa573 | 853 | getpid()); |
f3d8f6d6 | 854 | # endif /* LOG */ |
9416f3a0 EA |
855 | |
856 | /* don't use the headers from sendmail.cf... */ | |
a4076aed | 857 | e->e_header = NULL; |
9416f3a0 | 858 | |
3620ad97 | 859 | /* read the queue control file -- return if locked */ |
c6c403f7 | 860 | if (!readqf(e, !requeueflag)) |
dd1fe05b | 861 | { |
a7dc53bd EA |
862 | if (tTd(40, 4)) |
863 | printf("readqf(%s) failed\n", e->e_id); | |
864 | if (forkflag) | |
2e3062fe EA |
865 | exit(EX_OK); |
866 | else | |
867 | return; | |
dd1fe05b | 868 | } |
dd1fe05b | 869 | |
a4076aed | 870 | e->e_flags |= EF_INQUEUE; |
959cf51d | 871 | eatheader(e, requeueflag); |
dd1fe05b | 872 | |
f5303697 EA |
873 | if (requeueflag) |
874 | queueup(e, TRUE, FALSE); | |
875 | ||
dd1fe05b | 876 | /* do the delivery */ |
e678ee95 | 877 | sendall(e, SM_DELIVER); |
dd1fe05b | 878 | |
dd1fe05b | 879 | /* finish up and exit */ |
a7dc53bd | 880 | if (forkflag) |
2e3062fe EA |
881 | finis(); |
882 | else | |
a4076aed | 883 | dropenvelope(e); |
81b7d258 | 884 | } |
d0268270 EA |
885 | e->e_id = NULL; |
886 | return pid; | |
81b7d258 EA |
887 | } |
888 | \f/* | |
889 | ** READQF -- read queue file and set up environment. | |
890 | ** | |
891 | ** Parameters: | |
7338e3d4 | 892 | ** e -- the envelope of the job to run. |
c6c403f7 EA |
893 | ** announcefile -- if set, announce the name of the queue |
894 | ** file in error messages. | |
81b7d258 EA |
895 | ** |
896 | ** Returns: | |
3620ad97 EA |
897 | ** TRUE if it successfully read the queue file. |
898 | ** FALSE otherwise. | |
81b7d258 EA |
899 | ** |
900 | ** Side Effects: | |
3620ad97 | 901 | ** The queue file is returned locked. |
81b7d258 EA |
902 | */ |
903 | ||
3620ad97 | 904 | bool |
c6c403f7 | 905 | readqf(e, announcefile) |
7338e3d4 | 906 | register ENVELOPE *e; |
c6c403f7 | 907 | bool announcefile; |
81b7d258 | 908 | { |
11e3f8b5 | 909 | register FILE *qfp; |
d2213d6c | 910 | ADDRESS *ctladdr; |
dcbc04c7 | 911 | struct stat st; |
07b49560 | 912 | char *bp; |
e678ee95 | 913 | char qf[20]; |
07b49560 | 914 | char buf[MAXLINE]; |
8ff78b51 | 915 | extern long atol(); |
d2213d6c | 916 | extern ADDRESS *setctluser(); |
abae7b2d | 917 | extern ADDRESS *sendto(); |
81b7d258 | 918 | |
81b7d258 EA |
919 | /* |
920 | ** Read and process the file. | |
921 | */ | |
922 | ||
e678ee95 | 923 | strcpy(qf, queuename(e, 'q')); |
76b607b2 | 924 | qfp = fopen(qf, "r+"); |
11e3f8b5 EA |
925 | if (qfp == NULL) |
926 | { | |
a7dc53bd EA |
927 | if (tTd(40, 8)) |
928 | printf("readqf(%s): fopen failure (%s)\n", | |
929 | qf, errstring(errno)); | |
3f9ac8ea RA |
930 | if (errno != ENOENT) |
931 | syserr("readqf: no control file %s", qf); | |
3620ad97 | 932 | return FALSE; |
3f9ac8ea RA |
933 | } |
934 | ||
71936fbe | 935 | if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) |
e3778690 EA |
936 | { |
937 | /* being processed by another queuer */ | |
938 | if (tTd(40, 8)) | |
939 | printf("readqf(%s): locked\n", qf); | |
940 | if (Verbose) | |
941 | printf("%s: locked\n", e->e_id); | |
942 | # ifdef LOG | |
943 | if (LogLevel > 19) | |
944 | syslog(LOG_DEBUG, "%s: locked", e->e_id); | |
945 | # endif /* LOG */ | |
946 | (void) fclose(qfp); | |
947 | return FALSE; | |
948 | } | |
949 | ||
dcbc04c7 EA |
950 | /* |
951 | ** Check the queue file for plausibility to avoid attacks. | |
952 | */ | |
953 | ||
954 | if (fstat(fileno(qfp), &st) < 0) | |
955 | { | |
956 | /* must have been being processed by someone else */ | |
a7dc53bd EA |
957 | if (tTd(40, 8)) |
958 | printf("readqf(%s): fstat failure (%s)\n", | |
959 | qf, errstring(errno)); | |
dcbc04c7 EA |
960 | fclose(qfp); |
961 | return FALSE; | |
962 | } | |
963 | ||
d4d2f9a9 | 964 | if (st.st_uid != geteuid()) |
dcbc04c7 EA |
965 | { |
966 | # ifdef LOG | |
967 | if (LogLevel > 0) | |
968 | { | |
969 | syslog(LOG_ALERT, "%s: bogus queue file, uid=%d, mode=%o", | |
970 | e->e_id, st.st_uid, st.st_mode); | |
971 | } | |
f3d8f6d6 | 972 | # endif /* LOG */ |
a7dc53bd EA |
973 | if (tTd(40, 8)) |
974 | printf("readqf(%s): bogus file\n", qf); | |
8e5c6745 | 975 | rename(qf, queuename(e, 'Q')); |
e3778690 | 976 | fclose(qfp); |
dcbc04c7 EA |
977 | return FALSE; |
978 | } | |
979 | ||
e3778690 | 980 | if (st.st_size == 0) |
3f9ac8ea | 981 | { |
e3778690 EA |
982 | /* must be a bogus file -- just remove it */ |
983 | (void) unlink(qf); | |
984 | fclose(qfp); | |
3620ad97 | 985 | return FALSE; |
11e3f8b5 | 986 | } |
3f9ac8ea | 987 | |
e3778690 | 988 | if (st.st_nlink == 0) |
c87034b7 | 989 | { |
e3778690 EA |
990 | /* |
991 | ** Race condition -- we got a file just as it was being | |
992 | ** unlinked. Just assume it is zero length. | |
993 | */ | |
994 | ||
c87034b7 EA |
995 | fclose(qfp); |
996 | return FALSE; | |
997 | } | |
998 | ||
e3778690 | 999 | /* good file -- save this lock */ |
3620ad97 EA |
1000 | e->e_lockfp = qfp; |
1001 | ||
3f9ac8ea | 1002 | /* do basic system initialization */ |
a4076aed | 1003 | initsys(e); |
3f9ac8ea | 1004 | |
c6c403f7 EA |
1005 | if (announcefile) |
1006 | FileName = qf; | |
560a80d9 | 1007 | LineNumber = 0; |
c6c403f7 EA |
1008 | e->e_flags |= EF_GLOBALERRS; |
1009 | OpMode = MD_DELIVER; | |
3620ad97 | 1010 | if (Verbose) |
7338e3d4 | 1011 | printf("\nRunning %s\n", e->e_id); |
d2213d6c | 1012 | ctladdr = NULL; |
07b49560 | 1013 | while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) |
81b7d258 | 1014 | { |
c2bdb1dd | 1015 | register char *p; |
65b5adb6 EA |
1016 | struct stat st; |
1017 | ||
795590e2 | 1018 | if (tTd(40, 4)) |
07b49560 EA |
1019 | printf("+++++ %s\n", bp); |
1020 | switch (bp[0]) | |
81b7d258 | 1021 | { |
3fbc69d6 | 1022 | case 'C': /* specify controlling user */ |
07b49560 | 1023 | ctladdr = setctluser(&bp[1]); |
3fbc69d6 KB |
1024 | break; |
1025 | ||
81b7d258 | 1026 | case 'R': /* specify recipient */ |
abae7b2d | 1027 | (void) sendto(&buf[1], 1, (ADDRESS *) NULL, 0); |
81b7d258 EA |
1028 | break; |
1029 | ||
4e969be0 | 1030 | case 'E': /* specify error recipient */ |
1c7897ef | 1031 | (void) sendtolist(&bp[1], ctladdr, &e->e_errorqueue, e); |
4e969be0 EA |
1032 | break; |
1033 | ||
81b7d258 | 1034 | case 'H': /* header */ |
07b49560 | 1035 | (void) chompheader(&bp[1], FALSE, e); |
81b7d258 EA |
1036 | break; |
1037 | ||
baa0c390 | 1038 | case 'M': /* message */ |
07b49560 | 1039 | e->e_message = newstr(&bp[1]); |
baa0c390 EA |
1040 | break; |
1041 | ||
81b7d258 | 1042 | case 'S': /* sender */ |
4a2da288 | 1043 | setsender(newstr(&bp[1]), e, NULL, TRUE); |
81b7d258 EA |
1044 | break; |
1045 | ||
96bfbc2c EA |
1046 | case 'B': /* body type */ |
1047 | e->e_bodytype = newstr(&bp[1]); | |
1048 | break; | |
1049 | ||
81b7d258 | 1050 | case 'D': /* data file name */ |
07b49560 | 1051 | e->e_df = newstr(&bp[1]); |
912acb74 EA |
1052 | e->e_dfp = fopen(e->e_df, "r"); |
1053 | if (e->e_dfp == NULL) | |
68f7099c | 1054 | { |
7338e3d4 | 1055 | syserr("readqf: cannot open %s", e->e_df); |
68f7099c EA |
1056 | e->e_msgsize = -1; |
1057 | } | |
1058 | else if (fstat(fileno(e->e_dfp), &st) >= 0) | |
65b5adb6 | 1059 | e->e_msgsize = st.st_size; |
81b7d258 EA |
1060 | break; |
1061 | ||
96476cab | 1062 | case 'T': /* init time */ |
07b49560 | 1063 | e->e_ctime = atol(&bp[1]); |
81b7d258 EA |
1064 | break; |
1065 | ||
aba51985 | 1066 | case 'P': /* message priority */ |
07b49560 | 1067 | e->e_msgpriority = atol(&bp[1]) + WkTimeFact; |
dd1fe05b EA |
1068 | break; |
1069 | ||
c2bdb1dd EA |
1070 | case 'F': /* flag bits */ |
1071 | for (p = &bp[1]; *p != '\0'; p++) | |
1072 | { | |
1073 | switch (*p) | |
1074 | { | |
1075 | case 'w': /* warning sent */ | |
1076 | e->e_flags |= EF_WARNING; | |
1077 | break; | |
1078 | ||
1079 | case 'r': /* response */ | |
1080 | e->e_flags |= EF_RESPONSE; | |
1081 | break; | |
1082 | } | |
1083 | } | |
1084 | break; | |
1085 | ||
22293c34 | 1086 | case '$': /* define macro */ |
07b49560 | 1087 | define(bp[1], newstr(&bp[2]), e); |
22293c34 EA |
1088 | break; |
1089 | ||
2e3062fe EA |
1090 | case '\0': /* blank line; ignore */ |
1091 | break; | |
1092 | ||
81b7d258 | 1093 | default: |
ef3bc6b1 | 1094 | syserr("readqf: bad line \"%s\"", e->e_id, |
07b49560 | 1095 | LineNumber, bp); |
8e5c6745 EA |
1096 | fclose(qfp); |
1097 | rename(qf, queuename(e, 'Q')); | |
1098 | return FALSE; | |
81b7d258 | 1099 | } |
07b49560 EA |
1100 | |
1101 | if (bp != buf) | |
1102 | free(bp); | |
81b7d258 | 1103 | } |
7338e3d4 EA |
1104 | |
1105 | FileName = NULL; | |
2e3062fe EA |
1106 | |
1107 | /* | |
1108 | ** If we haven't read any lines, this queue file is empty. | |
1109 | ** Arrange to remove it without referencing any null pointers. | |
1110 | */ | |
1111 | ||
1112 | if (LineNumber == 0) | |
1113 | { | |
1114 | errno = 0; | |
1115 | e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; | |
1116 | } | |
3620ad97 | 1117 | return TRUE; |
81b7d258 EA |
1118 | } |
1119 | \f/* | |
64912e7e EA |
1120 | ** PRINTQUEUE -- print out a representation of the mail queue |
1121 | ** | |
1122 | ** Parameters: | |
1123 | ** none. | |
1124 | ** | |
1125 | ** Returns: | |
1126 | ** none. | |
1127 | ** | |
1128 | ** Side Effects: | |
1129 | ** Prints a listing of the mail queue on the standard output. | |
1130 | */ | |
1131 | ||
1132 | printqueue() | |
1133 | { | |
1134 | register WORK *w; | |
1135 | FILE *f; | |
0490b7d7 | 1136 | int nrequests; |
64912e7e EA |
1137 | char buf[MAXLINE]; |
1138 | ||
374579b6 EA |
1139 | /* |
1140 | ** Check for permission to print the queue | |
1141 | */ | |
1142 | ||
fd178efd | 1143 | if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) |
374579b6 EA |
1144 | { |
1145 | struct stat st; | |
3afbc0f1 EA |
1146 | # ifdef NGROUPS |
1147 | int n; | |
e6cb9fc4 | 1148 | GIDSET_T gidset[NGROUPS]; |
3afbc0f1 | 1149 | # endif |
374579b6 | 1150 | |
7c8b3fd4 | 1151 | if (stat(QueueDir, &st) < 0) |
374579b6 EA |
1152 | { |
1153 | syserr("Cannot stat %s", QueueDir); | |
1154 | return; | |
1155 | } | |
3afbc0f1 EA |
1156 | # ifdef NGROUPS |
1157 | n = getgroups(NGROUPS, gidset); | |
1158 | while (--n >= 0) | |
1159 | { | |
1160 | if (gidset[n] == st.st_gid) | |
1161 | break; | |
1162 | } | |
1163 | if (n < 0) | |
1164 | # else | |
fd57f063 | 1165 | if (RealGid != st.st_gid) |
3afbc0f1 | 1166 | # endif |
374579b6 EA |
1167 | { |
1168 | usrerr("510 You are not permitted to see the queue"); | |
1169 | setstat(EX_NOPERM); | |
1170 | return; | |
1171 | } | |
1172 | } | |
1173 | ||
64912e7e EA |
1174 | /* |
1175 | ** Read and order the queue. | |
1176 | */ | |
1177 | ||
2e3062fe | 1178 | nrequests = orderq(TRUE); |
64912e7e EA |
1179 | |
1180 | /* | |
1181 | ** Print the work list that we have read. | |
1182 | */ | |
1183 | ||
1184 | /* first see if there is anything */ | |
0490b7d7 | 1185 | if (nrequests <= 0) |
64912e7e | 1186 | { |
0490b7d7 | 1187 | printf("Mail queue is empty\n"); |
64912e7e EA |
1188 | return; |
1189 | } | |
1190 | ||
3620ad97 | 1191 | CurrentLA = getla(); /* get load average */ |
3f9ac8ea | 1192 | |
d7b5594c | 1193 | printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); |
4e969be0 EA |
1194 | if (nrequests > QUEUESIZE) |
1195 | printf(", only %d printed", QUEUESIZE); | |
f4b05990 | 1196 | if (Verbose) |
fbab5052 | 1197 | printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); |
f4b05990 | 1198 | else |
fbab5052 | 1199 | printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); |
64912e7e EA |
1200 | for (w = WorkQ; w != NULL; w = w->w_next) |
1201 | { | |
1202 | struct stat st; | |
0490b7d7 EA |
1203 | auto time_t submittime = 0; |
1204 | long dfsize = -1; | |
c2bdb1dd | 1205 | int flags = 0; |
baa0c390 | 1206 | char message[MAXLINE]; |
96bfbc2c | 1207 | char bodytype[MAXNAME]; |
64912e7e | 1208 | |
4ba6a6f4 | 1209 | printf("%8s", w->w_name + 2); |
560a80d9 EA |
1210 | f = fopen(w->w_name, "r"); |
1211 | if (f == NULL) | |
1212 | { | |
4ba6a6f4 | 1213 | printf(" (job completed)\n"); |
560a80d9 EA |
1214 | errno = 0; |
1215 | continue; | |
1216 | } | |
71936fbe | 1217 | if (!lockfile(fileno(f), w->w_name, NULL, LOCK_SH|LOCK_NB)) |
0490b7d7 | 1218 | printf("*"); |
abccefc9 | 1219 | else if (shouldqueue(w->w_pri, w->w_ctime)) |
2e3062fe | 1220 | printf("X"); |
0490b7d7 EA |
1221 | else |
1222 | printf(" "); | |
1223 | errno = 0; | |
560a80d9 | 1224 | |
96bfbc2c | 1225 | message[0] = bodytype[0] = '\0'; |
64912e7e EA |
1226 | while (fgets(buf, sizeof buf, f) != NULL) |
1227 | { | |
22293c34 | 1228 | register int i; |
c2bdb1dd | 1229 | register char *p; |
22293c34 | 1230 | |
64912e7e EA |
1231 | fixcrlf(buf, TRUE); |
1232 | switch (buf[0]) | |
1233 | { | |
baa0c390 | 1234 | case 'M': /* error message */ |
22293c34 | 1235 | if ((i = strlen(&buf[1])) >= sizeof message) |
c2bdb1dd | 1236 | i = sizeof message - 1; |
22293c34 EA |
1237 | bcopy(&buf[1], message, i); |
1238 | message[i] = '\0'; | |
baa0c390 EA |
1239 | break; |
1240 | ||
96bfbc2c EA |
1241 | case 'B': /* body type */ |
1242 | if ((i = strlen(&buf[1])) >= sizeof bodytype) | |
1243 | i = sizeof bodytype - 1; | |
1244 | bcopy(&buf[1], bodytype, i); | |
1245 | bodytype[i] = '\0'; | |
1246 | break; | |
1247 | ||
64912e7e | 1248 | case 'S': /* sender name */ |
f4b05990 | 1249 | if (Verbose) |
c2bdb1dd EA |
1250 | printf("%8ld %10ld%c%.12s %.38s", |
1251 | dfsize, | |
1252 | w->w_pri, | |
1253 | bitset(EF_WARNING, flags) ? '+' : ' ', | |
1254 | ctime(&submittime) + 4, | |
f4b05990 EA |
1255 | &buf[1]); |
1256 | else | |
1257 | printf("%8ld %.16s %.45s", dfsize, | |
1258 | ctime(&submittime), &buf[1]); | |
96bfbc2c EA |
1259 | if (message[0] != '\0' || bodytype[0] != '\0') |
1260 | { | |
1261 | printf("\n %10.10s", bodytype); | |
1262 | if (message[0] != '\0') | |
1263 | printf(" (%.60s)", message); | |
1264 | } | |
64912e7e | 1265 | break; |
3620ad97 | 1266 | |
3fbc69d6 | 1267 | case 'C': /* controlling user */ |
d2213d6c | 1268 | if (Verbose) |
fbab5052 EA |
1269 | printf("\n\t\t\t\t (---%.34s---)", |
1270 | &buf[1]); | |
3fbc69d6 | 1271 | break; |
64912e7e EA |
1272 | |
1273 | case 'R': /* recipient name */ | |
f4b05990 | 1274 | if (Verbose) |
fbab5052 | 1275 | printf("\n\t\t\t\t\t %.38s", &buf[1]); |
f4b05990 | 1276 | else |
fbab5052 | 1277 | printf("\n\t\t\t\t %.45s", &buf[1]); |
64912e7e EA |
1278 | break; |
1279 | ||
1280 | case 'T': /* creation time */ | |
2e3062fe | 1281 | submittime = atol(&buf[1]); |
0490b7d7 EA |
1282 | break; |
1283 | ||
1284 | case 'D': /* data file name */ | |
1285 | if (stat(&buf[1], &st) >= 0) | |
1286 | dfsize = st.st_size; | |
64912e7e | 1287 | break; |
c2bdb1dd EA |
1288 | |
1289 | case 'F': /* flag bits */ | |
1290 | for (p = &buf[1]; *p != '\0'; p++) | |
1291 | { | |
1292 | switch (*p) | |
1293 | { | |
1294 | case 'w': | |
1295 | flags |= EF_WARNING; | |
1296 | break; | |
1297 | } | |
1298 | } | |
64912e7e EA |
1299 | } |
1300 | } | |
0490b7d7 EA |
1301 | if (submittime == (time_t) 0) |
1302 | printf(" (no control file)"); | |
64912e7e | 1303 | printf("\n"); |
0e306e7f | 1304 | (void) fclose(f); |
64912e7e EA |
1305 | } |
1306 | } | |
884a20cb | 1307 | |
f3d8f6d6 | 1308 | # endif /* QUEUE */ |
560a80d9 EA |
1309 | \f/* |
1310 | ** QUEUENAME -- build a file name in the queue directory for this envelope. | |
1311 | ** | |
1312 | ** Assigns an id code if one does not already exist. | |
1313 | ** This code is very careful to avoid trashing existing files | |
1314 | ** under any circumstances. | |
560a80d9 EA |
1315 | ** |
1316 | ** Parameters: | |
1317 | ** e -- envelope to build it in/from. | |
1318 | ** type -- the file type, used as the first character | |
1319 | ** of the file name. | |
1320 | ** | |
1321 | ** Returns: | |
1322 | ** a pointer to the new file name (in a static buffer). | |
1323 | ** | |
1324 | ** Side Effects: | |
3620ad97 EA |
1325 | ** If no id code is already assigned, queuename will |
1326 | ** assign an id code, create a qf file, and leave a | |
1327 | ** locked, open-for-write file pointer in the envelope. | |
560a80d9 EA |
1328 | */ |
1329 | ||
1330 | char * | |
1331 | queuename(e, type) | |
1332 | register ENVELOPE *e; | |
ef3bc6b1 | 1333 | int type; |
560a80d9 | 1334 | { |
560a80d9 | 1335 | static int pid = -1; |
e3bb0d32 EA |
1336 | static char c0; |
1337 | static char c1; | |
1338 | static char c2; | |
fbab5052 EA |
1339 | time_t now; |
1340 | struct tm *tm; | |
f8a31c8e | 1341 | static char buf[MAXNAME]; |
560a80d9 EA |
1342 | |
1343 | if (e->e_id == NULL) | |
1344 | { | |
1345 | char qf[20]; | |
560a80d9 EA |
1346 | |
1347 | /* find a unique id */ | |
1348 | if (pid != getpid()) | |
1349 | { | |
1350 | /* new process -- start back at "AA" */ | |
1351 | pid = getpid(); | |
fbab5052 EA |
1352 | now = curtime(); |
1353 | tm = localtime(&now); | |
1354 | c0 = 'A' + tm->tm_hour; | |
560a80d9 EA |
1355 | c1 = 'A'; |
1356 | c2 = 'A' - 1; | |
1357 | } | |
fbab5052 | 1358 | (void) sprintf(qf, "qf%cAA%05d", c0, pid); |
560a80d9 EA |
1359 | |
1360 | while (c1 < '~' || c2 < 'Z') | |
1361 | { | |
1362 | int i; | |
1363 | ||
1364 | if (c2 >= 'Z') | |
1365 | { | |
1366 | c1++; | |
1367 | c2 = 'A' - 1; | |
1368 | } | |
fbab5052 EA |
1369 | qf[3] = c1; |
1370 | qf[4] = ++c2; | |
560a80d9 | 1371 | if (tTd(7, 20)) |
3f9ac8ea | 1372 | printf("queuename: trying \"%s\"\n", qf); |
560a80d9 | 1373 | |
3f9ac8ea | 1374 | i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); |
3620ad97 EA |
1375 | if (i < 0) |
1376 | { | |
1377 | if (errno == EEXIST) | |
1378 | continue; | |
1379 | syserr("queuename: Cannot create \"%s\" in \"%s\"", | |
1380 | qf, QueueDir); | |
1381 | exit(EX_UNAVAILABLE); | |
1382 | } | |
71936fbe | 1383 | if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) |
3620ad97 EA |
1384 | { |
1385 | e->e_lockfp = fdopen(i, "w"); | |
bb09c502 | 1386 | break; |
3f9ac8ea | 1387 | } |
3620ad97 EA |
1388 | |
1389 | /* a reader got the file; abandon it and try again */ | |
1390 | (void) close(i); | |
560a80d9 EA |
1391 | } |
1392 | if (c1 >= '~' && c2 >= 'Z') | |
1393 | { | |
1394 | syserr("queuename: Cannot create \"%s\" in \"%s\"", | |
1395 | qf, QueueDir); | |
1396 | exit(EX_OSERR); | |
1397 | } | |
1398 | e->e_id = newstr(&qf[2]); | |
1399 | define('i', e->e_id, e); | |
560a80d9 EA |
1400 | if (tTd(7, 1)) |
1401 | printf("queuename: assigned id %s, env=%x\n", e->e_id, e); | |
1402 | # ifdef LOG | |
68f7099c | 1403 | if (LogLevel > 93) |
560a80d9 | 1404 | syslog(LOG_DEBUG, "%s: assigned id", e->e_id); |
f3d8f6d6 | 1405 | # endif /* LOG */ |
560a80d9 EA |
1406 | } |
1407 | ||
1408 | if (type == '\0') | |
1409 | return (NULL); | |
1410 | (void) sprintf(buf, "%cf%s", type, e->e_id); | |
560a80d9 EA |
1411 | if (tTd(7, 2)) |
1412 | printf("queuename: %s\n", buf); | |
560a80d9 EA |
1413 | return (buf); |
1414 | } | |
1415 | \f/* | |
1416 | ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope | |
1417 | ** | |
1418 | ** Parameters: | |
1419 | ** e -- the envelope to unlock. | |
1420 | ** | |
1421 | ** Returns: | |
1422 | ** none | |
1423 | ** | |
1424 | ** Side Effects: | |
1425 | ** unlocks the queue for `e'. | |
1426 | */ | |
1427 | ||
1428 | unlockqueue(e) | |
1429 | ENVELOPE *e; | |
1430 | { | |
bc854e30 EA |
1431 | if (tTd(51, 4)) |
1432 | printf("unlockqueue(%s)\n", e->e_id); | |
1433 | ||
3620ad97 EA |
1434 | /* if there is a lock file in the envelope, close it */ |
1435 | if (e->e_lockfp != NULL) | |
bc854e30 | 1436 | xfclose(e->e_lockfp, "unlockqueue", e->e_id); |
3620ad97 EA |
1437 | e->e_lockfp = NULL; |
1438 | ||
32061c38 EA |
1439 | /* don't create a queue id if we don't already have one */ |
1440 | if (e->e_id == NULL) | |
1441 | return; | |
1442 | ||
560a80d9 | 1443 | /* remove the transcript */ |
560a80d9 | 1444 | # ifdef LOG |
68f7099c | 1445 | if (LogLevel > 87) |
560a80d9 | 1446 | syslog(LOG_DEBUG, "%s: unlock", e->e_id); |
f3d8f6d6 | 1447 | # endif /* LOG */ |
bc854e30 | 1448 | if (!tTd(51, 104)) |
560a80d9 EA |
1449 | xunlink(queuename(e, 'x')); |
1450 | ||
560a80d9 | 1451 | } |
3fbc69d6 | 1452 | \f/* |
d2213d6c | 1453 | ** SETCTLUSER -- create a controlling address |
3fbc69d6 | 1454 | ** |
d2213d6c EA |
1455 | ** Create a fake "address" given only a local login name; this is |
1456 | ** used as a "controlling user" for future recipient addresses. | |
3fbc69d6 KB |
1457 | ** |
1458 | ** Parameters: | |
d2213d6c | 1459 | ** user -- the user name of the controlling user. |
3fbc69d6 KB |
1460 | ** |
1461 | ** Returns: | |
d2213d6c | 1462 | ** An address descriptor for the controlling user. |
3fbc69d6 KB |
1463 | ** |
1464 | ** Side Effects: | |
1465 | ** none. | |
1466 | */ | |
1467 | ||
d2213d6c EA |
1468 | ADDRESS * |
1469 | setctluser(user) | |
1470 | char *user; | |
3fbc69d6 | 1471 | { |
d2213d6c | 1472 | register ADDRESS *a; |
3fbc69d6 | 1473 | struct passwd *pw; |
9cdb0b30 | 1474 | char *p; |
3fbc69d6 KB |
1475 | |
1476 | /* | |
d2213d6c | 1477 | ** See if this clears our concept of controlling user. |
3fbc69d6 KB |
1478 | */ |
1479 | ||
c6c403f7 EA |
1480 | if (user == NULL || *user == '\0') |
1481 | return NULL; | |
3fbc69d6 KB |
1482 | |
1483 | /* | |
d2213d6c | 1484 | ** Set up addr fields for controlling user. |
3fbc69d6 KB |
1485 | */ |
1486 | ||
d2213d6c EA |
1487 | a = (ADDRESS *) xalloc(sizeof *a); |
1488 | bzero((char *) a, sizeof *a); | |
9cdb0b30 EA |
1489 | |
1490 | p = strchr(user, ':'); | |
1491 | if (p != NULL) | |
1492 | *p++ = '\0'; | |
f7894324 | 1493 | if (*user != '\0' && (pw = getpwnam(user)) != NULL) |
3fbc69d6 | 1494 | { |
3fbc69d6 KB |
1495 | a->q_home = newstr(pw->pw_dir); |
1496 | a->q_uid = pw->pw_uid; | |
1497 | a->q_gid = pw->pw_gid; | |
6e99f903 | 1498 | a->q_user = newstr(user); |
f7894324 | 1499 | a->q_flags |= QGOODUID; |
3fbc69d6 KB |
1500 | } |
1501 | else | |
1502 | { | |
6e99f903 | 1503 | a->q_user = newstr(DefUser); |
3fbc69d6 KB |
1504 | } |
1505 | ||
f7894324 | 1506 | a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ |
d0a69620 | 1507 | a->q_mailer = LocalMailer; |
9cdb0b30 EA |
1508 | if (p == NULL) |
1509 | a->q_paddr = a->q_user; | |
1510 | else | |
1511 | a->q_paddr = newstr(p); | |
d2213d6c | 1512 | return a; |
3fbc69d6 | 1513 | } |