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*41636Srick static char sccsid[] = "@(#)queue.c 5.29 (Berkeley) 05/11/90 (with queueing)"; 2433731Sbostic #else 25*41636Srick static char sccsid[] = "@(#)queue.c 5.29 (Berkeley) 05/11/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> 3440973Sbostic # 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 *qf; 77*41636Srick char buf[MAXLINE], tf[MAXLINE]; 787812Seric register FILE *tfp; 794632Seric register HDR *h; 805007Seric register ADDRESS *q; 8110173Seric MAILER nullmailer; 82*41636Srick int fd, ret; 834632Seric 845037Seric /* 8517477Seric ** Create control file. 865037Seric */ 874632Seric 88*41636Srick do { 89*41636Srick strcpy(tf, queuename(e, 't')); 90*41636Srick fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); 91*41636Srick if (fd < 0) { 92*41636Srick if ( errno != EEXIST) { 93*41636Srick syserr("queueup: cannot create temp file %s", 94*41636Srick tf); 95*41636Srick return NULL; 96*41636Srick } 97*41636Srick } else { 98*41636Srick if (flock(fd, LOCK_EX|LOCK_NB) < 0) { 99*41636Srick if (errno != EWOULDBLOCK) 100*41636Srick syserr("cannot flock(%s)", tf); 101*41636Srick close(fd); 102*41636Srick fd = -1; 103*41636Srick } 104*41636Srick } 105*41636Srick } while (fd < 0); 106*41636Srick 10740934Srick tfp = fdopen(fd, "w"); 1084632Seric 1097677Seric if (tTd(40, 1)) 11017468Seric printf("queueing %s\n", e->e_id); 1114632Seric 1124632Seric /* 1136980Seric ** If there is no data file yet, create one. 1146980Seric */ 1156980Seric 1166980Seric if (e->e_df == NULL) 1176980Seric { 1186980Seric register FILE *dfp; 1199389Seric extern putbody(); 1206980Seric 1217812Seric e->e_df = newstr(queuename(e, 'd')); 12240934Srick fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode); 12340934Srick if (fd < 0) 1246980Seric { 1256980Seric syserr("queueup: cannot create %s", e->e_df); 1267812Seric (void) fclose(tfp); 12740934Srick return NULL; 1286980Seric } 12940934Srick dfp = fdopen(fd, "w"); 13010173Seric (*e->e_putbody)(dfp, ProgMailer, e); 1317009Seric (void) fclose(dfp); 1329389Seric e->e_putbody = putbody; 1336980Seric } 1346980Seric 1356980Seric /* 1364632Seric ** Output future work requests. 13725687Seric ** Priority and creation time should be first, since 13825687Seric ** they are required by orderq. 1394632Seric */ 1404632Seric 1419377Seric /* output message priority */ 1429377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 1439377Seric 1449630Seric /* output creation time */ 1459630Seric fprintf(tfp, "T%ld\n", e->e_ctime); 1469630Seric 1474632Seric /* output name of data file */ 1487812Seric fprintf(tfp, "D%s\n", e->e_df); 1494632Seric 15010108Seric /* message from envelope, if it exists */ 15110108Seric if (e->e_message != NULL) 15210108Seric fprintf(tfp, "M%s\n", e->e_message); 15310108Seric 1544632Seric /* output name of sender */ 1557812Seric fprintf(tfp, "S%s\n", e->e_from.q_paddr); 1564632Seric 1574632Seric /* output list of recipient addresses */ 1586980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 1594632Seric { 1607763Seric if (queueall ? !bitset(QDONTSEND, q->q_flags) : 1617763Seric bitset(QQUEUEUP, q->q_flags)) 1628245Seric { 16340973Sbostic char *ctluser, *getctluser(); 16440973Sbostic 16540973Sbostic if ((ctluser = getctluser(q)) != NULL) 16640973Sbostic fprintf(tfp, "C%s\n", ctluser); 1677812Seric fprintf(tfp, "R%s\n", q->q_paddr); 1689377Seric if (announce) 1699377Seric { 1709377Seric e->e_to = q->q_paddr; 1719377Seric message(Arpa_Info, "queued"); 1729377Seric if (LogLevel > 4) 1739377Seric logdelivery("queued"); 1749377Seric e->e_to = NULL; 1759377Seric } 1769387Seric if (tTd(40, 1)) 1779387Seric { 1789387Seric printf("queueing "); 1799387Seric printaddr(q, FALSE); 1809387Seric } 1818245Seric } 1824632Seric } 1834632Seric 18425687Seric /* output list of error recipients */ 18525687Seric for (q = e->e_errorqueue; q != NULL; q = q->q_next) 18625687Seric { 18726504Seric if (!bitset(QDONTSEND, q->q_flags)) 18840973Sbostic { 18940973Sbostic char *ctluser, *getctluser(); 19040973Sbostic 19140973Sbostic if ((ctluser = getctluser(q)) != NULL) 19240973Sbostic fprintf(tfp, "C%s\n", ctluser); 19326504Seric fprintf(tfp, "E%s\n", q->q_paddr); 19440973Sbostic } 19525687Seric } 19625687Seric 1979377Seric /* 1989377Seric ** Output headers for this message. 1999377Seric ** Expand macros completely here. Queue run will deal with 2009377Seric ** everything as absolute headers. 2019377Seric ** All headers that must be relative to the recipient 2029377Seric ** can be cracked later. 20310173Seric ** We set up a "null mailer" -- i.e., a mailer that will have 20410173Seric ** no effect on the addresses as they are output. 2059377Seric */ 2069377Seric 20710686Seric bzero((char *) &nullmailer, sizeof nullmailer); 20810173Seric nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; 20910349Seric nullmailer.m_eol = "\n"; 21010173Seric 21116147Seric define('g', "\001f", e); 2126980Seric for (h = e->e_header; h != NULL; h = h->h_link) 2134632Seric { 21410686Seric extern bool bitzerop(); 21510686Seric 21612015Seric /* don't output null headers */ 2174632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 2184632Seric continue; 21912015Seric 22012015Seric /* don't output resent headers on non-resent messages */ 22112015Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 22212015Seric continue; 22312015Seric 22412015Seric /* output this header */ 2257812Seric fprintf(tfp, "H"); 22612015Seric 22712015Seric /* if conditional, output the set of conditions */ 22810686Seric if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 22910686Seric { 23010686Seric int j; 23110686Seric 23223098Seric (void) putc('?', tfp); 23310686Seric for (j = '\0'; j <= '\177'; j++) 23410686Seric if (bitnset(j, h->h_mflags)) 23523098Seric (void) putc(j, tfp); 23623098Seric (void) putc('?', tfp); 23710686Seric } 23812015Seric 23912015Seric /* output the header: expand macros, convert addresses */ 2407763Seric if (bitset(H_DEFAULT, h->h_flags)) 2417763Seric { 2427763Seric (void) expand(h->h_value, buf, &buf[sizeof buf], e); 2438236Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 2447763Seric } 2458245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 2469348Seric { 2479348Seric commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 24810173Seric &nullmailer); 2499348Seric } 2507763Seric else 2518245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 2524632Seric } 2534632Seric 2544632Seric /* 2554632Seric ** Clean up. 2564632Seric */ 2574632Seric 25817468Seric qf = queuename(e, 'q'); 259*41636Srick if (rename(tf, qf) < 0) 260*41636Srick syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df); 261*41636Srick errno = 0; 2627391Seric 2637677Seric # ifdef LOG 2647677Seric /* save log info */ 2657878Seric if (LogLevel > 15) 2667878Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 2677677Seric # endif LOG 26840934Srick fflush(tfp); 26940934Srick return tfp; 2704632Seric } 2714632Seric /* 2724632Seric ** RUNQUEUE -- run the jobs in the queue. 2734632Seric ** 2744632Seric ** Gets the stuff out of the queue in some presumably logical 2754632Seric ** order and processes them. 2764632Seric ** 2774632Seric ** Parameters: 27824941Seric ** forkflag -- TRUE if the queue scanning should be done in 27924941Seric ** a child process. We double-fork so it is not our 28024941Seric ** child and we don't have to clean up after it. 2814632Seric ** 2824632Seric ** Returns: 2834632Seric ** none. 2844632Seric ** 2854632Seric ** Side Effects: 2864632Seric ** runs things in the mail queue. 2874632Seric */ 2884632Seric 2894639Seric runqueue(forkflag) 2904639Seric bool forkflag; 2914632Seric { 29224953Seric extern bool shouldqueue(); 29324953Seric 2947466Seric /* 29524953Seric ** If no work will ever be selected, don't even bother reading 29624953Seric ** the queue. 29724953Seric */ 29824953Seric 29940934Srick la = getla(); /* get load average */ 30040934Srick 30124953Seric if (shouldqueue(-100000000L)) 30224953Seric { 30324953Seric if (Verbose) 30424953Seric printf("Skipping queue run -- load average too high\n"); 30524953Seric 30624953Seric if (forkflag) 30724953Seric return; 30824953Seric finis(); 30924953Seric } 31024953Seric 31124953Seric /* 3127466Seric ** See if we want to go off and do other useful work. 3137466Seric */ 3144639Seric 3154639Seric if (forkflag) 3164639Seric { 3177943Seric int pid; 3187943Seric 3197943Seric pid = dofork(); 3207943Seric if (pid != 0) 3214639Seric { 32225184Seric extern reapchild(); 32325184Seric 3247943Seric /* parent -- pick up intermediate zombie */ 32525184Seric #ifndef SIGCHLD 3269377Seric (void) waitfor(pid); 32725184Seric #else SIGCHLD 32825184Seric (void) signal(SIGCHLD, reapchild); 32925184Seric #endif SIGCHLD 3307690Seric if (QueueIntvl != 0) 3319348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 3324639Seric return; 3334639Seric } 3347943Seric /* child -- double fork */ 33525184Seric #ifndef SIGCHLD 3367943Seric if (fork() != 0) 3377943Seric exit(EX_OK); 33825184Seric #else SIGCHLD 33925184Seric (void) signal(SIGCHLD, SIG_DFL); 34025184Seric #endif SIGCHLD 3414639Seric } 34224941Seric 34340934Srick setproctitle("running queue: %s", QueueDir); 34424941Seric 3457876Seric # ifdef LOG 3467876Seric if (LogLevel > 11) 3477943Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 3487876Seric # endif LOG 3494639Seric 3507466Seric /* 35110205Seric ** Release any resources used by the daemon code. 35210205Seric */ 35310205Seric 35410205Seric # ifdef DAEMON 35510205Seric clrdaemon(); 35610205Seric # endif DAEMON 35710205Seric 35810205Seric /* 35927175Seric ** Make sure the alias database is open. 36027175Seric */ 36127175Seric 36227175Seric initaliases(AliasFile, FALSE); 36327175Seric 36427175Seric /* 3657466Seric ** Start making passes through the queue. 3667466Seric ** First, read and sort the entire queue. 3677466Seric ** Then, process the work in that order. 3687466Seric ** But if you take too long, start over. 3697466Seric */ 3707466Seric 3717943Seric /* order the existing work requests */ 37224954Seric (void) orderq(FALSE); 3737690Seric 3747943Seric /* process them once at a time */ 3757943Seric while (WorkQ != NULL) 3764639Seric { 3777943Seric WORK *w = WorkQ; 3787881Seric 3797943Seric WorkQ = WorkQ->w_next; 3807943Seric dowork(w); 3817943Seric free(w->w_name); 3827943Seric free((char *) w); 3834639Seric } 38429866Seric 38529866Seric /* exit without the usual cleanup */ 38629866Seric exit(ExitStat); 3874634Seric } 3884634Seric /* 3894632Seric ** ORDERQ -- order the work queue. 3904632Seric ** 3914632Seric ** Parameters: 39224941Seric ** doall -- if set, include everything in the queue (even 39324941Seric ** the jobs that cannot be run because the load 39424941Seric ** average is too high). Otherwise, exclude those 39524941Seric ** jobs. 3964632Seric ** 3974632Seric ** Returns: 39810121Seric ** The number of request in the queue (not necessarily 39910121Seric ** the number of requests in WorkQ however). 4004632Seric ** 4014632Seric ** Side Effects: 4024632Seric ** Sets WorkQ to the queue of available work, in order. 4034632Seric */ 4044632Seric 40525687Seric # define NEED_P 001 40625687Seric # define NEED_T 002 4074632Seric 40824941Seric orderq(doall) 40924941Seric bool doall; 4104632Seric { 4116625Sglickman register struct direct *d; 4124632Seric register WORK *w; 4136625Sglickman DIR *f; 4144632Seric register int i; 41525687Seric WORK wlist[QUEUESIZE+1]; 41610070Seric int wn = -1; 4174632Seric extern workcmpf(); 4184632Seric 4194632Seric /* clear out old WorkQ */ 4204632Seric for (w = WorkQ; w != NULL; ) 4214632Seric { 4224632Seric register WORK *nw = w->w_next; 4234632Seric 4244632Seric WorkQ = nw; 4254632Seric free(w->w_name); 4264632Seric free((char *) w); 4274632Seric w = nw; 4284632Seric } 4294632Seric 4304632Seric /* open the queue directory */ 4318148Seric f = opendir("."); 4324632Seric if (f == NULL) 4334632Seric { 4348148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 43510070Seric return (0); 4364632Seric } 4374632Seric 4384632Seric /* 4394632Seric ** Read the work directory. 4404632Seric */ 4414632Seric 44210070Seric while ((d = readdir(f)) != NULL) 4434632Seric { 4449377Seric FILE *cf; 4454632Seric char lbuf[MAXNAME]; 4464632Seric 4474632Seric /* is this an interesting entry? */ 4487812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 4494632Seric continue; 4504632Seric 45110070Seric /* yes -- open control file (if not too many files) */ 45225687Seric if (++wn >= QUEUESIZE) 45310070Seric continue; 4548148Seric cf = fopen(d->d_name, "r"); 4554632Seric if (cf == NULL) 4564632Seric { 4577055Seric /* this may be some random person sending hir msgs */ 4587055Seric /* syserr("orderq: cannot open %s", cbuf); */ 45910090Seric if (tTd(41, 2)) 46010090Seric printf("orderq: cannot open %s (%d)\n", 46110090Seric d->d_name, errno); 4627055Seric errno = 0; 46310090Seric wn--; 4644632Seric continue; 4654632Seric } 46625687Seric w = &wlist[wn]; 46725687Seric w->w_name = newstr(d->d_name); 4684632Seric 46925027Seric /* make sure jobs in creation don't clog queue */ 47025687Seric w->w_pri = 0x7fffffff; 47125687Seric w->w_ctime = 0; 47225027Seric 4734632Seric /* extract useful information */ 47425687Seric i = NEED_P | NEED_T; 47525687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 4764632Seric { 47724954Seric extern long atol(); 47824954Seric 47924941Seric switch (lbuf[0]) 4804632Seric { 48124941Seric case 'P': 48225687Seric w->w_pri = atol(&lbuf[1]); 48325687Seric i &= ~NEED_P; 4844632Seric break; 48525013Seric 48625013Seric case 'T': 48725687Seric w->w_ctime = atol(&lbuf[1]); 48825687Seric i &= ~NEED_T; 48925013Seric break; 4904632Seric } 4914632Seric } 4924632Seric (void) fclose(cf); 49324953Seric 49425687Seric if (!doall && shouldqueue(w->w_pri)) 49524953Seric { 49624953Seric /* don't even bother sorting this job in */ 49724953Seric wn--; 49824953Seric } 4994632Seric } 5006625Sglickman (void) closedir(f); 50110090Seric wn++; 5024632Seric 5034632Seric /* 5044632Seric ** Sort the work directory. 5054632Seric */ 5064632Seric 50725687Seric qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); 5084632Seric 5094632Seric /* 5104632Seric ** Convert the work list into canonical form. 5119377Seric ** Should be turning it into a list of envelopes here perhaps. 5124632Seric */ 5134632Seric 51424981Seric WorkQ = NULL; 51525687Seric for (i = min(wn, QUEUESIZE); --i >= 0; ) 5164632Seric { 5174632Seric w = (WORK *) xalloc(sizeof *w); 5184632Seric w->w_name = wlist[i].w_name; 5194632Seric w->w_pri = wlist[i].w_pri; 52025013Seric w->w_ctime = wlist[i].w_ctime; 52124981Seric w->w_next = WorkQ; 52224981Seric WorkQ = w; 5234632Seric } 5244632Seric 5257677Seric if (tTd(40, 1)) 5264632Seric { 5274632Seric for (w = WorkQ; w != NULL; w = w->w_next) 5285037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 5294632Seric } 53010070Seric 53110090Seric return (wn); 5324632Seric } 5334632Seric /* 5347677Seric ** WORKCMPF -- compare function for ordering work. 5354632Seric ** 5364632Seric ** Parameters: 5374632Seric ** a -- the first argument. 5384632Seric ** b -- the second argument. 5394632Seric ** 5404632Seric ** Returns: 54124981Seric ** -1 if a < b 54224981Seric ** 0 if a == b 54324981Seric ** +1 if a > b 5444632Seric ** 5454632Seric ** Side Effects: 5464632Seric ** none. 5474632Seric */ 5484632Seric 5494632Seric workcmpf(a, b) 5505037Seric register WORK *a; 5515037Seric register WORK *b; 5524632Seric { 55325013Seric long pa = a->w_pri + a->w_ctime; 55425013Seric long pb = b->w_pri + b->w_ctime; 55524941Seric 55624941Seric if (pa == pb) 5574632Seric return (0); 55824941Seric else if (pa > pb) 55924981Seric return (1); 56024981Seric else 56110121Seric return (-1); 5624632Seric } 5634632Seric /* 5644632Seric ** DOWORK -- do a work request. 5654632Seric ** 5664632Seric ** Parameters: 5674632Seric ** w -- the work request to be satisfied. 5684632Seric ** 5694632Seric ** Returns: 5704632Seric ** none. 5714632Seric ** 5724632Seric ** Side Effects: 5734632Seric ** The work request is satisfied if possible. 5744632Seric */ 5754632Seric 5764632Seric dowork(w) 5774632Seric register WORK *w; 5784632Seric { 5794632Seric register int i; 58024941Seric extern bool shouldqueue(); 5814632Seric 5827677Seric if (tTd(40, 1)) 5835037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 5844632Seric 5854632Seric /* 58624941Seric ** Ignore jobs that are too expensive for the moment. 5874632Seric */ 5884632Seric 58924941Seric if (shouldqueue(w->w_pri)) 5904632Seric { 59124941Seric if (Verbose) 59224967Seric printf("\nSkipping %s\n", w->w_name + 2); 5934632Seric return; 5944632Seric } 5954632Seric 59624941Seric /* 59724941Seric ** Fork for work. 59824941Seric */ 59924941Seric 60024941Seric if (ForkQueueRuns) 60124941Seric { 60224941Seric i = fork(); 60324941Seric if (i < 0) 60424941Seric { 60524941Seric syserr("dowork: cannot fork"); 60624941Seric return; 60724941Seric } 60824941Seric } 60924941Seric else 61024941Seric { 61124941Seric i = 0; 61224941Seric } 61324941Seric 6144632Seric if (i == 0) 6154632Seric { 61640934Srick FILE *qflock, *readqf(); 6174632Seric /* 6184632Seric ** CHILD 6198148Seric ** Lock the control file to avoid duplicate deliveries. 6208148Seric ** Then run the file as though we had just read it. 6217350Seric ** We save an idea of the temporary name so we 6227350Seric ** can recover on interrupt. 6234632Seric */ 6244632Seric 6257763Seric /* set basic modes, etc. */ 6267356Seric (void) alarm(0); 62725612Seric clearenvelope(CurEnv, FALSE); 6284632Seric QueueRun = TRUE; 6299377Seric ErrorMode = EM_MAIL; 6308148Seric CurEnv->e_id = &w->w_name[2]; 6317876Seric # ifdef LOG 6327876Seric if (LogLevel > 11) 6337881Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 6347881Seric getpid()); 6357876Seric # endif LOG 6367763Seric 6377763Seric /* don't use the headers from sendmail.cf... */ 6387763Seric CurEnv->e_header = NULL; 6397763Seric 64040934Srick /* read the queue control file */ 64140934Srick /* and lock the control file during processing */ 64240934Srick if ((qflock=readqf(CurEnv, TRUE)) == NULL) 6436980Seric { 64424941Seric if (ForkQueueRuns) 64524941Seric exit(EX_OK); 64624941Seric else 64724941Seric return; 6486980Seric } 6496980Seric 6509338Seric CurEnv->e_flags |= EF_INQUEUE; 6519377Seric eatheader(CurEnv); 6526980Seric 6536980Seric /* do the delivery */ 6549338Seric if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 6559282Seric sendall(CurEnv, SM_DELIVER); 6566980Seric 6576980Seric /* finish up and exit */ 65824941Seric if (ForkQueueRuns) 65924941Seric finis(); 66024941Seric else 66124941Seric dropenvelope(CurEnv); 662*41636Srick fclose(qflock); 6634632Seric } 66424941Seric else 66524941Seric { 66624941Seric /* 66724941Seric ** Parent -- pick up results. 66824941Seric */ 6694632Seric 67024941Seric errno = 0; 67124941Seric (void) waitfor(i); 67224941Seric } 6734632Seric } 6744632Seric /* 6754632Seric ** READQF -- read queue file and set up environment. 6764632Seric ** 6774632Seric ** Parameters: 6789377Seric ** e -- the envelope of the job to run. 6799630Seric ** full -- if set, read in all information. Otherwise just 6809630Seric ** read in info needed for a queue print. 6814632Seric ** 6824632Seric ** Returns: 68340934Srick ** FILE * pointing to flock()ed fd so it can be closed 68440934Srick ** after the mail is delivered 6854632Seric ** 6864632Seric ** Side Effects: 6874632Seric ** cf is read and created as the current job, as though 6884632Seric ** we had been invoked by argument. 6894632Seric */ 6904632Seric 69140934Srick FILE * 69217477Seric readqf(e, full) 6939377Seric register ENVELOPE *e; 6949630Seric bool full; 6954632Seric { 69617477Seric char *qf; 69717477Seric register FILE *qfp; 6987785Seric char buf[MAXFIELD]; 6999348Seric extern char *fgetfolded(); 70024954Seric extern long atol(); 70140973Sbostic int gotctluser = 0; 70240934Srick int fd; 7034632Seric 7044632Seric /* 70517468Seric ** Read and process the file. 7064632Seric */ 7074632Seric 70817477Seric qf = queuename(e, 'q'); 70917477Seric qfp = fopen(qf, "r"); 71017477Seric if (qfp == NULL) 71117477Seric { 71240934Srick if (errno != ENOENT) 71340934Srick syserr("readqf: no control file %s", qf); 71440934Srick return NULL; 71517477Seric } 71640934Srick 71740934Srick if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0) 71840934Srick { 71940934Srick # ifdef LOG 72040934Srick /* being processed by another queuer */ 72140934Srick if (Verbose) 722*41636Srick printf("%s: locked\n", CurEnv->e_id); 72340934Srick # endif LOG 72440934Srick (void) fclose(qfp); 72540934Srick return NULL; 72640934Srick } 72740934Srick 72840934Srick /* do basic system initialization */ 72940934Srick initsys(); 73040934Srick 73117477Seric FileName = qf; 7329377Seric LineNumber = 0; 7339630Seric if (Verbose && full) 7349377Seric printf("\nRunning %s\n", e->e_id); 73517468Seric while (fgetfolded(buf, sizeof buf, qfp) != NULL) 7364632Seric { 73726504Seric if (tTd(40, 4)) 73826504Seric printf("+++++ %s\n", buf); 7394632Seric switch (buf[0]) 7404632Seric { 74140973Sbostic case 'C': /* specify controlling user */ 74240973Sbostic setctluser(&buf[1]); 74340973Sbostic gotctluser = 1; 74440973Sbostic break; 74540973Sbostic 7464632Seric case 'R': /* specify recipient */ 7479618Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 7484632Seric break; 7494632Seric 75025687Seric case 'E': /* specify error recipient */ 75125687Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue); 75225687Seric break; 75325687Seric 7544632Seric case 'H': /* header */ 7559630Seric if (full) 7569630Seric (void) chompheader(&buf[1], FALSE); 7574632Seric break; 7584632Seric 75910108Seric case 'M': /* message */ 76010108Seric e->e_message = newstr(&buf[1]); 76110108Seric break; 76210108Seric 7634632Seric case 'S': /* sender */ 7644634Seric setsender(newstr(&buf[1])); 7654632Seric break; 7664632Seric 7674632Seric case 'D': /* data file name */ 7689630Seric if (!full) 7699630Seric break; 7709377Seric e->e_df = newstr(&buf[1]); 7719544Seric e->e_dfp = fopen(e->e_df, "r"); 7729544Seric if (e->e_dfp == NULL) 7739377Seric syserr("readqf: cannot open %s", e->e_df); 7744632Seric break; 7754632Seric 7767860Seric case 'T': /* init time */ 77724941Seric e->e_ctime = atol(&buf[1]); 7784632Seric break; 7794632Seric 7804634Seric case 'P': /* message priority */ 78125008Seric e->e_msgpriority = atol(&buf[1]) + WkTimeFact; 7824634Seric break; 7834634Seric 78424941Seric case '\0': /* blank line; ignore */ 78524941Seric break; 78624941Seric 7874632Seric default: 78824941Seric syserr("readqf(%s:%d): bad line \"%s\"", e->e_id, 78924941Seric LineNumber, buf); 7904632Seric break; 7914632Seric } 79240973Sbostic /* 79340973Sbostic ** The `C' queue file command operates on the next line, 79440973Sbostic ** so we use "gotctluser" to maintain state as follows: 79540973Sbostic ** 0 - no controlling user, 79640973Sbostic ** 1 - controlling user has been set but not used, 79740973Sbostic ** 2 - controlling user must be used on next iteration. 79840973Sbostic */ 79940973Sbostic if (gotctluser == 1) 80040973Sbostic gotctluser++; 80140973Sbostic else if (gotctluser == 2) 80240973Sbostic { 80340973Sbostic clrctluser(); 80440973Sbostic gotctluser = 0; 80540973Sbostic } 8064632Seric } 8079377Seric 80840973Sbostic /* clear controlling user in case we break out prematurely */ 80940973Sbostic clrctluser(); 81040973Sbostic 8119377Seric FileName = NULL; 81224941Seric 81324941Seric /* 81424941Seric ** If we haven't read any lines, this queue file is empty. 81524941Seric ** Arrange to remove it without referencing any null pointers. 81624941Seric */ 81724941Seric 81824941Seric if (LineNumber == 0) 81924941Seric { 82024941Seric errno = 0; 82124941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 82224941Seric } 82340934Srick return qfp; 8244632Seric } 8254632Seric /* 8269630Seric ** PRINTQUEUE -- print out a representation of the mail queue 8279630Seric ** 8289630Seric ** Parameters: 8299630Seric ** none. 8309630Seric ** 8319630Seric ** Returns: 8329630Seric ** none. 8339630Seric ** 8349630Seric ** Side Effects: 8359630Seric ** Prints a listing of the mail queue on the standard output. 8369630Seric */ 8375182Seric 8389630Seric printqueue() 8399630Seric { 8409630Seric register WORK *w; 8419630Seric FILE *f; 84210070Seric int nrequests; 8439630Seric char buf[MAXLINE]; 84440973Sbostic char cbuf[MAXLINE]; 8459630Seric 8469630Seric /* 8479630Seric ** Read and order the queue. 8489630Seric */ 8499630Seric 85024941Seric nrequests = orderq(TRUE); 8519630Seric 8529630Seric /* 8539630Seric ** Print the work list that we have read. 8549630Seric */ 8559630Seric 8569630Seric /* first see if there is anything */ 85710070Seric if (nrequests <= 0) 8589630Seric { 85910070Seric printf("Mail queue is empty\n"); 8609630Seric return; 8619630Seric } 8629630Seric 86340934Srick la = getla(); /* get load average */ 86440934Srick 86510096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 86625687Seric if (nrequests > QUEUESIZE) 86725687Seric printf(", only %d printed", QUEUESIZE); 86824979Seric if (Verbose) 86925032Seric printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 87024979Seric else 87124979Seric printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 8729630Seric for (w = WorkQ; w != NULL; w = w->w_next) 8739630Seric { 8749630Seric struct stat st; 87510070Seric auto time_t submittime = 0; 87610070Seric long dfsize = -1; 87710108Seric char message[MAXLINE]; 87824941Seric extern bool shouldqueue(); 8799630Seric 88017468Seric f = fopen(w->w_name, "r"); 88117468Seric if (f == NULL) 88217468Seric { 88317468Seric errno = 0; 88417468Seric continue; 88517468Seric } 8869630Seric printf("%7s", w->w_name + 2); 88740934Srick if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0) 88810070Seric printf("*"); 88924941Seric else if (shouldqueue(w->w_pri)) 89024941Seric printf("X"); 89110070Seric else 89210070Seric printf(" "); 89310070Seric errno = 0; 89417468Seric 89510108Seric message[0] = '\0'; 89640973Sbostic cbuf[0] = '\0'; 8979630Seric while (fgets(buf, sizeof buf, f) != NULL) 8989630Seric { 8999630Seric fixcrlf(buf, TRUE); 9009630Seric switch (buf[0]) 9019630Seric { 90210108Seric case 'M': /* error message */ 90323098Seric (void) strcpy(message, &buf[1]); 90410108Seric break; 90510108Seric 9069630Seric case 'S': /* sender name */ 90724979Seric if (Verbose) 90825027Seric printf("%8ld %10ld %.12s %.38s", dfsize, 90925027Seric w->w_pri, ctime(&submittime) + 4, 91024979Seric &buf[1]); 91124979Seric else 91224979Seric printf("%8ld %.16s %.45s", dfsize, 91324979Seric ctime(&submittime), &buf[1]); 91410108Seric if (message[0] != '\0') 91525027Seric printf("\n\t\t (%.60s)", message); 9169630Seric break; 91740973Sbostic case 'C': /* controlling user */ 91840973Sbostic if (strlen(buf) < MAXLINE-3) /* sanity */ 91940973Sbostic (void) strcat(buf, ") "); 92040973Sbostic cbuf[0] = cbuf[1] = '('; 92140973Sbostic (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1); 92240973Sbostic cbuf[MAXLINE-1] = '\0'; 92340973Sbostic break; 9249630Seric 9259630Seric case 'R': /* recipient name */ 92640973Sbostic if (cbuf[0] != '\0') { 92740973Sbostic /* prepend controlling user to `buf' */ 92840973Sbostic (void) strncat(cbuf, &buf[1], 92940973Sbostic MAXLINE-strlen(cbuf)); 93040973Sbostic cbuf[MAXLINE-1] = '\0'; 93140973Sbostic (void) strcpy(buf, cbuf); 93240973Sbostic cbuf[0] = '\0'; 93340973Sbostic } 93424979Seric if (Verbose) 93525027Seric printf("\n\t\t\t\t\t %.38s", &buf[1]); 93624979Seric else 93724979Seric printf("\n\t\t\t\t %.45s", &buf[1]); 9389630Seric break; 9399630Seric 9409630Seric case 'T': /* creation time */ 94124941Seric submittime = atol(&buf[1]); 9429630Seric break; 94310070Seric 94410070Seric case 'D': /* data file name */ 94510070Seric if (stat(&buf[1], &st) >= 0) 94610070Seric dfsize = st.st_size; 94710070Seric break; 9489630Seric } 9499630Seric } 95010070Seric if (submittime == (time_t) 0) 95110070Seric printf(" (no control file)"); 9529630Seric printf("\n"); 95323098Seric (void) fclose(f); 9549630Seric } 9559630Seric } 9569630Seric 9575182Seric # endif QUEUE 95817468Seric /* 95917468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 96017468Seric ** 96117468Seric ** Assigns an id code if one does not already exist. 96217468Seric ** This code is very careful to avoid trashing existing files 96317468Seric ** under any circumstances. 96417468Seric ** 96517468Seric ** Parameters: 96617468Seric ** e -- envelope to build it in/from. 96717468Seric ** type -- the file type, used as the first character 96817468Seric ** of the file name. 96917468Seric ** 97017468Seric ** Returns: 97117468Seric ** a pointer to the new file name (in a static buffer). 97217468Seric ** 97317468Seric ** Side Effects: 97440934Srick ** Will create the qf file if no id code is 97517468Seric ** already assigned. This will cause the envelope 97617468Seric ** to be modified. 97717468Seric */ 97817468Seric 97917468Seric char * 98017468Seric queuename(e, type) 98117468Seric register ENVELOPE *e; 98217468Seric char type; 98317468Seric { 98417468Seric static char buf[MAXNAME]; 98517468Seric static int pid = -1; 98617468Seric char c1 = 'A'; 98717468Seric char c2 = 'A'; 98817468Seric 98917468Seric if (e->e_id == NULL) 99017468Seric { 99117468Seric char qf[20]; 99217468Seric 99317468Seric /* find a unique id */ 99417468Seric if (pid != getpid()) 99517468Seric { 99617468Seric /* new process -- start back at "AA" */ 99717468Seric pid = getpid(); 99817468Seric c1 = 'A'; 99917468Seric c2 = 'A' - 1; 100017468Seric } 100117468Seric (void) sprintf(qf, "qfAA%05d", pid); 100217468Seric 100317468Seric while (c1 < '~' || c2 < 'Z') 100417468Seric { 100517468Seric int i; 100617468Seric 100717468Seric if (c2 >= 'Z') 100817468Seric { 100917468Seric c1++; 101017468Seric c2 = 'A' - 1; 101117468Seric } 101240934Srick qf[2] = c1; 101340934Srick qf[3] = ++c2; 101417468Seric if (tTd(7, 20)) 101540934Srick printf("queuename: trying \"%s\"\n", qf); 101617468Seric 101740934Srick i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); 101840934Srick if (i < 0) { 101940934Srick if (errno != EEXIST) { 102036233Skarels syserr("queuename: Cannot create \"%s\" in \"%s\"", 102140934Srick qf, QueueDir); 102236233Skarels exit(EX_UNAVAILABLE); 102336233Skarels } 102440934Srick } else { 102540934Srick (void) close(i); 102640934Srick break; 102717468Seric } 102817468Seric } 102917468Seric if (c1 >= '~' && c2 >= 'Z') 103017468Seric { 103117468Seric syserr("queuename: Cannot create \"%s\" in \"%s\"", 103217468Seric qf, QueueDir); 103317468Seric exit(EX_OSERR); 103417468Seric } 103517468Seric e->e_id = newstr(&qf[2]); 103617468Seric define('i', e->e_id, e); 103717468Seric if (tTd(7, 1)) 103817468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 103917468Seric # ifdef LOG 104017468Seric if (LogLevel > 16) 104117468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 104217468Seric # endif LOG 104317468Seric } 104417468Seric 104517468Seric if (type == '\0') 104617468Seric return (NULL); 104717468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 104817468Seric if (tTd(7, 2)) 104917468Seric printf("queuename: %s\n", buf); 105017468Seric return (buf); 105117468Seric } 105217468Seric /* 105317468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 105417468Seric ** 105517468Seric ** Parameters: 105617468Seric ** e -- the envelope to unlock. 105717468Seric ** 105817468Seric ** Returns: 105917468Seric ** none 106017468Seric ** 106117468Seric ** Side Effects: 106217468Seric ** unlocks the queue for `e'. 106317468Seric */ 106417468Seric 106517468Seric unlockqueue(e) 106617468Seric ENVELOPE *e; 106717468Seric { 106817468Seric /* remove the transcript */ 106917468Seric # ifdef LOG 107017468Seric if (LogLevel > 19) 107117468Seric syslog(LOG_DEBUG, "%s: unlock", e->e_id); 107217468Seric # endif LOG 107317468Seric if (!tTd(51, 4)) 107417468Seric xunlink(queuename(e, 'x')); 107517468Seric 107617468Seric } 107740973Sbostic /* 107840973Sbostic ** GETCTLUSER -- return controlling user if mailing to prog or file 107940973Sbostic ** 108040973Sbostic ** Check for a "|" or "/" at the beginning of the address. If 108140973Sbostic ** found, return a controlling username. 108240973Sbostic ** 108340973Sbostic ** Parameters: 108440973Sbostic ** a - the address to check out 108540973Sbostic ** 108640973Sbostic ** Returns: 108740973Sbostic ** Either NULL, if we werent mailing to a program or file, 108840973Sbostic ** or a controlling user name (possibly in getpwuid's 108940973Sbostic ** static buffer). 109040973Sbostic ** 109140973Sbostic ** Side Effects: 109240973Sbostic ** none. 109340973Sbostic */ 109440973Sbostic 109540973Sbostic char * 109640973Sbostic getctluser(a) 109740973Sbostic ADDRESS *a; 109840973Sbostic { 109940973Sbostic extern ADDRESS *getctladdr(); 110040973Sbostic struct passwd *pw; 110140973Sbostic char *retstr; 110240973Sbostic 110340973Sbostic /* 110440973Sbostic ** Get unquoted user for file, program or user.name check. 110540973Sbostic ** N.B. remove this code block to always emit controlling 110640973Sbostic ** addresses (at the expense of backward compatibility). 110740973Sbostic */ 110840973Sbostic 110940973Sbostic { 111040973Sbostic char buf[MAXNAME]; 111140973Sbostic (void) strncpy(buf, a->q_paddr, MAXNAME); 111240973Sbostic buf[MAXNAME-1] = '\0'; 111340973Sbostic stripquotes(buf, TRUE); 111440973Sbostic 111540973Sbostic if (buf[0] != '|' && buf[0] != '/') 111640973Sbostic return((char *)NULL); 111740973Sbostic } 111840973Sbostic 111940973Sbostic a = getctladdr(a); /* find controlling address */ 112040973Sbostic 112140973Sbostic if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL) 112240973Sbostic retstr = pw->pw_name; 112340973Sbostic else /* use default user */ 112440973Sbostic retstr = DefUser; 112540973Sbostic 112640973Sbostic if (tTd(40, 5)) 112740973Sbostic printf("Set controlling user for `%s' to `%s'\n", 112840973Sbostic (a == NULL)? "<null>": a->q_paddr, retstr); 112940973Sbostic 113040973Sbostic return(retstr); 113140973Sbostic } 113240973Sbostic /* 113340973Sbostic ** SETCTLUSER - sets `CtlUser' to controlling user 113440973Sbostic ** CLRCTLUSER - clears controlling user (no params, nothing returned) 113540973Sbostic ** 113640973Sbostic ** These routines manipulate `CtlUser'. 113740973Sbostic ** 113840973Sbostic ** Parameters: 113940973Sbostic ** str - controlling user as passed to setctluser() 114040973Sbostic ** 114140973Sbostic ** Returns: 114240973Sbostic ** None. 114340973Sbostic ** 114440973Sbostic ** Side Effects: 114540973Sbostic ** `CtlUser' is changed. 114640973Sbostic */ 114740973Sbostic 114840973Sbostic static char CtlUser[MAXNAME]; 114940973Sbostic 115040973Sbostic setctluser(str) 115140973Sbostic register char *str; 115240973Sbostic { 115340973Sbostic (void) strncpy(CtlUser, str, MAXNAME); 115440973Sbostic CtlUser[MAXNAME-1] = '\0'; 115540973Sbostic } 115640973Sbostic 115740973Sbostic clrctluser() 115840973Sbostic { 115940973Sbostic CtlUser[0] = '\0'; 116040973Sbostic } 116140973Sbostic 116240973Sbostic /* 116340973Sbostic ** SETCTLADDR -- create a controlling address 116440973Sbostic ** 116540973Sbostic ** If global variable `CtlUser' is set and we are given a valid 116640973Sbostic ** address, make that address a controlling address; change the 116740973Sbostic ** `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID. 116840973Sbostic ** 116940973Sbostic ** Parameters: 117040973Sbostic ** a - address for which control uid/gid info may apply 117140973Sbostic ** 117240973Sbostic ** Returns: 117340973Sbostic ** None. 117440973Sbostic ** 117540973Sbostic ** Side Effects: 117640973Sbostic ** Fills in uid/gid fields in address and sets QGOODUID 117740973Sbostic ** flag if appropriate. 117840973Sbostic */ 117940973Sbostic 118040973Sbostic setctladdr(a) 118140973Sbostic ADDRESS *a; 118240973Sbostic { 118340973Sbostic struct passwd *pw; 118440973Sbostic 118540973Sbostic /* 118640973Sbostic ** If there is no current controlling user, or we were passed a 118740973Sbostic ** NULL addr ptr or we already have a controlling user, return. 118840973Sbostic */ 118940973Sbostic 119040973Sbostic if (CtlUser[0] == '\0' || a == NULL || a->q_ruser) 119140973Sbostic return; 119240973Sbostic 119340973Sbostic /* 119440973Sbostic ** Set up addr fields for controlling user. If `CtlUser' is no 119540973Sbostic ** longer valid, use the default user/group. 119640973Sbostic */ 119740973Sbostic 119840973Sbostic if ((pw = getpwnam(CtlUser)) != NULL) 119940973Sbostic { 120040973Sbostic if (a->q_home) 120140973Sbostic free(a->q_home); 120240973Sbostic a->q_home = newstr(pw->pw_dir); 120340973Sbostic a->q_uid = pw->pw_uid; 120440973Sbostic a->q_gid = pw->pw_gid; 120540973Sbostic a->q_ruser = newstr(CtlUser); 120640973Sbostic } 120740973Sbostic else 120840973Sbostic { 120940973Sbostic a->q_uid = DefUid; 121040973Sbostic a->q_gid = DefGid; 121140973Sbostic a->q_ruser = newstr(DefUser); 121240973Sbostic } 121340973Sbostic 121440973Sbostic a->q_flags |= QGOODUID; /* flag as a "ctladdr" */ 121540973Sbostic 121640973Sbostic if (tTd(40, 5)) 121740973Sbostic printf("Restored controlling user for `%s' to `%s'\n", 121840973Sbostic a->q_paddr, a->q_ruser); 121940973Sbostic } 1220