queue.c revision 111823
138032Speter/* 2111823Sgshapiro * Copyright (c) 1998-2003 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 1464562Sgshapiro#include <sendmail.h> 1564562Sgshapiro 16111823SgshapiroSM_RCSID("@(#)$Id: queue.c,v 8.863.2.28 2003/02/11 17:17:22 ca Exp $") 1738032Speter 1890792Sgshapiro#include <dirent.h> 1938032Speter 2090792Sgshapiro# define RELEASE_QUEUE (void) 0 2190792Sgshapiro# define ST_INODE(st) (st).st_ino 2290792Sgshapiro 2390792Sgshapiro 2490792Sgshapiro/* 2590792Sgshapiro** Historical notes: 26110560Sgshapiro** QF_VERSION == 4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY 27110560Sgshapiro** QF_VERSION == 5 was sendmail 8.10/8.11 with _FFR_QUEUEDELAY 28110560Sgshapiro** QF_VERSION == 6 is sendmail 8.12 without _FFR_QUEUEDELAY 29110560Sgshapiro** QF_VERSION == 7 is sendmail 8.12 with _FFR_QUEUEDELAY 3090792Sgshapiro*/ 3190792Sgshapiro 3290792Sgshapiro#if _FFR_QUEUEDELAY 3390792Sgshapiro# define QF_VERSION 7 /* version number of this queue format */ 3464562Sgshapirostatic time_t queuedelay __P((ENVELOPE *)); 3590792Sgshapiro#define queuedelay_qfver_unsupported(qfver) false 3690792Sgshapiro#else /* _FFR_QUEUEDELAY */ 3790792Sgshapiro# define QF_VERSION 6 /* version number of this queue format */ 3890792Sgshapiro# define queuedelay(e) MinQueueAge 3990792Sgshapiro#define queuedelay_qfver_unsupported(qfver) ((qfver) == 5 || (qfver) == 7) 4090792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 4190792Sgshapiro#if _FFR_QUARANTINE 4290792Sgshapirostatic char queue_letter __P((ENVELOPE *, int)); 4390792Sgshapirostatic bool quarantine_queue_item __P((int, int, ENVELOPE *, char *)); 4490792Sgshapiro#endif /* _FFR_QUARANTINE */ 4564562Sgshapiro 4690792Sgshapiro/* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */ 4790792Sgshapiro 4838032Speter/* 4938032Speter** Work queue. 5038032Speter*/ 5138032Speter 5238032Speterstruct work 5338032Speter{ 5438032Speter char *w_name; /* name of control file */ 5538032Speter char *w_host; /* name of recipient host */ 5638032Speter bool w_lock; /* is message locked? */ 5738032Speter bool w_tooyoung; /* is it too young to run? */ 5838032Speter long w_pri; /* priority of message, see below */ 5990792Sgshapiro time_t w_ctime; /* creation time */ 6090792Sgshapiro time_t w_mtime; /* modification time */ 6190792Sgshapiro int w_qgrp; /* queue group located in */ 6290792Sgshapiro int w_qdir; /* queue directory located in */ 6338032Speter struct work *w_next; /* next in queue */ 6438032Speter}; 6538032Speter 6638032Spetertypedef struct work WORK; 6738032Speter 6890792Sgshapirostatic WORK *WorkQ; /* queue of things to be done */ 6990792Sgshapirostatic int NumWorkGroups; /* number of work groups */ 7038032Speter 7190792Sgshapiro/* 7294334Sgshapiro** DoQueueRun indicates that a queue run is needed. 7394334Sgshapiro** Notice: DoQueueRun is modified in a signal handler! 7490792Sgshapiro*/ 7590792Sgshapiro 76111823Sgshapirostatic bool volatile DoQueueRun; /* non-interrupt time queue run needed */ 7790792Sgshapiro 7890792Sgshapiro/* 7990792Sgshapiro** Work group definition structure. 8090792Sgshapiro** Each work group contains one or more queue groups. This is done 8190792Sgshapiro** to manage the number of queue group runners active at the same time 8290792Sgshapiro** to be within the constraints of MaxQueueChildren (if it is set). 8390792Sgshapiro** The number of queue groups that can be run on the next work run 8490792Sgshapiro** is kept track of. The queue groups are run in a round robin. 8590792Sgshapiro*/ 8690792Sgshapiro 8790792Sgshapirostruct workgrp 8890792Sgshapiro{ 8990792Sgshapiro int wg_numqgrp; /* number of queue groups in work grp */ 9090792Sgshapiro int wg_runners; /* total runners */ 9190792Sgshapiro int wg_curqgrp; /* current queue group */ 9290792Sgshapiro QUEUEGRP **wg_qgs; /* array of queue groups */ 9390792Sgshapiro int wg_maxact; /* max # of active runners */ 9490792Sgshapiro time_t wg_lowqintvl; /* lowest queue interval */ 9590792Sgshapiro int wg_restart; /* needs restarting? */ 9690792Sgshapiro int wg_restartcnt; /* count of times restarted */ 9790792Sgshapiro}; 9890792Sgshapiro 9990792Sgshapirotypedef struct workgrp WORKGRP; 10090792Sgshapiro 10190792Sgshapirostatic WORKGRP volatile WorkGrp[MAXWORKGROUPS + 1]; /* work groups */ 10290792Sgshapiro 10390792Sgshapiro#if SM_HEAP_CHECK 10490792Sgshapirostatic SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q", 10590792Sgshapiro "@(#)$Debug: leak_q - trace memory leaks during queue processing $"); 10690792Sgshapiro#endif /* SM_HEAP_CHECK */ 10790792Sgshapiro 10890792Sgshapiro/* 10990792Sgshapiro** We use EmptyString instead of "" to avoid 11090792Sgshapiro** 'zero-length format string' warnings from gcc 11190792Sgshapiro*/ 11290792Sgshapiro 11390792Sgshapirostatic const char EmptyString[] = ""; 11490792Sgshapiro 11590792Sgshapirostatic void grow_wlist __P((int, int)); 11690792Sgshapirostatic int multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *)); 11790792Sgshapirostatic int gatherq __P((int, int, bool, bool *, bool *)); 11890792Sgshapirostatic int sortq __P((int)); 11990792Sgshapirostatic void printctladdr __P((ADDRESS *, SM_FILE_T *)); 12090792Sgshapirostatic bool readqf __P((ENVELOPE *, bool)); 12190792Sgshapirostatic void restart_work_group __P((int)); 12290792Sgshapirostatic void runner_work __P((ENVELOPE *, int, bool, int, int)); 12394334Sgshapirostatic void schedule_queue_runs __P((bool, int, bool)); 12464562Sgshapirostatic char *strrev __P((char *)); 12590792Sgshapirostatic ADDRESS *setctluser __P((char *, int, ENVELOPE *)); 12690792Sgshapiro#if _FFR_RHS 12790792Sgshapirostatic int sm_strshufflecmp __P((char *, char *)); 12890792Sgshapirostatic void init_shuffle_alphabet __P(()); 12990792Sgshapiro#endif /* _FFR_RHS */ 13064562Sgshapirostatic int workcmpf0(); 13164562Sgshapirostatic int workcmpf1(); 13264562Sgshapirostatic int workcmpf2(); 13364562Sgshapirostatic int workcmpf3(); 13464562Sgshapirostatic int workcmpf4(); 135110560Sgshapirostatic int randi = 3; /* index for workcmpf5() */ 13690792Sgshapirostatic int workcmpf5(); 13790792Sgshapirostatic int workcmpf6(); 13890792Sgshapiro#if _FFR_RHS 13990792Sgshapirostatic int workcmpf7(); 14090792Sgshapiro#endif /* _FFR_RHS */ 14138032Speter 14290792Sgshapiro#if RANDOMSHIFT 14390792Sgshapiro# define get_rand_mod(m) ((get_random() >> RANDOMSHIFT) % (m)) 14490792Sgshapiro#else /* RANDOMSHIFT */ 14590792Sgshapiro# define get_rand_mod(m) (get_random() % (m)) 14690792Sgshapiro#endif /* RANDOMSHIFT */ 14790792Sgshapiro 14880785Sgshapiro/* 14990792Sgshapiro** File system definition. 15090792Sgshapiro** Used to keep track of how much free space is available 15190792Sgshapiro** on a file system in which one or more queue directories reside. 15290792Sgshapiro*/ 15390792Sgshapiro 15490792Sgshapirotypedef struct filesys_shared FILESYS; 15590792Sgshapiro 15690792Sgshapirostruct filesys_shared 15790792Sgshapiro{ 15890792Sgshapiro dev_t fs_dev; /* unique device id */ 15990792Sgshapiro long fs_avail; /* number of free blocks available */ 16090792Sgshapiro long fs_blksize; /* block size, in bytes */ 16190792Sgshapiro}; 16290792Sgshapiro 16390792Sgshapiro/* probably kept in shared memory */ 16490792Sgshapirostatic FILESYS FileSys[MAXFILESYS]; /* queue file systems */ 16590792Sgshapirostatic char *FSPath[MAXFILESYS]; /* pathnames for file systems */ 16690792Sgshapiro 16790792Sgshapiro#if SM_CONF_SHM 16890792Sgshapiro 16990792Sgshapiro/* 17090792Sgshapiro** Shared memory data 17190792Sgshapiro** 17290792Sgshapiro** Current layout: 17390792Sgshapiro** size -- size of shared memory segment 17490792Sgshapiro** pid -- pid of owner, should be a unique id to avoid misinterpretations 17590792Sgshapiro** by other processes. 17690792Sgshapiro** tag -- should be a unique id to avoid misinterpretations by others. 17790792Sgshapiro** idea: hash over configuration data that will be stored here. 17890792Sgshapiro** NumFileSys -- number of file systems. 17990792Sgshapiro** FileSys -- (arrary of) structure for used file systems. 18090792Sgshapiro** RSATmpCnt -- counter for number of uses of ephemeral RSA key. 18190792Sgshapiro** QShm -- (array of) structure for information about queue directories. 18290792Sgshapiro*/ 18390792Sgshapiro 18490792Sgshapiro/* 18590792Sgshapiro** Queue data in shared memory 18690792Sgshapiro*/ 18790792Sgshapiro 18890792Sgshapirotypedef struct queue_shared QUEUE_SHM_T; 18990792Sgshapiro 19090792Sgshapirostruct queue_shared 19190792Sgshapiro{ 19290792Sgshapiro int qs_entries; /* number of entries */ 19390792Sgshapiro /* XXX more to follow? */ 19490792Sgshapiro}; 19590792Sgshapiro 19690792Sgshapirostatic void *Pshm; /* pointer to shared memory */ 19790792Sgshapirostatic FILESYS *PtrFileSys; /* pointer to queue file system array */ 19890792Sgshapiroint ShmId = SM_SHM_NO_ID; /* shared memory id */ 19990792Sgshapirostatic QUEUE_SHM_T *QShm; /* pointer to shared queue data */ 200110560Sgshapirostatic size_t shms; 20190792Sgshapiro 20290792Sgshapiro# define SHM_OFF_PID(p) (((char *) (p)) + sizeof(int)) 20390792Sgshapiro# define SHM_OFF_TAG(p) (((char *) (p)) + sizeof(pid_t) + sizeof(int)) 20490792Sgshapiro# define SHM_OFF_HEAD (sizeof(pid_t) + sizeof(int) * 2) 20590792Sgshapiro 20690792Sgshapiro/* how to access FileSys */ 20790792Sgshapiro# define FILE_SYS(i) (PtrFileSys[i]) 20890792Sgshapiro 20990792Sgshapiro/* first entry is a tag, for now just the size */ 21090792Sgshapiro# define OFF_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD) 21190792Sgshapiro 21290792Sgshapiro/* offset for PNumFileSys */ 21390792Sgshapiro# define OFF_NUM_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys)) 21490792Sgshapiro 21590792Sgshapiro/* offset for PRSATmpCnt */ 21690792Sgshapiro# define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int)) 21790792Sgshapiroint *PRSATmpCnt; 21890792Sgshapiro 21990792Sgshapiro/* offset for queue_shm */ 22090792Sgshapiro# define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2) 22190792Sgshapiro 22290792Sgshapiro#define QSHM_ENTRIES(i) QShm[i].qs_entries 22390792Sgshapiro 22490792Sgshapiro/* basic size of shared memory segment */ 22590792Sgshapiro# define SM_T_SIZE (SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2) 22690792Sgshapiro 22790792Sgshapirostatic unsigned int hash_q __P((char *, unsigned int)); 22890792Sgshapiro 22990792Sgshapiro/* 23090792Sgshapiro** HASH_Q -- simple hash function 23190792Sgshapiro** 23290792Sgshapiro** Parameters: 23390792Sgshapiro** p -- string to hash. 23490792Sgshapiro** h -- hash start value (from previous run). 23590792Sgshapiro** 23690792Sgshapiro** Returns: 23790792Sgshapiro** hash value. 23890792Sgshapiro*/ 23990792Sgshapiro 24090792Sgshapirostatic unsigned int 24190792Sgshapirohash_q(p, h) 24290792Sgshapiro char *p; 24390792Sgshapiro unsigned int h; 24490792Sgshapiro{ 24590792Sgshapiro int c, d; 24690792Sgshapiro 24790792Sgshapiro while (*p != '\0') 24890792Sgshapiro { 24990792Sgshapiro d = *p++; 25090792Sgshapiro c = d; 25190792Sgshapiro c ^= c<<6; 25290792Sgshapiro h += (c<<11) ^ (c>>1); 25390792Sgshapiro h ^= (d<<14) + (d<<7) + (d<<4) + d; 25490792Sgshapiro } 25590792Sgshapiro return h; 25690792Sgshapiro} 25790792Sgshapiro 258110560Sgshapiro 25990792Sgshapiro#else /* SM_CONF_SHM */ 26090792Sgshapiro# define FILE_SYS(i) FileSys[i] 26190792Sgshapiro#endif /* SM_CONF_SHM */ 26290792Sgshapiro 26390792Sgshapiro/* access to the various components of file system data */ 26490792Sgshapiro#define FILE_SYS_NAME(i) FSPath[i] 26590792Sgshapiro#define FILE_SYS_AVAIL(i) FILE_SYS(i).fs_avail 26690792Sgshapiro#define FILE_SYS_BLKSIZE(i) FILE_SYS(i).fs_blksize 26790792Sgshapiro#define FILE_SYS_DEV(i) FILE_SYS(i).fs_dev 26890792Sgshapiro 269110560Sgshapiro 27090792Sgshapiro/* 27180785Sgshapiro** Current qf file field assignments: 27280785Sgshapiro** 27380785Sgshapiro** A AUTH= parameter 27480785Sgshapiro** B body type 27580785Sgshapiro** C controlling user 27680785Sgshapiro** D data file name 27790792Sgshapiro** d data file directory name (added in 8.12) 27880785Sgshapiro** E error recipient 27980785Sgshapiro** F flag bits 28090792Sgshapiro** G queue delay algorithm (_FFR_QUEUEDELAY) 28180785Sgshapiro** H header 28280785Sgshapiro** I data file's inode number 28380785Sgshapiro** K time of last delivery attempt 28480785Sgshapiro** L Solaris Content-Length: header (obsolete) 28598841Sgshapiro** M message 28680785Sgshapiro** N number of delivery attempts 28780785Sgshapiro** P message priority 28890792Sgshapiro** q quarantine reason (_FFR_QUARANTINE) 28980785Sgshapiro** Q original recipient (ORCPT=) 29090792Sgshapiro** r final recipient (Final-Recipient: DSN field) 29180785Sgshapiro** R recipient 29280785Sgshapiro** S sender 29380785Sgshapiro** T init time 29480785Sgshapiro** V queue file version 29590792Sgshapiro** X free (was: character set if _FFR_SAVE_CHARSET) 29690792Sgshapiro** Y current delay (_FFR_QUEUEDELAY) 29780785Sgshapiro** Z original envelope id from ESMTP 29890792Sgshapiro** ! deliver by (added in 8.12) 29980785Sgshapiro** $ define macro 30080785Sgshapiro** . terminate file 30180785Sgshapiro*/ 30280785Sgshapiro 30390792Sgshapiro/* 30438032Speter** QUEUEUP -- queue a message up for future transmission. 30538032Speter** 30638032Speter** Parameters: 30738032Speter** e -- the envelope to queue up. 30890792Sgshapiro** announce -- if true, tell when you are queueing up. 30990792Sgshapiro** msync -- if true, then fsync() if SuperSafe interactive mode. 31038032Speter** 31138032Speter** Returns: 31238032Speter** none. 31338032Speter** 31438032Speter** Side Effects: 31590792Sgshapiro** The current request is saved in a control file. 31638032Speter** The queue file is left locked. 31738032Speter*/ 31838032Speter 31938032Spetervoid 32090792Sgshapiroqueueup(e, announce, msync) 32138032Speter register ENVELOPE *e; 32238032Speter bool announce; 32390792Sgshapiro bool msync; 32438032Speter{ 32590792Sgshapiro register SM_FILE_T *tfp; 32638032Speter register HDR *h; 32738032Speter register ADDRESS *q; 32864562Sgshapiro int tfd = -1; 32938032Speter int i; 33038032Speter bool newid; 33138032Speter register char *p; 33238032Speter MAILER nullmailer; 33338032Speter MCI mcibuf; 33490792Sgshapiro char qf[MAXPATHLEN]; 33564562Sgshapiro char tf[MAXPATHLEN]; 33690792Sgshapiro char df[MAXPATHLEN]; 33738032Speter char buf[MAXLINE]; 33838032Speter 33938032Speter /* 34038032Speter ** Create control file. 34138032Speter */ 34238032Speter 34338032Speter newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); 34490792Sgshapiro (void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof tf); 34538032Speter tfp = e->e_lockfp; 34638032Speter if (tfp == NULL) 34790792Sgshapiro newid = false; 34838032Speter 34990792Sgshapiro /* if newid, write the queue file directly (instead of temp file) */ 35038032Speter if (!newid) 35138032Speter { 35290792Sgshapiro const int flags = O_CREAT|O_WRONLY|O_EXCL; 35364562Sgshapiro 35438032Speter /* get a locked tf file */ 35538032Speter for (i = 0; i < 128; i++) 35638032Speter { 35764562Sgshapiro if (tfd < 0) 35838032Speter { 35990792Sgshapiro MODE_T oldumask = 0; 36064562Sgshapiro 36164562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 36264562Sgshapiro oldumask = umask(002); 36364562Sgshapiro tfd = open(tf, flags, QueueFileMode); 36464562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 36564562Sgshapiro (void) umask(oldumask); 36664562Sgshapiro 36764562Sgshapiro if (tfd < 0) 36864562Sgshapiro { 36964562Sgshapiro if (errno != EEXIST) 37064562Sgshapiro break; 37164562Sgshapiro if (LogLevel > 0 && (i % 32) == 0) 37264562Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 37364562Sgshapiro "queueup: cannot create %s, uid=%d: %s", 37498121Sgshapiro tf, (int) geteuid(), 37590792Sgshapiro sm_errstring(errno)); 37664562Sgshapiro } 37738032Speter } 37864562Sgshapiro if (tfd >= 0) 37938032Speter { 38064562Sgshapiro if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB)) 38138032Speter break; 38238032Speter else if (LogLevel > 0 && (i % 32) == 0) 38338032Speter sm_syslog(LOG_ALERT, e->e_id, 38464562Sgshapiro "queueup: cannot lock %s: %s", 38590792Sgshapiro tf, sm_errstring(errno)); 38664562Sgshapiro if ((i % 32) == 31) 38764562Sgshapiro { 38864562Sgshapiro (void) close(tfd); 38964562Sgshapiro tfd = -1; 39064562Sgshapiro } 39138032Speter } 39238032Speter 39338032Speter if ((i % 32) == 31) 39438032Speter { 39538032Speter /* save the old temp file away */ 39664562Sgshapiro (void) rename(tf, queuename(e, TEMPQF_LETTER)); 39738032Speter } 39838032Speter else 39964562Sgshapiro (void) sleep(i % 32); 40038032Speter } 40190792Sgshapiro if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 40290792Sgshapiro (void *) &tfd, SM_IO_WRONLY, 40390792Sgshapiro NULL)) == NULL) 40438032Speter { 40564562Sgshapiro int save_errno = errno; 40664562Sgshapiro 40790792Sgshapiro printopenfds(true); 40864562Sgshapiro errno = save_errno; 40938032Speter syserr("!queueup: cannot create queue temp file %s, uid=%d", 41098121Sgshapiro tf, (int) geteuid()); 41138032Speter } 41238032Speter } 41338032Speter 41438032Speter if (tTd(40, 1)) 41590792Sgshapiro sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n", 41690792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 41790792Sgshapiro queuename(e, ANYQFL_LETTER), 41890792Sgshapiro newid ? " (new id)" : ""); 41938032Speter if (tTd(40, 3)) 42038032Speter { 42190792Sgshapiro sm_dprintf(" e_flags="); 42238032Speter printenvflags(e); 42338032Speter } 42438032Speter if (tTd(40, 32)) 42538032Speter { 42690792Sgshapiro sm_dprintf(" sendq="); 42790792Sgshapiro printaddr(e->e_sendqueue, true); 42838032Speter } 42938032Speter if (tTd(40, 9)) 43038032Speter { 43190792Sgshapiro sm_dprintf(" tfp="); 43290792Sgshapiro dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false); 43390792Sgshapiro sm_dprintf(" lockfp="); 43438032Speter if (e->e_lockfp == NULL) 43590792Sgshapiro sm_dprintf("NULL\n"); 43638032Speter else 43790792Sgshapiro dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL), 43890792Sgshapiro true, false); 43938032Speter } 44038032Speter 44138032Speter /* 44238032Speter ** If there is no data file yet, create one. 44338032Speter */ 44438032Speter 44590792Sgshapiro (void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof df); 44664562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags)) 44738032Speter { 44890792Sgshapiro if (e->e_dfp != NULL && 44990792Sgshapiro SuperSafe != SAFE_REALLY && 45090792Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 45190792Sgshapiro errno != EINVAL) 45290792Sgshapiro { 45364562Sgshapiro syserr("!queueup: cannot commit data file %s, uid=%d", 45498121Sgshapiro queuename(e, DATAFL_LETTER), (int) geteuid()); 45590792Sgshapiro } 45690792Sgshapiro if (e->e_dfp != NULL && 45790792Sgshapiro SuperSafe == SAFE_INTERACTIVE && msync) 45890792Sgshapiro { 45990792Sgshapiro if (tTd(40,32)) 46090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 46190792Sgshapiro "queueup: fsync(e->e_dfp)"); 46290792Sgshapiro 46390792Sgshapiro if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, 46490792Sgshapiro NULL)) < 0) 46590792Sgshapiro { 46690792Sgshapiro if (newid) 46790792Sgshapiro syserr("!552 Error writing data file %s", 46890792Sgshapiro df); 46990792Sgshapiro else 47090792Sgshapiro syserr("!452 Error writing data file %s", 47190792Sgshapiro df); 47290792Sgshapiro } 47390792Sgshapiro } 47464562Sgshapiro } 47564562Sgshapiro else 47664562Sgshapiro { 47764562Sgshapiro int dfd; 47890792Sgshapiro MODE_T oldumask = 0; 47990792Sgshapiro register SM_FILE_T *dfp = NULL; 48038032Speter struct stat stbuf; 48138032Speter 48290792Sgshapiro if (e->e_dfp != NULL && 48390792Sgshapiro sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 48464562Sgshapiro syserr("committing over bf file"); 48564562Sgshapiro 48690792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 48790792Sgshapiro oldumask = umask(002); 48890792Sgshapiro dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC, QueueFileMode); 48990792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 49090792Sgshapiro (void) umask(oldumask); 49190792Sgshapiro if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 49290792Sgshapiro (void *) &dfd, SM_IO_WRONLY, 49390792Sgshapiro NULL)) == NULL) 49438032Speter syserr("!queueup: cannot create data temp file %s, uid=%d", 49598121Sgshapiro df, (int) geteuid()); 49664562Sgshapiro if (fstat(dfd, &stbuf) < 0) 49738032Speter e->e_dfino = -1; 49838032Speter else 49938032Speter { 50038032Speter e->e_dfdev = stbuf.st_dev; 50190792Sgshapiro e->e_dfino = ST_INODE(stbuf); 50238032Speter } 50338032Speter e->e_flags |= EF_HAS_DF; 50464562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 50538032Speter mcibuf.mci_out = dfp; 50638032Speter mcibuf.mci_mailer = FileMailer; 50738032Speter (*e->e_putbody)(&mcibuf, e, NULL); 50890792Sgshapiro 50990792Sgshapiro if (SuperSafe == SAFE_REALLY || 51090792Sgshapiro (SuperSafe == SAFE_INTERACTIVE && msync)) 51190792Sgshapiro { 51290792Sgshapiro if (tTd(40,32)) 51390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 51490792Sgshapiro "queueup: fsync(dfp)"); 51590792Sgshapiro 51690792Sgshapiro if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0) 51790792Sgshapiro { 51890792Sgshapiro if (newid) 51990792Sgshapiro syserr("!552 Error writing data file %s", 52090792Sgshapiro df); 52190792Sgshapiro else 52290792Sgshapiro syserr("!452 Error writing data file %s", 52390792Sgshapiro df); 52490792Sgshapiro } 52590792Sgshapiro } 52690792Sgshapiro 52790792Sgshapiro if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0) 52864562Sgshapiro syserr("!queueup: cannot save data temp file %s, uid=%d", 52998121Sgshapiro df, (int) geteuid()); 53038032Speter e->e_putbody = putbody; 53138032Speter } 53238032Speter 53338032Speter /* 53438032Speter ** Output future work requests. 53538032Speter ** Priority and creation time should be first, since 53690792Sgshapiro ** they are required by gatherq. 53738032Speter */ 53838032Speter 53938032Speter /* output queue version number (must be first!) */ 54090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION); 54138032Speter 54238032Speter /* output creation time */ 54390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime); 54438032Speter 54538032Speter /* output last delivery time */ 54690792Sgshapiro#if _FFR_QUEUEDELAY 54790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime); 54890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "G%d\n", e->e_queuealg); 54990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Y%ld\n", (long) e->e_queuedelay); 55064562Sgshapiro if (tTd(40, 64)) 55164562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 55264562Sgshapiro "queue alg: %d delay %ld next: %ld (now: %ld)\n", 55364562Sgshapiro e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime()); 55490792Sgshapiro#else /* _FFR_QUEUEDELAY */ 55590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime); 55690792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 55738032Speter 55838032Speter /* output number of delivery attempts */ 55990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries); 56038032Speter 56138032Speter /* output message priority */ 56290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority); 56338032Speter 56490792Sgshapiro /* 56590792Sgshapiro ** If data file is in a different directory than the queue file, 56690792Sgshapiro ** output a "d" record naming the directory of the data file. 56790792Sgshapiro */ 56890792Sgshapiro 56990792Sgshapiro if (e->e_dfqgrp != e->e_qgrp) 57090792Sgshapiro { 57190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n", 57290792Sgshapiro Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name); 57390792Sgshapiro } 57490792Sgshapiro 57538032Speter /* output inode number of data file */ 57638032Speter /* XXX should probably include device major/minor too */ 57738032Speter if (e->e_dfino != -1) 57838032Speter { 57990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n", 58090792Sgshapiro (long) major(e->e_dfdev), 58190792Sgshapiro (long) minor(e->e_dfdev), 58290792Sgshapiro (ULONGLONG_T) e->e_dfino); 58338032Speter } 58438032Speter 58538032Speter /* output body type */ 58638032Speter if (e->e_bodytype != NULL) 58790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n", 58890792Sgshapiro denlstring(e->e_bodytype, true, false)); 58938032Speter 59090792Sgshapiro#if _FFR_QUARANTINE 59190792Sgshapiro /* quarantine reason */ 59290792Sgshapiro if (e->e_quarmsg != NULL) 59390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n", 59490792Sgshapiro denlstring(e->e_quarmsg, true, false)); 59590792Sgshapiro#endif /* _FFR_QUARANTINE */ 59638032Speter 59738032Speter /* message from envelope, if it exists */ 59838032Speter if (e->e_message != NULL) 59990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", 60090792Sgshapiro denlstring(e->e_message, true, false)); 60138032Speter 60238032Speter /* send various flag bits through */ 60338032Speter p = buf; 60438032Speter if (bitset(EF_WARNING, e->e_flags)) 60538032Speter *p++ = 'w'; 60638032Speter if (bitset(EF_RESPONSE, e->e_flags)) 60738032Speter *p++ = 'r'; 60838032Speter if (bitset(EF_HAS8BIT, e->e_flags)) 60938032Speter *p++ = '8'; 61038032Speter if (bitset(EF_DELETE_BCC, e->e_flags)) 61138032Speter *p++ = 'b'; 61238032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 61338032Speter *p++ = 'd'; 61438032Speter if (bitset(EF_NO_BODY_RETN, e->e_flags)) 61538032Speter *p++ = 'n'; 61690792Sgshapiro if (bitset(EF_SPLIT, e->e_flags)) 61790792Sgshapiro *p++ = 's'; 61838032Speter *p++ = '\0'; 61938032Speter if (buf[0] != '\0') 62090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf); 62138032Speter 62264562Sgshapiro /* save $={persistentMacros} macro values */ 62390792Sgshapiro queueup_macros(macid("{persistentMacros}"), tfp, e); 62438032Speter 62538032Speter /* output name of sender */ 62638032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 62738032Speter p = e->e_sender; 62838032Speter else 62938032Speter p = e->e_from.q_paddr; 63090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n", 63190792Sgshapiro denlstring(p, true, false)); 63238032Speter 63338032Speter /* output ESMTP-supplied "original" information */ 63438032Speter if (e->e_envid != NULL) 63590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n", 63690792Sgshapiro denlstring(e->e_envid, true, false)); 63738032Speter 63864562Sgshapiro /* output AUTH= parameter */ 63964562Sgshapiro if (e->e_auth_param != NULL) 64090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n", 64190792Sgshapiro denlstring(e->e_auth_param, true, false)); 64290792Sgshapiro if (e->e_dlvr_flag != 0) 64390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n", 64490792Sgshapiro (char) e->e_dlvr_flag, e->e_deliver_by); 64564562Sgshapiro 64638032Speter /* output list of recipient addresses */ 64738032Speter printctladdr(NULL, NULL); 64838032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 64938032Speter { 65064562Sgshapiro if (!QS_IS_UNDELIVERED(q->q_state)) 65138032Speter continue; 65264562Sgshapiro 65390792Sgshapiro /* message for this recipient, if it exists */ 65490792Sgshapiro if (q->q_message != NULL) 65590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", 65690792Sgshapiro denlstring(q->q_message, true, 65790792Sgshapiro false)); 65890792Sgshapiro 65938032Speter printctladdr(q, tfp); 66038032Speter if (q->q_orcpt != NULL) 66190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n", 66290792Sgshapiro denlstring(q->q_orcpt, true, 66390792Sgshapiro false)); 66490792Sgshapiro if (q->q_finalrcpt != NULL) 66590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n", 66690792Sgshapiro denlstring(q->q_finalrcpt, true, 66790792Sgshapiro false)); 66890792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R'); 66938032Speter if (bitset(QPRIMARY, q->q_flags)) 67090792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P'); 67138032Speter if (bitset(QHASNOTIFY, q->q_flags)) 67290792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N'); 67338032Speter if (bitset(QPINGONSUCCESS, q->q_flags)) 67490792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S'); 67538032Speter if (bitset(QPINGONFAILURE, q->q_flags)) 67690792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F'); 67738032Speter if (bitset(QPINGONDELAY, q->q_flags)) 67890792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D'); 67971345Sgshapiro if (q->q_alias != NULL && 68071345Sgshapiro bitset(QALIAS, q->q_alias->q_flags)) 68190792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A'); 68290792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':'); 68390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n", 68490792Sgshapiro denlstring(q->q_paddr, true, false)); 68538032Speter if (announce) 68638032Speter { 68790792Sgshapiro char *tag = "queued"; 68890792Sgshapiro 68990792Sgshapiro#if _FFR_QUARANTINE 69090792Sgshapiro if (e->e_quarmsg != NULL) 69190792Sgshapiro tag = "quarantined"; 69290792Sgshapiro#endif /* _FFR_QUARANTINE */ 69390792Sgshapiro 69438032Speter e->e_to = q->q_paddr; 69590792Sgshapiro message(tag); 69638032Speter if (LogLevel > 8) 69764562Sgshapiro logdelivery(q->q_mailer, NULL, q->q_status, 69890792Sgshapiro tag, NULL, (time_t) 0, e); 69938032Speter e->e_to = NULL; 70038032Speter } 70138032Speter if (tTd(40, 1)) 70238032Speter { 70390792Sgshapiro sm_dprintf("queueing "); 70490792Sgshapiro printaddr(q, false); 70538032Speter } 70638032Speter } 70738032Speter 70838032Speter /* 70938032Speter ** Output headers for this message. 71038032Speter ** Expand macros completely here. Queue run will deal with 71138032Speter ** everything as absolute headers. 71238032Speter ** All headers that must be relative to the recipient 71338032Speter ** can be cracked later. 71438032Speter ** We set up a "null mailer" -- i.e., a mailer that will have 71538032Speter ** no effect on the addresses as they are output. 71638032Speter */ 71738032Speter 71864562Sgshapiro memset((char *) &nullmailer, '\0', sizeof nullmailer); 71938032Speter nullmailer.m_re_rwset = nullmailer.m_rh_rwset = 72038032Speter nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; 72138032Speter nullmailer.m_eol = "\n"; 72264562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 72338032Speter mcibuf.mci_mailer = &nullmailer; 72438032Speter mcibuf.mci_out = tfp; 72538032Speter 72690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', "\201f"); 72738032Speter for (h = e->e_header; h != NULL; h = h->h_link) 72838032Speter { 72943730Speter if (h->h_value == NULL) 73038032Speter continue; 73138032Speter 73238032Speter /* don't output resent headers on non-resent messages */ 73364562Sgshapiro if (bitset(H_RESENT, h->h_flags) && 73464562Sgshapiro !bitset(EF_RESENT, e->e_flags)) 73538032Speter continue; 73638032Speter 73738032Speter /* expand macros; if null, don't output header at all */ 73838032Speter if (bitset(H_DEFAULT, h->h_flags)) 73938032Speter { 74038032Speter (void) expand(h->h_value, buf, sizeof buf, e); 74138032Speter if (buf[0] == '\0') 74238032Speter continue; 74338032Speter } 74438032Speter 74538032Speter /* output this header */ 74690792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?"); 74738032Speter 74864562Sgshapiro /* output conditional macro if present */ 74964562Sgshapiro if (h->h_macro != '\0') 75038032Speter { 75164562Sgshapiro if (bitset(0200, h->h_macro)) 75290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, 75390792Sgshapiro "${%s}", 75490792Sgshapiro macname(bitidx(h->h_macro))); 75564562Sgshapiro else 75690792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, 75790792Sgshapiro "$%c", h->h_macro); 75864562Sgshapiro } 75964562Sgshapiro else if (!bitzerop(h->h_mflags) && 76064562Sgshapiro bitset(H_CHECK|H_ACHECK, h->h_flags)) 76164562Sgshapiro { 76238032Speter int j; 76338032Speter 76464562Sgshapiro /* if conditional, output the set of conditions */ 76538032Speter for (j = '\0'; j <= '\177'; j++) 76638032Speter if (bitnset(j, h->h_mflags)) 76790792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 76890792Sgshapiro j); 76938032Speter } 77090792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?'); 77138032Speter 77238032Speter /* output the header: expand macros, convert addresses */ 77364562Sgshapiro if (bitset(H_DEFAULT, h->h_flags) && 77464562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 77538032Speter { 77690792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n", 77790792Sgshapiro h->h_field, 77890792Sgshapiro denlstring(buf, false, true)); 77938032Speter } 78064562Sgshapiro else if (bitset(H_FROM|H_RCPT, h->h_flags) && 78164562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 78238032Speter { 78338032Speter bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 78490792Sgshapiro SM_FILE_T *savetrace = TrafficLogFile; 78538032Speter 78638032Speter TrafficLogFile = NULL; 78738032Speter 78838032Speter if (bitset(H_FROM, h->h_flags)) 78990792Sgshapiro oldstyle = false; 79038032Speter 79138032Speter commaize(h, h->h_value, oldstyle, &mcibuf, e); 79238032Speter 79338032Speter TrafficLogFile = savetrace; 79438032Speter } 79538032Speter else 79638032Speter { 79790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n", 79890792Sgshapiro h->h_field, 79990792Sgshapiro denlstring(h->h_value, false, 80090792Sgshapiro true)); 80138032Speter } 80238032Speter } 80338032Speter 80438032Speter /* 80538032Speter ** Clean up. 80638032Speter ** 80738032Speter ** Write a terminator record -- this is to prevent 80838032Speter ** scurrilous crackers from appending any data. 80938032Speter */ 81038032Speter 81190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n"); 81238032Speter 81390792Sgshapiro if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 || 81490792Sgshapiro ((SuperSafe == SAFE_REALLY || 81590792Sgshapiro (SuperSafe == SAFE_INTERACTIVE && msync)) && 81690792Sgshapiro fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) || 81790792Sgshapiro sm_io_error(tfp)) 81838032Speter { 81938032Speter if (newid) 82038032Speter syserr("!552 Error writing control file %s", tf); 82138032Speter else 82238032Speter syserr("!452 Error writing control file %s", tf); 82338032Speter } 82438032Speter 82538032Speter if (!newid) 82638032Speter { 82790792Sgshapiro#if _FFR_QUARANTINE 82890792Sgshapiro char new = queue_letter(e, ANYQFL_LETTER); 82990792Sgshapiro#endif /* _FFR_QUARANTINE */ 83090792Sgshapiro 83190792Sgshapiro /* rename (locked) tf to be (locked) [qh]f */ 83290792Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), 83390792Sgshapiro sizeof qf); 83438032Speter if (rename(tf, qf) < 0) 83538032Speter syserr("cannot rename(%s, %s), uid=%d", 83698121Sgshapiro tf, qf, (int) geteuid()); 83790792Sgshapiro# if _FFR_QUARANTINE 83890792Sgshapiro else 83990792Sgshapiro { 84090792Sgshapiro /* 84190792Sgshapiro ** Check if type has changed and only 84290792Sgshapiro ** remove the old item if the rename above 84390792Sgshapiro ** succeeded. 84490792Sgshapiro */ 84590792Sgshapiro 84690792Sgshapiro if (e->e_qfletter != '\0' && 84790792Sgshapiro e->e_qfletter != new) 84890792Sgshapiro { 84990792Sgshapiro if (tTd(40, 5)) 85090792Sgshapiro { 85190792Sgshapiro sm_dprintf("type changed from %c to %c\n", 85290792Sgshapiro e->e_qfletter, new); 85390792Sgshapiro } 85490792Sgshapiro 85590792Sgshapiro if (unlink(queuename(e, e->e_qfletter)) < 0) 85690792Sgshapiro { 85790792Sgshapiro /* XXX: something more drastic? */ 85890792Sgshapiro if (LogLevel > 0) 85990792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 86090792Sgshapiro "queueup: unlink(%s) failed: %s", 86190792Sgshapiro queuename(e, e->e_qfletter), 86290792Sgshapiro sm_errstring(errno)); 86390792Sgshapiro } 86490792Sgshapiro } 86590792Sgshapiro } 86690792Sgshapiro e->e_qfletter = new; 86790792Sgshapiro# endif /* _FFR_QUARANTINE */ 86890792Sgshapiro 86964562Sgshapiro /* 87090792Sgshapiro ** fsync() after renaming to make sure metadata is 87190792Sgshapiro ** written to disk on filesystems in which renames are 87290792Sgshapiro ** not guaranteed. 87364562Sgshapiro */ 87464562Sgshapiro 87590792Sgshapiro if (SuperSafe != SAFE_NO) 87690792Sgshapiro { 87790792Sgshapiro /* for softupdates */ 87890792Sgshapiro if (tfd >= 0 && fsync(tfd) < 0) 87990792Sgshapiro { 88090792Sgshapiro syserr("!queueup: cannot fsync queue temp file %s", 88190792Sgshapiro tf); 88290792Sgshapiro } 88390792Sgshapiro SYNC_DIR(qf, true); 88490792Sgshapiro } 88564562Sgshapiro 88690792Sgshapiro /* close and unlock old (locked) queue file */ 88738032Speter if (e->e_lockfp != NULL) 88890792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 88938032Speter e->e_lockfp = tfp; 89090792Sgshapiro 89190792Sgshapiro /* save log info */ 89290792Sgshapiro if (LogLevel > 79) 89390792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf); 89438032Speter } 89538032Speter else 89690792Sgshapiro { 89790792Sgshapiro /* save log info */ 89890792Sgshapiro if (LogLevel > 79) 89990792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf); 90090792Sgshapiro 90190792Sgshapiro#if _FFR_QUARANTINE 90290792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 90390792Sgshapiro#endif /* _FFR_QUARANTINE */ 90490792Sgshapiro } 90590792Sgshapiro 90638032Speter errno = 0; 90738032Speter e->e_flags |= EF_INQUEUE; 90838032Speter 90938032Speter if (tTd(40, 1)) 91090792Sgshapiro sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id); 91138032Speter return; 91238032Speter} 91338032Speter 91490792Sgshapiro/* 91590792Sgshapiro** PRINTCTLADDR -- print control address to file. 91690792Sgshapiro** 91790792Sgshapiro** Parameters: 91890792Sgshapiro** a -- address. 91990792Sgshapiro** tfp -- file pointer. 92090792Sgshapiro** 92190792Sgshapiro** Returns: 92290792Sgshapiro** none. 92390792Sgshapiro** 92490792Sgshapiro** Side Effects: 92590792Sgshapiro** The control address (if changed) is printed to the file. 92690792Sgshapiro** The last control address and uid are saved. 92790792Sgshapiro*/ 92890792Sgshapiro 92964562Sgshapirostatic void 93038032Speterprintctladdr(a, tfp) 93138032Speter register ADDRESS *a; 93290792Sgshapiro SM_FILE_T *tfp; 93338032Speter{ 93464562Sgshapiro char *user; 93538032Speter register ADDRESS *q; 93638032Speter uid_t uid; 93738032Speter gid_t gid; 93838032Speter static ADDRESS *lastctladdr = NULL; 93938032Speter static uid_t lastuid; 94038032Speter 94138032Speter /* initialization */ 94238032Speter if (a == NULL || a->q_alias == NULL || tfp == NULL) 94338032Speter { 94438032Speter if (lastctladdr != NULL && tfp != NULL) 94590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n"); 94638032Speter lastctladdr = NULL; 94738032Speter lastuid = 0; 94838032Speter return; 94938032Speter } 95038032Speter 95138032Speter /* find the active uid */ 95238032Speter q = getctladdr(a); 95338032Speter if (q == NULL) 95438032Speter { 95564562Sgshapiro user = NULL; 95638032Speter uid = 0; 95738032Speter gid = 0; 95838032Speter } 95938032Speter else 96038032Speter { 96164562Sgshapiro user = q->q_ruser != NULL ? q->q_ruser : q->q_user; 96238032Speter uid = q->q_uid; 96338032Speter gid = q->q_gid; 96438032Speter } 96538032Speter a = a->q_alias; 96638032Speter 96738032Speter /* check to see if this is the same as last time */ 96838032Speter if (lastctladdr != NULL && uid == lastuid && 96938032Speter strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) 97038032Speter return; 97138032Speter lastuid = uid; 97238032Speter lastctladdr = a; 97338032Speter 97464562Sgshapiro if (uid == 0 || user == NULL || user[0] == '\0') 97590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C"); 97638032Speter else 97790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld", 97890792Sgshapiro denlstring(user, true, false), (long) uid, 97990792Sgshapiro (long) gid); 98090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n", 98190792Sgshapiro denlstring(a->q_paddr, true, false)); 98238032Speter} 98390792Sgshapiro 98490792Sgshapiro/* 98590792Sgshapiro** RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process 98690792Sgshapiro** 98790792Sgshapiro** This propagates the signal to the child processes that are queue 98890792Sgshapiro** runners. This is for a queue runner "cleanup". After all of the 98990792Sgshapiro** child queue runner processes are signaled (it should be SIGTERM 99090792Sgshapiro** being the sig) then the old signal handler (Oldsh) is called 99190792Sgshapiro** to handle any cleanup set for this process (provided it is not 99290792Sgshapiro** SIG_DFL or SIG_IGN). The signal may not be handled immediately 99390792Sgshapiro** if the BlockOldsh flag is set. If the current process doesn't 99490792Sgshapiro** have a parent then handle the signal immediately, regardless of 99590792Sgshapiro** BlockOldsh. 99690792Sgshapiro** 99790792Sgshapiro** Parameters: 99890792Sgshapiro** sig -- the signal number being sent 99990792Sgshapiro** 100090792Sgshapiro** Returns: 100190792Sgshapiro** none. 100290792Sgshapiro** 100390792Sgshapiro** Side Effects: 100490792Sgshapiro** Sets the NoMoreRunners boolean to true to stop more runners 100590792Sgshapiro** from being started in runqueue(). 100690792Sgshapiro** 100790792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 100890792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 100990792Sgshapiro** DOING. 101090792Sgshapiro*/ 101190792Sgshapiro 101290792Sgshapirostatic bool volatile NoMoreRunners = false; 101390792Sgshapirostatic sigfunc_t Oldsh_term = SIG_DFL; 101490792Sgshapirostatic sigfunc_t Oldsh_hup = SIG_DFL; 101590792Sgshapirostatic sigfunc_t volatile Oldsh = SIG_DFL; 101690792Sgshapirostatic bool BlockOldsh = false; 101790792Sgshapirostatic int volatile Oldsig = 0; 101890792Sgshapirostatic SIGFUNC_DECL runners_sigterm __P((int)); 101990792Sgshapirostatic SIGFUNC_DECL runners_sighup __P((int)); 102090792Sgshapiro 102190792Sgshapirostatic SIGFUNC_DECL 102290792Sgshapirorunners_sigterm(sig) 102390792Sgshapiro int sig; 102490792Sgshapiro{ 102590792Sgshapiro int save_errno = errno; 102690792Sgshapiro 102790792Sgshapiro FIX_SYSV_SIGNAL(sig, runners_sigterm); 102890792Sgshapiro errno = save_errno; 102990792Sgshapiro CHECK_CRITICAL(sig); 103090792Sgshapiro NoMoreRunners = true; 103190792Sgshapiro Oldsh = Oldsh_term; 103290792Sgshapiro Oldsig = sig; 103390792Sgshapiro proc_list_signal(PROC_QUEUE, sig); 103490792Sgshapiro 103590792Sgshapiro if (!BlockOldsh || getppid() <= 1) 103690792Sgshapiro { 103790792Sgshapiro /* Check that a valid 'old signal handler' is callable */ 103890792Sgshapiro if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN && 103990792Sgshapiro Oldsh_term != runners_sigterm) 104090792Sgshapiro (*Oldsh_term)(sig); 104190792Sgshapiro } 104290792Sgshapiro errno = save_errno; 104390792Sgshapiro return SIGFUNC_RETURN; 104490792Sgshapiro} 104590792Sgshapiro/* 104690792Sgshapiro** RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process 104790792Sgshapiro** 104890792Sgshapiro** This propagates the signal to the child processes that are queue 104990792Sgshapiro** runners. This is for a queue runner "cleanup". After all of the 105090792Sgshapiro** child queue runner processes are signaled (it should be SIGHUP 105190792Sgshapiro** being the sig) then the old signal handler (Oldsh) is called to 105290792Sgshapiro** handle any cleanup set for this process (provided it is not SIG_DFL 105390792Sgshapiro** or SIG_IGN). The signal may not be handled immediately if the 105490792Sgshapiro** BlockOldsh flag is set. If the current process doesn't have 105590792Sgshapiro** a parent then handle the signal immediately, regardless of 105690792Sgshapiro** BlockOldsh. 105790792Sgshapiro** 105890792Sgshapiro** Parameters: 105990792Sgshapiro** sig -- the signal number being sent 106090792Sgshapiro** 106190792Sgshapiro** Returns: 106290792Sgshapiro** none. 106390792Sgshapiro** 106490792Sgshapiro** Side Effects: 106590792Sgshapiro** Sets the NoMoreRunners boolean to true to stop more runners 106690792Sgshapiro** from being started in runqueue(). 106790792Sgshapiro** 106890792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 106990792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 107090792Sgshapiro** DOING. 107190792Sgshapiro*/ 107290792Sgshapiro 107390792Sgshapirostatic SIGFUNC_DECL 107490792Sgshapirorunners_sighup(sig) 107590792Sgshapiro int sig; 107690792Sgshapiro{ 107790792Sgshapiro int save_errno = errno; 107890792Sgshapiro 107990792Sgshapiro FIX_SYSV_SIGNAL(sig, runners_sighup); 108090792Sgshapiro errno = save_errno; 108190792Sgshapiro CHECK_CRITICAL(sig); 108290792Sgshapiro NoMoreRunners = true; 108390792Sgshapiro Oldsh = Oldsh_hup; 108490792Sgshapiro Oldsig = sig; 108590792Sgshapiro proc_list_signal(PROC_QUEUE, sig); 108690792Sgshapiro 108790792Sgshapiro if (!BlockOldsh || getppid() <= 1) 108890792Sgshapiro { 108990792Sgshapiro /* Check that a valid 'old signal handler' is callable */ 109090792Sgshapiro if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN && 109190792Sgshapiro Oldsh_hup != runners_sighup) 109290792Sgshapiro (*Oldsh_hup)(sig); 109390792Sgshapiro } 109490792Sgshapiro errno = save_errno; 109590792Sgshapiro return SIGFUNC_RETURN; 109690792Sgshapiro} 109790792Sgshapiro/* 109890792Sgshapiro** MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart 109990792Sgshapiro** 110090792Sgshapiro** Sets a workgroup for restarting. 110190792Sgshapiro** 110290792Sgshapiro** Parameters: 110390792Sgshapiro** wgrp -- the work group id to restart. 110490792Sgshapiro** reason -- why (signal?), -1 to turn off restart 110590792Sgshapiro** 110690792Sgshapiro** Returns: 110790792Sgshapiro** none. 110890792Sgshapiro** 110990792Sgshapiro** Side effects: 111090792Sgshapiro** May set global RestartWorkGroup to true. 111190792Sgshapiro** 111290792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 111390792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 111490792Sgshapiro** DOING. 111590792Sgshapiro*/ 111690792Sgshapiro 111790792Sgshapirovoid 111890792Sgshapiromark_work_group_restart(wgrp, reason) 111990792Sgshapiro int wgrp; 112090792Sgshapiro int reason; 112190792Sgshapiro{ 112290792Sgshapiro if (wgrp < 0 || wgrp > NumWorkGroups) 112390792Sgshapiro return; 112490792Sgshapiro 112590792Sgshapiro WorkGrp[wgrp].wg_restart = reason; 112690792Sgshapiro if (reason >= 0) 112790792Sgshapiro RestartWorkGroup = true; 112890792Sgshapiro} 112990792Sgshapiro/* 113090792Sgshapiro** RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart 113190792Sgshapiro** 113290792Sgshapiro** Restart any workgroup marked as needing a restart provided more 113390792Sgshapiro** runners are allowed. 113490792Sgshapiro** 113590792Sgshapiro** Parameters: 113690792Sgshapiro** none. 113790792Sgshapiro** 113890792Sgshapiro** Returns: 113990792Sgshapiro** none. 114090792Sgshapiro** 114190792Sgshapiro** Side effects: 114290792Sgshapiro** Sets global RestartWorkGroup to false. 114390792Sgshapiro*/ 114490792Sgshapiro 114590792Sgshapirovoid 114690792Sgshapirorestart_marked_work_groups() 114790792Sgshapiro{ 114890792Sgshapiro int i; 114990792Sgshapiro int wasblocked; 115090792Sgshapiro 115190792Sgshapiro if (NoMoreRunners) 115290792Sgshapiro return; 115390792Sgshapiro 115490792Sgshapiro /* Block SIGCHLD so reapchild() doesn't mess with us */ 115590792Sgshapiro wasblocked = sm_blocksignal(SIGCHLD); 115690792Sgshapiro 115790792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 115890792Sgshapiro { 115990792Sgshapiro if (WorkGrp[i].wg_restart >= 0) 116090792Sgshapiro { 116190792Sgshapiro if (LogLevel > 8) 116290792Sgshapiro sm_syslog(LOG_ERR, NOQID, 116390792Sgshapiro "restart queue runner=%d due to signal 0x%x", 116490792Sgshapiro i, WorkGrp[i].wg_restart); 116590792Sgshapiro restart_work_group(i); 116690792Sgshapiro } 116790792Sgshapiro } 116890792Sgshapiro RestartWorkGroup = false; 116990792Sgshapiro 117090792Sgshapiro if (wasblocked == 0) 117190792Sgshapiro (void) sm_releasesignal(SIGCHLD); 117290792Sgshapiro} 117390792Sgshapiro/* 117490792Sgshapiro** RESTART_WORK_GROUP -- restart a specific work group 117590792Sgshapiro** 117690792Sgshapiro** Restart a specific workgroup provided more runners are allowed. 117790792Sgshapiro** If the requested work group has been restarted too many times log 117890792Sgshapiro** this and refuse to restart. 117990792Sgshapiro** 118090792Sgshapiro** Parameters: 118190792Sgshapiro** wgrp -- the work group id to restart 118290792Sgshapiro** 118390792Sgshapiro** Returns: 118490792Sgshapiro** none. 118590792Sgshapiro** 118690792Sgshapiro** Side Effects: 118790792Sgshapiro** starts another process doing the work of wgrp 118890792Sgshapiro*/ 118990792Sgshapiro 119090792Sgshapiro#define MAX_PERSIST_RESTART 10 /* max allowed number of restarts */ 119190792Sgshapiro 119290792Sgshapirostatic void 119390792Sgshapirorestart_work_group(wgrp) 119490792Sgshapiro int wgrp; 119590792Sgshapiro{ 119690792Sgshapiro if (NoMoreRunners || 119790792Sgshapiro wgrp < 0 || wgrp > NumWorkGroups) 119890792Sgshapiro return; 119990792Sgshapiro 120090792Sgshapiro WorkGrp[wgrp].wg_restart = -1; 120190792Sgshapiro if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART) 120290792Sgshapiro { 120390792Sgshapiro /* avoid overflow; increment here */ 120490792Sgshapiro WorkGrp[wgrp].wg_restartcnt++; 1205110560Sgshapiro (void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL); 120690792Sgshapiro } 120790792Sgshapiro else 120890792Sgshapiro { 120990792Sgshapiro sm_syslog(LOG_ERR, NOQID, 121090792Sgshapiro "ERROR: persistent queue runner=%d restarted too many times, queue runner lost", 121190792Sgshapiro wgrp); 121290792Sgshapiro } 121390792Sgshapiro} 121490792Sgshapiro/* 121590792Sgshapiro** SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group. 121690792Sgshapiro** 121790792Sgshapiro** Parameters: 121890792Sgshapiro** runall -- schedule even if individual bit is not set. 121990792Sgshapiro** wgrp -- the work group id to schedule. 122094334Sgshapiro** didit -- the queue run was performed for this work group. 122190792Sgshapiro** 122290792Sgshapiro** Returns: 122390792Sgshapiro** nothing 122490792Sgshapiro*/ 122590792Sgshapiro 122690792Sgshapiro#define INCR_MOD(v, m) if (++v >= m) \ 122790792Sgshapiro v = 0; \ 122890792Sgshapiro else 122990792Sgshapiro 123090792Sgshapirostatic void 123194334Sgshapiroschedule_queue_runs(runall, wgrp, didit) 123290792Sgshapiro bool runall; 123390792Sgshapiro int wgrp; 123494334Sgshapiro bool didit; 123590792Sgshapiro{ 123690792Sgshapiro int qgrp, cgrp, endgrp; 123794334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 123894334Sgshapiro time_t lastsched; 123994334Sgshapiro bool sched; 124094334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 124194334Sgshapiro time_t now; 124294334Sgshapiro time_t minqintvl; 124390792Sgshapiro 124490792Sgshapiro /* 124590792Sgshapiro ** This is a bit ugly since we have to duplicate the 124690792Sgshapiro ** code that "walks" through a work queue group. 124790792Sgshapiro */ 124890792Sgshapiro 124994334Sgshapiro now = curtime(); 125094334Sgshapiro minqintvl = 0; 125190792Sgshapiro cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp; 125290792Sgshapiro do 125390792Sgshapiro { 125490792Sgshapiro time_t qintvl; 125590792Sgshapiro 125694334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 125794334Sgshapiro lastsched = 0; 125894334Sgshapiro sched = false; 125994334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 126090792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index; 126190792Sgshapiro if (Queue[qgrp]->qg_queueintvl > 0) 126290792Sgshapiro qintvl = Queue[qgrp]->qg_queueintvl; 126390792Sgshapiro else if (QueueIntvl > 0) 126490792Sgshapiro qintvl = QueueIntvl; 126590792Sgshapiro else 126690792Sgshapiro qintvl = (time_t) 0; 126790792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 126894334Sgshapiro lastsched = Queue[qgrp]->qg_nextrun; 126994334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 127094334Sgshapiro if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0) 127194334Sgshapiro { 127294334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 127394334Sgshapiro sched = true; 127494334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 127594334Sgshapiro if (minqintvl == 0 || qintvl < minqintvl) 127694334Sgshapiro minqintvl = qintvl; 127794334Sgshapiro 127894334Sgshapiro /* 127994334Sgshapiro ** Only set a new time if a queue run was performed 128094334Sgshapiro ** for this queue group. If the queue was not run, 128194334Sgshapiro ** we could starve it by setting a new time on each 128294334Sgshapiro ** call. 128394334Sgshapiro */ 128494334Sgshapiro 128594334Sgshapiro if (didit) 128694334Sgshapiro Queue[qgrp]->qg_nextrun += qintvl; 128794334Sgshapiro } 128894334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 128990792Sgshapiro if (tTd(69, 10)) 129090792Sgshapiro sm_syslog(LOG_INFO, NOQID, 129194334Sgshapiro "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d", 129290792Sgshapiro wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl, 129394334Sgshapiro QueueIntvl, runall, lastsched, 129494334Sgshapiro Queue[qgrp]->qg_nextrun, sched); 129590792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 129690792Sgshapiro INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp); 129790792Sgshapiro } while (endgrp != cgrp); 129894334Sgshapiro if (minqintvl > 0) 129994334Sgshapiro (void) sm_setevent(minqintvl, runqueueevent, 0); 130090792Sgshapiro} 130194334Sgshapiro 130294334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA 130390792Sgshapiro/* 130494334Sgshapiro** CHECKQUEUERUNNER -- check whether a queue group hasn't been run. 130594334Sgshapiro** 130694334Sgshapiro** Use this if events may get lost and hence queue runners may not 130794334Sgshapiro** be started and mail will pile up in a queue. 130894334Sgshapiro** 130994334Sgshapiro** Parameters: 131094334Sgshapiro** none. 131194334Sgshapiro** 131294334Sgshapiro** Returns: 131394334Sgshapiro** true if a queue run is necessary. 131494334Sgshapiro** 131594334Sgshapiro** Side Effects: 131694334Sgshapiro** may schedule a queue run. 131794334Sgshapiro*/ 131894334Sgshapiro 131994334Sgshapirobool 132094334Sgshapirocheckqueuerunner() 132194334Sgshapiro{ 132294334Sgshapiro int qgrp; 132394334Sgshapiro time_t now, minqintvl; 132494334Sgshapiro 132594334Sgshapiro now = curtime(); 132694334Sgshapiro minqintvl = 0; 132794334Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 132894334Sgshapiro { 132994334Sgshapiro time_t qintvl; 133094334Sgshapiro 133194334Sgshapiro if (Queue[qgrp]->qg_queueintvl > 0) 133294334Sgshapiro qintvl = Queue[qgrp]->qg_queueintvl; 133394334Sgshapiro else if (QueueIntvl > 0) 133494334Sgshapiro qintvl = QueueIntvl; 133594334Sgshapiro else 133694334Sgshapiro qintvl = (time_t) 0; 133794334Sgshapiro if (Queue[qgrp]->qg_nextrun <= now - qintvl) 133894334Sgshapiro { 133994334Sgshapiro if (minqintvl == 0 || qintvl < minqintvl) 134094334Sgshapiro minqintvl = qintvl; 134194334Sgshapiro if (LogLevel > 1) 134294334Sgshapiro sm_syslog(LOG_WARNING, NOQID, 134394334Sgshapiro "checkqueuerunner: queue %d should have been run at %s, queue interval %ld", 134494334Sgshapiro qgrp, 134594334Sgshapiro arpadate(ctime(&Queue[qgrp]->qg_nextrun)), 134694334Sgshapiro qintvl); 134794334Sgshapiro } 134894334Sgshapiro } 134994334Sgshapiro if (minqintvl > 0) 135094334Sgshapiro { 135194334Sgshapiro (void) sm_setevent(minqintvl, runqueueevent, 0); 135294334Sgshapiro return true; 135394334Sgshapiro } 135494334Sgshapiro return false; 135594334Sgshapiro} 135694334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */ 135794334Sgshapiro 135894334Sgshapiro/* 135938032Speter** RUNQUEUE -- run the jobs in the queue. 136038032Speter** 136138032Speter** Gets the stuff out of the queue in some presumably logical 136238032Speter** order and processes them. 136338032Speter** 136438032Speter** Parameters: 136590792Sgshapiro** forkflag -- true if the queue scanning should be done in 136638032Speter** a child process. We double-fork so it is not our 136738032Speter** child and we don't have to clean up after it. 136890792Sgshapiro** false can be ignored if we have multiple queues. 136990792Sgshapiro** verbose -- if true, print out status information. 137090792Sgshapiro** persistent -- persistent queue runner? 137190792Sgshapiro** runall -- run all groups or only a subset (DoQueueRun)? 137238032Speter** 137338032Speter** Returns: 137490792Sgshapiro** true if the queue run successfully began. 137538032Speter** 137638032Speter** Side Effects: 137790792Sgshapiro** runs things in the mail queue using run_work_group(). 137890792Sgshapiro** maybe schedules next queue run. 137938032Speter*/ 138038032Speter 138164562Sgshapirostatic ENVELOPE QueueEnvelope; /* the queue run envelope */ 138264562Sgshapirostatic time_t LastQueueTime = 0; /* last time a queue ID assigned */ 138364562Sgshapirostatic pid_t LastQueuePid = -1; /* last PID which had a queue ID */ 138438032Speter 138564562Sgshapiro/* values for qp_supdirs */ 138664562Sgshapiro#define QP_NOSUB 0x0000 /* No subdirectories */ 138764562Sgshapiro#define QP_SUBDF 0x0001 /* "df" subdirectory */ 138864562Sgshapiro#define QP_SUBQF 0x0002 /* "qf" subdirectory */ 138964562Sgshapiro#define QP_SUBXF 0x0004 /* "xf" subdirectory */ 139064562Sgshapiro 139138032Speterbool 139290792Sgshapirorunqueue(forkflag, verbose, persistent, runall) 139338032Speter bool forkflag; 139438032Speter bool verbose; 139590792Sgshapiro bool persistent; 139690792Sgshapiro bool runall; 139738032Speter{ 139864562Sgshapiro int i; 139990792Sgshapiro bool ret = true; 140064562Sgshapiro static int curnum = 0; 140190792Sgshapiro sigfunc_t cursh; 140290792Sgshapiro#if SM_HEAP_CHECK 140390792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 140464562Sgshapiro 140590792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 140690792Sgshapiro { 140790792Sgshapiro oldgroup = sm_heap_group(); 140890792Sgshapiro sm_heap_newgroup(); 140990792Sgshapiro sm_dprintf("runqueue() heap group #%d\n", sm_heap_group()); 141090792Sgshapiro } 141190792Sgshapiro#endif /* SM_HEAP_CHECK */ 141271345Sgshapiro 141390792Sgshapiro /* queue run has been started, don't do any more this time */ 141494334Sgshapiro DoQueueRun = false; 141571345Sgshapiro 141690792Sgshapiro /* more than one queue or more than one directory per queue */ 141790792Sgshapiro if (!forkflag && !verbose && 141890792Sgshapiro (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 || 141990792Sgshapiro WorkGrp[0].wg_numqgrp > 1)) 142090792Sgshapiro forkflag = true; 142164562Sgshapiro 142290792Sgshapiro /* 142390792Sgshapiro ** For controlling queue runners via signals sent to this process. 142490792Sgshapiro ** Oldsh* will get called too by runners_sig* (if it is not SIG_IGN 142590792Sgshapiro ** or SIG_DFL) to preserve cleanup behavior. Now that this process 142690792Sgshapiro ** will have children (and perhaps grandchildren) this handler will 142790792Sgshapiro ** be left in place. This is because this process, once it has 142890792Sgshapiro ** finished spinning off queue runners, may go back to doing something 142990792Sgshapiro ** else (like being a daemon). And we still want on a SIG{TERM,HUP} to 143090792Sgshapiro ** clean up the child queue runners. Only install 'runners_sig*' once 143190792Sgshapiro ** else we'll get stuck looping forever. 143290792Sgshapiro */ 143390792Sgshapiro 143490792Sgshapiro cursh = sm_signal(SIGTERM, runners_sigterm); 143590792Sgshapiro if (cursh != runners_sigterm) 143690792Sgshapiro Oldsh_term = cursh; 143790792Sgshapiro cursh = sm_signal(SIGHUP, runners_sighup); 143890792Sgshapiro if (cursh != runners_sighup) 143990792Sgshapiro Oldsh_hup = cursh; 144090792Sgshapiro 144190792Sgshapiro for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++) 144264562Sgshapiro { 1443110560Sgshapiro int rwgflags = RWG_NONE; 1444110560Sgshapiro 144564562Sgshapiro /* 144690792Sgshapiro ** If MaxQueueChildren active then test whether the start 144790792Sgshapiro ** of the next queue group's additional queue runners (maximum) 144890792Sgshapiro ** will result in MaxQueueChildren being exceeded. 144990792Sgshapiro ** 145090792Sgshapiro ** Note: do not use continue; even though another workgroup 145190792Sgshapiro ** may have fewer queue runners, this would be "unfair", 145290792Sgshapiro ** i.e., this work group might "starve" then. 145364562Sgshapiro */ 145464562Sgshapiro 145590792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 145690792Sgshapiro if (tTd(69, 10)) 145790792Sgshapiro sm_syslog(LOG_INFO, NOQID, 145890792Sgshapiro "rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d", 145990792Sgshapiro curnum, MaxQueueChildren, CurRunners, 146090792Sgshapiro WorkGrp[curnum].wg_maxact); 146190792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 146290792Sgshapiro if (MaxQueueChildren > 0 && 146390792Sgshapiro CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren) 146490792Sgshapiro break; 146564562Sgshapiro 146664562Sgshapiro /* 146790792Sgshapiro ** Pick up where we left off (curnum), in case we 146890792Sgshapiro ** used up all the children last time without finishing. 146990792Sgshapiro ** This give a round-robin fairness to queue runs. 1470102528Sgshapiro ** 1471102528Sgshapiro ** Increment CurRunners before calling run_work_group() 1472102528Sgshapiro ** to avoid a "race condition" with proc_list_drop() which 1473102528Sgshapiro ** decrements CurRunners if the queue runners terminate. 1474102528Sgshapiro ** This actually doesn't cause any harm, but CurRunners 1475102528Sgshapiro ** might become negative which is at least confusing. 1476102528Sgshapiro ** 1477102528Sgshapiro ** Notice: CurRunners is an upper limit, in some cases 1478102528Sgshapiro ** (too few jobs in the queue) this value is larger than 1479102528Sgshapiro ** the actual number of queue runners. The discrepancy can 1480102528Sgshapiro ** increase if some queue runners "hang" for a long time. 148190792Sgshapiro */ 148290792Sgshapiro 1483102528Sgshapiro CurRunners += WorkGrp[curnum].wg_maxact; 1484110560Sgshapiro if (forkflag) 1485110560Sgshapiro rwgflags |= RWG_FORK; 1486110560Sgshapiro if (verbose) 1487110560Sgshapiro rwgflags |= RWG_VERBOSE; 1488110560Sgshapiro if (persistent) 1489110560Sgshapiro rwgflags |= RWG_PERSISTENT; 1490110560Sgshapiro if (runall) 1491110560Sgshapiro rwgflags |= RWG_RUNALL; 1492110560Sgshapiro ret = run_work_group(curnum, rwgflags); 149390792Sgshapiro 149490792Sgshapiro /* 149564562Sgshapiro ** Failure means a message was printed for ETRN 149664562Sgshapiro ** and subsequent queues are likely to fail as well. 1497102528Sgshapiro ** Decrement CurRunners in that case because 1498102528Sgshapiro ** none have been started. 149964562Sgshapiro */ 150064562Sgshapiro 150164562Sgshapiro if (!ret) 1502102528Sgshapiro { 1503102528Sgshapiro CurRunners -= WorkGrp[curnum].wg_maxact; 150464562Sgshapiro break; 1505102528Sgshapiro } 150664562Sgshapiro 150790792Sgshapiro if (!persistent) 150894334Sgshapiro schedule_queue_runs(runall, curnum, true); 150990792Sgshapiro INCR_MOD(curnum, NumWorkGroups); 151064562Sgshapiro } 151190792Sgshapiro 151290792Sgshapiro /* schedule left over queue runs */ 151390792Sgshapiro if (i < NumWorkGroups && !NoMoreRunners && !persistent) 151490792Sgshapiro { 151590792Sgshapiro int h; 151690792Sgshapiro 151790792Sgshapiro for (h = curnum; i < NumWorkGroups; i++) 151890792Sgshapiro { 151994334Sgshapiro schedule_queue_runs(runall, h, false); 152090792Sgshapiro INCR_MOD(h, NumWorkGroups); 152190792Sgshapiro } 152290792Sgshapiro } 152390792Sgshapiro 152490792Sgshapiro 152590792Sgshapiro#if SM_HEAP_CHECK 152690792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 152790792Sgshapiro sm_heap_setgroup(oldgroup); 152890792Sgshapiro#endif /* SM_HEAP_CHECK */ 152964562Sgshapiro return ret; 153064562Sgshapiro} 153190792Sgshapiro/* 153290792Sgshapiro** RUNNER_WORK -- have a queue runner do its work 153364562Sgshapiro** 153490792Sgshapiro** Have a queue runner do its work a list of entries. 153590792Sgshapiro** When work isn't directly being done then this process can take a signal 153690792Sgshapiro** and terminate immediately (in a clean fashion of course). 153790792Sgshapiro** When work is directly being done, it's not to be interrupted 153890792Sgshapiro** immediately: the work should be allowed to finish at a clean point 153990792Sgshapiro** before termination (in a clean fashion of course). 154090792Sgshapiro** 154190792Sgshapiro** Parameters: 154290792Sgshapiro** e -- envelope. 154390792Sgshapiro** sequenceno -- 'th process to run WorkQ. 154490792Sgshapiro** didfork -- did the calling process fork()? 154590792Sgshapiro** skip -- process only each skip'th item. 154690792Sgshapiro** njobs -- number of jobs in WorkQ. 154790792Sgshapiro** 154890792Sgshapiro** Returns: 154990792Sgshapiro** none. 155090792Sgshapiro** 155190792Sgshapiro** Side Effects: 155290792Sgshapiro** runs things in the mail queue. 155390792Sgshapiro*/ 155490792Sgshapiro 155590792Sgshapiro/* Get new load average every 30 seconds. */ 155690792Sgshapiro#define GET_NEW_LA_TIME 30 155790792Sgshapiro 155890792Sgshapirostatic void 155990792Sgshapirorunner_work(e, sequenceno, didfork, skip, njobs) 156090792Sgshapiro register ENVELOPE *e; 156190792Sgshapiro int sequenceno; 156290792Sgshapiro bool didfork; 156390792Sgshapiro int skip; 156490792Sgshapiro int njobs; 156590792Sgshapiro{ 156690792Sgshapiro int n; 156790792Sgshapiro WORK *w; 156890792Sgshapiro time_t current_la_time, now; 156990792Sgshapiro 157090792Sgshapiro current_la_time = curtime(); 157190792Sgshapiro 157290792Sgshapiro /* 157390792Sgshapiro ** Here we temporarily block the second calling of the handlers. 157490792Sgshapiro ** This allows us to handle the signal without terminating in the 157590792Sgshapiro ** middle of direct work. If a signal does come, the test for 157690792Sgshapiro ** NoMoreRunners will find it. 157790792Sgshapiro */ 157890792Sgshapiro 157990792Sgshapiro BlockOldsh = true; 158090792Sgshapiro 158190792Sgshapiro /* process them once at a time */ 158290792Sgshapiro while (WorkQ != NULL) 158390792Sgshapiro { 158490792Sgshapiro#if SM_HEAP_CHECK 158590792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 158690792Sgshapiro 158790792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 158890792Sgshapiro { 158990792Sgshapiro oldgroup = sm_heap_group(); 159090792Sgshapiro sm_heap_newgroup(); 159190792Sgshapiro sm_dprintf("run_queue_group() heap group #%d\n", 159290792Sgshapiro sm_heap_group()); 159390792Sgshapiro } 159490792Sgshapiro#endif /* SM_HEAP_CHECK */ 159590792Sgshapiro 159690792Sgshapiro /* do no more work */ 159790792Sgshapiro if (NoMoreRunners) 159890792Sgshapiro { 159990792Sgshapiro /* Check that a valid signal handler is callable */ 160090792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 160190792Sgshapiro Oldsh != runners_sighup && 160290792Sgshapiro Oldsh != runners_sigterm) 160390792Sgshapiro (*Oldsh)(Oldsig); 160490792Sgshapiro break; 160590792Sgshapiro } 160690792Sgshapiro 160790792Sgshapiro w = WorkQ; /* assign current work item */ 160890792Sgshapiro 160990792Sgshapiro /* 161090792Sgshapiro ** Set the head of the WorkQ to the next work item. 161190792Sgshapiro ** It is set 'skip' ahead (the number of parallel queue 161290792Sgshapiro ** runners working on WorkQ together) since each runner 161390792Sgshapiro ** works on every 'skip'th (N-th) item. 161490792Sgshapiro */ 161590792Sgshapiro 161690792Sgshapiro for (n = 0; n < skip && WorkQ != NULL; n++) 161790792Sgshapiro WorkQ = WorkQ->w_next; 161890792Sgshapiro e->e_to = NULL; 161990792Sgshapiro 162090792Sgshapiro /* 162190792Sgshapiro ** Ignore jobs that are too expensive for the moment. 162290792Sgshapiro ** 162390792Sgshapiro ** Get new load average every GET_NEW_LA_TIME seconds. 162490792Sgshapiro */ 162590792Sgshapiro 162690792Sgshapiro now = curtime(); 162790792Sgshapiro if (current_la_time < now - GET_NEW_LA_TIME) 162890792Sgshapiro { 162990792Sgshapiro sm_getla(); 163090792Sgshapiro current_la_time = now; 163190792Sgshapiro } 163290792Sgshapiro if (shouldqueue(WkRecipFact, current_la_time)) 163390792Sgshapiro { 163490792Sgshapiro char *msg = "Aborting queue run: load average too high"; 163590792Sgshapiro 163690792Sgshapiro if (Verbose) 163790792Sgshapiro message("%s", msg); 163890792Sgshapiro if (LogLevel > 8) 163990792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 164090792Sgshapiro break; 164190792Sgshapiro } 164290792Sgshapiro if (shouldqueue(w->w_pri, w->w_ctime)) 164390792Sgshapiro { 164490792Sgshapiro if (Verbose) 164590792Sgshapiro message(EmptyString); 164690792Sgshapiro if (QueueSortOrder == QSO_BYPRIORITY) 164790792Sgshapiro { 164890792Sgshapiro if (Verbose) 164990792Sgshapiro message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", 165090792Sgshapiro qid_printqueue(w->w_qgrp, 165190792Sgshapiro w->w_qdir), 165290792Sgshapiro w->w_name + 2, sequenceno, 165390792Sgshapiro njobs); 165490792Sgshapiro if (LogLevel > 8) 165590792Sgshapiro sm_syslog(LOG_INFO, NOQID, 165690792Sgshapiro "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", 165790792Sgshapiro qid_printqueue(w->w_qgrp, 165890792Sgshapiro w->w_qdir), 165990792Sgshapiro w->w_name + 2, w->w_pri, 166090792Sgshapiro CurrentLA, sequenceno, 166190792Sgshapiro njobs); 166290792Sgshapiro break; 166390792Sgshapiro } 166490792Sgshapiro else if (Verbose) 166590792Sgshapiro message("Skipping %s/%s (sequence %d of %d)", 166690792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 166790792Sgshapiro w->w_name + 2, sequenceno, njobs); 166890792Sgshapiro } 166990792Sgshapiro else 167090792Sgshapiro { 167190792Sgshapiro if (Verbose) 167290792Sgshapiro { 167390792Sgshapiro message(EmptyString); 167490792Sgshapiro message("Running %s/%s (sequence %d of %d)", 167590792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 167690792Sgshapiro w->w_name + 2, sequenceno, njobs); 167790792Sgshapiro } 167890792Sgshapiro if (didfork && MaxQueueChildren > 0) 167990792Sgshapiro { 168090792Sgshapiro sm_blocksignal(SIGCHLD); 168190792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 168290792Sgshapiro } 168390792Sgshapiro if (tTd(63, 100)) 168490792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 168590792Sgshapiro "runqueue %s dowork(%s)", 168690792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 168790792Sgshapiro w->w_name + 2); 168890792Sgshapiro 168990792Sgshapiro (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2, 1690111823Sgshapiro ForkQueueRuns, false, e); 169190792Sgshapiro errno = 0; 169290792Sgshapiro } 169390792Sgshapiro sm_free(w->w_name); /* XXX */ 169490792Sgshapiro if (w->w_host != NULL) 169590792Sgshapiro sm_free(w->w_host); /* XXX */ 169690792Sgshapiro sm_free((char *) w); /* XXX */ 169790792Sgshapiro sequenceno += skip; /* next sequence number */ 169890792Sgshapiro#if SM_HEAP_CHECK 169990792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 170090792Sgshapiro sm_heap_setgroup(oldgroup); 170190792Sgshapiro#endif /* SM_HEAP_CHECK */ 170290792Sgshapiro } 170390792Sgshapiro 170490792Sgshapiro BlockOldsh = false; 170590792Sgshapiro 170690792Sgshapiro /* check the signals didn't happen during the revert */ 170790792Sgshapiro if (NoMoreRunners) 170890792Sgshapiro { 170990792Sgshapiro /* Check that a valid signal handler is callable */ 171090792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 171190792Sgshapiro Oldsh != runners_sighup && Oldsh != runners_sigterm) 171290792Sgshapiro (*Oldsh)(Oldsig); 171390792Sgshapiro } 171490792Sgshapiro 171590792Sgshapiro Oldsh = SIG_DFL; /* after the NoMoreRunners check */ 171690792Sgshapiro} 171790792Sgshapiro/* 171890792Sgshapiro** RUN_WORK_GROUP -- run the jobs in a queue group from a work group. 171990792Sgshapiro** 172064562Sgshapiro** Gets the stuff out of the queue in some presumably logical 172164562Sgshapiro** order and processes them. 172264562Sgshapiro** 172364562Sgshapiro** Parameters: 172490792Sgshapiro** wgrp -- work group to process. 1725110560Sgshapiro** flags -- RWG_* flags 172664562Sgshapiro** 172764562Sgshapiro** Returns: 172890792Sgshapiro** true if the queue run successfully began. 172964562Sgshapiro** 173064562Sgshapiro** Side Effects: 173164562Sgshapiro** runs things in the mail queue. 173264562Sgshapiro*/ 173364562Sgshapiro 173490792Sgshapiro/* Minimum sleep time for persistent queue runners */ 173590792Sgshapiro#define MIN_SLEEP_TIME 5 173690792Sgshapiro 173790792Sgshapirobool 1738110560Sgshapirorun_work_group(wgrp, flags) 173990792Sgshapiro int wgrp; 1740110560Sgshapiro int flags; 174164562Sgshapiro{ 174238032Speter register ENVELOPE *e; 174390792Sgshapiro int njobs, qdir; 174490792Sgshapiro int sequenceno = 1; 174590792Sgshapiro int qgrp, endgrp, h, i; 174694334Sgshapiro time_t current_la_time, now; 174790792Sgshapiro bool full, more; 174890792Sgshapiro SM_RPOOL_T *rpool; 174990792Sgshapiro extern void rmexpstab __P((void)); 175038032Speter extern ENVELOPE BlankEnvelope; 175190792Sgshapiro extern SIGFUNC_DECL reapchild __P((int)); 175238032Speter 175390792Sgshapiro if (wgrp < 0) 175490792Sgshapiro return false; 175590792Sgshapiro 175638032Speter /* 175738032Speter ** If no work will ever be selected, don't even bother reading 175838032Speter ** the queue. 175938032Speter */ 176038032Speter 176190792Sgshapiro sm_getla(); /* get load average */ 176238032Speter current_la_time = curtime(); 176338032Speter 1764110560Sgshapiro if (!bitset(RWG_PERSISTENT, flags) && 1765110560Sgshapiro shouldqueue(WkRecipFact, current_la_time)) 176638032Speter { 176738032Speter char *msg = "Skipping queue run -- load average too high"; 176838032Speter 1769110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 177038032Speter message("458 %s\n", msg); 177138032Speter if (LogLevel > 8) 177290792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 177390792Sgshapiro return false; 177438032Speter } 177538032Speter 177638032Speter /* 177738032Speter ** See if we already have too many children. 177838032Speter */ 177938032Speter 1780110560Sgshapiro if (bitset(RWG_FORK, flags) && 1781110560Sgshapiro WorkGrp[wgrp].wg_lowqintvl > 0 && 1782110560Sgshapiro !bitset(RWG_PERSISTENT, flags) && 178338032Speter MaxChildren > 0 && CurChildren >= MaxChildren) 178438032Speter { 178564562Sgshapiro char *msg = "Skipping queue run -- too many children"; 178664562Sgshapiro 1787110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 178864562Sgshapiro message("458 %s (%d)\n", msg, CurChildren); 178964562Sgshapiro if (LogLevel > 8) 179090792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)", 179164562Sgshapiro msg, CurChildren); 179290792Sgshapiro return false; 179338032Speter } 179438032Speter 179538032Speter /* 179638032Speter ** See if we want to go off and do other useful work. 179738032Speter */ 179838032Speter 1799110560Sgshapiro if (bitset(RWG_FORK, flags)) 180038032Speter { 180138032Speter pid_t pid; 180238032Speter 180390792Sgshapiro (void) sm_blocksignal(SIGCHLD); 180490792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 180538032Speter 180638032Speter pid = dofork(); 180738032Speter if (pid == -1) 180838032Speter { 180938032Speter const char *msg = "Skipping queue run -- fork() failed"; 181090792Sgshapiro const char *err = sm_errstring(errno); 181138032Speter 1812110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 181338032Speter message("458 %s: %s\n", msg, err); 181438032Speter if (LogLevel > 8) 181590792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s", 181664562Sgshapiro msg, err); 181790792Sgshapiro (void) sm_releasesignal(SIGCHLD); 181890792Sgshapiro return false; 181938032Speter } 182038032Speter if (pid != 0) 182138032Speter { 182238032Speter /* parent -- pick up intermediate zombie */ 182390792Sgshapiro (void) sm_blocksignal(SIGALRM); 182490792Sgshapiro 182590792Sgshapiro /* wgrp only used when queue runners are persistent */ 182690792Sgshapiro proc_list_add(pid, "Queue runner", PROC_QUEUE, 182790792Sgshapiro WorkGrp[wgrp].wg_maxact, 1828110560Sgshapiro bitset(RWG_PERSISTENT, flags) ? wgrp : -1); 182990792Sgshapiro (void) sm_releasesignal(SIGALRM); 183090792Sgshapiro (void) sm_releasesignal(SIGCHLD); 183190792Sgshapiro return true; 183238032Speter } 183390792Sgshapiro 183464562Sgshapiro /* child -- clean up signals */ 183577349Sgshapiro 183677349Sgshapiro /* Reset global flags */ 183777349Sgshapiro RestartRequest = NULL; 183890792Sgshapiro RestartWorkGroup = false; 183977349Sgshapiro ShutdownRequest = NULL; 184077349Sgshapiro PendingSignal = 0; 184190792Sgshapiro CurrentPid = getpid(); 184277349Sgshapiro 184390792Sgshapiro /* 184490792Sgshapiro ** Initialize exception stack and default exception 184590792Sgshapiro ** handler for child process. 184690792Sgshapiro */ 184790792Sgshapiro 184890792Sgshapiro sm_exc_newthread(fatal_error); 184942575Speter clrcontrol(); 185038032Speter proc_list_clear(); 185142575Speter 185242575Speter /* Add parent process as first child item */ 185390792Sgshapiro proc_list_add(CurrentPid, "Queue runner child process", 185490792Sgshapiro PROC_QUEUE_CHILD, 0, -1); 185590792Sgshapiro (void) sm_releasesignal(SIGCHLD); 185690792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 185790792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 185890792Sgshapiro (void) sm_signal(SIGTERM, intsig); 185938032Speter } 186038032Speter 186138032Speter /* 186238032Speter ** Release any resources used by the daemon code. 186338032Speter */ 186438032Speter 186538032Speter clrdaemon(); 186638032Speter 186738032Speter /* force it to run expensive jobs */ 186890792Sgshapiro NoConnect = false; 186938032Speter 187038032Speter /* drop privileges */ 187138032Speter if (geteuid() == (uid_t) 0) 187290792Sgshapiro (void) drop_privileges(false); 187338032Speter 187438032Speter /* 187538032Speter ** Create ourselves an envelope 187638032Speter */ 187738032Speter 187838032Speter CurEnv = &QueueEnvelope; 187990792Sgshapiro rpool = sm_rpool_new_x(NULL); 188090792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 188138032Speter e->e_flags = BlankEnvelope.e_flags; 188273188Sgshapiro e->e_parent = NULL; 188338032Speter 188438032Speter /* make sure we have disconnected from parent */ 1885110560Sgshapiro if (bitset(RWG_FORK, flags)) 188638032Speter { 188738032Speter disconnect(1, e); 188890792Sgshapiro QuickAbort = false; 188938032Speter } 189038032Speter 189138032Speter /* 189238032Speter ** If we are running part of the queue, always ignore stored 189338032Speter ** host status. 189438032Speter */ 189538032Speter 189638032Speter if (QueueLimitId != NULL || QueueLimitSender != NULL || 189790792Sgshapiro#if _FFR_QUARANTINE 189890792Sgshapiro QueueLimitQuarantine != NULL || 189990792Sgshapiro#endif /* _FFR_QUARANTINE */ 190038032Speter QueueLimitRecipient != NULL) 190138032Speter { 190290792Sgshapiro IgnoreHostStatus = true; 190338032Speter MinQueueAge = 0; 190438032Speter } 190538032Speter 190638032Speter /* 190790792Sgshapiro ** Here is where we choose the queue group from the work group. 190890792Sgshapiro ** The caller of the "domorework" label must setup a new envelope. 190990792Sgshapiro */ 191090792Sgshapiro 191190792Sgshapiro endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */ 191290792Sgshapiro 191390792Sgshapiro domorework: 191490792Sgshapiro 191590792Sgshapiro /* 191690792Sgshapiro ** Run a queue group if: 1917110560Sgshapiro ** RWG_RUNALL bit is set or the bit for this group is set. 191890792Sgshapiro */ 191990792Sgshapiro 192094334Sgshapiro now = curtime(); 192190792Sgshapiro for (;;) 192290792Sgshapiro { 192390792Sgshapiro /* 192490792Sgshapiro ** Find the next queue group within the work group that 192590792Sgshapiro ** has been marked as needing a run. 192690792Sgshapiro */ 192790792Sgshapiro 192890792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index; 192990792Sgshapiro WorkGrp[wgrp].wg_curqgrp++; /* advance */ 193090792Sgshapiro WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */ 1931110560Sgshapiro if (bitset(RWG_RUNALL, flags) || 193294334Sgshapiro (Queue[qgrp]->qg_nextrun <= now && 193394334Sgshapiro Queue[qgrp]->qg_nextrun != (time_t) -1)) 193490792Sgshapiro break; 193590792Sgshapiro if (endgrp == WorkGrp[wgrp].wg_curqgrp) 193690792Sgshapiro { 193790792Sgshapiro e->e_id = NULL; 1938110560Sgshapiro if (bitset(RWG_FORK, flags)) 193990792Sgshapiro finis(true, true, ExitStat); 194090792Sgshapiro return true; /* we're done */ 194190792Sgshapiro } 194290792Sgshapiro } 194390792Sgshapiro 194490792Sgshapiro qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */ 194590792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 194690792Sgshapiro if (tTd(69, 12)) 194790792Sgshapiro sm_syslog(LOG_INFO, NOQID, 194890792Sgshapiro "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d", 194990792Sgshapiro wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir), 195090792Sgshapiro WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp); 195190792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 195290792Sgshapiro 195390792Sgshapiro#if HASNICE 195490792Sgshapiro /* tweak niceness of queue runs */ 195590792Sgshapiro if (Queue[qgrp]->qg_nice > 0) 195690792Sgshapiro (void) nice(Queue[qgrp]->qg_nice); 195790792Sgshapiro#endif /* HASNICE */ 195890792Sgshapiro 195990792Sgshapiro /* XXX running queue group... */ 196090792Sgshapiro sm_setproctitle(true, CurEnv, "running queue: %s", 196190792Sgshapiro qid_printqueue(qgrp, qdir)); 196290792Sgshapiro 196390792Sgshapiro if (LogLevel > 69 || tTd(63, 99)) 196490792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 196590792Sgshapiro "runqueue %s, pid=%d, forkflag=%d", 196690792Sgshapiro qid_printqueue(qgrp, qdir), (int) CurrentPid, 1967110560Sgshapiro bitset(RWG_FORK, flags)); 196890792Sgshapiro 196990792Sgshapiro /* 197038032Speter ** Start making passes through the queue. 197138032Speter ** First, read and sort the entire queue. 197238032Speter ** Then, process the work in that order. 197338032Speter ** But if you take too long, start over. 197438032Speter */ 197538032Speter 197690792Sgshapiro for (i = 0; i < Queue[qgrp]->qg_numqueues; i++) 197790792Sgshapiro { 197890792Sgshapiro h = gatherq(qgrp, qdir, false, &full, &more); 197990792Sgshapiro#if SM_CONF_SHM 198090792Sgshapiro if (ShmId != SM_SHM_NO_ID) 198190792Sgshapiro QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h; 198290792Sgshapiro#endif /* SM_CONF_SHM */ 198390792Sgshapiro /* If there are no more items in this queue advance */ 198490792Sgshapiro if (!more) 198590792Sgshapiro { 198690792Sgshapiro /* A round-robin advance */ 198790792Sgshapiro qdir++; 198890792Sgshapiro qdir %= Queue[qgrp]->qg_numqueues; 198990792Sgshapiro } 199090792Sgshapiro 199190792Sgshapiro /* Has the WorkList reached the limit? */ 199290792Sgshapiro if (full) 199390792Sgshapiro break; /* don't try to gather more */ 199490792Sgshapiro } 199590792Sgshapiro 199638032Speter /* order the existing work requests */ 199790792Sgshapiro njobs = sortq(Queue[qgrp]->qg_maxlist); 199890792Sgshapiro Queue[qgrp]->qg_curnum = qdir; /* update */ 199938032Speter 200064562Sgshapiro 200190792Sgshapiro if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags)) 200238032Speter { 200390792Sgshapiro int loop, maxrunners; 200490792Sgshapiro pid_t pid; 200538032Speter 200638032Speter /* 200790792Sgshapiro ** For this WorkQ we want to fork off N children (maxrunners) 200890792Sgshapiro ** at this point. Each child has a copy of WorkQ. Each child 200990792Sgshapiro ** will process every N-th item. The parent will wait for all 201090792Sgshapiro ** of the children to finish before moving on to the next 201190792Sgshapiro ** queue group within the work group. This saves us forking 201290792Sgshapiro ** a new runner-child for each work item. 201390792Sgshapiro ** It's valid for qg_maxqrun == 0 since this may be an 201490792Sgshapiro ** explicit "don't run this queue" setting. 201538032Speter */ 201638032Speter 201790792Sgshapiro maxrunners = Queue[qgrp]->qg_maxqrun; 201890792Sgshapiro 201990792Sgshapiro /* No need to have more runners then there are jobs */ 202090792Sgshapiro if (maxrunners > njobs) 202190792Sgshapiro maxrunners = njobs; 202290792Sgshapiro for (loop = 0; loop < maxrunners; loop++) 202338032Speter { 202490792Sgshapiro /* 202590792Sgshapiro ** Since the delivery may happen in a child and the 202690792Sgshapiro ** parent does not wait, the parent may close the 202790792Sgshapiro ** maps thereby removing any shared memory used by 202890792Sgshapiro ** the map. Therefore, close the maps now so the 202990792Sgshapiro ** child will dynamically open them if necessary. 203090792Sgshapiro */ 203190792Sgshapiro 203290792Sgshapiro closemaps(false); 203390792Sgshapiro 203490792Sgshapiro pid = fork(); 203590792Sgshapiro if (pid < 0) 203690792Sgshapiro { 203790792Sgshapiro syserr("run_work_group: cannot fork"); 203890792Sgshapiro return 0; 203990792Sgshapiro } 204090792Sgshapiro else if (pid > 0) 204190792Sgshapiro { 204290792Sgshapiro /* parent -- clean out connection cache */ 204390792Sgshapiro mci_flush(false, NULL); 204490792Sgshapiro WorkQ = WorkQ->w_next; /* for the skip */ 204590792Sgshapiro sequenceno++; 204690792Sgshapiro proc_list_add(pid, "Queue child runner process", 204790792Sgshapiro PROC_QUEUE_CHILD, 0, -1); 204890792Sgshapiro 204990792Sgshapiro /* No additional work, no additional runners */ 205090792Sgshapiro if (WorkQ == NULL) 205190792Sgshapiro break; 205290792Sgshapiro } 205390792Sgshapiro else 205490792Sgshapiro { 205590792Sgshapiro /* child -- Reset global flags */ 205690792Sgshapiro RestartRequest = NULL; 205790792Sgshapiro RestartWorkGroup = false; 205890792Sgshapiro ShutdownRequest = NULL; 205990792Sgshapiro PendingSignal = 0; 206090792Sgshapiro CurrentPid = getpid(); 206190792Sgshapiro 206290792Sgshapiro /* 206390792Sgshapiro ** Initialize exception stack and default 206490792Sgshapiro ** exception handler for child process. 206590792Sgshapiro ** When fork()'d the child now has a private 206690792Sgshapiro ** copy of WorkQ at its current position. 206790792Sgshapiro */ 206890792Sgshapiro 206990792Sgshapiro sm_exc_newthread(fatal_error); 207090792Sgshapiro 207190792Sgshapiro /* 207290792Sgshapiro ** SMTP processes (whether -bd or -bs) set 207390792Sgshapiro ** SIGCHLD to reapchild to collect 207490792Sgshapiro ** children status. However, at delivery 207590792Sgshapiro ** time, that status must be collected 207690792Sgshapiro ** by sm_wait() to be dealt with properly 207790792Sgshapiro ** (check success of delivery based 207890792Sgshapiro ** on status code, etc). Therefore, if we 207990792Sgshapiro ** are an SMTP process, reset SIGCHLD 208090792Sgshapiro ** back to the default so reapchild 208190792Sgshapiro ** doesn't collect status before 208290792Sgshapiro ** sm_wait(). 208390792Sgshapiro */ 208490792Sgshapiro 208590792Sgshapiro if (OpMode == MD_SMTP || 208690792Sgshapiro OpMode == MD_DAEMON || 208790792Sgshapiro MaxQueueChildren > 0) 208890792Sgshapiro { 208990792Sgshapiro proc_list_clear(); 209090792Sgshapiro sm_releasesignal(SIGCHLD); 209190792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 209290792Sgshapiro } 209390792Sgshapiro 209490792Sgshapiro /* child -- error messages to the transcript */ 209590792Sgshapiro QuickAbort = OnlyOneError = false; 209690792Sgshapiro runner_work(e, sequenceno, true, 209790792Sgshapiro maxrunners, njobs); 209890792Sgshapiro 209990792Sgshapiro /* This child is done */ 210090792Sgshapiro finis(true, true, ExitStat); 210190792Sgshapiro /* NOTREACHED */ 210290792Sgshapiro } 210338032Speter } 210490792Sgshapiro 210590792Sgshapiro sm_releasesignal(SIGCHLD); 210690792Sgshapiro 210790792Sgshapiro /* 210890792Sgshapiro ** Wait until all of the runners have completed before 210990792Sgshapiro ** seeing if there is another queue group in the 211090792Sgshapiro ** work group to process. 211190792Sgshapiro ** XXX Future enhancement: don't wait() for all children 211290792Sgshapiro ** here, just go ahead and make sure that overall the number 211390792Sgshapiro ** of children is not exceeded. 211490792Sgshapiro */ 211590792Sgshapiro 211690792Sgshapiro while (CurChildren > 0) 211738032Speter { 211890792Sgshapiro int status; 211990792Sgshapiro pid_t ret; 212038032Speter 212190792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 212290792Sgshapiro continue; 212390792Sgshapiro proc_list_drop(ret, status, NULL); 212438032Speter } 212590792Sgshapiro } 2126110560Sgshapiro else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags)) 212790792Sgshapiro { 212890792Sgshapiro /* 212990792Sgshapiro ** When current process will not fork children to do the work, 213090792Sgshapiro ** it will do the work itself. The 'skip' will be 1 since 213190792Sgshapiro ** there are no child runners to divide the work across. 213290792Sgshapiro */ 213390792Sgshapiro 213490792Sgshapiro runner_work(e, sequenceno, false, 1, njobs); 213590792Sgshapiro } 213690792Sgshapiro 213790792Sgshapiro /* free memory allocated by newenvelope() above */ 213890792Sgshapiro sm_rpool_free(rpool); 213990792Sgshapiro QueueEnvelope.e_rpool = NULL; 214090792Sgshapiro 214190792Sgshapiro /* Are there still more queues in the work group to process? */ 214290792Sgshapiro if (endgrp != WorkGrp[wgrp].wg_curqgrp) 214390792Sgshapiro { 214490792Sgshapiro rpool = sm_rpool_new_x(NULL); 214590792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 214690792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 214790792Sgshapiro goto domorework; 214890792Sgshapiro } 214990792Sgshapiro 215090792Sgshapiro /* No more queues in work group to process. Now check persistent. */ 2151110560Sgshapiro if (bitset(RWG_PERSISTENT, flags)) 215290792Sgshapiro { 215390792Sgshapiro sequenceno = 1; 215490792Sgshapiro sm_setproctitle(true, CurEnv, "running queue: %s", 215590792Sgshapiro qid_printqueue(qgrp, qdir)); 215690792Sgshapiro 215790792Sgshapiro /* 215890792Sgshapiro ** close bogus maps, i.e., maps which caused a tempfail, 215990792Sgshapiro ** so we get fresh map connections on the next lookup. 216090792Sgshapiro ** closemaps() is also called when children are started. 216190792Sgshapiro */ 216290792Sgshapiro 216390792Sgshapiro closemaps(true); 216490792Sgshapiro 216590792Sgshapiro /* Close any cached connections. */ 216690792Sgshapiro mci_flush(true, NULL); 216790792Sgshapiro 216890792Sgshapiro /* Clean out expired related entries. */ 216990792Sgshapiro rmexpstab(); 217090792Sgshapiro 217190792Sgshapiro#if NAMED_BIND 217290792Sgshapiro /* Update MX records for FallBackMX. */ 217390792Sgshapiro if (FallBackMX != NULL) 217490792Sgshapiro (void) getfallbackmxrr(FallBackMX); 217590792Sgshapiro#endif /* NAMED_BIND */ 217690792Sgshapiro 217790792Sgshapiro#if USERDB 217890792Sgshapiro /* close UserDatabase */ 217990792Sgshapiro _udbx_close(); 218090792Sgshapiro#endif /* USERDB */ 218190792Sgshapiro 218290792Sgshapiro#if SM_HEAP_CHECK 218390792Sgshapiro if (sm_debug_active(&SmHeapCheck, 2) 218490792Sgshapiro && access("memdump", F_OK) == 0 218590792Sgshapiro ) 218638032Speter { 218790792Sgshapiro SM_FILE_T *out; 218890792Sgshapiro 218990792Sgshapiro remove("memdump"); 219090792Sgshapiro out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, 219190792Sgshapiro "memdump.out", SM_IO_APPEND, NULL); 219290792Sgshapiro if (out != NULL) 219338032Speter { 219490792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n"); 219590792Sgshapiro sm_heap_report(out, 219690792Sgshapiro sm_debug_level(&SmHeapCheck) - 1); 219790792Sgshapiro (void) sm_io_close(out, SM_TIME_DEFAULT); 219838032Speter } 219938032Speter } 220090792Sgshapiro#endif /* SM_HEAP_CHECK */ 220190792Sgshapiro 220290792Sgshapiro /* let me rest for a second to catch my breath */ 220390792Sgshapiro if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME) 220490792Sgshapiro sleep(MIN_SLEEP_TIME); 220590792Sgshapiro else if (WorkGrp[wgrp].wg_lowqintvl <= 0) 220690792Sgshapiro sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME); 220738032Speter else 220890792Sgshapiro sleep(WorkGrp[wgrp].wg_lowqintvl); 220938032Speter 221090792Sgshapiro /* 221190792Sgshapiro ** Get the LA outside the WorkQ loop if necessary. 221290792Sgshapiro ** In a persistent queue runner the code is repeated over 221390792Sgshapiro ** and over but gatherq() may ignore entries due to 221490792Sgshapiro ** shouldqueue() (do we really have to do this twice?). 221590792Sgshapiro ** Hence the queue runners would just idle around when once 221690792Sgshapiro ** CurrentLA caused all entries in a queue to be ignored. 221790792Sgshapiro */ 221864562Sgshapiro 221990792Sgshapiro now = curtime(); 222090792Sgshapiro if (njobs == 0 && current_la_time < now - GET_NEW_LA_TIME) 222190792Sgshapiro { 222290792Sgshapiro sm_getla(); 222390792Sgshapiro current_la_time = now; 222438032Speter } 222590792Sgshapiro rpool = sm_rpool_new_x(NULL); 222690792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 222790792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 222890792Sgshapiro goto domorework; 222938032Speter } 223038032Speter 223138032Speter /* exit without the usual cleanup */ 223238032Speter e->e_id = NULL; 2233110560Sgshapiro if (bitset(RWG_FORK, flags)) 223490792Sgshapiro finis(true, true, ExitStat); 223564562Sgshapiro /* NOTREACHED */ 223690792Sgshapiro return true; 223738032Speter} 223838032Speter 223938032Speter/* 224090792Sgshapiro** DOQUEUERUN -- do a queue run? 224190792Sgshapiro*/ 224290792Sgshapiro 224390792Sgshapirobool 224490792Sgshapirodoqueuerun() 224590792Sgshapiro{ 224694334Sgshapiro return DoQueueRun; 224790792Sgshapiro} 224890792Sgshapiro 224990792Sgshapiro/* 225094334Sgshapiro** RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done. 225177349Sgshapiro** 225277349Sgshapiro** Parameters: 225394334Sgshapiro** none. 225477349Sgshapiro** 225577349Sgshapiro** Returns: 225677349Sgshapiro** none. 225777349Sgshapiro** 225890792Sgshapiro** Side Effects: 225990792Sgshapiro** The invocation of this function via an alarm may interrupt 226090792Sgshapiro** a set of actions. Thus errno may be set in that context. 226190792Sgshapiro** We need to restore errno at the end of this function to ensure 226290792Sgshapiro** that any work done here that sets errno doesn't return a 226390792Sgshapiro** misleading/false errno value. Errno may be EINTR upon entry to 226490792Sgshapiro** this function because of non-restartable/continuable system 226590792Sgshapiro** API was active. Iff this is true we will override errno as 226690792Sgshapiro** a timeout (as a more accurate error message). 226790792Sgshapiro** 226877349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 226977349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 227077349Sgshapiro** DOING. 227138032Speter*/ 227238032Speter 227390792Sgshapirovoid 227494334Sgshapirorunqueueevent() 227538032Speter{ 227690792Sgshapiro int save_errno = errno; 227790792Sgshapiro 227890792Sgshapiro /* 227990792Sgshapiro ** Set the general bit that we want a queue run, 228090792Sgshapiro ** tested in doqueuerun() 228190792Sgshapiro */ 228290792Sgshapiro 228394334Sgshapiro DoQueueRun = true; 228494334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 228594334Sgshapiro if (tTd(69, 10)) 228694334Sgshapiro sm_syslog(LOG_INFO, NOQID, "rqe: done"); 228794334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 228890792Sgshapiro 228990792Sgshapiro errno = save_errno; 229090792Sgshapiro if (errno == EINTR) 229190792Sgshapiro errno = ETIMEDOUT; 229238032Speter} 229390792Sgshapiro/* 229490792Sgshapiro** GATHERQ -- gather messages from the message queue(s) the work queue. 229538032Speter** 229638032Speter** Parameters: 229790792Sgshapiro** qgrp -- the index of the queue group. 229890792Sgshapiro** qdir -- the index of the queue directory. 229938032Speter** doall -- if set, include everything in the queue (even 230038032Speter** the jobs that cannot be run because the load 230190792Sgshapiro** average is too high, or MaxQueueRun is reached). 230290792Sgshapiro** Otherwise, exclude those jobs. 230390792Sgshapiro** full -- (optional) to be set 'true' if WorkList is full 230490792Sgshapiro** more -- (optional) to be set 'true' if there are still more 230590792Sgshapiro** messages in this queue not added to WorkList 230638032Speter** 230738032Speter** Returns: 230838032Speter** The number of request in the queue (not necessarily 230990792Sgshapiro** the number of requests in WorkList however). 231038032Speter** 231138032Speter** Side Effects: 231290792Sgshapiro** prepares available work into WorkList 231338032Speter*/ 231438032Speter 231590792Sgshapiro#define NEED_P 0001 /* 'P': priority */ 231690792Sgshapiro#define NEED_T 0002 /* 'T': time */ 231790792Sgshapiro#define NEED_R 0004 /* 'R': recipient */ 231890792Sgshapiro#define NEED_S 0010 /* 'S': sender */ 231990792Sgshapiro#define NEED_H 0020 /* host */ 232090792Sgshapiro#if _FFR_QUARANTINE 232190792Sgshapiro# define HAS_QUARANTINE 0040 /* has an unexpected 'q' line */ 232290792Sgshapiro# define NEED_QUARANTINE 0100 /* 'q': reason */ 232390792Sgshapiro#endif /* _FFR_QUARANTINE */ 232438032Speter 232590792Sgshapirostatic WORK *WorkList = NULL; /* list of unsort work */ 232690792Sgshapirostatic int WorkListSize = 0; /* current max size of WorkList */ 232790792Sgshapirostatic int WorkListCount = 0; /* # of work items in WorkList */ 232838032Speter 232964562Sgshapirostatic int 233090792Sgshapirogatherq(qgrp, qdir, doall, full, more) 233190792Sgshapiro int qgrp; 233290792Sgshapiro int qdir; 233338032Speter bool doall; 233490792Sgshapiro bool *full; 233590792Sgshapiro bool *more; 233638032Speter{ 233738032Speter register struct dirent *d; 233838032Speter register WORK *w; 233938032Speter register char *p; 234038032Speter DIR *f; 234190792Sgshapiro int i, num_ent; 234290792Sgshapiro int wn; 234338032Speter QUEUE_CHAR *check; 234464562Sgshapiro char qd[MAXPATHLEN]; 234564562Sgshapiro char qf[MAXPATHLEN]; 234664562Sgshapiro 234790792Sgshapiro wn = WorkListCount - 1; 234890792Sgshapiro num_ent = 0; 234990792Sgshapiro if (qdir == NOQDIR) 235090792Sgshapiro (void) sm_strlcpy(qd, ".", sizeof qd); 235164562Sgshapiro else 235290792Sgshapiro (void) sm_strlcpyn(qd, sizeof qd, 2, 235390792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 235490792Sgshapiro (bitset(QP_SUBQF, 235590792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 235690792Sgshapiro ? "/qf" : "")); 235764562Sgshapiro 235838032Speter if (tTd(41, 1)) 235938032Speter { 236090792Sgshapiro sm_dprintf("gatherq:\n"); 236138032Speter 236238032Speter check = QueueLimitId; 236338032Speter while (check != NULL) 236438032Speter { 236590792Sgshapiro sm_dprintf("\tQueueLimitId = %s%s\n", 236690792Sgshapiro check->queue_negate ? "!" : "", 236764562Sgshapiro check->queue_match); 236838032Speter check = check->queue_next; 236938032Speter } 237038032Speter 237138032Speter check = QueueLimitSender; 237238032Speter while (check != NULL) 237338032Speter { 237490792Sgshapiro sm_dprintf("\tQueueLimitSender = %s%s\n", 237590792Sgshapiro check->queue_negate ? "!" : "", 237664562Sgshapiro check->queue_match); 237738032Speter check = check->queue_next; 237838032Speter } 237938032Speter 238038032Speter check = QueueLimitRecipient; 238138032Speter while (check != NULL) 238238032Speter { 238390792Sgshapiro sm_dprintf("\tQueueLimitRecipient = %s%s\n", 238490792Sgshapiro check->queue_negate ? "!" : "", 238564562Sgshapiro check->queue_match); 238638032Speter check = check->queue_next; 238738032Speter } 238838032Speter 238990792Sgshapiro#if _FFR_QUARANTINE 239090792Sgshapiro if (QueueMode == QM_QUARANTINE) 239190792Sgshapiro { 239290792Sgshapiro check = QueueLimitQuarantine; 239390792Sgshapiro while (check != NULL) 239490792Sgshapiro { 239590792Sgshapiro sm_dprintf("\tQueueLimitQuarantine = %s%s\n", 239690792Sgshapiro check->queue_negate ? "!" : "", 239790792Sgshapiro check->queue_match); 239890792Sgshapiro check = check->queue_next; 239990792Sgshapiro } 240090792Sgshapiro } 240190792Sgshapiro#endif /* _FFR_QUARANTINE */ 240238032Speter } 240338032Speter 240438032Speter /* open the queue directory */ 240564562Sgshapiro f = opendir(qd); 240638032Speter if (f == NULL) 240738032Speter { 240890792Sgshapiro syserr("gatherq: cannot open \"%s\"", 240990792Sgshapiro qid_printqueue(qgrp, qdir)); 241090792Sgshapiro if (full != NULL) 241190792Sgshapiro *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0; 241290792Sgshapiro if (more != NULL) 241390792Sgshapiro *more = false; 241464562Sgshapiro return 0; 241538032Speter } 241638032Speter 241738032Speter /* 241838032Speter ** Read the work directory. 241938032Speter */ 242038032Speter 242138032Speter while ((d = readdir(f)) != NULL) 242238032Speter { 242390792Sgshapiro SM_FILE_T *cf; 242438032Speter int qfver = 0; 242538032Speter char lbuf[MAXNAME + 1]; 242664562Sgshapiro struct stat sbuf; 242738032Speter 242838032Speter if (tTd(41, 50)) 242990792Sgshapiro sm_dprintf("gatherq: checking %s..", d->d_name); 243038032Speter 243138032Speter /* is this an interesting entry? */ 243290792Sgshapiro#if _FFR_QUARANTINE 243390792Sgshapiro if (!(((QueueMode == QM_NORMAL && 243490792Sgshapiro d->d_name[0] == NORMQF_LETTER) || 243590792Sgshapiro (QueueMode == QM_QUARANTINE && 243690792Sgshapiro d->d_name[0] == QUARQF_LETTER) || 243790792Sgshapiro (QueueMode == QM_LOST && 243890792Sgshapiro d->d_name[0] == LOSEQF_LETTER)) && 243990792Sgshapiro d->d_name[1] == 'f')) 244090792Sgshapiro#else /* _FFR_QUARANTINE */ 244190792Sgshapiro if (d->d_name[0] != NORMQF_LETTER || d->d_name[1] != 'f') 244290792Sgshapiro#endif /* _FFR_QUARANTINE */ 244390792Sgshapiro { 244490792Sgshapiro if (tTd(41, 50)) 244590792Sgshapiro sm_dprintf(" skipping\n"); 244638032Speter continue; 244790792Sgshapiro } 244890792Sgshapiro if (tTd(41, 50)) 244990792Sgshapiro sm_dprintf("\n"); 245038032Speter 245164562Sgshapiro if (strlen(d->d_name) >= MAXQFNAME) 245242575Speter { 245342575Speter if (Verbose) 245490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 245590792Sgshapiro "gatherq: %s too long, %d max characters\n", 245690792Sgshapiro d->d_name, MAXQFNAME); 245742575Speter if (LogLevel > 0) 245842575Speter sm_syslog(LOG_ALERT, NOQID, 245990792Sgshapiro "gatherq: %s too long, %d max characters", 246064562Sgshapiro d->d_name, MAXQFNAME); 246138032Speter continue; 246242575Speter } 246338032Speter 246438032Speter check = QueueLimitId; 246538032Speter while (check != NULL) 246638032Speter { 246794334Sgshapiro if (strcontainedin(false, check->queue_match, 246890792Sgshapiro d->d_name) != check->queue_negate) 246938032Speter break; 247038032Speter else 247138032Speter check = check->queue_next; 247238032Speter } 247338032Speter if (QueueLimitId != NULL && check == NULL) 247438032Speter continue; 247538032Speter 247664562Sgshapiro /* grow work list if necessary */ 247738032Speter if (++wn >= MaxQueueRun && MaxQueueRun > 0) 247838032Speter { 247938032Speter if (wn == MaxQueueRun && LogLevel > 0) 248064562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 248164562Sgshapiro "WorkList for %s maxed out at %d", 248290792Sgshapiro qid_printqueue(qgrp, qdir), 248364562Sgshapiro MaxQueueRun); 248490792Sgshapiro if (doall) 248590792Sgshapiro continue; /* just count entries */ 248690792Sgshapiro break; 248738032Speter } 248838032Speter if (wn >= WorkListSize) 248938032Speter { 249090792Sgshapiro grow_wlist(qgrp, qdir); 249138032Speter if (wn >= WorkListSize) 249238032Speter continue; 249338032Speter } 249490792Sgshapiro SM_ASSERT(wn >= 0); 249564562Sgshapiro w = &WorkList[wn]; 249638032Speter 249790792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", d->d_name); 249864562Sgshapiro if (stat(qf, &sbuf) < 0) 249964562Sgshapiro { 250064562Sgshapiro if (errno != ENOENT) 250164562Sgshapiro sm_syslog(LOG_INFO, NOQID, 250290792Sgshapiro "gatherq: can't stat %s/%s", 250390792Sgshapiro qid_printqueue(qgrp, qdir), 250490792Sgshapiro d->d_name); 250564562Sgshapiro wn--; 250664562Sgshapiro continue; 250764562Sgshapiro } 250864562Sgshapiro if (!bitset(S_IFREG, sbuf.st_mode)) 250964562Sgshapiro { 251064562Sgshapiro /* Yikes! Skip it or we will hang on open! */ 251190792Sgshapiro if (!((d->d_name[0] == DATAFL_LETTER || 251290792Sgshapiro d->d_name[0] == NORMQF_LETTER || 251390792Sgshapiro#if _FFR_QUARANTINE 251490792Sgshapiro d->d_name[0] == QUARQF_LETTER || 251590792Sgshapiro d->d_name[0] == LOSEQF_LETTER || 251690792Sgshapiro#endif /* _FFR_QUARANTINE */ 251790792Sgshapiro d->d_name[0] == XSCRPT_LETTER) && 251890792Sgshapiro d->d_name[1] == 'f' && d->d_name[2] == '\0')) 251990792Sgshapiro syserr("gatherq: %s/%s is not a regular file", 252090792Sgshapiro qid_printqueue(qgrp, qdir), d->d_name); 252164562Sgshapiro wn--; 252264562Sgshapiro continue; 252364562Sgshapiro } 252464562Sgshapiro 252564562Sgshapiro /* avoid work if possible */ 252690792Sgshapiro if ((QueueSortOrder == QSO_BYFILENAME || 252790792Sgshapiro QueueSortOrder == QSO_BYMODTIME || 252890792Sgshapiro QueueSortOrder == QSO_RANDOM) && 252990792Sgshapiro#if _FFR_QUARANTINE 253090792Sgshapiro QueueLimitQuarantine == NULL && 253190792Sgshapiro#endif /* _FFR_QUARANTINE */ 253266494Sgshapiro QueueLimitSender == NULL && 253366494Sgshapiro QueueLimitRecipient == NULL) 253464562Sgshapiro { 253590792Sgshapiro w->w_qgrp = qgrp; 253690792Sgshapiro w->w_qdir = qdir; 253764562Sgshapiro w->w_name = newstr(d->d_name); 253864562Sgshapiro w->w_host = NULL; 253990792Sgshapiro w->w_lock = w->w_tooyoung = false; 254064562Sgshapiro w->w_pri = 0; 254164562Sgshapiro w->w_ctime = 0; 254290792Sgshapiro w->w_mtime = sbuf.st_mtime; 254390792Sgshapiro ++num_ent; 254464562Sgshapiro continue; 254564562Sgshapiro } 254664562Sgshapiro 254764562Sgshapiro /* open control file */ 254890792Sgshapiro cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY, 254990792Sgshapiro NULL); 255090792Sgshapiro if (cf == NULL && OpMode != MD_PRINT) 255138032Speter { 255238032Speter /* this may be some random person sending hir msgs */ 255338032Speter if (tTd(41, 2)) 255490792Sgshapiro sm_dprintf("gatherq: cannot open %s: %s\n", 255590792Sgshapiro d->d_name, sm_errstring(errno)); 255638032Speter errno = 0; 255738032Speter wn--; 255838032Speter continue; 255938032Speter } 256090792Sgshapiro w->w_qgrp = qgrp; 256190792Sgshapiro w->w_qdir = qdir; 256238032Speter w->w_name = newstr(d->d_name); 256338032Speter w->w_host = NULL; 256490792Sgshapiro if (cf != NULL) 256590792Sgshapiro { 256690792Sgshapiro w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD, 256790792Sgshapiro NULL), 256890792Sgshapiro w->w_name, NULL, 256990792Sgshapiro LOCK_SH|LOCK_NB); 257090792Sgshapiro } 257190792Sgshapiro w->w_tooyoung = false; 257238032Speter 257338032Speter /* make sure jobs in creation don't clog queue */ 257438032Speter w->w_pri = 0x7fffffff; 257538032Speter w->w_ctime = 0; 257690792Sgshapiro w->w_mtime = sbuf.st_mtime; 257738032Speter 257838032Speter /* extract useful information */ 257990792Sgshapiro i = NEED_P|NEED_T; 258090792Sgshapiro if (QueueSortOrder == QSO_BYHOST 258190792Sgshapiro#if _FFR_RHS 258290792Sgshapiro || QueueSortOrder == QSO_BYSHUFFLE 258390792Sgshapiro#endif /* _FFR_RHS */ 258490792Sgshapiro ) 258571345Sgshapiro { 258671345Sgshapiro /* need w_host set for host sort order */ 258771345Sgshapiro i |= NEED_H; 258871345Sgshapiro } 258938032Speter if (QueueLimitSender != NULL) 259038032Speter i |= NEED_S; 259164562Sgshapiro if (QueueLimitRecipient != NULL) 259238032Speter i |= NEED_R; 259390792Sgshapiro#if _FFR_QUARANTINE 259490792Sgshapiro if (QueueLimitQuarantine != NULL) 259590792Sgshapiro i |= NEED_QUARANTINE; 259690792Sgshapiro#endif /* _FFR_QUARANTINE */ 259790792Sgshapiro while (cf != NULL && i != 0 && 259890792Sgshapiro sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf, 259990792Sgshapiro sizeof lbuf) != NULL) 260038032Speter { 260138032Speter int c; 260238032Speter time_t age; 260338032Speter 260438032Speter p = strchr(lbuf, '\n'); 260538032Speter if (p != NULL) 260638032Speter *p = '\0'; 260738032Speter else 260838032Speter { 260938032Speter /* flush rest of overly long line */ 261090792Sgshapiro while ((c = sm_io_getc(cf, SM_TIME_DEFAULT)) 261190792Sgshapiro != SM_IO_EOF && c != '\n') 261238032Speter continue; 261338032Speter } 261438032Speter 261538032Speter switch (lbuf[0]) 261638032Speter { 261738032Speter case 'V': 261838032Speter qfver = atoi(&lbuf[1]); 261938032Speter break; 262038032Speter 262138032Speter case 'P': 262238032Speter w->w_pri = atol(&lbuf[1]); 262338032Speter i &= ~NEED_P; 262438032Speter break; 262538032Speter 262638032Speter case 'T': 262738032Speter w->w_ctime = atol(&lbuf[1]); 262838032Speter i &= ~NEED_T; 262938032Speter break; 263038032Speter 263190792Sgshapiro#if _FFR_QUARANTINE 263290792Sgshapiro case 'q': 263390792Sgshapiro if (QueueMode != QM_QUARANTINE && 263490792Sgshapiro QueueMode != QM_LOST) 263590792Sgshapiro { 263690792Sgshapiro if (tTd(41, 49)) 263790792Sgshapiro sm_dprintf("%s not marked as quarantined but has a 'q' line\n", 263890792Sgshapiro w->w_name); 263990792Sgshapiro i |= HAS_QUARANTINE; 264090792Sgshapiro } 264190792Sgshapiro else if (QueueMode == QM_QUARANTINE) 264290792Sgshapiro { 264390792Sgshapiro if (QueueLimitQuarantine == NULL) 264490792Sgshapiro { 264590792Sgshapiro i &= ~NEED_QUARANTINE; 264690792Sgshapiro break; 264790792Sgshapiro } 264890792Sgshapiro p = &lbuf[1]; 264990792Sgshapiro check = QueueLimitQuarantine; 265090792Sgshapiro while (check != NULL) 265190792Sgshapiro { 265290792Sgshapiro if (strcontainedin(false, 265390792Sgshapiro check->queue_match, 265490792Sgshapiro p) != 265590792Sgshapiro check->queue_negate) 265690792Sgshapiro break; 265790792Sgshapiro else 265890792Sgshapiro check = check->queue_next; 265990792Sgshapiro } 266090792Sgshapiro if (check != NULL) 266190792Sgshapiro i &= ~NEED_QUARANTINE; 266290792Sgshapiro } 266390792Sgshapiro break; 266490792Sgshapiro#endif /* _FFR_QUARANTINE */ 266590792Sgshapiro 266638032Speter case 'R': 266738032Speter if (w->w_host == NULL && 266838032Speter (p = strrchr(&lbuf[1], '@')) != NULL) 266964562Sgshapiro { 267090792Sgshapiro#if _FFR_RHS 267190792Sgshapiro if (QueueSortOrder == QSO_BYSHUFFLE) 267290792Sgshapiro w->w_host = newstr(&p[1]); 267390792Sgshapiro else 267490792Sgshapiro#endif /* _FFR_RHS */ 267590792Sgshapiro w->w_host = strrev(&p[1]); 267664562Sgshapiro makelower(w->w_host); 267771345Sgshapiro i &= ~NEED_H; 267864562Sgshapiro } 267938032Speter if (QueueLimitRecipient == NULL) 268038032Speter { 268138032Speter i &= ~NEED_R; 268238032Speter break; 268338032Speter } 268438032Speter if (qfver > 0) 268538032Speter { 268638032Speter p = strchr(&lbuf[1], ':'); 268738032Speter if (p == NULL) 268838032Speter p = &lbuf[1]; 268938032Speter } 269038032Speter else 269138032Speter p = &lbuf[1]; 269238032Speter check = QueueLimitRecipient; 269338032Speter while (check != NULL) 269438032Speter { 269590792Sgshapiro if (strcontainedin(true, 269690792Sgshapiro check->queue_match, 269790792Sgshapiro p) != 269890792Sgshapiro check->queue_negate) 269938032Speter break; 270038032Speter else 270138032Speter check = check->queue_next; 270238032Speter } 270338032Speter if (check != NULL) 270438032Speter i &= ~NEED_R; 270538032Speter break; 270638032Speter 270738032Speter case 'S': 270864562Sgshapiro check = QueueLimitSender; 270964562Sgshapiro while (check != NULL) 271064562Sgshapiro { 271190792Sgshapiro if (strcontainedin(true, 271290792Sgshapiro check->queue_match, 271390792Sgshapiro &lbuf[1]) != 271490792Sgshapiro check->queue_negate) 271564562Sgshapiro break; 271664562Sgshapiro else 271764562Sgshapiro check = check->queue_next; 271864562Sgshapiro } 271964562Sgshapiro if (check != NULL) 272064562Sgshapiro i &= ~NEED_S; 272138032Speter break; 272238032Speter 272338032Speter case 'K': 272438032Speter age = curtime() - (time_t) atol(&lbuf[1]); 272538032Speter if (age >= 0 && MinQueueAge > 0 && 272638032Speter age < MinQueueAge) 272790792Sgshapiro w->w_tooyoung = true; 272838032Speter break; 272938032Speter 273038032Speter case 'N': 273138032Speter if (atol(&lbuf[1]) == 0) 273290792Sgshapiro w->w_tooyoung = false; 273338032Speter break; 273464562Sgshapiro 273590792Sgshapiro#if _FFR_QUEUEDELAY 273664562Sgshapiro/* 273764562Sgshapiro case 'G': 273864562Sgshapiro queuealg = atoi(lbuf[1]); 273964562Sgshapiro break; 274064562Sgshapiro case 'Y': 274164562Sgshapiro queuedelay = (time_t) atol(&lbuf[1]); 274264562Sgshapiro break; 274364562Sgshapiro*/ 274490792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 274538032Speter } 274638032Speter } 274790792Sgshapiro if (cf != NULL) 274890792Sgshapiro (void) sm_io_close(cf, SM_TIME_DEFAULT); 274938032Speter 275038032Speter if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || 275190792Sgshapiro#if _FFR_QUARANTINE 275290792Sgshapiro bitset(HAS_QUARANTINE, i) || 275390792Sgshapiro bitset(NEED_QUARANTINE, i) || 275490792Sgshapiro#endif /* _FFR_QUARANTINE */ 275538032Speter bitset(NEED_R|NEED_S, i)) 275638032Speter { 275738032Speter /* don't even bother sorting this job in */ 275838032Speter if (tTd(41, 49)) 275990792Sgshapiro sm_dprintf("skipping %s (%x)\n", w->w_name, i); 276090792Sgshapiro sm_free(w->w_name); /* XXX */ 276190792Sgshapiro if (w->w_host != NULL) 276290792Sgshapiro sm_free(w->w_host); /* XXX */ 276338032Speter wn--; 276438032Speter } 276590792Sgshapiro else 276690792Sgshapiro ++num_ent; 276738032Speter } 276838032Speter (void) closedir(f); 276938032Speter wn++; 277038032Speter 277190792Sgshapiro i = wn - WorkListCount; 277290792Sgshapiro WorkListCount += SM_MIN(num_ent, WorkListSize); 277390792Sgshapiro 277490792Sgshapiro if (more != NULL) 277590792Sgshapiro *more = WorkListCount < wn; 277690792Sgshapiro 277790792Sgshapiro if (full != NULL) 277890792Sgshapiro *full = (wn >= MaxQueueRun && MaxQueueRun > 0) || 277990792Sgshapiro (WorkList == NULL && wn > 0); 278090792Sgshapiro 278190792Sgshapiro return i; 278290792Sgshapiro} 278390792Sgshapiro/* 278490792Sgshapiro** SORTQ -- sort the work list 278590792Sgshapiro** 278690792Sgshapiro** First the old WorkQ is cleared away. Then the WorkList is sorted 278790792Sgshapiro** for all items so that important (higher sorting value) items are not 278890792Sgshapiro** trunctated off. Then the most important items are moved from 278990792Sgshapiro** WorkList to WorkQ. The lower count of 'max' or MaxListCount items 279090792Sgshapiro** are moved. 279190792Sgshapiro** 279290792Sgshapiro** Parameters: 279390792Sgshapiro** max -- maximum number of items to be placed in WorkQ 279490792Sgshapiro** 279590792Sgshapiro** Returns: 279690792Sgshapiro** the number of items in WorkQ 279790792Sgshapiro** 279890792Sgshapiro** Side Effects: 279990792Sgshapiro** WorkQ gets released and filled with new work. WorkList 280090792Sgshapiro** gets released. Work items get sorted in order. 280190792Sgshapiro*/ 280290792Sgshapiro 280390792Sgshapirostatic int 280490792Sgshapirosortq(max) 280590792Sgshapiro int max; 280690792Sgshapiro{ 280790792Sgshapiro register int i; /* local counter */ 280890792Sgshapiro register WORK *w; /* tmp item pointer */ 280990792Sgshapiro int wc = WorkListCount; /* trim size for WorkQ */ 281090792Sgshapiro 281190792Sgshapiro if (WorkQ != NULL) 281290792Sgshapiro { 281390792Sgshapiro /* Clear out old WorkQ. */ 281490792Sgshapiro for (w = WorkQ; w != NULL; ) 281590792Sgshapiro { 281690792Sgshapiro register WORK *nw = w->w_next; 281790792Sgshapiro 281890792Sgshapiro WorkQ = nw; 281990792Sgshapiro sm_free(w->w_name); /* XXX */ 282090792Sgshapiro if (w->w_host != NULL) 282190792Sgshapiro sm_free(w->w_host); /* XXX */ 282290792Sgshapiro sm_free((char *) w); /* XXX */ 282390792Sgshapiro w = nw; 282490792Sgshapiro } 282590792Sgshapiro sm_free((char *) WorkQ); 282690792Sgshapiro WorkQ = NULL; 282790792Sgshapiro } 282890792Sgshapiro 282990792Sgshapiro if (WorkList == NULL || wc <= 0) 283064562Sgshapiro return 0; 283138032Speter 283290792Sgshapiro /* Check if the per queue group item limit will be exceeded */ 283390792Sgshapiro if (wc > max && max > 0) 283490792Sgshapiro wc = max; 283590792Sgshapiro 283690792Sgshapiro /* 283790792Sgshapiro ** The sort now takes place using all of the items in WorkList. 283890792Sgshapiro ** The list gets trimmed to the most important items after the sort. 283990792Sgshapiro ** If the trim were to happen before the sort then one or more 284090792Sgshapiro ** important items might get truncated off -- not what we want. 284190792Sgshapiro */ 284290792Sgshapiro 284364562Sgshapiro if (QueueSortOrder == QSO_BYHOST) 284438032Speter { 284538032Speter /* 284638032Speter ** Sort the work directory for the first time, 284738032Speter ** based on host name, lock status, and priority. 284838032Speter */ 284938032Speter 285038032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); 285138032Speter 285238032Speter /* 285338032Speter ** If one message to host is locked, "lock" all messages 285438032Speter ** to that host. 285538032Speter */ 285638032Speter 285738032Speter i = 0; 285838032Speter while (i < wc) 285938032Speter { 286038032Speter if (!WorkList[i].w_lock) 286138032Speter { 286238032Speter i++; 286338032Speter continue; 286438032Speter } 286538032Speter w = &WorkList[i]; 286638032Speter while (++i < wc) 286738032Speter { 286838032Speter if (WorkList[i].w_host == NULL && 286938032Speter w->w_host == NULL) 287090792Sgshapiro WorkList[i].w_lock = true; 287138032Speter else if (WorkList[i].w_host != NULL && 287238032Speter w->w_host != NULL && 287390792Sgshapiro sm_strcasecmp(WorkList[i].w_host, 287490792Sgshapiro w->w_host) == 0) 287590792Sgshapiro WorkList[i].w_lock = true; 287638032Speter else 287738032Speter break; 287838032Speter } 287938032Speter } 288038032Speter 288138032Speter /* 288238032Speter ** Sort the work directory for the second time, 288338032Speter ** based on lock status, host name, and priority. 288438032Speter */ 288538032Speter 288638032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); 288738032Speter } 288864562Sgshapiro else if (QueueSortOrder == QSO_BYTIME) 288938032Speter { 289038032Speter /* 289138032Speter ** Simple sort based on submission time only. 289238032Speter */ 289338032Speter 289438032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); 289538032Speter } 289664562Sgshapiro else if (QueueSortOrder == QSO_BYFILENAME) 289764562Sgshapiro { 289864562Sgshapiro /* 289990792Sgshapiro ** Sort based on queue filename. 290064562Sgshapiro */ 290164562Sgshapiro 290264562Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4); 290364562Sgshapiro } 290490792Sgshapiro else if (QueueSortOrder == QSO_RANDOM) 290590792Sgshapiro { 290690792Sgshapiro /* 2907110560Sgshapiro ** Sort randomly. To avoid problems with an instable sort, 2908110560Sgshapiro ** use a random index into the queue file name to start 2909110560Sgshapiro ** comparison. 291090792Sgshapiro */ 291190792Sgshapiro 2912110560Sgshapiro randi = get_rand_mod(MAXQFNAME); 2913110560Sgshapiro if (randi < 2) 2914110560Sgshapiro randi = 3; 291590792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf5); 291690792Sgshapiro } 291790792Sgshapiro else if (QueueSortOrder == QSO_BYMODTIME) 291890792Sgshapiro { 291990792Sgshapiro /* 292090792Sgshapiro ** Simple sort based on modification time of queue file. 292190792Sgshapiro ** This puts the oldest items first. 292290792Sgshapiro */ 292390792Sgshapiro 292490792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf6); 292590792Sgshapiro } 292690792Sgshapiro#if _FFR_RHS 292790792Sgshapiro else if (QueueSortOrder == QSO_BYSHUFFLE) 292890792Sgshapiro { 292990792Sgshapiro /* 293090792Sgshapiro ** Simple sort based on shuffled host name. 293190792Sgshapiro */ 293290792Sgshapiro 293390792Sgshapiro init_shuffle_alphabet(); 293490792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf7); 293590792Sgshapiro } 293690792Sgshapiro#endif /* _FFR_RHS */ 293738032Speter else 293838032Speter { 293938032Speter /* 294038032Speter ** Simple sort based on queue priority only. 294138032Speter */ 294238032Speter 294338032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); 294438032Speter } 294538032Speter 294638032Speter /* 294738032Speter ** Convert the work list into canonical form. 294838032Speter ** Should be turning it into a list of envelopes here perhaps. 294990792Sgshapiro ** Only take the most important items up to the per queue group 295090792Sgshapiro ** maximum. 295138032Speter */ 295238032Speter 295338032Speter for (i = wc; --i >= 0; ) 295438032Speter { 295538032Speter w = (WORK *) xalloc(sizeof *w); 295690792Sgshapiro w->w_qgrp = WorkList[i].w_qgrp; 295790792Sgshapiro w->w_qdir = WorkList[i].w_qdir; 295838032Speter w->w_name = WorkList[i].w_name; 295938032Speter w->w_host = WorkList[i].w_host; 296038032Speter w->w_lock = WorkList[i].w_lock; 296138032Speter w->w_tooyoung = WorkList[i].w_tooyoung; 296238032Speter w->w_pri = WorkList[i].w_pri; 296338032Speter w->w_ctime = WorkList[i].w_ctime; 296490792Sgshapiro w->w_mtime = WorkList[i].w_mtime; 296538032Speter w->w_next = WorkQ; 296638032Speter WorkQ = w; 296738032Speter } 296838032Speter if (WorkList != NULL) 296990792Sgshapiro sm_free(WorkList); /* XXX */ 297038032Speter WorkList = NULL; 297138032Speter WorkListSize = 0; 297290792Sgshapiro WorkListCount = 0; 297338032Speter 297438032Speter if (tTd(40, 1)) 297538032Speter { 297638032Speter for (w = WorkQ; w != NULL; w = w->w_next) 297764562Sgshapiro { 297864562Sgshapiro if (w->w_host != NULL) 297990792Sgshapiro sm_dprintf("%22s: pri=%ld %s\n", 298064562Sgshapiro w->w_name, w->w_pri, w->w_host); 298164562Sgshapiro else 298290792Sgshapiro sm_dprintf("%32s: pri=%ld\n", 298364562Sgshapiro w->w_name, w->w_pri); 298464562Sgshapiro } 298538032Speter } 298638032Speter 298790792Sgshapiro return wc; /* return number of WorkQ items */ 298838032Speter} 298990792Sgshapiro/* 299038032Speter** GROW_WLIST -- make the work list larger 299138032Speter** 299238032Speter** Parameters: 299390792Sgshapiro** qgrp -- the index for the queue group. 299490792Sgshapiro** qdir -- the index for the queue directory. 299538032Speter** 299638032Speter** Returns: 299738032Speter** none. 299838032Speter** 299938032Speter** Side Effects: 300038032Speter** Adds another QUEUESEGSIZE entries to WorkList if possible. 300138032Speter** It can fail if there isn't enough memory, so WorkListSize 300238032Speter** should be checked again upon return. 300338032Speter*/ 300438032Speter 300564562Sgshapirostatic void 300690792Sgshapirogrow_wlist(qgrp, qdir) 300790792Sgshapiro int qgrp; 300890792Sgshapiro int qdir; 300938032Speter{ 301038032Speter if (tTd(41, 1)) 301190792Sgshapiro sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); 301238032Speter if (WorkList == NULL) 301338032Speter { 301464562Sgshapiro WorkList = (WORK *) xalloc((sizeof *WorkList) * 301564562Sgshapiro (QUEUESEGSIZE + 1)); 301638032Speter WorkListSize = QUEUESEGSIZE; 301738032Speter } 301838032Speter else 301938032Speter { 302038032Speter int newsize = WorkListSize + QUEUESEGSIZE; 302190792Sgshapiro WORK *newlist = (WORK *) sm_realloc((char *) WorkList, 302290792Sgshapiro (unsigned) sizeof(WORK) * (newsize + 1)); 302338032Speter 302438032Speter if (newlist != NULL) 302538032Speter { 302638032Speter WorkListSize = newsize; 302738032Speter WorkList = newlist; 302838032Speter if (LogLevel > 1) 302938032Speter { 303064562Sgshapiro sm_syslog(LOG_INFO, NOQID, 303164562Sgshapiro "grew WorkList for %s to %d", 303290792Sgshapiro qid_printqueue(qgrp, qdir), 303364562Sgshapiro WorkListSize); 303438032Speter } 303538032Speter } 303638032Speter else if (LogLevel > 0) 303738032Speter { 303838032Speter sm_syslog(LOG_ALERT, NOQID, 303964562Sgshapiro "FAILED to grow WorkList for %s to %d", 304090792Sgshapiro qid_printqueue(qgrp, qdir), newsize); 304138032Speter } 304238032Speter } 304338032Speter if (tTd(41, 1)) 304490792Sgshapiro sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); 304538032Speter} 304690792Sgshapiro/* 304738032Speter** WORKCMPF0 -- simple priority-only compare function. 304838032Speter** 304938032Speter** Parameters: 305038032Speter** a -- the first argument. 305138032Speter** b -- the second argument. 305238032Speter** 305338032Speter** Returns: 305438032Speter** -1 if a < b 305538032Speter** 0 if a == b 305638032Speter** +1 if a > b 305738032Speter** 305838032Speter*/ 305938032Speter 306064562Sgshapirostatic int 306138032Speterworkcmpf0(a, b) 306238032Speter register WORK *a; 306338032Speter register WORK *b; 306438032Speter{ 306538032Speter long pa = a->w_pri; 306638032Speter long pb = b->w_pri; 306738032Speter 306838032Speter if (pa == pb) 306938032Speter return 0; 307038032Speter else if (pa > pb) 307138032Speter return 1; 307238032Speter else 307338032Speter return -1; 307438032Speter} 307590792Sgshapiro/* 307638032Speter** WORKCMPF1 -- first compare function for ordering work based on host name. 307738032Speter** 307838032Speter** Sorts on host name, lock status, and priority in that order. 307938032Speter** 308038032Speter** Parameters: 308138032Speter** a -- the first argument. 308238032Speter** b -- the second argument. 308338032Speter** 308438032Speter** Returns: 308538032Speter** <0 if a < b 308638032Speter** 0 if a == b 308738032Speter** >0 if a > b 308838032Speter** 308938032Speter*/ 309038032Speter 309164562Sgshapirostatic int 309238032Speterworkcmpf1(a, b) 309338032Speter register WORK *a; 309438032Speter register WORK *b; 309538032Speter{ 309638032Speter int i; 309738032Speter 309838032Speter /* host name */ 309938032Speter if (a->w_host != NULL && b->w_host == NULL) 310038032Speter return 1; 310138032Speter else if (a->w_host == NULL && b->w_host != NULL) 310238032Speter return -1; 310338032Speter if (a->w_host != NULL && b->w_host != NULL && 310438032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 310538032Speter return i; 310638032Speter 310738032Speter /* lock status */ 310838032Speter if (a->w_lock != b->w_lock) 310938032Speter return b->w_lock - a->w_lock; 311038032Speter 311138032Speter /* job priority */ 311273188Sgshapiro return workcmpf0(a, b); 311338032Speter} 311490792Sgshapiro/* 311538032Speter** WORKCMPF2 -- second compare function for ordering work based on host name. 311638032Speter** 311738032Speter** Sorts on lock status, host name, and priority in that order. 311838032Speter** 311938032Speter** Parameters: 312038032Speter** a -- the first argument. 312138032Speter** b -- the second argument. 312238032Speter** 312338032Speter** Returns: 312438032Speter** <0 if a < b 312538032Speter** 0 if a == b 312638032Speter** >0 if a > b 312738032Speter** 312838032Speter*/ 312938032Speter 313064562Sgshapirostatic int 313138032Speterworkcmpf2(a, b) 313238032Speter register WORK *a; 313338032Speter register WORK *b; 313438032Speter{ 313538032Speter int i; 313638032Speter 313738032Speter /* lock status */ 313838032Speter if (a->w_lock != b->w_lock) 313938032Speter return a->w_lock - b->w_lock; 314038032Speter 314138032Speter /* host name */ 314238032Speter if (a->w_host != NULL && b->w_host == NULL) 314338032Speter return 1; 314438032Speter else if (a->w_host == NULL && b->w_host != NULL) 314538032Speter return -1; 314638032Speter if (a->w_host != NULL && b->w_host != NULL && 314738032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 314838032Speter return i; 314938032Speter 315038032Speter /* job priority */ 315173188Sgshapiro return workcmpf0(a, b); 315238032Speter} 315390792Sgshapiro/* 315438032Speter** WORKCMPF3 -- simple submission-time-only compare function. 315538032Speter** 315638032Speter** Parameters: 315738032Speter** a -- the first argument. 315838032Speter** b -- the second argument. 315938032Speter** 316038032Speter** Returns: 316138032Speter** -1 if a < b 316238032Speter** 0 if a == b 316338032Speter** +1 if a > b 316438032Speter** 316538032Speter*/ 316638032Speter 316764562Sgshapirostatic int 316838032Speterworkcmpf3(a, b) 316938032Speter register WORK *a; 317038032Speter register WORK *b; 317138032Speter{ 317238032Speter if (a->w_ctime > b->w_ctime) 317338032Speter return 1; 317438032Speter else if (a->w_ctime < b->w_ctime) 317538032Speter return -1; 317638032Speter else 317738032Speter return 0; 317838032Speter} 317990792Sgshapiro/* 318064562Sgshapiro** WORKCMPF4 -- compare based on file name 318164562Sgshapiro** 318264562Sgshapiro** Parameters: 318364562Sgshapiro** a -- the first argument. 318464562Sgshapiro** b -- the second argument. 318564562Sgshapiro** 318664562Sgshapiro** Returns: 318764562Sgshapiro** -1 if a < b 318864562Sgshapiro** 0 if a == b 318964562Sgshapiro** +1 if a > b 319064562Sgshapiro** 319164562Sgshapiro*/ 319264562Sgshapiro 319364562Sgshapirostatic int 319464562Sgshapiroworkcmpf4(a, b) 319564562Sgshapiro register WORK *a; 319664562Sgshapiro register WORK *b; 319764562Sgshapiro{ 319864562Sgshapiro return strcmp(a->w_name, b->w_name); 319964562Sgshapiro} 320090792Sgshapiro/* 320190792Sgshapiro** WORKCMPF5 -- compare based on assigned random number 320290792Sgshapiro** 320390792Sgshapiro** Parameters: 320490792Sgshapiro** a -- the first argument (ignored). 320590792Sgshapiro** b -- the second argument (ignored). 320690792Sgshapiro** 320790792Sgshapiro** Returns: 320890792Sgshapiro** randomly 1/-1 320990792Sgshapiro*/ 321090792Sgshapiro 321190792Sgshapiro/* ARGSUSED0 */ 321290792Sgshapirostatic int 321390792Sgshapiroworkcmpf5(a, b) 321490792Sgshapiro register WORK *a; 321590792Sgshapiro register WORK *b; 321690792Sgshapiro{ 3217110560Sgshapiro if (strlen(a->w_name) < randi || strlen(b->w_name) < randi) 3218110560Sgshapiro return -1; 3219110560Sgshapiro return a->w_name[randi] - b->w_name[randi]; 322090792Sgshapiro} 322190792Sgshapiro/* 322290792Sgshapiro** WORKCMPF6 -- simple modification-time-only compare function. 322390792Sgshapiro** 322490792Sgshapiro** Parameters: 322590792Sgshapiro** a -- the first argument. 322690792Sgshapiro** b -- the second argument. 322790792Sgshapiro** 322890792Sgshapiro** Returns: 322990792Sgshapiro** -1 if a < b 323090792Sgshapiro** 0 if a == b 323190792Sgshapiro** +1 if a > b 323290792Sgshapiro** 323390792Sgshapiro*/ 323490792Sgshapiro 323590792Sgshapirostatic int 323690792Sgshapiroworkcmpf6(a, b) 323790792Sgshapiro register WORK *a; 323890792Sgshapiro register WORK *b; 323990792Sgshapiro{ 324090792Sgshapiro if (a->w_mtime > b->w_mtime) 324190792Sgshapiro return 1; 324290792Sgshapiro else if (a->w_mtime < b->w_mtime) 324390792Sgshapiro return -1; 324490792Sgshapiro else 324590792Sgshapiro return 0; 324690792Sgshapiro} 324790792Sgshapiro#if _FFR_RHS 324890792Sgshapiro/* 324990792Sgshapiro** WORKCMPF7 -- compare function for ordering work based on shuffled host name. 325090792Sgshapiro** 325190792Sgshapiro** Sorts on lock status, host name, and priority in that order. 325290792Sgshapiro** 325390792Sgshapiro** Parameters: 325490792Sgshapiro** a -- the first argument. 325590792Sgshapiro** b -- the second argument. 325690792Sgshapiro** 325790792Sgshapiro** Returns: 325890792Sgshapiro** <0 if a < b 325990792Sgshapiro** 0 if a == b 326090792Sgshapiro** >0 if a > b 326190792Sgshapiro** 326290792Sgshapiro*/ 326390792Sgshapiro 326490792Sgshapirostatic int 326590792Sgshapiroworkcmpf7(a, b) 326690792Sgshapiro register WORK *a; 326790792Sgshapiro register WORK *b; 326890792Sgshapiro{ 326990792Sgshapiro int i; 327090792Sgshapiro 327190792Sgshapiro /* lock status */ 327290792Sgshapiro if (a->w_lock != b->w_lock) 327390792Sgshapiro return a->w_lock - b->w_lock; 327490792Sgshapiro 327590792Sgshapiro /* host name */ 327690792Sgshapiro if (a->w_host != NULL && b->w_host == NULL) 327790792Sgshapiro return 1; 327890792Sgshapiro else if (a->w_host == NULL && b->w_host != NULL) 327990792Sgshapiro return -1; 328090792Sgshapiro if (a->w_host != NULL && b->w_host != NULL && 328190792Sgshapiro (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0) 328290792Sgshapiro return i; 328390792Sgshapiro 328490792Sgshapiro /* job priority */ 328590792Sgshapiro return workcmpf0(a, b); 328690792Sgshapiro} 328790792Sgshapiro#endif /* _FFR_RHS */ 328890792Sgshapiro/* 328964562Sgshapiro** STRREV -- reverse string 329064562Sgshapiro** 329164562Sgshapiro** Returns a pointer to a new string that is the reverse of 329264562Sgshapiro** the string pointed to by fwd. The space for the new 329364562Sgshapiro** string is obtained using xalloc(). 329464562Sgshapiro** 329564562Sgshapiro** Parameters: 329664562Sgshapiro** fwd -- the string to reverse. 329764562Sgshapiro** 329864562Sgshapiro** Returns: 329964562Sgshapiro** the reversed string. 330064562Sgshapiro*/ 330164562Sgshapiro 330264562Sgshapirostatic char * 330364562Sgshapirostrrev(fwd) 330464562Sgshapiro char *fwd; 330564562Sgshapiro{ 330664562Sgshapiro char *rev = NULL; 330764562Sgshapiro int len, cnt; 330864562Sgshapiro 330964562Sgshapiro len = strlen(fwd); 331064562Sgshapiro rev = xalloc(len + 1); 331164562Sgshapiro for (cnt = 0; cnt < len; ++cnt) 331264562Sgshapiro rev[cnt] = fwd[len - cnt - 1]; 331364562Sgshapiro rev[len] = '\0'; 331464562Sgshapiro return rev; 331564562Sgshapiro} 331690792Sgshapiro 331790792Sgshapiro#if _FFR_RHS 331890792Sgshapiro 331990792Sgshapiro#define NASCII 128 332090792Sgshapiro#define NCHAR 256 332190792Sgshapiro 332290792Sgshapirostatic unsigned char ShuffledAlphabet[NCHAR]; 332390792Sgshapiro 332490792Sgshapirovoid 332590792Sgshapiroinit_shuffle_alphabet() 332690792Sgshapiro{ 332790792Sgshapiro static bool init = false; 332890792Sgshapiro int i; 332990792Sgshapiro 333090792Sgshapiro if (init) 333190792Sgshapiro return; 333290792Sgshapiro 333390792Sgshapiro /* fill the ShuffledAlphabet */ 333490792Sgshapiro for (i = 0; i < NCHAR; i++) 333590792Sgshapiro ShuffledAlphabet[i] = i; 333690792Sgshapiro 333790792Sgshapiro /* mix it */ 333890792Sgshapiro for (i = 1; i < NCHAR; i++) 333990792Sgshapiro { 334090792Sgshapiro register int j = get_random() % NCHAR; 334190792Sgshapiro register int tmp; 334290792Sgshapiro 334390792Sgshapiro tmp = ShuffledAlphabet[j]; 334490792Sgshapiro ShuffledAlphabet[j] = ShuffledAlphabet[i]; 334590792Sgshapiro ShuffledAlphabet[i] = tmp; 334690792Sgshapiro } 334790792Sgshapiro 334890792Sgshapiro /* make it case insensitive */ 334990792Sgshapiro for (i = 'A'; i <= 'Z'; i++) 335090792Sgshapiro ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A']; 335190792Sgshapiro 335290792Sgshapiro /* fill the upper part */ 335390792Sgshapiro for (i = 0; i < NCHAR; i++) 335490792Sgshapiro ShuffledAlphabet[i + NCHAR] = ShuffledAlphabet[i]; 335590792Sgshapiro init = true; 335690792Sgshapiro} 335790792Sgshapiro 335890792Sgshapirostatic int 335990792Sgshapirosm_strshufflecmp(a, b) 336090792Sgshapiro char *a; 336190792Sgshapiro char *b; 336290792Sgshapiro{ 336390792Sgshapiro const unsigned char *us1 = (const unsigned char *) a; 336490792Sgshapiro const unsigned char *us2 = (const unsigned char *) b; 336590792Sgshapiro 336690792Sgshapiro while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++]) 336790792Sgshapiro { 336890792Sgshapiro if (*us1++ == '\0') 336990792Sgshapiro return 0; 337090792Sgshapiro } 337190792Sgshapiro return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]); 337290792Sgshapiro} 337390792Sgshapiro#endif /* _FFR_RHS */ 337490792Sgshapiro 337590792Sgshapiro/* 337638032Speter** DOWORK -- do a work request. 337738032Speter** 337838032Speter** Parameters: 337990792Sgshapiro** qgrp -- the index of the queue group for the job. 338090792Sgshapiro** qdir -- the index of the queue directory for the job. 338138032Speter** id -- the ID of the job to run. 338238032Speter** forkflag -- if set, run this in background. 338338032Speter** requeueflag -- if set, reinstantiate the queue quickly. 338438032Speter** This is used when expanding aliases in the queue. 338538032Speter** If forkflag is also set, it doesn't wait for the 338638032Speter** child. 338738032Speter** e - the envelope in which to run it. 338838032Speter** 338938032Speter** Returns: 339038032Speter** process id of process that is running the queue job. 339138032Speter** 339238032Speter** Side Effects: 339338032Speter** The work request is satisfied if possible. 339438032Speter*/ 339538032Speter 339638032Speterpid_t 339790792Sgshapirodowork(qgrp, qdir, id, forkflag, requeueflag, e) 339890792Sgshapiro int qgrp; 339990792Sgshapiro int qdir; 340038032Speter char *id; 340138032Speter bool forkflag; 340238032Speter bool requeueflag; 340338032Speter register ENVELOPE *e; 340438032Speter{ 340538032Speter register pid_t pid; 340690792Sgshapiro SM_RPOOL_T *rpool; 340738032Speter 340838032Speter if (tTd(40, 1)) 340990792Sgshapiro sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id); 341038032Speter 341138032Speter /* 341238032Speter ** Fork for work. 341338032Speter */ 341438032Speter 341538032Speter if (forkflag) 341638032Speter { 341764562Sgshapiro /* 341864562Sgshapiro ** Since the delivery may happen in a child and the 341964562Sgshapiro ** parent does not wait, the parent may close the 342064562Sgshapiro ** maps thereby removing any shared memory used by 342164562Sgshapiro ** the map. Therefore, close the maps now so the 342264562Sgshapiro ** child will dynamically open them if necessary. 342364562Sgshapiro */ 342464562Sgshapiro 342590792Sgshapiro closemaps(false); 342664562Sgshapiro 342738032Speter pid = fork(); 342838032Speter if (pid < 0) 342938032Speter { 343038032Speter syserr("dowork: cannot fork"); 343138032Speter return 0; 343238032Speter } 343338032Speter else if (pid > 0) 343438032Speter { 343538032Speter /* parent -- clean out connection cache */ 343690792Sgshapiro mci_flush(false, NULL); 343738032Speter } 343838032Speter else 343938032Speter { 344090792Sgshapiro /* 344190792Sgshapiro ** Initialize exception stack and default exception 344290792Sgshapiro ** handler for child process. 344390792Sgshapiro */ 344490792Sgshapiro 344590792Sgshapiro /* Reset global flags */ 344690792Sgshapiro RestartRequest = NULL; 344790792Sgshapiro RestartWorkGroup = false; 344890792Sgshapiro ShutdownRequest = NULL; 344990792Sgshapiro PendingSignal = 0; 345090792Sgshapiro CurrentPid = getpid(); 345190792Sgshapiro sm_exc_newthread(fatal_error); 345290792Sgshapiro 345390792Sgshapiro /* 345490792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 345590792Sgshapiro */ 345690792Sgshapiro 345790792Sgshapiro if (OpMode == MD_SMTP || 345890792Sgshapiro OpMode == MD_DAEMON || 345990792Sgshapiro MaxQueueChildren > 0) 346090792Sgshapiro { 346190792Sgshapiro proc_list_clear(); 346290792Sgshapiro sm_releasesignal(SIGCHLD); 346390792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 346490792Sgshapiro } 346590792Sgshapiro 346638032Speter /* child -- error messages to the transcript */ 346790792Sgshapiro QuickAbort = OnlyOneError = false; 346838032Speter } 346938032Speter } 347038032Speter else 347138032Speter { 347238032Speter pid = 0; 347338032Speter } 347438032Speter 347538032Speter if (pid == 0) 347638032Speter { 347738032Speter /* 347838032Speter ** CHILD 347938032Speter ** Lock the control file to avoid duplicate deliveries. 348038032Speter ** Then run the file as though we had just read it. 348138032Speter ** We save an idea of the temporary name so we 348238032Speter ** can recover on interrupt. 348338032Speter */ 348438032Speter 348590792Sgshapiro if (forkflag) 348690792Sgshapiro { 348790792Sgshapiro /* Reset global flags */ 348890792Sgshapiro RestartRequest = NULL; 348990792Sgshapiro RestartWorkGroup = false; 349090792Sgshapiro ShutdownRequest = NULL; 349190792Sgshapiro PendingSignal = 0; 349290792Sgshapiro } 349377349Sgshapiro 349438032Speter /* set basic modes, etc. */ 349590792Sgshapiro sm_clear_events(); 349664562Sgshapiro clearstats(); 349790792Sgshapiro rpool = sm_rpool_new_x(NULL); 349890792Sgshapiro clearenvelope(e, false, rpool); 349938032Speter e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 350064562Sgshapiro set_delivery_mode(SM_DELIVER, e); 350138032Speter e->e_errormode = EM_MAIL; 350238032Speter e->e_id = id; 350390792Sgshapiro e->e_qgrp = qgrp; 350490792Sgshapiro e->e_qdir = qdir; 350590792Sgshapiro GrabTo = UseErrorsTo = false; 350638032Speter ExitStat = EX_OK; 350738032Speter if (forkflag) 350838032Speter { 350938032Speter disconnect(1, e); 351090792Sgshapiro set_op_mode(MD_QUEUERUN); 351138032Speter } 351290792Sgshapiro sm_setproctitle(true, e, "%s from queue", qid_printname(e)); 351338032Speter if (LogLevel > 76) 351490792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d", 351590792Sgshapiro (int) CurrentPid); 351638032Speter 351738032Speter /* don't use the headers from sendmail.cf... */ 351838032Speter e->e_header = NULL; 351938032Speter 352038032Speter /* read the queue control file -- return if locked */ 352190792Sgshapiro if (!readqf(e, false)) 352238032Speter { 352338032Speter if (tTd(40, 4) && e->e_id != NULL) 352490792Sgshapiro sm_dprintf("readqf(%s) failed\n", 352564562Sgshapiro qid_printname(e)); 352638032Speter e->e_id = NULL; 352738032Speter if (forkflag) 352890792Sgshapiro finis(false, true, EX_OK); 352938032Speter else 353090792Sgshapiro { 353190792Sgshapiro /* adding this frees 8 bytes */ 353290792Sgshapiro clearenvelope(e, false, rpool); 353390792Sgshapiro 353490792Sgshapiro /* adding this frees 12 bytes */ 353590792Sgshapiro sm_rpool_free(rpool); 353690792Sgshapiro e->e_rpool = NULL; 353738032Speter return 0; 353890792Sgshapiro } 353938032Speter } 354038032Speter 354138032Speter e->e_flags |= EF_INQUEUE; 354290792Sgshapiro eatheader(e, requeueflag, true); 354338032Speter 354438032Speter if (requeueflag) 354590792Sgshapiro queueup(e, false, false); 354638032Speter 354738032Speter /* do the delivery */ 354838032Speter sendall(e, SM_DELIVER); 354938032Speter 355038032Speter /* finish up and exit */ 355138032Speter if (forkflag) 355290792Sgshapiro finis(true, true, ExitStat); 355338032Speter else 355490792Sgshapiro { 355590792Sgshapiro dropenvelope(e, true, false); 355690792Sgshapiro sm_rpool_free(rpool); 355790792Sgshapiro e->e_rpool = NULL; 355890792Sgshapiro } 355938032Speter } 356038032Speter e->e_id = NULL; 356138032Speter return pid; 356238032Speter} 356390792Sgshapiro 356490792Sgshapiro/* 356590792Sgshapiro** DOWORKLIST -- process a list of envelopes as work requests 356690792Sgshapiro** 356790792Sgshapiro** Similar to dowork(), except that after forking, it processes an 356890792Sgshapiro** envelope and its siblings, treating each envelope as a work request. 356990792Sgshapiro** 357090792Sgshapiro** Parameters: 357190792Sgshapiro** el -- envelope to be processed including its siblings. 357290792Sgshapiro** forkflag -- if set, run this in background. 357390792Sgshapiro** requeueflag -- if set, reinstantiate the queue quickly. 357490792Sgshapiro** This is used when expanding aliases in the queue. 357590792Sgshapiro** If forkflag is also set, it doesn't wait for the 357690792Sgshapiro** child. 357790792Sgshapiro** 357890792Sgshapiro** Returns: 357990792Sgshapiro** process id of process that is running the queue job. 358090792Sgshapiro** 358190792Sgshapiro** Side Effects: 358290792Sgshapiro** The work request is satisfied if possible. 358390792Sgshapiro*/ 358490792Sgshapiro 358590792Sgshapiropid_t 358690792Sgshapirodoworklist(el, forkflag, requeueflag) 358790792Sgshapiro ENVELOPE *el; 358890792Sgshapiro bool forkflag; 358990792Sgshapiro bool requeueflag; 359090792Sgshapiro{ 359190792Sgshapiro register pid_t pid; 359290792Sgshapiro ENVELOPE *ei; 359390792Sgshapiro 359490792Sgshapiro if (tTd(40, 1)) 359590792Sgshapiro sm_dprintf("doworklist()\n"); 359690792Sgshapiro 359790792Sgshapiro /* 359890792Sgshapiro ** Fork for work. 359990792Sgshapiro */ 360090792Sgshapiro 360190792Sgshapiro if (forkflag) 360290792Sgshapiro { 360390792Sgshapiro /* 360490792Sgshapiro ** Since the delivery may happen in a child and the 360590792Sgshapiro ** parent does not wait, the parent may close the 360690792Sgshapiro ** maps thereby removing any shared memory used by 360790792Sgshapiro ** the map. Therefore, close the maps now so the 360890792Sgshapiro ** child will dynamically open them if necessary. 360990792Sgshapiro */ 361090792Sgshapiro 361190792Sgshapiro closemaps(false); 361290792Sgshapiro 361390792Sgshapiro pid = fork(); 361490792Sgshapiro if (pid < 0) 361590792Sgshapiro { 361690792Sgshapiro syserr("doworklist: cannot fork"); 361790792Sgshapiro return 0; 361890792Sgshapiro } 361990792Sgshapiro else if (pid > 0) 362090792Sgshapiro { 362190792Sgshapiro /* parent -- clean out connection cache */ 362290792Sgshapiro mci_flush(false, NULL); 362390792Sgshapiro } 362490792Sgshapiro else 362590792Sgshapiro { 362690792Sgshapiro /* 362790792Sgshapiro ** Initialize exception stack and default exception 362890792Sgshapiro ** handler for child process. 362990792Sgshapiro */ 363090792Sgshapiro 363190792Sgshapiro /* Reset global flags */ 363290792Sgshapiro RestartRequest = NULL; 363390792Sgshapiro RestartWorkGroup = false; 363490792Sgshapiro ShutdownRequest = NULL; 363590792Sgshapiro PendingSignal = 0; 363690792Sgshapiro CurrentPid = getpid(); 363790792Sgshapiro sm_exc_newthread(fatal_error); 363890792Sgshapiro 363990792Sgshapiro /* 364090792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 364190792Sgshapiro */ 364290792Sgshapiro 364390792Sgshapiro if (OpMode == MD_SMTP || 364490792Sgshapiro OpMode == MD_DAEMON || 364590792Sgshapiro MaxQueueChildren > 0) 364690792Sgshapiro { 364790792Sgshapiro proc_list_clear(); 364890792Sgshapiro sm_releasesignal(SIGCHLD); 364990792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 365090792Sgshapiro } 365190792Sgshapiro 365290792Sgshapiro /* child -- error messages to the transcript */ 365390792Sgshapiro QuickAbort = OnlyOneError = false; 365490792Sgshapiro } 365590792Sgshapiro } 365690792Sgshapiro else 365790792Sgshapiro { 365890792Sgshapiro pid = 0; 365990792Sgshapiro } 366090792Sgshapiro 366190792Sgshapiro if (pid != 0) 366290792Sgshapiro return pid; 366390792Sgshapiro 366490792Sgshapiro /* 366590792Sgshapiro ** IN CHILD 366690792Sgshapiro ** Lock the control file to avoid duplicate deliveries. 366790792Sgshapiro ** Then run the file as though we had just read it. 366890792Sgshapiro ** We save an idea of the temporary name so we 366990792Sgshapiro ** can recover on interrupt. 367090792Sgshapiro */ 367190792Sgshapiro 367290792Sgshapiro if (forkflag) 367390792Sgshapiro { 367490792Sgshapiro /* Reset global flags */ 367590792Sgshapiro RestartRequest = NULL; 367690792Sgshapiro RestartWorkGroup = false; 367790792Sgshapiro ShutdownRequest = NULL; 367890792Sgshapiro PendingSignal = 0; 367990792Sgshapiro } 368090792Sgshapiro 368190792Sgshapiro /* set basic modes, etc. */ 368290792Sgshapiro sm_clear_events(); 368390792Sgshapiro clearstats(); 368490792Sgshapiro GrabTo = UseErrorsTo = false; 368590792Sgshapiro ExitStat = EX_OK; 368690792Sgshapiro if (forkflag) 368790792Sgshapiro { 368890792Sgshapiro disconnect(1, el); 368990792Sgshapiro set_op_mode(MD_QUEUERUN); 369090792Sgshapiro } 369190792Sgshapiro if (LogLevel > 76) 369290792Sgshapiro sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d", 369390792Sgshapiro (int) CurrentPid); 369490792Sgshapiro 369590792Sgshapiro for (ei = el; ei != NULL; ei = ei->e_sibling) 369690792Sgshapiro { 369790792Sgshapiro ENVELOPE e; 369890792Sgshapiro SM_RPOOL_T *rpool; 369990792Sgshapiro 370090792Sgshapiro if (WILL_BE_QUEUED(ei->e_sendmode)) 370190792Sgshapiro continue; 370290792Sgshapiro#if _FFR_QUARANTINE 370390792Sgshapiro else if (QueueMode != QM_QUARANTINE && 370490792Sgshapiro ei->e_quarmsg != NULL) 370590792Sgshapiro continue; 370690792Sgshapiro#endif /* _FFR_QUARANTINE */ 370790792Sgshapiro 370890792Sgshapiro rpool = sm_rpool_new_x(NULL); 370990792Sgshapiro clearenvelope(&e, true, rpool); 371090792Sgshapiro e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 371190792Sgshapiro set_delivery_mode(SM_DELIVER, &e); 371290792Sgshapiro e.e_errormode = EM_MAIL; 371390792Sgshapiro e.e_id = ei->e_id; 371490792Sgshapiro e.e_qgrp = ei->e_qgrp; 371590792Sgshapiro e.e_qdir = ei->e_qdir; 371690792Sgshapiro openxscript(&e); 371790792Sgshapiro sm_setproctitle(true, &e, "%s from queue", qid_printname(&e)); 371890792Sgshapiro 371990792Sgshapiro /* don't use the headers from sendmail.cf... */ 372090792Sgshapiro e.e_header = NULL; 372190792Sgshapiro CurEnv = &e; 372290792Sgshapiro 372390792Sgshapiro /* read the queue control file -- return if locked */ 372490792Sgshapiro if (readqf(&e, false)) 372590792Sgshapiro { 372690792Sgshapiro e.e_flags |= EF_INQUEUE; 372790792Sgshapiro eatheader(&e, requeueflag, true); 372890792Sgshapiro 372990792Sgshapiro if (requeueflag) 373090792Sgshapiro queueup(&e, false, false); 373190792Sgshapiro 373290792Sgshapiro /* do the delivery */ 373390792Sgshapiro sendall(&e, SM_DELIVER); 373490792Sgshapiro dropenvelope(&e, true, false); 373590792Sgshapiro } 373690792Sgshapiro else 373790792Sgshapiro { 373890792Sgshapiro if (tTd(40, 4) && e.e_id != NULL) 373990792Sgshapiro sm_dprintf("readqf(%s) failed\n", 374090792Sgshapiro qid_printname(&e)); 374190792Sgshapiro } 374290792Sgshapiro sm_rpool_free(rpool); 374390792Sgshapiro ei->e_id = NULL; 374490792Sgshapiro } 374590792Sgshapiro 374690792Sgshapiro /* restore CurEnv */ 374790792Sgshapiro CurEnv = el; 374890792Sgshapiro 374990792Sgshapiro /* finish up and exit */ 375090792Sgshapiro if (forkflag) 375190792Sgshapiro finis(true, true, ExitStat); 375290792Sgshapiro return 0; 375390792Sgshapiro} 375490792Sgshapiro/* 375538032Speter** READQF -- read queue file and set up environment. 375638032Speter** 375738032Speter** Parameters: 375838032Speter** e -- the envelope of the job to run. 375990792Sgshapiro** openonly -- only open the qf (returned as e_lockfp) 376038032Speter** 376138032Speter** Returns: 376290792Sgshapiro** true if it successfully read the queue file. 376390792Sgshapiro** false otherwise. 376438032Speter** 376538032Speter** Side Effects: 376638032Speter** The queue file is returned locked. 376738032Speter*/ 376838032Speter 376964562Sgshapirostatic bool 377090792Sgshapiroreadqf(e, openonly) 377138032Speter register ENVELOPE *e; 377290792Sgshapiro bool openonly; 377338032Speter{ 377490792Sgshapiro register SM_FILE_T *qfp; 377538032Speter ADDRESS *ctladdr; 377677349Sgshapiro struct stat st, stf; 377738032Speter char *bp; 377838032Speter int qfver = 0; 377938032Speter long hdrsize = 0; 378038032Speter register char *p; 378190792Sgshapiro char *frcpt = NULL; 378238032Speter char *orcpt = NULL; 378390792Sgshapiro bool nomore = false; 378490792Sgshapiro bool bogus = false; 378564562Sgshapiro MODE_T qsafe; 378694334Sgshapiro char *err; 378764562Sgshapiro char qf[MAXPATHLEN]; 378838032Speter char buf[MAXLINE]; 378938032Speter 379038032Speter /* 379138032Speter ** Read and process the file. 379238032Speter */ 379338032Speter 379490792Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof qf); 379590792Sgshapiro qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR, NULL); 379638032Speter if (qfp == NULL) 379738032Speter { 379864562Sgshapiro int save_errno = errno; 379964562Sgshapiro 380038032Speter if (tTd(40, 8)) 380190792Sgshapiro sm_dprintf("readqf(%s): sm_io_open failure (%s)\n", 380290792Sgshapiro qf, sm_errstring(errno)); 380364562Sgshapiro errno = save_errno; 380464562Sgshapiro if (errno != ENOENT 380564562Sgshapiro ) 380638032Speter syserr("readqf: no control file %s", qf); 380790792Sgshapiro RELEASE_QUEUE; 380890792Sgshapiro return false; 380938032Speter } 381038032Speter 381190792Sgshapiro if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL, 381290792Sgshapiro LOCK_EX|LOCK_NB)) 381338032Speter { 381438032Speter /* being processed by another queuer */ 381564562Sgshapiro if (Verbose) 381690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 381790792Sgshapiro "%s: locked\n", e->e_id); 381864562Sgshapiro if (tTd(40, 8)) 381990792Sgshapiro sm_dprintf("%s: locked\n", e->e_id); 382038032Speter if (LogLevel > 19) 382138032Speter sm_syslog(LOG_DEBUG, e->e_id, "locked"); 382290792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 382390792Sgshapiro RELEASE_QUEUE; 382490792Sgshapiro return false; 382538032Speter } 382638032Speter 382738032Speter /* 382877349Sgshapiro ** Prevent locking race condition. 382977349Sgshapiro ** 383077349Sgshapiro ** Process A: readqf(): qfp = fopen(qffile) 383177349Sgshapiro ** Process B: queueup(): rename(tf, qf) 383277349Sgshapiro ** Process B: unlocks(tf) 383377349Sgshapiro ** Process A: lockfile(qf); 383477349Sgshapiro ** 383577349Sgshapiro ** Process A (us) has the old qf file (before the rename deleted 383677349Sgshapiro ** the directory entry) and will be delivering based on old data. 383777349Sgshapiro ** This can lead to multiple deliveries of the same recipients. 383877349Sgshapiro ** 383977349Sgshapiro ** Catch this by checking if the underlying qf file has changed 384077349Sgshapiro ** *after* acquiring our lock and if so, act as though the file 384177349Sgshapiro ** was still locked (i.e., just return like the lockfile() case 384277349Sgshapiro ** above. 384338032Speter */ 384438032Speter 384577349Sgshapiro if (stat(qf, &stf) < 0 || 384690792Sgshapiro fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0) 384738032Speter { 384838032Speter /* must have been being processed by someone else */ 384938032Speter if (tTd(40, 8)) 385090792Sgshapiro sm_dprintf("readqf(%s): [f]stat failure (%s)\n", 385190792Sgshapiro qf, sm_errstring(errno)); 385290792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 385390792Sgshapiro RELEASE_QUEUE; 385490792Sgshapiro return false; 385538032Speter } 385638032Speter 385777349Sgshapiro if (st.st_nlink != stf.st_nlink || 385877349Sgshapiro st.st_dev != stf.st_dev || 385990792Sgshapiro ST_INODE(st) != ST_INODE(stf) || 386090792Sgshapiro#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 386177349Sgshapiro st.st_gen != stf.st_gen || 386290792Sgshapiro#endif /* HAS_ST_GEN && 0 */ 386377349Sgshapiro st.st_uid != stf.st_uid || 386477349Sgshapiro st.st_gid != stf.st_gid || 386577349Sgshapiro st.st_size != stf.st_size) 386677349Sgshapiro { 386777349Sgshapiro /* changed after opened */ 386877349Sgshapiro if (Verbose) 386990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 387090792Sgshapiro "%s: changed\n", e->e_id); 387177349Sgshapiro if (tTd(40, 8)) 387290792Sgshapiro sm_dprintf("%s: changed\n", e->e_id); 387377349Sgshapiro if (LogLevel > 19) 387477349Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "changed"); 387590792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 387690792Sgshapiro RELEASE_QUEUE; 387790792Sgshapiro return false; 387877349Sgshapiro } 387977349Sgshapiro 388077349Sgshapiro /* 388177349Sgshapiro ** Check the queue file for plausibility to avoid attacks. 388277349Sgshapiro */ 388377349Sgshapiro 388464562Sgshapiro qsafe = S_IWOTH|S_IWGRP; 388564562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 388664562Sgshapiro qsafe &= ~S_IWGRP; 388764562Sgshapiro 388890792Sgshapiro bogus = st.st_uid != geteuid() && 388990792Sgshapiro st.st_uid != TrustedUid && 389090792Sgshapiro geteuid() != RealUid; 389190792Sgshapiro 389290792Sgshapiro /* 389390792Sgshapiro ** If this qf file results from a set-group-ID binary, then 389490792Sgshapiro ** we check whether the directory is group-writable, 389590792Sgshapiro ** the queue file mode contains the group-writable bit, and 389690792Sgshapiro ** the groups are the same. 389790792Sgshapiro ** Notice: this requires that the set-group-ID binary is used to 389890792Sgshapiro ** run the queue! 389990792Sgshapiro */ 390090792Sgshapiro 390190792Sgshapiro if (bogus && st.st_gid == getegid() && UseMSP) 390238032Speter { 390390792Sgshapiro char delim; 390490792Sgshapiro struct stat dst; 390590792Sgshapiro 390690792Sgshapiro bp = SM_LAST_DIR_DELIM(qf); 390790792Sgshapiro if (bp == NULL) 390890792Sgshapiro delim = '\0'; 390990792Sgshapiro else 391090792Sgshapiro { 391190792Sgshapiro delim = *bp; 391290792Sgshapiro *bp = '\0'; 391390792Sgshapiro } 391490792Sgshapiro if (stat(delim == '\0' ? "." : qf, &dst) < 0) 391590792Sgshapiro syserr("readqf: cannot stat directory %s", 391690792Sgshapiro delim == '\0' ? "." : qf); 391790792Sgshapiro else 391890792Sgshapiro { 391990792Sgshapiro bogus = !(bitset(S_IWGRP, QueueFileMode) && 392090792Sgshapiro bitset(S_IWGRP, dst.st_mode) && 392190792Sgshapiro dst.st_gid == st.st_gid); 392290792Sgshapiro } 392390792Sgshapiro if (delim != '\0') 392490792Sgshapiro *bp = delim; 392590792Sgshapiro } 392690792Sgshapiro if (!bogus) 392790792Sgshapiro bogus = bitset(qsafe, st.st_mode); 392890792Sgshapiro if (bogus) 392990792Sgshapiro { 393038032Speter if (LogLevel > 0) 393138032Speter { 393238032Speter sm_syslog(LOG_ALERT, e->e_id, 393390792Sgshapiro "bogus queue file, uid=%d, gid=%d, mode=%o", 393490792Sgshapiro st.st_uid, st.st_gid, st.st_mode); 393538032Speter } 393638032Speter if (tTd(40, 8)) 393790792Sgshapiro sm_dprintf("readqf(%s): bogus file\n", qf); 393890792Sgshapiro e->e_flags |= EF_INQUEUE; 393990792Sgshapiro if (!openonly) 394090792Sgshapiro loseqfile(e, "bogus file uid/gid in mqueue"); 394190792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 394290792Sgshapiro RELEASE_QUEUE; 394390792Sgshapiro return false; 394438032Speter } 394538032Speter 394638032Speter if (st.st_size == 0) 394738032Speter { 394838032Speter /* must be a bogus file -- if also old, just remove it */ 394990792Sgshapiro if (!openonly && st.st_ctime + 10 * 60 < curtime()) 395038032Speter { 395190792Sgshapiro (void) xunlink(queuename(e, DATAFL_LETTER)); 395290792Sgshapiro (void) xunlink(queuename(e, ANYQFL_LETTER)); 395338032Speter } 395490792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 395590792Sgshapiro RELEASE_QUEUE; 395690792Sgshapiro return false; 395738032Speter } 395838032Speter 395938032Speter if (st.st_nlink == 0) 396038032Speter { 396138032Speter /* 396238032Speter ** Race condition -- we got a file just as it was being 396338032Speter ** unlinked. Just assume it is zero length. 396438032Speter */ 396538032Speter 396690792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 396790792Sgshapiro RELEASE_QUEUE; 396890792Sgshapiro return false; 396938032Speter } 397038032Speter 397190792Sgshapiro#if _FFR_TRUSTED_QF 397290792Sgshapiro /* 397390792Sgshapiro ** If we don't own the file mark it as unsafe. 397490792Sgshapiro ** However, allow TrustedUser to own it as well 397590792Sgshapiro ** in case TrustedUser manipulates the queue. 397690792Sgshapiro */ 397790792Sgshapiro 397890792Sgshapiro if (st.st_uid != geteuid() && st.st_uid != TrustedUid) 397990792Sgshapiro e->e_flags |= EF_UNSAFE; 398090792Sgshapiro#else /* _FFR_TRUSTED_QF */ 398190792Sgshapiro /* If we don't own the file mark it as unsafe */ 398290792Sgshapiro if (st.st_uid != geteuid()) 398390792Sgshapiro e->e_flags |= EF_UNSAFE; 398490792Sgshapiro#endif /* _FFR_TRUSTED_QF */ 398590792Sgshapiro 398638032Speter /* good file -- save this lock */ 398738032Speter e->e_lockfp = qfp; 398838032Speter 398990792Sgshapiro /* Just wanted the open file */ 399090792Sgshapiro if (openonly) 399190792Sgshapiro return true; 399290792Sgshapiro 399338032Speter /* do basic system initialization */ 399438032Speter initsys(e); 399590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 399638032Speter 399738032Speter LineNumber = 0; 399838032Speter e->e_flags |= EF_GLOBALERRS; 399990792Sgshapiro set_op_mode(MD_QUEUERUN); 400038032Speter ctladdr = NULL; 400190792Sgshapiro#if _FFR_QUARANTINE 400290792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 400390792Sgshapiro#endif /* _FFR_QUARANTINE */ 400490792Sgshapiro e->e_dfqgrp = e->e_qgrp; 400590792Sgshapiro e->e_dfqdir = e->e_qdir; 400690792Sgshapiro#if _FFR_QUEUE_MACRO 400790792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{queue}"), 400890792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 400990792Sgshapiro#endif /* _FFR_QUEUE_MACRO */ 401038032Speter e->e_dfino = -1; 401138032Speter e->e_msgsize = -1; 401290792Sgshapiro#if _FFR_QUEUEDELAY 401364562Sgshapiro e->e_queuealg = QD_LINEAR; 401464562Sgshapiro e->e_queuedelay = (time_t) 0; 401590792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 401638032Speter while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) 401738032Speter { 401890792Sgshapiro unsigned long qflags; 401938032Speter ADDRESS *q; 402090792Sgshapiro int r; 402171345Sgshapiro time_t now; 402238032Speter auto char *ep; 402338032Speter 402438032Speter if (tTd(40, 4)) 402590792Sgshapiro sm_dprintf("+++++ %s\n", bp); 402638032Speter if (nomore) 402738032Speter { 402838032Speter /* hack attack */ 402990792Sgshapiro hackattack: 403090792Sgshapiro syserr("SECURITY ALERT: extra or bogus data in queue file: %s", 403190792Sgshapiro bp); 403294334Sgshapiro err = "bogus queue line"; 403394334Sgshapiro goto fail; 403438032Speter } 403538032Speter switch (bp[0]) 403638032Speter { 403790792Sgshapiro case 'A': /* AUTH= parameter */ 403890792Sgshapiro if (!xtextok(&bp[1])) 403990792Sgshapiro goto hackattack; 404090792Sgshapiro e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 404190792Sgshapiro break; 404238032Speter 404390792Sgshapiro case 'B': /* body type */ 404490792Sgshapiro r = check_bodytype(&bp[1]); 404590792Sgshapiro if (!BODYTYPE_VALID(r)) 404690792Sgshapiro goto hackattack; 404790792Sgshapiro e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 404890792Sgshapiro break; 404990792Sgshapiro 405038032Speter case 'C': /* specify controlling user */ 405190792Sgshapiro ctladdr = setctluser(&bp[1], qfver, e); 405238032Speter break; 405338032Speter 405490792Sgshapiro case 'D': /* data file name */ 405590792Sgshapiro /* obsolete -- ignore */ 405638032Speter break; 405738032Speter 405890792Sgshapiro case 'd': /* data file directory name */ 405938032Speter { 406090792Sgshapiro int qgrp, qdir; 406190792Sgshapiro 406290792Sgshapiro#if _FFR_MSP_PARANOIA 406390792Sgshapiro /* forbid queue groups in MSP? */ 406490792Sgshapiro if (UseMSP) 406590792Sgshapiro goto hackattack; 406690792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 406790792Sgshapiro for (qgrp = 0; 406890792Sgshapiro qgrp < NumQueue && Queue[qgrp] != NULL; 406990792Sgshapiro ++qgrp) 407038032Speter { 407190792Sgshapiro for (qdir = 0; 407290792Sgshapiro qdir < Queue[qgrp]->qg_numqueues; 407390792Sgshapiro ++qdir) 407438032Speter { 407590792Sgshapiro if (strcmp(&bp[1], 407690792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name) 407790792Sgshapiro == 0) 407890792Sgshapiro { 407990792Sgshapiro e->e_dfqgrp = qgrp; 408090792Sgshapiro e->e_dfqdir = qdir; 408190792Sgshapiro goto done; 408290792Sgshapiro } 408338032Speter } 408438032Speter } 408594334Sgshapiro err = "bogus queue file directory"; 408694334Sgshapiro goto fail; 408790792Sgshapiro done: 408890792Sgshapiro break; 408938032Speter } 409038032Speter 409138032Speter case 'E': /* specify error recipient */ 409238032Speter /* no longer used */ 409338032Speter break; 409438032Speter 409590792Sgshapiro case 'F': /* flag bits */ 409690792Sgshapiro if (strncmp(bp, "From ", 5) == 0) 409790792Sgshapiro { 409890792Sgshapiro /* we are being spoofed! */ 409990792Sgshapiro syserr("SECURITY ALERT: bogus qf line %s", bp); 410094334Sgshapiro err = "bogus queue line"; 410194334Sgshapiro goto fail; 410290792Sgshapiro } 410390792Sgshapiro for (p = &bp[1]; *p != '\0'; p++) 410490792Sgshapiro { 410590792Sgshapiro switch (*p) 410690792Sgshapiro { 410790792Sgshapiro case '8': /* has 8 bit data */ 410890792Sgshapiro e->e_flags |= EF_HAS8BIT; 410990792Sgshapiro break; 411038032Speter 411190792Sgshapiro case 'b': /* delete Bcc: header */ 411290792Sgshapiro e->e_flags |= EF_DELETE_BCC; 411390792Sgshapiro break; 411438032Speter 411590792Sgshapiro case 'd': /* envelope has DSN RET= */ 411690792Sgshapiro e->e_flags |= EF_RET_PARAM; 411790792Sgshapiro break; 411838032Speter 411990792Sgshapiro case 'n': /* don't return body */ 412090792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 412190792Sgshapiro break; 412290792Sgshapiro 412390792Sgshapiro case 'r': /* response */ 412490792Sgshapiro e->e_flags |= EF_RESPONSE; 412590792Sgshapiro break; 412690792Sgshapiro 412790792Sgshapiro case 's': /* split */ 412890792Sgshapiro e->e_flags |= EF_SPLIT; 412990792Sgshapiro break; 413090792Sgshapiro 413190792Sgshapiro case 'w': /* warning sent */ 413290792Sgshapiro e->e_flags |= EF_WARNING; 413390792Sgshapiro break; 413490792Sgshapiro } 413590792Sgshapiro } 413638032Speter break; 413738032Speter 413890792Sgshapiro#if _FFR_QUEUEDELAY 413990792Sgshapiro case 'G': /* queue delay algorithm */ 414090792Sgshapiro e->e_queuealg = atoi(&buf[1]); 414138032Speter break; 414290792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 414338032Speter 414490792Sgshapiro#if _FFR_QUARANTINE 414590792Sgshapiro case 'q': /* quarantine reason */ 414690792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 414790792Sgshapiro macdefine(&e->e_macro, A_PERM, 414890792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 414938032Speter break; 415090792Sgshapiro#endif /* _FFR_QUARANTINE */ 415138032Speter 415290792Sgshapiro case 'H': /* header */ 415394334Sgshapiro 415494334Sgshapiro /* 415594334Sgshapiro ** count size before chompheader() destroys the line. 415694334Sgshapiro ** this isn't accurate due to macro expansion, but 415794334Sgshapiro ** better than before. "+3" to skip H?? at least. 415894334Sgshapiro */ 415994334Sgshapiro 416094334Sgshapiro hdrsize += strlen(bp + 3); 416190792Sgshapiro (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); 416238032Speter break; 416338032Speter 416438032Speter case 'I': /* data file's inode number */ 416538032Speter /* regenerated below */ 416638032Speter break; 416738032Speter 416880785Sgshapiro case 'K': /* time of last delivery attempt */ 416938032Speter e->e_dtime = atol(&buf[1]); 417038032Speter break; 417138032Speter 417290792Sgshapiro case 'L': /* Solaris Content-Length: */ 417390792Sgshapiro case 'M': /* message */ 417490792Sgshapiro /* ignore this; we want a new message next time */ 417564562Sgshapiro break; 417664562Sgshapiro 417738032Speter case 'N': /* number of delivery attempts */ 417838032Speter e->e_ntries = atoi(&buf[1]); 417938032Speter 418038032Speter /* if this has been tried recently, let it be */ 418171345Sgshapiro now = curtime(); 418271345Sgshapiro if (e->e_ntries > 0 && e->e_dtime <= now && 418371345Sgshapiro now < e->e_dtime + queuedelay(e)) 418438032Speter { 418564562Sgshapiro char *howlong; 418638032Speter 418790792Sgshapiro howlong = pintvl(now - e->e_dtime, true); 418864562Sgshapiro if (Verbose) 418990792Sgshapiro (void) sm_io_fprintf(smioout, 419090792Sgshapiro SM_TIME_DEFAULT, 419190792Sgshapiro "%s: too young (%s)\n", 419290792Sgshapiro e->e_id, howlong); 419364562Sgshapiro if (tTd(40, 8)) 419490792Sgshapiro sm_dprintf("%s: too young (%s)\n", 419538032Speter e->e_id, howlong); 419638032Speter if (LogLevel > 19) 419738032Speter sm_syslog(LOG_DEBUG, e->e_id, 419864562Sgshapiro "too young (%s)", 419964562Sgshapiro howlong); 420038032Speter e->e_id = NULL; 420138032Speter unlockqueue(e); 420290792Sgshapiro RELEASE_QUEUE; 420390792Sgshapiro return false; 420438032Speter } 420590792Sgshapiro macdefine(&e->e_macro, A_TEMP, 420690792Sgshapiro macid("{ntries}"), &buf[1]); 420764562Sgshapiro 420890792Sgshapiro#if NAMED_BIND 420964562Sgshapiro /* adjust BIND parameters immediately */ 421064562Sgshapiro if (e->e_ntries == 0) 421164562Sgshapiro { 421264562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 421364562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 421464562Sgshapiro } 421564562Sgshapiro else 421664562Sgshapiro { 421764562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_NORMAL]; 421864562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL]; 421964562Sgshapiro } 422090792Sgshapiro#endif /* NAMED_BIND */ 422138032Speter break; 422238032Speter 422338032Speter case 'P': /* message priority */ 422438032Speter e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 422538032Speter break; 422638032Speter 422790792Sgshapiro case 'Q': /* original recipient */ 422890792Sgshapiro orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 422990792Sgshapiro break; 423090792Sgshapiro 423198841Sgshapiro case 'r': /* final recipient */ 423290792Sgshapiro frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 423390792Sgshapiro break; 423490792Sgshapiro 423590792Sgshapiro case 'R': /* specify recipient */ 423690792Sgshapiro p = bp; 423790792Sgshapiro qflags = 0; 423890792Sgshapiro if (qfver >= 1) 423938032Speter { 424090792Sgshapiro /* get flag bits */ 424190792Sgshapiro while (*++p != '\0' && *p != ':') 424238032Speter { 424390792Sgshapiro switch (*p) 424490792Sgshapiro { 424590792Sgshapiro case 'N': 424690792Sgshapiro qflags |= QHASNOTIFY; 424790792Sgshapiro break; 424838032Speter 424990792Sgshapiro case 'S': 425090792Sgshapiro qflags |= QPINGONSUCCESS; 425190792Sgshapiro break; 425238032Speter 425390792Sgshapiro case 'F': 425490792Sgshapiro qflags |= QPINGONFAILURE; 425590792Sgshapiro break; 425638032Speter 425790792Sgshapiro case 'D': 425890792Sgshapiro qflags |= QPINGONDELAY; 425990792Sgshapiro break; 426038032Speter 426190792Sgshapiro case 'P': 426290792Sgshapiro qflags |= QPRIMARY; 426390792Sgshapiro break; 426438032Speter 426590792Sgshapiro case 'A': 426690792Sgshapiro if (ctladdr != NULL) 426790792Sgshapiro ctladdr->q_flags |= QALIAS; 426890792Sgshapiro break; 426990792Sgshapiro 427090792Sgshapiro default: /* ignore or complain? */ 427190792Sgshapiro break; 427290792Sgshapiro } 427338032Speter } 427438032Speter } 427590792Sgshapiro else 427690792Sgshapiro qflags |= QPRIMARY; 427790792Sgshapiro q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e, 427890792Sgshapiro true); 427990792Sgshapiro if (q != NULL) 428090792Sgshapiro { 428194334Sgshapiro /* make sure we keep the current qgrp */ 428294334Sgshapiro if (ISVALIDQGRP(e->e_qgrp)) 428394334Sgshapiro q->q_qgrp = e->e_qgrp; 428490792Sgshapiro q->q_alias = ctladdr; 428590792Sgshapiro if (qfver >= 1) 428690792Sgshapiro q->q_flags &= ~Q_PINGFLAGS; 428790792Sgshapiro q->q_flags |= qflags; 428890792Sgshapiro q->q_finalrcpt = frcpt; 428990792Sgshapiro q->q_orcpt = orcpt; 429090792Sgshapiro (void) recipient(q, &e->e_sendqueue, 0, e); 429190792Sgshapiro } 429290792Sgshapiro frcpt = NULL; 429390792Sgshapiro orcpt = NULL; 429438032Speter break; 429538032Speter 429690792Sgshapiro case 'S': /* sender */ 429790792Sgshapiro setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]), 429890792Sgshapiro e, NULL, '\0', true); 429938032Speter break; 430038032Speter 430190792Sgshapiro case 'T': /* init time */ 430290792Sgshapiro e->e_ctime = atol(&bp[1]); 430364562Sgshapiro break; 430464562Sgshapiro 430590792Sgshapiro case 'V': /* queue file version number */ 430690792Sgshapiro qfver = atoi(&bp[1]); 430790792Sgshapiro if (queuedelay_qfver_unsupported(qfver)) 430890792Sgshapiro syserr("queue file version %d not supported: %s", 430990792Sgshapiro qfver, 431090792Sgshapiro "sendmail not compiled with _FFR_QUEUEDELAY"); 431190792Sgshapiro if (qfver <= QF_VERSION) 431290792Sgshapiro break; 431390792Sgshapiro syserr("Version number in queue file (%d) greater than max (%d)", 431490792Sgshapiro qfver, QF_VERSION); 431594334Sgshapiro err = "unsupported queue file version"; 431694334Sgshapiro goto fail; 431790792Sgshapiro /* NOTREACHED */ 431890792Sgshapiro break; 431990792Sgshapiro 432090792Sgshapiro#if _FFR_QUEUEDELAY 432190792Sgshapiro case 'Y': /* current delay */ 432290792Sgshapiro e->e_queuedelay = (time_t) atol(&buf[1]); 432390792Sgshapiro break; 432490792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 432590792Sgshapiro 432690792Sgshapiro case 'Z': /* original envelope id from ESMTP */ 432790792Sgshapiro e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 432890792Sgshapiro macdefine(&e->e_macro, A_PERM, 432990792Sgshapiro macid("{dsn_envid}"), e->e_envid); 433090792Sgshapiro break; 433190792Sgshapiro 433290792Sgshapiro case '!': /* deliver by */ 433390792Sgshapiro 433490792Sgshapiro /* format: flag (1 char) space long-integer */ 433590792Sgshapiro e->e_dlvr_flag = buf[1]; 433690792Sgshapiro e->e_deliver_by = strtol(&buf[3], NULL, 10); 433790792Sgshapiro 433838032Speter case '$': /* define macro */ 433964562Sgshapiro { 434064562Sgshapiro char *p; 434164562Sgshapiro 434290792Sgshapiro /* XXX elimate p? */ 434390792Sgshapiro r = macid_parse(&bp[1], &ep); 434490792Sgshapiro if (r == 0) 434571345Sgshapiro break; 434690792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, ep); 434790792Sgshapiro macdefine(&e->e_macro, A_PERM, r, p); 434864562Sgshapiro } 434938032Speter break; 435038032Speter 435138032Speter case '.': /* terminate file */ 435290792Sgshapiro nomore = true; 435338032Speter break; 435438032Speter 435538032Speter default: 435638032Speter syserr("readqf: %s: line %d: bad line \"%s\"", 435738032Speter qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); 435894334Sgshapiro err = "unrecognized line"; 435994334Sgshapiro goto fail; 436038032Speter } 436138032Speter 436238032Speter if (bp != buf) 436390792Sgshapiro sm_free(bp); /* XXX */ 436438032Speter } 436538032Speter 436638032Speter /* 436738032Speter ** If we haven't read any lines, this queue file is empty. 436838032Speter ** Arrange to remove it without referencing any null pointers. 436938032Speter */ 437038032Speter 437138032Speter if (LineNumber == 0) 437238032Speter { 437338032Speter errno = 0; 437490792Sgshapiro e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE; 437590792Sgshapiro RELEASE_QUEUE; 437690792Sgshapiro return true; 437738032Speter } 437838032Speter 437990792Sgshapiro /* Check to make sure we have a complete queue file read */ 438090792Sgshapiro if (!nomore) 438190792Sgshapiro { 438290792Sgshapiro syserr("readqf: %s: incomplete queue file read", qf); 438390792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 438490792Sgshapiro RELEASE_QUEUE; 438590792Sgshapiro return false; 438690792Sgshapiro } 438790792Sgshapiro 438864562Sgshapiro /* possibly set ${dsn_ret} macro */ 438964562Sgshapiro if (bitset(EF_RET_PARAM, e->e_flags)) 439064562Sgshapiro { 439164562Sgshapiro if (bitset(EF_NO_BODY_RETN, e->e_flags)) 439290792Sgshapiro macdefine(&e->e_macro, A_PERM, 439390792Sgshapiro macid("{dsn_ret}"), "hdrs"); 439464562Sgshapiro else 439590792Sgshapiro macdefine(&e->e_macro, A_PERM, 439690792Sgshapiro macid("{dsn_ret}"), "full"); 439764562Sgshapiro } 439864562Sgshapiro 439938032Speter /* 440038032Speter ** Arrange to read the data file. 440138032Speter */ 440238032Speter 440390792Sgshapiro p = queuename(e, DATAFL_LETTER); 440490792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY, 440590792Sgshapiro NULL); 440638032Speter if (e->e_dfp == NULL) 440738032Speter { 440838032Speter syserr("readqf: cannot open %s", p); 440938032Speter } 441038032Speter else 441138032Speter { 441238032Speter e->e_flags |= EF_HAS_DF; 441390792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st) 441490792Sgshapiro >= 0) 441538032Speter { 441638032Speter e->e_msgsize = st.st_size + hdrsize; 441738032Speter e->e_dfdev = st.st_dev; 441890792Sgshapiro e->e_dfino = ST_INODE(st); 441998121Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", 442098121Sgshapiro e->e_msgsize); 442198121Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), 442298121Sgshapiro buf); 442338032Speter } 442438032Speter } 442538032Speter 442690792Sgshapiro RELEASE_QUEUE; 442790792Sgshapiro return true; 442894334Sgshapiro 442994334Sgshapiro fail: 443094334Sgshapiro /* 443194334Sgshapiro ** There was some error reading the qf file (reason is in err var.) 443294334Sgshapiro ** Cleanup: 443394334Sgshapiro ** close file; clear e_lockfp since it is the same as qfp, 443494334Sgshapiro ** hence it is invalid (as file) after qfp is closed; 443594334Sgshapiro ** the qf file is on disk, so set the flag to avoid calling 443694334Sgshapiro ** queueup() with bogus data. 443794334Sgshapiro */ 443894334Sgshapiro 443994334Sgshapiro if (qfp != NULL) 444094334Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 444194334Sgshapiro e->e_lockfp = NULL; 444294334Sgshapiro e->e_flags |= EF_INQUEUE; 444394334Sgshapiro loseqfile(e, err); 444494334Sgshapiro RELEASE_QUEUE; 444594334Sgshapiro return false; 444638032Speter} 444790792Sgshapiro/* 444864562Sgshapiro** PRTSTR -- print a string, "unprintable" characters are shown as \oct 444964562Sgshapiro** 445064562Sgshapiro** Parameters: 445164562Sgshapiro** s -- string to print 445264562Sgshapiro** ml -- maximum length of output 445364562Sgshapiro** 445464562Sgshapiro** Returns: 445590792Sgshapiro** number of entries 445664562Sgshapiro** 445764562Sgshapiro** Side Effects: 445864562Sgshapiro** Prints a string on stdout. 445964562Sgshapiro*/ 446064562Sgshapiro 446164562Sgshapirostatic void 446264562Sgshapiroprtstr(s, ml) 446364562Sgshapiro char *s; 446464562Sgshapiro int ml; 446564562Sgshapiro{ 446690792Sgshapiro int c; 446764562Sgshapiro 446864562Sgshapiro if (s == NULL) 446964562Sgshapiro return; 447064562Sgshapiro while (ml-- > 0 && ((c = *s++) != '\0')) 447164562Sgshapiro { 447264562Sgshapiro if (c == '\\') 447364562Sgshapiro { 447464562Sgshapiro if (ml-- > 0) 447564562Sgshapiro { 447690792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 447790792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 447864562Sgshapiro } 447964562Sgshapiro } 448064562Sgshapiro else if (isascii(c) && isprint(c)) 448190792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 448264562Sgshapiro else 448364562Sgshapiro { 448464562Sgshapiro if ((ml -= 3) > 0) 448590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 448690792Sgshapiro "\\%03o", c & 0xFF); 448764562Sgshapiro } 448864562Sgshapiro } 448964562Sgshapiro} 449090792Sgshapiro/* 449190792Sgshapiro** PRINTNQE -- print out number of entries in the mail queue 449290792Sgshapiro** 449390792Sgshapiro** Parameters: 449490792Sgshapiro** out -- output file pointer. 449590792Sgshapiro** prefix -- string to output in front of each line. 449690792Sgshapiro** 449790792Sgshapiro** Returns: 449890792Sgshapiro** none. 449990792Sgshapiro*/ 450090792Sgshapiro 450190792Sgshapirovoid 450290792Sgshapiroprintnqe(out, prefix) 450390792Sgshapiro SM_FILE_T *out; 450490792Sgshapiro char *prefix; 450590792Sgshapiro{ 450690792Sgshapiro#if SM_CONF_SHM 450790792Sgshapiro int i, k = 0, nrequests = 0; 450890792Sgshapiro bool unknown = false; 450990792Sgshapiro 451090792Sgshapiro if (ShmId == SM_SHM_NO_ID) 451190792Sgshapiro { 451290792Sgshapiro if (prefix == NULL) 451390792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 451490792Sgshapiro "Data unavailable: shared memory not updated\n"); 451590792Sgshapiro else 451690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 451790792Sgshapiro "%sNOTCONFIGURED:-1\r\n", prefix); 451890792Sgshapiro return; 451990792Sgshapiro } 452090792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 452190792Sgshapiro { 452290792Sgshapiro int j; 452390792Sgshapiro 452490792Sgshapiro k++; 452590792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 452690792Sgshapiro { 452790792Sgshapiro int n; 452890792Sgshapiro 452990792Sgshapiro if (StopRequest) 453090792Sgshapiro stop_sendmail(); 453190792Sgshapiro 453290792Sgshapiro n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx); 453390792Sgshapiro if (prefix != NULL) 453490792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 453590792Sgshapiro "%s%s:%d\r\n", 453690792Sgshapiro prefix, qid_printqueue(i, j), n); 453790792Sgshapiro else if (n < 0) 453890792Sgshapiro { 453990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 454090792Sgshapiro "%s: unknown number of entries\n", 454190792Sgshapiro qid_printqueue(i, j)); 454290792Sgshapiro unknown = true; 454390792Sgshapiro } 454490792Sgshapiro else if (n == 0) 454590792Sgshapiro { 454690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 454790792Sgshapiro "%s is empty\n", 454890792Sgshapiro qid_printqueue(i, j)); 454990792Sgshapiro } 455090792Sgshapiro else if (n > 0) 455190792Sgshapiro { 455290792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 455390792Sgshapiro "%s: entries=%d\n", 455490792Sgshapiro qid_printqueue(i, j), n); 455590792Sgshapiro nrequests += n; 455690792Sgshapiro k++; 455790792Sgshapiro } 455890792Sgshapiro } 455990792Sgshapiro } 456090792Sgshapiro if (prefix == NULL && k > 1) 456190792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 456290792Sgshapiro "\t\tTotal requests: %d%s\n", 456390792Sgshapiro nrequests, unknown ? " (about)" : ""); 456490792Sgshapiro#else /* SM_CONF_SHM */ 456590792Sgshapiro if (prefix == NULL) 456690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 456790792Sgshapiro "Data unavailable without shared memory support\n"); 456890792Sgshapiro else 456990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 457090792Sgshapiro "%sNOTAVAILABLE:-1\r\n", prefix); 457190792Sgshapiro#endif /* SM_CONF_SHM */ 457290792Sgshapiro} 457390792Sgshapiro/* 457438032Speter** PRINTQUEUE -- print out a representation of the mail queue 457538032Speter** 457638032Speter** Parameters: 457738032Speter** none. 457838032Speter** 457938032Speter** Returns: 458038032Speter** none. 458138032Speter** 458238032Speter** Side Effects: 458338032Speter** Prints a listing of the mail queue on the standard output. 458438032Speter*/ 458538032Speter 458638032Spetervoid 458738032Speterprintqueue() 458838032Speter{ 458990792Sgshapiro int i, k = 0, nrequests = 0; 459064562Sgshapiro 459190792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 459277349Sgshapiro { 459390792Sgshapiro int j; 459490792Sgshapiro 459590792Sgshapiro k++; 459690792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 459790792Sgshapiro { 459890792Sgshapiro if (StopRequest) 459990792Sgshapiro stop_sendmail(); 460090792Sgshapiro nrequests += print_single_queue(i, j); 460190792Sgshapiro k++; 460290792Sgshapiro } 460377349Sgshapiro } 460490792Sgshapiro if (k > 1) 460590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 460690792Sgshapiro "\t\tTotal requests: %d\n", 460790792Sgshapiro nrequests); 460864562Sgshapiro} 460990792Sgshapiro/* 461064562Sgshapiro** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue 461164562Sgshapiro** 461264562Sgshapiro** Parameters: 461390792Sgshapiro** qgrp -- the index of the queue group. 461490792Sgshapiro** qdir -- the queue directory. 461564562Sgshapiro** 461664562Sgshapiro** Returns: 461790792Sgshapiro** number of requests in mail queue. 461864562Sgshapiro** 461964562Sgshapiro** Side Effects: 462064562Sgshapiro** Prints a listing of the mail queue on the standard output. 462164562Sgshapiro*/ 462264562Sgshapiro 462390792Sgshapiroint 462490792Sgshapiroprint_single_queue(qgrp, qdir) 462590792Sgshapiro int qgrp; 462690792Sgshapiro int qdir; 462764562Sgshapiro{ 462838032Speter register WORK *w; 462990792Sgshapiro SM_FILE_T *f; 463038032Speter int nrequests; 463164562Sgshapiro char qd[MAXPATHLEN]; 463264562Sgshapiro char qddf[MAXPATHLEN]; 463338032Speter char buf[MAXLINE]; 463438032Speter 463590792Sgshapiro if (qdir == NOQDIR) 463664562Sgshapiro { 463790792Sgshapiro (void) sm_strlcpy(qd, ".", sizeof qd); 463890792Sgshapiro (void) sm_strlcpy(qddf, ".", sizeof qddf); 463964562Sgshapiro } 464064562Sgshapiro else 464164562Sgshapiro { 464290792Sgshapiro (void) sm_strlcpyn(qd, sizeof qd, 2, 464390792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 464490792Sgshapiro (bitset(QP_SUBQF, 464590792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 464690792Sgshapiro ? "/qf" : "")); 464790792Sgshapiro (void) sm_strlcpyn(qddf, sizeof qddf, 2, 464890792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 464990792Sgshapiro (bitset(QP_SUBDF, 465090792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 465190792Sgshapiro ? "/df" : "")); 465264562Sgshapiro } 465364562Sgshapiro 465438032Speter /* 465538032Speter ** Check for permission to print the queue 465638032Speter */ 465738032Speter 465838032Speter if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 465938032Speter { 466038032Speter struct stat st; 466190792Sgshapiro#ifdef NGROUPS_MAX 466238032Speter int n; 466338032Speter extern GIDSET_T InitialGidSet[NGROUPS_MAX]; 466490792Sgshapiro#endif /* NGROUPS_MAX */ 466538032Speter 466664562Sgshapiro if (stat(qd, &st) < 0) 466738032Speter { 466890792Sgshapiro syserr("Cannot stat %s", 466990792Sgshapiro qid_printqueue(qgrp, qdir)); 467064562Sgshapiro return 0; 467138032Speter } 467290792Sgshapiro#ifdef NGROUPS_MAX 467338032Speter n = NGROUPS_MAX; 467438032Speter while (--n >= 0) 467538032Speter { 467638032Speter if (InitialGidSet[n] == st.st_gid) 467738032Speter break; 467838032Speter } 467938032Speter if (n < 0 && RealGid != st.st_gid) 468090792Sgshapiro#else /* NGROUPS_MAX */ 468138032Speter if (RealGid != st.st_gid) 468290792Sgshapiro#endif /* NGROUPS_MAX */ 468338032Speter { 468438032Speter usrerr("510 You are not permitted to see the queue"); 468538032Speter setstat(EX_NOPERM); 468664562Sgshapiro return 0; 468738032Speter } 468838032Speter } 468938032Speter 469038032Speter /* 469138032Speter ** Read and order the queue. 469238032Speter */ 469338032Speter 469490792Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL); 469590792Sgshapiro (void) sortq(Queue[qgrp]->qg_maxlist); 469638032Speter 469738032Speter /* 469838032Speter ** Print the work list that we have read. 469938032Speter */ 470038032Speter 470138032Speter /* first see if there is anything */ 470238032Speter if (nrequests <= 0) 470338032Speter { 470490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n", 470590792Sgshapiro qid_printqueue(qgrp, qdir)); 470664562Sgshapiro return 0; 470738032Speter } 470838032Speter 470990792Sgshapiro sm_getla(); /* get load average */ 471038032Speter 471190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s", 471290792Sgshapiro qid_printqueue(qgrp, qdir), 471390792Sgshapiro nrequests, nrequests == 1 ? "" : "s"); 471438032Speter if (MaxQueueRun > 0 && nrequests > MaxQueueRun) 471590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 471690792Sgshapiro ", only %d printed", MaxQueueRun); 471738032Speter if (Verbose) 471890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 471990792Sgshapiro ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n"); 472038032Speter else 472190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 472290792Sgshapiro ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n"); 472338032Speter for (w = WorkQ; w != NULL; w = w->w_next) 472438032Speter { 472538032Speter struct stat st; 472638032Speter auto time_t submittime = 0; 472738032Speter long dfsize; 472838032Speter int flags = 0; 472938032Speter int qfver; 473090792Sgshapiro#if _FFR_QUARANTINE 473190792Sgshapiro char quarmsg[MAXLINE]; 473290792Sgshapiro#endif /* _FFR_QUARANTINE */ 473338032Speter char statmsg[MAXLINE]; 473438032Speter char bodytype[MAXNAME + 1]; 473564562Sgshapiro char qf[MAXPATHLEN]; 473638032Speter 473777349Sgshapiro if (StopRequest) 473877349Sgshapiro stop_sendmail(); 473977349Sgshapiro 474090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s", 474190792Sgshapiro w->w_name + 2); 474290792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", w->w_name); 474390792Sgshapiro f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY, 474490792Sgshapiro NULL); 474538032Speter if (f == NULL) 474638032Speter { 474790792Sgshapiro if (errno == EPERM) 474890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 474990792Sgshapiro " (permission denied)\n"); 475090792Sgshapiro else if (errno == ENOENT) 475190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 475290792Sgshapiro " (job completed)\n"); 475390792Sgshapiro else 475490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 475590792Sgshapiro " (%s)\n", 475690792Sgshapiro sm_errstring(errno)); 475738032Speter errno = 0; 475838032Speter continue; 475938032Speter } 476090792Sgshapiro w->w_name[0] = DATAFL_LETTER; 476190792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qddf, "/", w->w_name); 476264562Sgshapiro if (stat(qf, &st) >= 0) 476338032Speter dfsize = st.st_size; 476438032Speter else 476590792Sgshapiro { 476690792Sgshapiro ENVELOPE e; 476790792Sgshapiro 476890792Sgshapiro /* 476990792Sgshapiro ** Maybe the df file can't be statted because 477090792Sgshapiro ** it is in a different directory than the qf file. 477190792Sgshapiro ** In order to find out, we must read the qf file. 477290792Sgshapiro */ 477390792Sgshapiro 477490792Sgshapiro newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL)); 477590792Sgshapiro e.e_id = w->w_name + 2; 477690792Sgshapiro e.e_qgrp = qgrp; 477790792Sgshapiro e.e_qdir = qdir; 477838032Speter dfsize = -1; 477990792Sgshapiro if (readqf(&e, false)) 478090792Sgshapiro { 478190792Sgshapiro char *df = queuename(&e, DATAFL_LETTER); 478290792Sgshapiro if (stat(df, &st) >= 0) 478390792Sgshapiro dfsize = st.st_size; 478490792Sgshapiro } 478590792Sgshapiro if (e.e_lockfp != NULL) 478690792Sgshapiro { 478790792Sgshapiro (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT); 478890792Sgshapiro e.e_lockfp = NULL; 478990792Sgshapiro } 479090792Sgshapiro clearenvelope(&e, false, e.e_rpool); 479190792Sgshapiro sm_rpool_free(e.e_rpool); 479290792Sgshapiro } 479338032Speter if (w->w_lock) 479490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*"); 479590792Sgshapiro#if _FFR_QUARANTINE 479690792Sgshapiro else if (QueueMode == QM_LOST) 479790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?"); 479890792Sgshapiro#endif /* _FFR_QUARANTINE */ 479938032Speter else if (w->w_tooyoung) 480090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-"); 480138032Speter else if (shouldqueue(w->w_pri, w->w_ctime)) 480290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X"); 480338032Speter else 480490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " "); 480590792Sgshapiro 480638032Speter errno = 0; 480738032Speter 480890792Sgshapiro#if _FFR_QUARANTINE 480990792Sgshapiro quarmsg[0] = '\0'; 481090792Sgshapiro#endif /* _FFR_QUARANTINE */ 481138032Speter statmsg[0] = bodytype[0] = '\0'; 481238032Speter qfver = 0; 481390792Sgshapiro while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 481438032Speter { 481538032Speter register int i; 481638032Speter register char *p; 481738032Speter 481877349Sgshapiro if (StopRequest) 481977349Sgshapiro stop_sendmail(); 482077349Sgshapiro 482190792Sgshapiro fixcrlf(buf, true); 482238032Speter switch (buf[0]) 482338032Speter { 482438032Speter case 'V': /* queue file version */ 482538032Speter qfver = atoi(&buf[1]); 482638032Speter break; 482738032Speter 482838032Speter case 'M': /* error message */ 482938032Speter if ((i = strlen(&buf[1])) >= sizeof statmsg) 483038032Speter i = sizeof statmsg - 1; 483164562Sgshapiro memmove(statmsg, &buf[1], i); 483238032Speter statmsg[i] = '\0'; 483338032Speter break; 483438032Speter 483590792Sgshapiro#if _FFR_QUARANTINE 483690792Sgshapiro case 'q': /* quarantine reason */ 483790792Sgshapiro if ((i = strlen(&buf[1])) >= sizeof quarmsg) 483890792Sgshapiro i = sizeof quarmsg - 1; 483990792Sgshapiro memmove(quarmsg, &buf[1], i); 484090792Sgshapiro quarmsg[i] = '\0'; 484190792Sgshapiro break; 484290792Sgshapiro#endif /* _FFR_QUARANTINE */ 484390792Sgshapiro 484438032Speter case 'B': /* body type */ 484538032Speter if ((i = strlen(&buf[1])) >= sizeof bodytype) 484638032Speter i = sizeof bodytype - 1; 484764562Sgshapiro memmove(bodytype, &buf[1], i); 484838032Speter bodytype[i] = '\0'; 484938032Speter break; 485038032Speter 485138032Speter case 'S': /* sender name */ 485238032Speter if (Verbose) 485364562Sgshapiro { 485490792Sgshapiro (void) sm_io_fprintf(smioout, 485590792Sgshapiro SM_TIME_DEFAULT, 485690792Sgshapiro "%8ld %10ld%c%.12s ", 485790792Sgshapiro dfsize, 485890792Sgshapiro w->w_pri, 485990792Sgshapiro bitset(EF_WARNING, flags) 486090792Sgshapiro ? '+' : ' ', 486190792Sgshapiro ctime(&submittime) + 4); 486264562Sgshapiro prtstr(&buf[1], 78); 486364562Sgshapiro } 486438032Speter else 486564562Sgshapiro { 486690792Sgshapiro (void) sm_io_fprintf(smioout, 486790792Sgshapiro SM_TIME_DEFAULT, 486890792Sgshapiro "%8ld %.16s ", 486990792Sgshapiro dfsize, 487090792Sgshapiro ctime(&submittime)); 487190792Sgshapiro prtstr(&buf[1], 39); 487264562Sgshapiro } 487390792Sgshapiro#if _FFR_QUARANTINE 487490792Sgshapiro if (quarmsg[0] != '\0') 487590792Sgshapiro { 487690792Sgshapiro (void) sm_io_fprintf(smioout, 487790792Sgshapiro SM_TIME_DEFAULT, 487890792Sgshapiro "\n QUARANTINE: %.*s", 487990792Sgshapiro Verbose ? 100 : 60, 488090792Sgshapiro quarmsg); 488190792Sgshapiro quarmsg[0] = '\0'; 488290792Sgshapiro } 488390792Sgshapiro#endif /* _FFR_QUARANTINE */ 488438032Speter if (statmsg[0] != '\0' || bodytype[0] != '\0') 488538032Speter { 488690792Sgshapiro (void) sm_io_fprintf(smioout, 488790792Sgshapiro SM_TIME_DEFAULT, 488890792Sgshapiro "\n %10.10s", 488990792Sgshapiro bodytype); 489038032Speter if (statmsg[0] != '\0') 489190792Sgshapiro (void) sm_io_fprintf(smioout, 489290792Sgshapiro SM_TIME_DEFAULT, 489390792Sgshapiro " (%.*s)", 489490792Sgshapiro Verbose ? 100 : 60, 489590792Sgshapiro statmsg); 489690792Sgshapiro statmsg[0] = '\0'; 489738032Speter } 489838032Speter break; 489938032Speter 490038032Speter case 'C': /* controlling user */ 490138032Speter if (Verbose) 490290792Sgshapiro (void) sm_io_fprintf(smioout, 490390792Sgshapiro SM_TIME_DEFAULT, 490490792Sgshapiro "\n\t\t\t\t\t\t(---%.64s---)", 490590792Sgshapiro &buf[1]); 490638032Speter break; 490738032Speter 490838032Speter case 'R': /* recipient name */ 490938032Speter p = &buf[1]; 491038032Speter if (qfver >= 1) 491138032Speter { 491238032Speter p = strchr(p, ':'); 491338032Speter if (p == NULL) 491438032Speter break; 491538032Speter p++; 491638032Speter } 491738032Speter if (Verbose) 491864562Sgshapiro { 491990792Sgshapiro (void) sm_io_fprintf(smioout, 492090792Sgshapiro SM_TIME_DEFAULT, 492190792Sgshapiro "\n\t\t\t\t\t\t"); 492290792Sgshapiro prtstr(p, 71); 492364562Sgshapiro } 492438032Speter else 492564562Sgshapiro { 492690792Sgshapiro (void) sm_io_fprintf(smioout, 492790792Sgshapiro SM_TIME_DEFAULT, 492890792Sgshapiro "\n\t\t\t\t\t "); 492990792Sgshapiro prtstr(p, 38); 493064562Sgshapiro } 493190792Sgshapiro if (Verbose && statmsg[0] != '\0') 493290792Sgshapiro { 493390792Sgshapiro (void) sm_io_fprintf(smioout, 493490792Sgshapiro SM_TIME_DEFAULT, 493590792Sgshapiro "\n\t\t (%.100s)", 493690792Sgshapiro statmsg); 493790792Sgshapiro statmsg[0] = '\0'; 493890792Sgshapiro } 493938032Speter break; 494038032Speter 494138032Speter case 'T': /* creation time */ 494238032Speter submittime = atol(&buf[1]); 494338032Speter break; 494438032Speter 494538032Speter case 'F': /* flag bits */ 494638032Speter for (p = &buf[1]; *p != '\0'; p++) 494738032Speter { 494838032Speter switch (*p) 494938032Speter { 495038032Speter case 'w': 495138032Speter flags |= EF_WARNING; 495238032Speter break; 495338032Speter } 495438032Speter } 495538032Speter } 495638032Speter } 495738032Speter if (submittime == (time_t) 0) 495890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 495990792Sgshapiro " (no control file)"); 496090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 496190792Sgshapiro (void) sm_io_close(f, SM_TIME_DEFAULT); 496238032Speter } 496364562Sgshapiro return nrequests; 496438032Speter} 496590792Sgshapiro 496690792Sgshapiro#if _FFR_QUARANTINE 496790792Sgshapiro/* 496890792Sgshapiro** QUEUE_LETTER -- get the proper queue letter for the current QueueMode. 496990792Sgshapiro** 497090792Sgshapiro** Parameters: 497190792Sgshapiro** e -- envelope to build it in/from. 497290792Sgshapiro** type -- the file type, used as the first character 497390792Sgshapiro** of the file name. 497490792Sgshapiro** 497590792Sgshapiro** Returns: 497690792Sgshapiro** the letter to use 497790792Sgshapiro*/ 497890792Sgshapiro 497990792Sgshapirostatic char 498090792Sgshapiroqueue_letter(e, type) 498190792Sgshapiro ENVELOPE *e; 498290792Sgshapiro int type; 498390792Sgshapiro{ 498490792Sgshapiro /* Change type according to QueueMode */ 498590792Sgshapiro if (type == ANYQFL_LETTER) 498690792Sgshapiro { 498790792Sgshapiro if (e->e_quarmsg != NULL) 498890792Sgshapiro type = QUARQF_LETTER; 498990792Sgshapiro else 499090792Sgshapiro { 499190792Sgshapiro switch (QueueMode) 499290792Sgshapiro { 499390792Sgshapiro case QM_NORMAL: 499490792Sgshapiro type = NORMQF_LETTER; 499590792Sgshapiro break; 499690792Sgshapiro 499790792Sgshapiro case QM_QUARANTINE: 499890792Sgshapiro type = QUARQF_LETTER; 499990792Sgshapiro break; 500090792Sgshapiro 500190792Sgshapiro case QM_LOST: 500290792Sgshapiro type = LOSEQF_LETTER; 500390792Sgshapiro break; 500490792Sgshapiro 500590792Sgshapiro default: 500690792Sgshapiro /* should never happen */ 500790792Sgshapiro abort(); 500890792Sgshapiro /* NOTREACHED */ 500990792Sgshapiro } 501090792Sgshapiro } 501190792Sgshapiro } 501290792Sgshapiro return type; 501390792Sgshapiro} 501490792Sgshapiro#endif /* _FFR_QUARANTINE */ 501590792Sgshapiro 501690792Sgshapiro/* 501738032Speter** QUEUENAME -- build a file name in the queue directory for this envelope. 501838032Speter** 501938032Speter** Parameters: 502038032Speter** e -- envelope to build it in/from. 502138032Speter** type -- the file type, used as the first character 502238032Speter** of the file name. 502338032Speter** 502438032Speter** Returns: 502564562Sgshapiro** a pointer to the queue name (in a static buffer). 502638032Speter** 502738032Speter** Side Effects: 502864562Sgshapiro** If no id code is already assigned, queuename() will 502964562Sgshapiro** assign an id code with assign_queueid(). If no queue 503064562Sgshapiro** directory is assigned, one will be set with setnewqueue(). 503138032Speter*/ 503238032Speter 503338032Speterchar * 503438032Speterqueuename(e, type) 503538032Speter register ENVELOPE *e; 503638032Speter int type; 503738032Speter{ 503890792Sgshapiro int qd, qg; 503990792Sgshapiro char *sub = "/"; 504090792Sgshapiro char pref[3]; 504164562Sgshapiro static char buf[MAXPATHLEN]; 504238032Speter 504364562Sgshapiro /* Assign an ID if needed */ 504438032Speter if (e->e_id == NULL) 504564562Sgshapiro assign_queueid(e); 504664562Sgshapiro 504790792Sgshapiro#if _FFR_QUARANTINE 504890792Sgshapiro type = queue_letter(e, type); 504990792Sgshapiro#endif /* _FFR_QUARANTINE */ 505064562Sgshapiro 505190792Sgshapiro /* begin of filename */ 505290792Sgshapiro pref[0] = (char) type; 505390792Sgshapiro pref[1] = 'f'; 505490792Sgshapiro pref[2] = '\0'; 505590792Sgshapiro 505690792Sgshapiro /* Assign a queue group/directory if needed */ 505790792Sgshapiro if (type == XSCRPT_LETTER) 505890792Sgshapiro { 505990792Sgshapiro /* 506090792Sgshapiro ** We don't want to call setnewqueue() if we are fetching 506190792Sgshapiro ** the pathname of the transcript file, because setnewqueue 506290792Sgshapiro ** chooses a queue, and sometimes we need to write to the 506390792Sgshapiro ** transcript file before we have gathered enough information 506490792Sgshapiro ** to choose a queue. 506590792Sgshapiro */ 506690792Sgshapiro 506790792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 506890792Sgshapiro { 506990792Sgshapiro if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR) 507090792Sgshapiro { 507190792Sgshapiro e->e_xfqgrp = e->e_qgrp; 507290792Sgshapiro e->e_xfqdir = e->e_qdir; 507390792Sgshapiro } 507490792Sgshapiro else 507590792Sgshapiro { 507690792Sgshapiro e->e_xfqgrp = 0; 507790792Sgshapiro if (Queue[e->e_xfqgrp]->qg_numqueues <= 1) 507890792Sgshapiro e->e_xfqdir = 0; 507990792Sgshapiro else 508090792Sgshapiro { 508190792Sgshapiro e->e_xfqdir = get_rand_mod( 508290792Sgshapiro Queue[e->e_xfqgrp]->qg_numqueues); 508390792Sgshapiro } 508490792Sgshapiro } 508590792Sgshapiro } 508690792Sgshapiro qd = e->e_xfqdir; 508790792Sgshapiro qg = e->e_xfqgrp; 508890792Sgshapiro } 508964562Sgshapiro else 509038032Speter { 509190792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 509290792Sgshapiro setnewqueue(e); 509390792Sgshapiro if (type == DATAFL_LETTER) 509490792Sgshapiro { 509590792Sgshapiro qd = e->e_dfqdir; 509690792Sgshapiro qg = e->e_dfqgrp; 509790792Sgshapiro } 509890792Sgshapiro else 509990792Sgshapiro { 510090792Sgshapiro qd = e->e_qdir; 510190792Sgshapiro qg = e->e_qgrp; 510290792Sgshapiro } 510390792Sgshapiro } 510490792Sgshapiro 510594334Sgshapiro /* xf files always have a valid qd and qg picked above */ 510694334Sgshapiro if (e->e_qdir == NOQDIR && type != XSCRPT_LETTER) 510790792Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id); 510890792Sgshapiro else 510990792Sgshapiro { 511064562Sgshapiro switch (type) 511164562Sgshapiro { 511290792Sgshapiro case DATAFL_LETTER: 511390792Sgshapiro if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 511490792Sgshapiro sub = "/df/"; 511564562Sgshapiro break; 511638032Speter 511790792Sgshapiro#if _FFR_QUARANTINE 511890792Sgshapiro case QUARQF_LETTER: 511990792Sgshapiro#endif /* _FFR_QUARANTINE */ 512071345Sgshapiro case TEMPQF_LETTER: 512190792Sgshapiro case NEWQFL_LETTER: 512271345Sgshapiro case LOSEQF_LETTER: 512390792Sgshapiro case NORMQF_LETTER: 512490792Sgshapiro if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 512590792Sgshapiro sub = "/qf/"; 512664562Sgshapiro break; 512764562Sgshapiro 512890792Sgshapiro case XSCRPT_LETTER: 512990792Sgshapiro if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 513090792Sgshapiro sub = "/xf/"; 513164562Sgshapiro break; 513290792Sgshapiro 513390792Sgshapiro default: 513490792Sgshapiro sm_abort("queuename: bad queue file type %d", type); 513538032Speter } 513638032Speter 513790792Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 4, 513890792Sgshapiro Queue[qg]->qg_qpaths[qd].qp_name, 513990792Sgshapiro sub, pref, e->e_id); 514064562Sgshapiro } 514138032Speter 514264562Sgshapiro if (tTd(7, 2)) 514390792Sgshapiro sm_dprintf("queuename: %s\n", buf); 514464562Sgshapiro return buf; 514564562Sgshapiro} 514690792Sgshapiro/* 514764562Sgshapiro** ASSIGN_QUEUEID -- assign a queue ID for this envelope. 514864562Sgshapiro** 514964562Sgshapiro** Assigns an id code if one does not already exist. 515064562Sgshapiro** This code assumes that nothing will remain in the queue for 515164562Sgshapiro** longer than 60 years. It is critical that files with the given 515290792Sgshapiro** name do not already exist in the queue. 515390792Sgshapiro** [No longer initializes e_qdir to NOQDIR.] 515464562Sgshapiro** 515564562Sgshapiro** Parameters: 515664562Sgshapiro** e -- envelope to set it in. 515764562Sgshapiro** 515864562Sgshapiro** Returns: 515964562Sgshapiro** none. 516064562Sgshapiro*/ 516138032Speter 516277349Sgshapirostatic const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"; 516377349Sgshapiro# define QIC_LEN 60 516490792Sgshapiro# define queuenextid() CurrentPid 516538032Speter 516690792Sgshapiro 516764562Sgshapirovoid 516864562Sgshapiroassign_queueid(e) 516964562Sgshapiro register ENVELOPE *e; 517064562Sgshapiro{ 517190792Sgshapiro pid_t pid = queuenextid(); 517290792Sgshapiro static int cX = 0; 517364562Sgshapiro static long random_offset; 517464562Sgshapiro struct tm *tm; 517564562Sgshapiro char idbuf[MAXQFNAME - 2]; 517690792Sgshapiro int seq; 517738032Speter 517864562Sgshapiro if (e->e_id != NULL) 517964562Sgshapiro return; 518038032Speter 518164562Sgshapiro /* see if we need to get a new base time/pid */ 518290792Sgshapiro if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 || 518390792Sgshapiro LastQueuePid != pid) 518464562Sgshapiro { 518564562Sgshapiro time_t then = LastQueueTime; 518664562Sgshapiro 518764562Sgshapiro /* if the first time through, pick a random offset */ 518864562Sgshapiro if (LastQueueTime == 0) 518964562Sgshapiro random_offset = get_random(); 519064562Sgshapiro 519164562Sgshapiro while ((LastQueueTime = curtime()) == then && 519264562Sgshapiro LastQueuePid == pid) 519338032Speter { 519464562Sgshapiro (void) sleep(1); 519538032Speter } 519690792Sgshapiro LastQueuePid = queuenextid(); 519764562Sgshapiro cX = 0; 519838032Speter } 519990792Sgshapiro 520090792Sgshapiro /* 520190792Sgshapiro ** Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1. 520290792Sgshapiro ** This lets us generate up to QIC_LEN*QIC_LEN unique queue ids 520390792Sgshapiro ** per second, per process. With envelope splitting, 520490792Sgshapiro ** a single message can consume many queue ids. 520590792Sgshapiro */ 520690792Sgshapiro 520790792Sgshapiro seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN)); 520890792Sgshapiro ++cX; 520964562Sgshapiro if (tTd(7, 50)) 521090792Sgshapiro sm_dprintf("assign_queueid: random_offset = %ld (%d)\n", 521190792Sgshapiro random_offset, seq); 521238032Speter 521364562Sgshapiro tm = gmtime(&LastQueueTime); 521477349Sgshapiro idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN]; 521577349Sgshapiro idbuf[1] = QueueIdChars[tm->tm_mon]; 521677349Sgshapiro idbuf[2] = QueueIdChars[tm->tm_mday]; 521777349Sgshapiro idbuf[3] = QueueIdChars[tm->tm_hour]; 521877349Sgshapiro idbuf[4] = QueueIdChars[tm->tm_min]; 521977349Sgshapiro idbuf[5] = QueueIdChars[tm->tm_sec]; 522090792Sgshapiro idbuf[6] = QueueIdChars[seq / QIC_LEN]; 522190792Sgshapiro idbuf[7] = QueueIdChars[seq % QIC_LEN]; 522290792Sgshapiro (void) sm_snprintf(&idbuf[8], sizeof idbuf - 8, "%06d", 522390792Sgshapiro (int) LastQueuePid); 522490792Sgshapiro e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf); 522590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 522690792Sgshapiro#if 0 522790792Sgshapiro /* XXX: inherited from MainEnvelope */ 522890792Sgshapiro e->e_qgrp = NOQGRP; /* too early to do anything else */ 522990792Sgshapiro e->e_qdir = NOQDIR; 523090792Sgshapiro e->e_xfqgrp = NOQGRP; 523190792Sgshapiro#endif /* 0 */ 523290792Sgshapiro#if _FFR_QUARANTINE 523390792Sgshapiro /* New ID means it's not on disk yet */ 523490792Sgshapiro e->e_qfletter = '\0'; 523590792Sgshapiro#endif /* _FFR_QUARANTINE */ 523664562Sgshapiro if (tTd(7, 1)) 523790792Sgshapiro sm_dprintf("assign_queueid: assigned id %s, e=%p\n", 523890792Sgshapiro e->e_id, e); 523964562Sgshapiro if (LogLevel > 93) 524064562Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); 524138032Speter} 524290792Sgshapiro/* 524364562Sgshapiro** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second 524464562Sgshapiro** 524564562Sgshapiro** Make sure one PID can't be used by two processes in any one second. 524664562Sgshapiro** 524764562Sgshapiro** If the system rotates PIDs fast enough, may get the 524864562Sgshapiro** same pid in the same second for two distinct processes. 524964562Sgshapiro** This will interfere with the queue file naming system. 525064562Sgshapiro** 525164562Sgshapiro** Parameters: 525264562Sgshapiro** none 525364562Sgshapiro** 525464562Sgshapiro** Returns: 525564562Sgshapiro** none 525664562Sgshapiro*/ 525790792Sgshapiro 525864562Sgshapirovoid 525964562Sgshapirosync_queue_time() 526064562Sgshapiro{ 526190792Sgshapiro#if FAST_PID_RECYCLE 526264562Sgshapiro if (OpMode != MD_TEST && 526364562Sgshapiro OpMode != MD_VERIFY && 526464562Sgshapiro LastQueueTime > 0 && 526590792Sgshapiro LastQueuePid == CurrentPid && 526664562Sgshapiro curtime() == LastQueueTime) 526764562Sgshapiro (void) sleep(1); 526890792Sgshapiro#endif /* FAST_PID_RECYCLE */ 526964562Sgshapiro} 527090792Sgshapiro/* 527138032Speter** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 527238032Speter** 527338032Speter** Parameters: 527438032Speter** e -- the envelope to unlock. 527538032Speter** 527638032Speter** Returns: 527738032Speter** none 527838032Speter** 527938032Speter** Side Effects: 528038032Speter** unlocks the queue for `e'. 528138032Speter*/ 528238032Speter 528338032Spetervoid 528438032Speterunlockqueue(e) 528538032Speter ENVELOPE *e; 528638032Speter{ 528738032Speter if (tTd(51, 4)) 528890792Sgshapiro sm_dprintf("unlockqueue(%s)\n", 528938032Speter e->e_id == NULL ? "NOQUEUE" : e->e_id); 529038032Speter 529164562Sgshapiro 529238032Speter /* if there is a lock file in the envelope, close it */ 529338032Speter if (e->e_lockfp != NULL) 529490792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 529538032Speter e->e_lockfp = NULL; 529638032Speter 529738032Speter /* don't create a queue id if we don't already have one */ 529838032Speter if (e->e_id == NULL) 529938032Speter return; 530038032Speter 530138032Speter /* remove the transcript */ 530238032Speter if (LogLevel > 87) 530338032Speter sm_syslog(LOG_DEBUG, e->e_id, "unlock"); 530438032Speter if (!tTd(51, 104)) 530590792Sgshapiro (void) xunlink(queuename(e, XSCRPT_LETTER)); 530638032Speter} 530790792Sgshapiro/* 530838032Speter** SETCTLUSER -- create a controlling address 530938032Speter** 531038032Speter** Create a fake "address" given only a local login name; this is 531138032Speter** used as a "controlling user" for future recipient addresses. 531238032Speter** 531338032Speter** Parameters: 531438032Speter** user -- the user name of the controlling user. 531590792Sgshapiro** qfver -- the version stamp of this queue file. 531690792Sgshapiro** e -- envelope 531738032Speter** 531838032Speter** Returns: 531990792Sgshapiro** An address descriptor for the controlling user, 532090792Sgshapiro** using storage allocated from e->e_rpool. 532138032Speter** 532238032Speter*/ 532338032Speter 532464562Sgshapirostatic ADDRESS * 532590792Sgshapirosetctluser(user, qfver, e) 532638032Speter char *user; 532738032Speter int qfver; 532890792Sgshapiro ENVELOPE *e; 532938032Speter{ 533038032Speter register ADDRESS *a; 533138032Speter struct passwd *pw; 533238032Speter char *p; 533338032Speter 533438032Speter /* 533538032Speter ** See if this clears our concept of controlling user. 533638032Speter */ 533738032Speter 533838032Speter if (user == NULL || *user == '\0') 533938032Speter return NULL; 534038032Speter 534138032Speter /* 534238032Speter ** Set up addr fields for controlling user. 534338032Speter */ 534438032Speter 534590792Sgshapiro a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a); 534664562Sgshapiro memset((char *) a, '\0', sizeof *a); 534738032Speter 534890792Sgshapiro if (*user == ':') 534938032Speter { 535038032Speter p = &user[1]; 535190792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, p); 535238032Speter } 535338032Speter else 535438032Speter { 535538032Speter p = strtok(user, ":"); 535690792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, user); 535738032Speter if (qfver >= 2) 535838032Speter { 535938032Speter if ((p = strtok(NULL, ":")) != NULL) 536038032Speter a->q_uid = atoi(p); 536138032Speter if ((p = strtok(NULL, ":")) != NULL) 536238032Speter a->q_gid = atoi(p); 536338032Speter if ((p = strtok(NULL, ":")) != NULL) 536480785Sgshapiro { 536580785Sgshapiro char *o; 536680785Sgshapiro 536738032Speter a->q_flags |= QGOODUID; 536880785Sgshapiro 536980785Sgshapiro /* if there is another ':': restore it */ 537080785Sgshapiro if ((o = strtok(NULL, ":")) != NULL && o > p) 537180785Sgshapiro o[-1] = ':'; 537280785Sgshapiro } 537338032Speter } 537438032Speter else if ((pw = sm_getpwnam(user)) != NULL) 537538032Speter { 537666494Sgshapiro if (*pw->pw_dir == '\0') 537766494Sgshapiro a->q_home = NULL; 537866494Sgshapiro else if (strcmp(pw->pw_dir, "/") == 0) 537938032Speter a->q_home = ""; 538038032Speter else 538190792Sgshapiro a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir); 538238032Speter a->q_uid = pw->pw_uid; 538338032Speter a->q_gid = pw->pw_gid; 538438032Speter a->q_flags |= QGOODUID; 538538032Speter } 538638032Speter } 538738032Speter 538864562Sgshapiro a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 538938032Speter a->q_mailer = LocalMailer; 539038032Speter if (p == NULL) 539190792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); 539238032Speter else 539390792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p); 539438032Speter return a; 539538032Speter} 539690792Sgshapiro/* 539790792Sgshapiro** LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know 539838032Speter** 539938032Speter** Parameters: 540038032Speter** e -- the envelope (e->e_id will be used). 540138032Speter** why -- reported to whomever can hear. 540238032Speter** 540338032Speter** Returns: 540438032Speter** none. 540538032Speter*/ 540638032Speter 540738032Spetervoid 540838032Speterloseqfile(e, why) 540938032Speter register ENVELOPE *e; 541038032Speter char *why; 541138032Speter{ 541290792Sgshapiro bool loseit = true; 541338032Speter char *p; 541464562Sgshapiro char buf[MAXPATHLEN]; 541538032Speter 541638032Speter if (e == NULL || e->e_id == NULL) 541738032Speter return; 541890792Sgshapiro p = queuename(e, ANYQFL_LETTER); 541990792Sgshapiro if (sm_strlcpy(buf, p, sizeof buf) >= sizeof buf) 542038032Speter return; 542190792Sgshapiro if (!bitset(EF_INQUEUE, e->e_flags)) 542290792Sgshapiro queueup(e, false, true); 542390792Sgshapiro#if _FFR_QUARANTINE 542490792Sgshapiro else if (QueueMode == QM_LOST) 542590792Sgshapiro loseit = false; 542690792Sgshapiro#endif /* _FFR_QUARANTINE */ 542790792Sgshapiro 542890792Sgshapiro /* if already lost, no need to re-lose */ 542990792Sgshapiro if (loseit) 543090792Sgshapiro { 543190792Sgshapiro p = queuename(e, LOSEQF_LETTER); 543290792Sgshapiro if (rename(buf, p) < 0) 543390792Sgshapiro syserr("cannot rename(%s, %s), uid=%d", 543498121Sgshapiro buf, p, (int) geteuid()); 543590792Sgshapiro else if (LogLevel > 0) 543690792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 543790792Sgshapiro "Losing %s: %s", buf, why); 543890792Sgshapiro } 543990792Sgshapiro if (e->e_dfp != NULL) 544090792Sgshapiro { 544190792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 544290792Sgshapiro e->e_dfp = NULL; 544390792Sgshapiro } 544490792Sgshapiro e->e_flags &= ~EF_HAS_DF; 544538032Speter} 544690792Sgshapiro/* 544790792Sgshapiro** NAME2QID -- translate a queue group name to a queue group id 544890792Sgshapiro** 544990792Sgshapiro** Parameters: 545090792Sgshapiro** queuename -- name of queue group. 545190792Sgshapiro** 545290792Sgshapiro** Returns: 545390792Sgshapiro** queue group id if found. 545490792Sgshapiro** NOQGRP otherwise. 545590792Sgshapiro*/ 545690792Sgshapiro 545790792Sgshapiroint 545890792Sgshapironame2qid(queuename) 545990792Sgshapiro char *queuename; 546090792Sgshapiro{ 546190792Sgshapiro register STAB *s; 546290792Sgshapiro 546390792Sgshapiro s = stab(queuename, ST_QUEUE, ST_FIND); 546490792Sgshapiro if (s == NULL) 546590792Sgshapiro return NOQGRP; 546690792Sgshapiro return s->s_quegrp->qg_index; 546790792Sgshapiro} 546890792Sgshapiro/* 546964562Sgshapiro** QID_PRINTNAME -- create externally printable version of queue id 547064562Sgshapiro** 547164562Sgshapiro** Parameters: 547264562Sgshapiro** e -- the envelope. 547364562Sgshapiro** 547464562Sgshapiro** Returns: 547564562Sgshapiro** a printable version 547664562Sgshapiro*/ 547764562Sgshapiro 547864562Sgshapirochar * 547964562Sgshapiroqid_printname(e) 548064562Sgshapiro ENVELOPE *e; 548164562Sgshapiro{ 548264562Sgshapiro char *id; 548364562Sgshapiro static char idbuf[MAXQFNAME + 34]; 548464562Sgshapiro 548564562Sgshapiro if (e == NULL) 548664562Sgshapiro return ""; 548764562Sgshapiro 548864562Sgshapiro if (e->e_id == NULL) 548964562Sgshapiro id = ""; 549064562Sgshapiro else 549164562Sgshapiro id = e->e_id; 549264562Sgshapiro 549390792Sgshapiro if (e->e_qdir == NOQDIR) 549464562Sgshapiro return id; 549564562Sgshapiro 549690792Sgshapiro (void) sm_snprintf(idbuf, sizeof idbuf, "%.32s/%s", 549790792Sgshapiro Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name, 549890792Sgshapiro id); 549964562Sgshapiro return idbuf; 550064562Sgshapiro} 550190792Sgshapiro/* 550290792Sgshapiro** QID_PRINTQUEUE -- create full version of queue directory for data files 550364562Sgshapiro** 550464562Sgshapiro** Parameters: 550590792Sgshapiro** qgrp -- index in queue group. 550690792Sgshapiro** qdir -- the short version of the queue directory 550764562Sgshapiro** 550864562Sgshapiro** Returns: 550990792Sgshapiro** the full pathname to the queue (might point to a static var) 551064562Sgshapiro*/ 551164562Sgshapiro 551264562Sgshapirochar * 551390792Sgshapiroqid_printqueue(qgrp, qdir) 551490792Sgshapiro int qgrp; 551590792Sgshapiro int qdir; 551664562Sgshapiro{ 551764562Sgshapiro char *subdir; 551864562Sgshapiro static char dir[MAXPATHLEN]; 551964562Sgshapiro 552090792Sgshapiro if (qdir == NOQDIR) 552190792Sgshapiro return Queue[qgrp]->qg_qdir; 552264562Sgshapiro 552390792Sgshapiro if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0) 552464562Sgshapiro subdir = NULL; 552564562Sgshapiro else 552690792Sgshapiro subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name; 552764562Sgshapiro 552890792Sgshapiro (void) sm_strlcpyn(dir, sizeof dir, 4, 552990792Sgshapiro Queue[qgrp]->qg_qdir, 553064562Sgshapiro subdir == NULL ? "" : "/", 553164562Sgshapiro subdir == NULL ? "" : subdir, 553290792Sgshapiro (bitset(QP_SUBDF, 553390792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 553490792Sgshapiro ? "/df" : "")); 553564562Sgshapiro return dir; 553664562Sgshapiro} 553790792Sgshapiro 553890792Sgshapiro/* 553990792Sgshapiro** PICKQDIR -- Pick a queue directory from a queue group 554064562Sgshapiro** 554190792Sgshapiro** Parameters: 554290792Sgshapiro** qg -- queue group 554390792Sgshapiro** fsize -- file size in bytes 554490792Sgshapiro** e -- envelope, or NULL 554564562Sgshapiro** 554690792Sgshapiro** Result: 554790792Sgshapiro** NOQDIR if no queue directory in qg has enough free space to 554890792Sgshapiro** hold a file of size 'fsize', otherwise the index of 554990792Sgshapiro** a randomly selected queue directory which resides on a 555090792Sgshapiro** file system with enough disk space. 555190792Sgshapiro** XXX This could be extended to select a queuedir with 555290792Sgshapiro** a few (the fewest?) number of entries. That data 555390792Sgshapiro** is available if shared memory is used. 555464562Sgshapiro** 555590792Sgshapiro** Side Effects: 555690792Sgshapiro** If the request fails and e != NULL then sm_syslog is called. 555790792Sgshapiro*/ 555890792Sgshapiro 555990792Sgshapiroint 556090792Sgshapiropickqdir(qg, fsize, e) 556190792Sgshapiro QUEUEGRP *qg; 556290792Sgshapiro long fsize; 556390792Sgshapiro ENVELOPE *e; 556490792Sgshapiro{ 556590792Sgshapiro int qdir; 556690792Sgshapiro int i; 556790792Sgshapiro long avail = 0; 556890792Sgshapiro 556990792Sgshapiro /* Pick a random directory, as a starting point. */ 557090792Sgshapiro if (qg->qg_numqueues <= 1) 557190792Sgshapiro qdir = 0; 557290792Sgshapiro else 557390792Sgshapiro qdir = get_rand_mod(qg->qg_numqueues); 557490792Sgshapiro 557590792Sgshapiro if (MinBlocksFree <= 0 && fsize <= 0) 557690792Sgshapiro return qdir; 557790792Sgshapiro 557890792Sgshapiro /* 557990792Sgshapiro ** Now iterate over the queue directories, 558090792Sgshapiro ** looking for a directory with enough space for this message. 558190792Sgshapiro */ 558290792Sgshapiro 558390792Sgshapiro i = qdir; 558490792Sgshapiro do 558590792Sgshapiro { 558690792Sgshapiro QPATHS *qp = &qg->qg_qpaths[i]; 558790792Sgshapiro long needed = 0; 558890792Sgshapiro long fsavail = 0; 558990792Sgshapiro 559090792Sgshapiro if (fsize > 0) 559190792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx) 559290792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx) 559390792Sgshapiro > 0) ? 1 : 0); 559490792Sgshapiro if (MinBlocksFree > 0) 559590792Sgshapiro needed += MinBlocksFree; 559690792Sgshapiro fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx); 559790792Sgshapiro#if SM_CONF_SHM 559890792Sgshapiro if (fsavail <= 0) 559990792Sgshapiro { 560090792Sgshapiro long blksize; 560190792Sgshapiro 560290792Sgshapiro /* 560390792Sgshapiro ** might be not correctly updated, 560490792Sgshapiro ** let's try to get the info directly. 560590792Sgshapiro */ 560690792Sgshapiro 560790792Sgshapiro fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx), 560890792Sgshapiro &blksize); 560990792Sgshapiro if (fsavail < 0) 561090792Sgshapiro fsavail = 0; 561190792Sgshapiro } 561290792Sgshapiro#endif /* SM_CONF_SHM */ 561390792Sgshapiro if (needed <= fsavail) 561490792Sgshapiro return i; 561590792Sgshapiro if (avail < fsavail) 561690792Sgshapiro avail = fsavail; 561790792Sgshapiro 561890792Sgshapiro if (qg->qg_numqueues > 0) 561990792Sgshapiro i = (i + 1) % qg->qg_numqueues; 562090792Sgshapiro } while (i != qdir); 562190792Sgshapiro 562290792Sgshapiro if (e != NULL && LogLevel > 0) 562390792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 562490792Sgshapiro "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld", 562590792Sgshapiro CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, 562690792Sgshapiro fsize, MinBlocksFree, 562790792Sgshapiro qg->qg_qdir, avail); 562890792Sgshapiro return NOQDIR; 562990792Sgshapiro} 563090792Sgshapiro/* 563190792Sgshapiro** SETNEWQUEUE -- Sets a new queue group and directory 563290792Sgshapiro** 563390792Sgshapiro** Assign a queue group and directory to an envelope and store the 563490792Sgshapiro** directory in e->e_qdir. 563590792Sgshapiro** 563664562Sgshapiro** Parameters: 563764562Sgshapiro** e -- envelope to assign a queue for. 563864562Sgshapiro** 563964562Sgshapiro** Returns: 564090792Sgshapiro** true if successful 564190792Sgshapiro** false otherwise 564290792Sgshapiro** 564390792Sgshapiro** Side Effects: 564490792Sgshapiro** On success, e->e_qgrp and e->e_qdir are non-negative. 564590792Sgshapiro** On failure (not enough disk space), 564690792Sgshapiro** e->qgrp = NOQGRP, e->e_qdir = NOQDIR 564790792Sgshapiro** and usrerr() is invoked (which could raise an exception). 564864562Sgshapiro*/ 564964562Sgshapiro 565090792Sgshapirobool 565164562Sgshapirosetnewqueue(e) 565264562Sgshapiro ENVELOPE *e; 565364562Sgshapiro{ 565464562Sgshapiro if (tTd(41, 20)) 565590792Sgshapiro sm_dprintf("setnewqueue: called\n"); 565664562Sgshapiro 565790792Sgshapiro /* not set somewhere else */ 565890792Sgshapiro if (e->e_qgrp == NOQGRP) 565964562Sgshapiro { 5660102528Sgshapiro ADDRESS *q; 5661102528Sgshapiro 566290792Sgshapiro /* 5663102528Sgshapiro ** Use the queue group of the "first" recipient, as set by 566490792Sgshapiro ** the "queuegroup" rule set. If that is not defined, then 566590792Sgshapiro ** use the queue group of the mailer of the first recipient. 566690792Sgshapiro ** If that is not defined either, then use the default 566790792Sgshapiro ** queue group. 5668102528Sgshapiro ** Notice: "first" depends on the sorting of sendqueue 5669102528Sgshapiro ** in recipient(). 5670102528Sgshapiro ** To avoid problems with "bad" recipients look 5671102528Sgshapiro ** for a valid address first. 567290792Sgshapiro */ 567390792Sgshapiro 5674102528Sgshapiro q = e->e_sendqueue; 5675102528Sgshapiro while (q != NULL && 5676102528Sgshapiro (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state))) 5677102528Sgshapiro { 5678102528Sgshapiro q = q->q_next; 5679102528Sgshapiro } 5680102528Sgshapiro if (q == NULL) 568190792Sgshapiro e->e_qgrp = 0; 5682102528Sgshapiro else if (q->q_qgrp >= 0) 5683102528Sgshapiro e->e_qgrp = q->q_qgrp; 5684102528Sgshapiro else if (q->q_mailer != NULL && 5685102528Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 5686102528Sgshapiro e->e_qgrp = q->q_mailer->m_qgrp; 568790792Sgshapiro else 568890792Sgshapiro e->e_qgrp = 0; 568990792Sgshapiro e->e_dfqgrp = e->e_qgrp; 569090792Sgshapiro } 569190792Sgshapiro 569290792Sgshapiro if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir)) 569390792Sgshapiro { 569464562Sgshapiro if (tTd(41, 20)) 569590792Sgshapiro sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n", 569690792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 569790792Sgshapiro return true; 569864562Sgshapiro } 569964562Sgshapiro 570090792Sgshapiro filesys_update(); 570190792Sgshapiro e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e); 570290792Sgshapiro if (e->e_qdir == NOQDIR) 570364562Sgshapiro { 570490792Sgshapiro e->e_qgrp = NOQGRP; 570590792Sgshapiro if (!bitset(EF_FATALERRS, e->e_flags)) 570690792Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 570790792Sgshapiro e->e_flags |= EF_FATALERRS; 570890792Sgshapiro return false; 570964562Sgshapiro } 571064562Sgshapiro 571164562Sgshapiro if (tTd(41, 3)) 571290792Sgshapiro sm_dprintf("setnewqueue: Assigned queue directory %s\n", 571390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 571490792Sgshapiro 571590792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 571690792Sgshapiro { 571790792Sgshapiro e->e_xfqgrp = e->e_qgrp; 571890792Sgshapiro e->e_xfqdir = e->e_qdir; 571990792Sgshapiro } 572090792Sgshapiro e->e_dfqdir = e->e_qdir; 572190792Sgshapiro return true; 572264562Sgshapiro} 572390792Sgshapiro/* 572464562Sgshapiro** CHKQDIR -- check a queue directory 572564562Sgshapiro** 572664562Sgshapiro** Parameters: 572764562Sgshapiro** name -- name of queue directory 572864562Sgshapiro** sff -- flags for safefile() 572964562Sgshapiro** 573064562Sgshapiro** Returns: 573164562Sgshapiro** is it a queue directory? 573264562Sgshapiro*/ 573364562Sgshapiro 573464562Sgshapirostatic bool 573564562Sgshapirochkqdir(name, sff) 573664562Sgshapiro char *name; 573764562Sgshapiro long sff; 573864562Sgshapiro{ 573964562Sgshapiro struct stat statb; 574064562Sgshapiro int i; 574164562Sgshapiro 574266494Sgshapiro /* skip over . and .. directories */ 574366494Sgshapiro if (name[0] == '.' && 574477349Sgshapiro (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) 574590792Sgshapiro return false; 574690792Sgshapiro#if HASLSTAT 574764562Sgshapiro if (lstat(name, &statb) < 0) 574890792Sgshapiro#else /* HASLSTAT */ 574964562Sgshapiro if (stat(name, &statb) < 0) 575090792Sgshapiro#endif /* HASLSTAT */ 575164562Sgshapiro { 575264562Sgshapiro if (tTd(41, 2)) 575390792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 575490792Sgshapiro name, sm_errstring(errno)); 575590792Sgshapiro return false; 575664562Sgshapiro } 575790792Sgshapiro#if HASLSTAT 575864562Sgshapiro if (S_ISLNK(statb.st_mode)) 575964562Sgshapiro { 576064562Sgshapiro /* 576164562Sgshapiro ** For a symlink we need to make sure the 576264562Sgshapiro ** target is a directory 576364562Sgshapiro */ 576490792Sgshapiro 576564562Sgshapiro if (stat(name, &statb) < 0) 576664562Sgshapiro { 576764562Sgshapiro if (tTd(41, 2)) 576890792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 576990792Sgshapiro name, sm_errstring(errno)); 577090792Sgshapiro return false; 577164562Sgshapiro } 577264562Sgshapiro } 577390792Sgshapiro#endif /* HASLSTAT */ 577464562Sgshapiro 577564562Sgshapiro if (!S_ISDIR(statb.st_mode)) 577664562Sgshapiro { 577764562Sgshapiro if (tTd(41, 2)) 577890792Sgshapiro sm_dprintf("chkqdir: \"%s\": Not a directory\n", 577964562Sgshapiro name); 578090792Sgshapiro return false; 578164562Sgshapiro } 578264562Sgshapiro 578364562Sgshapiro /* Print a warning if unsafe (but still use it) */ 578490792Sgshapiro /* XXX do this only if we want the warning? */ 578564562Sgshapiro i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0); 578698121Sgshapiro if (i != 0) 578798121Sgshapiro { 578898121Sgshapiro if (tTd(41, 2)) 578998121Sgshapiro sm_dprintf("chkqdir: \"%s\": Not safe: %s\n", 579098121Sgshapiro name, sm_errstring(i)); 579198121Sgshapiro#if _FFR_CHK_QUEUE 579298121Sgshapiro if (LogLevel > 8) 579398121Sgshapiro sm_syslog(LOG_WARNING, NOQID, 579498121Sgshapiro "queue directory \"%s\": Not safe: %s", 579598121Sgshapiro name, sm_errstring(i)); 579698121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 579798121Sgshapiro } 579890792Sgshapiro return true; 579964562Sgshapiro} 580090792Sgshapiro/* 580164562Sgshapiro** MULTIQUEUE_CACHE -- cache a list of paths to queues. 580264562Sgshapiro** 580364562Sgshapiro** Each potential queue is checked as the cache is built. 580464562Sgshapiro** Thereafter, each is blindly trusted. 580564562Sgshapiro** Note that we can be called again after a timeout to rebuild 580664562Sgshapiro** (although code for that is not ready yet). 580764562Sgshapiro** 580864562Sgshapiro** Parameters: 580990792Sgshapiro** basedir -- base of all queue directories. 581090792Sgshapiro** blen -- strlen(basedir). 581190792Sgshapiro** qg -- queue group. 581290792Sgshapiro** qn -- number of queue directories already cached. 581390792Sgshapiro** phash -- pointer to hash value over queue dirs. 581490792Sgshapiro#if SM_CONF_SHM 581590792Sgshapiro** only used if shared memory is active. 581690792Sgshapiro#endif * SM_CONF_SHM * 581764562Sgshapiro** 581864562Sgshapiro** Returns: 581990792Sgshapiro** new number of queue directories. 582064562Sgshapiro*/ 582164562Sgshapiro 582290792Sgshapiro#define INITIAL_SLOTS 20 582390792Sgshapiro#define ADD_SLOTS 10 582490792Sgshapiro 582590792Sgshapirostatic int 582690792Sgshapiromultiqueue_cache(basedir, blen, qg, qn, phash) 582790792Sgshapiro char *basedir; 582890792Sgshapiro int blen; 582990792Sgshapiro QUEUEGRP *qg; 583090792Sgshapiro int qn; 583190792Sgshapiro unsigned int *phash; 583264562Sgshapiro{ 583364562Sgshapiro char *cp; 583464562Sgshapiro int i, len; 583564562Sgshapiro int slotsleft = 0; 583664562Sgshapiro long sff = SFF_ANYFILE; 583764562Sgshapiro char qpath[MAXPATHLEN]; 583864562Sgshapiro char subdir[MAXPATHLEN]; 583990792Sgshapiro char prefix[MAXPATHLEN]; /* dir relative to basedir */ 584064562Sgshapiro 584164562Sgshapiro if (tTd(41, 20)) 584290792Sgshapiro sm_dprintf("multiqueue_cache: called\n"); 584364562Sgshapiro 584490792Sgshapiro /* Initialize to current directory */ 584590792Sgshapiro prefix[0] = '.'; 584690792Sgshapiro prefix[1] = '\0'; 584790792Sgshapiro if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL) 584864562Sgshapiro { 584990792Sgshapiro for (i = 0; i < qg->qg_numqueues; i++) 585064562Sgshapiro { 585190792Sgshapiro if (qg->qg_qpaths[i].qp_name != NULL) 585290792Sgshapiro (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */ 585364562Sgshapiro } 585490792Sgshapiro (void) sm_free((char *) qg->qg_qpaths); /* XXX */ 585590792Sgshapiro qg->qg_qpaths = NULL; 585690792Sgshapiro qg->qg_numqueues = 0; 585764562Sgshapiro } 585864562Sgshapiro 585964562Sgshapiro /* If running as root, allow safedirpath() checks to use privs */ 586064562Sgshapiro if (RunAsUid == 0) 586164562Sgshapiro sff |= SFF_ROOTOK; 586298121Sgshapiro#if _FFR_CHK_QUEUE 586398121Sgshapiro sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES; 586498121Sgshapiro if (!UseMSP) 586598121Sgshapiro sff |= SFF_NOGWFILES; 586698121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 586764562Sgshapiro 586890792Sgshapiro if (!SM_IS_DIR_START(qg->qg_qdir)) 586990792Sgshapiro { 587090792Sgshapiro /* 587190792Sgshapiro ** XXX we could add basedir, but then we have to realloc() 587290792Sgshapiro ** the string... Maybe another time. 587390792Sgshapiro */ 587490792Sgshapiro 587590792Sgshapiro syserr("QueuePath %s not absolute", qg->qg_qdir); 587690792Sgshapiro ExitStat = EX_CONFIG; 587790792Sgshapiro return qn; 587890792Sgshapiro } 587990792Sgshapiro 588090792Sgshapiro /* qpath: directory of current workgroup */ 588190792Sgshapiro len = sm_strlcpy(qpath, qg->qg_qdir, sizeof qpath); 588290792Sgshapiro if (len >= sizeof qpath) 588390792Sgshapiro { 588490792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 588590792Sgshapiro qg->qg_qdir, (int) sizeof qpath); 588690792Sgshapiro ExitStat = EX_CONFIG; 588790792Sgshapiro return qn; 588890792Sgshapiro } 588990792Sgshapiro 589090792Sgshapiro /* begin of qpath must be same as basedir */ 589190792Sgshapiro if (strncmp(basedir, qpath, blen) != 0 && 589290792Sgshapiro (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1)) 589390792Sgshapiro { 589490792Sgshapiro syserr("QueuePath %s not subpath of QueueDirectory %s", 589590792Sgshapiro qpath, basedir); 589690792Sgshapiro ExitStat = EX_CONFIG; 589790792Sgshapiro return qn; 589890792Sgshapiro } 589990792Sgshapiro 590090792Sgshapiro /* Do we have a nested subdirectory? */ 590190792Sgshapiro if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL) 590290792Sgshapiro { 590390792Sgshapiro 590490792Sgshapiro /* Copy subdirectory into prefix for later use */ 590590792Sgshapiro if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof prefix) >= 590690792Sgshapiro sizeof prefix) 590790792Sgshapiro { 590890792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 590990792Sgshapiro qg->qg_qdir, (int) sizeof qpath); 591090792Sgshapiro ExitStat = EX_CONFIG; 591190792Sgshapiro return qn; 591290792Sgshapiro } 591390792Sgshapiro cp = SM_LAST_DIR_DELIM(prefix); 591490792Sgshapiro SM_ASSERT(cp != NULL); 591590792Sgshapiro *cp = '\0'; /* cut off trailing / */ 591690792Sgshapiro } 591790792Sgshapiro 591890792Sgshapiro /* This is guaranteed by the basedir check above */ 591990792Sgshapiro SM_ASSERT(len >= blen - 1); 592090792Sgshapiro cp = &qpath[len - 1]; 592164562Sgshapiro if (*cp == '*') 592264562Sgshapiro { 592390792Sgshapiro register DIR *dp; 592490792Sgshapiro register struct dirent *d; 592590792Sgshapiro int off; 592690792Sgshapiro char *delim; 592790792Sgshapiro char relpath[MAXPATHLEN]; 592890792Sgshapiro 592990792Sgshapiro *cp = '\0'; /* Overwrite wildcard */ 593090792Sgshapiro if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL) 593164562Sgshapiro { 593264562Sgshapiro syserr("QueueDirectory: can not wildcard relative path"); 593364562Sgshapiro if (tTd(41, 2)) 593490792Sgshapiro sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n", 593571345Sgshapiro qpath); 593664562Sgshapiro ExitStat = EX_CONFIG; 593790792Sgshapiro return qn; 593864562Sgshapiro } 593964562Sgshapiro if (cp == qpath) 594064562Sgshapiro { 594164562Sgshapiro /* 594264562Sgshapiro ** Special case of top level wildcard, like /foo* 594390792Sgshapiro ** Change to //foo* 594464562Sgshapiro */ 594564562Sgshapiro 594690792Sgshapiro (void) sm_strlcpy(qpath + 1, qpath, sizeof qpath - 1); 594764562Sgshapiro ++cp; 594864562Sgshapiro } 594990792Sgshapiro delim = cp; 595090792Sgshapiro *(cp++) = '\0'; /* Replace / with \0 */ 595190792Sgshapiro len = strlen(cp); /* Last component of queue directory */ 595264562Sgshapiro 595390792Sgshapiro /* 595490792Sgshapiro ** Path relative to basedir, with trailing / 595590792Sgshapiro ** It will be modified below to specify the subdirectories 595690792Sgshapiro ** so they can be opened without chdir(). 595790792Sgshapiro */ 595890792Sgshapiro 595990792Sgshapiro off = sm_strlcpyn(relpath, sizeof relpath, 2, prefix, "/"); 596090792Sgshapiro SM_ASSERT(off < sizeof relpath); 596190792Sgshapiro 596264562Sgshapiro if (tTd(41, 2)) 596390792Sgshapiro sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n", 596490792Sgshapiro relpath, cp); 596564562Sgshapiro 596690792Sgshapiro /* It is always basedir: we don't need to store it per group */ 596790792Sgshapiro /* XXX: optimize this! -> one more global? */ 596890792Sgshapiro qg->qg_qdir = newstr(basedir); 596990792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */ 597064562Sgshapiro 597164562Sgshapiro /* 597264562Sgshapiro ** XXX Should probably wrap this whole loop in a timeout 597364562Sgshapiro ** in case some wag decides to NFS mount the queues. 597464562Sgshapiro */ 597564562Sgshapiro 597690792Sgshapiro /* Test path to get warning messages. */ 597790792Sgshapiro if (qn == 0) 597864562Sgshapiro { 597990792Sgshapiro /* XXX qg_runasuid and qg_runasgid for specials? */ 598090792Sgshapiro i = safedirpath(basedir, RunAsUid, RunAsGid, NULL, 598190792Sgshapiro sff, 0, 0); 598290792Sgshapiro if (i != 0 && tTd(41, 2)) 598390792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", 598490792Sgshapiro basedir, sm_errstring(i)); 598564562Sgshapiro } 598664562Sgshapiro 598790792Sgshapiro if ((dp = opendir(prefix)) == NULL) 598864562Sgshapiro { 598990792Sgshapiro syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix); 599064562Sgshapiro if (tTd(41, 2)) 599190792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n", 599290792Sgshapiro qg->qg_qdir, prefix, 599390792Sgshapiro sm_errstring(errno)); 599464562Sgshapiro ExitStat = EX_CONFIG; 599590792Sgshapiro return qn; 599664562Sgshapiro } 599764562Sgshapiro while ((d = readdir(dp)) != NULL) 599864562Sgshapiro { 599990792Sgshapiro i = strlen(d->d_name); 600090792Sgshapiro if (i < len || strncmp(d->d_name, cp, len) != 0) 600164562Sgshapiro { 600264562Sgshapiro if (tTd(41, 5)) 600390792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\", skipped\n", 600464562Sgshapiro d->d_name); 600564562Sgshapiro continue; 600664562Sgshapiro } 600790792Sgshapiro 600890792Sgshapiro /* Create relative pathname: prefix + local directory */ 600990792Sgshapiro i = sizeof(relpath) - off; 601090792Sgshapiro if (sm_strlcpy(relpath + off, d->d_name, i) >= i) 601190792Sgshapiro continue; /* way too long */ 601290792Sgshapiro 601390792Sgshapiro if (!chkqdir(relpath, sff)) 601464562Sgshapiro continue; 601564562Sgshapiro 601690792Sgshapiro if (qg->qg_qpaths == NULL) 601764562Sgshapiro { 601890792Sgshapiro slotsleft = INITIAL_SLOTS; 601990792Sgshapiro qg->qg_qpaths = (QPATHS *)xalloc((sizeof *qg->qg_qpaths) * 602090792Sgshapiro slotsleft); 602190792Sgshapiro qg->qg_numqueues = 0; 602264562Sgshapiro } 602364562Sgshapiro else if (slotsleft < 1) 602464562Sgshapiro { 602590792Sgshapiro qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths, 602690792Sgshapiro (sizeof *qg->qg_qpaths) * 602790792Sgshapiro (qg->qg_numqueues + 602890792Sgshapiro ADD_SLOTS)); 602990792Sgshapiro if (qg->qg_qpaths == NULL) 603064562Sgshapiro { 603164562Sgshapiro (void) closedir(dp); 603290792Sgshapiro return qn; 603364562Sgshapiro } 603490792Sgshapiro slotsleft += ADD_SLOTS; 603564562Sgshapiro } 603664562Sgshapiro 603764562Sgshapiro /* check subdirs */ 603890792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB; 603964562Sgshapiro 604090792Sgshapiro#define CHKRSUBDIR(name, flag) \ 604190792Sgshapiro (void) sm_strlcpyn(subdir, sizeof subdir, 3, relpath, "/", name); \ 604290792Sgshapiro if (chkqdir(subdir, sff)) \ 604390792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \ 604490792Sgshapiro else 604564562Sgshapiro 604664562Sgshapiro 604790792Sgshapiro CHKRSUBDIR("qf", QP_SUBQF); 604890792Sgshapiro CHKRSUBDIR("df", QP_SUBDF); 604990792Sgshapiro CHKRSUBDIR("xf", QP_SUBXF); 605090792Sgshapiro 605164562Sgshapiro /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */ 605264562Sgshapiro /* maybe even - 17 (subdirs) */ 605390792Sgshapiro 605490792Sgshapiro if (prefix[0] != '.') 605590792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 605690792Sgshapiro newstr(relpath); 605790792Sgshapiro else 605890792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 605990792Sgshapiro newstr(d->d_name); 606090792Sgshapiro 606164562Sgshapiro if (tTd(41, 2)) 606290792Sgshapiro sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", 606390792Sgshapiro qg->qg_numqueues, relpath, 606490792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs); 606590792Sgshapiro#if SM_CONF_SHM 606690792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn; 606790792Sgshapiro *phash = hash_q(relpath, *phash); 606890792Sgshapiro#endif /* SM_CONF_SHM */ 606990792Sgshapiro qg->qg_numqueues++; 607090792Sgshapiro ++qn; 607164562Sgshapiro slotsleft--; 607264562Sgshapiro } 607364562Sgshapiro (void) closedir(dp); 607490792Sgshapiro 607590792Sgshapiro /* undo damage */ 607690792Sgshapiro *delim = '/'; 607764562Sgshapiro } 607890792Sgshapiro if (qg->qg_numqueues == 0) 607964562Sgshapiro { 608090792Sgshapiro qg->qg_qpaths = (QPATHS *) xalloc(sizeof *qg->qg_qpaths); 608164562Sgshapiro 608264562Sgshapiro /* test path to get warning messages */ 608390792Sgshapiro i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0); 608490792Sgshapiro if (i == ENOENT) 608564562Sgshapiro { 608690792Sgshapiro syserr("can not opendir(%s)", qpath); 608764562Sgshapiro if (tTd(41, 2)) 608890792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", 608990792Sgshapiro qpath, sm_errstring(i)); 609064562Sgshapiro ExitStat = EX_CONFIG; 609190792Sgshapiro return qn; 609264562Sgshapiro } 609364562Sgshapiro 609490792Sgshapiro qg->qg_qpaths[0].qp_subdirs = QP_NOSUB; 609590792Sgshapiro qg->qg_numqueues = 1; 609690792Sgshapiro 609764562Sgshapiro /* check subdirs */ 609890792Sgshapiro#define CHKSUBDIR(name, flag) \ 609990792Sgshapiro (void) sm_strlcpyn(subdir, sizeof subdir, 3, qg->qg_qdir, "/", name); \ 610090792Sgshapiro if (chkqdir(subdir, sff)) \ 610190792Sgshapiro qg->qg_qpaths[0].qp_subdirs |= flag; \ 610290792Sgshapiro else 610364562Sgshapiro 610490792Sgshapiro CHKSUBDIR("qf", QP_SUBQF); 610590792Sgshapiro CHKSUBDIR("df", QP_SUBDF); 610690792Sgshapiro CHKSUBDIR("xf", QP_SUBXF); 610764562Sgshapiro 610890792Sgshapiro if (qg->qg_qdir[blen - 1] != '\0' && 610990792Sgshapiro qg->qg_qdir[blen] != '\0') 611090792Sgshapiro { 611190792Sgshapiro /* 611290792Sgshapiro ** Copy the last component into qpaths and 611390792Sgshapiro ** cut off qdir 611490792Sgshapiro */ 611590792Sgshapiro 611690792Sgshapiro qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen); 611790792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; 611890792Sgshapiro } 611990792Sgshapiro else 612090792Sgshapiro qg->qg_qpaths[0].qp_name = newstr("."); 612190792Sgshapiro 612290792Sgshapiro#if SM_CONF_SHM 612390792Sgshapiro qg->qg_qpaths[0].qp_idx = qn; 612490792Sgshapiro *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash); 612590792Sgshapiro#endif /* SM_CONF_SHM */ 612690792Sgshapiro ++qn; 612764562Sgshapiro } 612890792Sgshapiro return qn; 612964562Sgshapiro} 613064562Sgshapiro 613190792Sgshapiro/* 613290792Sgshapiro** FILESYS_FIND -- find entry in FileSys table, or add new one 613390792Sgshapiro** 613490792Sgshapiro** Given the pathname of a directory, determine the file system 613590792Sgshapiro** in which that directory resides, and return a pointer to the 613690792Sgshapiro** entry in the FileSys table that describes the file system. 613790792Sgshapiro** A new entry is added if necessary (and requested). 613890792Sgshapiro** If the directory does not exist, -1 is returned. 613990792Sgshapiro** 614090792Sgshapiro** Parameters: 614190792Sgshapiro** path -- pathname of directory 614290792Sgshapiro** add -- add to structure if not found. 614390792Sgshapiro** 614490792Sgshapiro** Returns: 614590792Sgshapiro** >=0: found: index in file system table 614690792Sgshapiro** <0: some error, i.e., 614790792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 614890792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 614990792Sgshapiro** FSF_NOT_FOUND: not in list 615090792Sgshapiro*/ 615190792Sgshapiro 615290792Sgshapirostatic short filesys_find __P((char *, bool)); 615390792Sgshapiro 615490792Sgshapiro#define FSF_NOT_FOUND (-1) 615590792Sgshapiro#define FSF_STAT_FAIL (-2) 615690792Sgshapiro#define FSF_TOO_MANY (-3) 615790792Sgshapiro 615890792Sgshapirostatic short 615990792Sgshapirofilesys_find(path, add) 616090792Sgshapiro char *path; 616190792Sgshapiro bool add; 616290792Sgshapiro{ 616390792Sgshapiro struct stat st; 616490792Sgshapiro short i; 616590792Sgshapiro 616690792Sgshapiro if (stat(path, &st) < 0) 616790792Sgshapiro { 616890792Sgshapiro syserr("cannot stat queue directory %s", path); 616990792Sgshapiro return FSF_STAT_FAIL; 617090792Sgshapiro } 617190792Sgshapiro for (i = 0; i < NumFileSys; ++i) 617290792Sgshapiro { 617390792Sgshapiro if (FILE_SYS_DEV(i) == st.st_dev) 617490792Sgshapiro return i; 617590792Sgshapiro } 617690792Sgshapiro if (i >= MAXFILESYS) 617790792Sgshapiro { 617890792Sgshapiro syserr("too many queue file systems (%d max)", MAXFILESYS); 617990792Sgshapiro return FSF_TOO_MANY; 618090792Sgshapiro } 618190792Sgshapiro if (!add) 618290792Sgshapiro return FSF_NOT_FOUND; 618390792Sgshapiro 618490792Sgshapiro ++NumFileSys; 618590792Sgshapiro FILE_SYS_NAME(i) = path; 618690792Sgshapiro FILE_SYS_DEV(i) = st.st_dev; 618790792Sgshapiro FILE_SYS_AVAIL(i) = 0; 618890792Sgshapiro FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */ 618990792Sgshapiro return i; 619090792Sgshapiro} 619190792Sgshapiro 619290792Sgshapiro/* 619390792Sgshapiro** FILESYS_SETUP -- set up mapping from queue directories to file systems 619490792Sgshapiro** 619590792Sgshapiro** This data structure is used to efficiently check the amount of 619690792Sgshapiro** free space available in a set of queue directories. 619790792Sgshapiro** 619890792Sgshapiro** Parameters: 619990792Sgshapiro** add -- initialize structure if necessary. 620090792Sgshapiro** 620190792Sgshapiro** Returns: 620290792Sgshapiro** 0: success 620390792Sgshapiro** <0: some error, i.e., 620490792Sgshapiro** FSF_NOT_FOUND: not in list 620590792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 620690792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 620790792Sgshapiro*/ 620890792Sgshapiro 620990792Sgshapirostatic int filesys_setup __P((bool)); 621090792Sgshapiro 621190792Sgshapirostatic int 621290792Sgshapirofilesys_setup(add) 621390792Sgshapiro bool add; 621490792Sgshapiro{ 621590792Sgshapiro int i, j; 621690792Sgshapiro short fs; 621790792Sgshapiro int ret; 621890792Sgshapiro 621990792Sgshapiro ret = 0; 622090792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 622190792Sgshapiro { 622290792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; ++j) 622390792Sgshapiro { 622490792Sgshapiro QPATHS *qp = &Queue[i]->qg_qpaths[j]; 622590792Sgshapiro 622690792Sgshapiro fs = filesys_find(qp->qp_name, add); 622790792Sgshapiro if (fs >= 0) 622890792Sgshapiro qp->qp_fsysidx = fs; 622990792Sgshapiro else 623090792Sgshapiro qp->qp_fsysidx = 0; 623190792Sgshapiro if (fs < ret) 623290792Sgshapiro ret = fs; 623390792Sgshapiro } 623490792Sgshapiro } 623590792Sgshapiro return ret; 623690792Sgshapiro} 623790792Sgshapiro 623890792Sgshapiro/* 623990792Sgshapiro** FILESYS_UPDATE -- update amount of free space on all file systems 624090792Sgshapiro** 624190792Sgshapiro** The FileSys table is used to cache the amount of free space 624290792Sgshapiro** available on all queue directory file systems. 624390792Sgshapiro** This function updates the cached information if it has expired. 624490792Sgshapiro** 624590792Sgshapiro** Parameters: 624690792Sgshapiro** none. 624790792Sgshapiro** 624890792Sgshapiro** Returns: 624990792Sgshapiro** none. 625090792Sgshapiro** 625190792Sgshapiro** Side Effects: 625290792Sgshapiro** Updates FileSys table. 625390792Sgshapiro*/ 625490792Sgshapiro 625590792Sgshapirovoid 625690792Sgshapirofilesys_update() 625790792Sgshapiro{ 625890792Sgshapiro int i; 625990792Sgshapiro long avail, blksize; 626090792Sgshapiro time_t now; 626190792Sgshapiro static time_t nextupdate = 0; 626290792Sgshapiro 626390792Sgshapiro#if SM_CONF_SHM 626490792Sgshapiro /* only the daemon updates this structure */ 626590792Sgshapiro if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid) 626690792Sgshapiro return; 626790792Sgshapiro#endif /* SM_CONF_SHM */ 626890792Sgshapiro now = curtime(); 626990792Sgshapiro if (now < nextupdate) 627090792Sgshapiro return; 627190792Sgshapiro nextupdate = now + FILESYS_UPDATE_INTERVAL; 627290792Sgshapiro for (i = 0; i < NumFileSys; ++i) 627390792Sgshapiro { 627490792Sgshapiro FILESYS *fs = &FILE_SYS(i); 627590792Sgshapiro 627690792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 627790792Sgshapiro if (avail < 0 || blksize <= 0) 627890792Sgshapiro { 627990792Sgshapiro if (LogLevel > 5) 628090792Sgshapiro sm_syslog(LOG_ERR, NOQID, 628190792Sgshapiro "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld", 628290792Sgshapiro sm_errstring(errno), 628390792Sgshapiro FILE_SYS_NAME(i), avail, blksize); 628490792Sgshapiro fs->fs_avail = 0; 628590792Sgshapiro fs->fs_blksize = 1024; /* avoid divide by zero */ 628690792Sgshapiro nextupdate = now + 2; /* let's do this soon again */ 628790792Sgshapiro } 628890792Sgshapiro else 628990792Sgshapiro { 629090792Sgshapiro fs->fs_avail = avail; 629190792Sgshapiro fs->fs_blksize = blksize; 629290792Sgshapiro } 629390792Sgshapiro } 629490792Sgshapiro} 629590792Sgshapiro 629690792Sgshapiro#if _FFR_ANY_FREE_FS 629790792Sgshapiro/* 629890792Sgshapiro** FILESYS_FREE -- check whether there is at least one fs with enough space. 629990792Sgshapiro** 630090792Sgshapiro** Parameters: 630190792Sgshapiro** fsize -- file size in bytes 630290792Sgshapiro** 630390792Sgshapiro** Returns: 630490792Sgshapiro** true iff there is one fs with more than fsize bytes free. 630590792Sgshapiro*/ 630690792Sgshapiro 630790792Sgshapirobool 630890792Sgshapirofilesys_free(fsize) 630990792Sgshapiro long fsize; 631090792Sgshapiro{ 631190792Sgshapiro int i; 631290792Sgshapiro 631390792Sgshapiro if (fsize <= 0) 631490792Sgshapiro return true; 631590792Sgshapiro for (i = 0; i < NumFileSys; ++i) 631690792Sgshapiro { 631790792Sgshapiro long needed = 0; 631890792Sgshapiro 631990792Sgshapiro if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0) 632090792Sgshapiro continue; 632190792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(i) 632290792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(i) 632390792Sgshapiro > 0) ? 1 : 0) 632490792Sgshapiro + MinBlocksFree; 632590792Sgshapiro if (needed <= FILE_SYS_AVAIL(i)) 632690792Sgshapiro return true; 632790792Sgshapiro } 632890792Sgshapiro return false; 632990792Sgshapiro} 633090792Sgshapiro#endif /* _FFR_ANY_FREE_FS */ 633190792Sgshapiro 633290792Sgshapiro#if _FFR_CONTROL_MSTAT 633390792Sgshapiro/* 633490792Sgshapiro** DISK_STATUS -- show amount of free space in queue directories 633590792Sgshapiro** 633690792Sgshapiro** Parameters: 633790792Sgshapiro** out -- output file pointer. 633890792Sgshapiro** prefix -- string to output in front of each line. 633990792Sgshapiro** 634090792Sgshapiro** Returns: 634190792Sgshapiro** none. 634290792Sgshapiro*/ 634390792Sgshapiro 634490792Sgshapirovoid 634590792Sgshapirodisk_status(out, prefix) 634690792Sgshapiro SM_FILE_T *out; 634790792Sgshapiro char *prefix; 634890792Sgshapiro{ 634990792Sgshapiro int i; 635090792Sgshapiro long avail, blksize; 635190792Sgshapiro long free; 635290792Sgshapiro 635390792Sgshapiro for (i = 0; i < NumFileSys; ++i) 635490792Sgshapiro { 635590792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 635690792Sgshapiro if (avail >= 0 && blksize > 0) 635790792Sgshapiro { 635890792Sgshapiro free = (long)((double) avail * 635990792Sgshapiro ((double) blksize / 1024)); 636090792Sgshapiro } 636190792Sgshapiro else 636290792Sgshapiro free = -1; 636390792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 636490792Sgshapiro "%s%d/%s/%ld\r\n", 636590792Sgshapiro prefix, i, 636690792Sgshapiro FILE_SYS_NAME(i), 636790792Sgshapiro free); 636890792Sgshapiro } 636990792Sgshapiro} 637090792Sgshapiro#endif /* _FFR_CONTROL_MSTAT */ 637190792Sgshapiro 637290792Sgshapiro#if SM_CONF_SHM 637390792Sgshapiro/* 637490792Sgshapiro** UPD_QS -- update information about queue when adding/deleting an entry 637590792Sgshapiro** 637690792Sgshapiro** Parameters: 637790792Sgshapiro** e -- envelope. 637890792Sgshapiro** delete -- delete/add entry. 637990792Sgshapiro** avail -- update the space available as well. 638090792Sgshapiro** 638190792Sgshapiro** Returns: 638290792Sgshapiro** none. 638390792Sgshapiro** 638490792Sgshapiro** Side Effects: 638590792Sgshapiro** Modifies available space in filesystem. 638690792Sgshapiro** Changes number of entries in queue directory. 638790792Sgshapiro*/ 638890792Sgshapiro 638990792Sgshapirovoid 639090792Sgshapiroupd_qs(e, delete, avail) 639190792Sgshapiro ENVELOPE *e; 639290792Sgshapiro bool delete; 639390792Sgshapiro bool avail; 639490792Sgshapiro{ 639590792Sgshapiro short fidx; 639690792Sgshapiro int idx; 639790792Sgshapiro long s; 639890792Sgshapiro 639990792Sgshapiro if (ShmId == SM_SHM_NO_ID || e == NULL) 640090792Sgshapiro return; 640190792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 640290792Sgshapiro return; 640390792Sgshapiro idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx; 640490792Sgshapiro 640590792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 640690792Sgshapiro if (QSHM_ENTRIES(idx) >= 0) 640790792Sgshapiro { 640890792Sgshapiro if (delete) 640990792Sgshapiro --QSHM_ENTRIES(idx); 641090792Sgshapiro else 641190792Sgshapiro ++QSHM_ENTRIES(idx); 641290792Sgshapiro } 641390792Sgshapiro 641490792Sgshapiro fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx; 641590792Sgshapiro if (fidx < 0) 641690792Sgshapiro return; 641790792Sgshapiro 641890792Sgshapiro /* update available space also? (might be loseqfile) */ 641990792Sgshapiro if (!avail) 642090792Sgshapiro return; 642190792Sgshapiro 642290792Sgshapiro /* convert size to blocks; this causes rounding errors */ 642390792Sgshapiro s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx); 642490792Sgshapiro if (s == 0) 642590792Sgshapiro return; 642690792Sgshapiro 642790792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 642890792Sgshapiro if (delete) 642990792Sgshapiro FILE_SYS_AVAIL(fidx) += s; 643090792Sgshapiro else 643190792Sgshapiro FILE_SYS_AVAIL(fidx) -= s; 643290792Sgshapiro 643390792Sgshapiro} 643494334Sgshapiro 643594334Sgshapiro#if _FFR_SELECT_SHM 643694334Sgshapiro 643794334Sgshapirostatic bool write_key_file __P((char *, long)); 643894334Sgshapirostatic long read_key_file __P((char *, long)); 643994334Sgshapiro 644090792Sgshapiro/* 644194334Sgshapiro** WRITE_KEY_FILE -- record some key into a file. 644294334Sgshapiro** 644394334Sgshapiro** Parameters: 644494334Sgshapiro** keypath -- file name. 644594334Sgshapiro** key -- key to write. 644694334Sgshapiro** 644794334Sgshapiro** Returns: 644894334Sgshapiro** true iff file could be written. 644994334Sgshapiro** 645094334Sgshapiro** Side Effects: 645194334Sgshapiro** writes file. 645294334Sgshapiro*/ 645394334Sgshapiro 645494334Sgshapirostatic bool 645594334Sgshapirowrite_key_file(keypath, key) 645694334Sgshapiro char *keypath; 645794334Sgshapiro long key; 645894334Sgshapiro{ 645994334Sgshapiro bool ok; 646094334Sgshapiro long sff; 646194334Sgshapiro SM_FILE_T *keyf; 646294334Sgshapiro 646394334Sgshapiro ok = false; 646494334Sgshapiro if (keypath == NULL || *keypath == '\0') 646594334Sgshapiro return ok; 646694334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; 646794334Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 646894334Sgshapiro sff |= SFF_OPENASROOT; 6469110560Sgshapiro keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff); 647094334Sgshapiro if (keyf == NULL) 647194334Sgshapiro { 647294334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", 647394334Sgshapiro keypath, sm_errstring(errno)); 647494334Sgshapiro } 647594334Sgshapiro else 647694334Sgshapiro { 647794334Sgshapiro ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) != 647894334Sgshapiro SM_IO_EOF; 6479110560Sgshapiro ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok; 648094334Sgshapiro } 648194334Sgshapiro return ok; 648294334Sgshapiro} 648394334Sgshapiro 648494334Sgshapiro/* 648594334Sgshapiro** READ_KEY_FILE -- read a key from a file. 648694334Sgshapiro** 648794334Sgshapiro** Parameters: 648894334Sgshapiro** keypath -- file name. 648994334Sgshapiro** key -- default key. 649094334Sgshapiro** 649194334Sgshapiro** Returns: 649294334Sgshapiro** key. 649394334Sgshapiro*/ 649494334Sgshapiro 649594334Sgshapirostatic long 649694334Sgshapiroread_key_file(keypath, key) 649794334Sgshapiro char *keypath; 649894334Sgshapiro long key; 649994334Sgshapiro{ 650094334Sgshapiro int r; 650194334Sgshapiro long sff, n; 650294334Sgshapiro SM_FILE_T *keyf; 650394334Sgshapiro 650494334Sgshapiro if (keypath == NULL || *keypath == '\0') 650594334Sgshapiro return key; 650694334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY; 6507110560Sgshapiro if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid)) 650894334Sgshapiro sff |= SFF_OPENASROOT; 6509110560Sgshapiro keyf = safefopen(keypath, O_RDONLY, FileMode, sff); 651094334Sgshapiro if (keyf == NULL) 651194334Sgshapiro { 651294334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s", 651394334Sgshapiro keypath, sm_errstring(errno)); 651494334Sgshapiro } 651594334Sgshapiro else 651694334Sgshapiro { 651794334Sgshapiro r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n); 651894334Sgshapiro if (r == 1) 651994334Sgshapiro key = n; 652094334Sgshapiro (void) sm_io_close(keyf, SM_TIME_DEFAULT); 652194334Sgshapiro } 652294334Sgshapiro return key; 652394334Sgshapiro} 652494334Sgshapiro#endif /* _FFR_SELECT_SHM */ 652594334Sgshapiro 652694334Sgshapiro/* 652790792Sgshapiro** INIT_SHM -- initialize shared memory structure 652890792Sgshapiro** 652990792Sgshapiro** Initialize or attach to shared memory segment. 653090792Sgshapiro** Currently it is not a fatal error if this doesn't work. 653190792Sgshapiro** However, it causes us to have a "fallback" storage location 653290792Sgshapiro** for everything that is supposed to be in the shared memory, 653390792Sgshapiro** which makes the code slightly ugly. 653490792Sgshapiro** 653590792Sgshapiro** Parameters: 653690792Sgshapiro** qn -- number of queue directories. 653790792Sgshapiro** owner -- owner of shared memory. 653890792Sgshapiro** hash -- identifies data that is stored in shared memory. 653990792Sgshapiro** 654090792Sgshapiro** Returns: 654190792Sgshapiro** none. 654290792Sgshapiro*/ 654390792Sgshapiro 654490792Sgshapirostatic void init_shm __P((int, bool, unsigned int)); 654590792Sgshapiro 654690792Sgshapirostatic void 654790792Sgshapiroinit_shm(qn, owner, hash) 654890792Sgshapiro int qn; 654990792Sgshapiro bool owner; 655090792Sgshapiro unsigned int hash; 655190792Sgshapiro{ 655290792Sgshapiro int i; 655394334Sgshapiro#if _FFR_SELECT_SHM 655494334Sgshapiro bool keyselect; 655594334Sgshapiro#endif /* _FFR_SELECT_SHM */ 655690792Sgshapiro 655790792Sgshapiro PtrFileSys = &FileSys[0]; 655890792Sgshapiro PNumFileSys = &Numfilesys; 655994334Sgshapiro#if _FFR_SELECT_SHM 656094334Sgshapiro/* if this "key" is specified: select one yourself */ 656194334Sgshapiro# define SEL_SHM_KEY ((key_t) -1) 656294334Sgshapiro# define FIRST_SHM_KEY 25 656394334Sgshapiro#endif /* _FFR_SELECT_SHM */ 656490792Sgshapiro 656590792Sgshapiro /* This allows us to disable shared memory at runtime. */ 656690792Sgshapiro if (ShmKey != 0) 656790792Sgshapiro { 656890792Sgshapiro int count; 656990792Sgshapiro int save_errno; 657090792Sgshapiro 657190792Sgshapiro count = 0; 657290792Sgshapiro shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T); 657394334Sgshapiro#if _FFR_SELECT_SHM 657494334Sgshapiro keyselect = ShmKey == SEL_SHM_KEY; 657594334Sgshapiro if (keyselect) 657694334Sgshapiro { 657794334Sgshapiro if (owner) 657894334Sgshapiro ShmKey = FIRST_SHM_KEY; 657994334Sgshapiro else 658094334Sgshapiro { 658194334Sgshapiro ShmKey = read_key_file(ShmKeyFile, ShmKey); 658294334Sgshapiro keyselect = false; 658394334Sgshapiro if (ShmKey == SEL_SHM_KEY) 658494334Sgshapiro goto error; 658594334Sgshapiro } 658694334Sgshapiro } 658794334Sgshapiro#endif /* _FFR_SELECT_SHM */ 658890792Sgshapiro for (;;) 658990792Sgshapiro { 659090792Sgshapiro /* XXX: maybe allow read access for group? */ 659190792Sgshapiro Pshm = sm_shmstart(ShmKey, shms, SHM_R|SHM_W, &ShmId, 659290792Sgshapiro owner); 659390792Sgshapiro save_errno = errno; 659490792Sgshapiro if (Pshm != NULL || save_errno != EEXIST) 659590792Sgshapiro break; 659690792Sgshapiro if (++count >= 3) 659794334Sgshapiro { 659894334Sgshapiro#if _FFR_SELECT_SHM 659994334Sgshapiro if (keyselect) 660094334Sgshapiro { 660194334Sgshapiro ++ShmKey; 660294334Sgshapiro 660394334Sgshapiro /* back where we started? */ 660494334Sgshapiro if (ShmKey == SEL_SHM_KEY) 660594334Sgshapiro break; 660694334Sgshapiro continue; 660794334Sgshapiro } 660894334Sgshapiro#endif /* _FFR_SELECT_SHM */ 660990792Sgshapiro break; 661094334Sgshapiro } 661194334Sgshapiro#if _FFR_SELECT_SHM 661294334Sgshapiro /* only sleep if we are at the first key */ 661394334Sgshapiro if (!keyselect || ShmKey == SEL_SHM_KEY) 661494334Sgshapiro#endif /* _FFR_SELECT_SHM */ 661590792Sgshapiro sleep(count); 661690792Sgshapiro } 661790792Sgshapiro if (Pshm != NULL) 661890792Sgshapiro { 661990792Sgshapiro int *p; 662090792Sgshapiro 662194334Sgshapiro#if _FFR_SELECT_SHM 662294334Sgshapiro if (keyselect) 662394334Sgshapiro (void) write_key_file(ShmKeyFile, (long) ShmKey); 662494334Sgshapiro#endif /* _FFR_SELECT_SHM */ 662590792Sgshapiro p = (int *) Pshm; 662690792Sgshapiro if (owner) 662790792Sgshapiro { 662890792Sgshapiro *p = (int) shms; 662990792Sgshapiro *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid; 663090792Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 663190792Sgshapiro *p = hash; 663290792Sgshapiro } 663390792Sgshapiro else 663490792Sgshapiro { 663590792Sgshapiro if (*p != (int) shms) 663690792Sgshapiro { 663790792Sgshapiro save_errno = EINVAL; 663890792Sgshapiro cleanup_shm(false); 663990792Sgshapiro goto error; 664090792Sgshapiro } 664190792Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 664290792Sgshapiro if (*p != (int) hash) 664390792Sgshapiro { 664490792Sgshapiro save_errno = EINVAL; 664590792Sgshapiro cleanup_shm(false); 664690792Sgshapiro goto error; 664790792Sgshapiro } 664890792Sgshapiro 664990792Sgshapiro /* 665090792Sgshapiro ** XXX how to check the pid? 665190792Sgshapiro ** Read it from the pid-file? That does 665290792Sgshapiro ** not need to exist. 665390792Sgshapiro ** We could disable shm if we can't confirm 665490792Sgshapiro ** that it is the right one. 665590792Sgshapiro */ 665690792Sgshapiro } 665790792Sgshapiro 665890792Sgshapiro PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm); 665990792Sgshapiro PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm); 666090792Sgshapiro QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm); 666190792Sgshapiro PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm); 666290792Sgshapiro *PRSATmpCnt = 0; 666390792Sgshapiro if (owner) 666490792Sgshapiro { 666590792Sgshapiro /* initialize values in shared memory */ 666690792Sgshapiro NumFileSys = 0; 666790792Sgshapiro for (i = 0; i < qn; i++) 666890792Sgshapiro QShm[i].qs_entries = -1; 666990792Sgshapiro } 667090792Sgshapiro return; 667190792Sgshapiro } 667290792Sgshapiro error: 667390792Sgshapiro if (LogLevel > (owner ? 8 : 11)) 667490792Sgshapiro { 667590792Sgshapiro sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID, 667690792Sgshapiro "can't %s shared memory, key=%ld: %s", 667790792Sgshapiro owner ? "initialize" : "attach to", 667890792Sgshapiro (long) ShmKey, sm_errstring(save_errno)); 667990792Sgshapiro } 668090792Sgshapiro } 668190792Sgshapiro} 668290792Sgshapiro#endif /* SM_CONF_SHM */ 668390792Sgshapiro 668490792Sgshapiro/* 668590792Sgshapiro** SETUP_QUEUES -- setup all queue groups 668690792Sgshapiro** 668790792Sgshapiro** Parameters: 668890792Sgshapiro** owner -- owner of shared memory. 668990792Sgshapiro** 669090792Sgshapiro** Returns: 669190792Sgshapiro** none. 669290792Sgshapiro** 669390792Sgshapiro#if SM_CONF_SHM 669490792Sgshapiro** Side Effects: 669590792Sgshapiro** attaches shared memory. 669690792Sgshapiro#endif * SM_CONF_SHM * 669790792Sgshapiro*/ 669890792Sgshapiro 669990792Sgshapirovoid 670090792Sgshapirosetup_queues(owner) 670190792Sgshapiro bool owner; 670290792Sgshapiro{ 670390792Sgshapiro int i, qn, len; 670490792Sgshapiro unsigned int hashval; 670594334Sgshapiro time_t now; 670690792Sgshapiro char basedir[MAXPATHLEN]; 670790792Sgshapiro struct stat st; 670890792Sgshapiro 670990792Sgshapiro /* 671090792Sgshapiro ** Determine basedir for all queue directories. 671190792Sgshapiro ** All queue directories must be (first level) subdirectories 671290792Sgshapiro ** of the basedir. The basedir is the QueueDir 671390792Sgshapiro ** without wildcards, but with trailing / 671490792Sgshapiro */ 671590792Sgshapiro 671690792Sgshapiro hashval = 0; 671790792Sgshapiro errno = 0; 671890792Sgshapiro len = sm_strlcpy(basedir, QueueDir, sizeof basedir); 6719111823Sgshapiro 6720111823Sgshapiro /* Provide space for trailing '/' */ 6721111823Sgshapiro if (len >= sizeof basedir - 1) 672290792Sgshapiro { 672390792Sgshapiro syserr("QueueDirectory: path too long: %d, max %d", 6724111823Sgshapiro len, (int) sizeof basedir - 1); 672590792Sgshapiro ExitStat = EX_CONFIG; 672690792Sgshapiro return; 672790792Sgshapiro } 672890792Sgshapiro SM_ASSERT(len > 0); 672990792Sgshapiro if (basedir[len - 1] == '*') 673090792Sgshapiro { 673190792Sgshapiro char *cp; 673290792Sgshapiro 673390792Sgshapiro cp = SM_LAST_DIR_DELIM(basedir); 673490792Sgshapiro if (cp == NULL) 673590792Sgshapiro { 673690792Sgshapiro syserr("QueueDirectory: can not wildcard relative path \"%s\"", 673790792Sgshapiro QueueDir); 673890792Sgshapiro if (tTd(41, 2)) 673990792Sgshapiro sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n", 674090792Sgshapiro QueueDir); 674190792Sgshapiro ExitStat = EX_CONFIG; 674290792Sgshapiro return; 674390792Sgshapiro } 674490792Sgshapiro 674590792Sgshapiro /* cut off wildcard pattern */ 674690792Sgshapiro *++cp = '\0'; 674790792Sgshapiro len = cp - basedir; 674890792Sgshapiro } 674990792Sgshapiro else if (!SM_IS_DIR_DELIM(basedir[len - 1])) 675090792Sgshapiro { 675190792Sgshapiro /* append trailing slash since it is a directory */ 675290792Sgshapiro basedir[len] = '/'; 675390792Sgshapiro basedir[++len] = '\0'; 675490792Sgshapiro } 675590792Sgshapiro 675690792Sgshapiro /* len counts up to the last directory delimiter */ 675790792Sgshapiro SM_ASSERT(basedir[len - 1] == '/'); 675890792Sgshapiro 675990792Sgshapiro if (chdir(basedir) < 0) 676090792Sgshapiro { 676190792Sgshapiro int save_errno = errno; 676290792Sgshapiro 676390792Sgshapiro syserr("can not chdir(%s)", basedir); 676490792Sgshapiro if (save_errno == EACCES) 676590792Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 676690792Sgshapiro "Program mode requires special privileges, e.g., root or TrustedUser.\n"); 676790792Sgshapiro if (tTd(41, 2)) 676890792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 676990792Sgshapiro basedir, sm_errstring(errno)); 677090792Sgshapiro ExitStat = EX_CONFIG; 677190792Sgshapiro return; 677290792Sgshapiro } 677390792Sgshapiro#if SM_CONF_SHM 677490792Sgshapiro hashval = hash_q(basedir, hashval); 677590792Sgshapiro#endif /* SM_CONF_SHM */ 677690792Sgshapiro 677794334Sgshapiro /* initialize for queue runs */ 677894334Sgshapiro DoQueueRun = false; 677994334Sgshapiro now = curtime(); 678094334Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 678194334Sgshapiro Queue[i]->qg_nextrun = now; 678290792Sgshapiro 678390792Sgshapiro 678490792Sgshapiro if (UseMSP && OpMode != MD_TEST) 678590792Sgshapiro { 678690792Sgshapiro long sff = SFF_CREAT; 678790792Sgshapiro 678890792Sgshapiro if (stat(".", &st) < 0) 678990792Sgshapiro { 679090792Sgshapiro syserr("can not stat(%s)", basedir); 679190792Sgshapiro if (tTd(41, 2)) 679290792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 679390792Sgshapiro basedir, sm_errstring(errno)); 679490792Sgshapiro ExitStat = EX_CONFIG; 679590792Sgshapiro return; 679690792Sgshapiro } 679790792Sgshapiro if (RunAsUid == 0) 679890792Sgshapiro sff |= SFF_ROOTOK; 679990792Sgshapiro 680090792Sgshapiro /* 680190792Sgshapiro ** Check queue directory permissions. 680290792Sgshapiro ** Can we write to a group writable queue directory? 680390792Sgshapiro */ 680490792Sgshapiro 680590792Sgshapiro if (bitset(S_IWGRP, QueueFileMode) && 680690792Sgshapiro bitset(S_IWGRP, st.st_mode) && 680790792Sgshapiro safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff, 680890792Sgshapiro QueueFileMode, NULL) != 0) 680990792Sgshapiro { 681090792Sgshapiro syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)", 681190792Sgshapiro basedir, (int) RunAsGid, (int) st.st_gid); 681290792Sgshapiro } 681390792Sgshapiro if (bitset(S_IWOTH|S_IXOTH, st.st_mode)) 681490792Sgshapiro { 681590792Sgshapiro#if _FFR_MSP_PARANOIA 681690792Sgshapiro syserr("dangerous permissions=%o on queue directory %s", 681790792Sgshapiro (int) st.st_mode, basedir); 681890792Sgshapiro#else /* _FFR_MSP_PARANOIA */ 681990792Sgshapiro if (LogLevel > 0) 682090792Sgshapiro sm_syslog(LOG_ERR, NOQID, 682190792Sgshapiro "dangerous permissions=%o on queue directory %s", 682290792Sgshapiro (int) st.st_mode, basedir); 682390792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 682490792Sgshapiro } 682590792Sgshapiro#if _FFR_MSP_PARANOIA 682690792Sgshapiro if (NumQueue > 1) 682790792Sgshapiro syserr("can not use multiple queues for MSP"); 682890792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 682990792Sgshapiro } 683090792Sgshapiro 683190792Sgshapiro /* initial number of queue directories */ 683290792Sgshapiro qn = 0; 683390792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 683490792Sgshapiro qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval); 683590792Sgshapiro 683690792Sgshapiro#if SM_CONF_SHM 683790792Sgshapiro init_shm(qn, owner, hashval); 683890792Sgshapiro i = filesys_setup(owner || ShmId == SM_SHM_NO_ID); 683990792Sgshapiro if (i == FSF_NOT_FOUND) 684090792Sgshapiro { 684190792Sgshapiro /* 684290792Sgshapiro ** We didn't get the right filesystem data 684390792Sgshapiro ** This may happen if we don't have the right shared memory. 684490792Sgshapiro ** So let's do this without shared memory. 684590792Sgshapiro */ 684690792Sgshapiro 684790792Sgshapiro SM_ASSERT(!owner); 684890792Sgshapiro cleanup_shm(false); /* release shared memory */ 684990792Sgshapiro i = filesys_setup(false); 685090792Sgshapiro if (i < 0) 685190792Sgshapiro syserr("filesys_setup failed twice, result=%d", i); 685290792Sgshapiro else if (LogLevel > 8) 685390792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 685490792Sgshapiro "shared memory does not contain expected data, ignored"); 685590792Sgshapiro } 685690792Sgshapiro#else /* SM_CONF_SHM */ 685790792Sgshapiro i = filesys_setup(true); 685890792Sgshapiro#endif /* SM_CONF_SHM */ 685990792Sgshapiro if (i < 0) 686090792Sgshapiro ExitStat = EX_CONFIG; 686190792Sgshapiro} 686290792Sgshapiro 686390792Sgshapiro#if SM_CONF_SHM 686490792Sgshapiro/* 686590792Sgshapiro** CLEANUP_SHM -- do some cleanup work for shared memory etc 686690792Sgshapiro** 686790792Sgshapiro** Parameters: 686890792Sgshapiro** owner -- owner of shared memory? 686990792Sgshapiro** 687090792Sgshapiro** Returns: 687190792Sgshapiro** none. 687290792Sgshapiro** 687390792Sgshapiro** Side Effects: 687490792Sgshapiro** detaches shared memory. 687590792Sgshapiro*/ 687690792Sgshapiro 687790792Sgshapirovoid 687890792Sgshapirocleanup_shm(owner) 687990792Sgshapiro bool owner; 688090792Sgshapiro{ 688190792Sgshapiro if (ShmId != SM_SHM_NO_ID) 688290792Sgshapiro { 688390792Sgshapiro if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8) 688498121Sgshapiro sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s", 688590792Sgshapiro sm_errstring(errno)); 688690792Sgshapiro Pshm = NULL; 688790792Sgshapiro ShmId = SM_SHM_NO_ID; 688890792Sgshapiro } 688990792Sgshapiro} 689090792Sgshapiro#endif /* SM_CONF_SHM */ 689190792Sgshapiro 689290792Sgshapiro/* 689390792Sgshapiro** CLEANUP_QUEUES -- do some cleanup work for queues 689490792Sgshapiro** 689590792Sgshapiro** Parameters: 689690792Sgshapiro** none. 689790792Sgshapiro** 689890792Sgshapiro** Returns: 689990792Sgshapiro** none. 690090792Sgshapiro** 690190792Sgshapiro*/ 690290792Sgshapiro 690390792Sgshapirovoid 690490792Sgshapirocleanup_queues() 690590792Sgshapiro{ 690690792Sgshapiro sync_queue_time(); 690790792Sgshapiro} 690890792Sgshapiro/* 690990792Sgshapiro** SET_DEF_QUEUEVAL -- set default values for a queue group. 691090792Sgshapiro** 691190792Sgshapiro** Parameters: 691290792Sgshapiro** qg -- queue group 691390792Sgshapiro** all -- set all values (true for default group)? 691490792Sgshapiro** 691590792Sgshapiro** Returns: 691690792Sgshapiro** none. 691790792Sgshapiro** 691890792Sgshapiro** Side Effects: 691990792Sgshapiro** sets default values for the queue group. 692090792Sgshapiro*/ 692190792Sgshapiro 692290792Sgshapirovoid 692390792Sgshapiroset_def_queueval(qg, all) 692490792Sgshapiro QUEUEGRP *qg; 692590792Sgshapiro bool all; 692690792Sgshapiro{ 692790792Sgshapiro if (bitnset(QD_DEFINED, qg->qg_flags)) 692890792Sgshapiro return; 692990792Sgshapiro if (all) 693090792Sgshapiro qg->qg_qdir = QueueDir; 693194334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 693290792Sgshapiro qg->qg_sortorder = QueueSortOrder; 693394334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 693490792Sgshapiro qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1; 693590792Sgshapiro qg->qg_nice = NiceQueueRun; 693690792Sgshapiro} 693790792Sgshapiro/* 693890792Sgshapiro** MAKEQUEUE -- define a new queue. 693990792Sgshapiro** 694090792Sgshapiro** Parameters: 694190792Sgshapiro** line -- description of queue. This is in labeled fields. 694290792Sgshapiro** The fields are: 694390792Sgshapiro** F -- the flags associated with the queue 694490792Sgshapiro** I -- the interval between running the queue 694590792Sgshapiro** J -- the maximum # of jobs in work list 694690792Sgshapiro** [M -- the maximum # of jobs in a queue run] 694790792Sgshapiro** N -- the niceness at which to run 694890792Sgshapiro** P -- the path to the queue 694990792Sgshapiro** S -- the queue sorting order 695090792Sgshapiro** R -- number of parallel queue runners 695190792Sgshapiro** r -- max recipients per envelope 695290792Sgshapiro** The first word is the canonical name of the queue. 695390792Sgshapiro** qdef -- this is a 'Q' definition from .cf 695490792Sgshapiro** 695590792Sgshapiro** Returns: 695690792Sgshapiro** none. 695790792Sgshapiro** 695890792Sgshapiro** Side Effects: 695990792Sgshapiro** enters the queue into the queue table. 696090792Sgshapiro*/ 696190792Sgshapiro 696290792Sgshapirovoid 696390792Sgshapiromakequeue(line, qdef) 696490792Sgshapiro char *line; 696590792Sgshapiro bool qdef; 696690792Sgshapiro{ 696790792Sgshapiro register char *p; 696890792Sgshapiro register QUEUEGRP *qg; 696990792Sgshapiro register STAB *s; 697090792Sgshapiro int i; 697190792Sgshapiro char fcode; 697290792Sgshapiro 697390792Sgshapiro /* allocate a queue and set up defaults */ 697490792Sgshapiro qg = (QUEUEGRP *) xalloc(sizeof *qg); 697590792Sgshapiro memset((char *) qg, '\0', sizeof *qg); 697690792Sgshapiro 697790792Sgshapiro if (line[0] == '\0') 697890792Sgshapiro { 697990792Sgshapiro syserr("name required for queue"); 698090792Sgshapiro return; 698190792Sgshapiro } 698290792Sgshapiro 698390792Sgshapiro /* collect the queue name */ 698490792Sgshapiro for (p = line; 698590792Sgshapiro *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 698690792Sgshapiro p++) 698790792Sgshapiro continue; 698890792Sgshapiro if (*p != '\0') 698990792Sgshapiro *p++ = '\0'; 699090792Sgshapiro qg->qg_name = newstr(line); 699190792Sgshapiro 699290792Sgshapiro /* set default values, can be overridden below */ 699390792Sgshapiro set_def_queueval(qg, false); 699490792Sgshapiro 699590792Sgshapiro /* now scan through and assign info from the fields */ 699690792Sgshapiro while (*p != '\0') 699790792Sgshapiro { 699890792Sgshapiro auto char *delimptr; 699990792Sgshapiro 700090792Sgshapiro while (*p != '\0' && 700190792Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 700290792Sgshapiro p++; 700390792Sgshapiro 700490792Sgshapiro /* p now points to field code */ 700590792Sgshapiro fcode = *p; 700690792Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 700790792Sgshapiro p++; 700890792Sgshapiro if (*p++ != '=') 700990792Sgshapiro { 701090792Sgshapiro syserr("queue %s: `=' expected", qg->qg_name); 701190792Sgshapiro return; 701290792Sgshapiro } 701390792Sgshapiro while (isascii(*p) && isspace(*p)) 701490792Sgshapiro p++; 701590792Sgshapiro 701690792Sgshapiro /* p now points to the field body */ 701790792Sgshapiro p = munchstring(p, &delimptr, ','); 701890792Sgshapiro 701990792Sgshapiro /* install the field into the queue struct */ 702090792Sgshapiro switch (fcode) 702190792Sgshapiro { 702290792Sgshapiro case 'P': /* pathname */ 702390792Sgshapiro if (*p == '\0') 702490792Sgshapiro syserr("queue %s: empty path name", 702590792Sgshapiro qg->qg_name); 702690792Sgshapiro else 702790792Sgshapiro qg->qg_qdir = newstr(p); 702890792Sgshapiro break; 702990792Sgshapiro 703090792Sgshapiro case 'F': /* flags */ 703190792Sgshapiro for (; *p != '\0'; p++) 703290792Sgshapiro if (!(isascii(*p) && isspace(*p))) 703390792Sgshapiro setbitn(*p, qg->qg_flags); 703490792Sgshapiro break; 703590792Sgshapiro 703690792Sgshapiro /* 703790792Sgshapiro ** Do we need two intervals here: 703890792Sgshapiro ** One for persistent queue runners, 703990792Sgshapiro ** one for "normal" queue runs? 704090792Sgshapiro */ 704190792Sgshapiro 704290792Sgshapiro case 'I': /* interval between running the queue */ 704390792Sgshapiro qg->qg_queueintvl = convtime(p, 'm'); 704490792Sgshapiro break; 704590792Sgshapiro 704690792Sgshapiro case 'N': /* run niceness */ 704790792Sgshapiro qg->qg_nice = atoi(p); 704890792Sgshapiro break; 704990792Sgshapiro 705090792Sgshapiro case 'R': /* maximum # of runners for the group */ 705190792Sgshapiro i = atoi(p); 705290792Sgshapiro 705390792Sgshapiro /* can't have more runners than allowed total */ 705490792Sgshapiro if (MaxQueueChildren > 0 && i > MaxQueueChildren) 705590792Sgshapiro { 705690792Sgshapiro qg->qg_maxqrun = MaxQueueChildren; 705790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 705890792Sgshapiro "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n", 705990792Sgshapiro qg->qg_name, i, 706090792Sgshapiro MaxQueueChildren); 706190792Sgshapiro } 706290792Sgshapiro else 706390792Sgshapiro qg->qg_maxqrun = i; 706490792Sgshapiro break; 706590792Sgshapiro 706690792Sgshapiro case 'J': /* maximum # of jobs in work list */ 706790792Sgshapiro qg->qg_maxlist = atoi(p); 706890792Sgshapiro break; 706990792Sgshapiro 707090792Sgshapiro case 'r': /* max recipients per envelope */ 707190792Sgshapiro qg->qg_maxrcpt = atoi(p); 707290792Sgshapiro break; 707390792Sgshapiro 707494334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 707590792Sgshapiro case 'S': /* queue sorting order */ 707690792Sgshapiro switch (*p) 707790792Sgshapiro { 707890792Sgshapiro case 'h': /* Host first */ 707990792Sgshapiro case 'H': 708090792Sgshapiro qg->qg_sortorder = QSO_BYHOST; 708190792Sgshapiro break; 708290792Sgshapiro 708390792Sgshapiro case 'p': /* Priority order */ 708490792Sgshapiro case 'P': 708590792Sgshapiro qg->qg_sortorder = QSO_BYPRIORITY; 708690792Sgshapiro break; 708790792Sgshapiro 708890792Sgshapiro case 't': /* Submission time */ 708990792Sgshapiro case 'T': 709090792Sgshapiro qg->qg_sortorder = QSO_BYTIME; 709190792Sgshapiro break; 709290792Sgshapiro 709390792Sgshapiro case 'f': /* File name */ 709490792Sgshapiro case 'F': 709590792Sgshapiro qg->qg_sortorder = QSO_BYFILENAME; 709690792Sgshapiro break; 709790792Sgshapiro 709890792Sgshapiro case 'm': /* Modification time */ 709990792Sgshapiro case 'M': 710094334Sgshapiro qg->qg_sortorder = QSO_BYMODTIME; 710190792Sgshapiro break; 710290792Sgshapiro 710394334Sgshapiro case 'r': /* Random */ 710494334Sgshapiro case 'R': 710594334Sgshapiro qg->qg_sortorder = QSO_RANDOM; 710694334Sgshapiro break; 710794334Sgshapiro 710894334Sgshapiro# if _FFR_RHS 710994334Sgshapiro case 's': /* Shuffled host name */ 711094334Sgshapiro case 'S': 711194334Sgshapiro qg->qg_sortorder = QSO_BYSHUFFLE; 711294334Sgshapiro break; 711394334Sgshapiro# endif /* _FFR_RHS */ 711494334Sgshapiro 711590792Sgshapiro default: 711690792Sgshapiro syserr("Invalid queue sort order \"%s\"", p); 711790792Sgshapiro } 711890792Sgshapiro break; 711994334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 712090792Sgshapiro 712190792Sgshapiro default: 712290792Sgshapiro syserr("Q%s: unknown queue equate %c=", 712390792Sgshapiro qg->qg_name, fcode); 712490792Sgshapiro break; 712590792Sgshapiro } 712690792Sgshapiro 712790792Sgshapiro p = delimptr; 712890792Sgshapiro } 712990792Sgshapiro 713090792Sgshapiro#if !HASNICE 713190792Sgshapiro if (qg->qg_nice != NiceQueueRun) 713290792Sgshapiro { 713390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 713490792Sgshapiro "Q%s: Warning: N= set on system that doesn't support nice()\n", 713590792Sgshapiro qg->qg_name); 713690792Sgshapiro } 713790792Sgshapiro#endif /* !HASNICE */ 713890792Sgshapiro 713990792Sgshapiro /* do some rationality checking */ 714090792Sgshapiro if (NumQueue >= MAXQUEUEGROUPS) 714190792Sgshapiro { 714290792Sgshapiro syserr("too many queue groups defined (%d max)", 714390792Sgshapiro MAXQUEUEGROUPS); 714490792Sgshapiro return; 714590792Sgshapiro } 714690792Sgshapiro 714790792Sgshapiro if (qg->qg_qdir == NULL) 714890792Sgshapiro { 714990792Sgshapiro if (QueueDir == NULL || *QueueDir == '\0') 715090792Sgshapiro { 715190792Sgshapiro syserr("QueueDir must be defined before queue groups"); 715290792Sgshapiro return; 715390792Sgshapiro } 715490792Sgshapiro qg->qg_qdir = newstr(QueueDir); 715590792Sgshapiro } 715690792Sgshapiro 715790792Sgshapiro if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags)) 715890792Sgshapiro { 715990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 716090792Sgshapiro "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n", 716190792Sgshapiro qg->qg_name, qg->qg_maxqrun, QD_FORK); 716290792Sgshapiro } 716390792Sgshapiro 716490792Sgshapiro /* enter the queue into the symbol table */ 716590792Sgshapiro if (tTd(37, 8)) 716690792Sgshapiro sm_syslog(LOG_INFO, NOQID, 716790792Sgshapiro "Adding %s to stab, path: %s", qg->qg_name, 716890792Sgshapiro qg->qg_qdir); 716990792Sgshapiro s = stab(qg->qg_name, ST_QUEUE, ST_ENTER); 717090792Sgshapiro if (s->s_quegrp != NULL) 717190792Sgshapiro { 717290792Sgshapiro i = s->s_quegrp->qg_index; 717390792Sgshapiro 717490792Sgshapiro /* XXX what about the pointers inside this struct? */ 717590792Sgshapiro sm_free(s->s_quegrp); /* XXX */ 717690792Sgshapiro } 717790792Sgshapiro else 717890792Sgshapiro i = NumQueue++; 717990792Sgshapiro Queue[i] = s->s_quegrp = qg; 718090792Sgshapiro qg->qg_index = i; 718190792Sgshapiro 718290792Sgshapiro /* set default value for max queue runners */ 718390792Sgshapiro if (qg->qg_maxqrun < 0) 718490792Sgshapiro { 718590792Sgshapiro if (MaxRunnersPerQueue > 0) 718690792Sgshapiro qg->qg_maxqrun = MaxRunnersPerQueue; 718790792Sgshapiro else 718890792Sgshapiro qg->qg_maxqrun = 1; 718990792Sgshapiro } 719090792Sgshapiro if (qdef) 719190792Sgshapiro setbitn(QD_DEFINED, qg->qg_flags); 719290792Sgshapiro} 719390792Sgshapiro#if 0 719490792Sgshapiro/* 719564562Sgshapiro** HASHFQN -- calculate a hash value for a fully qualified host name 719664562Sgshapiro** 719764562Sgshapiro** Arguments: 719864562Sgshapiro** fqn -- an all lower-case host.domain string 719964562Sgshapiro** buckets -- the number of buckets (queue directories) 720064562Sgshapiro** 720164562Sgshapiro** Returns: 720264562Sgshapiro** a bucket number (signed integer) 720364562Sgshapiro** -1 on error 720464562Sgshapiro** 720564562Sgshapiro** Contributed by Exactis.com, Inc. 720664562Sgshapiro*/ 720764562Sgshapiro 720864562Sgshapiroint 720964562Sgshapirohashfqn(fqn, buckets) 721064562Sgshapiro register char *fqn; 721164562Sgshapiro int buckets; 721264562Sgshapiro{ 721364562Sgshapiro register char *p; 721464562Sgshapiro register int h = 0, hash, cnt; 721564562Sgshapiro 721664562Sgshapiro if (fqn == NULL) 721764562Sgshapiro return -1; 721864562Sgshapiro 721964562Sgshapiro /* 722064562Sgshapiro ** A variation on the gdb hash 722164562Sgshapiro ** This is the best as of Feb 19, 1996 --bcx 722264562Sgshapiro */ 722364562Sgshapiro 722464562Sgshapiro p = fqn; 722564562Sgshapiro h = 0x238F13AF * strlen(p); 722664562Sgshapiro for (cnt = 0; *p != 0; ++p, cnt++) 722764562Sgshapiro { 722864562Sgshapiro h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF; 722964562Sgshapiro } 723064562Sgshapiro h = (1103515243 * h + 12345) & 0x7FFFFFFF; 723164562Sgshapiro if (buckets < 2) 723264562Sgshapiro hash = 0; 723364562Sgshapiro else 723464562Sgshapiro hash = (h % buckets); 723564562Sgshapiro 723664562Sgshapiro return hash; 723764562Sgshapiro} 723890792Sgshapiro#endif /* 0 */ 723964562Sgshapiro 724090792Sgshapiro#if _FFR_QUEUEDELAY 724190792Sgshapiro/* 724264562Sgshapiro** QUEUEDELAY -- compute queue delay time 724364562Sgshapiro** 724464562Sgshapiro** Parameters: 724564562Sgshapiro** e -- the envelope to queue up. 724664562Sgshapiro** 724764562Sgshapiro** Returns: 724864562Sgshapiro** queue delay time 724964562Sgshapiro** 725064562Sgshapiro** Side Effects: 725164562Sgshapiro** may change e_queuedelay 725264562Sgshapiro*/ 725364562Sgshapiro 725464562Sgshapirostatic time_t 725564562Sgshapiroqueuedelay(e) 725664562Sgshapiro ENVELOPE *e; 725764562Sgshapiro{ 725864562Sgshapiro time_t qd; 725964562Sgshapiro 726064562Sgshapiro if (e->e_queuealg == QD_EXP) 726164562Sgshapiro { 726264562Sgshapiro if (e->e_queuedelay == 0) 726364562Sgshapiro e->e_queuedelay = QueueInitDelay; 726464562Sgshapiro else 726564562Sgshapiro { 726664562Sgshapiro e->e_queuedelay *= 2; 726764562Sgshapiro if (e->e_queuedelay > QueueMaxDelay) 726864562Sgshapiro e->e_queuedelay = QueueMaxDelay; 726964562Sgshapiro } 727064562Sgshapiro qd = e->e_queuedelay; 727164562Sgshapiro } 727264562Sgshapiro else 727364562Sgshapiro qd = MinQueueAge; 727464562Sgshapiro return qd; 727564562Sgshapiro} 727690792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 727790792Sgshapiro 727890792Sgshapiro/* 727990792Sgshapiro** A structure for sorting Queue according to maxqrun without 728090792Sgshapiro** screwing up Queue itself. 728190792Sgshapiro*/ 728290792Sgshapiro 728390792Sgshapirostruct sortqgrp 728490792Sgshapiro{ 728590792Sgshapiro int sg_idx; /* original index */ 728690792Sgshapiro int sg_maxqrun; /* max queue runners */ 728790792Sgshapiro}; 728890792Sgshapirotypedef struct sortqgrp SORTQGRP_T; 728990792Sgshapirostatic int cmpidx __P((const void *, const void *)); 729090792Sgshapiro 729190792Sgshapirostatic int 729290792Sgshapirocmpidx(a, b) 729390792Sgshapiro const void *a; 729490792Sgshapiro const void *b; 729590792Sgshapiro{ 729690792Sgshapiro /* The sort is highest to lowest, so the comparison is reversed */ 729790792Sgshapiro if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun) 729890792Sgshapiro return 1; 729990792Sgshapiro else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun) 730090792Sgshapiro return -1; 730190792Sgshapiro else 730290792Sgshapiro return 0; 730390792Sgshapiro} 730490792Sgshapiro 730590792Sgshapiro/* 730690792Sgshapiro** MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren 730790792Sgshapiro** 730890792Sgshapiro** Take the now defined queue groups and assign them to work groups. 730990792Sgshapiro** This is done to balance out the number of concurrently active 731090792Sgshapiro** queue runners such that MaxQueueChildren is not exceeded. This may 731190792Sgshapiro** result in more than one queue group per work group. In such a case 731290792Sgshapiro** the number of running queue groups in that work group will have no 731390792Sgshapiro** more than the work group maximum number of runners (a "fair" portion 731490792Sgshapiro** of MaxQueueRunners). All queue groups within a work group will get a 731590792Sgshapiro** chance at running. 731690792Sgshapiro** 731790792Sgshapiro** Parameters: 731890792Sgshapiro** none. 731990792Sgshapiro** 732090792Sgshapiro** Returns: 732190792Sgshapiro** nothing. 732290792Sgshapiro** 732390792Sgshapiro** Side Effects: 732490792Sgshapiro** Sets up WorkGrp structure. 732590792Sgshapiro*/ 732690792Sgshapiro 732790792Sgshapirovoid 732890792Sgshapiromakeworkgroups() 732990792Sgshapiro{ 733090792Sgshapiro int i, j, total_runners = 0; 733190792Sgshapiro int dir; 733290792Sgshapiro SORTQGRP_T si[MAXQUEUEGROUPS + 1]; 733390792Sgshapiro 733490792Sgshapiro if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0) 733590792Sgshapiro { 733690792Sgshapiro /* 733790792Sgshapiro ** There is only the "mqueue" queue group (a default) 733890792Sgshapiro ** containing all of the queues. We want to provide to 733990792Sgshapiro ** this queue group the maximum allowable queue runners. 734090792Sgshapiro ** To match older behavior (8.10/8.11) we'll try for 734190792Sgshapiro ** 1 runner per queue capping it at MaxQueueChildren. 734290792Sgshapiro ** So if there are N queues, then there will be N runners 734390792Sgshapiro ** for the "mqueue" queue group (where N is kept less than 734490792Sgshapiro ** MaxQueueChildren). 734590792Sgshapiro */ 734690792Sgshapiro 734790792Sgshapiro NumWorkGroups = 1; 734890792Sgshapiro WorkGrp[0].wg_numqgrp = 1; 734990792Sgshapiro WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *)); 735090792Sgshapiro WorkGrp[0].wg_qgs[0] = Queue[0]; 735190792Sgshapiro if (MaxQueueChildren > 0 && 735290792Sgshapiro Queue[0]->qg_numqueues > MaxQueueChildren) 735390792Sgshapiro WorkGrp[0].wg_runners = MaxQueueChildren; 735490792Sgshapiro else 735590792Sgshapiro WorkGrp[0].wg_runners = Queue[0]->qg_numqueues; 735690792Sgshapiro 735790792Sgshapiro Queue[0]->qg_wgrp = 0; 735890792Sgshapiro 735990792Sgshapiro /* can't have more runners than allowed total */ 736090792Sgshapiro if (MaxQueueChildren > 0 && 736190792Sgshapiro Queue[0]->qg_maxqrun > MaxQueueChildren) 736290792Sgshapiro Queue[0]->qg_maxqrun = MaxQueueChildren; 736390792Sgshapiro WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun; 736490792Sgshapiro WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl; 736590792Sgshapiro return; 736690792Sgshapiro } 736790792Sgshapiro 736890792Sgshapiro for (i = 0; i < NumQueue; i++) 736990792Sgshapiro { 737090792Sgshapiro si[i].sg_maxqrun = Queue[i]->qg_maxqrun; 737190792Sgshapiro si[i].sg_idx = i; 737290792Sgshapiro } 737390792Sgshapiro qsort(si, NumQueue, sizeof(si[0]), cmpidx); 737490792Sgshapiro 737590792Sgshapiro NumWorkGroups = 0; 737690792Sgshapiro for (i = 0; i < NumQueue; i++) 737790792Sgshapiro { 737890792Sgshapiro total_runners += si[i].sg_maxqrun; 737990792Sgshapiro if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren) 738090792Sgshapiro NumWorkGroups++; 738190792Sgshapiro else 738290792Sgshapiro break; 738390792Sgshapiro } 738490792Sgshapiro 738590792Sgshapiro if (NumWorkGroups < 1) 738690792Sgshapiro NumWorkGroups = 1; /* gotta have one at least */ 738790792Sgshapiro else if (NumWorkGroups > MAXWORKGROUPS) 738890792Sgshapiro NumWorkGroups = MAXWORKGROUPS; /* the limit */ 738990792Sgshapiro 739090792Sgshapiro /* 739190792Sgshapiro ** We now know the number of work groups to pack the queue groups 739290792Sgshapiro ** into. The queue groups in 'Queue' are sorted from highest 739390792Sgshapiro ** to lowest for the number of runners per queue group. 739490792Sgshapiro ** We put the queue groups with the largest number of runners 739590792Sgshapiro ** into work groups first. Then the smaller ones are fitted in 739690792Sgshapiro ** where it looks best. 739790792Sgshapiro */ 739890792Sgshapiro 739990792Sgshapiro j = 0; 740090792Sgshapiro dir = 1; 740190792Sgshapiro for (i = 0; i < NumQueue; i++) 740290792Sgshapiro { 740390792Sgshapiro /* a to-and-fro packing scheme, continue from last position */ 740490792Sgshapiro if (j >= NumWorkGroups) 740590792Sgshapiro { 740690792Sgshapiro dir = -1; 740790792Sgshapiro j = NumWorkGroups - 1; 740890792Sgshapiro } 740990792Sgshapiro else if (j < 0) 741090792Sgshapiro { 741190792Sgshapiro j = 0; 741290792Sgshapiro dir = 1; 741390792Sgshapiro } 741490792Sgshapiro 741590792Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 741694334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) * 741794334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 741894334Sgshapiro else 741994334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs, 742094334Sgshapiro sizeof(QUEUEGRP *) * 742194334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 742294334Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 742390792Sgshapiro { 742494334Sgshapiro syserr("!cannot allocate memory for work queues, need %d bytes", 742590792Sgshapiro (int) (sizeof(QUEUEGRP *) * 742690792Sgshapiro (WorkGrp[j].wg_numqgrp + 1))); 742790792Sgshapiro } 742890792Sgshapiro 742990792Sgshapiro WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[si[i].sg_idx]; 743090792Sgshapiro WorkGrp[j].wg_numqgrp++; 743190792Sgshapiro WorkGrp[j].wg_runners += Queue[i]->qg_maxqrun; 743290792Sgshapiro Queue[si[i].sg_idx]->qg_wgrp = j; 743390792Sgshapiro 743490792Sgshapiro if (WorkGrp[j].wg_maxact == 0) 743590792Sgshapiro { 743690792Sgshapiro /* can't have more runners than allowed total */ 743790792Sgshapiro if (MaxQueueChildren > 0 && 743890792Sgshapiro Queue[i]->qg_maxqrun > MaxQueueChildren) 743990792Sgshapiro Queue[i]->qg_maxqrun = MaxQueueChildren; 744090792Sgshapiro WorkGrp[j].wg_maxact = Queue[i]->qg_maxqrun; 744190792Sgshapiro } 744290792Sgshapiro 744390792Sgshapiro /* 744490792Sgshapiro ** XXX: must wg_lowqintvl be the GCD? 744590792Sgshapiro ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for 744690792Sgshapiro ** qg2 occur? 744790792Sgshapiro */ 744890792Sgshapiro 744990792Sgshapiro /* keep track of the lowest interval for a persistent runner */ 745090792Sgshapiro if (Queue[si[i].sg_idx]->qg_queueintvl > 0 && 745190792Sgshapiro WorkGrp[j].wg_lowqintvl < Queue[si[i].sg_idx]->qg_queueintvl) 745290792Sgshapiro WorkGrp[j].wg_lowqintvl = Queue[si[i].sg_idx]->qg_queueintvl; 745390792Sgshapiro j += dir; 745490792Sgshapiro } 745590792Sgshapiro if (tTd(41, 9)) 745690792Sgshapiro { 745790792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 745890792Sgshapiro { 745990792Sgshapiro sm_dprintf("Workgroup[%d]=", i); 746090792Sgshapiro for (j = 0; j < WorkGrp[i].wg_numqgrp; j++) 746190792Sgshapiro { 746290792Sgshapiro sm_dprintf("%s, ", 746390792Sgshapiro WorkGrp[i].wg_qgs[j]->qg_name); 746490792Sgshapiro } 746590792Sgshapiro sm_dprintf("\n"); 746690792Sgshapiro } 746790792Sgshapiro } 746890792Sgshapiro} 746990792Sgshapiro 747090792Sgshapiro/* 747190792Sgshapiro** DUP_DF -- duplicate envelope data file 747290792Sgshapiro** 747390792Sgshapiro** Copy the data file from the 'old' envelope to the 'new' envelope 747490792Sgshapiro** in the most efficient way possible. 747590792Sgshapiro** 747690792Sgshapiro** Create a hard link from the 'old' data file to the 'new' data file. 747790792Sgshapiro** If the old and new queue directories are on different file systems, 747890792Sgshapiro** then the new data file link is created in the old queue directory, 747990792Sgshapiro** and the new queue file will contain a 'd' record pointing to the 748090792Sgshapiro** directory containing the new data file. 748190792Sgshapiro** 748290792Sgshapiro** Parameters: 748390792Sgshapiro** old -- old envelope. 748490792Sgshapiro** new -- new envelope. 748590792Sgshapiro** 748690792Sgshapiro** Results: 748790792Sgshapiro** Returns true on success, false on failure. 748890792Sgshapiro** 748990792Sgshapiro** Side Effects: 749090792Sgshapiro** On success, the new data file is created. 749190792Sgshapiro** On fatal failure, EF_FATALERRS is set in old->e_flags. 749290792Sgshapiro*/ 749390792Sgshapiro 749490792Sgshapirostatic bool dup_df __P((ENVELOPE *, ENVELOPE *)); 749590792Sgshapiro 749690792Sgshapirostatic bool 749790792Sgshapirodup_df(old, new) 749890792Sgshapiro ENVELOPE *old; 749990792Sgshapiro ENVELOPE *new; 750090792Sgshapiro{ 750190792Sgshapiro int ofs, nfs, r; 750290792Sgshapiro char opath[MAXPATHLEN]; 750390792Sgshapiro char npath[MAXPATHLEN]; 750490792Sgshapiro 750594334Sgshapiro if (!bitset(EF_HAS_DF, old->e_flags)) 750694334Sgshapiro { 750794334Sgshapiro /* 750894334Sgshapiro ** this can happen if: SuperSafe != True 750994334Sgshapiro ** and a bounce mail is sent that is split. 751094334Sgshapiro */ 751194334Sgshapiro 751294334Sgshapiro queueup(old, false, true); 751394334Sgshapiro } 751490792Sgshapiro SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir)); 751590792Sgshapiro SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir)); 751690792Sgshapiro 751790792Sgshapiro (void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof opath); 751890792Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath); 751990792Sgshapiro 752090792Sgshapiro if (old->e_dfp != NULL) 752190792Sgshapiro { 752290792Sgshapiro r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL); 752390792Sgshapiro if (r < 0 && errno != EINVAL) 752490792Sgshapiro { 752590792Sgshapiro syserr("@can't commit %s", opath); 752690792Sgshapiro old->e_flags |= EF_FATALERRS; 752790792Sgshapiro return false; 752890792Sgshapiro } 752990792Sgshapiro } 753090792Sgshapiro 753190792Sgshapiro /* 753290792Sgshapiro ** Attempt to create a hard link, if we think both old and new 753390792Sgshapiro ** are on the same file system, otherwise copy the file. 753490792Sgshapiro ** 753590792Sgshapiro ** Don't waste time attempting a hard link unless old and new 753690792Sgshapiro ** are on the same file system. 753790792Sgshapiro */ 753890792Sgshapiro 753990792Sgshapiro ofs = Queue[old->e_qgrp]->qg_qpaths[old->e_qdir].qp_fsysidx; 754090792Sgshapiro nfs = Queue[new->e_qgrp]->qg_qpaths[new->e_qdir].qp_fsysidx; 754190792Sgshapiro if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs)) 754290792Sgshapiro { 754390792Sgshapiro if (link(opath, npath) == 0) 754490792Sgshapiro { 754590792Sgshapiro new->e_flags |= EF_HAS_DF; 754690792Sgshapiro SYNC_DIR(npath, true); 754790792Sgshapiro return true; 754890792Sgshapiro } 754990792Sgshapiro goto error; 755090792Sgshapiro } 755190792Sgshapiro 755290792Sgshapiro /* 755390792Sgshapiro ** Can't link across queue directories, so try to create a hard 755490792Sgshapiro ** link in the same queue directory as the old df file. 755590792Sgshapiro ** The qf file will refer to the new df file using a 'd' record. 755690792Sgshapiro */ 755790792Sgshapiro 755890792Sgshapiro new->e_dfqgrp = old->e_dfqgrp; 755990792Sgshapiro new->e_dfqdir = old->e_dfqdir; 756090792Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath); 756190792Sgshapiro if (link(opath, npath) == 0) 756290792Sgshapiro { 756390792Sgshapiro new->e_flags |= EF_HAS_DF; 756490792Sgshapiro SYNC_DIR(npath, true); 756590792Sgshapiro return true; 756690792Sgshapiro } 756790792Sgshapiro 756890792Sgshapiro error: 756990792Sgshapiro if (LogLevel > 0) 757090792Sgshapiro sm_syslog(LOG_ERR, old->e_id, 757190792Sgshapiro "dup_df: can't link %s to %s, error=%s, envelope splitting failed", 757290792Sgshapiro opath, npath, sm_errstring(errno)); 757390792Sgshapiro return false; 757490792Sgshapiro} 757590792Sgshapiro 757690792Sgshapiro/* 757790792Sgshapiro** SPLIT_ENV -- Allocate a new envelope based on a given envelope. 757890792Sgshapiro** 757990792Sgshapiro** Parameters: 758090792Sgshapiro** e -- envelope. 758190792Sgshapiro** sendqueue -- sendqueue for new envelope. 758290792Sgshapiro** qgrp -- index of queue group. 758390792Sgshapiro** qdir -- queue directory. 758490792Sgshapiro** 758590792Sgshapiro** Results: 758690792Sgshapiro** new envelope. 758790792Sgshapiro** 758890792Sgshapiro*/ 758990792Sgshapiro 759090792Sgshapirostatic ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int)); 759190792Sgshapiro 759290792Sgshapirostatic ENVELOPE * 759390792Sgshapirosplit_env(e, sendqueue, qgrp, qdir) 759490792Sgshapiro ENVELOPE *e; 759590792Sgshapiro ADDRESS *sendqueue; 759690792Sgshapiro int qgrp; 759790792Sgshapiro int qdir; 759890792Sgshapiro{ 759990792Sgshapiro ENVELOPE *ee; 760090792Sgshapiro 760190792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof *ee); 760290792Sgshapiro STRUCTCOPY(*e, *ee); 760390792Sgshapiro ee->e_message = NULL; /* XXX use original message? */ 760490792Sgshapiro ee->e_id = NULL; 760590792Sgshapiro assign_queueid(ee); 760690792Sgshapiro ee->e_sendqueue = sendqueue; 760790792Sgshapiro ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS 760890792Sgshapiro |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF); 760990792Sgshapiro ee->e_flags |= EF_NORECEIPT; /* XXX really? */ 761090792Sgshapiro ee->e_from.q_state = QS_SENDER; 761190792Sgshapiro ee->e_dfp = NULL; 761290792Sgshapiro ee->e_lockfp = NULL; 761390792Sgshapiro if (e->e_xfp != NULL) 761490792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 761594334Sgshapiro 761694334Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 761794334Sgshapiro if (ee->e_xfp == NULL) 761894334Sgshapiro openxscript(ee); 761994334Sgshapiro 762090792Sgshapiro ee->e_qgrp = ee->e_dfqgrp = qgrp; 762190792Sgshapiro ee->e_qdir = ee->e_dfqdir = qdir; 762290792Sgshapiro ee->e_errormode = EM_MAIL; 762390792Sgshapiro ee->e_statmsg = NULL; 762490792Sgshapiro#if _FFR_QUARANTINE 762590792Sgshapiro if (e->e_quarmsg != NULL) 762690792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 762790792Sgshapiro e->e_quarmsg); 762890792Sgshapiro#endif /* _FFR_QUARANTINE */ 762990792Sgshapiro 763090792Sgshapiro /* 763190792Sgshapiro ** XXX Not sure if this copying is necessary. 763294334Sgshapiro ** sendall() does this copying, but I (dm) don't know if that is 763390792Sgshapiro ** because of the storage management discipline we were using 763490792Sgshapiro ** before rpools were introduced, or if it is because these lists 763590792Sgshapiro ** can be modified later. 763690792Sgshapiro */ 763790792Sgshapiro 763890792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 763990792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool); 764090792Sgshapiro 764190792Sgshapiro return ee; 764290792Sgshapiro} 764390792Sgshapiro 764490792Sgshapiro/* return values from split functions, check also below! */ 764590792Sgshapiro#define SM_SPLIT_FAIL (0) 764690792Sgshapiro#define SM_SPLIT_NONE (1) 764790792Sgshapiro#define SM_SPLIT_NEW(n) (1 + (n)) 764890792Sgshapiro 764990792Sgshapiro/* 765090792Sgshapiro** SPLIT_ACROSS_QUEUE_GROUPS 765190792Sgshapiro** 765290792Sgshapiro** This function splits an envelope across multiple queue groups 765390792Sgshapiro** based on the queue group of each recipient. 765490792Sgshapiro** 765590792Sgshapiro** Parameters: 765690792Sgshapiro** e -- envelope. 765790792Sgshapiro** 765890792Sgshapiro** Results: 765990792Sgshapiro** SM_SPLIT_FAIL on failure 766090792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 766190792Sgshapiro** or 1 + the number of additional envelopes created. 766290792Sgshapiro** 766390792Sgshapiro** Side Effects: 766490792Sgshapiro** On success, e->e_sibling points to a list of zero or more 766590792Sgshapiro** additional envelopes, and the associated data files exist 766690792Sgshapiro** on disk. But the queue files are not created. 766790792Sgshapiro** 766890792Sgshapiro** On failure, e->e_sibling is not changed. 766990792Sgshapiro** The order of recipients in e->e_sendqueue is permuted. 767090792Sgshapiro** Abandoned data files for additional envelopes that failed 767190792Sgshapiro** to be created may exist on disk. 767290792Sgshapiro*/ 767390792Sgshapiro 767490792Sgshapirostatic int q_qgrp_compare __P((const void *, const void *)); 767590792Sgshapirostatic int e_filesys_compare __P((const void *, const void *)); 767690792Sgshapiro 767790792Sgshapirostatic int 767890792Sgshapiroq_qgrp_compare(p1, p2) 767990792Sgshapiro const void *p1; 768090792Sgshapiro const void *p2; 768190792Sgshapiro{ 768290792Sgshapiro ADDRESS **pq1 = (ADDRESS **) p1; 768390792Sgshapiro ADDRESS **pq2 = (ADDRESS **) p2; 768490792Sgshapiro 768590792Sgshapiro return (*pq1)->q_qgrp - (*pq2)->q_qgrp; 768690792Sgshapiro} 768790792Sgshapiro 768890792Sgshapirostatic int 768990792Sgshapiroe_filesys_compare(p1, p2) 769090792Sgshapiro const void *p1; 769190792Sgshapiro const void *p2; 769290792Sgshapiro{ 769390792Sgshapiro ENVELOPE **pe1 = (ENVELOPE **) p1; 769490792Sgshapiro ENVELOPE **pe2 = (ENVELOPE **) p2; 769590792Sgshapiro int fs1, fs2; 769690792Sgshapiro 769790792Sgshapiro fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx; 769890792Sgshapiro fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx; 769990792Sgshapiro if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2)) 770090792Sgshapiro return -1; 770190792Sgshapiro if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2)) 770290792Sgshapiro return 1; 770390792Sgshapiro return 0; 770490792Sgshapiro} 770590792Sgshapiro 770690792Sgshapirostatic int 770790792Sgshapirosplit_across_queue_groups(e) 770890792Sgshapiro ENVELOPE *e; 770990792Sgshapiro{ 771090792Sgshapiro int naddrs, nsplits, i; 7711102528Sgshapiro bool changed; 771290792Sgshapiro char **pvp; 771390792Sgshapiro ADDRESS *q, **addrs; 771490792Sgshapiro ENVELOPE *ee, *es; 771590792Sgshapiro ENVELOPE *splits[MAXQUEUEGROUPS]; 771690792Sgshapiro char pvpbuf[PSBUFSIZE]; 771790792Sgshapiro 771890792Sgshapiro SM_REQUIRE(ISVALIDQGRP(e->e_qgrp)); 771990792Sgshapiro 772090792Sgshapiro /* Count addresses and assign queue groups. */ 772190792Sgshapiro naddrs = 0; 7722102528Sgshapiro changed = false; 772390792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 772490792Sgshapiro { 772590792Sgshapiro if (QS_IS_DEAD(q->q_state)) 772690792Sgshapiro continue; 772790792Sgshapiro ++naddrs; 772890792Sgshapiro 772990792Sgshapiro /* bad addresses and those already sent stay put */ 773090792Sgshapiro if (QS_IS_BADADDR(q->q_state) || 773190792Sgshapiro QS_IS_SENT(q->q_state)) 773290792Sgshapiro q->q_qgrp = e->e_qgrp; 773390792Sgshapiro else if (!ISVALIDQGRP(q->q_qgrp)) 773490792Sgshapiro { 773590792Sgshapiro /* call ruleset which should return a queue group */ 773690792Sgshapiro i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp, 773790792Sgshapiro pvpbuf, sizeof(pvpbuf)); 773890792Sgshapiro if (i == EX_OK && 773990792Sgshapiro pvp != NULL && pvp[0] != NULL && 774090792Sgshapiro (pvp[0][0] & 0377) == CANONNET && 774190792Sgshapiro pvp[1] != NULL && pvp[1][0] != '\0') 774290792Sgshapiro { 774390792Sgshapiro i = name2qid(pvp[1]); 774490792Sgshapiro if (ISVALIDQGRP(i)) 774590792Sgshapiro { 774690792Sgshapiro q->q_qgrp = i; 7747102528Sgshapiro changed = true; 774890792Sgshapiro if (tTd(20, 4)) 774990792Sgshapiro sm_syslog(LOG_INFO, NOQID, 775090792Sgshapiro "queue group name %s -> %d", 775190792Sgshapiro pvp[1], i); 775290792Sgshapiro continue; 775390792Sgshapiro } 775490792Sgshapiro else if (LogLevel > 10) 775590792Sgshapiro sm_syslog(LOG_INFO, NOQID, 775690792Sgshapiro "can't find queue group name %s, selection ignored", 775790792Sgshapiro pvp[1]); 775890792Sgshapiro } 775990792Sgshapiro if (q->q_mailer != NULL && 776090792Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 7761102528Sgshapiro { 7762102528Sgshapiro changed = true; 776390792Sgshapiro q->q_qgrp = q->q_mailer->m_qgrp; 7764102528Sgshapiro } 776594334Sgshapiro else if (ISVALIDQGRP(e->e_qgrp)) 776694334Sgshapiro q->q_qgrp = e->e_qgrp; 776790792Sgshapiro else 776890792Sgshapiro q->q_qgrp = 0; 776990792Sgshapiro } 777090792Sgshapiro } 777190792Sgshapiro 777290792Sgshapiro /* only one address? nothing to split. */ 7773102528Sgshapiro if (naddrs <= 1 && !changed) 777490792Sgshapiro return SM_SPLIT_NONE; 777590792Sgshapiro 777690792Sgshapiro /* sort the addresses by queue group */ 777790792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *)); 777890792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 777990792Sgshapiro { 778090792Sgshapiro if (QS_IS_DEAD(q->q_state)) 778190792Sgshapiro continue; 778290792Sgshapiro addrs[i++] = q; 778390792Sgshapiro } 778490792Sgshapiro qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare); 778590792Sgshapiro 778690792Sgshapiro /* split into multiple envelopes, by queue group */ 778790792Sgshapiro nsplits = 0; 778890792Sgshapiro es = NULL; 778990792Sgshapiro e->e_sendqueue = NULL; 779090792Sgshapiro for (i = 0; i < naddrs; ++i) 779190792Sgshapiro { 779290792Sgshapiro if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp) 779390792Sgshapiro addrs[i]->q_next = NULL; 779490792Sgshapiro else 779590792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 779690792Sgshapiro 779790792Sgshapiro /* same queue group as original envelope? */ 779890792Sgshapiro if (addrs[i]->q_qgrp == e->e_qgrp) 779990792Sgshapiro { 780090792Sgshapiro if (e->e_sendqueue == NULL) 780190792Sgshapiro e->e_sendqueue = addrs[i]; 780290792Sgshapiro continue; 780390792Sgshapiro } 780490792Sgshapiro 780590792Sgshapiro /* different queue group than original envelope */ 780690792Sgshapiro if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp) 780790792Sgshapiro { 780890792Sgshapiro ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR); 780990792Sgshapiro es = ee; 781090792Sgshapiro splits[nsplits++] = ee; 781190792Sgshapiro } 781290792Sgshapiro } 781390792Sgshapiro 781490792Sgshapiro /* no splits? return right now. */ 781590792Sgshapiro if (nsplits <= 0) 781690792Sgshapiro return SM_SPLIT_NONE; 781790792Sgshapiro 781890792Sgshapiro /* assign a queue directory to each additional envelope */ 781990792Sgshapiro for (i = 0; i < nsplits; ++i) 782090792Sgshapiro { 782190792Sgshapiro es = splits[i]; 782290792Sgshapiro#if 0 782390792Sgshapiro es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es); 782490792Sgshapiro#endif /* 0 */ 782590792Sgshapiro if (!setnewqueue(es)) 782690792Sgshapiro goto failure; 782790792Sgshapiro } 782890792Sgshapiro 782990792Sgshapiro /* sort the additional envelopes by queue file system */ 783090792Sgshapiro qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare); 783190792Sgshapiro 783290792Sgshapiro /* create data files for each additional envelope */ 783390792Sgshapiro if (!dup_df(e, splits[0])) 783490792Sgshapiro { 783590792Sgshapiro i = 0; 783690792Sgshapiro goto failure; 783790792Sgshapiro } 783890792Sgshapiro for (i = 1; i < nsplits; ++i) 783990792Sgshapiro { 784090792Sgshapiro /* copy or link to the previous data file */ 784190792Sgshapiro if (!dup_df(splits[i - 1], splits[i])) 784290792Sgshapiro goto failure; 784390792Sgshapiro } 784490792Sgshapiro 784590792Sgshapiro /* success: prepend the new envelopes to the e->e_sibling list */ 784690792Sgshapiro for (i = 0; i < nsplits; ++i) 784790792Sgshapiro { 784890792Sgshapiro es = splits[i]; 784990792Sgshapiro es->e_sibling = e->e_sibling; 785090792Sgshapiro e->e_sibling = es; 785190792Sgshapiro } 785290792Sgshapiro return SM_SPLIT_NEW(nsplits); 785390792Sgshapiro 785490792Sgshapiro /* failure: clean up */ 785590792Sgshapiro failure: 785690792Sgshapiro if (i > 0) 785790792Sgshapiro { 785890792Sgshapiro int j; 785990792Sgshapiro 786090792Sgshapiro for (j = 0; j < i; j++) 786190792Sgshapiro (void) unlink(queuename(splits[j], DATAFL_LETTER)); 786290792Sgshapiro } 786390792Sgshapiro e->e_sendqueue = addrs[0]; 786490792Sgshapiro for (i = 0; i < naddrs - 1; ++i) 786590792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 786690792Sgshapiro addrs[naddrs - 1]->q_next = NULL; 786790792Sgshapiro return SM_SPLIT_FAIL; 786890792Sgshapiro} 786990792Sgshapiro 787090792Sgshapiro/* 787190792Sgshapiro** SPLIT_WITHIN_QUEUE 787290792Sgshapiro** 787390792Sgshapiro** Split an envelope with multiple recipients into several 787490792Sgshapiro** envelopes within the same queue directory, if the number of 787590792Sgshapiro** recipients exceeds the limit for the queue group. 787690792Sgshapiro** 787790792Sgshapiro** Parameters: 787890792Sgshapiro** e -- envelope. 787990792Sgshapiro** 788090792Sgshapiro** Results: 788190792Sgshapiro** SM_SPLIT_FAIL on failure 788290792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 788390792Sgshapiro** or 1 + the number of additional envelopes created. 788490792Sgshapiro*/ 788590792Sgshapiro 788690792Sgshapiro#define SPLIT_LOG_LEVEL 8 788790792Sgshapiro 788890792Sgshapirostatic int split_within_queue __P((ENVELOPE *)); 788990792Sgshapiro 789090792Sgshapirostatic int 789190792Sgshapirosplit_within_queue(e) 789290792Sgshapiro ENVELOPE *e; 789390792Sgshapiro{ 789490792Sgshapiro int maxrcpt, nrcpt, ndead, nsplit, i; 789590792Sgshapiro int j, l; 789690792Sgshapiro char *lsplits; 789790792Sgshapiro ADDRESS *q, **addrs; 789890792Sgshapiro ENVELOPE *ee, *firstsibling; 789990792Sgshapiro 790090792Sgshapiro if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags)) 790190792Sgshapiro return SM_SPLIT_NONE; 790290792Sgshapiro 790390792Sgshapiro /* don't bother if there is no recipient limit */ 790490792Sgshapiro maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt; 790590792Sgshapiro if (maxrcpt <= 0) 790690792Sgshapiro return SM_SPLIT_NONE; 790790792Sgshapiro 790890792Sgshapiro /* count recipients */ 790990792Sgshapiro nrcpt = 0; 791090792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 791190792Sgshapiro { 791290792Sgshapiro if (QS_IS_DEAD(q->q_state)) 791390792Sgshapiro continue; 791490792Sgshapiro ++nrcpt; 791590792Sgshapiro } 791690792Sgshapiro if (nrcpt <= maxrcpt) 791790792Sgshapiro return SM_SPLIT_NONE; 791890792Sgshapiro 791990792Sgshapiro /* 792090792Sgshapiro ** Preserve the recipient list 792190792Sgshapiro ** so that we can restore it in case of error. 792290792Sgshapiro ** (But we discard dead addresses.) 792390792Sgshapiro */ 792490792Sgshapiro 792590792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *)); 792690792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 792790792Sgshapiro { 792890792Sgshapiro if (QS_IS_DEAD(q->q_state)) 792990792Sgshapiro continue; 793090792Sgshapiro addrs[i++] = q; 793190792Sgshapiro } 793290792Sgshapiro 793390792Sgshapiro /* 793490792Sgshapiro ** Partition the recipient list so that bad and sent addresses 793590792Sgshapiro ** come first. These will go with the original envelope, and 793690792Sgshapiro ** do not count towards the maxrcpt limit. 793790792Sgshapiro ** addrs[] does not contain QS_IS_DEAD() addresses. 793890792Sgshapiro */ 793990792Sgshapiro 794090792Sgshapiro ndead = 0; 794190792Sgshapiro for (i = 0; i < nrcpt; ++i) 794290792Sgshapiro { 794390792Sgshapiro if (QS_IS_BADADDR(addrs[i]->q_state) || 794490792Sgshapiro QS_IS_SENT(addrs[i]->q_state) || 794590792Sgshapiro QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */ 794690792Sgshapiro { 794790792Sgshapiro if (i > ndead) 794890792Sgshapiro { 794990792Sgshapiro ADDRESS *tmp = addrs[i]; 795090792Sgshapiro 795190792Sgshapiro addrs[i] = addrs[ndead]; 795290792Sgshapiro addrs[ndead] = tmp; 795390792Sgshapiro } 795490792Sgshapiro ++ndead; 795590792Sgshapiro } 795690792Sgshapiro } 795790792Sgshapiro 795890792Sgshapiro /* Check if no splitting required. */ 795990792Sgshapiro if (nrcpt - ndead <= maxrcpt) 796090792Sgshapiro return SM_SPLIT_NONE; 796190792Sgshapiro 796290792Sgshapiro /* fix links */ 796390792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 796490792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 796590792Sgshapiro addrs[nrcpt - 1]->q_next = NULL; 796690792Sgshapiro e->e_sendqueue = addrs[0]; 796790792Sgshapiro 796890792Sgshapiro /* prepare buffer for logging */ 796990792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL) 797090792Sgshapiro { 797190792Sgshapiro l = MAXLINE; 797290792Sgshapiro lsplits = sm_malloc(l); 797390792Sgshapiro if (lsplits != NULL) 797490792Sgshapiro *lsplits = '\0'; 797590792Sgshapiro j = 0; 797690792Sgshapiro } 797790792Sgshapiro else 797890792Sgshapiro { 797990792Sgshapiro /* get rid of stupid compiler warnings */ 798090792Sgshapiro lsplits = NULL; 798190792Sgshapiro j = l = 0; 798290792Sgshapiro } 798390792Sgshapiro 798490792Sgshapiro /* split the envelope */ 798590792Sgshapiro firstsibling = e->e_sibling; 798690792Sgshapiro i = maxrcpt + ndead; 798790792Sgshapiro nsplit = 0; 798890792Sgshapiro for (;;) 798990792Sgshapiro { 799090792Sgshapiro addrs[i - 1]->q_next = NULL; 799190792Sgshapiro ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir); 799290792Sgshapiro if (!dup_df(e, ee)) 799390792Sgshapiro { 799490792Sgshapiro 799590792Sgshapiro ee = firstsibling; 799690792Sgshapiro while (ee != NULL) 799790792Sgshapiro { 799890792Sgshapiro (void) unlink(queuename(ee, DATAFL_LETTER)); 799990792Sgshapiro ee = ee->e_sibling; 800090792Sgshapiro } 800190792Sgshapiro 800290792Sgshapiro /* Error. Restore e's sibling & recipient lists. */ 800390792Sgshapiro e->e_sibling = firstsibling; 800490792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 800590792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 8006110560Sgshapiro if (lsplits != NULL) 8007110560Sgshapiro sm_free(lsplits); 800890792Sgshapiro return SM_SPLIT_FAIL; 800990792Sgshapiro } 801090792Sgshapiro 801190792Sgshapiro /* prepend the new envelope to e->e_sibling */ 801290792Sgshapiro ee->e_sibling = e->e_sibling; 801390792Sgshapiro e->e_sibling = ee; 801490792Sgshapiro ++nsplit; 801590792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 801690792Sgshapiro { 801790792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 801890792Sgshapiro { 801990792Sgshapiro char *p; 802090792Sgshapiro 802190792Sgshapiro l += MAXLINE; 802290792Sgshapiro p = sm_realloc(lsplits, l); 802390792Sgshapiro if (p == NULL) 802490792Sgshapiro { 802590792Sgshapiro /* let's try to get this done */ 802690792Sgshapiro sm_free(lsplits); 802790792Sgshapiro lsplits = NULL; 802890792Sgshapiro } 802990792Sgshapiro else 803090792Sgshapiro lsplits = p; 803190792Sgshapiro } 803290792Sgshapiro if (lsplits != NULL) 803390792Sgshapiro { 803490792Sgshapiro if (j == 0) 803590792Sgshapiro j += sm_strlcat(lsplits + j, 803690792Sgshapiro ee->e_id, 803790792Sgshapiro l - j); 803890792Sgshapiro else 803990792Sgshapiro j += sm_strlcat2(lsplits + j, 804090792Sgshapiro "; ", 804190792Sgshapiro ee->e_id, 804290792Sgshapiro l - j); 804390792Sgshapiro SM_ASSERT(j < l); 804490792Sgshapiro } 804590792Sgshapiro } 804690792Sgshapiro if (nrcpt - i <= maxrcpt) 804790792Sgshapiro break; 804890792Sgshapiro i += maxrcpt; 804990792Sgshapiro } 8050110560Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 805190792Sgshapiro { 8052110560Sgshapiro if (nsplit > 0) 8053110560Sgshapiro { 8054110560Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, 8055110560Sgshapiro "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s", 8056110560Sgshapiro maxrcpt, nrcpt - ndead, nsplit, 8057110560Sgshapiro nsplit > 1 ? "s" : "", lsplits); 8058110560Sgshapiro } 805990792Sgshapiro sm_free(lsplits); 806090792Sgshapiro } 806190792Sgshapiro return SM_SPLIT_NEW(nsplit); 806290792Sgshapiro} 806390792Sgshapiro/* 806490792Sgshapiro** SPLIT_BY_RECIPIENT 806590792Sgshapiro** 806690792Sgshapiro** Split an envelope with multiple recipients into multiple 806790792Sgshapiro** envelopes as required by the sendmail configuration. 806890792Sgshapiro** 806990792Sgshapiro** Parameters: 807090792Sgshapiro** e -- envelope. 807190792Sgshapiro** 807290792Sgshapiro** Results: 807390792Sgshapiro** Returns true on success, false on failure. 807490792Sgshapiro** 807590792Sgshapiro** Side Effects: 807690792Sgshapiro** see split_across_queue_groups(), split_within_queue(e) 807790792Sgshapiro*/ 807890792Sgshapiro 807990792Sgshapirobool 808090792Sgshapirosplit_by_recipient(e) 808190792Sgshapiro ENVELOPE *e; 808290792Sgshapiro{ 808390792Sgshapiro int split, n, i, j, l; 808490792Sgshapiro char *lsplits; 808590792Sgshapiro ENVELOPE *ee, *next, *firstsibling; 808690792Sgshapiro 808790792Sgshapiro if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) || 808890792Sgshapiro bitset(EF_SPLIT, e->e_flags)) 808990792Sgshapiro return true; 809090792Sgshapiro n = split_across_queue_groups(e); 809190792Sgshapiro if (n == SM_SPLIT_FAIL) 809290792Sgshapiro return false; 809390792Sgshapiro firstsibling = ee = e->e_sibling; 809490792Sgshapiro if (n > 1 && LogLevel > SPLIT_LOG_LEVEL) 809590792Sgshapiro { 809690792Sgshapiro l = MAXLINE; 809790792Sgshapiro lsplits = sm_malloc(l); 809890792Sgshapiro if (lsplits != NULL) 809990792Sgshapiro *lsplits = '\0'; 810090792Sgshapiro j = 0; 810190792Sgshapiro } 810290792Sgshapiro else 810390792Sgshapiro { 810490792Sgshapiro /* get rid of stupid compiler warnings */ 810590792Sgshapiro lsplits = NULL; 810690792Sgshapiro j = l = 0; 810790792Sgshapiro } 810890792Sgshapiro for (i = 1; i < n; ++i) 810990792Sgshapiro { 811090792Sgshapiro next = ee->e_sibling; 811190792Sgshapiro if (split_within_queue(ee) == SM_SPLIT_FAIL) 811290792Sgshapiro { 811390792Sgshapiro e->e_sibling = firstsibling; 811490792Sgshapiro return false; 811590792Sgshapiro } 811690792Sgshapiro ee->e_flags |= EF_SPLIT; 811790792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 811890792Sgshapiro { 811990792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 812090792Sgshapiro { 812190792Sgshapiro char *p; 812290792Sgshapiro 812390792Sgshapiro l += MAXLINE; 812490792Sgshapiro p = sm_realloc(lsplits, l); 812590792Sgshapiro if (p == NULL) 812690792Sgshapiro { 812790792Sgshapiro /* let's try to get this done */ 812890792Sgshapiro sm_free(lsplits); 812990792Sgshapiro lsplits = NULL; 813090792Sgshapiro } 813190792Sgshapiro else 813290792Sgshapiro lsplits = p; 813390792Sgshapiro } 813490792Sgshapiro if (lsplits != NULL) 813590792Sgshapiro { 813690792Sgshapiro if (j == 0) 813790792Sgshapiro j += sm_strlcat(lsplits + j, 813890792Sgshapiro ee->e_id, l - j); 813990792Sgshapiro else 814090792Sgshapiro j += sm_strlcat2(lsplits + j, "; ", 814190792Sgshapiro ee->e_id, l - j); 814290792Sgshapiro SM_ASSERT(j < l); 814390792Sgshapiro } 814490792Sgshapiro } 814590792Sgshapiro ee = next; 814690792Sgshapiro } 814790792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1) 814890792Sgshapiro { 814990792Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s", 815090792Sgshapiro n - 1, n > 2 ? "s" : "", lsplits); 815190792Sgshapiro sm_free(lsplits); 815290792Sgshapiro } 815390792Sgshapiro split = split_within_queue(e) != SM_SPLIT_FAIL; 815490792Sgshapiro if (split) 815590792Sgshapiro e->e_flags |= EF_SPLIT; 815690792Sgshapiro return split; 815790792Sgshapiro} 815890792Sgshapiro 815990792Sgshapiro#if _FFR_QUARANTINE 816090792Sgshapiro/* 816190792Sgshapiro** QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope 816290792Sgshapiro** 816390792Sgshapiro** Add/remove quarantine reason and requeue appropriately. 816490792Sgshapiro** 816590792Sgshapiro** Parameters: 816690792Sgshapiro** qgrp -- queue group for the item 816790792Sgshapiro** qdir -- queue directory in the given queue group 816890792Sgshapiro** e -- envelope information for the item 816990792Sgshapiro** reason -- quarantine reason, NULL means unquarantine. 817090792Sgshapiro** 817190792Sgshapiro** Results: 817290792Sgshapiro** true if item changed, false otherwise 817390792Sgshapiro** 817490792Sgshapiro** Side Effects: 817590792Sgshapiro** Changes quarantine tag in queue file and renames it. 817690792Sgshapiro*/ 817790792Sgshapiro 817890792Sgshapirostatic bool 817990792Sgshapiroquarantine_queue_item(qgrp, qdir, e, reason) 818090792Sgshapiro int qgrp; 818190792Sgshapiro int qdir; 818290792Sgshapiro ENVELOPE *e; 818390792Sgshapiro char *reason; 818490792Sgshapiro{ 818590792Sgshapiro bool dirty = false; 818690792Sgshapiro bool failing = false; 818790792Sgshapiro bool foundq = false; 818890792Sgshapiro bool finished = false; 818990792Sgshapiro int fd; 819090792Sgshapiro int flags; 819190792Sgshapiro int oldtype; 819290792Sgshapiro int newtype; 819390792Sgshapiro int save_errno; 819490792Sgshapiro MODE_T oldumask = 0; 819590792Sgshapiro SM_FILE_T *oldqfp, *tempqfp; 819690792Sgshapiro char *bp; 819790792Sgshapiro char oldqf[MAXPATHLEN]; 819890792Sgshapiro char tempqf[MAXPATHLEN]; 819990792Sgshapiro char newqf[MAXPATHLEN]; 820090792Sgshapiro char buf[MAXLINE]; 820190792Sgshapiro 820290792Sgshapiro oldtype = queue_letter(e, ANYQFL_LETTER); 820390792Sgshapiro (void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof oldqf); 820490792Sgshapiro (void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof tempqf); 820590792Sgshapiro 820690792Sgshapiro /* 820790792Sgshapiro ** Instead of duplicating all the open 820890792Sgshapiro ** and lock code here, tell readqf() to 820990792Sgshapiro ** do that work and return the open 821090792Sgshapiro ** file pointer in e_lockfp. Note that 821190792Sgshapiro ** we must release the locks properly when 821290792Sgshapiro ** we are done. 821390792Sgshapiro */ 821490792Sgshapiro 821590792Sgshapiro if (!readqf(e, true)) 821690792Sgshapiro { 821790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 821890792Sgshapiro "Skipping %s\n", qid_printname(e)); 821990792Sgshapiro return false; 822090792Sgshapiro } 822190792Sgshapiro oldqfp = e->e_lockfp; 822290792Sgshapiro 822390792Sgshapiro /* open the new queue file */ 822490792Sgshapiro flags = O_CREAT|O_WRONLY|O_EXCL; 822590792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 822690792Sgshapiro oldumask = umask(002); 822790792Sgshapiro fd = open(tempqf, flags, QueueFileMode); 822890792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 822990792Sgshapiro (void) umask(oldumask); 823090792Sgshapiro RELEASE_QUEUE; 823190792Sgshapiro 823290792Sgshapiro if (fd < 0) 823390792Sgshapiro { 823490792Sgshapiro save_errno = errno; 823590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 823690792Sgshapiro "Skipping %s: Could not open %s: %s\n", 823790792Sgshapiro qid_printname(e), tempqf, 823890792Sgshapiro sm_errstring(save_errno)); 823990792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 824090792Sgshapiro return false; 824190792Sgshapiro } 824290792Sgshapiro if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB)) 824390792Sgshapiro { 824490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 824590792Sgshapiro "Skipping %s: Could not lock %s\n", 824690792Sgshapiro qid_printname(e), tempqf); 824790792Sgshapiro (void) close(fd); 824890792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 824990792Sgshapiro return false; 825090792Sgshapiro } 825190792Sgshapiro 825290792Sgshapiro tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd, 825390792Sgshapiro SM_IO_WRONLY, NULL); 825490792Sgshapiro if (tempqfp == NULL) 825590792Sgshapiro { 825690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 825790792Sgshapiro "Skipping %s: Could not lock %s\n", 825890792Sgshapiro qid_printname(e), tempqf); 825990792Sgshapiro (void) close(fd); 826090792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 826190792Sgshapiro return false; 826290792Sgshapiro } 826390792Sgshapiro 826490792Sgshapiro /* Copy the data over, changing the quarantine reason */ 826590792Sgshapiro while ((bp = fgetfolded(buf, sizeof buf, oldqfp)) != NULL) 826690792Sgshapiro { 826790792Sgshapiro if (tTd(40, 4)) 826890792Sgshapiro sm_dprintf("+++++ %s\n", bp); 826990792Sgshapiro switch (bp[0]) 827090792Sgshapiro { 827190792Sgshapiro case 'q': /* quarantine reason */ 827290792Sgshapiro foundq = true; 827390792Sgshapiro if (reason == NULL) 827490792Sgshapiro { 827590792Sgshapiro if (Verbose) 827690792Sgshapiro { 827790792Sgshapiro (void) sm_io_fprintf(smioout, 827890792Sgshapiro SM_TIME_DEFAULT, 827990792Sgshapiro "%s: Removed quarantine of \"%s\"\n", 828090792Sgshapiro e->e_id, &bp[1]); 828190792Sgshapiro } 828290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "unquarantine"); 828390792Sgshapiro dirty = true; 828490792Sgshapiro continue; 828590792Sgshapiro } 828690792Sgshapiro else if (strcmp(reason, &bp[1]) == 0) 828790792Sgshapiro { 828890792Sgshapiro if (Verbose) 828990792Sgshapiro { 829090792Sgshapiro (void) sm_io_fprintf(smioout, 829190792Sgshapiro SM_TIME_DEFAULT, 829290792Sgshapiro "%s: Already quarantined with \"%s\"\n", 829390792Sgshapiro e->e_id, reason); 829490792Sgshapiro } 829590792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 829690792Sgshapiro "q%s\n", reason); 829790792Sgshapiro } 829890792Sgshapiro else 829990792Sgshapiro { 830090792Sgshapiro if (Verbose) 830190792Sgshapiro { 830290792Sgshapiro (void) sm_io_fprintf(smioout, 830390792Sgshapiro SM_TIME_DEFAULT, 830490792Sgshapiro "%s: Quarantine changed from \"%s\" to \"%s\"\n", 830590792Sgshapiro e->e_id, &bp[1], 830690792Sgshapiro reason); 830790792Sgshapiro } 830890792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 830990792Sgshapiro "q%s\n", reason); 831090792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 831190792Sgshapiro reason); 831290792Sgshapiro dirty = true; 831390792Sgshapiro } 831490792Sgshapiro break; 831590792Sgshapiro 831698121Sgshapiro case 'S': 831790792Sgshapiro /* 831890792Sgshapiro ** If we are quarantining an unquarantined item, 831990792Sgshapiro ** need to put in a new 'q' line before it's 832090792Sgshapiro ** too late. 832190792Sgshapiro */ 832290792Sgshapiro 832390792Sgshapiro if (!foundq && reason != NULL) 832490792Sgshapiro { 832590792Sgshapiro if (Verbose) 832690792Sgshapiro { 832790792Sgshapiro (void) sm_io_fprintf(smioout, 832890792Sgshapiro SM_TIME_DEFAULT, 832990792Sgshapiro "%s: Quarantined with \"%s\"\n", 833090792Sgshapiro e->e_id, reason); 833190792Sgshapiro } 833290792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 833390792Sgshapiro "q%s\n", reason); 833490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 833590792Sgshapiro reason); 833690792Sgshapiro foundq = true; 833790792Sgshapiro dirty = true; 833890792Sgshapiro } 833990792Sgshapiro 834090792Sgshapiro /* Copy the line to the new file */ 834190792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 834290792Sgshapiro "%s\n", bp); 834390792Sgshapiro break; 834490792Sgshapiro 834590792Sgshapiro case '.': 834690792Sgshapiro finished = true; 834790792Sgshapiro /* FALLTHROUGH */ 834890792Sgshapiro 834990792Sgshapiro default: 835090792Sgshapiro /* Copy the line to the new file */ 835190792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 835290792Sgshapiro "%s\n", bp); 835390792Sgshapiro break; 835490792Sgshapiro } 835590792Sgshapiro } 835690792Sgshapiro 835790792Sgshapiro /* Make sure we read the whole old file */ 835890792Sgshapiro errno = sm_io_error(tempqfp); 835990792Sgshapiro if (errno != 0 && errno != SM_IO_EOF) 836090792Sgshapiro { 836190792Sgshapiro save_errno = errno; 836290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 836390792Sgshapiro "Skipping %s: Error reading %s: %s\n", 836490792Sgshapiro qid_printname(e), oldqf, 836590792Sgshapiro sm_errstring(save_errno)); 836690792Sgshapiro failing = true; 836790792Sgshapiro } 836890792Sgshapiro 836990792Sgshapiro if (!failing && !finished) 837090792Sgshapiro { 837190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 837290792Sgshapiro "Skipping %s: Incomplete file: %s\n", 837390792Sgshapiro qid_printname(e), oldqf); 837490792Sgshapiro failing = true; 837590792Sgshapiro } 837690792Sgshapiro 837790792Sgshapiro /* Check if we actually changed anything or we can just bail now */ 837890792Sgshapiro if (!dirty) 837990792Sgshapiro { 838090792Sgshapiro /* pretend we failed, even though we technically didn't */ 838190792Sgshapiro failing = true; 838290792Sgshapiro } 838390792Sgshapiro 838490792Sgshapiro /* Make sure we wrote things out safely */ 838590792Sgshapiro if (!failing && 838690792Sgshapiro (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 || 838790792Sgshapiro ((SuperSafe == SAFE_REALLY || SuperSafe == SAFE_INTERACTIVE) && 838890792Sgshapiro fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) || 838990792Sgshapiro ((errno = sm_io_error(tempqfp)) != 0))) 839090792Sgshapiro { 839190792Sgshapiro save_errno = errno; 839290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 839390792Sgshapiro "Skipping %s: Error writing %s: %s\n", 839490792Sgshapiro qid_printname(e), tempqf, 839590792Sgshapiro sm_errstring(save_errno)); 839690792Sgshapiro failing = true; 839790792Sgshapiro } 839890792Sgshapiro 839990792Sgshapiro 840090792Sgshapiro /* Figure out the new filename */ 840190792Sgshapiro newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER); 840290792Sgshapiro if (oldtype == newtype) 840390792Sgshapiro { 840490792Sgshapiro /* going to rename tempqf to oldqf */ 840590792Sgshapiro (void) sm_strlcpy(newqf, oldqf, sizeof newqf); 840690792Sgshapiro } 840790792Sgshapiro else 840890792Sgshapiro { 840990792Sgshapiro /* going to rename tempqf to new name based on newtype */ 841090792Sgshapiro (void) sm_strlcpy(newqf, queuename(e, newtype), sizeof newqf); 841190792Sgshapiro } 841290792Sgshapiro 841390792Sgshapiro save_errno = 0; 841490792Sgshapiro 841590792Sgshapiro /* rename tempqf to newqf */ 841690792Sgshapiro if (!failing && 841790792Sgshapiro rename(tempqf, newqf) < 0) 841890792Sgshapiro save_errno = (errno == 0) ? EINVAL : errno; 841990792Sgshapiro 842090792Sgshapiro /* Check rename() success */ 842190792Sgshapiro if (!failing && save_errno != 0) 842290792Sgshapiro { 842390792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, 842490792Sgshapiro "quarantine_queue_item: rename(%s, %s): %s", 842590792Sgshapiro tempqf, newqf, sm_errstring(save_errno)); 842690792Sgshapiro 842790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 842890792Sgshapiro "Error renaming %s to %s: %s\n", 842990792Sgshapiro tempqf, newqf, 843090792Sgshapiro sm_errstring(save_errno)); 843190792Sgshapiro if (oldtype == newtype) 843290792Sgshapiro { 843390792Sgshapiro /* 843490792Sgshapiro ** Bail here since we don't know the state of 843590792Sgshapiro ** the filesystem and may need to keep tempqf 843690792Sgshapiro ** for the user to rescue us. 843790792Sgshapiro */ 843890792Sgshapiro 843990792Sgshapiro RELEASE_QUEUE; 844090792Sgshapiro errno = save_errno; 844190792Sgshapiro syserr("!452 Error renaming control file %s", tempqf); 844290792Sgshapiro /* NOTREACHED */ 844390792Sgshapiro } 844490792Sgshapiro else 844590792Sgshapiro { 844690792Sgshapiro /* remove new file (if rename() half completed) */ 844790792Sgshapiro if (xunlink(newqf) < 0) 844890792Sgshapiro { 844990792Sgshapiro save_errno = errno; 845090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 845190792Sgshapiro "Error removing %s: %s\n", 845290792Sgshapiro newqf, 845390792Sgshapiro sm_errstring(save_errno)); 845490792Sgshapiro } 845590792Sgshapiro 845690792Sgshapiro /* tempqf removed below */ 845790792Sgshapiro failing = true; 845890792Sgshapiro } 845990792Sgshapiro 846090792Sgshapiro } 846190792Sgshapiro 846290792Sgshapiro /* If changing file types, need to remove old type */ 846390792Sgshapiro if (!failing && oldtype != newtype) 846490792Sgshapiro { 846590792Sgshapiro if (xunlink(oldqf) < 0) 846690792Sgshapiro { 846790792Sgshapiro save_errno = errno; 846890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 846990792Sgshapiro "Error removing %s: %s\n", 847090792Sgshapiro oldqf, sm_errstring(save_errno)); 847190792Sgshapiro } 847290792Sgshapiro } 847390792Sgshapiro 847490792Sgshapiro /* see if anything above failed */ 847590792Sgshapiro if (failing) 847690792Sgshapiro { 847790792Sgshapiro /* Something failed: remove new file, old file still there */ 847890792Sgshapiro (void) xunlink(tempqf); 847990792Sgshapiro } 848090792Sgshapiro 848190792Sgshapiro /* 848290792Sgshapiro ** fsync() after file operations to make sure metadata is 848390792Sgshapiro ** written to disk on filesystems in which renames are 848490792Sgshapiro ** not guaranteed. It's ok if they fail, mail won't be lost. 848590792Sgshapiro */ 848690792Sgshapiro 848790792Sgshapiro if (SuperSafe != SAFE_NO) 848890792Sgshapiro { 848990792Sgshapiro /* for soft-updates */ 849090792Sgshapiro (void) fsync(sm_io_getinfo(tempqfp, 849190792Sgshapiro SM_IO_WHAT_FD, NULL)); 849290792Sgshapiro 849390792Sgshapiro if (!failing) 849490792Sgshapiro { 849590792Sgshapiro /* for soft-updates */ 849690792Sgshapiro (void) fsync(sm_io_getinfo(oldqfp, 849790792Sgshapiro SM_IO_WHAT_FD, NULL)); 849890792Sgshapiro } 849990792Sgshapiro 850090792Sgshapiro /* for other odd filesystems */ 850190792Sgshapiro SYNC_DIR(tempqf, false); 850290792Sgshapiro } 850390792Sgshapiro 850490792Sgshapiro /* Close up shop */ 850590792Sgshapiro RELEASE_QUEUE; 850690792Sgshapiro if (tempqfp != NULL) 850790792Sgshapiro (void) sm_io_close(tempqfp, SM_TIME_DEFAULT); 850890792Sgshapiro if (oldqfp != NULL) 850990792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 851090792Sgshapiro 851190792Sgshapiro /* All went well */ 851290792Sgshapiro return !failing; 851390792Sgshapiro} 851490792Sgshapiro 851590792Sgshapiro/* 851690792Sgshapiro** QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue 851790792Sgshapiro** 851890792Sgshapiro** Read all matching queue items, add/remove quarantine 851990792Sgshapiro** reason, and requeue appropriately. 852090792Sgshapiro** 852190792Sgshapiro** Parameters: 852290792Sgshapiro** reason -- quarantine reason, "." means unquarantine. 852390792Sgshapiro** qgrplimit -- limit to single queue group unless NOQGRP 852490792Sgshapiro** 852590792Sgshapiro** Results: 852690792Sgshapiro** none. 852790792Sgshapiro** 852890792Sgshapiro** Side Effects: 852990792Sgshapiro** Lots of changes to the queue. 853090792Sgshapiro*/ 853190792Sgshapiro 853290792Sgshapirovoid 853390792Sgshapiroquarantine_queue(reason, qgrplimit) 853490792Sgshapiro char *reason; 853590792Sgshapiro int qgrplimit; 853690792Sgshapiro{ 853790792Sgshapiro int changed = 0; 853890792Sgshapiro int qgrp; 853990792Sgshapiro 854090792Sgshapiro /* Convert internal representation of unquarantine */ 854190792Sgshapiro if (reason != NULL && reason[0] == '.' && reason[1] == '\0') 854290792Sgshapiro reason = NULL; 854390792Sgshapiro 854490792Sgshapiro if (reason != NULL) 854590792Sgshapiro { 854690792Sgshapiro /* clean it */ 854790792Sgshapiro reason = newstr(denlstring(reason, true, true)); 854890792Sgshapiro } 854990792Sgshapiro 855090792Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 855190792Sgshapiro { 855290792Sgshapiro int qdir; 855390792Sgshapiro 855490792Sgshapiro if (qgrplimit != NOQGRP && qgrplimit != qgrp) 855590792Sgshapiro continue; 855690792Sgshapiro 855790792Sgshapiro for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++) 855890792Sgshapiro { 855990792Sgshapiro int i; 856090792Sgshapiro int nrequests; 856190792Sgshapiro 856290792Sgshapiro if (StopRequest) 856390792Sgshapiro stop_sendmail(); 856490792Sgshapiro 856590792Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL); 856690792Sgshapiro 856790792Sgshapiro /* first see if there is anything */ 856890792Sgshapiro if (nrequests <= 0) 856990792Sgshapiro { 857090792Sgshapiro if (Verbose) 857190792Sgshapiro { 857290792Sgshapiro (void) sm_io_fprintf(smioout, 857390792Sgshapiro SM_TIME_DEFAULT, "%s: no matches\n", 857490792Sgshapiro qid_printqueue(qgrp, qdir)); 857590792Sgshapiro } 857690792Sgshapiro continue; 857790792Sgshapiro } 857890792Sgshapiro 857990792Sgshapiro if (Verbose) 858090792Sgshapiro { 858190792Sgshapiro (void) sm_io_fprintf(smioout, 858290792Sgshapiro SM_TIME_DEFAULT, "Processing %s:\n", 858390792Sgshapiro qid_printqueue(qgrp, qdir)); 858490792Sgshapiro } 858590792Sgshapiro 858690792Sgshapiro for (i = 0; i < WorkListCount; i++) 858790792Sgshapiro { 858890792Sgshapiro ENVELOPE e; 858990792Sgshapiro 859090792Sgshapiro if (StopRequest) 859190792Sgshapiro stop_sendmail(); 859290792Sgshapiro 859390792Sgshapiro /* setup envelope */ 859490792Sgshapiro clearenvelope(&e, true, sm_rpool_new_x(NULL)); 859590792Sgshapiro e.e_id = WorkList[i].w_name + 2; 859690792Sgshapiro e.e_qgrp = qgrp; 859790792Sgshapiro e.e_qdir = qdir; 859890792Sgshapiro 859990792Sgshapiro if (tTd(70, 101)) 860090792Sgshapiro { 860190792Sgshapiro sm_io_fprintf(smioout, SM_TIME_DEFAULT, 860290792Sgshapiro "Would do %s\n", e.e_id); 860390792Sgshapiro changed++; 860490792Sgshapiro } 860590792Sgshapiro else if (quarantine_queue_item(qgrp, qdir, 860690792Sgshapiro &e, reason)) 860790792Sgshapiro changed++; 860890792Sgshapiro 860990792Sgshapiro /* clean up */ 861090792Sgshapiro sm_rpool_free(e.e_rpool); 861190792Sgshapiro e.e_rpool = NULL; 861290792Sgshapiro } 861390792Sgshapiro if (WorkList != NULL) 861490792Sgshapiro sm_free(WorkList); /* XXX */ 861590792Sgshapiro WorkList = NULL; 861690792Sgshapiro WorkListSize = 0; 861790792Sgshapiro WorkListCount = 0; 861890792Sgshapiro } 861990792Sgshapiro } 862090792Sgshapiro if (Verbose) 862190792Sgshapiro { 862290792Sgshapiro if (changed == 0) 862390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 862490792Sgshapiro "No changes\n"); 862590792Sgshapiro else 862690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 862790792Sgshapiro "%d change%s\n", 862890792Sgshapiro changed, 862990792Sgshapiro changed == 1 ? "" : "s"); 863090792Sgshapiro } 863190792Sgshapiro} 863290792Sgshapiro#endif /* _FFR_QUARANTINE */ 8633