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