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