diff options
Diffstat (limited to 'src/queue.c')
-rw-r--r-- | src/queue.c | 2422 |
1 files changed, 0 insertions, 2422 deletions
diff --git a/src/queue.c b/src/queue.c deleted file mode 100644 index b02fc08..0000000 --- a/src/queue.c +++ /dev/null @@ -1,2422 +0,0 @@ -/* - * Copyright (c) 1998 Sendmail, Inc. All rights reserved. - * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. - * Copyright (c) 1988, 1993 - * The Regents of the University of California. All rights reserved. - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the sendmail distribution. - * - */ - -# include "sendmail.h" - -#ifndef lint -#if QUEUE -static char sccsid[] = "@(#)queue.c 8.211 (Berkeley) 1/25/1999 (with queueing)"; -#else -static char sccsid[] = "@(#)queue.c 8.211 (Berkeley) 1/25/1999 (without queueing)"; -#endif -#endif /* not lint */ - -# include <errno.h> -# include <dirent.h> - -# if QUEUE - -/* -** Work queue. -*/ - -struct work -{ - char *w_name; /* name of control file */ - char *w_host; /* name of recipient host */ - bool w_lock; /* is message locked? */ - bool w_tooyoung; /* is it too young to run? */ - 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 */ - -#define QF_VERSION 2 /* version number of this queue format */ - -extern int orderq __P((bool)); -/* -** QUEUEUP -- queue a message up for future transmission. -** -** Parameters: -** e -- the envelope to queue up. -** announce -- if TRUE, tell when you are queueing up. -** -** Returns: -** none. -** -** Side Effects: -** The current request are saved in a control file. -** The queue file is left locked. -*/ - -void -queueup(e, announce) - register ENVELOPE *e; - bool announce; -{ - char *qf; - register FILE *tfp; - register HDR *h; - register ADDRESS *q; - int fd; - int i; - bool newid; - register char *p; - MAILER nullmailer; - MCI mcibuf; - char tf[MAXQFNAME]; - char buf[MAXLINE]; - extern void printctladdr __P((ADDRESS *, FILE *)); - - /* - ** Create control file. - */ - - newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); - - /* if newid, queuename will create a locked qf file in e->lockfp */ - strcpy(tf, queuename(e, 't')); - tfp = e->e_lockfp; - if (tfp == NULL) - newid = FALSE; - - /* if newid, just write the qf file directly (instead of tf file) */ - if (!newid) - { - /* get a locked tf file */ - for (i = 0; i < 128; i++) - { - fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); - if (fd < 0) - { - if (errno != EEXIST) - break; - if (LogLevel > 0 && (i % 32) == 0) - sm_syslog(LOG_ALERT, e->e_id, - "queueup: cannot create %s, uid=%d: %s", - tf, geteuid(), errstring(errno)); - } - else - { - if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) - break; - else if (LogLevel > 0 && (i % 32) == 0) - sm_syslog(LOG_ALERT, e->e_id, - "queueup: cannot lock %s: %s", - tf, errstring(errno)); - close(fd); - } - - if ((i % 32) == 31) - { - /* save the old temp file away */ - (void) rename(tf, queuename(e, 'T')); - } - else - sleep(i % 32); - } - if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL) - { - printopenfds(TRUE); - syserr("!queueup: cannot create queue temp file %s, uid=%d", - tf, geteuid()); - } - } - - if (tTd(40, 1)) - printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id, - newid ? " (new id)" : ""); - if (tTd(40, 3)) - { - extern void printenvflags __P((ENVELOPE *)); - - printf(" e_flags="); - printenvflags(e); - } - if (tTd(40, 32)) - { - printf(" sendq="); - printaddr(e->e_sendqueue, TRUE); - } - if (tTd(40, 9)) - { - printf(" tfp="); - dumpfd(fileno(tfp), TRUE, FALSE); - printf(" lockfp="); - if (e->e_lockfp == NULL) - printf("NULL\n"); - else - dumpfd(fileno(e->e_lockfp), TRUE, FALSE); - } - - /* - ** If there is no data file yet, create one. - */ - - if (!bitset(EF_HAS_DF, e->e_flags)) - { - register FILE *dfp = NULL; - char dfname[MAXQFNAME]; - struct stat stbuf; - - strcpy(dfname, queuename(e, 'd')); - fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); - if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL) - syserr("!queueup: cannot create data temp file %s, uid=%d", - dfname, geteuid()); - if (fstat(fd, &stbuf) < 0) - e->e_dfino = -1; - else - { - e->e_dfdev = stbuf.st_dev; - e->e_dfino = stbuf.st_ino; - } - e->e_flags |= EF_HAS_DF; - bzero(&mcibuf, sizeof mcibuf); - mcibuf.mci_out = dfp; - mcibuf.mci_mailer = FileMailer; - (*e->e_putbody)(&mcibuf, e, NULL); - (void) xfclose(dfp, "queueup dfp", e->e_id); - e->e_putbody = putbody; - } - - /* - ** Output future work requests. - ** Priority and creation time should be first, since - ** they are required by orderq. - */ - - /* output queue version number (must be first!) */ - fprintf(tfp, "V%d\n", QF_VERSION); - - /* output creation time */ - fprintf(tfp, "T%ld\n", (long) e->e_ctime); - - /* output last delivery time */ - fprintf(tfp, "K%ld\n", (long) e->e_dtime); - - /* output number of delivery attempts */ - fprintf(tfp, "N%d\n", e->e_ntries); - - /* output message priority */ - fprintf(tfp, "P%ld\n", e->e_msgpriority); - - /* output inode number of data file */ - /* XXX should probably include device major/minor too */ - if (e->e_dfino != -1) - { - if (sizeof e->e_dfino > sizeof(long)) - fprintf(tfp, "I%d/%d/%s\n", - major(e->e_dfdev), minor(e->e_dfdev), - quad_to_string(e->e_dfino)); - else - fprintf(tfp, "I%d/%d/%lu\n", - major(e->e_dfdev), minor(e->e_dfdev), - (unsigned long) e->e_dfino); - } - - /* output body type */ - if (e->e_bodytype != NULL) - fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE)); - -#if _FFR_SAVE_CHARSET - if (e->e_charset != NULL) - fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE)); -#endif - - /* message from envelope, if it exists */ - if (e->e_message != NULL) - fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE)); - - /* send various flag bits through */ - p = buf; - if (bitset(EF_WARNING, e->e_flags)) - *p++ = 'w'; - if (bitset(EF_RESPONSE, e->e_flags)) - *p++ = 'r'; - if (bitset(EF_HAS8BIT, e->e_flags)) - *p++ = '8'; - if (bitset(EF_DELETE_BCC, e->e_flags)) - *p++ = 'b'; - if (bitset(EF_RET_PARAM, e->e_flags)) - *p++ = 'd'; - if (bitset(EF_NO_BODY_RETN, e->e_flags)) - *p++ = 'n'; - *p++ = '\0'; - if (buf[0] != '\0') - fprintf(tfp, "F%s\n", buf); - - /* $r and $s and $_ macro values */ - if ((p = macvalue('r', e)) != NULL) - fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE)); - if ((p = macvalue('s', e)) != NULL) - fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE)); - if ((p = macvalue('_', e)) != NULL) - fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE)); - - /* output name of sender */ - if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) - p = e->e_sender; - else - p = e->e_from.q_paddr; - fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE)); - - /* output ESMTP-supplied "original" information */ - if (e->e_envid != NULL) - fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE)); - - /* output list of recipient addresses */ - printctladdr(NULL, NULL); - for (q = e->e_sendqueue; q != NULL; q = q->q_next) - { - if (bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)) - { -#if XDEBUG - if (bitset(QQUEUEUP, q->q_flags)) - sm_syslog(LOG_DEBUG, e->e_id, - "dropenvelope: q_flags = %x, paddr = %s", - q->q_flags, q->q_paddr); -#endif - continue; - } - printctladdr(q, tfp); - if (q->q_orcpt != NULL) - fprintf(tfp, "Q%s\n", - denlstring(q->q_orcpt, TRUE, FALSE)); - putc('R', tfp); - if (bitset(QPRIMARY, q->q_flags)) - putc('P', tfp); - if (bitset(QHASNOTIFY, q->q_flags)) - putc('N', tfp); - if (bitset(QPINGONSUCCESS, q->q_flags)) - putc('S', tfp); - if (bitset(QPINGONFAILURE, q->q_flags)) - putc('F', tfp); - if (bitset(QPINGONDELAY, q->q_flags)) - putc('D', tfp); - putc(':', tfp); - fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); - if (announce) - { - e->e_to = q->q_paddr; - message("queued"); - if (LogLevel > 8) - logdelivery(q->q_mailer, NULL, "queued", - NULL, (time_t) 0, e); - e->e_to = NULL; - } - if (tTd(40, 1)) - { - printf("queueing "); - printaddr(q, FALSE); - } - } - - /* - ** 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_re_rwset = nullmailer.m_rh_rwset = - nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; - nullmailer.m_eol = "\n"; - bzero(&mcibuf, sizeof mcibuf); - mcibuf.mci_mailer = &nullmailer; - mcibuf.mci_out = tfp; - - define('g', "\201f", e); - for (h = e->e_header; h != NULL; h = h->h_link) - { - extern bool bitzerop __P((BITMAP)); - - if (h->h_value == NULL) - continue; - - /* don't output resent headers on non-resent messages */ - if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) - continue; - - /* expand macros; if null, don't output header at all */ - if (bitset(H_DEFAULT, h->h_flags)) - { - (void) expand(h->h_value, buf, sizeof buf, e); - if (buf[0] == '\0') - 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)) - { - fprintf(tfp, "%s: %s\n", - h->h_field, - denlstring(buf, FALSE, TRUE)); - } - else if (bitset(H_FROM|H_RCPT, h->h_flags)) - { - bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); - FILE *savetrace = TrafficLogFile; - - TrafficLogFile = NULL; - - if (bitset(H_FROM, h->h_flags)) - oldstyle = FALSE; - - commaize(h, h->h_value, oldstyle, &mcibuf, e); - - TrafficLogFile = savetrace; - } - else - { - fprintf(tfp, "%s: %s\n", - h->h_field, - denlstring(h->h_value, FALSE, TRUE)); - } - } - - /* - ** Clean up. - ** - ** Write a terminator record -- this is to prevent - ** scurrilous crackers from appending any data. - */ - - fprintf(tfp, ".\n"); - - if (fflush(tfp) < 0 || - (SuperSafe && fsync(fileno(tfp)) < 0) || - ferror(tfp)) - { - if (newid) - syserr("!552 Error writing control file %s", tf); - else - syserr("!452 Error writing control file %s", tf); - } - - if (!newid) - { - /* rename (locked) tf to be (locked) qf */ - qf = queuename(e, 'q'); - if (rename(tf, qf) < 0) - syserr("cannot rename(%s, %s), uid=%d", - tf, qf, geteuid()); - - /* close and unlock old (locked) qf */ - if (e->e_lockfp != NULL) - (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); - e->e_lockfp = tfp; - } - else - qf = tf; - errno = 0; - e->e_flags |= EF_INQUEUE; - - /* save log info */ - if (LogLevel > 79) - sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf); - - if (tTd(40, 1)) - printf("<<<<< done queueing %s <<<<<\n\n", e->e_id); - return; -} - -void -printctladdr(a, tfp) - register ADDRESS *a; - FILE *tfp; -{ - char *uname; - register ADDRESS *q; - uid_t uid; - gid_t gid; - static ADDRESS *lastctladdr = NULL; - static uid_t lastuid; - - /* initialization */ - if (a == NULL || a->q_alias == NULL || tfp == NULL) - { - if (lastctladdr != NULL && tfp != NULL) - fprintf(tfp, "C\n"); - lastctladdr = NULL; - lastuid = 0; - return; - } - - /* find the active uid */ - q = getctladdr(a); - if (q == NULL) - { - uname = NULL; - uid = 0; - gid = 0; - } - else - { - uname = q->q_ruser != NULL ? q->q_ruser : q->q_user; - uid = q->q_uid; - gid = q->q_gid; - } - a = a->q_alias; - - /* check to see if this is the same as last time */ - if (lastctladdr != NULL && uid == lastuid && - strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) - return; - lastuid = uid; - lastctladdr = a; - - if (uid == 0 || uname == NULL || uname[0] == '\0') - fprintf(tfp, "C"); - else - fprintf(tfp, "C%s:%ld:%ld", - denlstring(uname, TRUE, FALSE), (long) uid, (long) gid); - fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE)); -} -/* -** 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. -** verbose -- if TRUE, print out status information. -** -** Returns: -** TRUE if the queue run successfully began. -** -** Side Effects: -** runs things in the mail queue. -*/ - -ENVELOPE QueueEnvelope; /* the queue run envelope */ -extern int get_num_procs_online __P((void)); - -bool -runqueue(forkflag, verbose) - bool forkflag; - bool verbose; -{ - register ENVELOPE *e; - int njobs; - int sequenceno = 0; - time_t current_la_time; - extern ENVELOPE BlankEnvelope; - extern void clrdaemon __P((void)); - extern void runqueueevent __P((void)); - - DoQueueRun = FALSE; - - /* - ** If no work will ever be selected, don't even bother reading - ** the queue. - */ - - CurrentLA = getla(); /* get load average */ - current_la_time = curtime(); - - if (shouldqueue(WkRecipFact, current_la_time)) - { - char *msg = "Skipping queue run -- load average too high"; - - if (verbose) - message("458 %s\n", msg); - if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: %s", - msg); - if (forkflag && QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, 0); - return FALSE; - } - - /* - ** See if we already have too many children. - */ - - if (forkflag && QueueIntvl != 0 && - MaxChildren > 0 && CurChildren >= MaxChildren) - { - (void) setevent(QueueIntvl, runqueueevent, 0); - return FALSE; - } - - /* - ** See if we want to go off and do other useful work. - */ - - if (forkflag) - { - pid_t pid; - extern SIGFUNC_DECL intsig __P((int)); - extern SIGFUNC_DECL reapchild __P((int)); - - blocksignal(SIGCHLD); - (void) setsignal(SIGCHLD, reapchild); - - pid = dofork(); - if (pid == -1) - { - const char *msg = "Skipping queue run -- fork() failed"; - const char *err = errstring(errno); - - if (verbose) - message("458 %s: %s\n", msg, err); - if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: %s: %s", - msg, err); - if (QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, 0); - (void) releasesignal(SIGCHLD); - return FALSE; - } - if (pid != 0) - { - /* parent -- pick up intermediate zombie */ - (void) blocksignal(SIGALRM); - proc_list_add(pid, "Queue runner"); - (void) releasesignal(SIGALRM); - releasesignal(SIGCHLD); - if (QueueIntvl != 0) - (void) setevent(QueueIntvl, runqueueevent, 0); - return TRUE; - } - /* child -- double fork and clean up signals */ - clrcontrol(); - proc_list_clear(); - - /* Add parent process as first child item */ - proc_list_add(getpid(), "Queue runner child process"); - releasesignal(SIGCHLD); - (void) setsignal(SIGCHLD, SIG_DFL); - (void) setsignal(SIGHUP, intsig); - } - - sm_setproctitle(TRUE, "running queue: %s", QueueDir); - - if (LogLevel > 69) - sm_syslog(LOG_DEBUG, NOQID, - "runqueue %s, pid=%d, forkflag=%d", - QueueDir, getpid(), forkflag); - - /* - ** Release any resources used by the daemon code. - */ - -# if DAEMON - clrdaemon(); -# endif /* DAEMON */ - - /* force it to run expensive jobs */ - NoConnect = FALSE; - - /* drop privileges */ - if (geteuid() == (uid_t) 0) - (void) drop_privileges(FALSE); - - /* - ** Create ourselves an envelope - */ - - CurEnv = &QueueEnvelope; - e = newenvelope(&QueueEnvelope, CurEnv); - e->e_flags = BlankEnvelope.e_flags; - - /* make sure we have disconnected from parent */ - if (forkflag) - { - disconnect(1, e); - QuickAbort = FALSE; - } - - /* - ** Make sure the alias database is open. - */ - - initmaps(FALSE, e); - - /* - ** If we are running part of the queue, always ignore stored - ** host status. - */ - - if (QueueLimitId != NULL || QueueLimitSender != NULL || - QueueLimitRecipient != NULL) - { - IgnoreHostStatus = TRUE; - MinQueueAge = 0; - } - - /* - ** 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 */ - njobs = orderq(FALSE); - - /* process them once at a time */ - while (WorkQ != NULL) - { - WORK *w = WorkQ; - - WorkQ = WorkQ->w_next; - e->e_to = NULL; - - /* - ** Ignore jobs that are too expensive for the moment. - ** - ** Get new load average every 30 seconds. - */ - - if (current_la_time < curtime() - 30) - { - CurrentLA = getla(); - current_la_time = curtime(); - } - if (shouldqueue(WkRecipFact, current_la_time)) - { - char *msg = "Aborting queue run: load average too high"; - - if (Verbose) - message("%s", msg); - if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: %s", - msg); - break; - } - sequenceno++; - if (shouldqueue(w->w_pri, w->w_ctime)) - { - if (Verbose) - message(""); - if (QueueSortOrder == QS_BYPRIORITY) - { - if (Verbose) - message("Skipping %s (sequence %d of %d) and flushing rest of queue", - w->w_name + 2, - sequenceno, - njobs); - if (LogLevel > 8) - sm_syslog(LOG_INFO, NOQID, - "runqueue: Flushing queue from %s (pri %ld, LA %d, %d of %d)", - w->w_name + 2, - w->w_pri, - CurrentLA, - sequenceno, - njobs); - break; - } - else if (Verbose) - message("Skipping %s (sequence %d of %d)", - w->w_name + 2, sequenceno, njobs); - } - else - { - pid_t pid; - - if (Verbose) - { - message(""); - message("Running %s (sequence %d of %d)", - w->w_name + 2, sequenceno, njobs); - } - pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); - errno = 0; - if (pid != 0) - (void) waitfor(pid); - } - free(w->w_name); - if (w->w_host) - free(w->w_host); - free((char *) w); - } - - /* exit without the usual cleanup */ - e->e_id = NULL; - finis(TRUE, ExitStat); - /*NOTREACHED*/ - return TRUE; -} - - -/* -** RUNQUEUEEVENT -- stub for use in setevent -*/ - -void -runqueueevent() -{ - DoQueueRun = TRUE; -} -/* -** 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 004 -# define NEED_S 010 - -static WORK *WorkList = NULL; -static int WorkListSize = 0; - -int -orderq(doall) - bool doall; -{ - register struct dirent *d; - register WORK *w; - register char *p; - DIR *f; - register int i; - int wn = -1; - int wc; - QUEUE_CHAR *check; - - if (tTd(41, 1)) - { - printf("orderq:\n"); - - check = QueueLimitId; - while (check != NULL) - { - printf("\tQueueLimitId = %s\n", - check->queue_match); - check = check->queue_next; - } - - check = QueueLimitSender; - while (check != NULL) - { - printf("\tQueueLimitSender = %s\n", - check->queue_match); - check = check->queue_next; - } - - check = QueueLimitRecipient; - while (check != NULL) - { - printf("\tQueueLimitRecipient = %s\n", - check->queue_match); - check = check->queue_next; - } - } - - /* clear out old WorkQ */ - for (w = WorkQ; w != NULL; ) - { - register WORK *nw = w->w_next; - - WorkQ = nw; - free(w->w_name); - if (w->w_host) - free(w->w_host); - 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; - int qfver = 0; - char lbuf[MAXNAME + 1]; - extern bool strcontainedin __P((char *, char *)); - - if (tTd(41, 50)) - printf("orderq: checking %s\n", d->d_name); - - /* is this an interesting entry? */ - if (d->d_name[0] != 'q' || d->d_name[1] != 'f') - continue; - - if (strlen(d->d_name) > MAXQFNAME) - { - if (Verbose) - printf("orderq: %s too long, %d max characters\n", - d->d_name, MAXQFNAME); - if (LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, - "orderq: %s too long, %d max characters", - d->d_name, MAXQFNAME); - continue; - } - - check = QueueLimitId; - while (check != NULL) - { - if (strcontainedin(check->queue_match, d->d_name)) - break; - else - check = check->queue_next; - } - if (QueueLimitId != NULL && check == NULL) - continue; - -#ifdef PICKY_QF_NAME_CHECK - /* - ** Check queue name for plausibility. This handles - ** both old and new type ids. - */ - - p = d->d_name + 2; - if (isupper(p[0]) && isupper(p[2])) - p += 3; - else if (isupper(p[1])) - p += 2; - else - p = d->d_name; - for (i = 0; isdigit(*p); p++) - i++; - if (i < 5 || *p != '\0') - { - if (Verbose) - printf("orderq: bogus qf name %s\n", d->d_name); - if (LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, - "orderq: bogus qf name %s", - d->d_name); - if (strlen(d->d_name) > (SIZE_T) MAXNAME) - d->d_name[MAXNAME] = '\0'; - strcpy(lbuf, d->d_name); - lbuf[0] = 'Q'; - (void) rename(d->d_name, lbuf); - continue; - } -#endif - - /* open control file (if not too many files) */ - if (++wn >= MaxQueueRun && MaxQueueRun > 0) - { - if (wn == MaxQueueRun && LogLevel > 0) - sm_syslog(LOG_ALERT, NOQID, - "WorkList for %s maxed out at %d", - QueueDir, MaxQueueRun); - continue; - } - if (wn >= WorkListSize) - { - extern void grow_wlist __P((void)); - - grow_wlist(); - if (wn >= WorkListSize) - 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); */ - if (tTd(41, 2)) - printf("orderq: cannot open %s: %s\n", - d->d_name, errstring(errno)); - errno = 0; - wn--; - continue; - } - w = &WorkList[wn]; - w->w_name = newstr(d->d_name); - w->w_host = NULL; - w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); - w->w_tooyoung = FALSE; - - /* 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 (QueueLimitSender != NULL) - i |= NEED_S; - if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL) - i |= NEED_R; - while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) - { - int c; - time_t age; - extern bool strcontainedin __P((char *, char *)); - - p = strchr(lbuf, '\n'); - if (p != NULL) - *p = '\0'; - else - { - /* flush rest of overly long line */ - while ((c = getc(cf)) != EOF && c != '\n') - continue; - } - - switch (lbuf[0]) - { - case 'V': - qfver = atoi(&lbuf[1]); - break; - - 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 (w->w_host == NULL && - (p = strrchr(&lbuf[1], '@')) != NULL) - w->w_host = newstr(&p[1]); - if (QueueLimitRecipient == NULL) - { - i &= ~NEED_R; - break; - } - if (qfver > 0) - { - p = strchr(&lbuf[1], ':'); - if (p == NULL) - p = &lbuf[1]; - } - else - p = &lbuf[1]; - check = QueueLimitRecipient; - while (check != NULL) - { - if (strcontainedin(check->queue_match, - p)) - break; - else - check = check->queue_next; - } - if (check != NULL) - i &= ~NEED_R; - break; - - case 'S': - check = QueueLimitSender; - while (check != NULL) - { - if (strcontainedin(check->queue_match, - &lbuf[1])) - break; - else - check = check->queue_next; - } - if (check != NULL) - i &= ~NEED_S; - break; - - case 'K': - age = curtime() - (time_t) atol(&lbuf[1]); - if (age >= 0 && MinQueueAge > 0 && - age < MinQueueAge) - w->w_tooyoung = TRUE; - break; - - case 'N': - if (atol(&lbuf[1]) == 0) - w->w_tooyoung = FALSE; - break; - } - } - (void) fclose(cf); - - if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || - bitset(NEED_R|NEED_S, i)) - { - /* don't even bother sorting this job in */ - if (tTd(41, 49)) - printf("skipping %s (%x)\n", w->w_name, i); - free(w->w_name); - if (w->w_host) - free(w->w_host); - wn--; - } - } - (void) closedir(f); - wn++; - - wc = min(wn, WorkListSize); - if (wc > MaxQueueRun && MaxQueueRun > 0) - wc = MaxQueueRun; - - if (QueueSortOrder == QS_BYHOST) - { - extern int workcmpf1(); - extern int workcmpf2(); - - /* - ** Sort the work directory for the first time, - ** based on host name, lock status, and priority. - */ - - qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); - - /* - ** If one message to host is locked, "lock" all messages - ** to that host. - */ - - i = 0; - while (i < wc) - { - if (!WorkList[i].w_lock) - { - i++; - continue; - } - w = &WorkList[i]; - while (++i < wc) - { - extern int sm_strcasecmp __P((char *, char *)); - - if (WorkList[i].w_host == NULL && - w->w_host == NULL) - WorkList[i].w_lock = TRUE; - else if (WorkList[i].w_host != NULL && - w->w_host != NULL && - sm_strcasecmp(WorkList[i].w_host, w->w_host) == 0) - WorkList[i].w_lock = TRUE; - else - break; - } - } - - /* - ** Sort the work directory for the second time, - ** based on lock status, host name, and priority. - */ - - qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); - } - else if (QueueSortOrder == QS_BYTIME) - { - extern int workcmpf3(); - - /* - ** Simple sort based on submission time only. - */ - - qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); - } - else - { - extern int workcmpf0(); - - /* - ** Simple sort based on queue priority only. - */ - - qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); - } - - /* - ** Convert the work list into canonical form. - ** Should be turning it into a list of envelopes here perhaps. - */ - - WorkQ = NULL; - for (i = wc; --i >= 0; ) - { - w = (WORK *) xalloc(sizeof *w); - w->w_name = WorkList[i].w_name; - w->w_host = WorkList[i].w_host; - w->w_lock = WorkList[i].w_lock; - w->w_tooyoung = WorkList[i].w_tooyoung; - w->w_pri = WorkList[i].w_pri; - w->w_ctime = WorkList[i].w_ctime; - w->w_next = WorkQ; - WorkQ = w; - } - if (WorkList != NULL) - free(WorkList); - WorkList = NULL; - WorkListSize = 0; - - if (tTd(40, 1)) - { - for (w = WorkQ; w != NULL; w = w->w_next) - printf("%32s: pri=%ld\n", w->w_name, w->w_pri); - } - - return (wn); -} -/* -** GROW_WLIST -- make the work list larger -** -** Parameters: -** none. -** -** Returns: -** none. -** -** Side Effects: -** Adds another QUEUESEGSIZE entries to WorkList if possible. -** It can fail if there isn't enough memory, so WorkListSize -** should be checked again upon return. -*/ - -void -grow_wlist() -{ - if (tTd(41, 1)) - printf("grow_wlist: WorkListSize=%d\n", WorkListSize); - if (WorkList == NULL) - { - WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1)); - WorkListSize = QUEUESEGSIZE; - } - else - { - int newsize = WorkListSize + QUEUESEGSIZE; - WORK *newlist = (WORK *) realloc((char *)WorkList, - (unsigned)sizeof(WORK) * (newsize + 1)); - - if (newlist != NULL) - { - WorkListSize = newsize; - WorkList = newlist; - if (LogLevel > 1) - { - sm_syslog(LOG_NOTICE, NOQID, - "grew WorkList for %s to %d", - QueueDir, WorkListSize); - } - } - else if (LogLevel > 0) - { - sm_syslog(LOG_ALERT, NOQID, - "FAILED to grow WorkList for %s to %d", - QueueDir, newsize); - } - } - if (tTd(41, 1)) - printf("grow_wlist: WorkListSize now %d\n", WorkListSize); -} -/* -** WORKCMPF0 -- simple priority-only compare function. -** -** 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. -*/ - -int -workcmpf0(a, b) - register WORK *a; - register WORK *b; -{ - long pa = a->w_pri; - long pb = b->w_pri; - - if (pa == pb) - return 0; - else if (pa > pb) - return 1; - else - return -1; -} -/* -** WORKCMPF1 -- first compare function for ordering work based on host name. -** -** Sorts on host name, lock status, and priority in that order. -** -** Parameters: -** a -- the first argument. -** b -- the second argument. -** -** Returns: -** <0 if a < b -** 0 if a == b -** >0 if a > b -** -** Side Effects: -** none. -*/ - -int -workcmpf1(a, b) - register WORK *a; - register WORK *b; -{ - int i; - extern int sm_strcasecmp __P((char *, char *)); - - /* host name */ - if (a->w_host != NULL && b->w_host == NULL) - return 1; - else if (a->w_host == NULL && b->w_host != NULL) - return -1; - if (a->w_host != NULL && b->w_host != NULL && - (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) - return i; - - /* lock status */ - if (a->w_lock != b->w_lock) - return b->w_lock - a->w_lock; - - /* job priority */ - return a->w_pri - b->w_pri; -} -/* -** WORKCMPF2 -- second compare function for ordering work based on host name. -** -** Sorts on lock status, host name, and priority in that order. -** -** Parameters: -** a -- the first argument. -** b -- the second argument. -** -** Returns: -** <0 if a < b -** 0 if a == b -** >0 if a > b -** -** Side Effects: -** none. -*/ - -int -workcmpf2(a, b) - register WORK *a; - register WORK *b; -{ - int i; - extern int sm_strcasecmp __P((char *, char *)); - - /* lock status */ - if (a->w_lock != b->w_lock) - return a->w_lock - b->w_lock; - - /* host name */ - if (a->w_host != NULL && b->w_host == NULL) - return 1; - else if (a->w_host == NULL && b->w_host != NULL) - return -1; - if (a->w_host != NULL && b->w_host != NULL && - (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) - return i; - - /* job priority */ - return a->w_pri - b->w_pri; -} -/* -** WORKCMPF3 -- simple submission-time-only compare function. -** -** 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. -*/ - -int -workcmpf3(a, b) - register WORK *a; - register WORK *b; -{ - if (a->w_ctime > b->w_ctime) - return 1; - else if (a->w_ctime < b->w_ctime) - return -1; - else - return 0; -} -/* -** DOWORK -- do a work request. -** -** Parameters: -** id -- the ID of the job to run. -** forkflag -- if set, run this in background. -** requeueflag -- if set, reinstantiate the queue quickly. -** This is used when expanding aliases in the queue. -** If forkflag is also set, it doesn't wait for the -** child. -** e - the envelope in which to run it. -** -** Returns: -** process id of process that is running the queue job. -** -** Side Effects: -** The work request is satisfied if possible. -*/ - -pid_t -dowork(id, forkflag, requeueflag, e) - char *id; - bool forkflag; - bool requeueflag; - register ENVELOPE *e; -{ - register pid_t pid; - extern bool readqf __P((ENVELOPE *)); - - if (tTd(40, 1)) - printf("dowork(%s)\n", id); - - /* - ** Fork for work. - */ - - if (forkflag) - { - pid = fork(); - if (pid < 0) - { - syserr("dowork: cannot fork"); - return 0; - } - else if (pid > 0) - { - /* parent -- clean out connection cache */ - mci_flush(FALSE, NULL); - } - else - { - /* child -- error messages to the transcript */ - QuickAbort = OnlyOneError = FALSE; - - /* - ** Since the delivery may happen in a child and the - ** parent does not wait, the parent may close the - ** maps thereby removing any shared memory used by - ** the map. Therefore, open a copy of the maps for - ** the delivery process. - */ - - initmaps(FALSE, e); - } - } - else - { - pid = 0; - } - - if (pid == 0) - { - /* - ** CHILD - ** Lock the control file to avoid duplicate deliveries. - ** Then run the file as though we had just read it. - ** We save an idea of the temporary name so we - ** can recover on interrupt. - */ - - /* set basic modes, etc. */ - (void) alarm(0); - clearenvelope(e, FALSE); - e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; - e->e_sendmode = SM_DELIVER; - e->e_errormode = EM_MAIL; - e->e_id = id; - GrabTo = UseErrorsTo = FALSE; - ExitStat = EX_OK; - if (forkflag) - { - disconnect(1, e); - OpMode = MD_DELIVER; - } - sm_setproctitle(TRUE, "%s: from queue", id); - if (LogLevel > 76) - sm_syslog(LOG_DEBUG, e->e_id, - "dowork, pid=%d", - getpid()); - - /* don't use the headers from sendmail.cf... */ - e->e_header = NULL; - - /* read the queue control file -- return if locked */ - if (!readqf(e)) - { - if (tTd(40, 4) && e->e_id != NULL) - printf("readqf(%s) failed\n", e->e_id); - e->e_id = NULL; - if (forkflag) - finis(FALSE, EX_OK); - else - return 0; - } - - e->e_flags |= EF_INQUEUE; - eatheader(e, requeueflag); - - if (requeueflag) - queueup(e, FALSE); - - /* do the delivery */ - sendall(e, SM_DELIVER); - - /* finish up and exit */ - if (forkflag) - finis(TRUE, ExitStat); - else - dropenvelope(e, TRUE); - } - e->e_id = NULL; - return pid; -} -/* -** READQF -- read queue file and set up environment. -** -** Parameters: -** e -- the envelope of the job to run. -** -** Returns: -** TRUE if it successfully read the queue file. -** FALSE otherwise. -** -** Side Effects: -** The queue file is returned locked. -*/ - -bool -readqf(e) - register ENVELOPE *e; -{ - register FILE *qfp; - ADDRESS *ctladdr; - struct stat st; - char *bp; - int qfver = 0; - long hdrsize = 0; - register char *p; - char *orcpt = NULL; - bool nomore = FALSE; - char qf[MAXQFNAME]; - char buf[MAXLINE]; - extern ADDRESS *setctluser __P((char *, int)); - - /* - ** Read and process the file. - */ - - strcpy(qf, queuename(e, 'q')); - qfp = fopen(qf, "r+"); - if (qfp == NULL) - { - if (tTd(40, 8)) - printf("readqf(%s): fopen failure (%s)\n", - qf, errstring(errno)); - if (errno != ENOENT) - syserr("readqf: no control file %s", qf); - return FALSE; - } - - if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) - { - /* being processed by another queuer */ - if (Verbose || tTd(40, 8)) - printf("%s: locked\n", e->e_id); - if (LogLevel > 19) - sm_syslog(LOG_DEBUG, e->e_id, "locked"); - (void) fclose(qfp); - return FALSE; - } - - /* - ** Check the queue file for plausibility to avoid attacks. - */ - - if (fstat(fileno(qfp), &st) < 0) - { - /* must have been being processed by someone else */ - if (tTd(40, 8)) - printf("readqf(%s): fstat failure (%s)\n", - qf, errstring(errno)); - fclose(qfp); - return FALSE; - } - - if ((st.st_uid != geteuid() && geteuid() != RealUid) || - bitset(S_IWOTH|S_IWGRP, st.st_mode)) - { - if (LogLevel > 0) - { - sm_syslog(LOG_ALERT, e->e_id, - "bogus queue file, uid=%d, mode=%o", - st.st_uid, st.st_mode); - } - if (tTd(40, 8)) - printf("readqf(%s): bogus file\n", qf); - loseqfile(e, "bogus file uid in mqueue"); - fclose(qfp); - return FALSE; - } - - if (st.st_size == 0) - { - /* must be a bogus file -- if also old, just remove it */ - if (st.st_ctime + 10 * 60 < curtime()) - { - qf[0] = 'd'; - (void) unlink(qf); - qf[0] = 'q'; - (void) unlink(qf); - } - fclose(qfp); - return FALSE; - } - - if (st.st_nlink == 0) - { - /* - ** Race condition -- we got a file just as it was being - ** unlinked. Just assume it is zero length. - */ - - fclose(qfp); - return FALSE; - } - - /* good file -- save this lock */ - e->e_lockfp = qfp; - - /* do basic system initialization */ - initsys(e); - define('i', e->e_id, e); - - LineNumber = 0; - e->e_flags |= EF_GLOBALERRS; - OpMode = MD_DELIVER; - ctladdr = NULL; - e->e_dfino = -1; - e->e_msgsize = -1; - while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) - { - register char *p; - u_long qflags; - ADDRESS *q; - int mid; - auto char *ep; - - if (tTd(40, 4)) - printf("+++++ %s\n", bp); - if (nomore) - { - /* hack attack */ - syserr("SECURITY ALERT: extra data in qf: %s", bp); - fclose(qfp); - loseqfile(e, "bogus queue line"); - return FALSE; - } - switch (bp[0]) - { - case 'V': /* queue file version number */ - qfver = atoi(&bp[1]); - if (qfver <= QF_VERSION) - break; - syserr("Version number in qf (%d) greater than max (%d)", - qfver, QF_VERSION); - fclose(qfp); - loseqfile(e, "unsupported qf file version"); - return FALSE; - - case 'C': /* specify controlling user */ - ctladdr = setctluser(&bp[1], qfver); - break; - - case 'Q': /* original recipient */ - orcpt = newstr(&bp[1]); - break; - - case 'R': /* specify recipient */ - p = bp; - qflags = 0; - if (qfver >= 1) - { - /* get flag bits */ - while (*++p != '\0' && *p != ':') - { - switch (*p) - { - case 'N': - qflags |= QHASNOTIFY; - break; - - case 'S': - qflags |= QPINGONSUCCESS; - break; - - case 'F': - qflags |= QPINGONFAILURE; - break; - - case 'D': - qflags |= QPINGONDELAY; - break; - - case 'P': - qflags |= QPRIMARY; - break; - } - } - } - else - qflags |= QPRIMARY; - q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e); - if (q != NULL) - { - q->q_alias = ctladdr; - if (qfver >= 1) - q->q_flags &= ~Q_PINGFLAGS; - q->q_flags |= qflags; - q->q_orcpt = orcpt; - (void) recipient(q, &e->e_sendqueue, 0, e); - } - orcpt = NULL; - break; - - case 'E': /* specify error recipient */ - /* no longer used */ - break; - - case 'H': /* header */ - (void) chompheader(&bp[1], FALSE, NULL, e); - hdrsize += strlen(&bp[1]); - break; - - case 'L': /* Solaris Content-Length: */ - case 'M': /* message */ - /* ignore this; we want a new message next time */ - break; - - case 'S': /* sender */ - setsender(newstr(&bp[1]), e, NULL, '\0', TRUE); - break; - - case 'B': /* body type */ - e->e_bodytype = newstr(&bp[1]); - break; - -#if _FFR_SAVE_CHARSET - case 'X': /* character set */ - e->e_charset = newstr(&bp[1]); - break; -#endif - - case 'D': /* data file name */ - /* obsolete -- ignore */ - break; - - case 'T': /* init time */ - e->e_ctime = atol(&bp[1]); - break; - - case 'I': /* data file's inode number */ - /* regenerated below */ - break; - - case 'K': /* time of last deliver attempt */ - e->e_dtime = atol(&buf[1]); - break; - - case 'N': /* number of delivery attempts */ - e->e_ntries = atoi(&buf[1]); - - /* if this has been tried recently, let it be */ - if (e->e_ntries > 0 && - MinQueueAge > 0 && e->e_dtime <= curtime() && - curtime() < e->e_dtime + MinQueueAge) - { - char *howlong = pintvl(curtime() - e->e_dtime, TRUE); - - if (Verbose || tTd(40, 8)) - printf("%s: too young (%s)\n", - e->e_id, howlong); - if (LogLevel > 19) - sm_syslog(LOG_DEBUG, e->e_id, - "too young (%s)", - howlong); - e->e_id = NULL; - unlockqueue(e); - return FALSE; - } - break; - - case 'P': /* message priority */ - e->e_msgpriority = atol(&bp[1]) + WkTimeFact; - break; - - case 'F': /* flag bits */ - if (strncmp(bp, "From ", 5) == 0) - { - /* we are being spoofed! */ - syserr("SECURITY ALERT: bogus qf line %s", bp); - fclose(qfp); - loseqfile(e, "bogus queue line"); - return FALSE; - } - for (p = &bp[1]; *p != '\0'; p++) - { - switch (*p) - { - case 'w': /* warning sent */ - e->e_flags |= EF_WARNING; - break; - - case 'r': /* response */ - e->e_flags |= EF_RESPONSE; - break; - - case '8': /* has 8 bit data */ - e->e_flags |= EF_HAS8BIT; - break; - - case 'b': /* delete Bcc: header */ - e->e_flags |= EF_DELETE_BCC; - break; - - case 'd': /* envelope has DSN RET= */ - e->e_flags |= EF_RET_PARAM; - break; - - case 'n': /* don't return body */ - e->e_flags |= EF_NO_BODY_RETN; - break; - } - } - break; - - case 'Z': /* original envelope id from ESMTP */ - e->e_envid = newstr(&bp[1]); - break; - - case '$': /* define macro */ - mid = macid(&bp[1], &ep); - define(mid, newstr(ep), e); - break; - - case '.': /* terminate file */ - nomore = TRUE; - break; - - default: - syserr("readqf: %s: line %d: bad line \"%s\"", - qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); - fclose(qfp); - loseqfile(e, "unrecognized line"); - return FALSE; - } - - if (bp != buf) - free(bp); - } - - /* - ** If we haven't read any lines, this queue file is empty. - ** Arrange to remove it without referencing any null pointers. - */ - - if (LineNumber == 0) - { - errno = 0; - e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; - return TRUE; - } - - /* - ** Arrange to read the data file. - */ - - p = queuename(e, 'd'); - e->e_dfp = fopen(p, "r"); - if (e->e_dfp == NULL) - { - syserr("readqf: cannot open %s", p); - } - else - { - e->e_flags |= EF_HAS_DF; - if (fstat(fileno(e->e_dfp), &st) >= 0) - { - e->e_msgsize = st.st_size + hdrsize; - e->e_dfdev = st.st_dev; - e->e_dfino = st.st_ino; - } - } - - return TRUE; -} -/* -** PRINTQUEUE -- print out a representation of the mail queue -** -** Parameters: -** none. -** -** Returns: -** none. -** -** Side Effects: -** Prints a listing of the mail queue on the standard output. -*/ - -void -printqueue() -{ - register WORK *w; - FILE *f; - int nrequests; - char buf[MAXLINE]; - - /* - ** Check for permission to print the queue - */ - - if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) - { - struct stat st; -# ifdef NGROUPS_MAX - int n; - extern GIDSET_T InitialGidSet[NGROUPS_MAX]; -# endif - - if (stat(QueueDir, &st) < 0) - { - syserr("Cannot stat %s", QueueDir); - return; - } -# ifdef NGROUPS_MAX - n = NGROUPS_MAX; - while (--n >= 0) - { - if (InitialGidSet[n] == st.st_gid) - break; - } - if (n < 0 && RealGid != st.st_gid) -# else - if (RealGid != st.st_gid) -# endif - { - usrerr("510 You are not permitted to see the queue"); - setstat(EX_NOPERM); - return; - } - } - - /* - ** Read and order the queue. - */ - - nrequests = orderq(TRUE); - - /* - ** Print the work list that we have read. - */ - - /* first see if there is anything */ - if (nrequests <= 0) - { - printf("Mail queue is empty\n"); - return; - } - - CurrentLA = getla(); /* get load average */ - - printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); - if (MaxQueueRun > 0 && nrequests > MaxQueueRun) - printf(", only %d printed", MaxQueueRun); - if (Verbose) - printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); - else - printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); - for (w = WorkQ; w != NULL; w = w->w_next) - { - struct stat st; - auto time_t submittime = 0; - long dfsize; - int flags = 0; - int qfver; - char statmsg[MAXLINE]; - char bodytype[MAXNAME + 1]; - - printf("%8s", w->w_name + 2); - f = fopen(w->w_name, "r"); - if (f == NULL) - { - printf(" (job completed)\n"); - errno = 0; - continue; - } - w->w_name[0] = 'd'; - if (stat(w->w_name, &st) >= 0) - dfsize = st.st_size; - else - dfsize = -1; - if (w->w_lock) - printf("*"); - else if (w->w_tooyoung) - printf("-"); - else if (shouldqueue(w->w_pri, w->w_ctime)) - printf("X"); - else - printf(" "); - errno = 0; - - statmsg[0] = bodytype[0] = '\0'; - qfver = 0; - while (fgets(buf, sizeof buf, f) != NULL) - { - register int i; - register char *p; - - fixcrlf(buf, TRUE); - switch (buf[0]) - { - case 'V': /* queue file version */ - qfver = atoi(&buf[1]); - break; - - case 'M': /* error message */ - if ((i = strlen(&buf[1])) >= sizeof statmsg) - i = sizeof statmsg - 1; - bcopy(&buf[1], statmsg, i); - statmsg[i] = '\0'; - break; - - case 'B': /* body type */ - if ((i = strlen(&buf[1])) >= sizeof bodytype) - i = sizeof bodytype - 1; - bcopy(&buf[1], bodytype, i); - bodytype[i] = '\0'; - break; - - case 'S': /* sender name */ - if (Verbose) - printf("%8ld %10ld%c%.12s %.78s", - dfsize, - w->w_pri, - bitset(EF_WARNING, flags) ? '+' : ' ', - ctime(&submittime) + 4, - &buf[1]); - else - printf("%8ld %.16s %.45s", dfsize, - ctime(&submittime), &buf[1]); - if (statmsg[0] != '\0' || bodytype[0] != '\0') - { - printf("\n %10.10s", bodytype); - if (statmsg[0] != '\0') - printf(" (%.*s)", - Verbose ? 100 : 60, - statmsg); - } - break; - - case 'C': /* controlling user */ - if (Verbose) - printf("\n\t\t\t\t (---%.74s---)", - &buf[1]); - break; - - case 'R': /* recipient name */ - p = &buf[1]; - if (qfver >= 1) - { - p = strchr(p, ':'); - if (p == NULL) - break; - p++; - } - if (Verbose) - printf("\n\t\t\t\t\t %.78s", p); - else - printf("\n\t\t\t\t %.45s", p); - break; - - case 'T': /* creation time */ - submittime = atol(&buf[1]); - break; - - case 'F': /* flag bits */ - for (p = &buf[1]; *p != '\0'; p++) - { - switch (*p) - { - case 'w': - flags |= EF_WARNING; - break; - } - } - } - } - if (submittime == (time_t) 0) - printf(" (no control file)"); - printf("\n"); - (void) fclose(f); - } -} - -# endif /* QUEUE */ -/* -** QUEUENAME -- build a file name in the queue directory for this envelope. -** -** Assigns an id code if one does not already exist. -** This code is very careful to avoid trashing existing files -** under any circumstances. -** -** Parameters: -** e -- envelope to build it in/from. -** type -- the file type, used as the first character -** of the file name. -** -** Returns: -** a pointer to the new file name (in a static buffer). -** -** Side Effects: -** If no id code is already assigned, queuename will -** assign an id code, create a qf file, and leave a -** locked, open-for-write file pointer in the envelope. -*/ - -#ifndef ENOLCK -# define ENOLCK -1 -#endif -#ifndef ENOSPC -# define ENOSPC -1 -#endif - -char * -queuename(e, type) - register ENVELOPE *e; - int type; -{ - static pid_t pid = -1; - static char c0; - static char c1; - static char c2; - time_t now; - struct tm *tm; - static char buf[MAXNAME + 1]; - - if (e->e_id == NULL) - { - char qf[MAXQFNAME]; - - /* find a unique id */ - if (pid != getpid()) - { - /* new process -- start back at "AA" */ - pid = getpid(); - now = curtime(); - tm = localtime(&now); - c0 = 'A' + tm->tm_hour; - c1 = 'A'; - c2 = 'A' - 1; - } - (void) snprintf(qf, sizeof qf, "qf%cAA%05d", c0, pid); - - while (c1 < '~' || c2 < 'Z') - { - int i; - int attempts = 0; - - if (c2 >= 'Z') - { - c1++; - c2 = 'A' - 1; - } - qf[3] = c1; - qf[4] = ++c2; - if (tTd(7, 20)) - printf("queuename: trying \"%s\"\n", qf); - - i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); - if (i < 0) - { - if (errno == EEXIST) - continue; - syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", - qf, QueueDir, geteuid()); - finis(FALSE, EX_UNAVAILABLE); - } - do - { - if (attempts > 0) - sleep(attempts); - e->e_lockfp = 0; - if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) - { - e->e_lockfp = fdopen(i, "w"); - break; - } - } while ((errno == ENOLCK || errno == ENOSPC) && - attempts++ < 4); - - /* Successful lock */ - if (e->e_lockfp != 0) - break; - -#if !HASFLOCK - if (errno != EAGAIN && errno != EACCES) -#else - if (errno != EWOULDBLOCK) -#endif - { - syserr("queuename: Cannot lock \"%s\" in \"%s\" (euid=%d)", - qf, QueueDir, geteuid()); - finis(FALSE, EX_OSERR); - } - - /* a reader got the file; abandon it and try again */ - (void) close(i); - } - if (c1 >= '~' && c2 >= 'Z') - { - syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", - qf, QueueDir, geteuid()); - finis(FALSE, EX_OSERR); - } - e->e_id = newstr(&qf[2]); - define('i', e->e_id, e); - if (tTd(7, 1)) - printf("queuename: assigned id %s, env=%lx\n", - e->e_id, (u_long) e); - if (tTd(7, 9)) - { - printf(" lockfd="); - dumpfd(fileno(e->e_lockfp), TRUE, FALSE); - } - if (LogLevel > 93) - sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); - } - - if (type == '\0') - return (NULL); - (void) snprintf(buf, sizeof buf, "%cf%s", type, e->e_id); - if (tTd(7, 2)) - printf("queuename: %s\n", buf); - return (buf); -} -/* -** UNLOCKQUEUE -- unlock the queue entry for a specified envelope -** -** Parameters: -** e -- the envelope to unlock. -** -** Returns: -** none -** -** Side Effects: -** unlocks the queue for `e'. -*/ - -void -unlockqueue(e) - ENVELOPE *e; -{ - if (tTd(51, 4)) - printf("unlockqueue(%s)\n", - e->e_id == NULL ? "NOQUEUE" : e->e_id); - - /* if there is a lock file in the envelope, close it */ - if (e->e_lockfp != NULL) - xfclose(e->e_lockfp, "unlockqueue", e->e_id); - e->e_lockfp = NULL; - - /* don't create a queue id if we don't already have one */ - if (e->e_id == NULL) - return; - - /* remove the transcript */ - if (LogLevel > 87) - sm_syslog(LOG_DEBUG, e->e_id, "unlock"); - if (!tTd(51, 104)) - xunlink(queuename(e, 'x')); - -} -/* -** SETCTLUSER -- create a controlling address -** -** Create a fake "address" given only a local login name; this is -** used as a "controlling user" for future recipient addresses. -** -** Parameters: -** user -- the user name of the controlling user. -** qfver -- the version stamp of this qf file. -** -** Returns: -** An address descriptor for the controlling user. -** -** Side Effects: -** none. -*/ - -ADDRESS * -setctluser(user, qfver) - char *user; - int qfver; -{ - register ADDRESS *a; - struct passwd *pw; - char *p; - - /* - ** See if this clears our concept of controlling user. - */ - - if (user == NULL || *user == '\0') - return NULL; - - /* - ** Set up addr fields for controlling user. - */ - - a = (ADDRESS *) xalloc(sizeof *a); - bzero((char *) a, sizeof *a); - - if (*user == '\0') - { - p = NULL; - a->q_user = newstr(DefUser); - } - else if (*user == ':') - { - p = &user[1]; - a->q_user = newstr(p); - } - else - { - p = strtok(user, ":"); - a->q_user = newstr(user); - if (qfver >= 2) - { - if ((p = strtok(NULL, ":")) != NULL) - a->q_uid = atoi(p); - if ((p = strtok(NULL, ":")) != NULL) - a->q_gid = atoi(p); - if ((p = strtok(NULL, ":")) != NULL) - a->q_flags |= QGOODUID; - } - else if ((pw = sm_getpwnam(user)) != NULL) - { - if (strcmp(pw->pw_dir, "/") == 0) - a->q_home = ""; - else - a->q_home = newstr(pw->pw_dir); - a->q_uid = pw->pw_uid; - a->q_gid = pw->pw_gid; - a->q_flags |= QGOODUID; - } - } - - a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ - a->q_mailer = LocalMailer; - if (p == NULL) - a->q_paddr = a->q_user; - else - a->q_paddr = newstr(p); - return a; -} -/* -** LOSEQFILE -- save the qf as Qf and try to let someone know -** -** Parameters: -** e -- the envelope (e->e_id will be used). -** why -- reported to whomever can hear. -** -** Returns: -** none. -*/ - -void -loseqfile(e, why) - register ENVELOPE *e; - char *why; -{ - char *p; - char buf[MAXQFNAME + 1]; - - if (e == NULL || e->e_id == NULL) - return; - p = queuename(e, 'q'); - if (strlen(p) > MAXQFNAME) - { - syserr("loseqfile: queuename (%s) too long", p); - return; - } - strcpy(buf, p); - p = queuename(e, 'Q'); - if (rename(buf, p) < 0) - syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); - else if (LogLevel > 0) - sm_syslog(LOG_ALERT, e->e_id, - "Losing %s: %s", buf, why); -} |