📄 queue.c
字号:
/* * Copyright (c) 1983 Eric P. Allman * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */# include "sendmail.h"# include <sys/stat.h># include <sys/dir.h># include <signal.h># include <errno.h># include <pwd.h># ifndef QUEUESCCSID(@(#)queue.c 1.1 92/07/30 SMI (no queueing)); /* from UCB 5.23 3/13/88 */# else QUEUESCCSID(@(#)queue.c 1.1 92/07/30 SMI); /* from UCB 5.23 3/13/88 *//*** Work queue.*/struct work{ char *w_name; /* name of control file */ long w_pri; /* priority of message, see below */ time_t w_ctime; /* creation time of message */ struct work *w_next; /* next in queue */};typedef struct work WORK;WORK *WorkQ; /* queue of things to be done *//*** QUEUEUP -- queue a message up for future transmission.**** Parameters:** e -- the envelope to queue up.** queueall -- if TRUE, queue all addresses, rather than** just those with the QQUEUEUP flag set.** announce -- if TRUE, tell when you are queueing up.**** Returns:** none.**** Side Effects:** The current request is saved in a control file.*/queueup(e, queueall, announce) register ENVELOPE *e; bool queueall; bool announce;{ char *tf; char *qf; char buf[MAXLINE]; register FILE *tfp; register HDR *h; register ADDRESS *q; MAILER nullmailer; /* ** Create control file. */ tf = newstr(queuename(e, 't')); tfp = fopen(tf, "w"); if (tfp == NULL) { syserr("queueup: cannot create temp file %s", tf); return; } (void) chmod(tf, FileMode);# ifdef DEBUG if (tTd(40, 1)) printf("queueing %s\n", e->e_id);# endif DEBUG /* ** If there is no data file yet, create one. */ if (e->e_df == NULL) { register FILE *dfp; extern putbody(); e->e_df = newstr(queuename(e, 'd')); dfp = fopen(e->e_df, "w"); if (dfp == NULL) { syserr("queueup: cannot create %s", e->e_df); (void) fclose(tfp); return; } (void) chmod(e->e_df, FileMode); (*e->e_putbody)(dfp, ProgMailer, e); (void) fclose(dfp); e->e_putbody = putbody; } /* ** Output future work requests. ** Priority and creation time should be first, since ** they are required by orderq. */ /* output message priority */ fprintf(tfp, "P%ld\n", e->e_msgpriority); /* output creation time */ fprintf(tfp, "T%ld\n", e->e_ctime); /* output name of data file */ fprintf(tfp, "D%s\n", e->e_df); /* message from envelope, if it exists */ if (e->e_message != NULL) fprintf(tfp, "M%s\n", e->e_message); /* output name of sender */ fprintf(tfp, "S%s\n", e->e_from.q_paddr); /* output list of recipient addresses */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (queueall ? !bitset(QDONTSEND, q->q_flags) : bitset(QQUEUEUP, q->q_flags)) { char *ctluser, *getctluser(); if ((ctluser = getctluser(q)) != NULL) fprintf(tfp, "C%s\n", ctluser); fprintf(tfp, "R%s\n", q->q_paddr); if (announce) { e->e_to = q->q_paddr; message(Arpa_Info, "queued"); if (LogLevel > 4) logdelivery("queued"); e->e_to = NULL; }#ifdef DEBUG if (tTd(40, 1)) { printf("queueing "); printaddr(q, FALSE); }#endif } } /* output list of error recipients */ for (q = e->e_errorqueue; q != NULL; q = q->q_next) { if (!bitset(QDONTSEND, q->q_flags)) { char *ctluser, *getctluser(); if ((ctluser = getctluser(q)) != NULL) fprintf(tfp, "C%s\n", ctluser); fprintf(tfp, "E%s\n", q->q_paddr); } } /* ** Output headers for this message. ** Expand macros completely here. Queue run will deal with ** everything as absolute headers. ** All headers that must be relative to the recipient ** can be cracked later. ** We set up a "null mailer" -- i.e., a mailer that will have ** no effect on the addresses as they are output. */ bzero((char *) &nullmailer, sizeof nullmailer); nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; nullmailer.m_eol = "\n"; nullmailer.m_argvsize = 10000; define('g', "\001f", e); for (h = e->e_header; h != NULL; h = h->h_link) { extern bool bitzerop(); /* don't output null headers */ if (h->h_value == NULL || h->h_value[0] == '\0') continue; /* don't output resent headers on non-resent messages */ if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) continue; /* output this header */ fprintf(tfp, "H"); /* if conditional, output the set of conditions */ if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) { int j; (void) putc('?', tfp); for (j = '\0'; j <= '\177'; j++) if (bitnset(j, h->h_mflags)) (void) putc(j, tfp); (void) putc('?', tfp); } /* output the header: expand macros, convert addresses */ if (bitset(H_DEFAULT, h->h_flags)) { (void) expand(h->h_value, buf, &buf[sizeof buf], e); fprintf(tfp, "%s: %s\n", h->h_field, buf); } else if (bitset(H_FROM|H_RCPT, h->h_flags)) { commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), &nullmailer); } else fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); } /* ** Clean up. */ (void) fclose(tfp); qf = queuename(e, 'q'); if (tf != NULL) { (void) unlink(qf); if (rename(tf, qf) < 0) syserr("cannot unlink(%s, %s), df=%s", tf, qf, e->e_df); errno = 0; }# ifdef LOG /* save log info */ if (LogLevel > 15) syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);# endif LOG}/*** checkpoint -- rewrite the qf file to reflect the updated queue of** recipients. WARNING: This function assumes that the recipient** lines are all at the END of the queue files, after all other lines.** "others" points to the list of recipients that have NOT been** processed yet. We save any already processed ** recipients that have been marked for queuing, plus** all the non-processed recipients except those that** have already been delivered to.**/checkpoint(e,others) ENVELOPE *e; ADDRESS *others; { register ADDRESS *to; register FILE *qfp; char buf[MAXFIELD]; extern char *fgetfolded(); long position; if (others==NULL) return; qfp = fopen(queuename(e,'q'),"r+"); if (qfp==NULL) return; while (fgetfolded(buf, sizeof buf, qfp) != NULL) { if (buf[0]=='R') break; position = ftell(qfp); } if (feof(qfp) || ferror(qfp)) { /* * got an error reading the queue file - give up. */ fclose(qfp); return; } fseek(qfp,position,0); for (to = e->e_sendqueue; to != NULL && to != others; to = to->q_next) if (bitset(QQUEUEUP, to->q_flags) ) fprintf(qfp, "R%s\n", to->q_paddr); if (to != NULL) for (; to != NULL; to = to->q_next) if (!bitset(QDONTSEND, to->q_flags) || bitset(QQUEUEUP, to->q_flags) ) fprintf(qfp, "R%s\n", to->q_paddr); ftruncate(fileno(qfp), ftell(qfp)); fclose(qfp); e->e_flags |= EF_INQUEUE; }/*** RUNQUEUE -- run the jobs in the queue.**** Gets the stuff out of the queue in some presumably logical** order and processes them.**** Parameters:** forkflag -- TRUE if the queue scanning should be done in** a child process. We double-fork so it is not our** child and we don't have to clean up after it.**** Returns:** none.**** Side Effects:** runs things in the mail queue.*/runqueue(forkflag) bool forkflag;{ extern bool shouldqueue(); /* ** If no work will ever be selected, don't even bother reading ** the queue. */ if (shouldqueue(-100000000L)) { if (Verbose) printf("Skipping queue run -- load average too high\n"); if (forkflag) return; finis(); } /* ** See if we want to go off and do other useful work. */ if (forkflag) { int pid; pid = dofork(); if (pid != 0) { extern reapchild(); /* parent -- pick up intermediate zombie */#ifndef SIGCHLD (void) waitfor(pid);#else SIGCHLD (void) signal(SIGCHLD, reapchild);#endif SIGCHLD if (QueueIntvl != 0) (void) setevent(QueueIntvl, runqueue, TRUE); return; } /* child -- double fork */#ifndef SIGCHLD if (fork() != 0) exit(EX_OK);#else SIGCHLD (void) signal(SIGCHLD, SIG_DFL);#endif SIGCHLD } setproctitle("running queue");# ifdef LOG if (LogLevel > 11) syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());# endif LOG /* ** Release any resources used by the daemon code. */# ifdef DAEMON clrdaemon();# endif DAEMON /* ** Start making passes through the queue. ** First, read and sort the entire queue. ** Then, process the work in that order. ** But if you take too long, start over. */ /* order the existing work requests */ (void) orderq(FALSE); /* process them once at a time */ while (WorkQ != NULL) { WORK *w = WorkQ; WorkQ = WorkQ->w_next; dowork(w); free(w->w_name); free((char *) w); } /* * Don't let dropenvelope() trash locked queue files! */ CurEnv->e_id = NULL; finis();}/*** ORDERQ -- order the work queue.**** Parameters:** doall -- if set, include everything in the queue (even** the jobs that cannot be run because the load** average is too high). Otherwise, exclude those** jobs.**** Returns:** The number of request in the queue (not necessarily** the number of requests in WorkQ however).**** Side Effects:** Sets WorkQ to the queue of available work, in order.*/# define NEED_P 001# define NEED_T 002# define NEED_R 004orderq(doall) bool doall;{ register struct direct *d; register WORK *w; DIR *f; register int i; WORK wlist[QUEUESIZE+1]; int wn = -1; extern workcmpf(); extern int OnlyRunId; /* main.c */ extern char *OnlyRunRecip; /* main.c */ /* clear out old WorkQ */ for (w = WorkQ; w != NULL; ) { register WORK *nw = w->w_next; WorkQ = nw; free(w->w_name); free((char *) w); w = nw; } /* open the queue directory */ f = opendir("."); if (f == NULL) { syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); return (0); } /* ** Read the work directory. */ while ((d = readdir(f)) != NULL) { FILE *cf; char lbuf[MAXNAME]; /* is this an interesting entry? */ if (d->d_name[0] != 'q' || d->d_name[1] != 'f') continue; /* ** If we're only interested in a particular job, check ** for that one. */ if (OnlyRunId) { if (OnlyRunId != atoi(&d->d_name[4])) { continue; } } /* yes -- open control file (if not too many files) */ if (++wn >= QUEUESIZE) continue; cf = fopen(d->d_name, "r"); if (cf == NULL) { /* this may be some random person sending hir msgs */ /* syserr("orderq: cannot open %s", cbuf); */#ifdef DEBUG if (tTd(41, 2)) printf("orderq: cannot open %s (%d)\n", d->d_name, errno);#endif DEBUG errno = 0; wn--; continue; } w = &wlist[wn]; w->w_name = newstr(d->d_name); /* make sure jobs in creation don't clog queue */ w->w_pri = 0x7fffffff; w->w_ctime = 0; /* extract useful information */ i = NEED_P | NEED_T; if (OnlyRunRecip != 0) i |= NEED_R; while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) { extern long atol(); switch (lbuf[0]) { case 'P': w->w_pri = atol(&lbuf[1]); i &= ~NEED_P; break; case 'T': w->w_ctime = atol(&lbuf[1]); i &= ~NEED_T; break; case 'R': if (i & NEED_R) { register char *sp; for (sp = &lbuf[1]; *sp; sp++) { if (strncasecmp(sp, OnlyRunRecip, strlen(OnlyRunRecip)) == 0) { i &= ~NEED_R; break; } } } } } (void) fclose(cf); /* If recip name didn't match, don't take this queue entry */ if ( (shouldqueue(wlist[wn].w_pri) && !doall) || (i & NEED_R)!= 0) { /* don't even bother sorting this job in */ wn--; } } (void) closedir(f); wn++; /* ** Sort the work directory. */ qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); /* ** Convert the work list into canonical form. ** Should be turning it into a list of envelopes here perhaps. */ WorkQ = NULL; for (i = min(wn, QUEUESIZE); --i >= 0; ) { w = (WORK *) xalloc(sizeof *w); w->w_name = wlist[i].w_name; w->w_pri = wlist[i].w_pri; w->w_ctime = wlist[i].w_ctime; w->w_next = WorkQ; WorkQ = w; }# ifdef DEBUG if (tTd(40, 1)) { for (w = WorkQ; w != NULL; w = w->w_next) printf("%32s: pri=%ld\n", w->w_name, w->w_pri); }# endif DEBUG return (wn);}/*** WORKCMPF -- compare function for ordering work.**** Parameters:** a -- the first argument.** b -- the second argument.**** Returns:** -1 if a < b** 0 if a == b** +1 if a > b**** Side Effects:** none.*/workcmpf(a, b) register WORK *a; register WORK *b;{ long pa = a->w_pri + a->w_ctime; long pb = b->w_pri + b->w_ctime; if (pa == pb) return (0); else if (pa > pb) return (1); else return (-1);}/*** DOWORK -- do a work request.**** Parameters:** w -- the work request to be satisfied.**** Returns:** none.**** Side Effects:** The work request is satisfied if possible.*/staticdowork(w) register WORK *w;{ ENVELOPE *newenvelope(); extern ENVELOPE BlankEnvelope;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -