122708Sdist /* 268839Seric * Copyright (c) 1983, 1995 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*69835Seric static char sccsid[] = "@(#)queue.c 8.86 (Berkeley) 06/10/95 (with queueing)"; 1433731Sbostic #else 15*69835Seric static char sccsid[] = "@(#)queue.c 8.86 (Berkeley) 06/10/95 (without queueing)"; 1633731Sbostic #endif 1733731Sbostic #endif /* not lint */ 1833731Sbostic 194632Seric # include <errno.h> 2057736Seric # include <dirent.h> 214632Seric 2233731Sbostic # ifdef QUEUE 234632Seric 244632Seric /* 259377Seric ** Work queue. 269377Seric */ 279377Seric 289377Seric struct work 299377Seric { 309377Seric char *w_name; /* name of control file */ 3168481Seric char *w_host; /* name of recipient host */ 3268481Seric bool w_lock; /* is message locked? */ 339377Seric long w_pri; /* priority of message, see below */ 3425013Seric time_t w_ctime; /* creation time of message */ 359377Seric struct work *w_next; /* next in queue */ 369377Seric }; 379377Seric 389377Seric typedef struct work WORK; 399377Seric 409377Seric WORK *WorkQ; /* queue of things to be done */ 4168481Seric 4268481Seric #define QF_VERSION 1 /* version number of this queue format */ 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: 5351920Seric ** none. 544632Seric ** 554632Seric ** Side Effects: 569377Seric ** The current request are saved in a control file. 5751920Seric ** The queue file is left locked. 584632Seric */ 594632Seric 6069748Seric void 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]; 7769748Seric extern void printctladdr __P((ADDRESS *, FILE *)); 784632Seric 795037Seric /* 8017477Seric ** Create control file. 815037Seric */ 824632Seric 8364745Seric newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); 8464277Seric 8564277Seric /* if newid, queuename will create a locked qf file in e->lockfp */ 8651920Seric strcpy(tf, queuename(e, 't')); 8751920Seric tfp = e->e_lockfp; 8851920Seric if (tfp == NULL) 8951920Seric newid = FALSE; 9064277Seric 9164277Seric /* if newid, just write the qf file directly (instead of tf file) */ 9264745Seric if (!newid) 9351835Seric { 9451920Seric /* get a locked tf file */ 9564070Seric for (i = 0; i < 128; i++) 9651835Seric { 9751920Seric fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); 9851920Seric if (fd < 0) 9951835Seric { 10064070Seric if (errno != EEXIST) 10164070Seric break; 10264070Seric #ifdef LOG 10364070Seric if (LogLevel > 0 && (i % 32) == 0) 10465090Seric syslog(LOG_ALERT, "queueup: cannot create %s, uid=%d: %s", 10565090Seric tf, geteuid(), errstring(errno)); 10664070Seric #endif 10741636Srick } 10865090Seric else 10965090Seric { 11065090Seric if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) 11165090Seric break; 11264070Seric #ifdef LOG 11365090Seric else if (LogLevel > 0 && (i % 32) == 0) 11465090Seric syslog(LOG_ALERT, "queueup: cannot lock %s: %s", 11565090Seric tf, errstring(errno)); 11664070Seric #endif 11765090Seric close(fd); 11865090Seric } 11958689Seric 12064070Seric if ((i % 32) == 31) 12164070Seric { 12264070Seric /* save the old temp file away */ 12364070Seric (void) rename(tf, queuename(e, 'T')); 12464070Seric } 12564070Seric else 12664070Seric sleep(i % 32); 12741636Srick } 12864724Seric if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL) 12964921Seric { 13064921Seric printopenfds(TRUE); 13165090Seric syserr("!queueup: cannot create queue temp file %s, uid=%d", 13265090Seric tf, geteuid()); 13364921Seric } 13451920Seric } 1354632Seric 1367677Seric if (tTd(40, 1)) 13768481Seric printf("\n>>>>> queueing %s%s queueall=%d >>>>>\n", e->e_id, 13868481Seric newid ? " (new id)" : "", queueall); 13968603Seric if (tTd(40, 3)) 14068603Seric { 14168603Seric extern void printenvflags(); 14268603Seric 14368603Seric printf(" e_flags="); 14468603Seric printenvflags(e); 14568603Seric } 14668481Seric if (tTd(40, 32)) 14768481Seric { 14868481Seric printf(" sendq="); 14968481Seric printaddr(e->e_sendqueue, TRUE); 15068481Seric } 15164745Seric if (tTd(40, 9)) 15264745Seric { 15364745Seric printf(" tfp="); 15464745Seric dumpfd(fileno(tfp), TRUE, FALSE); 15564745Seric printf(" lockfp="); 15664745Seric if (e->e_lockfp == NULL) 15764745Seric printf("NULL\n"); 15864745Seric else 15964745Seric dumpfd(fileno(e->e_lockfp), TRUE, FALSE); 16064745Seric } 1614632Seric 1624632Seric /* 1636980Seric ** If there is no data file yet, create one. 1646980Seric */ 1656980Seric 16668564Seric if (!bitset(EF_HAS_DF, e->e_flags)) 1676980Seric { 1686980Seric register FILE *dfp; 16968564Seric char dfname[20]; 17068481Seric struct stat stbuf; 1716980Seric 17268564Seric strcpy(dfname, queuename(e, 'd')); 17368564Seric fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); 17464724Seric if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL) 17565090Seric syserr("!queueup: cannot create data temp file %s, uid=%d", 17668564Seric dfname, geteuid()); 17768481Seric if (fstat(fd, &stbuf) < 0) 17868481Seric e->e_dfino = -1; 17968481Seric else 18068481Seric { 18168481Seric e->e_dfdev = stbuf.st_dev; 18268481Seric e->e_dfino = stbuf.st_ino; 18368481Seric } 18468564Seric e->e_flags |= EF_HAS_DF; 18565870Seric bzero(&mcibuf, sizeof mcibuf); 18665870Seric mcibuf.mci_out = dfp; 18765870Seric mcibuf.mci_mailer = FileMailer; 18868228Seric (*e->e_putbody)(&mcibuf, e, NULL); 18958680Seric (void) xfclose(dfp, "queueup dfp", e->e_id); 1909389Seric e->e_putbody = putbody; 1916980Seric } 1926980Seric 1936980Seric /* 1944632Seric ** Output future work requests. 19525687Seric ** Priority and creation time should be first, since 19625687Seric ** they are required by orderq. 1974632Seric */ 1984632Seric 19968481Seric /* output queue version number (must be first!) */ 20068481Seric fprintf(tfp, "V%d\n", QF_VERSION); 20168481Seric 2029377Seric /* output message priority */ 2039377Seric fprintf(tfp, "P%ld\n", e->e_msgpriority); 2049377Seric 2059630Seric /* output creation time */ 2069630Seric fprintf(tfp, "T%ld\n", e->e_ctime); 2079630Seric 20868481Seric /* output last delivery time */ 20968481Seric fprintf(tfp, "K%ld\n", e->e_dtime); 21068481Seric 21168481Seric /* output number of delivery attempts */ 21268481Seric fprintf(tfp, "N%d\n", e->e_ntries); 21368481Seric 21468481Seric /* output inode number of data file */ 21568481Seric /* XXX should probably include device major/minor too */ 21668481Seric if (e->e_dfino != -1) 21768481Seric fprintf(tfp, "I%d/%d/%ld\n", 21868481Seric major(e->e_dfdev), minor(e->e_dfdev), e->e_dfino); 21968481Seric 22068564Seric /* output body type */ 22159093Seric if (e->e_bodytype != NULL) 22259093Seric fprintf(tfp, "B%s\n", e->e_bodytype); 2234632Seric 22410108Seric /* message from envelope, if it exists */ 22510108Seric if (e->e_message != NULL) 22668478Seric fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE)); 22710108Seric 22858737Seric /* send various flag bits through */ 22958737Seric p = buf; 23058737Seric if (bitset(EF_WARNING, e->e_flags)) 23158737Seric *p++ = 'w'; 23258737Seric if (bitset(EF_RESPONSE, e->e_flags)) 23358737Seric *p++ = 'r'; 23468481Seric if (bitset(EF_HAS8BIT, e->e_flags)) 23568481Seric *p++ = '8'; 23658737Seric *p++ = '\0'; 23758737Seric if (buf[0] != '\0') 23858737Seric fprintf(tfp, "F%s\n", buf); 23958737Seric 24058957Seric /* $r and $s and $_ macro values */ 24153400Seric if ((p = macvalue('r', e)) != NULL) 24268478Seric fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE)); 24353400Seric if ((p = macvalue('s', e)) != NULL) 24468478Seric fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE)); 24558957Seric if ((p = macvalue('_', e)) != NULL) 24668478Seric fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE)); 24753400Seric 2484632Seric /* output name of sender */ 24968481Seric if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 25068481Seric p = e->e_sender; 25168481Seric else 25268481Seric p = e->e_from.q_paddr; 25368481Seric fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE)); 2544632Seric 25568481Seric /* output ESMTP-supplied "original" information */ 25668481Seric if (e->e_envid != NULL) 25768481Seric fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE)); 25868481Seric 25968558Seric /* output list of recipient addresses */ 26059670Seric printctladdr(NULL, NULL); 2616980Seric for (q = e->e_sendqueue; q != NULL; q = q->q_next) 2624632Seric { 26358250Seric if (bitset(QQUEUEUP, q->q_flags) || 26458680Seric (queueall && !bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags))) 2658245Seric { 26659113Seric printctladdr(q, tfp); 26768481Seric if (q->q_orcpt != NULL) 26868481Seric fprintf(tfp, "Q%s\n", 26968481Seric denlstring(q->q_orcpt, TRUE, FALSE)); 27068481Seric putc('R', tfp); 27168481Seric if (bitset(QPRIMARY, q->q_flags)) 27268481Seric putc('P', tfp); 27368481Seric if (bitset(QPINGONSUCCESS, q->q_flags)) 27468481Seric putc('S', tfp); 27568481Seric if (bitset(QPINGONFAILURE, q->q_flags)) 27668481Seric putc('F', tfp); 27768481Seric if (bitset(QPINGONDELAY, q->q_flags)) 27868481Seric putc('D', tfp); 27968481Seric putc(':', tfp); 28068481Seric fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); 2819377Seric if (announce) 2829377Seric { 2839377Seric e->e_to = q->q_paddr; 28458151Seric message("queued"); 28558020Seric if (LogLevel > 8) 28668481Seric logdelivery(NULL, NULL, "queued", 28768481Seric NULL, (time_t) 0, e); 2889377Seric e->e_to = NULL; 2899377Seric } 2909387Seric if (tTd(40, 1)) 2919387Seric { 2929387Seric printf("queueing "); 2939387Seric printaddr(q, FALSE); 2949387Seric } 2958245Seric } 2964632Seric } 2974632Seric 2989377Seric /* 2999377Seric ** Output headers for this message. 3009377Seric ** Expand macros completely here. Queue run will deal with 3019377Seric ** everything as absolute headers. 3029377Seric ** All headers that must be relative to the recipient 3039377Seric ** can be cracked later. 30410173Seric ** We set up a "null mailer" -- i.e., a mailer that will have 30510173Seric ** no effect on the addresses as they are output. 3069377Seric */ 3079377Seric 30810686Seric bzero((char *) &nullmailer, sizeof nullmailer); 30958020Seric nullmailer.m_re_rwset = nullmailer.m_rh_rwset = 31065584Seric nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; 31110349Seric nullmailer.m_eol = "\n"; 31265870Seric bzero(&mcibuf, sizeof mcibuf); 31365870Seric mcibuf.mci_mailer = &nullmailer; 31465870Seric mcibuf.mci_out = tfp; 31510173Seric 31658050Seric define('g', "\201f", e); 3176980Seric for (h = e->e_header; h != NULL; h = h->h_link) 3184632Seric { 31910686Seric extern bool bitzerop(); 32010686Seric 32112015Seric /* don't output null headers */ 3224632Seric if (h->h_value == NULL || h->h_value[0] == '\0') 3234632Seric continue; 32412015Seric 32512015Seric /* don't output resent headers on non-resent messages */ 32612015Seric if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 32712015Seric continue; 32812015Seric 32965267Seric /* expand macros; if null, don't output header at all */ 33065267Seric if (bitset(H_DEFAULT, h->h_flags)) 33165267Seric { 33268529Seric (void) expand(h->h_value, buf, sizeof buf, e); 33365267Seric if (buf[0] == '\0') 33465267Seric continue; 33565267Seric } 33665267Seric 33712015Seric /* output this header */ 3387812Seric fprintf(tfp, "H"); 33912015Seric 34012015Seric /* if conditional, output the set of conditions */ 34110686Seric if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 34210686Seric { 34310686Seric int j; 34410686Seric 34523098Seric (void) putc('?', tfp); 34610686Seric for (j = '\0'; j <= '\177'; j++) 34710686Seric if (bitnset(j, h->h_mflags)) 34823098Seric (void) putc(j, tfp); 34923098Seric (void) putc('?', tfp); 35010686Seric } 35112015Seric 35212015Seric /* output the header: expand macros, convert addresses */ 3537763Seric if (bitset(H_DEFAULT, h->h_flags)) 3547763Seric { 35565267Seric fprintf(tfp, "%s: %s\n", h->h_field, buf); 3567763Seric } 3578245Seric else if (bitset(H_FROM|H_RCPT, h->h_flags)) 3589348Seric { 35958737Seric bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 36063753Seric FILE *savetrace = TrafficLogFile; 36158737Seric 36263753Seric TrafficLogFile = NULL; 36363753Seric 36458737Seric if (bitset(H_FROM, h->h_flags)) 36558737Seric oldstyle = FALSE; 36658737Seric 36765870Seric commaize(h, h->h_value, oldstyle, &mcibuf, e); 36863753Seric 36963753Seric TrafficLogFile = savetrace; 3709348Seric } 3717763Seric else 3728245Seric fprintf(tfp, "%s: %s\n", h->h_field, h->h_value); 3734632Seric } 3744632Seric 3754632Seric /* 3764632Seric ** Clean up. 3774632Seric */ 3784632Seric 37964762Seric if (fflush(tfp) < 0 || fsync(fileno(tfp)) < 0 || ferror(tfp)) 38058732Seric { 38158732Seric if (newid) 38258732Seric syserr("!552 Error writing control file %s", tf); 38358732Seric else 38458732Seric syserr("!452 Error writing control file %s", tf); 38558732Seric } 38658732Seric 38751920Seric if (!newid) 38851920Seric { 38964277Seric /* rename (locked) tf to be (locked) qf */ 39051920Seric qf = queuename(e, 'q'); 39151920Seric if (rename(tf, qf) < 0) 39268564Seric syserr("cannot rename(%s, %s), uid=%d", 39368564Seric tf, qf, geteuid()); 39464277Seric 39564277Seric /* close and unlock old (locked) qf */ 39651920Seric if (e->e_lockfp != NULL) 39758680Seric (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); 39851920Seric e->e_lockfp = tfp; 39951920Seric } 40051920Seric else 40151920Seric qf = tf; 40241636Srick errno = 0; 40364745Seric e->e_flags |= EF_INQUEUE; 4047391Seric 4057677Seric # ifdef LOG 4067677Seric /* save log info */ 40758020Seric if (LogLevel > 79) 40868564Seric syslog(LOG_DEBUG, "%s: queueup, qf=%s", e->e_id, qf); 40956795Seric # endif /* LOG */ 41064307Seric 41164307Seric if (tTd(40, 1)) 41264307Seric printf("<<<<< done queueing %s <<<<<\n\n", e->e_id); 41351920Seric return; 4144632Seric } 41554974Seric 41669748Seric void 41754974Seric printctladdr(a, tfp) 41859113Seric register ADDRESS *a; 41954974Seric FILE *tfp; 42054974Seric { 42159113Seric char *uname; 42259113Seric register struct passwd *pw; 42359113Seric register ADDRESS *q; 42459113Seric uid_t uid; 42559113Seric static ADDRESS *lastctladdr; 42659113Seric static uid_t lastuid; 42754974Seric 42859113Seric /* initialization */ 42963850Seric if (a == NULL || a->q_alias == NULL || tfp == NULL) 43054974Seric { 43159670Seric if (lastctladdr != NULL && tfp != NULL) 43259113Seric fprintf(tfp, "C\n"); 43359113Seric lastctladdr = NULL; 43459113Seric lastuid = 0; 43554974Seric return; 43654974Seric } 43759113Seric 43859113Seric /* find the active uid */ 43959113Seric q = getctladdr(a); 44059113Seric if (q == NULL) 44159113Seric uid = 0; 44254974Seric else 44359113Seric uid = q->q_uid; 44463850Seric a = a->q_alias; 44559113Seric 44659113Seric /* check to see if this is the same as last time */ 44759113Seric if (lastctladdr != NULL && uid == lastuid && 44859113Seric strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) 44959113Seric return; 45059113Seric lastuid = uid; 45159113Seric lastctladdr = a; 45259113Seric 45368693Seric if (uid == 0 || (pw = sm_getpwuid(uid)) == NULL) 45459270Seric uname = ""; 45559113Seric else 45659113Seric uname = pw->pw_name; 45759113Seric 45868478Seric fprintf(tfp, "C%s:%s\n", uname, denlstring(a->q_paddr, TRUE, FALSE)); 45954974Seric } 4604632Seric /* 4614632Seric ** RUNQUEUE -- run the jobs in the queue. 4624632Seric ** 4634632Seric ** Gets the stuff out of the queue in some presumably logical 4644632Seric ** order and processes them. 4654632Seric ** 4664632Seric ** Parameters: 46724941Seric ** forkflag -- TRUE if the queue scanning should be done in 46824941Seric ** a child process. We double-fork so it is not our 46924941Seric ** child and we don't have to clean up after it. 4704632Seric ** 4714632Seric ** Returns: 4724632Seric ** none. 4734632Seric ** 4744632Seric ** Side Effects: 4754632Seric ** runs things in the mail queue. 4764632Seric */ 4774632Seric 47855360Seric ENVELOPE QueueEnvelope; /* the queue run envelope */ 47955360Seric 48068481Seric void 48155360Seric runqueue(forkflag) 4824639Seric bool forkflag; 4834632Seric { 48455360Seric register ENVELOPE *e; 48568481Seric int njobs; 48668481Seric int sequenceno = 0; 48755360Seric extern ENVELOPE BlankEnvelope; 48824953Seric 4897466Seric /* 49024953Seric ** If no work will ever be selected, don't even bother reading 49124953Seric ** the queue. 49224953Seric */ 49324953Seric 49451920Seric CurrentLA = getla(); /* get load average */ 49540934Srick 49658132Seric if (shouldqueue(0L, curtime())) 49724953Seric { 49868854Seric char *msg = "Skipping queue run -- load average too high"; 49968854Seric 50024953Seric if (Verbose) 50168854Seric printf("%s\n", msg); 50268854Seric #ifdef LOG 50368854Seric if (LogLevel > 8) 50468854Seric syslog(LOG_INFO, "runqueue: %s", msg); 50568854Seric #endif 50658107Seric if (forkflag && QueueIntvl != 0) 50758107Seric (void) setevent(QueueIntvl, runqueue, TRUE); 50855360Seric return; 50924953Seric } 51024953Seric 51124953Seric /* 5127466Seric ** See if we want to go off and do other useful work. 5137466Seric */ 5144639Seric 5154639Seric if (forkflag) 5164639Seric { 5177943Seric int pid; 51865223Seric #ifdef SIGCHLD 51965223Seric extern void reapchild(); 5207943Seric 52165223Seric (void) setsignal(SIGCHLD, reapchild); 52265223Seric #endif 52365223Seric 5247943Seric pid = dofork(); 5257943Seric if (pid != 0) 5264639Seric { 5277943Seric /* parent -- pick up intermediate zombie */ 52825184Seric #ifndef SIGCHLD 5299377Seric (void) waitfor(pid); 53056795Seric #endif /* SIGCHLD */ 5317690Seric if (QueueIntvl != 0) 5329348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 5334639Seric return; 5344639Seric } 5357943Seric /* child -- double fork */ 53625184Seric #ifndef SIGCHLD 5377943Seric if (fork() != 0) 5387943Seric exit(EX_OK); 53956795Seric #else /* SIGCHLD */ 54064035Seric (void) setsignal(SIGCHLD, SIG_DFL); 54156795Seric #endif /* SIGCHLD */ 5424639Seric } 54324941Seric 54440934Srick setproctitle("running queue: %s", QueueDir); 54524941Seric 5467876Seric # ifdef LOG 54758020Seric if (LogLevel > 69) 54855360Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d, forkflag=%d", 54955360Seric QueueDir, getpid(), forkflag); 55056795Seric # endif /* LOG */ 5514639Seric 5527466Seric /* 55310205Seric ** Release any resources used by the daemon code. 55410205Seric */ 55510205Seric 55610205Seric # ifdef DAEMON 55710205Seric clrdaemon(); 55856795Seric # endif /* DAEMON */ 55910205Seric 56064658Seric /* force it to run expensive jobs */ 56164658Seric NoConnect = FALSE; 56264658Seric 56310205Seric /* 56455360Seric ** Create ourselves an envelope 56555360Seric */ 56655360Seric 56755360Seric CurEnv = &QueueEnvelope; 56858179Seric e = newenvelope(&QueueEnvelope, CurEnv); 56955360Seric e->e_flags = BlankEnvelope.e_flags; 57055360Seric 57155360Seric /* 57227175Seric ** Make sure the alias database is open. 57327175Seric */ 57427175Seric 57560537Seric initmaps(FALSE, e); 57627175Seric 57727175Seric /* 5787466Seric ** Start making passes through the queue. 5797466Seric ** First, read and sort the entire queue. 5807466Seric ** Then, process the work in that order. 5817466Seric ** But if you take too long, start over. 5827466Seric */ 5837466Seric 5847943Seric /* order the existing work requests */ 58568481Seric njobs = orderq(FALSE); 5867690Seric 5877943Seric /* process them once at a time */ 5887943Seric while (WorkQ != NULL) 5894639Seric { 5907943Seric WORK *w = WorkQ; 5917881Seric 5927943Seric WorkQ = WorkQ->w_next; 59358884Seric 59458884Seric /* 59558884Seric ** Ignore jobs that are too expensive for the moment. 59658884Seric */ 59758884Seric 59868481Seric sequenceno++; 59958884Seric if (shouldqueue(w->w_pri, w->w_ctime)) 60058884Seric { 60158884Seric if (Verbose) 60268481Seric printf("\nSkipping %s (sequence %d of %d)\n", 60368481Seric w->w_name + 2, sequenceno, njobs); 60458884Seric } 60558930Seric else 60658930Seric { 60764296Seric pid_t pid; 60864296Seric extern pid_t dowork(); 60964296Seric 61068481Seric if (Verbose) 61168481Seric printf("\nRunning %s (sequence %d of %d)\n", 61268481Seric w->w_name + 2, sequenceno, njobs); 61364296Seric pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); 61464296Seric errno = 0; 61566316Seric if (pid != 0) 61666316Seric (void) waitfor(pid); 61758930Seric } 6187943Seric free(w->w_name); 61968481Seric if (w->w_host) 62068481Seric free(w->w_host); 6217943Seric free((char *) w); 6224639Seric } 62329866Seric 62429866Seric /* exit without the usual cleanup */ 62555467Seric e->e_id = NULL; 62655467Seric finis(); 6274634Seric } 6284634Seric /* 6294632Seric ** ORDERQ -- order the work queue. 6304632Seric ** 6314632Seric ** Parameters: 63224941Seric ** doall -- if set, include everything in the queue (even 63324941Seric ** the jobs that cannot be run because the load 63424941Seric ** average is too high). Otherwise, exclude those 63524941Seric ** jobs. 6364632Seric ** 6374632Seric ** Returns: 63810121Seric ** The number of request in the queue (not necessarily 63910121Seric ** the number of requests in WorkQ however). 6404632Seric ** 6414632Seric ** Side Effects: 6424632Seric ** Sets WorkQ to the queue of available work, in order. 6434632Seric */ 6444632Seric 64525687Seric # define NEED_P 001 64625687Seric # define NEED_T 002 64758318Seric # define NEED_R 004 64858318Seric # define NEED_S 010 6494632Seric 65069722Seric static WORK *WorkList = NULL; 65169722Seric static int WorkListSize = 0; 65269722Seric 65369748Seric int 65424941Seric orderq(doall) 65524941Seric bool doall; 6564632Seric { 65760219Seric register struct dirent *d; 6584632Seric register WORK *w; 6596625Sglickman DIR *f; 6604632Seric register int i; 66110070Seric int wn = -1; 66268481Seric int wc; 6634632Seric 66458318Seric if (tTd(41, 1)) 66558318Seric { 66658318Seric printf("orderq:\n"); 66758318Seric if (QueueLimitId != NULL) 66858318Seric printf("\tQueueLimitId = %s\n", QueueLimitId); 66958318Seric if (QueueLimitSender != NULL) 67058318Seric printf("\tQueueLimitSender = %s\n", QueueLimitSender); 67158318Seric if (QueueLimitRecipient != NULL) 67258318Seric printf("\tQueueLimitRecipient = %s\n", QueueLimitRecipient); 67358318Seric } 67458318Seric 6754632Seric /* clear out old WorkQ */ 6764632Seric for (w = WorkQ; w != NULL; ) 6774632Seric { 6784632Seric register WORK *nw = w->w_next; 6794632Seric 6804632Seric WorkQ = nw; 6814632Seric free(w->w_name); 68268481Seric if (w->w_host) 68368481Seric free(w->w_host); 6844632Seric free((char *) w); 6854632Seric w = nw; 6864632Seric } 6874632Seric 6884632Seric /* open the queue directory */ 6898148Seric f = opendir("."); 6904632Seric if (f == NULL) 6914632Seric { 6928148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 69310070Seric return (0); 6944632Seric } 6954632Seric 6964632Seric /* 6974632Seric ** Read the work directory. 6984632Seric */ 6994632Seric 70010070Seric while ((d = readdir(f)) != NULL) 7014632Seric { 7029377Seric FILE *cf; 70364492Seric register char *p; 70468528Seric char lbuf[MAXNAME + 1]; 70558318Seric extern bool strcontainedin(); 7064632Seric 7074632Seric /* is this an interesting entry? */ 7087812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 7094632Seric continue; 7104632Seric 71158318Seric if (QueueLimitId != NULL && 71258318Seric !strcontainedin(QueueLimitId, d->d_name)) 71358318Seric continue; 71458318Seric 71568563Seric #ifdef PICKY_QF_NAME_CHECK 71658722Seric /* 71758722Seric ** Check queue name for plausibility. This handles 71858722Seric ** both old and new type ids. 71958722Seric */ 72058722Seric 72164492Seric p = d->d_name + 2; 72264492Seric if (isupper(p[0]) && isupper(p[2])) 72364492Seric p += 3; 72464492Seric else if (isupper(p[1])) 72564492Seric p += 2; 72664492Seric else 72764492Seric p = d->d_name; 72864492Seric for (i = 0; isdigit(*p); p++) 72964492Seric i++; 73064492Seric if (i < 5 || *p != '\0') 73158020Seric { 73258020Seric if (Verbose) 73358020Seric printf("orderq: bogus qf name %s\n", d->d_name); 73468563Seric # ifdef LOG 73568563Seric if (LogLevel > 0) 73668563Seric syslog(LOG_ALERT, "orderq: bogus qf name %s", 73758020Seric d->d_name); 73868563Seric # endif 73968706Seric if (strlen(d->d_name) > (SIZE_T) MAXNAME) 74068528Seric d->d_name[MAXNAME] = '\0'; 74158020Seric strcpy(lbuf, d->d_name); 74258020Seric lbuf[0] = 'Q'; 74358020Seric (void) rename(d->d_name, lbuf); 74458020Seric continue; 74558020Seric } 74668563Seric #endif 74758020Seric 74868563Seric /* open control file (if not too many files) */ 74969746Seric if (++wn > MaxQueueRun && MaxQueueRun > 0) 75069722Seric { 75169746Seric # ifdef LOG 75269746Seric if (wn == MaxQueueRun + 1 && LogLevel > 0) 75369746Seric syslog(LOG_ALERT, "WorkList for %s maxed out at %d", 75469746Seric QueueDir, MaxQueueRun); 75569746Seric # endif 75669746Seric continue; 75769746Seric } 75869746Seric if (wn >= WorkListSize) 75969746Seric { 76069748Seric extern void grow_wlist __P((void)); 76169748Seric 76269722Seric grow_wlist(); 76369722Seric if (wn >= WorkListSize) 76469722Seric continue; 76569722Seric } 76658318Seric 7678148Seric cf = fopen(d->d_name, "r"); 7684632Seric if (cf == NULL) 7694632Seric { 7707055Seric /* this may be some random person sending hir msgs */ 7717055Seric /* syserr("orderq: cannot open %s", cbuf); */ 77210090Seric if (tTd(41, 2)) 77310090Seric printf("orderq: cannot open %s (%d)\n", 77410090Seric d->d_name, errno); 7757055Seric errno = 0; 77610090Seric wn--; 7774632Seric continue; 7784632Seric } 77969722Seric w = &WorkList[wn]; 78025687Seric w->w_name = newstr(d->d_name); 78168481Seric w->w_host = NULL; 78268481Seric w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); 7834632Seric 78425027Seric /* make sure jobs in creation don't clog queue */ 78525687Seric w->w_pri = 0x7fffffff; 78625687Seric w->w_ctime = 0; 78725027Seric 7884632Seric /* extract useful information */ 78925687Seric i = NEED_P | NEED_T; 79058318Seric if (QueueLimitSender != NULL) 79158318Seric i |= NEED_S; 79268481Seric if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL) 79358318Seric i |= NEED_R; 79425687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 7954632Seric { 79658318Seric extern bool strcontainedin(); 79724954Seric 79824941Seric switch (lbuf[0]) 7994632Seric { 80024941Seric case 'P': 80125687Seric w->w_pri = atol(&lbuf[1]); 80225687Seric i &= ~NEED_P; 8034632Seric break; 80425013Seric 80525013Seric case 'T': 80625687Seric w->w_ctime = atol(&lbuf[1]); 80725687Seric i &= ~NEED_T; 80825013Seric break; 80958318Seric 81058318Seric case 'R': 81168481Seric if (w->w_host == NULL && 81268481Seric (p = strrchr(&lbuf[1], '@')) != NULL) 81368481Seric w->w_host = newstr(&p[1]); 81468481Seric if (QueueLimitRecipient == NULL || 81558318Seric strcontainedin(QueueLimitRecipient, &lbuf[1])) 81658318Seric i &= ~NEED_R; 81758318Seric break; 81858318Seric 81958318Seric case 'S': 82058318Seric if (QueueLimitSender != NULL && 82158318Seric strcontainedin(QueueLimitSender, &lbuf[1])) 82258318Seric i &= ~NEED_S; 82358318Seric break; 8244632Seric } 8254632Seric } 8264632Seric (void) fclose(cf); 82724953Seric 82858318Seric if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || 82958318Seric bitset(NEED_R|NEED_S, i)) 83024953Seric { 83124953Seric /* don't even bother sorting this job in */ 83268481Seric free(w->w_name); 83368481Seric if (w->w_host) 83468481Seric free(w->w_host); 83524953Seric wn--; 83624953Seric } 8374632Seric } 8386625Sglickman (void) closedir(f); 83910090Seric wn++; 8404632Seric 84169722Seric wc = min(wn, WorkListSize); 8424632Seric 84368481Seric if (QueueSortOrder == QS_BYHOST) 84468481Seric { 84568481Seric extern workcmpf1(); 84668481Seric extern workcmpf2(); 84767612Seric 84868481Seric /* 84968481Seric ** Sort the work directory for the first time, 85068481Seric ** based on host name, lock status, and priority. 85168481Seric */ 85268481Seric 85369722Seric qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); 85468481Seric 85568481Seric /* 85668481Seric ** If one message to host is locked, "lock" all messages 85768481Seric ** to that host. 85868481Seric */ 85968481Seric 86068481Seric i = 0; 86168481Seric while (i < wc) 86268481Seric { 86369722Seric if (!WorkList[i].w_lock) 86468481Seric { 86568481Seric i++; 86668481Seric continue; 86768481Seric } 86869722Seric w = &WorkList[i]; 86968481Seric while (++i < wc) 87068481Seric { 87169722Seric if (WorkList[i].w_host == NULL && 87268481Seric w->w_host == NULL) 87369722Seric WorkList[i].w_lock = TRUE; 87469722Seric else if (WorkList[i].w_host != NULL && 87568481Seric w->w_host != NULL && 87669722Seric strcmp(WorkList[i].w_host, w->w_host) == 0) 87769722Seric WorkList[i].w_lock = TRUE; 87868481Seric else 87968481Seric break; 88068481Seric } 88168481Seric } 88268481Seric 88368481Seric /* 88468481Seric ** Sort the work directory for the second time, 88568481Seric ** based on lock status, host name, and priority. 88668481Seric */ 88768481Seric 88869722Seric qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); 88968481Seric } 89068481Seric else 89168481Seric { 89268481Seric extern workcmpf0(); 89368481Seric 89468481Seric /* 89568481Seric ** Simple sort based on queue priority only. 89668481Seric */ 89768481Seric 89869722Seric qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); 89968481Seric } 90068481Seric 9014632Seric /* 9024632Seric ** Convert the work list into canonical form. 9039377Seric ** Should be turning it into a list of envelopes here perhaps. 9044632Seric */ 9054632Seric 90624981Seric WorkQ = NULL; 90768481Seric for (i = wc; --i >= 0; ) 9084632Seric { 9094632Seric w = (WORK *) xalloc(sizeof *w); 91069722Seric w->w_name = WorkList[i].w_name; 91169722Seric w->w_host = WorkList[i].w_host; 91269722Seric w->w_lock = WorkList[i].w_lock; 91369722Seric w->w_pri = WorkList[i].w_pri; 91469722Seric w->w_ctime = WorkList[i].w_ctime; 91524981Seric w->w_next = WorkQ; 91624981Seric WorkQ = w; 9174632Seric } 91869722Seric free(WorkList); 91969722Seric WorkList = NULL; 9204632Seric 9217677Seric if (tTd(40, 1)) 9224632Seric { 9234632Seric for (w = WorkQ; w != NULL; w = w->w_next) 9245037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 9254632Seric } 92610070Seric 92710090Seric return (wn); 9284632Seric } 92969746Seric /* 93069746Seric ** GROW_WLIST -- make the work list larger 93169746Seric ** 93269746Seric ** Parameters: 93369746Seric ** none. 93469746Seric ** 93569746Seric ** Returns: 93669746Seric ** none. 93769746Seric ** 93869746Seric ** Side Effects: 93969746Seric ** Adds another QUEUESEGSIZE entries to WorkList if possible. 94069746Seric ** It can fail if there isn't enough memory, so WorkListSize 94169746Seric ** should be checked again upon return. 94269746Seric */ 94369722Seric 94469748Seric void 94569722Seric grow_wlist() 94669722Seric { 94769722Seric if (tTd(41, 1)) 94869722Seric printf("grow_wlist: WorkListSize=%d\n", WorkListSize); 94969746Seric if (WorkList == NULL) 95069722Seric { 95169722Seric WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1)); 95269722Seric WorkListSize = QUEUESEGSIZE; 95369722Seric } 95469722Seric else 95569722Seric { 95669722Seric int newsize = WorkListSize + QUEUESEGSIZE; 95769722Seric WORK *newlist = (WORK *) realloc((char *)WorkList, 95869722Seric (unsigned)sizeof(WORK) * (newsize + 1)); 95969722Seric 96069722Seric if (newlist != NULL) 96169722Seric { 96269722Seric WorkListSize = newsize; 96369722Seric WorkList = newlist; 96469722Seric # ifdef LOG 96569722Seric if (LogLevel > 1) 96669722Seric { 96769722Seric syslog(LOG_NOTICE, "grew WorkList for %s to %d", 96869722Seric QueueDir, WorkListSize); 96969722Seric } 97069722Seric } 97169722Seric else if (LogLevel > 0) 97269722Seric { 97369722Seric syslog(LOG_ALERT, "FAILED to grow WorkList for %s to %d", 97469722Seric QueueDir, newsize); 97569722Seric # endif 97669722Seric } 97769722Seric } 97869722Seric if (tTd(41, 1)) 97969722Seric printf("grow_wlist: WorkListSize now %d\n", WorkListSize); 98069722Seric } 9814632Seric /* 98268481Seric ** WORKCMPF0 -- simple priority-only compare function. 9834632Seric ** 9844632Seric ** Parameters: 9854632Seric ** a -- the first argument. 9864632Seric ** b -- the second argument. 9874632Seric ** 9884632Seric ** Returns: 98924981Seric ** -1 if a < b 99024981Seric ** 0 if a == b 99124981Seric ** +1 if a > b 9924632Seric ** 9934632Seric ** Side Effects: 9944632Seric ** none. 9954632Seric */ 9964632Seric 99769748Seric int 99868481Seric workcmpf0(a, b) 9995037Seric register WORK *a; 10005037Seric register WORK *b; 10014632Seric { 100257438Seric long pa = a->w_pri; 100357438Seric long pb = b->w_pri; 100424941Seric 100524941Seric if (pa == pb) 100668481Seric return 0; 100724941Seric else if (pa > pb) 100868481Seric return 1; 100924981Seric else 101068481Seric return -1; 10114632Seric } 10124632Seric /* 101368481Seric ** WORKCMPF1 -- first compare function for ordering work based on host name. 101468481Seric ** 101568481Seric ** Sorts on host name, lock status, and priority in that order. 101668481Seric ** 101768481Seric ** Parameters: 101868481Seric ** a -- the first argument. 101968481Seric ** b -- the second argument. 102068481Seric ** 102168481Seric ** Returns: 102268481Seric ** <0 if a < b 102368481Seric ** 0 if a == b 102468481Seric ** >0 if a > b 102568481Seric ** 102668481Seric ** Side Effects: 102768481Seric ** none. 102868481Seric */ 102968481Seric 103069748Seric int 103168481Seric workcmpf1(a, b) 103268481Seric register WORK *a; 103368481Seric register WORK *b; 103468481Seric { 103568481Seric int i; 103668481Seric 103768481Seric /* host name */ 103868481Seric if (a->w_host != NULL && b->w_host == NULL) 103968481Seric return 1; 104068481Seric else if (a->w_host == NULL && b->w_host != NULL) 104168481Seric return -1; 104268481Seric if (a->w_host != NULL && b->w_host != NULL && 104368481Seric (i = strcmp(a->w_host, b->w_host))) 104468481Seric return i; 104568481Seric 104668481Seric /* lock status */ 104768481Seric if (a->w_lock != b->w_lock) 104868481Seric return b->w_lock - a->w_lock; 104968481Seric 105068481Seric /* job priority */ 105168481Seric return a->w_pri - b->w_pri; 105268481Seric } 105368481Seric /* 105468481Seric ** WORKCMPF2 -- second compare function for ordering work based on host name. 105568481Seric ** 105668481Seric ** Sorts on lock status, host name, and priority in that order. 105768481Seric ** 105868481Seric ** Parameters: 105968481Seric ** a -- the first argument. 106068481Seric ** b -- the second argument. 106168481Seric ** 106268481Seric ** Returns: 106368481Seric ** <0 if a < b 106468481Seric ** 0 if a == b 106568481Seric ** >0 if a > b 106668481Seric ** 106768481Seric ** Side Effects: 106868481Seric ** none. 106968481Seric */ 107068481Seric 107169748Seric int 107268481Seric workcmpf2(a, b) 107368481Seric register WORK *a; 107468481Seric register WORK *b; 107568481Seric { 107668481Seric int i; 107768481Seric 107868481Seric /* lock status */ 107968481Seric if (a->w_lock != b->w_lock) 108068481Seric return a->w_lock - b->w_lock; 108168481Seric 108268481Seric /* host name */ 108368481Seric if (a->w_host != NULL && b->w_host == NULL) 108468481Seric return 1; 108568481Seric else if (a->w_host == NULL && b->w_host != NULL) 108668481Seric return -1; 108768481Seric if (a->w_host != NULL && b->w_host != NULL && 108868481Seric (i = strcmp(a->w_host, b->w_host))) 108968481Seric return i; 109068481Seric 109168481Seric /* job priority */ 109268481Seric return a->w_pri - b->w_pri; 109368481Seric } 109468481Seric /* 10954632Seric ** DOWORK -- do a work request. 10964632Seric ** 10974632Seric ** Parameters: 109858884Seric ** id -- the ID of the job to run. 109958884Seric ** forkflag -- if set, run this in background. 110058924Seric ** requeueflag -- if set, reinstantiate the queue quickly. 110158924Seric ** This is used when expanding aliases in the queue. 110261094Seric ** If forkflag is also set, it doesn't wait for the 110361094Seric ** child. 110458884Seric ** e - the envelope in which to run it. 11054632Seric ** 11064632Seric ** Returns: 110764296Seric ** process id of process that is running the queue job. 11084632Seric ** 11094632Seric ** Side Effects: 11104632Seric ** The work request is satisfied if possible. 11114632Seric */ 11124632Seric 111364296Seric pid_t 111458924Seric dowork(id, forkflag, requeueflag, e) 111558884Seric char *id; 111658884Seric bool forkflag; 111758924Seric bool requeueflag; 111855012Seric register ENVELOPE *e; 11194632Seric { 112064296Seric register pid_t pid; 112151920Seric extern bool readqf(); 11224632Seric 11237677Seric if (tTd(40, 1)) 112458884Seric printf("dowork(%s)\n", id); 11254632Seric 11264632Seric /* 112724941Seric ** Fork for work. 112824941Seric */ 112924941Seric 113058884Seric if (forkflag) 113124941Seric { 113264296Seric pid = fork(); 113364296Seric if (pid < 0) 113424941Seric { 113524941Seric syserr("dowork: cannot fork"); 113664296Seric return 0; 113724941Seric } 113864839Seric else if (pid > 0) 113964839Seric { 114064839Seric /* parent -- clean out connection cache */ 114164839Seric mci_flush(FALSE, NULL); 114264839Seric } 114324941Seric } 114424941Seric else 114524941Seric { 114664296Seric pid = 0; 114724941Seric } 114824941Seric 114964296Seric if (pid == 0) 11504632Seric { 11514632Seric /* 11524632Seric ** CHILD 11538148Seric ** Lock the control file to avoid duplicate deliveries. 11548148Seric ** Then run the file as though we had just read it. 11557350Seric ** We save an idea of the temporary name so we 11567350Seric ** can recover on interrupt. 11574632Seric */ 11584632Seric 11597763Seric /* set basic modes, etc. */ 11607356Seric (void) alarm(0); 116155012Seric clearenvelope(e, FALSE); 116264554Seric e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 116358734Seric e->e_errormode = EM_MAIL; 116458884Seric e->e_id = id; 116564765Seric GrabTo = UseErrorsTo = FALSE; 116666316Seric ExitStat = EX_OK; 116763846Seric if (forkflag) 116864554Seric { 116964296Seric disconnect(1, e); 117064554Seric OpMode = MD_DELIVER; 117164554Seric } 11727876Seric # ifdef LOG 117358020Seric if (LogLevel > 76) 117455012Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id, 11757881Seric getpid()); 117656795Seric # endif /* LOG */ 11777763Seric 11787763Seric /* don't use the headers from sendmail.cf... */ 117955012Seric e->e_header = NULL; 11807763Seric 118151920Seric /* read the queue control file -- return if locked */ 118265200Seric if (!readqf(e)) 11836980Seric { 118458884Seric if (tTd(40, 4)) 118558884Seric printf("readqf(%s) failed\n", e->e_id); 118658884Seric if (forkflag) 118724941Seric exit(EX_OK); 118824941Seric else 118966316Seric return 0; 11906980Seric } 11916980Seric 119255012Seric e->e_flags |= EF_INQUEUE; 11936980Seric 119468481Seric /* if this has been tried recently, let it be */ 119568481Seric if (e->e_ntries > 0 && (curtime() - e->e_dtime) < MinQueueAge) 119668481Seric { 119768481Seric char *howlong = pintvl(curtime() - e->e_dtime, TRUE); 119858924Seric 119968481Seric e->e_flags |= EF_KEEPQUEUE; 120068481Seric if (Verbose || tTd(40, 8)) 120168481Seric printf("%s: too young (%s)\n", 120268481Seric e->e_id, howlong); 120368481Seric #ifdef LOG 120468481Seric if (LogLevel > 19) 120568481Seric syslog(LOG_DEBUG, "%s: too young (%s)", 120668481Seric e->e_id, howlong); 120768481Seric #endif 120868481Seric } 120968481Seric else 121068481Seric { 121168481Seric eatheader(e, requeueflag); 12126980Seric 121368481Seric if (requeueflag) 121468481Seric queueup(e, TRUE, FALSE); 121568481Seric 121668481Seric /* do the delivery */ 121768481Seric sendall(e, SM_DELIVER); 121868481Seric } 121968481Seric 12206980Seric /* finish up and exit */ 122158884Seric if (forkflag) 122224941Seric finis(); 122324941Seric else 122455012Seric dropenvelope(e); 12254632Seric } 122664296Seric e->e_id = NULL; 122764296Seric return pid; 12284632Seric } 12294632Seric /* 12304632Seric ** READQF -- read queue file and set up environment. 12314632Seric ** 12324632Seric ** Parameters: 12339377Seric ** e -- the envelope of the job to run. 12344632Seric ** 12354632Seric ** Returns: 123651920Seric ** TRUE if it successfully read the queue file. 123751920Seric ** FALSE otherwise. 12384632Seric ** 12394632Seric ** Side Effects: 124051920Seric ** The queue file is returned locked. 12414632Seric */ 12424632Seric 124351920Seric bool 124465200Seric readqf(e) 12459377Seric register ENVELOPE *e; 12464632Seric { 124717477Seric register FILE *qfp; 124854974Seric ADDRESS *ctladdr; 124956400Seric struct stat st; 125057135Seric char *bp; 125168481Seric int qfver = 0; 125268481Seric register char *p; 125368481Seric char *orcpt = NULL; 125458915Seric char qf[20]; 125557135Seric char buf[MAXLINE]; 125654974Seric extern ADDRESS *setctluser(); 125768490Seric extern void loseqfile(); 12584632Seric 12594632Seric /* 126017468Seric ** Read and process the file. 12614632Seric */ 12624632Seric 126358915Seric strcpy(qf, queuename(e, 'q')); 126451937Seric qfp = fopen(qf, "r+"); 126517477Seric if (qfp == NULL) 126617477Seric { 126758884Seric if (tTd(40, 8)) 126858884Seric printf("readqf(%s): fopen failure (%s)\n", 126958884Seric qf, errstring(errno)); 127040934Srick if (errno != ENOENT) 127140934Srick syserr("readqf: no control file %s", qf); 127251920Seric return FALSE; 127317477Seric } 127440934Srick 127564335Seric if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) 127664277Seric { 127764277Seric /* being processed by another queuer */ 127868481Seric if (Verbose || tTd(40, 8)) 127964277Seric printf("%s: locked\n", e->e_id); 128064277Seric # ifdef LOG 128164277Seric if (LogLevel > 19) 128264277Seric syslog(LOG_DEBUG, "%s: locked", e->e_id); 128364277Seric # endif /* LOG */ 128464277Seric (void) fclose(qfp); 128564277Seric return FALSE; 128664277Seric } 128764277Seric 128856400Seric /* 128956400Seric ** Check the queue file for plausibility to avoid attacks. 129056400Seric */ 129156400Seric 129256400Seric if (fstat(fileno(qfp), &st) < 0) 129356400Seric { 129456400Seric /* must have been being processed by someone else */ 129558884Seric if (tTd(40, 8)) 129658884Seric printf("readqf(%s): fstat failure (%s)\n", 129758884Seric qf, errstring(errno)); 129856400Seric fclose(qfp); 129956400Seric return FALSE; 130056400Seric } 130156400Seric 130268563Seric if (st.st_uid != geteuid() || bitset(S_IWOTH|S_IWGRP, st.st_mode)) 130356400Seric { 130456400Seric # ifdef LOG 130556400Seric if (LogLevel > 0) 130656400Seric { 130756400Seric syslog(LOG_ALERT, "%s: bogus queue file, uid=%d, mode=%o", 130856400Seric e->e_id, st.st_uid, st.st_mode); 130956400Seric } 131056795Seric # endif /* LOG */ 131158884Seric if (tTd(40, 8)) 131258884Seric printf("readqf(%s): bogus file\n", qf); 131368490Seric loseqfile(e, "bogus file uid in mqueue"); 131456400Seric fclose(qfp); 131556400Seric return FALSE; 131656400Seric } 131756400Seric 131864277Seric if (st.st_size == 0) 131940934Srick { 132064277Seric /* must be a bogus file -- just remove it */ 132164277Seric (void) unlink(qf); 132264277Seric fclose(qfp); 132351920Seric return FALSE; 132440934Srick } 132540934Srick 132664277Seric if (st.st_nlink == 0) 132759101Seric { 132864277Seric /* 132964277Seric ** Race condition -- we got a file just as it was being 133064277Seric ** unlinked. Just assume it is zero length. 133164277Seric */ 133264277Seric 133359101Seric fclose(qfp); 133459101Seric return FALSE; 133559101Seric } 133659101Seric 133764277Seric /* good file -- save this lock */ 133851920Seric e->e_lockfp = qfp; 133951920Seric 134040934Srick /* do basic system initialization */ 134155012Seric initsys(e); 134265164Seric define('i', e->e_id, e); 134340934Srick 13449377Seric LineNumber = 0; 134563850Seric e->e_flags |= EF_GLOBALERRS; 134663850Seric OpMode = MD_DELIVER; 134754974Seric ctladdr = NULL; 134868481Seric e->e_dfino = -1; 134968564Seric e->e_msgsize = -1; 135057135Seric while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) 13514632Seric { 135258737Seric register char *p; 135368481Seric u_long qflags; 135468481Seric ADDRESS *q; 135557529Seric 135626504Seric if (tTd(40, 4)) 135757135Seric printf("+++++ %s\n", bp); 135857135Seric switch (bp[0]) 13594632Seric { 136068481Seric case 'V': /* queue file version number */ 136168481Seric qfver = atoi(&bp[1]); 136268481Seric if (qfver > QF_VERSION) 136368481Seric { 136468481Seric syserr("Version number in qf (%d) greater than max (%d)", 136568481Seric qfver, QF_VERSION); 136668481Seric } 136768481Seric break; 136868481Seric 136940973Sbostic case 'C': /* specify controlling user */ 137057135Seric ctladdr = setctluser(&bp[1]); 137140973Sbostic break; 137240973Sbostic 137368481Seric case 'Q': /* original recipient */ 137468481Seric orcpt = newstr(&bp[1]); 137568481Seric break; 137668481Seric 13774632Seric case 'R': /* specify recipient */ 137868481Seric p = bp; 137968481Seric qflags = 0; 138068481Seric if (qfver >= 1) 138168481Seric { 138268481Seric /* get flag bits */ 138368481Seric while (*++p != '\0' && *p != ':') 138468481Seric { 138568481Seric switch (*p) 138668481Seric { 138768481Seric case 'S': 138868481Seric qflags |= QPINGONSUCCESS; 138968481Seric break; 139068481Seric 139168481Seric case 'F': 139268481Seric qflags |= QPINGONFAILURE; 139368481Seric break; 139468481Seric 139568481Seric case 'D': 139668481Seric qflags |= QPINGONDELAY; 139768481Seric break; 139868481Seric 139968481Seric case 'P': 140068481Seric qflags |= QPRIMARY; 140168481Seric break; 140268481Seric } 140368481Seric } 140468481Seric } 140568481Seric else 140668481Seric qflags |= QPRIMARY; 140768481Seric q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e); 140868481Seric if (q != NULL) 140968481Seric { 141068481Seric q->q_alias = ctladdr; 141168481Seric q->q_flags |= qflags; 141268481Seric q->q_orcpt = orcpt; 141368481Seric (void) recipient(q, &e->e_sendqueue, 0, e); 141468481Seric } 141568481Seric orcpt = NULL; 14164632Seric break; 14174632Seric 141825687Seric case 'E': /* specify error recipient */ 141968558Seric /* no longer used */ 142025687Seric break; 142125687Seric 14224632Seric case 'H': /* header */ 142368717Seric (void) chompheader(&bp[1], FALSE, NULL, e); 14244632Seric break; 14254632Seric 142610108Seric case 'M': /* message */ 142764705Seric /* ignore this; we want a new message next time */ 142810108Seric break; 142910108Seric 14304632Seric case 'S': /* sender */ 143158704Seric setsender(newstr(&bp[1]), e, NULL, TRUE); 14324632Seric break; 14334632Seric 143459093Seric case 'B': /* body type */ 143559093Seric e->e_bodytype = newstr(&bp[1]); 143659093Seric break; 143759093Seric 14384632Seric case 'D': /* data file name */ 143968564Seric /* obsolete -- ignore */ 14404632Seric break; 14414632Seric 14427860Seric case 'T': /* init time */ 144357135Seric e->e_ctime = atol(&bp[1]); 14444632Seric break; 14454632Seric 144668481Seric case 'I': /* data file's inode number */ 144768481Seric if (e->e_dfino == -1) 144868481Seric e->e_dfino = atol(&buf[1]); 144968481Seric break; 145068481Seric 145168481Seric case 'K': /* time of last deliver attempt */ 145268481Seric e->e_dtime = atol(&buf[1]); 145368481Seric break; 145468481Seric 145568481Seric case 'N': /* number of delivery attempts */ 145668481Seric e->e_ntries = atoi(&buf[1]); 145768481Seric break; 145868481Seric 14594634Seric case 'P': /* message priority */ 146057135Seric e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 14614634Seric break; 14624634Seric 146358737Seric case 'F': /* flag bits */ 146458737Seric for (p = &bp[1]; *p != '\0'; p++) 146558737Seric { 146658737Seric switch (*p) 146758737Seric { 146858737Seric case 'w': /* warning sent */ 146958737Seric e->e_flags |= EF_WARNING; 147058737Seric break; 147158737Seric 147258737Seric case 'r': /* response */ 147358737Seric e->e_flags |= EF_RESPONSE; 147458737Seric break; 147568481Seric 147668481Seric case '8': /* has 8 bit data */ 147768481Seric e->e_flags |= EF_HAS8BIT; 147868481Seric break; 147958737Seric } 148058737Seric } 148158737Seric break; 148258737Seric 148368481Seric case 'Z': /* original envelope id from ESMTP */ 148468481Seric e->e_envid = newstr(&bp[1]); 148568481Seric break; 148668481Seric 148753400Seric case '$': /* define macro */ 148857135Seric define(bp[1], newstr(&bp[2]), e); 148953400Seric break; 149053400Seric 149124941Seric case '\0': /* blank line; ignore */ 149224941Seric break; 149324941Seric 14944632Seric default: 149565960Seric syserr("readqf: %s: line %d: bad line \"%s\"", 149665200Seric qf, LineNumber, bp); 149763753Seric fclose(qfp); 149868490Seric loseqfile(e, "unrecognized line"); 149963753Seric return FALSE; 15004632Seric } 150157135Seric 150257135Seric if (bp != buf) 150357135Seric free(bp); 15044632Seric } 15059377Seric 150624941Seric /* 150724941Seric ** If we haven't read any lines, this queue file is empty. 150824941Seric ** Arrange to remove it without referencing any null pointers. 150924941Seric */ 151024941Seric 151124941Seric if (LineNumber == 0) 151224941Seric { 151324941Seric errno = 0; 151424941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 151524941Seric } 151668564Seric else 151768564Seric { 151868564Seric /* 151968564Seric ** Arrange to read the data file. 152068564Seric */ 152168564Seric 152268564Seric p = queuename(e, 'd'); 152368564Seric e->e_dfp = fopen(p, "r"); 152468564Seric if (e->e_dfp == NULL) 152568564Seric { 152668564Seric syserr("readqf: cannot open %s", p); 152768564Seric } 152868603Seric else 152968564Seric { 153068603Seric e->e_flags |= EF_HAS_DF; 153168603Seric if (fstat(fileno(e->e_dfp), &st) >= 0) 153268603Seric { 153368603Seric e->e_msgsize = st.st_size; 153468603Seric e->e_dfdev = st.st_dev; 153568603Seric e->e_dfino = st.st_ino; 153668603Seric } 153768564Seric } 153868564Seric } 153968564Seric 154051920Seric return TRUE; 15414632Seric } 15424632Seric /* 15439630Seric ** PRINTQUEUE -- print out a representation of the mail queue 15449630Seric ** 15459630Seric ** Parameters: 15469630Seric ** none. 15479630Seric ** 15489630Seric ** Returns: 15499630Seric ** none. 15509630Seric ** 15519630Seric ** Side Effects: 15529630Seric ** Prints a listing of the mail queue on the standard output. 15539630Seric */ 15545182Seric 155569748Seric void 15569630Seric printqueue() 15579630Seric { 15589630Seric register WORK *w; 15599630Seric FILE *f; 156010070Seric int nrequests; 15619630Seric char buf[MAXLINE]; 15629630Seric 15639630Seric /* 156458250Seric ** Check for permission to print the queue 156558250Seric */ 156658250Seric 156764333Seric if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 156858250Seric { 156958250Seric struct stat st; 157058523Seric # ifdef NGROUPS 157158523Seric int n; 157263937Seric GIDSET_T gidset[NGROUPS]; 157358523Seric # endif 157458250Seric 157558517Seric if (stat(QueueDir, &st) < 0) 157658250Seric { 157758250Seric syserr("Cannot stat %s", QueueDir); 157858250Seric return; 157958250Seric } 158058523Seric # ifdef NGROUPS 158158523Seric n = getgroups(NGROUPS, gidset); 158258523Seric while (--n >= 0) 158358523Seric { 158458523Seric if (gidset[n] == st.st_gid) 158558523Seric break; 158658523Seric } 1587*69835Seric if (n < 0 && RealGid != st.st_gid) 158858523Seric # else 158963787Seric if (RealGid != st.st_gid) 159058523Seric # endif 159158250Seric { 159258250Seric usrerr("510 You are not permitted to see the queue"); 159358250Seric setstat(EX_NOPERM); 159458250Seric return; 159558250Seric } 159658250Seric } 159758250Seric 159858250Seric /* 15999630Seric ** Read and order the queue. 16009630Seric */ 16019630Seric 160224941Seric nrequests = orderq(TRUE); 16039630Seric 16049630Seric /* 16059630Seric ** Print the work list that we have read. 16069630Seric */ 16079630Seric 16089630Seric /* first see if there is anything */ 160910070Seric if (nrequests <= 0) 16109630Seric { 161110070Seric printf("Mail queue is empty\n"); 16129630Seric return; 16139630Seric } 16149630Seric 161551920Seric CurrentLA = getla(); /* get load average */ 161640934Srick 161710096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 161869722Seric if (nrequests > WorkListSize) 161969722Seric printf(", only %d printed", WorkListSize); 162024979Seric if (Verbose) 162158716Seric printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 162224979Seric else 162358716Seric printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 16249630Seric for (w = WorkQ; w != NULL; w = w->w_next) 16259630Seric { 16269630Seric struct stat st; 162710070Seric auto time_t submittime = 0; 162868565Seric long dfsize; 162958737Seric int flags = 0; 163068481Seric int qfver; 163169538Seric char statmsg[MAXLINE]; 163268528Seric char bodytype[MAXNAME + 1]; 16339630Seric 163464355Seric printf("%8s", w->w_name + 2); 163517468Seric f = fopen(w->w_name, "r"); 163617468Seric if (f == NULL) 163717468Seric { 163864355Seric printf(" (job completed)\n"); 163917468Seric errno = 0; 164017468Seric continue; 164117468Seric } 164268565Seric w->w_name[0] = 'd'; 164368565Seric if (stat(w->w_name, &st) >= 0) 164468565Seric dfsize = st.st_size; 164568565Seric else 164668565Seric dfsize = -1; 164768481Seric if (w->w_lock) 164810070Seric printf("*"); 164957438Seric else if (shouldqueue(w->w_pri, w->w_ctime)) 165024941Seric printf("X"); 165110070Seric else 165210070Seric printf(" "); 165310070Seric errno = 0; 165417468Seric 165569538Seric statmsg[0] = bodytype[0] = '\0'; 165668481Seric qfver = 0; 16579630Seric while (fgets(buf, sizeof buf, f) != NULL) 16589630Seric { 165953400Seric register int i; 166058737Seric register char *p; 166153400Seric 16629630Seric fixcrlf(buf, TRUE); 16639630Seric switch (buf[0]) 16649630Seric { 166568481Seric case 'V': /* queue file version */ 166668481Seric qfver = atoi(&buf[1]); 166768481Seric break; 166868481Seric 166910108Seric case 'M': /* error message */ 167069538Seric if ((i = strlen(&buf[1])) >= sizeof statmsg) 167169538Seric i = sizeof statmsg - 1; 167269538Seric bcopy(&buf[1], statmsg, i); 167369538Seric statmsg[i] = '\0'; 167410108Seric break; 167510108Seric 167659093Seric case 'B': /* body type */ 167759093Seric if ((i = strlen(&buf[1])) >= sizeof bodytype) 167859093Seric i = sizeof bodytype - 1; 167959093Seric bcopy(&buf[1], bodytype, i); 168059093Seric bodytype[i] = '\0'; 168159093Seric break; 168259093Seric 16839630Seric case 'S': /* sender name */ 168424979Seric if (Verbose) 168558737Seric printf("%8ld %10ld%c%.12s %.38s", 168658737Seric dfsize, 168758737Seric w->w_pri, 168858737Seric bitset(EF_WARNING, flags) ? '+' : ' ', 168958737Seric ctime(&submittime) + 4, 169024979Seric &buf[1]); 169124979Seric else 169224979Seric printf("%8ld %.16s %.45s", dfsize, 169324979Seric ctime(&submittime), &buf[1]); 169469538Seric if (statmsg[0] != '\0' || bodytype[0] != '\0') 169559093Seric { 169659093Seric printf("\n %10.10s", bodytype); 169769538Seric if (statmsg[0] != '\0') 169869538Seric printf(" (%.60s)", statmsg); 169959093Seric } 17009630Seric break; 170151920Seric 170240973Sbostic case 'C': /* controlling user */ 170354974Seric if (Verbose) 170458716Seric printf("\n\t\t\t\t (---%.34s---)", 170558716Seric &buf[1]); 170640973Sbostic break; 17079630Seric 17089630Seric case 'R': /* recipient name */ 170968481Seric p = &buf[1]; 171068481Seric if (qfver >= 1) 171168481Seric { 171268481Seric p = strchr(p, ':'); 171368481Seric if (p == NULL) 171468481Seric break; 171568481Seric p++; 171668481Seric } 171724979Seric if (Verbose) 171868481Seric printf("\n\t\t\t\t\t %.38s", p); 171924979Seric else 172068481Seric printf("\n\t\t\t\t %.45s", p); 17219630Seric break; 17229630Seric 17239630Seric case 'T': /* creation time */ 172424941Seric submittime = atol(&buf[1]); 17259630Seric break; 172610070Seric 172758737Seric case 'F': /* flag bits */ 172858737Seric for (p = &buf[1]; *p != '\0'; p++) 172958737Seric { 173058737Seric switch (*p) 173158737Seric { 173258737Seric case 'w': 173358737Seric flags |= EF_WARNING; 173458737Seric break; 173558737Seric } 173658737Seric } 17379630Seric } 17389630Seric } 173910070Seric if (submittime == (time_t) 0) 174010070Seric printf(" (no control file)"); 17419630Seric printf("\n"); 174223098Seric (void) fclose(f); 17439630Seric } 17449630Seric } 17459630Seric 174656795Seric # endif /* QUEUE */ 174717468Seric /* 174817468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 174917468Seric ** 175017468Seric ** Assigns an id code if one does not already exist. 175117468Seric ** This code is very careful to avoid trashing existing files 175217468Seric ** under any circumstances. 175317468Seric ** 175417468Seric ** Parameters: 175517468Seric ** e -- envelope to build it in/from. 175617468Seric ** type -- the file type, used as the first character 175717468Seric ** of the file name. 175817468Seric ** 175917468Seric ** Returns: 176017468Seric ** a pointer to the new file name (in a static buffer). 176117468Seric ** 176217468Seric ** Side Effects: 176351920Seric ** If no id code is already assigned, queuename will 176451920Seric ** assign an id code, create a qf file, and leave a 176551920Seric ** locked, open-for-write file pointer in the envelope. 176617468Seric */ 176717468Seric 176817468Seric char * 176917468Seric queuename(e, type) 177017468Seric register ENVELOPE *e; 177159700Seric int type; 177217468Seric { 177317468Seric static int pid = -1; 177458741Seric static char c0; 177558741Seric static char c1; 177658741Seric static char c2; 177758716Seric time_t now; 177858716Seric struct tm *tm; 177968528Seric static char buf[MAXNAME + 1]; 178017468Seric 178117468Seric if (e->e_id == NULL) 178217468Seric { 178317468Seric char qf[20]; 178417468Seric 178517468Seric /* find a unique id */ 178617468Seric if (pid != getpid()) 178717468Seric { 178817468Seric /* new process -- start back at "AA" */ 178917468Seric pid = getpid(); 179058716Seric now = curtime(); 179158716Seric tm = localtime(&now); 179258716Seric c0 = 'A' + tm->tm_hour; 179317468Seric c1 = 'A'; 179417468Seric c2 = 'A' - 1; 179517468Seric } 179658716Seric (void) sprintf(qf, "qf%cAA%05d", c0, pid); 179717468Seric 179817468Seric while (c1 < '~' || c2 < 'Z') 179917468Seric { 180017468Seric int i; 180117468Seric 180217468Seric if (c2 >= 'Z') 180317468Seric { 180417468Seric c1++; 180517468Seric c2 = 'A' - 1; 180617468Seric } 180758716Seric qf[3] = c1; 180858716Seric qf[4] = ++c2; 180917468Seric if (tTd(7, 20)) 181040934Srick printf("queuename: trying \"%s\"\n", qf); 181117468Seric 181240934Srick i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); 181351920Seric if (i < 0) 181451920Seric { 181551920Seric if (errno == EEXIST) 181651920Seric continue; 181764705Seric syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", 181864705Seric qf, QueueDir, geteuid()); 181951920Seric exit(EX_UNAVAILABLE); 182051920Seric } 182164335Seric if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) 182251920Seric { 182351920Seric e->e_lockfp = fdopen(i, "w"); 182440934Srick break; 182517468Seric } 182651920Seric 182751920Seric /* a reader got the file; abandon it and try again */ 182851920Seric (void) close(i); 182917468Seric } 183017468Seric if (c1 >= '~' && c2 >= 'Z') 183117468Seric { 183264705Seric syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", 183364705Seric qf, QueueDir, geteuid()); 183417468Seric exit(EX_OSERR); 183517468Seric } 183617468Seric e->e_id = newstr(&qf[2]); 183717468Seric define('i', e->e_id, e); 183817468Seric if (tTd(7, 1)) 183917468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 184064745Seric if (tTd(7, 9)) 184164745Seric { 184264745Seric printf(" lockfd="); 184364745Seric dumpfd(fileno(e->e_lockfp), TRUE, FALSE); 184464745Seric } 184517468Seric # ifdef LOG 184658020Seric if (LogLevel > 93) 184717468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 184856795Seric # endif /* LOG */ 184917468Seric } 185017468Seric 185117468Seric if (type == '\0') 185217468Seric return (NULL); 185317468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 185417468Seric if (tTd(7, 2)) 185517468Seric printf("queuename: %s\n", buf); 185617468Seric return (buf); 185717468Seric } 185817468Seric /* 185917468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 186017468Seric ** 186117468Seric ** Parameters: 186217468Seric ** e -- the envelope to unlock. 186317468Seric ** 186417468Seric ** Returns: 186517468Seric ** none 186617468Seric ** 186717468Seric ** Side Effects: 186817468Seric ** unlocks the queue for `e'. 186917468Seric */ 187017468Seric 187169748Seric void 187217468Seric unlockqueue(e) 187317468Seric ENVELOPE *e; 187417468Seric { 187558680Seric if (tTd(51, 4)) 187658680Seric printf("unlockqueue(%s)\n", e->e_id); 187758680Seric 187851920Seric /* if there is a lock file in the envelope, close it */ 187951920Seric if (e->e_lockfp != NULL) 188058680Seric xfclose(e->e_lockfp, "unlockqueue", e->e_id); 188151920Seric e->e_lockfp = NULL; 188251920Seric 188358728Seric /* don't create a queue id if we don't already have one */ 188458728Seric if (e->e_id == NULL) 188558728Seric return; 188658728Seric 188717468Seric /* remove the transcript */ 188817468Seric # ifdef LOG 188958020Seric if (LogLevel > 87) 189017468Seric syslog(LOG_DEBUG, "%s: unlock", e->e_id); 189156795Seric # endif /* LOG */ 189258680Seric if (!tTd(51, 104)) 189317468Seric xunlink(queuename(e, 'x')); 189417468Seric 189517468Seric } 189640973Sbostic /* 189754974Seric ** SETCTLUSER -- create a controlling address 189840973Sbostic ** 189954974Seric ** Create a fake "address" given only a local login name; this is 190054974Seric ** used as a "controlling user" for future recipient addresses. 190140973Sbostic ** 190240973Sbostic ** Parameters: 190354974Seric ** user -- the user name of the controlling user. 190440973Sbostic ** 190540973Sbostic ** Returns: 190654974Seric ** An address descriptor for the controlling user. 190740973Sbostic ** 190840973Sbostic ** Side Effects: 190940973Sbostic ** none. 191040973Sbostic */ 191140973Sbostic 191254974Seric ADDRESS * 191354974Seric setctluser(user) 191454974Seric char *user; 191540973Sbostic { 191654974Seric register ADDRESS *a; 191740973Sbostic struct passwd *pw; 191859113Seric char *p; 191940973Sbostic 192040973Sbostic /* 192154974Seric ** See if this clears our concept of controlling user. 192240973Sbostic */ 192340973Sbostic 192463850Seric if (user == NULL || *user == '\0') 192563850Seric return NULL; 192640973Sbostic 192740973Sbostic /* 192854974Seric ** Set up addr fields for controlling user. 192940973Sbostic */ 193040973Sbostic 193154974Seric a = (ADDRESS *) xalloc(sizeof *a); 193254974Seric bzero((char *) a, sizeof *a); 193359113Seric 193459113Seric p = strchr(user, ':'); 193559113Seric if (p != NULL) 193659113Seric *p++ = '\0'; 193768693Seric if (*user != '\0' && (pw = sm_getpwnam(user)) != NULL) 193840973Sbostic { 193965822Seric if (strcmp(pw->pw_dir, "/") == 0) 194065822Seric a->q_home = ""; 194165822Seric else 194265822Seric a->q_home = newstr(pw->pw_dir); 194340973Sbostic a->q_uid = pw->pw_uid; 194440973Sbostic a->q_gid = pw->pw_gid; 194559270Seric a->q_flags |= QGOODUID; 194640973Sbostic } 194768603Seric 194868603Seric if (*user != '\0') 194968603Seric a->q_user = newstr(user); 195068603Seric else if (p != NULL) 195168603Seric a->q_user = newstr(p); 195240973Sbostic else 195357642Seric a->q_user = newstr(DefUser); 195440973Sbostic 195559270Seric a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 195656328Seric a->q_mailer = LocalMailer; 195759113Seric if (p == NULL) 195859113Seric a->q_paddr = a->q_user; 195959113Seric else 196059113Seric a->q_paddr = newstr(p); 196154974Seric return a; 196240973Sbostic } 196368490Seric /* 196468490Seric ** LOSEQFILE -- save the qf as Qf and try to let someone know 196568490Seric ** 196668490Seric ** Parameters: 196768490Seric ** e -- the envelope (e->e_id will be used). 196868490Seric ** why -- reported to whomever can hear. 196968490Seric ** 197068490Seric ** Returns: 197168490Seric ** none. 197268490Seric */ 197368490Seric 197468490Seric void 197568490Seric loseqfile(e, why) 197668490Seric register ENVELOPE *e; 197768490Seric char *why; 197868490Seric { 197968563Seric char *p; 198068490Seric char buf[40]; 198168490Seric 198268490Seric if (e == NULL || e->e_id == NULL) 198368490Seric return; 198468490Seric if (strlen(e->e_id) > sizeof buf - 4) 198568490Seric return; 198668490Seric strcpy(buf, queuename(e, 'q')); 198768563Seric p = queuename(e, 'Q'); 198868563Seric if (rename(buf, p) < 0) 198968563Seric syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); 199068490Seric #ifdef LOG 199168563Seric else if (LogLevel > 0) 199268563Seric syslog(LOG_ALERT, "Losing %s: %s", buf, why); 199368490Seric #endif 199468490Seric } 1995