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*69839Seric static char sccsid[] = "@(#)queue.c 8.87 (Berkeley) 06/10/95 (with queueing)"; 1433731Sbostic #else 15*69839Seric static char sccsid[] = "@(#)queue.c 8.87 (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); 530*69839Seric #else 531*69839Seric CurChildren++; 53256795Seric #endif /* SIGCHLD */ 5337690Seric if (QueueIntvl != 0) 5349348Seric (void) setevent(QueueIntvl, runqueue, TRUE); 5354639Seric return; 5364639Seric } 5377943Seric /* child -- double fork */ 53825184Seric #ifndef SIGCHLD 5397943Seric if (fork() != 0) 5407943Seric exit(EX_OK); 54156795Seric #else /* SIGCHLD */ 54264035Seric (void) setsignal(SIGCHLD, SIG_DFL); 54356795Seric #endif /* SIGCHLD */ 5444639Seric } 54524941Seric 54640934Srick setproctitle("running queue: %s", QueueDir); 54724941Seric 5487876Seric # ifdef LOG 54958020Seric if (LogLevel > 69) 55055360Seric syslog(LOG_DEBUG, "runqueue %s, pid=%d, forkflag=%d", 55155360Seric QueueDir, getpid(), forkflag); 55256795Seric # endif /* LOG */ 5534639Seric 5547466Seric /* 55510205Seric ** Release any resources used by the daemon code. 55610205Seric */ 55710205Seric 55810205Seric # ifdef DAEMON 55910205Seric clrdaemon(); 56056795Seric # endif /* DAEMON */ 56110205Seric 56264658Seric /* force it to run expensive jobs */ 56364658Seric NoConnect = FALSE; 56464658Seric 56510205Seric /* 56655360Seric ** Create ourselves an envelope 56755360Seric */ 56855360Seric 56955360Seric CurEnv = &QueueEnvelope; 57058179Seric e = newenvelope(&QueueEnvelope, CurEnv); 57155360Seric e->e_flags = BlankEnvelope.e_flags; 57255360Seric 57355360Seric /* 57427175Seric ** Make sure the alias database is open. 57527175Seric */ 57627175Seric 57760537Seric initmaps(FALSE, e); 57827175Seric 57927175Seric /* 5807466Seric ** Start making passes through the queue. 5817466Seric ** First, read and sort the entire queue. 5827466Seric ** Then, process the work in that order. 5837466Seric ** But if you take too long, start over. 5847466Seric */ 5857466Seric 5867943Seric /* order the existing work requests */ 58768481Seric njobs = orderq(FALSE); 5887690Seric 5897943Seric /* process them once at a time */ 5907943Seric while (WorkQ != NULL) 5914639Seric { 5927943Seric WORK *w = WorkQ; 5937881Seric 5947943Seric WorkQ = WorkQ->w_next; 59558884Seric 59658884Seric /* 59758884Seric ** Ignore jobs that are too expensive for the moment. 59858884Seric */ 59958884Seric 60068481Seric sequenceno++; 60158884Seric if (shouldqueue(w->w_pri, w->w_ctime)) 60258884Seric { 60358884Seric if (Verbose) 60468481Seric printf("\nSkipping %s (sequence %d of %d)\n", 60568481Seric w->w_name + 2, sequenceno, njobs); 60658884Seric } 60758930Seric else 60858930Seric { 60964296Seric pid_t pid; 61064296Seric extern pid_t dowork(); 61164296Seric 61268481Seric if (Verbose) 61368481Seric printf("\nRunning %s (sequence %d of %d)\n", 61468481Seric w->w_name + 2, sequenceno, njobs); 61564296Seric pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); 61664296Seric errno = 0; 61766316Seric if (pid != 0) 61866316Seric (void) waitfor(pid); 61958930Seric } 6207943Seric free(w->w_name); 62168481Seric if (w->w_host) 62268481Seric free(w->w_host); 6237943Seric free((char *) w); 6244639Seric } 62529866Seric 62629866Seric /* exit without the usual cleanup */ 62755467Seric e->e_id = NULL; 62855467Seric finis(); 6294634Seric } 6304634Seric /* 6314632Seric ** ORDERQ -- order the work queue. 6324632Seric ** 6334632Seric ** Parameters: 63424941Seric ** doall -- if set, include everything in the queue (even 63524941Seric ** the jobs that cannot be run because the load 63624941Seric ** average is too high). Otherwise, exclude those 63724941Seric ** jobs. 6384632Seric ** 6394632Seric ** Returns: 64010121Seric ** The number of request in the queue (not necessarily 64110121Seric ** the number of requests in WorkQ however). 6424632Seric ** 6434632Seric ** Side Effects: 6444632Seric ** Sets WorkQ to the queue of available work, in order. 6454632Seric */ 6464632Seric 64725687Seric # define NEED_P 001 64825687Seric # define NEED_T 002 64958318Seric # define NEED_R 004 65058318Seric # define NEED_S 010 6514632Seric 65269722Seric static WORK *WorkList = NULL; 65369722Seric static int WorkListSize = 0; 65469722Seric 65569748Seric int 65624941Seric orderq(doall) 65724941Seric bool doall; 6584632Seric { 65960219Seric register struct dirent *d; 6604632Seric register WORK *w; 6616625Sglickman DIR *f; 6624632Seric register int i; 66310070Seric int wn = -1; 66468481Seric int wc; 6654632Seric 66658318Seric if (tTd(41, 1)) 66758318Seric { 66858318Seric printf("orderq:\n"); 66958318Seric if (QueueLimitId != NULL) 67058318Seric printf("\tQueueLimitId = %s\n", QueueLimitId); 67158318Seric if (QueueLimitSender != NULL) 67258318Seric printf("\tQueueLimitSender = %s\n", QueueLimitSender); 67358318Seric if (QueueLimitRecipient != NULL) 67458318Seric printf("\tQueueLimitRecipient = %s\n", QueueLimitRecipient); 67558318Seric } 67658318Seric 6774632Seric /* clear out old WorkQ */ 6784632Seric for (w = WorkQ; w != NULL; ) 6794632Seric { 6804632Seric register WORK *nw = w->w_next; 6814632Seric 6824632Seric WorkQ = nw; 6834632Seric free(w->w_name); 68468481Seric if (w->w_host) 68568481Seric free(w->w_host); 6864632Seric free((char *) w); 6874632Seric w = nw; 6884632Seric } 6894632Seric 6904632Seric /* open the queue directory */ 6918148Seric f = opendir("."); 6924632Seric if (f == NULL) 6934632Seric { 6948148Seric syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 69510070Seric return (0); 6964632Seric } 6974632Seric 6984632Seric /* 6994632Seric ** Read the work directory. 7004632Seric */ 7014632Seric 70210070Seric while ((d = readdir(f)) != NULL) 7034632Seric { 7049377Seric FILE *cf; 70564492Seric register char *p; 70668528Seric char lbuf[MAXNAME + 1]; 70758318Seric extern bool strcontainedin(); 7084632Seric 7094632Seric /* is this an interesting entry? */ 7107812Seric if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 7114632Seric continue; 7124632Seric 71358318Seric if (QueueLimitId != NULL && 71458318Seric !strcontainedin(QueueLimitId, d->d_name)) 71558318Seric continue; 71658318Seric 71768563Seric #ifdef PICKY_QF_NAME_CHECK 71858722Seric /* 71958722Seric ** Check queue name for plausibility. This handles 72058722Seric ** both old and new type ids. 72158722Seric */ 72258722Seric 72364492Seric p = d->d_name + 2; 72464492Seric if (isupper(p[0]) && isupper(p[2])) 72564492Seric p += 3; 72664492Seric else if (isupper(p[1])) 72764492Seric p += 2; 72864492Seric else 72964492Seric p = d->d_name; 73064492Seric for (i = 0; isdigit(*p); p++) 73164492Seric i++; 73264492Seric if (i < 5 || *p != '\0') 73358020Seric { 73458020Seric if (Verbose) 73558020Seric printf("orderq: bogus qf name %s\n", d->d_name); 73668563Seric # ifdef LOG 73768563Seric if (LogLevel > 0) 73868563Seric syslog(LOG_ALERT, "orderq: bogus qf name %s", 73958020Seric d->d_name); 74068563Seric # endif 74168706Seric if (strlen(d->d_name) > (SIZE_T) MAXNAME) 74268528Seric d->d_name[MAXNAME] = '\0'; 74358020Seric strcpy(lbuf, d->d_name); 74458020Seric lbuf[0] = 'Q'; 74558020Seric (void) rename(d->d_name, lbuf); 74658020Seric continue; 74758020Seric } 74868563Seric #endif 74958020Seric 75068563Seric /* open control file (if not too many files) */ 75169746Seric if (++wn > MaxQueueRun && MaxQueueRun > 0) 75269722Seric { 75369746Seric # ifdef LOG 75469746Seric if (wn == MaxQueueRun + 1 && LogLevel > 0) 75569746Seric syslog(LOG_ALERT, "WorkList for %s maxed out at %d", 75669746Seric QueueDir, MaxQueueRun); 75769746Seric # endif 75869746Seric continue; 75969746Seric } 76069746Seric if (wn >= WorkListSize) 76169746Seric { 76269748Seric extern void grow_wlist __P((void)); 76369748Seric 76469722Seric grow_wlist(); 76569722Seric if (wn >= WorkListSize) 76669722Seric continue; 76769722Seric } 76858318Seric 7698148Seric cf = fopen(d->d_name, "r"); 7704632Seric if (cf == NULL) 7714632Seric { 7727055Seric /* this may be some random person sending hir msgs */ 7737055Seric /* syserr("orderq: cannot open %s", cbuf); */ 77410090Seric if (tTd(41, 2)) 77510090Seric printf("orderq: cannot open %s (%d)\n", 77610090Seric d->d_name, errno); 7777055Seric errno = 0; 77810090Seric wn--; 7794632Seric continue; 7804632Seric } 78169722Seric w = &WorkList[wn]; 78225687Seric w->w_name = newstr(d->d_name); 78368481Seric w->w_host = NULL; 78468481Seric w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); 7854632Seric 78625027Seric /* make sure jobs in creation don't clog queue */ 78725687Seric w->w_pri = 0x7fffffff; 78825687Seric w->w_ctime = 0; 78925027Seric 7904632Seric /* extract useful information */ 79125687Seric i = NEED_P | NEED_T; 79258318Seric if (QueueLimitSender != NULL) 79358318Seric i |= NEED_S; 79468481Seric if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL) 79558318Seric i |= NEED_R; 79625687Seric while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 7974632Seric { 79858318Seric extern bool strcontainedin(); 79924954Seric 80024941Seric switch (lbuf[0]) 8014632Seric { 80224941Seric case 'P': 80325687Seric w->w_pri = atol(&lbuf[1]); 80425687Seric i &= ~NEED_P; 8054632Seric break; 80625013Seric 80725013Seric case 'T': 80825687Seric w->w_ctime = atol(&lbuf[1]); 80925687Seric i &= ~NEED_T; 81025013Seric break; 81158318Seric 81258318Seric case 'R': 81368481Seric if (w->w_host == NULL && 81468481Seric (p = strrchr(&lbuf[1], '@')) != NULL) 81568481Seric w->w_host = newstr(&p[1]); 81668481Seric if (QueueLimitRecipient == NULL || 81758318Seric strcontainedin(QueueLimitRecipient, &lbuf[1])) 81858318Seric i &= ~NEED_R; 81958318Seric break; 82058318Seric 82158318Seric case 'S': 82258318Seric if (QueueLimitSender != NULL && 82358318Seric strcontainedin(QueueLimitSender, &lbuf[1])) 82458318Seric i &= ~NEED_S; 82558318Seric break; 8264632Seric } 8274632Seric } 8284632Seric (void) fclose(cf); 82924953Seric 83058318Seric if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || 83158318Seric bitset(NEED_R|NEED_S, i)) 83224953Seric { 83324953Seric /* don't even bother sorting this job in */ 83468481Seric free(w->w_name); 83568481Seric if (w->w_host) 83668481Seric free(w->w_host); 83724953Seric wn--; 83824953Seric } 8394632Seric } 8406625Sglickman (void) closedir(f); 84110090Seric wn++; 8424632Seric 84369722Seric wc = min(wn, WorkListSize); 8444632Seric 84568481Seric if (QueueSortOrder == QS_BYHOST) 84668481Seric { 84768481Seric extern workcmpf1(); 84868481Seric extern workcmpf2(); 84967612Seric 85068481Seric /* 85168481Seric ** Sort the work directory for the first time, 85268481Seric ** based on host name, lock status, and priority. 85368481Seric */ 85468481Seric 85569722Seric qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); 85668481Seric 85768481Seric /* 85868481Seric ** If one message to host is locked, "lock" all messages 85968481Seric ** to that host. 86068481Seric */ 86168481Seric 86268481Seric i = 0; 86368481Seric while (i < wc) 86468481Seric { 86569722Seric if (!WorkList[i].w_lock) 86668481Seric { 86768481Seric i++; 86868481Seric continue; 86968481Seric } 87069722Seric w = &WorkList[i]; 87168481Seric while (++i < wc) 87268481Seric { 87369722Seric if (WorkList[i].w_host == NULL && 87468481Seric w->w_host == NULL) 87569722Seric WorkList[i].w_lock = TRUE; 87669722Seric else if (WorkList[i].w_host != NULL && 87768481Seric w->w_host != NULL && 87869722Seric strcmp(WorkList[i].w_host, w->w_host) == 0) 87969722Seric WorkList[i].w_lock = TRUE; 88068481Seric else 88168481Seric break; 88268481Seric } 88368481Seric } 88468481Seric 88568481Seric /* 88668481Seric ** Sort the work directory for the second time, 88768481Seric ** based on lock status, host name, and priority. 88868481Seric */ 88968481Seric 89069722Seric qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); 89168481Seric } 89268481Seric else 89368481Seric { 89468481Seric extern workcmpf0(); 89568481Seric 89668481Seric /* 89768481Seric ** Simple sort based on queue priority only. 89868481Seric */ 89968481Seric 90069722Seric qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); 90168481Seric } 90268481Seric 9034632Seric /* 9044632Seric ** Convert the work list into canonical form. 9059377Seric ** Should be turning it into a list of envelopes here perhaps. 9064632Seric */ 9074632Seric 90824981Seric WorkQ = NULL; 90968481Seric for (i = wc; --i >= 0; ) 9104632Seric { 9114632Seric w = (WORK *) xalloc(sizeof *w); 91269722Seric w->w_name = WorkList[i].w_name; 91369722Seric w->w_host = WorkList[i].w_host; 91469722Seric w->w_lock = WorkList[i].w_lock; 91569722Seric w->w_pri = WorkList[i].w_pri; 91669722Seric w->w_ctime = WorkList[i].w_ctime; 91724981Seric w->w_next = WorkQ; 91824981Seric WorkQ = w; 9194632Seric } 92069722Seric free(WorkList); 92169722Seric WorkList = NULL; 9224632Seric 9237677Seric if (tTd(40, 1)) 9244632Seric { 9254632Seric for (w = WorkQ; w != NULL; w = w->w_next) 9265037Seric printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 9274632Seric } 92810070Seric 92910090Seric return (wn); 9304632Seric } 93169746Seric /* 93269746Seric ** GROW_WLIST -- make the work list larger 93369746Seric ** 93469746Seric ** Parameters: 93569746Seric ** none. 93669746Seric ** 93769746Seric ** Returns: 93869746Seric ** none. 93969746Seric ** 94069746Seric ** Side Effects: 94169746Seric ** Adds another QUEUESEGSIZE entries to WorkList if possible. 94269746Seric ** It can fail if there isn't enough memory, so WorkListSize 94369746Seric ** should be checked again upon return. 94469746Seric */ 94569722Seric 94669748Seric void 94769722Seric grow_wlist() 94869722Seric { 94969722Seric if (tTd(41, 1)) 95069722Seric printf("grow_wlist: WorkListSize=%d\n", WorkListSize); 95169746Seric if (WorkList == NULL) 95269722Seric { 95369722Seric WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1)); 95469722Seric WorkListSize = QUEUESEGSIZE; 95569722Seric } 95669722Seric else 95769722Seric { 95869722Seric int newsize = WorkListSize + QUEUESEGSIZE; 95969722Seric WORK *newlist = (WORK *) realloc((char *)WorkList, 96069722Seric (unsigned)sizeof(WORK) * (newsize + 1)); 96169722Seric 96269722Seric if (newlist != NULL) 96369722Seric { 96469722Seric WorkListSize = newsize; 96569722Seric WorkList = newlist; 96669722Seric # ifdef LOG 96769722Seric if (LogLevel > 1) 96869722Seric { 96969722Seric syslog(LOG_NOTICE, "grew WorkList for %s to %d", 97069722Seric QueueDir, WorkListSize); 97169722Seric } 97269722Seric } 97369722Seric else if (LogLevel > 0) 97469722Seric { 97569722Seric syslog(LOG_ALERT, "FAILED to grow WorkList for %s to %d", 97669722Seric QueueDir, newsize); 97769722Seric # endif 97869722Seric } 97969722Seric } 98069722Seric if (tTd(41, 1)) 98169722Seric printf("grow_wlist: WorkListSize now %d\n", WorkListSize); 98269722Seric } 9834632Seric /* 98468481Seric ** WORKCMPF0 -- simple priority-only compare function. 9854632Seric ** 9864632Seric ** Parameters: 9874632Seric ** a -- the first argument. 9884632Seric ** b -- the second argument. 9894632Seric ** 9904632Seric ** Returns: 99124981Seric ** -1 if a < b 99224981Seric ** 0 if a == b 99324981Seric ** +1 if a > b 9944632Seric ** 9954632Seric ** Side Effects: 9964632Seric ** none. 9974632Seric */ 9984632Seric 99969748Seric int 100068481Seric workcmpf0(a, b) 10015037Seric register WORK *a; 10025037Seric register WORK *b; 10034632Seric { 100457438Seric long pa = a->w_pri; 100557438Seric long pb = b->w_pri; 100624941Seric 100724941Seric if (pa == pb) 100868481Seric return 0; 100924941Seric else if (pa > pb) 101068481Seric return 1; 101124981Seric else 101268481Seric return -1; 10134632Seric } 10144632Seric /* 101568481Seric ** WORKCMPF1 -- first compare function for ordering work based on host name. 101668481Seric ** 101768481Seric ** Sorts on host name, lock status, and priority in that order. 101868481Seric ** 101968481Seric ** Parameters: 102068481Seric ** a -- the first argument. 102168481Seric ** b -- the second argument. 102268481Seric ** 102368481Seric ** Returns: 102468481Seric ** <0 if a < b 102568481Seric ** 0 if a == b 102668481Seric ** >0 if a > b 102768481Seric ** 102868481Seric ** Side Effects: 102968481Seric ** none. 103068481Seric */ 103168481Seric 103269748Seric int 103368481Seric workcmpf1(a, b) 103468481Seric register WORK *a; 103568481Seric register WORK *b; 103668481Seric { 103768481Seric int i; 103868481Seric 103968481Seric /* host name */ 104068481Seric if (a->w_host != NULL && b->w_host == NULL) 104168481Seric return 1; 104268481Seric else if (a->w_host == NULL && b->w_host != NULL) 104368481Seric return -1; 104468481Seric if (a->w_host != NULL && b->w_host != NULL && 104568481Seric (i = strcmp(a->w_host, b->w_host))) 104668481Seric return i; 104768481Seric 104868481Seric /* lock status */ 104968481Seric if (a->w_lock != b->w_lock) 105068481Seric return b->w_lock - a->w_lock; 105168481Seric 105268481Seric /* job priority */ 105368481Seric return a->w_pri - b->w_pri; 105468481Seric } 105568481Seric /* 105668481Seric ** WORKCMPF2 -- second compare function for ordering work based on host name. 105768481Seric ** 105868481Seric ** Sorts on lock status, host name, and priority in that order. 105968481Seric ** 106068481Seric ** Parameters: 106168481Seric ** a -- the first argument. 106268481Seric ** b -- the second argument. 106368481Seric ** 106468481Seric ** Returns: 106568481Seric ** <0 if a < b 106668481Seric ** 0 if a == b 106768481Seric ** >0 if a > b 106868481Seric ** 106968481Seric ** Side Effects: 107068481Seric ** none. 107168481Seric */ 107268481Seric 107369748Seric int 107468481Seric workcmpf2(a, b) 107568481Seric register WORK *a; 107668481Seric register WORK *b; 107768481Seric { 107868481Seric int i; 107968481Seric 108068481Seric /* lock status */ 108168481Seric if (a->w_lock != b->w_lock) 108268481Seric return a->w_lock - b->w_lock; 108368481Seric 108468481Seric /* host name */ 108568481Seric if (a->w_host != NULL && b->w_host == NULL) 108668481Seric return 1; 108768481Seric else if (a->w_host == NULL && b->w_host != NULL) 108868481Seric return -1; 108968481Seric if (a->w_host != NULL && b->w_host != NULL && 109068481Seric (i = strcmp(a->w_host, b->w_host))) 109168481Seric return i; 109268481Seric 109368481Seric /* job priority */ 109468481Seric return a->w_pri - b->w_pri; 109568481Seric } 109668481Seric /* 10974632Seric ** DOWORK -- do a work request. 10984632Seric ** 10994632Seric ** Parameters: 110058884Seric ** id -- the ID of the job to run. 110158884Seric ** forkflag -- if set, run this in background. 110258924Seric ** requeueflag -- if set, reinstantiate the queue quickly. 110358924Seric ** This is used when expanding aliases in the queue. 110461094Seric ** If forkflag is also set, it doesn't wait for the 110561094Seric ** child. 110658884Seric ** e - the envelope in which to run it. 11074632Seric ** 11084632Seric ** Returns: 110964296Seric ** process id of process that is running the queue job. 11104632Seric ** 11114632Seric ** Side Effects: 11124632Seric ** The work request is satisfied if possible. 11134632Seric */ 11144632Seric 111564296Seric pid_t 111658924Seric dowork(id, forkflag, requeueflag, e) 111758884Seric char *id; 111858884Seric bool forkflag; 111958924Seric bool requeueflag; 112055012Seric register ENVELOPE *e; 11214632Seric { 112264296Seric register pid_t pid; 112351920Seric extern bool readqf(); 11244632Seric 11257677Seric if (tTd(40, 1)) 112658884Seric printf("dowork(%s)\n", id); 11274632Seric 11284632Seric /* 112924941Seric ** Fork for work. 113024941Seric */ 113124941Seric 113258884Seric if (forkflag) 113324941Seric { 113464296Seric pid = fork(); 113564296Seric if (pid < 0) 113624941Seric { 113724941Seric syserr("dowork: cannot fork"); 113864296Seric return 0; 113924941Seric } 114064839Seric else if (pid > 0) 114164839Seric { 114264839Seric /* parent -- clean out connection cache */ 114364839Seric mci_flush(FALSE, NULL); 114464839Seric } 114524941Seric } 114624941Seric else 114724941Seric { 114864296Seric pid = 0; 114924941Seric } 115024941Seric 115164296Seric if (pid == 0) 11524632Seric { 11534632Seric /* 11544632Seric ** CHILD 11558148Seric ** Lock the control file to avoid duplicate deliveries. 11568148Seric ** Then run the file as though we had just read it. 11577350Seric ** We save an idea of the temporary name so we 11587350Seric ** can recover on interrupt. 11594632Seric */ 11604632Seric 11617763Seric /* set basic modes, etc. */ 11627356Seric (void) alarm(0); 116355012Seric clearenvelope(e, FALSE); 116464554Seric e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 116558734Seric e->e_errormode = EM_MAIL; 116658884Seric e->e_id = id; 116764765Seric GrabTo = UseErrorsTo = FALSE; 116866316Seric ExitStat = EX_OK; 116963846Seric if (forkflag) 117064554Seric { 117164296Seric disconnect(1, e); 117264554Seric OpMode = MD_DELIVER; 117364554Seric } 11747876Seric # ifdef LOG 117558020Seric if (LogLevel > 76) 117655012Seric syslog(LOG_DEBUG, "%s: dowork, pid=%d", e->e_id, 11777881Seric getpid()); 117856795Seric # endif /* LOG */ 11797763Seric 11807763Seric /* don't use the headers from sendmail.cf... */ 118155012Seric e->e_header = NULL; 11827763Seric 118351920Seric /* read the queue control file -- return if locked */ 118465200Seric if (!readqf(e)) 11856980Seric { 118658884Seric if (tTd(40, 4)) 118758884Seric printf("readqf(%s) failed\n", e->e_id); 118858884Seric if (forkflag) 118924941Seric exit(EX_OK); 119024941Seric else 119166316Seric return 0; 11926980Seric } 11936980Seric 119455012Seric e->e_flags |= EF_INQUEUE; 11956980Seric 119668481Seric /* if this has been tried recently, let it be */ 119768481Seric if (e->e_ntries > 0 && (curtime() - e->e_dtime) < MinQueueAge) 119868481Seric { 119968481Seric char *howlong = pintvl(curtime() - e->e_dtime, TRUE); 120058924Seric 120168481Seric e->e_flags |= EF_KEEPQUEUE; 120268481Seric if (Verbose || tTd(40, 8)) 120368481Seric printf("%s: too young (%s)\n", 120468481Seric e->e_id, howlong); 120568481Seric #ifdef LOG 120668481Seric if (LogLevel > 19) 120768481Seric syslog(LOG_DEBUG, "%s: too young (%s)", 120868481Seric e->e_id, howlong); 120968481Seric #endif 121068481Seric } 121168481Seric else 121268481Seric { 121368481Seric eatheader(e, requeueflag); 12146980Seric 121568481Seric if (requeueflag) 121668481Seric queueup(e, TRUE, FALSE); 121768481Seric 121868481Seric /* do the delivery */ 121968481Seric sendall(e, SM_DELIVER); 122068481Seric } 122168481Seric 12226980Seric /* finish up and exit */ 122358884Seric if (forkflag) 122424941Seric finis(); 122524941Seric else 122655012Seric dropenvelope(e); 12274632Seric } 122864296Seric e->e_id = NULL; 122964296Seric return pid; 12304632Seric } 12314632Seric /* 12324632Seric ** READQF -- read queue file and set up environment. 12334632Seric ** 12344632Seric ** Parameters: 12359377Seric ** e -- the envelope of the job to run. 12364632Seric ** 12374632Seric ** Returns: 123851920Seric ** TRUE if it successfully read the queue file. 123951920Seric ** FALSE otherwise. 12404632Seric ** 12414632Seric ** Side Effects: 124251920Seric ** The queue file is returned locked. 12434632Seric */ 12444632Seric 124551920Seric bool 124665200Seric readqf(e) 12479377Seric register ENVELOPE *e; 12484632Seric { 124917477Seric register FILE *qfp; 125054974Seric ADDRESS *ctladdr; 125156400Seric struct stat st; 125257135Seric char *bp; 125368481Seric int qfver = 0; 125468481Seric register char *p; 125568481Seric char *orcpt = NULL; 125658915Seric char qf[20]; 125757135Seric char buf[MAXLINE]; 125854974Seric extern ADDRESS *setctluser(); 125968490Seric extern void loseqfile(); 12604632Seric 12614632Seric /* 126217468Seric ** Read and process the file. 12634632Seric */ 12644632Seric 126558915Seric strcpy(qf, queuename(e, 'q')); 126651937Seric qfp = fopen(qf, "r+"); 126717477Seric if (qfp == NULL) 126817477Seric { 126958884Seric if (tTd(40, 8)) 127058884Seric printf("readqf(%s): fopen failure (%s)\n", 127158884Seric qf, errstring(errno)); 127240934Srick if (errno != ENOENT) 127340934Srick syserr("readqf: no control file %s", qf); 127451920Seric return FALSE; 127517477Seric } 127640934Srick 127764335Seric if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) 127864277Seric { 127964277Seric /* being processed by another queuer */ 128068481Seric if (Verbose || tTd(40, 8)) 128164277Seric printf("%s: locked\n", e->e_id); 128264277Seric # ifdef LOG 128364277Seric if (LogLevel > 19) 128464277Seric syslog(LOG_DEBUG, "%s: locked", e->e_id); 128564277Seric # endif /* LOG */ 128664277Seric (void) fclose(qfp); 128764277Seric return FALSE; 128864277Seric } 128964277Seric 129056400Seric /* 129156400Seric ** Check the queue file for plausibility to avoid attacks. 129256400Seric */ 129356400Seric 129456400Seric if (fstat(fileno(qfp), &st) < 0) 129556400Seric { 129656400Seric /* must have been being processed by someone else */ 129758884Seric if (tTd(40, 8)) 129858884Seric printf("readqf(%s): fstat failure (%s)\n", 129958884Seric qf, errstring(errno)); 130056400Seric fclose(qfp); 130156400Seric return FALSE; 130256400Seric } 130356400Seric 130468563Seric if (st.st_uid != geteuid() || bitset(S_IWOTH|S_IWGRP, st.st_mode)) 130556400Seric { 130656400Seric # ifdef LOG 130756400Seric if (LogLevel > 0) 130856400Seric { 130956400Seric syslog(LOG_ALERT, "%s: bogus queue file, uid=%d, mode=%o", 131056400Seric e->e_id, st.st_uid, st.st_mode); 131156400Seric } 131256795Seric # endif /* LOG */ 131358884Seric if (tTd(40, 8)) 131458884Seric printf("readqf(%s): bogus file\n", qf); 131568490Seric loseqfile(e, "bogus file uid in mqueue"); 131656400Seric fclose(qfp); 131756400Seric return FALSE; 131856400Seric } 131956400Seric 132064277Seric if (st.st_size == 0) 132140934Srick { 132264277Seric /* must be a bogus file -- just remove it */ 132364277Seric (void) unlink(qf); 132464277Seric fclose(qfp); 132551920Seric return FALSE; 132640934Srick } 132740934Srick 132864277Seric if (st.st_nlink == 0) 132959101Seric { 133064277Seric /* 133164277Seric ** Race condition -- we got a file just as it was being 133264277Seric ** unlinked. Just assume it is zero length. 133364277Seric */ 133464277Seric 133559101Seric fclose(qfp); 133659101Seric return FALSE; 133759101Seric } 133859101Seric 133964277Seric /* good file -- save this lock */ 134051920Seric e->e_lockfp = qfp; 134151920Seric 134240934Srick /* do basic system initialization */ 134355012Seric initsys(e); 134465164Seric define('i', e->e_id, e); 134540934Srick 13469377Seric LineNumber = 0; 134763850Seric e->e_flags |= EF_GLOBALERRS; 134863850Seric OpMode = MD_DELIVER; 134954974Seric ctladdr = NULL; 135068481Seric e->e_dfino = -1; 135168564Seric e->e_msgsize = -1; 135257135Seric while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) 13534632Seric { 135458737Seric register char *p; 135568481Seric u_long qflags; 135668481Seric ADDRESS *q; 135757529Seric 135826504Seric if (tTd(40, 4)) 135957135Seric printf("+++++ %s\n", bp); 136057135Seric switch (bp[0]) 13614632Seric { 136268481Seric case 'V': /* queue file version number */ 136368481Seric qfver = atoi(&bp[1]); 136468481Seric if (qfver > QF_VERSION) 136568481Seric { 136668481Seric syserr("Version number in qf (%d) greater than max (%d)", 136768481Seric qfver, QF_VERSION); 136868481Seric } 136968481Seric break; 137068481Seric 137140973Sbostic case 'C': /* specify controlling user */ 137257135Seric ctladdr = setctluser(&bp[1]); 137340973Sbostic break; 137440973Sbostic 137568481Seric case 'Q': /* original recipient */ 137668481Seric orcpt = newstr(&bp[1]); 137768481Seric break; 137868481Seric 13794632Seric case 'R': /* specify recipient */ 138068481Seric p = bp; 138168481Seric qflags = 0; 138268481Seric if (qfver >= 1) 138368481Seric { 138468481Seric /* get flag bits */ 138568481Seric while (*++p != '\0' && *p != ':') 138668481Seric { 138768481Seric switch (*p) 138868481Seric { 138968481Seric case 'S': 139068481Seric qflags |= QPINGONSUCCESS; 139168481Seric break; 139268481Seric 139368481Seric case 'F': 139468481Seric qflags |= QPINGONFAILURE; 139568481Seric break; 139668481Seric 139768481Seric case 'D': 139868481Seric qflags |= QPINGONDELAY; 139968481Seric break; 140068481Seric 140168481Seric case 'P': 140268481Seric qflags |= QPRIMARY; 140368481Seric break; 140468481Seric } 140568481Seric } 140668481Seric } 140768481Seric else 140868481Seric qflags |= QPRIMARY; 140968481Seric q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e); 141068481Seric if (q != NULL) 141168481Seric { 141268481Seric q->q_alias = ctladdr; 141368481Seric q->q_flags |= qflags; 141468481Seric q->q_orcpt = orcpt; 141568481Seric (void) recipient(q, &e->e_sendqueue, 0, e); 141668481Seric } 141768481Seric orcpt = NULL; 14184632Seric break; 14194632Seric 142025687Seric case 'E': /* specify error recipient */ 142168558Seric /* no longer used */ 142225687Seric break; 142325687Seric 14244632Seric case 'H': /* header */ 142568717Seric (void) chompheader(&bp[1], FALSE, NULL, e); 14264632Seric break; 14274632Seric 142810108Seric case 'M': /* message */ 142964705Seric /* ignore this; we want a new message next time */ 143010108Seric break; 143110108Seric 14324632Seric case 'S': /* sender */ 143358704Seric setsender(newstr(&bp[1]), e, NULL, TRUE); 14344632Seric break; 14354632Seric 143659093Seric case 'B': /* body type */ 143759093Seric e->e_bodytype = newstr(&bp[1]); 143859093Seric break; 143959093Seric 14404632Seric case 'D': /* data file name */ 144168564Seric /* obsolete -- ignore */ 14424632Seric break; 14434632Seric 14447860Seric case 'T': /* init time */ 144557135Seric e->e_ctime = atol(&bp[1]); 14464632Seric break; 14474632Seric 144868481Seric case 'I': /* data file's inode number */ 144968481Seric if (e->e_dfino == -1) 145068481Seric e->e_dfino = atol(&buf[1]); 145168481Seric break; 145268481Seric 145368481Seric case 'K': /* time of last deliver attempt */ 145468481Seric e->e_dtime = atol(&buf[1]); 145568481Seric break; 145668481Seric 145768481Seric case 'N': /* number of delivery attempts */ 145868481Seric e->e_ntries = atoi(&buf[1]); 145968481Seric break; 146068481Seric 14614634Seric case 'P': /* message priority */ 146257135Seric e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 14634634Seric break; 14644634Seric 146558737Seric case 'F': /* flag bits */ 146658737Seric for (p = &bp[1]; *p != '\0'; p++) 146758737Seric { 146858737Seric switch (*p) 146958737Seric { 147058737Seric case 'w': /* warning sent */ 147158737Seric e->e_flags |= EF_WARNING; 147258737Seric break; 147358737Seric 147458737Seric case 'r': /* response */ 147558737Seric e->e_flags |= EF_RESPONSE; 147658737Seric break; 147768481Seric 147868481Seric case '8': /* has 8 bit data */ 147968481Seric e->e_flags |= EF_HAS8BIT; 148068481Seric break; 148158737Seric } 148258737Seric } 148358737Seric break; 148458737Seric 148568481Seric case 'Z': /* original envelope id from ESMTP */ 148668481Seric e->e_envid = newstr(&bp[1]); 148768481Seric break; 148868481Seric 148953400Seric case '$': /* define macro */ 149057135Seric define(bp[1], newstr(&bp[2]), e); 149153400Seric break; 149253400Seric 149324941Seric case '\0': /* blank line; ignore */ 149424941Seric break; 149524941Seric 14964632Seric default: 149765960Seric syserr("readqf: %s: line %d: bad line \"%s\"", 149865200Seric qf, LineNumber, bp); 149963753Seric fclose(qfp); 150068490Seric loseqfile(e, "unrecognized line"); 150163753Seric return FALSE; 15024632Seric } 150357135Seric 150457135Seric if (bp != buf) 150557135Seric free(bp); 15064632Seric } 15079377Seric 150824941Seric /* 150924941Seric ** If we haven't read any lines, this queue file is empty. 151024941Seric ** Arrange to remove it without referencing any null pointers. 151124941Seric */ 151224941Seric 151324941Seric if (LineNumber == 0) 151424941Seric { 151524941Seric errno = 0; 151624941Seric e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 151724941Seric } 151868564Seric else 151968564Seric { 152068564Seric /* 152168564Seric ** Arrange to read the data file. 152268564Seric */ 152368564Seric 152468564Seric p = queuename(e, 'd'); 152568564Seric e->e_dfp = fopen(p, "r"); 152668564Seric if (e->e_dfp == NULL) 152768564Seric { 152868564Seric syserr("readqf: cannot open %s", p); 152968564Seric } 153068603Seric else 153168564Seric { 153268603Seric e->e_flags |= EF_HAS_DF; 153368603Seric if (fstat(fileno(e->e_dfp), &st) >= 0) 153468603Seric { 153568603Seric e->e_msgsize = st.st_size; 153668603Seric e->e_dfdev = st.st_dev; 153768603Seric e->e_dfino = st.st_ino; 153868603Seric } 153968564Seric } 154068564Seric } 154168564Seric 154251920Seric return TRUE; 15434632Seric } 15444632Seric /* 15459630Seric ** PRINTQUEUE -- print out a representation of the mail queue 15469630Seric ** 15479630Seric ** Parameters: 15489630Seric ** none. 15499630Seric ** 15509630Seric ** Returns: 15519630Seric ** none. 15529630Seric ** 15539630Seric ** Side Effects: 15549630Seric ** Prints a listing of the mail queue on the standard output. 15559630Seric */ 15565182Seric 155769748Seric void 15589630Seric printqueue() 15599630Seric { 15609630Seric register WORK *w; 15619630Seric FILE *f; 156210070Seric int nrequests; 15639630Seric char buf[MAXLINE]; 15649630Seric 15659630Seric /* 156658250Seric ** Check for permission to print the queue 156758250Seric */ 156858250Seric 156964333Seric if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 157058250Seric { 157158250Seric struct stat st; 157258523Seric # ifdef NGROUPS 157358523Seric int n; 157463937Seric GIDSET_T gidset[NGROUPS]; 157558523Seric # endif 157658250Seric 157758517Seric if (stat(QueueDir, &st) < 0) 157858250Seric { 157958250Seric syserr("Cannot stat %s", QueueDir); 158058250Seric return; 158158250Seric } 158258523Seric # ifdef NGROUPS 158358523Seric n = getgroups(NGROUPS, gidset); 158458523Seric while (--n >= 0) 158558523Seric { 158658523Seric if (gidset[n] == st.st_gid) 158758523Seric break; 158858523Seric } 158969835Seric if (n < 0 && RealGid != st.st_gid) 159058523Seric # else 159163787Seric if (RealGid != st.st_gid) 159258523Seric # endif 159358250Seric { 159458250Seric usrerr("510 You are not permitted to see the queue"); 159558250Seric setstat(EX_NOPERM); 159658250Seric return; 159758250Seric } 159858250Seric } 159958250Seric 160058250Seric /* 16019630Seric ** Read and order the queue. 16029630Seric */ 16039630Seric 160424941Seric nrequests = orderq(TRUE); 16059630Seric 16069630Seric /* 16079630Seric ** Print the work list that we have read. 16089630Seric */ 16099630Seric 16109630Seric /* first see if there is anything */ 161110070Seric if (nrequests <= 0) 16129630Seric { 161310070Seric printf("Mail queue is empty\n"); 16149630Seric return; 16159630Seric } 16169630Seric 161751920Seric CurrentLA = getla(); /* get load average */ 161840934Srick 161910096Seric printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 162069722Seric if (nrequests > WorkListSize) 162169722Seric printf(", only %d printed", WorkListSize); 162224979Seric if (Verbose) 162358716Seric printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 162424979Seric else 162558716Seric printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 16269630Seric for (w = WorkQ; w != NULL; w = w->w_next) 16279630Seric { 16289630Seric struct stat st; 162910070Seric auto time_t submittime = 0; 163068565Seric long dfsize; 163158737Seric int flags = 0; 163268481Seric int qfver; 163369538Seric char statmsg[MAXLINE]; 163468528Seric char bodytype[MAXNAME + 1]; 16359630Seric 163664355Seric printf("%8s", w->w_name + 2); 163717468Seric f = fopen(w->w_name, "r"); 163817468Seric if (f == NULL) 163917468Seric { 164064355Seric printf(" (job completed)\n"); 164117468Seric errno = 0; 164217468Seric continue; 164317468Seric } 164468565Seric w->w_name[0] = 'd'; 164568565Seric if (stat(w->w_name, &st) >= 0) 164668565Seric dfsize = st.st_size; 164768565Seric else 164868565Seric dfsize = -1; 164968481Seric if (w->w_lock) 165010070Seric printf("*"); 165157438Seric else if (shouldqueue(w->w_pri, w->w_ctime)) 165224941Seric printf("X"); 165310070Seric else 165410070Seric printf(" "); 165510070Seric errno = 0; 165617468Seric 165769538Seric statmsg[0] = bodytype[0] = '\0'; 165868481Seric qfver = 0; 16599630Seric while (fgets(buf, sizeof buf, f) != NULL) 16609630Seric { 166153400Seric register int i; 166258737Seric register char *p; 166353400Seric 16649630Seric fixcrlf(buf, TRUE); 16659630Seric switch (buf[0]) 16669630Seric { 166768481Seric case 'V': /* queue file version */ 166868481Seric qfver = atoi(&buf[1]); 166968481Seric break; 167068481Seric 167110108Seric case 'M': /* error message */ 167269538Seric if ((i = strlen(&buf[1])) >= sizeof statmsg) 167369538Seric i = sizeof statmsg - 1; 167469538Seric bcopy(&buf[1], statmsg, i); 167569538Seric statmsg[i] = '\0'; 167610108Seric break; 167710108Seric 167859093Seric case 'B': /* body type */ 167959093Seric if ((i = strlen(&buf[1])) >= sizeof bodytype) 168059093Seric i = sizeof bodytype - 1; 168159093Seric bcopy(&buf[1], bodytype, i); 168259093Seric bodytype[i] = '\0'; 168359093Seric break; 168459093Seric 16859630Seric case 'S': /* sender name */ 168624979Seric if (Verbose) 168758737Seric printf("%8ld %10ld%c%.12s %.38s", 168858737Seric dfsize, 168958737Seric w->w_pri, 169058737Seric bitset(EF_WARNING, flags) ? '+' : ' ', 169158737Seric ctime(&submittime) + 4, 169224979Seric &buf[1]); 169324979Seric else 169424979Seric printf("%8ld %.16s %.45s", dfsize, 169524979Seric ctime(&submittime), &buf[1]); 169669538Seric if (statmsg[0] != '\0' || bodytype[0] != '\0') 169759093Seric { 169859093Seric printf("\n %10.10s", bodytype); 169969538Seric if (statmsg[0] != '\0') 170069538Seric printf(" (%.60s)", statmsg); 170159093Seric } 17029630Seric break; 170351920Seric 170440973Sbostic case 'C': /* controlling user */ 170554974Seric if (Verbose) 170658716Seric printf("\n\t\t\t\t (---%.34s---)", 170758716Seric &buf[1]); 170840973Sbostic break; 17099630Seric 17109630Seric case 'R': /* recipient name */ 171168481Seric p = &buf[1]; 171268481Seric if (qfver >= 1) 171368481Seric { 171468481Seric p = strchr(p, ':'); 171568481Seric if (p == NULL) 171668481Seric break; 171768481Seric p++; 171868481Seric } 171924979Seric if (Verbose) 172068481Seric printf("\n\t\t\t\t\t %.38s", p); 172124979Seric else 172268481Seric printf("\n\t\t\t\t %.45s", p); 17239630Seric break; 17249630Seric 17259630Seric case 'T': /* creation time */ 172624941Seric submittime = atol(&buf[1]); 17279630Seric break; 172810070Seric 172958737Seric case 'F': /* flag bits */ 173058737Seric for (p = &buf[1]; *p != '\0'; p++) 173158737Seric { 173258737Seric switch (*p) 173358737Seric { 173458737Seric case 'w': 173558737Seric flags |= EF_WARNING; 173658737Seric break; 173758737Seric } 173858737Seric } 17399630Seric } 17409630Seric } 174110070Seric if (submittime == (time_t) 0) 174210070Seric printf(" (no control file)"); 17439630Seric printf("\n"); 174423098Seric (void) fclose(f); 17459630Seric } 17469630Seric } 17479630Seric 174856795Seric # endif /* QUEUE */ 174917468Seric /* 175017468Seric ** QUEUENAME -- build a file name in the queue directory for this envelope. 175117468Seric ** 175217468Seric ** Assigns an id code if one does not already exist. 175317468Seric ** This code is very careful to avoid trashing existing files 175417468Seric ** under any circumstances. 175517468Seric ** 175617468Seric ** Parameters: 175717468Seric ** e -- envelope to build it in/from. 175817468Seric ** type -- the file type, used as the first character 175917468Seric ** of the file name. 176017468Seric ** 176117468Seric ** Returns: 176217468Seric ** a pointer to the new file name (in a static buffer). 176317468Seric ** 176417468Seric ** Side Effects: 176551920Seric ** If no id code is already assigned, queuename will 176651920Seric ** assign an id code, create a qf file, and leave a 176751920Seric ** locked, open-for-write file pointer in the envelope. 176817468Seric */ 176917468Seric 177017468Seric char * 177117468Seric queuename(e, type) 177217468Seric register ENVELOPE *e; 177359700Seric int type; 177417468Seric { 177517468Seric static int pid = -1; 177658741Seric static char c0; 177758741Seric static char c1; 177858741Seric static char c2; 177958716Seric time_t now; 178058716Seric struct tm *tm; 178168528Seric static char buf[MAXNAME + 1]; 178217468Seric 178317468Seric if (e->e_id == NULL) 178417468Seric { 178517468Seric char qf[20]; 178617468Seric 178717468Seric /* find a unique id */ 178817468Seric if (pid != getpid()) 178917468Seric { 179017468Seric /* new process -- start back at "AA" */ 179117468Seric pid = getpid(); 179258716Seric now = curtime(); 179358716Seric tm = localtime(&now); 179458716Seric c0 = 'A' + tm->tm_hour; 179517468Seric c1 = 'A'; 179617468Seric c2 = 'A' - 1; 179717468Seric } 179858716Seric (void) sprintf(qf, "qf%cAA%05d", c0, pid); 179917468Seric 180017468Seric while (c1 < '~' || c2 < 'Z') 180117468Seric { 180217468Seric int i; 180317468Seric 180417468Seric if (c2 >= 'Z') 180517468Seric { 180617468Seric c1++; 180717468Seric c2 = 'A' - 1; 180817468Seric } 180958716Seric qf[3] = c1; 181058716Seric qf[4] = ++c2; 181117468Seric if (tTd(7, 20)) 181240934Srick printf("queuename: trying \"%s\"\n", qf); 181317468Seric 181440934Srick i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); 181551920Seric if (i < 0) 181651920Seric { 181751920Seric if (errno == EEXIST) 181851920Seric continue; 181964705Seric syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", 182064705Seric qf, QueueDir, geteuid()); 182151920Seric exit(EX_UNAVAILABLE); 182251920Seric } 182364335Seric if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) 182451920Seric { 182551920Seric e->e_lockfp = fdopen(i, "w"); 182640934Srick break; 182717468Seric } 182851920Seric 182951920Seric /* a reader got the file; abandon it and try again */ 183051920Seric (void) close(i); 183117468Seric } 183217468Seric if (c1 >= '~' && c2 >= 'Z') 183317468Seric { 183464705Seric syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", 183564705Seric qf, QueueDir, geteuid()); 183617468Seric exit(EX_OSERR); 183717468Seric } 183817468Seric e->e_id = newstr(&qf[2]); 183917468Seric define('i', e->e_id, e); 184017468Seric if (tTd(7, 1)) 184117468Seric printf("queuename: assigned id %s, env=%x\n", e->e_id, e); 184264745Seric if (tTd(7, 9)) 184364745Seric { 184464745Seric printf(" lockfd="); 184564745Seric dumpfd(fileno(e->e_lockfp), TRUE, FALSE); 184664745Seric } 184717468Seric # ifdef LOG 184858020Seric if (LogLevel > 93) 184917468Seric syslog(LOG_DEBUG, "%s: assigned id", e->e_id); 185056795Seric # endif /* LOG */ 185117468Seric } 185217468Seric 185317468Seric if (type == '\0') 185417468Seric return (NULL); 185517468Seric (void) sprintf(buf, "%cf%s", type, e->e_id); 185617468Seric if (tTd(7, 2)) 185717468Seric printf("queuename: %s\n", buf); 185817468Seric return (buf); 185917468Seric } 186017468Seric /* 186117468Seric ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 186217468Seric ** 186317468Seric ** Parameters: 186417468Seric ** e -- the envelope to unlock. 186517468Seric ** 186617468Seric ** Returns: 186717468Seric ** none 186817468Seric ** 186917468Seric ** Side Effects: 187017468Seric ** unlocks the queue for `e'. 187117468Seric */ 187217468Seric 187369748Seric void 187417468Seric unlockqueue(e) 187517468Seric ENVELOPE *e; 187617468Seric { 187758680Seric if (tTd(51, 4)) 187858680Seric printf("unlockqueue(%s)\n", e->e_id); 187958680Seric 188051920Seric /* if there is a lock file in the envelope, close it */ 188151920Seric if (e->e_lockfp != NULL) 188258680Seric xfclose(e->e_lockfp, "unlockqueue", e->e_id); 188351920Seric e->e_lockfp = NULL; 188451920Seric 188558728Seric /* don't create a queue id if we don't already have one */ 188658728Seric if (e->e_id == NULL) 188758728Seric return; 188858728Seric 188917468Seric /* remove the transcript */ 189017468Seric # ifdef LOG 189158020Seric if (LogLevel > 87) 189217468Seric syslog(LOG_DEBUG, "%s: unlock", e->e_id); 189356795Seric # endif /* LOG */ 189458680Seric if (!tTd(51, 104)) 189517468Seric xunlink(queuename(e, 'x')); 189617468Seric 189717468Seric } 189840973Sbostic /* 189954974Seric ** SETCTLUSER -- create a controlling address 190040973Sbostic ** 190154974Seric ** Create a fake "address" given only a local login name; this is 190254974Seric ** used as a "controlling user" for future recipient addresses. 190340973Sbostic ** 190440973Sbostic ** Parameters: 190554974Seric ** user -- the user name of the controlling user. 190640973Sbostic ** 190740973Sbostic ** Returns: 190854974Seric ** An address descriptor for the controlling user. 190940973Sbostic ** 191040973Sbostic ** Side Effects: 191140973Sbostic ** none. 191240973Sbostic */ 191340973Sbostic 191454974Seric ADDRESS * 191554974Seric setctluser(user) 191654974Seric char *user; 191740973Sbostic { 191854974Seric register ADDRESS *a; 191940973Sbostic struct passwd *pw; 192059113Seric char *p; 192140973Sbostic 192240973Sbostic /* 192354974Seric ** See if this clears our concept of controlling user. 192440973Sbostic */ 192540973Sbostic 192663850Seric if (user == NULL || *user == '\0') 192763850Seric return NULL; 192840973Sbostic 192940973Sbostic /* 193054974Seric ** Set up addr fields for controlling user. 193140973Sbostic */ 193240973Sbostic 193354974Seric a = (ADDRESS *) xalloc(sizeof *a); 193454974Seric bzero((char *) a, sizeof *a); 193559113Seric 193659113Seric p = strchr(user, ':'); 193759113Seric if (p != NULL) 193859113Seric *p++ = '\0'; 193968693Seric if (*user != '\0' && (pw = sm_getpwnam(user)) != NULL) 194040973Sbostic { 194165822Seric if (strcmp(pw->pw_dir, "/") == 0) 194265822Seric a->q_home = ""; 194365822Seric else 194465822Seric a->q_home = newstr(pw->pw_dir); 194540973Sbostic a->q_uid = pw->pw_uid; 194640973Sbostic a->q_gid = pw->pw_gid; 194759270Seric a->q_flags |= QGOODUID; 194840973Sbostic } 194968603Seric 195068603Seric if (*user != '\0') 195168603Seric a->q_user = newstr(user); 195268603Seric else if (p != NULL) 195368603Seric a->q_user = newstr(p); 195440973Sbostic else 195557642Seric a->q_user = newstr(DefUser); 195640973Sbostic 195759270Seric a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 195856328Seric a->q_mailer = LocalMailer; 195959113Seric if (p == NULL) 196059113Seric a->q_paddr = a->q_user; 196159113Seric else 196259113Seric a->q_paddr = newstr(p); 196354974Seric return a; 196440973Sbostic } 196568490Seric /* 196668490Seric ** LOSEQFILE -- save the qf as Qf and try to let someone know 196768490Seric ** 196868490Seric ** Parameters: 196968490Seric ** e -- the envelope (e->e_id will be used). 197068490Seric ** why -- reported to whomever can hear. 197168490Seric ** 197268490Seric ** Returns: 197368490Seric ** none. 197468490Seric */ 197568490Seric 197668490Seric void 197768490Seric loseqfile(e, why) 197868490Seric register ENVELOPE *e; 197968490Seric char *why; 198068490Seric { 198168563Seric char *p; 198268490Seric char buf[40]; 198368490Seric 198468490Seric if (e == NULL || e->e_id == NULL) 198568490Seric return; 198668490Seric if (strlen(e->e_id) > sizeof buf - 4) 198768490Seric return; 198868490Seric strcpy(buf, queuename(e, 'q')); 198968563Seric p = queuename(e, 'Q'); 199068563Seric if (rename(buf, p) < 0) 199168563Seric syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); 199268490Seric #ifdef LOG 199368563Seric else if (LogLevel > 0) 199468563Seric syslog(LOG_ALERT, "Losing %s: %s", buf, why); 199568490Seric #endif 199668490Seric } 1997