122708Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333731Sbostic * Copyright (c) 1988 Regents of the University of California. 433731Sbostic * All rights reserved. 533731Sbostic * 633731Sbostic * Redistribution and use in source and binary forms are permitted 734921Sbostic * provided that the above copyright notice and this paragraph are 834921Sbostic * duplicated in all such forms and that any documentation, 934921Sbostic * advertising materials, and other materials related to such 1034921Sbostic * distribution and use acknowledge that the software was developed 1134921Sbostic * by the University of California, Berkeley. The name of the 1234921Sbostic * University may not be used to endorse or promote products derived 1334921Sbostic * from this software without specific prior written permission. 1434921Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1534921Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1634921Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1733731Sbostic */ 1822708Sdist 1933731Sbostic # include "sendmail.h" 2022708Sdist 2133731Sbostic #ifndef lint 2233731Sbostic #ifdef QUEUE 23*40973Sbostic static char sccsid[] = "@(#)queue.c 5.28 (Berkeley) 04/18/90 (with queueing)"; 2433731Sbostic #else 25*40973Sbostic static char sccsid[] = "@(#)queue.c 5.28 (Berkeley) 04/18/90 (without queueing)"; 2633731Sbostic #endif 2733731Sbostic #endif /* not lint */ 2833731Sbostic 294632Seric # include <sys/stat.h> 3013707Ssam # include <sys/dir.h> 3140934Srick # include <sys/file.h> 324634Seric # include <signal.h> 334632Seric # include <errno.h> 34*40973Sbostic # include <pwd.h> 354632Seric 3633731Sbostic # ifdef QUEUE 374632Seric 384632Seric /* 399377Seric ** Work queue. 409377Seric */ 419377Seric 429377Seric struct work 439377Seric { 449377Seric char *w_name; /* name of control file */ 459377Seric long w_pri; /* priority of message, see below */ 4625013Seric time_t w_ctime; /* creation time of message */ 479377Seric struct work *w_next; /* next in queue */ 489377Seric }; 499377Seric 509377Seric typedef struct work WORK; 5140934Srick extern int la; 529377Seric 539377Seric WORK *WorkQ; /* queue of things to be done */ 549377Seric /* 554632Seric ** QUEUEUP -- queue a message up for future transmission. 564632Seric ** 574632Seric ** Parameters: 586980Seric ** e -- the envelope to queue up. 596999Seric ** queueall -- if TRUE, queue all addresses, rather than 606999Seric ** just those with the QQUEUEUP flag set. 619377Seric ** announce -- if TRUE, tell when you are queueing up. 624632Seric ** 634632Seric ** Returns: 6440934Srick ** locked FILE* to q file 654632Seric ** 664632Seric ** Side Effects: 679377Seric ** The current request are saved in a control file. 684632Seric */ 694632Seric 7040934Srick FILE * 719377Seric queueup(e, queueall, announce) 726980Seric register ENVELOPE *e; 736999Seric bool queueall; 749377Seric bool announce; 754632Seric { 767812Seric char *tf; 777812Seric char *qf; 787763Seric char buf[MAXLINE]; 797812Seric register FILE *tfp; 804632Seric register HDR *h; 815007Seric register ADDRESS *q; 8210173Seric MAILER nullmailer; 8340934Srick int fd; 844632Seric 855037Seric /* 8617477Seric ** Create control file. 875037Seric */ 884632Seric 8917477Seric tf = newstr(queuename(e, 't')); 9040934Srick fd = open(tf, O_CREAT|O_WRONLY, FileMode); 9140934Srick if (fd < 0) 924632Seric { 9317477Seric syserr("queueup: cannot create temp file %s", tf); 9440934Srick return NULL; 954632Seric } 9640934Srick tfp = fdopen(fd, "w"); 974632Seric 987677Seric if (tTd(40, 1)) 9917468Seric printf("queueing %s\n", e->e_id); 1004632Seric 1014632Seric /* 1026980Seric ** If there is no data file yet, create one. 1036980Seric */ 1046980Seric 1056980Seric if (e->e_df == NULL) 1066980Seric { 1076980Seric register FILE *dfp; 1089389Seric extern putbody(); 1096980Seric 1107812Seric e->e_df = newstr(queuename(e, 'd')); 11140934Srick fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode); 11240934Srick if (fd < 0) 1136980Seric { 1146980Seric syserr("queueup: cannot create %s", e->e_df); 1157812Seric (void) fclose(tfp); 11640934Srick return NULL; 1176980Seric } 11840934Srick dfp = fdopen(fd, "w"); 11910173Seric (*e->e_putbody)(dfp, ProgMailer, e); 1207009Seric (void) fclose(dfp); 1219389Seric e->e_putbody = putbody; 1226980Seric } 1236980Seric 1246980Seric /* 1254632Seric ** Output future work requests. 12625687Seric ** Priority and creation time should be first, since 12725687Seric ** they are required by orderq. 1284632Seric */ 1294632Seric 1309377Seric /* output message priority */ 1319377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 1329377Seric 1339630Seric /* output creation time */ 1349630Seric fprintf(tfp, "T%ld\n", e->e_ctime); 1359630Seric 1364632Seric /* output name of data file */ 1377812Seric fprintf(tfp, "D%s\n", e->e_df); 1384632Seric 13910108Seric /* message from envelope, if it exists */ 14010108Seric if (e->e_message != NULL) 14110108Seric fprintf(tfp, "M%s\n", e->e_message); 14210108Seric 1434632Seric /* output name of sender */ 1447812Seric fprintf(tfp, "S%s\n", e->e_from.q_paddr); 1454632Seric 1464632Seric /* output list of recipient addresses */ 1476980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 1484632Seric { 1497763Seric if (queueall ? !bitset(QDONTSEND, q->q_flags) : 1507763Seric bitset(QQUEUEUP, q->q_flags)) 1518245Seric { 152*40973Sbostic char *ctluser, *getctluser(); 153*40973Sbostic 154*40973Sbostic if ((ctluser = getctluser(q)) != NULL) 155*40973Sbostic fprintf(tfp, "C%s\n", ctluser); 1567812Seric fprintf(tfp, "R%s\n", q->q_paddr); 1579377Seric if (announce) 1589377Seric { 1599377Seric e->e_to = q->q_paddr; 1609377Seric message(Arpa_Info, "queued"); 1619377Seric if (LogLevel > 4) 1629377Seric logdelivery("queued"); 1639377Seric e->e_to = NULL; 1649377Seric } 1659387Seric if (tTd(40, 1)) 1669387Seric { 1679387Seric printf("queueing "); 1689387Seric printaddr(q, FALSE); 1699387Seric } 1708245Seric } 1714632Seric } 1724632Seric 17325687Seric /* output list of error recipients */ 17425687Seric for (q = e->e_errorqueue; q != NULL; q = q->q_next) 17525687Seric { 17626504Seric if (!bitset(QDONTSEND, q->q_flags)) 177*40973Sbostic { 178*40973Sbostic char *ctluser, *getctluser(); 179*40973Sbostic 180*40973Sbostic if ((ctluser = getctluser(q)) != NULL) 181*40973Sbostic fprintf(tfp, "C%s\n", ctluser); 18226504Seric fprintf(tfp, "E%s\n", q->q_paddr); 183*40973Sbostic } 18425687Seric } 18525687Seric 1869377Seric /* 1879377Seric ** Output headers for this message. 1889377Seric ** Expand macros completely here. Queue run will deal with 1899377Seric ** everything as absolute headers. 1909377Seric ** All headers that must be relative to the recipient 1919377Seric ** can be cracked later. 19210173Seric ** We set up a "null mailer" -- i.e., a mailer that will have 19310173Seric ** no effect on the addresses as they are output. 1949377Seric */ 1959377Seric 19610686Seric bzero((char *) &nullmailer, sizeof nullmailer); 19710173Seric nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; 19810349Seric nullmailer.m_eol = "\n"; 19910173Seric 20016147Seric define('g', "\001f", e); 2016980Seric for (h = e->e_header; h != NULL; h = h->h_link) 2024632Seric { 20310686Seric extern bool bitzerop(); 20410686Seric 20512015Seric /* don't output null headers */ 2064632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 2074632Seric continue; 20812015Seric 20912015Seric /* don't output resent headers on non-resent messages */ 21012015Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 21112015Seric continue; 21212015Seric 21312015Seric /* output this header */ 2147812Seric fprintf(tfp, "H"); 21512015Seric 21612015Seric /* if conditional, output the set of conditions */ 21710686Seric if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 21810686Seric { 21910686Seric int j; 22010686Seric 22123098Seric (void) putc('?', tfp); 22210686Seric for (j = '\0'; j <= '\177'; j++) 22310686Seric if (bitnset(j, h->h_mflags)) 22423098Seric (void) putc(j, tfp); 22523098Seric (void) putc('?', tfp); 22610686Seric } 22712015Seric 22812015Seric /* output the header: expand macros, convert addresses */ 2297763Seric if (bitset(H_DEFAULT, h->h_flags)) 2307763Seric { 2317763Seric (void) expand(h->h_value, buf, &buf[sizeof buf], e); 2328236Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 2337763Seric } 2348245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 2359348Seric { 2369348Seric commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 23710173Seric &nullmailer); 2389348Seric } 2397763Seric else 2408245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 2414632Seric } 2424632Seric 2434632Seric /* 2444632Seric ** Clean up. 2454632Seric */ 2464632Seric 24740934Srick if (flock(fileno(tfp), LOCK_EX|LOCK_NB) < 0) 24840934Srick { 24940934Srick syserr("cannot flock(%s)", tf); 25040934Srick } 25140934Srick 25217468Seric qf = queuename(e, 'q'); 25317468Seric if (tf != NULL) 25417468Seric { 25517468Seric (void) unlink(qf); 25624968Seric if (rename(tf, qf) < 0) 25740934Srick syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df); 25824968Seric errno = 0; 25917468Seric } 2607391Seric 2617677Seric # ifdef LOG 2627677Seric /* save log info */ 2637878Seric if (LogLevel > 15) 2647878Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 2657677Seric # endif LOG 26640934Srick fflush(tfp); 26740934Srick return tfp; 2684632Seric } 2694632Seric /* 2704632Seric ** RUNQUEUE -- run the jobs in the queue. 2714632Seric ** 2724632Seric ** Gets the stuff out of the queue in some presumably logical 2734632Seric ** order and processes them. 2744632Seric ** 2754632Seric ** Parameters: 27624941Seric ** forkflag -- TRUE if the queue scanning should be done in 27724941Seric ** a child process. We double-fork so it is not our 27824941Seric ** child and we don't have to clean up after it. 2794632Seric ** 2804632Seric ** Returns: 2814632Seric ** none. 2824632Seric ** 2834632Seric ** Side Effects: 2844632Seric ** runs things in the mail queue. 2854632Seric */ 2864632Seric 2874639Seric runqueue(forkflag) 2884639Seric bool forkflag; 2894632Seric { 29024953Seric extern bool shouldqueue(); 29124953Seric 2927466Seric /* 29324953Seric ** If no work will ever be selected, don't even bother reading 29424953Seric ** the queue. 29524953Seric */ 29624953Seric 29740934Srick la = getla(); /* get load average */ 29840934Srick 29924953Seric if (shouldqueue(-100000000L)) 30024953Seric { 30124953Seric if (Verbose) 30224953Seric printf("Skipping queue run -- load average too high\n"); 30324953Seric 30424953Seric if (forkflag) 30524953Seric return; 30624953Seric finis(); 30724953Seric } 30824953Seric 30924953Seric /* 3107466Seric ** See if we want to go off and do other useful work. 3117466Seric */ 3124639Seric 3134639Seric if (forkflag) 3144639Seric { 3157943Seric int pid; 3167943Seric 3177943Seric pid = dofork(); 3187943Seric if (pid != 0) 3194639Seric { 32025184Seric extern reapchild(); 32125184Seric 3227943Seric /* parent -- pick up intermediate zombie */ 32325184Seric #ifndef SIGCHLD 3249377Seric (void) waitfor(pid); 32525184Seric #else SIGCHLD 32625184Seric (void) signal(SIGCHLD, reapchild); 32725184Seric #endif SIGCHLD 3287690Seric if (QueueIntvl != 0) 3299348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 3304639Seric return; 3314639Seric } 3327943Seric /* child -- double fork */ 33325184Seric #ifndef SIGCHLD 3347943Seric if (fork() != 0) 3357943Seric exit(EX_OK); 33625184Seric #else SIGCHLD 33725184Seric (void) signal(SIGCHLD, SIG_DFL); 33825184Seric #endif SIGCHLD 3394639Seric } 34024941Seric 34140934Srick setproctitle("running queue: %s", QueueDir); 34224941Seric 3437876Seric # ifdef LOG 3447876Seric if (LogLevel > 11) 3457943Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 3467876Seric # endif LOG 3474639Seric 3487466Seric /* 34910205Seric ** Release any resources used by the daemon code. 35010205Seric */ 35110205Seric 35210205Seric # ifdef DAEMON 35310205Seric clrdaemon(); 35410205Seric # endif DAEMON 35510205Seric 35610205Seric /* 35727175Seric ** Make sure the alias database is open. 35827175Seric */ 35927175Seric 36027175Seric initaliases(AliasFile, FALSE); 36127175Seric 36227175Seric /* 3637466Seric ** Start making passes through the queue. 3647466Seric ** First, read and sort the entire queue. 3657466Seric ** Then, process the work in that order. 3667466Seric ** But if you take too long, start over. 3677466Seric */ 3687466Seric 3697943Seric /* order the existing work requests */ 37024954Seric (void) orderq(FALSE); 3717690Seric 3727943Seric /* process them once at a time */ 3737943Seric while (WorkQ != NULL) 3744639Seric { 3757943Seric WORK *w = WorkQ; 3767881Seric 3777943Seric WorkQ = WorkQ->w_next; 3787943Seric dowork(w); 3797943Seric free(w->w_name); 3807943Seric free((char *) w); 3814639Seric } 38229866Seric 38329866Seric /* exit without the usual cleanup */ 38429866Seric exit(ExitStat); 3854634Seric } 3864634Seric /* 3874632Seric ** ORDERQ -- order the work queue. 3884632Seric ** 3894632Seric ** Parameters: 39024941Seric ** doall -- if set, include everything in the queue (even 39124941Seric ** the jobs that cannot be run because the load 39224941Seric ** average is too high). Otherwise, exclude those 39324941Seric ** jobs. 3944632Seric ** 3954632Seric ** Returns: 39610121Seric ** The number of request in the queue (not necessarily 39710121Seric ** the number of requests in WorkQ however). 3984632Seric ** 3994632Seric ** Side Effects: 4004632Seric ** Sets WorkQ to the queue of available work, in order. 4014632Seric */ 4024632Seric 40325687Seric # define NEED_P 001 40425687Seric # define NEED_T 002 4054632Seric 40624941Seric orderq(doall) 40724941Seric bool doall; 4084632Seric { 4096625Sglickman register struct direct *d; 4104632Seric register WORK *w; 4116625Sglickman DIR *f; 4124632Seric register int i; 41325687Seric WORK wlist[QUEUESIZE+1]; 41410070Seric int wn = -1; 4154632Seric extern workcmpf(); 4164632Seric 4174632Seric /* clear out old WorkQ */ 4184632Seric for (w = WorkQ; w != NULL; ) 4194632Seric { 4204632Seric register WORK *nw = w->w_next; 4214632Seric 4224632Seric WorkQ = nw; 4234632Seric free(w->w_name); 4244632Seric free((char *) w); 4254632Seric w = nw; 4264632Seric } 4274632Seric 4284632Seric /* open the queue directory */ 4298148Seric f = opendir("."); 4304632Seric if (f == NULL) 4314632Seric { 4328148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 43310070Seric return (0); 4344632Seric } 4354632Seric 4364632Seric /* 4374632Seric ** Read the work directory. 4384632Seric */ 4394632Seric 44010070Seric while ((d = readdir(f)) != NULL) 4414632Seric { 4429377Seric FILE *cf; 4434632Seric char lbuf[MAXNAME]; 4444632Seric 4454632Seric /* is this an interesting entry? */ 4467812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 4474632Seric continue; 4484632Seric 44910070Seric /* yes -- open control file (if not too many files) */ 45025687Seric if (++wn >= QUEUESIZE) 45110070Seric continue; 4528148Seric cf = fopen(d->d_name, "r"); 4534632Seric if (cf == NULL) 4544632Seric { 4557055Seric /* this may be some random person sending hir msgs */ 4567055Seric /* syserr("orderq: cannot open %s", cbuf); */ 45710090Seric if (tTd(41, 2)) 45810090Seric printf("orderq: cannot open %s (%d)\n", 45910090Seric d->d_name, errno); 4607055Seric errno = 0; 46110090Seric wn--; 4624632Seric continue; 4634632Seric } 46425687Seric w = &wlist[wn]; 46525687Seric w->w_name = newstr(d->d_name); 4664632Seric 46725027Seric /* make sure jobs in creation don't clog queue */ 46825687Seric w->w_pri = 0x7fffffff; 46925687Seric w->w_ctime = 0; 47025027Seric 4714632Seric /* extract useful information */ 47225687Seric i = NEED_P | NEED_T; 47325687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 4744632Seric { 47524954Seric extern long atol(); 47624954Seric 47724941Seric switch (lbuf[0]) 4784632Seric { 47924941Seric case 'P': 48025687Seric w->w_pri = atol(&lbuf[1]); 48125687Seric i &= ~NEED_P; 4824632Seric break; 48325013Seric 48425013Seric case 'T': 48525687Seric w->w_ctime = atol(&lbuf[1]); 48625687Seric i &= ~NEED_T; 48725013Seric break; 4884632Seric } 4894632Seric } 4904632Seric (void) fclose(cf); 49124953Seric 49225687Seric if (!doall && shouldqueue(w->w_pri)) 49324953Seric { 49424953Seric /* don't even bother sorting this job in */ 49524953Seric wn--; 49624953Seric } 4974632Seric } 4986625Sglickman (void) closedir(f); 49910090Seric wn++; 5004632Seric 5014632Seric /* 5024632Seric ** Sort the work directory. 5034632Seric */ 5044632Seric 50525687Seric qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); 5064632Seric 5074632Seric /* 5084632Seric ** Convert the work list into canonical form. 5099377Seric ** Should be turning it into a list of envelopes here perhaps. 5104632Seric */ 5114632Seric 51224981Seric WorkQ = NULL; 51325687Seric for (i = min(wn, QUEUESIZE); --i >= 0; ) 5144632Seric { 5154632Seric w = (WORK *) xalloc(sizeof *w); 5164632Seric w->w_name = wlist[i].w_name; 5174632Seric w->w_pri = wlist[i].w_pri; 51825013Seric w->w_ctime = wlist[i].w_ctime; 51924981Seric w->w_next = WorkQ; 52024981Seric WorkQ = w; 5214632Seric } 5224632Seric 5237677Seric if (tTd(40, 1)) 5244632Seric { 5254632Seric for (w = WorkQ; w != NULL; w = w->w_next) 5265037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 5274632Seric } 52810070Seric 52910090Seric return (wn); 5304632Seric } 5314632Seric /* 5327677Seric ** WORKCMPF -- compare function for ordering work. 5334632Seric ** 5344632Seric ** Parameters: 5354632Seric ** a -- the first argument. 5364632Seric ** b -- the second argument. 5374632Seric ** 5384632Seric ** Returns: 53924981Seric ** -1 if a < b 54024981Seric ** 0 if a == b 54124981Seric ** +1 if a > b 5424632Seric ** 5434632Seric ** Side Effects: 5444632Seric ** none. 5454632Seric */ 5464632Seric 5474632Seric workcmpf(a, b) 5485037Seric register WORK *a; 5495037Seric register WORK *b; 5504632Seric { 55125013Seric long pa = a->w_pri + a->w_ctime; 55225013Seric long pb = b->w_pri + b->w_ctime; 55324941Seric 55424941Seric if (pa == pb) 5554632Seric return (0); 55624941Seric else if (pa > pb) 55724981Seric return (1); 55824981Seric else 55910121Seric return (-1); 5604632Seric } 5614632Seric /* 5624632Seric ** DOWORK -- do a work request. 5634632Seric ** 5644632Seric ** Parameters: 5654632Seric ** w -- the work request to be satisfied. 5664632Seric ** 5674632Seric ** Returns: 5684632Seric ** none. 5694632Seric ** 5704632Seric ** Side Effects: 5714632Seric ** The work request is satisfied if possible. 5724632Seric */ 5734632Seric 5744632Seric dowork(w) 5754632Seric register WORK *w; 5764632Seric { 5774632Seric register int i; 57824941Seric extern bool shouldqueue(); 5794632Seric 5807677Seric if (tTd(40, 1)) 5815037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 5824632Seric 5834632Seric /* 58424941Seric ** Ignore jobs that are too expensive for the moment. 5854632Seric */ 5864632Seric 58724941Seric if (shouldqueue(w->w_pri)) 5884632Seric { 58924941Seric if (Verbose) 59024967Seric printf("\nSkipping %s\n", w->w_name + 2); 5914632Seric return; 5924632Seric } 5934632Seric 59424941Seric /* 59524941Seric ** Fork for work. 59624941Seric */ 59724941Seric 59824941Seric if (ForkQueueRuns) 59924941Seric { 60024941Seric i = fork(); 60124941Seric if (i < 0) 60224941Seric { 60324941Seric syserr("dowork: cannot fork"); 60424941Seric return; 60524941Seric } 60624941Seric } 60724941Seric else 60824941Seric { 60924941Seric i = 0; 61024941Seric } 61124941Seric 6124632Seric if (i == 0) 6134632Seric { 61440934Srick FILE *qflock, *readqf(); 6154632Seric /* 6164632Seric ** CHILD 6178148Seric ** Lock the control file to avoid duplicate deliveries. 6188148Seric ** Then run the file as though we had just read it. 6197350Seric ** We save an idea of the temporary name so we 6207350Seric ** can recover on interrupt. 6214632Seric */ 6224632Seric 6237763Seric /* set basic modes, etc. */ 6247356Seric (void) alarm(0); 62525612Seric clearenvelope(CurEnv, FALSE); 6264632Seric QueueRun = TRUE; 6279377Seric ErrorMode = EM_MAIL; 6288148Seric CurEnv->e_id = &w->w_name[2]; 6297876Seric # ifdef LOG 6307876Seric if (LogLevel > 11) 6317881Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 6327881Seric getpid()); 6337876Seric # endif LOG 6347763Seric 6357763Seric /* don't use the headers from sendmail.cf... */ 6367763Seric CurEnv->e_header = NULL; 6377763Seric 63840934Srick /* read the queue control file */ 63940934Srick /* and lock the control file during processing */ 64040934Srick if ((qflock=readqf(CurEnv, TRUE)) == NULL) 6416980Seric { 64224941Seric if (ForkQueueRuns) 64324941Seric exit(EX_OK); 64424941Seric else 64524941Seric return; 6466980Seric } 6476980Seric 6489338Seric CurEnv->e_flags |= EF_INQUEUE; 6499377Seric eatheader(CurEnv); 6506980Seric 6516980Seric /* do the delivery */ 6529338Seric if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 6539282Seric sendall(CurEnv, SM_DELIVER); 6546980Seric 65540934Srick fclose(qflock); 6566980Seric /* finish up and exit */ 65724941Seric if (ForkQueueRuns) 65824941Seric finis(); 65924941Seric else 66024941Seric dropenvelope(CurEnv); 6614632Seric } 66224941Seric else 66324941Seric { 66424941Seric /* 66524941Seric ** Parent -- pick up results. 66624941Seric */ 6674632Seric 66824941Seric errno = 0; 66924941Seric (void) waitfor(i); 67024941Seric } 6714632Seric } 6724632Seric /* 6734632Seric ** READQF -- read queue file and set up environment. 6744632Seric ** 6754632Seric ** Parameters: 6769377Seric ** e -- the envelope of the job to run. 6779630Seric ** full -- if set, read in all information. Otherwise just 6789630Seric ** read in info needed for a queue print. 6794632Seric ** 6804632Seric ** Returns: 68140934Srick ** FILE * pointing to flock()ed fd so it can be closed 68240934Srick ** after the mail is delivered 6834632Seric ** 6844632Seric ** Side Effects: 6854632Seric ** cf is read and created as the current job, as though 6864632Seric ** we had been invoked by argument. 6874632Seric */ 6884632Seric 68940934Srick FILE * 69017477Seric readqf(e, full) 6919377Seric register ENVELOPE *e; 6929630Seric bool full; 6934632Seric { 69417477Seric char *qf; 69517477Seric register FILE *qfp; 6967785Seric char buf[MAXFIELD]; 6979348Seric extern char *fgetfolded(); 69824954Seric extern long atol(); 699*40973Sbostic int gotctluser = 0; 70040934Srick int fd; 7014632Seric 7024632Seric /* 70317468Seric ** Read and process the file. 7044632Seric */ 7054632Seric 70617477Seric qf = queuename(e, 'q'); 70717477Seric qfp = fopen(qf, "r"); 70817477Seric if (qfp == NULL) 70917477Seric { 71040934Srick if (errno != ENOENT) 71140934Srick syserr("readqf: no control file %s", qf); 71240934Srick return NULL; 71317477Seric } 71440934Srick 71540934Srick if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0) 71640934Srick { 71740934Srick # ifdef LOG 71840934Srick /* being processed by another queuer */ 71940934Srick if (Verbose) 72040934Srick printf("%s: locked", CurEnv->e_id); 72140934Srick # endif LOG 72240934Srick (void) fclose(qfp); 72340934Srick return NULL; 72440934Srick } 72540934Srick 72640934Srick /* do basic system initialization */ 72740934Srick initsys(); 72840934Srick 72917477Seric FileName = qf; 7309377Seric LineNumber = 0; 7319630Seric if (Verbose && full) 7329377Seric printf("\nRunning %s\n", e->e_id); 73317468Seric while (fgetfolded(buf, sizeof buf, qfp) != NULL) 7344632Seric { 73526504Seric if (tTd(40, 4)) 73626504Seric printf("+++++ %s\n", buf); 7374632Seric switch (buf[0]) 7384632Seric { 739*40973Sbostic case 'C': /* specify controlling user */ 740*40973Sbostic setctluser(&buf[1]); 741*40973Sbostic gotctluser = 1; 742*40973Sbostic break; 743*40973Sbostic 7444632Seric case 'R': /* specify recipient */ 7459618Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 7464632Seric break; 7474632Seric 74825687Seric case 'E': /* specify error recipient */ 74925687Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue); 75025687Seric break; 75125687Seric 7524632Seric case 'H': /* header */ 7539630Seric if (full) 7549630Seric (void) chompheader(&buf[1], FALSE); 7554632Seric break; 7564632Seric 75710108Seric case 'M': /* message */ 75810108Seric e->e_message = newstr(&buf[1]); 75910108Seric break; 76010108Seric 7614632Seric case 'S': /* sender */ 7624634Seric setsender(newstr(&buf[1])); 7634632Seric break; 7644632Seric 7654632Seric case 'D': /* data file name */ 7669630Seric if (!full) 7679630Seric break; 7689377Seric e->e_df = newstr(&buf[1]); 7699544Seric e->e_dfp = fopen(e->e_df, "r"); 7709544Seric if (e->e_dfp == NULL) 7719377Seric syserr("readqf: cannot open %s", e->e_df); 7724632Seric break; 7734632Seric 7747860Seric case 'T': /* init time */ 77524941Seric e->e_ctime = atol(&buf[1]); 7764632Seric break; 7774632Seric 7784634Seric case 'P': /* message priority */ 77925008Seric e->e_msgpriority = atol(&buf[1]) + WkTimeFact; 7804634Seric break; 7814634Seric 78224941Seric case '\0': /* blank line; ignore */ 78324941Seric break; 78424941Seric 7854632Seric default: 78624941Seric syserr("readqf(%s:%d): bad line \"%s\"", e->e_id, 78724941Seric LineNumber, buf); 7884632Seric break; 7894632Seric } 790*40973Sbostic /* 791*40973Sbostic ** The `C' queue file command operates on the next line, 792*40973Sbostic ** so we use "gotctluser" to maintain state as follows: 793*40973Sbostic ** 0 - no controlling user, 794*40973Sbostic ** 1 - controlling user has been set but not used, 795*40973Sbostic ** 2 - controlling user must be used on next iteration. 796*40973Sbostic */ 797*40973Sbostic if (gotctluser == 1) 798*40973Sbostic gotctluser++; 799*40973Sbostic else if (gotctluser == 2) 800*40973Sbostic { 801*40973Sbostic clrctluser(); 802*40973Sbostic gotctluser = 0; 803*40973Sbostic } 8044632Seric } 8059377Seric 806*40973Sbostic /* clear controlling user in case we break out prematurely */ 807*40973Sbostic clrctluser(); 808*40973Sbostic 8099377Seric FileName = NULL; 81024941Seric 81124941Seric /* 81224941Seric ** If we haven't read any lines, this queue file is empty. 81324941Seric ** Arrange to remove it without referencing any null pointers. 81424941Seric */ 81524941Seric 81624941Seric if (LineNumber == 0) 81724941Seric { 81824941Seric errno = 0; 81924941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 82024941Seric } 82140934Srick return qfp; 8224632Seric } 8234632Seric /* 8249630Seric ** PRINTQUEUE -- print out a representation of the mail queue 8259630Seric ** 8269630Seric ** Parameters: 8279630Seric ** none. 8289630Seric ** 8299630Seric ** Returns: 8309630Seric ** none. 8319630Seric ** 8329630Seric ** Side Effects: 8339630Seric ** Prints a listing of the mail queue on the standard output. 8349630Seric */ 8355182Seric 8369630Seric printqueue() 8379630Seric { 8389630Seric register WORK *w; 8399630Seric FILE *f; 84010070Seric int nrequests; 8419630Seric char buf[MAXLINE]; 842*40973Sbostic char cbuf[MAXLINE]; 8439630Seric 8449630Seric /* 8459630Seric ** Read and order the queue. 8469630Seric */ 8479630Seric 84824941Seric nrequests = orderq(TRUE); 8499630Seric 8509630Seric /* 8519630Seric ** Print the work list that we have read. 8529630Seric */ 8539630Seric 8549630Seric /* first see if there is anything */ 85510070Seric if (nrequests <= 0) 8569630Seric { 85710070Seric printf("Mail queue is empty\n"); 8589630Seric return; 8599630Seric } 8609630Seric 86140934Srick la = getla(); /* get load average */ 86240934Srick 86310096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 86425687Seric if (nrequests > QUEUESIZE) 86525687Seric printf(", only %d printed", QUEUESIZE); 86624979Seric if (Verbose) 86725032Seric printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 86824979Seric else 86924979Seric printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 8709630Seric for (w = WorkQ; w != NULL; w = w->w_next) 8719630Seric { 8729630Seric struct stat st; 87310070Seric auto time_t submittime = 0; 87410070Seric long dfsize = -1; 87510108Seric char message[MAXLINE]; 87624941Seric extern bool shouldqueue(); 8779630Seric 87817468Seric f = fopen(w->w_name, "r"); 87917468Seric if (f == NULL) 88017468Seric { 88117468Seric errno = 0; 88217468Seric continue; 88317468Seric } 8849630Seric printf("%7s", w->w_name + 2); 88540934Srick if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0) 88610070Seric printf("*"); 88724941Seric else if (shouldqueue(w->w_pri)) 88824941Seric printf("X"); 88910070Seric else 89010070Seric printf(" "); 89110070Seric errno = 0; 89217468Seric 89310108Seric message[0] = '\0'; 894*40973Sbostic cbuf[0] = '\0'; 8959630Seric while (fgets(buf, sizeof buf, f) != NULL) 8969630Seric { 8979630Seric fixcrlf(buf, TRUE); 8989630Seric switch (buf[0]) 8999630Seric { 90010108Seric case 'M': /* error message */ 90123098Seric (void) strcpy(message, &buf[1]); 90210108Seric break; 90310108Seric 9049630Seric case 'S': /* sender name */ 90524979Seric if (Verbose) 90625027Seric printf("%8ld %10ld %.12s %.38s", dfsize, 90725027Seric w->w_pri, ctime(&submittime) + 4, 90824979Seric &buf[1]); 90924979Seric else 91024979Seric printf("%8ld %.16s %.45s", dfsize, 91124979Seric ctime(&submittime), &buf[1]); 91210108Seric if (message[0] != '\0') 91325027Seric printf("\n\t\t (%.60s)", message); 9149630Seric break; 915*40973Sbostic case 'C': /* controlling user */ 916*40973Sbostic if (strlen(buf) < MAXLINE-3) /* sanity */ 917*40973Sbostic (void) strcat(buf, ") "); 918*40973Sbostic cbuf[0] = cbuf[1] = '('; 919*40973Sbostic (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1); 920*40973Sbostic cbuf[MAXLINE-1] = '\0'; 921*40973Sbostic break; 9229630Seric 9239630Seric case 'R': /* recipient name */ 924*40973Sbostic if (cbuf[0] != '\0') { 925*40973Sbostic /* prepend controlling user to `buf' */ 926*40973Sbostic (void) strncat(cbuf, &buf[1], 927*40973Sbostic MAXLINE-strlen(cbuf)); 928*40973Sbostic cbuf[MAXLINE-1] = '\0'; 929*40973Sbostic (void) strcpy(buf, cbuf); 930*40973Sbostic cbuf[0] = '\0'; 931*40973Sbostic } 93224979Seric if (Verbose) 93325027Seric printf("\n\t\t\t\t\t %.38s", &buf[1]); 93424979Seric else 93524979Seric printf("\n\t\t\t\t %.45s", &buf[1]); 9369630Seric break; 9379630Seric 9389630Seric case 'T': /* creation time */ 93924941Seric submittime = atol(&buf[1]); 9409630Seric break; 94110070Seric 94210070Seric case 'D': /* data file name */ 94310070Seric if (stat(&buf[1], &st) >= 0) 94410070Seric dfsize = st.st_size; 94510070Seric break; 9469630Seric } 9479630Seric } 94810070Seric if (submittime == (time_t) 0) 94910070Seric printf(" (no control file)"); 9509630Seric printf("\n"); 95123098Seric (void) fclose(f); 9529630Seric } 9539630Seric } 9549630Seric 9555182Seric # endif QUEUE 95617468Seric /* 95717468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 95817468Seric ** 95917468Seric ** Assigns an id code if one does not already exist. 96017468Seric ** This code is very careful to avoid trashing existing files 96117468Seric ** under any circumstances. 96217468Seric ** 96317468Seric ** Parameters: 96417468Seric ** e -- envelope to build it in/from. 96517468Seric ** type -- the file type, used as the first character 96617468Seric ** of the file name. 96717468Seric ** 96817468Seric ** Returns: 96917468Seric ** a pointer to the new file name (in a static buffer). 97017468Seric ** 97117468Seric ** Side Effects: 97240934Srick ** Will create the qf file if no id code is 97317468Seric ** already assigned. This will cause the envelope 97417468Seric ** to be modified. 97517468Seric */ 97617468Seric 97717468Seric char * 97817468Seric queuename(e, type) 97917468Seric register ENVELOPE *e; 98017468Seric char type; 98117468Seric { 98217468Seric static char buf[MAXNAME]; 98317468Seric static int pid = -1; 98417468Seric char c1 = 'A'; 98517468Seric char c2 = 'A'; 98617468Seric 98717468Seric if (e->e_id == NULL) 98817468Seric { 98917468Seric char qf[20]; 99017468Seric 99117468Seric /* find a unique id */ 99217468Seric if (pid != getpid()) 99317468Seric { 99417468Seric /* new process -- start back at "AA" */ 99517468Seric pid = getpid(); 99617468Seric c1 = 'A'; 99717468Seric c2 = 'A' - 1; 99817468Seric } 99917468Seric (void) sprintf(qf, "qfAA%05d", pid); 100017468Seric 100117468Seric while (c1 < '~' || c2 < 'Z') 100217468Seric { 100317468Seric int i; 100417468Seric 100517468Seric if (c2 >= 'Z') 100617468Seric { 100717468Seric c1++; 100817468Seric c2 = 'A' - 1; 100917468Seric } 101040934Srick qf[2] = c1; 101140934Srick qf[3] = ++c2; 101217468Seric if (tTd(7, 20)) 101340934Srick printf("queuename: trying \"%s\"\n", qf); 101417468Seric 101540934Srick i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); 101640934Srick if (i < 0) { 101740934Srick if (errno != EEXIST) { 101836233Skarels syserr("queuename: Cannot create \"%s\" in \"%s\"", 101940934Srick qf, QueueDir); 102036233Skarels exit(EX_UNAVAILABLE); 102136233Skarels } 102240934Srick } else { 102340934Srick (void) close(i); 102440934Srick break; 102517468Seric } 102617468Seric } 102717468Seric if (c1 >= '~' && c2 >= 'Z') 102817468Seric { 102917468Seric syserr("queuename: Cannot create \"%s\" in \"%s\"", 103017468Seric qf, QueueDir); 103117468Seric exit(EX_OSERR); 103217468Seric } 103317468Seric e->e_id = newstr(&qf[2]); 103417468Seric define('i', e->e_id, e); 103517468Seric if (tTd(7, 1)) 103617468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 103717468Seric # ifdef LOG 103817468Seric if (LogLevel > 16) 103917468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 104017468Seric # endif LOG 104117468Seric } 104217468Seric 104317468Seric if (type == '\0') 104417468Seric return (NULL); 104517468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 104617468Seric if (tTd(7, 2)) 104717468Seric printf("queuename: %s\n", buf); 104817468Seric return (buf); 104917468Seric } 105017468Seric /* 105117468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 105217468Seric ** 105317468Seric ** Parameters: 105417468Seric ** e -- the envelope to unlock. 105517468Seric ** 105617468Seric ** Returns: 105717468Seric ** none 105817468Seric ** 105917468Seric ** Side Effects: 106017468Seric ** unlocks the queue for `e'. 106117468Seric */ 106217468Seric 106317468Seric unlockqueue(e) 106417468Seric ENVELOPE *e; 106517468Seric { 106617468Seric /* remove the transcript */ 106717468Seric # ifdef LOG 106817468Seric if (LogLevel > 19) 106917468Seric syslog(LOG_DEBUG, "%s: unlock", e->e_id); 107017468Seric # endif LOG 107117468Seric if (!tTd(51, 4)) 107217468Seric xunlink(queuename(e, 'x')); 107317468Seric 107417468Seric } 1075*40973Sbostic /* 1076*40973Sbostic ** GETCTLUSER -- return controlling user if mailing to prog or file 1077*40973Sbostic ** 1078*40973Sbostic ** Check for a "|" or "/" at the beginning of the address. If 1079*40973Sbostic ** found, return a controlling username. 1080*40973Sbostic ** 1081*40973Sbostic ** Parameters: 1082*40973Sbostic ** a - the address to check out 1083*40973Sbostic ** 1084*40973Sbostic ** Returns: 1085*40973Sbostic ** Either NULL, if we werent mailing to a program or file, 1086*40973Sbostic ** or a controlling user name (possibly in getpwuid's 1087*40973Sbostic ** static buffer). 1088*40973Sbostic ** 1089*40973Sbostic ** Side Effects: 1090*40973Sbostic ** none. 1091*40973Sbostic */ 1092*40973Sbostic 1093*40973Sbostic char * 1094*40973Sbostic getctluser(a) 1095*40973Sbostic ADDRESS *a; 1096*40973Sbostic { 1097*40973Sbostic extern ADDRESS *getctladdr(); 1098*40973Sbostic struct passwd *pw; 1099*40973Sbostic char *retstr; 1100*40973Sbostic 1101*40973Sbostic /* 1102*40973Sbostic ** Get unquoted user for file, program or user.name check. 1103*40973Sbostic ** N.B. remove this code block to always emit controlling 1104*40973Sbostic ** addresses (at the expense of backward compatibility). 1105*40973Sbostic */ 1106*40973Sbostic 1107*40973Sbostic { 1108*40973Sbostic char buf[MAXNAME]; 1109*40973Sbostic (void) strncpy(buf, a->q_paddr, MAXNAME); 1110*40973Sbostic buf[MAXNAME-1] = '\0'; 1111*40973Sbostic stripquotes(buf, TRUE); 1112*40973Sbostic 1113*40973Sbostic if (buf[0] != '|' && buf[0] != '/') 1114*40973Sbostic return((char *)NULL); 1115*40973Sbostic } 1116*40973Sbostic 1117*40973Sbostic a = getctladdr(a); /* find controlling address */ 1118*40973Sbostic 1119*40973Sbostic if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL) 1120*40973Sbostic retstr = pw->pw_name; 1121*40973Sbostic else /* use default user */ 1122*40973Sbostic retstr = DefUser; 1123*40973Sbostic 1124*40973Sbostic if (tTd(40, 5)) 1125*40973Sbostic printf("Set controlling user for `%s' to `%s'\n", 1126*40973Sbostic (a == NULL)? "<null>": a->q_paddr, retstr); 1127*40973Sbostic 1128*40973Sbostic return(retstr); 1129*40973Sbostic } 1130*40973Sbostic /* 1131*40973Sbostic ** SETCTLUSER - sets `CtlUser' to controlling user 1132*40973Sbostic ** CLRCTLUSER - clears controlling user (no params, nothing returned) 1133*40973Sbostic ** 1134*40973Sbostic ** These routines manipulate `CtlUser'. 1135*40973Sbostic ** 1136*40973Sbostic ** Parameters: 1137*40973Sbostic ** str - controlling user as passed to setctluser() 1138*40973Sbostic ** 1139*40973Sbostic ** Returns: 1140*40973Sbostic ** None. 1141*40973Sbostic ** 1142*40973Sbostic ** Side Effects: 1143*40973Sbostic ** `CtlUser' is changed. 1144*40973Sbostic */ 1145*40973Sbostic 1146*40973Sbostic static char CtlUser[MAXNAME]; 1147*40973Sbostic 1148*40973Sbostic setctluser(str) 1149*40973Sbostic register char *str; 1150*40973Sbostic { 1151*40973Sbostic (void) strncpy(CtlUser, str, MAXNAME); 1152*40973Sbostic CtlUser[MAXNAME-1] = '\0'; 1153*40973Sbostic } 1154*40973Sbostic 1155*40973Sbostic clrctluser() 1156*40973Sbostic { 1157*40973Sbostic CtlUser[0] = '\0'; 1158*40973Sbostic } 1159*40973Sbostic 1160*40973Sbostic /* 1161*40973Sbostic ** SETCTLADDR -- create a controlling address 1162*40973Sbostic ** 1163*40973Sbostic ** If global variable `CtlUser' is set and we are given a valid 1164*40973Sbostic ** address, make that address a controlling address; change the 1165*40973Sbostic ** `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID. 1166*40973Sbostic ** 1167*40973Sbostic ** Parameters: 1168*40973Sbostic ** a - address for which control uid/gid info may apply 1169*40973Sbostic ** 1170*40973Sbostic ** Returns: 1171*40973Sbostic ** None. 1172*40973Sbostic ** 1173*40973Sbostic ** Side Effects: 1174*40973Sbostic ** Fills in uid/gid fields in address and sets QGOODUID 1175*40973Sbostic ** flag if appropriate. 1176*40973Sbostic */ 1177*40973Sbostic 1178*40973Sbostic setctladdr(a) 1179*40973Sbostic ADDRESS *a; 1180*40973Sbostic { 1181*40973Sbostic struct passwd *pw; 1182*40973Sbostic 1183*40973Sbostic /* 1184*40973Sbostic ** If there is no current controlling user, or we were passed a 1185*40973Sbostic ** NULL addr ptr or we already have a controlling user, return. 1186*40973Sbostic */ 1187*40973Sbostic 1188*40973Sbostic if (CtlUser[0] == '\0' || a == NULL || a->q_ruser) 1189*40973Sbostic return; 1190*40973Sbostic 1191*40973Sbostic /* 1192*40973Sbostic ** Set up addr fields for controlling user. If `CtlUser' is no 1193*40973Sbostic ** longer valid, use the default user/group. 1194*40973Sbostic */ 1195*40973Sbostic 1196*40973Sbostic if ((pw = getpwnam(CtlUser)) != NULL) 1197*40973Sbostic { 1198*40973Sbostic if (a->q_home) 1199*40973Sbostic free(a->q_home); 1200*40973Sbostic a->q_home = newstr(pw->pw_dir); 1201*40973Sbostic a->q_uid = pw->pw_uid; 1202*40973Sbostic a->q_gid = pw->pw_gid; 1203*40973Sbostic a->q_ruser = newstr(CtlUser); 1204*40973Sbostic } 1205*40973Sbostic else 1206*40973Sbostic { 1207*40973Sbostic a->q_uid = DefUid; 1208*40973Sbostic a->q_gid = DefGid; 1209*40973Sbostic a->q_ruser = newstr(DefUser); 1210*40973Sbostic } 1211*40973Sbostic 1212*40973Sbostic a->q_flags |= QGOODUID; /* flag as a "ctladdr" */ 1213*40973Sbostic 1214*40973Sbostic if (tTd(40, 5)) 1215*40973Sbostic printf("Restored controlling user for `%s' to `%s'\n", 1216*40973Sbostic a->q_paddr, a->q_ruser); 1217*40973Sbostic } 1218