Commit | Line | Data |
---|---|---|
b2a81223 | 1 | /* |
792e6158 | 2 | * Copyright (c) 1983, 1995 Eric P. Allman |
eb0bafab KB |
3 | * Copyright (c) 1988, 1993 |
4 | * The Regents of the University of California. All rights reserved. | |
bee79b64 | 5 | * |
417f7a11 | 6 | * %sccs.include.redist.c% |
bee79b64 | 7 | */ |
b2a81223 | 8 | |
81b7d258 | 9 | # include "sendmail.h" |
bee79b64 KB |
10 | |
11 | #ifndef lint | |
12 | #ifdef QUEUE | |
4fdb944e | 13 | static char sccsid[] = "@(#)queue.c 8.87 (Berkeley) %G% (with queueing)"; |
bee79b64 | 14 | #else |
4fdb944e | 15 | static char sccsid[] = "@(#)queue.c 8.87 (Berkeley) %G% (without queueing)"; |
bee79b64 KB |
16 | #endif |
17 | #endif /* not lint */ | |
18 | ||
81b7d258 | 19 | # include <errno.h> |
6feb509e | 20 | # include <dirent.h> |
4287d84d | 21 | |
76b607b2 EA |
22 | # ifdef QUEUE |
23 | ||
81b7d258 | 24 | /* |
7338e3d4 EA |
25 | ** Work queue. |
26 | */ | |
27 | ||
28 | struct work | |
29 | { | |
30 | char *w_name; /* name of control file */ | |
a9ca3259 EA |
31 | char *w_host; /* name of recipient host */ |
32 | bool w_lock; /* is message locked? */ | |
7338e3d4 | 33 | long w_pri; /* priority of message, see below */ |
f746582a | 34 | time_t w_ctime; /* creation time of message */ |
7338e3d4 EA |
35 | struct work *w_next; /* next in queue */ |
36 | }; | |
37 | ||
38 | typedef struct work WORK; | |
39 | ||
40 | WORK *WorkQ; /* queue of things to be done */ | |
68d9129a EA |
41 | |
42 | #define QF_VERSION 1 /* version number of this queue format */ | |
7338e3d4 | 43 | \f/* |
81b7d258 EA |
44 | ** QUEUEUP -- queue a message up for future transmission. |
45 | ** | |
81b7d258 | 46 | ** Parameters: |
dd1fe05b | 47 | ** e -- the envelope to queue up. |
d0bd03ce EA |
48 | ** queueall -- if TRUE, queue all addresses, rather than |
49 | ** just those with the QQUEUEUP flag set. | |
7338e3d4 | 50 | ** announce -- if TRUE, tell when you are queueing up. |
81b7d258 EA |
51 | ** |
52 | ** Returns: | |
3620ad97 | 53 | ** none. |
81b7d258 EA |
54 | ** |
55 | ** Side Effects: | |
7338e3d4 | 56 | ** The current request are saved in a control file. |
3620ad97 | 57 | ** The queue file is left locked. |
81b7d258 EA |
58 | */ |
59 | ||
179d940c | 60 | void |
7338e3d4 | 61 | queueup(e, queueall, announce) |
dd1fe05b | 62 | register ENVELOPE *e; |
d0bd03ce | 63 | bool queueall; |
7338e3d4 | 64 | bool announce; |
81b7d258 | 65 | { |
2cce0c26 | 66 | char *qf; |
2cce0c26 | 67 | register FILE *tfp; |
81b7d258 | 68 | register HDR *h; |
d4f42161 | 69 | register ADDRESS *q; |
3620ad97 EA |
70 | int fd; |
71 | int i; | |
72 | bool newid; | |
22293c34 | 73 | register char *p; |
4db45bf1 | 74 | MAILER nullmailer; |
23fafb99 | 75 | MCI mcibuf; |
3620ad97 | 76 | char buf[MAXLINE], tf[MAXLINE]; |
179d940c | 77 | extern void printctladdr __P((ADDRESS *, FILE *)); |
81b7d258 | 78 | |
9ccf54c4 | 79 | /* |
11e3f8b5 | 80 | ** Create control file. |
9ccf54c4 | 81 | */ |
81b7d258 | 82 | |
eead656c | 83 | newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); |
e3778690 EA |
84 | |
85 | /* if newid, queuename will create a locked qf file in e->lockfp */ | |
3620ad97 EA |
86 | strcpy(tf, queuename(e, 't')); |
87 | tfp = e->e_lockfp; | |
88 | if (tfp == NULL) | |
89 | newid = FALSE; | |
e3778690 EA |
90 | |
91 | /* if newid, just write the qf file directly (instead of tf file) */ | |
eead656c | 92 | if (!newid) |
3620ad97 EA |
93 | { |
94 | /* get a locked tf file */ | |
b1c8be09 | 95 | for (i = 0; i < 128; i++) |
4287d84d | 96 | { |
3620ad97 EA |
97 | fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); |
98 | if (fd < 0) | |
4287d84d | 99 | { |
b1c8be09 EA |
100 | if (errno != EEXIST) |
101 | break; | |
102 | #ifdef LOG | |
103 | if (LogLevel > 0 && (i % 32) == 0) | |
e8f51a65 EA |
104 | syslog(LOG_ALERT, "queueup: cannot create %s, uid=%d: %s", |
105 | tf, geteuid(), errstring(errno)); | |
b1c8be09 | 106 | #endif |
6680179d | 107 | } |
e8f51a65 EA |
108 | else |
109 | { | |
110 | if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) | |
111 | break; | |
b1c8be09 | 112 | #ifdef LOG |
e8f51a65 EA |
113 | else if (LogLevel > 0 && (i % 32) == 0) |
114 | syslog(LOG_ALERT, "queueup: cannot lock %s: %s", | |
115 | tf, errstring(errno)); | |
b1c8be09 | 116 | #endif |
e8f51a65 EA |
117 | close(fd); |
118 | } | |
b1c8be09 EA |
119 | |
120 | if ((i % 32) == 31) | |
121 | { | |
122 | /* save the old temp file away */ | |
123 | (void) rename(tf, queuename(e, 'T')); | |
124 | } | |
125 | else | |
126 | sleep(i % 32); | |
6680179d | 127 | } |
e9d81da2 | 128 | if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL) |
cea561e2 EA |
129 | { |
130 | printopenfds(TRUE); | |
e8f51a65 EA |
131 | syserr("!queueup: cannot create queue temp file %s, uid=%d", |
132 | tf, geteuid()); | |
cea561e2 | 133 | } |
3620ad97 | 134 | } |
81b7d258 | 135 | |
9678c96d | 136 | if (tTd(40, 1)) |
68d9129a EA |
137 | printf("\n>>>>> queueing %s%s queueall=%d >>>>>\n", e->e_id, |
138 | newid ? " (new id)" : "", queueall); | |
66d16835 EA |
139 | if (tTd(40, 3)) |
140 | { | |
141 | extern void printenvflags(); | |
142 | ||
143 | printf(" e_flags="); | |
144 | printenvflags(e); | |
145 | } | |
68d9129a EA |
146 | if (tTd(40, 32)) |
147 | { | |
148 | printf(" sendq="); | |
149 | printaddr(e->e_sendqueue, TRUE); | |
150 | } | |
eead656c EA |
151 | if (tTd(40, 9)) |
152 | { | |
153 | printf(" tfp="); | |
154 | dumpfd(fileno(tfp), TRUE, FALSE); | |
155 | printf(" lockfp="); | |
156 | if (e->e_lockfp == NULL) | |
157 | printf("NULL\n"); | |
158 | else | |
159 | dumpfd(fileno(e->e_lockfp), TRUE, FALSE); | |
160 | } | |
81b7d258 | 161 | |
dd1fe05b EA |
162 | /* |
163 | ** If there is no data file yet, create one. | |
164 | */ | |
165 | ||
18a81ac0 | 166 | if (!bitset(EF_HAS_DF, e->e_flags)) |
dd1fe05b EA |
167 | { |
168 | register FILE *dfp; | |
18a81ac0 | 169 | char dfname[20]; |
f009d24b | 170 | struct stat stbuf; |
dd1fe05b | 171 | |
18a81ac0 EA |
172 | strcpy(dfname, queuename(e, 'd')); |
173 | fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); | |
e9d81da2 | 174 | if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL) |
e8f51a65 | 175 | syserr("!queueup: cannot create data temp file %s, uid=%d", |
18a81ac0 | 176 | dfname, geteuid()); |
f009d24b EA |
177 | if (fstat(fd, &stbuf) < 0) |
178 | e->e_dfino = -1; | |
179 | else | |
bb4fde22 EA |
180 | { |
181 | e->e_dfdev = stbuf.st_dev; | |
f009d24b | 182 | e->e_dfino = stbuf.st_ino; |
bb4fde22 | 183 | } |
18a81ac0 | 184 | e->e_flags |= EF_HAS_DF; |
23fafb99 EA |
185 | bzero(&mcibuf, sizeof mcibuf); |
186 | mcibuf.mci_out = dfp; | |
187 | mcibuf.mci_mailer = FileMailer; | |
51d448e5 | 188 | (*e->e_putbody)(&mcibuf, e, NULL); |
bc854e30 | 189 | (void) xfclose(dfp, "queueup dfp", e->e_id); |
80482eb5 | 190 | e->e_putbody = putbody; |
dd1fe05b EA |
191 | } |
192 | ||
81b7d258 EA |
193 | /* |
194 | ** Output future work requests. | |
4e969be0 EA |
195 | ** Priority and creation time should be first, since |
196 | ** they are required by orderq. | |
81b7d258 EA |
197 | */ |
198 | ||
68d9129a EA |
199 | /* output queue version number (must be first!) */ |
200 | fprintf(tfp, "V%d\n", QF_VERSION); | |
201 | ||
7338e3d4 EA |
202 | /* output message priority */ |
203 | fprintf(tfp, "P%ld\n", e->e_msgpriority); | |
204 | ||
64912e7e EA |
205 | /* output creation time */ |
206 | fprintf(tfp, "T%ld\n", e->e_ctime); | |
207 | ||
f009d24b EA |
208 | /* output last delivery time */ |
209 | fprintf(tfp, "K%ld\n", e->e_dtime); | |
210 | ||
211 | /* output number of delivery attempts */ | |
212 | fprintf(tfp, "N%d\n", e->e_ntries); | |
213 | ||
bb4fde22 EA |
214 | /* output inode number of data file */ |
215 | /* XXX should probably include device major/minor too */ | |
216 | if (e->e_dfino != -1) | |
217 | fprintf(tfp, "I%d/%d/%ld\n", | |
218 | major(e->e_dfdev), minor(e->e_dfdev), e->e_dfino); | |
219 | ||
18a81ac0 | 220 | /* output body type */ |
96bfbc2c EA |
221 | if (e->e_bodytype != NULL) |
222 | fprintf(tfp, "B%s\n", e->e_bodytype); | |
81b7d258 | 223 | |
baa0c390 EA |
224 | /* message from envelope, if it exists */ |
225 | if (e->e_message != NULL) | |
dcf45886 | 226 | fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE)); |
baa0c390 | 227 | |
c2bdb1dd EA |
228 | /* send various flag bits through */ |
229 | p = buf; | |
230 | if (bitset(EF_WARNING, e->e_flags)) | |
231 | *p++ = 'w'; | |
232 | if (bitset(EF_RESPONSE, e->e_flags)) | |
233 | *p++ = 'r'; | |
e3c84ea8 EA |
234 | if (bitset(EF_HAS8BIT, e->e_flags)) |
235 | *p++ = '8'; | |
c2bdb1dd EA |
236 | *p++ = '\0'; |
237 | if (buf[0] != '\0') | |
238 | fprintf(tfp, "F%s\n", buf); | |
239 | ||
a9bac7a9 | 240 | /* $r and $s and $_ macro values */ |
22293c34 | 241 | if ((p = macvalue('r', e)) != NULL) |
dcf45886 | 242 | fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE)); |
22293c34 | 243 | if ((p = macvalue('s', e)) != NULL) |
dcf45886 | 244 | fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE)); |
a9bac7a9 | 245 | if ((p = macvalue('_', e)) != NULL) |
dcf45886 | 246 | fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE)); |
22293c34 | 247 | |
81b7d258 | 248 | /* output name of sender */ |
b2af5ecf | 249 | fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE)); |
81b7d258 | 250 | |
6da3ba01 EA |
251 | /* output ESMTP-supplied "original" information */ |
252 | if (e->e_envid != NULL) | |
6da3ba01 | 253 | |
ca4d0c0b | 254 | /* output list of recipient addresses */ |
b7b719e4 | 255 | printctladdr(NULL, NULL); |
dd1fe05b | 256 | for (q = e->e_sendqueue; q != NULL; q = q->q_next) |
81b7d258 | 257 | { |
374579b6 | 258 | if (bitset(QQUEUEUP, q->q_flags) || |
bc854e30 | 259 | (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))) |
4d237f16 | 260 | { |
9cdb0b30 | 261 | printctladdr(q, tfp); |
3b6c2253 | 262 | if (q->q_orcpt != NULL) |
b2af5ecf | 263 | fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); |
7338e3d4 EA |
264 | if (announce) |
265 | { | |
266 | e->e_to = q->q_paddr; | |
b6edea3d | 267 | message("queued"); |
68f7099c | 268 | if (LogLevel > 8) |
3500a891 EA |
269 | logdelivery(NULL, NULL, "queued", |
270 | NULL, (time_t) 0, e); | |
7338e3d4 EA |
271 | e->e_to = NULL; |
272 | } | |
b9accadd EA |
273 | if (tTd(40, 1)) |
274 | { | |
275 | printf("queueing "); | |
276 | printaddr(q, FALSE); | |
277 | } | |
4d237f16 | 278 | } |
81b7d258 EA |
279 | } |
280 | ||
7338e3d4 EA |
281 | /* |
282 | ** Output headers for this message. | |
283 | ** Expand macros completely here. Queue run will deal with | |
284 | ** everything as absolute headers. | |
285 | ** All headers that must be relative to the recipient | |
286 | ** can be cracked later. | |
4db45bf1 EA |
287 | ** We set up a "null mailer" -- i.e., a mailer that will have |
288 | ** no effect on the addresses as they are output. | |
7338e3d4 EA |
289 | */ |
290 | ||
1dbda134 | 291 | bzero((char *) &nullmailer, sizeof nullmailer); |
68f7099c | 292 | nullmailer.m_re_rwset = nullmailer.m_rh_rwset = |
21efe526 | 293 | nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; |
281ef7f0 | 294 | nullmailer.m_eol = "\n"; |
23fafb99 EA |
295 | bzero(&mcibuf, sizeof mcibuf); |
296 | mcibuf.mci_mailer = &nullmailer; | |
297 | mcibuf.mci_out = tfp; | |
4db45bf1 | 298 | |
2bee003d | 299 | define('g', "\201f", e); |
dd1fe05b | 300 | for (h = e->e_header; h != NULL; h = h->h_link) |
81b7d258 | 301 | { |
1dbda134 EA |
302 | extern bool bitzerop(); |
303 | ||
83e9910e | 304 | /* don't output null headers */ |
81b7d258 EA |
305 | if (h->h_value == NULL || h->h_value[0] == '\0') |
306 | continue; | |
83e9910e EA |
307 | |
308 | /* don't output resent headers on non-resent messages */ | |
309 | if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) | |
310 | continue; | |
311 | ||
d27723b2 EA |
312 | /* expand macros; if null, don't output header at all */ |
313 | if (bitset(H_DEFAULT, h->h_flags)) | |
314 | { | |
832e8a27 | 315 | (void) expand(h->h_value, buf, sizeof buf, e); |
d27723b2 EA |
316 | if (buf[0] == '\0') |
317 | continue; | |
318 | } | |
319 | ||
83e9910e | 320 | /* output this header */ |
2cce0c26 | 321 | fprintf(tfp, "H"); |
83e9910e EA |
322 | |
323 | /* if conditional, output the set of conditions */ | |
1dbda134 EA |
324 | if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) |
325 | { | |
326 | int j; | |
327 | ||
0e306e7f | 328 | (void) putc('?', tfp); |
1dbda134 EA |
329 | for (j = '\0'; j <= '\177'; j++) |
330 | if (bitnset(j, h->h_mflags)) | |
0e306e7f EA |
331 | (void) putc(j, tfp); |
332 | (void) putc('?', tfp); | |
1dbda134 | 333 | } |
83e9910e EA |
334 | |
335 | /* output the header: expand macros, convert addresses */ | |
9416f3a0 EA |
336 | if (bitset(H_DEFAULT, h->h_flags)) |
337 | { | |
d27723b2 | 338 | fprintf(tfp, "%s: %s\n", h->h_field, buf); |
9416f3a0 | 339 | } |
4d237f16 | 340 | else if (bitset(H_FROM|H_RCPT, h->h_flags)) |
611b763d | 341 | { |
c2bdb1dd | 342 | bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); |
8e5c6745 EA |
343 | FILE *savetrace = TrafficLogFile; |
344 | ||
345 | TrafficLogFile = NULL; | |
c2bdb1dd EA |
346 | |
347 | if (bitset(H_FROM, h->h_flags)) | |
348 | oldstyle = FALSE; | |
349 | ||
23fafb99 | 350 | commaize(h, h->h_value, oldstyle, &mcibuf, e); |
8e5c6745 EA |
351 | |
352 | TrafficLogFile = savetrace; | |
611b763d | 353 | } |
4d237f16 EA |
354 | else |
355 | fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); | |
81b7d258 EA |
356 | } |
357 | ||
358 | /* | |
359 | ** Clean up. | |
360 | */ | |
361 | ||
eb1ada3c | 362 | if (fflush(tfp) < 0 || fsync(fileno(tfp)) < 0 || ferror(tfp)) |
e6d43796 EA |
363 | { |
364 | if (newid) | |
365 | syserr("!552 Error writing control file %s", tf); | |
366 | else | |
367 | syserr("!452 Error writing control file %s", tf); | |
368 | } | |
369 | ||
3620ad97 EA |
370 | if (!newid) |
371 | { | |
e3778690 | 372 | /* rename (locked) tf to be (locked) qf */ |
3620ad97 EA |
373 | qf = queuename(e, 'q'); |
374 | if (rename(tf, qf) < 0) | |
18a81ac0 EA |
375 | syserr("cannot rename(%s, %s), uid=%d", |
376 | tf, qf, geteuid()); | |
e3778690 EA |
377 | |
378 | /* close and unlock old (locked) qf */ | |
3620ad97 | 379 | if (e->e_lockfp != NULL) |
bc854e30 | 380 | (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); |
3620ad97 EA |
381 | e->e_lockfp = tfp; |
382 | } | |
383 | else | |
384 | qf = tf; | |
6680179d | 385 | errno = 0; |
eead656c | 386 | e->e_flags |= EF_INQUEUE; |
1a13ccd8 | 387 | |
9678c96d EA |
388 | # ifdef LOG |
389 | /* save log info */ | |
68f7099c | 390 | if (LogLevel > 79) |
18a81ac0 | 391 | syslog(LOG_DEBUG, "%s: queueup, qf=%s", e->e_id, qf); |
f3d8f6d6 | 392 | # endif /* LOG */ |
51458e80 EA |
393 | |
394 | if (tTd(40, 1)) | |
395 | printf("<<<<< done queueing %s <<<<<\n\n", e->e_id); | |
3620ad97 | 396 | return; |
81b7d258 | 397 | } |
d2213d6c | 398 | |
179d940c | 399 | void |
d2213d6c | 400 | printctladdr(a, tfp) |
9cdb0b30 | 401 | register ADDRESS *a; |
d2213d6c EA |
402 | FILE *tfp; |
403 | { | |
9cdb0b30 EA |
404 | char *uname; |
405 | register struct passwd *pw; | |
406 | register ADDRESS *q; | |
407 | uid_t uid; | |
408 | static ADDRESS *lastctladdr; | |
409 | static uid_t lastuid; | |
d2213d6c | 410 | |
9cdb0b30 | 411 | /* initialization */ |
c6c403f7 | 412 | if (a == NULL || a->q_alias == NULL || tfp == NULL) |
d2213d6c | 413 | { |
31739b4e | 414 | if (lastctladdr != NULL && tfp != NULL) |
9cdb0b30 EA |
415 | fprintf(tfp, "C\n"); |
416 | lastctladdr = NULL; | |
417 | lastuid = 0; | |
d2213d6c EA |
418 | return; |
419 | } | |
9cdb0b30 EA |
420 | |
421 | /* find the active uid */ | |
422 | q = getctladdr(a); | |
423 | if (q == NULL) | |
424 | uid = 0; | |
425 | else | |
426 | uid = q->q_uid; | |
c6c403f7 | 427 | a = a->q_alias; |
9cdb0b30 EA |
428 | |
429 | /* check to see if this is the same as last time */ | |
430 | if (lastctladdr != NULL && uid == lastuid && | |
431 | strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) | |
432 | return; | |
433 | lastuid = uid; | |
434 | lastctladdr = a; | |
435 | ||
5b7a2dfe | 436 | if (uid == 0 || (pw = sm_getpwuid(uid)) == NULL) |
f7894324 | 437 | uname = ""; |
d2213d6c | 438 | else |
9cdb0b30 EA |
439 | uname = pw->pw_name; |
440 | ||
dcf45886 | 441 | fprintf(tfp, "C%s:%s\n", uname, denlstring(a->q_paddr, TRUE, FALSE)); |
d2213d6c | 442 | } |
81b7d258 EA |
443 | \f/* |
444 | ** RUNQUEUE -- run the jobs in the queue. | |
445 | ** | |
446 | ** Gets the stuff out of the queue in some presumably logical | |
447 | ** order and processes them. | |
448 | ** | |
449 | ** Parameters: | |
2e3062fe EA |
450 | ** forkflag -- TRUE if the queue scanning should be done in |
451 | ** a child process. We double-fork so it is not our | |
452 | ** child and we don't have to clean up after it. | |
81b7d258 EA |
453 | ** |
454 | ** Returns: | |
455 | ** none. | |
456 | ** | |
457 | ** Side Effects: | |
458 | ** runs things in the mail queue. | |
459 | */ | |
460 | ||
ca4d0c0b EA |
461 | ENVELOPE QueueEnvelope; /* the queue run envelope */ |
462 | ||
ea07b2d2 | 463 | void |
ca4d0c0b | 464 | runqueue(forkflag) |
2cf55cb7 | 465 | bool forkflag; |
81b7d258 | 466 | { |
ca4d0c0b | 467 | register ENVELOPE *e; |
d3a63a56 EA |
468 | int njobs; |
469 | int sequenceno = 0; | |
ca4d0c0b | 470 | extern ENVELOPE BlankEnvelope; |
4bc44f60 EA |
471 | |
472 | /* | |
473 | ** If no work will ever be selected, don't even bother reading | |
474 | ** the queue. | |
475 | */ | |
476 | ||
3620ad97 | 477 | CurrentLA = getla(); /* get load average */ |
3f9ac8ea | 478 | |
986065ef | 479 | if (shouldqueue(0L, curtime())) |
4bc44f60 | 480 | { |
303d144e EA |
481 | char *msg = "Skipping queue run -- load average too high"; |
482 | ||
4bc44f60 | 483 | if (Verbose) |
303d144e EA |
484 | printf("%s\n", msg); |
485 | #ifdef LOG | |
486 | if (LogLevel > 8) | |
487 | syslog(LOG_INFO, "runqueue: %s", msg); | |
488 | #endif | |
fe80f8e1 EA |
489 | if (forkflag && QueueIntvl != 0) |
490 | (void) setevent(QueueIntvl, runqueue, TRUE); | |
ca4d0c0b | 491 | return; |
4bc44f60 EA |
492 | } |
493 | ||
b6dffdf5 EA |
494 | /* |
495 | ** See if we want to go off and do other useful work. | |
496 | */ | |
2cf55cb7 EA |
497 | |
498 | if (forkflag) | |
499 | { | |
6d06102a | 500 | int pid; |
a72af781 EA |
501 | #ifdef SIGCHLD |
502 | extern void reapchild(); | |
503 | ||
504 | (void) setsignal(SIGCHLD, reapchild); | |
505 | #endif | |
6d06102a EA |
506 | |
507 | pid = dofork(); | |
508 | if (pid != 0) | |
2cf55cb7 | 509 | { |
6d06102a | 510 | /* parent -- pick up intermediate zombie */ |
c8ad461a | 511 | #ifndef SIGCHLD |
7338e3d4 | 512 | (void) waitfor(pid); |
4fdb944e EA |
513 | #else |
514 | CurChildren++; | |
f3d8f6d6 | 515 | #endif /* SIGCHLD */ |
c4442979 | 516 | if (QueueIntvl != 0) |
611b763d | 517 | (void) setevent(QueueIntvl, runqueue, TRUE); |
2cf55cb7 EA |
518 | return; |
519 | } | |
6d06102a | 520 | /* child -- double fork */ |
c8ad461a | 521 | #ifndef SIGCHLD |
6d06102a EA |
522 | if (fork() != 0) |
523 | exit(EX_OK); | |
f3d8f6d6 | 524 | #else /* SIGCHLD */ |
39270cfd | 525 | (void) setsignal(SIGCHLD, SIG_DFL); |
f3d8f6d6 | 526 | #endif /* SIGCHLD */ |
b6dffdf5 | 527 | } |
2e3062fe | 528 | |
3f9ac8ea | 529 | setproctitle("running queue: %s", QueueDir); |
2e3062fe | 530 | |
36a4e219 | 531 | # ifdef LOG |
68f7099c | 532 | if (LogLevel > 69) |
ca4d0c0b EA |
533 | syslog(LOG_DEBUG, "runqueue %s, pid=%d, forkflag=%d", |
534 | QueueDir, getpid(), forkflag); | |
f3d8f6d6 | 535 | # endif /* LOG */ |
81b7d258 | 536 | |
eb8af3ce EA |
537 | /* |
538 | ** Release any resources used by the daemon code. | |
539 | */ | |
540 | ||
541 | # ifdef DAEMON | |
542 | clrdaemon(); | |
f3d8f6d6 | 543 | # endif /* DAEMON */ |
eb8af3ce | 544 | |
6b951123 EA |
545 | /* force it to run expensive jobs */ |
546 | NoConnect = FALSE; | |
547 | ||
ca4d0c0b EA |
548 | /* |
549 | ** Create ourselves an envelope | |
550 | */ | |
551 | ||
552 | CurEnv = &QueueEnvelope; | |
fda58daa | 553 | e = newenvelope(&QueueEnvelope, CurEnv); |
ca4d0c0b EA |
554 | e->e_flags = BlankEnvelope.e_flags; |
555 | ||
a78155b0 EA |
556 | /* |
557 | ** Make sure the alias database is open. | |
558 | */ | |
559 | ||
36b09297 | 560 | initmaps(FALSE, e); |
a78155b0 | 561 | |
b6dffdf5 EA |
562 | /* |
563 | ** Start making passes through the queue. | |
564 | ** First, read and sort the entire queue. | |
565 | ** Then, process the work in that order. | |
566 | ** But if you take too long, start over. | |
b6dffdf5 | 567 | */ |
81b7d258 | 568 | |
6d06102a | 569 | /* order the existing work requests */ |
d3a63a56 | 570 | njobs = orderq(FALSE); |
291fa573 | 571 | |
6d06102a EA |
572 | /* process them once at a time */ |
573 | while (WorkQ != NULL) | |
2cf55cb7 | 574 | { |
6d06102a | 575 | WORK *w = WorkQ; |
2cf55cb7 | 576 | |
6d06102a | 577 | WorkQ = WorkQ->w_next; |
a7dc53bd EA |
578 | |
579 | /* | |
580 | ** Ignore jobs that are too expensive for the moment. | |
581 | */ | |
582 | ||
d3a63a56 | 583 | sequenceno++; |
a7dc53bd EA |
584 | if (shouldqueue(w->w_pri, w->w_ctime)) |
585 | { | |
586 | if (Verbose) | |
d3a63a56 EA |
587 | printf("\nSkipping %s (sequence %d of %d)\n", |
588 | w->w_name + 2, sequenceno, njobs); | |
a7dc53bd | 589 | } |
1c791deb EA |
590 | else |
591 | { | |
d0268270 EA |
592 | pid_t pid; |
593 | extern pid_t dowork(); | |
594 | ||
d3a63a56 EA |
595 | if (Verbose) |
596 | printf("\nRunning %s (sequence %d of %d)\n", | |
597 | w->w_name + 2, sequenceno, njobs); | |
d0268270 EA |
598 | pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); |
599 | errno = 0; | |
a9927082 EA |
600 | if (pid != 0) |
601 | (void) waitfor(pid); | |
1c791deb | 602 | } |
6d06102a | 603 | free(w->w_name); |
a9ca3259 EA |
604 | if (w->w_host) |
605 | free(w->w_host); | |
6d06102a | 606 | free((char *) w); |
2cf55cb7 | 607 | } |
37ea3cbf EA |
608 | |
609 | /* exit without the usual cleanup */ | |
4e20c4d2 EA |
610 | e->e_id = NULL; |
611 | finis(); | |
aba51985 EA |
612 | } |
613 | \f/* | |
81b7d258 EA |
614 | ** ORDERQ -- order the work queue. |
615 | ** | |
616 | ** Parameters: | |
2e3062fe EA |
617 | ** doall -- if set, include everything in the queue (even |
618 | ** the jobs that cannot be run because the load | |
619 | ** average is too high). Otherwise, exclude those | |
620 | ** jobs. | |
81b7d258 EA |
621 | ** |
622 | ** Returns: | |
e65b95c4 EA |
623 | ** The number of request in the queue (not necessarily |
624 | ** the number of requests in WorkQ however). | |
81b7d258 EA |
625 | ** |
626 | ** Side Effects: | |
627 | ** Sets WorkQ to the queue of available work, in order. | |
628 | */ | |
629 | ||
4e969be0 EA |
630 | # define NEED_P 001 |
631 | # define NEED_T 002 | |
d6c28d1a EA |
632 | # define NEED_R 004 |
633 | # define NEED_S 010 | |
81b7d258 | 634 | |
8a6b1962 EA |
635 | static WORK *WorkList = NULL; |
636 | static int WorkListSize = 0; | |
637 | ||
179d940c | 638 | int |
38d3b461 EA |
639 | # ifndef DIR |
640 | # define DIR FILE | |
641 | # define direct dir | |
642 | # define opendir(d) fopen(d, "r") | |
07a8f29b | 643 | # define readdir(f) ((fread(&dbuf, sizeof dbuf, 1, f) > 0) ? &dbuf : 0) |
38d3b461 EA |
644 | static struct dir dbuf; |
645 | # define closedir(f) fclose(f) | |
646 | # endif DIR | |
647 | ||
2e3062fe EA |
648 | orderq(doall) |
649 | bool doall; | |
81b7d258 | 650 | { |
9114f86c | 651 | register struct dirent *d; |
81b7d258 | 652 | register WORK *w; |
ccd1d833 | 653 | DIR *f; |
81b7d258 | 654 | register int i; |
0490b7d7 | 655 | int wn = -1; |
a9ca3259 | 656 | int wc; |
81b7d258 | 657 | |
d6c28d1a EA |
658 | if (tTd(41, 1)) |
659 | { | |
660 | printf("orderq:\n"); | |
661 | if (QueueLimitId != NULL) | |
662 | printf("\tQueueLimitId = %s\n", QueueLimitId); | |
663 | if (QueueLimitSender != NULL) | |
664 | printf("\tQueueLimitSender = %s\n", QueueLimitSender); | |
665 | if (QueueLimitRecipient != NULL) | |
666 | printf("\tQueueLimitRecipient = %s\n", QueueLimitRecipient); | |
667 | } | |
668 | ||
81b7d258 EA |
669 | /* clear out old WorkQ */ |
670 | for (w = WorkQ; w != NULL; ) | |
671 | { | |
672 | register WORK *nw = w->w_next; | |
673 | ||
674 | WorkQ = nw; | |
675 | free(w->w_name); | |
a9ca3259 EA |
676 | if (w->w_host) |
677 | free(w->w_host); | |
81b7d258 EA |
678 | free((char *) w); |
679 | w = nw; | |
680 | } | |
681 | ||
682 | /* open the queue directory */ | |
6bbaf971 | 683 | f = opendir("."); |
81b7d258 EA |
684 | if (f == NULL) |
685 | { | |
6bbaf971 | 686 | syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); |
0490b7d7 | 687 | return (0); |
81b7d258 EA |
688 | } |
689 | ||
690 | /* | |
691 | ** Read the work directory. | |
692 | */ | |
693 | ||
0490b7d7 | 694 | while ((d = readdir(f)) != NULL) |
81b7d258 | 695 | { |
81b7d258 | 696 | FILE *cf; |
bb530db4 | 697 | register char *p; |
8446c922 | 698 | char lbuf[MAXNAME + 1]; |
d6c28d1a | 699 | extern bool strcontainedin(); |
81b7d258 EA |
700 | |
701 | /* is this an interesting entry? */ | |
07a8f29b EA |
702 | if (d->d_ino == 0) |
703 | continue; | |
704 | # ifdef DEBUG | |
705 | if (tTd(40, 10)) | |
706 | printf("orderq: %12s\n", d->d_name); | |
707 | # endif DEBUG | |
2cce0c26 | 708 | if (d->d_name[0] != 'q' || d->d_name[1] != 'f') |
81b7d258 EA |
709 | continue; |
710 | ||
d6c28d1a EA |
711 | if (QueueLimitId != NULL && |
712 | !strcontainedin(QueueLimitId, d->d_name)) | |
713 | continue; | |
714 | ||
6502fd93 | 715 | #ifdef PICKY_QF_NAME_CHECK |
45ce4b34 EA |
716 | /* |
717 | ** Check queue name for plausibility. This handles | |
718 | ** both old and new type ids. | |
719 | */ | |
720 | ||
bb530db4 EA |
721 | p = d->d_name + 2; |
722 | if (isupper(p[0]) && isupper(p[2])) | |
723 | p += 3; | |
724 | else if (isupper(p[1])) | |
725 | p += 2; | |
726 | else | |
727 | p = d->d_name; | |
728 | for (i = 0; isdigit(*p); p++) | |
729 | i++; | |
730 | if (i < 5 || *p != '\0') | |
68f7099c EA |
731 | { |
732 | if (Verbose) | |
733 | printf("orderq: bogus qf name %s\n", d->d_name); | |
6502fd93 EA |
734 | # ifdef LOG |
735 | if (LogLevel > 0) | |
736 | syslog(LOG_ALERT, "orderq: bogus qf name %s", | |
68f7099c | 737 | d->d_name); |
6502fd93 | 738 | # endif |
2604d017 | 739 | if (strlen(d->d_name) > (SIZE_T) MAXNAME) |
8446c922 | 740 | d->d_name[MAXNAME] = '\0'; |
68f7099c EA |
741 | strcpy(lbuf, d->d_name); |
742 | lbuf[0] = 'Q'; | |
743 | (void) rename(d->d_name, lbuf); | |
744 | continue; | |
745 | } | |
6502fd93 | 746 | #endif |
68f7099c | 747 | |
6502fd93 | 748 | /* open control file (if not too many files) */ |
28b647b8 EA |
749 | if (++wn > MaxQueueRun && MaxQueueRun > 0) |
750 | { | |
751 | # ifdef LOG | |
752 | if (wn == MaxQueueRun + 1 && LogLevel > 0) | |
753 | syslog(LOG_ALERT, "WorkList for %s maxed out at %d", | |
754 | QueueDir, MaxQueueRun); | |
755 | # endif | |
756 | continue; | |
757 | } | |
758 | if (wn >= WorkListSize) | |
8a6b1962 | 759 | { |
179d940c EA |
760 | extern void grow_wlist __P((void)); |
761 | ||
8a6b1962 EA |
762 | grow_wlist(); |
763 | if (wn >= WorkListSize) | |
764 | continue; | |
765 | } | |
d6c28d1a | 766 | |
6bbaf971 | 767 | cf = fopen(d->d_name, "r"); |
81b7d258 EA |
768 | if (cf == NULL) |
769 | { | |
91f69adf EA |
770 | /* this may be some random person sending hir msgs */ |
771 | /* syserr("orderq: cannot open %s", cbuf); */ | |
bd0712bd EA |
772 | if (tTd(41, 2)) |
773 | printf("orderq: cannot open %s (%d)\n", | |
774 | d->d_name, errno); | |
91f69adf | 775 | errno = 0; |
bd0712bd | 776 | wn--; |
81b7d258 EA |
777 | continue; |
778 | } | |
8a6b1962 | 779 | w = &WorkList[wn]; |
4e969be0 | 780 | w->w_name = newstr(d->d_name); |
a9ca3259 EA |
781 | w->w_host = NULL; |
782 | w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); | |
81b7d258 | 783 | |
4ff794a3 | 784 | /* make sure jobs in creation don't clog queue */ |
4e969be0 EA |
785 | w->w_pri = 0x7fffffff; |
786 | w->w_ctime = 0; | |
4ff794a3 | 787 | |
81b7d258 | 788 | /* extract useful information */ |
4e969be0 | 789 | i = NEED_P | NEED_T; |
d6c28d1a EA |
790 | if (QueueLimitSender != NULL) |
791 | i |= NEED_S; | |
92830179 | 792 | if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL) |
d6c28d1a | 793 | i |= NEED_R; |
4e969be0 | 794 | while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) |
81b7d258 | 795 | { |
d6c28d1a | 796 | extern bool strcontainedin(); |
8ff78b51 | 797 | |
2e3062fe | 798 | switch (lbuf[0]) |
81b7d258 | 799 | { |
2e3062fe | 800 | case 'P': |
4e969be0 EA |
801 | w->w_pri = atol(&lbuf[1]); |
802 | i &= ~NEED_P; | |
2e3062fe | 803 | break; |
f746582a EA |
804 | |
805 | case 'T': | |
4e969be0 EA |
806 | w->w_ctime = atol(&lbuf[1]); |
807 | i &= ~NEED_T; | |
f746582a | 808 | break; |
d6c28d1a EA |
809 | |
810 | case 'R': | |
a9ca3259 EA |
811 | if (w->w_host == NULL && |
812 | (p = strrchr(&lbuf[1], '@')) != NULL) | |
813 | w->w_host = newstr(&p[1]); | |
814 | if (QueueLimitRecipient == NULL || | |
d6c28d1a EA |
815 | strcontainedin(QueueLimitRecipient, &lbuf[1])) |
816 | i &= ~NEED_R; | |
817 | break; | |
818 | ||
819 | case 'S': | |
820 | if (QueueLimitSender != NULL && | |
821 | strcontainedin(QueueLimitSender, &lbuf[1])) | |
822 | i &= ~NEED_S; | |
823 | break; | |
81b7d258 EA |
824 | } |
825 | } | |
81b7d258 | 826 | (void) fclose(cf); |
4bc44f60 | 827 | |
d6c28d1a EA |
828 | if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || |
829 | bitset(NEED_R|NEED_S, i)) | |
4bc44f60 EA |
830 | { |
831 | /* don't even bother sorting this job in */ | |
a9ca3259 EA |
832 | free(w->w_name); |
833 | if (w->w_host) | |
834 | free(w->w_host); | |
4bc44f60 EA |
835 | wn--; |
836 | } | |
81b7d258 | 837 | } |
ccd1d833 | 838 | (void) closedir(f); |
bd0712bd | 839 | wn++; |
81b7d258 | 840 | |
8a6b1962 | 841 | wc = min(wn, WorkListSize); |
a9ca3259 | 842 | |
92830179 | 843 | if (QueueSortOrder == QS_BYHOST) |
a9ca3259 EA |
844 | { |
845 | extern workcmpf1(); | |
846 | extern workcmpf2(); | |
847 | ||
848 | /* | |
849 | ** Sort the work directory for the first time, | |
850 | ** based on host name, lock status, and priority. | |
851 | */ | |
852 | ||
8a6b1962 | 853 | qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); |
a9ca3259 EA |
854 | |
855 | /* | |
856 | ** If one message to host is locked, "lock" all messages | |
857 | ** to that host. | |
858 | */ | |
859 | ||
860 | i = 0; | |
861 | while (i < wc) | |
862 | { | |
8a6b1962 | 863 | if (!WorkList[i].w_lock) |
a9ca3259 EA |
864 | { |
865 | i++; | |
866 | continue; | |
867 | } | |
8a6b1962 | 868 | w = &WorkList[i]; |
a9ca3259 EA |
869 | while (++i < wc) |
870 | { | |
8a6b1962 | 871 | if (WorkList[i].w_host == NULL && |
a9ca3259 | 872 | w->w_host == NULL) |
8a6b1962 EA |
873 | WorkList[i].w_lock = TRUE; |
874 | else if (WorkList[i].w_host != NULL && | |
a9ca3259 | 875 | w->w_host != NULL && |
8a6b1962 EA |
876 | strcmp(WorkList[i].w_host, w->w_host) == 0) |
877 | WorkList[i].w_lock = TRUE; | |
a9ca3259 EA |
878 | else |
879 | break; | |
880 | } | |
881 | } | |
882 | ||
883 | /* | |
884 | ** Sort the work directory for the second time, | |
885 | ** based on lock status, host name, and priority. | |
886 | */ | |
887 | ||
8a6b1962 | 888 | qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); |
a9ca3259 | 889 | } |
92830179 EA |
890 | else |
891 | { | |
892 | extern workcmpf0(); | |
893 | ||
894 | /* | |
895 | ** Simple sort based on queue priority only. | |
896 | */ | |
897 | ||
8a6b1962 | 898 | qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); |
92830179 | 899 | } |
81b7d258 EA |
900 | |
901 | /* | |
902 | ** Convert the work list into canonical form. | |
7338e3d4 | 903 | ** Should be turning it into a list of envelopes here perhaps. |
81b7d258 EA |
904 | */ |
905 | ||
a0225d08 | 906 | WorkQ = NULL; |
a9ca3259 | 907 | for (i = wc; --i >= 0; ) |
81b7d258 EA |
908 | { |
909 | w = (WORK *) xalloc(sizeof *w); | |
8a6b1962 EA |
910 | w->w_name = WorkList[i].w_name; |
911 | w->w_host = WorkList[i].w_host; | |
912 | w->w_lock = WorkList[i].w_lock; | |
913 | w->w_pri = WorkList[i].w_pri; | |
914 | w->w_ctime = WorkList[i].w_ctime; | |
a0225d08 EA |
915 | w->w_next = WorkQ; |
916 | WorkQ = w; | |
81b7d258 | 917 | } |
8a6b1962 EA |
918 | free(WorkList); |
919 | WorkList = NULL; | |
81b7d258 | 920 | |
9678c96d | 921 | if (tTd(40, 1)) |
81b7d258 EA |
922 | { |
923 | for (w = WorkQ; w != NULL; w = w->w_next) | |
9ccf54c4 | 924 | printf("%32s: pri=%ld\n", w->w_name, w->w_pri); |
81b7d258 | 925 | } |
0490b7d7 | 926 | |
bd0712bd | 927 | return (wn); |
81b7d258 | 928 | } |
28b647b8 EA |
929 | \f/* |
930 | ** GROW_WLIST -- make the work list larger | |
931 | ** | |
932 | ** Parameters: | |
933 | ** none. | |
934 | ** | |
935 | ** Returns: | |
936 | ** none. | |
937 | ** | |
938 | ** Side Effects: | |
939 | ** Adds another QUEUESEGSIZE entries to WorkList if possible. | |
940 | ** It can fail if there isn't enough memory, so WorkListSize | |
941 | ** should be checked again upon return. | |
942 | */ | |
8a6b1962 | 943 | |
179d940c | 944 | void |
8a6b1962 EA |
945 | grow_wlist() |
946 | { | |
947 | if (tTd(41, 1)) | |
948 | printf("grow_wlist: WorkListSize=%d\n", WorkListSize); | |
28b647b8 | 949 | if (WorkList == NULL) |
8a6b1962 EA |
950 | { |
951 | WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1)); | |
952 | WorkListSize = QUEUESEGSIZE; | |
953 | } | |
954 | else | |
955 | { | |
956 | int newsize = WorkListSize + QUEUESEGSIZE; | |
957 | WORK *newlist = (WORK *) realloc((char *)WorkList, | |
958 | (unsigned)sizeof(WORK) * (newsize + 1)); | |
959 | ||
960 | if (newlist != NULL) | |
961 | { | |
962 | WorkListSize = newsize; | |
963 | WorkList = newlist; | |
964 | # ifdef LOG | |
965 | if (LogLevel > 1) | |
966 | { | |
967 | syslog(LOG_NOTICE, "grew WorkList for %s to %d", | |
968 | QueueDir, WorkListSize); | |
969 | } | |
970 | } | |
971 | else if (LogLevel > 0) | |
972 | { | |
973 | syslog(LOG_ALERT, "FAILED to grow WorkList for %s to %d", | |
974 | QueueDir, newsize); | |
975 | # endif | |
976 | } | |
977 | } | |
978 | if (tTd(41, 1)) | |
979 | printf("grow_wlist: WorkListSize now %d\n", WorkListSize); | |
980 | } | |
81b7d258 | 981 | \f/* |
a9ca3259 | 982 | ** WORKCMPF0 -- simple priority-only compare function. |
81b7d258 EA |
983 | ** |
984 | ** Parameters: | |
985 | ** a -- the first argument. | |
986 | ** b -- the second argument. | |
987 | ** | |
988 | ** Returns: | |
a0225d08 EA |
989 | ** -1 if a < b |
990 | ** 0 if a == b | |
991 | ** +1 if a > b | |
81b7d258 EA |
992 | ** |
993 | ** Side Effects: | |
994 | ** none. | |
995 | */ | |
996 | ||
179d940c | 997 | int |
a9ca3259 | 998 | workcmpf0(a, b) |
9ccf54c4 EA |
999 | register WORK *a; |
1000 | register WORK *b; | |
81b7d258 | 1001 | { |
abccefc9 EA |
1002 | long pa = a->w_pri; |
1003 | long pb = b->w_pri; | |
2e3062fe EA |
1004 | |
1005 | if (pa == pb) | |
a9ca3259 | 1006 | return 0; |
2e3062fe | 1007 | else if (pa > pb) |
a9ca3259 | 1008 | return 1; |
a0225d08 | 1009 | else |
a9ca3259 EA |
1010 | return -1; |
1011 | } | |
1012 | \f/* | |
1013 | ** WORKCMPF1 -- first compare function for ordering work based on host name. | |
1014 | ** | |
1015 | ** Sorts on host name, lock status, and priority in that order. | |
1016 | ** | |
1017 | ** Parameters: | |
1018 | ** a -- the first argument. | |
1019 | ** b -- the second argument. | |
1020 | ** | |
1021 | ** Returns: | |
1022 | ** <0 if a < b | |
1023 | ** 0 if a == b | |
1024 | ** >0 if a > b | |
1025 | ** | |
1026 | ** Side Effects: | |
1027 | ** none. | |
1028 | */ | |
1029 | ||
179d940c | 1030 | int |
a9ca3259 EA |
1031 | workcmpf1(a, b) |
1032 | register WORK *a; | |
1033 | register WORK *b; | |
1034 | { | |
1035 | int i; | |
1036 | ||
1037 | /* host name */ | |
1038 | if (a->w_host != NULL && b->w_host == NULL) | |
1039 | return 1; | |
1040 | else if (a->w_host == NULL && b->w_host != NULL) | |
1041 | return -1; | |
1042 | if (a->w_host != NULL && b->w_host != NULL && | |
1043 | (i = strcmp(a->w_host, b->w_host))) | |
1044 | return i; | |
1045 | ||
1046 | /* lock status */ | |
1047 | if (a->w_lock != b->w_lock) | |
1048 | return b->w_lock - a->w_lock; | |
1049 | ||
1050 | /* job priority */ | |
1051 | return a->w_pri - b->w_pri; | |
1052 | } | |
1053 | \f/* | |
1054 | ** WORKCMPF2 -- second compare function for ordering work based on host name. | |
1055 | ** | |
1056 | ** Sorts on lock status, host name, and priority in that order. | |
1057 | ** | |
1058 | ** Parameters: | |
1059 | ** a -- the first argument. | |
1060 | ** b -- the second argument. | |
1061 | ** | |
1062 | ** Returns: | |
1063 | ** <0 if a < b | |
1064 | ** 0 if a == b | |
1065 | ** >0 if a > b | |
1066 | ** | |
1067 | ** Side Effects: | |
1068 | ** none. | |
1069 | */ | |
1070 | ||
179d940c | 1071 | int |
a9ca3259 EA |
1072 | workcmpf2(a, b) |
1073 | register WORK *a; | |
1074 | register WORK *b; | |
1075 | { | |
1076 | int i; | |
1077 | ||
1078 | /* lock status */ | |
1079 | if (a->w_lock != b->w_lock) | |
1080 | return a->w_lock - b->w_lock; | |
1081 | ||
1082 | /* host name */ | |
1083 | if (a->w_host != NULL && b->w_host == NULL) | |
1084 | return 1; | |
1085 | else if (a->w_host == NULL && b->w_host != NULL) | |
1086 | return -1; | |
1087 | if (a->w_host != NULL && b->w_host != NULL && | |
1088 | (i = strcmp(a->w_host, b->w_host))) | |
1089 | return i; | |
1090 | ||
1091 | /* job priority */ | |
1092 | return a->w_pri - b->w_pri; | |
81b7d258 EA |
1093 | } |
1094 | \f/* | |
1095 | ** DOWORK -- do a work request. | |
1096 | ** | |
1097 | ** Parameters: | |
a7dc53bd EA |
1098 | ** id -- the ID of the job to run. |
1099 | ** forkflag -- if set, run this in background. | |
f5303697 EA |
1100 | ** requeueflag -- if set, reinstantiate the queue quickly. |
1101 | ** This is used when expanding aliases in the queue. | |
8d00c219 EA |
1102 | ** If forkflag is also set, it doesn't wait for the |
1103 | ** child. | |
a7dc53bd | 1104 | ** e - the envelope in which to run it. |
81b7d258 EA |
1105 | ** |
1106 | ** Returns: | |
d0268270 | 1107 | ** process id of process that is running the queue job. |
81b7d258 EA |
1108 | ** |
1109 | ** Side Effects: | |
1110 | ** The work request is satisfied if possible. | |
1111 | */ | |
1112 | ||
d0268270 | 1113 | pid_t |
f5303697 | 1114 | dowork(id, forkflag, requeueflag, e) |
a7dc53bd EA |
1115 | char *id; |
1116 | bool forkflag; | |
f5303697 | 1117 | bool requeueflag; |
a4076aed | 1118 | register ENVELOPE *e; |
81b7d258 | 1119 | { |
d0268270 | 1120 | register pid_t pid; |
3620ad97 | 1121 | extern bool readqf(); |
81b7d258 | 1122 | |
9678c96d | 1123 | if (tTd(40, 1)) |
a7dc53bd | 1124 | printf("dowork(%s)\n", id); |
81b7d258 | 1125 | |
2e3062fe EA |
1126 | /* |
1127 | ** Fork for work. | |
1128 | */ | |
1129 | ||
a7dc53bd | 1130 | if (forkflag) |
2e3062fe | 1131 | { |
d0268270 EA |
1132 | pid = fork(); |
1133 | if (pid < 0) | |
2e3062fe EA |
1134 | { |
1135 | syserr("dowork: cannot fork"); | |
d0268270 | 1136 | return 0; |
2e3062fe | 1137 | } |
5cf47fbf EA |
1138 | else if (pid > 0) |
1139 | { | |
1140 | /* parent -- clean out connection cache */ | |
1141 | mci_flush(FALSE, NULL); | |
1142 | } | |
2e3062fe EA |
1143 | } |
1144 | else | |
1145 | { | |
d0268270 | 1146 | pid = 0; |
2e3062fe EA |
1147 | } |
1148 | ||
d0268270 | 1149 | if (pid == 0) |
81b7d258 EA |
1150 | { |
1151 | /* | |
1152 | ** CHILD | |
6bbaf971 EA |
1153 | ** Lock the control file to avoid duplicate deliveries. |
1154 | ** Then run the file as though we had just read it. | |
68f0b54c EA |
1155 | ** We save an idea of the temporary name so we |
1156 | ** can recover on interrupt. | |
81b7d258 EA |
1157 | */ |
1158 | ||
9416f3a0 | 1159 | /* set basic modes, etc. */ |
37eaaadb | 1160 | (void) alarm(0); |
a4076aed | 1161 | clearenvelope(e, FALSE); |
164f3011 | 1162 | e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; |
8c8e8e94 | 1163 | e->e_errormode = EM_MAIL; |
a7dc53bd | 1164 | e->e_id = id; |
48cb554d | 1165 | GrabTo = UseErrorsTo = FALSE; |
a9927082 | 1166 | ExitStat = EX_OK; |
47e2f242 | 1167 | if (forkflag) |
164f3011 | 1168 | { |
d0268270 | 1169 | disconnect(1, e); |
164f3011 EA |
1170 | OpMode = MD_DELIVER; |
1171 | } | |
36a4e219 | 1172 | # ifdef LOG |
68f7099c | 1173 | if (LogLevel > 76) |
a4076aed | 1174 | syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id, |
291fa573 | 1175 | getpid()); |
f3d8f6d6 | 1176 | # endif /* LOG */ |
9416f3a0 EA |
1177 | |
1178 | /* don't use the headers from sendmail.cf... */ | |
a4076aed | 1179 | e->e_header = NULL; |
9416f3a0 | 1180 | |
3620ad97 | 1181 | /* read the queue control file -- return if locked */ |
63a34fcd | 1182 | if (!readqf(e)) |
dd1fe05b | 1183 | { |
a7dc53bd EA |
1184 | if (tTd(40, 4)) |
1185 | printf("readqf(%s) failed\n", e->e_id); | |
1186 | if (forkflag) | |
2e3062fe EA |
1187 | exit(EX_OK); |
1188 | else | |
a9927082 | 1189 | return 0; |
dd1fe05b | 1190 | } |
dd1fe05b | 1191 | |
a4076aed | 1192 | e->e_flags |= EF_INQUEUE; |
dd1fe05b | 1193 | |
3df50535 | 1194 | /* if this has been tried recently, let it be */ |
1b9b1753 | 1195 | if (e->e_ntries > 0 && (curtime() - e->e_dtime) < MinQueueAge) |
3df50535 EA |
1196 | { |
1197 | char *howlong = pintvl(curtime() - e->e_dtime, TRUE); | |
f5303697 | 1198 | |
3df50535 EA |
1199 | e->e_flags |= EF_KEEPQUEUE; |
1200 | if (Verbose || tTd(40, 8)) | |
1201 | printf("%s: too young (%s)\n", | |
1202 | e->e_id, howlong); | |
1203 | #ifdef LOG | |
1204 | if (LogLevel > 19) | |
1205 | syslog(LOG_DEBUG, "%s: too young (%s)", | |
1206 | e->e_id, howlong); | |
1207 | #endif | |
1208 | } | |
1209 | else | |
1210 | { | |
1211 | eatheader(e, requeueflag); | |
1212 | ||
1213 | if (requeueflag) | |
1214 | queueup(e, TRUE, FALSE); | |
1215 | ||
1216 | /* do the delivery */ | |
1217 | sendall(e, SM_DELIVER); | |
1218 | } | |
dd1fe05b | 1219 | |
dd1fe05b | 1220 | /* finish up and exit */ |
a7dc53bd | 1221 | if (forkflag) |
2e3062fe EA |
1222 | finis(); |
1223 | else | |
a4076aed | 1224 | dropenvelope(e); |
81b7d258 | 1225 | } |
d0268270 EA |
1226 | e->e_id = NULL; |
1227 | return pid; | |
81b7d258 EA |
1228 | } |
1229 | \f/* | |
1230 | ** READQF -- read queue file and set up environment. | |
1231 | ** | |
1232 | ** Parameters: | |
7338e3d4 | 1233 | ** e -- the envelope of the job to run. |
81b7d258 EA |
1234 | ** |
1235 | ** Returns: | |
3620ad97 EA |
1236 | ** TRUE if it successfully read the queue file. |
1237 | ** FALSE otherwise. | |
81b7d258 EA |
1238 | ** |
1239 | ** Side Effects: | |
3620ad97 | 1240 | ** The queue file is returned locked. |
81b7d258 EA |
1241 | */ |
1242 | ||
3620ad97 | 1243 | bool |
63a34fcd | 1244 | readqf(e) |
7338e3d4 | 1245 | register ENVELOPE *e; |
81b7d258 | 1246 | { |
11e3f8b5 | 1247 | register FILE *qfp; |
d2213d6c | 1248 | ADDRESS *ctladdr; |
dcbc04c7 | 1249 | struct stat st; |
07b49560 | 1250 | char *bp; |
68d9129a EA |
1251 | int qfver = 0; |
1252 | register char *p; | |
3b6c2253 | 1253 | char *orcpt = NULL; |
e678ee95 | 1254 | char qf[20]; |
07b49560 | 1255 | char buf[MAXLINE]; |
d2213d6c | 1256 | extern ADDRESS *setctluser(); |
99fd9645 | 1257 | extern void loseqfile(); |
abae7b2d | 1258 | extern ADDRESS *sendto(); |
81b7d258 | 1259 | |
81b7d258 EA |
1260 | /* |
1261 | ** Read and process the file. | |
1262 | */ | |
1263 | ||
e678ee95 | 1264 | strcpy(qf, queuename(e, 'q')); |
76b607b2 | 1265 | qfp = fopen(qf, "r+"); |
11e3f8b5 EA |
1266 | if (qfp == NULL) |
1267 | { | |
a7dc53bd EA |
1268 | if (tTd(40, 8)) |
1269 | printf("readqf(%s): fopen failure (%s)\n", | |
1270 | qf, errstring(errno)); | |
3f9ac8ea RA |
1271 | if (errno != ENOENT) |
1272 | syserr("readqf: no control file %s", qf); | |
3620ad97 | 1273 | return FALSE; |
3f9ac8ea RA |
1274 | } |
1275 | ||
71936fbe | 1276 | if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) |
e3778690 EA |
1277 | { |
1278 | /* being processed by another queuer */ | |
3df50535 | 1279 | if (Verbose || tTd(40, 8)) |
e3778690 EA |
1280 | printf("%s: locked\n", e->e_id); |
1281 | # ifdef LOG | |
1282 | if (LogLevel > 19) | |
1283 | syslog(LOG_DEBUG, "%s: locked", e->e_id); | |
1284 | # endif /* LOG */ | |
1285 | (void) fclose(qfp); | |
1286 | return FALSE; | |
1287 | } | |
1288 | ||
dcbc04c7 EA |
1289 | /* |
1290 | ** Check the queue file for plausibility to avoid attacks. | |
1291 | */ | |
1292 | ||
1293 | if (fstat(fileno(qfp), &st) < 0) | |
1294 | { | |
1295 | /* must have been being processed by someone else */ | |
a7dc53bd EA |
1296 | if (tTd(40, 8)) |
1297 | printf("readqf(%s): fstat failure (%s)\n", | |
1298 | qf, errstring(errno)); | |
dcbc04c7 EA |
1299 | fclose(qfp); |
1300 | return FALSE; | |
1301 | } | |
1302 | ||
6502fd93 | 1303 | if (st.st_uid != geteuid() || bitset(S_IWOTH|S_IWGRP, st.st_mode)) |
dcbc04c7 EA |
1304 | { |
1305 | # ifdef LOG | |
1306 | if (LogLevel > 0) | |
1307 | { | |
1308 | syslog(LOG_ALERT, "%s: bogus queue file, uid=%d, mode=%o", | |
1309 | e->e_id, st.st_uid, st.st_mode); | |
1310 | } | |
f3d8f6d6 | 1311 | # endif /* LOG */ |
a7dc53bd EA |
1312 | if (tTd(40, 8)) |
1313 | printf("readqf(%s): bogus file\n", qf); | |
99fd9645 | 1314 | loseqfile(e, "bogus file uid in mqueue"); |
e3778690 | 1315 | fclose(qfp); |
dcbc04c7 EA |
1316 | return FALSE; |
1317 | } | |
1318 | ||
e3778690 | 1319 | if (st.st_size == 0) |
3f9ac8ea | 1320 | { |
e3778690 EA |
1321 | /* must be a bogus file -- just remove it */ |
1322 | (void) unlink(qf); | |
1323 | fclose(qfp); | |
3620ad97 | 1324 | return FALSE; |
11e3f8b5 | 1325 | } |
3f9ac8ea | 1326 | |
e3778690 | 1327 | if (st.st_nlink == 0) |
c87034b7 | 1328 | { |
e3778690 EA |
1329 | /* |
1330 | ** Race condition -- we got a file just as it was being | |
1331 | ** unlinked. Just assume it is zero length. | |
1332 | */ | |
1333 | ||
c87034b7 EA |
1334 | fclose(qfp); |
1335 | return FALSE; | |
1336 | } | |
1337 | ||
e3778690 | 1338 | /* good file -- save this lock */ |
3620ad97 EA |
1339 | e->e_lockfp = qfp; |
1340 | ||
3f9ac8ea | 1341 | /* do basic system initialization */ |
a4076aed | 1342 | initsys(e); |
b276598d | 1343 | define('i', e->e_id, e); |
3f9ac8ea | 1344 | |
560a80d9 | 1345 | LineNumber = 0; |
c6c403f7 EA |
1346 | e->e_flags |= EF_GLOBALERRS; |
1347 | OpMode = MD_DELIVER; | |
d2213d6c | 1348 | ctladdr = NULL; |
bb4fde22 | 1349 | e->e_dfino = -1; |
18a81ac0 | 1350 | e->e_msgsize = -1; |
07b49560 | 1351 | while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) |
81b7d258 | 1352 | { |
c2bdb1dd | 1353 | register char *p; |
68d9129a EA |
1354 | u_long qflags; |
1355 | ADDRESS *q; | |
65b5adb6 | 1356 | |
795590e2 | 1357 | if (tTd(40, 4)) |
07b49560 EA |
1358 | printf("+++++ %s\n", bp); |
1359 | switch (bp[0]) | |
81b7d258 | 1360 | { |
68d9129a EA |
1361 | case 'V': /* queue file version number */ |
1362 | qfver = atoi(&bp[1]); | |
1363 | if (qfver > QF_VERSION) | |
1364 | { | |
1365 | syserr("Version number in qf (%d) greater than max (%d)", | |
1366 | qfver, QF_VERSION); | |
1367 | } | |
1368 | break; | |
1369 | ||
3fbc69d6 | 1370 | case 'C': /* specify controlling user */ |
07b49560 | 1371 | ctladdr = setctluser(&bp[1]); |
3fbc69d6 KB |
1372 | break; |
1373 | ||
3b6c2253 EA |
1374 | case 'Q': /* original recipient */ |
1375 | orcpt = newstr(&bp[1]); | |
1376 | break; | |
1377 | ||
81b7d258 | 1378 | case 'R': /* specify recipient */ |
abae7b2d | 1379 | (void) sendto(&buf[1], 1, (ADDRESS *) NULL, 0); |
81b7d258 EA |
1380 | break; |
1381 | ||
4e969be0 | 1382 | case 'E': /* specify error recipient */ |
b7b719e4 | 1383 | /* no longer used */ |
4e969be0 EA |
1384 | break; |
1385 | ||
81b7d258 | 1386 | case 'H': /* header */ |
9905202c | 1387 | (void) chompheader(&bp[1], FALSE, NULL, e); |
81b7d258 EA |
1388 | break; |
1389 | ||
baa0c390 | 1390 | case 'M': /* message */ |
82530719 | 1391 | /* ignore this; we want a new message next time */ |
baa0c390 EA |
1392 | break; |
1393 | ||
81b7d258 | 1394 | case 'S': /* sender */ |
4a2da288 | 1395 | setsender(newstr(&bp[1]), e, NULL, TRUE); |
81b7d258 EA |
1396 | break; |
1397 | ||
96bfbc2c EA |
1398 | case 'B': /* body type */ |
1399 | e->e_bodytype = newstr(&bp[1]); | |
1400 | break; | |
1401 | ||
81b7d258 | 1402 | case 'D': /* data file name */ |
18a81ac0 | 1403 | /* obsolete -- ignore */ |
81b7d258 EA |
1404 | break; |
1405 | ||
96476cab | 1406 | case 'T': /* init time */ |
07b49560 | 1407 | e->e_ctime = atol(&bp[1]); |
81b7d258 EA |
1408 | break; |
1409 | ||
f009d24b | 1410 | case 'I': /* data file's inode number */ |
bb4fde22 EA |
1411 | if (e->e_dfino == -1) |
1412 | e->e_dfino = atol(&buf[1]); | |
f009d24b EA |
1413 | break; |
1414 | ||
1415 | case 'K': /* time of last deliver attempt */ | |
1416 | e->e_dtime = atol(&buf[1]); | |
1417 | break; | |
1418 | ||
1419 | case 'N': /* number of delivery attempts */ | |
1420 | e->e_ntries = atoi(&buf[1]); | |
1421 | break; | |
1422 | ||
aba51985 | 1423 | case 'P': /* message priority */ |
07b49560 | 1424 | e->e_msgpriority = atol(&bp[1]) + WkTimeFact; |
dd1fe05b EA |
1425 | break; |
1426 | ||
c2bdb1dd EA |
1427 | case 'F': /* flag bits */ |
1428 | for (p = &bp[1]; *p != '\0'; p++) | |
1429 | { | |
1430 | switch (*p) | |
1431 | { | |
1432 | case 'w': /* warning sent */ | |
1433 | e->e_flags |= EF_WARNING; | |
1434 | break; | |
1435 | ||
1436 | case 'r': /* response */ | |
1437 | e->e_flags |= EF_RESPONSE; | |
1438 | break; | |
e3c84ea8 EA |
1439 | |
1440 | case '8': /* has 8 bit data */ | |
1441 | e->e_flags |= EF_HAS8BIT; | |
1442 | break; | |
c2bdb1dd EA |
1443 | } |
1444 | } | |
1445 | break; | |
1446 | ||
6da3ba01 | 1447 | case 'Z': /* original envelope id from ESMTP */ |
36c7ff6a | 1448 | e->e_envid = newstr(&bp[1]); |
6da3ba01 EA |
1449 | break; |
1450 | ||
22293c34 | 1451 | case '$': /* define macro */ |
07b49560 | 1452 | define(bp[1], newstr(&bp[2]), e); |
22293c34 EA |
1453 | break; |
1454 | ||
2e3062fe EA |
1455 | case '\0': /* blank line; ignore */ |
1456 | break; | |
1457 | ||
81b7d258 | 1458 | default: |
5e90e563 | 1459 | syserr("readqf: %s: line %d: bad line \"%s\"", |
63a34fcd | 1460 | qf, LineNumber, bp); |
8e5c6745 | 1461 | fclose(qfp); |
99fd9645 | 1462 | loseqfile(e, "unrecognized line"); |
8e5c6745 | 1463 | return FALSE; |
81b7d258 | 1464 | } |
07b49560 EA |
1465 | |
1466 | if (bp != buf) | |
1467 | free(bp); | |
81b7d258 | 1468 | } |
7338e3d4 | 1469 | |
2e3062fe EA |
1470 | /* |
1471 | ** If we haven't read any lines, this queue file is empty. | |
1472 | ** Arrange to remove it without referencing any null pointers. | |
1473 | */ | |
1474 | ||
1475 | if (LineNumber == 0) | |
1476 | { | |
1477 | errno = 0; | |
1478 | e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; | |
1479 | } | |
18a81ac0 EA |
1480 | else |
1481 | { | |
1482 | /* | |
1483 | ** Arrange to read the data file. | |
1484 | */ | |
1485 | ||
1486 | p = queuename(e, 'd'); | |
1487 | e->e_dfp = fopen(p, "r"); | |
1488 | if (e->e_dfp == NULL) | |
1489 | { | |
1490 | syserr("readqf: cannot open %s", p); | |
1491 | } | |
66d16835 | 1492 | else |
18a81ac0 | 1493 | { |
66d16835 EA |
1494 | e->e_flags |= EF_HAS_DF; |
1495 | if (fstat(fileno(e->e_dfp), &st) >= 0) | |
1496 | { | |
1497 | e->e_msgsize = st.st_size; | |
1498 | e->e_dfdev = st.st_dev; | |
1499 | e->e_dfino = st.st_ino; | |
1500 | } | |
18a81ac0 EA |
1501 | } |
1502 | } | |
1503 | ||
3620ad97 | 1504 | return TRUE; |
81b7d258 EA |
1505 | } |
1506 | \f/* | |
64912e7e EA |
1507 | ** PRINTQUEUE -- print out a representation of the mail queue |
1508 | ** | |
1509 | ** Parameters: | |
1510 | ** none. | |
1511 | ** | |
1512 | ** Returns: | |
1513 | ** none. | |
1514 | ** | |
1515 | ** Side Effects: | |
1516 | ** Prints a listing of the mail queue on the standard output. | |
1517 | */ | |
1518 | ||
179d940c | 1519 | void |
64912e7e EA |
1520 | printqueue() |
1521 | { | |
1522 | register WORK *w; | |
1523 | FILE *f; | |
0490b7d7 | 1524 | int nrequests; |
64912e7e EA |
1525 | char buf[MAXLINE]; |
1526 | ||
374579b6 EA |
1527 | /* |
1528 | ** Check for permission to print the queue | |
1529 | */ | |
1530 | ||
fd178efd | 1531 | if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) |
374579b6 EA |
1532 | { |
1533 | struct stat st; | |
3afbc0f1 EA |
1534 | # ifdef NGROUPS |
1535 | int n; | |
e6cb9fc4 | 1536 | GIDSET_T gidset[NGROUPS]; |
3afbc0f1 | 1537 | # endif |
374579b6 | 1538 | |
7c8b3fd4 | 1539 | if (stat(QueueDir, &st) < 0) |
374579b6 EA |
1540 | { |
1541 | syserr("Cannot stat %s", QueueDir); | |
1542 | return; | |
1543 | } | |
3afbc0f1 EA |
1544 | # ifdef NGROUPS |
1545 | n = getgroups(NGROUPS, gidset); | |
1546 | while (--n >= 0) | |
1547 | { | |
1548 | if (gidset[n] == st.st_gid) | |
1549 | break; | |
1550 | } | |
ab3de76b | 1551 | if (n < 0 && RealGid != st.st_gid) |
3afbc0f1 | 1552 | # else |
fd57f063 | 1553 | if (RealGid != st.st_gid) |
3afbc0f1 | 1554 | # endif |
374579b6 EA |
1555 | { |
1556 | usrerr("510 You are not permitted to see the queue"); | |
1557 | setstat(EX_NOPERM); | |
1558 | return; | |
1559 | } | |
1560 | } | |
1561 | ||
64912e7e EA |
1562 | /* |
1563 | ** Read and order the queue. | |
1564 | */ | |
1565 | ||
2e3062fe | 1566 | nrequests = orderq(TRUE); |
64912e7e EA |
1567 | |
1568 | /* | |
1569 | ** Print the work list that we have read. | |
1570 | */ | |
1571 | ||
1572 | /* first see if there is anything */ | |
0490b7d7 | 1573 | if (nrequests <= 0) |
64912e7e | 1574 | { |
0490b7d7 | 1575 | printf("Mail queue is empty\n"); |
64912e7e EA |
1576 | return; |
1577 | } | |
1578 | ||
3620ad97 | 1579 | CurrentLA = getla(); /* get load average */ |
3f9ac8ea | 1580 | |
d7b5594c | 1581 | printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); |
8a6b1962 EA |
1582 | if (nrequests > WorkListSize) |
1583 | printf(", only %d printed", WorkListSize); | |
f4b05990 | 1584 | if (Verbose) |
fbab5052 | 1585 | printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); |
f4b05990 | 1586 | else |
fbab5052 | 1587 | printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); |
64912e7e EA |
1588 | for (w = WorkQ; w != NULL; w = w->w_next) |
1589 | { | |
1590 | struct stat st; | |
0490b7d7 | 1591 | auto time_t submittime = 0; |
73af81f9 | 1592 | long dfsize; |
c2bdb1dd | 1593 | int flags = 0; |
68d9129a | 1594 | int qfver; |
0c6ce473 | 1595 | char statmsg[MAXLINE]; |
8446c922 | 1596 | char bodytype[MAXNAME + 1]; |
64912e7e | 1597 | |
4ba6a6f4 | 1598 | printf("%8s", w->w_name + 2); |
560a80d9 EA |
1599 | f = fopen(w->w_name, "r"); |
1600 | if (f == NULL) | |
1601 | { | |
4ba6a6f4 | 1602 | printf(" (job completed)\n"); |
560a80d9 EA |
1603 | errno = 0; |
1604 | continue; | |
1605 | } | |
73af81f9 EA |
1606 | w->w_name[0] = 'd'; |
1607 | if (stat(w->w_name, &st) >= 0) | |
1608 | dfsize = st.st_size; | |
1609 | else | |
1610 | dfsize = -1; | |
a9ca3259 | 1611 | if (w->w_lock) |
0490b7d7 | 1612 | printf("*"); |
abccefc9 | 1613 | else if (shouldqueue(w->w_pri, w->w_ctime)) |
2e3062fe | 1614 | printf("X"); |
0490b7d7 EA |
1615 | else |
1616 | printf(" "); | |
1617 | errno = 0; | |
560a80d9 | 1618 | |
0c6ce473 | 1619 | statmsg[0] = bodytype[0] = '\0'; |
68d9129a | 1620 | qfver = 0; |
64912e7e EA |
1621 | while (fgets(buf, sizeof buf, f) != NULL) |
1622 | { | |
22293c34 | 1623 | register int i; |
c2bdb1dd | 1624 | register char *p; |
22293c34 | 1625 | |
64912e7e EA |
1626 | fixcrlf(buf, TRUE); |
1627 | switch (buf[0]) | |
1628 | { | |
68d9129a EA |
1629 | case 'V': /* queue file version */ |
1630 | qfver = atoi(&buf[1]); | |
1631 | break; | |
1632 | ||
baa0c390 | 1633 | case 'M': /* error message */ |
0c6ce473 EA |
1634 | if ((i = strlen(&buf[1])) >= sizeof statmsg) |
1635 | i = sizeof statmsg - 1; | |
1636 | bcopy(&buf[1], statmsg, i); | |
1637 | statmsg[i] = '\0'; | |
baa0c390 EA |
1638 | break; |
1639 | ||
96bfbc2c EA |
1640 | case 'B': /* body type */ |
1641 | if ((i = strlen(&buf[1])) >= sizeof bodytype) | |
1642 | i = sizeof bodytype - 1; | |
1643 | bcopy(&buf[1], bodytype, i); | |
1644 | bodytype[i] = '\0'; | |
1645 | break; | |
1646 | ||
64912e7e | 1647 | case 'S': /* sender name */ |
f4b05990 | 1648 | if (Verbose) |
c2bdb1dd EA |
1649 | printf("%8ld %10ld%c%.12s %.38s", |
1650 | dfsize, | |
1651 | w->w_pri, | |
1652 | bitset(EF_WARNING, flags) ? '+' : ' ', | |
1653 | ctime(&submittime) + 4, | |
f4b05990 EA |
1654 | &buf[1]); |
1655 | else | |
1656 | printf("%8ld %.16s %.45s", dfsize, | |
1657 | ctime(&submittime), &buf[1]); | |
0c6ce473 | 1658 | if (statmsg[0] != '\0' || bodytype[0] != '\0') |
96bfbc2c EA |
1659 | { |
1660 | printf("\n %10.10s", bodytype); | |
0c6ce473 EA |
1661 | if (statmsg[0] != '\0') |
1662 | printf(" (%.60s)", statmsg); | |
96bfbc2c | 1663 | } |
64912e7e | 1664 | break; |
3620ad97 | 1665 | |
3fbc69d6 | 1666 | case 'C': /* controlling user */ |
d2213d6c | 1667 | if (Verbose) |
fbab5052 EA |
1668 | printf("\n\t\t\t\t (---%.34s---)", |
1669 | &buf[1]); | |
3fbc69d6 | 1670 | break; |
64912e7e EA |
1671 | |
1672 | case 'R': /* recipient name */ | |
68d9129a EA |
1673 | p = &buf[1]; |
1674 | if (qfver >= 1) | |
1675 | { | |
1676 | p = strchr(p, ':'); | |
1677 | if (p == NULL) | |
1678 | break; | |
1679 | p++; | |
1680 | } | |
f4b05990 | 1681 | if (Verbose) |
68d9129a | 1682 | printf("\n\t\t\t\t\t %.38s", p); |
f4b05990 | 1683 | else |
68d9129a | 1684 | printf("\n\t\t\t\t %.45s", p); |
64912e7e EA |
1685 | break; |
1686 | ||
1687 | case 'T': /* creation time */ | |
2e3062fe | 1688 | submittime = atol(&buf[1]); |
0490b7d7 EA |
1689 | break; |
1690 | ||
c2bdb1dd EA |
1691 | case 'F': /* flag bits */ |
1692 | for (p = &buf[1]; *p != '\0'; p++) | |
1693 | { | |
1694 | switch (*p) | |
1695 | { | |
1696 | case 'w': | |
1697 | flags |= EF_WARNING; | |
1698 | break; | |
1699 | } | |
1700 | } | |
64912e7e EA |
1701 | } |
1702 | } | |
0490b7d7 EA |
1703 | if (submittime == (time_t) 0) |
1704 | printf(" (no control file)"); | |
64912e7e | 1705 | printf("\n"); |
0e306e7f | 1706 | (void) fclose(f); |
64912e7e EA |
1707 | } |
1708 | } | |
884a20cb | 1709 | |
f3d8f6d6 | 1710 | # endif /* QUEUE */ |
560a80d9 EA |
1711 | \f/* |
1712 | ** QUEUENAME -- build a file name in the queue directory for this envelope. | |
1713 | ** | |
1714 | ** Assigns an id code if one does not already exist. | |
1715 | ** This code is very careful to avoid trashing existing files | |
1716 | ** under any circumstances. | |
560a80d9 EA |
1717 | ** |
1718 | ** Parameters: | |
1719 | ** e -- envelope to build it in/from. | |
1720 | ** type -- the file type, used as the first character | |
1721 | ** of the file name. | |
1722 | ** | |
1723 | ** Returns: | |
1724 | ** a pointer to the new file name (in a static buffer). | |
1725 | ** | |
1726 | ** Side Effects: | |
3620ad97 EA |
1727 | ** If no id code is already assigned, queuename will |
1728 | ** assign an id code, create a qf file, and leave a | |
1729 | ** locked, open-for-write file pointer in the envelope. | |
560a80d9 EA |
1730 | */ |
1731 | ||
1732 | char * | |
1733 | queuename(e, type) | |
1734 | register ENVELOPE *e; | |
ef3bc6b1 | 1735 | int type; |
560a80d9 | 1736 | { |
560a80d9 | 1737 | static int pid = -1; |
e3bb0d32 EA |
1738 | static char c0; |
1739 | static char c1; | |
1740 | static char c2; | |
fbab5052 EA |
1741 | time_t now; |
1742 | struct tm *tm; | |
8446c922 | 1743 | static char buf[MAXNAME + 1]; |
560a80d9 EA |
1744 | |
1745 | if (e->e_id == NULL) | |
1746 | { | |
1747 | char qf[20]; | |
560a80d9 EA |
1748 | |
1749 | /* find a unique id */ | |
1750 | if (pid != getpid()) | |
1751 | { | |
1752 | /* new process -- start back at "AA" */ | |
1753 | pid = getpid(); | |
fbab5052 EA |
1754 | now = curtime(); |
1755 | tm = localtime(&now); | |
1756 | c0 = 'A' + tm->tm_hour; | |
560a80d9 EA |
1757 | c1 = 'A'; |
1758 | c2 = 'A' - 1; | |
1759 | } | |
fbab5052 | 1760 | (void) sprintf(qf, "qf%cAA%05d", c0, pid); |
560a80d9 EA |
1761 | |
1762 | while (c1 < '~' || c2 < 'Z') | |
1763 | { | |
1764 | int i; | |
1765 | ||
1766 | if (c2 >= 'Z') | |
1767 | { | |
1768 | c1++; | |
1769 | c2 = 'A' - 1; | |
1770 | } | |
fbab5052 EA |
1771 | qf[3] = c1; |
1772 | qf[4] = ++c2; | |
560a80d9 | 1773 | if (tTd(7, 20)) |
3f9ac8ea | 1774 | printf("queuename: trying \"%s\"\n", qf); |
560a80d9 | 1775 | |
3f9ac8ea | 1776 | i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); |
3620ad97 EA |
1777 | if (i < 0) |
1778 | { | |
1779 | if (errno == EEXIST) | |
1780 | continue; | |
82530719 EA |
1781 | syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", |
1782 | qf, QueueDir, geteuid()); | |
3620ad97 EA |
1783 | exit(EX_UNAVAILABLE); |
1784 | } | |
71936fbe | 1785 | if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) |
3620ad97 EA |
1786 | { |
1787 | e->e_lockfp = fdopen(i, "w"); | |
bb09c502 | 1788 | break; |
3f9ac8ea | 1789 | } |
3620ad97 EA |
1790 | |
1791 | /* a reader got the file; abandon it and try again */ | |
1792 | (void) close(i); | |
560a80d9 EA |
1793 | } |
1794 | if (c1 >= '~' && c2 >= 'Z') | |
1795 | { | |
82530719 EA |
1796 | syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", |
1797 | qf, QueueDir, geteuid()); | |
560a80d9 EA |
1798 | exit(EX_OSERR); |
1799 | } | |
1800 | e->e_id = newstr(&qf[2]); | |
1801 | define('i', e->e_id, e); | |
560a80d9 EA |
1802 | if (tTd(7, 1)) |
1803 | printf("queuename: assigned id %s, env=%x\n", e->e_id, e); | |
eead656c EA |
1804 | if (tTd(7, 9)) |
1805 | { | |
1806 | printf(" lockfd="); | |
1807 | dumpfd(fileno(e->e_lockfp), TRUE, FALSE); | |
1808 | } | |
560a80d9 | 1809 | # ifdef LOG |
68f7099c | 1810 | if (LogLevel > 93) |
560a80d9 | 1811 | syslog(LOG_DEBUG, "%s: assigned id", e->e_id); |
f3d8f6d6 | 1812 | # endif /* LOG */ |
560a80d9 EA |
1813 | } |
1814 | ||
1815 | if (type == '\0') | |
1816 | return (NULL); | |
1817 | (void) sprintf(buf, "%cf%s", type, e->e_id); | |
560a80d9 EA |
1818 | if (tTd(7, 2)) |
1819 | printf("queuename: %s\n", buf); | |
560a80d9 EA |
1820 | return (buf); |
1821 | } | |
1822 | \f/* | |
1823 | ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope | |
1824 | ** | |
1825 | ** Parameters: | |
1826 | ** e -- the envelope to unlock. | |
1827 | ** | |
1828 | ** Returns: | |
1829 | ** none | |
1830 | ** | |
1831 | ** Side Effects: | |
1832 | ** unlocks the queue for `e'. | |
1833 | */ | |
1834 | ||
179d940c | 1835 | void |
560a80d9 EA |
1836 | unlockqueue(e) |
1837 | ENVELOPE *e; | |
1838 | { | |
bc854e30 EA |
1839 | if (tTd(51, 4)) |
1840 | printf("unlockqueue(%s)\n", e->e_id); | |
1841 | ||
3620ad97 EA |
1842 | /* if there is a lock file in the envelope, close it */ |
1843 | if (e->e_lockfp != NULL) | |
bc854e30 | 1844 | xfclose(e->e_lockfp, "unlockqueue", e->e_id); |
3620ad97 EA |
1845 | e->e_lockfp = NULL; |
1846 | ||
32061c38 EA |
1847 | /* don't create a queue id if we don't already have one */ |
1848 | if (e->e_id == NULL) | |
1849 | return; | |
1850 | ||
560a80d9 | 1851 | /* remove the transcript */ |
560a80d9 | 1852 | # ifdef LOG |
68f7099c | 1853 | if (LogLevel > 87) |
560a80d9 | 1854 | syslog(LOG_DEBUG, "%s: unlock", e->e_id); |
f3d8f6d6 | 1855 | # endif /* LOG */ |
bc854e30 | 1856 | if (!tTd(51, 104)) |
560a80d9 EA |
1857 | xunlink(queuename(e, 'x')); |
1858 | ||
560a80d9 | 1859 | } |
3fbc69d6 | 1860 | \f/* |
d2213d6c | 1861 | ** SETCTLUSER -- create a controlling address |
3fbc69d6 | 1862 | ** |
d2213d6c EA |
1863 | ** Create a fake "address" given only a local login name; this is |
1864 | ** used as a "controlling user" for future recipient addresses. | |
3fbc69d6 KB |
1865 | ** |
1866 | ** Parameters: | |
d2213d6c | 1867 | ** user -- the user name of the controlling user. |
3fbc69d6 KB |
1868 | ** |
1869 | ** Returns: | |
d2213d6c | 1870 | ** An address descriptor for the controlling user. |
3fbc69d6 KB |
1871 | ** |
1872 | ** Side Effects: | |
1873 | ** none. | |
1874 | */ | |
1875 | ||
d2213d6c EA |
1876 | ADDRESS * |
1877 | setctluser(user) | |
1878 | char *user; | |
3fbc69d6 | 1879 | { |
d2213d6c | 1880 | register ADDRESS *a; |
3fbc69d6 | 1881 | struct passwd *pw; |
9cdb0b30 | 1882 | char *p; |
3fbc69d6 KB |
1883 | |
1884 | /* | |
d2213d6c | 1885 | ** See if this clears our concept of controlling user. |
3fbc69d6 KB |
1886 | */ |
1887 | ||
c6c403f7 EA |
1888 | if (user == NULL || *user == '\0') |
1889 | return NULL; | |
3fbc69d6 KB |
1890 | |
1891 | /* | |
d2213d6c | 1892 | ** Set up addr fields for controlling user. |
3fbc69d6 KB |
1893 | */ |
1894 | ||
d2213d6c EA |
1895 | a = (ADDRESS *) xalloc(sizeof *a); |
1896 | bzero((char *) a, sizeof *a); | |
9cdb0b30 EA |
1897 | |
1898 | p = strchr(user, ':'); | |
1899 | if (p != NULL) | |
1900 | *p++ = '\0'; | |
5b7a2dfe | 1901 | if (*user != '\0' && (pw = sm_getpwnam(user)) != NULL) |
3fbc69d6 | 1902 | { |
c841d671 EA |
1903 | if (strcmp(pw->pw_dir, "/") == 0) |
1904 | a->q_home = ""; | |
1905 | else | |
1906 | a->q_home = newstr(pw->pw_dir); | |
3fbc69d6 KB |
1907 | a->q_uid = pw->pw_uid; |
1908 | a->q_gid = pw->pw_gid; | |
f7894324 | 1909 | a->q_flags |= QGOODUID; |
3fbc69d6 | 1910 | } |
66d16835 EA |
1911 | |
1912 | if (*user != '\0') | |
1913 | a->q_user = newstr(user); | |
1914 | else if (p != NULL) | |
1915 | a->q_user = newstr(p); | |
3fbc69d6 | 1916 | else |
6e99f903 | 1917 | a->q_user = newstr(DefUser); |
3fbc69d6 | 1918 | |
f7894324 | 1919 | a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ |
d0a69620 | 1920 | a->q_mailer = LocalMailer; |
9cdb0b30 EA |
1921 | if (p == NULL) |
1922 | a->q_paddr = a->q_user; | |
1923 | else | |
1924 | a->q_paddr = newstr(p); | |
d2213d6c | 1925 | return a; |
3fbc69d6 | 1926 | } |
99fd9645 EA |
1927 | \f/* |
1928 | ** LOSEQFILE -- save the qf as Qf and try to let someone know | |
1929 | ** | |
1930 | ** Parameters: | |
1931 | ** e -- the envelope (e->e_id will be used). | |
1932 | ** why -- reported to whomever can hear. | |
1933 | ** | |
1934 | ** Returns: | |
1935 | ** none. | |
1936 | */ | |
1937 | ||
1938 | void | |
1939 | loseqfile(e, why) | |
1940 | register ENVELOPE *e; | |
1941 | char *why; | |
1942 | { | |
6502fd93 | 1943 | char *p; |
99fd9645 EA |
1944 | char buf[40]; |
1945 | ||
1946 | if (e == NULL || e->e_id == NULL) | |
1947 | return; | |
1948 | if (strlen(e->e_id) > sizeof buf - 4) | |
1949 | return; | |
1950 | strcpy(buf, queuename(e, 'q')); | |
6502fd93 EA |
1951 | p = queuename(e, 'Q'); |
1952 | if (rename(buf, p) < 0) | |
1953 | syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); | |
99fd9645 | 1954 | #ifdef LOG |
6502fd93 EA |
1955 | else if (LogLevel > 0) |
1956 | syslog(LOG_ALERT, "Losing %s: %s", buf, why); | |
99fd9645 EA |
1957 | #endif |
1958 | } |