queue.c revision 38032
138032Speter/* 238032Speter * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 338032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 438032Speter * Copyright (c) 1988, 1993 538032Speter * The Regents of the University of California. All rights reserved. 638032Speter * 738032Speter * By using this file, you agree to the terms and conditions set 838032Speter * forth in the LICENSE file which can be found at the top level of 938032Speter * the sendmail distribution. 1038032Speter * 1138032Speter */ 1238032Speter 1338032Speter# include "sendmail.h" 1438032Speter 1538032Speter#ifndef lint 1638032Speter#if QUEUE 1738032Speterstatic char sccsid[] = "@(#)queue.c 8.202 (Berkeley) 6/15/98 (with queueing)"; 1838032Speter#else 1938032Speterstatic char sccsid[] = "@(#)queue.c 8.202 (Berkeley) 6/15/98 (without queueing)"; 2038032Speter#endif 2138032Speter#endif /* not lint */ 2238032Speter 2338032Speter# include <errno.h> 2438032Speter# include <dirent.h> 2538032Speter 2638032Speter# if QUEUE 2738032Speter 2838032Speter/* 2938032Speter** Work queue. 3038032Speter*/ 3138032Speter 3238032Speterstruct work 3338032Speter{ 3438032Speter char *w_name; /* name of control file */ 3538032Speter char *w_host; /* name of recipient host */ 3638032Speter bool w_lock; /* is message locked? */ 3738032Speter bool w_tooyoung; /* is it too young to run? */ 3838032Speter long w_pri; /* priority of message, see below */ 3938032Speter time_t w_ctime; /* creation time of message */ 4038032Speter struct work *w_next; /* next in queue */ 4138032Speter}; 4238032Speter 4338032Spetertypedef struct work WORK; 4438032Speter 4538032SpeterWORK *WorkQ; /* queue of things to be done */ 4638032Speter 4738032Speter#define QF_VERSION 2 /* version number of this queue format */ 4838032Speter 4938032Speterextern int orderq __P((bool)); 5038032Speter/* 5138032Speter** QUEUEUP -- queue a message up for future transmission. 5238032Speter** 5338032Speter** Parameters: 5438032Speter** e -- the envelope to queue up. 5538032Speter** announce -- if TRUE, tell when you are queueing up. 5638032Speter** 5738032Speter** Returns: 5838032Speter** none. 5938032Speter** 6038032Speter** Side Effects: 6138032Speter** The current request are saved in a control file. 6238032Speter** The queue file is left locked. 6338032Speter*/ 6438032Speter 6538032Spetervoid 6638032Speterqueueup(e, announce) 6738032Speter register ENVELOPE *e; 6838032Speter bool announce; 6938032Speter{ 7038032Speter char *qf; 7138032Speter register FILE *tfp; 7238032Speter register HDR *h; 7338032Speter register ADDRESS *q; 7438032Speter int fd; 7538032Speter int i; 7638032Speter bool newid; 7738032Speter register char *p; 7838032Speter MAILER nullmailer; 7938032Speter MCI mcibuf; 8038032Speter char tf[MAXQFNAME]; 8138032Speter char buf[MAXLINE]; 8238032Speter extern void printctladdr __P((ADDRESS *, FILE *)); 8338032Speter 8438032Speter /* 8538032Speter ** Create control file. 8638032Speter */ 8738032Speter 8838032Speter newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); 8938032Speter 9038032Speter /* if newid, queuename will create a locked qf file in e->lockfp */ 9138032Speter strcpy(tf, queuename(e, 't')); 9238032Speter tfp = e->e_lockfp; 9338032Speter if (tfp == NULL) 9438032Speter newid = FALSE; 9538032Speter 9638032Speter /* if newid, just write the qf file directly (instead of tf file) */ 9738032Speter if (!newid) 9838032Speter { 9938032Speter /* get a locked tf file */ 10038032Speter for (i = 0; i < 128; i++) 10138032Speter { 10238032Speter fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode); 10338032Speter if (fd < 0) 10438032Speter { 10538032Speter if (errno != EEXIST) 10638032Speter break; 10738032Speter if (LogLevel > 0 && (i % 32) == 0) 10838032Speter sm_syslog(LOG_ALERT, e->e_id, 10938032Speter "queueup: cannot create %s, uid=%d: %s", 11038032Speter tf, geteuid(), errstring(errno)); 11138032Speter } 11238032Speter else 11338032Speter { 11438032Speter if (lockfile(fd, tf, NULL, LOCK_EX|LOCK_NB)) 11538032Speter break; 11638032Speter else if (LogLevel > 0 && (i % 32) == 0) 11738032Speter sm_syslog(LOG_ALERT, e->e_id, 11838032Speter "queueup: cannot lock %s: %s", 11938032Speter tf, errstring(errno)); 12038032Speter close(fd); 12138032Speter } 12238032Speter 12338032Speter if ((i % 32) == 31) 12438032Speter { 12538032Speter /* save the old temp file away */ 12638032Speter (void) rename(tf, queuename(e, 'T')); 12738032Speter } 12838032Speter else 12938032Speter sleep(i % 32); 13038032Speter } 13138032Speter if (fd < 0 || (tfp = fdopen(fd, "w")) == NULL) 13238032Speter { 13338032Speter printopenfds(TRUE); 13438032Speter syserr("!queueup: cannot create queue temp file %s, uid=%d", 13538032Speter tf, geteuid()); 13638032Speter } 13738032Speter } 13838032Speter 13938032Speter if (tTd(40, 1)) 14038032Speter printf("\n>>>>> queueing %s%s >>>>>\n", e->e_id, 14138032Speter newid ? " (new id)" : ""); 14238032Speter if (tTd(40, 3)) 14338032Speter { 14438032Speter extern void printenvflags __P((ENVELOPE *)); 14538032Speter 14638032Speter printf(" e_flags="); 14738032Speter printenvflags(e); 14838032Speter } 14938032Speter if (tTd(40, 32)) 15038032Speter { 15138032Speter printf(" sendq="); 15238032Speter printaddr(e->e_sendqueue, TRUE); 15338032Speter } 15438032Speter if (tTd(40, 9)) 15538032Speter { 15638032Speter printf(" tfp="); 15738032Speter dumpfd(fileno(tfp), TRUE, FALSE); 15838032Speter printf(" lockfp="); 15938032Speter if (e->e_lockfp == NULL) 16038032Speter printf("NULL\n"); 16138032Speter else 16238032Speter dumpfd(fileno(e->e_lockfp), TRUE, FALSE); 16338032Speter } 16438032Speter 16538032Speter /* 16638032Speter ** If there is no data file yet, create one. 16738032Speter */ 16838032Speter 16938032Speter if (!bitset(EF_HAS_DF, e->e_flags)) 17038032Speter { 17138032Speter register FILE *dfp = NULL; 17238032Speter char dfname[MAXQFNAME]; 17338032Speter struct stat stbuf; 17438032Speter 17538032Speter strcpy(dfname, queuename(e, 'd')); 17638032Speter fd = open(dfname, O_WRONLY|O_CREAT|O_TRUNC, FileMode); 17738032Speter if (fd < 0 || (dfp = fdopen(fd, "w")) == NULL) 17838032Speter syserr("!queueup: cannot create data temp file %s, uid=%d", 17938032Speter dfname, geteuid()); 18038032Speter if (fstat(fd, &stbuf) < 0) 18138032Speter e->e_dfino = -1; 18238032Speter else 18338032Speter { 18438032Speter e->e_dfdev = stbuf.st_dev; 18538032Speter e->e_dfino = stbuf.st_ino; 18638032Speter } 18738032Speter e->e_flags |= EF_HAS_DF; 18838032Speter bzero(&mcibuf, sizeof mcibuf); 18938032Speter mcibuf.mci_out = dfp; 19038032Speter mcibuf.mci_mailer = FileMailer; 19138032Speter (*e->e_putbody)(&mcibuf, e, NULL); 19238032Speter (void) xfclose(dfp, "queueup dfp", e->e_id); 19338032Speter e->e_putbody = putbody; 19438032Speter } 19538032Speter 19638032Speter /* 19738032Speter ** Output future work requests. 19838032Speter ** Priority and creation time should be first, since 19938032Speter ** they are required by orderq. 20038032Speter */ 20138032Speter 20238032Speter /* output queue version number (must be first!) */ 20338032Speter fprintf(tfp, "V%d\n", QF_VERSION); 20438032Speter 20538032Speter /* output creation time */ 20638032Speter fprintf(tfp, "T%ld\n", (long) e->e_ctime); 20738032Speter 20838032Speter /* output last delivery time */ 20938032Speter fprintf(tfp, "K%ld\n", (long) e->e_dtime); 21038032Speter 21138032Speter /* output number of delivery attempts */ 21238032Speter fprintf(tfp, "N%d\n", e->e_ntries); 21338032Speter 21438032Speter /* output message priority */ 21538032Speter fprintf(tfp, "P%ld\n", e->e_msgpriority); 21638032Speter 21738032Speter /* output inode number of data file */ 21838032Speter /* XXX should probably include device major/minor too */ 21938032Speter if (e->e_dfino != -1) 22038032Speter { 22138032Speter if (sizeof e->e_dfino > sizeof(long)) 22238032Speter fprintf(tfp, "I%d/%d/%s\n", 22338032Speter major(e->e_dfdev), minor(e->e_dfdev), 22438032Speter quad_to_string(e->e_dfino)); 22538032Speter else 22638032Speter fprintf(tfp, "I%d/%d/%lu\n", 22738032Speter major(e->e_dfdev), minor(e->e_dfdev), 22838032Speter (unsigned long) e->e_dfino); 22938032Speter } 23038032Speter 23138032Speter /* output body type */ 23238032Speter if (e->e_bodytype != NULL) 23338032Speter fprintf(tfp, "B%s\n", denlstring(e->e_bodytype, TRUE, FALSE)); 23438032Speter 23538032Speter#if _FFR_SAVE_CHARSET 23638032Speter if (e->e_charset != NULL) 23738032Speter fprintf(tfp, "X%s\n", denlstring(e->e_charset, TRUE, FALSE)); 23838032Speter#endif 23938032Speter 24038032Speter /* message from envelope, if it exists */ 24138032Speter if (e->e_message != NULL) 24238032Speter fprintf(tfp, "M%s\n", denlstring(e->e_message, TRUE, FALSE)); 24338032Speter 24438032Speter /* send various flag bits through */ 24538032Speter p = buf; 24638032Speter if (bitset(EF_WARNING, e->e_flags)) 24738032Speter *p++ = 'w'; 24838032Speter if (bitset(EF_RESPONSE, e->e_flags)) 24938032Speter *p++ = 'r'; 25038032Speter if (bitset(EF_HAS8BIT, e->e_flags)) 25138032Speter *p++ = '8'; 25238032Speter if (bitset(EF_DELETE_BCC, e->e_flags)) 25338032Speter *p++ = 'b'; 25438032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 25538032Speter *p++ = 'd'; 25638032Speter if (bitset(EF_NO_BODY_RETN, e->e_flags)) 25738032Speter *p++ = 'n'; 25838032Speter *p++ = '\0'; 25938032Speter if (buf[0] != '\0') 26038032Speter fprintf(tfp, "F%s\n", buf); 26138032Speter 26238032Speter /* $r and $s and $_ macro values */ 26338032Speter if ((p = macvalue('r', e)) != NULL) 26438032Speter fprintf(tfp, "$r%s\n", denlstring(p, TRUE, FALSE)); 26538032Speter if ((p = macvalue('s', e)) != NULL) 26638032Speter fprintf(tfp, "$s%s\n", denlstring(p, TRUE, FALSE)); 26738032Speter if ((p = macvalue('_', e)) != NULL) 26838032Speter fprintf(tfp, "$_%s\n", denlstring(p, TRUE, FALSE)); 26938032Speter 27038032Speter /* output name of sender */ 27138032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 27238032Speter p = e->e_sender; 27338032Speter else 27438032Speter p = e->e_from.q_paddr; 27538032Speter fprintf(tfp, "S%s\n", denlstring(p, TRUE, FALSE)); 27638032Speter 27738032Speter /* output ESMTP-supplied "original" information */ 27838032Speter if (e->e_envid != NULL) 27938032Speter fprintf(tfp, "Z%s\n", denlstring(e->e_envid, TRUE, FALSE)); 28038032Speter 28138032Speter /* output list of recipient addresses */ 28238032Speter printctladdr(NULL, NULL); 28338032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 28438032Speter { 28538032Speter if (bitset(QDONTSEND|QBADADDR|QSENT, q->q_flags)) 28638032Speter { 28738032Speter#if XDEBUG 28838032Speter if (bitset(QQUEUEUP, q->q_flags)) 28938032Speter sm_syslog(LOG_DEBUG, e->e_id, 29038032Speter "dropenvelope: q_flags = %x, paddr = %s", 29138032Speter q->q_flags, q->q_paddr); 29238032Speter#endif 29338032Speter continue; 29438032Speter } 29538032Speter printctladdr(q, tfp); 29638032Speter if (q->q_orcpt != NULL) 29738032Speter fprintf(tfp, "Q%s\n", 29838032Speter denlstring(q->q_orcpt, TRUE, FALSE)); 29938032Speter putc('R', tfp); 30038032Speter if (bitset(QPRIMARY, q->q_flags)) 30138032Speter putc('P', tfp); 30238032Speter if (bitset(QHASNOTIFY, q->q_flags)) 30338032Speter putc('N', tfp); 30438032Speter if (bitset(QPINGONSUCCESS, q->q_flags)) 30538032Speter putc('S', tfp); 30638032Speter if (bitset(QPINGONFAILURE, q->q_flags)) 30738032Speter putc('F', tfp); 30838032Speter if (bitset(QPINGONDELAY, q->q_flags)) 30938032Speter putc('D', tfp); 31038032Speter putc(':', tfp); 31138032Speter fprintf(tfp, "%s\n", denlstring(q->q_paddr, TRUE, FALSE)); 31238032Speter if (announce) 31338032Speter { 31438032Speter e->e_to = q->q_paddr; 31538032Speter message("queued"); 31638032Speter if (LogLevel > 8) 31738032Speter logdelivery(q->q_mailer, NULL, "queued", 31838032Speter NULL, (time_t) 0, e); 31938032Speter e->e_to = NULL; 32038032Speter } 32138032Speter if (tTd(40, 1)) 32238032Speter { 32338032Speter printf("queueing "); 32438032Speter printaddr(q, FALSE); 32538032Speter } 32638032Speter } 32738032Speter 32838032Speter /* 32938032Speter ** Output headers for this message. 33038032Speter ** Expand macros completely here. Queue run will deal with 33138032Speter ** everything as absolute headers. 33238032Speter ** All headers that must be relative to the recipient 33338032Speter ** can be cracked later. 33438032Speter ** We set up a "null mailer" -- i.e., a mailer that will have 33538032Speter ** no effect on the addresses as they are output. 33638032Speter */ 33738032Speter 33838032Speter bzero((char *) &nullmailer, sizeof nullmailer); 33938032Speter nullmailer.m_re_rwset = nullmailer.m_rh_rwset = 34038032Speter nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; 34138032Speter nullmailer.m_eol = "\n"; 34238032Speter bzero(&mcibuf, sizeof mcibuf); 34338032Speter mcibuf.mci_mailer = &nullmailer; 34438032Speter mcibuf.mci_out = tfp; 34538032Speter 34638032Speter define('g', "\201f", e); 34738032Speter for (h = e->e_header; h != NULL; h = h->h_link) 34838032Speter { 34938032Speter extern bool bitzerop __P((BITMAP)); 35038032Speter 35138032Speter /* don't output null headers */ 35238032Speter if (h->h_value == NULL || h->h_value[0] == '\0') 35338032Speter continue; 35438032Speter 35538032Speter /* don't output resent headers on non-resent messages */ 35638032Speter if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags)) 35738032Speter continue; 35838032Speter 35938032Speter /* expand macros; if null, don't output header at all */ 36038032Speter if (bitset(H_DEFAULT, h->h_flags)) 36138032Speter { 36238032Speter (void) expand(h->h_value, buf, sizeof buf, e); 36338032Speter if (buf[0] == '\0') 36438032Speter continue; 36538032Speter } 36638032Speter 36738032Speter /* output this header */ 36838032Speter fprintf(tfp, "H"); 36938032Speter 37038032Speter /* if conditional, output the set of conditions */ 37138032Speter if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags)) 37238032Speter { 37338032Speter int j; 37438032Speter 37538032Speter (void) putc('?', tfp); 37638032Speter for (j = '\0'; j <= '\177'; j++) 37738032Speter if (bitnset(j, h->h_mflags)) 37838032Speter (void) putc(j, tfp); 37938032Speter (void) putc('?', tfp); 38038032Speter } 38138032Speter 38238032Speter /* output the header: expand macros, convert addresses */ 38338032Speter if (bitset(H_DEFAULT, h->h_flags)) 38438032Speter { 38538032Speter fprintf(tfp, "%s: %s\n", 38638032Speter h->h_field, 38738032Speter denlstring(buf, FALSE, TRUE)); 38838032Speter } 38938032Speter else if (bitset(H_FROM|H_RCPT, h->h_flags)) 39038032Speter { 39138032Speter bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 39238032Speter FILE *savetrace = TrafficLogFile; 39338032Speter 39438032Speter TrafficLogFile = NULL; 39538032Speter 39638032Speter if (bitset(H_FROM, h->h_flags)) 39738032Speter oldstyle = FALSE; 39838032Speter 39938032Speter commaize(h, h->h_value, oldstyle, &mcibuf, e); 40038032Speter 40138032Speter TrafficLogFile = savetrace; 40238032Speter } 40338032Speter else 40438032Speter { 40538032Speter fprintf(tfp, "%s: %s\n", 40638032Speter h->h_field, 40738032Speter denlstring(h->h_value, FALSE, TRUE)); 40838032Speter } 40938032Speter } 41038032Speter 41138032Speter /* 41238032Speter ** Clean up. 41338032Speter ** 41438032Speter ** Write a terminator record -- this is to prevent 41538032Speter ** scurrilous crackers from appending any data. 41638032Speter */ 41738032Speter 41838032Speter fprintf(tfp, ".\n"); 41938032Speter 42038032Speter if (fflush(tfp) < 0 || 42138032Speter (SuperSafe && fsync(fileno(tfp)) < 0) || 42238032Speter ferror(tfp)) 42338032Speter { 42438032Speter if (newid) 42538032Speter syserr("!552 Error writing control file %s", tf); 42638032Speter else 42738032Speter syserr("!452 Error writing control file %s", tf); 42838032Speter } 42938032Speter 43038032Speter if (!newid) 43138032Speter { 43238032Speter /* rename (locked) tf to be (locked) qf */ 43338032Speter qf = queuename(e, 'q'); 43438032Speter if (rename(tf, qf) < 0) 43538032Speter syserr("cannot rename(%s, %s), uid=%d", 43638032Speter tf, qf, geteuid()); 43738032Speter 43838032Speter /* close and unlock old (locked) qf */ 43938032Speter if (e->e_lockfp != NULL) 44038032Speter (void) xfclose(e->e_lockfp, "queueup lockfp", e->e_id); 44138032Speter e->e_lockfp = tfp; 44238032Speter } 44338032Speter else 44438032Speter qf = tf; 44538032Speter errno = 0; 44638032Speter e->e_flags |= EF_INQUEUE; 44738032Speter 44838032Speter /* save log info */ 44938032Speter if (LogLevel > 79) 45038032Speter sm_syslog(LOG_DEBUG, e->e_id, "queueup, qf=%s", qf); 45138032Speter 45238032Speter if (tTd(40, 1)) 45338032Speter printf("<<<<< done queueing %s <<<<<\n\n", e->e_id); 45438032Speter return; 45538032Speter} 45638032Speter 45738032Spetervoid 45838032Speterprintctladdr(a, tfp) 45938032Speter register ADDRESS *a; 46038032Speter FILE *tfp; 46138032Speter{ 46238032Speter char *uname; 46338032Speter register ADDRESS *q; 46438032Speter uid_t uid; 46538032Speter gid_t gid; 46638032Speter static ADDRESS *lastctladdr = NULL; 46738032Speter static uid_t lastuid; 46838032Speter 46938032Speter /* initialization */ 47038032Speter if (a == NULL || a->q_alias == NULL || tfp == NULL) 47138032Speter { 47238032Speter if (lastctladdr != NULL && tfp != NULL) 47338032Speter fprintf(tfp, "C\n"); 47438032Speter lastctladdr = NULL; 47538032Speter lastuid = 0; 47638032Speter return; 47738032Speter } 47838032Speter 47938032Speter /* find the active uid */ 48038032Speter q = getctladdr(a); 48138032Speter if (q == NULL) 48238032Speter { 48338032Speter uname = NULL; 48438032Speter uid = 0; 48538032Speter gid = 0; 48638032Speter } 48738032Speter else 48838032Speter { 48938032Speter uname = q->q_ruser != NULL ? q->q_ruser : q->q_user; 49038032Speter uid = q->q_uid; 49138032Speter gid = q->q_gid; 49238032Speter } 49338032Speter a = a->q_alias; 49438032Speter 49538032Speter /* check to see if this is the same as last time */ 49638032Speter if (lastctladdr != NULL && uid == lastuid && 49738032Speter strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) 49838032Speter return; 49938032Speter lastuid = uid; 50038032Speter lastctladdr = a; 50138032Speter 50238032Speter if (uid == 0 || uname == NULL || uname[0] == '\0') 50338032Speter fprintf(tfp, "C"); 50438032Speter else 50538032Speter fprintf(tfp, "C%s:%ld:%ld", 50638032Speter denlstring(uname, TRUE, FALSE), (long) uid, (long) gid); 50738032Speter fprintf(tfp, ":%s\n", denlstring(a->q_paddr, TRUE, FALSE)); 50838032Speter} 50938032Speter/* 51038032Speter** RUNQUEUE -- run the jobs in the queue. 51138032Speter** 51238032Speter** Gets the stuff out of the queue in some presumably logical 51338032Speter** order and processes them. 51438032Speter** 51538032Speter** Parameters: 51638032Speter** forkflag -- TRUE if the queue scanning should be done in 51738032Speter** a child process. We double-fork so it is not our 51838032Speter** child and we don't have to clean up after it. 51938032Speter** verbose -- if TRUE, print out status information. 52038032Speter** 52138032Speter** Returns: 52238032Speter** TRUE if the queue run successfully began. 52338032Speter** 52438032Speter** Side Effects: 52538032Speter** runs things in the mail queue. 52638032Speter*/ 52738032Speter 52838032SpeterENVELOPE QueueEnvelope; /* the queue run envelope */ 52938032Speterextern int get_num_procs_online __P((void)); 53038032Speter 53138032Speterbool 53238032Speterrunqueue(forkflag, verbose) 53338032Speter bool forkflag; 53438032Speter bool verbose; 53538032Speter{ 53638032Speter register ENVELOPE *e; 53738032Speter int njobs; 53838032Speter int sequenceno = 0; 53938032Speter time_t current_la_time; 54038032Speter extern ENVELOPE BlankEnvelope; 54138032Speter extern void clrdaemon __P((void)); 54238032Speter extern void runqueueevent __P((void)); 54338032Speter 54438032Speter DoQueueRun = FALSE; 54538032Speter 54638032Speter /* 54738032Speter ** If no work will ever be selected, don't even bother reading 54838032Speter ** the queue. 54938032Speter */ 55038032Speter 55138032Speter CurrentLA = getla(); /* get load average */ 55238032Speter current_la_time = curtime(); 55338032Speter 55438032Speter if (shouldqueue(WkRecipFact, current_la_time)) 55538032Speter { 55638032Speter char *msg = "Skipping queue run -- load average too high"; 55738032Speter 55838032Speter if (verbose) 55938032Speter message("458 %s\n", msg); 56038032Speter if (LogLevel > 8) 56138032Speter sm_syslog(LOG_INFO, NOQID, 56238032Speter "runqueue: %s", 56338032Speter msg); 56438032Speter if (forkflag && QueueIntvl != 0) 56538032Speter (void) setevent(QueueIntvl, runqueueevent, 0); 56638032Speter return FALSE; 56738032Speter } 56838032Speter 56938032Speter /* 57038032Speter ** See if we already have too many children. 57138032Speter */ 57238032Speter 57338032Speter if (forkflag && QueueIntvl != 0 && 57438032Speter MaxChildren > 0 && CurChildren >= MaxChildren) 57538032Speter { 57638032Speter (void) setevent(QueueIntvl, runqueueevent, 0); 57738032Speter return FALSE; 57838032Speter } 57938032Speter 58038032Speter /* 58138032Speter ** See if we want to go off and do other useful work. 58238032Speter */ 58338032Speter 58438032Speter if (forkflag) 58538032Speter { 58638032Speter pid_t pid; 58738032Speter extern SIGFUNC_DECL intsig __P((int)); 58838032Speter extern SIGFUNC_DECL reapchild __P((int)); 58938032Speter 59038032Speter blocksignal(SIGCHLD); 59138032Speter (void) setsignal(SIGCHLD, reapchild); 59238032Speter 59338032Speter pid = dofork(); 59438032Speter if (pid == -1) 59538032Speter { 59638032Speter const char *msg = "Skipping queue run -- fork() failed"; 59738032Speter const char *err = errstring(errno); 59838032Speter 59938032Speter if (verbose) 60038032Speter message("458 %s: %s\n", msg, err); 60138032Speter if (LogLevel > 8) 60238032Speter sm_syslog(LOG_INFO, NOQID, 60338032Speter "runqueue: %s: %s", 60438032Speter msg, err); 60538032Speter if (QueueIntvl != 0) 60638032Speter (void) setevent(QueueIntvl, runqueueevent, 0); 60738032Speter (void) releasesignal(SIGCHLD); 60838032Speter return FALSE; 60938032Speter } 61038032Speter if (pid != 0) 61138032Speter { 61238032Speter /* parent -- pick up intermediate zombie */ 61338032Speter (void) blocksignal(SIGALRM); 61438032Speter proc_list_add(pid); 61538032Speter (void) releasesignal(SIGALRM); 61638032Speter releasesignal(SIGCHLD); 61738032Speter if (QueueIntvl != 0) 61838032Speter (void) setevent(QueueIntvl, runqueueevent, 0); 61938032Speter return TRUE; 62038032Speter } 62138032Speter /* child -- double fork and clean up signals */ 62238032Speter proc_list_clear(); 62338032Speter releasesignal(SIGCHLD); 62438032Speter (void) setsignal(SIGCHLD, SIG_DFL); 62538032Speter (void) setsignal(SIGHUP, intsig); 62638032Speter } 62738032Speter 62838032Speter setproctitle("running queue: %s", QueueDir); 62938032Speter 63038032Speter if (LogLevel > 69) 63138032Speter sm_syslog(LOG_DEBUG, NOQID, 63238032Speter "runqueue %s, pid=%d, forkflag=%d", 63338032Speter QueueDir, getpid(), forkflag); 63438032Speter 63538032Speter /* 63638032Speter ** Release any resources used by the daemon code. 63738032Speter */ 63838032Speter 63938032Speter# if DAEMON 64038032Speter clrdaemon(); 64138032Speter# endif /* DAEMON */ 64238032Speter 64338032Speter /* force it to run expensive jobs */ 64438032Speter NoConnect = FALSE; 64538032Speter 64638032Speter /* drop privileges */ 64738032Speter if (geteuid() == (uid_t) 0) 64838032Speter (void) drop_privileges(FALSE); 64938032Speter 65038032Speter /* 65138032Speter ** Create ourselves an envelope 65238032Speter */ 65338032Speter 65438032Speter CurEnv = &QueueEnvelope; 65538032Speter e = newenvelope(&QueueEnvelope, CurEnv); 65638032Speter e->e_flags = BlankEnvelope.e_flags; 65738032Speter 65838032Speter /* make sure we have disconnected from parent */ 65938032Speter if (forkflag) 66038032Speter { 66138032Speter disconnect(1, e); 66238032Speter QuickAbort = FALSE; 66338032Speter } 66438032Speter 66538032Speter /* 66638032Speter ** Make sure the alias database is open. 66738032Speter */ 66838032Speter 66938032Speter initmaps(FALSE, e); 67038032Speter 67138032Speter /* 67238032Speter ** If we are running part of the queue, always ignore stored 67338032Speter ** host status. 67438032Speter */ 67538032Speter 67638032Speter if (QueueLimitId != NULL || QueueLimitSender != NULL || 67738032Speter QueueLimitRecipient != NULL) 67838032Speter { 67938032Speter IgnoreHostStatus = TRUE; 68038032Speter MinQueueAge = 0; 68138032Speter } 68238032Speter 68338032Speter /* 68438032Speter ** Start making passes through the queue. 68538032Speter ** First, read and sort the entire queue. 68638032Speter ** Then, process the work in that order. 68738032Speter ** But if you take too long, start over. 68838032Speter */ 68938032Speter 69038032Speter /* order the existing work requests */ 69138032Speter njobs = orderq(FALSE); 69238032Speter 69338032Speter /* process them once at a time */ 69438032Speter while (WorkQ != NULL) 69538032Speter { 69638032Speter WORK *w = WorkQ; 69738032Speter 69838032Speter WorkQ = WorkQ->w_next; 69938032Speter e->e_to = NULL; 70038032Speter 70138032Speter /* 70238032Speter ** Ignore jobs that are too expensive for the moment. 70338032Speter ** 70438032Speter ** Get new load average every 30 seconds. 70538032Speter */ 70638032Speter 70738032Speter if (current_la_time < curtime() - 30) 70838032Speter { 70938032Speter CurrentLA = getla(); 71038032Speter current_la_time = curtime(); 71138032Speter } 71238032Speter if (shouldqueue(WkRecipFact, current_la_time)) 71338032Speter { 71438032Speter char *msg = "Aborting queue run: load average too high"; 71538032Speter 71638032Speter if (Verbose) 71738032Speter message("%s", msg); 71838032Speter if (LogLevel > 8) 71938032Speter sm_syslog(LOG_INFO, NOQID, 72038032Speter "runqueue: %s", 72138032Speter msg); 72238032Speter break; 72338032Speter } 72438032Speter sequenceno++; 72538032Speter if (shouldqueue(w->w_pri, w->w_ctime)) 72638032Speter { 72738032Speter if (Verbose) 72838032Speter message(""); 72938032Speter if (QueueSortOrder == QS_BYPRIORITY) 73038032Speter { 73138032Speter if (Verbose) 73238032Speter message("Skipping %s (sequence %d of %d) and flushing rest of queue", 73338032Speter w->w_name + 2, 73438032Speter sequenceno, 73538032Speter njobs); 73638032Speter if (LogLevel > 8) 73738032Speter sm_syslog(LOG_INFO, NOQID, 73838032Speter "runqueue: Flushing queue from %s (pri %ld, LA %d, %d of %d)", 73938032Speter w->w_name + 2, 74038032Speter w->w_pri, 74138032Speter CurrentLA, 74238032Speter sequenceno, 74338032Speter njobs); 74438032Speter break; 74538032Speter } 74638032Speter else if (Verbose) 74738032Speter message("Skipping %s (sequence %d of %d)", 74838032Speter w->w_name + 2, sequenceno, njobs); 74938032Speter } 75038032Speter else 75138032Speter { 75238032Speter pid_t pid; 75338032Speter extern pid_t dowork __P((char *, bool, bool, ENVELOPE *)); 75438032Speter 75538032Speter if (Verbose) 75638032Speter { 75738032Speter message(""); 75838032Speter message("Running %s (sequence %d of %d)", 75938032Speter w->w_name + 2, sequenceno, njobs); 76038032Speter } 76138032Speter pid = dowork(w->w_name + 2, ForkQueueRuns, FALSE, e); 76238032Speter errno = 0; 76338032Speter if (pid != 0) 76438032Speter (void) waitfor(pid); 76538032Speter } 76638032Speter free(w->w_name); 76738032Speter if (w->w_host) 76838032Speter free(w->w_host); 76938032Speter free((char *) w); 77038032Speter } 77138032Speter 77238032Speter /* exit without the usual cleanup */ 77338032Speter e->e_id = NULL; 77438032Speter finis(); 77538032Speter /*NOTREACHED*/ 77638032Speter return TRUE; 77738032Speter} 77838032Speter 77938032Speter 78038032Speter/* 78138032Speter** RUNQUEUEEVENT -- stub for use in setevent 78238032Speter*/ 78338032Speter 78438032Spetervoid 78538032Speterrunqueueevent() 78638032Speter{ 78738032Speter DoQueueRun = TRUE; 78838032Speter} 78938032Speter/* 79038032Speter** ORDERQ -- order the work queue. 79138032Speter** 79238032Speter** Parameters: 79338032Speter** doall -- if set, include everything in the queue (even 79438032Speter** the jobs that cannot be run because the load 79538032Speter** average is too high). Otherwise, exclude those 79638032Speter** jobs. 79738032Speter** 79838032Speter** Returns: 79938032Speter** The number of request in the queue (not necessarily 80038032Speter** the number of requests in WorkQ however). 80138032Speter** 80238032Speter** Side Effects: 80338032Speter** Sets WorkQ to the queue of available work, in order. 80438032Speter*/ 80538032Speter 80638032Speter# define NEED_P 001 80738032Speter# define NEED_T 002 80838032Speter# define NEED_R 004 80938032Speter# define NEED_S 010 81038032Speter 81138032Speterstatic WORK *WorkList = NULL; 81238032Speterstatic int WorkListSize = 0; 81338032Speter 81438032Speterint 81538032Speterorderq(doall) 81638032Speter bool doall; 81738032Speter{ 81838032Speter register struct dirent *d; 81938032Speter register WORK *w; 82038032Speter register char *p; 82138032Speter DIR *f; 82238032Speter register int i; 82338032Speter int wn = -1; 82438032Speter int wc; 82538032Speter QUEUE_CHAR *check; 82638032Speter 82738032Speter if (tTd(41, 1)) 82838032Speter { 82938032Speter printf("orderq:\n"); 83038032Speter 83138032Speter check = QueueLimitId; 83238032Speter while (check != NULL) 83338032Speter { 83438032Speter printf("\tQueueLimitId = %s\n", 83538032Speter check->queue_match); 83638032Speter check = check->queue_next; 83738032Speter } 83838032Speter 83938032Speter check = QueueLimitSender; 84038032Speter while (check != NULL) 84138032Speter { 84238032Speter printf("\tQueueLimitSender = %s\n", 84338032Speter check->queue_match); 84438032Speter check = check->queue_next; 84538032Speter } 84638032Speter 84738032Speter check = QueueLimitRecipient; 84838032Speter while (check != NULL) 84938032Speter { 85038032Speter printf("\tQueueLimitRecipient = %s\n", 85138032Speter check->queue_match); 85238032Speter check = check->queue_next; 85338032Speter } 85438032Speter } 85538032Speter 85638032Speter /* clear out old WorkQ */ 85738032Speter for (w = WorkQ; w != NULL; ) 85838032Speter { 85938032Speter register WORK *nw = w->w_next; 86038032Speter 86138032Speter WorkQ = nw; 86238032Speter free(w->w_name); 86338032Speter if (w->w_host) 86438032Speter free(w->w_host); 86538032Speter free((char *) w); 86638032Speter w = nw; 86738032Speter } 86838032Speter 86938032Speter /* open the queue directory */ 87038032Speter f = opendir("."); 87138032Speter if (f == NULL) 87238032Speter { 87338032Speter syserr("orderq: cannot open \"%s\" as \".\"", QueueDir); 87438032Speter return (0); 87538032Speter } 87638032Speter 87738032Speter /* 87838032Speter ** Read the work directory. 87938032Speter */ 88038032Speter 88138032Speter while ((d = readdir(f)) != NULL) 88238032Speter { 88338032Speter FILE *cf; 88438032Speter int qfver = 0; 88538032Speter char lbuf[MAXNAME + 1]; 88638032Speter extern bool strcontainedin __P((char *, char *)); 88738032Speter 88838032Speter if (tTd(41, 50)) 88938032Speter printf("orderq: checking %s\n", d->d_name); 89038032Speter 89138032Speter /* is this an interesting entry? */ 89238032Speter if (d->d_name[0] != 'q' || d->d_name[1] != 'f') 89338032Speter continue; 89438032Speter 89538032Speter if (strlen(d->d_name) > MAXQFNAME) 89638032Speter continue; 89738032Speter 89838032Speter check = QueueLimitId; 89938032Speter while (check != NULL) 90038032Speter { 90138032Speter if (strcontainedin(check->queue_match, d->d_name)) 90238032Speter break; 90338032Speter else 90438032Speter check = check->queue_next; 90538032Speter } 90638032Speter if (QueueLimitId != NULL && check == NULL) 90738032Speter continue; 90838032Speter 90938032Speter#ifdef PICKY_QF_NAME_CHECK 91038032Speter /* 91138032Speter ** Check queue name for plausibility. This handles 91238032Speter ** both old and new type ids. 91338032Speter */ 91438032Speter 91538032Speter p = d->d_name + 2; 91638032Speter if (isupper(p[0]) && isupper(p[2])) 91738032Speter p += 3; 91838032Speter else if (isupper(p[1])) 91938032Speter p += 2; 92038032Speter else 92138032Speter p = d->d_name; 92238032Speter for (i = 0; isdigit(*p); p++) 92338032Speter i++; 92438032Speter if (i < 5 || *p != '\0') 92538032Speter { 92638032Speter if (Verbose) 92738032Speter printf("orderq: bogus qf name %s\n", d->d_name); 92838032Speter if (LogLevel > 0) 92938032Speter sm_syslog(LOG_ALERT, NOQID, 93038032Speter "orderq: bogus qf name %s", 93138032Speter d->d_name); 93238032Speter if (strlen(d->d_name) > (SIZE_T) MAXNAME) 93338032Speter d->d_name[MAXNAME] = '\0'; 93438032Speter strcpy(lbuf, d->d_name); 93538032Speter lbuf[0] = 'Q'; 93638032Speter (void) rename(d->d_name, lbuf); 93738032Speter continue; 93838032Speter } 93938032Speter#endif 94038032Speter 94138032Speter /* open control file (if not too many files) */ 94238032Speter if (++wn >= MaxQueueRun && MaxQueueRun > 0) 94338032Speter { 94438032Speter if (wn == MaxQueueRun && LogLevel > 0) 94538032Speter sm_syslog(LOG_ALERT, NOQID, 94638032Speter "WorkList for %s maxed out at %d", 94738032Speter QueueDir, MaxQueueRun); 94838032Speter continue; 94938032Speter } 95038032Speter if (wn >= WorkListSize) 95138032Speter { 95238032Speter extern void grow_wlist __P((void)); 95338032Speter 95438032Speter grow_wlist(); 95538032Speter if (wn >= WorkListSize) 95638032Speter continue; 95738032Speter } 95838032Speter 95938032Speter cf = fopen(d->d_name, "r"); 96038032Speter if (cf == NULL) 96138032Speter { 96238032Speter /* this may be some random person sending hir msgs */ 96338032Speter /* syserr("orderq: cannot open %s", cbuf); */ 96438032Speter if (tTd(41, 2)) 96538032Speter printf("orderq: cannot open %s: %s\n", 96638032Speter d->d_name, errstring(errno)); 96738032Speter errno = 0; 96838032Speter wn--; 96938032Speter continue; 97038032Speter } 97138032Speter w = &WorkList[wn]; 97238032Speter w->w_name = newstr(d->d_name); 97338032Speter w->w_host = NULL; 97438032Speter w->w_lock = !lockfile(fileno(cf), w->w_name, NULL, LOCK_SH|LOCK_NB); 97538032Speter w->w_tooyoung = FALSE; 97638032Speter 97738032Speter /* make sure jobs in creation don't clog queue */ 97838032Speter w->w_pri = 0x7fffffff; 97938032Speter w->w_ctime = 0; 98038032Speter 98138032Speter /* extract useful information */ 98238032Speter i = NEED_P | NEED_T; 98338032Speter if (QueueLimitSender != NULL) 98438032Speter i |= NEED_S; 98538032Speter if (QueueSortOrder == QS_BYHOST || QueueLimitRecipient != NULL) 98638032Speter i |= NEED_R; 98738032Speter while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL) 98838032Speter { 98938032Speter int c; 99038032Speter time_t age; 99138032Speter extern bool strcontainedin __P((char *, char *)); 99238032Speter 99338032Speter p = strchr(lbuf, '\n'); 99438032Speter if (p != NULL) 99538032Speter *p = '\0'; 99638032Speter else 99738032Speter { 99838032Speter /* flush rest of overly long line */ 99938032Speter while ((c = getc(cf)) != EOF && c != '\n') 100038032Speter continue; 100138032Speter } 100238032Speter 100338032Speter switch (lbuf[0]) 100438032Speter { 100538032Speter case 'V': 100638032Speter qfver = atoi(&lbuf[1]); 100738032Speter break; 100838032Speter 100938032Speter case 'P': 101038032Speter w->w_pri = atol(&lbuf[1]); 101138032Speter i &= ~NEED_P; 101238032Speter break; 101338032Speter 101438032Speter case 'T': 101538032Speter w->w_ctime = atol(&lbuf[1]); 101638032Speter i &= ~NEED_T; 101738032Speter break; 101838032Speter 101938032Speter case 'R': 102038032Speter if (w->w_host == NULL && 102138032Speter (p = strrchr(&lbuf[1], '@')) != NULL) 102238032Speter w->w_host = newstr(&p[1]); 102338032Speter if (QueueLimitRecipient == NULL) 102438032Speter { 102538032Speter i &= ~NEED_R; 102638032Speter break; 102738032Speter } 102838032Speter if (qfver > 0) 102938032Speter { 103038032Speter p = strchr(&lbuf[1], ':'); 103138032Speter if (p == NULL) 103238032Speter p = &lbuf[1]; 103338032Speter } 103438032Speter else 103538032Speter p = &lbuf[1]; 103638032Speter check = QueueLimitRecipient; 103738032Speter while (check != NULL) 103838032Speter { 103938032Speter if (strcontainedin(check->queue_match, 104038032Speter p)) 104138032Speter break; 104238032Speter else 104338032Speter check = check->queue_next; 104438032Speter } 104538032Speter if (check != NULL) 104638032Speter i &= ~NEED_R; 104738032Speter break; 104838032Speter 104938032Speter case 'S': 105038032Speter check = QueueLimitSender; 105138032Speter while (check != NULL) 105238032Speter { 105338032Speter if (strcontainedin(check->queue_match, 105438032Speter &lbuf[1])) 105538032Speter break; 105638032Speter else 105738032Speter check = check->queue_next; 105838032Speter } 105938032Speter if (check != NULL) 106038032Speter i &= ~NEED_S; 106138032Speter break; 106238032Speter 106338032Speter case 'K': 106438032Speter age = curtime() - (time_t) atol(&lbuf[1]); 106538032Speter if (age >= 0 && MinQueueAge > 0 && 106638032Speter age < MinQueueAge) 106738032Speter w->w_tooyoung = TRUE; 106838032Speter break; 106938032Speter 107038032Speter case 'N': 107138032Speter if (atol(&lbuf[1]) == 0) 107238032Speter w->w_tooyoung = FALSE; 107338032Speter break; 107438032Speter } 107538032Speter } 107638032Speter (void) fclose(cf); 107738032Speter 107838032Speter if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || 107938032Speter bitset(NEED_R|NEED_S, i)) 108038032Speter { 108138032Speter /* don't even bother sorting this job in */ 108238032Speter if (tTd(41, 49)) 108338032Speter printf("skipping %s (%x)\n", w->w_name, i); 108438032Speter free(w->w_name); 108538032Speter if (w->w_host) 108638032Speter free(w->w_host); 108738032Speter wn--; 108838032Speter } 108938032Speter } 109038032Speter (void) closedir(f); 109138032Speter wn++; 109238032Speter 109338032Speter wc = min(wn, WorkListSize); 109438032Speter if (wc > MaxQueueRun && MaxQueueRun > 0) 109538032Speter wc = MaxQueueRun; 109638032Speter 109738032Speter if (QueueSortOrder == QS_BYHOST) 109838032Speter { 109938032Speter extern int workcmpf1(); 110038032Speter extern int workcmpf2(); 110138032Speter 110238032Speter /* 110338032Speter ** Sort the work directory for the first time, 110438032Speter ** based on host name, lock status, and priority. 110538032Speter */ 110638032Speter 110738032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); 110838032Speter 110938032Speter /* 111038032Speter ** If one message to host is locked, "lock" all messages 111138032Speter ** to that host. 111238032Speter */ 111338032Speter 111438032Speter i = 0; 111538032Speter while (i < wc) 111638032Speter { 111738032Speter if (!WorkList[i].w_lock) 111838032Speter { 111938032Speter i++; 112038032Speter continue; 112138032Speter } 112238032Speter w = &WorkList[i]; 112338032Speter while (++i < wc) 112438032Speter { 112538032Speter extern int sm_strcasecmp __P((char *, char *)); 112638032Speter 112738032Speter if (WorkList[i].w_host == NULL && 112838032Speter w->w_host == NULL) 112938032Speter WorkList[i].w_lock = TRUE; 113038032Speter else if (WorkList[i].w_host != NULL && 113138032Speter w->w_host != NULL && 113238032Speter sm_strcasecmp(WorkList[i].w_host, w->w_host) == 0) 113338032Speter WorkList[i].w_lock = TRUE; 113438032Speter else 113538032Speter break; 113638032Speter } 113738032Speter } 113838032Speter 113938032Speter /* 114038032Speter ** Sort the work directory for the second time, 114138032Speter ** based on lock status, host name, and priority. 114238032Speter */ 114338032Speter 114438032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); 114538032Speter } 114638032Speter else if (QueueSortOrder == QS_BYTIME) 114738032Speter { 114838032Speter extern int workcmpf3(); 114938032Speter 115038032Speter /* 115138032Speter ** Simple sort based on submission time only. 115238032Speter */ 115338032Speter 115438032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); 115538032Speter } 115638032Speter else 115738032Speter { 115838032Speter extern int workcmpf0(); 115938032Speter 116038032Speter /* 116138032Speter ** Simple sort based on queue priority only. 116238032Speter */ 116338032Speter 116438032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); 116538032Speter } 116638032Speter 116738032Speter /* 116838032Speter ** Convert the work list into canonical form. 116938032Speter ** Should be turning it into a list of envelopes here perhaps. 117038032Speter */ 117138032Speter 117238032Speter WorkQ = NULL; 117338032Speter for (i = wc; --i >= 0; ) 117438032Speter { 117538032Speter w = (WORK *) xalloc(sizeof *w); 117638032Speter w->w_name = WorkList[i].w_name; 117738032Speter w->w_host = WorkList[i].w_host; 117838032Speter w->w_lock = WorkList[i].w_lock; 117938032Speter w->w_tooyoung = WorkList[i].w_tooyoung; 118038032Speter w->w_pri = WorkList[i].w_pri; 118138032Speter w->w_ctime = WorkList[i].w_ctime; 118238032Speter w->w_next = WorkQ; 118338032Speter WorkQ = w; 118438032Speter } 118538032Speter if (WorkList != NULL) 118638032Speter free(WorkList); 118738032Speter WorkList = NULL; 118838032Speter WorkListSize = 0; 118938032Speter 119038032Speter if (tTd(40, 1)) 119138032Speter { 119238032Speter for (w = WorkQ; w != NULL; w = w->w_next) 119338032Speter printf("%32s: pri=%ld\n", w->w_name, w->w_pri); 119438032Speter } 119538032Speter 119638032Speter return (wn); 119738032Speter} 119838032Speter/* 119938032Speter** GROW_WLIST -- make the work list larger 120038032Speter** 120138032Speter** Parameters: 120238032Speter** none. 120338032Speter** 120438032Speter** Returns: 120538032Speter** none. 120638032Speter** 120738032Speter** Side Effects: 120838032Speter** Adds another QUEUESEGSIZE entries to WorkList if possible. 120938032Speter** It can fail if there isn't enough memory, so WorkListSize 121038032Speter** should be checked again upon return. 121138032Speter*/ 121238032Speter 121338032Spetervoid 121438032Spetergrow_wlist() 121538032Speter{ 121638032Speter if (tTd(41, 1)) 121738032Speter printf("grow_wlist: WorkListSize=%d\n", WorkListSize); 121838032Speter if (WorkList == NULL) 121938032Speter { 122038032Speter WorkList = (WORK *) xalloc(sizeof(WORK) * (QUEUESEGSIZE + 1)); 122138032Speter WorkListSize = QUEUESEGSIZE; 122238032Speter } 122338032Speter else 122438032Speter { 122538032Speter int newsize = WorkListSize + QUEUESEGSIZE; 122638032Speter WORK *newlist = (WORK *) realloc((char *)WorkList, 122738032Speter (unsigned)sizeof(WORK) * (newsize + 1)); 122838032Speter 122938032Speter if (newlist != NULL) 123038032Speter { 123138032Speter WorkListSize = newsize; 123238032Speter WorkList = newlist; 123338032Speter if (LogLevel > 1) 123438032Speter { 123538032Speter sm_syslog(LOG_NOTICE, NOQID, 123638032Speter "grew WorkList for %s to %d", 123738032Speter QueueDir, WorkListSize); 123838032Speter } 123938032Speter } 124038032Speter else if (LogLevel > 0) 124138032Speter { 124238032Speter sm_syslog(LOG_ALERT, NOQID, 124338032Speter "FAILED to grow WorkList for %s to %d", 124438032Speter QueueDir, newsize); 124538032Speter } 124638032Speter } 124738032Speter if (tTd(41, 1)) 124838032Speter printf("grow_wlist: WorkListSize now %d\n", WorkListSize); 124938032Speter} 125038032Speter/* 125138032Speter** WORKCMPF0 -- simple priority-only compare function. 125238032Speter** 125338032Speter** Parameters: 125438032Speter** a -- the first argument. 125538032Speter** b -- the second argument. 125638032Speter** 125738032Speter** Returns: 125838032Speter** -1 if a < b 125938032Speter** 0 if a == b 126038032Speter** +1 if a > b 126138032Speter** 126238032Speter** Side Effects: 126338032Speter** none. 126438032Speter*/ 126538032Speter 126638032Speterint 126738032Speterworkcmpf0(a, b) 126838032Speter register WORK *a; 126938032Speter register WORK *b; 127038032Speter{ 127138032Speter long pa = a->w_pri; 127238032Speter long pb = b->w_pri; 127338032Speter 127438032Speter if (pa == pb) 127538032Speter return 0; 127638032Speter else if (pa > pb) 127738032Speter return 1; 127838032Speter else 127938032Speter return -1; 128038032Speter} 128138032Speter/* 128238032Speter** WORKCMPF1 -- first compare function for ordering work based on host name. 128338032Speter** 128438032Speter** Sorts on host name, lock status, and priority in that order. 128538032Speter** 128638032Speter** Parameters: 128738032Speter** a -- the first argument. 128838032Speter** b -- the second argument. 128938032Speter** 129038032Speter** Returns: 129138032Speter** <0 if a < b 129238032Speter** 0 if a == b 129338032Speter** >0 if a > b 129438032Speter** 129538032Speter** Side Effects: 129638032Speter** none. 129738032Speter*/ 129838032Speter 129938032Speterint 130038032Speterworkcmpf1(a, b) 130138032Speter register WORK *a; 130238032Speter register WORK *b; 130338032Speter{ 130438032Speter int i; 130538032Speter extern int sm_strcasecmp __P((char *, char *)); 130638032Speter 130738032Speter /* host name */ 130838032Speter if (a->w_host != NULL && b->w_host == NULL) 130938032Speter return 1; 131038032Speter else if (a->w_host == NULL && b->w_host != NULL) 131138032Speter return -1; 131238032Speter if (a->w_host != NULL && b->w_host != NULL && 131338032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 131438032Speter return i; 131538032Speter 131638032Speter /* lock status */ 131738032Speter if (a->w_lock != b->w_lock) 131838032Speter return b->w_lock - a->w_lock; 131938032Speter 132038032Speter /* job priority */ 132138032Speter return a->w_pri - b->w_pri; 132238032Speter} 132338032Speter/* 132438032Speter** WORKCMPF2 -- second compare function for ordering work based on host name. 132538032Speter** 132638032Speter** Sorts on lock status, host name, and priority in that order. 132738032Speter** 132838032Speter** Parameters: 132938032Speter** a -- the first argument. 133038032Speter** b -- the second argument. 133138032Speter** 133238032Speter** Returns: 133338032Speter** <0 if a < b 133438032Speter** 0 if a == b 133538032Speter** >0 if a > b 133638032Speter** 133738032Speter** Side Effects: 133838032Speter** none. 133938032Speter*/ 134038032Speter 134138032Speterint 134238032Speterworkcmpf2(a, b) 134338032Speter register WORK *a; 134438032Speter register WORK *b; 134538032Speter{ 134638032Speter int i; 134738032Speter extern int sm_strcasecmp __P((char *, char *)); 134838032Speter 134938032Speter /* lock status */ 135038032Speter if (a->w_lock != b->w_lock) 135138032Speter return a->w_lock - b->w_lock; 135238032Speter 135338032Speter /* host name */ 135438032Speter if (a->w_host != NULL && b->w_host == NULL) 135538032Speter return 1; 135638032Speter else if (a->w_host == NULL && b->w_host != NULL) 135738032Speter return -1; 135838032Speter if (a->w_host != NULL && b->w_host != NULL && 135938032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 136038032Speter return i; 136138032Speter 136238032Speter /* job priority */ 136338032Speter return a->w_pri - b->w_pri; 136438032Speter} 136538032Speter/* 136638032Speter** WORKCMPF3 -- simple submission-time-only compare function. 136738032Speter** 136838032Speter** Parameters: 136938032Speter** a -- the first argument. 137038032Speter** b -- the second argument. 137138032Speter** 137238032Speter** Returns: 137338032Speter** -1 if a < b 137438032Speter** 0 if a == b 137538032Speter** +1 if a > b 137638032Speter** 137738032Speter** Side Effects: 137838032Speter** none. 137938032Speter*/ 138038032Speter 138138032Speterint 138238032Speterworkcmpf3(a, b) 138338032Speter register WORK *a; 138438032Speter register WORK *b; 138538032Speter{ 138638032Speter if (a->w_ctime > b->w_ctime) 138738032Speter return 1; 138838032Speter else if (a->w_ctime < b->w_ctime) 138938032Speter return -1; 139038032Speter else 139138032Speter return 0; 139238032Speter} 139338032Speter/* 139438032Speter** DOWORK -- do a work request. 139538032Speter** 139638032Speter** Parameters: 139738032Speter** id -- the ID of the job to run. 139838032Speter** forkflag -- if set, run this in background. 139938032Speter** requeueflag -- if set, reinstantiate the queue quickly. 140038032Speter** This is used when expanding aliases in the queue. 140138032Speter** If forkflag is also set, it doesn't wait for the 140238032Speter** child. 140338032Speter** e - the envelope in which to run it. 140438032Speter** 140538032Speter** Returns: 140638032Speter** process id of process that is running the queue job. 140738032Speter** 140838032Speter** Side Effects: 140938032Speter** The work request is satisfied if possible. 141038032Speter*/ 141138032Speter 141238032Speterpid_t 141338032Speterdowork(id, forkflag, requeueflag, e) 141438032Speter char *id; 141538032Speter bool forkflag; 141638032Speter bool requeueflag; 141738032Speter register ENVELOPE *e; 141838032Speter{ 141938032Speter register pid_t pid; 142038032Speter extern bool readqf __P((ENVELOPE *)); 142138032Speter 142238032Speter if (tTd(40, 1)) 142338032Speter printf("dowork(%s)\n", id); 142438032Speter 142538032Speter /* 142638032Speter ** Fork for work. 142738032Speter */ 142838032Speter 142938032Speter if (forkflag) 143038032Speter { 143138032Speter pid = fork(); 143238032Speter if (pid < 0) 143338032Speter { 143438032Speter syserr("dowork: cannot fork"); 143538032Speter return 0; 143638032Speter } 143738032Speter else if (pid > 0) 143838032Speter { 143938032Speter /* parent -- clean out connection cache */ 144038032Speter mci_flush(FALSE, NULL); 144138032Speter } 144238032Speter else 144338032Speter { 144438032Speter /* child -- error messages to the transcript */ 144538032Speter QuickAbort = OnlyOneError = FALSE; 144638032Speter } 144738032Speter } 144838032Speter else 144938032Speter { 145038032Speter pid = 0; 145138032Speter } 145238032Speter 145338032Speter if (pid == 0) 145438032Speter { 145538032Speter /* 145638032Speter ** CHILD 145738032Speter ** Lock the control file to avoid duplicate deliveries. 145838032Speter ** Then run the file as though we had just read it. 145938032Speter ** We save an idea of the temporary name so we 146038032Speter ** can recover on interrupt. 146138032Speter */ 146238032Speter 146338032Speter /* set basic modes, etc. */ 146438032Speter (void) alarm(0); 146538032Speter clearenvelope(e, FALSE); 146638032Speter e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 146738032Speter e->e_sendmode = SM_DELIVER; 146838032Speter e->e_errormode = EM_MAIL; 146938032Speter e->e_id = id; 147038032Speter GrabTo = UseErrorsTo = FALSE; 147138032Speter ExitStat = EX_OK; 147238032Speter if (forkflag) 147338032Speter { 147438032Speter disconnect(1, e); 147538032Speter OpMode = MD_DELIVER; 147638032Speter } 147738032Speter setproctitle("%s: from queue", id); 147838032Speter if (LogLevel > 76) 147938032Speter sm_syslog(LOG_DEBUG, e->e_id, 148038032Speter "dowork, pid=%d", 148138032Speter getpid()); 148238032Speter 148338032Speter /* don't use the headers from sendmail.cf... */ 148438032Speter e->e_header = NULL; 148538032Speter 148638032Speter /* read the queue control file -- return if locked */ 148738032Speter if (!readqf(e)) 148838032Speter { 148938032Speter if (tTd(40, 4) && e->e_id != NULL) 149038032Speter printf("readqf(%s) failed\n", e->e_id); 149138032Speter e->e_id = NULL; 149238032Speter if (forkflag) 149338032Speter exit(EX_OK); 149438032Speter else 149538032Speter return 0; 149638032Speter } 149738032Speter 149838032Speter e->e_flags |= EF_INQUEUE; 149938032Speter eatheader(e, requeueflag); 150038032Speter 150138032Speter if (requeueflag) 150238032Speter queueup(e, FALSE); 150338032Speter 150438032Speter /* do the delivery */ 150538032Speter sendall(e, SM_DELIVER); 150638032Speter 150738032Speter /* finish up and exit */ 150838032Speter if (forkflag) 150938032Speter finis(); 151038032Speter else 151138032Speter dropenvelope(e, TRUE); 151238032Speter } 151338032Speter e->e_id = NULL; 151438032Speter return pid; 151538032Speter} 151638032Speter/* 151738032Speter** READQF -- read queue file and set up environment. 151838032Speter** 151938032Speter** Parameters: 152038032Speter** e -- the envelope of the job to run. 152138032Speter** 152238032Speter** Returns: 152338032Speter** TRUE if it successfully read the queue file. 152438032Speter** FALSE otherwise. 152538032Speter** 152638032Speter** Side Effects: 152738032Speter** The queue file is returned locked. 152838032Speter*/ 152938032Speter 153038032Speterbool 153138032Speterreadqf(e) 153238032Speter register ENVELOPE *e; 153338032Speter{ 153438032Speter register FILE *qfp; 153538032Speter ADDRESS *ctladdr; 153638032Speter struct stat st; 153738032Speter char *bp; 153838032Speter int qfver = 0; 153938032Speter long hdrsize = 0; 154038032Speter register char *p; 154138032Speter char *orcpt = NULL; 154238032Speter bool nomore = FALSE; 154338032Speter char qf[MAXQFNAME]; 154438032Speter char buf[MAXLINE]; 154538032Speter extern ADDRESS *setctluser __P((char *, int)); 154638032Speter 154738032Speter /* 154838032Speter ** Read and process the file. 154938032Speter */ 155038032Speter 155138032Speter strcpy(qf, queuename(e, 'q')); 155238032Speter qfp = fopen(qf, "r+"); 155338032Speter if (qfp == NULL) 155438032Speter { 155538032Speter if (tTd(40, 8)) 155638032Speter printf("readqf(%s): fopen failure (%s)\n", 155738032Speter qf, errstring(errno)); 155838032Speter if (errno != ENOENT) 155938032Speter syserr("readqf: no control file %s", qf); 156038032Speter return FALSE; 156138032Speter } 156238032Speter 156338032Speter if (!lockfile(fileno(qfp), qf, NULL, LOCK_EX|LOCK_NB)) 156438032Speter { 156538032Speter /* being processed by another queuer */ 156638032Speter if (Verbose || tTd(40, 8)) 156738032Speter printf("%s: locked\n", e->e_id); 156838032Speter if (LogLevel > 19) 156938032Speter sm_syslog(LOG_DEBUG, e->e_id, "locked"); 157038032Speter (void) fclose(qfp); 157138032Speter return FALSE; 157238032Speter } 157338032Speter 157438032Speter /* 157538032Speter ** Check the queue file for plausibility to avoid attacks. 157638032Speter */ 157738032Speter 157838032Speter if (fstat(fileno(qfp), &st) < 0) 157938032Speter { 158038032Speter /* must have been being processed by someone else */ 158138032Speter if (tTd(40, 8)) 158238032Speter printf("readqf(%s): fstat failure (%s)\n", 158338032Speter qf, errstring(errno)); 158438032Speter fclose(qfp); 158538032Speter return FALSE; 158638032Speter } 158738032Speter 158838032Speter if ((st.st_uid != geteuid() && geteuid() != RealUid) || 158938032Speter bitset(S_IWOTH|S_IWGRP, st.st_mode)) 159038032Speter { 159138032Speter if (LogLevel > 0) 159238032Speter { 159338032Speter sm_syslog(LOG_ALERT, e->e_id, 159438032Speter "bogus queue file, uid=%d, mode=%o", 159538032Speter st.st_uid, st.st_mode); 159638032Speter } 159738032Speter if (tTd(40, 8)) 159838032Speter printf("readqf(%s): bogus file\n", qf); 159938032Speter loseqfile(e, "bogus file uid in mqueue"); 160038032Speter fclose(qfp); 160138032Speter return FALSE; 160238032Speter } 160338032Speter 160438032Speter if (st.st_size == 0) 160538032Speter { 160638032Speter /* must be a bogus file -- if also old, just remove it */ 160738032Speter if (st.st_ctime + 10 * 60 < curtime()) 160838032Speter { 160938032Speter qf[0] = 'd'; 161038032Speter (void) unlink(qf); 161138032Speter qf[0] = 'q'; 161238032Speter (void) unlink(qf); 161338032Speter } 161438032Speter fclose(qfp); 161538032Speter return FALSE; 161638032Speter } 161738032Speter 161838032Speter if (st.st_nlink == 0) 161938032Speter { 162038032Speter /* 162138032Speter ** Race condition -- we got a file just as it was being 162238032Speter ** unlinked. Just assume it is zero length. 162338032Speter */ 162438032Speter 162538032Speter fclose(qfp); 162638032Speter return FALSE; 162738032Speter } 162838032Speter 162938032Speter /* good file -- save this lock */ 163038032Speter e->e_lockfp = qfp; 163138032Speter 163238032Speter /* do basic system initialization */ 163338032Speter initsys(e); 163438032Speter define('i', e->e_id, e); 163538032Speter 163638032Speter LineNumber = 0; 163738032Speter e->e_flags |= EF_GLOBALERRS; 163838032Speter OpMode = MD_DELIVER; 163938032Speter ctladdr = NULL; 164038032Speter e->e_dfino = -1; 164138032Speter e->e_msgsize = -1; 164238032Speter while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) 164338032Speter { 164438032Speter register char *p; 164538032Speter u_long qflags; 164638032Speter ADDRESS *q; 164738032Speter int mid; 164838032Speter auto char *ep; 164938032Speter 165038032Speter if (tTd(40, 4)) 165138032Speter printf("+++++ %s\n", bp); 165238032Speter if (nomore) 165338032Speter { 165438032Speter /* hack attack */ 165538032Speter syserr("SECURITY ALERT: extra data in qf: %s", bp); 165638032Speter fclose(qfp); 165738032Speter loseqfile(e, "bogus queue line"); 165838032Speter return FALSE; 165938032Speter } 166038032Speter switch (bp[0]) 166138032Speter { 166238032Speter case 'V': /* queue file version number */ 166338032Speter qfver = atoi(&bp[1]); 166438032Speter if (qfver <= QF_VERSION) 166538032Speter break; 166638032Speter syserr("Version number in qf (%d) greater than max (%d)", 166738032Speter qfver, QF_VERSION); 166838032Speter fclose(qfp); 166938032Speter loseqfile(e, "unsupported qf file version"); 167038032Speter return FALSE; 167138032Speter 167238032Speter case 'C': /* specify controlling user */ 167338032Speter ctladdr = setctluser(&bp[1], qfver); 167438032Speter break; 167538032Speter 167638032Speter case 'Q': /* original recipient */ 167738032Speter orcpt = newstr(&bp[1]); 167838032Speter break; 167938032Speter 168038032Speter case 'R': /* specify recipient */ 168138032Speter p = bp; 168238032Speter qflags = 0; 168338032Speter if (qfver >= 1) 168438032Speter { 168538032Speter /* get flag bits */ 168638032Speter while (*++p != '\0' && *p != ':') 168738032Speter { 168838032Speter switch (*p) 168938032Speter { 169038032Speter case 'N': 169138032Speter qflags |= QHASNOTIFY; 169238032Speter break; 169338032Speter 169438032Speter case 'S': 169538032Speter qflags |= QPINGONSUCCESS; 169638032Speter break; 169738032Speter 169838032Speter case 'F': 169938032Speter qflags |= QPINGONFAILURE; 170038032Speter break; 170138032Speter 170238032Speter case 'D': 170338032Speter qflags |= QPINGONDELAY; 170438032Speter break; 170538032Speter 170638032Speter case 'P': 170738032Speter qflags |= QPRIMARY; 170838032Speter break; 170938032Speter } 171038032Speter } 171138032Speter } 171238032Speter else 171338032Speter qflags |= QPRIMARY; 171438032Speter q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e); 171538032Speter if (q != NULL) 171638032Speter { 171738032Speter q->q_alias = ctladdr; 171838032Speter if (qfver >= 1) 171938032Speter q->q_flags &= ~Q_PINGFLAGS; 172038032Speter q->q_flags |= qflags; 172138032Speter q->q_orcpt = orcpt; 172238032Speter (void) recipient(q, &e->e_sendqueue, 0, e); 172338032Speter } 172438032Speter orcpt = NULL; 172538032Speter break; 172638032Speter 172738032Speter case 'E': /* specify error recipient */ 172838032Speter /* no longer used */ 172938032Speter break; 173038032Speter 173138032Speter case 'H': /* header */ 173238032Speter (void) chompheader(&bp[1], FALSE, NULL, e); 173338032Speter hdrsize += strlen(&bp[1]); 173438032Speter break; 173538032Speter 173638032Speter case 'L': /* Solaris Content-Length: */ 173738032Speter case 'M': /* message */ 173838032Speter /* ignore this; we want a new message next time */ 173938032Speter break; 174038032Speter 174138032Speter case 'S': /* sender */ 174238032Speter setsender(newstr(&bp[1]), e, NULL, '\0', TRUE); 174338032Speter break; 174438032Speter 174538032Speter case 'B': /* body type */ 174638032Speter e->e_bodytype = newstr(&bp[1]); 174738032Speter break; 174838032Speter 174938032Speter#if _FFR_SAVE_CHARSET 175038032Speter case 'X': /* character set */ 175138032Speter e->e_charset = newstr(&bp[1]); 175238032Speter break; 175338032Speter#endif 175438032Speter 175538032Speter case 'D': /* data file name */ 175638032Speter /* obsolete -- ignore */ 175738032Speter break; 175838032Speter 175938032Speter case 'T': /* init time */ 176038032Speter e->e_ctime = atol(&bp[1]); 176138032Speter break; 176238032Speter 176338032Speter case 'I': /* data file's inode number */ 176438032Speter /* regenerated below */ 176538032Speter break; 176638032Speter 176738032Speter case 'K': /* time of last deliver attempt */ 176838032Speter e->e_dtime = atol(&buf[1]); 176938032Speter break; 177038032Speter 177138032Speter case 'N': /* number of delivery attempts */ 177238032Speter e->e_ntries = atoi(&buf[1]); 177338032Speter 177438032Speter /* if this has been tried recently, let it be */ 177538032Speter if (e->e_ntries > 0 && 177638032Speter MinQueueAge > 0 && e->e_dtime <= curtime() && 177738032Speter curtime() < e->e_dtime + MinQueueAge) 177838032Speter { 177938032Speter char *howlong = pintvl(curtime() - e->e_dtime, TRUE); 178038032Speter extern void unlockqueue __P((ENVELOPE *)); 178138032Speter 178238032Speter if (Verbose || tTd(40, 8)) 178338032Speter printf("%s: too young (%s)\n", 178438032Speter e->e_id, howlong); 178538032Speter if (LogLevel > 19) 178638032Speter sm_syslog(LOG_DEBUG, e->e_id, 178738032Speter "too young (%s)", 178838032Speter howlong); 178938032Speter e->e_id = NULL; 179038032Speter unlockqueue(e); 179138032Speter return FALSE; 179238032Speter } 179338032Speter break; 179438032Speter 179538032Speter case 'P': /* message priority */ 179638032Speter e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 179738032Speter break; 179838032Speter 179938032Speter case 'F': /* flag bits */ 180038032Speter if (strncmp(bp, "From ", 5) == 0) 180138032Speter { 180238032Speter /* we are being spoofed! */ 180338032Speter syserr("SECURITY ALERT: bogus qf line %s", bp); 180438032Speter fclose(qfp); 180538032Speter loseqfile(e, "bogus queue line"); 180638032Speter return FALSE; 180738032Speter } 180838032Speter for (p = &bp[1]; *p != '\0'; p++) 180938032Speter { 181038032Speter switch (*p) 181138032Speter { 181238032Speter case 'w': /* warning sent */ 181338032Speter e->e_flags |= EF_WARNING; 181438032Speter break; 181538032Speter 181638032Speter case 'r': /* response */ 181738032Speter e->e_flags |= EF_RESPONSE; 181838032Speter break; 181938032Speter 182038032Speter case '8': /* has 8 bit data */ 182138032Speter e->e_flags |= EF_HAS8BIT; 182238032Speter break; 182338032Speter 182438032Speter case 'b': /* delete Bcc: header */ 182538032Speter e->e_flags |= EF_DELETE_BCC; 182638032Speter break; 182738032Speter 182838032Speter case 'd': /* envelope has DSN RET= */ 182938032Speter e->e_flags |= EF_RET_PARAM; 183038032Speter break; 183138032Speter 183238032Speter case 'n': /* don't return body */ 183338032Speter e->e_flags |= EF_NO_BODY_RETN; 183438032Speter break; 183538032Speter } 183638032Speter } 183738032Speter break; 183838032Speter 183938032Speter case 'Z': /* original envelope id from ESMTP */ 184038032Speter e->e_envid = newstr(&bp[1]); 184138032Speter break; 184238032Speter 184338032Speter case '$': /* define macro */ 184438032Speter mid = macid(&bp[1], &ep); 184538032Speter define(mid, newstr(ep), e); 184638032Speter break; 184738032Speter 184838032Speter case '.': /* terminate file */ 184938032Speter nomore = TRUE; 185038032Speter break; 185138032Speter 185238032Speter default: 185338032Speter syserr("readqf: %s: line %d: bad line \"%s\"", 185438032Speter qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); 185538032Speter fclose(qfp); 185638032Speter loseqfile(e, "unrecognized line"); 185738032Speter return FALSE; 185838032Speter } 185938032Speter 186038032Speter if (bp != buf) 186138032Speter free(bp); 186238032Speter } 186338032Speter 186438032Speter /* 186538032Speter ** If we haven't read any lines, this queue file is empty. 186638032Speter ** Arrange to remove it without referencing any null pointers. 186738032Speter */ 186838032Speter 186938032Speter if (LineNumber == 0) 187038032Speter { 187138032Speter errno = 0; 187238032Speter e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE; 187338032Speter return TRUE; 187438032Speter } 187538032Speter 187638032Speter /* 187738032Speter ** Arrange to read the data file. 187838032Speter */ 187938032Speter 188038032Speter p = queuename(e, 'd'); 188138032Speter e->e_dfp = fopen(p, "r"); 188238032Speter if (e->e_dfp == NULL) 188338032Speter { 188438032Speter syserr("readqf: cannot open %s", p); 188538032Speter } 188638032Speter else 188738032Speter { 188838032Speter e->e_flags |= EF_HAS_DF; 188938032Speter if (fstat(fileno(e->e_dfp), &st) >= 0) 189038032Speter { 189138032Speter e->e_msgsize = st.st_size + hdrsize; 189238032Speter e->e_dfdev = st.st_dev; 189338032Speter e->e_dfino = st.st_ino; 189438032Speter } 189538032Speter } 189638032Speter 189738032Speter return TRUE; 189838032Speter} 189938032Speter/* 190038032Speter** PRINTQUEUE -- print out a representation of the mail queue 190138032Speter** 190238032Speter** Parameters: 190338032Speter** none. 190438032Speter** 190538032Speter** Returns: 190638032Speter** none. 190738032Speter** 190838032Speter** Side Effects: 190938032Speter** Prints a listing of the mail queue on the standard output. 191038032Speter*/ 191138032Speter 191238032Spetervoid 191338032Speterprintqueue() 191438032Speter{ 191538032Speter register WORK *w; 191638032Speter FILE *f; 191738032Speter int nrequests; 191838032Speter char buf[MAXLINE]; 191938032Speter 192038032Speter /* 192138032Speter ** Check for permission to print the queue 192238032Speter */ 192338032Speter 192438032Speter if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 192538032Speter { 192638032Speter struct stat st; 192738032Speter# ifdef NGROUPS_MAX 192838032Speter int n; 192938032Speter extern GIDSET_T InitialGidSet[NGROUPS_MAX]; 193038032Speter# endif 193138032Speter 193238032Speter if (stat(QueueDir, &st) < 0) 193338032Speter { 193438032Speter syserr("Cannot stat %s", QueueDir); 193538032Speter return; 193638032Speter } 193738032Speter# ifdef NGROUPS_MAX 193838032Speter n = NGROUPS_MAX; 193938032Speter while (--n >= 0) 194038032Speter { 194138032Speter if (InitialGidSet[n] == st.st_gid) 194238032Speter break; 194338032Speter } 194438032Speter if (n < 0 && RealGid != st.st_gid) 194538032Speter# else 194638032Speter if (RealGid != st.st_gid) 194738032Speter# endif 194838032Speter { 194938032Speter usrerr("510 You are not permitted to see the queue"); 195038032Speter setstat(EX_NOPERM); 195138032Speter return; 195238032Speter } 195338032Speter } 195438032Speter 195538032Speter /* 195638032Speter ** Read and order the queue. 195738032Speter */ 195838032Speter 195938032Speter nrequests = orderq(TRUE); 196038032Speter 196138032Speter /* 196238032Speter ** Print the work list that we have read. 196338032Speter */ 196438032Speter 196538032Speter /* first see if there is anything */ 196638032Speter if (nrequests <= 0) 196738032Speter { 196838032Speter printf("Mail queue is empty\n"); 196938032Speter return; 197038032Speter } 197138032Speter 197238032Speter CurrentLA = getla(); /* get load average */ 197338032Speter 197438032Speter printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s"); 197538032Speter if (MaxQueueRun > 0 && nrequests > MaxQueueRun) 197638032Speter printf(", only %d printed", MaxQueueRun); 197738032Speter if (Verbose) 197838032Speter printf(")\n--Q-ID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n"); 197938032Speter else 198038032Speter printf(")\n--Q-ID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n"); 198138032Speter for (w = WorkQ; w != NULL; w = w->w_next) 198238032Speter { 198338032Speter struct stat st; 198438032Speter auto time_t submittime = 0; 198538032Speter long dfsize; 198638032Speter int flags = 0; 198738032Speter int qfver; 198838032Speter char statmsg[MAXLINE]; 198938032Speter char bodytype[MAXNAME + 1]; 199038032Speter 199138032Speter printf("%8s", w->w_name + 2); 199238032Speter f = fopen(w->w_name, "r"); 199338032Speter if (f == NULL) 199438032Speter { 199538032Speter printf(" (job completed)\n"); 199638032Speter errno = 0; 199738032Speter continue; 199838032Speter } 199938032Speter w->w_name[0] = 'd'; 200038032Speter if (stat(w->w_name, &st) >= 0) 200138032Speter dfsize = st.st_size; 200238032Speter else 200338032Speter dfsize = -1; 200438032Speter if (w->w_lock) 200538032Speter printf("*"); 200638032Speter else if (w->w_tooyoung) 200738032Speter printf("-"); 200838032Speter else if (shouldqueue(w->w_pri, w->w_ctime)) 200938032Speter printf("X"); 201038032Speter else 201138032Speter printf(" "); 201238032Speter errno = 0; 201338032Speter 201438032Speter statmsg[0] = bodytype[0] = '\0'; 201538032Speter qfver = 0; 201638032Speter while (fgets(buf, sizeof buf, f) != NULL) 201738032Speter { 201838032Speter register int i; 201938032Speter register char *p; 202038032Speter 202138032Speter fixcrlf(buf, TRUE); 202238032Speter switch (buf[0]) 202338032Speter { 202438032Speter case 'V': /* queue file version */ 202538032Speter qfver = atoi(&buf[1]); 202638032Speter break; 202738032Speter 202838032Speter case 'M': /* error message */ 202938032Speter if ((i = strlen(&buf[1])) >= sizeof statmsg) 203038032Speter i = sizeof statmsg - 1; 203138032Speter bcopy(&buf[1], statmsg, i); 203238032Speter statmsg[i] = '\0'; 203338032Speter break; 203438032Speter 203538032Speter case 'B': /* body type */ 203638032Speter if ((i = strlen(&buf[1])) >= sizeof bodytype) 203738032Speter i = sizeof bodytype - 1; 203838032Speter bcopy(&buf[1], bodytype, i); 203938032Speter bodytype[i] = '\0'; 204038032Speter break; 204138032Speter 204238032Speter case 'S': /* sender name */ 204338032Speter if (Verbose) 204438032Speter printf("%8ld %10ld%c%.12s %.78s", 204538032Speter dfsize, 204638032Speter w->w_pri, 204738032Speter bitset(EF_WARNING, flags) ? '+' : ' ', 204838032Speter ctime(&submittime) + 4, 204938032Speter &buf[1]); 205038032Speter else 205138032Speter printf("%8ld %.16s %.45s", dfsize, 205238032Speter ctime(&submittime), &buf[1]); 205338032Speter if (statmsg[0] != '\0' || bodytype[0] != '\0') 205438032Speter { 205538032Speter printf("\n %10.10s", bodytype); 205638032Speter if (statmsg[0] != '\0') 205738032Speter printf(" (%.*s)", 205838032Speter Verbose ? 100 : 60, 205938032Speter statmsg); 206038032Speter } 206138032Speter break; 206238032Speter 206338032Speter case 'C': /* controlling user */ 206438032Speter if (Verbose) 206538032Speter printf("\n\t\t\t\t (---%.74s---)", 206638032Speter &buf[1]); 206738032Speter break; 206838032Speter 206938032Speter case 'R': /* recipient name */ 207038032Speter p = &buf[1]; 207138032Speter if (qfver >= 1) 207238032Speter { 207338032Speter p = strchr(p, ':'); 207438032Speter if (p == NULL) 207538032Speter break; 207638032Speter p++; 207738032Speter } 207838032Speter if (Verbose) 207938032Speter printf("\n\t\t\t\t\t %.78s", p); 208038032Speter else 208138032Speter printf("\n\t\t\t\t %.45s", p); 208238032Speter break; 208338032Speter 208438032Speter case 'T': /* creation time */ 208538032Speter submittime = atol(&buf[1]); 208638032Speter break; 208738032Speter 208838032Speter case 'F': /* flag bits */ 208938032Speter for (p = &buf[1]; *p != '\0'; p++) 209038032Speter { 209138032Speter switch (*p) 209238032Speter { 209338032Speter case 'w': 209438032Speter flags |= EF_WARNING; 209538032Speter break; 209638032Speter } 209738032Speter } 209838032Speter } 209938032Speter } 210038032Speter if (submittime == (time_t) 0) 210138032Speter printf(" (no control file)"); 210238032Speter printf("\n"); 210338032Speter (void) fclose(f); 210438032Speter } 210538032Speter} 210638032Speter 210738032Speter# endif /* QUEUE */ 210838032Speter/* 210938032Speter** QUEUENAME -- build a file name in the queue directory for this envelope. 211038032Speter** 211138032Speter** Assigns an id code if one does not already exist. 211238032Speter** This code is very careful to avoid trashing existing files 211338032Speter** under any circumstances. 211438032Speter** 211538032Speter** Parameters: 211638032Speter** e -- envelope to build it in/from. 211738032Speter** type -- the file type, used as the first character 211838032Speter** of the file name. 211938032Speter** 212038032Speter** Returns: 212138032Speter** a pointer to the new file name (in a static buffer). 212238032Speter** 212338032Speter** Side Effects: 212438032Speter** If no id code is already assigned, queuename will 212538032Speter** assign an id code, create a qf file, and leave a 212638032Speter** locked, open-for-write file pointer in the envelope. 212738032Speter*/ 212838032Speter 212938032Speter#ifndef ENOLCK 213038032Speter# define ENOLCK -1 213138032Speter#endif 213238032Speter#ifndef ENOSPC 213338032Speter# define ENOSPC -1 213438032Speter#endif 213538032Speter 213638032Speterchar * 213738032Speterqueuename(e, type) 213838032Speter register ENVELOPE *e; 213938032Speter int type; 214038032Speter{ 214138032Speter static pid_t pid = -1; 214238032Speter static char c0; 214338032Speter static char c1; 214438032Speter static char c2; 214538032Speter time_t now; 214638032Speter struct tm *tm; 214738032Speter static char buf[MAXNAME + 1]; 214838032Speter 214938032Speter if (e->e_id == NULL) 215038032Speter { 215138032Speter char qf[MAXQFNAME]; 215238032Speter 215338032Speter /* find a unique id */ 215438032Speter if (pid != getpid()) 215538032Speter { 215638032Speter /* new process -- start back at "AA" */ 215738032Speter pid = getpid(); 215838032Speter now = curtime(); 215938032Speter tm = localtime(&now); 216038032Speter c0 = 'A' + tm->tm_hour; 216138032Speter c1 = 'A'; 216238032Speter c2 = 'A' - 1; 216338032Speter } 216438032Speter (void) snprintf(qf, sizeof qf, "qf%cAA%05d", c0, pid); 216538032Speter 216638032Speter while (c1 < '~' || c2 < 'Z') 216738032Speter { 216838032Speter int i; 216938032Speter int attempts = 0; 217038032Speter 217138032Speter if (c2 >= 'Z') 217238032Speter { 217338032Speter c1++; 217438032Speter c2 = 'A' - 1; 217538032Speter } 217638032Speter qf[3] = c1; 217738032Speter qf[4] = ++c2; 217838032Speter if (tTd(7, 20)) 217938032Speter printf("queuename: trying \"%s\"\n", qf); 218038032Speter 218138032Speter i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode); 218238032Speter if (i < 0) 218338032Speter { 218438032Speter if (errno == EEXIST) 218538032Speter continue; 218638032Speter syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", 218738032Speter qf, QueueDir, geteuid()); 218838032Speter exit(EX_UNAVAILABLE); 218938032Speter } 219038032Speter do 219138032Speter { 219238032Speter if (attempts > 0) 219338032Speter sleep(attempts); 219438032Speter e->e_lockfp = 0; 219538032Speter if (lockfile(i, qf, NULL, LOCK_EX|LOCK_NB)) 219638032Speter { 219738032Speter e->e_lockfp = fdopen(i, "w"); 219838032Speter break; 219938032Speter } 220038032Speter } while ((errno == ENOLCK || errno == ENOSPC) && 220138032Speter attempts++ < 4); 220238032Speter 220338032Speter /* Successful lock */ 220438032Speter if (e->e_lockfp != 0) 220538032Speter break; 220638032Speter 220738032Speter#if !HASFLOCK 220838032Speter if (errno != EAGAIN && errno != EACCES) 220938032Speter#else 221038032Speter if (errno != EWOULDBLOCK) 221138032Speter#endif 221238032Speter { 221338032Speter syserr("queuename: Cannot lock \"%s\" in \"%s\" (euid=%d)", 221438032Speter qf, QueueDir, geteuid()); 221538032Speter exit(EX_OSERR); 221638032Speter } 221738032Speter 221838032Speter /* a reader got the file; abandon it and try again */ 221938032Speter (void) close(i); 222038032Speter } 222138032Speter if (c1 >= '~' && c2 >= 'Z') 222238032Speter { 222338032Speter syserr("queuename: Cannot create \"%s\" in \"%s\" (euid=%d)", 222438032Speter qf, QueueDir, geteuid()); 222538032Speter exit(EX_OSERR); 222638032Speter } 222738032Speter e->e_id = newstr(&qf[2]); 222838032Speter define('i', e->e_id, e); 222938032Speter if (tTd(7, 1)) 223038032Speter printf("queuename: assigned id %s, env=%lx\n", 223138032Speter e->e_id, (u_long) e); 223238032Speter if (tTd(7, 9)) 223338032Speter { 223438032Speter printf(" lockfd="); 223538032Speter dumpfd(fileno(e->e_lockfp), TRUE, FALSE); 223638032Speter } 223738032Speter if (LogLevel > 93) 223838032Speter sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); 223938032Speter } 224038032Speter 224138032Speter if (type == '\0') 224238032Speter return (NULL); 224338032Speter (void) snprintf(buf, sizeof buf, "%cf%s", type, e->e_id); 224438032Speter if (tTd(7, 2)) 224538032Speter printf("queuename: %s\n", buf); 224638032Speter return (buf); 224738032Speter} 224838032Speter/* 224938032Speter** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 225038032Speter** 225138032Speter** Parameters: 225238032Speter** e -- the envelope to unlock. 225338032Speter** 225438032Speter** Returns: 225538032Speter** none 225638032Speter** 225738032Speter** Side Effects: 225838032Speter** unlocks the queue for `e'. 225938032Speter*/ 226038032Speter 226138032Spetervoid 226238032Speterunlockqueue(e) 226338032Speter ENVELOPE *e; 226438032Speter{ 226538032Speter if (tTd(51, 4)) 226638032Speter printf("unlockqueue(%s)\n", 226738032Speter e->e_id == NULL ? "NOQUEUE" : e->e_id); 226838032Speter 226938032Speter /* if there is a lock file in the envelope, close it */ 227038032Speter if (e->e_lockfp != NULL) 227138032Speter xfclose(e->e_lockfp, "unlockqueue", e->e_id); 227238032Speter e->e_lockfp = NULL; 227338032Speter 227438032Speter /* don't create a queue id if we don't already have one */ 227538032Speter if (e->e_id == NULL) 227638032Speter return; 227738032Speter 227838032Speter /* remove the transcript */ 227938032Speter if (LogLevel > 87) 228038032Speter sm_syslog(LOG_DEBUG, e->e_id, "unlock"); 228138032Speter if (!tTd(51, 104)) 228238032Speter xunlink(queuename(e, 'x')); 228338032Speter 228438032Speter} 228538032Speter/* 228638032Speter** SETCTLUSER -- create a controlling address 228738032Speter** 228838032Speter** Create a fake "address" given only a local login name; this is 228938032Speter** used as a "controlling user" for future recipient addresses. 229038032Speter** 229138032Speter** Parameters: 229238032Speter** user -- the user name of the controlling user. 229338032Speter** qfver -- the version stamp of this qf file. 229438032Speter** 229538032Speter** Returns: 229638032Speter** An address descriptor for the controlling user. 229738032Speter** 229838032Speter** Side Effects: 229938032Speter** none. 230038032Speter*/ 230138032Speter 230238032SpeterADDRESS * 230338032Spetersetctluser(user, qfver) 230438032Speter char *user; 230538032Speter int qfver; 230638032Speter{ 230738032Speter register ADDRESS *a; 230838032Speter struct passwd *pw; 230938032Speter char *p; 231038032Speter 231138032Speter /* 231238032Speter ** See if this clears our concept of controlling user. 231338032Speter */ 231438032Speter 231538032Speter if (user == NULL || *user == '\0') 231638032Speter return NULL; 231738032Speter 231838032Speter /* 231938032Speter ** Set up addr fields for controlling user. 232038032Speter */ 232138032Speter 232238032Speter a = (ADDRESS *) xalloc(sizeof *a); 232338032Speter bzero((char *) a, sizeof *a); 232438032Speter 232538032Speter if (*user == '\0') 232638032Speter { 232738032Speter p = NULL; 232838032Speter a->q_user = newstr(DefUser); 232938032Speter } 233038032Speter else if (*user == ':') 233138032Speter { 233238032Speter p = &user[1]; 233338032Speter a->q_user = newstr(p); 233438032Speter } 233538032Speter else 233638032Speter { 233738032Speter p = strtok(user, ":"); 233838032Speter a->q_user = newstr(user); 233938032Speter if (qfver >= 2) 234038032Speter { 234138032Speter if ((p = strtok(NULL, ":")) != NULL) 234238032Speter a->q_uid = atoi(p); 234338032Speter if ((p = strtok(NULL, ":")) != NULL) 234438032Speter a->q_gid = atoi(p); 234538032Speter if ((p = strtok(NULL, ":")) != NULL) 234638032Speter a->q_flags |= QGOODUID; 234738032Speter } 234838032Speter else if ((pw = sm_getpwnam(user)) != NULL) 234938032Speter { 235038032Speter if (strcmp(pw->pw_dir, "/") == 0) 235138032Speter a->q_home = ""; 235238032Speter else 235338032Speter a->q_home = newstr(pw->pw_dir); 235438032Speter a->q_uid = pw->pw_uid; 235538032Speter a->q_gid = pw->pw_gid; 235638032Speter a->q_flags |= QGOODUID; 235738032Speter } 235838032Speter } 235938032Speter 236038032Speter a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 236138032Speter a->q_mailer = LocalMailer; 236238032Speter if (p == NULL) 236338032Speter a->q_paddr = a->q_user; 236438032Speter else 236538032Speter a->q_paddr = newstr(p); 236638032Speter return a; 236738032Speter} 236838032Speter/* 236938032Speter** LOSEQFILE -- save the qf as Qf and try to let someone know 237038032Speter** 237138032Speter** Parameters: 237238032Speter** e -- the envelope (e->e_id will be used). 237338032Speter** why -- reported to whomever can hear. 237438032Speter** 237538032Speter** Returns: 237638032Speter** none. 237738032Speter*/ 237838032Speter 237938032Spetervoid 238038032Speterloseqfile(e, why) 238138032Speter register ENVELOPE *e; 238238032Speter char *why; 238338032Speter{ 238438032Speter char *p; 238538032Speter char buf[MAXQFNAME + 1]; 238638032Speter 238738032Speter if (e == NULL || e->e_id == NULL) 238838032Speter return; 238938032Speter p = queuename(e, 'q'); 239038032Speter if (strlen(p) > MAXQFNAME) 239138032Speter { 239238032Speter syserr("loseqfile: queuename (%s) too long", p); 239338032Speter return; 239438032Speter } 239538032Speter strcpy(buf, p); 239638032Speter p = queuename(e, 'Q'); 239738032Speter if (rename(buf, p) < 0) 239838032Speter syserr("cannot rename(%s, %s), uid=%d", buf, p, geteuid()); 239938032Speter else if (LogLevel > 0) 240038032Speter sm_syslog(LOG_ALERT, e->e_id, 240138032Speter "Losing %s: %s", buf, why); 240238032Speter} 2403