122708Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362528Sbostic * Copyright (c) 1988, 1993 462528Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822708Sdist 933731Sbostic # include "sendmail.h" 1022708Sdist 1133731Sbostic #ifndef lint 1233731Sbostic #ifdef QUEUE 13*68565Seric static char sccsid[] = "@(#)queue.c 8.73 (Berkeley) 03/21/95 (with queueing)"; 1433731Sbostic #else 15*68565Seric static char sccsid[] = "@(#)queue.c 8.73 (Berkeley) 03/21/95 (without queueing)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 194632Seric # include <errno.h> 2040973Sbostic # include <pwd.h> 2157736Seric # include <dirent.h> 224632Seric 2333731Sbostic # ifdef QUEUE 244632Seric 254632Seric /* 269377Seric ** Work queue. 279377Seric */ 289377Seric 299377Seric struct work 309377Seric { 319377Seric char *w_name; /* name of control file */ 3268481Seric char *w_host; /* name of recipient host */ 3368481Seric bool w_lock; /* is message locked? */ 349377Seric long w_pri; /* priority of message, see below */ 3525013Seric time_t w_ctime; /* creation time of message */ 369377Seric struct work *w_next; /* next in queue */ 379377Seric }; 389377Seric 399377Seric typedef struct work WORK; 409377Seric 419377Seric WORK *WorkQ; /* queue of things to be done */ 4268481Seric 4368481Seric #define QF_VERSION 1 /* version number of this queue format */ 449377Seric /* 454632Seric ** QUEUEUP -- queue a message up for future transmission. 464632Seric ** 474632Seric ** Parameters: 486980Seric ** e -- the envelope to queue up. 496999Seric ** queueall -- if TRUE, queue all addresses, rather than 506999Seric ** just those with the QQUEUEUP flag set. 519377Seric ** announce -- if TRUE, tell when you are queueing up. 524632Seric ** 534632Seric ** Returns: 5451920Seric ** none. 554632Seric ** 564632Seric ** Side Effects: 579377Seric ** The current request are saved in a control file. 5851920Seric ** The queue file is left locked. 594632Seric */ 604632Seric 619377Seric queueup(e, queueall, announce) 626980Seric register ENVELOPE *e; 636999Seric bool queueall; 649377Seric bool announce; 654632Seric { 667812Seric char *qf; 677812Seric register FILE *tfp; 684632Seric register HDR *h; 695007Seric register ADDRESS *q; 7051920Seric int fd; 7151920Seric int i; 7251920Seric bool newid; 7353400Seric register char *p; 7410173Seric MAILER nullmailer; 7565870Seric MCI mcibuf; 7651920Seric char buf[MAXLINE], tf[MAXLINE]; 774632Seric 785037Seric /* 7917477Seric ** Create control file. 805037Seric */ 814632Seric 8264745Seric newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); 8364277Seric 8464277Seric /* if newid, queuename will create a locked qf file in e->lockfp */ 8551920Seric strcpy(tf, queuename(e, 't')); 8651920Seric tfp = e->e_lockfp; 8751920Seric if (tfp == NULL) 8851920Seric newid = FALSE; 8964277Seric 9064277Seric /* if newid, just write the qf file directly (instead of tf file) */ 9164745Seric if (!newid) 9251835Seric { 9351920Seric /* get a locked tf file */ 9464070Seric for (i = 0; i < 128; i++) 9551835Seric { 9651920Seric fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); 9751920Seric if (fd < 0) 9851835Seric { 9964070Seric if (errno != EEXIST) 10064070Seric break; 10164070Seric #ifdef LOG 10264070Seric if (LogLevel > 0 && (i % 32) == 0) 10365090Seric syslog(LOG_ALERT, "queueup: cannot create %s, uid=%d: %s", 10465090Seric tf, geteuid(), errstring(errno)); 10564070Seric #endif 10641636Srick } 10765090Seric else 10865090Seric { 10965090Seric if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) 11065090Seric break; 11164070Seric #ifdef LOG 11265090Seric else if (LogLevel > 0 && (i % 32) == 0) 11365090Seric syslog(LOG_ALERT, "queueup: cannot lock %s: %s", 11465090Seric tf, errstring(errno)); 11564070Seric #endif 11665090Seric close(fd); 11765090Seric } 11858689Seric 11964070Seric if ((i % 32) == 31) 12064070Seric { 12164070Seric /* save the old temp file away */ 12264070Seric (void) rename(tf, queuename(e, 'T')); 12364070Seric } 12464070Seric else 12564070Seric sleep(i % 32); 12641636Srick } 12764724Seric if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL) 12864921Seric { 12964921Seric printopenfds(TRUE); 13065090Seric syserr("!queueup: cannot create queue temp file %s, uid=%d", 13165090Seric tf, geteuid()); 13264921Seric } 13351920Seric } 1344632Seric 1357677Seric if (tTd(40, 1)) 13668481Seric printf("\n>>>>> queueing %s%s queueall=%d >>>>>\n", e->e_id, 13768481Seric newid ? " (new id)" : "", queueall); 13868481Seric if (tTd(40, 32)) 13968481Seric { 14068481Seric printf(" sendq="); 14168481Seric printaddr(e->e_sendqueue, TRUE); 14268481Seric } 14364745Seric if (tTd(40, 9)) 14464745Seric { 14564745Seric printf(" tfp="); 14664745Seric dumpfd(fileno(tfp), TRUE, FALSE); 14764745Seric printf(" lockfp="); 14864745Seric if (e->e_lockfp == NULL) 14964745Seric printf("NULL\n"); 15064745Seric else 15164745Seric dumpfd(fileno(e->e_lockfp), TRUE, FALSE); 15264745Seric } 1534632Seric 1544632Seric /* 1556980Seric ** If there is no data file yet, create one. 1566980Seric */ 1576980Seric 15868564Seric if (!bitset(EF_HAS_DF, e->e_flags)) 1596980Seric { 1606980Seric register FILE *dfp; 16168564Seric char dfname[20]; 16268481Seric struct stat stbuf; 1639389Seric extern putbody(); 1646980Seric 16568564Seric strcpy(dfname, queuename(e, 'd')); 16668564Seric fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); 16764724Seric if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL) 16865090Seric syserr("!queueup: cannot create data temp file %s, uid=%d", 16968564Seric dfname, geteuid()); 17068481Seric if (fstat(fd, &stbuf) < 0) 17168481Seric e->e_dfino = -1; 17268481Seric else 17368481Seric { 17468481Seric e->e_dfdev = stbuf.st_dev; 17568481Seric e->e_dfino = stbuf.st_ino; 17668481Seric } 17768564Seric e->e_flags |= EF_HAS_DF; 17865870Seric bzero(&mcibuf, sizeof mcibuf); 17965870Seric mcibuf.mci_out = dfp; 18065870Seric mcibuf.mci_mailer = FileMailer; 18168228Seric (*e->e_putbody)(&mcibuf, e, NULL); 18258680Seric (void) xfclose(dfp, "queueup dfp", e->e_id); 1839389Seric e->e_putbody = putbody; 1846980Seric } 1856980Seric 1866980Seric /* 1874632Seric ** Output future work requests. 18825687Seric ** Priority and creation time should be first, since 18925687Seric ** they are required by orderq. 1904632Seric */ 1914632Seric 19268481Seric /* output queue version number (must be first!) */ 19368481Seric fprintf(tfp, "V%d\n", QF_VERSION); 19468481Seric 1959377Seric /* output message priority */ 1969377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 1979377Seric 1989630Seric /* output creation time */ 1999630Seric fprintf(tfp, "T%ld\n", e->e_ctime); 2009630Seric 20168481Seric /* output last delivery time */ 20268481Seric fprintf(tfp, "K%ld\n", e->e_dtime); 20368481Seric 20468481Seric /* output number of delivery attempts */ 20568481Seric fprintf(tfp, "N%d\n", e->e_ntries); 20668481Seric 20768481Seric /* output inode number of data file */ 20868481Seric /* XXX should probably include device major/minor too */ 20968481Seric if (e->e_dfino != -1) 21068481Seric fprintf(tfp, "I%d/%d/%ld\n", 21168481Seric major(e->e_dfdev), minor(e->e_dfdev), e->e_dfino); 21268481Seric 21368564Seric /* output body type */ 21459093Seric if (e->e_bodytype != NULL) 21559093Seric fprintf(tfp, "B%s\n", e->e_bodytype); 2164632Seric 21710108Seric /* message from envelope, if it exists */ 21810108Seric if (e->e_message != NULL) 21968478Seric fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE)); 22010108Seric 22158737Seric /* send various flag bits through */ 22258737Seric p = buf; 22358737Seric if (bitset(EF_WARNING, e->e_flags)) 22458737Seric *p++ = 'w'; 22558737Seric if (bitset(EF_RESPONSE, e->e_flags)) 22658737Seric *p++ = 'r'; 22768481Seric if (bitset(EF_HAS8BIT, e->e_flags)) 22868481Seric *p++ = '8'; 22958737Seric *p++ = '\0'; 23058737Seric if (buf[0] != '\0') 23158737Seric fprintf(tfp, "F%s\n", buf); 23258737Seric 23358957Seric /* $r and $s and $_ macro values */ 23453400Seric if ((p = macvalue('r', e)) != NULL) 23568478Seric fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE)); 23653400Seric if ((p = macvalue('s', e)) != NULL) 23768478Seric fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE)); 23858957Seric if ((p = macvalue('_', e)) != NULL) 23968478Seric fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE)); 24053400Seric 2414632Seric /* output name of sender */ 24268481Seric if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 24368481Seric p = e->e_sender; 24468481Seric else 24568481Seric p = e->e_from.q_paddr; 24668481Seric fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE)); 2474632Seric 24868481Seric /* output ESMTP-supplied "original" information */ 24968481Seric if (e->e_envid != NULL) 25068481Seric fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE)); 25168481Seric 25268558Seric /* output list of recipient addresses */ 25359670Seric printctladdr(NULL, NULL); 2546980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 2554632Seric { 25658250Seric if (bitset(QQUEUEUP, q->q_flags) || 25758680Seric (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))) 2588245Seric { 25959113Seric printctladdr(q, tfp); 26068481Seric if (q->q_orcpt != NULL) 26168481Seric fprintf(tfp, "Q%s\n", 26268481Seric denlstring(q->q_orcpt, TRUE, FALSE)); 26368481Seric putc('R', tfp); 26468481Seric if (bitset(QPRIMARY, q->q_flags)) 26568481Seric putc('P', tfp); 26668481Seric if (bitset(QPINGONSUCCESS, q->q_flags)) 26768481Seric putc('S', tfp); 26868481Seric if (bitset(QPINGONFAILURE, q->q_flags)) 26968481Seric putc('F', tfp); 27068481Seric if (bitset(QPINGONDELAY, q->q_flags)) 27168481Seric putc('D', tfp); 27268481Seric putc(':', tfp); 27368481Seric fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); 2749377Seric if (announce) 2759377Seric { 2769377Seric e->e_to = q->q_paddr; 27758151Seric message("queued"); 27858020Seric if (LogLevel > 8) 27968481Seric logdelivery(NULL, NULL, "queued", 28068481Seric NULL, (time_t) 0, e); 2819377Seric e->e_to = NULL; 2829377Seric } 2839387Seric if (tTd(40, 1)) 2849387Seric { 2859387Seric printf("queueing "); 2869387Seric printaddr(q, FALSE); 2879387Seric } 2888245Seric } 2894632Seric } 2904632Seric 2919377Seric /* 2929377Seric ** Output headers for this message. 2939377Seric ** Expand macros completely here. Queue run will deal with 2949377Seric ** everything as absolute headers. 2959377Seric ** All headers that must be relative to the recipient 2969377Seric ** can be cracked later. 29710173Seric ** We set up a "null mailer" -- i.e., a mailer that will have 29810173Seric ** no effect on the addresses as they are output. 2999377Seric */ 3009377Seric 30110686Seric bzero((char *) &nullmailer, sizeof nullmailer); 30258020Seric nullmailer.m_re_rwset = nullmailer.m_rh_rwset = 30365584Seric nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; 30410349Seric nullmailer.m_eol = "\n"; 30565870Seric bzero(&mcibuf, sizeof mcibuf); 30665870Seric mcibuf.mci_mailer = &nullmailer; 30765870Seric mcibuf.mci_out = tfp; 30810173Seric 30958050Seric define('g', "\201f", e); 3106980Seric for (h = e->e_header; h != NULL; h = h->h_link) 3114632Seric { 31210686Seric extern bool bitzerop(); 31310686Seric 31412015Seric /* don't output null headers */ 3154632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 3164632Seric continue; 31712015Seric 31812015Seric /* don't output resent headers on non-resent messages */ 31912015Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 32012015Seric continue; 32112015Seric 32265267Seric /* expand macros; if null, don't output header at all */ 32365267Seric if (bitset(H_DEFAULT, h->h_flags)) 32465267Seric { 32568529Seric (void) expand(h->h_value, buf, sizeof buf, e); 32665267Seric if (buf[0] == '\0') 32765267Seric continue; 32865267Seric } 32965267Seric 33012015Seric /* output this header */ 3317812Seric fprintf(tfp, "H"); 33212015Seric 33312015Seric /* if conditional, output the set of conditions */ 33410686Seric if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 33510686Seric { 33610686Seric int j; 33710686Seric 33823098Seric (void) putc('?', tfp); 33910686Seric for (j = '\0'; j <= '\177'; j++) 34010686Seric if (bitnset(j, h->h_mflags)) 34123098Seric (void) putc(j, tfp); 34223098Seric (void) putc('?', tfp); 34310686Seric } 34412015Seric 34512015Seric /* output the header: expand macros, convert addresses */ 3467763Seric if (bitset(H_DEFAULT, h->h_flags)) 3477763Seric { 34865267Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 3497763Seric } 3508245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 3519348Seric { 35258737Seric bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 35363753Seric FILE *savetrace = TrafficLogFile; 35458737Seric 35563753Seric TrafficLogFile = NULL; 35663753Seric 35758737Seric if (bitset(H_FROM, h->h_flags)) 35858737Seric oldstyle = FALSE; 35958737Seric 36065870Seric commaize(h, h->h_value, oldstyle, &mcibuf, e); 36163753Seric 36263753Seric TrafficLogFile = savetrace; 3639348Seric } 3647763Seric else 3658245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 3664632Seric } 3674632Seric 3684632Seric /* 3694632Seric ** Clean up. 3704632Seric */ 3714632Seric 37264762Seric if (fflush(tfp) < 0 || fsync(fileno(tfp)) < 0 || ferror(tfp)) 37358732Seric { 37458732Seric if (newid) 37558732Seric syserr("!552 Error writing control file %s", tf); 37658732Seric else 37758732Seric syserr("!452 Error writing control file %s", tf); 37858732Seric } 37958732Seric 38051920Seric if (!newid) 38151920Seric { 38264277Seric /* rename (locked) tf to be (locked) qf */ 38351920Seric qf = queuename(e, 'q'); 38451920Seric if (rename(tf, qf) < 0) 38568564Seric syserr("cannot rename(%s, %s), uid=%d", 38668564Seric tf, qf, geteuid()); 38764277Seric 38864277Seric /* close and unlock old (locked) qf */ 38951920Seric if (e->e_lockfp != NULL) 39058680Seric (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); 39151920Seric e->e_lockfp = tfp; 39251920Seric } 39351920Seric else 39451920Seric qf = tf; 39541636Srick errno = 0; 39664745Seric e->e_flags |= EF_INQUEUE; 3977391Seric 3987677Seric # ifdef LOG 3997677Seric /* save log info */ 40058020Seric if (LogLevel > 79) 40168564Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s", e->e_id, qf); 40256795Seric # endif /* LOG */ 40364307Seric 40464307Seric if (tTd(40, 1)) 40564307Seric printf("<<<<< done queueing %s <<<<<\n\n", e->e_id); 40651920Seric return; 4074632Seric } 40854974Seric 40954974Seric printctladdr(a, tfp) 41059113Seric register ADDRESS *a; 41154974Seric FILE *tfp; 41254974Seric { 41359113Seric char *uname; 41459113Seric register struct passwd *pw; 41559113Seric register ADDRESS *q; 41659113Seric uid_t uid; 41759113Seric static ADDRESS *lastctladdr; 41859113Seric static uid_t lastuid; 41954974Seric 42059113Seric /* initialization */ 42163850Seric if (a == NULL || a->q_alias == NULL || tfp == NULL) 42254974Seric { 42359670Seric if (lastctladdr != NULL && tfp != NULL) 42459113Seric fprintf(tfp, "C\n"); 42559113Seric lastctladdr = NULL; 42659113Seric lastuid = 0; 42754974Seric return; 42854974Seric } 42959113Seric 43059113Seric /* find the active uid */ 43159113Seric q = getctladdr(a); 43259113Seric if (q == NULL) 43359113Seric uid = 0; 43454974Seric else 43559113Seric uid = q->q_uid; 43663850Seric a = a->q_alias; 43759113Seric 43859113Seric /* check to see if this is the same as last time */ 43959113Seric if (lastctladdr != NULL && uid == lastuid && 44059113Seric strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) 44159113Seric return; 44259113Seric lastuid = uid; 44359113Seric lastctladdr = a; 44459113Seric 44559113Seric if (uid == 0 || (pw = getpwuid(uid)) == NULL) 44659270Seric uname = ""; 44759113Seric else 44859113Seric uname = pw->pw_name; 44959113Seric 45068478Seric fprintf(tfp, "C%s:%s\n", uname, denlstring(a->q_paddr, TRUE, FALSE)); 45154974Seric } 4524632Seric /* 4534632Seric ** RUNQUEUE -- run the jobs in the queue. 4544632Seric ** 4554632Seric ** Gets the stuff out of the queue in some presumably logical 4564632Seric ** order and processes them. 4574632Seric ** 4584632Seric ** Parameters: 45924941Seric ** forkflag -- TRUE if the queue scanning should be done in 46024941Seric ** a child process. We double-fork so it is not our 46124941Seric ** child and we don't have to clean up after it. 4624632Seric ** 4634632Seric ** Returns: 4644632Seric ** none. 4654632Seric ** 4664632Seric ** Side Effects: 4674632Seric ** runs things in the mail queue. 4684632Seric */ 4694632Seric 47055360Seric ENVELOPE QueueEnvelope; /* the queue run envelope */ 47155360Seric 47268481Seric void 47355360Seric runqueue(forkflag) 4744639Seric bool forkflag; 4754632Seric { 47655360Seric register ENVELOPE *e; 47768481Seric int njobs; 47868481Seric int sequenceno = 0; 47955360Seric extern ENVELOPE BlankEnvelope; 48024953Seric 4817466Seric /* 48224953Seric ** If no work will ever be selected, don't even bother reading 48324953Seric ** the queue. 48424953Seric */ 48524953Seric 48651920Seric CurrentLA = getla(); /* get load average */ 48740934Srick 48858132Seric if (shouldqueue(0L, curtime())) 48924953Seric { 49024953Seric if (Verbose) 49124953Seric printf("Skipping queue run -- load average too high\n"); 49258107Seric if (forkflag && QueueIntvl != 0) 49358107Seric (void) setevent(QueueIntvl, runqueue, TRUE); 49455360Seric return; 49524953Seric } 49624953Seric 49724953Seric /* 4987466Seric ** See if we want to go off and do other useful work. 4997466Seric */ 5004639Seric 5014639Seric if (forkflag) 5024639Seric { 5037943Seric int pid; 50465223Seric #ifdef SIGCHLD 50565223Seric extern void reapchild(); 5067943Seric 50765223Seric (void) setsignal(SIGCHLD, reapchild); 50865223Seric #endif 50965223Seric 5107943Seric pid = dofork(); 5117943Seric if (pid != 0) 5124639Seric { 5137943Seric /* parent -- pick up intermediate zombie */ 51425184Seric #ifndef SIGCHLD 5159377Seric (void) waitfor(pid); 51656795Seric #endif /* SIGCHLD */ 5177690Seric if (QueueIntvl != 0) 5189348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 5194639Seric return; 5204639Seric } 5217943Seric /* child -- double fork */ 52225184Seric #ifndef SIGCHLD 5237943Seric if (fork() != 0) 5247943Seric exit(EX_OK); 52556795Seric #else /* SIGCHLD */ 52664035Seric (void) setsignal(SIGCHLD, SIG_DFL); 52756795Seric #endif /* SIGCHLD */ 5284639Seric } 52924941Seric 53040934Srick setproctitle("running queue: %s", QueueDir); 53124941Seric 5327876Seric # ifdef LOG 53358020Seric if (LogLevel > 69) 53455360Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d, forkflag=%d", 53555360Seric QueueDir, getpid(), forkflag); 53656795Seric # endif /* LOG */ 5374639Seric 5387466Seric /* 53910205Seric ** Release any resources used by the daemon code. 54010205Seric */ 54110205Seric 54210205Seric # ifdef DAEMON 54310205Seric clrdaemon(); 54456795Seric # endif /* DAEMON */ 54510205Seric 54664658Seric /* force it to run expensive jobs */ 54764658Seric NoConnect = FALSE; 54864658Seric 54910205Seric /* 55055360Seric ** Create ourselves an envelope 55155360Seric */ 55255360Seric 55355360Seric CurEnv = &QueueEnvelope; 55458179Seric e = newenvelope(&QueueEnvelope, CurEnv); 55555360Seric e->e_flags = BlankEnvelope.e_flags; 55655360Seric 55755360Seric /* 55827175Seric ** Make sure the alias database is open. 55927175Seric */ 56027175Seric 56160537Seric initmaps(FALSE, e); 56227175Seric 56327175Seric /* 5647466Seric ** Start making passes through the queue. 5657466Seric ** First, read and sort the entire queue. 5667466Seric ** Then, process the work in that order. 5677466Seric ** But if you take too long, start over. 5687466Seric */ 5697466Seric 5707943Seric /* order the existing work requests */ 57168481Seric njobs = orderq(FALSE); 5727690Seric 5737943Seric /* process them once at a time */ 5747943Seric while (WorkQ != NULL) 5754639Seric { 5767943Seric WORK *w = WorkQ; 5777881Seric 5787943Seric WorkQ = WorkQ->w_next; 57958884Seric 58058884Seric /* 58158884Seric ** Ignore jobs that are too expensive for the moment. 58258884Seric */ 58358884Seric 58468481Seric sequenceno++; 58558884Seric if (shouldqueue(w->w_pri, w->w_ctime)) 58658884Seric { 58758884Seric if (Verbose) 58868481Seric printf("\nSkipping %s (sequence %d of %d)\n", 58968481Seric w->w_name + 2, sequenceno, njobs); 59058884Seric } 59158930Seric else 59258930Seric { 59364296Seric pid_t pid; 59464296Seric extern pid_t dowork(); 59564296Seric 59668481Seric if (Verbose) 59768481Seric printf("\nRunning %s (sequence %d of %d)\n", 59868481Seric w->w_name + 2, sequenceno, njobs); 59964296Seric pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); 60064296Seric errno = 0; 60166316Seric if (pid != 0) 60266316Seric (void) waitfor(pid); 60358930Seric } 6047943Seric free(w->w_name); 60568481Seric if (w->w_host) 60668481Seric free(w->w_host); 6077943Seric free((char *) w); 6084639Seric } 60929866Seric 61029866Seric /* exit without the usual cleanup */ 61155467Seric e->e_id = NULL; 61255467Seric finis(); 6134634Seric } 6144634Seric /* 6154632Seric ** ORDERQ -- order the work queue. 6164632Seric ** 6174632Seric ** Parameters: 61824941Seric ** doall -- if set, include everything in the queue (even 61924941Seric ** the jobs that cannot be run because the load 62024941Seric ** average is too high). Otherwise, exclude those 62124941Seric ** jobs. 6224632Seric ** 6234632Seric ** Returns: 62410121Seric ** The number of request in the queue (not necessarily 62510121Seric ** the number of requests in WorkQ however). 6264632Seric ** 6274632Seric ** Side Effects: 6284632Seric ** Sets WorkQ to the queue of available work, in order. 6294632Seric */ 6304632Seric 63125687Seric # define NEED_P 001 63225687Seric # define NEED_T 002 63358318Seric # define NEED_R 004 63458318Seric # define NEED_S 010 6354632Seric 63624941Seric orderq(doall) 63724941Seric bool doall; 6384632Seric { 63960219Seric register struct dirent *d; 6404632Seric register WORK *w; 6416625Sglickman DIR *f; 6424632Seric register int i; 64325687Seric WORK wlist[QUEUESIZE+1]; 64410070Seric int wn = -1; 64568481Seric int wc; 6464632Seric 64758318Seric if (tTd(41, 1)) 64858318Seric { 64958318Seric printf("orderq:\n"); 65058318Seric if (QueueLimitId != NULL) 65158318Seric printf("\tQueueLimitId = %s\n", QueueLimitId); 65258318Seric if (QueueLimitSender != NULL) 65358318Seric printf("\tQueueLimitSender = %s\n", QueueLimitSender); 65458318Seric if (QueueLimitRecipient != NULL) 65558318Seric printf("\tQueueLimitRecipient = %s\n", QueueLimitRecipient); 65658318Seric } 65758318Seric 6584632Seric /* clear out old WorkQ */ 6594632Seric for (w = WorkQ; w != NULL; ) 6604632Seric { 6614632Seric register WORK *nw = w->w_next; 6624632Seric 6634632Seric WorkQ = nw; 6644632Seric free(w->w_name); 66568481Seric if (w->w_host) 66668481Seric free(w->w_host); 6674632Seric free((char *) w); 6684632Seric w = nw; 6694632Seric } 6704632Seric 6714632Seric /* open the queue directory */ 6728148Seric f = opendir("."); 6734632Seric if (f == NULL) 6744632Seric { 6758148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 67610070Seric return (0); 6774632Seric } 6784632Seric 6794632Seric /* 6804632Seric ** Read the work directory. 6814632Seric */ 6824632Seric 68310070Seric while ((d = readdir(f)) != NULL) 6844632Seric { 6859377Seric FILE *cf; 68664492Seric register char *p; 68768528Seric char lbuf[MAXNAME + 1]; 68858318Seric extern bool strcontainedin(); 6894632Seric 6904632Seric /* is this an interesting entry? */ 6917812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 6924632Seric continue; 6934632Seric 69458318Seric if (QueueLimitId != NULL && 69558318Seric !strcontainedin(QueueLimitId, d->d_name)) 69658318Seric continue; 69758318Seric 69868563Seric #ifdef PICKY_QF_NAME_CHECK 69958722Seric /* 70058722Seric ** Check queue name for plausibility. This handles 70158722Seric ** both old and new type ids. 70258722Seric */ 70358722Seric 70464492Seric p = d->d_name + 2; 70564492Seric if (isupper(p[0]) && isupper(p[2])) 70664492Seric p += 3; 70764492Seric else if (isupper(p[1])) 70864492Seric p += 2; 70964492Seric else 71064492Seric p = d->d_name; 71164492Seric for (i = 0; isdigit(*p); p++) 71264492Seric i++; 71364492Seric if (i < 5 || *p != '\0') 71458020Seric { 71558020Seric if (Verbose) 71658020Seric printf("orderq: bogus qf name %s\n", d->d_name); 71768563Seric # ifdef LOG 71868563Seric if (LogLevel > 0) 71968563Seric syslog(LOG_ALERT, "orderq: bogus qf name %s", 72058020Seric d->d_name); 72168563Seric # endif 72268528Seric if (strlen(d->d_name) > MAXNAME) 72368528Seric d->d_name[MAXNAME] = '\0'; 72458020Seric strcpy(lbuf, d->d_name); 72558020Seric lbuf[0] = 'Q'; 72658020Seric (void) rename(d->d_name, lbuf); 72758020Seric continue; 72858020Seric } 72968563Seric #endif 73058020Seric 73168563Seric /* open control file (if not too many files) */ 73225687Seric if (++wn >= QUEUESIZE) 73310070Seric continue; 73458318Seric 7358148Seric cf = fopen(d->d_name, "r"); 7364632Seric if (cf == NULL) 7374632Seric { 7387055Seric /* this may be some random person sending hir msgs */ 7397055Seric /* syserr("orderq: cannot open %s", cbuf); */ 74010090Seric if (tTd(41, 2)) 74110090Seric printf("orderq: cannot open %s (%d)\n", 74210090Seric d->d_name, errno); 7437055Seric errno = 0; 74410090Seric wn--; 7454632Seric continue; 7464632Seric } 74725687Seric w = &wlist[wn]; 74825687Seric w->w_name = newstr(d->d_name); 74968481Seric w->w_host = NULL; 75068481Seric w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); 7514632Seric 75225027Seric /* make sure jobs in creation don't clog queue */ 75325687Seric w->w_pri = 0x7fffffff; 75425687Seric w->w_ctime = 0; 75525027Seric 7564632Seric /* extract useful information */ 75725687Seric i = NEED_P | NEED_T; 75858318Seric if (QueueLimitSender != NULL) 75958318Seric i |= NEED_S; 76068481Seric if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL) 76158318Seric i |= NEED_R; 76225687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 7634632Seric { 76424954Seric extern long atol(); 76558318Seric extern bool strcontainedin(); 76624954Seric 76724941Seric switch (lbuf[0]) 7684632Seric { 76924941Seric case 'P': 77025687Seric w->w_pri = atol(&lbuf[1]); 77125687Seric i &= ~NEED_P; 7724632Seric break; 77325013Seric 77425013Seric case 'T': 77525687Seric w->w_ctime = atol(&lbuf[1]); 77625687Seric i &= ~NEED_T; 77725013Seric break; 77858318Seric 77958318Seric case 'R': 78068481Seric if (w->w_host == NULL && 78168481Seric (p = strrchr(&lbuf[1], '@')) != NULL) 78268481Seric w->w_host = newstr(&p[1]); 78368481Seric if (QueueLimitRecipient == NULL || 78458318Seric strcontainedin(QueueLimitRecipient, &lbuf[1])) 78558318Seric i &= ~NEED_R; 78658318Seric break; 78758318Seric 78858318Seric case 'S': 78958318Seric if (QueueLimitSender != NULL && 79058318Seric strcontainedin(QueueLimitSender, &lbuf[1])) 79158318Seric i &= ~NEED_S; 79258318Seric break; 7934632Seric } 7944632Seric } 7954632Seric (void) fclose(cf); 79624953Seric 79758318Seric if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || 79858318Seric bitset(NEED_R|NEED_S, i)) 79924953Seric { 80024953Seric /* don't even bother sorting this job in */ 80168481Seric free(w->w_name); 80268481Seric if (w->w_host) 80368481Seric free(w->w_host); 80424953Seric wn--; 80524953Seric } 8064632Seric } 8076625Sglickman (void) closedir(f); 80810090Seric wn++; 8094632Seric 81068481Seric wc = min(wn, QUEUESIZE); 8114632Seric 81268481Seric if (QueueSortOrder == QS_BYHOST) 81368481Seric { 81468481Seric extern workcmpf1(); 81568481Seric extern workcmpf2(); 81667612Seric 81768481Seric /* 81868481Seric ** Sort the work directory for the first time, 81968481Seric ** based on host name, lock status, and priority. 82068481Seric */ 82168481Seric 82268481Seric qsort((char *) wlist, wc, sizeof *wlist, workcmpf1); 82368481Seric 82468481Seric /* 82568481Seric ** If one message to host is locked, "lock" all messages 82668481Seric ** to that host. 82768481Seric */ 82868481Seric 82968481Seric i = 0; 83068481Seric while (i < wc) 83168481Seric { 83268481Seric if (!wlist[i].w_lock) 83368481Seric { 83468481Seric i++; 83568481Seric continue; 83668481Seric } 83768481Seric w = &wlist[i]; 83868481Seric while (++i < wc) 83968481Seric { 84068481Seric if (wlist[i].w_host == NULL && 84168481Seric w->w_host == NULL) 84268481Seric wlist[i].w_lock = TRUE; 84368481Seric else if (wlist[i].w_host != NULL && 84468481Seric w->w_host != NULL && 84568481Seric strcmp(wlist[i].w_host, w->w_host) == 0) 84668481Seric wlist[i].w_lock = TRUE; 84768481Seric else 84868481Seric break; 84968481Seric } 85068481Seric } 85168481Seric 85268481Seric /* 85368481Seric ** Sort the work directory for the second time, 85468481Seric ** based on lock status, host name, and priority. 85568481Seric */ 85668481Seric 85768481Seric qsort((char *) wlist, wc, sizeof *wlist, workcmpf2); 85868481Seric } 85968481Seric else 86068481Seric { 86168481Seric extern workcmpf0(); 86268481Seric 86368481Seric /* 86468481Seric ** Simple sort based on queue priority only. 86568481Seric */ 86668481Seric 86768481Seric qsort((char *) wlist, wc, sizeof *wlist, workcmpf0); 86868481Seric } 86968481Seric 8704632Seric /* 8714632Seric ** Convert the work list into canonical form. 8729377Seric ** Should be turning it into a list of envelopes here perhaps. 8734632Seric */ 8744632Seric 87524981Seric WorkQ = NULL; 87668481Seric for (i = wc; --i >= 0; ) 8774632Seric { 8784632Seric w = (WORK *) xalloc(sizeof *w); 8794632Seric w->w_name = wlist[i].w_name; 88068481Seric w->w_host = wlist[i].w_host; 88168481Seric w->w_lock = wlist[i].w_lock; 8824632Seric w->w_pri = wlist[i].w_pri; 88325013Seric w->w_ctime = wlist[i].w_ctime; 88424981Seric w->w_next = WorkQ; 88524981Seric WorkQ = w; 8864632Seric } 8874632Seric 8887677Seric if (tTd(40, 1)) 8894632Seric { 8904632Seric for (w = WorkQ; w != NULL; w = w->w_next) 8915037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 8924632Seric } 89310070Seric 89410090Seric return (wn); 8954632Seric } 8964632Seric /* 89768481Seric ** WORKCMPF0 -- simple priority-only compare function. 8984632Seric ** 8994632Seric ** Parameters: 9004632Seric ** a -- the first argument. 9014632Seric ** b -- the second argument. 9024632Seric ** 9034632Seric ** Returns: 90424981Seric ** -1 if a < b 90524981Seric ** 0 if a == b 90624981Seric ** +1 if a > b 9074632Seric ** 9084632Seric ** Side Effects: 9094632Seric ** none. 9104632Seric */ 9114632Seric 91268481Seric workcmpf0(a, b) 9135037Seric register WORK *a; 9145037Seric register WORK *b; 9154632Seric { 91657438Seric long pa = a->w_pri; 91757438Seric long pb = b->w_pri; 91824941Seric 91924941Seric if (pa == pb) 92068481Seric return 0; 92124941Seric else if (pa > pb) 92268481Seric return 1; 92324981Seric else 92468481Seric return -1; 9254632Seric } 9264632Seric /* 92768481Seric ** WORKCMPF1 -- first compare function for ordering work based on host name. 92868481Seric ** 92968481Seric ** Sorts on host name, lock status, and priority in that order. 93068481Seric ** 93168481Seric ** Parameters: 93268481Seric ** a -- the first argument. 93368481Seric ** b -- the second argument. 93468481Seric ** 93568481Seric ** Returns: 93668481Seric ** <0 if a < b 93768481Seric ** 0 if a == b 93868481Seric ** >0 if a > b 93968481Seric ** 94068481Seric ** Side Effects: 94168481Seric ** none. 94268481Seric */ 94368481Seric 94468481Seric workcmpf1(a, b) 94568481Seric register WORK *a; 94668481Seric register WORK *b; 94768481Seric { 94868481Seric int i; 94968481Seric 95068481Seric /* host name */ 95168481Seric if (a->w_host != NULL && b->w_host == NULL) 95268481Seric return 1; 95368481Seric else if (a->w_host == NULL && b->w_host != NULL) 95468481Seric return -1; 95568481Seric if (a->w_host != NULL && b->w_host != NULL && 95668481Seric (i = strcmp(a->w_host, b->w_host))) 95768481Seric return i; 95868481Seric 95968481Seric /* lock status */ 96068481Seric if (a->w_lock != b->w_lock) 96168481Seric return b->w_lock - a->w_lock; 96268481Seric 96368481Seric /* job priority */ 96468481Seric return a->w_pri - b->w_pri; 96568481Seric } 96668481Seric /* 96768481Seric ** WORKCMPF2 -- second compare function for ordering work based on host name. 96868481Seric ** 96968481Seric ** Sorts on lock status, host name, and priority in that order. 97068481Seric ** 97168481Seric ** Parameters: 97268481Seric ** a -- the first argument. 97368481Seric ** b -- the second argument. 97468481Seric ** 97568481Seric ** Returns: 97668481Seric ** <0 if a < b 97768481Seric ** 0 if a == b 97868481Seric ** >0 if a > b 97968481Seric ** 98068481Seric ** Side Effects: 98168481Seric ** none. 98268481Seric */ 98368481Seric 98468481Seric workcmpf2(a, b) 98568481Seric register WORK *a; 98668481Seric register WORK *b; 98768481Seric { 98868481Seric int i; 98968481Seric 99068481Seric /* lock status */ 99168481Seric if (a->w_lock != b->w_lock) 99268481Seric return a->w_lock - b->w_lock; 99368481Seric 99468481Seric /* host name */ 99568481Seric if (a->w_host != NULL && b->w_host == NULL) 99668481Seric return 1; 99768481Seric else if (a->w_host == NULL && b->w_host != NULL) 99868481Seric return -1; 99968481Seric if (a->w_host != NULL && b->w_host != NULL && 100068481Seric (i = strcmp(a->w_host, b->w_host))) 100168481Seric return i; 100268481Seric 100368481Seric /* job priority */ 100468481Seric return a->w_pri - b->w_pri; 100568481Seric } 100668481Seric /* 10074632Seric ** DOWORK -- do a work request. 10084632Seric ** 10094632Seric ** Parameters: 101058884Seric ** id -- the ID of the job to run. 101158884Seric ** forkflag -- if set, run this in background. 101258924Seric ** requeueflag -- if set, reinstantiate the queue quickly. 101358924Seric ** This is used when expanding aliases in the queue. 101461094Seric ** If forkflag is also set, it doesn't wait for the 101561094Seric ** child. 101658884Seric ** e - the envelope in which to run it. 10174632Seric ** 10184632Seric ** Returns: 101964296Seric ** process id of process that is running the queue job. 10204632Seric ** 10214632Seric ** Side Effects: 10224632Seric ** The work request is satisfied if possible. 10234632Seric */ 10244632Seric 102564296Seric pid_t 102658924Seric dowork(id, forkflag, requeueflag, e) 102758884Seric char *id; 102858884Seric bool forkflag; 102958924Seric bool requeueflag; 103055012Seric register ENVELOPE *e; 10314632Seric { 103264296Seric register pid_t pid; 103351920Seric extern bool readqf(); 10344632Seric 10357677Seric if (tTd(40, 1)) 103658884Seric printf("dowork(%s)\n", id); 10374632Seric 10384632Seric /* 103924941Seric ** Fork for work. 104024941Seric */ 104124941Seric 104258884Seric if (forkflag) 104324941Seric { 104464296Seric pid = fork(); 104564296Seric if (pid < 0) 104624941Seric { 104724941Seric syserr("dowork: cannot fork"); 104864296Seric return 0; 104924941Seric } 105064839Seric else if (pid > 0) 105164839Seric { 105264839Seric /* parent -- clean out connection cache */ 105364839Seric mci_flush(FALSE, NULL); 105464839Seric } 105524941Seric } 105624941Seric else 105724941Seric { 105864296Seric pid = 0; 105924941Seric } 106024941Seric 106164296Seric if (pid == 0) 10624632Seric { 10634632Seric /* 10644632Seric ** CHILD 10658148Seric ** Lock the control file to avoid duplicate deliveries. 10668148Seric ** Then run the file as though we had just read it. 10677350Seric ** We save an idea of the temporary name so we 10687350Seric ** can recover on interrupt. 10694632Seric */ 10704632Seric 10717763Seric /* set basic modes, etc. */ 10727356Seric (void) alarm(0); 107355012Seric clearenvelope(e, FALSE); 107464554Seric e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 107558734Seric e->e_errormode = EM_MAIL; 107658884Seric e->e_id = id; 107764765Seric GrabTo = UseErrorsTo = FALSE; 107866316Seric ExitStat = EX_OK; 107963846Seric if (forkflag) 108064554Seric { 108164296Seric disconnect(1, e); 108264554Seric OpMode = MD_DELIVER; 108364554Seric } 10847876Seric # ifdef LOG 108558020Seric if (LogLevel > 76) 108655012Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id, 10877881Seric getpid()); 108856795Seric # endif /* LOG */ 10897763Seric 10907763Seric /* don't use the headers from sendmail.cf... */ 109155012Seric e->e_header = NULL; 10927763Seric 109351920Seric /* read the queue control file -- return if locked */ 109465200Seric if (!readqf(e)) 10956980Seric { 109658884Seric if (tTd(40, 4)) 109758884Seric printf("readqf(%s) failed\n", e->e_id); 109858884Seric if (forkflag) 109924941Seric exit(EX_OK); 110024941Seric else 110166316Seric return 0; 11026980Seric } 11036980Seric 110455012Seric e->e_flags |= EF_INQUEUE; 11056980Seric 110668481Seric /* if this has been tried recently, let it be */ 110768481Seric if (e->e_ntries > 0 && (curtime() - e->e_dtime) < MinQueueAge) 110868481Seric { 110968481Seric char *howlong = pintvl(curtime() - e->e_dtime, TRUE); 111058924Seric 111168481Seric e->e_flags |= EF_KEEPQUEUE; 111268481Seric if (Verbose || tTd(40, 8)) 111368481Seric printf("%s: too young (%s)\n", 111468481Seric e->e_id, howlong); 111568481Seric #ifdef LOG 111668481Seric if (LogLevel > 19) 111768481Seric syslog(LOG_DEBUG, "%s: too young (%s)", 111868481Seric e->e_id, howlong); 111968481Seric #endif 112068481Seric } 112168481Seric else 112268481Seric { 112368481Seric eatheader(e, requeueflag); 11246980Seric 112568481Seric if (requeueflag) 112668481Seric queueup(e, TRUE, FALSE); 112768481Seric 112868481Seric /* do the delivery */ 112968481Seric sendall(e, SM_DELIVER); 113068481Seric } 113168481Seric 11326980Seric /* finish up and exit */ 113358884Seric if (forkflag) 113424941Seric finis(); 113524941Seric else 113655012Seric dropenvelope(e); 11374632Seric } 113864296Seric e->e_id = NULL; 113964296Seric return pid; 11404632Seric } 11414632Seric /* 11424632Seric ** READQF -- read queue file and set up environment. 11434632Seric ** 11444632Seric ** Parameters: 11459377Seric ** e -- the envelope of the job to run. 11464632Seric ** 11474632Seric ** Returns: 114851920Seric ** TRUE if it successfully read the queue file. 114951920Seric ** FALSE otherwise. 11504632Seric ** 11514632Seric ** Side Effects: 115251920Seric ** The queue file is returned locked. 11534632Seric */ 11544632Seric 115551920Seric bool 115665200Seric readqf(e) 11579377Seric register ENVELOPE *e; 11584632Seric { 115917477Seric register FILE *qfp; 116054974Seric ADDRESS *ctladdr; 116156400Seric struct stat st; 116257135Seric char *bp; 116368481Seric int qfver = 0; 116468481Seric register char *p; 116568481Seric char *orcpt = NULL; 116658915Seric char qf[20]; 116757135Seric char buf[MAXLINE]; 116824954Seric extern long atol(); 116954974Seric extern ADDRESS *setctluser(); 117068490Seric extern void loseqfile(); 11714632Seric 11724632Seric /* 117317468Seric ** Read and process the file. 11744632Seric */ 11754632Seric 117658915Seric strcpy(qf, queuename(e, 'q')); 117751937Seric qfp = fopen(qf, "r+"); 117817477Seric if (qfp == NULL) 117917477Seric { 118058884Seric if (tTd(40, 8)) 118158884Seric printf("readqf(%s): fopen failure (%s)\n", 118258884Seric qf, errstring(errno)); 118340934Srick if (errno != ENOENT) 118440934Srick syserr("readqf: no control file %s", qf); 118551920Seric return FALSE; 118617477Seric } 118740934Srick 118864335Seric if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) 118964277Seric { 119064277Seric /* being processed by another queuer */ 119168481Seric if (Verbose || tTd(40, 8)) 119264277Seric printf("%s: locked\n", e->e_id); 119364277Seric # ifdef LOG 119464277Seric if (LogLevel > 19) 119564277Seric syslog(LOG_DEBUG, "%s: locked", e->e_id); 119664277Seric # endif /* LOG */ 119764277Seric (void) fclose(qfp); 119864277Seric return FALSE; 119964277Seric } 120064277Seric 120156400Seric /* 120256400Seric ** Check the queue file for plausibility to avoid attacks. 120356400Seric */ 120456400Seric 120556400Seric if (fstat(fileno(qfp), &st) < 0) 120656400Seric { 120756400Seric /* must have been being processed by someone else */ 120858884Seric if (tTd(40, 8)) 120958884Seric printf("readqf(%s): fstat failure (%s)\n", 121058884Seric qf, errstring(errno)); 121156400Seric fclose(qfp); 121256400Seric return FALSE; 121356400Seric } 121456400Seric 121568563Seric if (st.st_uid != geteuid() || bitset(S_IWOTH|S_IWGRP, st.st_mode)) 121656400Seric { 121756400Seric # ifdef LOG 121856400Seric if (LogLevel > 0) 121956400Seric { 122056400Seric syslog(LOG_ALERT, "%s: bogus queue file, uid=%d, mode=%o", 122156400Seric e->e_id, st.st_uid, st.st_mode); 122256400Seric } 122356795Seric # endif /* LOG */ 122458884Seric if (tTd(40, 8)) 122558884Seric printf("readqf(%s): bogus file\n", qf); 122668490Seric loseqfile(e, "bogus file uid in mqueue"); 122756400Seric fclose(qfp); 122856400Seric return FALSE; 122956400Seric } 123056400Seric 123164277Seric if (st.st_size == 0) 123240934Srick { 123364277Seric /* must be a bogus file -- just remove it */ 123464277Seric (void) unlink(qf); 123564277Seric fclose(qfp); 123651920Seric return FALSE; 123740934Srick } 123840934Srick 123964277Seric if (st.st_nlink == 0) 124059101Seric { 124164277Seric /* 124264277Seric ** Race condition -- we got a file just as it was being 124364277Seric ** unlinked. Just assume it is zero length. 124464277Seric */ 124564277Seric 124659101Seric fclose(qfp); 124759101Seric return FALSE; 124859101Seric } 124959101Seric 125064277Seric /* good file -- save this lock */ 125151920Seric e->e_lockfp = qfp; 125251920Seric 125340934Srick /* do basic system initialization */ 125455012Seric initsys(e); 125565164Seric define('i', e->e_id, e); 125640934Srick 12579377Seric LineNumber = 0; 125863850Seric e->e_flags |= EF_GLOBALERRS; 125963850Seric OpMode = MD_DELIVER; 126054974Seric ctladdr = NULL; 126168481Seric e->e_dfino = -1; 126268564Seric e->e_msgsize = -1; 126357135Seric while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) 12644632Seric { 126558737Seric register char *p; 126668481Seric u_long qflags; 126768481Seric ADDRESS *q; 126857529Seric 126926504Seric if (tTd(40, 4)) 127057135Seric printf("+++++ %s\n", bp); 127157135Seric switch (bp[0]) 12724632Seric { 127368481Seric case 'V': /* queue file version number */ 127468481Seric qfver = atoi(&bp[1]); 127568481Seric if (qfver > QF_VERSION) 127668481Seric { 127768481Seric syserr("Version number in qf (%d) greater than max (%d)", 127868481Seric qfver, QF_VERSION); 127968481Seric } 128068481Seric break; 128168481Seric 128240973Sbostic case 'C': /* specify controlling user */ 128357135Seric ctladdr = setctluser(&bp[1]); 128440973Sbostic break; 128540973Sbostic 128668481Seric case 'Q': /* original recipient */ 128768481Seric orcpt = newstr(&bp[1]); 128868481Seric break; 128968481Seric 12904632Seric case 'R': /* specify recipient */ 129168481Seric p = bp; 129268481Seric qflags = 0; 129368481Seric if (qfver >= 1) 129468481Seric { 129568481Seric /* get flag bits */ 129668481Seric while (*++p != '\0' && *p != ':') 129768481Seric { 129868481Seric switch (*p) 129968481Seric { 130068481Seric case 'S': 130168481Seric qflags |= QPINGONSUCCESS; 130268481Seric break; 130368481Seric 130468481Seric case 'F': 130568481Seric qflags |= QPINGONFAILURE; 130668481Seric break; 130768481Seric 130868481Seric case 'D': 130968481Seric qflags |= QPINGONDELAY; 131068481Seric break; 131168481Seric 131268481Seric case 'P': 131368481Seric qflags |= QPRIMARY; 131468481Seric break; 131568481Seric } 131668481Seric } 131768481Seric } 131868481Seric else 131968481Seric qflags |= QPRIMARY; 132068481Seric q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e); 132168481Seric if (q != NULL) 132268481Seric { 132368481Seric q->q_alias = ctladdr; 132468481Seric q->q_flags |= qflags; 132568481Seric q->q_orcpt = orcpt; 132668481Seric (void) recipient(q, &e->e_sendqueue, 0, e); 132768481Seric } 132868481Seric orcpt = NULL; 13294632Seric break; 13304632Seric 133125687Seric case 'E': /* specify error recipient */ 133268558Seric /* no longer used */ 133325687Seric break; 133425687Seric 13354632Seric case 'H': /* header */ 133657135Seric (void) chompheader(&bp[1], FALSE, e); 13374632Seric break; 13384632Seric 133910108Seric case 'M': /* message */ 134064705Seric /* ignore this; we want a new message next time */ 134110108Seric break; 134210108Seric 13434632Seric case 'S': /* sender */ 134458704Seric setsender(newstr(&bp[1]), e, NULL, TRUE); 13454632Seric break; 13464632Seric 134759093Seric case 'B': /* body type */ 134859093Seric e->e_bodytype = newstr(&bp[1]); 134959093Seric break; 135059093Seric 13514632Seric case 'D': /* data file name */ 135268564Seric /* obsolete -- ignore */ 13534632Seric break; 13544632Seric 13557860Seric case 'T': /* init time */ 135657135Seric e->e_ctime = atol(&bp[1]); 13574632Seric break; 13584632Seric 135968481Seric case 'I': /* data file's inode number */ 136068481Seric if (e->e_dfino == -1) 136168481Seric e->e_dfino = atol(&buf[1]); 136268481Seric break; 136368481Seric 136468481Seric case 'K': /* time of last deliver attempt */ 136568481Seric e->e_dtime = atol(&buf[1]); 136668481Seric break; 136768481Seric 136868481Seric case 'N': /* number of delivery attempts */ 136968481Seric e->e_ntries = atoi(&buf[1]); 137068481Seric break; 137168481Seric 13724634Seric case 'P': /* message priority */ 137357135Seric e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 13744634Seric break; 13754634Seric 137658737Seric case 'F': /* flag bits */ 137758737Seric for (p = &bp[1]; *p != '\0'; p++) 137858737Seric { 137958737Seric switch (*p) 138058737Seric { 138158737Seric case 'w': /* warning sent */ 138258737Seric e->e_flags |= EF_WARNING; 138358737Seric break; 138458737Seric 138558737Seric case 'r': /* response */ 138658737Seric e->e_flags |= EF_RESPONSE; 138758737Seric break; 138868481Seric 138968481Seric case '8': /* has 8 bit data */ 139068481Seric e->e_flags |= EF_HAS8BIT; 139168481Seric break; 139258737Seric } 139358737Seric } 139458737Seric break; 139558737Seric 139668481Seric case 'Z': /* original envelope id from ESMTP */ 139768481Seric e->e_envid = newstr(&bp[1]); 139868481Seric break; 139968481Seric 140053400Seric case '$': /* define macro */ 140157135Seric define(bp[1], newstr(&bp[2]), e); 140253400Seric break; 140353400Seric 140424941Seric case '\0': /* blank line; ignore */ 140524941Seric break; 140624941Seric 14074632Seric default: 140865960Seric syserr("readqf: %s: line %d: bad line \"%s\"", 140965200Seric qf, LineNumber, bp); 141063753Seric fclose(qfp); 141168490Seric loseqfile(e, "unrecognized line"); 141263753Seric return FALSE; 14134632Seric } 141457135Seric 141557135Seric if (bp != buf) 141657135Seric free(bp); 14174632Seric } 14189377Seric 141924941Seric /* 142024941Seric ** If we haven't read any lines, this queue file is empty. 142124941Seric ** Arrange to remove it without referencing any null pointers. 142224941Seric */ 142324941Seric 142424941Seric if (LineNumber == 0) 142524941Seric { 142624941Seric errno = 0; 142724941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 142824941Seric } 142968564Seric else 143068564Seric { 143168564Seric /* 143268564Seric ** Arrange to read the data file. 143368564Seric */ 143468564Seric 143568564Seric p = queuename(e, 'd'); 143668564Seric e->e_dfp = fopen(p, "r"); 143768564Seric if (e->e_dfp == NULL) 143868564Seric { 143968564Seric syserr("readqf: cannot open %s", p); 144068564Seric } 144168564Seric else if (fstat(fileno(e->e_dfp), &st) >= 0) 144268564Seric { 144368564Seric e->e_msgsize = st.st_size; 144468564Seric e->e_dfdev = st.st_dev; 144568564Seric e->e_dfino = st.st_ino; 144668564Seric } 144768564Seric } 144868564Seric 144951920Seric return TRUE; 14504632Seric } 14514632Seric /* 14529630Seric ** PRINTQUEUE -- print out a representation of the mail queue 14539630Seric ** 14549630Seric ** Parameters: 14559630Seric ** none. 14569630Seric ** 14579630Seric ** Returns: 14589630Seric ** none. 14599630Seric ** 14609630Seric ** Side Effects: 14619630Seric ** Prints a listing of the mail queue on the standard output. 14629630Seric */ 14635182Seric 14649630Seric printqueue() 14659630Seric { 14669630Seric register WORK *w; 14679630Seric FILE *f; 146810070Seric int nrequests; 14699630Seric char buf[MAXLINE]; 14709630Seric 14719630Seric /* 147258250Seric ** Check for permission to print the queue 147358250Seric */ 147458250Seric 147564333Seric if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 147658250Seric { 147758250Seric struct stat st; 147858523Seric # ifdef NGROUPS 147958523Seric int n; 148063937Seric GIDSET_T gidset[NGROUPS]; 148158523Seric # endif 148258250Seric 148358517Seric if (stat(QueueDir, &st) < 0) 148458250Seric { 148558250Seric syserr("Cannot stat %s", QueueDir); 148658250Seric return; 148758250Seric } 148858523Seric # ifdef NGROUPS 148958523Seric n = getgroups(NGROUPS, gidset); 149058523Seric while (--n >= 0) 149158523Seric { 149258523Seric if (gidset[n] == st.st_gid) 149358523Seric break; 149458523Seric } 149558523Seric if (n < 0) 149658523Seric # else 149763787Seric if (RealGid != st.st_gid) 149858523Seric # endif 149958250Seric { 150058250Seric usrerr("510 You are not permitted to see the queue"); 150158250Seric setstat(EX_NOPERM); 150258250Seric return; 150358250Seric } 150458250Seric } 150558250Seric 150658250Seric /* 15079630Seric ** Read and order the queue. 15089630Seric */ 15099630Seric 151024941Seric nrequests = orderq(TRUE); 15119630Seric 15129630Seric /* 15139630Seric ** Print the work list that we have read. 15149630Seric */ 15159630Seric 15169630Seric /* first see if there is anything */ 151710070Seric if (nrequests <= 0) 15189630Seric { 151910070Seric printf("Mail queue is empty\n"); 15209630Seric return; 15219630Seric } 15229630Seric 152351920Seric CurrentLA = getla(); /* get load average */ 152440934Srick 152510096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 152625687Seric if (nrequests > QUEUESIZE) 152725687Seric printf(", only %d printed", QUEUESIZE); 152824979Seric if (Verbose) 152958716Seric printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 153024979Seric else 153158716Seric printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 15329630Seric for (w = WorkQ; w != NULL; w = w->w_next) 15339630Seric { 15349630Seric struct stat st; 153510070Seric auto time_t submittime = 0; 1536*68565Seric long dfsize; 153758737Seric int flags = 0; 153868481Seric int qfver; 153910108Seric char message[MAXLINE]; 154068528Seric char bodytype[MAXNAME + 1]; 15419630Seric 154264355Seric printf("%8s", w->w_name + 2); 154317468Seric f = fopen(w->w_name, "r"); 154417468Seric if (f == NULL) 154517468Seric { 154664355Seric printf(" (job completed)\n"); 154717468Seric errno = 0; 154817468Seric continue; 154917468Seric } 1550*68565Seric w->w_name[0] = 'd'; 1551*68565Seric if (stat(w->w_name, &st) >= 0) 1552*68565Seric dfsize = st.st_size; 1553*68565Seric else 1554*68565Seric dfsize = -1; 155568481Seric if (w->w_lock) 155610070Seric printf("*"); 155757438Seric else if (shouldqueue(w->w_pri, w->w_ctime)) 155824941Seric printf("X"); 155910070Seric else 156010070Seric printf(" "); 156110070Seric errno = 0; 156217468Seric 156359093Seric message[0] = bodytype[0] = '\0'; 156468481Seric qfver = 0; 15659630Seric while (fgets(buf, sizeof buf, f) != NULL) 15669630Seric { 156753400Seric register int i; 156858737Seric register char *p; 156953400Seric 15709630Seric fixcrlf(buf, TRUE); 15719630Seric switch (buf[0]) 15729630Seric { 157368481Seric case 'V': /* queue file version */ 157468481Seric qfver = atoi(&buf[1]); 157568481Seric break; 157668481Seric 157710108Seric case 'M': /* error message */ 157853400Seric if ((i = strlen(&buf[1])) >= sizeof message) 157958737Seric i = sizeof message - 1; 158053400Seric bcopy(&buf[1], message, i); 158153400Seric message[i] = '\0'; 158210108Seric break; 158310108Seric 158459093Seric case 'B': /* body type */ 158559093Seric if ((i = strlen(&buf[1])) >= sizeof bodytype) 158659093Seric i = sizeof bodytype - 1; 158759093Seric bcopy(&buf[1], bodytype, i); 158859093Seric bodytype[i] = '\0'; 158959093Seric break; 159059093Seric 15919630Seric case 'S': /* sender name */ 159224979Seric if (Verbose) 159358737Seric printf("%8ld %10ld%c%.12s %.38s", 159458737Seric dfsize, 159558737Seric w->w_pri, 159658737Seric bitset(EF_WARNING, flags) ? '+' : ' ', 159758737Seric ctime(&submittime) + 4, 159824979Seric &buf[1]); 159924979Seric else 160024979Seric printf("%8ld %.16s %.45s", dfsize, 160124979Seric ctime(&submittime), &buf[1]); 160259093Seric if (message[0] != '\0' || bodytype[0] != '\0') 160359093Seric { 160459093Seric printf("\n %10.10s", bodytype); 160559093Seric if (message[0] != '\0') 160659093Seric printf(" (%.60s)", message); 160759093Seric } 16089630Seric break; 160951920Seric 161040973Sbostic case 'C': /* controlling user */ 161154974Seric if (Verbose) 161258716Seric printf("\n\t\t\t\t (---%.34s---)", 161358716Seric &buf[1]); 161440973Sbostic break; 16159630Seric 16169630Seric case 'R': /* recipient name */ 161768481Seric p = &buf[1]; 161868481Seric if (qfver >= 1) 161968481Seric { 162068481Seric p = strchr(p, ':'); 162168481Seric if (p == NULL) 162268481Seric break; 162368481Seric p++; 162468481Seric } 162524979Seric if (Verbose) 162668481Seric printf("\n\t\t\t\t\t %.38s", p); 162724979Seric else 162868481Seric printf("\n\t\t\t\t %.45s", p); 16299630Seric break; 16309630Seric 16319630Seric case 'T': /* creation time */ 163224941Seric submittime = atol(&buf[1]); 16339630Seric break; 163410070Seric 163558737Seric case 'F': /* flag bits */ 163658737Seric for (p = &buf[1]; *p != '\0'; p++) 163758737Seric { 163858737Seric switch (*p) 163958737Seric { 164058737Seric case 'w': 164158737Seric flags |= EF_WARNING; 164258737Seric break; 164358737Seric } 164458737Seric } 16459630Seric } 16469630Seric } 164710070Seric if (submittime == (time_t) 0) 164810070Seric printf(" (no control file)"); 16499630Seric printf("\n"); 165023098Seric (void) fclose(f); 16519630Seric } 16529630Seric } 16539630Seric 165456795Seric # endif /* QUEUE */ 165517468Seric /* 165617468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 165717468Seric ** 165817468Seric ** Assigns an id code if one does not already exist. 165917468Seric ** This code is very careful to avoid trashing existing files 166017468Seric ** under any circumstances. 166117468Seric ** 166217468Seric ** Parameters: 166317468Seric ** e -- envelope to build it in/from. 166417468Seric ** type -- the file type, used as the first character 166517468Seric ** of the file name. 166617468Seric ** 166717468Seric ** Returns: 166817468Seric ** a pointer to the new file name (in a static buffer). 166917468Seric ** 167017468Seric ** Side Effects: 167151920Seric ** If no id code is already assigned, queuename will 167251920Seric ** assign an id code, create a qf file, and leave a 167351920Seric ** locked, open-for-write file pointer in the envelope. 167417468Seric */ 167517468Seric 167617468Seric char * 167717468Seric queuename(e, type) 167817468Seric register ENVELOPE *e; 167959700Seric int type; 168017468Seric { 168117468Seric static int pid = -1; 168258741Seric static char c0; 168358741Seric static char c1; 168458741Seric static char c2; 168558716Seric time_t now; 168658716Seric struct tm *tm; 168768528Seric static char buf[MAXNAME + 1]; 168817468Seric 168917468Seric if (e->e_id == NULL) 169017468Seric { 169117468Seric char qf[20]; 169217468Seric 169317468Seric /* find a unique id */ 169417468Seric if (pid != getpid()) 169517468Seric { 169617468Seric /* new process -- start back at "AA" */ 169717468Seric pid = getpid(); 169858716Seric now = curtime(); 169958716Seric tm = localtime(&now); 170058716Seric c0 = 'A' + tm->tm_hour; 170117468Seric c1 = 'A'; 170217468Seric c2 = 'A' - 1; 170317468Seric } 170458716Seric (void) sprintf(qf, "qf%cAA%05d", c0, pid); 170517468Seric 170617468Seric while (c1 < '~' || c2 < 'Z') 170717468Seric { 170817468Seric int i; 170917468Seric 171017468Seric if (c2 >= 'Z') 171117468Seric { 171217468Seric c1++; 171317468Seric c2 = 'A' - 1; 171417468Seric } 171558716Seric qf[3] = c1; 171658716Seric qf[4] = ++c2; 171717468Seric if (tTd(7, 20)) 171840934Srick printf("queuename: trying \"%s\"\n", qf); 171917468Seric 172040934Srick i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); 172151920Seric if (i < 0) 172251920Seric { 172351920Seric if (errno == EEXIST) 172451920Seric continue; 172564705Seric syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", 172664705Seric qf, QueueDir, geteuid()); 172751920Seric exit(EX_UNAVAILABLE); 172851920Seric } 172964335Seric if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) 173051920Seric { 173151920Seric e->e_lockfp = fdopen(i, "w"); 173240934Srick break; 173317468Seric } 173451920Seric 173551920Seric /* a reader got the file; abandon it and try again */ 173651920Seric (void) close(i); 173717468Seric } 173817468Seric if (c1 >= '~' && c2 >= 'Z') 173917468Seric { 174064705Seric syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", 174164705Seric qf, QueueDir, geteuid()); 174217468Seric exit(EX_OSERR); 174317468Seric } 174417468Seric e->e_id = newstr(&qf[2]); 174517468Seric define('i', e->e_id, e); 174617468Seric if (tTd(7, 1)) 174717468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 174864745Seric if (tTd(7, 9)) 174964745Seric { 175064745Seric printf(" lockfd="); 175164745Seric dumpfd(fileno(e->e_lockfp), TRUE, FALSE); 175264745Seric } 175317468Seric # ifdef LOG 175458020Seric if (LogLevel > 93) 175517468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 175656795Seric # endif /* LOG */ 175717468Seric } 175817468Seric 175917468Seric if (type == '\0') 176017468Seric return (NULL); 176117468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 176217468Seric if (tTd(7, 2)) 176317468Seric printf("queuename: %s\n", buf); 176417468Seric return (buf); 176517468Seric } 176617468Seric /* 176717468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 176817468Seric ** 176917468Seric ** Parameters: 177017468Seric ** e -- the envelope to unlock. 177117468Seric ** 177217468Seric ** Returns: 177317468Seric ** none 177417468Seric ** 177517468Seric ** Side Effects: 177617468Seric ** unlocks the queue for `e'. 177717468Seric */ 177817468Seric 177917468Seric unlockqueue(e) 178017468Seric ENVELOPE *e; 178117468Seric { 178258680Seric if (tTd(51, 4)) 178358680Seric printf("unlockqueue(%s)\n", e->e_id); 178458680Seric 178551920Seric /* if there is a lock file in the envelope, close it */ 178651920Seric if (e->e_lockfp != NULL) 178758680Seric xfclose(e->e_lockfp, "unlockqueue", e->e_id); 178851920Seric e->e_lockfp = NULL; 178951920Seric 179058728Seric /* don't create a queue id if we don't already have one */ 179158728Seric if (e->e_id == NULL) 179258728Seric return; 179358728Seric 179417468Seric /* remove the transcript */ 179517468Seric # ifdef LOG 179658020Seric if (LogLevel > 87) 179717468Seric syslog(LOG_DEBUG, "%s: unlock", e->e_id); 179856795Seric # endif /* LOG */ 179958680Seric if (!tTd(51, 104)) 180017468Seric xunlink(queuename(e, 'x')); 180117468Seric 180217468Seric } 180340973Sbostic /* 180454974Seric ** SETCTLUSER -- create a controlling address 180540973Sbostic ** 180654974Seric ** Create a fake "address" given only a local login name; this is 180754974Seric ** used as a "controlling user" for future recipient addresses. 180840973Sbostic ** 180940973Sbostic ** Parameters: 181054974Seric ** user -- the user name of the controlling user. 181140973Sbostic ** 181240973Sbostic ** Returns: 181354974Seric ** An address descriptor for the controlling user. 181440973Sbostic ** 181540973Sbostic ** Side Effects: 181640973Sbostic ** none. 181740973Sbostic */ 181840973Sbostic 181954974Seric ADDRESS * 182054974Seric setctluser(user) 182154974Seric char *user; 182240973Sbostic { 182354974Seric register ADDRESS *a; 182440973Sbostic struct passwd *pw; 182559113Seric char *p; 182640973Sbostic 182740973Sbostic /* 182854974Seric ** See if this clears our concept of controlling user. 182940973Sbostic */ 183040973Sbostic 183163850Seric if (user == NULL || *user == '\0') 183263850Seric return NULL; 183340973Sbostic 183440973Sbostic /* 183554974Seric ** Set up addr fields for controlling user. 183640973Sbostic */ 183740973Sbostic 183854974Seric a = (ADDRESS *) xalloc(sizeof *a); 183954974Seric bzero((char *) a, sizeof *a); 184059113Seric 184159113Seric p = strchr(user, ':'); 184259113Seric if (p != NULL) 184359113Seric *p++ = '\0'; 184459270Seric if (*user != '\0' && (pw = getpwnam(user)) != NULL) 184540973Sbostic { 184665822Seric if (strcmp(pw->pw_dir, "/") == 0) 184765822Seric a->q_home = ""; 184865822Seric else 184965822Seric a->q_home = newstr(pw->pw_dir); 185040973Sbostic a->q_uid = pw->pw_uid; 185140973Sbostic a->q_gid = pw->pw_gid; 185257642Seric a->q_user = newstr(user); 185359270Seric a->q_flags |= QGOODUID; 185440973Sbostic } 185540973Sbostic else 185640973Sbostic { 185757642Seric a->q_user = newstr(DefUser); 185840973Sbostic } 185940973Sbostic 186059270Seric a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 186156328Seric a->q_mailer = LocalMailer; 186259113Seric if (p == NULL) 186359113Seric a->q_paddr = a->q_user; 186459113Seric else 186559113Seric a->q_paddr = newstr(p); 186654974Seric return a; 186740973Sbostic } 186868490Seric /* 186968490Seric ** LOSEQFILE -- save the qf as Qf and try to let someone know 187068490Seric ** 187168490Seric ** Parameters: 187268490Seric ** e -- the envelope (e->e_id will be used). 187368490Seric ** why -- reported to whomever can hear. 187468490Seric ** 187568490Seric ** Returns: 187668490Seric ** none. 187768490Seric */ 187868490Seric 187968490Seric void 188068490Seric loseqfile(e, why) 188168490Seric register ENVELOPE *e; 188268490Seric char *why; 188368490Seric { 188468563Seric char *p; 188568490Seric char buf[40]; 188668490Seric 188768490Seric if (e == NULL || e->e_id == NULL) 188868490Seric return; 188968490Seric if (strlen(e->e_id) > sizeof buf - 4) 189068490Seric return; 189168490Seric strcpy(buf, queuename(e, 'q')); 189268563Seric p = queuename(e, 'Q'); 189368563Seric if (rename(buf, p) < 0) 189468563Seric syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); 189568490Seric #ifdef LOG 189668563Seric else if (LogLevel > 0) 189768563Seric syslog(LOG_ALERT, "Losing %s: %s", buf, why); 189868490Seric #endif 189968490Seric } 1900