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