queue.c revision 73188
138032Speter/* 273188Sgshapiro * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 538032Speter * Copyright (c) 1988, 1993 638032Speter * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1138032Speter * 1238032Speter */ 1338032Speter 1438032Speter 1564562Sgshapiro#include <sendmail.h> 1664562Sgshapiro 1738032Speter#ifndef lint 1864562Sgshapiro# if QUEUE 1973188Sgshapirostatic char id[] = "@(#)$Id: queue.c,v 8.343.4.44 2001/02/22 00:55:35 ca Exp $ (with queueing)"; 2064562Sgshapiro# else /* QUEUE */ 2173188Sgshapirostatic char id[] = "@(#)$Id: queue.c,v 8.343.4.44 2001/02/22 00:55:35 ca Exp $ (without queueing)"; 2264562Sgshapiro# endif /* QUEUE */ 2364562Sgshapiro#endif /* ! lint */ 2438032Speter 2538032Speter# include <dirent.h> 2638032Speter 2764562Sgshapiro#if QUEUE 2838032Speter 2964562Sgshapiro# if _FFR_QUEUEDELAY 3064562Sgshapiro# define QF_VERSION 5 /* version number of this queue format */ 3164562Sgshapirostatic time_t queuedelay __P((ENVELOPE *)); 3264562Sgshapiro# else /* _FFR_QUEUEDELAY */ 3364562Sgshapiro# define QF_VERSION 4 /* version number of this queue format */ 3464562Sgshapiro# define queuedelay(e) MinQueueAge 3564562Sgshapiro# endif /* _FFR_QUEUEDELAY */ 3664562Sgshapiro 3738032Speter/* 3838032Speter** Work queue. 3938032Speter*/ 4038032Speter 4138032Speterstruct work 4238032Speter{ 4338032Speter char *w_name; /* name of control file */ 4438032Speter char *w_host; /* name of recipient host */ 4538032Speter bool w_lock; /* is message locked? */ 4638032Speter bool w_tooyoung; /* is it too young to run? */ 4738032Speter long w_pri; /* priority of message, see below */ 4838032Speter time_t w_ctime; /* creation time of message */ 4938032Speter struct work *w_next; /* next in queue */ 5038032Speter}; 5138032Speter 5238032Spetertypedef struct work WORK; 5338032Speter 5464562Sgshapirostatic WORK *WorkQ; /* queue of things to be done */ 5538032Speter 5664562Sgshapirostatic void grow_wlist __P((int)); 5764562Sgshapirostatic int orderq __P((int, bool)); 5864562Sgshapirostatic void printctladdr __P((ADDRESS *, FILE *)); 5964562Sgshapirostatic int print_single_queue __P((int)); 6064562Sgshapirostatic bool readqf __P((ENVELOPE *)); 6164562Sgshapirostatic void runqueueevent __P((void)); 6264562Sgshapirostatic int run_single_queue __P((int, bool, bool)); 6364562Sgshapirostatic char *strrev __P((char *)); 6464562Sgshapirostatic ADDRESS *setctluser __P((char *, int)); 6564562Sgshapirostatic int workcmpf0(); 6664562Sgshapirostatic int workcmpf1(); 6764562Sgshapirostatic int workcmpf2(); 6864562Sgshapirostatic int workcmpf3(); 6964562Sgshapirostatic int workcmpf4(); 7038032Speter 7138032Speter/* 7238032Speter** QUEUEUP -- queue a message up for future transmission. 7338032Speter** 7438032Speter** Parameters: 7538032Speter** e -- the envelope to queue up. 7638032Speter** announce -- if TRUE, tell when you are queueing up. 7738032Speter** 7838032Speter** Returns: 7938032Speter** none. 8038032Speter** 8138032Speter** Side Effects: 8238032Speter** The current request are saved in a control file. 8338032Speter** The queue file is left locked. 8438032Speter*/ 8538032Speter 8664562Sgshapiro# define TEMPQF_LETTER 'T' 8771345Sgshapiro# define LOSEQF_LETTER 'Q' 8864562Sgshapiro 8938032Spetervoid 9038032Speterqueueup(e, announce) 9138032Speter register ENVELOPE *e; 9238032Speter bool announce; 9338032Speter{ 9438032Speter char *qf; 9538032Speter register FILE *tfp; 9638032Speter register HDR *h; 9738032Speter register ADDRESS *q; 9864562Sgshapiro int tfd = -1; 9938032Speter int i; 10038032Speter bool newid; 10138032Speter register char *p; 10238032Speter MAILER nullmailer; 10338032Speter MCI mcibuf; 10464562Sgshapiro char tf[MAXPATHLEN]; 10538032Speter char buf[MAXLINE]; 10638032Speter 10738032Speter /* 10838032Speter ** Create control file. 10938032Speter */ 11038032Speter 11138032Speter newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); 11238032Speter 11338032Speter /* if newid, queuename will create a locked qf file in e->lockfp */ 11464562Sgshapiro (void) strlcpy(tf, queuename(e, 't'), sizeof tf); 11538032Speter tfp = e->e_lockfp; 11638032Speter if (tfp == NULL) 11738032Speter newid = FALSE; 11838032Speter 11938032Speter /* if newid, just write the qf file directly (instead of tf file) */ 12038032Speter if (!newid) 12138032Speter { 12264562Sgshapiro int flags; 12364562Sgshapiro 12464562Sgshapiro flags = O_CREAT|O_WRONLY|O_EXCL; 12564562Sgshapiro 12638032Speter /* get a locked tf file */ 12738032Speter for (i = 0; i < 128; i++) 12838032Speter { 12964562Sgshapiro if (tfd < 0) 13038032Speter { 13164562Sgshapiro#if _FFR_QUEUE_FILE_MODE 13264562Sgshapiro MODE_T oldumask; 13364562Sgshapiro 13464562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 13564562Sgshapiro oldumask = umask(002); 13664562Sgshapiro tfd = open(tf, flags, QueueFileMode); 13764562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 13864562Sgshapiro (void) umask(oldumask); 13964562Sgshapiro#else /* _FFR_QUEUE_FILE_MODE */ 14064562Sgshapiro tfd = open(tf, flags, FileMode); 14164562Sgshapiro#endif /* _FFR_QUEUE_FILE_MODE */ 14264562Sgshapiro 14364562Sgshapiro if (tfd < 0) 14464562Sgshapiro { 14564562Sgshapiro if (errno != EEXIST) 14664562Sgshapiro break; 14764562Sgshapiro if (LogLevel > 0 && (i % 32) == 0) 14864562Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 14964562Sgshapiro "queueup: cannot create %s, uid=%d: %s", 15064562Sgshapiro tf, geteuid(), errstring(errno)); 15164562Sgshapiro } 15238032Speter } 15364562Sgshapiro if (tfd >= 0) 15438032Speter { 15564562Sgshapiro if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB)) 15638032Speter break; 15738032Speter else if (LogLevel > 0 && (i % 32) == 0) 15838032Speter sm_syslog(LOG_ALERT, e->e_id, 15964562Sgshapiro "queueup: cannot lock %s: %s", 16064562Sgshapiro tf, errstring(errno)); 16164562Sgshapiro if ((i % 32) == 31) 16264562Sgshapiro { 16364562Sgshapiro (void) close(tfd); 16464562Sgshapiro tfd = -1; 16564562Sgshapiro } 16638032Speter } 16738032Speter 16838032Speter if ((i % 32) == 31) 16938032Speter { 17038032Speter /* save the old temp file away */ 17164562Sgshapiro (void) rename(tf, queuename(e, TEMPQF_LETTER)); 17238032Speter } 17338032Speter else 17464562Sgshapiro (void) sleep(i % 32); 17538032Speter } 17664562Sgshapiro if (tfd < 0 || (tfp = fdopen(tfd, "w")) == NULL) 17738032Speter { 17864562Sgshapiro int save_errno = errno; 17964562Sgshapiro 18038032Speter printopenfds(TRUE); 18164562Sgshapiro errno = save_errno; 18238032Speter syserr("!queueup: cannot create queue temp file %s, uid=%d", 18338032Speter tf, geteuid()); 18438032Speter } 18538032Speter } 18638032Speter 18738032Speter if (tTd(40, 1)) 18864562Sgshapiro dprintf("\n>>>>> queueing %s/qf%s%s >>>>>\n", 18964562Sgshapiro qid_printqueue(e->e_queuedir), e->e_id, 19038032Speter newid ? " (new id)" : ""); 19138032Speter if (tTd(40, 3)) 19238032Speter { 19364562Sgshapiro dprintf(" e_flags="); 19438032Speter printenvflags(e); 19538032Speter } 19638032Speter if (tTd(40, 32)) 19738032Speter { 19864562Sgshapiro dprintf(" sendq="); 19938032Speter printaddr(e->e_sendqueue, TRUE); 20038032Speter } 20138032Speter if (tTd(40, 9)) 20238032Speter { 20364562Sgshapiro dprintf(" tfp="); 20438032Speter dumpfd(fileno(tfp), TRUE, FALSE); 20564562Sgshapiro dprintf(" lockfp="); 20638032Speter if (e->e_lockfp == NULL) 20764562Sgshapiro dprintf("NULL\n"); 20838032Speter else 20938032Speter dumpfd(fileno(e->e_lockfp), TRUE, FALSE); 21038032Speter } 21138032Speter 21238032Speter /* 21338032Speter ** If there is no data file yet, create one. 21438032Speter */ 21538032Speter 21664562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags)) 21738032Speter { 21864562Sgshapiro if (e->e_dfp != NULL && bfcommit(e->e_dfp) < 0) 21964562Sgshapiro syserr("!queueup: cannot commit data file %s, uid=%d", 22064562Sgshapiro queuename(e, 'd'), geteuid()); 22164562Sgshapiro } 22264562Sgshapiro else 22364562Sgshapiro { 22464562Sgshapiro int dfd; 22538032Speter register FILE *dfp = NULL; 22664562Sgshapiro char dfname[MAXPATHLEN]; 22738032Speter struct stat stbuf; 22838032Speter 22964562Sgshapiro if (e->e_dfp != NULL && bftest(e->e_dfp)) 23064562Sgshapiro syserr("committing over bf file"); 23164562Sgshapiro 23264562Sgshapiro (void) strlcpy(dfname, queuename(e, 'd'), sizeof dfname); 23364562Sgshapiro#if _FFR_QUEUE_FILE_MODE 23464562Sgshapiro { 23564562Sgshapiro MODE_T oldumask; 23664562Sgshapiro 23764562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 23864562Sgshapiro oldumask = umask(002); 23964562Sgshapiro dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, 24064562Sgshapiro QueueFileMode); 24164562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 24264562Sgshapiro (void) umask(oldumask); 24364562Sgshapiro } 24464562Sgshapiro#else /* _FFR_QUEUE_FILE_MODE */ 24564562Sgshapiro dfd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); 24664562Sgshapiro#endif /* _FFR_QUEUE_FILE_MODE */ 24764562Sgshapiro if (dfd < 0 || (dfp = fdopen(dfd, "w")) == NULL) 24838032Speter syserr("!queueup: cannot create data temp file %s, uid=%d", 24938032Speter dfname, geteuid()); 25064562Sgshapiro if (fstat(dfd, &stbuf) < 0) 25138032Speter e->e_dfino = -1; 25238032Speter else 25338032Speter { 25438032Speter e->e_dfdev = stbuf.st_dev; 25538032Speter e->e_dfino = stbuf.st_ino; 25638032Speter } 25738032Speter e->e_flags |= EF_HAS_DF; 25864562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 25938032Speter mcibuf.mci_out = dfp; 26038032Speter mcibuf.mci_mailer = FileMailer; 26138032Speter (*e->e_putbody)(&mcibuf, e, NULL); 26264562Sgshapiro if (fclose(dfp) < 0) 26364562Sgshapiro syserr("!queueup: cannot save data temp file %s, uid=%d", 26464562Sgshapiro dfname, geteuid()); 26538032Speter e->e_putbody = putbody; 26638032Speter } 26738032Speter 26838032Speter /* 26938032Speter ** Output future work requests. 27038032Speter ** Priority and creation time should be first, since 27138032Speter ** they are required by orderq. 27238032Speter */ 27338032Speter 27438032Speter /* output queue version number (must be first!) */ 27538032Speter fprintf(tfp, "V%d\n", QF_VERSION); 27638032Speter 27738032Speter /* output creation time */ 27838032Speter fprintf(tfp, "T%ld\n", (long) e->e_ctime); 27938032Speter 28038032Speter /* output last delivery time */ 28164562Sgshapiro# if _FFR_QUEUEDELAY 28238032Speter fprintf(tfp, "K%ld\n", (long) e->e_dtime); 28364562Sgshapiro fprintf(tfp, "G%d\n", e->e_queuealg); 28464562Sgshapiro fprintf(tfp, "Y%ld\n", (long) e->e_queuedelay); 28564562Sgshapiro if (tTd(40, 64)) 28664562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 28764562Sgshapiro "queue alg: %d delay %ld next: %ld (now: %ld)\n", 28864562Sgshapiro e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime()); 28964562Sgshapiro# else /* _FFR_QUEUEDELAY */ 29064562Sgshapiro fprintf(tfp, "K%ld\n", (long) e->e_dtime); 29164562Sgshapiro# endif /* _FFR_QUEUEDELAY */ 29238032Speter 29338032Speter /* output number of delivery attempts */ 29438032Speter fprintf(tfp, "N%d\n", e->e_ntries); 29538032Speter 29638032Speter /* output message priority */ 29738032Speter fprintf(tfp, "P%ld\n", e->e_msgpriority); 29838032Speter 29938032Speter /* output inode number of data file */ 30038032Speter /* XXX should probably include device major/minor too */ 30138032Speter if (e->e_dfino != -1) 30238032Speter { 30364562Sgshapiro /*CONSTCOND*/ 30438032Speter if (sizeof e->e_dfino > sizeof(long)) 30564562Sgshapiro fprintf(tfp, "I%ld/%ld/%s\n", 30664562Sgshapiro (long) major(e->e_dfdev), 30764562Sgshapiro (long) minor(e->e_dfdev), 30838032Speter quad_to_string(e->e_dfino)); 30938032Speter else 31064562Sgshapiro fprintf(tfp, "I%ld/%ld/%lu\n", 31164562Sgshapiro (long) major(e->e_dfdev), 31264562Sgshapiro (long) minor(e->e_dfdev), 31338032Speter (unsigned long) e->e_dfino); 31438032Speter } 31538032Speter 31638032Speter /* output body type */ 31738032Speter if (e->e_bodytype != NULL) 31838032Speter fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE)); 31938032Speter 32064562Sgshapiro# if _FFR_SAVE_CHARSET 32138032Speter if (e->e_charset != NULL) 32238032Speter fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE)); 32364562Sgshapiro# endif /* _FFR_SAVE_CHARSET */ 32438032Speter 32538032Speter /* message from envelope, if it exists */ 32638032Speter if (e->e_message != NULL) 32738032Speter fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE)); 32838032Speter 32938032Speter /* send various flag bits through */ 33038032Speter p = buf; 33138032Speter if (bitset(EF_WARNING, e->e_flags)) 33238032Speter *p++ = 'w'; 33338032Speter if (bitset(EF_RESPONSE, e->e_flags)) 33438032Speter *p++ = 'r'; 33538032Speter if (bitset(EF_HAS8BIT, e->e_flags)) 33638032Speter *p++ = '8'; 33738032Speter if (bitset(EF_DELETE_BCC, e->e_flags)) 33838032Speter *p++ = 'b'; 33938032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 34038032Speter *p++ = 'd'; 34138032Speter if (bitset(EF_NO_BODY_RETN, e->e_flags)) 34238032Speter *p++ = 'n'; 34338032Speter *p++ = '\0'; 34438032Speter if (buf[0] != '\0') 34538032Speter fprintf(tfp, "F%s\n", buf); 34638032Speter 34764562Sgshapiro /* save $={persistentMacros} macro values */ 34864562Sgshapiro queueup_macros(macid("{persistentMacros}", NULL), tfp, e); 34938032Speter 35038032Speter /* output name of sender */ 35138032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 35238032Speter p = e->e_sender; 35338032Speter else 35438032Speter p = e->e_from.q_paddr; 35538032Speter fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE)); 35638032Speter 35738032Speter /* output ESMTP-supplied "original" information */ 35838032Speter if (e->e_envid != NULL) 35938032Speter fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE)); 36038032Speter 36164562Sgshapiro /* output AUTH= parameter */ 36264562Sgshapiro if (e->e_auth_param != NULL) 36364562Sgshapiro fprintf(tfp, "A%s\n", denlstring(e->e_auth_param, 36464562Sgshapiro TRUE, FALSE)); 36564562Sgshapiro 36638032Speter /* output list of recipient addresses */ 36738032Speter printctladdr(NULL, NULL); 36838032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 36938032Speter { 37064562Sgshapiro if (!QS_IS_UNDELIVERED(q->q_state)) 37138032Speter continue; 37264562Sgshapiro 37338032Speter printctladdr(q, tfp); 37438032Speter if (q->q_orcpt != NULL) 37538032Speter fprintf(tfp, "Q%s\n", 37638032Speter denlstring(q->q_orcpt, TRUE, FALSE)); 37764562Sgshapiro (void) putc('R', tfp); 37838032Speter if (bitset(QPRIMARY, q->q_flags)) 37964562Sgshapiro (void) putc('P', tfp); 38038032Speter if (bitset(QHASNOTIFY, q->q_flags)) 38164562Sgshapiro (void) putc('N', tfp); 38238032Speter if (bitset(QPINGONSUCCESS, q->q_flags)) 38364562Sgshapiro (void) putc('S', tfp); 38438032Speter if (bitset(QPINGONFAILURE, q->q_flags)) 38564562Sgshapiro (void) putc('F', tfp); 38638032Speter if (bitset(QPINGONDELAY, q->q_flags)) 38764562Sgshapiro (void) putc('D', tfp); 38871345Sgshapiro if (q->q_alias != NULL && 38971345Sgshapiro bitset(QALIAS, q->q_alias->q_flags)) 39071345Sgshapiro (void) putc('A', tfp); 39164562Sgshapiro (void) putc(':', tfp); 39264562Sgshapiro (void) fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); 39338032Speter if (announce) 39438032Speter { 39538032Speter e->e_to = q->q_paddr; 39638032Speter message("queued"); 39738032Speter if (LogLevel > 8) 39864562Sgshapiro logdelivery(q->q_mailer, NULL, q->q_status, 39964562Sgshapiro "queued", NULL, (time_t) 0, e); 40038032Speter e->e_to = NULL; 40138032Speter } 40238032Speter if (tTd(40, 1)) 40338032Speter { 40464562Sgshapiro dprintf("queueing "); 40538032Speter printaddr(q, FALSE); 40638032Speter } 40738032Speter } 40838032Speter 40938032Speter /* 41038032Speter ** Output headers for this message. 41138032Speter ** Expand macros completely here. Queue run will deal with 41238032Speter ** everything as absolute headers. 41338032Speter ** All headers that must be relative to the recipient 41438032Speter ** can be cracked later. 41538032Speter ** We set up a "null mailer" -- i.e., a mailer that will have 41638032Speter ** no effect on the addresses as they are output. 41738032Speter */ 41838032Speter 41964562Sgshapiro memset((char *) &nullmailer, '\0', sizeof nullmailer); 42038032Speter nullmailer.m_re_rwset = nullmailer.m_rh_rwset = 42138032Speter nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; 42238032Speter nullmailer.m_eol = "\n"; 42364562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 42438032Speter mcibuf.mci_mailer = &nullmailer; 42538032Speter mcibuf.mci_out = tfp; 42638032Speter 42738032Speter define('g', "\201f", e); 42838032Speter for (h = e->e_header; h != NULL; h = h->h_link) 42938032Speter { 43043730Speter if (h->h_value == NULL) 43138032Speter continue; 43238032Speter 43338032Speter /* don't output resent headers on non-resent messages */ 43464562Sgshapiro if (bitset(H_RESENT, h->h_flags) && 43564562Sgshapiro !bitset(EF_RESENT, e->e_flags)) 43638032Speter continue; 43738032Speter 43838032Speter /* expand macros; if null, don't output header at all */ 43938032Speter if (bitset(H_DEFAULT, h->h_flags)) 44038032Speter { 44138032Speter (void) expand(h->h_value, buf, sizeof buf, e); 44238032Speter if (buf[0] == '\0') 44338032Speter continue; 44438032Speter } 44538032Speter 44638032Speter /* output this header */ 44764562Sgshapiro fprintf(tfp, "H?"); 44838032Speter 44964562Sgshapiro /* output conditional macro if present */ 45064562Sgshapiro if (h->h_macro != '\0') 45138032Speter { 45264562Sgshapiro if (bitset(0200, h->h_macro)) 45364562Sgshapiro fprintf(tfp, "${%s}", 45471345Sgshapiro macname(bitidx(h->h_macro))); 45564562Sgshapiro else 45664562Sgshapiro fprintf(tfp, "$%c", h->h_macro); 45764562Sgshapiro } 45864562Sgshapiro else if (!bitzerop(h->h_mflags) && 45964562Sgshapiro bitset(H_CHECK|H_ACHECK, h->h_flags)) 46064562Sgshapiro { 46138032Speter int j; 46238032Speter 46364562Sgshapiro /* if conditional, output the set of conditions */ 46438032Speter for (j = '\0'; j <= '\177'; j++) 46538032Speter if (bitnset(j, h->h_mflags)) 46638032Speter (void) putc(j, tfp); 46738032Speter } 46864562Sgshapiro (void) putc('?', tfp); 46938032Speter 47038032Speter /* output the header: expand macros, convert addresses */ 47164562Sgshapiro if (bitset(H_DEFAULT, h->h_flags) && 47264562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 47338032Speter { 47438032Speter fprintf(tfp, "%s: %s\n", 47538032Speter h->h_field, 47638032Speter denlstring(buf, FALSE, TRUE)); 47738032Speter } 47864562Sgshapiro else if (bitset(H_FROM|H_RCPT, h->h_flags) && 47964562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 48038032Speter { 48138032Speter bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 48238032Speter FILE *savetrace = TrafficLogFile; 48338032Speter 48438032Speter TrafficLogFile = NULL; 48538032Speter 48638032Speter if (bitset(H_FROM, h->h_flags)) 48738032Speter oldstyle = FALSE; 48838032Speter 48938032Speter commaize(h, h->h_value, oldstyle, &mcibuf, e); 49038032Speter 49138032Speter TrafficLogFile = savetrace; 49238032Speter } 49338032Speter else 49438032Speter { 49538032Speter fprintf(tfp, "%s: %s\n", 49638032Speter h->h_field, 49738032Speter denlstring(h->h_value, FALSE, TRUE)); 49838032Speter } 49938032Speter } 50038032Speter 50138032Speter /* 50238032Speter ** Clean up. 50338032Speter ** 50438032Speter ** Write a terminator record -- this is to prevent 50538032Speter ** scurrilous crackers from appending any data. 50638032Speter */ 50738032Speter 50838032Speter fprintf(tfp, ".\n"); 50938032Speter 51038032Speter if (fflush(tfp) < 0 || 51138032Speter (SuperSafe && fsync(fileno(tfp)) < 0) || 51238032Speter ferror(tfp)) 51338032Speter { 51438032Speter if (newid) 51538032Speter syserr("!552 Error writing control file %s", tf); 51638032Speter else 51738032Speter syserr("!452 Error writing control file %s", tf); 51838032Speter } 51938032Speter 52038032Speter if (!newid) 52138032Speter { 52238032Speter /* rename (locked) tf to be (locked) qf */ 52338032Speter qf = queuename(e, 'q'); 52438032Speter if (rename(tf, qf) < 0) 52538032Speter syserr("cannot rename(%s, %s), uid=%d", 52638032Speter tf, qf, geteuid()); 52764562Sgshapiro /* 52864562Sgshapiro ** fsync() after renaming to make sure 52964562Sgshapiro ** metadata is written to disk on 53064562Sgshapiro ** filesystems in which renames are 53164562Sgshapiro ** not guaranteed such as softupdates. 53264562Sgshapiro */ 53364562Sgshapiro 53464562Sgshapiro if (tfd >= 0 && SuperSafe && fsync(tfd) < 0) 53571345Sgshapiro syserr("!queueup: cannot fsync queue temp file %s", tf); 53664562Sgshapiro 53738032Speter /* close and unlock old (locked) qf */ 53838032Speter if (e->e_lockfp != NULL) 53964562Sgshapiro (void) fclose(e->e_lockfp); 54038032Speter e->e_lockfp = tfp; 54138032Speter } 54238032Speter else 54338032Speter qf = tf; 54438032Speter errno = 0; 54538032Speter e->e_flags |= EF_INQUEUE; 54638032Speter 54738032Speter /* save log info */ 54838032Speter if (LogLevel > 79) 54938032Speter sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf); 55038032Speter 55138032Speter if (tTd(40, 1)) 55264562Sgshapiro dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id); 55338032Speter return; 55438032Speter} 55538032Speter 55664562Sgshapirostatic void 55738032Speterprintctladdr(a, tfp) 55838032Speter register ADDRESS *a; 55938032Speter FILE *tfp; 56038032Speter{ 56164562Sgshapiro char *user; 56238032Speter register ADDRESS *q; 56338032Speter uid_t uid; 56438032Speter gid_t gid; 56538032Speter static ADDRESS *lastctladdr = NULL; 56638032Speter static uid_t lastuid; 56738032Speter 56838032Speter /* initialization */ 56938032Speter if (a == NULL || a->q_alias == NULL || tfp == NULL) 57038032Speter { 57138032Speter if (lastctladdr != NULL && tfp != NULL) 57238032Speter fprintf(tfp, "C\n"); 57338032Speter lastctladdr = NULL; 57438032Speter lastuid = 0; 57538032Speter return; 57638032Speter } 57738032Speter 57838032Speter /* find the active uid */ 57938032Speter q = getctladdr(a); 58038032Speter if (q == NULL) 58138032Speter { 58264562Sgshapiro user = NULL; 58338032Speter uid = 0; 58438032Speter gid = 0; 58538032Speter } 58638032Speter else 58738032Speter { 58864562Sgshapiro user = q->q_ruser != NULL ? q->q_ruser : q->q_user; 58938032Speter uid = q->q_uid; 59038032Speter gid = q->q_gid; 59138032Speter } 59238032Speter a = a->q_alias; 59338032Speter 59438032Speter /* check to see if this is the same as last time */ 59538032Speter if (lastctladdr != NULL && uid == lastuid && 59638032Speter strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) 59738032Speter return; 59838032Speter lastuid = uid; 59938032Speter lastctladdr = a; 60038032Speter 60164562Sgshapiro if (uid == 0 || user == NULL || user[0] == '\0') 60238032Speter fprintf(tfp, "C"); 60338032Speter else 60438032Speter fprintf(tfp, "C%s:%ld:%ld", 60564562Sgshapiro denlstring(user, TRUE, FALSE), (long) uid, (long) gid); 60638032Speter fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE)); 60738032Speter} 60838032Speter/* 60938032Speter** RUNQUEUE -- run the jobs in the queue. 61038032Speter** 61138032Speter** Gets the stuff out of the queue in some presumably logical 61238032Speter** order and processes them. 61338032Speter** 61438032Speter** Parameters: 61538032Speter** forkflag -- TRUE if the queue scanning should be done in 61638032Speter** a child process. We double-fork so it is not our 61738032Speter** child and we don't have to clean up after it. 61864562Sgshapiro** FALSE can be ignored if we have multiple queues. 61938032Speter** verbose -- if TRUE, print out status information. 62038032Speter** 62138032Speter** Returns: 62238032Speter** TRUE if the queue run successfully began. 62338032Speter** 62438032Speter** Side Effects: 62538032Speter** runs things in the mail queue. 62638032Speter*/ 62738032Speter 62864562Sgshapirostatic ENVELOPE QueueEnvelope; /* the queue run envelope */ 62964562Sgshapiroint NumQueues = 0; /* number of queues */ 63064562Sgshapirostatic time_t LastQueueTime = 0; /* last time a queue ID assigned */ 63164562Sgshapirostatic pid_t LastQueuePid = -1; /* last PID which had a queue ID */ 63238032Speter 63364562Sgshapirostruct qpaths_s 63464562Sgshapiro{ 63564562Sgshapiro char *qp_name; /* name of queue dir */ 63664562Sgshapiro short qp_subdirs; /* use subdirs? */ 63764562Sgshapiro}; 63864562Sgshapiro 63964562Sgshapirotypedef struct qpaths_s QPATHS; 64064562Sgshapiro 64164562Sgshapiro/* values for qp_supdirs */ 64264562Sgshapiro#define QP_NOSUB 0x0000 /* No subdirectories */ 64364562Sgshapiro#define QP_SUBDF 0x0001 /* "df" subdirectory */ 64464562Sgshapiro#define QP_SUBQF 0x0002 /* "qf" subdirectory */ 64564562Sgshapiro#define QP_SUBXF 0x0004 /* "xf" subdirectory */ 64664562Sgshapiro 64764562Sgshapirostatic QPATHS *QPaths = NULL; /* list of queue directories */ 64864562Sgshapiro 64938032Speterbool 65038032Speterrunqueue(forkflag, verbose) 65138032Speter bool forkflag; 65238032Speter bool verbose; 65338032Speter{ 65464562Sgshapiro int i; 65564562Sgshapiro bool ret = TRUE; 65664562Sgshapiro static int curnum = 0; 65764562Sgshapiro 65871345Sgshapiro DoQueueRun = FALSE; 65971345Sgshapiro 66071345Sgshapiro 66164562Sgshapiro if (!forkflag && NumQueues > 1 && !verbose) 66264562Sgshapiro forkflag = TRUE; 66364562Sgshapiro 66464562Sgshapiro for (i = 0; i < NumQueues; i++) 66564562Sgshapiro { 66664562Sgshapiro /* 66764562Sgshapiro ** Pick up where we left off, in case we 66864562Sgshapiro ** used up all the children last time 66964562Sgshapiro ** without finishing. 67064562Sgshapiro */ 67164562Sgshapiro 67264562Sgshapiro ret = run_single_queue(curnum, forkflag, verbose); 67364562Sgshapiro 67464562Sgshapiro /* 67564562Sgshapiro ** Failure means a message was printed for ETRN 67664562Sgshapiro ** and subsequent queues are likely to fail as well. 67764562Sgshapiro */ 67864562Sgshapiro 67964562Sgshapiro if (!ret) 68064562Sgshapiro break; 68164562Sgshapiro 68264562Sgshapiro if (++curnum >= NumQueues) 68364562Sgshapiro curnum = 0; 68464562Sgshapiro } 68564562Sgshapiro if (QueueIntvl != 0) 68664562Sgshapiro (void) setevent(QueueIntvl, runqueueevent, 0); 68764562Sgshapiro return ret; 68864562Sgshapiro} 68964562Sgshapiro/* 69064562Sgshapiro** RUN_SINGLE_QUEUE -- run the jobs in a single queue. 69164562Sgshapiro** 69264562Sgshapiro** Gets the stuff out of the queue in some presumably logical 69364562Sgshapiro** order and processes them. 69464562Sgshapiro** 69564562Sgshapiro** Parameters: 69664562Sgshapiro** queuedir -- queue to process 69764562Sgshapiro** forkflag -- TRUE if the queue scanning should be done in 69864562Sgshapiro** a child process. We double-fork so it is not our 69964562Sgshapiro** child and we don't have to clean up after it. 70064562Sgshapiro** verbose -- if TRUE, print out status information. 70164562Sgshapiro** 70264562Sgshapiro** Returns: 70364562Sgshapiro** TRUE if the queue run successfully began. 70464562Sgshapiro** 70564562Sgshapiro** Side Effects: 70664562Sgshapiro** runs things in the mail queue. 70764562Sgshapiro*/ 70864562Sgshapiro 70964562Sgshapirostatic bool 71064562Sgshapirorun_single_queue(queuedir, forkflag, verbose) 71164562Sgshapiro int queuedir; 71264562Sgshapiro bool forkflag; 71364562Sgshapiro bool verbose; 71464562Sgshapiro{ 71538032Speter register ENVELOPE *e; 71638032Speter int njobs; 71738032Speter int sequenceno = 0; 71871345Sgshapiro time_t current_la_time, now; 71938032Speter extern ENVELOPE BlankEnvelope; 72038032Speter 72138032Speter /* 72238032Speter ** If no work will ever be selected, don't even bother reading 72338032Speter ** the queue. 72438032Speter */ 72538032Speter 72664562Sgshapiro CurrentLA = sm_getla(NULL); /* get load average */ 72738032Speter current_la_time = curtime(); 72838032Speter 72938032Speter if (shouldqueue(WkRecipFact, current_la_time)) 73038032Speter { 73138032Speter char *msg = "Skipping queue run -- load average too high"; 73238032Speter 73338032Speter if (verbose) 73438032Speter message("458 %s\n", msg); 73538032Speter if (LogLevel > 8) 73638032Speter sm_syslog(LOG_INFO, NOQID, 73764562Sgshapiro "runqueue: %s", 73864562Sgshapiro msg); 73938032Speter return FALSE; 74038032Speter } 74138032Speter 74238032Speter /* 74338032Speter ** See if we already have too many children. 74438032Speter */ 74538032Speter 74638032Speter if (forkflag && QueueIntvl != 0 && 74738032Speter MaxChildren > 0 && CurChildren >= MaxChildren) 74838032Speter { 74964562Sgshapiro char *msg = "Skipping queue run -- too many children"; 75064562Sgshapiro 75164562Sgshapiro if (verbose) 75264562Sgshapiro message("458 %s (%d)\n", msg, CurChildren); 75364562Sgshapiro if (LogLevel > 8) 75464562Sgshapiro sm_syslog(LOG_INFO, NOQID, 75564562Sgshapiro "runqueue: %s (%d)", 75664562Sgshapiro msg, CurChildren); 75738032Speter return FALSE; 75838032Speter } 75938032Speter 76038032Speter /* 76138032Speter ** See if we want to go off and do other useful work. 76238032Speter */ 76338032Speter 76438032Speter if (forkflag) 76538032Speter { 76638032Speter pid_t pid; 76738032Speter 76864562Sgshapiro (void) blocksignal(SIGCHLD); 76938032Speter (void) setsignal(SIGCHLD, reapchild); 77038032Speter 77138032Speter pid = dofork(); 77238032Speter if (pid == -1) 77338032Speter { 77438032Speter const char *msg = "Skipping queue run -- fork() failed"; 77538032Speter const char *err = errstring(errno); 77638032Speter 77738032Speter if (verbose) 77838032Speter message("458 %s: %s\n", msg, err); 77938032Speter if (LogLevel > 8) 78038032Speter sm_syslog(LOG_INFO, NOQID, 78164562Sgshapiro "runqueue: %s: %s", 78264562Sgshapiro msg, err); 78338032Speter (void) releasesignal(SIGCHLD); 78438032Speter return FALSE; 78538032Speter } 78638032Speter if (pid != 0) 78738032Speter { 78838032Speter /* parent -- pick up intermediate zombie */ 78938032Speter (void) blocksignal(SIGALRM); 79064562Sgshapiro proc_list_add(pid, "Queue runner", PROC_QUEUE); 79138032Speter (void) releasesignal(SIGALRM); 79264562Sgshapiro (void) releasesignal(SIGCHLD); 79338032Speter return TRUE; 79438032Speter } 79564562Sgshapiro /* child -- clean up signals */ 79642575Speter clrcontrol(); 79738032Speter proc_list_clear(); 79842575Speter 79942575Speter /* Add parent process as first child item */ 80064562Sgshapiro proc_list_add(getpid(), "Queue runner child process", 80164562Sgshapiro PROC_QUEUE_CHILD); 80264562Sgshapiro (void) releasesignal(SIGCHLD); 80338032Speter (void) setsignal(SIGCHLD, SIG_DFL); 80438032Speter (void) setsignal(SIGHUP, intsig); 80564562Sgshapiro 80638032Speter } 80738032Speter 80864562Sgshapiro sm_setproctitle(TRUE, CurEnv, "running queue: %s", 80964562Sgshapiro qid_printqueue(queuedir)); 81038032Speter 81164562Sgshapiro if (LogLevel > 69 || tTd(63, 99)) 81238032Speter sm_syslog(LOG_DEBUG, NOQID, 81364562Sgshapiro "runqueue %s, pid=%d, forkflag=%d", 81464562Sgshapiro qid_printqueue(queuedir), getpid(), forkflag); 81538032Speter 81638032Speter /* 81738032Speter ** Release any resources used by the daemon code. 81838032Speter */ 81938032Speter 82038032Speter# if DAEMON 82138032Speter clrdaemon(); 82238032Speter# endif /* DAEMON */ 82338032Speter 82438032Speter /* force it to run expensive jobs */ 82538032Speter NoConnect = FALSE; 82638032Speter 82738032Speter /* drop privileges */ 82838032Speter if (geteuid() == (uid_t) 0) 82938032Speter (void) drop_privileges(FALSE); 83038032Speter 83138032Speter /* 83238032Speter ** Create ourselves an envelope 83338032Speter */ 83438032Speter 83538032Speter CurEnv = &QueueEnvelope; 83638032Speter e = newenvelope(&QueueEnvelope, CurEnv); 83738032Speter e->e_flags = BlankEnvelope.e_flags; 83873188Sgshapiro e->e_parent = NULL; 83938032Speter 84038032Speter /* make sure we have disconnected from parent */ 84138032Speter if (forkflag) 84238032Speter { 84338032Speter disconnect(1, e); 84438032Speter QuickAbort = FALSE; 84538032Speter } 84638032Speter 84738032Speter /* 84838032Speter ** If we are running part of the queue, always ignore stored 84938032Speter ** host status. 85038032Speter */ 85138032Speter 85238032Speter if (QueueLimitId != NULL || QueueLimitSender != NULL || 85338032Speter QueueLimitRecipient != NULL) 85438032Speter { 85538032Speter IgnoreHostStatus = TRUE; 85638032Speter MinQueueAge = 0; 85738032Speter } 85838032Speter 85938032Speter /* 86038032Speter ** Start making passes through the queue. 86138032Speter ** First, read and sort the entire queue. 86238032Speter ** Then, process the work in that order. 86338032Speter ** But if you take too long, start over. 86438032Speter */ 86538032Speter 86638032Speter /* order the existing work requests */ 86764562Sgshapiro njobs = orderq(queuedir, FALSE); 86838032Speter 86964562Sgshapiro 87038032Speter /* process them once at a time */ 87138032Speter while (WorkQ != NULL) 87238032Speter { 87338032Speter WORK *w = WorkQ; 87438032Speter 87538032Speter WorkQ = WorkQ->w_next; 87638032Speter e->e_to = NULL; 87738032Speter 87838032Speter /* 87938032Speter ** Ignore jobs that are too expensive for the moment. 88038032Speter ** 88138032Speter ** Get new load average every 30 seconds. 88238032Speter */ 88338032Speter 88471345Sgshapiro now = curtime(); 88571345Sgshapiro if (current_la_time < now - 30) 88638032Speter { 88764562Sgshapiro CurrentLA = sm_getla(e); 88871345Sgshapiro current_la_time = now; 88938032Speter } 89038032Speter if (shouldqueue(WkRecipFact, current_la_time)) 89138032Speter { 89238032Speter char *msg = "Aborting queue run: load average too high"; 89338032Speter 89438032Speter if (Verbose) 89538032Speter message("%s", msg); 89638032Speter if (LogLevel > 8) 89738032Speter sm_syslog(LOG_INFO, NOQID, 89864562Sgshapiro "runqueue: %s", 89964562Sgshapiro msg); 90038032Speter break; 90138032Speter } 90238032Speter sequenceno++; 90338032Speter if (shouldqueue(w->w_pri, w->w_ctime)) 90438032Speter { 90538032Speter if (Verbose) 90638032Speter message(""); 90764562Sgshapiro if (QueueSortOrder == QSO_BYPRIORITY) 90838032Speter { 90938032Speter if (Verbose) 91064562Sgshapiro message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", 91164562Sgshapiro qid_printqueue(queuedir), 91238032Speter w->w_name + 2, 91338032Speter sequenceno, 91438032Speter njobs); 91538032Speter if (LogLevel > 8) 91638032Speter sm_syslog(LOG_INFO, NOQID, 91764562Sgshapiro "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", 91864562Sgshapiro qid_printqueue(queuedir), 91964562Sgshapiro w->w_name + 2, 92064562Sgshapiro w->w_pri, 92164562Sgshapiro CurrentLA, 92264562Sgshapiro sequenceno, 92364562Sgshapiro njobs); 92438032Speter break; 92538032Speter } 92638032Speter else if (Verbose) 92764562Sgshapiro message("Skipping %s/%s (sequence %d of %d)", 92864562Sgshapiro qid_printqueue(queuedir), 92964562Sgshapiro w->w_name + 2, 93064562Sgshapiro sequenceno, njobs); 93138032Speter } 93238032Speter else 93338032Speter { 93438032Speter pid_t pid; 93538032Speter 93638032Speter if (Verbose) 93738032Speter { 93838032Speter message(""); 93964562Sgshapiro message("Running %s/%s (sequence %d of %d)", 94064562Sgshapiro qid_printqueue(queuedir), 94164562Sgshapiro w->w_name + 2, 94264562Sgshapiro sequenceno, njobs); 94338032Speter } 94464562Sgshapiro if (tTd(63, 100)) 94564562Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 94664562Sgshapiro "runqueue %s dowork(%s)", 94764562Sgshapiro qid_printqueue(queuedir), 94864562Sgshapiro w->w_name + 2); 94964562Sgshapiro 95064562Sgshapiro pid = dowork(queuedir, w->w_name + 2, 95164562Sgshapiro ForkQueueRuns, FALSE, e); 95238032Speter errno = 0; 95338032Speter if (pid != 0) 95438032Speter (void) waitfor(pid); 95538032Speter } 95638032Speter free(w->w_name); 95738032Speter if (w->w_host) 95838032Speter free(w->w_host); 95938032Speter free((char *) w); 96038032Speter } 96138032Speter 96238032Speter /* exit without the usual cleanup */ 96338032Speter e->e_id = NULL; 96464562Sgshapiro if (forkflag) 96564562Sgshapiro finis(TRUE, ExitStat); 96664562Sgshapiro /* NOTREACHED */ 96738032Speter return TRUE; 96838032Speter} 96938032Speter 97038032Speter/* 97138032Speter** RUNQUEUEEVENT -- stub for use in setevent 97238032Speter*/ 97338032Speter 97464562Sgshapirostatic void 97538032Speterrunqueueevent() 97638032Speter{ 97738032Speter DoQueueRun = TRUE; 97838032Speter} 97938032Speter/* 98038032Speter** ORDERQ -- order the work queue. 98138032Speter** 98238032Speter** Parameters: 98364562Sgshapiro** queuedir -- the index of the queue directory. 98438032Speter** doall -- if set, include everything in the queue (even 98538032Speter** the jobs that cannot be run because the load 98638032Speter** average is too high). Otherwise, exclude those 98738032Speter** jobs. 98838032Speter** 98938032Speter** Returns: 99038032Speter** The number of request in the queue (not necessarily 99138032Speter** the number of requests in WorkQ however). 99238032Speter** 99338032Speter** Side Effects: 99438032Speter** Sets WorkQ to the queue of available work, in order. 99538032Speter*/ 99638032Speter 99738032Speter# define NEED_P 001 99838032Speter# define NEED_T 002 99938032Speter# define NEED_R 004 100038032Speter# define NEED_S 010 100171345Sgshapiro# define NEED_H 020 100238032Speter 100338032Speterstatic WORK *WorkList = NULL; 100438032Speterstatic int WorkListSize = 0; 100538032Speter 100664562Sgshapirostatic int 100764562Sgshapiroorderq(queuedir, doall) 100864562Sgshapiro int queuedir; 100938032Speter bool doall; 101038032Speter{ 101138032Speter register struct dirent *d; 101238032Speter register WORK *w; 101338032Speter register char *p; 101438032Speter DIR *f; 101538032Speter register int i; 101638032Speter int wn = -1; 101738032Speter int wc; 101838032Speter QUEUE_CHAR *check; 101964562Sgshapiro char qd[MAXPATHLEN]; 102064562Sgshapiro char qf[MAXPATHLEN]; 102164562Sgshapiro 102264562Sgshapiro if (queuedir == NOQDIR) 102364562Sgshapiro (void) strlcpy(qd, ".", sizeof qd); 102464562Sgshapiro else 102564562Sgshapiro (void) snprintf(qd, sizeof qd, "%s%s", 102664562Sgshapiro QPaths[queuedir].qp_name, 102764562Sgshapiro (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : "")); 102864562Sgshapiro 102938032Speter if (tTd(41, 1)) 103038032Speter { 103164562Sgshapiro dprintf("orderq:\n"); 103238032Speter 103338032Speter check = QueueLimitId; 103438032Speter while (check != NULL) 103538032Speter { 103664562Sgshapiro dprintf("\tQueueLimitId = %s\n", 103764562Sgshapiro check->queue_match); 103838032Speter check = check->queue_next; 103938032Speter } 104038032Speter 104138032Speter check = QueueLimitSender; 104238032Speter while (check != NULL) 104338032Speter { 104464562Sgshapiro dprintf("\tQueueLimitSender = %s\n", 104564562Sgshapiro check->queue_match); 104638032Speter check = check->queue_next; 104738032Speter } 104838032Speter 104938032Speter check = QueueLimitRecipient; 105038032Speter while (check != NULL) 105138032Speter { 105264562Sgshapiro dprintf("\tQueueLimitRecipient = %s\n", 105364562Sgshapiro check->queue_match); 105438032Speter check = check->queue_next; 105538032Speter } 105638032Speter } 105738032Speter 105838032Speter /* clear out old WorkQ */ 105938032Speter for (w = WorkQ; w != NULL; ) 106038032Speter { 106138032Speter register WORK *nw = w->w_next; 106238032Speter 106338032Speter WorkQ = nw; 106438032Speter free(w->w_name); 106566494Sgshapiro if (w->w_host != NULL) 106638032Speter free(w->w_host); 106738032Speter free((char *) w); 106838032Speter w = nw; 106938032Speter } 107038032Speter 107138032Speter /* open the queue directory */ 107264562Sgshapiro f = opendir(qd); 107338032Speter if (f == NULL) 107438032Speter { 107564562Sgshapiro syserr("orderq: cannot open \"%s\"", qid_printqueue(queuedir)); 107664562Sgshapiro return 0; 107738032Speter } 107838032Speter 107938032Speter /* 108038032Speter ** Read the work directory. 108138032Speter */ 108238032Speter 108338032Speter while ((d = readdir(f)) != NULL) 108438032Speter { 108538032Speter FILE *cf; 108638032Speter int qfver = 0; 108738032Speter char lbuf[MAXNAME + 1]; 108864562Sgshapiro struct stat sbuf; 108938032Speter 109038032Speter if (tTd(41, 50)) 109164562Sgshapiro dprintf("orderq: checking %s\n", d->d_name); 109238032Speter 109338032Speter /* is this an interesting entry? */ 109438032Speter if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 109538032Speter continue; 109638032Speter 109764562Sgshapiro if (strlen(d->d_name) >= MAXQFNAME) 109842575Speter { 109942575Speter if (Verbose) 110042575Speter printf("orderq: %s too long, %d max characters\n", 110142575Speter d->d_name, MAXQFNAME); 110242575Speter if (LogLevel > 0) 110342575Speter sm_syslog(LOG_ALERT, NOQID, 110464562Sgshapiro "orderq: %s too long, %d max characters", 110564562Sgshapiro d->d_name, MAXQFNAME); 110638032Speter continue; 110742575Speter } 110838032Speter 110938032Speter check = QueueLimitId; 111038032Speter while (check != NULL) 111138032Speter { 111238032Speter if (strcontainedin(check->queue_match, d->d_name)) 111338032Speter break; 111438032Speter else 111538032Speter check = check->queue_next; 111638032Speter } 111738032Speter if (QueueLimitId != NULL && check == NULL) 111838032Speter continue; 111938032Speter 112064562Sgshapiro /* grow work list if necessary */ 112138032Speter if (++wn >= MaxQueueRun && MaxQueueRun > 0) 112238032Speter { 112338032Speter if (wn == MaxQueueRun && LogLevel > 0) 112464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 112564562Sgshapiro "WorkList for %s maxed out at %d", 112664562Sgshapiro qid_printqueue(queuedir), 112764562Sgshapiro MaxQueueRun); 112838032Speter continue; 112938032Speter } 113038032Speter if (wn >= WorkListSize) 113138032Speter { 113264562Sgshapiro grow_wlist(queuedir); 113338032Speter if (wn >= WorkListSize) 113438032Speter continue; 113538032Speter } 113664562Sgshapiro w = &WorkList[wn]; 113738032Speter 113864562Sgshapiro (void) snprintf(qf, sizeof qf, "%s/%s", qd, d->d_name); 113964562Sgshapiro if (stat(qf, &sbuf) < 0) 114064562Sgshapiro { 114164562Sgshapiro if (errno != ENOENT) 114264562Sgshapiro sm_syslog(LOG_INFO, NOQID, 114364562Sgshapiro "orderq: can't stat %s/%s", 114464562Sgshapiro qid_printqueue(queuedir), d->d_name); 114564562Sgshapiro wn--; 114664562Sgshapiro continue; 114764562Sgshapiro } 114864562Sgshapiro if (!bitset(S_IFREG, sbuf.st_mode)) 114964562Sgshapiro { 115064562Sgshapiro /* Yikes! Skip it or we will hang on open! */ 115164562Sgshapiro syserr("orderq: %s/%s is not a regular file", 115264562Sgshapiro qid_printqueue(queuedir), d->d_name); 115364562Sgshapiro wn--; 115464562Sgshapiro continue; 115564562Sgshapiro } 115664562Sgshapiro 115764562Sgshapiro /* avoid work if possible */ 115866494Sgshapiro if (QueueSortOrder == QSO_BYFILENAME && 115966494Sgshapiro QueueLimitSender == NULL && 116066494Sgshapiro QueueLimitRecipient == NULL) 116164562Sgshapiro { 116264562Sgshapiro w->w_name = newstr(d->d_name); 116364562Sgshapiro w->w_host = NULL; 116464562Sgshapiro w->w_lock = w->w_tooyoung = FALSE; 116564562Sgshapiro w->w_pri = 0; 116664562Sgshapiro w->w_ctime = 0; 116764562Sgshapiro continue; 116864562Sgshapiro } 116964562Sgshapiro 117064562Sgshapiro /* open control file */ 117164562Sgshapiro cf = fopen(qf, "r"); 117271345Sgshapiro 117338032Speter if (cf == NULL) 117438032Speter { 117538032Speter /* this may be some random person sending hir msgs */ 117638032Speter /* syserr("orderq: cannot open %s", cbuf); */ 117738032Speter if (tTd(41, 2)) 117864562Sgshapiro dprintf("orderq: cannot open %s: %s\n", 117938032Speter d->d_name, errstring(errno)); 118038032Speter errno = 0; 118138032Speter wn--; 118238032Speter continue; 118338032Speter } 118438032Speter w->w_name = newstr(d->d_name); 118538032Speter w->w_host = NULL; 118638032Speter w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); 118738032Speter w->w_tooyoung = FALSE; 118838032Speter 118938032Speter /* make sure jobs in creation don't clog queue */ 119038032Speter w->w_pri = 0x7fffffff; 119138032Speter w->w_ctime = 0; 119238032Speter 119338032Speter /* extract useful information */ 119438032Speter i = NEED_P | NEED_T; 119571345Sgshapiro if (QueueSortOrder == QSO_BYHOST) 119671345Sgshapiro { 119771345Sgshapiro /* need w_host set for host sort order */ 119871345Sgshapiro i |= NEED_H; 119971345Sgshapiro } 120038032Speter if (QueueLimitSender != NULL) 120138032Speter i |= NEED_S; 120264562Sgshapiro if (QueueLimitRecipient != NULL) 120338032Speter i |= NEED_R; 120438032Speter while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 120538032Speter { 120638032Speter int c; 120738032Speter time_t age; 120838032Speter 120938032Speter p = strchr(lbuf, '\n'); 121038032Speter if (p != NULL) 121138032Speter *p = '\0'; 121238032Speter else 121338032Speter { 121438032Speter /* flush rest of overly long line */ 121538032Speter while ((c = getc(cf)) != EOF && c != '\n') 121638032Speter continue; 121738032Speter } 121838032Speter 121938032Speter switch (lbuf[0]) 122038032Speter { 122138032Speter case 'V': 122238032Speter qfver = atoi(&lbuf[1]); 122338032Speter break; 122438032Speter 122538032Speter case 'P': 122638032Speter w->w_pri = atol(&lbuf[1]); 122738032Speter i &= ~NEED_P; 122838032Speter break; 122938032Speter 123038032Speter case 'T': 123138032Speter w->w_ctime = atol(&lbuf[1]); 123238032Speter i &= ~NEED_T; 123338032Speter break; 123438032Speter 123538032Speter case 'R': 123638032Speter if (w->w_host == NULL && 123738032Speter (p = strrchr(&lbuf[1], '@')) != NULL) 123864562Sgshapiro { 123964562Sgshapiro w->w_host = strrev(&p[1]); 124064562Sgshapiro makelower(w->w_host); 124171345Sgshapiro i &= ~NEED_H; 124264562Sgshapiro } 124338032Speter if (QueueLimitRecipient == NULL) 124438032Speter { 124538032Speter i &= ~NEED_R; 124638032Speter break; 124738032Speter } 124838032Speter if (qfver > 0) 124938032Speter { 125038032Speter p = strchr(&lbuf[1], ':'); 125138032Speter if (p == NULL) 125238032Speter p = &lbuf[1]; 125338032Speter } 125438032Speter else 125538032Speter p = &lbuf[1]; 125638032Speter check = QueueLimitRecipient; 125738032Speter while (check != NULL) 125838032Speter { 125938032Speter if (strcontainedin(check->queue_match, 126038032Speter p)) 126138032Speter break; 126238032Speter else 126338032Speter check = check->queue_next; 126438032Speter } 126538032Speter if (check != NULL) 126638032Speter i &= ~NEED_R; 126738032Speter break; 126838032Speter 126938032Speter case 'S': 127064562Sgshapiro check = QueueLimitSender; 127164562Sgshapiro while (check != NULL) 127264562Sgshapiro { 127364562Sgshapiro if (strcontainedin(check->queue_match, 127464562Sgshapiro &lbuf[1])) 127564562Sgshapiro break; 127664562Sgshapiro else 127764562Sgshapiro check = check->queue_next; 127864562Sgshapiro } 127964562Sgshapiro if (check != NULL) 128064562Sgshapiro i &= ~NEED_S; 128138032Speter break; 128238032Speter 128338032Speter case 'K': 128438032Speter age = curtime() - (time_t) atol(&lbuf[1]); 128538032Speter if (age >= 0 && MinQueueAge > 0 && 128638032Speter age < MinQueueAge) 128738032Speter w->w_tooyoung = TRUE; 128838032Speter break; 128938032Speter 129038032Speter case 'N': 129138032Speter if (atol(&lbuf[1]) == 0) 129238032Speter w->w_tooyoung = FALSE; 129338032Speter break; 129464562Sgshapiro 129564562Sgshapiro# if _FFR_QUEUEDELAY 129664562Sgshapiro/* 129764562Sgshapiro case 'G': 129864562Sgshapiro queuealg = atoi(lbuf[1]); 129964562Sgshapiro break; 130064562Sgshapiro case 'Y': 130164562Sgshapiro queuedelay = (time_t) atol(&lbuf[1]); 130264562Sgshapiro break; 130364562Sgshapiro*/ 130464562Sgshapiro# endif /* _FFR_QUEUEDELAY */ 130538032Speter } 130638032Speter } 130738032Speter (void) fclose(cf); 130838032Speter 130938032Speter if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || 131038032Speter bitset(NEED_R|NEED_S, i)) 131138032Speter { 131238032Speter /* don't even bother sorting this job in */ 131338032Speter if (tTd(41, 49)) 131464562Sgshapiro dprintf("skipping %s (%x)\n", w->w_name, i); 131538032Speter free(w->w_name); 131638032Speter if (w->w_host) 131738032Speter free(w->w_host); 131838032Speter wn--; 131938032Speter } 132038032Speter } 132138032Speter (void) closedir(f); 132238032Speter wn++; 132338032Speter 132464562Sgshapiro WorkQ = NULL; 132564562Sgshapiro if (WorkList == NULL) 132664562Sgshapiro return 0; 132738032Speter wc = min(wn, WorkListSize); 132838032Speter if (wc > MaxQueueRun && MaxQueueRun > 0) 132938032Speter wc = MaxQueueRun; 133038032Speter 133164562Sgshapiro if (QueueSortOrder == QSO_BYHOST) 133238032Speter { 133338032Speter /* 133438032Speter ** Sort the work directory for the first time, 133538032Speter ** based on host name, lock status, and priority. 133638032Speter */ 133738032Speter 133838032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); 133938032Speter 134038032Speter /* 134138032Speter ** If one message to host is locked, "lock" all messages 134238032Speter ** to that host. 134338032Speter */ 134438032Speter 134538032Speter i = 0; 134638032Speter while (i < wc) 134738032Speter { 134838032Speter if (!WorkList[i].w_lock) 134938032Speter { 135038032Speter i++; 135138032Speter continue; 135238032Speter } 135338032Speter w = &WorkList[i]; 135438032Speter while (++i < wc) 135538032Speter { 135638032Speter if (WorkList[i].w_host == NULL && 135738032Speter w->w_host == NULL) 135838032Speter WorkList[i].w_lock = TRUE; 135938032Speter else if (WorkList[i].w_host != NULL && 136038032Speter w->w_host != NULL && 136138032Speter sm_strcasecmp(WorkList[i].w_host, w->w_host) == 0) 136238032Speter WorkList[i].w_lock = TRUE; 136338032Speter else 136438032Speter break; 136538032Speter } 136638032Speter } 136738032Speter 136838032Speter /* 136938032Speter ** Sort the work directory for the second time, 137038032Speter ** based on lock status, host name, and priority. 137138032Speter */ 137238032Speter 137338032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); 137438032Speter } 137564562Sgshapiro else if (QueueSortOrder == QSO_BYTIME) 137638032Speter { 137738032Speter /* 137838032Speter ** Simple sort based on submission time only. 137938032Speter */ 138038032Speter 138138032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); 138238032Speter } 138364562Sgshapiro else if (QueueSortOrder == QSO_BYFILENAME) 138464562Sgshapiro { 138564562Sgshapiro /* 138664562Sgshapiro ** Sort based on qf filename. 138764562Sgshapiro */ 138864562Sgshapiro 138964562Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4); 139064562Sgshapiro } 139138032Speter else 139238032Speter { 139338032Speter /* 139438032Speter ** Simple sort based on queue priority only. 139538032Speter */ 139638032Speter 139738032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); 139838032Speter } 139938032Speter 140038032Speter /* 140138032Speter ** Convert the work list into canonical form. 140238032Speter ** Should be turning it into a list of envelopes here perhaps. 140338032Speter */ 140438032Speter 140538032Speter for (i = wc; --i >= 0; ) 140638032Speter { 140738032Speter w = (WORK *) xalloc(sizeof *w); 140838032Speter w->w_name = WorkList[i].w_name; 140938032Speter w->w_host = WorkList[i].w_host; 141038032Speter w->w_lock = WorkList[i].w_lock; 141138032Speter w->w_tooyoung = WorkList[i].w_tooyoung; 141238032Speter w->w_pri = WorkList[i].w_pri; 141338032Speter w->w_ctime = WorkList[i].w_ctime; 141438032Speter w->w_next = WorkQ; 141538032Speter WorkQ = w; 141638032Speter } 141738032Speter if (WorkList != NULL) 141838032Speter free(WorkList); 141938032Speter WorkList = NULL; 142038032Speter WorkListSize = 0; 142138032Speter 142238032Speter if (tTd(40, 1)) 142338032Speter { 142438032Speter for (w = WorkQ; w != NULL; w = w->w_next) 142564562Sgshapiro { 142664562Sgshapiro if (w->w_host != NULL) 142764562Sgshapiro dprintf("%22s: pri=%ld %s\n", 142864562Sgshapiro w->w_name, w->w_pri, w->w_host); 142964562Sgshapiro else 143064562Sgshapiro dprintf("%32s: pri=%ld\n", 143164562Sgshapiro w->w_name, w->w_pri); 143264562Sgshapiro } 143338032Speter } 143438032Speter 143564562Sgshapiro return wn; 143638032Speter} 143738032Speter/* 143838032Speter** GROW_WLIST -- make the work list larger 143938032Speter** 144038032Speter** Parameters: 144164562Sgshapiro** queuedir -- the index for the queue directory. 144238032Speter** 144338032Speter** Returns: 144438032Speter** none. 144538032Speter** 144638032Speter** Side Effects: 144738032Speter** Adds another QUEUESEGSIZE entries to WorkList if possible. 144838032Speter** It can fail if there isn't enough memory, so WorkListSize 144938032Speter** should be checked again upon return. 145038032Speter*/ 145138032Speter 145264562Sgshapirostatic void 145364562Sgshapirogrow_wlist(queuedir) 145464562Sgshapiro int queuedir; 145538032Speter{ 145638032Speter if (tTd(41, 1)) 145764562Sgshapiro dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); 145838032Speter if (WorkList == NULL) 145938032Speter { 146064562Sgshapiro WorkList = (WORK *) xalloc((sizeof *WorkList) * 146164562Sgshapiro (QUEUESEGSIZE + 1)); 146238032Speter WorkListSize = QUEUESEGSIZE; 146338032Speter } 146438032Speter else 146538032Speter { 146638032Speter int newsize = WorkListSize + QUEUESEGSIZE; 146738032Speter WORK *newlist = (WORK *) realloc((char *)WorkList, 146838032Speter (unsigned)sizeof(WORK) * (newsize + 1)); 146938032Speter 147038032Speter if (newlist != NULL) 147138032Speter { 147238032Speter WorkListSize = newsize; 147338032Speter WorkList = newlist; 147438032Speter if (LogLevel > 1) 147538032Speter { 147664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 147764562Sgshapiro "grew WorkList for %s to %d", 147864562Sgshapiro qid_printqueue(queuedir), 147964562Sgshapiro WorkListSize); 148038032Speter } 148138032Speter } 148238032Speter else if (LogLevel > 0) 148338032Speter { 148438032Speter sm_syslog(LOG_ALERT, NOQID, 148564562Sgshapiro "FAILED to grow WorkList for %s to %d", 148664562Sgshapiro qid_printqueue(queuedir), newsize); 148738032Speter } 148838032Speter } 148938032Speter if (tTd(41, 1)) 149064562Sgshapiro dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); 149138032Speter} 149238032Speter/* 149338032Speter** WORKCMPF0 -- simple priority-only compare function. 149438032Speter** 149538032Speter** Parameters: 149638032Speter** a -- the first argument. 149738032Speter** b -- the second argument. 149838032Speter** 149938032Speter** Returns: 150038032Speter** -1 if a < b 150138032Speter** 0 if a == b 150238032Speter** +1 if a > b 150338032Speter** 150438032Speter** Side Effects: 150538032Speter** none. 150638032Speter*/ 150738032Speter 150864562Sgshapirostatic int 150938032Speterworkcmpf0(a, b) 151038032Speter register WORK *a; 151138032Speter register WORK *b; 151238032Speter{ 151338032Speter long pa = a->w_pri; 151438032Speter long pb = b->w_pri; 151538032Speter 151638032Speter if (pa == pb) 151738032Speter return 0; 151838032Speter else if (pa > pb) 151938032Speter return 1; 152038032Speter else 152138032Speter return -1; 152238032Speter} 152338032Speter/* 152438032Speter** WORKCMPF1 -- first compare function for ordering work based on host name. 152538032Speter** 152638032Speter** Sorts on host name, lock status, and priority in that order. 152738032Speter** 152838032Speter** Parameters: 152938032Speter** a -- the first argument. 153038032Speter** b -- the second argument. 153138032Speter** 153238032Speter** Returns: 153338032Speter** <0 if a < b 153438032Speter** 0 if a == b 153538032Speter** >0 if a > b 153638032Speter** 153738032Speter** Side Effects: 153838032Speter** none. 153938032Speter*/ 154038032Speter 154164562Sgshapirostatic int 154238032Speterworkcmpf1(a, b) 154338032Speter register WORK *a; 154438032Speter register WORK *b; 154538032Speter{ 154638032Speter int i; 154738032Speter 154838032Speter /* host name */ 154938032Speter if (a->w_host != NULL && b->w_host == NULL) 155038032Speter return 1; 155138032Speter else if (a->w_host == NULL && b->w_host != NULL) 155238032Speter return -1; 155338032Speter if (a->w_host != NULL && b->w_host != NULL && 155438032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 155538032Speter return i; 155638032Speter 155738032Speter /* lock status */ 155838032Speter if (a->w_lock != b->w_lock) 155938032Speter return b->w_lock - a->w_lock; 156038032Speter 156138032Speter /* job priority */ 156273188Sgshapiro return workcmpf0(a, b); 156338032Speter} 156438032Speter/* 156538032Speter** WORKCMPF2 -- second compare function for ordering work based on host name. 156638032Speter** 156738032Speter** Sorts on lock status, host name, and priority in that order. 156838032Speter** 156938032Speter** Parameters: 157038032Speter** a -- the first argument. 157138032Speter** b -- the second argument. 157238032Speter** 157338032Speter** Returns: 157438032Speter** <0 if a < b 157538032Speter** 0 if a == b 157638032Speter** >0 if a > b 157738032Speter** 157838032Speter** Side Effects: 157938032Speter** none. 158038032Speter*/ 158138032Speter 158264562Sgshapirostatic int 158338032Speterworkcmpf2(a, b) 158438032Speter register WORK *a; 158538032Speter register WORK *b; 158638032Speter{ 158738032Speter int i; 158838032Speter 158938032Speter /* lock status */ 159038032Speter if (a->w_lock != b->w_lock) 159138032Speter return a->w_lock - b->w_lock; 159238032Speter 159338032Speter /* host name */ 159438032Speter if (a->w_host != NULL && b->w_host == NULL) 159538032Speter return 1; 159638032Speter else if (a->w_host == NULL && b->w_host != NULL) 159738032Speter return -1; 159838032Speter if (a->w_host != NULL && b->w_host != NULL && 159938032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 160038032Speter return i; 160138032Speter 160238032Speter /* job priority */ 160373188Sgshapiro return workcmpf0(a, b); 160438032Speter} 160538032Speter/* 160638032Speter** WORKCMPF3 -- simple submission-time-only compare function. 160738032Speter** 160838032Speter** Parameters: 160938032Speter** a -- the first argument. 161038032Speter** b -- the second argument. 161138032Speter** 161238032Speter** Returns: 161338032Speter** -1 if a < b 161438032Speter** 0 if a == b 161538032Speter** +1 if a > b 161638032Speter** 161738032Speter** Side Effects: 161838032Speter** none. 161938032Speter*/ 162038032Speter 162164562Sgshapirostatic int 162238032Speterworkcmpf3(a, b) 162338032Speter register WORK *a; 162438032Speter register WORK *b; 162538032Speter{ 162638032Speter if (a->w_ctime > b->w_ctime) 162738032Speter return 1; 162838032Speter else if (a->w_ctime < b->w_ctime) 162938032Speter return -1; 163038032Speter else 163138032Speter return 0; 163238032Speter} 163338032Speter/* 163464562Sgshapiro** WORKCMPF4 -- compare based on file name 163564562Sgshapiro** 163664562Sgshapiro** Parameters: 163764562Sgshapiro** a -- the first argument. 163864562Sgshapiro** b -- the second argument. 163964562Sgshapiro** 164064562Sgshapiro** Returns: 164164562Sgshapiro** -1 if a < b 164264562Sgshapiro** 0 if a == b 164364562Sgshapiro** +1 if a > b 164464562Sgshapiro** 164564562Sgshapiro** Side Effects: 164664562Sgshapiro** none. 164764562Sgshapiro*/ 164864562Sgshapiro 164964562Sgshapirostatic int 165064562Sgshapiroworkcmpf4(a, b) 165164562Sgshapiro register WORK *a; 165264562Sgshapiro register WORK *b; 165364562Sgshapiro{ 165464562Sgshapiro return strcmp(a->w_name, b->w_name); 165564562Sgshapiro} 165664562Sgshapiro/* 165764562Sgshapiro** STRREV -- reverse string 165864562Sgshapiro** 165964562Sgshapiro** Returns a pointer to a new string that is the reverse of 166064562Sgshapiro** the string pointed to by fwd. The space for the new 166164562Sgshapiro** string is obtained using xalloc(). 166264562Sgshapiro** 166364562Sgshapiro** Parameters: 166464562Sgshapiro** fwd -- the string to reverse. 166564562Sgshapiro** 166664562Sgshapiro** Returns: 166764562Sgshapiro** the reversed string. 166864562Sgshapiro*/ 166964562Sgshapiro 167064562Sgshapirostatic char * 167164562Sgshapirostrrev(fwd) 167264562Sgshapiro char *fwd; 167364562Sgshapiro{ 167464562Sgshapiro char *rev = NULL; 167564562Sgshapiro int len, cnt; 167664562Sgshapiro 167764562Sgshapiro len = strlen(fwd); 167864562Sgshapiro rev = xalloc(len + 1); 167964562Sgshapiro for (cnt = 0; cnt < len; ++cnt) 168064562Sgshapiro rev[cnt] = fwd[len - cnt - 1]; 168164562Sgshapiro rev[len] = '\0'; 168264562Sgshapiro return rev; 168364562Sgshapiro} 168464562Sgshapiro/* 168538032Speter** DOWORK -- do a work request. 168638032Speter** 168738032Speter** Parameters: 168864562Sgshapiro** queuedir -- the index of the queue directory for the job. 168938032Speter** id -- the ID of the job to run. 169038032Speter** forkflag -- if set, run this in background. 169138032Speter** requeueflag -- if set, reinstantiate the queue quickly. 169238032Speter** This is used when expanding aliases in the queue. 169338032Speter** If forkflag is also set, it doesn't wait for the 169438032Speter** child. 169538032Speter** e - the envelope in which to run it. 169638032Speter** 169738032Speter** Returns: 169838032Speter** process id of process that is running the queue job. 169938032Speter** 170038032Speter** Side Effects: 170138032Speter** The work request is satisfied if possible. 170238032Speter*/ 170338032Speter 170438032Speterpid_t 170564562Sgshapirodowork(queuedir, id, forkflag, requeueflag, e) 170664562Sgshapiro int queuedir; 170738032Speter char *id; 170838032Speter bool forkflag; 170938032Speter bool requeueflag; 171038032Speter register ENVELOPE *e; 171138032Speter{ 171238032Speter register pid_t pid; 171338032Speter 171438032Speter if (tTd(40, 1)) 171564562Sgshapiro dprintf("dowork(%s/%s)\n", qid_printqueue(queuedir), id); 171638032Speter 171738032Speter /* 171838032Speter ** Fork for work. 171938032Speter */ 172038032Speter 172138032Speter if (forkflag) 172238032Speter { 172364562Sgshapiro /* 172464562Sgshapiro ** Since the delivery may happen in a child and the 172564562Sgshapiro ** parent does not wait, the parent may close the 172664562Sgshapiro ** maps thereby removing any shared memory used by 172764562Sgshapiro ** the map. Therefore, close the maps now so the 172864562Sgshapiro ** child will dynamically open them if necessary. 172964562Sgshapiro */ 173064562Sgshapiro 173164562Sgshapiro closemaps(); 173264562Sgshapiro 173338032Speter pid = fork(); 173438032Speter if (pid < 0) 173538032Speter { 173638032Speter syserr("dowork: cannot fork"); 173738032Speter return 0; 173838032Speter } 173938032Speter else if (pid > 0) 174038032Speter { 174138032Speter /* parent -- clean out connection cache */ 174238032Speter mci_flush(FALSE, NULL); 174338032Speter } 174438032Speter else 174538032Speter { 174638032Speter /* child -- error messages to the transcript */ 174738032Speter QuickAbort = OnlyOneError = FALSE; 174838032Speter } 174938032Speter } 175038032Speter else 175138032Speter { 175238032Speter pid = 0; 175338032Speter } 175438032Speter 175538032Speter if (pid == 0) 175638032Speter { 175738032Speter /* 175838032Speter ** CHILD 175938032Speter ** Lock the control file to avoid duplicate deliveries. 176038032Speter ** Then run the file as though we had just read it. 176138032Speter ** We save an idea of the temporary name so we 176238032Speter ** can recover on interrupt. 176338032Speter */ 176438032Speter 176538032Speter /* set basic modes, etc. */ 176638032Speter (void) alarm(0); 176764562Sgshapiro clearstats(); 176838032Speter clearenvelope(e, FALSE); 176938032Speter e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 177064562Sgshapiro set_delivery_mode(SM_DELIVER, e); 177138032Speter e->e_errormode = EM_MAIL; 177238032Speter e->e_id = id; 177364562Sgshapiro e->e_queuedir = queuedir; 177438032Speter GrabTo = UseErrorsTo = FALSE; 177538032Speter ExitStat = EX_OK; 177638032Speter if (forkflag) 177738032Speter { 177838032Speter disconnect(1, e); 177964562Sgshapiro OpMode = MD_QUEUERUN; 178038032Speter } 178164562Sgshapiro sm_setproctitle(TRUE, e, "%s: from queue", qid_printname(e)); 178238032Speter if (LogLevel > 76) 178338032Speter sm_syslog(LOG_DEBUG, e->e_id, 178464562Sgshapiro "dowork, pid=%d", 178564562Sgshapiro getpid()); 178638032Speter 178738032Speter /* don't use the headers from sendmail.cf... */ 178838032Speter e->e_header = NULL; 178938032Speter 179038032Speter /* read the queue control file -- return if locked */ 179138032Speter if (!readqf(e)) 179238032Speter { 179338032Speter if (tTd(40, 4) && e->e_id != NULL) 179464562Sgshapiro dprintf("readqf(%s) failed\n", 179564562Sgshapiro qid_printname(e)); 179638032Speter e->e_id = NULL; 179738032Speter if (forkflag) 179842575Speter finis(FALSE, EX_OK); 179938032Speter else 180038032Speter return 0; 180138032Speter } 180238032Speter 180338032Speter e->e_flags |= EF_INQUEUE; 180438032Speter eatheader(e, requeueflag); 180538032Speter 180638032Speter if (requeueflag) 180738032Speter queueup(e, FALSE); 180838032Speter 180938032Speter /* do the delivery */ 181038032Speter sendall(e, SM_DELIVER); 181138032Speter 181238032Speter /* finish up and exit */ 181338032Speter if (forkflag) 181442575Speter finis(TRUE, ExitStat); 181538032Speter else 181638032Speter dropenvelope(e, TRUE); 181738032Speter } 181838032Speter e->e_id = NULL; 181938032Speter return pid; 182038032Speter} 182138032Speter/* 182238032Speter** READQF -- read queue file and set up environment. 182338032Speter** 182438032Speter** Parameters: 182538032Speter** e -- the envelope of the job to run. 182638032Speter** 182738032Speter** Returns: 182838032Speter** TRUE if it successfully read the queue file. 182938032Speter** FALSE otherwise. 183038032Speter** 183138032Speter** Side Effects: 183238032Speter** The queue file is returned locked. 183338032Speter*/ 183438032Speter 183564562Sgshapirostatic bool 183638032Speterreadqf(e) 183738032Speter register ENVELOPE *e; 183838032Speter{ 183938032Speter register FILE *qfp; 184038032Speter ADDRESS *ctladdr; 184138032Speter struct stat st; 184238032Speter char *bp; 184338032Speter int qfver = 0; 184438032Speter long hdrsize = 0; 184538032Speter register char *p; 184638032Speter char *orcpt = NULL; 184738032Speter bool nomore = FALSE; 184864562Sgshapiro MODE_T qsafe; 184964562Sgshapiro char qf[MAXPATHLEN]; 185038032Speter char buf[MAXLINE]; 185138032Speter 185238032Speter /* 185338032Speter ** Read and process the file. 185438032Speter */ 185538032Speter 185664562Sgshapiro (void) strlcpy(qf, queuename(e, 'q'), sizeof qf); 185738032Speter qfp = fopen(qf, "r+"); 185838032Speter if (qfp == NULL) 185938032Speter { 186064562Sgshapiro int save_errno = errno; 186164562Sgshapiro 186238032Speter if (tTd(40, 8)) 186364562Sgshapiro dprintf("readqf(%s): fopen failure (%s)\n", 186438032Speter qf, errstring(errno)); 186564562Sgshapiro errno = save_errno; 186664562Sgshapiro if (errno != ENOENT 186764562Sgshapiro ) 186838032Speter syserr("readqf: no control file %s", qf); 186938032Speter return FALSE; 187038032Speter } 187138032Speter 187238032Speter if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) 187338032Speter { 187438032Speter /* being processed by another queuer */ 187564562Sgshapiro if (Verbose) 187638032Speter printf("%s: locked\n", e->e_id); 187764562Sgshapiro if (tTd(40, 8)) 187864562Sgshapiro dprintf("%s: locked\n", e->e_id); 187938032Speter if (LogLevel > 19) 188038032Speter sm_syslog(LOG_DEBUG, e->e_id, "locked"); 188138032Speter (void) fclose(qfp); 188238032Speter return FALSE; 188338032Speter } 188438032Speter 188538032Speter /* 188638032Speter ** Check the queue file for plausibility to avoid attacks. 188738032Speter */ 188838032Speter 188938032Speter if (fstat(fileno(qfp), &st) < 0) 189038032Speter { 189138032Speter /* must have been being processed by someone else */ 189238032Speter if (tTd(40, 8)) 189364562Sgshapiro dprintf("readqf(%s): fstat failure (%s)\n", 189438032Speter qf, errstring(errno)); 189564562Sgshapiro (void) fclose(qfp); 189638032Speter return FALSE; 189738032Speter } 189838032Speter 189964562Sgshapiro qsafe = S_IWOTH|S_IWGRP; 190064562Sgshapiro#if _FFR_QUEUE_FILE_MODE 190164562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 190264562Sgshapiro qsafe &= ~S_IWGRP; 190364562Sgshapiro#endif /* _FFR_QUEUE_FILE_MODE */ 190464562Sgshapiro 190564562Sgshapiro if ((st.st_uid != geteuid() && 190664562Sgshapiro st.st_uid != TrustedUid && 190764562Sgshapiro geteuid() != RealUid) || 190864562Sgshapiro bitset(qsafe, st.st_mode)) 190938032Speter { 191038032Speter if (LogLevel > 0) 191138032Speter { 191238032Speter sm_syslog(LOG_ALERT, e->e_id, 191364562Sgshapiro "bogus queue file, uid=%d, mode=%o", 191464562Sgshapiro st.st_uid, st.st_mode); 191538032Speter } 191638032Speter if (tTd(40, 8)) 191764562Sgshapiro dprintf("readqf(%s): bogus file\n", qf); 191838032Speter loseqfile(e, "bogus file uid in mqueue"); 191964562Sgshapiro (void) fclose(qfp); 192038032Speter return FALSE; 192138032Speter } 192238032Speter 192338032Speter if (st.st_size == 0) 192438032Speter { 192538032Speter /* must be a bogus file -- if also old, just remove it */ 192638032Speter if (st.st_ctime + 10 * 60 < curtime()) 192738032Speter { 192864562Sgshapiro (void) xunlink(queuename(e, 'd')); 192964562Sgshapiro (void) xunlink(queuename(e, 'q')); 193038032Speter } 193164562Sgshapiro (void) fclose(qfp); 193238032Speter return FALSE; 193338032Speter } 193438032Speter 193538032Speter if (st.st_nlink == 0) 193638032Speter { 193738032Speter /* 193838032Speter ** Race condition -- we got a file just as it was being 193938032Speter ** unlinked. Just assume it is zero length. 194038032Speter */ 194138032Speter 194264562Sgshapiro (void) fclose(qfp); 194338032Speter return FALSE; 194438032Speter } 194538032Speter 194638032Speter /* good file -- save this lock */ 194738032Speter e->e_lockfp = qfp; 194838032Speter 194938032Speter /* do basic system initialization */ 195038032Speter initsys(e); 195138032Speter define('i', e->e_id, e); 195238032Speter 195338032Speter LineNumber = 0; 195438032Speter e->e_flags |= EF_GLOBALERRS; 195564562Sgshapiro OpMode = MD_QUEUERUN; 195638032Speter ctladdr = NULL; 195738032Speter e->e_dfino = -1; 195838032Speter e->e_msgsize = -1; 195964562Sgshapiro# if _FFR_QUEUEDELAY 196064562Sgshapiro e->e_queuealg = QD_LINEAR; 196164562Sgshapiro e->e_queuedelay = (time_t) 0; 196264562Sgshapiro# endif /* _FFR_QUEUEDELAY */ 196338032Speter while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) 196438032Speter { 196538032Speter u_long qflags; 196638032Speter ADDRESS *q; 196738032Speter int mid; 196871345Sgshapiro time_t now; 196938032Speter auto char *ep; 197038032Speter 197138032Speter if (tTd(40, 4)) 197264562Sgshapiro dprintf("+++++ %s\n", bp); 197338032Speter if (nomore) 197438032Speter { 197538032Speter /* hack attack */ 197638032Speter syserr("SECURITY ALERT: extra data in qf: %s", bp); 197764562Sgshapiro (void) fclose(qfp); 197838032Speter loseqfile(e, "bogus queue line"); 197938032Speter return FALSE; 198038032Speter } 198138032Speter switch (bp[0]) 198238032Speter { 198338032Speter case 'V': /* queue file version number */ 198438032Speter qfver = atoi(&bp[1]); 198538032Speter if (qfver <= QF_VERSION) 198638032Speter break; 198738032Speter syserr("Version number in qf (%d) greater than max (%d)", 198838032Speter qfver, QF_VERSION); 198964562Sgshapiro (void) fclose(qfp); 199038032Speter loseqfile(e, "unsupported qf file version"); 199138032Speter return FALSE; 199238032Speter 199338032Speter case 'C': /* specify controlling user */ 199438032Speter ctladdr = setctluser(&bp[1], qfver); 199538032Speter break; 199638032Speter 199738032Speter case 'Q': /* original recipient */ 199838032Speter orcpt = newstr(&bp[1]); 199938032Speter break; 200038032Speter 200138032Speter case 'R': /* specify recipient */ 200238032Speter p = bp; 200338032Speter qflags = 0; 200438032Speter if (qfver >= 1) 200538032Speter { 200638032Speter /* get flag bits */ 200738032Speter while (*++p != '\0' && *p != ':') 200838032Speter { 200938032Speter switch (*p) 201038032Speter { 201138032Speter case 'N': 201238032Speter qflags |= QHASNOTIFY; 201338032Speter break; 201438032Speter 201538032Speter case 'S': 201638032Speter qflags |= QPINGONSUCCESS; 201738032Speter break; 201838032Speter 201938032Speter case 'F': 202038032Speter qflags |= QPINGONFAILURE; 202138032Speter break; 202238032Speter 202338032Speter case 'D': 202438032Speter qflags |= QPINGONDELAY; 202538032Speter break; 202638032Speter 202738032Speter case 'P': 202838032Speter qflags |= QPRIMARY; 202938032Speter break; 203071345Sgshapiro 203171345Sgshapiro case 'A': 203271345Sgshapiro if (ctladdr != NULL) 203371345Sgshapiro ctladdr->q_flags |= QALIAS; 203471345Sgshapiro break; 203538032Speter } 203638032Speter } 203738032Speter } 203838032Speter else 203938032Speter qflags |= QPRIMARY; 204038032Speter q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e); 204138032Speter if (q != NULL) 204238032Speter { 204338032Speter q->q_alias = ctladdr; 204438032Speter if (qfver >= 1) 204538032Speter q->q_flags &= ~Q_PINGFLAGS; 204638032Speter q->q_flags |= qflags; 204738032Speter q->q_orcpt = orcpt; 204838032Speter (void) recipient(q, &e->e_sendqueue, 0, e); 204938032Speter } 205038032Speter orcpt = NULL; 205138032Speter break; 205238032Speter 205338032Speter case 'E': /* specify error recipient */ 205438032Speter /* no longer used */ 205538032Speter break; 205638032Speter 205738032Speter case 'H': /* header */ 205866494Sgshapiro (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); 205938032Speter hdrsize += strlen(&bp[1]); 206038032Speter break; 206138032Speter 206238032Speter case 'L': /* Solaris Content-Length: */ 206338032Speter case 'M': /* message */ 206438032Speter /* ignore this; we want a new message next time */ 206538032Speter break; 206638032Speter 206738032Speter case 'S': /* sender */ 206838032Speter setsender(newstr(&bp[1]), e, NULL, '\0', TRUE); 206938032Speter break; 207038032Speter 207138032Speter case 'B': /* body type */ 207238032Speter e->e_bodytype = newstr(&bp[1]); 207338032Speter break; 207438032Speter 207564562Sgshapiro# if _FFR_SAVE_CHARSET 207638032Speter case 'X': /* character set */ 207738032Speter e->e_charset = newstr(&bp[1]); 207838032Speter break; 207964562Sgshapiro# endif /* _FFR_SAVE_CHARSET */ 208038032Speter 208138032Speter case 'D': /* data file name */ 208238032Speter /* obsolete -- ignore */ 208338032Speter break; 208438032Speter 208538032Speter case 'T': /* init time */ 208638032Speter e->e_ctime = atol(&bp[1]); 208738032Speter break; 208838032Speter 208938032Speter case 'I': /* data file's inode number */ 209038032Speter /* regenerated below */ 209138032Speter break; 209238032Speter 209364562Sgshapiro case 'K': /* time of last delivery attempt */ 209438032Speter e->e_dtime = atol(&buf[1]); 209538032Speter break; 209638032Speter 209764562Sgshapiro# if _FFR_QUEUEDELAY 209864562Sgshapiro case 'G': /* queue delay algorithm */ 209964562Sgshapiro e->e_queuealg = atoi(&buf[1]); 210064562Sgshapiro break; 210164562Sgshapiro case 'Y': /* current delay */ 210264562Sgshapiro e->e_queuedelay = (time_t) atol(&buf[1]); 210364562Sgshapiro break; 210464562Sgshapiro# endif /* _FFR_QUEUEDELAY */ 210564562Sgshapiro 210638032Speter case 'N': /* number of delivery attempts */ 210738032Speter e->e_ntries = atoi(&buf[1]); 210838032Speter 210938032Speter /* if this has been tried recently, let it be */ 211071345Sgshapiro now = curtime(); 211171345Sgshapiro if (e->e_ntries > 0 && e->e_dtime <= now && 211271345Sgshapiro now < e->e_dtime + queuedelay(e)) 211338032Speter { 211464562Sgshapiro char *howlong; 211538032Speter 211671345Sgshapiro howlong = pintvl(now - e->e_dtime, TRUE); 211764562Sgshapiro if (Verbose) 211838032Speter printf("%s: too young (%s)\n", 211964562Sgshapiro e->e_id, howlong); 212064562Sgshapiro if (tTd(40, 8)) 212164562Sgshapiro dprintf("%s: too young (%s)\n", 212238032Speter e->e_id, howlong); 212338032Speter if (LogLevel > 19) 212438032Speter sm_syslog(LOG_DEBUG, e->e_id, 212564562Sgshapiro "too young (%s)", 212664562Sgshapiro howlong); 212738032Speter e->e_id = NULL; 212838032Speter unlockqueue(e); 212938032Speter return FALSE; 213038032Speter } 213164562Sgshapiro define(macid("{ntries}", NULL), newstr(&buf[1]), e); 213264562Sgshapiro 213364562Sgshapiro# if NAMED_BIND 213464562Sgshapiro /* adjust BIND parameters immediately */ 213564562Sgshapiro if (e->e_ntries == 0) 213664562Sgshapiro { 213764562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 213864562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 213964562Sgshapiro } 214064562Sgshapiro else 214164562Sgshapiro { 214264562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_NORMAL]; 214364562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL]; 214464562Sgshapiro } 214564562Sgshapiro# endif /* NAMED_BIND */ 214638032Speter break; 214738032Speter 214838032Speter case 'P': /* message priority */ 214938032Speter e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 215038032Speter break; 215138032Speter 215238032Speter case 'F': /* flag bits */ 215338032Speter if (strncmp(bp, "From ", 5) == 0) 215438032Speter { 215538032Speter /* we are being spoofed! */ 215638032Speter syserr("SECURITY ALERT: bogus qf line %s", bp); 215764562Sgshapiro (void) fclose(qfp); 215838032Speter loseqfile(e, "bogus queue line"); 215938032Speter return FALSE; 216038032Speter } 216138032Speter for (p = &bp[1]; *p != '\0'; p++) 216238032Speter { 216338032Speter switch (*p) 216438032Speter { 216538032Speter case 'w': /* warning sent */ 216638032Speter e->e_flags |= EF_WARNING; 216738032Speter break; 216838032Speter 216938032Speter case 'r': /* response */ 217038032Speter e->e_flags |= EF_RESPONSE; 217138032Speter break; 217238032Speter 217338032Speter case '8': /* has 8 bit data */ 217438032Speter e->e_flags |= EF_HAS8BIT; 217538032Speter break; 217638032Speter 217738032Speter case 'b': /* delete Bcc: header */ 217838032Speter e->e_flags |= EF_DELETE_BCC; 217938032Speter break; 218038032Speter 218138032Speter case 'd': /* envelope has DSN RET= */ 218238032Speter e->e_flags |= EF_RET_PARAM; 218338032Speter break; 218438032Speter 218538032Speter case 'n': /* don't return body */ 218638032Speter e->e_flags |= EF_NO_BODY_RETN; 218738032Speter break; 218838032Speter } 218938032Speter } 219038032Speter break; 219138032Speter 219238032Speter case 'Z': /* original envelope id from ESMTP */ 219338032Speter e->e_envid = newstr(&bp[1]); 219464562Sgshapiro define(macid("{dsn_envid}", NULL), newstr(&bp[1]), e); 219538032Speter break; 219638032Speter 219764562Sgshapiro case 'A': /* AUTH= parameter */ 219864562Sgshapiro e->e_auth_param = newstr(&bp[1]); 219964562Sgshapiro break; 220064562Sgshapiro 220138032Speter case '$': /* define macro */ 220264562Sgshapiro { 220364562Sgshapiro char *p; 220464562Sgshapiro 220564562Sgshapiro mid = macid(&bp[1], &ep); 220671345Sgshapiro if (mid == 0) 220771345Sgshapiro break; 220871345Sgshapiro 220964562Sgshapiro p = newstr(ep); 221064562Sgshapiro define(mid, p, e); 221164562Sgshapiro 221264562Sgshapiro /* 221364562Sgshapiro ** HACK ALERT: Unfortunately, 8.10 and 221464562Sgshapiro ** 8.11 reused the ${if_addr} and 221564562Sgshapiro ** ${if_family} macros for both the incoming 221664562Sgshapiro ** interface address/family (getrequests()) 221764562Sgshapiro ** and the outgoing interface address/family 221864562Sgshapiro ** (makeconnection()). In order for D_BINDIF 221964562Sgshapiro ** to work properly, have to preserve the 222064562Sgshapiro ** incoming information in the queue file for 222164562Sgshapiro ** later delivery attempts. The original 222264562Sgshapiro ** information is stored in the envelope 222364562Sgshapiro ** in readqf() so it can be stored in 222464562Sgshapiro ** queueup_macros(). This should be fixed 222564562Sgshapiro ** in 8.12. 222664562Sgshapiro */ 222764562Sgshapiro 222864562Sgshapiro if (strcmp(macname(mid), "if_addr") == 0) 222964562Sgshapiro e->e_if_macros[EIF_ADDR] = p; 223064562Sgshapiro } 223138032Speter break; 223238032Speter 223338032Speter case '.': /* terminate file */ 223438032Speter nomore = TRUE; 223538032Speter break; 223638032Speter 223738032Speter default: 223838032Speter syserr("readqf: %s: line %d: bad line \"%s\"", 223938032Speter qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); 224064562Sgshapiro (void) fclose(qfp); 224138032Speter loseqfile(e, "unrecognized line"); 224238032Speter return FALSE; 224338032Speter } 224438032Speter 224538032Speter if (bp != buf) 224638032Speter free(bp); 224738032Speter } 224838032Speter 224938032Speter /* 225038032Speter ** If we haven't read any lines, this queue file is empty. 225138032Speter ** Arrange to remove it without referencing any null pointers. 225238032Speter */ 225338032Speter 225438032Speter if (LineNumber == 0) 225538032Speter { 225638032Speter errno = 0; 225738032Speter e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 225838032Speter return TRUE; 225938032Speter } 226038032Speter 226164562Sgshapiro /* possibly set ${dsn_ret} macro */ 226264562Sgshapiro if (bitset(EF_RET_PARAM, e->e_flags)) 226364562Sgshapiro { 226464562Sgshapiro if (bitset(EF_NO_BODY_RETN, e->e_flags)) 226564562Sgshapiro define(macid("{dsn_ret}", NULL), "hdrs", e); 226664562Sgshapiro else 226764562Sgshapiro define(macid("{dsn_ret}", NULL), "full", e); 226864562Sgshapiro } 226964562Sgshapiro 227038032Speter /* 227138032Speter ** Arrange to read the data file. 227238032Speter */ 227338032Speter 227438032Speter p = queuename(e, 'd'); 227538032Speter e->e_dfp = fopen(p, "r"); 227638032Speter if (e->e_dfp == NULL) 227738032Speter { 227838032Speter syserr("readqf: cannot open %s", p); 227938032Speter } 228038032Speter else 228138032Speter { 228238032Speter e->e_flags |= EF_HAS_DF; 228338032Speter if (fstat(fileno(e->e_dfp), &st) >= 0) 228438032Speter { 228538032Speter e->e_msgsize = st.st_size + hdrsize; 228638032Speter e->e_dfdev = st.st_dev; 228738032Speter e->e_dfino = st.st_ino; 228838032Speter } 228938032Speter } 229038032Speter 229138032Speter return TRUE; 229238032Speter} 229338032Speter/* 229464562Sgshapiro** PRTSTR -- print a string, "unprintable" characters are shown as \oct 229564562Sgshapiro** 229664562Sgshapiro** Parameters: 229764562Sgshapiro** s -- string to print 229864562Sgshapiro** ml -- maximum length of output 229964562Sgshapiro** 230064562Sgshapiro** Returns: 230164562Sgshapiro** none. 230264562Sgshapiro** 230364562Sgshapiro** Side Effects: 230464562Sgshapiro** Prints a string on stdout. 230564562Sgshapiro*/ 230664562Sgshapiro 230764562Sgshapirostatic void 230864562Sgshapiroprtstr(s, ml) 230964562Sgshapiro char *s; 231064562Sgshapiro int ml; 231164562Sgshapiro{ 231264562Sgshapiro char c; 231364562Sgshapiro 231464562Sgshapiro if (s == NULL) 231564562Sgshapiro return; 231664562Sgshapiro while (ml-- > 0 && ((c = *s++) != '\0')) 231764562Sgshapiro { 231864562Sgshapiro if (c == '\\') 231964562Sgshapiro { 232064562Sgshapiro if (ml-- > 0) 232164562Sgshapiro { 232264562Sgshapiro putchar(c); 232364562Sgshapiro putchar(c); 232464562Sgshapiro } 232564562Sgshapiro } 232664562Sgshapiro else if (isascii(c) && isprint(c)) 232764562Sgshapiro putchar(c); 232864562Sgshapiro else 232964562Sgshapiro { 233064562Sgshapiro if ((ml -= 3) > 0) 233164562Sgshapiro printf("\\%03o", c); 233264562Sgshapiro } 233364562Sgshapiro } 233464562Sgshapiro} 233564562Sgshapiro/* 233638032Speter** PRINTQUEUE -- print out a representation of the mail queue 233738032Speter** 233838032Speter** Parameters: 233938032Speter** none. 234038032Speter** 234138032Speter** Returns: 234238032Speter** none. 234338032Speter** 234438032Speter** Side Effects: 234538032Speter** Prints a listing of the mail queue on the standard output. 234638032Speter*/ 234738032Speter 234838032Spetervoid 234938032Speterprintqueue() 235038032Speter{ 235164562Sgshapiro int i, nrequests = 0; 235264562Sgshapiro 235364562Sgshapiro for (i = 0; i < NumQueues; i++) 235464562Sgshapiro nrequests += print_single_queue(i); 235564562Sgshapiro if (NumQueues > 1) 235664562Sgshapiro printf("\t\tTotal Requests: %d\n", nrequests); 235764562Sgshapiro} 235864562Sgshapiro/* 235964562Sgshapiro** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue 236064562Sgshapiro** 236164562Sgshapiro** Parameters: 236264562Sgshapiro** queuedir -- queue directory 236364562Sgshapiro** 236464562Sgshapiro** Returns: 236571345Sgshapiro** number of entries 236664562Sgshapiro** 236764562Sgshapiro** Side Effects: 236864562Sgshapiro** Prints a listing of the mail queue on the standard output. 236964562Sgshapiro*/ 237064562Sgshapiro 237164562Sgshapirostatic int 237264562Sgshapiroprint_single_queue(queuedir) 237364562Sgshapiro int queuedir; 237464562Sgshapiro{ 237538032Speter register WORK *w; 237638032Speter FILE *f; 237738032Speter int nrequests; 237864562Sgshapiro char qd[MAXPATHLEN]; 237964562Sgshapiro char qddf[MAXPATHLEN]; 238038032Speter char buf[MAXLINE]; 238138032Speter 238264562Sgshapiro if (queuedir == NOQDIR) 238364562Sgshapiro { 238464562Sgshapiro (void) strlcpy(qd, ".", sizeof qd); 238564562Sgshapiro (void) strlcpy(qddf, ".", sizeof qddf); 238664562Sgshapiro } 238764562Sgshapiro else 238864562Sgshapiro { 238964562Sgshapiro (void) snprintf(qd, sizeof qd, "%s%s", 239064562Sgshapiro QPaths[queuedir].qp_name, 239164562Sgshapiro (bitset(QP_SUBQF, QPaths[queuedir].qp_subdirs) ? "/qf" : "")); 239264562Sgshapiro (void) snprintf(qddf, sizeof qddf, "%s%s", 239364562Sgshapiro QPaths[queuedir].qp_name, 239464562Sgshapiro (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : "")); 239564562Sgshapiro } 239664562Sgshapiro 239738032Speter /* 239838032Speter ** Check for permission to print the queue 239938032Speter */ 240038032Speter 240138032Speter if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 240238032Speter { 240338032Speter struct stat st; 240438032Speter# ifdef NGROUPS_MAX 240538032Speter int n; 240638032Speter extern GIDSET_T InitialGidSet[NGROUPS_MAX]; 240764562Sgshapiro# endif /* NGROUPS_MAX */ 240838032Speter 240964562Sgshapiro if (stat(qd, &st) < 0) 241038032Speter { 241164562Sgshapiro syserr("Cannot stat %s", qid_printqueue(queuedir)); 241264562Sgshapiro return 0; 241338032Speter } 241438032Speter# ifdef NGROUPS_MAX 241538032Speter n = NGROUPS_MAX; 241638032Speter while (--n >= 0) 241738032Speter { 241838032Speter if (InitialGidSet[n] == st.st_gid) 241938032Speter break; 242038032Speter } 242138032Speter if (n < 0 && RealGid != st.st_gid) 242264562Sgshapiro# else /* NGROUPS_MAX */ 242338032Speter if (RealGid != st.st_gid) 242464562Sgshapiro# endif /* NGROUPS_MAX */ 242538032Speter { 242638032Speter usrerr("510 You are not permitted to see the queue"); 242738032Speter setstat(EX_NOPERM); 242864562Sgshapiro return 0; 242938032Speter } 243038032Speter } 243138032Speter 243238032Speter /* 243338032Speter ** Read and order the queue. 243438032Speter */ 243538032Speter 243664562Sgshapiro nrequests = orderq(queuedir, TRUE); 243738032Speter 243838032Speter /* 243938032Speter ** Print the work list that we have read. 244038032Speter */ 244138032Speter 244238032Speter /* first see if there is anything */ 244338032Speter if (nrequests <= 0) 244438032Speter { 244564562Sgshapiro printf("%s is empty\n", qid_printqueue(queuedir)); 244664562Sgshapiro return 0; 244738032Speter } 244838032Speter 244964562Sgshapiro CurrentLA = sm_getla(NULL); /* get load average */ 245038032Speter 245164562Sgshapiro printf("\t\t%s (%d request%s", qid_printqueue(queuedir), nrequests, 245264562Sgshapiro nrequests == 1 ? "" : "s"); 245338032Speter if (MaxQueueRun > 0 && nrequests > MaxQueueRun) 245438032Speter printf(", only %d printed", MaxQueueRun); 245538032Speter if (Verbose) 245664562Sgshapiro printf(")\n----Q-ID---- --Size-- -Priority- ---Q-Time--- ---------Sender/Recipient--------\n"); 245738032Speter else 245864562Sgshapiro printf(")\n----Q-ID---- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 245938032Speter for (w = WorkQ; w != NULL; w = w->w_next) 246038032Speter { 246138032Speter struct stat st; 246238032Speter auto time_t submittime = 0; 246338032Speter long dfsize; 246438032Speter int flags = 0; 246538032Speter int qfver; 246638032Speter char statmsg[MAXLINE]; 246738032Speter char bodytype[MAXNAME + 1]; 246864562Sgshapiro char qf[MAXPATHLEN]; 246938032Speter 247064562Sgshapiro printf("%12s", w->w_name + 2); 247164562Sgshapiro (void) snprintf(qf, sizeof qf, "%s/%s", qd, w->w_name); 247264562Sgshapiro f = fopen(qf, "r"); 247338032Speter if (f == NULL) 247438032Speter { 247538032Speter printf(" (job completed)\n"); 247638032Speter errno = 0; 247738032Speter continue; 247838032Speter } 247938032Speter w->w_name[0] = 'd'; 248064562Sgshapiro (void) snprintf(qf, sizeof qf, "%s/%s", qddf, w->w_name); 248164562Sgshapiro if (stat(qf, &st) >= 0) 248238032Speter dfsize = st.st_size; 248338032Speter else 248438032Speter dfsize = -1; 248538032Speter if (w->w_lock) 248638032Speter printf("*"); 248738032Speter else if (w->w_tooyoung) 248838032Speter printf("-"); 248938032Speter else if (shouldqueue(w->w_pri, w->w_ctime)) 249038032Speter printf("X"); 249138032Speter else 249238032Speter printf(" "); 249338032Speter errno = 0; 249438032Speter 249538032Speter statmsg[0] = bodytype[0] = '\0'; 249638032Speter qfver = 0; 249738032Speter while (fgets(buf, sizeof buf, f) != NULL) 249838032Speter { 249938032Speter register int i; 250038032Speter register char *p; 250138032Speter 250238032Speter fixcrlf(buf, TRUE); 250338032Speter switch (buf[0]) 250438032Speter { 250538032Speter case 'V': /* queue file version */ 250638032Speter qfver = atoi(&buf[1]); 250738032Speter break; 250838032Speter 250938032Speter case 'M': /* error message */ 251038032Speter if ((i = strlen(&buf[1])) >= sizeof statmsg) 251138032Speter i = sizeof statmsg - 1; 251264562Sgshapiro memmove(statmsg, &buf[1], i); 251338032Speter statmsg[i] = '\0'; 251438032Speter break; 251538032Speter 251638032Speter case 'B': /* body type */ 251738032Speter if ((i = strlen(&buf[1])) >= sizeof bodytype) 251838032Speter i = sizeof bodytype - 1; 251964562Sgshapiro memmove(bodytype, &buf[1], i); 252038032Speter bodytype[i] = '\0'; 252138032Speter break; 252238032Speter 252338032Speter case 'S': /* sender name */ 252438032Speter if (Verbose) 252564562Sgshapiro { 252664562Sgshapiro printf("%8ld %10ld%c%.12s ", 252764562Sgshapiro dfsize, 252864562Sgshapiro w->w_pri, 252964562Sgshapiro bitset(EF_WARNING, flags) ? '+' : ' ', 253064562Sgshapiro ctime(&submittime) + 4); 253164562Sgshapiro prtstr(&buf[1], 78); 253264562Sgshapiro } 253338032Speter else 253464562Sgshapiro { 253564562Sgshapiro printf("%8ld %.16s ", dfsize, 253664562Sgshapiro ctime(&submittime)); 253764562Sgshapiro prtstr(&buf[1], 40); 253864562Sgshapiro } 253938032Speter if (statmsg[0] != '\0' || bodytype[0] != '\0') 254038032Speter { 254138032Speter printf("\n %10.10s", bodytype); 254238032Speter if (statmsg[0] != '\0') 254338032Speter printf(" (%.*s)", 254464562Sgshapiro Verbose ? 100 : 60, 254564562Sgshapiro statmsg); 254638032Speter } 254738032Speter break; 254838032Speter 254938032Speter case 'C': /* controlling user */ 255038032Speter if (Verbose) 255138032Speter printf("\n\t\t\t\t (---%.74s---)", 255264562Sgshapiro &buf[1]); 255338032Speter break; 255438032Speter 255538032Speter case 'R': /* recipient name */ 255638032Speter p = &buf[1]; 255738032Speter if (qfver >= 1) 255838032Speter { 255938032Speter p = strchr(p, ':'); 256038032Speter if (p == NULL) 256138032Speter break; 256238032Speter p++; 256338032Speter } 256438032Speter if (Verbose) 256564562Sgshapiro { 256664562Sgshapiro printf("\n\t\t\t\t\t "); 256764562Sgshapiro prtstr(p, 73); 256864562Sgshapiro } 256938032Speter else 257064562Sgshapiro { 257164562Sgshapiro printf("\n\t\t\t\t "); 257264562Sgshapiro prtstr(p, 40); 257364562Sgshapiro } 257438032Speter break; 257538032Speter 257638032Speter case 'T': /* creation time */ 257738032Speter submittime = atol(&buf[1]); 257838032Speter break; 257938032Speter 258038032Speter case 'F': /* flag bits */ 258138032Speter for (p = &buf[1]; *p != '\0'; p++) 258238032Speter { 258338032Speter switch (*p) 258438032Speter { 258538032Speter case 'w': 258638032Speter flags |= EF_WARNING; 258738032Speter break; 258838032Speter } 258938032Speter } 259038032Speter } 259138032Speter } 259238032Speter if (submittime == (time_t) 0) 259338032Speter printf(" (no control file)"); 259438032Speter printf("\n"); 259538032Speter (void) fclose(f); 259638032Speter } 259764562Sgshapiro return nrequests; 259838032Speter} 259938032Speter/* 260038032Speter** QUEUENAME -- build a file name in the queue directory for this envelope. 260138032Speter** 260238032Speter** Parameters: 260338032Speter** e -- envelope to build it in/from. 260438032Speter** type -- the file type, used as the first character 260538032Speter** of the file name. 260638032Speter** 260738032Speter** Returns: 260864562Sgshapiro** a pointer to the queue name (in a static buffer). 260938032Speter** 261038032Speter** Side Effects: 261164562Sgshapiro** If no id code is already assigned, queuename() will 261264562Sgshapiro** assign an id code with assign_queueid(). If no queue 261364562Sgshapiro** directory is assigned, one will be set with setnewqueue(). 261438032Speter*/ 261538032Speter 261638032Speterchar * 261738032Speterqueuename(e, type) 261838032Speter register ENVELOPE *e; 261938032Speter int type; 262038032Speter{ 262164562Sgshapiro char *sub = ""; 262264562Sgshapiro static char buf[MAXPATHLEN]; 262338032Speter 262464562Sgshapiro /* Assign an ID if needed */ 262538032Speter if (e->e_id == NULL) 262664562Sgshapiro assign_queueid(e); 262764562Sgshapiro 262864562Sgshapiro /* Assign a queue directory if needed */ 262964562Sgshapiro if (e->e_queuedir == NOQDIR) 263064562Sgshapiro setnewqueue(e); 263164562Sgshapiro 263264562Sgshapiro if (e->e_queuedir == NOQDIR) 263364562Sgshapiro (void) snprintf(buf, sizeof buf, "%cf%s", 263464562Sgshapiro type, e->e_id); 263564562Sgshapiro else 263638032Speter { 263764562Sgshapiro switch (type) 263864562Sgshapiro { 263964562Sgshapiro case 'd': 264064562Sgshapiro if (bitset(QP_SUBDF, QPaths[e->e_queuedir].qp_subdirs)) 264164562Sgshapiro sub = "/df"; 264264562Sgshapiro break; 264338032Speter 264471345Sgshapiro case TEMPQF_LETTER: 264564562Sgshapiro case 't': 264671345Sgshapiro case LOSEQF_LETTER: 264764562Sgshapiro case 'q': 264864562Sgshapiro if (bitset(QP_SUBQF, QPaths[e->e_queuedir].qp_subdirs)) 264964562Sgshapiro sub = "/qf"; 265064562Sgshapiro break; 265164562Sgshapiro 265264562Sgshapiro case 'x': 265364562Sgshapiro if (bitset(QP_SUBXF, QPaths[e->e_queuedir].qp_subdirs)) 265464562Sgshapiro sub = "/xf"; 265564562Sgshapiro break; 265638032Speter } 265738032Speter 265864562Sgshapiro (void) snprintf(buf, sizeof buf, "%s%s/%cf%s", 265964562Sgshapiro QPaths[e->e_queuedir].qp_name, 266064562Sgshapiro sub, type, e->e_id); 266164562Sgshapiro } 266238032Speter 266364562Sgshapiro if (tTd(7, 2)) 266464562Sgshapiro dprintf("queuename: %s\n", buf); 266564562Sgshapiro return buf; 266664562Sgshapiro} 266764562Sgshapiro/* 266864562Sgshapiro** ASSIGN_QUEUEID -- assign a queue ID for this envelope. 266964562Sgshapiro** 267064562Sgshapiro** Assigns an id code if one does not already exist. 267164562Sgshapiro** This code assumes that nothing will remain in the queue for 267264562Sgshapiro** longer than 60 years. It is critical that files with the given 267364562Sgshapiro** name not already exist in the queue. 267464562Sgshapiro** Also initializes e_queuedir to NOQDIR. 267564562Sgshapiro** 267664562Sgshapiro** Parameters: 267764562Sgshapiro** e -- envelope to set it in. 267864562Sgshapiro** 267964562Sgshapiro** Returns: 268064562Sgshapiro** none. 268164562Sgshapiro*/ 268238032Speter 268364562Sgshapirostatic char Base60Code[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"; 268438032Speter 268564562Sgshapirovoid 268664562Sgshapiroassign_queueid(e) 268764562Sgshapiro register ENVELOPE *e; 268864562Sgshapiro{ 268964562Sgshapiro pid_t pid = getpid(); 269064562Sgshapiro static char cX = 0; 269164562Sgshapiro static long random_offset; 269264562Sgshapiro struct tm *tm; 269364562Sgshapiro char idbuf[MAXQFNAME - 2]; 269438032Speter 269564562Sgshapiro if (e->e_id != NULL) 269664562Sgshapiro return; 269738032Speter 269864562Sgshapiro /* see if we need to get a new base time/pid */ 269964562Sgshapiro if (cX >= 60 || LastQueueTime == 0 || LastQueuePid != pid) 270064562Sgshapiro { 270164562Sgshapiro time_t then = LastQueueTime; 270264562Sgshapiro 270364562Sgshapiro /* if the first time through, pick a random offset */ 270464562Sgshapiro if (LastQueueTime == 0) 270564562Sgshapiro random_offset = get_random(); 270664562Sgshapiro 270764562Sgshapiro while ((LastQueueTime = curtime()) == then && 270864562Sgshapiro LastQueuePid == pid) 270938032Speter { 271064562Sgshapiro (void) sleep(1); 271138032Speter } 271264562Sgshapiro LastQueuePid = getpid(); 271364562Sgshapiro cX = 0; 271438032Speter } 271564562Sgshapiro if (tTd(7, 50)) 271664562Sgshapiro dprintf("assign_queueid: random_offset = %ld (%d)\n", 271764562Sgshapiro random_offset, (int)(cX + random_offset) % 60); 271838032Speter 271964562Sgshapiro tm = gmtime(&LastQueueTime); 272064562Sgshapiro idbuf[0] = Base60Code[tm->tm_year % 60]; 272164562Sgshapiro idbuf[1] = Base60Code[tm->tm_mon]; 272264562Sgshapiro idbuf[2] = Base60Code[tm->tm_mday]; 272364562Sgshapiro idbuf[3] = Base60Code[tm->tm_hour]; 272464562Sgshapiro idbuf[4] = Base60Code[tm->tm_min]; 272564562Sgshapiro idbuf[5] = Base60Code[tm->tm_sec]; 272664562Sgshapiro idbuf[6] = Base60Code[((int)cX++ + random_offset) % 60]; 272764562Sgshapiro (void) snprintf(&idbuf[7], sizeof idbuf - 7, "%05d", 272864562Sgshapiro (int) LastQueuePid); 272964562Sgshapiro e->e_id = newstr(idbuf); 273064562Sgshapiro define('i', e->e_id, e); 273164562Sgshapiro e->e_queuedir = NOQDIR; 273264562Sgshapiro if (tTd(7, 1)) 273364562Sgshapiro dprintf("assign_queueid: assigned id %s, e=%lx\n", 273464562Sgshapiro e->e_id, (u_long) e); 273564562Sgshapiro if (LogLevel > 93) 273664562Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); 273738032Speter} 273838032Speter/* 273964562Sgshapiro** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second 274064562Sgshapiro** 274164562Sgshapiro** Make sure one PID can't be used by two processes in any one second. 274264562Sgshapiro** 274364562Sgshapiro** If the system rotates PIDs fast enough, may get the 274464562Sgshapiro** same pid in the same second for two distinct processes. 274564562Sgshapiro** This will interfere with the queue file naming system. 274664562Sgshapiro** 274764562Sgshapiro** Parameters: 274864562Sgshapiro** none 274964562Sgshapiro** 275064562Sgshapiro** Returns: 275164562Sgshapiro** none 275264562Sgshapiro*/ 275364562Sgshapirovoid 275464562Sgshapirosync_queue_time() 275564562Sgshapiro{ 275664562Sgshapiro# if FAST_PID_RECYCLE 275764562Sgshapiro if (OpMode != MD_TEST && 275864562Sgshapiro OpMode != MD_VERIFY && 275964562Sgshapiro LastQueueTime > 0 && 276064562Sgshapiro LastQueuePid == getpid() && 276164562Sgshapiro curtime() == LastQueueTime) 276264562Sgshapiro (void) sleep(1); 276364562Sgshapiro# endif /* FAST_PID_RECYCLE */ 276464562Sgshapiro} 276564562Sgshapiro/* 276638032Speter** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 276738032Speter** 276838032Speter** Parameters: 276938032Speter** e -- the envelope to unlock. 277038032Speter** 277138032Speter** Returns: 277238032Speter** none 277338032Speter** 277438032Speter** Side Effects: 277538032Speter** unlocks the queue for `e'. 277638032Speter*/ 277738032Speter 277838032Spetervoid 277938032Speterunlockqueue(e) 278038032Speter ENVELOPE *e; 278138032Speter{ 278238032Speter if (tTd(51, 4)) 278364562Sgshapiro dprintf("unlockqueue(%s)\n", 278438032Speter e->e_id == NULL ? "NOQUEUE" : e->e_id); 278538032Speter 278664562Sgshapiro 278738032Speter /* if there is a lock file in the envelope, close it */ 278838032Speter if (e->e_lockfp != NULL) 278964562Sgshapiro (void) fclose(e->e_lockfp); 279038032Speter e->e_lockfp = NULL; 279138032Speter 279238032Speter /* don't create a queue id if we don't already have one */ 279338032Speter if (e->e_id == NULL) 279438032Speter return; 279538032Speter 279638032Speter /* remove the transcript */ 279738032Speter if (LogLevel > 87) 279838032Speter sm_syslog(LOG_DEBUG, e->e_id, "unlock"); 279938032Speter if (!tTd(51, 104)) 280038032Speter xunlink(queuename(e, 'x')); 280138032Speter 280238032Speter} 280338032Speter/* 280438032Speter** SETCTLUSER -- create a controlling address 280538032Speter** 280638032Speter** Create a fake "address" given only a local login name; this is 280738032Speter** used as a "controlling user" for future recipient addresses. 280838032Speter** 280938032Speter** Parameters: 281038032Speter** user -- the user name of the controlling user. 281138032Speter** qfver -- the version stamp of this qf file. 281238032Speter** 281338032Speter** Returns: 281438032Speter** An address descriptor for the controlling user. 281538032Speter** 281638032Speter** Side Effects: 281738032Speter** none. 281838032Speter*/ 281938032Speter 282064562Sgshapirostatic ADDRESS * 282138032Spetersetctluser(user, qfver) 282238032Speter char *user; 282338032Speter int qfver; 282438032Speter{ 282538032Speter register ADDRESS *a; 282638032Speter struct passwd *pw; 282738032Speter char *p; 282838032Speter 282938032Speter /* 283038032Speter ** See if this clears our concept of controlling user. 283138032Speter */ 283238032Speter 283338032Speter if (user == NULL || *user == '\0') 283438032Speter return NULL; 283538032Speter 283638032Speter /* 283738032Speter ** Set up addr fields for controlling user. 283838032Speter */ 283938032Speter 284038032Speter a = (ADDRESS *) xalloc(sizeof *a); 284164562Sgshapiro memset((char *) a, '\0', sizeof *a); 284238032Speter 284338032Speter if (*user == '\0') 284438032Speter { 284538032Speter p = NULL; 284638032Speter a->q_user = newstr(DefUser); 284738032Speter } 284838032Speter else if (*user == ':') 284938032Speter { 285038032Speter p = &user[1]; 285138032Speter a->q_user = newstr(p); 285238032Speter } 285338032Speter else 285438032Speter { 285538032Speter p = strtok(user, ":"); 285638032Speter a->q_user = newstr(user); 285738032Speter if (qfver >= 2) 285838032Speter { 285938032Speter if ((p = strtok(NULL, ":")) != NULL) 286038032Speter a->q_uid = atoi(p); 286138032Speter if ((p = strtok(NULL, ":")) != NULL) 286238032Speter a->q_gid = atoi(p); 286338032Speter if ((p = strtok(NULL, ":")) != NULL) 286438032Speter a->q_flags |= QGOODUID; 286538032Speter } 286638032Speter else if ((pw = sm_getpwnam(user)) != NULL) 286738032Speter { 286866494Sgshapiro if (*pw->pw_dir == '\0') 286966494Sgshapiro a->q_home = NULL; 287066494Sgshapiro else if (strcmp(pw->pw_dir, "/") == 0) 287138032Speter a->q_home = ""; 287238032Speter else 287338032Speter a->q_home = newstr(pw->pw_dir); 287438032Speter a->q_uid = pw->pw_uid; 287538032Speter a->q_gid = pw->pw_gid; 287638032Speter a->q_flags |= QGOODUID; 287738032Speter } 287838032Speter } 287938032Speter 288064562Sgshapiro a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 288138032Speter a->q_mailer = LocalMailer; 288238032Speter if (p == NULL) 288364562Sgshapiro a->q_paddr = newstr(a->q_user); 288438032Speter else 288538032Speter a->q_paddr = newstr(p); 288638032Speter return a; 288738032Speter} 288838032Speter/* 288938032Speter** LOSEQFILE -- save the qf as Qf and try to let someone know 289038032Speter** 289138032Speter** Parameters: 289238032Speter** e -- the envelope (e->e_id will be used). 289338032Speter** why -- reported to whomever can hear. 289438032Speter** 289538032Speter** Returns: 289638032Speter** none. 289738032Speter*/ 289838032Speter 289938032Spetervoid 290038032Speterloseqfile(e, why) 290138032Speter register ENVELOPE *e; 290238032Speter char *why; 290338032Speter{ 290438032Speter char *p; 290564562Sgshapiro char buf[MAXPATHLEN]; 290638032Speter 290738032Speter if (e == NULL || e->e_id == NULL) 290838032Speter return; 290938032Speter p = queuename(e, 'q'); 291064562Sgshapiro if (strlen(p) >= (SIZE_T) sizeof buf) 291138032Speter return; 291264562Sgshapiro (void) strlcpy(buf, p, sizeof buf); 291364562Sgshapiro p = queuename(e, LOSEQF_LETTER); 291438032Speter if (rename(buf, p) < 0) 291538032Speter syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); 291638032Speter else if (LogLevel > 0) 291738032Speter sm_syslog(LOG_ALERT, e->e_id, 291864562Sgshapiro "Losing %s: %s", buf, why); 291938032Speter} 292064562Sgshapiro/* 292164562Sgshapiro** QID_PRINTNAME -- create externally printable version of queue id 292264562Sgshapiro** 292364562Sgshapiro** Parameters: 292464562Sgshapiro** e -- the envelope. 292564562Sgshapiro** 292664562Sgshapiro** Returns: 292764562Sgshapiro** a printable version 292864562Sgshapiro*/ 292964562Sgshapiro 293064562Sgshapirochar * 293164562Sgshapiroqid_printname(e) 293264562Sgshapiro ENVELOPE *e; 293364562Sgshapiro{ 293464562Sgshapiro char *id; 293564562Sgshapiro static char idbuf[MAXQFNAME + 34]; 293664562Sgshapiro 293764562Sgshapiro if (e == NULL) 293864562Sgshapiro return ""; 293964562Sgshapiro 294064562Sgshapiro if (e->e_id == NULL) 294164562Sgshapiro id = ""; 294264562Sgshapiro else 294364562Sgshapiro id = e->e_id; 294464562Sgshapiro 294564562Sgshapiro if (e->e_queuedir == NOQDIR) 294664562Sgshapiro return id; 294764562Sgshapiro 294864562Sgshapiro (void) snprintf(idbuf, sizeof idbuf, "%.32s/%s", 294964562Sgshapiro QPaths[e->e_queuedir].qp_name, id); 295064562Sgshapiro return idbuf; 295164562Sgshapiro} 295264562Sgshapiro/* 295364562Sgshapiro** QID_PRINTQUEUE -- create full version of queue directory for df files 295464562Sgshapiro** 295564562Sgshapiro** Parameters: 295664562Sgshapiro** queuedir -- the short version of the queue directory 295764562Sgshapiro** 295864562Sgshapiro** Returns: 295964562Sgshapiro** the full pathname to the queue (static) 296064562Sgshapiro*/ 296164562Sgshapiro 296264562Sgshapirochar * 296364562Sgshapiroqid_printqueue(queuedir) 296464562Sgshapiro int queuedir; 296564562Sgshapiro{ 296664562Sgshapiro char *subdir; 296764562Sgshapiro static char dir[MAXPATHLEN]; 296864562Sgshapiro 296964562Sgshapiro if (queuedir == NOQDIR) 297064562Sgshapiro return QueueDir; 297164562Sgshapiro 297264562Sgshapiro if (strcmp(QPaths[queuedir].qp_name, ".") == 0) 297364562Sgshapiro subdir = NULL; 297464562Sgshapiro else 297564562Sgshapiro subdir = QPaths[queuedir].qp_name; 297664562Sgshapiro 297764562Sgshapiro (void) snprintf(dir, sizeof dir, "%s%s%s%s", QueueDir, 297864562Sgshapiro subdir == NULL ? "" : "/", 297964562Sgshapiro subdir == NULL ? "" : subdir, 298064562Sgshapiro (bitset(QP_SUBDF, QPaths[queuedir].qp_subdirs) ? "/df" : "")); 298164562Sgshapiro return dir; 298264562Sgshapiro} 298364562Sgshapiro/* 298464562Sgshapiro** SETNEWQUEUE -- Sets a new queue directory 298564562Sgshapiro** 298664562Sgshapiro** Assign a queue directory to an envelope and store the directory 298764562Sgshapiro** in e->e_queuedir. The queue is chosen at random. 298864562Sgshapiro** 298964562Sgshapiro** This routine may be improved in the future to allow for more 299064562Sgshapiro** elaborate queueing schemes. Suggestions and code contributions 299164562Sgshapiro** are welcome. 299264562Sgshapiro** 299364562Sgshapiro** Parameters: 299464562Sgshapiro** e -- envelope to assign a queue for. 299564562Sgshapiro** 299664562Sgshapiro** Returns: 299764562Sgshapiro** none. 299864562Sgshapiro*/ 299964562Sgshapiro 300064562Sgshapirovoid 300164562Sgshapirosetnewqueue(e) 300264562Sgshapiro ENVELOPE *e; 300364562Sgshapiro{ 300464562Sgshapiro int idx; 300564562Sgshapiro 300664562Sgshapiro if (tTd(41, 20)) 300764562Sgshapiro dprintf("setnewqueue: called\n"); 300864562Sgshapiro 300964562Sgshapiro if (e->e_queuedir != NOQDIR) 301064562Sgshapiro { 301164562Sgshapiro if (tTd(41, 20)) 301264562Sgshapiro dprintf("setnewqueue: e_queuedir already assigned (%s)\n", 301364562Sgshapiro qid_printqueue(e->e_queuedir)); 301464562Sgshapiro return; 301564562Sgshapiro } 301664562Sgshapiro 301764562Sgshapiro if (NumQueues == 1) 301864562Sgshapiro idx = 0; 301964562Sgshapiro else 302064562Sgshapiro { 302164562Sgshapiro#if RANDOMSHIFT 302264562Sgshapiro /* lower bits are not random "enough", select others */ 302364562Sgshapiro idx = (get_random() >> RANDOMSHIFT) % NumQueues; 302464562Sgshapiro#else /* RANDOMSHIFT */ 302564562Sgshapiro idx = get_random() % NumQueues; 302664562Sgshapiro#endif /* RANDOMSHIFT */ 302764562Sgshapiro if (tTd(41, 15)) 302864562Sgshapiro dprintf("setnewqueue: get_random() %% %d = %d\n", 302964562Sgshapiro NumQueues, idx); 303064562Sgshapiro } 303164562Sgshapiro 303264562Sgshapiro e->e_queuedir = idx; 303364562Sgshapiro if (tTd(41, 3)) 303464562Sgshapiro dprintf("setnewqueue: Assigned queue directory %s\n", 303564562Sgshapiro qid_printqueue(e->e_queuedir)); 303664562Sgshapiro} 303764562Sgshapiro 303864562Sgshapiro/* 303964562Sgshapiro** CHKQDIR -- check a queue directory 304064562Sgshapiro** 304164562Sgshapiro** Parameters: 304264562Sgshapiro** name -- name of queue directory 304364562Sgshapiro** sff -- flags for safefile() 304464562Sgshapiro** 304564562Sgshapiro** Returns: 304664562Sgshapiro** is it a queue directory? 304764562Sgshapiro*/ 304864562Sgshapiro 304964562Sgshapirostatic bool 305064562Sgshapirochkqdir(name, sff) 305164562Sgshapiro char *name; 305264562Sgshapiro long sff; 305364562Sgshapiro{ 305464562Sgshapiro struct stat statb; 305564562Sgshapiro int i; 305664562Sgshapiro 305766494Sgshapiro /* skip over . and .. directories */ 305866494Sgshapiro if (name[0] == '.' && 305966494Sgshapiro (name[1] == '\0' || (name[2] == '.' && name[3] == '\0'))) 306066494Sgshapiro return FALSE; 306164562Sgshapiro# if HASLSTAT 306264562Sgshapiro if (lstat(name, &statb) < 0) 306364562Sgshapiro# else /* HASLSTAT */ 306464562Sgshapiro if (stat(name, &statb) < 0) 306564562Sgshapiro# endif /* HASLSTAT */ 306664562Sgshapiro { 306764562Sgshapiro if (tTd(41, 2)) 306864562Sgshapiro dprintf("multiqueue_cache: stat(\"%s\"): %s\n", 306964562Sgshapiro name, errstring(errno)); 307064562Sgshapiro return FALSE; 307164562Sgshapiro } 307264562Sgshapiro# if HASLSTAT 307364562Sgshapiro if (S_ISLNK(statb.st_mode)) 307464562Sgshapiro { 307564562Sgshapiro /* 307664562Sgshapiro ** For a symlink we need to make sure the 307764562Sgshapiro ** target is a directory 307864562Sgshapiro */ 307964562Sgshapiro if (stat(name, &statb) < 0) 308064562Sgshapiro { 308164562Sgshapiro if (tTd(41, 2)) 308264562Sgshapiro dprintf("multiqueue_cache: stat(\"%s\"): %s\n", 308364562Sgshapiro name, errstring(errno)); 308464562Sgshapiro return FALSE; 308564562Sgshapiro } 308664562Sgshapiro } 308764562Sgshapiro# endif /* HASLSTAT */ 308864562Sgshapiro 308964562Sgshapiro if (!S_ISDIR(statb.st_mode)) 309064562Sgshapiro { 309164562Sgshapiro if (tTd(41, 2)) 309264562Sgshapiro dprintf("multiqueue_cache: \"%s\": Not a directory\n", 309364562Sgshapiro name); 309464562Sgshapiro return FALSE; 309564562Sgshapiro } 309664562Sgshapiro 309764562Sgshapiro /* Print a warning if unsafe (but still use it) */ 309864562Sgshapiro i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0); 309964562Sgshapiro if (i != 0 && tTd(41, 2)) 310064562Sgshapiro dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", 310164562Sgshapiro name, errstring(i)); 310264562Sgshapiro return TRUE; 310364562Sgshapiro} 310464562Sgshapiro 310564562Sgshapiro/* 310664562Sgshapiro** MULTIQUEUE_CACHE -- cache a list of paths to queues. 310764562Sgshapiro** 310864562Sgshapiro** Each potential queue is checked as the cache is built. 310964562Sgshapiro** Thereafter, each is blindly trusted. 311064562Sgshapiro** Note that we can be called again after a timeout to rebuild 311164562Sgshapiro** (although code for that is not ready yet). 311264562Sgshapiro** 311364562Sgshapiro** Parameters: 311464562Sgshapiro** none 311564562Sgshapiro** 311664562Sgshapiro** Returns: 311764562Sgshapiro** none 311864562Sgshapiro*/ 311964562Sgshapiro 312064562Sgshapirovoid 312164562Sgshapiromultiqueue_cache() 312264562Sgshapiro{ 312364562Sgshapiro register DIR *dp; 312464562Sgshapiro register struct dirent *d; 312564562Sgshapiro char *cp; 312664562Sgshapiro int i, len; 312764562Sgshapiro int slotsleft = 0; 312864562Sgshapiro long sff = SFF_ANYFILE; 312964562Sgshapiro char qpath[MAXPATHLEN]; 313064562Sgshapiro char subdir[MAXPATHLEN]; 313164562Sgshapiro 313264562Sgshapiro if (tTd(41, 20)) 313364562Sgshapiro dprintf("multiqueue_cache: called\n"); 313464562Sgshapiro 313564562Sgshapiro if (NumQueues != 0 && QPaths != NULL) 313664562Sgshapiro { 313764562Sgshapiro for (i = 0; i < NumQueues; i++) 313864562Sgshapiro { 313964562Sgshapiro if (QPaths[i].qp_name != NULL) 314064562Sgshapiro (void) free(QPaths[i].qp_name); 314164562Sgshapiro } 314264562Sgshapiro (void) free((char *)QPaths); 314364562Sgshapiro QPaths = NULL; 314464562Sgshapiro NumQueues = 0; 314564562Sgshapiro } 314664562Sgshapiro 314764562Sgshapiro /* If running as root, allow safedirpath() checks to use privs */ 314864562Sgshapiro if (RunAsUid == 0) 314964562Sgshapiro sff |= SFF_ROOTOK; 315064562Sgshapiro 315164562Sgshapiro (void) snprintf(qpath, sizeof qpath, "%s", QueueDir); 315264562Sgshapiro len = strlen(qpath) - 1; 315364562Sgshapiro cp = &qpath[len]; 315464562Sgshapiro if (*cp == '*') 315564562Sgshapiro { 315664562Sgshapiro *cp = '\0'; 315764562Sgshapiro if ((cp = strrchr(qpath, '/')) == NULL) 315864562Sgshapiro { 315964562Sgshapiro syserr("QueueDirectory: can not wildcard relative path"); 316064562Sgshapiro if (tTd(41, 2)) 316164562Sgshapiro dprintf("multiqueue_cache: \"%s\": Can not wildcard relative path.\n", 316271345Sgshapiro qpath); 316364562Sgshapiro ExitStat = EX_CONFIG; 316464562Sgshapiro return; 316564562Sgshapiro } 316664562Sgshapiro if (cp == qpath) 316764562Sgshapiro { 316864562Sgshapiro /* 316964562Sgshapiro ** Special case of top level wildcard, like /foo* 317064562Sgshapiro */ 317164562Sgshapiro 317264562Sgshapiro (void) snprintf(qpath + 1, sizeof qpath - 1, 317364562Sgshapiro "%s", qpath); 317464562Sgshapiro ++cp; 317564562Sgshapiro } 317664562Sgshapiro *(cp++) = '\0'; 317764562Sgshapiro len = strlen(cp); 317864562Sgshapiro 317964562Sgshapiro if (tTd(41, 2)) 318064562Sgshapiro dprintf("multiqueue_cache: prefix=\"%s\"\n", cp); 318164562Sgshapiro 318264562Sgshapiro QueueDir = newstr(qpath); 318364562Sgshapiro 318464562Sgshapiro /* 318564562Sgshapiro ** XXX Should probably wrap this whole loop in a timeout 318664562Sgshapiro ** in case some wag decides to NFS mount the queues. 318764562Sgshapiro */ 318864562Sgshapiro 318964562Sgshapiro /* test path to get warning messages */ 319064562Sgshapiro i= safedirpath(QueueDir, RunAsUid, RunAsGid, NULL, sff, 0, 0); 319164562Sgshapiro if (i != 0 && tTd(41, 2)) 319264562Sgshapiro dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", 319364562Sgshapiro QueueDir, errstring(i)); 319464562Sgshapiro 319564562Sgshapiro if (chdir(QueueDir) < 0) 319664562Sgshapiro { 319764562Sgshapiro syserr("can not chdir(%s)", QueueDir); 319864562Sgshapiro if (tTd(41, 2)) 319964562Sgshapiro dprintf("multiqueue_cache: \"%s\": %s\n", 320064562Sgshapiro qpath, errstring(errno)); 320164562Sgshapiro ExitStat = EX_CONFIG; 320264562Sgshapiro return; 320364562Sgshapiro } 320464562Sgshapiro 320564562Sgshapiro if ((dp = opendir(".")) == NULL) 320664562Sgshapiro { 320764562Sgshapiro syserr("can not opendir(%s)", QueueDir); 320864562Sgshapiro if (tTd(41, 2)) 320964562Sgshapiro dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", 321064562Sgshapiro QueueDir, errstring(errno)); 321164562Sgshapiro ExitStat = EX_CONFIG; 321264562Sgshapiro return; 321364562Sgshapiro } 321464562Sgshapiro while ((d = readdir(dp)) != NULL) 321564562Sgshapiro { 321664562Sgshapiro if (strncmp(d->d_name, cp, len) != 0) 321764562Sgshapiro { 321864562Sgshapiro if (tTd(41, 5)) 321964562Sgshapiro dprintf("multiqueue_cache: \"%s\", skipped\n", 322064562Sgshapiro d->d_name); 322164562Sgshapiro continue; 322264562Sgshapiro } 322364562Sgshapiro if (!chkqdir(d->d_name, sff)) 322464562Sgshapiro continue; 322564562Sgshapiro 322664562Sgshapiro if (QPaths == NULL) 322764562Sgshapiro { 322864562Sgshapiro slotsleft = 20; 322964562Sgshapiro QPaths = (QPATHS *)xalloc((sizeof *QPaths) * 323064562Sgshapiro slotsleft); 323164562Sgshapiro NumQueues = 0; 323264562Sgshapiro } 323364562Sgshapiro else if (slotsleft < 1) 323464562Sgshapiro { 323564562Sgshapiro QPaths = (QPATHS *)realloc((char *)QPaths, 323664562Sgshapiro (sizeof *QPaths) * 323764562Sgshapiro (NumQueues + 10)); 323864562Sgshapiro if (QPaths == NULL) 323964562Sgshapiro { 324064562Sgshapiro (void) closedir(dp); 324164562Sgshapiro return; 324264562Sgshapiro } 324364562Sgshapiro slotsleft += 10; 324464562Sgshapiro } 324564562Sgshapiro 324664562Sgshapiro /* check subdirs */ 324764562Sgshapiro QPaths[NumQueues].qp_subdirs = QP_NOSUB; 324864562Sgshapiro (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", 324964562Sgshapiro qpath, d->d_name, "qf"); 325064562Sgshapiro if (chkqdir(subdir, sff)) 325164562Sgshapiro QPaths[NumQueues].qp_subdirs |= QP_SUBQF; 325264562Sgshapiro 325364562Sgshapiro (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", 325464562Sgshapiro qpath, d->d_name, "df"); 325564562Sgshapiro if (chkqdir(subdir, sff)) 325664562Sgshapiro QPaths[NumQueues].qp_subdirs |= QP_SUBDF; 325764562Sgshapiro 325864562Sgshapiro (void) snprintf(subdir, sizeof subdir, "%s/%s/%s", 325964562Sgshapiro qpath, d->d_name, "xf"); 326064562Sgshapiro if (chkqdir(subdir, sff)) 326164562Sgshapiro QPaths[NumQueues].qp_subdirs |= QP_SUBXF; 326264562Sgshapiro 326364562Sgshapiro /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */ 326464562Sgshapiro /* maybe even - 17 (subdirs) */ 326564562Sgshapiro QPaths[NumQueues].qp_name = newstr(d->d_name); 326664562Sgshapiro if (tTd(41, 2)) 326764562Sgshapiro dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", 326864562Sgshapiro NumQueues, d->d_name, 326964562Sgshapiro QPaths[NumQueues].qp_subdirs); 327064562Sgshapiro NumQueues++; 327164562Sgshapiro slotsleft--; 327264562Sgshapiro } 327364562Sgshapiro (void) closedir(dp); 327464562Sgshapiro } 327564562Sgshapiro if (NumQueues == 0) 327664562Sgshapiro { 327764562Sgshapiro if (*cp != '*' && tTd(41, 2)) 327864562Sgshapiro dprintf("multiqueue_cache: \"%s\": No wildcard suffix character\n", 327964562Sgshapiro QueueDir); 328064562Sgshapiro QPaths = (QPATHS *)xalloc(sizeof *QPaths); 328164562Sgshapiro QPaths[0].qp_name = newstr("."); 328264562Sgshapiro QPaths[0].qp_subdirs = QP_NOSUB; 328364562Sgshapiro NumQueues = 1; 328464562Sgshapiro 328564562Sgshapiro /* test path to get warning messages */ 328664562Sgshapiro (void) safedirpath(QueueDir, RunAsUid, RunAsGid, 328764562Sgshapiro NULL, sff, 0, 0); 328864562Sgshapiro if (chdir(QueueDir) < 0) 328964562Sgshapiro { 329064562Sgshapiro syserr("can not chdir(%s)", QueueDir); 329164562Sgshapiro if (tTd(41, 2)) 329264562Sgshapiro dprintf("multiqueue_cache: \"%s\": %s\n", 329364562Sgshapiro QueueDir, errstring(errno)); 329464562Sgshapiro ExitStat = EX_CONFIG; 329564562Sgshapiro } 329664562Sgshapiro 329764562Sgshapiro /* check subdirs */ 329864562Sgshapiro (void) snprintf(subdir, sizeof subdir, "%s/qf", QueueDir); 329964562Sgshapiro if (chkqdir(subdir, sff)) 330064562Sgshapiro QPaths[0].qp_subdirs |= QP_SUBQF; 330164562Sgshapiro 330264562Sgshapiro (void) snprintf(subdir, sizeof subdir, "%s/df", QueueDir); 330364562Sgshapiro if (chkqdir(subdir, sff)) 330464562Sgshapiro QPaths[0].qp_subdirs |= QP_SUBDF; 330564562Sgshapiro 330664562Sgshapiro (void) snprintf(subdir, sizeof subdir, "%s/xf", QueueDir); 330764562Sgshapiro if (chkqdir(subdir, sff)) 330864562Sgshapiro QPaths[0].qp_subdirs |= QP_SUBXF; 330964562Sgshapiro } 331064562Sgshapiro} 331164562Sgshapiro 331264562Sgshapiro# if 0 331364562Sgshapiro/* 331464562Sgshapiro** HASHFQN -- calculate a hash value for a fully qualified host name 331564562Sgshapiro** 331664562Sgshapiro** Arguments: 331764562Sgshapiro** fqn -- an all lower-case host.domain string 331864562Sgshapiro** buckets -- the number of buckets (queue directories) 331964562Sgshapiro** 332064562Sgshapiro** Returns: 332164562Sgshapiro** a bucket number (signed integer) 332264562Sgshapiro** -1 on error 332364562Sgshapiro** 332464562Sgshapiro** Contributed by Exactis.com, Inc. 332564562Sgshapiro*/ 332664562Sgshapiro 332764562Sgshapiroint 332864562Sgshapirohashfqn(fqn, buckets) 332964562Sgshapiro register char *fqn; 333064562Sgshapiro int buckets; 333164562Sgshapiro{ 333264562Sgshapiro register char *p; 333364562Sgshapiro register int h = 0, hash, cnt; 333464562Sgshapiro# define WATERINC (1000) 333564562Sgshapiro 333664562Sgshapiro if (fqn == NULL) 333764562Sgshapiro return -1; 333864562Sgshapiro 333964562Sgshapiro /* 334064562Sgshapiro ** A variation on the gdb hash 334164562Sgshapiro ** This is the best as of Feb 19, 1996 --bcx 334264562Sgshapiro */ 334364562Sgshapiro 334464562Sgshapiro p = fqn; 334564562Sgshapiro h = 0x238F13AF * strlen(p); 334664562Sgshapiro for (cnt = 0; *p != 0; ++p, cnt++) 334764562Sgshapiro { 334864562Sgshapiro h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF; 334964562Sgshapiro } 335064562Sgshapiro h = (1103515243 * h + 12345) & 0x7FFFFFFF; 335164562Sgshapiro if (buckets < 2) 335264562Sgshapiro hash = 0; 335364562Sgshapiro else 335464562Sgshapiro hash = (h % buckets); 335564562Sgshapiro 335664562Sgshapiro return hash; 335764562Sgshapiro} 335864562Sgshapiro# endif /* 0 */ 335964562Sgshapiro 336064562Sgshapiro# if _FFR_QUEUEDELAY 336164562Sgshapiro/* 336264562Sgshapiro** QUEUEDELAY -- compute queue delay time 336364562Sgshapiro** 336464562Sgshapiro** Parameters: 336564562Sgshapiro** e -- the envelope to queue up. 336664562Sgshapiro** 336764562Sgshapiro** Returns: 336864562Sgshapiro** queue delay time 336964562Sgshapiro** 337064562Sgshapiro** Side Effects: 337164562Sgshapiro** may change e_queuedelay 337264562Sgshapiro*/ 337364562Sgshapiro 337464562Sgshapirostatic time_t 337564562Sgshapiroqueuedelay(e) 337664562Sgshapiro ENVELOPE *e; 337764562Sgshapiro{ 337864562Sgshapiro time_t qd; 337964562Sgshapiro 338064562Sgshapiro if (e->e_queuealg == QD_EXP) 338164562Sgshapiro { 338264562Sgshapiro if (e->e_queuedelay == 0) 338364562Sgshapiro e->e_queuedelay = QueueInitDelay; 338464562Sgshapiro else 338564562Sgshapiro { 338664562Sgshapiro e->e_queuedelay *= 2; 338764562Sgshapiro if (e->e_queuedelay > QueueMaxDelay) 338864562Sgshapiro e->e_queuedelay = QueueMaxDelay; 338964562Sgshapiro } 339064562Sgshapiro qd = e->e_queuedelay; 339164562Sgshapiro } 339264562Sgshapiro else 339364562Sgshapiro qd = MinQueueAge; 339464562Sgshapiro return qd; 339564562Sgshapiro} 339664562Sgshapiro# endif /* _FFR_QUEUEDELAY */ 339764562Sgshapiro#endif /* QUEUE */ 3398