first "working" visual version
[unix-history] / usr / src / usr.sbin / sendmail / src / queue.c
CommitLineData
81b7d258
EA
1# include "sendmail.h"
2# include <sys/stat.h>
ccd1d833 3# include <ndir.h>
aba51985 4# include <signal.h>
81b7d258
EA
5# include <errno.h>
6
884a20cb 7# ifndef QUEUE
96476cab 8SCCSID(@(#)queue.c 3.35 %G% (no queueing));
884a20cb
EA
9# else QUEUE
10
96476cab 11SCCSID(@(#)queue.c 3.35 %G%);
81b7d258
EA
12
13/*
14** QUEUEUP -- queue a message up for future transmission.
15**
16** The queued message should already be in the correct place.
17** This routine just outputs the control file as appropriate.
18**
19** Parameters:
dd1fe05b 20** e -- the envelope to queue up.
d0bd03ce
EA
21** queueall -- if TRUE, queue all addresses, rather than
22** just those with the QQUEUEUP flag set.
81b7d258
EA
23**
24** Returns:
25** none.
26**
27** Side Effects:
28** The current request (only unsatisfied addresses)
29** are saved in a control file.
30*/
31
d0bd03ce 32queueup(e, queueall)
dd1fe05b 33 register ENVELOPE *e;
d0bd03ce 34 bool queueall;
81b7d258 35{
2cce0c26
EA
36 char *tf;
37 char *qf;
9416f3a0 38 char buf[MAXLINE];
2cce0c26 39 register FILE *tfp;
81b7d258 40 register HDR *h;
d4f42161 41 register ADDRESS *q;
808ac89b 42 register int i;
81b7d258 43
9ccf54c4
EA
44 /*
45 ** Create control file.
46 */
81b7d258 47
2cce0c26
EA
48 tf = newstr(queuename(e, 't'));
49 tfp = fopen(tf, "w");
50 if (tfp == NULL)
81b7d258 51 {
2cce0c26 52 syserr("queueup: cannot create temp file %s", tf);
81b7d258
EA
53 return;
54 }
2cce0c26 55 (void) chmod(tf, 0600);
81b7d258
EA
56
57# ifdef DEBUG
9678c96d 58 if (tTd(40, 1))
2cce0c26 59 printf("queueing in %s\n", tf);
81b7d258
EA
60# endif DEBUG
61
dd1fe05b
EA
62 /*
63 ** If there is no data file yet, create one.
64 */
65
66 if (e->e_df == NULL)
67 {
68 register FILE *dfp;
69
2cce0c26 70 e->e_df = newstr(queuename(e, 'd'));
dd1fe05b
EA
71 dfp = fopen(e->e_df, "w");
72 if (dfp == NULL)
73 {
74 syserr("queueup: cannot create %s", e->e_df);
2cce0c26 75 (void) fclose(tfp);
dd1fe05b
EA
76 return;
77 }
c3eff282 78 (void) chmod(e->e_df, 0600);
dd1fe05b 79 (*e->e_putbody)(dfp, Mailer[1], FALSE);
f9566d23 80 (void) fclose(dfp);
dd1fe05b
EA
81 }
82
81b7d258
EA
83 /*
84 ** Output future work requests.
85 */
86
87 /* output name of data file */
2cce0c26 88 fprintf(tfp, "D%s\n", e->e_df);
81b7d258
EA
89
90 /* output name of sender */
2cce0c26 91 fprintf(tfp, "S%s\n", e->e_from.q_paddr);
81b7d258 92
96476cab
EA
93 /* output creation time */
94 fprintf(tfp, "T%ld\n", e->e_ctime);
81b7d258 95
aba51985 96 /* output message priority */
2cce0c26 97 fprintf(tfp, "P%ld\n", e->e_msgpriority);
dd1fe05b
EA
98
99 /* output message class */
2cce0c26 100 fprintf(tfp, "C%d\n", e->e_class);
aba51985 101
808ac89b 102 /* output macro definitions */
9416f3a0 103 /* I don't think this is needed any more.....
808ac89b
EA
104 for (i = 0; i < 128; i++)
105 {
dd1fe05b 106 register char *p = e->e_macro[i];
808ac89b
EA
107
108 if (p != NULL && i != (int) 'b')
2cce0c26 109 fprintf(tfp, "M%c%s\n", i, p);
808ac89b 110 }
9416f3a0 111 ..... */
808ac89b 112
81b7d258 113 /* output list of recipient addresses */
dd1fe05b 114 for (q = e->e_sendqueue; q != NULL; q = q->q_next)
81b7d258 115 {
9ccf54c4 116# ifdef DEBUG
9678c96d 117 if (tTd(40, 1))
9ccf54c4
EA
118 {
119 printf("queueing ");
120 printaddr(q, FALSE);
121 }
122# endif DEBUG
9416f3a0
EA
123 if (queueall ? !bitset(QDONTSEND, q->q_flags) :
124 bitset(QQUEUEUP, q->q_flags))
2cce0c26 125 fprintf(tfp, "R%s\n", q->q_paddr);
81b7d258
EA
126 }
127
128 /* output headers for this message */
9416f3a0 129 define('g', "$f");
dd1fe05b 130 for (h = e->e_header; h != NULL; h = h->h_link)
81b7d258
EA
131 {
132 if (h->h_value == NULL || h->h_value[0] == '\0')
133 continue;
2cce0c26 134 fprintf(tfp, "H");
81b7d258 135 if (h->h_mflags != 0 && bitset(H_CHECK|H_ACHECK, h->h_flags))
2cce0c26
EA
136 mfdecode(h->h_mflags, tfp);
137 fprintf(tfp, "%s: ", h->h_field);
9416f3a0
EA
138 if (bitset(H_DEFAULT, h->h_flags))
139 {
140 (void) expand(h->h_value, buf, &buf[sizeof buf], e);
2cce0c26 141 fprintf(tfp, "%s\n", buf);
9416f3a0
EA
142 }
143 else
2cce0c26 144 fprintf(tfp, "%s\n", h->h_value);
81b7d258
EA
145 }
146
147 /*
148 ** Clean up.
149 */
150
2cce0c26
EA
151 (void) fclose(tfp);
152 qf = queuename(e, 'q');
153 (void) unlink(qf);
154 if (link(tf, qf) < 0)
155 syserr("cannot link(%s, %s), df=%s", tf, qf, e->e_df);
dd1fe05b 156 else
2cce0c26
EA
157 (void) unlink(tf);
158 e->e_qf = NULL;
1a13ccd8 159
9678c96d
EA
160# ifdef LOG
161 /* save log info */
162 if (LogLevel > 9)
2cce0c26 163 syslog(LOG_INFO, "%s queueup: qf=%s, df=%s\n", e->e_id, qf, e->e_df);
9678c96d
EA
164# endif LOG
165
1a13ccd8
EA
166 /* disconnect this temp file from the job */
167 e->e_df = NULL;
81b7d258
EA
168}
169\f/*
170** RUNQUEUE -- run the jobs in the queue.
171**
172** Gets the stuff out of the queue in some presumably logical
173** order and processes them.
174**
175** Parameters:
176** none.
177**
178** Returns:
179** none.
180**
181** Side Effects:
182** runs things in the mail queue.
183*/
184
2cf55cb7
EA
185bool ReorderQueue; /* if set, reorder the send queue */
186int QueuePid; /* pid of child running queue */
aba51985 187
2cf55cb7
EA
188runqueue(forkflag)
189 bool forkflag;
81b7d258 190{
aba51985 191 extern reordersig();
81b7d258 192
b6dffdf5
EA
193 /*
194 ** See if we want to go off and do other useful work.
195 */
2cf55cb7
EA
196
197 if (forkflag)
198 {
199 QueuePid = dofork();
200 if (QueuePid > 0)
201 {
202 /* parent */
c4442979
EA
203 if (QueueIntvl != 0)
204 setevent(QueueIntvl, reordersig, TRUE);
2cf55cb7
EA
205 return;
206 }
b6dffdf5 207 }
81b7d258 208
b6dffdf5
EA
209 /*
210 ** Start making passes through the queue.
211 ** First, read and sort the entire queue.
212 ** Then, process the work in that order.
213 ** But if you take too long, start over.
214 ** There is a race condition at the end -- we could get
215 ** a reorder signal after finishing the queue.
216 ** In this case we will hang for one more queue
217 ** interval -- clearly a botch, but rare and
218 ** relatively innocuous.
219 */
81b7d258 220
b6dffdf5
EA
221 for (;;)
222 {
223 /* order the existing work requests */
224 orderq();
c4442979
EA
225
226 /* arrange to reorder later */
aba51985 227 ReorderQueue = FALSE;
c4442979
EA
228 if (QueueIntvl != 0)
229 setevent(QueueIntvl, reordersig, FALSE);
aba51985 230
b6dffdf5 231 /* process them once at a time */
aba51985
EA
232 while (WorkQ != NULL)
233 {
234 WORK *w = WorkQ;
235
236 WorkQ = WorkQ->w_next;
237 dowork(w);
238 free(w->w_name);
239 free((char *) w);
240 if (ReorderQueue)
241 break;
242 }
243
b6dffdf5 244 /* if we are just doing one pass, then we are done */
aba51985 245 if (QueueIntvl == 0)
c4442979 246 finis();
b6dffdf5
EA
247
248 /* wait for work -- note (harmless) race condition here */
c4442979
EA
249 while (!ReorderQueue)
250 {
251 if (forkflag)
252 finis();
b6dffdf5 253 pause();
c4442979 254 }
81b7d258
EA
255 }
256}
257\f/*
c4442979 258** REORDERSIG -- catch the reorder signal and tell sendmail to reorder queue.
aba51985
EA
259**
260** Parameters:
c4442979
EA
261** parent -- if set, called from parent (i.e., not
262** really doing the work).
aba51985
EA
263**
264** Returns:
265** none.
266**
267** Side Effects:
268** sets the "reorder work queue" flag.
269*/
270
c4442979
EA
271reordersig(parent)
272 bool parent;
aba51985 273{
c4442979 274 if (!parent)
2cf55cb7
EA
275 {
276 /* we are in a child doing queueing */
277 ReorderQueue = TRUE;
278 }
279 else
280 {
c4442979
EA
281 /*
282 ** In parent. If the child still exists, we want
283 ** to do nothing. If the child is gone, we will
284 ** start up a new one.
285 ** If the child exists, it is responsible for
286 ** doing a queue reorder.
287 ** This code really sucks.
288 */
289
2cf55cb7
EA
290 if (kill(QueuePid, SIGALRM) < 0)
291 {
292 /* no child -- get zombie & start new one */
293 static int st;
294
62fff4f9 295 (void) wait(&st);
c4442979 296 runqueue(TRUE);
2cf55cb7
EA
297 }
298 }
299
300 /*
301 ** Arrange to get this signal again.
302 */
303
c4442979 304 setevent(QueueIntvl, reordersig, parent);
aba51985
EA
305}
306\f/*
81b7d258
EA
307** ORDERQ -- order the work queue.
308**
309** Parameters:
310** none.
311**
312** Returns:
313** none.
314**
315** Side Effects:
316** Sets WorkQ to the queue of available work, in order.
317*/
318
319# define WLSIZE 120 /* max size of worklist per sort */
320
321orderq()
322{
ccd1d833 323 register struct direct *d;
81b7d258
EA
324 register WORK *w;
325 register WORK **wp; /* parent of w */
ccd1d833 326 DIR *f;
81b7d258 327 register int i;
81b7d258
EA
328 WORK wlist[WLSIZE];
329 int wn = 0;
330 extern workcmpf();
331 extern char *QueueDir;
332
333 /* clear out old WorkQ */
334 for (w = WorkQ; w != NULL; )
335 {
336 register WORK *nw = w->w_next;
337
338 WorkQ = nw;
339 free(w->w_name);
340 free((char *) w);
341 w = nw;
342 }
343
344 /* open the queue directory */
ccd1d833 345 f = opendir(QueueDir);
81b7d258
EA
346 if (f == NULL)
347 {
348 syserr("orderq: cannot open %s", QueueDir);
349 return;
350 }
351
352 /*
353 ** Read the work directory.
354 */
355
ccd1d833 356 while (wn < WLSIZE && (d = readdir(f)) != NULL)
81b7d258
EA
357 {
358 char cbuf[MAXNAME];
359 char lbuf[MAXNAME];
360 FILE *cf;
361 register char *p;
362
363 /* is this an interesting entry? */
2cce0c26 364 if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
81b7d258
EA
365 continue;
366
367 /* yes -- find the control file location */
f9566d23
EA
368 (void) strcpy(cbuf, QueueDir);
369 (void) strcat(cbuf, "/");
81b7d258 370 p = &cbuf[strlen(cbuf)];
f9566d23 371 (void) strcpy(p, d->d_name);
81b7d258
EA
372
373 /* open control file */
374 cf = fopen(cbuf, "r");
375 if (cf == NULL)
376 {
91f69adf
EA
377 /* this may be some random person sending hir msgs */
378 /* syserr("orderq: cannot open %s", cbuf); */
379 errno = 0;
81b7d258
EA
380 continue;
381 }
9ccf54c4 382 wlist[wn].w_name = newstr(cbuf);
81b7d258
EA
383
384 /* extract useful information */
81b7d258
EA
385 while (fgets(lbuf, sizeof lbuf, cf) != NULL)
386 {
387 fixcrlf(lbuf, TRUE);
388
389 switch (lbuf[0])
390 {
81b7d258 391 case 'P': /* message priority */
9ccf54c4 392 (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri);
81b7d258
EA
393 break;
394 }
395 }
396 wn++;
397 (void) fclose(cf);
398 }
ccd1d833 399 (void) closedir(f);
81b7d258
EA
400
401 /*
402 ** Sort the work directory.
403 */
404
405 qsort(wlist, wn, sizeof *wlist, workcmpf);
406
407 /*
408 ** Convert the work list into canonical form.
409 */
410
411 wp = &WorkQ;
412 for (i = 0; i < wn; i++)
413 {
414 w = (WORK *) xalloc(sizeof *w);
415 w->w_name = wlist[i].w_name;
81b7d258
EA
416 w->w_pri = wlist[i].w_pri;
417 w->w_next = NULL;
418 *wp = w;
419 wp = &w->w_next;
420 }
421
422# ifdef DEBUG
9678c96d 423 if (tTd(40, 1))
81b7d258
EA
424 {
425 for (w = WorkQ; w != NULL; w = w->w_next)
9ccf54c4 426 printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
81b7d258
EA
427 }
428# endif DEBUG
429}
430\f/*
9678c96d 431** WORKCMPF -- compare function for ordering work.
81b7d258
EA
432**
433** Parameters:
434** a -- the first argument.
435** b -- the second argument.
436**
437** Returns:
438** -1 if a < b
439** 0 if a == b
440** 1 if a > b
441**
442** Side Effects:
443** none.
444*/
445
446# define PRIFACT 1800 /* bytes each priority point is worth */
447
448workcmpf(a, b)
9ccf54c4
EA
449 register WORK *a;
450 register WORK *b;
81b7d258 451{
9ccf54c4 452 if (a->w_pri == b->w_pri)
81b7d258 453 return (0);
9ccf54c4 454 else if (a->w_pri > b->w_pri)
81b7d258
EA
455 return (1);
456 else
457 return (-1);
458}
459\f/*
460** DOWORK -- do a work request.
461**
462** Parameters:
463** w -- the work request to be satisfied.
464**
465** Returns:
466** none.
467**
468** Side Effects:
469** The work request is satisfied if possible.
470*/
471
472dowork(w)
473 register WORK *w;
474{
475 register int i;
476 auto int xstat;
477
478# ifdef DEBUG
9678c96d 479 if (tTd(40, 1))
9ccf54c4 480 printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
81b7d258
EA
481# endif DEBUG
482
483 /*
484 ** Fork for work.
485 */
486
487 i = fork();
488 if (i < 0)
489 {
490 syserr("dowork: cannot fork");
491 return;
492 }
493
494 if (i == 0)
495 {
dd1fe05b
EA
496 char buf[MAXNAME];
497
81b7d258
EA
498 /*
499 ** CHILD
dd1fe05b 500 ** Change the name of the control file to avoid
68f0b54c
EA
501 ** duplicate deliveries. Then run the file
502 ** as though we had just read it.
503 ** We save an idea of the temporary name so we
504 ** can recover on interrupt.
81b7d258
EA
505 */
506
9416f3a0 507 /* set basic modes, etc. */
37eaaadb 508 (void) alarm(0);
16671f5a 509 FatalErrors = FALSE;
81b7d258 510 QueueRun = TRUE;
1ff06f70 511 MailBack = TRUE;
2cce0c26
EA
512 CurEnv->e_qf = w->w_name;
513 CurEnv->e_id = &w->w_name[strlen(QueueDir) + 3];
9416f3a0
EA
514
515 /* don't use the headers from sendmail.cf... */
516 CurEnv->e_header = NULL;
517 chompheader("from: $q", TRUE);
518
519 /* create the link to the control file during processing */
2cce0c26 520 if (link(w->w_name, queuename(CurEnv, 'l')) < 0)
dd1fe05b 521 {
2cce0c26 522 /* being processed by another queuer */
dd1fe05b
EA
523 exit(EX_OK);
524 }
dd1fe05b
EA
525
526 /* create ourselves a transcript file */
aba51985 527 openxscrpt();
dd1fe05b
EA
528
529 /* do basic system initialization */
81b7d258 530 initsys();
dd1fe05b
EA
531
532 /* read the queue control file */
2cce0c26 533 readqf(CurEnv->e_qf);
dd6ec29b 534 eatheader();
dd1fe05b
EA
535
536 /* do the delivery */
a5a315e5
EA
537 if (!FatalErrors)
538 sendall(CurEnv, FALSE);
dd1fe05b
EA
539
540 /* if still not sent, perhaps we should time out.... */
aba51985 541# ifdef DEBUG
9678c96d 542 if (tTd(40, 3))
96476cab
EA
543 printf("CurTime=%ld, TimeOut=%ld\n", CurTime,
544 CurEnv->e_ctime + TimeOut);
aba51985 545# endif DEBUG
96476cab 546 if (CurEnv->e_queueup && CurTime > CurEnv->e_ctime + TimeOut)
aba51985 547 timeout(w);
dd1fe05b 548
dd1fe05b 549 /* finish up and exit */
81b7d258
EA
550 finis();
551 }
552
553 /*
554 ** Parent -- pick up results.
555 */
556
557 errno = 0;
558 while ((i = wait(&xstat)) > 0 && errno != EINTR)
559 {
560 if (errno == EINTR)
561 {
562 errno = 0;
563 }
564 }
565}
566\f/*
567** READQF -- read queue file and set up environment.
568**
569** Parameters:
570** cf -- name of queue control file.
571**
572** Returns:
573** none.
574**
575** Side Effects:
576** cf is read and created as the current job, as though
577** we had been invoked by argument.
578*/
579
580readqf(cf)
581 char *cf;
582{
583 register FILE *f;
6a86d693 584 char buf[MAXFIELD];
d08bed81
EA
585 register char *p;
586 register int i;
abae7b2d 587 extern ADDRESS *sendto();
81b7d258
EA
588
589 /*
590 ** Open the file created by queueup.
591 */
592
593 f = fopen(cf, "r");
594 if (f == NULL)
595 {
596 syserr("readqf: no cf file %s", cf);
597 return;
598 }
599
600 /*
601 ** Read and process the file.
602 */
603
37eaaadb
EA
604 if (Verbose)
605 printf("\nRunning %s\n", cf);
6a86d693 606 while (fgetfolded(buf, sizeof buf, f) != NULL)
81b7d258 607 {
81b7d258
EA
608 switch (buf[0])
609 {
610 case 'R': /* specify recipient */
abae7b2d 611 (void) sendto(&buf[1], 1, (ADDRESS *) NULL, 0);
81b7d258
EA
612 break;
613
614 case 'H': /* header */
615 (void) chompheader(&buf[1], FALSE);
616 break;
617
618 case 'S': /* sender */
aba51985 619 setsender(newstr(&buf[1]));
81b7d258
EA
620 break;
621
622 case 'D': /* data file name */
1ff06f70
EA
623 CurEnv->e_df = newstr(&buf[1]);
624 TempFile = fopen(CurEnv->e_df, "r");
81b7d258 625 if (TempFile == NULL)
1ff06f70 626 syserr("readqf: cannot open %s", CurEnv->e_df);
81b7d258
EA
627 break;
628
96476cab
EA
629 case 'T': /* init time */
630 (void) sscanf(&buf[1], "%ld", &CurEnv->e_ctime);
81b7d258
EA
631 break;
632
aba51985 633 case 'P': /* message priority */
2654b031 634 (void) sscanf(&buf[1], "%ld", &CurEnv->e_msgpriority);
9ccf54c4
EA
635
636 /* make sure that big things get sent eventually */
2654b031 637 CurEnv->e_msgpriority -= WKTIMEFACT;
aba51985
EA
638 break;
639
dd1fe05b
EA
640 case 'C': /* message class */
641 (void) sscanf(&buf[1], "%hd", &CurEnv->e_class);
642 break;
643
808ac89b
EA
644 case 'M': /* define macro */
645 define(buf[1], newstr(&buf[2]));
646 break;
647
81b7d258
EA
648 default:
649 syserr("readqf(%s): bad line \"%s\"", cf, buf);
650 break;
651 }
652 }
653}
654\f/*
655** TIMEOUT -- process timeout on queue file.
656**
657** Parameters:
658** w -- pointer to work request that timed out.
659**
660** Returns:
661** none.
662**
663** Side Effects:
664** Returns a message to the sender saying that this
665** message has timed out.
666*/
667
668timeout(w)
669 register WORK *w;
670{
42c2dff8 671 char buf[MAXLINE];
96476cab 672 extern char *pintvl();
42c2dff8 673
aba51985 674# ifdef DEBUG
9678c96d 675 if (tTd(40, 3))
aba51985
EA
676 printf("timeout(%s)\n", w->w_name);
677# endif DEBUG
1ff06f70 678 message(Arpa_Info, "Message has timed out");
aba51985
EA
679
680 /* return message to sender */
96476cab 681 (void) sprintf(buf, "Cannot send mail for %s", pintvl(TimeOut, FALSE));
42c2dff8 682 (void) returntosender(buf, &CurEnv->e_from, TRUE);
aba51985
EA
683
684 /* arrange to remove files from queue */
96476cab 685 CurEnv->e_dontqueue = TRUE;
81b7d258 686}
884a20cb
EA
687
688# endif QUEUE