|
|
1.1 root 1: # include "sendmail.h"
2: # include <sys/stat.h>
3: # include <sys/dir.h>
4: # include <signal.h>
5: # include <errno.h>
6:
7: # ifndef QUEUE
8: SCCSID(@(#)queue.c 4.1 7/25/83 (no queueing));
9: # else QUEUE
10:
11: SCCSID(@(#)queue.c 4.1 7/25/83);
12:
13: /*
14: ** Work queue.
15: */
16:
17: struct work
18: {
19: char *w_name; /* name of control file */
20: long w_pri; /* priority of message, see below */
21: struct work *w_next; /* next in queue */
22: };
23:
24: typedef struct work WORK;
25:
26: WORK *WorkQ; /* queue of things to be done */
27: /*
28: ** QUEUEUP -- queue a message up for future transmission.
29: **
30: ** Parameters:
31: ** e -- the envelope to queue up.
32: ** queueall -- if TRUE, queue all addresses, rather than
33: ** just those with the QQUEUEUP flag set.
34: ** announce -- if TRUE, tell when you are queueing up.
35: **
36: ** Returns:
37: ** none.
38: **
39: ** Side Effects:
40: ** The current request are saved in a control file.
41: */
42:
43: queueup(e, queueall, announce)
44: register ENVELOPE *e;
45: bool queueall;
46: bool announce;
47: {
48: char *tf;
49: char *qf;
50: char buf[MAXLINE];
51: register FILE *tfp;
52: register HDR *h;
53: register ADDRESS *q;
54: MAILER nullmailer;
55:
56: /*
57: ** Create control file.
58: */
59:
60: tf = newstr(queuename(e, 't'));
61: tfp = fopen(tf, "w");
62: if (tfp == NULL)
63: {
64: syserr("queueup: cannot create temp file %s", tf);
65: return;
66: }
67: (void) chmod(tf, FileMode);
68:
69: # ifdef DEBUG
70: if (tTd(40, 1))
71: printf("queueing in %s\n", tf);
72: # endif DEBUG
73:
74: /*
75: ** If there is no data file yet, create one.
76: */
77:
78: if (e->e_df == NULL)
79: {
80: register FILE *dfp;
81: extern putbody();
82:
83: e->e_df = newstr(queuename(e, 'd'));
84: dfp = fopen(e->e_df, "w");
85: if (dfp == NULL)
86: {
87: syserr("queueup: cannot create %s", e->e_df);
88: (void) fclose(tfp);
89: return;
90: }
91: (void) chmod(e->e_df, FileMode);
92: (*e->e_putbody)(dfp, ProgMailer, e);
93: (void) fclose(dfp);
94: e->e_putbody = putbody;
95: }
96:
97: /*
98: ** Output future work requests.
99: ** Priority should be first, since it is read by orderq.
100: */
101:
102: /* output message priority */
103: fprintf(tfp, "P%ld\n", e->e_msgpriority);
104:
105: /* output creation time */
106: fprintf(tfp, "T%ld\n", e->e_ctime);
107:
108: /* output name of data file */
109: fprintf(tfp, "D%s\n", e->e_df);
110:
111: /* message from envelope, if it exists */
112: if (e->e_message != NULL)
113: fprintf(tfp, "M%s\n", e->e_message);
114:
115: /* output name of sender */
116: fprintf(tfp, "S%s\n", e->e_from.q_paddr);
117:
118: /* output list of recipient addresses */
119: for (q = e->e_sendqueue; q != NULL; q = q->q_next)
120: {
121: if (queueall ? !bitset(QDONTSEND, q->q_flags) :
122: bitset(QQUEUEUP, q->q_flags))
123: {
124: fprintf(tfp, "R%s\n", q->q_paddr);
125: if (announce)
126: {
127: e->e_to = q->q_paddr;
128: message(Arpa_Info, "queued");
129: if (LogLevel > 4)
130: logdelivery("queued");
131: e->e_to = NULL;
132: }
133: #ifdef DEBUG
134: if (tTd(40, 1))
135: {
136: printf("queueing ");
137: printaddr(q, FALSE);
138: }
139: #endif DEBUG
140: }
141: }
142:
143: /*
144: ** Output headers for this message.
145: ** Expand macros completely here. Queue run will deal with
146: ** everything as absolute headers.
147: ** All headers that must be relative to the recipient
148: ** can be cracked later.
149: ** We set up a "null mailer" -- i.e., a mailer that will have
150: ** no effect on the addresses as they are output.
151: */
152:
153: bzero((char *) &nullmailer, sizeof nullmailer);
154: nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1;
155: nullmailer.m_eol = "\n";
156:
157: define('g', "$f", e);
158: for (h = e->e_header; h != NULL; h = h->h_link)
159: {
160: extern bool bitzerop();
161:
162: /* don't output null headers */
163: if (h->h_value == NULL || h->h_value[0] == '\0')
164: continue;
165:
166: /* don't output resent headers on non-resent messages */
167: if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
168: continue;
169:
170: /* output this header */
171: fprintf(tfp, "H");
172:
173: /* if conditional, output the set of conditions */
174: if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
175: {
176: int j;
177:
178: putc('?', tfp);
179: for (j = '\0'; j <= '\177'; j++)
180: if (bitnset(j, h->h_mflags))
181: putc(j, tfp);
182: putc('?', tfp);
183: }
184:
185: /* output the header: expand macros, convert addresses */
186: if (bitset(H_DEFAULT, h->h_flags))
187: {
188: (void) expand(h->h_value, buf, &buf[sizeof buf], e);
189: fprintf(tfp, "%s: %s\n", h->h_field, buf);
190: }
191: else if (bitset(H_FROM|H_RCPT, h->h_flags))
192: {
193: commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
194: &nullmailer);
195: }
196: else
197: fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
198: }
199:
200: /*
201: ** Clean up.
202: */
203:
204: (void) fclose(tfp);
205: qf = queuename(e, 'q');
206: holdsigs();
207: (void) unlink(qf);
208: if (link(tf, qf) < 0)
209: syserr("cannot link(%s, %s), df=%s", tf, qf, e->e_df);
210: else
211: (void) unlink(tf);
212: rlsesigs();
213:
214: # ifdef LOG
215: /* save log info */
216: if (LogLevel > 15)
217: syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
218: # endif LOG
219: }
220: /*
221: ** RUNQUEUE -- run the jobs in the queue.
222: **
223: ** Gets the stuff out of the queue in some presumably logical
224: ** order and processes them.
225: **
226: ** Parameters:
227: ** none.
228: **
229: ** Returns:
230: ** none.
231: **
232: ** Side Effects:
233: ** runs things in the mail queue.
234: */
235:
236: runqueue(forkflag)
237: bool forkflag;
238: {
239: /*
240: ** See if we want to go off and do other useful work.
241: */
242:
243: if (forkflag)
244: {
245: int pid;
246:
247: pid = dofork();
248: if (pid != 0)
249: {
250: /* parent -- pick up intermediate zombie */
251: (void) waitfor(pid);
252: if (QueueIntvl != 0)
253: (void) setevent(QueueIntvl, runqueue, TRUE);
254: return;
255: }
256: /* child -- double fork */
257: if (fork() != 0)
258: exit(EX_OK);
259: }
260: # ifdef LOG
261: if (LogLevel > 11)
262: syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
263: # endif LOG
264:
265: /*
266: ** Release any resources used by the daemon code.
267: */
268:
269: # ifdef DAEMON
270: clrdaemon();
271: # endif DAEMON
272:
273: /*
274: ** Start making passes through the queue.
275: ** First, read and sort the entire queue.
276: ** Then, process the work in that order.
277: ** But if you take too long, start over.
278: */
279:
280: /* order the existing work requests */
281: (void) orderq();
282:
283: /* process them once at a time */
284: while (WorkQ != NULL)
285: {
286: WORK *w = WorkQ;
287:
288: WorkQ = WorkQ->w_next;
289: dowork(w);
290: free(w->w_name);
291: free((char *) w);
292: }
293: finis();
294: }
295: /*
296: ** ORDERQ -- order the work queue.
297: **
298: ** Parameters:
299: ** none.
300: **
301: ** Returns:
302: ** The number of request in the queue (not necessarily
303: ** the number of requests in WorkQ however).
304: **
305: ** Side Effects:
306: ** Sets WorkQ to the queue of available work, in order.
307: */
308:
309: # define WLSIZE 120 /* max size of worklist per sort */
310:
311: orderq()
312: {
313: register struct direct *d;
314: register WORK *w;
315: register WORK **wp; /* parent of w */
316: DIR *f;
317: register int i;
318: WORK wlist[WLSIZE+1];
319: int wn = -1;
320: extern workcmpf();
321:
322: /* clear out old WorkQ */
323: for (w = WorkQ; w != NULL; )
324: {
325: register WORK *nw = w->w_next;
326:
327: WorkQ = nw;
328: free(w->w_name);
329: free((char *) w);
330: w = nw;
331: }
332:
333: /* open the queue directory */
334: f = opendir(".");
335: if (f == NULL)
336: {
337: syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
338: return (0);
339: }
340:
341: /*
342: ** Read the work directory.
343: */
344:
345: while ((d = readdir(f)) != NULL)
346: {
347: FILE *cf;
348: char lbuf[MAXNAME];
349:
350: /* is this an interesting entry? */
351: if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
352: continue;
353:
354: /* yes -- open control file (if not too many files) */
355: if (++wn >= WLSIZE)
356: continue;
357: cf = fopen(d->d_name, "r");
358: if (cf == NULL)
359: {
360: /* this may be some random person sending hir msgs */
361: /* syserr("orderq: cannot open %s", cbuf); */
362: #ifdef DEBUG
363: if (tTd(41, 2))
364: printf("orderq: cannot open %s (%d)\n",
365: d->d_name, errno);
366: #endif DEBUG
367: errno = 0;
368: wn--;
369: continue;
370: }
371: wlist[wn].w_name = newstr(d->d_name);
372:
373: /* extract useful information */
374: while (fgets(lbuf, sizeof lbuf, cf) != NULL)
375: {
376: if (lbuf[0] == 'P')
377: {
378: (void) sscanf(&lbuf[1], "%ld", &wlist[wn].w_pri);
379: break;
380: }
381: }
382: (void) fclose(cf);
383: }
384: (void) closedir(f);
385: wn++;
386:
387: /*
388: ** Sort the work directory.
389: */
390:
391: qsort(wlist, min(wn, WLSIZE), sizeof *wlist, workcmpf);
392:
393: /*
394: ** Convert the work list into canonical form.
395: ** Should be turning it into a list of envelopes here perhaps.
396: */
397:
398: wp = &WorkQ;
399: for (i = min(wn, WLSIZE); --i >= 0; )
400: {
401: w = (WORK *) xalloc(sizeof *w);
402: w->w_name = wlist[i].w_name;
403: w->w_pri = wlist[i].w_pri;
404: w->w_next = NULL;
405: *wp = w;
406: wp = &w->w_next;
407: }
408:
409: # ifdef DEBUG
410: if (tTd(40, 1))
411: {
412: for (w = WorkQ; w != NULL; w = w->w_next)
413: printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
414: }
415: # endif DEBUG
416:
417: return (wn);
418: }
419: /*
420: ** WORKCMPF -- compare function for ordering work.
421: **
422: ** Parameters:
423: ** a -- the first argument.
424: ** b -- the second argument.
425: **
426: ** Returns:
427: ** 1 if a < b
428: ** 0 if a == b
429: ** -1 if a > b
430: **
431: ** Side Effects:
432: ** none.
433: */
434:
435: workcmpf(a, b)
436: register WORK *a;
437: register WORK *b;
438: {
439: if (a->w_pri == b->w_pri)
440: return (0);
441: else if (a->w_pri > b->w_pri)
442: return (-1);
443: else
444: return (1);
445: }
446: /*
447: ** DOWORK -- do a work request.
448: **
449: ** Parameters:
450: ** w -- the work request to be satisfied.
451: **
452: ** Returns:
453: ** none.
454: **
455: ** Side Effects:
456: ** The work request is satisfied if possible.
457: */
458:
459: dowork(w)
460: register WORK *w;
461: {
462: register int i;
463:
464: # ifdef DEBUG
465: if (tTd(40, 1))
466: printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
467: # endif DEBUG
468:
469: /*
470: ** Fork for work.
471: */
472:
473: i = fork();
474: if (i < 0)
475: {
476: syserr("dowork: cannot fork");
477: return;
478: }
479:
480: if (i == 0)
481: {
482: /*
483: ** CHILD
484: ** Lock the control file to avoid duplicate deliveries.
485: ** Then run the file as though we had just read it.
486: ** We save an idea of the temporary name so we
487: ** can recover on interrupt.
488: */
489:
490: /* set basic modes, etc. */
491: (void) alarm(0);
492: closexscript(CurEnv);
493: CurEnv->e_flags &= ~EF_FATALERRS;
494: QueueRun = TRUE;
495: ErrorMode = EM_MAIL;
496: CurEnv->e_id = &w->w_name[2];
497: # ifdef LOG
498: if (LogLevel > 11)
499: syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
500: getpid());
501: # endif LOG
502:
503: /* don't use the headers from sendmail.cf... */
504: CurEnv->e_header = NULL;
505:
506: /* create the link to the control file during processing */
507: if (link(w->w_name, queuename(CurEnv, 'l')) < 0)
508: {
509: /* being processed by another queuer */
510: # ifdef LOG
511: if (LogLevel > 4)
512: syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id);
513: # endif LOG
514: exit(EX_OK);
515: }
516:
517: /* do basic system initialization */
518: initsys();
519:
520: /* read the queue control file */
521: readqf(CurEnv, TRUE);
522: CurEnv->e_flags |= EF_INQUEUE;
523: eatheader(CurEnv);
524:
525: /* do the delivery */
526: if (!bitset(EF_FATALERRS, CurEnv->e_flags))
527: sendall(CurEnv, SM_DELIVER);
528:
529: /* finish up and exit */
530: finis();
531: }
532:
533: /*
534: ** Parent -- pick up results.
535: */
536:
537: errno = 0;
538: (void) waitfor(i);
539: }
540: /*
541: ** READQF -- read queue file and set up environment.
542: **
543: ** Parameters:
544: ** e -- the envelope of the job to run.
545: ** full -- if set, read in all information. Otherwise just
546: ** read in info needed for a queue print.
547: **
548: ** Returns:
549: ** none.
550: **
551: ** Side Effects:
552: ** cf is read and created as the current job, as though
553: ** we had been invoked by argument.
554: */
555:
556: readqf(e, full)
557: register ENVELOPE *e;
558: bool full;
559: {
560: register FILE *f;
561: char buf[MAXFIELD];
562: extern char *fgetfolded();
563: register char *p;
564:
565: /*
566: ** Open the file created by queueup.
567: */
568:
569: p = queuename(e, 'q');
570: f = fopen(p, "r");
571: if (f == NULL)
572: {
573: syserr("readqf: no control file %s", p);
574: return;
575: }
576: FileName = p;
577: LineNumber = 0;
578:
579: /*
580: ** Read and process the file.
581: */
582:
583: if (Verbose && full)
584: printf("\nRunning %s\n", e->e_id);
585: while (fgetfolded(buf, sizeof buf, f) != NULL)
586: {
587: switch (buf[0])
588: {
589: case 'R': /* specify recipient */
590: sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
591: break;
592:
593: case 'H': /* header */
594: if (full)
595: (void) chompheader(&buf[1], FALSE);
596: break;
597:
598: case 'M': /* message */
599: e->e_message = newstr(&buf[1]);
600: break;
601:
602: case 'S': /* sender */
603: setsender(newstr(&buf[1]));
604: break;
605:
606: case 'D': /* data file name */
607: if (!full)
608: break;
609: e->e_df = newstr(&buf[1]);
610: e->e_dfp = fopen(e->e_df, "r");
611: if (e->e_dfp == NULL)
612: syserr("readqf: cannot open %s", e->e_df);
613: break;
614:
615: case 'T': /* init time */
616: (void) sscanf(&buf[1], "%ld", &e->e_ctime);
617: break;
618:
619: case 'P': /* message priority */
620: (void) sscanf(&buf[1], "%ld", &e->e_msgpriority);
621:
622: /* make sure that big things get sent eventually */
623: e->e_msgpriority -= WKTIMEFACT;
624: break;
625:
626: default:
627: syserr("readqf(%s): bad line \"%s\"", e->e_id, buf);
628: break;
629: }
630: }
631:
632: FileName = NULL;
633: }
634: /*
635: ** PRINTQUEUE -- print out a representation of the mail queue
636: **
637: ** Parameters:
638: ** none.
639: **
640: ** Returns:
641: ** none.
642: **
643: ** Side Effects:
644: ** Prints a listing of the mail queue on the standard output.
645: */
646:
647: printqueue()
648: {
649: register WORK *w;
650: FILE *f;
651: int nrequests;
652: char buf[MAXLINE];
653:
654: /*
655: ** Read and order the queue.
656: */
657:
658: nrequests = orderq();
659:
660: /*
661: ** Print the work list that we have read.
662: */
663:
664: /* first see if there is anything */
665: if (nrequests <= 0)
666: {
667: printf("Mail queue is empty\n");
668: return;
669: }
670:
671: printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
672: if (nrequests > WLSIZE)
673: printf(", only %d printed", WLSIZE);
674: printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
675: for (w = WorkQ; w != NULL; w = w->w_next)
676: {
677: struct stat st;
678: auto time_t submittime = 0;
679: long dfsize = -1;
680: char lf[20];
681: char message[MAXLINE];
682:
683: printf("%7s", w->w_name + 2);
684: strcpy(lf, w->w_name);
685: lf[0] = 'l';
686: if (stat(lf, &st) >= 0)
687: printf("*");
688: else
689: printf(" ");
690: errno = 0;
691: f = fopen(w->w_name, "r");
692: if (f == NULL)
693: {
694: printf(" (finished)\n");
695: errno = 0;
696: continue;
697: }
698: message[0] = '\0';
699: while (fgets(buf, sizeof buf, f) != NULL)
700: {
701: fixcrlf(buf, TRUE);
702: switch (buf[0])
703: {
704: case 'M': /* error message */
705: strcpy(message, &buf[1]);
706: break;
707:
708: case 'S': /* sender name */
709: printf("%8ld %.16s %.45s", dfsize,
710: ctime(&submittime), &buf[1]);
711: if (message[0] != '\0')
712: printf("\n\t\t\t\t (%.43s)", message);
713: break;
714:
715: case 'R': /* recipient name */
716: printf("\n\t\t\t\t %.45s", &buf[1]);
717: break;
718:
719: case 'T': /* creation time */
720: sscanf(&buf[1], "%ld", &submittime);
721: break;
722:
723: case 'D': /* data file name */
724: if (stat(&buf[1], &st) >= 0)
725: dfsize = st.st_size;
726: break;
727: }
728: }
729: if (submittime == (time_t) 0)
730: printf(" (no control file)");
731: printf("\n");
732: fclose(f);
733: }
734: }
735:
736: # endif QUEUE
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.