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