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