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*26504Seric static char SccsId[] = "@(#)queue.c 5.20 (Berkeley) 03/08/86 (no queueing)"; 2123116Seric # endif not lint 225182Seric # else QUEUE 234632Seric 2423116Seric # ifndef lint 25*26504Seric static char SccsId[] = "@(#)queue.c 5.20 (Berkeley) 03/08/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 { 163*26504Seric if (!bitset(QDONTSEND, q->q_flags)) 164*26504Seric 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 /* 3307466Seric ** Start making passes through the queue. 3317466Seric ** First, read and sort the entire queue. 3327466Seric ** Then, process the work in that order. 3337466Seric ** But if you take too long, start over. 3347466Seric */ 3357466Seric 3367943Seric /* order the existing work requests */ 33724954Seric (void) orderq(FALSE); 3387690Seric 3397943Seric /* process them once at a time */ 3407943Seric while (WorkQ != NULL) 3414639Seric { 3427943Seric WORK *w = WorkQ; 3437881Seric 3447943Seric WorkQ = WorkQ->w_next; 3457943Seric dowork(w); 3467943Seric free(w->w_name); 3477943Seric free((char *) w); 3484639Seric } 3497943Seric finis(); 3504634Seric } 3514634Seric /* 3524632Seric ** ORDERQ -- order the work queue. 3534632Seric ** 3544632Seric ** Parameters: 35524941Seric ** doall -- if set, include everything in the queue (even 35624941Seric ** the jobs that cannot be run because the load 35724941Seric ** average is too high). Otherwise, exclude those 35824941Seric ** jobs. 3594632Seric ** 3604632Seric ** Returns: 36110121Seric ** The number of request in the queue (not necessarily 36210121Seric ** the number of requests in WorkQ however). 3634632Seric ** 3644632Seric ** Side Effects: 3654632Seric ** Sets WorkQ to the queue of available work, in order. 3664632Seric */ 3674632Seric 36825687Seric # define NEED_P 001 36925687Seric # define NEED_T 002 3704632Seric 37124941Seric orderq(doall) 37224941Seric bool doall; 3734632Seric { 3746625Sglickman register struct direct *d; 3754632Seric register WORK *w; 3766625Sglickman DIR *f; 3774632Seric register int i; 37825687Seric WORK wlist[QUEUESIZE+1]; 37910070Seric int wn = -1; 3804632Seric extern workcmpf(); 3814632Seric 3824632Seric /* clear out old WorkQ */ 3834632Seric for (w = WorkQ; w != NULL; ) 3844632Seric { 3854632Seric register WORK *nw = w->w_next; 3864632Seric 3874632Seric WorkQ = nw; 3884632Seric free(w->w_name); 3894632Seric free((char *) w); 3904632Seric w = nw; 3914632Seric } 3924632Seric 3934632Seric /* open the queue directory */ 3948148Seric f = opendir("."); 3954632Seric if (f == NULL) 3964632Seric { 3978148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 39810070Seric return (0); 3994632Seric } 4004632Seric 4014632Seric /* 4024632Seric ** Read the work directory. 4034632Seric */ 4044632Seric 40510070Seric while ((d = readdir(f)) != NULL) 4064632Seric { 4079377Seric FILE *cf; 4084632Seric char lbuf[MAXNAME]; 4094632Seric 4104632Seric /* is this an interesting entry? */ 4117812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 4124632Seric continue; 4134632Seric 41410070Seric /* yes -- open control file (if not too many files) */ 41525687Seric if (++wn >= QUEUESIZE) 41610070Seric continue; 4178148Seric cf = fopen(d->d_name, "r"); 4184632Seric if (cf == NULL) 4194632Seric { 4207055Seric /* this may be some random person sending hir msgs */ 4217055Seric /* syserr("orderq: cannot open %s", cbuf); */ 42210090Seric #ifdef DEBUG 42310090Seric if (tTd(41, 2)) 42410090Seric printf("orderq: cannot open %s (%d)\n", 42510090Seric d->d_name, errno); 42610090Seric #endif DEBUG 4277055Seric errno = 0; 42810090Seric wn--; 4294632Seric continue; 4304632Seric } 43125687Seric w = &wlist[wn]; 43225687Seric w->w_name = newstr(d->d_name); 4334632Seric 43425027Seric /* make sure jobs in creation don't clog queue */ 43525687Seric w->w_pri = 0x7fffffff; 43625687Seric w->w_ctime = 0; 43725027Seric 4384632Seric /* extract useful information */ 43925687Seric i = NEED_P | NEED_T; 44025687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 4414632Seric { 44224954Seric extern long atol(); 44324954Seric 44424941Seric switch (lbuf[0]) 4454632Seric { 44624941Seric case 'P': 44725687Seric w->w_pri = atol(&lbuf[1]); 44825687Seric i &= ~NEED_P; 4494632Seric break; 45025013Seric 45125013Seric case 'T': 45225687Seric w->w_ctime = atol(&lbuf[1]); 45325687Seric i &= ~NEED_T; 45425013Seric break; 4554632Seric } 4564632Seric } 4574632Seric (void) fclose(cf); 45824953Seric 45925687Seric if (!doall && shouldqueue(w->w_pri)) 46024953Seric { 46124953Seric /* don't even bother sorting this job in */ 46224953Seric wn--; 46324953Seric } 4644632Seric } 4656625Sglickman (void) closedir(f); 46610090Seric wn++; 4674632Seric 4684632Seric /* 4694632Seric ** Sort the work directory. 4704632Seric */ 4714632Seric 47225687Seric qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf); 4734632Seric 4744632Seric /* 4754632Seric ** Convert the work list into canonical form. 4769377Seric ** Should be turning it into a list of envelopes here perhaps. 4774632Seric */ 4784632Seric 47924981Seric WorkQ = NULL; 48025687Seric for (i = min(wn, QUEUESIZE); --i >= 0; ) 4814632Seric { 4824632Seric w = (WORK *) xalloc(sizeof *w); 4834632Seric w->w_name = wlist[i].w_name; 4844632Seric w->w_pri = wlist[i].w_pri; 48525013Seric w->w_ctime = wlist[i].w_ctime; 48624981Seric w->w_next = WorkQ; 48724981Seric WorkQ = w; 4884632Seric } 4894632Seric 4904632Seric # ifdef DEBUG 4917677Seric if (tTd(40, 1)) 4924632Seric { 4934632Seric for (w = WorkQ; w != NULL; w = w->w_next) 4945037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 4954632Seric } 4964632Seric # endif DEBUG 49710070Seric 49810090Seric return (wn); 4994632Seric } 5004632Seric /* 5017677Seric ** WORKCMPF -- compare function for ordering work. 5024632Seric ** 5034632Seric ** Parameters: 5044632Seric ** a -- the first argument. 5054632Seric ** b -- the second argument. 5064632Seric ** 5074632Seric ** Returns: 50824981Seric ** -1 if a < b 50924981Seric ** 0 if a == b 51024981Seric ** +1 if a > b 5114632Seric ** 5124632Seric ** Side Effects: 5134632Seric ** none. 5144632Seric */ 5154632Seric 5164632Seric workcmpf(a, b) 5175037Seric register WORK *a; 5185037Seric register WORK *b; 5194632Seric { 52025013Seric long pa = a->w_pri + a->w_ctime; 52125013Seric long pb = b->w_pri + b->w_ctime; 52224941Seric 52324941Seric if (pa == pb) 5244632Seric return (0); 52524941Seric else if (pa > pb) 52624981Seric return (1); 52724981Seric else 52810121Seric return (-1); 5294632Seric } 5304632Seric /* 5314632Seric ** DOWORK -- do a work request. 5324632Seric ** 5334632Seric ** Parameters: 5344632Seric ** w -- the work request to be satisfied. 5354632Seric ** 5364632Seric ** Returns: 5374632Seric ** none. 5384632Seric ** 5394632Seric ** Side Effects: 5404632Seric ** The work request is satisfied if possible. 5414632Seric */ 5424632Seric 5434632Seric dowork(w) 5444632Seric register WORK *w; 5454632Seric { 5464632Seric register int i; 54724941Seric extern bool shouldqueue(); 5484632Seric 5494632Seric # ifdef DEBUG 5507677Seric if (tTd(40, 1)) 5515037Seric printf("dowork: %s pri %ld\n", w->w_name, w->w_pri); 5524632Seric # endif DEBUG 5534632Seric 5544632Seric /* 55524941Seric ** Ignore jobs that are too expensive for the moment. 5564632Seric */ 5574632Seric 55824941Seric if (shouldqueue(w->w_pri)) 5594632Seric { 56024941Seric if (Verbose) 56124967Seric printf("\nSkipping %s\n", w->w_name + 2); 5624632Seric return; 5634632Seric } 5644632Seric 56524941Seric /* 56624941Seric ** Fork for work. 56724941Seric */ 56824941Seric 56924941Seric if (ForkQueueRuns) 57024941Seric { 57124941Seric i = fork(); 57224941Seric if (i < 0) 57324941Seric { 57424941Seric syserr("dowork: cannot fork"); 57524941Seric return; 57624941Seric } 57724941Seric } 57824941Seric else 57924941Seric { 58024941Seric i = 0; 58124941Seric } 58224941Seric 5834632Seric if (i == 0) 5844632Seric { 5854632Seric /* 5864632Seric ** CHILD 5878148Seric ** Lock the control file to avoid duplicate deliveries. 5888148Seric ** Then run the file as though we had just read it. 5897350Seric ** We save an idea of the temporary name so we 5907350Seric ** can recover on interrupt. 5914632Seric */ 5924632Seric 5937763Seric /* set basic modes, etc. */ 5947356Seric (void) alarm(0); 59525612Seric clearenvelope(CurEnv, FALSE); 5964632Seric QueueRun = TRUE; 5979377Seric ErrorMode = EM_MAIL; 5988148Seric CurEnv->e_id = &w->w_name[2]; 5997876Seric # ifdef LOG 6007876Seric if (LogLevel > 11) 6017881Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id, 6027881Seric getpid()); 6037876Seric # endif LOG 6047763Seric 6057763Seric /* don't use the headers from sendmail.cf... */ 6067763Seric CurEnv->e_header = NULL; 6077763Seric 60817468Seric /* lock the control file during processing */ 6097812Seric if (link(w->w_name, queuename(CurEnv, 'l')) < 0) 6106980Seric { 6117812Seric /* being processed by another queuer */ 6127881Seric # ifdef LOG 6137881Seric if (LogLevel > 4) 6147881Seric syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id); 6157881Seric # endif LOG 61624941Seric if (ForkQueueRuns) 61724941Seric exit(EX_OK); 61824941Seric else 61924941Seric return; 6206980Seric } 6216980Seric 6226980Seric /* do basic system initialization */ 6234632Seric initsys(); 6246980Seric 6256980Seric /* read the queue control file */ 62617477Seric readqf(CurEnv, TRUE); 6279338Seric CurEnv->e_flags |= EF_INQUEUE; 6289377Seric eatheader(CurEnv); 6296980Seric 6306980Seric /* do the delivery */ 6319338Seric if (!bitset(EF_FATALERRS, CurEnv->e_flags)) 6329282Seric sendall(CurEnv, SM_DELIVER); 6336980Seric 6346980Seric /* finish up and exit */ 63524941Seric if (ForkQueueRuns) 63624941Seric finis(); 63724941Seric else 63824941Seric dropenvelope(CurEnv); 6394632Seric } 64024941Seric else 64124941Seric { 64224941Seric /* 64324941Seric ** Parent -- pick up results. 64424941Seric */ 6454632Seric 64624941Seric errno = 0; 64724941Seric (void) waitfor(i); 64824941Seric } 6494632Seric } 6504632Seric /* 6514632Seric ** READQF -- read queue file and set up environment. 6524632Seric ** 6534632Seric ** Parameters: 6549377Seric ** e -- the envelope of the job to run. 6559630Seric ** full -- if set, read in all information. Otherwise just 6569630Seric ** read in info needed for a queue print. 6574632Seric ** 6584632Seric ** Returns: 6594632Seric ** none. 6604632Seric ** 6614632Seric ** Side Effects: 6624632Seric ** cf is read and created as the current job, as though 6634632Seric ** we had been invoked by argument. 6644632Seric */ 6654632Seric 66617477Seric readqf(e, full) 6679377Seric register ENVELOPE *e; 6689630Seric bool full; 6694632Seric { 67017477Seric char *qf; 67117477Seric register FILE *qfp; 6727785Seric char buf[MAXFIELD]; 6739348Seric extern char *fgetfolded(); 67424954Seric extern long atol(); 6754632Seric 6764632Seric /* 67717468Seric ** Read and process the file. 6784632Seric */ 6794632Seric 68017477Seric qf = queuename(e, 'q'); 68117477Seric qfp = fopen(qf, "r"); 68217477Seric if (qfp == NULL) 68317477Seric { 68417477Seric syserr("readqf: no control file %s", qf); 68517477Seric return; 68617477Seric } 68717477Seric FileName = qf; 6889377Seric LineNumber = 0; 6899630Seric if (Verbose && full) 6909377Seric printf("\nRunning %s\n", e->e_id); 69117468Seric while (fgetfolded(buf, sizeof buf, qfp) != NULL) 6924632Seric { 693*26504Seric # ifdef DEBUG 694*26504Seric if (tTd(40, 4)) 695*26504Seric printf("+++++ %s\n", buf); 696*26504Seric # endif DEBUG 6974632Seric switch (buf[0]) 6984632Seric { 6994632Seric case 'R': /* specify recipient */ 7009618Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue); 7014632Seric break; 7024632Seric 70325687Seric case 'E': /* specify error recipient */ 70425687Seric sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue); 70525687Seric break; 70625687Seric 7074632Seric case 'H': /* header */ 7089630Seric if (full) 7099630Seric (void) chompheader(&buf[1], FALSE); 7104632Seric break; 7114632Seric 71210108Seric case 'M': /* message */ 71310108Seric e->e_message = newstr(&buf[1]); 71410108Seric break; 71510108Seric 7164632Seric case 'S': /* sender */ 7174634Seric setsender(newstr(&buf[1])); 7184632Seric break; 7194632Seric 7204632Seric case 'D': /* data file name */ 7219630Seric if (!full) 7229630Seric break; 7239377Seric e->e_df = newstr(&buf[1]); 7249544Seric e->e_dfp = fopen(e->e_df, "r"); 7259544Seric if (e->e_dfp == NULL) 7269377Seric syserr("readqf: cannot open %s", e->e_df); 7274632Seric break; 7284632Seric 7297860Seric case 'T': /* init time */ 73024941Seric e->e_ctime = atol(&buf[1]); 7314632Seric break; 7324632Seric 7334634Seric case 'P': /* message priority */ 73425008Seric e->e_msgpriority = atol(&buf[1]) + WkTimeFact; 7354634Seric break; 7364634Seric 73724941Seric case '\0': /* blank line; ignore */ 73824941Seric break; 73924941Seric 7404632Seric default: 74124941Seric syserr("readqf(%s:%d): bad line \"%s\"", e->e_id, 74224941Seric LineNumber, buf); 7434632Seric break; 7444632Seric } 7454632Seric } 7469377Seric 74724954Seric (void) fclose(qfp); 7489377Seric FileName = NULL; 74924941Seric 75024941Seric /* 75124941Seric ** If we haven't read any lines, this queue file is empty. 75224941Seric ** Arrange to remove it without referencing any null pointers. 75324941Seric */ 75424941Seric 75524941Seric if (LineNumber == 0) 75624941Seric { 75724941Seric errno = 0; 75824941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 75924941Seric } 7604632Seric } 7614632Seric /* 7629630Seric ** PRINTQUEUE -- print out a representation of the mail queue 7639630Seric ** 7649630Seric ** Parameters: 7659630Seric ** none. 7669630Seric ** 7679630Seric ** Returns: 7689630Seric ** none. 7699630Seric ** 7709630Seric ** Side Effects: 7719630Seric ** Prints a listing of the mail queue on the standard output. 7729630Seric */ 7735182Seric 7749630Seric printqueue() 7759630Seric { 7769630Seric register WORK *w; 7779630Seric FILE *f; 77810070Seric int nrequests; 7799630Seric char buf[MAXLINE]; 7809630Seric 7819630Seric /* 7829630Seric ** Read and order the queue. 7839630Seric */ 7849630Seric 78524941Seric nrequests = orderq(TRUE); 7869630Seric 7879630Seric /* 7889630Seric ** Print the work list that we have read. 7899630Seric */ 7909630Seric 7919630Seric /* first see if there is anything */ 79210070Seric if (nrequests <= 0) 7939630Seric { 79410070Seric printf("Mail queue is empty\n"); 7959630Seric return; 7969630Seric } 7979630Seric 79810096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 79925687Seric if (nrequests > QUEUESIZE) 80025687Seric printf(", only %d printed", QUEUESIZE); 80124979Seric if (Verbose) 80225032Seric printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 80324979Seric else 80424979Seric printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 8059630Seric for (w = WorkQ; w != NULL; w = w->w_next) 8069630Seric { 8079630Seric struct stat st; 80810070Seric auto time_t submittime = 0; 80910070Seric long dfsize = -1; 81010108Seric char lf[20]; 81110108Seric char message[MAXLINE]; 81224941Seric extern bool shouldqueue(); 8139630Seric 81417468Seric f = fopen(w->w_name, "r"); 81517468Seric if (f == NULL) 81617468Seric { 81717468Seric errno = 0; 81817468Seric continue; 81917468Seric } 8209630Seric printf("%7s", w->w_name + 2); 82123098Seric (void) strcpy(lf, w->w_name); 82210070Seric lf[0] = 'l'; 82310070Seric if (stat(lf, &st) >= 0) 82410070Seric printf("*"); 82524941Seric else if (shouldqueue(w->w_pri)) 82624941Seric printf("X"); 82710070Seric else 82810070Seric printf(" "); 82910070Seric errno = 0; 83017468Seric 83110108Seric message[0] = '\0'; 8329630Seric while (fgets(buf, sizeof buf, f) != NULL) 8339630Seric { 8349630Seric fixcrlf(buf, TRUE); 8359630Seric switch (buf[0]) 8369630Seric { 83710108Seric case 'M': /* error message */ 83823098Seric (void) strcpy(message, &buf[1]); 83910108Seric break; 84010108Seric 8419630Seric case 'S': /* sender name */ 84224979Seric if (Verbose) 84325027Seric printf("%8ld %10ld %.12s %.38s", dfsize, 84425027Seric w->w_pri, ctime(&submittime) + 4, 84524979Seric &buf[1]); 84624979Seric else 84724979Seric printf("%8ld %.16s %.45s", dfsize, 84824979Seric ctime(&submittime), &buf[1]); 84910108Seric if (message[0] != '\0') 85025027Seric printf("\n\t\t (%.60s)", message); 8519630Seric break; 8529630Seric 8539630Seric case 'R': /* recipient name */ 85424979Seric if (Verbose) 85525027Seric printf("\n\t\t\t\t\t %.38s", &buf[1]); 85624979Seric else 85724979Seric printf("\n\t\t\t\t %.45s", &buf[1]); 8589630Seric break; 8599630Seric 8609630Seric case 'T': /* creation time */ 86124941Seric submittime = atol(&buf[1]); 8629630Seric break; 86310070Seric 86410070Seric case 'D': /* data file name */ 86510070Seric if (stat(&buf[1], &st) >= 0) 86610070Seric dfsize = st.st_size; 86710070Seric break; 8689630Seric } 8699630Seric } 87010070Seric if (submittime == (time_t) 0) 87110070Seric printf(" (no control file)"); 8729630Seric printf("\n"); 87323098Seric (void) fclose(f); 8749630Seric } 8759630Seric } 8769630Seric 8775182Seric # endif QUEUE 87817468Seric /* 87917468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 88017468Seric ** 88117468Seric ** Assigns an id code if one does not already exist. 88217468Seric ** This code is very careful to avoid trashing existing files 88317468Seric ** under any circumstances. 88417468Seric ** We first create an nf file that is only used when 88517468Seric ** assigning an id. This file is always empty, so that 88617468Seric ** we can never accidently truncate an lf file. 88717468Seric ** 88817468Seric ** Parameters: 88917468Seric ** e -- envelope to build it in/from. 89017468Seric ** type -- the file type, used as the first character 89117468Seric ** of the file name. 89217468Seric ** 89317468Seric ** Returns: 89417468Seric ** a pointer to the new file name (in a static buffer). 89517468Seric ** 89617468Seric ** Side Effects: 89717468Seric ** Will create the lf and qf files if no id code is 89817468Seric ** already assigned. This will cause the envelope 89917468Seric ** to be modified. 90017468Seric */ 90117468Seric 90217468Seric char * 90317468Seric queuename(e, type) 90417468Seric register ENVELOPE *e; 90517468Seric char type; 90617468Seric { 90717468Seric static char buf[MAXNAME]; 90817468Seric static int pid = -1; 90917468Seric char c1 = 'A'; 91017468Seric char c2 = 'A'; 91117468Seric 91217468Seric if (e->e_id == NULL) 91317468Seric { 91417468Seric char qf[20]; 91517468Seric char nf[20]; 91617468Seric char lf[20]; 91717468Seric 91817468Seric /* find a unique id */ 91917468Seric if (pid != getpid()) 92017468Seric { 92117468Seric /* new process -- start back at "AA" */ 92217468Seric pid = getpid(); 92317468Seric c1 = 'A'; 92417468Seric c2 = 'A' - 1; 92517468Seric } 92617468Seric (void) sprintf(qf, "qfAA%05d", pid); 92723098Seric (void) strcpy(lf, qf); 92817468Seric lf[0] = 'l'; 92923098Seric (void) strcpy(nf, qf); 93017468Seric nf[0] = 'n'; 93117468Seric 93217468Seric while (c1 < '~' || c2 < 'Z') 93317468Seric { 93417468Seric int i; 93517468Seric 93617468Seric if (c2 >= 'Z') 93717468Seric { 93817468Seric c1++; 93917468Seric c2 = 'A' - 1; 94017468Seric } 94117477Seric lf[2] = nf[2] = qf[2] = c1; 94217477Seric lf[3] = nf[3] = qf[3] = ++c2; 94317468Seric # ifdef DEBUG 94417468Seric if (tTd(7, 20)) 94517468Seric printf("queuename: trying \"%s\"\n", nf); 94617468Seric # endif DEBUG 94717468Seric 94817468Seric # ifdef QUEUE 94917468Seric if (access(lf, 0) >= 0 || access(qf, 0) >= 0) 95017468Seric continue; 95117468Seric errno = 0; 95217468Seric i = creat(nf, FileMode); 95317468Seric if (i < 0) 95417468Seric { 95517468Seric (void) unlink(nf); /* kernel bug */ 95617468Seric continue; 95717468Seric } 95817468Seric (void) close(i); 95917468Seric i = link(nf, lf); 96017468Seric (void) unlink(nf); 96117468Seric if (i < 0) 96217468Seric continue; 96317468Seric if (link(lf, qf) >= 0) 96417468Seric break; 96517468Seric (void) unlink(lf); 96617468Seric # else QUEUE 96717982Seric if (close(creat(qf, FileMode)) >= 0) 96817982Seric break; 96917468Seric # endif QUEUE 97017468Seric } 97117468Seric if (c1 >= '~' && c2 >= 'Z') 97217468Seric { 97317468Seric syserr("queuename: Cannot create \"%s\" in \"%s\"", 97417468Seric qf, QueueDir); 97517468Seric exit(EX_OSERR); 97617468Seric } 97717468Seric e->e_id = newstr(&qf[2]); 97817468Seric define('i', e->e_id, e); 97917468Seric # ifdef DEBUG 98017468Seric if (tTd(7, 1)) 98117468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 98217468Seric # ifdef LOG 98317468Seric if (LogLevel > 16) 98417468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 98517468Seric # endif LOG 98617468Seric # endif DEBUG 98717468Seric } 98817468Seric 98917468Seric if (type == '\0') 99017468Seric return (NULL); 99117468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 99217468Seric # ifdef DEBUG 99317468Seric if (tTd(7, 2)) 99417468Seric printf("queuename: %s\n", buf); 99517468Seric # endif DEBUG 99617468Seric return (buf); 99717468Seric } 99817468Seric /* 99917468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 100017468Seric ** 100117468Seric ** Parameters: 100217468Seric ** e -- the envelope to unlock. 100317468Seric ** 100417468Seric ** Returns: 100517468Seric ** none 100617468Seric ** 100717468Seric ** Side Effects: 100817468Seric ** unlocks the queue for `e'. 100917468Seric */ 101017468Seric 101117468Seric unlockqueue(e) 101217468Seric ENVELOPE *e; 101317468Seric { 101417468Seric /* remove the transcript */ 101517468Seric #ifdef DEBUG 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 #endif DEBUG 102217468Seric xunlink(queuename(e, 'x')); 102317468Seric 102417468Seric # ifdef QUEUE 102517468Seric /* last but not least, remove the lock */ 102617468Seric xunlink(queuename(e, 'l')); 102717468Seric # endif QUEUE 102817468Seric } 1029