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*40934Srick static char sccsid[] = "@(#)queue.c 5.27 (Berkeley) 04/16/90 (with queueing)"; 2433731Sbostic #else 25*40934Srick static char sccsid[] = "@(#)queue.c 5.27 (Berkeley) 04/16/90 (without queueing)"; 2633731Sbostic #endif 2733731Sbostic #endif /* not lint */ 2833731Sbostic 294632Seric # include <sys/stat.h> 3013707Ssam # include <sys/dir.h> 31*40934Srick # include <sys/file.h> 324634Seric # include <signal.h> 334632Seric # include <errno.h> 344632Seric 3533731Sbostic # ifdef QUEUE 364632Seric 374632Seric /* 389377Seric ** Work queue. 399377Seric */ 409377Seric 419377Seric struct work 429377Seric { 439377Seric char *w_name; /* name of control file */ 449377Seric long w_pri; /* priority of message, see below */ 4525013Seric time_t w_ctime; /* creation time of message */ 469377Seric struct work *w_next; /* next in queue */ 479377Seric }; 489377Seric 499377Seric typedef struct work WORK; 50*40934Srick extern int la; 519377Seric 529377Seric WORK *WorkQ; /* queue of things to be done */ 539377Seric /* 544632Seric ** QUEUEUP -- queue a message up for future transmission. 554632Seric ** 564632Seric ** Parameters: 576980Seric ** e -- the envelope to queue up. 586999Seric ** queueall -- if TRUE, queue all addresses, rather than 596999Seric ** just those with the QQUEUEUP flag set. 609377Seric ** announce -- if TRUE, tell when you are queueing up. 614632Seric ** 624632Seric ** Returns: 63*40934Srick ** locked FILE* to q file 644632Seric ** 654632Seric ** Side Effects: 669377Seric ** The current request are saved in a control file. 674632Seric */ 684632Seric 69*40934Srick FILE * 709377Seric queueup(e, queueall, announce) 716980Seric register ENVELOPE *e; 726999Seric bool queueall; 739377Seric bool announce; 744632Seric { 757812Seric char *tf; 767812Seric char *qf; 777763Seric char buf[MAXLINE]; 787812Seric register FILE *tfp; 794632Seric register HDR *h; 805007Seric register ADDRESS *q; 8110173Seric MAILER nullmailer; 82*40934Srick int fd; 834632Seric 845037Seric /* 8517477Seric ** Create control file. 865037Seric */ 874632Seric 8817477Seric tf = newstr(queuename(e, 't')); 89*40934Srick fd = open(tf, O_CREAT|O_WRONLY, FileMode); 90*40934Srick if (fd < 0) 914632Seric { 9217477Seric syserr("queueup: cannot create temp file %s", tf); 93*40934Srick return NULL; 944632Seric } 95*40934Srick tfp = fdopen(fd, "w"); 964632Seric 977677Seric if (tTd(40, 1)) 9817468Seric printf("queueing %s\n", e->e_id); 994632Seric 1004632Seric /* 1016980Seric ** If there is no data file yet, create one. 1026980Seric */ 1036980Seric 1046980Seric if (e->e_df == NULL) 1056980Seric { 1066980Seric register FILE *dfp; 1079389Seric extern putbody(); 1086980Seric 1097812Seric e->e_df = newstr(queuename(e, 'd')); 110*40934Srick fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode); 111*40934Srick if (fd < 0) 1126980Seric { 1136980Seric syserr("queueup: cannot create %s", e->e_df); 1147812Seric (void) fclose(tfp); 115*40934Srick return NULL; 1166980Seric } 117*40934Srick dfp = fdopen(fd, "w"); 11810173Seric (*e->e_putbody)(dfp, ProgMailer, e); 1197009Seric (void) fclose(dfp); 1209389Seric e->e_putbody = putbody; 1216980Seric } 1226980Seric 1236980Seric /* 1244632Seric ** Output future work requests. 12525687Seric ** Priority and creation time should be first, since 12625687Seric ** they are required by orderq. 1274632Seric */ 1284632Seric 1299377Seric /* output message priority */ 1309377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 1319377Seric 1329630Seric /* output creation time */ 1339630Seric fprintf(tfp, "T%ld\n", e->e_ctime); 1349630Seric 1354632Seric /* output name of data file */ 1367812Seric fprintf(tfp, "D%s\n", e->e_df); 1374632Seric 13810108Seric /* message from envelope, if it exists */ 13910108Seric if (e->e_message != NULL) 14010108Seric fprintf(tfp, "M%s\n", e->e_message); 14110108Seric 1424632Seric /* output name of sender */ 1437812Seric fprintf(tfp, "S%s\n", e->e_from.q_paddr); 1444632Seric 1454632Seric /* output list of recipient addresses */ 1466980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 1474632Seric { 1487763Seric if (queueall ? !bitset(QDONTSEND, q->q_flags) : 1497763Seric bitset(QQUEUEUP, q->q_flags)) 1508245Seric { 1517812Seric fprintf(tfp, "R%s\n", q->q_paddr); 1529377Seric if (announce) 1539377Seric { 1549377Seric e->e_to = q->q_paddr; 1559377Seric message(Arpa_Info, "queued"); 1569377Seric if (LogLevel > 4) 1579377Seric logdelivery("queued"); 1589377Seric e->e_to = NULL; 1599377Seric } 1609387Seric if (tTd(40, 1)) 1619387Seric { 1629387Seric printf("queueing "); 1639387Seric printaddr(q, FALSE); 1649387Seric } 1658245Seric } 1664632Seric } 1674632Seric 16825687Seric /* output list of error recipients */ 16925687Seric for (q = e->e_errorqueue; q != NULL; q = q->q_next) 17025687Seric { 17126504Seric if (!bitset(QDONTSEND, q->q_flags)) 17226504Seric fprintf(tfp, "E%s\n", q->q_paddr); 17325687Seric } 17425687Seric 1759377Seric /* 1769377Seric ** Output headers for this message. 1779377Seric ** Expand macros completely here. Queue run will deal with 1789377Seric ** everything as absolute headers. 1799377Seric ** All headers that must be relative to the recipient 1809377Seric ** can be cracked later. 18110173Seric ** We set up a "null mailer" -- i.e., a mailer that will have 18210173Seric ** no effect on the addresses as they are output. 1839377Seric */ 1849377Seric 18510686Seric bzero((char *) &nullmailer, sizeof nullmailer); 18610173Seric nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; 18710349Seric nullmailer.m_eol = "\n"; 18810173Seric 18916147Seric define('g', "\001f", e); 1906980Seric for (h = e->e_header; h != NULL; h = h->h_link) 1914632Seric { 19210686Seric extern bool bitzerop(); 19310686Seric 19412015Seric /* don't output null headers */ 1954632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 1964632Seric continue; 19712015Seric 19812015Seric /* don't output resent headers on non-resent messages */ 19912015Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 20012015Seric continue; 20112015Seric 20212015Seric /* output this header */ 2037812Seric fprintf(tfp, "H"); 20412015Seric 20512015Seric /* if conditional, output the set of conditions */ 20610686Seric if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 20710686Seric { 20810686Seric int j; 20910686Seric 21023098Seric (void) putc('?', tfp); 21110686Seric for (j = '\0'; j <= '\177'; j++) 21210686Seric if (bitnset(j, h->h_mflags)) 21323098Seric (void) putc(j, tfp); 21423098Seric (void) putc('?', tfp); 21510686Seric } 21612015Seric 21712015Seric /* output the header: expand macros, convert addresses */ 2187763Seric if (bitset(H_DEFAULT, h->h_flags)) 2197763Seric { 2207763Seric (void) expand(h->h_value, buf, &buf[sizeof buf], e); 2218236Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 2227763Seric } 2238245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 2249348Seric { 2259348Seric commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 22610173Seric &nullmailer); 2279348Seric } 2287763Seric else 2298245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 2304632Seric } 2314632Seric 2324632Seric /* 2334632Seric ** Clean up. 2344632Seric */ 2354632Seric 236*40934Srick if (flock(fileno(tfp), LOCK_EX|LOCK_NB) < 0) 237*40934Srick { 238*40934Srick syserr("cannot flock(%s)", tf); 239*40934Srick } 240*40934Srick 24117468Seric qf = queuename(e, 'q'); 24217468Seric if (tf != NULL) 24317468Seric { 24417468Seric (void) unlink(qf); 24524968Seric if (rename(tf, qf) < 0) 246*40934Srick syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df); 24724968Seric errno = 0; 24817468Seric } 2497391Seric 2507677Seric # ifdef LOG 2517677Seric /* save log info */ 2527878Seric if (LogLevel > 15) 2537878Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 2547677Seric # endif LOG 255*40934Srick fflush(tfp); 256*40934Srick return tfp; 2574632Seric } 2584632Seric /* 2594632Seric ** RUNQUEUE -- run the jobs in the queue. 2604632Seric ** 2614632Seric ** Gets the stuff out of the queue in some presumably logical 2624632Seric ** order and processes them. 2634632Seric ** 2644632Seric ** Parameters: 26524941Seric ** forkflag -- TRUE if the queue scanning should be done in 26624941Seric ** a child process. We double-fork so it is not our 26724941Seric ** child and we don't have to clean up after it. 2684632Seric ** 2694632Seric ** Returns: 2704632Seric ** none. 2714632Seric ** 2724632Seric ** Side Effects: 2734632Seric ** runs things in the mail queue. 2744632Seric */ 2754632Seric 2764639Seric runqueue(forkflag) 2774639Seric bool forkflag; 2784632Seric { 27924953Seric extern bool shouldqueue(); 28024953Seric 2817466Seric /* 28224953Seric ** If no work will ever be selected, don't even bother reading 28324953Seric ** the queue. 28424953Seric */ 28524953Seric 286*40934Srick la = getla(); /* get load average */ 287*40934Srick 28824953Seric if (shouldqueue(-100000000L)) 28924953Seric { 29024953Seric if (Verbose) 29124953Seric printf("Skipping queue run -- load average too high\n"); 29224953Seric 29324953Seric if (forkflag) 29424953Seric return; 29524953Seric finis(); 29624953Seric } 29724953Seric 29824953Seric /* 2997466Seric ** See if we want to go off and do other useful work. 3007466Seric */ 3014639Seric 3024639Seric if (forkflag) 3034639Seric { 3047943Seric int pid; 3057943Seric 3067943Seric pid = dofork(); 3077943Seric if (pid != 0) 3084639Seric { 30925184Seric extern reapchild(); 31025184Seric 3117943Seric /* parent -- pick up intermediate zombie */ 31225184Seric #ifndef SIGCHLD 3139377Seric (void) waitfor(pid); 31425184Seric #else SIGCHLD 31525184Seric (void) signal(SIGCHLD, reapchild); 31625184Seric #endif SIGCHLD 3177690Seric if (QueueIntvl != 0) 3189348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 3194639Seric return; 3204639Seric } 3217943Seric /* child -- double fork */ 32225184Seric #ifndef SIGCHLD 3237943Seric if (fork() != 0) 3247943Seric exit(EX_OK); 32525184Seric #else SIGCHLD 32625184Seric (void) signal(SIGCHLD, SIG_DFL); 32725184Seric #endif SIGCHLD 3284639Seric } 32924941Seric 330*40934Srick setproctitle("running queue: %s", QueueDir); 33124941Seric 3327876Seric # ifdef LOG 3337876Seric if (LogLevel > 11) 3347943Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 3357876Seric # endif LOG 3364639Seric 3377466Seric /* 33810205Seric ** Release any resources used by the daemon code. 33910205Seric */ 34010205Seric 34110205Seric # ifdef DAEMON 34210205Seric clrdaemon(); 34310205Seric # endif DAEMON 34410205Seric 34510205Seric /* 34627175Seric ** Make sure the alias database is open. 34727175Seric */ 34827175Seric 34927175Seric initaliases(AliasFile, FALSE); 35027175Seric 35127175Seric /* 3527466Seric ** Start making passes through the queue. 3537466Seric ** First, read and sort the entire queue. 3547466Seric ** Then, process the work in that order. 3557466Seric ** But if you take too long, start over. 3567466Seric */ 3577466Seric 3587943Seric /* order the existing work requests */ 35924954Seric (void) orderq(FALSE); 3607690Seric 3617943Seric /* process them once at a time */ 3627943Seric while (WorkQ != NULL) 3634639Seric { 3647943Seric WORK *w = WorkQ; 3657881Seric 3667943Seric WorkQ = WorkQ->w_next; 3677943Seric dowork(w); 3687943Seric free(w->w_name); 3697943Seric free((char *) w); 3704639Seric } 37129866Seric 37229866Seric /* exit without the usual cleanup */ 37329866Seric exit(ExitStat); 3744634Seric } 3754634Seric /* 3764632Seric ** ORDERQ -- order the work queue. 3774632Seric ** 3784632Seric ** Parameters: 37924941Seric ** doall -- if set, include everything in the queue (even 38024941Seric ** the jobs that cannot be run because the load 38124941Seric ** average is too high). Otherwise, exclude those 38224941Seric ** jobs. 3834632Seric ** 3844632Seric ** Returns: 38510121Seric ** The number of request in the queue (not necessarily 38610121Seric ** the number of requests in WorkQ however). 3874632Seric ** 3884632Seric ** Side Effects: 3894632Seric ** Sets WorkQ to the queue of available work, in order. 3904632Seric */ 3914632Seric 39225687Seric # define NEED_P 001 39325687Seric # define NEED_T 002 3944632Seric 39524941Seric orderq(doall) 39624941Seric bool doall; 3974632Seric { 3986625Sglickman register struct direct *d; 3994632Seric register WORK *w; 4006625Sglickman DIR *f; 4014632Seric register int i; 40225687Seric WORK wlist[QUEUESIZE+1]; 40310070Seric int wn = -1; 4044632Seric extern workcmpf(); 4054632Seric 4064632Seric /* clear out old WorkQ */ 4074632Seric for (w = WorkQ; w != NULL; ) 4084632Seric { 4094632Seric register WORK *nw = w->w_next; 4104632Seric 4114632Seric WorkQ = nw; 4124632Seric free(w->w_name); 4134632Seric free((char *) w); 4144632Seric w = nw; 4154632Seric } 4164632Seric 4174632Seric /* open the queue directory */ 4188148Seric f = opendir("."); 4194632Seric if (f == NULL) 4204632Seric { 4218148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 42210070Seric return (0); 4234632Seric } 4244632Seric 4254632Seric /* 4264632Seric ** Read the work directory. 4274632Seric */ 4284632Seric 42910070Seric while ((d = readdir(f)) != NULL) 4304632Seric { 4319377Seric FILE *cf; 4324632Seric char lbuf[MAXNAME]; 4334632Seric 4344632Seric /* is this an interesting entry? */ 4357812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 4364632Seric continue; 4374632Seric 43810070Seric /* yes -- open control file (if not too many files) */ 43925687Seric if (++wn >= QUEUESIZE) 44010070Seric continue; 4418148Seric cf = fopen(d->d_name, "r"); 4424632Seric if (cf == NULL) 4434632Seric { 4447055Seric /* this may be some random person sending hir msgs */ 4457055Seric /* syserr("orderq: cannot open %s", cbuf); */ 44610090Seric if (tTd(41, 2)) 44710090Seric printf("orderq: cannot open %s (%d)\n", 44810090Seric d->d_name, errno); 4497055Seric errno = 0; 45010090Seric wn--; 4514632Seric continue; 4524632Seric } 45325687Seric w = &wlist[wn]; 45425687Seric w->w_name = newstr(d->d_name); 4554632Seric 45625027Seric /* make sure jobs in creation don't clog queue */ 45725687Seric w->w_pri = 0x7fffffff; 45825687Seric w->w_ctime = 0; 45925027Seric 4604632Seric /* extract useful information */ 46125687Seric i = NEED_P | NEED_T; 46225687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 4634632Seric { 46424954Seric extern long atol(); 46524954Seric 46624941Seric switch (lbuf[0]) 4674632Seric { 46824941Seric case 'P': 46925687Seric w->w_pri = atol(&lbuf[1]); 47025687Seric i &= ~NEED_P; 4714632Seric break; 47225013Seric 47325013Seric case 'T': 47425687Seric w->w_ctime = atol(&lbuf[1]); 47525687Seric i &= ~NEED_T; 47625013Seric break; 4774632Seric } 4784632Seric } 4794632Seric (void) fclose(cf); 48024953Seric 48125687Seric if (!doall && shouldqueue(w->w_pri)) 48224953Seric { 48324953Seric /* don't even bother sorting this job in */ 48424953Seric wn--; 48524953Seric } 4864632Seric } 4876625Sglickman (void) closedir(f); 48810090Seric wn++; 4894632Seric 4904632Seric /* 4914632Seric ** Sort the work directory. 4924632Seric */ 4934632Seric 49425687Seric qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); 4954632Seric 4964632Seric /* 4974632Seric ** Convert the work list into canonical form. 4989377Seric ** Should be turning it into a list of envelopes here perhaps. 4994632Seric */ 5004632Seric 50124981Seric WorkQ = NULL; 50225687Seric for (i = min(wn, QUEUESIZE); --i >= 0; ) 5034632Seric { 5044632Seric w = (WORK *) xalloc(sizeof *w); 5054632Seric w->w_name = wlist[i].w_name; 5064632Seric w->w_pri = wlist[i].w_pri; 50725013Seric w->w_ctime = wlist[i].w_ctime; 50824981Seric w->w_next = WorkQ; 50924981Seric WorkQ = w; 5104632Seric } 5114632Seric 5127677Seric if (tTd(40, 1)) 5134632Seric { 5144632Seric for (w = WorkQ; w != NULL; w = w->w_next) 5155037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 5164632Seric } 51710070Seric 51810090Seric return (wn); 5194632Seric } 5204632Seric /* 5217677Seric ** WORKCMPF -- compare function for ordering work. 5224632Seric ** 5234632Seric ** Parameters: 5244632Seric ** a -- the first argument. 5254632Seric ** b -- the second argument. 5264632Seric ** 5274632Seric ** Returns: 52824981Seric ** -1 if a < b 52924981Seric ** 0 if a == b 53024981Seric ** +1 if a > b 5314632Seric ** 5324632Seric ** Side Effects: 5334632Seric ** none. 5344632Seric */ 5354632Seric 5364632Seric workcmpf(a, b) 5375037Seric register WORK *a; 5385037Seric register WORK *b; 5394632Seric { 54025013Seric long pa = a->w_pri + a->w_ctime; 54125013Seric long pb = b->w_pri + b->w_ctime; 54224941Seric 54324941Seric if (pa == pb) 5444632Seric return (0); 54524941Seric else if (pa > pb) 54624981Seric return (1); 54724981Seric else 54810121Seric return (-1); 5494632Seric } 5504632Seric /* 5514632Seric ** DOWORK -- do a work request. 5524632Seric ** 5534632Seric ** Parameters: 5544632Seric ** w -- the work request to be satisfied. 5554632Seric ** 5564632Seric ** Returns: 5574632Seric ** none. 5584632Seric ** 5594632Seric ** Side Effects: 5604632Seric ** The work request is satisfied if possible. 5614632Seric */ 5624632Seric 5634632Seric dowork(w) 5644632Seric register WORK *w; 5654632Seric { 5664632Seric register int i; 56724941Seric extern bool shouldqueue(); 5684632Seric 5697677Seric if (tTd(40, 1)) 5705037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 5714632Seric 5724632Seric /* 57324941Seric ** Ignore jobs that are too expensive for the moment. 5744632Seric */ 5754632Seric 57624941Seric if (shouldqueue(w->w_pri)) 5774632Seric { 57824941Seric if (Verbose) 57924967Seric printf("\nSkipping %s\n", w->w_name + 2); 5804632Seric return; 5814632Seric } 5824632Seric 58324941Seric /* 58424941Seric ** Fork for work. 58524941Seric */ 58624941Seric 58724941Seric if (ForkQueueRuns) 58824941Seric { 58924941Seric i = fork(); 59024941Seric if (i < 0) 59124941Seric { 59224941Seric syserr("dowork: cannot fork"); 59324941Seric return; 59424941Seric } 59524941Seric } 59624941Seric else 59724941Seric { 59824941Seric i = 0; 59924941Seric } 60024941Seric 6014632Seric if (i == 0) 6024632Seric { 603*40934Srick FILE *qflock, *readqf(); 6044632Seric /* 6054632Seric ** CHILD 6068148Seric ** Lock the control file to avoid duplicate deliveries. 6078148Seric ** Then run the file as though we had just read it. 6087350Seric ** We save an idea of the temporary name so we 6097350Seric ** can recover on interrupt. 6104632Seric */ 6114632Seric 6127763Seric /* set basic modes, etc. */ 6137356Seric (void) alarm(0); 61425612Seric clearenvelope(CurEnv, FALSE); 6154632Seric QueueRun = TRUE; 6169377Seric ErrorMode = EM_MAIL; 6178148Seric CurEnv->e_id = &w->w_name[2]; 6187876Seric # ifdef LOG 6197876Seric if (LogLevel > 11) 6207881Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 6217881Seric getpid()); 6227876Seric # endif LOG 6237763Seric 6247763Seric /* don't use the headers from sendmail.cf... */ 6257763Seric CurEnv->e_header = NULL; 6267763Seric 627*40934Srick /* read the queue control file */ 628*40934Srick /* and lock the control file during processing */ 629*40934Srick if ((qflock=readqf(CurEnv, TRUE)) == NULL) 6306980Seric { 63124941Seric if (ForkQueueRuns) 63224941Seric exit(EX_OK); 63324941Seric else 63424941Seric return; 6356980Seric } 6366980Seric 6379338Seric CurEnv->e_flags |= EF_INQUEUE; 6389377Seric eatheader(CurEnv); 6396980Seric 6406980Seric /* do the delivery */ 6419338Seric if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 6429282Seric sendall(CurEnv, SM_DELIVER); 6436980Seric 644*40934Srick fclose(qflock); 6456980Seric /* finish up and exit */ 64624941Seric if (ForkQueueRuns) 64724941Seric finis(); 64824941Seric else 64924941Seric dropenvelope(CurEnv); 6504632Seric } 65124941Seric else 65224941Seric { 65324941Seric /* 65424941Seric ** Parent -- pick up results. 65524941Seric */ 6564632Seric 65724941Seric errno = 0; 65824941Seric (void) waitfor(i); 65924941Seric } 6604632Seric } 6614632Seric /* 6624632Seric ** READQF -- read queue file and set up environment. 6634632Seric ** 6644632Seric ** Parameters: 6659377Seric ** e -- the envelope of the job to run. 6669630Seric ** full -- if set, read in all information. Otherwise just 6679630Seric ** read in info needed for a queue print. 6684632Seric ** 6694632Seric ** Returns: 670*40934Srick ** FILE * pointing to flock()ed fd so it can be closed 671*40934Srick ** after the mail is delivered 6724632Seric ** 6734632Seric ** Side Effects: 6744632Seric ** cf is read and created as the current job, as though 6754632Seric ** we had been invoked by argument. 6764632Seric */ 6774632Seric 678*40934Srick FILE * 67917477Seric readqf(e, full) 6809377Seric register ENVELOPE *e; 6819630Seric bool full; 6824632Seric { 68317477Seric char *qf; 68417477Seric register FILE *qfp; 6857785Seric char buf[MAXFIELD]; 6869348Seric extern char *fgetfolded(); 68724954Seric extern long atol(); 688*40934Srick int fd; 6894632Seric 6904632Seric /* 69117468Seric ** Read and process the file. 6924632Seric */ 6934632Seric 69417477Seric qf = queuename(e, 'q'); 69517477Seric qfp = fopen(qf, "r"); 69617477Seric if (qfp == NULL) 69717477Seric { 698*40934Srick if (errno != ENOENT) 699*40934Srick syserr("readqf: no control file %s", qf); 700*40934Srick return NULL; 70117477Seric } 702*40934Srick 703*40934Srick if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0) 704*40934Srick { 705*40934Srick # ifdef LOG 706*40934Srick /* being processed by another queuer */ 707*40934Srick if (Verbose) 708*40934Srick printf("%s: locked", CurEnv->e_id); 709*40934Srick # endif LOG 710*40934Srick (void) fclose(qfp); 711*40934Srick return NULL; 712*40934Srick } 713*40934Srick 714*40934Srick /* do basic system initialization */ 715*40934Srick initsys(); 716*40934Srick 71717477Seric FileName = qf; 7189377Seric LineNumber = 0; 7199630Seric if (Verbose && full) 7209377Seric printf("\nRunning %s\n", e->e_id); 72117468Seric while (fgetfolded(buf, sizeof buf, qfp) != NULL) 7224632Seric { 72326504Seric if (tTd(40, 4)) 72426504Seric printf("+++++ %s\n", buf); 7254632Seric switch (buf[0]) 7264632Seric { 7274632Seric case 'R': /* specify recipient */ 7289618Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 7294632Seric break; 7304632Seric 73125687Seric case 'E': /* specify error recipient */ 73225687Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue); 73325687Seric break; 73425687Seric 7354632Seric case 'H': /* header */ 7369630Seric if (full) 7379630Seric (void) chompheader(&buf[1], FALSE); 7384632Seric break; 7394632Seric 74010108Seric case 'M': /* message */ 74110108Seric e->e_message = newstr(&buf[1]); 74210108Seric break; 74310108Seric 7444632Seric case 'S': /* sender */ 7454634Seric setsender(newstr(&buf[1])); 7464632Seric break; 7474632Seric 7484632Seric case 'D': /* data file name */ 7499630Seric if (!full) 7509630Seric break; 7519377Seric e->e_df = newstr(&buf[1]); 7529544Seric e->e_dfp = fopen(e->e_df, "r"); 7539544Seric if (e->e_dfp == NULL) 7549377Seric syserr("readqf: cannot open %s", e->e_df); 7554632Seric break; 7564632Seric 7577860Seric case 'T': /* init time */ 75824941Seric e->e_ctime = atol(&buf[1]); 7594632Seric break; 7604632Seric 7614634Seric case 'P': /* message priority */ 76225008Seric e->e_msgpriority = atol(&buf[1]) + WkTimeFact; 7634634Seric break; 7644634Seric 76524941Seric case '\0': /* blank line; ignore */ 76624941Seric break; 76724941Seric 7684632Seric default: 76924941Seric syserr("readqf(%s:%d): bad line \"%s\"", e->e_id, 77024941Seric LineNumber, buf); 7714632Seric break; 7724632Seric } 7734632Seric } 7749377Seric 7759377Seric FileName = NULL; 77624941Seric 77724941Seric /* 77824941Seric ** If we haven't read any lines, this queue file is empty. 77924941Seric ** Arrange to remove it without referencing any null pointers. 78024941Seric */ 78124941Seric 78224941Seric if (LineNumber == 0) 78324941Seric { 78424941Seric errno = 0; 78524941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 78624941Seric } 787*40934Srick return qfp; 7884632Seric } 7894632Seric /* 7909630Seric ** PRINTQUEUE -- print out a representation of the mail queue 7919630Seric ** 7929630Seric ** Parameters: 7939630Seric ** none. 7949630Seric ** 7959630Seric ** Returns: 7969630Seric ** none. 7979630Seric ** 7989630Seric ** Side Effects: 7999630Seric ** Prints a listing of the mail queue on the standard output. 8009630Seric */ 8015182Seric 8029630Seric printqueue() 8039630Seric { 8049630Seric register WORK *w; 8059630Seric FILE *f; 80610070Seric int nrequests; 8079630Seric char buf[MAXLINE]; 8089630Seric 8099630Seric /* 8109630Seric ** Read and order the queue. 8119630Seric */ 8129630Seric 81324941Seric nrequests = orderq(TRUE); 8149630Seric 8159630Seric /* 8169630Seric ** Print the work list that we have read. 8179630Seric */ 8189630Seric 8199630Seric /* first see if there is anything */ 82010070Seric if (nrequests <= 0) 8219630Seric { 82210070Seric printf("Mail queue is empty\n"); 8239630Seric return; 8249630Seric } 8259630Seric 826*40934Srick la = getla(); /* get load average */ 827*40934Srick 82810096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 82925687Seric if (nrequests > QUEUESIZE) 83025687Seric printf(", only %d printed", QUEUESIZE); 83124979Seric if (Verbose) 83225032Seric printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 83324979Seric else 83424979Seric printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 8359630Seric for (w = WorkQ; w != NULL; w = w->w_next) 8369630Seric { 8379630Seric struct stat st; 83810070Seric auto time_t submittime = 0; 83910070Seric long dfsize = -1; 84010108Seric char message[MAXLINE]; 84124941Seric extern bool shouldqueue(); 8429630Seric 84317468Seric f = fopen(w->w_name, "r"); 84417468Seric if (f == NULL) 84517468Seric { 84617468Seric errno = 0; 84717468Seric continue; 84817468Seric } 8499630Seric printf("%7s", w->w_name + 2); 850*40934Srick if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0) 85110070Seric printf("*"); 85224941Seric else if (shouldqueue(w->w_pri)) 85324941Seric printf("X"); 85410070Seric else 85510070Seric printf(" "); 85610070Seric errno = 0; 85717468Seric 85810108Seric message[0] = '\0'; 8599630Seric while (fgets(buf, sizeof buf, f) != NULL) 8609630Seric { 8619630Seric fixcrlf(buf, TRUE); 8629630Seric switch (buf[0]) 8639630Seric { 86410108Seric case 'M': /* error message */ 86523098Seric (void) strcpy(message, &buf[1]); 86610108Seric break; 86710108Seric 8689630Seric case 'S': /* sender name */ 86924979Seric if (Verbose) 87025027Seric printf("%8ld %10ld %.12s %.38s", dfsize, 87125027Seric w->w_pri, ctime(&submittime) + 4, 87224979Seric &buf[1]); 87324979Seric else 87424979Seric printf("%8ld %.16s %.45s", dfsize, 87524979Seric ctime(&submittime), &buf[1]); 87610108Seric if (message[0] != '\0') 87725027Seric printf("\n\t\t (%.60s)", message); 8789630Seric break; 8799630Seric 8809630Seric case 'R': /* recipient name */ 88124979Seric if (Verbose) 88225027Seric printf("\n\t\t\t\t\t %.38s", &buf[1]); 88324979Seric else 88424979Seric printf("\n\t\t\t\t %.45s", &buf[1]); 8859630Seric break; 8869630Seric 8879630Seric case 'T': /* creation time */ 88824941Seric submittime = atol(&buf[1]); 8899630Seric break; 89010070Seric 89110070Seric case 'D': /* data file name */ 89210070Seric if (stat(&buf[1], &st) >= 0) 89310070Seric dfsize = st.st_size; 89410070Seric break; 8959630Seric } 8969630Seric } 89710070Seric if (submittime == (time_t) 0) 89810070Seric printf(" (no control file)"); 8999630Seric printf("\n"); 90023098Seric (void) fclose(f); 9019630Seric } 9029630Seric } 9039630Seric 9045182Seric # endif QUEUE 90517468Seric /* 90617468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 90717468Seric ** 90817468Seric ** Assigns an id code if one does not already exist. 90917468Seric ** This code is very careful to avoid trashing existing files 91017468Seric ** under any circumstances. 91117468Seric ** 91217468Seric ** Parameters: 91317468Seric ** e -- envelope to build it in/from. 91417468Seric ** type -- the file type, used as the first character 91517468Seric ** of the file name. 91617468Seric ** 91717468Seric ** Returns: 91817468Seric ** a pointer to the new file name (in a static buffer). 91917468Seric ** 92017468Seric ** Side Effects: 921*40934Srick ** Will create the qf file if no id code is 92217468Seric ** already assigned. This will cause the envelope 92317468Seric ** to be modified. 92417468Seric */ 92517468Seric 92617468Seric char * 92717468Seric queuename(e, type) 92817468Seric register ENVELOPE *e; 92917468Seric char type; 93017468Seric { 93117468Seric static char buf[MAXNAME]; 93217468Seric static int pid = -1; 93317468Seric char c1 = 'A'; 93417468Seric char c2 = 'A'; 93517468Seric 93617468Seric if (e->e_id == NULL) 93717468Seric { 93817468Seric char qf[20]; 93917468Seric 94017468Seric /* find a unique id */ 94117468Seric if (pid != getpid()) 94217468Seric { 94317468Seric /* new process -- start back at "AA" */ 94417468Seric pid = getpid(); 94517468Seric c1 = 'A'; 94617468Seric c2 = 'A' - 1; 94717468Seric } 94817468Seric (void) sprintf(qf, "qfAA%05d", pid); 94917468Seric 95017468Seric while (c1 < '~' || c2 < 'Z') 95117468Seric { 95217468Seric int i; 95317468Seric 95417468Seric if (c2 >= 'Z') 95517468Seric { 95617468Seric c1++; 95717468Seric c2 = 'A' - 1; 95817468Seric } 959*40934Srick qf[2] = c1; 960*40934Srick qf[3] = ++c2; 96117468Seric if (tTd(7, 20)) 962*40934Srick printf("queuename: trying \"%s\"\n", qf); 96317468Seric 964*40934Srick i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); 965*40934Srick if (i < 0) { 966*40934Srick if (errno != EEXIST) { 96736233Skarels syserr("queuename: Cannot create \"%s\" in \"%s\"", 968*40934Srick qf, QueueDir); 96936233Skarels exit(EX_UNAVAILABLE); 97036233Skarels } 971*40934Srick } else { 972*40934Srick (void) close(i); 973*40934Srick break; 97417468Seric } 97517468Seric } 97617468Seric if (c1 >= '~' && c2 >= 'Z') 97717468Seric { 97817468Seric syserr("queuename: Cannot create \"%s\" in \"%s\"", 97917468Seric qf, QueueDir); 98017468Seric exit(EX_OSERR); 98117468Seric } 98217468Seric e->e_id = newstr(&qf[2]); 98317468Seric define('i', e->e_id, e); 98417468Seric if (tTd(7, 1)) 98517468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 98617468Seric # ifdef LOG 98717468Seric if (LogLevel > 16) 98817468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 98917468Seric # endif LOG 99017468Seric } 99117468Seric 99217468Seric if (type == '\0') 99317468Seric return (NULL); 99417468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 99517468Seric if (tTd(7, 2)) 99617468Seric printf("queuename: %s\n", buf); 99717468Seric return (buf); 99817468Seric } 99917468Seric /* 100017468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 100117468Seric ** 100217468Seric ** Parameters: 100317468Seric ** e -- the envelope to unlock. 100417468Seric ** 100517468Seric ** Returns: 100617468Seric ** none 100717468Seric ** 100817468Seric ** Side Effects: 100917468Seric ** unlocks the queue for `e'. 101017468Seric */ 101117468Seric 101217468Seric unlockqueue(e) 101317468Seric ENVELOPE *e; 101417468Seric { 101517468Seric /* remove the transcript */ 101617468Seric # ifdef LOG 101717468Seric if (LogLevel > 19) 101817468Seric syslog(LOG_DEBUG, "%s: unlock", e->e_id); 101917468Seric # endif LOG 102017468Seric if (!tTd(51, 4)) 102117468Seric xunlink(queuename(e, 'x')); 102217468Seric 102317468Seric } 1024