122708Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333731Sbostic * Copyright (c) 1988 Regents of the University of California. 433731Sbostic * All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822708Sdist 933731Sbostic # include "sendmail.h" 1022708Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef QUEUE 13*51835Seric static char sccsid[] = "@(#)queue.c 5.33 (Berkeley) 12/05/91 (with queueing)"; 1433731Sbostic #else 15*51835Seric static char sccsid[] = "@(#)queue.c 5.33 (Berkeley) 12/05/91 (without queueing)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 194632Seric # include <sys/stat.h> 2013707Ssam # include <sys/dir.h> 2140934Srick # include <sys/file.h> 224634Seric # include <signal.h> 234632Seric # include <errno.h> 2440973Sbostic # include <pwd.h> 254632Seric 2633731Sbostic # ifdef QUEUE 274632Seric 28*51835Seric # ifdef LOCKF 29*51835Seric # include <unistd.h> 30*51835Seric # endif 31*51835Seric 324632Seric /* 339377Seric ** Work queue. 349377Seric */ 359377Seric 369377Seric struct work 379377Seric { 389377Seric char *w_name; /* name of control file */ 399377Seric long w_pri; /* priority of message, see below */ 4025013Seric time_t w_ctime; /* creation time of message */ 419377Seric struct work *w_next; /* next in queue */ 429377Seric }; 439377Seric 449377Seric typedef struct work WORK; 4540934Srick extern int la; 469377Seric 479377Seric WORK *WorkQ; /* queue of things to be done */ 489377Seric /* 494632Seric ** QUEUEUP -- queue a message up for future transmission. 504632Seric ** 514632Seric ** Parameters: 526980Seric ** e -- the envelope to queue up. 536999Seric ** queueall -- if TRUE, queue all addresses, rather than 546999Seric ** just those with the QQUEUEUP flag set. 559377Seric ** announce -- if TRUE, tell when you are queueing up. 564632Seric ** 574632Seric ** Returns: 5840934Srick ** locked FILE* to q file 594632Seric ** 604632Seric ** Side Effects: 619377Seric ** The current request are saved in a control file. 624632Seric */ 634632Seric 6440934Srick FILE * 659377Seric queueup(e, queueall, announce) 666980Seric register ENVELOPE *e; 676999Seric bool queueall; 689377Seric bool announce; 694632Seric { 707812Seric char *qf; 7141636Srick char buf[MAXLINE], tf[MAXLINE]; 727812Seric register FILE *tfp; 734632Seric register HDR *h; 745007Seric register ADDRESS *q; 7510173Seric MAILER nullmailer; 7641636Srick int fd, ret; 774632Seric 785037Seric /* 7917477Seric ** Create control file. 805037Seric */ 814632Seric 82*51835Seric do 83*51835Seric { 8441636Srick strcpy(tf, queuename(e, 't')); 8541636Srick fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); 86*51835Seric if (fd < 0) 87*51835Seric { 88*51835Seric if (errno != EEXIST) 89*51835Seric { 9041636Srick syserr("queueup: cannot create temp file %s", 9141636Srick tf); 9241636Srick return NULL; 9341636Srick } 94*51835Seric } 95*51835Seric else 96*51835Seric { 97*51835Seric # ifdef LOCKF 98*51835Seric if (lockf(fd, F_TLOCK, 0) < 0) 99*51835Seric { 100*51835Seric if (errno != EACCES) 101*51835Seric syserr("cannot lockf(%s)", tf); 102*51835Seric close(fd); 103*51835Seric fd = -1; 104*51835Seric } 105*51835Seric # else 106*51835Seric if (flock(fd, LOCK_EX|LOCK_NB) < 0) 107*51835Seric { 10841636Srick if (errno != EWOULDBLOCK) 10941636Srick syserr("cannot flock(%s)", tf); 11041636Srick close(fd); 11141636Srick fd = -1; 11241636Srick } 113*51835Seric # endif 11441636Srick } 11541636Srick } while (fd < 0); 11641636Srick 11740934Srick tfp = fdopen(fd, "w"); 1184632Seric 1197677Seric if (tTd(40, 1)) 12017468Seric printf("queueing %s\n", e->e_id); 1214632Seric 1224632Seric /* 1236980Seric ** If there is no data file yet, create one. 1246980Seric */ 1256980Seric 1266980Seric if (e->e_df == NULL) 1276980Seric { 1286980Seric register FILE *dfp; 1299389Seric extern putbody(); 1306980Seric 1317812Seric e->e_df = newstr(queuename(e, 'd')); 13240934Srick fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode); 13340934Srick if (fd < 0) 1346980Seric { 1356980Seric syserr("queueup: cannot create %s", e->e_df); 1367812Seric (void) fclose(tfp); 13740934Srick return NULL; 1386980Seric } 13940934Srick dfp = fdopen(fd, "w"); 14010173Seric (*e->e_putbody)(dfp, ProgMailer, e); 1417009Seric (void) fclose(dfp); 1429389Seric e->e_putbody = putbody; 1436980Seric } 1446980Seric 1456980Seric /* 1464632Seric ** Output future work requests. 14725687Seric ** Priority and creation time should be first, since 14825687Seric ** they are required by orderq. 1494632Seric */ 1504632Seric 1519377Seric /* output message priority */ 1529377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 1539377Seric 1549630Seric /* output creation time */ 1559630Seric fprintf(tfp, "T%ld\n", e->e_ctime); 1569630Seric 1574632Seric /* output name of data file */ 1587812Seric fprintf(tfp, "D%s\n", e->e_df); 1594632Seric 16010108Seric /* message from envelope, if it exists */ 16110108Seric if (e->e_message != NULL) 16210108Seric fprintf(tfp, "M%s\n", e->e_message); 16310108Seric 1644632Seric /* output name of sender */ 1657812Seric fprintf(tfp, "S%s\n", e->e_from.q_paddr); 1664632Seric 1674632Seric /* output list of recipient addresses */ 1686980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 1694632Seric { 17047284Seric if (queueall ? !bitset(QDONTSEND|QSENT, q->q_flags) : 1717763Seric bitset(QQUEUEUP, q->q_flags)) 1728245Seric { 17340973Sbostic char *ctluser, *getctluser(); 17440973Sbostic 17540973Sbostic if ((ctluser = getctluser(q)) != NULL) 17640973Sbostic fprintf(tfp, "C%s\n", ctluser); 1777812Seric fprintf(tfp, "R%s\n", q->q_paddr); 1789377Seric if (announce) 1799377Seric { 1809377Seric e->e_to = q->q_paddr; 1819377Seric message(Arpa_Info, "queued"); 1829377Seric if (LogLevel > 4) 1839377Seric logdelivery("queued"); 1849377Seric e->e_to = NULL; 1859377Seric } 1869387Seric if (tTd(40, 1)) 1879387Seric { 1889387Seric printf("queueing "); 1899387Seric printaddr(q, FALSE); 1909387Seric } 1918245Seric } 1924632Seric } 1934632Seric 19425687Seric /* output list of error recipients */ 19525687Seric for (q = e->e_errorqueue; q != NULL; q = q->q_next) 19625687Seric { 19726504Seric if (!bitset(QDONTSEND, q->q_flags)) 19840973Sbostic { 19940973Sbostic char *ctluser, *getctluser(); 20040973Sbostic 20140973Sbostic if ((ctluser = getctluser(q)) != NULL) 20240973Sbostic fprintf(tfp, "C%s\n", ctluser); 20326504Seric fprintf(tfp, "E%s\n", q->q_paddr); 20440973Sbostic } 20525687Seric } 20625687Seric 2079377Seric /* 2089377Seric ** Output headers for this message. 2099377Seric ** Expand macros completely here. Queue run will deal with 2109377Seric ** everything as absolute headers. 2119377Seric ** All headers that must be relative to the recipient 2129377Seric ** can be cracked later. 21310173Seric ** We set up a "null mailer" -- i.e., a mailer that will have 21410173Seric ** no effect on the addresses as they are output. 2159377Seric */ 2169377Seric 21710686Seric bzero((char *) &nullmailer, sizeof nullmailer); 21810173Seric nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; 21910349Seric nullmailer.m_eol = "\n"; 22010173Seric 22116147Seric define('g', "\001f", e); 2226980Seric for (h = e->e_header; h != NULL; h = h->h_link) 2234632Seric { 22410686Seric extern bool bitzerop(); 22510686Seric 22612015Seric /* don't output null headers */ 2274632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 2284632Seric continue; 22912015Seric 23012015Seric /* don't output resent headers on non-resent messages */ 23112015Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 23212015Seric continue; 23312015Seric 23412015Seric /* output this header */ 2357812Seric fprintf(tfp, "H"); 23612015Seric 23712015Seric /* if conditional, output the set of conditions */ 23810686Seric if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 23910686Seric { 24010686Seric int j; 24110686Seric 24223098Seric (void) putc('?', tfp); 24310686Seric for (j = '\0'; j <= '\177'; j++) 24410686Seric if (bitnset(j, h->h_mflags)) 24523098Seric (void) putc(j, tfp); 24623098Seric (void) putc('?', tfp); 24710686Seric } 24812015Seric 24912015Seric /* output the header: expand macros, convert addresses */ 2507763Seric if (bitset(H_DEFAULT, h->h_flags)) 2517763Seric { 2527763Seric (void) expand(h->h_value, buf, &buf[sizeof buf], e); 2538236Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 2547763Seric } 2558245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 2569348Seric { 2579348Seric commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 25810173Seric &nullmailer); 2599348Seric } 2607763Seric else 2618245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 2624632Seric } 2634632Seric 2644632Seric /* 2654632Seric ** Clean up. 2664632Seric */ 2674632Seric 26817468Seric qf = queuename(e, 'q'); 26941636Srick if (rename(tf, qf) < 0) 27041636Srick syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df); 27141636Srick errno = 0; 2727391Seric 2737677Seric # ifdef LOG 2747677Seric /* save log info */ 2757878Seric if (LogLevel > 15) 2767878Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 2777677Seric # endif LOG 27840934Srick fflush(tfp); 27940934Srick return tfp; 2804632Seric } 2814632Seric /* 2824632Seric ** RUNQUEUE -- run the jobs in the queue. 2834632Seric ** 2844632Seric ** Gets the stuff out of the queue in some presumably logical 2854632Seric ** order and processes them. 2864632Seric ** 2874632Seric ** Parameters: 28824941Seric ** forkflag -- TRUE if the queue scanning should be done in 28924941Seric ** a child process. We double-fork so it is not our 29024941Seric ** child and we don't have to clean up after it. 2914632Seric ** 2924632Seric ** Returns: 2934632Seric ** none. 2944632Seric ** 2954632Seric ** Side Effects: 2964632Seric ** runs things in the mail queue. 2974632Seric */ 2984632Seric 2994639Seric runqueue(forkflag) 3004639Seric bool forkflag; 3014632Seric { 30224953Seric extern bool shouldqueue(); 30324953Seric 3047466Seric /* 30524953Seric ** If no work will ever be selected, don't even bother reading 30624953Seric ** the queue. 30724953Seric */ 30824953Seric 30940934Srick la = getla(); /* get load average */ 31040934Srick 31124953Seric if (shouldqueue(-100000000L)) 31224953Seric { 31324953Seric if (Verbose) 31424953Seric printf("Skipping queue run -- load average too high\n"); 31524953Seric 31624953Seric if (forkflag) 31724953Seric return; 31824953Seric finis(); 31924953Seric } 32024953Seric 32124953Seric /* 3227466Seric ** See if we want to go off and do other useful work. 3237466Seric */ 3244639Seric 3254639Seric if (forkflag) 3264639Seric { 3277943Seric int pid; 3287943Seric 3297943Seric pid = dofork(); 3307943Seric if (pid != 0) 3314639Seric { 33246928Sbostic extern void reapchild(); 33325184Seric 3347943Seric /* parent -- pick up intermediate zombie */ 33525184Seric #ifndef SIGCHLD 3369377Seric (void) waitfor(pid); 33725184Seric #else SIGCHLD 33825184Seric (void) signal(SIGCHLD, reapchild); 33925184Seric #endif SIGCHLD 3407690Seric if (QueueIntvl != 0) 3419348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 3424639Seric return; 3434639Seric } 3447943Seric /* child -- double fork */ 34525184Seric #ifndef SIGCHLD 3467943Seric if (fork() != 0) 3477943Seric exit(EX_OK); 34825184Seric #else SIGCHLD 34925184Seric (void) signal(SIGCHLD, SIG_DFL); 35025184Seric #endif SIGCHLD 3514639Seric } 35224941Seric 35340934Srick setproctitle("running queue: %s", QueueDir); 35424941Seric 3557876Seric # ifdef LOG 3567876Seric if (LogLevel > 11) 3577943Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 3587876Seric # endif LOG 3594639Seric 3607466Seric /* 36110205Seric ** Release any resources used by the daemon code. 36210205Seric */ 36310205Seric 36410205Seric # ifdef DAEMON 36510205Seric clrdaemon(); 36610205Seric # endif DAEMON 36710205Seric 36810205Seric /* 36927175Seric ** Make sure the alias database is open. 37027175Seric */ 37127175Seric 37227175Seric initaliases(AliasFile, FALSE); 37327175Seric 37427175Seric /* 3757466Seric ** Start making passes through the queue. 3767466Seric ** First, read and sort the entire queue. 3777466Seric ** Then, process the work in that order. 3787466Seric ** But if you take too long, start over. 3797466Seric */ 3807466Seric 3817943Seric /* order the existing work requests */ 38224954Seric (void) orderq(FALSE); 3837690Seric 3847943Seric /* process them once at a time */ 3857943Seric while (WorkQ != NULL) 3864639Seric { 3877943Seric WORK *w = WorkQ; 3887881Seric 3897943Seric WorkQ = WorkQ->w_next; 3907943Seric dowork(w); 3917943Seric free(w->w_name); 3927943Seric free((char *) w); 3934639Seric } 39429866Seric 39529866Seric /* exit without the usual cleanup */ 39629866Seric exit(ExitStat); 3974634Seric } 3984634Seric /* 3994632Seric ** ORDERQ -- order the work queue. 4004632Seric ** 4014632Seric ** Parameters: 40224941Seric ** doall -- if set, include everything in the queue (even 40324941Seric ** the jobs that cannot be run because the load 40424941Seric ** average is too high). Otherwise, exclude those 40524941Seric ** jobs. 4064632Seric ** 4074632Seric ** Returns: 40810121Seric ** The number of request in the queue (not necessarily 40910121Seric ** the number of requests in WorkQ however). 4104632Seric ** 4114632Seric ** Side Effects: 4124632Seric ** Sets WorkQ to the queue of available work, in order. 4134632Seric */ 4144632Seric 41525687Seric # define NEED_P 001 41625687Seric # define NEED_T 002 4174632Seric 41824941Seric orderq(doall) 41924941Seric bool doall; 4204632Seric { 4216625Sglickman register struct direct *d; 4224632Seric register WORK *w; 4236625Sglickman DIR *f; 4244632Seric register int i; 42525687Seric WORK wlist[QUEUESIZE+1]; 42610070Seric int wn = -1; 4274632Seric extern workcmpf(); 4284632Seric 4294632Seric /* clear out old WorkQ */ 4304632Seric for (w = WorkQ; w != NULL; ) 4314632Seric { 4324632Seric register WORK *nw = w->w_next; 4334632Seric 4344632Seric WorkQ = nw; 4354632Seric free(w->w_name); 4364632Seric free((char *) w); 4374632Seric w = nw; 4384632Seric } 4394632Seric 4404632Seric /* open the queue directory */ 4418148Seric f = opendir("."); 4424632Seric if (f == NULL) 4434632Seric { 4448148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 44510070Seric return (0); 4464632Seric } 4474632Seric 4484632Seric /* 4494632Seric ** Read the work directory. 4504632Seric */ 4514632Seric 45210070Seric while ((d = readdir(f)) != NULL) 4534632Seric { 4549377Seric FILE *cf; 4554632Seric char lbuf[MAXNAME]; 4564632Seric 4574632Seric /* is this an interesting entry? */ 4587812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 4594632Seric continue; 4604632Seric 46110070Seric /* yes -- open control file (if not too many files) */ 46225687Seric if (++wn >= QUEUESIZE) 46310070Seric continue; 4648148Seric cf = fopen(d->d_name, "r"); 4654632Seric if (cf == NULL) 4664632Seric { 4677055Seric /* this may be some random person sending hir msgs */ 4687055Seric /* syserr("orderq: cannot open %s", cbuf); */ 46910090Seric if (tTd(41, 2)) 47010090Seric printf("orderq: cannot open %s (%d)\n", 47110090Seric d->d_name, errno); 4727055Seric errno = 0; 47310090Seric wn--; 4744632Seric continue; 4754632Seric } 47625687Seric w = &wlist[wn]; 47725687Seric w->w_name = newstr(d->d_name); 4784632Seric 47925027Seric /* make sure jobs in creation don't clog queue */ 48025687Seric w->w_pri = 0x7fffffff; 48125687Seric w->w_ctime = 0; 48225027Seric 4834632Seric /* extract useful information */ 48425687Seric i = NEED_P | NEED_T; 48525687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 4864632Seric { 48724954Seric extern long atol(); 48824954Seric 48924941Seric switch (lbuf[0]) 4904632Seric { 49124941Seric case 'P': 49225687Seric w->w_pri = atol(&lbuf[1]); 49325687Seric i &= ~NEED_P; 4944632Seric break; 49525013Seric 49625013Seric case 'T': 49725687Seric w->w_ctime = atol(&lbuf[1]); 49825687Seric i &= ~NEED_T; 49925013Seric break; 5004632Seric } 5014632Seric } 5024632Seric (void) fclose(cf); 50324953Seric 50425687Seric if (!doall && shouldqueue(w->w_pri)) 50524953Seric { 50624953Seric /* don't even bother sorting this job in */ 50724953Seric wn--; 50824953Seric } 5094632Seric } 5106625Sglickman (void) closedir(f); 51110090Seric wn++; 5124632Seric 5134632Seric /* 5144632Seric ** Sort the work directory. 5154632Seric */ 5164632Seric 51725687Seric qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); 5184632Seric 5194632Seric /* 5204632Seric ** Convert the work list into canonical form. 5219377Seric ** Should be turning it into a list of envelopes here perhaps. 5224632Seric */ 5234632Seric 52424981Seric WorkQ = NULL; 52525687Seric for (i = min(wn, QUEUESIZE); --i >= 0; ) 5264632Seric { 5274632Seric w = (WORK *) xalloc(sizeof *w); 5284632Seric w->w_name = wlist[i].w_name; 5294632Seric w->w_pri = wlist[i].w_pri; 53025013Seric w->w_ctime = wlist[i].w_ctime; 53124981Seric w->w_next = WorkQ; 53224981Seric WorkQ = w; 5334632Seric } 5344632Seric 5357677Seric if (tTd(40, 1)) 5364632Seric { 5374632Seric for (w = WorkQ; w != NULL; w = w->w_next) 5385037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 5394632Seric } 54010070Seric 54110090Seric return (wn); 5424632Seric } 5434632Seric /* 5447677Seric ** WORKCMPF -- compare function for ordering work. 5454632Seric ** 5464632Seric ** Parameters: 5474632Seric ** a -- the first argument. 5484632Seric ** b -- the second argument. 5494632Seric ** 5504632Seric ** Returns: 55124981Seric ** -1 if a < b 55224981Seric ** 0 if a == b 55324981Seric ** +1 if a > b 5544632Seric ** 5554632Seric ** Side Effects: 5564632Seric ** none. 5574632Seric */ 5584632Seric 5594632Seric workcmpf(a, b) 5605037Seric register WORK *a; 5615037Seric register WORK *b; 5624632Seric { 56325013Seric long pa = a->w_pri + a->w_ctime; 56425013Seric long pb = b->w_pri + b->w_ctime; 56524941Seric 56624941Seric if (pa == pb) 5674632Seric return (0); 56824941Seric else if (pa > pb) 56924981Seric return (1); 57024981Seric else 57110121Seric return (-1); 5724632Seric } 5734632Seric /* 5744632Seric ** DOWORK -- do a work request. 5754632Seric ** 5764632Seric ** Parameters: 5774632Seric ** w -- the work request to be satisfied. 5784632Seric ** 5794632Seric ** Returns: 5804632Seric ** none. 5814632Seric ** 5824632Seric ** Side Effects: 5834632Seric ** The work request is satisfied if possible. 5844632Seric */ 5854632Seric 5864632Seric dowork(w) 5874632Seric register WORK *w; 5884632Seric { 5894632Seric register int i; 59024941Seric extern bool shouldqueue(); 5914632Seric 5927677Seric if (tTd(40, 1)) 5935037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 5944632Seric 5954632Seric /* 59624941Seric ** Ignore jobs that are too expensive for the moment. 5974632Seric */ 5984632Seric 59924941Seric if (shouldqueue(w->w_pri)) 6004632Seric { 60124941Seric if (Verbose) 60224967Seric printf("\nSkipping %s\n", w->w_name + 2); 6034632Seric return; 6044632Seric } 6054632Seric 60624941Seric /* 60724941Seric ** Fork for work. 60824941Seric */ 60924941Seric 61024941Seric if (ForkQueueRuns) 61124941Seric { 61224941Seric i = fork(); 61324941Seric if (i < 0) 61424941Seric { 61524941Seric syserr("dowork: cannot fork"); 61624941Seric return; 61724941Seric } 61824941Seric } 61924941Seric else 62024941Seric { 62124941Seric i = 0; 62224941Seric } 62324941Seric 6244632Seric if (i == 0) 6254632Seric { 62640934Srick FILE *qflock, *readqf(); 627*51835Seric 6284632Seric /* 6294632Seric ** CHILD 6308148Seric ** Lock the control file to avoid duplicate deliveries. 6318148Seric ** Then run the file as though we had just read it. 6327350Seric ** We save an idea of the temporary name so we 6337350Seric ** can recover on interrupt. 6344632Seric */ 6354632Seric 6367763Seric /* set basic modes, etc. */ 6377356Seric (void) alarm(0); 63825612Seric clearenvelope(CurEnv, FALSE); 6394632Seric QueueRun = TRUE; 6409377Seric ErrorMode = EM_MAIL; 6418148Seric CurEnv->e_id = &w->w_name[2]; 6427876Seric # ifdef LOG 6437876Seric if (LogLevel > 11) 6447881Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 6457881Seric getpid()); 6467876Seric # endif LOG 6477763Seric 6487763Seric /* don't use the headers from sendmail.cf... */ 6497763Seric CurEnv->e_header = NULL; 6507763Seric 65140934Srick /* read the queue control file */ 65240934Srick /* and lock the control file during processing */ 653*51835Seric if ((qflock = readqf(CurEnv, TRUE)) == NULL) 6546980Seric { 65524941Seric if (ForkQueueRuns) 65624941Seric exit(EX_OK); 65724941Seric else 65824941Seric return; 6596980Seric } 6606980Seric 6619338Seric CurEnv->e_flags |= EF_INQUEUE; 6629377Seric eatheader(CurEnv); 6636980Seric 6646980Seric /* do the delivery */ 6659338Seric if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 6669282Seric sendall(CurEnv, SM_DELIVER); 6676980Seric 6686980Seric /* finish up and exit */ 66924941Seric if (ForkQueueRuns) 67024941Seric finis(); 67124941Seric else 67224941Seric dropenvelope(CurEnv); 67341636Srick fclose(qflock); 6744632Seric } 67524941Seric else 67624941Seric { 67724941Seric /* 67824941Seric ** Parent -- pick up results. 67924941Seric */ 6804632Seric 68124941Seric errno = 0; 68224941Seric (void) waitfor(i); 68324941Seric } 6844632Seric } 6854632Seric /* 6864632Seric ** READQF -- read queue file and set up environment. 6874632Seric ** 6884632Seric ** Parameters: 6899377Seric ** e -- the envelope of the job to run. 6909630Seric ** full -- if set, read in all information. Otherwise just 6919630Seric ** read in info needed for a queue print. 6924632Seric ** 6934632Seric ** Returns: 69440934Srick ** FILE * pointing to flock()ed fd so it can be closed 69540934Srick ** after the mail is delivered 6964632Seric ** 6974632Seric ** Side Effects: 6984632Seric ** cf is read and created as the current job, as though 6994632Seric ** we had been invoked by argument. 7004632Seric */ 7014632Seric 70240934Srick FILE * 70317477Seric readqf(e, full) 7049377Seric register ENVELOPE *e; 7059630Seric bool full; 7064632Seric { 70717477Seric char *qf; 70817477Seric register FILE *qfp; 7097785Seric char buf[MAXFIELD]; 7109348Seric extern char *fgetfolded(); 71124954Seric extern long atol(); 71240973Sbostic int gotctluser = 0; 71340934Srick int fd; 7144632Seric 7154632Seric /* 71617468Seric ** Read and process the file. 7174632Seric */ 7184632Seric 71917477Seric qf = queuename(e, 'q'); 72017477Seric qfp = fopen(qf, "r"); 72117477Seric if (qfp == NULL) 72217477Seric { 72340934Srick if (errno != ENOENT) 72440934Srick syserr("readqf: no control file %s", qf); 72540934Srick return NULL; 72617477Seric } 72740934Srick 728*51835Seric # ifdef LOCKF 729*51835Seric if (lockf(fileno(qfp), F_TLOCK, 0) < 0) 730*51835Seric # else 73140934Srick if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0) 732*51835Seric # endif 73340934Srick { 73440934Srick # ifdef LOG 73540934Srick /* being processed by another queuer */ 73640934Srick if (Verbose) 73741636Srick printf("%s: locked\n", CurEnv->e_id); 73840934Srick # endif LOG 73940934Srick (void) fclose(qfp); 74040934Srick return NULL; 74140934Srick } 74240934Srick 74340934Srick /* do basic system initialization */ 74440934Srick initsys(); 74540934Srick 74617477Seric FileName = qf; 7479377Seric LineNumber = 0; 7489630Seric if (Verbose && full) 7499377Seric printf("\nRunning %s\n", e->e_id); 75017468Seric while (fgetfolded(buf, sizeof buf, qfp) != NULL) 7514632Seric { 75226504Seric if (tTd(40, 4)) 75326504Seric printf("+++++ %s\n", buf); 7544632Seric switch (buf[0]) 7554632Seric { 75640973Sbostic case 'C': /* specify controlling user */ 75740973Sbostic setctluser(&buf[1]); 75840973Sbostic gotctluser = 1; 75940973Sbostic break; 76040973Sbostic 7614632Seric case 'R': /* specify recipient */ 7629618Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 7634632Seric break; 7644632Seric 76525687Seric case 'E': /* specify error recipient */ 76625687Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue); 76725687Seric break; 76825687Seric 7694632Seric case 'H': /* header */ 7709630Seric if (full) 7719630Seric (void) chompheader(&buf[1], FALSE); 7724632Seric break; 7734632Seric 77410108Seric case 'M': /* message */ 77510108Seric e->e_message = newstr(&buf[1]); 77610108Seric break; 77710108Seric 7784632Seric case 'S': /* sender */ 7794634Seric setsender(newstr(&buf[1])); 7804632Seric break; 7814632Seric 7824632Seric case 'D': /* data file name */ 7839630Seric if (!full) 7849630Seric break; 7859377Seric e->e_df = newstr(&buf[1]); 7869544Seric e->e_dfp = fopen(e->e_df, "r"); 7879544Seric if (e->e_dfp == NULL) 7889377Seric syserr("readqf: cannot open %s", e->e_df); 7894632Seric break; 7904632Seric 7917860Seric case 'T': /* init time */ 79224941Seric e->e_ctime = atol(&buf[1]); 7934632Seric break; 7944632Seric 7954634Seric case 'P': /* message priority */ 79625008Seric e->e_msgpriority = atol(&buf[1]) + WkTimeFact; 7974634Seric break; 7984634Seric 79924941Seric case '\0': /* blank line; ignore */ 80024941Seric break; 80124941Seric 8024632Seric default: 80324941Seric syserr("readqf(%s:%d): bad line \"%s\"", e->e_id, 80424941Seric LineNumber, buf); 8054632Seric break; 8064632Seric } 80740973Sbostic /* 80840973Sbostic ** The `C' queue file command operates on the next line, 80940973Sbostic ** so we use "gotctluser" to maintain state as follows: 81040973Sbostic ** 0 - no controlling user, 81140973Sbostic ** 1 - controlling user has been set but not used, 81240973Sbostic ** 2 - controlling user must be used on next iteration. 81340973Sbostic */ 81440973Sbostic if (gotctluser == 1) 81540973Sbostic gotctluser++; 81640973Sbostic else if (gotctluser == 2) 81740973Sbostic { 81840973Sbostic clrctluser(); 81940973Sbostic gotctluser = 0; 82040973Sbostic } 8214632Seric } 8229377Seric 82340973Sbostic /* clear controlling user in case we break out prematurely */ 82440973Sbostic clrctluser(); 82540973Sbostic 8269377Seric FileName = NULL; 82724941Seric 82824941Seric /* 82924941Seric ** If we haven't read any lines, this queue file is empty. 83024941Seric ** Arrange to remove it without referencing any null pointers. 83124941Seric */ 83224941Seric 83324941Seric if (LineNumber == 0) 83424941Seric { 83524941Seric errno = 0; 83624941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 83724941Seric } 83840934Srick return qfp; 8394632Seric } 8404632Seric /* 8419630Seric ** PRINTQUEUE -- print out a representation of the mail queue 8429630Seric ** 8439630Seric ** Parameters: 8449630Seric ** none. 8459630Seric ** 8469630Seric ** Returns: 8479630Seric ** none. 8489630Seric ** 8499630Seric ** Side Effects: 8509630Seric ** Prints a listing of the mail queue on the standard output. 8519630Seric */ 8525182Seric 8539630Seric printqueue() 8549630Seric { 8559630Seric register WORK *w; 8569630Seric FILE *f; 85710070Seric int nrequests; 8589630Seric char buf[MAXLINE]; 85940973Sbostic char cbuf[MAXLINE]; 8609630Seric 8619630Seric /* 8629630Seric ** Read and order the queue. 8639630Seric */ 8649630Seric 86524941Seric nrequests = orderq(TRUE); 8669630Seric 8679630Seric /* 8689630Seric ** Print the work list that we have read. 8699630Seric */ 8709630Seric 8719630Seric /* first see if there is anything */ 87210070Seric if (nrequests <= 0) 8739630Seric { 87410070Seric printf("Mail queue is empty\n"); 8759630Seric return; 8769630Seric } 8779630Seric 87840934Srick la = getla(); /* get load average */ 87940934Srick 88010096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 88125687Seric if (nrequests > QUEUESIZE) 88225687Seric printf(", only %d printed", QUEUESIZE); 88324979Seric if (Verbose) 88425032Seric printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 88524979Seric else 88624979Seric printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 8879630Seric for (w = WorkQ; w != NULL; w = w->w_next) 8889630Seric { 8899630Seric struct stat st; 89010070Seric auto time_t submittime = 0; 89110070Seric long dfsize = -1; 89210108Seric char message[MAXLINE]; 89324941Seric extern bool shouldqueue(); 8949630Seric 89517468Seric f = fopen(w->w_name, "r"); 89617468Seric if (f == NULL) 89717468Seric { 89817468Seric errno = 0; 89917468Seric continue; 90017468Seric } 9019630Seric printf("%7s", w->w_name + 2); 902*51835Seric # ifdef LOCKF 903*51835Seric if (flock(fileno(f), F_TEST, 0) < 0) 904*51835Seric # else 90540934Srick if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0) 906*51835Seric # endif 90710070Seric printf("*"); 90824941Seric else if (shouldqueue(w->w_pri)) 90924941Seric printf("X"); 91010070Seric else 91110070Seric printf(" "); 91210070Seric errno = 0; 91317468Seric 91410108Seric message[0] = '\0'; 91540973Sbostic cbuf[0] = '\0'; 9169630Seric while (fgets(buf, sizeof buf, f) != NULL) 9179630Seric { 9189630Seric fixcrlf(buf, TRUE); 9199630Seric switch (buf[0]) 9209630Seric { 92110108Seric case 'M': /* error message */ 92223098Seric (void) strcpy(message, &buf[1]); 92310108Seric break; 92410108Seric 9259630Seric case 'S': /* sender name */ 92624979Seric if (Verbose) 92725027Seric printf("%8ld %10ld %.12s %.38s", dfsize, 92825027Seric w->w_pri, ctime(&submittime) + 4, 92924979Seric &buf[1]); 93024979Seric else 93124979Seric printf("%8ld %.16s %.45s", dfsize, 93224979Seric ctime(&submittime), &buf[1]); 93310108Seric if (message[0] != '\0') 93425027Seric printf("\n\t\t (%.60s)", message); 9359630Seric break; 93640973Sbostic case 'C': /* controlling user */ 93740973Sbostic if (strlen(buf) < MAXLINE-3) /* sanity */ 93840973Sbostic (void) strcat(buf, ") "); 93940973Sbostic cbuf[0] = cbuf[1] = '('; 94040973Sbostic (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1); 94140973Sbostic cbuf[MAXLINE-1] = '\0'; 94240973Sbostic break; 9439630Seric 9449630Seric case 'R': /* recipient name */ 94540973Sbostic if (cbuf[0] != '\0') { 94640973Sbostic /* prepend controlling user to `buf' */ 94740973Sbostic (void) strncat(cbuf, &buf[1], 94840973Sbostic MAXLINE-strlen(cbuf)); 94940973Sbostic cbuf[MAXLINE-1] = '\0'; 95040973Sbostic (void) strcpy(buf, cbuf); 95140973Sbostic cbuf[0] = '\0'; 95240973Sbostic } 95324979Seric if (Verbose) 95425027Seric printf("\n\t\t\t\t\t %.38s", &buf[1]); 95524979Seric else 95624979Seric printf("\n\t\t\t\t %.45s", &buf[1]); 9579630Seric break; 9589630Seric 9599630Seric case 'T': /* creation time */ 96024941Seric submittime = atol(&buf[1]); 9619630Seric break; 96210070Seric 96310070Seric case 'D': /* data file name */ 96410070Seric if (stat(&buf[1], &st) >= 0) 96510070Seric dfsize = st.st_size; 96610070Seric break; 9679630Seric } 9689630Seric } 96910070Seric if (submittime == (time_t) 0) 97010070Seric printf(" (no control file)"); 9719630Seric printf("\n"); 97223098Seric (void) fclose(f); 9739630Seric } 9749630Seric } 9759630Seric 9765182Seric # endif QUEUE 97717468Seric /* 97817468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 97917468Seric ** 98017468Seric ** Assigns an id code if one does not already exist. 98117468Seric ** This code is very careful to avoid trashing existing files 98217468Seric ** under any circumstances. 98317468Seric ** 98417468Seric ** Parameters: 98517468Seric ** e -- envelope to build it in/from. 98617468Seric ** type -- the file type, used as the first character 98717468Seric ** of the file name. 98817468Seric ** 98917468Seric ** Returns: 99017468Seric ** a pointer to the new file name (in a static buffer). 99117468Seric ** 99217468Seric ** Side Effects: 99340934Srick ** Will create the qf file if no id code is 99417468Seric ** already assigned. This will cause the envelope 99517468Seric ** to be modified. 99617468Seric */ 99717468Seric 99817468Seric char * 99917468Seric queuename(e, type) 100017468Seric register ENVELOPE *e; 100117468Seric char type; 100217468Seric { 100317468Seric static char buf[MAXNAME]; 100417468Seric static int pid = -1; 100517468Seric char c1 = 'A'; 100617468Seric char c2 = 'A'; 100717468Seric 100817468Seric if (e->e_id == NULL) 100917468Seric { 101017468Seric char qf[20]; 101117468Seric 101217468Seric /* find a unique id */ 101317468Seric if (pid != getpid()) 101417468Seric { 101517468Seric /* new process -- start back at "AA" */ 101617468Seric pid = getpid(); 101717468Seric c1 = 'A'; 101817468Seric c2 = 'A' - 1; 101917468Seric } 102017468Seric (void) sprintf(qf, "qfAA%05d", pid); 102117468Seric 102217468Seric while (c1 < '~' || c2 < 'Z') 102317468Seric { 102417468Seric int i; 102517468Seric 102617468Seric if (c2 >= 'Z') 102717468Seric { 102817468Seric c1++; 102917468Seric c2 = 'A' - 1; 103017468Seric } 103140934Srick qf[2] = c1; 103240934Srick qf[3] = ++c2; 103317468Seric if (tTd(7, 20)) 103440934Srick printf("queuename: trying \"%s\"\n", qf); 103517468Seric 103640934Srick i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); 103740934Srick if (i < 0) { 103840934Srick if (errno != EEXIST) { 103936233Skarels syserr("queuename: Cannot create \"%s\" in \"%s\"", 104040934Srick qf, QueueDir); 104136233Skarels exit(EX_UNAVAILABLE); 104236233Skarels } 104340934Srick } else { 104440934Srick (void) close(i); 104540934Srick break; 104617468Seric } 104717468Seric } 104817468Seric if (c1 >= '~' && c2 >= 'Z') 104917468Seric { 105017468Seric syserr("queuename: Cannot create \"%s\" in \"%s\"", 105117468Seric qf, QueueDir); 105217468Seric exit(EX_OSERR); 105317468Seric } 105417468Seric e->e_id = newstr(&qf[2]); 105517468Seric define('i', e->e_id, e); 105617468Seric if (tTd(7, 1)) 105717468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 105817468Seric # ifdef LOG 105917468Seric if (LogLevel > 16) 106017468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 106117468Seric # endif LOG 106217468Seric } 106317468Seric 106417468Seric if (type == '\0') 106517468Seric return (NULL); 106617468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 106717468Seric if (tTd(7, 2)) 106817468Seric printf("queuename: %s\n", buf); 106917468Seric return (buf); 107017468Seric } 107117468Seric /* 107217468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 107317468Seric ** 107417468Seric ** Parameters: 107517468Seric ** e -- the envelope to unlock. 107617468Seric ** 107717468Seric ** Returns: 107817468Seric ** none 107917468Seric ** 108017468Seric ** Side Effects: 108117468Seric ** unlocks the queue for `e'. 108217468Seric */ 108317468Seric 108417468Seric unlockqueue(e) 108517468Seric ENVELOPE *e; 108617468Seric { 108717468Seric /* remove the transcript */ 108817468Seric # ifdef LOG 108917468Seric if (LogLevel > 19) 109017468Seric syslog(LOG_DEBUG, "%s: unlock", e->e_id); 109117468Seric # endif LOG 109217468Seric if (!tTd(51, 4)) 109317468Seric xunlink(queuename(e, 'x')); 109417468Seric 109517468Seric } 109640973Sbostic /* 109740973Sbostic ** GETCTLUSER -- return controlling user if mailing to prog or file 109840973Sbostic ** 109940973Sbostic ** Check for a "|" or "/" at the beginning of the address. If 110040973Sbostic ** found, return a controlling username. 110140973Sbostic ** 110240973Sbostic ** Parameters: 110340973Sbostic ** a - the address to check out 110440973Sbostic ** 110540973Sbostic ** Returns: 110640973Sbostic ** Either NULL, if we werent mailing to a program or file, 110740973Sbostic ** or a controlling user name (possibly in getpwuid's 110840973Sbostic ** static buffer). 110940973Sbostic ** 111040973Sbostic ** Side Effects: 111140973Sbostic ** none. 111240973Sbostic */ 111340973Sbostic 111440973Sbostic char * 111540973Sbostic getctluser(a) 111640973Sbostic ADDRESS *a; 111740973Sbostic { 111840973Sbostic extern ADDRESS *getctladdr(); 111940973Sbostic struct passwd *pw; 112040973Sbostic char *retstr; 112140973Sbostic 112240973Sbostic /* 112340973Sbostic ** Get unquoted user for file, program or user.name check. 112440973Sbostic ** N.B. remove this code block to always emit controlling 112540973Sbostic ** addresses (at the expense of backward compatibility). 112640973Sbostic */ 112740973Sbostic 112840973Sbostic { 112940973Sbostic char buf[MAXNAME]; 113040973Sbostic (void) strncpy(buf, a->q_paddr, MAXNAME); 113140973Sbostic buf[MAXNAME-1] = '\0'; 113240973Sbostic stripquotes(buf, TRUE); 113340973Sbostic 113440973Sbostic if (buf[0] != '|' && buf[0] != '/') 113540973Sbostic return((char *)NULL); 113640973Sbostic } 113740973Sbostic 113840973Sbostic a = getctladdr(a); /* find controlling address */ 113940973Sbostic 114040973Sbostic if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL) 114140973Sbostic retstr = pw->pw_name; 114240973Sbostic else /* use default user */ 114340973Sbostic retstr = DefUser; 114440973Sbostic 114540973Sbostic if (tTd(40, 5)) 114640973Sbostic printf("Set controlling user for `%s' to `%s'\n", 114740973Sbostic (a == NULL)? "<null>": a->q_paddr, retstr); 114840973Sbostic 114940973Sbostic return(retstr); 115040973Sbostic } 115140973Sbostic /* 115240973Sbostic ** SETCTLUSER - sets `CtlUser' to controlling user 115340973Sbostic ** CLRCTLUSER - clears controlling user (no params, nothing returned) 115440973Sbostic ** 115540973Sbostic ** These routines manipulate `CtlUser'. 115640973Sbostic ** 115740973Sbostic ** Parameters: 115840973Sbostic ** str - controlling user as passed to setctluser() 115940973Sbostic ** 116040973Sbostic ** Returns: 116140973Sbostic ** None. 116240973Sbostic ** 116340973Sbostic ** Side Effects: 116440973Sbostic ** `CtlUser' is changed. 116540973Sbostic */ 116640973Sbostic 116740973Sbostic static char CtlUser[MAXNAME]; 116840973Sbostic 116940973Sbostic setctluser(str) 117040973Sbostic register char *str; 117140973Sbostic { 117240973Sbostic (void) strncpy(CtlUser, str, MAXNAME); 117340973Sbostic CtlUser[MAXNAME-1] = '\0'; 117440973Sbostic } 117540973Sbostic 117640973Sbostic clrctluser() 117740973Sbostic { 117840973Sbostic CtlUser[0] = '\0'; 117940973Sbostic } 118040973Sbostic 118140973Sbostic /* 118240973Sbostic ** SETCTLADDR -- create a controlling address 118340973Sbostic ** 118440973Sbostic ** If global variable `CtlUser' is set and we are given a valid 118540973Sbostic ** address, make that address a controlling address; change the 118640973Sbostic ** `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID. 118740973Sbostic ** 118840973Sbostic ** Parameters: 118940973Sbostic ** a - address for which control uid/gid info may apply 119040973Sbostic ** 119140973Sbostic ** Returns: 119240973Sbostic ** None. 119340973Sbostic ** 119440973Sbostic ** Side Effects: 119540973Sbostic ** Fills in uid/gid fields in address and sets QGOODUID 119640973Sbostic ** flag if appropriate. 119740973Sbostic */ 119840973Sbostic 119940973Sbostic setctladdr(a) 120040973Sbostic ADDRESS *a; 120140973Sbostic { 120240973Sbostic struct passwd *pw; 120340973Sbostic 120440973Sbostic /* 120540973Sbostic ** If there is no current controlling user, or we were passed a 120640973Sbostic ** NULL addr ptr or we already have a controlling user, return. 120740973Sbostic */ 120840973Sbostic 120940973Sbostic if (CtlUser[0] == '\0' || a == NULL || a->q_ruser) 121040973Sbostic return; 121140973Sbostic 121240973Sbostic /* 121340973Sbostic ** Set up addr fields for controlling user. If `CtlUser' is no 121440973Sbostic ** longer valid, use the default user/group. 121540973Sbostic */ 121640973Sbostic 121740973Sbostic if ((pw = getpwnam(CtlUser)) != NULL) 121840973Sbostic { 121940973Sbostic if (a->q_home) 122040973Sbostic free(a->q_home); 122140973Sbostic a->q_home = newstr(pw->pw_dir); 122240973Sbostic a->q_uid = pw->pw_uid; 122340973Sbostic a->q_gid = pw->pw_gid; 122440973Sbostic a->q_ruser = newstr(CtlUser); 122540973Sbostic } 122640973Sbostic else 122740973Sbostic { 122840973Sbostic a->q_uid = DefUid; 122940973Sbostic a->q_gid = DefGid; 123040973Sbostic a->q_ruser = newstr(DefUser); 123140973Sbostic } 123240973Sbostic 123340973Sbostic a->q_flags |= QGOODUID; /* flag as a "ctladdr" */ 123440973Sbostic 123540973Sbostic if (tTd(40, 5)) 123640973Sbostic printf("Restored controlling user for `%s' to `%s'\n", 123740973Sbostic a->q_paddr, a->q_ruser); 123840973Sbostic } 1239