122708Sdist /* 222708Sdist ** Sendmail 322708Sdist ** Copyright (c) 1983 Eric P. Allman 422708Sdist ** Berkeley, California 522708Sdist ** 622708Sdist ** Copyright (c) 1983 Regents of the University of California. 722708Sdist ** All rights reserved. The Berkeley software License Agreement 822708Sdist ** specifies the terms and conditions for redistribution. 922708Sdist */ 1022708Sdist 1122708Sdist 124632Seric # include "sendmail.h" 134632Seric # include <sys/stat.h> 1413707Ssam # include <sys/dir.h> 154634Seric # include <signal.h> 164632Seric # include <errno.h> 174632Seric 185182Seric # ifndef QUEUE 1923116Seric # ifndef lint 20*27175Seric static char SccsId[] = "@(#)queue.c 5.21 (Berkeley) 04/17/86 (no queueing)"; 2123116Seric # endif not lint 225182Seric # else QUEUE 234632Seric 2423116Seric # ifndef lint 25*27175Seric static char SccsId[] = "@(#)queue.c 5.21 (Berkeley) 04/17/86"; 2623116Seric # endif not lint 275182Seric 284632Seric /* 299377Seric ** Work queue. 309377Seric */ 319377Seric 329377Seric struct work 339377Seric { 349377Seric char *w_name; /* name of control file */ 359377Seric long w_pri; /* priority of message, see below */ 3625013Seric time_t w_ctime; /* creation time of message */ 379377Seric struct work *w_next; /* next in queue */ 389377Seric }; 399377Seric 409377Seric typedef struct work WORK; 419377Seric 429377Seric WORK *WorkQ; /* queue of things to be done */ 439377Seric /* 444632Seric ** QUEUEUP -- queue a message up for future transmission. 454632Seric ** 464632Seric ** Parameters: 476980Seric ** e -- the envelope to queue up. 486999Seric ** queueall -- if TRUE, queue all addresses, rather than 496999Seric ** just those with the QQUEUEUP flag set. 509377Seric ** announce -- if TRUE, tell when you are queueing up. 514632Seric ** 524632Seric ** Returns: 534632Seric ** none. 544632Seric ** 554632Seric ** Side Effects: 569377Seric ** The current request are saved in a control file. 574632Seric */ 584632Seric 599377Seric queueup(e, queueall, announce) 606980Seric register ENVELOPE *e; 616999Seric bool queueall; 629377Seric bool announce; 634632Seric { 647812Seric char *tf; 657812Seric char *qf; 667763Seric char buf[MAXLINE]; 677812Seric register FILE *tfp; 684632Seric register HDR *h; 695007Seric register ADDRESS *q; 7010173Seric MAILER nullmailer; 714632Seric 725037Seric /* 7317477Seric ** Create control file. 745037Seric */ 754632Seric 7617477Seric tf = newstr(queuename(e, 't')); 7717477Seric tfp = fopen(tf, "w"); 787812Seric if (tfp == NULL) 794632Seric { 8017477Seric syserr("queueup: cannot create temp file %s", tf); 8117477Seric return; 824632Seric } 8317477Seric (void) chmod(tf, FileMode); 844632Seric 854632Seric # ifdef DEBUG 867677Seric if (tTd(40, 1)) 8717468Seric printf("queueing %s\n", e->e_id); 884632Seric # endif DEBUG 894632Seric 904632Seric /* 916980Seric ** If there is no data file yet, create one. 926980Seric */ 936980Seric 946980Seric if (e->e_df == NULL) 956980Seric { 966980Seric register FILE *dfp; 979389Seric extern putbody(); 986980Seric 997812Seric e->e_df = newstr(queuename(e, 'd')); 1006980Seric dfp = fopen(e->e_df, "w"); 1016980Seric if (dfp == NULL) 1026980Seric { 1036980Seric syserr("queueup: cannot create %s", e->e_df); 1047812Seric (void) fclose(tfp); 1056980Seric return; 1066980Seric } 1079048Seric (void) chmod(e->e_df, FileMode); 10810173Seric (*e->e_putbody)(dfp, ProgMailer, e); 1097009Seric (void) fclose(dfp); 1109389Seric e->e_putbody = putbody; 1116980Seric } 1126980Seric 1136980Seric /* 1144632Seric ** Output future work requests. 11525687Seric ** Priority and creation time should be first, since 11625687Seric ** they are required by orderq. 1174632Seric */ 1184632Seric 1199377Seric /* output message priority */ 1209377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 1219377Seric 1229630Seric /* output creation time */ 1239630Seric fprintf(tfp, "T%ld\n", e->e_ctime); 1249630Seric 1254632Seric /* output name of data file */ 1267812Seric fprintf(tfp, "D%s\n", e->e_df); 1274632Seric 12810108Seric /* message from envelope, if it exists */ 12910108Seric if (e->e_message != NULL) 13010108Seric fprintf(tfp, "M%s\n", e->e_message); 13110108Seric 1324632Seric /* output name of sender */ 1337812Seric fprintf(tfp, "S%s\n", e->e_from.q_paddr); 1344632Seric 1354632Seric /* output list of recipient addresses */ 1366980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 1374632Seric { 1387763Seric if (queueall ? !bitset(QDONTSEND, q->q_flags) : 1397763Seric bitset(QQUEUEUP, q->q_flags)) 1408245Seric { 1417812Seric fprintf(tfp, "R%s\n", q->q_paddr); 1429377Seric if (announce) 1439377Seric { 1449377Seric e->e_to = q->q_paddr; 1459377Seric message(Arpa_Info, "queued"); 1469377Seric if (LogLevel > 4) 1479377Seric logdelivery("queued"); 1489377Seric e->e_to = NULL; 1499377Seric } 1509387Seric #ifdef DEBUG 1519387Seric if (tTd(40, 1)) 1529387Seric { 1539387Seric printf("queueing "); 1549387Seric printaddr(q, FALSE); 1559387Seric } 1569387Seric #endif DEBUG 1578245Seric } 1584632Seric } 1594632Seric 16025687Seric /* output list of error recipients */ 16125687Seric for (q = e->e_errorqueue; q != NULL; q = q->q_next) 16225687Seric { 16326504Seric if (!bitset(QDONTSEND, q->q_flags)) 16426504Seric fprintf(tfp, "E%s\n", q->q_paddr); 16525687Seric } 16625687Seric 1679377Seric /* 1689377Seric ** Output headers for this message. 1699377Seric ** Expand macros completely here. Queue run will deal with 1709377Seric ** everything as absolute headers. 1719377Seric ** All headers that must be relative to the recipient 1729377Seric ** can be cracked later. 17310173Seric ** We set up a "null mailer" -- i.e., a mailer that will have 17410173Seric ** no effect on the addresses as they are output. 1759377Seric */ 1769377Seric 17710686Seric bzero((char *) &nullmailer, sizeof nullmailer); 17810173Seric nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1; 17910349Seric nullmailer.m_eol = "\n"; 18010173Seric 18116147Seric define('g', "\001f", e); 1826980Seric for (h = e->e_header; h != NULL; h = h->h_link) 1834632Seric { 18410686Seric extern bool bitzerop(); 18510686Seric 18612015Seric /* don't output null headers */ 1874632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 1884632Seric continue; 18912015Seric 19012015Seric /* don't output resent headers on non-resent messages */ 19112015Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 19212015Seric continue; 19312015Seric 19412015Seric /* output this header */ 1957812Seric fprintf(tfp, "H"); 19612015Seric 19712015Seric /* if conditional, output the set of conditions */ 19810686Seric if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 19910686Seric { 20010686Seric int j; 20110686Seric 20223098Seric (void) putc('?', tfp); 20310686Seric for (j = '\0'; j <= '\177'; j++) 20410686Seric if (bitnset(j, h->h_mflags)) 20523098Seric (void) putc(j, tfp); 20623098Seric (void) putc('?', tfp); 20710686Seric } 20812015Seric 20912015Seric /* output the header: expand macros, convert addresses */ 2107763Seric if (bitset(H_DEFAULT, h->h_flags)) 2117763Seric { 2127763Seric (void) expand(h->h_value, buf, &buf[sizeof buf], e); 2138236Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 2147763Seric } 2158245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 2169348Seric { 2179348Seric commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags), 21810173Seric &nullmailer); 2199348Seric } 2207763Seric else 2218245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 2224632Seric } 2234632Seric 2244632Seric /* 2254632Seric ** Clean up. 2264632Seric */ 2274632Seric 22817477Seric (void) fclose(tfp); 22917468Seric qf = queuename(e, 'q'); 23017468Seric if (tf != NULL) 23117468Seric { 23217468Seric (void) unlink(qf); 23324968Seric if (rename(tf, qf) < 0) 23424968Seric syserr("cannot unlink(%s, %s), df=%s", tf, qf, e->e_df); 23524968Seric errno = 0; 23617468Seric } 2377391Seric 2387677Seric # ifdef LOG 2397677Seric /* save log info */ 2407878Seric if (LogLevel > 15) 2417878Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df); 2427677Seric # endif LOG 2434632Seric } 2444632Seric /* 2454632Seric ** RUNQUEUE -- run the jobs in the queue. 2464632Seric ** 2474632Seric ** Gets the stuff out of the queue in some presumably logical 2484632Seric ** order and processes them. 2494632Seric ** 2504632Seric ** Parameters: 25124941Seric ** forkflag -- TRUE if the queue scanning should be done in 25224941Seric ** a child process. We double-fork so it is not our 25324941Seric ** child and we don't have to clean up after it. 2544632Seric ** 2554632Seric ** Returns: 2564632Seric ** none. 2574632Seric ** 2584632Seric ** Side Effects: 2594632Seric ** runs things in the mail queue. 2604632Seric */ 2614632Seric 2624639Seric runqueue(forkflag) 2634639Seric bool forkflag; 2644632Seric { 26524953Seric extern bool shouldqueue(); 26624953Seric 2677466Seric /* 26824953Seric ** If no work will ever be selected, don't even bother reading 26924953Seric ** the queue. 27024953Seric */ 27124953Seric 27224953Seric if (shouldqueue(-100000000L)) 27324953Seric { 27424953Seric if (Verbose) 27524953Seric printf("Skipping queue run -- load average too high\n"); 27624953Seric 27724953Seric if (forkflag) 27824953Seric return; 27924953Seric finis(); 28024953Seric } 28124953Seric 28224953Seric /* 2837466Seric ** See if we want to go off and do other useful work. 2847466Seric */ 2854639Seric 2864639Seric if (forkflag) 2874639Seric { 2887943Seric int pid; 2897943Seric 2907943Seric pid = dofork(); 2917943Seric if (pid != 0) 2924639Seric { 29325184Seric extern reapchild(); 29425184Seric 2957943Seric /* parent -- pick up intermediate zombie */ 29625184Seric #ifndef SIGCHLD 2979377Seric (void) waitfor(pid); 29825184Seric #else SIGCHLD 29925184Seric (void) signal(SIGCHLD, reapchild); 30025184Seric #endif SIGCHLD 3017690Seric if (QueueIntvl != 0) 3029348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 3034639Seric return; 3044639Seric } 3057943Seric /* child -- double fork */ 30625184Seric #ifndef SIGCHLD 3077943Seric if (fork() != 0) 3087943Seric exit(EX_OK); 30925184Seric #else SIGCHLD 31025184Seric (void) signal(SIGCHLD, SIG_DFL); 31125184Seric #endif SIGCHLD 3124639Seric } 31324941Seric 31424941Seric setproctitle("running queue"); 31524941Seric 3167876Seric # ifdef LOG 3177876Seric if (LogLevel > 11) 3187943Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid()); 3197876Seric # endif LOG 3204639Seric 3217466Seric /* 32210205Seric ** Release any resources used by the daemon code. 32310205Seric */ 32410205Seric 32510205Seric # ifdef DAEMON 32610205Seric clrdaemon(); 32710205Seric # endif DAEMON 32810205Seric 32910205Seric /* 330*27175Seric ** Make sure the alias database is open. 331*27175Seric */ 332*27175Seric 333*27175Seric initaliases(AliasFile, FALSE); 334*27175Seric 335*27175Seric /* 3367466Seric ** Start making passes through the queue. 3377466Seric ** First, read and sort the entire queue. 3387466Seric ** Then, process the work in that order. 3397466Seric ** But if you take too long, start over. 3407466Seric */ 3417466Seric 3427943Seric /* order the existing work requests */ 34324954Seric (void) orderq(FALSE); 3447690Seric 3457943Seric /* process them once at a time */ 3467943Seric while (WorkQ != NULL) 3474639Seric { 3487943Seric WORK *w = WorkQ; 3497881Seric 3507943Seric WorkQ = WorkQ->w_next; 3517943Seric dowork(w); 3527943Seric free(w->w_name); 3537943Seric free((char *) w); 3544639Seric } 3557943Seric finis(); 3564634Seric } 3574634Seric /* 3584632Seric ** ORDERQ -- order the work queue. 3594632Seric ** 3604632Seric ** Parameters: 36124941Seric ** doall -- if set, include everything in the queue (even 36224941Seric ** the jobs that cannot be run because the load 36324941Seric ** average is too high). Otherwise, exclude those 36424941Seric ** jobs. 3654632Seric ** 3664632Seric ** Returns: 36710121Seric ** The number of request in the queue (not necessarily 36810121Seric ** the number of requests in WorkQ however). 3694632Seric ** 3704632Seric ** Side Effects: 3714632Seric ** Sets WorkQ to the queue of available work, in order. 3724632Seric */ 3734632Seric 37425687Seric # define NEED_P 001 37525687Seric # define NEED_T 002 3764632Seric 37724941Seric orderq(doall) 37824941Seric bool doall; 3794632Seric { 3806625Sglickman register struct direct *d; 3814632Seric register WORK *w; 3826625Sglickman DIR *f; 3834632Seric register int i; 38425687Seric WORK wlist[QUEUESIZE+1]; 38510070Seric int wn = -1; 3864632Seric extern workcmpf(); 3874632Seric 3884632Seric /* clear out old WorkQ */ 3894632Seric for (w = WorkQ; w != NULL; ) 3904632Seric { 3914632Seric register WORK *nw = w->w_next; 3924632Seric 3934632Seric WorkQ = nw; 3944632Seric free(w->w_name); 3954632Seric free((char *) w); 3964632Seric w = nw; 3974632Seric } 3984632Seric 3994632Seric /* open the queue directory */ 4008148Seric f = opendir("."); 4014632Seric if (f == NULL) 4024632Seric { 4038148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 40410070Seric return (0); 4054632Seric } 4064632Seric 4074632Seric /* 4084632Seric ** Read the work directory. 4094632Seric */ 4104632Seric 41110070Seric while ((d = readdir(f)) != NULL) 4124632Seric { 4139377Seric FILE *cf; 4144632Seric char lbuf[MAXNAME]; 4154632Seric 4164632Seric /* is this an interesting entry? */ 4177812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 4184632Seric continue; 4194632Seric 42010070Seric /* yes -- open control file (if not too many files) */ 42125687Seric if (++wn >= QUEUESIZE) 42210070Seric continue; 4238148Seric cf = fopen(d->d_name, "r"); 4244632Seric if (cf == NULL) 4254632Seric { 4267055Seric /* this may be some random person sending hir msgs */ 4277055Seric /* syserr("orderq: cannot open %s", cbuf); */ 42810090Seric #ifdef DEBUG 42910090Seric if (tTd(41, 2)) 43010090Seric printf("orderq: cannot open %s (%d)\n", 43110090Seric d->d_name, errno); 43210090Seric #endif DEBUG 4337055Seric errno = 0; 43410090Seric wn--; 4354632Seric continue; 4364632Seric } 43725687Seric w = &wlist[wn]; 43825687Seric w->w_name = newstr(d->d_name); 4394632Seric 44025027Seric /* make sure jobs in creation don't clog queue */ 44125687Seric w->w_pri = 0x7fffffff; 44225687Seric w->w_ctime = 0; 44325027Seric 4444632Seric /* extract useful information */ 44525687Seric i = NEED_P | NEED_T; 44625687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 4474632Seric { 44824954Seric extern long atol(); 44924954Seric 45024941Seric switch (lbuf[0]) 4514632Seric { 45224941Seric case 'P': 45325687Seric w->w_pri = atol(&lbuf[1]); 45425687Seric i &= ~NEED_P; 4554632Seric break; 45625013Seric 45725013Seric case 'T': 45825687Seric w->w_ctime = atol(&lbuf[1]); 45925687Seric i &= ~NEED_T; 46025013Seric break; 4614632Seric } 4624632Seric } 4634632Seric (void) fclose(cf); 46424953Seric 46525687Seric if (!doall && shouldqueue(w->w_pri)) 46624953Seric { 46724953Seric /* don't even bother sorting this job in */ 46824953Seric wn--; 46924953Seric } 4704632Seric } 4716625Sglickman (void) closedir(f); 47210090Seric wn++; 4734632Seric 4744632Seric /* 4754632Seric ** Sort the work directory. 4764632Seric */ 4774632Seric 47825687Seric qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); 4794632Seric 4804632Seric /* 4814632Seric ** Convert the work list into canonical form. 4829377Seric ** Should be turning it into a list of envelopes here perhaps. 4834632Seric */ 4844632Seric 48524981Seric WorkQ = NULL; 48625687Seric for (i = min(wn, QUEUESIZE); --i >= 0; ) 4874632Seric { 4884632Seric w = (WORK *) xalloc(sizeof *w); 4894632Seric w->w_name = wlist[i].w_name; 4904632Seric w->w_pri = wlist[i].w_pri; 49125013Seric w->w_ctime = wlist[i].w_ctime; 49224981Seric w->w_next = WorkQ; 49324981Seric WorkQ = w; 4944632Seric } 4954632Seric 4964632Seric # ifdef DEBUG 4977677Seric if (tTd(40, 1)) 4984632Seric { 4994632Seric for (w = WorkQ; w != NULL; w = w->w_next) 5005037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 5014632Seric } 5024632Seric # endif DEBUG 50310070Seric 50410090Seric return (wn); 5054632Seric } 5064632Seric /* 5077677Seric ** WORKCMPF -- compare function for ordering work. 5084632Seric ** 5094632Seric ** Parameters: 5104632Seric ** a -- the first argument. 5114632Seric ** b -- the second argument. 5124632Seric ** 5134632Seric ** Returns: 51424981Seric ** -1 if a < b 51524981Seric ** 0 if a == b 51624981Seric ** +1 if a > b 5174632Seric ** 5184632Seric ** Side Effects: 5194632Seric ** none. 5204632Seric */ 5214632Seric 5224632Seric workcmpf(a, b) 5235037Seric register WORK *a; 5245037Seric register WORK *b; 5254632Seric { 52625013Seric long pa = a->w_pri + a->w_ctime; 52725013Seric long pb = b->w_pri + b->w_ctime; 52824941Seric 52924941Seric if (pa == pb) 5304632Seric return (0); 53124941Seric else if (pa > pb) 53224981Seric return (1); 53324981Seric else 53410121Seric return (-1); 5354632Seric } 5364632Seric /* 5374632Seric ** DOWORK -- do a work request. 5384632Seric ** 5394632Seric ** Parameters: 5404632Seric ** w -- the work request to be satisfied. 5414632Seric ** 5424632Seric ** Returns: 5434632Seric ** none. 5444632Seric ** 5454632Seric ** Side Effects: 5464632Seric ** The work request is satisfied if possible. 5474632Seric */ 5484632Seric 5494632Seric dowork(w) 5504632Seric register WORK *w; 5514632Seric { 5524632Seric register int i; 55324941Seric extern bool shouldqueue(); 5544632Seric 5554632Seric # ifdef DEBUG 5567677Seric if (tTd(40, 1)) 5575037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 5584632Seric # endif DEBUG 5594632Seric 5604632Seric /* 56124941Seric ** Ignore jobs that are too expensive for the moment. 5624632Seric */ 5634632Seric 56424941Seric if (shouldqueue(w->w_pri)) 5654632Seric { 56624941Seric if (Verbose) 56724967Seric printf("\nSkipping %s\n", w->w_name + 2); 5684632Seric return; 5694632Seric } 5704632Seric 57124941Seric /* 57224941Seric ** Fork for work. 57324941Seric */ 57424941Seric 57524941Seric if (ForkQueueRuns) 57624941Seric { 57724941Seric i = fork(); 57824941Seric if (i < 0) 57924941Seric { 58024941Seric syserr("dowork: cannot fork"); 58124941Seric return; 58224941Seric } 58324941Seric } 58424941Seric else 58524941Seric { 58624941Seric i = 0; 58724941Seric } 58824941Seric 5894632Seric if (i == 0) 5904632Seric { 5914632Seric /* 5924632Seric ** CHILD 5938148Seric ** Lock the control file to avoid duplicate deliveries. 5948148Seric ** Then run the file as though we had just read it. 5957350Seric ** We save an idea of the temporary name so we 5967350Seric ** can recover on interrupt. 5974632Seric */ 5984632Seric 5997763Seric /* set basic modes, etc. */ 6007356Seric (void) alarm(0); 60125612Seric clearenvelope(CurEnv, FALSE); 6024632Seric QueueRun = TRUE; 6039377Seric ErrorMode = EM_MAIL; 6048148Seric CurEnv->e_id = &w->w_name[2]; 6057876Seric # ifdef LOG 6067876Seric if (LogLevel > 11) 6077881Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 6087881Seric getpid()); 6097876Seric # endif LOG 6107763Seric 6117763Seric /* don't use the headers from sendmail.cf... */ 6127763Seric CurEnv->e_header = NULL; 6137763Seric 61417468Seric /* lock the control file during processing */ 6157812Seric if (link(w->w_name, queuename(CurEnv, 'l')) < 0) 6166980Seric { 6177812Seric /* being processed by another queuer */ 6187881Seric # ifdef LOG 6197881Seric if (LogLevel > 4) 6207881Seric syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id); 6217881Seric # endif LOG 62224941Seric if (ForkQueueRuns) 62324941Seric exit(EX_OK); 62424941Seric else 62524941Seric return; 6266980Seric } 6276980Seric 6286980Seric /* do basic system initialization */ 6294632Seric initsys(); 6306980Seric 6316980Seric /* read the queue control file */ 63217477Seric readqf(CurEnv, TRUE); 6339338Seric CurEnv->e_flags |= EF_INQUEUE; 6349377Seric eatheader(CurEnv); 6356980Seric 6366980Seric /* do the delivery */ 6379338Seric if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 6389282Seric sendall(CurEnv, SM_DELIVER); 6396980Seric 6406980Seric /* finish up and exit */ 64124941Seric if (ForkQueueRuns) 64224941Seric finis(); 64324941Seric else 64424941Seric dropenvelope(CurEnv); 6454632Seric } 64624941Seric else 64724941Seric { 64824941Seric /* 64924941Seric ** Parent -- pick up results. 65024941Seric */ 6514632Seric 65224941Seric errno = 0; 65324941Seric (void) waitfor(i); 65424941Seric } 6554632Seric } 6564632Seric /* 6574632Seric ** READQF -- read queue file and set up environment. 6584632Seric ** 6594632Seric ** Parameters: 6609377Seric ** e -- the envelope of the job to run. 6619630Seric ** full -- if set, read in all information. Otherwise just 6629630Seric ** read in info needed for a queue print. 6634632Seric ** 6644632Seric ** Returns: 6654632Seric ** none. 6664632Seric ** 6674632Seric ** Side Effects: 6684632Seric ** cf is read and created as the current job, as though 6694632Seric ** we had been invoked by argument. 6704632Seric */ 6714632Seric 67217477Seric readqf(e, full) 6739377Seric register ENVELOPE *e; 6749630Seric bool full; 6754632Seric { 67617477Seric char *qf; 67717477Seric register FILE *qfp; 6787785Seric char buf[MAXFIELD]; 6799348Seric extern char *fgetfolded(); 68024954Seric extern long atol(); 6814632Seric 6824632Seric /* 68317468Seric ** Read and process the file. 6844632Seric */ 6854632Seric 68617477Seric qf = queuename(e, 'q'); 68717477Seric qfp = fopen(qf, "r"); 68817477Seric if (qfp == NULL) 68917477Seric { 69017477Seric syserr("readqf: no control file %s", qf); 69117477Seric return; 69217477Seric } 69317477Seric FileName = qf; 6949377Seric LineNumber = 0; 6959630Seric if (Verbose && full) 6969377Seric printf("\nRunning %s\n", e->e_id); 69717468Seric while (fgetfolded(buf, sizeof buf, qfp) != NULL) 6984632Seric { 69926504Seric # ifdef DEBUG 70026504Seric if (tTd(40, 4)) 70126504Seric printf("+++++ %s\n", buf); 70226504Seric # endif DEBUG 7034632Seric switch (buf[0]) 7044632Seric { 7054632Seric case 'R': /* specify recipient */ 7069618Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 7074632Seric break; 7084632Seric 70925687Seric case 'E': /* specify error recipient */ 71025687Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue); 71125687Seric break; 71225687Seric 7134632Seric case 'H': /* header */ 7149630Seric if (full) 7159630Seric (void) chompheader(&buf[1], FALSE); 7164632Seric break; 7174632Seric 71810108Seric case 'M': /* message */ 71910108Seric e->e_message = newstr(&buf[1]); 72010108Seric break; 72110108Seric 7224632Seric case 'S': /* sender */ 7234634Seric setsender(newstr(&buf[1])); 7244632Seric break; 7254632Seric 7264632Seric case 'D': /* data file name */ 7279630Seric if (!full) 7289630Seric break; 7299377Seric e->e_df = newstr(&buf[1]); 7309544Seric e->e_dfp = fopen(e->e_df, "r"); 7319544Seric if (e->e_dfp == NULL) 7329377Seric syserr("readqf: cannot open %s", e->e_df); 7334632Seric break; 7344632Seric 7357860Seric case 'T': /* init time */ 73624941Seric e->e_ctime = atol(&buf[1]); 7374632Seric break; 7384632Seric 7394634Seric case 'P': /* message priority */ 74025008Seric e->e_msgpriority = atol(&buf[1]) + WkTimeFact; 7414634Seric break; 7424634Seric 74324941Seric case '\0': /* blank line; ignore */ 74424941Seric break; 74524941Seric 7464632Seric default: 74724941Seric syserr("readqf(%s:%d): bad line \"%s\"", e->e_id, 74824941Seric LineNumber, buf); 7494632Seric break; 7504632Seric } 7514632Seric } 7529377Seric 75324954Seric (void) fclose(qfp); 7549377Seric FileName = NULL; 75524941Seric 75624941Seric /* 75724941Seric ** If we haven't read any lines, this queue file is empty. 75824941Seric ** Arrange to remove it without referencing any null pointers. 75924941Seric */ 76024941Seric 76124941Seric if (LineNumber == 0) 76224941Seric { 76324941Seric errno = 0; 76424941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 76524941Seric } 7664632Seric } 7674632Seric /* 7689630Seric ** PRINTQUEUE -- print out a representation of the mail queue 7699630Seric ** 7709630Seric ** Parameters: 7719630Seric ** none. 7729630Seric ** 7739630Seric ** Returns: 7749630Seric ** none. 7759630Seric ** 7769630Seric ** Side Effects: 7779630Seric ** Prints a listing of the mail queue on the standard output. 7789630Seric */ 7795182Seric 7809630Seric printqueue() 7819630Seric { 7829630Seric register WORK *w; 7839630Seric FILE *f; 78410070Seric int nrequests; 7859630Seric char buf[MAXLINE]; 7869630Seric 7879630Seric /* 7889630Seric ** Read and order the queue. 7899630Seric */ 7909630Seric 79124941Seric nrequests = orderq(TRUE); 7929630Seric 7939630Seric /* 7949630Seric ** Print the work list that we have read. 7959630Seric */ 7969630Seric 7979630Seric /* first see if there is anything */ 79810070Seric if (nrequests <= 0) 7999630Seric { 80010070Seric printf("Mail queue is empty\n"); 8019630Seric return; 8029630Seric } 8039630Seric 80410096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 80525687Seric if (nrequests > QUEUESIZE) 80625687Seric printf(", only %d printed", QUEUESIZE); 80724979Seric if (Verbose) 80825032Seric printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 80924979Seric else 81024979Seric printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 8119630Seric for (w = WorkQ; w != NULL; w = w->w_next) 8129630Seric { 8139630Seric struct stat st; 81410070Seric auto time_t submittime = 0; 81510070Seric long dfsize = -1; 81610108Seric char lf[20]; 81710108Seric char message[MAXLINE]; 81824941Seric extern bool shouldqueue(); 8199630Seric 82017468Seric f = fopen(w->w_name, "r"); 82117468Seric if (f == NULL) 82217468Seric { 82317468Seric errno = 0; 82417468Seric continue; 82517468Seric } 8269630Seric printf("%7s", w->w_name + 2); 82723098Seric (void) strcpy(lf, w->w_name); 82810070Seric lf[0] = 'l'; 82910070Seric if (stat(lf, &st) >= 0) 83010070Seric printf("*"); 83124941Seric else if (shouldqueue(w->w_pri)) 83224941Seric printf("X"); 83310070Seric else 83410070Seric printf(" "); 83510070Seric errno = 0; 83617468Seric 83710108Seric message[0] = '\0'; 8389630Seric while (fgets(buf, sizeof buf, f) != NULL) 8399630Seric { 8409630Seric fixcrlf(buf, TRUE); 8419630Seric switch (buf[0]) 8429630Seric { 84310108Seric case 'M': /* error message */ 84423098Seric (void) strcpy(message, &buf[1]); 84510108Seric break; 84610108Seric 8479630Seric case 'S': /* sender name */ 84824979Seric if (Verbose) 84925027Seric printf("%8ld %10ld %.12s %.38s", dfsize, 85025027Seric w->w_pri, ctime(&submittime) + 4, 85124979Seric &buf[1]); 85224979Seric else 85324979Seric printf("%8ld %.16s %.45s", dfsize, 85424979Seric ctime(&submittime), &buf[1]); 85510108Seric if (message[0] != '\0') 85625027Seric printf("\n\t\t (%.60s)", message); 8579630Seric break; 8589630Seric 8599630Seric case 'R': /* recipient name */ 86024979Seric if (Verbose) 86125027Seric printf("\n\t\t\t\t\t %.38s", &buf[1]); 86224979Seric else 86324979Seric printf("\n\t\t\t\t %.45s", &buf[1]); 8649630Seric break; 8659630Seric 8669630Seric case 'T': /* creation time */ 86724941Seric submittime = atol(&buf[1]); 8689630Seric break; 86910070Seric 87010070Seric case 'D': /* data file name */ 87110070Seric if (stat(&buf[1], &st) >= 0) 87210070Seric dfsize = st.st_size; 87310070Seric break; 8749630Seric } 8759630Seric } 87610070Seric if (submittime == (time_t) 0) 87710070Seric printf(" (no control file)"); 8789630Seric printf("\n"); 87923098Seric (void) fclose(f); 8809630Seric } 8819630Seric } 8829630Seric 8835182Seric # endif QUEUE 88417468Seric /* 88517468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 88617468Seric ** 88717468Seric ** Assigns an id code if one does not already exist. 88817468Seric ** This code is very careful to avoid trashing existing files 88917468Seric ** under any circumstances. 89017468Seric ** We first create an nf file that is only used when 89117468Seric ** assigning an id. This file is always empty, so that 89217468Seric ** we can never accidently truncate an lf file. 89317468Seric ** 89417468Seric ** Parameters: 89517468Seric ** e -- envelope to build it in/from. 89617468Seric ** type -- the file type, used as the first character 89717468Seric ** of the file name. 89817468Seric ** 89917468Seric ** Returns: 90017468Seric ** a pointer to the new file name (in a static buffer). 90117468Seric ** 90217468Seric ** Side Effects: 90317468Seric ** Will create the lf and qf files if no id code is 90417468Seric ** already assigned. This will cause the envelope 90517468Seric ** to be modified. 90617468Seric */ 90717468Seric 90817468Seric char * 90917468Seric queuename(e, type) 91017468Seric register ENVELOPE *e; 91117468Seric char type; 91217468Seric { 91317468Seric static char buf[MAXNAME]; 91417468Seric static int pid = -1; 91517468Seric char c1 = 'A'; 91617468Seric char c2 = 'A'; 91717468Seric 91817468Seric if (e->e_id == NULL) 91917468Seric { 92017468Seric char qf[20]; 92117468Seric char nf[20]; 92217468Seric char lf[20]; 92317468Seric 92417468Seric /* find a unique id */ 92517468Seric if (pid != getpid()) 92617468Seric { 92717468Seric /* new process -- start back at "AA" */ 92817468Seric pid = getpid(); 92917468Seric c1 = 'A'; 93017468Seric c2 = 'A' - 1; 93117468Seric } 93217468Seric (void) sprintf(qf, "qfAA%05d", pid); 93323098Seric (void) strcpy(lf, qf); 93417468Seric lf[0] = 'l'; 93523098Seric (void) strcpy(nf, qf); 93617468Seric nf[0] = 'n'; 93717468Seric 93817468Seric while (c1 < '~' || c2 < 'Z') 93917468Seric { 94017468Seric int i; 94117468Seric 94217468Seric if (c2 >= 'Z') 94317468Seric { 94417468Seric c1++; 94517468Seric c2 = 'A' - 1; 94617468Seric } 94717477Seric lf[2] = nf[2] = qf[2] = c1; 94817477Seric lf[3] = nf[3] = qf[3] = ++c2; 94917468Seric # ifdef DEBUG 95017468Seric if (tTd(7, 20)) 95117468Seric printf("queuename: trying \"%s\"\n", nf); 95217468Seric # endif DEBUG 95317468Seric 95417468Seric # ifdef QUEUE 95517468Seric if (access(lf, 0) >= 0 || access(qf, 0) >= 0) 95617468Seric continue; 95717468Seric errno = 0; 95817468Seric i = creat(nf, FileMode); 95917468Seric if (i < 0) 96017468Seric { 96117468Seric (void) unlink(nf); /* kernel bug */ 96217468Seric continue; 96317468Seric } 96417468Seric (void) close(i); 96517468Seric i = link(nf, lf); 96617468Seric (void) unlink(nf); 96717468Seric if (i < 0) 96817468Seric continue; 96917468Seric if (link(lf, qf) >= 0) 97017468Seric break; 97117468Seric (void) unlink(lf); 97217468Seric # else QUEUE 97317982Seric if (close(creat(qf, FileMode)) >= 0) 97417982Seric break; 97517468Seric # endif QUEUE 97617468Seric } 97717468Seric if (c1 >= '~' && c2 >= 'Z') 97817468Seric { 97917468Seric syserr("queuename: Cannot create \"%s\" in \"%s\"", 98017468Seric qf, QueueDir); 98117468Seric exit(EX_OSERR); 98217468Seric } 98317468Seric e->e_id = newstr(&qf[2]); 98417468Seric define('i', e->e_id, e); 98517468Seric # ifdef DEBUG 98617468Seric if (tTd(7, 1)) 98717468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 98817468Seric # ifdef LOG 98917468Seric if (LogLevel > 16) 99017468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 99117468Seric # endif LOG 99217468Seric # endif DEBUG 99317468Seric } 99417468Seric 99517468Seric if (type == '\0') 99617468Seric return (NULL); 99717468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 99817468Seric # ifdef DEBUG 99917468Seric if (tTd(7, 2)) 100017468Seric printf("queuename: %s\n", buf); 100117468Seric # endif DEBUG 100217468Seric return (buf); 100317468Seric } 100417468Seric /* 100517468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 100617468Seric ** 100717468Seric ** Parameters: 100817468Seric ** e -- the envelope to unlock. 100917468Seric ** 101017468Seric ** Returns: 101117468Seric ** none 101217468Seric ** 101317468Seric ** Side Effects: 101417468Seric ** unlocks the queue for `e'. 101517468Seric */ 101617468Seric 101717468Seric unlockqueue(e) 101817468Seric ENVELOPE *e; 101917468Seric { 102017468Seric /* remove the transcript */ 102117468Seric #ifdef DEBUG 102217468Seric # ifdef LOG 102317468Seric if (LogLevel > 19) 102417468Seric syslog(LOG_DEBUG, "%s: unlock", e->e_id); 102517468Seric # endif LOG 102617468Seric if (!tTd(51, 4)) 102717468Seric #endif DEBUG 102817468Seric xunlink(queuename(e, 'x')); 102917468Seric 103017468Seric # ifdef QUEUE 103117468Seric /* last but not least, remove the lock */ 103217468Seric xunlink(queuename(e, 'l')); 103317468Seric # endif QUEUE 103417468Seric } 1035