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