queue.c revision 102528
138032Speter/* 290792Sgshapiro * Copyright (c) 1998-2002 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 16102528SgshapiroSM_RCSID("@(#)$Id: queue.c,v 8.863.2.6 2002/08/16 16:27:37 gshapiro Exp $") 1738032Speter 1890792Sgshapiro#include <dirent.h> 1938032Speter 2090792Sgshapiro#if SM_CONF_SHM 2190792Sgshapiro# include <sm/shm.h> 2290792Sgshapiro#endif /* SM_CONF_SHM */ 2338032Speter 2490792Sgshapiro# define RELEASE_QUEUE (void) 0 2590792Sgshapiro# define ST_INODE(st) (st).st_ino 2690792Sgshapiro 2790792Sgshapiro 2890792Sgshapiro/* 2990792Sgshapiro** Historical notes: 3090792Sgshapiro** QF_VERSION==4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY 3190792Sgshapiro** QF_VERSION==5 was sendmail 8.10/8.11 with _FFR_QUEUEDELAY 3290792Sgshapiro*/ 3390792Sgshapiro 3490792Sgshapiro#if _FFR_QUEUEDELAY 3590792Sgshapiro# define QF_VERSION 7 /* version number of this queue format */ 3664562Sgshapirostatic time_t queuedelay __P((ENVELOPE *)); 3790792Sgshapiro#define queuedelay_qfver_unsupported(qfver) false 3890792Sgshapiro#else /* _FFR_QUEUEDELAY */ 3990792Sgshapiro# define QF_VERSION 6 /* version number of this queue format */ 4090792Sgshapiro# define queuedelay(e) MinQueueAge 4190792Sgshapiro#define queuedelay_qfver_unsupported(qfver) ((qfver) == 5 || (qfver) == 7) 4290792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 4390792Sgshapiro#if _FFR_QUARANTINE 4490792Sgshapirostatic char queue_letter __P((ENVELOPE *, int)); 4590792Sgshapirostatic bool quarantine_queue_item __P((int, int, ENVELOPE *, char *)); 4690792Sgshapiro#endif /* _FFR_QUARANTINE */ 4764562Sgshapiro 4890792Sgshapiro/* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */ 4990792Sgshapiro 5038032Speter/* 5138032Speter** Work queue. 5238032Speter*/ 5338032Speter 5438032Speterstruct work 5538032Speter{ 5638032Speter char *w_name; /* name of control file */ 5738032Speter char *w_host; /* name of recipient host */ 5838032Speter bool w_lock; /* is message locked? */ 5938032Speter bool w_tooyoung; /* is it too young to run? */ 6038032Speter long w_pri; /* priority of message, see below */ 6190792Sgshapiro time_t w_ctime; /* creation time */ 6290792Sgshapiro time_t w_mtime; /* modification time */ 6390792Sgshapiro int w_qgrp; /* queue group located in */ 6490792Sgshapiro int w_qdir; /* queue directory located in */ 6538032Speter struct work *w_next; /* next in queue */ 6638032Speter}; 6738032Speter 6838032Spetertypedef struct work WORK; 6938032Speter 7090792Sgshapirostatic WORK *WorkQ; /* queue of things to be done */ 7190792Sgshapirostatic int NumWorkGroups; /* number of work groups */ 7238032Speter 7390792Sgshapiro/* 7494334Sgshapiro** DoQueueRun indicates that a queue run is needed. 7594334Sgshapiro** Notice: DoQueueRun is modified in a signal handler! 7690792Sgshapiro*/ 7790792Sgshapiro 7894334Sgshapirostatic bool volatile DoQueueRun; /* non-interrupt time queue run needed */ 7990792Sgshapiro 8090792Sgshapiro/* 8190792Sgshapiro** Work group definition structure. 8290792Sgshapiro** Each work group contains one or more queue groups. This is done 8390792Sgshapiro** to manage the number of queue group runners active at the same time 8490792Sgshapiro** to be within the constraints of MaxQueueChildren (if it is set). 8590792Sgshapiro** The number of queue groups that can be run on the next work run 8690792Sgshapiro** is kept track of. The queue groups are run in a round robin. 8790792Sgshapiro*/ 8890792Sgshapiro 8990792Sgshapirostruct workgrp 9090792Sgshapiro{ 9190792Sgshapiro int wg_numqgrp; /* number of queue groups in work grp */ 9290792Sgshapiro int wg_runners; /* total runners */ 9390792Sgshapiro int wg_curqgrp; /* current queue group */ 9490792Sgshapiro QUEUEGRP **wg_qgs; /* array of queue groups */ 9590792Sgshapiro int wg_maxact; /* max # of active runners */ 9690792Sgshapiro time_t wg_lowqintvl; /* lowest queue interval */ 9790792Sgshapiro int wg_restart; /* needs restarting? */ 9890792Sgshapiro int wg_restartcnt; /* count of times restarted */ 9990792Sgshapiro}; 10090792Sgshapiro 10190792Sgshapirotypedef struct workgrp WORKGRP; 10290792Sgshapiro 10390792Sgshapirostatic WORKGRP volatile WorkGrp[MAXWORKGROUPS + 1]; /* work groups */ 10490792Sgshapiro 10590792Sgshapiro#if SM_HEAP_CHECK 10690792Sgshapirostatic SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q", 10790792Sgshapiro "@(#)$Debug: leak_q - trace memory leaks during queue processing $"); 10890792Sgshapiro#endif /* SM_HEAP_CHECK */ 10990792Sgshapiro 11090792Sgshapiro/* 11190792Sgshapiro** We use EmptyString instead of "" to avoid 11290792Sgshapiro** 'zero-length format string' warnings from gcc 11390792Sgshapiro*/ 11490792Sgshapiro 11590792Sgshapirostatic const char EmptyString[] = ""; 11690792Sgshapiro 11790792Sgshapirostatic void grow_wlist __P((int, int)); 11890792Sgshapirostatic int multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *)); 11990792Sgshapirostatic int gatherq __P((int, int, bool, bool *, bool *)); 12090792Sgshapirostatic int sortq __P((int)); 12190792Sgshapirostatic void printctladdr __P((ADDRESS *, SM_FILE_T *)); 12290792Sgshapirostatic bool readqf __P((ENVELOPE *, bool)); 12390792Sgshapirostatic void restart_work_group __P((int)); 12490792Sgshapirostatic void runner_work __P((ENVELOPE *, int, bool, int, int)); 12594334Sgshapirostatic void schedule_queue_runs __P((bool, int, bool)); 12664562Sgshapirostatic char *strrev __P((char *)); 12790792Sgshapirostatic ADDRESS *setctluser __P((char *, int, ENVELOPE *)); 12890792Sgshapiro#if _FFR_RHS 12990792Sgshapirostatic int sm_strshufflecmp __P((char *, char *)); 13090792Sgshapirostatic void init_shuffle_alphabet __P(()); 13190792Sgshapiro#endif /* _FFR_RHS */ 13264562Sgshapirostatic int workcmpf0(); 13364562Sgshapirostatic int workcmpf1(); 13464562Sgshapirostatic int workcmpf2(); 13564562Sgshapirostatic int workcmpf3(); 13664562Sgshapirostatic int workcmpf4(); 13790792Sgshapirostatic int workcmpf5(); 13890792Sgshapirostatic int workcmpf6(); 13990792Sgshapiro#if _FFR_RHS 14090792Sgshapirostatic int workcmpf7(); 14190792Sgshapiro#endif /* _FFR_RHS */ 14238032Speter 14390792Sgshapiro#if RANDOMSHIFT 14490792Sgshapiro# define get_rand_mod(m) ((get_random() >> RANDOMSHIFT) % (m)) 14590792Sgshapiro#else /* RANDOMSHIFT */ 14690792Sgshapiro# define get_rand_mod(m) (get_random() % (m)) 14790792Sgshapiro#endif /* RANDOMSHIFT */ 14890792Sgshapiro 14980785Sgshapiro/* 15090792Sgshapiro** File system definition. 15190792Sgshapiro** Used to keep track of how much free space is available 15290792Sgshapiro** on a file system in which one or more queue directories reside. 15390792Sgshapiro*/ 15490792Sgshapiro 15590792Sgshapirotypedef struct filesys_shared FILESYS; 15690792Sgshapiro 15790792Sgshapirostruct filesys_shared 15890792Sgshapiro{ 15990792Sgshapiro dev_t fs_dev; /* unique device id */ 16090792Sgshapiro long fs_avail; /* number of free blocks available */ 16190792Sgshapiro long fs_blksize; /* block size, in bytes */ 16290792Sgshapiro}; 16390792Sgshapiro 16490792Sgshapiro/* probably kept in shared memory */ 16590792Sgshapirostatic FILESYS FileSys[MAXFILESYS]; /* queue file systems */ 16690792Sgshapirostatic char *FSPath[MAXFILESYS]; /* pathnames for file systems */ 16790792Sgshapiro 16890792Sgshapiro#if SM_CONF_SHM 16990792Sgshapiro 17090792Sgshapiro/* 17190792Sgshapiro** Shared memory data 17290792Sgshapiro** 17390792Sgshapiro** Current layout: 17490792Sgshapiro** size -- size of shared memory segment 17590792Sgshapiro** pid -- pid of owner, should be a unique id to avoid misinterpretations 17690792Sgshapiro** by other processes. 17790792Sgshapiro** tag -- should be a unique id to avoid misinterpretations by others. 17890792Sgshapiro** idea: hash over configuration data that will be stored here. 17990792Sgshapiro** NumFileSys -- number of file systems. 18090792Sgshapiro** FileSys -- (arrary of) structure for used file systems. 18190792Sgshapiro** RSATmpCnt -- counter for number of uses of ephemeral RSA key. 18290792Sgshapiro** QShm -- (array of) structure for information about queue directories. 18390792Sgshapiro*/ 18490792Sgshapiro 18590792Sgshapiro/* 18690792Sgshapiro** Queue data in shared memory 18790792Sgshapiro*/ 18890792Sgshapiro 18990792Sgshapirotypedef struct queue_shared QUEUE_SHM_T; 19090792Sgshapiro 19190792Sgshapirostruct queue_shared 19290792Sgshapiro{ 19390792Sgshapiro int qs_entries; /* number of entries */ 19490792Sgshapiro /* XXX more to follow? */ 19590792Sgshapiro}; 19690792Sgshapiro 19790792Sgshapirostatic void *Pshm; /* pointer to shared memory */ 19890792Sgshapirostatic FILESYS *PtrFileSys; /* pointer to queue file system array */ 19990792Sgshapiroint ShmId = SM_SHM_NO_ID; /* shared memory id */ 20090792Sgshapirostatic QUEUE_SHM_T *QShm; /* pointer to shared queue data */ 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 25890792Sgshapiro#else /* SM_CONF_SHM */ 25990792Sgshapiro# define FILE_SYS(i) FileSys[i] 26090792Sgshapiro#endif /* SM_CONF_SHM */ 26190792Sgshapiro 26290792Sgshapiro/* access to the various components of file system data */ 26390792Sgshapiro#define FILE_SYS_NAME(i) FSPath[i] 26490792Sgshapiro#define FILE_SYS_AVAIL(i) FILE_SYS(i).fs_avail 26590792Sgshapiro#define FILE_SYS_BLKSIZE(i) FILE_SYS(i).fs_blksize 26690792Sgshapiro#define FILE_SYS_DEV(i) FILE_SYS(i).fs_dev 26790792Sgshapiro 26890792Sgshapiro/* 26980785Sgshapiro** Current qf file field assignments: 27080785Sgshapiro** 27180785Sgshapiro** A AUTH= parameter 27280785Sgshapiro** B body type 27380785Sgshapiro** C controlling user 27480785Sgshapiro** D data file name 27590792Sgshapiro** d data file directory name (added in 8.12) 27680785Sgshapiro** E error recipient 27780785Sgshapiro** F flag bits 27890792Sgshapiro** G queue delay algorithm (_FFR_QUEUEDELAY) 27980785Sgshapiro** H header 28080785Sgshapiro** I data file's inode number 28180785Sgshapiro** K time of last delivery attempt 28280785Sgshapiro** L Solaris Content-Length: header (obsolete) 28398841Sgshapiro** M message 28480785Sgshapiro** N number of delivery attempts 28580785Sgshapiro** P message priority 28690792Sgshapiro** q quarantine reason (_FFR_QUARANTINE) 28780785Sgshapiro** Q original recipient (ORCPT=) 28890792Sgshapiro** r final recipient (Final-Recipient: DSN field) 28980785Sgshapiro** R recipient 29080785Sgshapiro** S sender 29180785Sgshapiro** T init time 29280785Sgshapiro** V queue file version 29390792Sgshapiro** X free (was: character set if _FFR_SAVE_CHARSET) 29490792Sgshapiro** Y current delay (_FFR_QUEUEDELAY) 29580785Sgshapiro** Z original envelope id from ESMTP 29690792Sgshapiro** ! deliver by (added in 8.12) 29780785Sgshapiro** $ define macro 29880785Sgshapiro** . terminate file 29980785Sgshapiro*/ 30080785Sgshapiro 30190792Sgshapiro/* 30238032Speter** QUEUEUP -- queue a message up for future transmission. 30338032Speter** 30438032Speter** Parameters: 30538032Speter** e -- the envelope to queue up. 30690792Sgshapiro** announce -- if true, tell when you are queueing up. 30790792Sgshapiro** msync -- if true, then fsync() if SuperSafe interactive mode. 30838032Speter** 30938032Speter** Returns: 31038032Speter** none. 31138032Speter** 31238032Speter** Side Effects: 31390792Sgshapiro** The current request is saved in a control file. 31438032Speter** The queue file is left locked. 31538032Speter*/ 31638032Speter 31738032Spetervoid 31890792Sgshapiroqueueup(e, announce, msync) 31938032Speter register ENVELOPE *e; 32038032Speter bool announce; 32190792Sgshapiro bool msync; 32238032Speter{ 32390792Sgshapiro register SM_FILE_T *tfp; 32438032Speter register HDR *h; 32538032Speter register ADDRESS *q; 32664562Sgshapiro int tfd = -1; 32738032Speter int i; 32838032Speter bool newid; 32938032Speter register char *p; 33038032Speter MAILER nullmailer; 33138032Speter MCI mcibuf; 33290792Sgshapiro char qf[MAXPATHLEN]; 33364562Sgshapiro char tf[MAXPATHLEN]; 33490792Sgshapiro char df[MAXPATHLEN]; 33538032Speter char buf[MAXLINE]; 33638032Speter 33738032Speter /* 33838032Speter ** Create control file. 33938032Speter */ 34038032Speter 34138032Speter newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); 34290792Sgshapiro (void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof tf); 34338032Speter tfp = e->e_lockfp; 34438032Speter if (tfp == NULL) 34590792Sgshapiro newid = false; 34638032Speter 34790792Sgshapiro /* if newid, write the queue file directly (instead of temp file) */ 34838032Speter if (!newid) 34938032Speter { 35090792Sgshapiro const int flags = O_CREAT|O_WRONLY|O_EXCL; 35164562Sgshapiro 35238032Speter /* get a locked tf file */ 35338032Speter for (i = 0; i < 128; i++) 35438032Speter { 35564562Sgshapiro if (tfd < 0) 35638032Speter { 35790792Sgshapiro MODE_T oldumask = 0; 35864562Sgshapiro 35964562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 36064562Sgshapiro oldumask = umask(002); 36164562Sgshapiro tfd = open(tf, flags, QueueFileMode); 36264562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 36364562Sgshapiro (void) umask(oldumask); 36464562Sgshapiro 36564562Sgshapiro if (tfd < 0) 36664562Sgshapiro { 36764562Sgshapiro if (errno != EEXIST) 36864562Sgshapiro break; 36964562Sgshapiro if (LogLevel > 0 && (i % 32) == 0) 37064562Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 37164562Sgshapiro "queueup: cannot create %s, uid=%d: %s", 37298121Sgshapiro tf, (int) geteuid(), 37390792Sgshapiro sm_errstring(errno)); 37464562Sgshapiro } 37538032Speter } 37664562Sgshapiro if (tfd >= 0) 37738032Speter { 37864562Sgshapiro if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB)) 37938032Speter break; 38038032Speter else if (LogLevel > 0 && (i % 32) == 0) 38138032Speter sm_syslog(LOG_ALERT, e->e_id, 38264562Sgshapiro "queueup: cannot lock %s: %s", 38390792Sgshapiro tf, sm_errstring(errno)); 38464562Sgshapiro if ((i % 32) == 31) 38564562Sgshapiro { 38664562Sgshapiro (void) close(tfd); 38764562Sgshapiro tfd = -1; 38864562Sgshapiro } 38938032Speter } 39038032Speter 39138032Speter if ((i % 32) == 31) 39238032Speter { 39338032Speter /* save the old temp file away */ 39464562Sgshapiro (void) rename(tf, queuename(e, TEMPQF_LETTER)); 39538032Speter } 39638032Speter else 39764562Sgshapiro (void) sleep(i % 32); 39838032Speter } 39990792Sgshapiro if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 40090792Sgshapiro (void *) &tfd, SM_IO_WRONLY, 40190792Sgshapiro NULL)) == NULL) 40238032Speter { 40364562Sgshapiro int save_errno = errno; 40464562Sgshapiro 40590792Sgshapiro printopenfds(true); 40664562Sgshapiro errno = save_errno; 40738032Speter syserr("!queueup: cannot create queue temp file %s, uid=%d", 40898121Sgshapiro tf, (int) geteuid()); 40938032Speter } 41038032Speter } 41138032Speter 41238032Speter if (tTd(40, 1)) 41390792Sgshapiro sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n", 41490792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 41590792Sgshapiro queuename(e, ANYQFL_LETTER), 41690792Sgshapiro newid ? " (new id)" : ""); 41738032Speter if (tTd(40, 3)) 41838032Speter { 41990792Sgshapiro sm_dprintf(" e_flags="); 42038032Speter printenvflags(e); 42138032Speter } 42238032Speter if (tTd(40, 32)) 42338032Speter { 42490792Sgshapiro sm_dprintf(" sendq="); 42590792Sgshapiro printaddr(e->e_sendqueue, true); 42638032Speter } 42738032Speter if (tTd(40, 9)) 42838032Speter { 42990792Sgshapiro sm_dprintf(" tfp="); 43090792Sgshapiro dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false); 43190792Sgshapiro sm_dprintf(" lockfp="); 43238032Speter if (e->e_lockfp == NULL) 43390792Sgshapiro sm_dprintf("NULL\n"); 43438032Speter else 43590792Sgshapiro dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL), 43690792Sgshapiro true, false); 43738032Speter } 43838032Speter 43938032Speter /* 44038032Speter ** If there is no data file yet, create one. 44138032Speter */ 44238032Speter 44390792Sgshapiro (void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof df); 44464562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags)) 44538032Speter { 44690792Sgshapiro if (e->e_dfp != NULL && 44790792Sgshapiro SuperSafe != SAFE_REALLY && 44890792Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 44990792Sgshapiro errno != EINVAL) 45090792Sgshapiro { 45164562Sgshapiro syserr("!queueup: cannot commit data file %s, uid=%d", 45298121Sgshapiro queuename(e, DATAFL_LETTER), (int) geteuid()); 45390792Sgshapiro } 45490792Sgshapiro if (e->e_dfp != NULL && 45590792Sgshapiro SuperSafe == SAFE_INTERACTIVE && msync) 45690792Sgshapiro { 45790792Sgshapiro if (tTd(40,32)) 45890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 45990792Sgshapiro "queueup: fsync(e->e_dfp)"); 46090792Sgshapiro 46190792Sgshapiro if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, 46290792Sgshapiro NULL)) < 0) 46390792Sgshapiro { 46490792Sgshapiro if (newid) 46590792Sgshapiro syserr("!552 Error writing data file %s", 46690792Sgshapiro df); 46790792Sgshapiro else 46890792Sgshapiro syserr("!452 Error writing data file %s", 46990792Sgshapiro df); 47090792Sgshapiro } 47190792Sgshapiro } 47264562Sgshapiro } 47364562Sgshapiro else 47464562Sgshapiro { 47564562Sgshapiro int dfd; 47690792Sgshapiro MODE_T oldumask = 0; 47790792Sgshapiro register SM_FILE_T *dfp = NULL; 47838032Speter struct stat stbuf; 47938032Speter 48090792Sgshapiro if (e->e_dfp != NULL && 48190792Sgshapiro sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 48264562Sgshapiro syserr("committing over bf file"); 48364562Sgshapiro 48490792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 48590792Sgshapiro oldumask = umask(002); 48690792Sgshapiro dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC, QueueFileMode); 48790792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 48890792Sgshapiro (void) umask(oldumask); 48990792Sgshapiro if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 49090792Sgshapiro (void *) &dfd, SM_IO_WRONLY, 49190792Sgshapiro NULL)) == NULL) 49238032Speter syserr("!queueup: cannot create data temp file %s, uid=%d", 49398121Sgshapiro df, (int) geteuid()); 49464562Sgshapiro if (fstat(dfd, &stbuf) < 0) 49538032Speter e->e_dfino = -1; 49638032Speter else 49738032Speter { 49838032Speter e->e_dfdev = stbuf.st_dev; 49990792Sgshapiro e->e_dfino = ST_INODE(stbuf); 50038032Speter } 50138032Speter e->e_flags |= EF_HAS_DF; 50264562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 50338032Speter mcibuf.mci_out = dfp; 50438032Speter mcibuf.mci_mailer = FileMailer; 50538032Speter (*e->e_putbody)(&mcibuf, e, NULL); 50690792Sgshapiro 50790792Sgshapiro if (SuperSafe == SAFE_REALLY || 50890792Sgshapiro (SuperSafe == SAFE_INTERACTIVE && msync)) 50990792Sgshapiro { 51090792Sgshapiro if (tTd(40,32)) 51190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 51290792Sgshapiro "queueup: fsync(dfp)"); 51390792Sgshapiro 51490792Sgshapiro if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0) 51590792Sgshapiro { 51690792Sgshapiro if (newid) 51790792Sgshapiro syserr("!552 Error writing data file %s", 51890792Sgshapiro df); 51990792Sgshapiro else 52090792Sgshapiro syserr("!452 Error writing data file %s", 52190792Sgshapiro df); 52290792Sgshapiro } 52390792Sgshapiro } 52490792Sgshapiro 52590792Sgshapiro if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0) 52664562Sgshapiro syserr("!queueup: cannot save data temp file %s, uid=%d", 52798121Sgshapiro df, (int) geteuid()); 52838032Speter e->e_putbody = putbody; 52938032Speter } 53038032Speter 53138032Speter /* 53238032Speter ** Output future work requests. 53338032Speter ** Priority and creation time should be first, since 53490792Sgshapiro ** they are required by gatherq. 53538032Speter */ 53638032Speter 53738032Speter /* output queue version number (must be first!) */ 53890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION); 53938032Speter 54038032Speter /* output creation time */ 54190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime); 54238032Speter 54338032Speter /* output last delivery time */ 54490792Sgshapiro#if _FFR_QUEUEDELAY 54590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime); 54690792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "G%d\n", e->e_queuealg); 54790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Y%ld\n", (long) e->e_queuedelay); 54864562Sgshapiro if (tTd(40, 64)) 54964562Sgshapiro sm_syslog(LOG_INFO, e->e_id, 55064562Sgshapiro "queue alg: %d delay %ld next: %ld (now: %ld)\n", 55164562Sgshapiro e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime()); 55290792Sgshapiro#else /* _FFR_QUEUEDELAY */ 55390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime); 55490792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 55538032Speter 55638032Speter /* output number of delivery attempts */ 55790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries); 55838032Speter 55938032Speter /* output message priority */ 56090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority); 56138032Speter 56290792Sgshapiro /* 56390792Sgshapiro ** If data file is in a different directory than the queue file, 56490792Sgshapiro ** output a "d" record naming the directory of the data file. 56590792Sgshapiro */ 56690792Sgshapiro 56790792Sgshapiro if (e->e_dfqgrp != e->e_qgrp) 56890792Sgshapiro { 56990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n", 57090792Sgshapiro Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name); 57190792Sgshapiro } 57290792Sgshapiro 57338032Speter /* output inode number of data file */ 57438032Speter /* XXX should probably include device major/minor too */ 57538032Speter if (e->e_dfino != -1) 57638032Speter { 57790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n", 57890792Sgshapiro (long) major(e->e_dfdev), 57990792Sgshapiro (long) minor(e->e_dfdev), 58090792Sgshapiro (ULONGLONG_T) e->e_dfino); 58138032Speter } 58238032Speter 58338032Speter /* output body type */ 58438032Speter if (e->e_bodytype != NULL) 58590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n", 58690792Sgshapiro denlstring(e->e_bodytype, true, false)); 58738032Speter 58890792Sgshapiro#if _FFR_QUARANTINE 58990792Sgshapiro /* quarantine reason */ 59090792Sgshapiro if (e->e_quarmsg != NULL) 59190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n", 59290792Sgshapiro denlstring(e->e_quarmsg, true, false)); 59390792Sgshapiro#endif /* _FFR_QUARANTINE */ 59438032Speter 59538032Speter /* message from envelope, if it exists */ 59638032Speter if (e->e_message != NULL) 59790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", 59890792Sgshapiro denlstring(e->e_message, true, false)); 59938032Speter 60038032Speter /* send various flag bits through */ 60138032Speter p = buf; 60238032Speter if (bitset(EF_WARNING, e->e_flags)) 60338032Speter *p++ = 'w'; 60438032Speter if (bitset(EF_RESPONSE, e->e_flags)) 60538032Speter *p++ = 'r'; 60638032Speter if (bitset(EF_HAS8BIT, e->e_flags)) 60738032Speter *p++ = '8'; 60838032Speter if (bitset(EF_DELETE_BCC, e->e_flags)) 60938032Speter *p++ = 'b'; 61038032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 61138032Speter *p++ = 'd'; 61238032Speter if (bitset(EF_NO_BODY_RETN, e->e_flags)) 61338032Speter *p++ = 'n'; 61490792Sgshapiro if (bitset(EF_SPLIT, e->e_flags)) 61590792Sgshapiro *p++ = 's'; 61638032Speter *p++ = '\0'; 61738032Speter if (buf[0] != '\0') 61890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf); 61938032Speter 62064562Sgshapiro /* save $={persistentMacros} macro values */ 62190792Sgshapiro queueup_macros(macid("{persistentMacros}"), tfp, e); 62238032Speter 62338032Speter /* output name of sender */ 62438032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 62538032Speter p = e->e_sender; 62638032Speter else 62738032Speter p = e->e_from.q_paddr; 62890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n", 62990792Sgshapiro denlstring(p, true, false)); 63038032Speter 63138032Speter /* output ESMTP-supplied "original" information */ 63238032Speter if (e->e_envid != NULL) 63390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n", 63490792Sgshapiro denlstring(e->e_envid, true, false)); 63538032Speter 63664562Sgshapiro /* output AUTH= parameter */ 63764562Sgshapiro if (e->e_auth_param != NULL) 63890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n", 63990792Sgshapiro denlstring(e->e_auth_param, true, false)); 64090792Sgshapiro if (e->e_dlvr_flag != 0) 64190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n", 64290792Sgshapiro (char) e->e_dlvr_flag, e->e_deliver_by); 64364562Sgshapiro 64438032Speter /* output list of recipient addresses */ 64538032Speter printctladdr(NULL, NULL); 64638032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 64738032Speter { 64864562Sgshapiro if (!QS_IS_UNDELIVERED(q->q_state)) 64938032Speter continue; 65064562Sgshapiro 65190792Sgshapiro /* message for this recipient, if it exists */ 65290792Sgshapiro if (q->q_message != NULL) 65390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", 65490792Sgshapiro denlstring(q->q_message, true, 65590792Sgshapiro false)); 65690792Sgshapiro 65738032Speter printctladdr(q, tfp); 65838032Speter if (q->q_orcpt != NULL) 65990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n", 66090792Sgshapiro denlstring(q->q_orcpt, true, 66190792Sgshapiro false)); 66290792Sgshapiro if (q->q_finalrcpt != NULL) 66390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n", 66490792Sgshapiro denlstring(q->q_finalrcpt, true, 66590792Sgshapiro false)); 66690792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R'); 66738032Speter if (bitset(QPRIMARY, q->q_flags)) 66890792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P'); 66938032Speter if (bitset(QHASNOTIFY, q->q_flags)) 67090792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N'); 67138032Speter if (bitset(QPINGONSUCCESS, q->q_flags)) 67290792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S'); 67338032Speter if (bitset(QPINGONFAILURE, q->q_flags)) 67490792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F'); 67538032Speter if (bitset(QPINGONDELAY, q->q_flags)) 67690792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D'); 67771345Sgshapiro if (q->q_alias != NULL && 67871345Sgshapiro bitset(QALIAS, q->q_alias->q_flags)) 67990792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A'); 68090792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':'); 68190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n", 68290792Sgshapiro denlstring(q->q_paddr, true, false)); 68338032Speter if (announce) 68438032Speter { 68590792Sgshapiro char *tag = "queued"; 68690792Sgshapiro 68790792Sgshapiro#if _FFR_QUARANTINE 68890792Sgshapiro if (e->e_quarmsg != NULL) 68990792Sgshapiro tag = "quarantined"; 69090792Sgshapiro#endif /* _FFR_QUARANTINE */ 69190792Sgshapiro 69238032Speter e->e_to = q->q_paddr; 69390792Sgshapiro message(tag); 69438032Speter if (LogLevel > 8) 69564562Sgshapiro logdelivery(q->q_mailer, NULL, q->q_status, 69690792Sgshapiro tag, NULL, (time_t) 0, e); 69738032Speter e->e_to = NULL; 69838032Speter } 69938032Speter if (tTd(40, 1)) 70038032Speter { 70190792Sgshapiro sm_dprintf("queueing "); 70290792Sgshapiro printaddr(q, false); 70338032Speter } 70438032Speter } 70538032Speter 70638032Speter /* 70738032Speter ** Output headers for this message. 70838032Speter ** Expand macros completely here. Queue run will deal with 70938032Speter ** everything as absolute headers. 71038032Speter ** All headers that must be relative to the recipient 71138032Speter ** can be cracked later. 71238032Speter ** We set up a "null mailer" -- i.e., a mailer that will have 71338032Speter ** no effect on the addresses as they are output. 71438032Speter */ 71538032Speter 71664562Sgshapiro memset((char *) &nullmailer, '\0', sizeof nullmailer); 71738032Speter nullmailer.m_re_rwset = nullmailer.m_rh_rwset = 71838032Speter nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; 71938032Speter nullmailer.m_eol = "\n"; 72064562Sgshapiro memset(&mcibuf, '\0', sizeof mcibuf); 72138032Speter mcibuf.mci_mailer = &nullmailer; 72238032Speter mcibuf.mci_out = tfp; 72338032Speter 72490792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', "\201f"); 72538032Speter for (h = e->e_header; h != NULL; h = h->h_link) 72638032Speter { 72743730Speter if (h->h_value == NULL) 72838032Speter continue; 72938032Speter 73038032Speter /* don't output resent headers on non-resent messages */ 73164562Sgshapiro if (bitset(H_RESENT, h->h_flags) && 73264562Sgshapiro !bitset(EF_RESENT, e->e_flags)) 73338032Speter continue; 73438032Speter 73538032Speter /* expand macros; if null, don't output header at all */ 73638032Speter if (bitset(H_DEFAULT, h->h_flags)) 73738032Speter { 73838032Speter (void) expand(h->h_value, buf, sizeof buf, e); 73938032Speter if (buf[0] == '\0') 74038032Speter continue; 74138032Speter } 74238032Speter 74338032Speter /* output this header */ 74490792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?"); 74538032Speter 74664562Sgshapiro /* output conditional macro if present */ 74764562Sgshapiro if (h->h_macro != '\0') 74838032Speter { 74964562Sgshapiro if (bitset(0200, h->h_macro)) 75090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, 75190792Sgshapiro "${%s}", 75290792Sgshapiro macname(bitidx(h->h_macro))); 75364562Sgshapiro else 75490792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, 75590792Sgshapiro "$%c", h->h_macro); 75664562Sgshapiro } 75764562Sgshapiro else if (!bitzerop(h->h_mflags) && 75864562Sgshapiro bitset(H_CHECK|H_ACHECK, h->h_flags)) 75964562Sgshapiro { 76038032Speter int j; 76138032Speter 76264562Sgshapiro /* if conditional, output the set of conditions */ 76338032Speter for (j = '\0'; j <= '\177'; j++) 76438032Speter if (bitnset(j, h->h_mflags)) 76590792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 76690792Sgshapiro j); 76738032Speter } 76890792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?'); 76938032Speter 77038032Speter /* output the header: expand macros, convert addresses */ 77164562Sgshapiro if (bitset(H_DEFAULT, h->h_flags) && 77264562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 77338032Speter { 77490792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n", 77590792Sgshapiro h->h_field, 77690792Sgshapiro denlstring(buf, false, true)); 77738032Speter } 77864562Sgshapiro else if (bitset(H_FROM|H_RCPT, h->h_flags) && 77964562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 78038032Speter { 78138032Speter bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 78290792Sgshapiro SM_FILE_T *savetrace = TrafficLogFile; 78338032Speter 78438032Speter TrafficLogFile = NULL; 78538032Speter 78638032Speter if (bitset(H_FROM, h->h_flags)) 78790792Sgshapiro oldstyle = false; 78838032Speter 78938032Speter commaize(h, h->h_value, oldstyle, &mcibuf, e); 79038032Speter 79138032Speter TrafficLogFile = savetrace; 79238032Speter } 79338032Speter else 79438032Speter { 79590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n", 79690792Sgshapiro h->h_field, 79790792Sgshapiro denlstring(h->h_value, false, 79890792Sgshapiro true)); 79938032Speter } 80038032Speter } 80138032Speter 80238032Speter /* 80338032Speter ** Clean up. 80438032Speter ** 80538032Speter ** Write a terminator record -- this is to prevent 80638032Speter ** scurrilous crackers from appending any data. 80738032Speter */ 80838032Speter 80990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n"); 81038032Speter 81190792Sgshapiro if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 || 81290792Sgshapiro ((SuperSafe == SAFE_REALLY || 81390792Sgshapiro (SuperSafe == SAFE_INTERACTIVE && msync)) && 81490792Sgshapiro fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) || 81590792Sgshapiro sm_io_error(tfp)) 81638032Speter { 81738032Speter if (newid) 81838032Speter syserr("!552 Error writing control file %s", tf); 81938032Speter else 82038032Speter syserr("!452 Error writing control file %s", tf); 82138032Speter } 82238032Speter 82338032Speter if (!newid) 82438032Speter { 82590792Sgshapiro#if _FFR_QUARANTINE 82690792Sgshapiro char new = queue_letter(e, ANYQFL_LETTER); 82790792Sgshapiro#endif /* _FFR_QUARANTINE */ 82890792Sgshapiro 82990792Sgshapiro /* rename (locked) tf to be (locked) [qh]f */ 83090792Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), 83190792Sgshapiro sizeof qf); 83238032Speter if (rename(tf, qf) < 0) 83338032Speter syserr("cannot rename(%s, %s), uid=%d", 83498121Sgshapiro tf, qf, (int) geteuid()); 83590792Sgshapiro# if _FFR_QUARANTINE 83690792Sgshapiro else 83790792Sgshapiro { 83890792Sgshapiro /* 83990792Sgshapiro ** Check if type has changed and only 84090792Sgshapiro ** remove the old item if the rename above 84190792Sgshapiro ** succeeded. 84290792Sgshapiro */ 84390792Sgshapiro 84490792Sgshapiro if (e->e_qfletter != '\0' && 84590792Sgshapiro e->e_qfletter != new) 84690792Sgshapiro { 84790792Sgshapiro if (tTd(40, 5)) 84890792Sgshapiro { 84990792Sgshapiro sm_dprintf("type changed from %c to %c\n", 85090792Sgshapiro e->e_qfletter, new); 85190792Sgshapiro } 85290792Sgshapiro 85390792Sgshapiro if (unlink(queuename(e, e->e_qfletter)) < 0) 85490792Sgshapiro { 85590792Sgshapiro /* XXX: something more drastic? */ 85690792Sgshapiro if (LogLevel > 0) 85790792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 85890792Sgshapiro "queueup: unlink(%s) failed: %s", 85990792Sgshapiro queuename(e, e->e_qfletter), 86090792Sgshapiro sm_errstring(errno)); 86190792Sgshapiro } 86290792Sgshapiro } 86390792Sgshapiro } 86490792Sgshapiro e->e_qfletter = new; 86590792Sgshapiro# endif /* _FFR_QUARANTINE */ 86690792Sgshapiro 86764562Sgshapiro /* 86890792Sgshapiro ** fsync() after renaming to make sure metadata is 86990792Sgshapiro ** written to disk on filesystems in which renames are 87090792Sgshapiro ** not guaranteed. 87164562Sgshapiro */ 87264562Sgshapiro 87390792Sgshapiro if (SuperSafe != SAFE_NO) 87490792Sgshapiro { 87590792Sgshapiro /* for softupdates */ 87690792Sgshapiro if (tfd >= 0 && fsync(tfd) < 0) 87790792Sgshapiro { 87890792Sgshapiro syserr("!queueup: cannot fsync queue temp file %s", 87990792Sgshapiro tf); 88090792Sgshapiro } 88190792Sgshapiro SYNC_DIR(qf, true); 88290792Sgshapiro } 88364562Sgshapiro 88490792Sgshapiro /* close and unlock old (locked) queue file */ 88538032Speter if (e->e_lockfp != NULL) 88690792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 88738032Speter e->e_lockfp = tfp; 88890792Sgshapiro 88990792Sgshapiro /* save log info */ 89090792Sgshapiro if (LogLevel > 79) 89190792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf); 89238032Speter } 89338032Speter else 89490792Sgshapiro { 89590792Sgshapiro /* save log info */ 89690792Sgshapiro if (LogLevel > 79) 89790792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf); 89890792Sgshapiro 89990792Sgshapiro#if _FFR_QUARANTINE 90090792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 90190792Sgshapiro#endif /* _FFR_QUARANTINE */ 90290792Sgshapiro } 90390792Sgshapiro 90438032Speter errno = 0; 90538032Speter e->e_flags |= EF_INQUEUE; 90638032Speter 90738032Speter if (tTd(40, 1)) 90890792Sgshapiro sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id); 90938032Speter return; 91038032Speter} 91138032Speter 91290792Sgshapiro/* 91390792Sgshapiro** PRINTCTLADDR -- print control address to file. 91490792Sgshapiro** 91590792Sgshapiro** Parameters: 91690792Sgshapiro** a -- address. 91790792Sgshapiro** tfp -- file pointer. 91890792Sgshapiro** 91990792Sgshapiro** Returns: 92090792Sgshapiro** none. 92190792Sgshapiro** 92290792Sgshapiro** Side Effects: 92390792Sgshapiro** The control address (if changed) is printed to the file. 92490792Sgshapiro** The last control address and uid are saved. 92590792Sgshapiro*/ 92690792Sgshapiro 92764562Sgshapirostatic void 92838032Speterprintctladdr(a, tfp) 92938032Speter register ADDRESS *a; 93090792Sgshapiro SM_FILE_T *tfp; 93138032Speter{ 93264562Sgshapiro char *user; 93338032Speter register ADDRESS *q; 93438032Speter uid_t uid; 93538032Speter gid_t gid; 93638032Speter static ADDRESS *lastctladdr = NULL; 93738032Speter static uid_t lastuid; 93838032Speter 93938032Speter /* initialization */ 94038032Speter if (a == NULL || a->q_alias == NULL || tfp == NULL) 94138032Speter { 94238032Speter if (lastctladdr != NULL && tfp != NULL) 94390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n"); 94438032Speter lastctladdr = NULL; 94538032Speter lastuid = 0; 94638032Speter return; 94738032Speter } 94838032Speter 94938032Speter /* find the active uid */ 95038032Speter q = getctladdr(a); 95138032Speter if (q == NULL) 95238032Speter { 95364562Sgshapiro user = NULL; 95438032Speter uid = 0; 95538032Speter gid = 0; 95638032Speter } 95738032Speter else 95838032Speter { 95964562Sgshapiro user = q->q_ruser != NULL ? q->q_ruser : q->q_user; 96038032Speter uid = q->q_uid; 96138032Speter gid = q->q_gid; 96238032Speter } 96338032Speter a = a->q_alias; 96438032Speter 96538032Speter /* check to see if this is the same as last time */ 96638032Speter if (lastctladdr != NULL && uid == lastuid && 96738032Speter strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) 96838032Speter return; 96938032Speter lastuid = uid; 97038032Speter lastctladdr = a; 97138032Speter 97264562Sgshapiro if (uid == 0 || user == NULL || user[0] == '\0') 97390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C"); 97438032Speter else 97590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld", 97690792Sgshapiro denlstring(user, true, false), (long) uid, 97790792Sgshapiro (long) gid); 97890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n", 97990792Sgshapiro denlstring(a->q_paddr, true, false)); 98038032Speter} 98190792Sgshapiro 98290792Sgshapiro/* 98390792Sgshapiro** RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process 98490792Sgshapiro** 98590792Sgshapiro** This propagates the signal to the child processes that are queue 98690792Sgshapiro** runners. This is for a queue runner "cleanup". After all of the 98790792Sgshapiro** child queue runner processes are signaled (it should be SIGTERM 98890792Sgshapiro** being the sig) then the old signal handler (Oldsh) is called 98990792Sgshapiro** to handle any cleanup set for this process (provided it is not 99090792Sgshapiro** SIG_DFL or SIG_IGN). The signal may not be handled immediately 99190792Sgshapiro** if the BlockOldsh flag is set. If the current process doesn't 99290792Sgshapiro** have a parent then handle the signal immediately, regardless of 99390792Sgshapiro** BlockOldsh. 99490792Sgshapiro** 99590792Sgshapiro** Parameters: 99690792Sgshapiro** sig -- the signal number being sent 99790792Sgshapiro** 99890792Sgshapiro** Returns: 99990792Sgshapiro** none. 100090792Sgshapiro** 100190792Sgshapiro** Side Effects: 100290792Sgshapiro** Sets the NoMoreRunners boolean to true to stop more runners 100390792Sgshapiro** from being started in runqueue(). 100490792Sgshapiro** 100590792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 100690792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 100790792Sgshapiro** DOING. 100890792Sgshapiro*/ 100990792Sgshapiro 101090792Sgshapirostatic bool volatile NoMoreRunners = false; 101190792Sgshapirostatic sigfunc_t Oldsh_term = SIG_DFL; 101290792Sgshapirostatic sigfunc_t Oldsh_hup = SIG_DFL; 101390792Sgshapirostatic sigfunc_t volatile Oldsh = SIG_DFL; 101490792Sgshapirostatic bool BlockOldsh = false; 101590792Sgshapirostatic int volatile Oldsig = 0; 101690792Sgshapirostatic SIGFUNC_DECL runners_sigterm __P((int)); 101790792Sgshapirostatic SIGFUNC_DECL runners_sighup __P((int)); 101890792Sgshapiro 101990792Sgshapirostatic SIGFUNC_DECL 102090792Sgshapirorunners_sigterm(sig) 102190792Sgshapiro int sig; 102290792Sgshapiro{ 102390792Sgshapiro int save_errno = errno; 102490792Sgshapiro 102590792Sgshapiro FIX_SYSV_SIGNAL(sig, runners_sigterm); 102690792Sgshapiro errno = save_errno; 102790792Sgshapiro CHECK_CRITICAL(sig); 102890792Sgshapiro NoMoreRunners = true; 102990792Sgshapiro Oldsh = Oldsh_term; 103090792Sgshapiro Oldsig = sig; 103190792Sgshapiro proc_list_signal(PROC_QUEUE, sig); 103290792Sgshapiro 103390792Sgshapiro if (!BlockOldsh || getppid() <= 1) 103490792Sgshapiro { 103590792Sgshapiro /* Check that a valid 'old signal handler' is callable */ 103690792Sgshapiro if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN && 103790792Sgshapiro Oldsh_term != runners_sigterm) 103890792Sgshapiro (*Oldsh_term)(sig); 103990792Sgshapiro } 104090792Sgshapiro errno = save_errno; 104190792Sgshapiro return SIGFUNC_RETURN; 104290792Sgshapiro} 104390792Sgshapiro/* 104490792Sgshapiro** RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process 104590792Sgshapiro** 104690792Sgshapiro** This propagates the signal to the child processes that are queue 104790792Sgshapiro** runners. This is for a queue runner "cleanup". After all of the 104890792Sgshapiro** child queue runner processes are signaled (it should be SIGHUP 104990792Sgshapiro** being the sig) then the old signal handler (Oldsh) is called to 105090792Sgshapiro** handle any cleanup set for this process (provided it is not SIG_DFL 105190792Sgshapiro** or SIG_IGN). The signal may not be handled immediately if the 105290792Sgshapiro** BlockOldsh flag is set. If the current process doesn't have 105390792Sgshapiro** a parent then handle the signal immediately, regardless of 105490792Sgshapiro** BlockOldsh. 105590792Sgshapiro** 105690792Sgshapiro** Parameters: 105790792Sgshapiro** sig -- the signal number being sent 105890792Sgshapiro** 105990792Sgshapiro** Returns: 106090792Sgshapiro** none. 106190792Sgshapiro** 106290792Sgshapiro** Side Effects: 106390792Sgshapiro** Sets the NoMoreRunners boolean to true to stop more runners 106490792Sgshapiro** from being started in runqueue(). 106590792Sgshapiro** 106690792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 106790792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 106890792Sgshapiro** DOING. 106990792Sgshapiro*/ 107090792Sgshapiro 107190792Sgshapirostatic SIGFUNC_DECL 107290792Sgshapirorunners_sighup(sig) 107390792Sgshapiro int sig; 107490792Sgshapiro{ 107590792Sgshapiro int save_errno = errno; 107690792Sgshapiro 107790792Sgshapiro FIX_SYSV_SIGNAL(sig, runners_sighup); 107890792Sgshapiro errno = save_errno; 107990792Sgshapiro CHECK_CRITICAL(sig); 108090792Sgshapiro NoMoreRunners = true; 108190792Sgshapiro Oldsh = Oldsh_hup; 108290792Sgshapiro Oldsig = sig; 108390792Sgshapiro proc_list_signal(PROC_QUEUE, sig); 108490792Sgshapiro 108590792Sgshapiro if (!BlockOldsh || getppid() <= 1) 108690792Sgshapiro { 108790792Sgshapiro /* Check that a valid 'old signal handler' is callable */ 108890792Sgshapiro if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN && 108990792Sgshapiro Oldsh_hup != runners_sighup) 109090792Sgshapiro (*Oldsh_hup)(sig); 109190792Sgshapiro } 109290792Sgshapiro errno = save_errno; 109390792Sgshapiro return SIGFUNC_RETURN; 109490792Sgshapiro} 109590792Sgshapiro/* 109690792Sgshapiro** MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart 109790792Sgshapiro** 109890792Sgshapiro** Sets a workgroup for restarting. 109990792Sgshapiro** 110090792Sgshapiro** Parameters: 110190792Sgshapiro** wgrp -- the work group id to restart. 110290792Sgshapiro** reason -- why (signal?), -1 to turn off restart 110390792Sgshapiro** 110490792Sgshapiro** Returns: 110590792Sgshapiro** none. 110690792Sgshapiro** 110790792Sgshapiro** Side effects: 110890792Sgshapiro** May set global RestartWorkGroup to true. 110990792Sgshapiro** 111090792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 111190792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 111290792Sgshapiro** DOING. 111390792Sgshapiro*/ 111490792Sgshapiro 111590792Sgshapirovoid 111690792Sgshapiromark_work_group_restart(wgrp, reason) 111790792Sgshapiro int wgrp; 111890792Sgshapiro int reason; 111990792Sgshapiro{ 112090792Sgshapiro if (wgrp < 0 || wgrp > NumWorkGroups) 112190792Sgshapiro return; 112290792Sgshapiro 112390792Sgshapiro WorkGrp[wgrp].wg_restart = reason; 112490792Sgshapiro if (reason >= 0) 112590792Sgshapiro RestartWorkGroup = true; 112690792Sgshapiro} 112790792Sgshapiro/* 112890792Sgshapiro** RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart 112990792Sgshapiro** 113090792Sgshapiro** Restart any workgroup marked as needing a restart provided more 113190792Sgshapiro** runners are allowed. 113290792Sgshapiro** 113390792Sgshapiro** Parameters: 113490792Sgshapiro** none. 113590792Sgshapiro** 113690792Sgshapiro** Returns: 113790792Sgshapiro** none. 113890792Sgshapiro** 113990792Sgshapiro** Side effects: 114090792Sgshapiro** Sets global RestartWorkGroup to false. 114190792Sgshapiro*/ 114290792Sgshapiro 114390792Sgshapirovoid 114490792Sgshapirorestart_marked_work_groups() 114590792Sgshapiro{ 114690792Sgshapiro int i; 114790792Sgshapiro int wasblocked; 114890792Sgshapiro 114990792Sgshapiro if (NoMoreRunners) 115090792Sgshapiro return; 115190792Sgshapiro 115290792Sgshapiro /* Block SIGCHLD so reapchild() doesn't mess with us */ 115390792Sgshapiro wasblocked = sm_blocksignal(SIGCHLD); 115490792Sgshapiro 115590792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 115690792Sgshapiro { 115790792Sgshapiro if (WorkGrp[i].wg_restart >= 0) 115890792Sgshapiro { 115990792Sgshapiro if (LogLevel > 8) 116090792Sgshapiro sm_syslog(LOG_ERR, NOQID, 116190792Sgshapiro "restart queue runner=%d due to signal 0x%x", 116290792Sgshapiro i, WorkGrp[i].wg_restart); 116390792Sgshapiro restart_work_group(i); 116490792Sgshapiro } 116590792Sgshapiro } 116690792Sgshapiro RestartWorkGroup = false; 116790792Sgshapiro 116890792Sgshapiro if (wasblocked == 0) 116990792Sgshapiro (void) sm_releasesignal(SIGCHLD); 117090792Sgshapiro} 117190792Sgshapiro/* 117290792Sgshapiro** RESTART_WORK_GROUP -- restart a specific work group 117390792Sgshapiro** 117490792Sgshapiro** Restart a specific workgroup provided more runners are allowed. 117590792Sgshapiro** If the requested work group has been restarted too many times log 117690792Sgshapiro** this and refuse to restart. 117790792Sgshapiro** 117890792Sgshapiro** Parameters: 117990792Sgshapiro** wgrp -- the work group id to restart 118090792Sgshapiro** 118190792Sgshapiro** Returns: 118290792Sgshapiro** none. 118390792Sgshapiro** 118490792Sgshapiro** Side Effects: 118590792Sgshapiro** starts another process doing the work of wgrp 118690792Sgshapiro*/ 118790792Sgshapiro 118890792Sgshapiro#define MAX_PERSIST_RESTART 10 /* max allowed number of restarts */ 118990792Sgshapiro 119090792Sgshapirostatic void 119190792Sgshapirorestart_work_group(wgrp) 119290792Sgshapiro int wgrp; 119390792Sgshapiro{ 119490792Sgshapiro if (NoMoreRunners || 119590792Sgshapiro wgrp < 0 || wgrp > NumWorkGroups) 119690792Sgshapiro return; 119790792Sgshapiro 119890792Sgshapiro WorkGrp[wgrp].wg_restart = -1; 119990792Sgshapiro if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART) 120090792Sgshapiro { 120190792Sgshapiro /* avoid overflow; increment here */ 120290792Sgshapiro WorkGrp[wgrp].wg_restartcnt++; 120390792Sgshapiro (void) run_work_group(wgrp, true, false, true, true); 120490792Sgshapiro } 120590792Sgshapiro else 120690792Sgshapiro { 120790792Sgshapiro sm_syslog(LOG_ERR, NOQID, 120890792Sgshapiro "ERROR: persistent queue runner=%d restarted too many times, queue runner lost", 120990792Sgshapiro wgrp); 121090792Sgshapiro } 121190792Sgshapiro} 121290792Sgshapiro/* 121390792Sgshapiro** SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group. 121490792Sgshapiro** 121590792Sgshapiro** Parameters: 121690792Sgshapiro** runall -- schedule even if individual bit is not set. 121790792Sgshapiro** wgrp -- the work group id to schedule. 121894334Sgshapiro** didit -- the queue run was performed for this work group. 121990792Sgshapiro** 122090792Sgshapiro** Returns: 122190792Sgshapiro** nothing 122290792Sgshapiro*/ 122390792Sgshapiro 122490792Sgshapiro#define INCR_MOD(v, m) if (++v >= m) \ 122590792Sgshapiro v = 0; \ 122690792Sgshapiro else 122790792Sgshapiro 122890792Sgshapirostatic void 122994334Sgshapiroschedule_queue_runs(runall, wgrp, didit) 123090792Sgshapiro bool runall; 123190792Sgshapiro int wgrp; 123294334Sgshapiro bool didit; 123390792Sgshapiro{ 123490792Sgshapiro int qgrp, cgrp, endgrp; 123594334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 123694334Sgshapiro time_t lastsched; 123794334Sgshapiro bool sched; 123894334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 123994334Sgshapiro time_t now; 124094334Sgshapiro time_t minqintvl; 124190792Sgshapiro 124290792Sgshapiro /* 124390792Sgshapiro ** This is a bit ugly since we have to duplicate the 124490792Sgshapiro ** code that "walks" through a work queue group. 124590792Sgshapiro */ 124690792Sgshapiro 124794334Sgshapiro now = curtime(); 124894334Sgshapiro minqintvl = 0; 124990792Sgshapiro cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp; 125090792Sgshapiro do 125190792Sgshapiro { 125290792Sgshapiro time_t qintvl; 125390792Sgshapiro 125494334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 125594334Sgshapiro lastsched = 0; 125694334Sgshapiro sched = false; 125794334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 125890792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index; 125990792Sgshapiro if (Queue[qgrp]->qg_queueintvl > 0) 126090792Sgshapiro qintvl = Queue[qgrp]->qg_queueintvl; 126190792Sgshapiro else if (QueueIntvl > 0) 126290792Sgshapiro qintvl = QueueIntvl; 126390792Sgshapiro else 126490792Sgshapiro qintvl = (time_t) 0; 126590792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 126694334Sgshapiro lastsched = Queue[qgrp]->qg_nextrun; 126794334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 126894334Sgshapiro if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0) 126994334Sgshapiro { 127094334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 127194334Sgshapiro sched = true; 127294334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 127394334Sgshapiro if (minqintvl == 0 || qintvl < minqintvl) 127494334Sgshapiro minqintvl = qintvl; 127594334Sgshapiro 127694334Sgshapiro /* 127794334Sgshapiro ** Only set a new time if a queue run was performed 127894334Sgshapiro ** for this queue group. If the queue was not run, 127994334Sgshapiro ** we could starve it by setting a new time on each 128094334Sgshapiro ** call. 128194334Sgshapiro */ 128294334Sgshapiro 128394334Sgshapiro if (didit) 128494334Sgshapiro Queue[qgrp]->qg_nextrun += qintvl; 128594334Sgshapiro } 128694334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 128790792Sgshapiro if (tTd(69, 10)) 128890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 128994334Sgshapiro "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d", 129090792Sgshapiro wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl, 129194334Sgshapiro QueueIntvl, runall, lastsched, 129294334Sgshapiro Queue[qgrp]->qg_nextrun, sched); 129390792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 129490792Sgshapiro INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp); 129590792Sgshapiro } while (endgrp != cgrp); 129694334Sgshapiro if (minqintvl > 0) 129794334Sgshapiro (void) sm_setevent(minqintvl, runqueueevent, 0); 129890792Sgshapiro} 129994334Sgshapiro 130094334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA 130190792Sgshapiro/* 130294334Sgshapiro** CHECKQUEUERUNNER -- check whether a queue group hasn't been run. 130394334Sgshapiro** 130494334Sgshapiro** Use this if events may get lost and hence queue runners may not 130594334Sgshapiro** be started and mail will pile up in a queue. 130694334Sgshapiro** 130794334Sgshapiro** Parameters: 130894334Sgshapiro** none. 130994334Sgshapiro** 131094334Sgshapiro** Returns: 131194334Sgshapiro** true if a queue run is necessary. 131294334Sgshapiro** 131394334Sgshapiro** Side Effects: 131494334Sgshapiro** may schedule a queue run. 131594334Sgshapiro*/ 131694334Sgshapiro 131794334Sgshapirobool 131894334Sgshapirocheckqueuerunner() 131994334Sgshapiro{ 132094334Sgshapiro int qgrp; 132194334Sgshapiro time_t now, minqintvl; 132294334Sgshapiro 132394334Sgshapiro now = curtime(); 132494334Sgshapiro minqintvl = 0; 132594334Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 132694334Sgshapiro { 132794334Sgshapiro time_t qintvl; 132894334Sgshapiro 132994334Sgshapiro if (Queue[qgrp]->qg_queueintvl > 0) 133094334Sgshapiro qintvl = Queue[qgrp]->qg_queueintvl; 133194334Sgshapiro else if (QueueIntvl > 0) 133294334Sgshapiro qintvl = QueueIntvl; 133394334Sgshapiro else 133494334Sgshapiro qintvl = (time_t) 0; 133594334Sgshapiro if (Queue[qgrp]->qg_nextrun <= now - qintvl) 133694334Sgshapiro { 133794334Sgshapiro if (minqintvl == 0 || qintvl < minqintvl) 133894334Sgshapiro minqintvl = qintvl; 133994334Sgshapiro if (LogLevel > 1) 134094334Sgshapiro sm_syslog(LOG_WARNING, NOQID, 134194334Sgshapiro "checkqueuerunner: queue %d should have been run at %s, queue interval %ld", 134294334Sgshapiro qgrp, 134394334Sgshapiro arpadate(ctime(&Queue[qgrp]->qg_nextrun)), 134494334Sgshapiro qintvl); 134594334Sgshapiro } 134694334Sgshapiro } 134794334Sgshapiro if (minqintvl > 0) 134894334Sgshapiro { 134994334Sgshapiro (void) sm_setevent(minqintvl, runqueueevent, 0); 135094334Sgshapiro return true; 135194334Sgshapiro } 135294334Sgshapiro return false; 135394334Sgshapiro} 135494334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */ 135594334Sgshapiro 135694334Sgshapiro/* 135738032Speter** RUNQUEUE -- run the jobs in the queue. 135838032Speter** 135938032Speter** Gets the stuff out of the queue in some presumably logical 136038032Speter** order and processes them. 136138032Speter** 136238032Speter** Parameters: 136390792Sgshapiro** forkflag -- true if the queue scanning should be done in 136438032Speter** a child process. We double-fork so it is not our 136538032Speter** child and we don't have to clean up after it. 136690792Sgshapiro** false can be ignored if we have multiple queues. 136790792Sgshapiro** verbose -- if true, print out status information. 136890792Sgshapiro** persistent -- persistent queue runner? 136990792Sgshapiro** runall -- run all groups or only a subset (DoQueueRun)? 137038032Speter** 137138032Speter** Returns: 137290792Sgshapiro** true if the queue run successfully began. 137338032Speter** 137438032Speter** Side Effects: 137590792Sgshapiro** runs things in the mail queue using run_work_group(). 137690792Sgshapiro** maybe schedules next queue run. 137738032Speter*/ 137838032Speter 137964562Sgshapirostatic ENVELOPE QueueEnvelope; /* the queue run envelope */ 138064562Sgshapirostatic time_t LastQueueTime = 0; /* last time a queue ID assigned */ 138164562Sgshapirostatic pid_t LastQueuePid = -1; /* last PID which had a queue ID */ 138238032Speter 138364562Sgshapiro/* values for qp_supdirs */ 138464562Sgshapiro#define QP_NOSUB 0x0000 /* No subdirectories */ 138564562Sgshapiro#define QP_SUBDF 0x0001 /* "df" subdirectory */ 138664562Sgshapiro#define QP_SUBQF 0x0002 /* "qf" subdirectory */ 138764562Sgshapiro#define QP_SUBXF 0x0004 /* "xf" subdirectory */ 138864562Sgshapiro 138938032Speterbool 139090792Sgshapirorunqueue(forkflag, verbose, persistent, runall) 139138032Speter bool forkflag; 139238032Speter bool verbose; 139390792Sgshapiro bool persistent; 139490792Sgshapiro bool runall; 139538032Speter{ 139664562Sgshapiro int i; 139790792Sgshapiro bool ret = true; 139864562Sgshapiro static int curnum = 0; 139990792Sgshapiro sigfunc_t cursh; 140090792Sgshapiro#if SM_HEAP_CHECK 140190792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 140264562Sgshapiro 140390792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 140490792Sgshapiro { 140590792Sgshapiro oldgroup = sm_heap_group(); 140690792Sgshapiro sm_heap_newgroup(); 140790792Sgshapiro sm_dprintf("runqueue() heap group #%d\n", sm_heap_group()); 140890792Sgshapiro } 140990792Sgshapiro#endif /* SM_HEAP_CHECK */ 141071345Sgshapiro 141190792Sgshapiro /* queue run has been started, don't do any more this time */ 141294334Sgshapiro DoQueueRun = false; 141371345Sgshapiro 141490792Sgshapiro /* more than one queue or more than one directory per queue */ 141590792Sgshapiro if (!forkflag && !verbose && 141690792Sgshapiro (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 || 141790792Sgshapiro WorkGrp[0].wg_numqgrp > 1)) 141890792Sgshapiro forkflag = true; 141964562Sgshapiro 142090792Sgshapiro /* 142190792Sgshapiro ** For controlling queue runners via signals sent to this process. 142290792Sgshapiro ** Oldsh* will get called too by runners_sig* (if it is not SIG_IGN 142390792Sgshapiro ** or SIG_DFL) to preserve cleanup behavior. Now that this process 142490792Sgshapiro ** will have children (and perhaps grandchildren) this handler will 142590792Sgshapiro ** be left in place. This is because this process, once it has 142690792Sgshapiro ** finished spinning off queue runners, may go back to doing something 142790792Sgshapiro ** else (like being a daemon). And we still want on a SIG{TERM,HUP} to 142890792Sgshapiro ** clean up the child queue runners. Only install 'runners_sig*' once 142990792Sgshapiro ** else we'll get stuck looping forever. 143090792Sgshapiro */ 143190792Sgshapiro 143290792Sgshapiro cursh = sm_signal(SIGTERM, runners_sigterm); 143390792Sgshapiro if (cursh != runners_sigterm) 143490792Sgshapiro Oldsh_term = cursh; 143590792Sgshapiro cursh = sm_signal(SIGHUP, runners_sighup); 143690792Sgshapiro if (cursh != runners_sighup) 143790792Sgshapiro Oldsh_hup = cursh; 143890792Sgshapiro 143990792Sgshapiro for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++) 144064562Sgshapiro { 144164562Sgshapiro /* 144290792Sgshapiro ** If MaxQueueChildren active then test whether the start 144390792Sgshapiro ** of the next queue group's additional queue runners (maximum) 144490792Sgshapiro ** will result in MaxQueueChildren being exceeded. 144590792Sgshapiro ** 144690792Sgshapiro ** Note: do not use continue; even though another workgroup 144790792Sgshapiro ** may have fewer queue runners, this would be "unfair", 144890792Sgshapiro ** i.e., this work group might "starve" then. 144964562Sgshapiro */ 145064562Sgshapiro 145190792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 145290792Sgshapiro if (tTd(69, 10)) 145390792Sgshapiro sm_syslog(LOG_INFO, NOQID, 145490792Sgshapiro "rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d", 145590792Sgshapiro curnum, MaxQueueChildren, CurRunners, 145690792Sgshapiro WorkGrp[curnum].wg_maxact); 145790792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 145890792Sgshapiro if (MaxQueueChildren > 0 && 145990792Sgshapiro CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren) 146090792Sgshapiro break; 146164562Sgshapiro 146264562Sgshapiro /* 146390792Sgshapiro ** Pick up where we left off (curnum), in case we 146490792Sgshapiro ** used up all the children last time without finishing. 146590792Sgshapiro ** This give a round-robin fairness to queue runs. 1466102528Sgshapiro ** 1467102528Sgshapiro ** Increment CurRunners before calling run_work_group() 1468102528Sgshapiro ** to avoid a "race condition" with proc_list_drop() which 1469102528Sgshapiro ** decrements CurRunners if the queue runners terminate. 1470102528Sgshapiro ** This actually doesn't cause any harm, but CurRunners 1471102528Sgshapiro ** might become negative which is at least confusing. 1472102528Sgshapiro ** 1473102528Sgshapiro ** Notice: CurRunners is an upper limit, in some cases 1474102528Sgshapiro ** (too few jobs in the queue) this value is larger than 1475102528Sgshapiro ** the actual number of queue runners. The discrepancy can 1476102528Sgshapiro ** increase if some queue runners "hang" for a long time. 147790792Sgshapiro */ 147890792Sgshapiro 1479102528Sgshapiro CurRunners += WorkGrp[curnum].wg_maxact; 148090792Sgshapiro ret = run_work_group(curnum, forkflag, verbose, persistent, 148190792Sgshapiro runall); 148290792Sgshapiro 148390792Sgshapiro /* 148464562Sgshapiro ** Failure means a message was printed for ETRN 148564562Sgshapiro ** and subsequent queues are likely to fail as well. 1486102528Sgshapiro ** Decrement CurRunners in that case because 1487102528Sgshapiro ** none have been started. 148864562Sgshapiro */ 148964562Sgshapiro 149064562Sgshapiro if (!ret) 1491102528Sgshapiro { 1492102528Sgshapiro CurRunners -= WorkGrp[curnum].wg_maxact; 149364562Sgshapiro break; 1494102528Sgshapiro } 149564562Sgshapiro 149690792Sgshapiro if (!persistent) 149794334Sgshapiro schedule_queue_runs(runall, curnum, true); 149890792Sgshapiro INCR_MOD(curnum, NumWorkGroups); 149964562Sgshapiro } 150090792Sgshapiro 150190792Sgshapiro /* schedule left over queue runs */ 150290792Sgshapiro if (i < NumWorkGroups && !NoMoreRunners && !persistent) 150390792Sgshapiro { 150490792Sgshapiro int h; 150590792Sgshapiro 150690792Sgshapiro for (h = curnum; i < NumWorkGroups; i++) 150790792Sgshapiro { 150894334Sgshapiro schedule_queue_runs(runall, h, false); 150990792Sgshapiro INCR_MOD(h, NumWorkGroups); 151090792Sgshapiro } 151190792Sgshapiro } 151290792Sgshapiro 151390792Sgshapiro 151490792Sgshapiro#if SM_HEAP_CHECK 151590792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 151690792Sgshapiro sm_heap_setgroup(oldgroup); 151790792Sgshapiro#endif /* SM_HEAP_CHECK */ 151864562Sgshapiro return ret; 151964562Sgshapiro} 152090792Sgshapiro/* 152190792Sgshapiro** RUNNER_WORK -- have a queue runner do its work 152264562Sgshapiro** 152390792Sgshapiro** Have a queue runner do its work a list of entries. 152490792Sgshapiro** When work isn't directly being done then this process can take a signal 152590792Sgshapiro** and terminate immediately (in a clean fashion of course). 152690792Sgshapiro** When work is directly being done, it's not to be interrupted 152790792Sgshapiro** immediately: the work should be allowed to finish at a clean point 152890792Sgshapiro** before termination (in a clean fashion of course). 152990792Sgshapiro** 153090792Sgshapiro** Parameters: 153190792Sgshapiro** e -- envelope. 153290792Sgshapiro** sequenceno -- 'th process to run WorkQ. 153390792Sgshapiro** didfork -- did the calling process fork()? 153490792Sgshapiro** skip -- process only each skip'th item. 153590792Sgshapiro** njobs -- number of jobs in WorkQ. 153690792Sgshapiro** 153790792Sgshapiro** Returns: 153890792Sgshapiro** none. 153990792Sgshapiro** 154090792Sgshapiro** Side Effects: 154190792Sgshapiro** runs things in the mail queue. 154290792Sgshapiro*/ 154390792Sgshapiro 154490792Sgshapiro/* Get new load average every 30 seconds. */ 154590792Sgshapiro#define GET_NEW_LA_TIME 30 154690792Sgshapiro 154790792Sgshapirostatic void 154890792Sgshapirorunner_work(e, sequenceno, didfork, skip, njobs) 154990792Sgshapiro register ENVELOPE *e; 155090792Sgshapiro int sequenceno; 155190792Sgshapiro bool didfork; 155290792Sgshapiro int skip; 155390792Sgshapiro int njobs; 155490792Sgshapiro{ 155590792Sgshapiro int n; 155690792Sgshapiro WORK *w; 155790792Sgshapiro time_t current_la_time, now; 155890792Sgshapiro 155990792Sgshapiro current_la_time = curtime(); 156090792Sgshapiro 156190792Sgshapiro /* 156290792Sgshapiro ** Here we temporarily block the second calling of the handlers. 156390792Sgshapiro ** This allows us to handle the signal without terminating in the 156490792Sgshapiro ** middle of direct work. If a signal does come, the test for 156590792Sgshapiro ** NoMoreRunners will find it. 156690792Sgshapiro */ 156790792Sgshapiro 156890792Sgshapiro BlockOldsh = true; 156990792Sgshapiro 157090792Sgshapiro /* process them once at a time */ 157190792Sgshapiro while (WorkQ != NULL) 157290792Sgshapiro { 157390792Sgshapiro#if SM_HEAP_CHECK 157490792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 157590792Sgshapiro 157690792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 157790792Sgshapiro { 157890792Sgshapiro oldgroup = sm_heap_group(); 157990792Sgshapiro sm_heap_newgroup(); 158090792Sgshapiro sm_dprintf("run_queue_group() heap group #%d\n", 158190792Sgshapiro sm_heap_group()); 158290792Sgshapiro } 158390792Sgshapiro#endif /* SM_HEAP_CHECK */ 158490792Sgshapiro 158590792Sgshapiro /* do no more work */ 158690792Sgshapiro if (NoMoreRunners) 158790792Sgshapiro { 158890792Sgshapiro /* Check that a valid signal handler is callable */ 158990792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 159090792Sgshapiro Oldsh != runners_sighup && 159190792Sgshapiro Oldsh != runners_sigterm) 159290792Sgshapiro (*Oldsh)(Oldsig); 159390792Sgshapiro break; 159490792Sgshapiro } 159590792Sgshapiro 159690792Sgshapiro w = WorkQ; /* assign current work item */ 159790792Sgshapiro 159890792Sgshapiro /* 159990792Sgshapiro ** Set the head of the WorkQ to the next work item. 160090792Sgshapiro ** It is set 'skip' ahead (the number of parallel queue 160190792Sgshapiro ** runners working on WorkQ together) since each runner 160290792Sgshapiro ** works on every 'skip'th (N-th) item. 160390792Sgshapiro */ 160490792Sgshapiro 160590792Sgshapiro for (n = 0; n < skip && WorkQ != NULL; n++) 160690792Sgshapiro WorkQ = WorkQ->w_next; 160790792Sgshapiro e->e_to = NULL; 160890792Sgshapiro 160990792Sgshapiro /* 161090792Sgshapiro ** Ignore jobs that are too expensive for the moment. 161190792Sgshapiro ** 161290792Sgshapiro ** Get new load average every GET_NEW_LA_TIME seconds. 161390792Sgshapiro */ 161490792Sgshapiro 161590792Sgshapiro now = curtime(); 161690792Sgshapiro if (current_la_time < now - GET_NEW_LA_TIME) 161790792Sgshapiro { 161890792Sgshapiro sm_getla(); 161990792Sgshapiro current_la_time = now; 162090792Sgshapiro } 162190792Sgshapiro if (shouldqueue(WkRecipFact, current_la_time)) 162290792Sgshapiro { 162390792Sgshapiro char *msg = "Aborting queue run: load average too high"; 162490792Sgshapiro 162590792Sgshapiro if (Verbose) 162690792Sgshapiro message("%s", msg); 162790792Sgshapiro if (LogLevel > 8) 162890792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 162990792Sgshapiro break; 163090792Sgshapiro } 163190792Sgshapiro if (shouldqueue(w->w_pri, w->w_ctime)) 163290792Sgshapiro { 163390792Sgshapiro if (Verbose) 163490792Sgshapiro message(EmptyString); 163590792Sgshapiro if (QueueSortOrder == QSO_BYPRIORITY) 163690792Sgshapiro { 163790792Sgshapiro if (Verbose) 163890792Sgshapiro message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", 163990792Sgshapiro qid_printqueue(w->w_qgrp, 164090792Sgshapiro w->w_qdir), 164190792Sgshapiro w->w_name + 2, sequenceno, 164290792Sgshapiro njobs); 164390792Sgshapiro if (LogLevel > 8) 164490792Sgshapiro sm_syslog(LOG_INFO, NOQID, 164590792Sgshapiro "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", 164690792Sgshapiro qid_printqueue(w->w_qgrp, 164790792Sgshapiro w->w_qdir), 164890792Sgshapiro w->w_name + 2, w->w_pri, 164990792Sgshapiro CurrentLA, sequenceno, 165090792Sgshapiro njobs); 165190792Sgshapiro break; 165290792Sgshapiro } 165390792Sgshapiro else if (Verbose) 165490792Sgshapiro message("Skipping %s/%s (sequence %d of %d)", 165590792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 165690792Sgshapiro w->w_name + 2, sequenceno, njobs); 165790792Sgshapiro } 165890792Sgshapiro else 165990792Sgshapiro { 166090792Sgshapiro if (Verbose) 166190792Sgshapiro { 166290792Sgshapiro message(EmptyString); 166390792Sgshapiro message("Running %s/%s (sequence %d of %d)", 166490792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 166590792Sgshapiro w->w_name + 2, sequenceno, njobs); 166690792Sgshapiro } 166790792Sgshapiro if (didfork && MaxQueueChildren > 0) 166890792Sgshapiro { 166990792Sgshapiro sm_blocksignal(SIGCHLD); 167090792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 167190792Sgshapiro } 167290792Sgshapiro if (tTd(63, 100)) 167390792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 167490792Sgshapiro "runqueue %s dowork(%s)", 167590792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 167690792Sgshapiro w->w_name + 2); 167790792Sgshapiro 167890792Sgshapiro (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2, 167990792Sgshapiro false, false, e); 168090792Sgshapiro errno = 0; 168190792Sgshapiro } 168290792Sgshapiro sm_free(w->w_name); /* XXX */ 168390792Sgshapiro if (w->w_host != NULL) 168490792Sgshapiro sm_free(w->w_host); /* XXX */ 168590792Sgshapiro sm_free((char *) w); /* XXX */ 168690792Sgshapiro sequenceno += skip; /* next sequence number */ 168790792Sgshapiro#if SM_HEAP_CHECK 168890792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 168990792Sgshapiro sm_heap_setgroup(oldgroup); 169090792Sgshapiro#endif /* SM_HEAP_CHECK */ 169190792Sgshapiro } 169290792Sgshapiro 169390792Sgshapiro BlockOldsh = false; 169490792Sgshapiro 169590792Sgshapiro /* check the signals didn't happen during the revert */ 169690792Sgshapiro if (NoMoreRunners) 169790792Sgshapiro { 169890792Sgshapiro /* Check that a valid signal handler is callable */ 169990792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 170090792Sgshapiro Oldsh != runners_sighup && Oldsh != runners_sigterm) 170190792Sgshapiro (*Oldsh)(Oldsig); 170290792Sgshapiro } 170390792Sgshapiro 170490792Sgshapiro Oldsh = SIG_DFL; /* after the NoMoreRunners check */ 170590792Sgshapiro} 170690792Sgshapiro/* 170790792Sgshapiro** RUN_WORK_GROUP -- run the jobs in a queue group from a work group. 170890792Sgshapiro** 170964562Sgshapiro** Gets the stuff out of the queue in some presumably logical 171064562Sgshapiro** order and processes them. 171164562Sgshapiro** 171264562Sgshapiro** Parameters: 171390792Sgshapiro** wgrp -- work group to process. 171490792Sgshapiro** forkflag -- true if the queue scanning should be done in 171564562Sgshapiro** a child process. We double-fork so it is not our 171664562Sgshapiro** child and we don't have to clean up after it. 171790792Sgshapiro** verbose -- if true, print out status information. 171890792Sgshapiro** persistent -- persistent queue runner? 171990792Sgshapiro** runall -- true: run all of the queue groups in this work group 172064562Sgshapiro** 172164562Sgshapiro** Returns: 172290792Sgshapiro** true if the queue run successfully began. 172364562Sgshapiro** 172464562Sgshapiro** Side Effects: 172564562Sgshapiro** runs things in the mail queue. 172664562Sgshapiro*/ 172764562Sgshapiro 172890792Sgshapiro/* Minimum sleep time for persistent queue runners */ 172990792Sgshapiro#define MIN_SLEEP_TIME 5 173090792Sgshapiro 173190792Sgshapirobool 173290792Sgshapirorun_work_group(wgrp, forkflag, verbose, persistent, runall) 173390792Sgshapiro int wgrp; 173464562Sgshapiro bool forkflag; 173564562Sgshapiro bool verbose; 173690792Sgshapiro bool persistent; 173790792Sgshapiro bool runall; 173864562Sgshapiro{ 173938032Speter register ENVELOPE *e; 174090792Sgshapiro int njobs, qdir; 174190792Sgshapiro int sequenceno = 1; 174290792Sgshapiro int qgrp, endgrp, h, i; 174394334Sgshapiro time_t current_la_time, now; 174490792Sgshapiro bool full, more; 174590792Sgshapiro SM_RPOOL_T *rpool; 174690792Sgshapiro extern void rmexpstab __P((void)); 174738032Speter extern ENVELOPE BlankEnvelope; 174890792Sgshapiro extern SIGFUNC_DECL reapchild __P((int)); 174938032Speter 175090792Sgshapiro if (wgrp < 0) 175190792Sgshapiro return false; 175290792Sgshapiro 175338032Speter /* 175438032Speter ** If no work will ever be selected, don't even bother reading 175538032Speter ** the queue. 175638032Speter */ 175738032Speter 175890792Sgshapiro sm_getla(); /* get load average */ 175938032Speter current_la_time = curtime(); 176038032Speter 176190792Sgshapiro if (!persistent && shouldqueue(WkRecipFact, current_la_time)) 176238032Speter { 176338032Speter char *msg = "Skipping queue run -- load average too high"; 176438032Speter 176538032Speter if (verbose) 176638032Speter message("458 %s\n", msg); 176738032Speter if (LogLevel > 8) 176890792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 176990792Sgshapiro return false; 177038032Speter } 177138032Speter 177238032Speter /* 177338032Speter ** See if we already have too many children. 177438032Speter */ 177538032Speter 177690792Sgshapiro if (forkflag && WorkGrp[wgrp].wg_lowqintvl > 0 && !persistent && 177738032Speter MaxChildren > 0 && CurChildren >= MaxChildren) 177838032Speter { 177964562Sgshapiro char *msg = "Skipping queue run -- too many children"; 178064562Sgshapiro 178164562Sgshapiro if (verbose) 178264562Sgshapiro message("458 %s (%d)\n", msg, CurChildren); 178364562Sgshapiro if (LogLevel > 8) 178490792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)", 178564562Sgshapiro msg, CurChildren); 178690792Sgshapiro return false; 178738032Speter } 178838032Speter 178938032Speter /* 179038032Speter ** See if we want to go off and do other useful work. 179138032Speter */ 179238032Speter 179338032Speter if (forkflag) 179438032Speter { 179538032Speter pid_t pid; 179638032Speter 179790792Sgshapiro (void) sm_blocksignal(SIGCHLD); 179890792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 179938032Speter 180038032Speter pid = dofork(); 180138032Speter if (pid == -1) 180238032Speter { 180338032Speter const char *msg = "Skipping queue run -- fork() failed"; 180490792Sgshapiro const char *err = sm_errstring(errno); 180538032Speter 180638032Speter if (verbose) 180738032Speter message("458 %s: %s\n", msg, err); 180838032Speter if (LogLevel > 8) 180990792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s", 181064562Sgshapiro msg, err); 181190792Sgshapiro (void) sm_releasesignal(SIGCHLD); 181290792Sgshapiro return false; 181338032Speter } 181438032Speter if (pid != 0) 181538032Speter { 181638032Speter /* parent -- pick up intermediate zombie */ 181790792Sgshapiro (void) sm_blocksignal(SIGALRM); 181890792Sgshapiro 181990792Sgshapiro /* wgrp only used when queue runners are persistent */ 182090792Sgshapiro proc_list_add(pid, "Queue runner", PROC_QUEUE, 182190792Sgshapiro WorkGrp[wgrp].wg_maxact, 182290792Sgshapiro persistent ? wgrp : -1); 182390792Sgshapiro (void) sm_releasesignal(SIGALRM); 182490792Sgshapiro (void) sm_releasesignal(SIGCHLD); 182590792Sgshapiro return true; 182638032Speter } 182790792Sgshapiro 182864562Sgshapiro /* child -- clean up signals */ 182977349Sgshapiro 183077349Sgshapiro /* Reset global flags */ 183177349Sgshapiro RestartRequest = NULL; 183290792Sgshapiro RestartWorkGroup = false; 183377349Sgshapiro ShutdownRequest = NULL; 183477349Sgshapiro PendingSignal = 0; 183590792Sgshapiro CurrentPid = getpid(); 183677349Sgshapiro 183790792Sgshapiro /* 183890792Sgshapiro ** Initialize exception stack and default exception 183990792Sgshapiro ** handler for child process. 184090792Sgshapiro */ 184190792Sgshapiro 184290792Sgshapiro sm_exc_newthread(fatal_error); 184342575Speter clrcontrol(); 184438032Speter proc_list_clear(); 184542575Speter 184642575Speter /* Add parent process as first child item */ 184790792Sgshapiro proc_list_add(CurrentPid, "Queue runner child process", 184890792Sgshapiro PROC_QUEUE_CHILD, 0, -1); 184990792Sgshapiro (void) sm_releasesignal(SIGCHLD); 185090792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 185190792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 185290792Sgshapiro (void) sm_signal(SIGTERM, intsig); 185338032Speter } 185438032Speter 185538032Speter /* 185638032Speter ** Release any resources used by the daemon code. 185738032Speter */ 185838032Speter 185938032Speter clrdaemon(); 186038032Speter 186138032Speter /* force it to run expensive jobs */ 186290792Sgshapiro NoConnect = false; 186338032Speter 186438032Speter /* drop privileges */ 186538032Speter if (geteuid() == (uid_t) 0) 186690792Sgshapiro (void) drop_privileges(false); 186738032Speter 186838032Speter /* 186938032Speter ** Create ourselves an envelope 187038032Speter */ 187138032Speter 187238032Speter CurEnv = &QueueEnvelope; 187390792Sgshapiro rpool = sm_rpool_new_x(NULL); 187490792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 187538032Speter e->e_flags = BlankEnvelope.e_flags; 187673188Sgshapiro e->e_parent = NULL; 187738032Speter 187838032Speter /* make sure we have disconnected from parent */ 187938032Speter if (forkflag) 188038032Speter { 188138032Speter disconnect(1, e); 188290792Sgshapiro QuickAbort = false; 188338032Speter } 188438032Speter 188538032Speter /* 188638032Speter ** If we are running part of the queue, always ignore stored 188738032Speter ** host status. 188838032Speter */ 188938032Speter 189038032Speter if (QueueLimitId != NULL || QueueLimitSender != NULL || 189190792Sgshapiro#if _FFR_QUARANTINE 189290792Sgshapiro QueueLimitQuarantine != NULL || 189390792Sgshapiro#endif /* _FFR_QUARANTINE */ 189438032Speter QueueLimitRecipient != NULL) 189538032Speter { 189690792Sgshapiro IgnoreHostStatus = true; 189738032Speter MinQueueAge = 0; 189838032Speter } 189938032Speter 190038032Speter /* 190190792Sgshapiro ** Here is where we choose the queue group from the work group. 190290792Sgshapiro ** The caller of the "domorework" label must setup a new envelope. 190390792Sgshapiro */ 190490792Sgshapiro 190590792Sgshapiro endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */ 190690792Sgshapiro 190790792Sgshapiro domorework: 190890792Sgshapiro 190990792Sgshapiro /* 191090792Sgshapiro ** Run a queue group if: 191190792Sgshapiro ** runall is set or the bit for this group is set. 191290792Sgshapiro */ 191390792Sgshapiro 191494334Sgshapiro now = curtime(); 191590792Sgshapiro for (;;) 191690792Sgshapiro { 191790792Sgshapiro /* 191890792Sgshapiro ** Find the next queue group within the work group that 191990792Sgshapiro ** has been marked as needing a run. 192090792Sgshapiro */ 192190792Sgshapiro 192290792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index; 192390792Sgshapiro WorkGrp[wgrp].wg_curqgrp++; /* advance */ 192490792Sgshapiro WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */ 192594334Sgshapiro if (runall || 192694334Sgshapiro (Queue[qgrp]->qg_nextrun <= now && 192794334Sgshapiro Queue[qgrp]->qg_nextrun != (time_t) -1)) 192890792Sgshapiro break; 192990792Sgshapiro if (endgrp == WorkGrp[wgrp].wg_curqgrp) 193090792Sgshapiro { 193190792Sgshapiro e->e_id = NULL; 193290792Sgshapiro if (forkflag) 193390792Sgshapiro finis(true, true, ExitStat); 193490792Sgshapiro return true; /* we're done */ 193590792Sgshapiro } 193690792Sgshapiro } 193790792Sgshapiro 193890792Sgshapiro qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */ 193990792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 194090792Sgshapiro if (tTd(69, 12)) 194190792Sgshapiro sm_syslog(LOG_INFO, NOQID, 194290792Sgshapiro "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d", 194390792Sgshapiro wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir), 194490792Sgshapiro WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp); 194590792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 194690792Sgshapiro 194790792Sgshapiro#if HASNICE 194890792Sgshapiro /* tweak niceness of queue runs */ 194990792Sgshapiro if (Queue[qgrp]->qg_nice > 0) 195090792Sgshapiro (void) nice(Queue[qgrp]->qg_nice); 195190792Sgshapiro#endif /* HASNICE */ 195290792Sgshapiro 195390792Sgshapiro /* XXX running queue group... */ 195490792Sgshapiro sm_setproctitle(true, CurEnv, "running queue: %s", 195590792Sgshapiro qid_printqueue(qgrp, qdir)); 195690792Sgshapiro 195790792Sgshapiro if (LogLevel > 69 || tTd(63, 99)) 195890792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 195990792Sgshapiro "runqueue %s, pid=%d, forkflag=%d", 196090792Sgshapiro qid_printqueue(qgrp, qdir), (int) CurrentPid, 196190792Sgshapiro forkflag); 196290792Sgshapiro 196390792Sgshapiro /* 196438032Speter ** Start making passes through the queue. 196538032Speter ** First, read and sort the entire queue. 196638032Speter ** Then, process the work in that order. 196738032Speter ** But if you take too long, start over. 196838032Speter */ 196938032Speter 197090792Sgshapiro for (i = 0; i < Queue[qgrp]->qg_numqueues; i++) 197190792Sgshapiro { 197290792Sgshapiro h = gatherq(qgrp, qdir, false, &full, &more); 197390792Sgshapiro#if SM_CONF_SHM 197490792Sgshapiro if (ShmId != SM_SHM_NO_ID) 197590792Sgshapiro QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h; 197690792Sgshapiro#endif /* SM_CONF_SHM */ 197790792Sgshapiro /* If there are no more items in this queue advance */ 197890792Sgshapiro if (!more) 197990792Sgshapiro { 198090792Sgshapiro /* A round-robin advance */ 198190792Sgshapiro qdir++; 198290792Sgshapiro qdir %= Queue[qgrp]->qg_numqueues; 198390792Sgshapiro } 198490792Sgshapiro 198590792Sgshapiro /* Has the WorkList reached the limit? */ 198690792Sgshapiro if (full) 198790792Sgshapiro break; /* don't try to gather more */ 198890792Sgshapiro } 198990792Sgshapiro 199038032Speter /* order the existing work requests */ 199190792Sgshapiro njobs = sortq(Queue[qgrp]->qg_maxlist); 199290792Sgshapiro Queue[qgrp]->qg_curnum = qdir; /* update */ 199338032Speter 199464562Sgshapiro 199590792Sgshapiro if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags)) 199638032Speter { 199790792Sgshapiro int loop, maxrunners; 199890792Sgshapiro pid_t pid; 199938032Speter 200038032Speter /* 200190792Sgshapiro ** For this WorkQ we want to fork off N children (maxrunners) 200290792Sgshapiro ** at this point. Each child has a copy of WorkQ. Each child 200390792Sgshapiro ** will process every N-th item. The parent will wait for all 200490792Sgshapiro ** of the children to finish before moving on to the next 200590792Sgshapiro ** queue group within the work group. This saves us forking 200690792Sgshapiro ** a new runner-child for each work item. 200790792Sgshapiro ** It's valid for qg_maxqrun == 0 since this may be an 200890792Sgshapiro ** explicit "don't run this queue" setting. 200938032Speter */ 201038032Speter 201190792Sgshapiro maxrunners = Queue[qgrp]->qg_maxqrun; 201290792Sgshapiro 201390792Sgshapiro /* No need to have more runners then there are jobs */ 201490792Sgshapiro if (maxrunners > njobs) 201590792Sgshapiro maxrunners = njobs; 201690792Sgshapiro for (loop = 0; loop < maxrunners; loop++) 201738032Speter { 2018102528Sgshapiro#if _FFR_NONSTOP_PERSISTENCE 201990792Sgshapiro /* 2020102528Sgshapiro ** Require a free "slot" before processing 2021102528Sgshapiro ** this queue runner. 2022102528Sgshapiro */ 2023102528Sgshapiro 2024102528Sgshapiro while (MaxQueueChildren > 0 && 2025102528Sgshapiro CurChildren > MaxQueueChildren) 2026102528Sgshapiro { 2027102528Sgshapiro int status; 2028102528Sgshapiro pid_t ret; 2029102528Sgshapiro 2030102528Sgshapiro while ((ret = sm_wait(&status)) <= 0) 2031102528Sgshapiro continue; 2032102528Sgshapiro proc_list_drop(ret, status, NULL); 2033102528Sgshapiro } 2034102528Sgshapiro#endif /* _FFR_NONSTOP_PERSISTENCE */ 2035102528Sgshapiro 2036102528Sgshapiro /* 203790792Sgshapiro ** Since the delivery may happen in a child and the 203890792Sgshapiro ** parent does not wait, the parent may close the 203990792Sgshapiro ** maps thereby removing any shared memory used by 204090792Sgshapiro ** the map. Therefore, close the maps now so the 204190792Sgshapiro ** child will dynamically open them if necessary. 204290792Sgshapiro */ 204390792Sgshapiro 204490792Sgshapiro closemaps(false); 204590792Sgshapiro 204690792Sgshapiro pid = fork(); 204790792Sgshapiro if (pid < 0) 204890792Sgshapiro { 204990792Sgshapiro syserr("run_work_group: cannot fork"); 205090792Sgshapiro return 0; 205190792Sgshapiro } 205290792Sgshapiro else if (pid > 0) 205390792Sgshapiro { 205490792Sgshapiro /* parent -- clean out connection cache */ 205590792Sgshapiro mci_flush(false, NULL); 205690792Sgshapiro WorkQ = WorkQ->w_next; /* for the skip */ 205790792Sgshapiro sequenceno++; 205890792Sgshapiro proc_list_add(pid, "Queue child runner process", 205990792Sgshapiro PROC_QUEUE_CHILD, 0, -1); 206090792Sgshapiro 206190792Sgshapiro /* No additional work, no additional runners */ 206290792Sgshapiro if (WorkQ == NULL) 206390792Sgshapiro break; 206490792Sgshapiro } 206590792Sgshapiro else 206690792Sgshapiro { 206790792Sgshapiro /* child -- Reset global flags */ 206890792Sgshapiro RestartRequest = NULL; 206990792Sgshapiro RestartWorkGroup = false; 207090792Sgshapiro ShutdownRequest = NULL; 207190792Sgshapiro PendingSignal = 0; 207290792Sgshapiro CurrentPid = getpid(); 207390792Sgshapiro 207490792Sgshapiro /* 207590792Sgshapiro ** Initialize exception stack and default 207690792Sgshapiro ** exception handler for child process. 207790792Sgshapiro ** When fork()'d the child now has a private 207890792Sgshapiro ** copy of WorkQ at its current position. 207990792Sgshapiro */ 208090792Sgshapiro 208190792Sgshapiro sm_exc_newthread(fatal_error); 208290792Sgshapiro 208390792Sgshapiro /* 208490792Sgshapiro ** SMTP processes (whether -bd or -bs) set 208590792Sgshapiro ** SIGCHLD to reapchild to collect 208690792Sgshapiro ** children status. However, at delivery 208790792Sgshapiro ** time, that status must be collected 208890792Sgshapiro ** by sm_wait() to be dealt with properly 208990792Sgshapiro ** (check success of delivery based 209090792Sgshapiro ** on status code, etc). Therefore, if we 209190792Sgshapiro ** are an SMTP process, reset SIGCHLD 209290792Sgshapiro ** back to the default so reapchild 209390792Sgshapiro ** doesn't collect status before 209490792Sgshapiro ** sm_wait(). 209590792Sgshapiro */ 209690792Sgshapiro 209790792Sgshapiro if (OpMode == MD_SMTP || 209890792Sgshapiro OpMode == MD_DAEMON || 209990792Sgshapiro MaxQueueChildren > 0) 210090792Sgshapiro { 210190792Sgshapiro proc_list_clear(); 210290792Sgshapiro sm_releasesignal(SIGCHLD); 210390792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 210490792Sgshapiro } 210590792Sgshapiro 210690792Sgshapiro /* child -- error messages to the transcript */ 210790792Sgshapiro QuickAbort = OnlyOneError = false; 210890792Sgshapiro runner_work(e, sequenceno, true, 210990792Sgshapiro maxrunners, njobs); 211090792Sgshapiro 211190792Sgshapiro /* This child is done */ 211290792Sgshapiro finis(true, true, ExitStat); 211390792Sgshapiro /* NOTREACHED */ 211490792Sgshapiro } 211538032Speter } 211690792Sgshapiro 211790792Sgshapiro sm_releasesignal(SIGCHLD); 211890792Sgshapiro 2119102528Sgshapiro#if !_FFR_NONSTOP_PERSISTENCE 212090792Sgshapiro /* 212190792Sgshapiro ** Wait until all of the runners have completed before 212290792Sgshapiro ** seeing if there is another queue group in the 212390792Sgshapiro ** work group to process. 212490792Sgshapiro ** XXX Future enhancement: don't wait() for all children 212590792Sgshapiro ** here, just go ahead and make sure that overall the number 212690792Sgshapiro ** of children is not exceeded. 212790792Sgshapiro */ 212890792Sgshapiro 212990792Sgshapiro while (CurChildren > 0) 213038032Speter { 213190792Sgshapiro int status; 213290792Sgshapiro pid_t ret; 213338032Speter 213490792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 213590792Sgshapiro continue; 213690792Sgshapiro proc_list_drop(ret, status, NULL); 213738032Speter } 2138102528Sgshapiro#endif /* !_FFR_NONSTOP_PERSISTENCE */ 213990792Sgshapiro } 214090792Sgshapiro else 214190792Sgshapiro { 214290792Sgshapiro /* 214390792Sgshapiro ** When current process will not fork children to do the work, 214490792Sgshapiro ** it will do the work itself. The 'skip' will be 1 since 214590792Sgshapiro ** there are no child runners to divide the work across. 214690792Sgshapiro */ 214790792Sgshapiro 214890792Sgshapiro runner_work(e, sequenceno, false, 1, njobs); 214990792Sgshapiro } 215090792Sgshapiro 215190792Sgshapiro /* free memory allocated by newenvelope() above */ 215290792Sgshapiro sm_rpool_free(rpool); 215390792Sgshapiro QueueEnvelope.e_rpool = NULL; 215490792Sgshapiro 215590792Sgshapiro /* Are there still more queues in the work group to process? */ 215690792Sgshapiro if (endgrp != WorkGrp[wgrp].wg_curqgrp) 215790792Sgshapiro { 215890792Sgshapiro rpool = sm_rpool_new_x(NULL); 215990792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 216090792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 216190792Sgshapiro goto domorework; 216290792Sgshapiro } 216390792Sgshapiro 216490792Sgshapiro /* No more queues in work group to process. Now check persistent. */ 216590792Sgshapiro if (persistent) 216690792Sgshapiro { 216790792Sgshapiro sequenceno = 1; 216890792Sgshapiro sm_setproctitle(true, CurEnv, "running queue: %s", 216990792Sgshapiro qid_printqueue(qgrp, qdir)); 217090792Sgshapiro 217190792Sgshapiro /* 217290792Sgshapiro ** close bogus maps, i.e., maps which caused a tempfail, 217390792Sgshapiro ** so we get fresh map connections on the next lookup. 217490792Sgshapiro ** closemaps() is also called when children are started. 217590792Sgshapiro */ 217690792Sgshapiro 217790792Sgshapiro closemaps(true); 217890792Sgshapiro 217990792Sgshapiro /* Close any cached connections. */ 218090792Sgshapiro mci_flush(true, NULL); 218190792Sgshapiro 218290792Sgshapiro /* Clean out expired related entries. */ 218390792Sgshapiro rmexpstab(); 218490792Sgshapiro 218590792Sgshapiro#if NAMED_BIND 218690792Sgshapiro /* Update MX records for FallBackMX. */ 218790792Sgshapiro if (FallBackMX != NULL) 218890792Sgshapiro (void) getfallbackmxrr(FallBackMX); 218990792Sgshapiro#endif /* NAMED_BIND */ 219090792Sgshapiro 219190792Sgshapiro#if USERDB 219290792Sgshapiro /* close UserDatabase */ 219390792Sgshapiro _udbx_close(); 219490792Sgshapiro#endif /* USERDB */ 219590792Sgshapiro 219690792Sgshapiro#if SM_HEAP_CHECK 219790792Sgshapiro if (sm_debug_active(&SmHeapCheck, 2) 219890792Sgshapiro && access("memdump", F_OK) == 0 219990792Sgshapiro ) 220038032Speter { 220190792Sgshapiro SM_FILE_T *out; 220290792Sgshapiro 220390792Sgshapiro remove("memdump"); 220490792Sgshapiro out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, 220590792Sgshapiro "memdump.out", SM_IO_APPEND, NULL); 220690792Sgshapiro if (out != NULL) 220738032Speter { 220890792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n"); 220990792Sgshapiro sm_heap_report(out, 221090792Sgshapiro sm_debug_level(&SmHeapCheck) - 1); 221190792Sgshapiro (void) sm_io_close(out, SM_TIME_DEFAULT); 221238032Speter } 221338032Speter } 221490792Sgshapiro#endif /* SM_HEAP_CHECK */ 221590792Sgshapiro 221690792Sgshapiro /* let me rest for a second to catch my breath */ 221790792Sgshapiro if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME) 221890792Sgshapiro sleep(MIN_SLEEP_TIME); 221990792Sgshapiro else if (WorkGrp[wgrp].wg_lowqintvl <= 0) 222090792Sgshapiro sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME); 222138032Speter else 222290792Sgshapiro sleep(WorkGrp[wgrp].wg_lowqintvl); 222338032Speter 222490792Sgshapiro /* 222590792Sgshapiro ** Get the LA outside the WorkQ loop if necessary. 222690792Sgshapiro ** In a persistent queue runner the code is repeated over 222790792Sgshapiro ** and over but gatherq() may ignore entries due to 222890792Sgshapiro ** shouldqueue() (do we really have to do this twice?). 222990792Sgshapiro ** Hence the queue runners would just idle around when once 223090792Sgshapiro ** CurrentLA caused all entries in a queue to be ignored. 223190792Sgshapiro */ 223264562Sgshapiro 223390792Sgshapiro now = curtime(); 223490792Sgshapiro if (njobs == 0 && current_la_time < now - GET_NEW_LA_TIME) 223590792Sgshapiro { 223690792Sgshapiro sm_getla(); 223790792Sgshapiro current_la_time = now; 223838032Speter } 223990792Sgshapiro rpool = sm_rpool_new_x(NULL); 224090792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 224190792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 224290792Sgshapiro goto domorework; 224338032Speter } 224438032Speter 224538032Speter /* exit without the usual cleanup */ 224638032Speter e->e_id = NULL; 224764562Sgshapiro if (forkflag) 224890792Sgshapiro finis(true, true, ExitStat); 224964562Sgshapiro /* NOTREACHED */ 225090792Sgshapiro return true; 225138032Speter} 225238032Speter 225338032Speter/* 225490792Sgshapiro** DOQUEUERUN -- do a queue run? 225590792Sgshapiro*/ 225690792Sgshapiro 225790792Sgshapirobool 225890792Sgshapirodoqueuerun() 225990792Sgshapiro{ 226094334Sgshapiro return DoQueueRun; 226190792Sgshapiro} 226290792Sgshapiro 226390792Sgshapiro/* 226494334Sgshapiro** RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done. 226577349Sgshapiro** 226677349Sgshapiro** Parameters: 226794334Sgshapiro** none. 226877349Sgshapiro** 226977349Sgshapiro** Returns: 227077349Sgshapiro** none. 227177349Sgshapiro** 227290792Sgshapiro** Side Effects: 227390792Sgshapiro** The invocation of this function via an alarm may interrupt 227490792Sgshapiro** a set of actions. Thus errno may be set in that context. 227590792Sgshapiro** We need to restore errno at the end of this function to ensure 227690792Sgshapiro** that any work done here that sets errno doesn't return a 227790792Sgshapiro** misleading/false errno value. Errno may be EINTR upon entry to 227890792Sgshapiro** this function because of non-restartable/continuable system 227990792Sgshapiro** API was active. Iff this is true we will override errno as 228090792Sgshapiro** a timeout (as a more accurate error message). 228190792Sgshapiro** 228277349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 228377349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 228477349Sgshapiro** DOING. 228538032Speter*/ 228638032Speter 228790792Sgshapirovoid 228894334Sgshapirorunqueueevent() 228938032Speter{ 229090792Sgshapiro int save_errno = errno; 229190792Sgshapiro 229290792Sgshapiro /* 229390792Sgshapiro ** Set the general bit that we want a queue run, 229490792Sgshapiro ** tested in doqueuerun() 229590792Sgshapiro */ 229690792Sgshapiro 229794334Sgshapiro DoQueueRun = true; 229894334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 229994334Sgshapiro if (tTd(69, 10)) 230094334Sgshapiro sm_syslog(LOG_INFO, NOQID, "rqe: done"); 230194334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 230290792Sgshapiro 230390792Sgshapiro errno = save_errno; 230490792Sgshapiro if (errno == EINTR) 230590792Sgshapiro errno = ETIMEDOUT; 230638032Speter} 230790792Sgshapiro/* 230890792Sgshapiro** GATHERQ -- gather messages from the message queue(s) the work queue. 230938032Speter** 231038032Speter** Parameters: 231190792Sgshapiro** qgrp -- the index of the queue group. 231290792Sgshapiro** qdir -- the index of the queue directory. 231338032Speter** doall -- if set, include everything in the queue (even 231438032Speter** the jobs that cannot be run because the load 231590792Sgshapiro** average is too high, or MaxQueueRun is reached). 231690792Sgshapiro** Otherwise, exclude those jobs. 231790792Sgshapiro** full -- (optional) to be set 'true' if WorkList is full 231890792Sgshapiro** more -- (optional) to be set 'true' if there are still more 231990792Sgshapiro** messages in this queue not added to WorkList 232038032Speter** 232138032Speter** Returns: 232238032Speter** The number of request in the queue (not necessarily 232390792Sgshapiro** the number of requests in WorkList however). 232438032Speter** 232538032Speter** Side Effects: 232690792Sgshapiro** prepares available work into WorkList 232738032Speter*/ 232838032Speter 232990792Sgshapiro#define NEED_P 0001 /* 'P': priority */ 233090792Sgshapiro#define NEED_T 0002 /* 'T': time */ 233190792Sgshapiro#define NEED_R 0004 /* 'R': recipient */ 233290792Sgshapiro#define NEED_S 0010 /* 'S': sender */ 233390792Sgshapiro#define NEED_H 0020 /* host */ 233490792Sgshapiro#if _FFR_QUARANTINE 233590792Sgshapiro# define HAS_QUARANTINE 0040 /* has an unexpected 'q' line */ 233690792Sgshapiro# define NEED_QUARANTINE 0100 /* 'q': reason */ 233790792Sgshapiro#endif /* _FFR_QUARANTINE */ 233838032Speter 233990792Sgshapirostatic WORK *WorkList = NULL; /* list of unsort work */ 234090792Sgshapirostatic int WorkListSize = 0; /* current max size of WorkList */ 234190792Sgshapirostatic int WorkListCount = 0; /* # of work items in WorkList */ 234238032Speter 234364562Sgshapirostatic int 234490792Sgshapirogatherq(qgrp, qdir, doall, full, more) 234590792Sgshapiro int qgrp; 234690792Sgshapiro int qdir; 234738032Speter bool doall; 234890792Sgshapiro bool *full; 234990792Sgshapiro bool *more; 235038032Speter{ 235138032Speter register struct dirent *d; 235238032Speter register WORK *w; 235338032Speter register char *p; 235438032Speter DIR *f; 235590792Sgshapiro int i, num_ent; 235690792Sgshapiro int wn; 235738032Speter QUEUE_CHAR *check; 235864562Sgshapiro char qd[MAXPATHLEN]; 235964562Sgshapiro char qf[MAXPATHLEN]; 236064562Sgshapiro 236190792Sgshapiro wn = WorkListCount - 1; 236290792Sgshapiro num_ent = 0; 236390792Sgshapiro if (qdir == NOQDIR) 236490792Sgshapiro (void) sm_strlcpy(qd, ".", sizeof qd); 236564562Sgshapiro else 236690792Sgshapiro (void) sm_strlcpyn(qd, sizeof qd, 2, 236790792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 236890792Sgshapiro (bitset(QP_SUBQF, 236990792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 237090792Sgshapiro ? "/qf" : "")); 237164562Sgshapiro 237238032Speter if (tTd(41, 1)) 237338032Speter { 237490792Sgshapiro sm_dprintf("gatherq:\n"); 237538032Speter 237638032Speter check = QueueLimitId; 237738032Speter while (check != NULL) 237838032Speter { 237990792Sgshapiro sm_dprintf("\tQueueLimitId = %s%s\n", 238090792Sgshapiro check->queue_negate ? "!" : "", 238164562Sgshapiro check->queue_match); 238238032Speter check = check->queue_next; 238338032Speter } 238438032Speter 238538032Speter check = QueueLimitSender; 238638032Speter while (check != NULL) 238738032Speter { 238890792Sgshapiro sm_dprintf("\tQueueLimitSender = %s%s\n", 238990792Sgshapiro check->queue_negate ? "!" : "", 239064562Sgshapiro check->queue_match); 239138032Speter check = check->queue_next; 239238032Speter } 239338032Speter 239438032Speter check = QueueLimitRecipient; 239538032Speter while (check != NULL) 239638032Speter { 239790792Sgshapiro sm_dprintf("\tQueueLimitRecipient = %s%s\n", 239890792Sgshapiro check->queue_negate ? "!" : "", 239964562Sgshapiro check->queue_match); 240038032Speter check = check->queue_next; 240138032Speter } 240238032Speter 240390792Sgshapiro#if _FFR_QUARANTINE 240490792Sgshapiro if (QueueMode == QM_QUARANTINE) 240590792Sgshapiro { 240690792Sgshapiro check = QueueLimitQuarantine; 240790792Sgshapiro while (check != NULL) 240890792Sgshapiro { 240990792Sgshapiro sm_dprintf("\tQueueLimitQuarantine = %s%s\n", 241090792Sgshapiro check->queue_negate ? "!" : "", 241190792Sgshapiro check->queue_match); 241290792Sgshapiro check = check->queue_next; 241390792Sgshapiro } 241490792Sgshapiro } 241590792Sgshapiro#endif /* _FFR_QUARANTINE */ 241638032Speter } 241738032Speter 241838032Speter /* open the queue directory */ 241964562Sgshapiro f = opendir(qd); 242038032Speter if (f == NULL) 242138032Speter { 242290792Sgshapiro syserr("gatherq: cannot open \"%s\"", 242390792Sgshapiro qid_printqueue(qgrp, qdir)); 242490792Sgshapiro if (full != NULL) 242590792Sgshapiro *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0; 242690792Sgshapiro if (more != NULL) 242790792Sgshapiro *more = false; 242864562Sgshapiro return 0; 242938032Speter } 243038032Speter 243138032Speter /* 243238032Speter ** Read the work directory. 243338032Speter */ 243438032Speter 243538032Speter while ((d = readdir(f)) != NULL) 243638032Speter { 243790792Sgshapiro SM_FILE_T *cf; 243838032Speter int qfver = 0; 243938032Speter char lbuf[MAXNAME + 1]; 244064562Sgshapiro struct stat sbuf; 244138032Speter 244238032Speter if (tTd(41, 50)) 244390792Sgshapiro sm_dprintf("gatherq: checking %s..", d->d_name); 244438032Speter 244538032Speter /* is this an interesting entry? */ 244690792Sgshapiro#if _FFR_QUARANTINE 244790792Sgshapiro if (!(((QueueMode == QM_NORMAL && 244890792Sgshapiro d->d_name[0] == NORMQF_LETTER) || 244990792Sgshapiro (QueueMode == QM_QUARANTINE && 245090792Sgshapiro d->d_name[0] == QUARQF_LETTER) || 245190792Sgshapiro (QueueMode == QM_LOST && 245290792Sgshapiro d->d_name[0] == LOSEQF_LETTER)) && 245390792Sgshapiro d->d_name[1] == 'f')) 245490792Sgshapiro#else /* _FFR_QUARANTINE */ 245590792Sgshapiro if (d->d_name[0] != NORMQF_LETTER || d->d_name[1] != 'f') 245690792Sgshapiro#endif /* _FFR_QUARANTINE */ 245790792Sgshapiro { 245890792Sgshapiro if (tTd(41, 50)) 245990792Sgshapiro sm_dprintf(" skipping\n"); 246038032Speter continue; 246190792Sgshapiro } 246290792Sgshapiro if (tTd(41, 50)) 246390792Sgshapiro sm_dprintf("\n"); 246438032Speter 246564562Sgshapiro if (strlen(d->d_name) >= MAXQFNAME) 246642575Speter { 246742575Speter if (Verbose) 246890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 246990792Sgshapiro "gatherq: %s too long, %d max characters\n", 247090792Sgshapiro d->d_name, MAXQFNAME); 247142575Speter if (LogLevel > 0) 247242575Speter sm_syslog(LOG_ALERT, NOQID, 247390792Sgshapiro "gatherq: %s too long, %d max characters", 247464562Sgshapiro d->d_name, MAXQFNAME); 247538032Speter continue; 247642575Speter } 247738032Speter 247838032Speter check = QueueLimitId; 247938032Speter while (check != NULL) 248038032Speter { 248194334Sgshapiro if (strcontainedin(false, check->queue_match, 248290792Sgshapiro d->d_name) != check->queue_negate) 248338032Speter break; 248438032Speter else 248538032Speter check = check->queue_next; 248638032Speter } 248738032Speter if (QueueLimitId != NULL && check == NULL) 248838032Speter continue; 248938032Speter 249064562Sgshapiro /* grow work list if necessary */ 249138032Speter if (++wn >= MaxQueueRun && MaxQueueRun > 0) 249238032Speter { 249338032Speter if (wn == MaxQueueRun && LogLevel > 0) 249464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 249564562Sgshapiro "WorkList for %s maxed out at %d", 249690792Sgshapiro qid_printqueue(qgrp, qdir), 249764562Sgshapiro MaxQueueRun); 249890792Sgshapiro if (doall) 249990792Sgshapiro continue; /* just count entries */ 250090792Sgshapiro break; 250138032Speter } 250238032Speter if (wn >= WorkListSize) 250338032Speter { 250490792Sgshapiro grow_wlist(qgrp, qdir); 250538032Speter if (wn >= WorkListSize) 250638032Speter continue; 250738032Speter } 250890792Sgshapiro SM_ASSERT(wn >= 0); 250964562Sgshapiro w = &WorkList[wn]; 251038032Speter 251190792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", d->d_name); 251264562Sgshapiro if (stat(qf, &sbuf) < 0) 251364562Sgshapiro { 251464562Sgshapiro if (errno != ENOENT) 251564562Sgshapiro sm_syslog(LOG_INFO, NOQID, 251690792Sgshapiro "gatherq: can't stat %s/%s", 251790792Sgshapiro qid_printqueue(qgrp, qdir), 251890792Sgshapiro d->d_name); 251964562Sgshapiro wn--; 252064562Sgshapiro continue; 252164562Sgshapiro } 252264562Sgshapiro if (!bitset(S_IFREG, sbuf.st_mode)) 252364562Sgshapiro { 252464562Sgshapiro /* Yikes! Skip it or we will hang on open! */ 252590792Sgshapiro if (!((d->d_name[0] == DATAFL_LETTER || 252690792Sgshapiro d->d_name[0] == NORMQF_LETTER || 252790792Sgshapiro#if _FFR_QUARANTINE 252890792Sgshapiro d->d_name[0] == QUARQF_LETTER || 252990792Sgshapiro d->d_name[0] == LOSEQF_LETTER || 253090792Sgshapiro#endif /* _FFR_QUARANTINE */ 253190792Sgshapiro d->d_name[0] == XSCRPT_LETTER) && 253290792Sgshapiro d->d_name[1] == 'f' && d->d_name[2] == '\0')) 253390792Sgshapiro syserr("gatherq: %s/%s is not a regular file", 253490792Sgshapiro qid_printqueue(qgrp, qdir), d->d_name); 253564562Sgshapiro wn--; 253664562Sgshapiro continue; 253764562Sgshapiro } 253864562Sgshapiro 253964562Sgshapiro /* avoid work if possible */ 254090792Sgshapiro if ((QueueSortOrder == QSO_BYFILENAME || 254190792Sgshapiro QueueSortOrder == QSO_BYMODTIME || 254290792Sgshapiro QueueSortOrder == QSO_RANDOM) && 254390792Sgshapiro#if _FFR_QUARANTINE 254490792Sgshapiro QueueLimitQuarantine == NULL && 254590792Sgshapiro#endif /* _FFR_QUARANTINE */ 254666494Sgshapiro QueueLimitSender == NULL && 254766494Sgshapiro QueueLimitRecipient == NULL) 254864562Sgshapiro { 254990792Sgshapiro w->w_qgrp = qgrp; 255090792Sgshapiro w->w_qdir = qdir; 255164562Sgshapiro w->w_name = newstr(d->d_name); 255264562Sgshapiro w->w_host = NULL; 255390792Sgshapiro w->w_lock = w->w_tooyoung = false; 255464562Sgshapiro w->w_pri = 0; 255564562Sgshapiro w->w_ctime = 0; 255690792Sgshapiro w->w_mtime = sbuf.st_mtime; 255790792Sgshapiro ++num_ent; 255864562Sgshapiro continue; 255964562Sgshapiro } 256064562Sgshapiro 256164562Sgshapiro /* open control file */ 256290792Sgshapiro cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY, 256390792Sgshapiro NULL); 256490792Sgshapiro if (cf == NULL && OpMode != MD_PRINT) 256538032Speter { 256638032Speter /* this may be some random person sending hir msgs */ 256738032Speter if (tTd(41, 2)) 256890792Sgshapiro sm_dprintf("gatherq: cannot open %s: %s\n", 256990792Sgshapiro d->d_name, sm_errstring(errno)); 257038032Speter errno = 0; 257138032Speter wn--; 257238032Speter continue; 257338032Speter } 257490792Sgshapiro w->w_qgrp = qgrp; 257590792Sgshapiro w->w_qdir = qdir; 257638032Speter w->w_name = newstr(d->d_name); 257738032Speter w->w_host = NULL; 257890792Sgshapiro if (cf != NULL) 257990792Sgshapiro { 258090792Sgshapiro w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD, 258190792Sgshapiro NULL), 258290792Sgshapiro w->w_name, NULL, 258390792Sgshapiro LOCK_SH|LOCK_NB); 258490792Sgshapiro } 258590792Sgshapiro w->w_tooyoung = false; 258638032Speter 258738032Speter /* make sure jobs in creation don't clog queue */ 258838032Speter w->w_pri = 0x7fffffff; 258938032Speter w->w_ctime = 0; 259090792Sgshapiro w->w_mtime = sbuf.st_mtime; 259138032Speter 259238032Speter /* extract useful information */ 259390792Sgshapiro i = NEED_P|NEED_T; 259490792Sgshapiro if (QueueSortOrder == QSO_BYHOST 259590792Sgshapiro#if _FFR_RHS 259690792Sgshapiro || QueueSortOrder == QSO_BYSHUFFLE 259790792Sgshapiro#endif /* _FFR_RHS */ 259890792Sgshapiro ) 259971345Sgshapiro { 260071345Sgshapiro /* need w_host set for host sort order */ 260171345Sgshapiro i |= NEED_H; 260271345Sgshapiro } 260338032Speter if (QueueLimitSender != NULL) 260438032Speter i |= NEED_S; 260564562Sgshapiro if (QueueLimitRecipient != NULL) 260638032Speter i |= NEED_R; 260790792Sgshapiro#if _FFR_QUARANTINE 260890792Sgshapiro if (QueueLimitQuarantine != NULL) 260990792Sgshapiro i |= NEED_QUARANTINE; 261090792Sgshapiro#endif /* _FFR_QUARANTINE */ 261190792Sgshapiro while (cf != NULL && i != 0 && 261290792Sgshapiro sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf, 261390792Sgshapiro sizeof lbuf) != NULL) 261438032Speter { 261538032Speter int c; 261638032Speter time_t age; 261738032Speter 261838032Speter p = strchr(lbuf, '\n'); 261938032Speter if (p != NULL) 262038032Speter *p = '\0'; 262138032Speter else 262238032Speter { 262338032Speter /* flush rest of overly long line */ 262490792Sgshapiro while ((c = sm_io_getc(cf, SM_TIME_DEFAULT)) 262590792Sgshapiro != SM_IO_EOF && c != '\n') 262638032Speter continue; 262738032Speter } 262838032Speter 262938032Speter switch (lbuf[0]) 263038032Speter { 263138032Speter case 'V': 263238032Speter qfver = atoi(&lbuf[1]); 263338032Speter break; 263438032Speter 263538032Speter case 'P': 263638032Speter w->w_pri = atol(&lbuf[1]); 263738032Speter i &= ~NEED_P; 263838032Speter break; 263938032Speter 264038032Speter case 'T': 264138032Speter w->w_ctime = atol(&lbuf[1]); 264238032Speter i &= ~NEED_T; 264338032Speter break; 264438032Speter 264590792Sgshapiro#if _FFR_QUARANTINE 264690792Sgshapiro case 'q': 264790792Sgshapiro if (QueueMode != QM_QUARANTINE && 264890792Sgshapiro QueueMode != QM_LOST) 264990792Sgshapiro { 265090792Sgshapiro if (tTd(41, 49)) 265190792Sgshapiro sm_dprintf("%s not marked as quarantined but has a 'q' line\n", 265290792Sgshapiro w->w_name); 265390792Sgshapiro i |= HAS_QUARANTINE; 265490792Sgshapiro } 265590792Sgshapiro else if (QueueMode == QM_QUARANTINE) 265690792Sgshapiro { 265790792Sgshapiro if (QueueLimitQuarantine == NULL) 265890792Sgshapiro { 265990792Sgshapiro i &= ~NEED_QUARANTINE; 266090792Sgshapiro break; 266190792Sgshapiro } 266290792Sgshapiro p = &lbuf[1]; 266390792Sgshapiro check = QueueLimitQuarantine; 266490792Sgshapiro while (check != NULL) 266590792Sgshapiro { 266690792Sgshapiro if (strcontainedin(false, 266790792Sgshapiro check->queue_match, 266890792Sgshapiro p) != 266990792Sgshapiro check->queue_negate) 267090792Sgshapiro break; 267190792Sgshapiro else 267290792Sgshapiro check = check->queue_next; 267390792Sgshapiro } 267490792Sgshapiro if (check != NULL) 267590792Sgshapiro i &= ~NEED_QUARANTINE; 267690792Sgshapiro } 267790792Sgshapiro break; 267890792Sgshapiro#endif /* _FFR_QUARANTINE */ 267990792Sgshapiro 268038032Speter case 'R': 268138032Speter if (w->w_host == NULL && 268238032Speter (p = strrchr(&lbuf[1], '@')) != NULL) 268364562Sgshapiro { 268490792Sgshapiro#if _FFR_RHS 268590792Sgshapiro if (QueueSortOrder == QSO_BYSHUFFLE) 268690792Sgshapiro w->w_host = newstr(&p[1]); 268790792Sgshapiro else 268890792Sgshapiro#endif /* _FFR_RHS */ 268990792Sgshapiro w->w_host = strrev(&p[1]); 269064562Sgshapiro makelower(w->w_host); 269171345Sgshapiro i &= ~NEED_H; 269264562Sgshapiro } 269338032Speter if (QueueLimitRecipient == NULL) 269438032Speter { 269538032Speter i &= ~NEED_R; 269638032Speter break; 269738032Speter } 269838032Speter if (qfver > 0) 269938032Speter { 270038032Speter p = strchr(&lbuf[1], ':'); 270138032Speter if (p == NULL) 270238032Speter p = &lbuf[1]; 270338032Speter } 270438032Speter else 270538032Speter p = &lbuf[1]; 270638032Speter check = QueueLimitRecipient; 270738032Speter while (check != NULL) 270838032Speter { 270990792Sgshapiro if (strcontainedin(true, 271090792Sgshapiro check->queue_match, 271190792Sgshapiro p) != 271290792Sgshapiro check->queue_negate) 271338032Speter break; 271438032Speter else 271538032Speter check = check->queue_next; 271638032Speter } 271738032Speter if (check != NULL) 271838032Speter i &= ~NEED_R; 271938032Speter break; 272038032Speter 272138032Speter case 'S': 272264562Sgshapiro check = QueueLimitSender; 272364562Sgshapiro while (check != NULL) 272464562Sgshapiro { 272590792Sgshapiro if (strcontainedin(true, 272690792Sgshapiro check->queue_match, 272790792Sgshapiro &lbuf[1]) != 272890792Sgshapiro check->queue_negate) 272964562Sgshapiro break; 273064562Sgshapiro else 273164562Sgshapiro check = check->queue_next; 273264562Sgshapiro } 273364562Sgshapiro if (check != NULL) 273464562Sgshapiro i &= ~NEED_S; 273538032Speter break; 273638032Speter 273738032Speter case 'K': 273838032Speter age = curtime() - (time_t) atol(&lbuf[1]); 273938032Speter if (age >= 0 && MinQueueAge > 0 && 274038032Speter age < MinQueueAge) 274190792Sgshapiro w->w_tooyoung = true; 274238032Speter break; 274338032Speter 274438032Speter case 'N': 274538032Speter if (atol(&lbuf[1]) == 0) 274690792Sgshapiro w->w_tooyoung = false; 274738032Speter break; 274864562Sgshapiro 274990792Sgshapiro#if _FFR_QUEUEDELAY 275064562Sgshapiro/* 275164562Sgshapiro case 'G': 275264562Sgshapiro queuealg = atoi(lbuf[1]); 275364562Sgshapiro break; 275464562Sgshapiro case 'Y': 275564562Sgshapiro queuedelay = (time_t) atol(&lbuf[1]); 275664562Sgshapiro break; 275764562Sgshapiro*/ 275890792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 275938032Speter } 276038032Speter } 276190792Sgshapiro if (cf != NULL) 276290792Sgshapiro (void) sm_io_close(cf, SM_TIME_DEFAULT); 276338032Speter 276438032Speter if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || 276590792Sgshapiro#if _FFR_QUARANTINE 276690792Sgshapiro bitset(HAS_QUARANTINE, i) || 276790792Sgshapiro bitset(NEED_QUARANTINE, i) || 276890792Sgshapiro#endif /* _FFR_QUARANTINE */ 276938032Speter bitset(NEED_R|NEED_S, i)) 277038032Speter { 277138032Speter /* don't even bother sorting this job in */ 277238032Speter if (tTd(41, 49)) 277390792Sgshapiro sm_dprintf("skipping %s (%x)\n", w->w_name, i); 277490792Sgshapiro sm_free(w->w_name); /* XXX */ 277590792Sgshapiro if (w->w_host != NULL) 277690792Sgshapiro sm_free(w->w_host); /* XXX */ 277738032Speter wn--; 277838032Speter } 277990792Sgshapiro else 278090792Sgshapiro ++num_ent; 278138032Speter } 278238032Speter (void) closedir(f); 278338032Speter wn++; 278438032Speter 278590792Sgshapiro i = wn - WorkListCount; 278690792Sgshapiro WorkListCount += SM_MIN(num_ent, WorkListSize); 278790792Sgshapiro 278890792Sgshapiro if (more != NULL) 278990792Sgshapiro *more = WorkListCount < wn; 279090792Sgshapiro 279190792Sgshapiro if (full != NULL) 279290792Sgshapiro *full = (wn >= MaxQueueRun && MaxQueueRun > 0) || 279390792Sgshapiro (WorkList == NULL && wn > 0); 279490792Sgshapiro 279590792Sgshapiro return i; 279690792Sgshapiro} 279790792Sgshapiro/* 279890792Sgshapiro** SORTQ -- sort the work list 279990792Sgshapiro** 280090792Sgshapiro** First the old WorkQ is cleared away. Then the WorkList is sorted 280190792Sgshapiro** for all items so that important (higher sorting value) items are not 280290792Sgshapiro** trunctated off. Then the most important items are moved from 280390792Sgshapiro** WorkList to WorkQ. The lower count of 'max' or MaxListCount items 280490792Sgshapiro** are moved. 280590792Sgshapiro** 280690792Sgshapiro** Parameters: 280790792Sgshapiro** max -- maximum number of items to be placed in WorkQ 280890792Sgshapiro** 280990792Sgshapiro** Returns: 281090792Sgshapiro** the number of items in WorkQ 281190792Sgshapiro** 281290792Sgshapiro** Side Effects: 281390792Sgshapiro** WorkQ gets released and filled with new work. WorkList 281490792Sgshapiro** gets released. Work items get sorted in order. 281590792Sgshapiro*/ 281690792Sgshapiro 281790792Sgshapirostatic int 281890792Sgshapirosortq(max) 281990792Sgshapiro int max; 282090792Sgshapiro{ 282190792Sgshapiro register int i; /* local counter */ 282290792Sgshapiro register WORK *w; /* tmp item pointer */ 282390792Sgshapiro int wc = WorkListCount; /* trim size for WorkQ */ 282490792Sgshapiro 282590792Sgshapiro if (WorkQ != NULL) 282690792Sgshapiro { 282790792Sgshapiro /* Clear out old WorkQ. */ 282890792Sgshapiro for (w = WorkQ; w != NULL; ) 282990792Sgshapiro { 283090792Sgshapiro register WORK *nw = w->w_next; 283190792Sgshapiro 283290792Sgshapiro WorkQ = nw; 283390792Sgshapiro sm_free(w->w_name); /* XXX */ 283490792Sgshapiro if (w->w_host != NULL) 283590792Sgshapiro sm_free(w->w_host); /* XXX */ 283690792Sgshapiro sm_free((char *) w); /* XXX */ 283790792Sgshapiro w = nw; 283890792Sgshapiro } 283990792Sgshapiro sm_free((char *) WorkQ); 284090792Sgshapiro WorkQ = NULL; 284190792Sgshapiro } 284290792Sgshapiro 284390792Sgshapiro if (WorkList == NULL || wc <= 0) 284464562Sgshapiro return 0; 284538032Speter 284690792Sgshapiro /* Check if the per queue group item limit will be exceeded */ 284790792Sgshapiro if (wc > max && max > 0) 284890792Sgshapiro wc = max; 284990792Sgshapiro 285090792Sgshapiro /* 285190792Sgshapiro ** The sort now takes place using all of the items in WorkList. 285290792Sgshapiro ** The list gets trimmed to the most important items after the sort. 285390792Sgshapiro ** If the trim were to happen before the sort then one or more 285490792Sgshapiro ** important items might get truncated off -- not what we want. 285590792Sgshapiro */ 285690792Sgshapiro 285764562Sgshapiro if (QueueSortOrder == QSO_BYHOST) 285838032Speter { 285938032Speter /* 286038032Speter ** Sort the work directory for the first time, 286138032Speter ** based on host name, lock status, and priority. 286238032Speter */ 286338032Speter 286438032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); 286538032Speter 286638032Speter /* 286738032Speter ** If one message to host is locked, "lock" all messages 286838032Speter ** to that host. 286938032Speter */ 287038032Speter 287138032Speter i = 0; 287238032Speter while (i < wc) 287338032Speter { 287438032Speter if (!WorkList[i].w_lock) 287538032Speter { 287638032Speter i++; 287738032Speter continue; 287838032Speter } 287938032Speter w = &WorkList[i]; 288038032Speter while (++i < wc) 288138032Speter { 288238032Speter if (WorkList[i].w_host == NULL && 288338032Speter w->w_host == NULL) 288490792Sgshapiro WorkList[i].w_lock = true; 288538032Speter else if (WorkList[i].w_host != NULL && 288638032Speter w->w_host != NULL && 288790792Sgshapiro sm_strcasecmp(WorkList[i].w_host, 288890792Sgshapiro w->w_host) == 0) 288990792Sgshapiro WorkList[i].w_lock = true; 289038032Speter else 289138032Speter break; 289238032Speter } 289338032Speter } 289438032Speter 289538032Speter /* 289638032Speter ** Sort the work directory for the second time, 289738032Speter ** based on lock status, host name, and priority. 289838032Speter */ 289938032Speter 290038032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); 290138032Speter } 290264562Sgshapiro else if (QueueSortOrder == QSO_BYTIME) 290338032Speter { 290438032Speter /* 290538032Speter ** Simple sort based on submission time only. 290638032Speter */ 290738032Speter 290838032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); 290938032Speter } 291064562Sgshapiro else if (QueueSortOrder == QSO_BYFILENAME) 291164562Sgshapiro { 291264562Sgshapiro /* 291390792Sgshapiro ** Sort based on queue filename. 291464562Sgshapiro */ 291564562Sgshapiro 291664562Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4); 291764562Sgshapiro } 291890792Sgshapiro else if (QueueSortOrder == QSO_RANDOM) 291990792Sgshapiro { 292090792Sgshapiro /* 292190792Sgshapiro ** Sort randomly. 292290792Sgshapiro ** workcmpf5() returns a random 1 or -1. 292390792Sgshapiro ** As long as nobody does a verification pass over the 292490792Sgshapiro ** sorted list, we should be golden. 292590792Sgshapiro */ 292690792Sgshapiro 292790792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf5); 292890792Sgshapiro } 292990792Sgshapiro else if (QueueSortOrder == QSO_BYMODTIME) 293090792Sgshapiro { 293190792Sgshapiro /* 293290792Sgshapiro ** Simple sort based on modification time of queue file. 293390792Sgshapiro ** This puts the oldest items first. 293490792Sgshapiro */ 293590792Sgshapiro 293690792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf6); 293790792Sgshapiro } 293890792Sgshapiro#if _FFR_RHS 293990792Sgshapiro else if (QueueSortOrder == QSO_BYSHUFFLE) 294090792Sgshapiro { 294190792Sgshapiro /* 294290792Sgshapiro ** Simple sort based on shuffled host name. 294390792Sgshapiro */ 294490792Sgshapiro 294590792Sgshapiro init_shuffle_alphabet(); 294690792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf7); 294790792Sgshapiro } 294890792Sgshapiro#endif /* _FFR_RHS */ 294938032Speter else 295038032Speter { 295138032Speter /* 295238032Speter ** Simple sort based on queue priority only. 295338032Speter */ 295438032Speter 295538032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); 295638032Speter } 295738032Speter 295838032Speter /* 295938032Speter ** Convert the work list into canonical form. 296038032Speter ** Should be turning it into a list of envelopes here perhaps. 296190792Sgshapiro ** Only take the most important items up to the per queue group 296290792Sgshapiro ** maximum. 296338032Speter */ 296438032Speter 296538032Speter for (i = wc; --i >= 0; ) 296638032Speter { 296738032Speter w = (WORK *) xalloc(sizeof *w); 296890792Sgshapiro w->w_qgrp = WorkList[i].w_qgrp; 296990792Sgshapiro w->w_qdir = WorkList[i].w_qdir; 297038032Speter w->w_name = WorkList[i].w_name; 297138032Speter w->w_host = WorkList[i].w_host; 297238032Speter w->w_lock = WorkList[i].w_lock; 297338032Speter w->w_tooyoung = WorkList[i].w_tooyoung; 297438032Speter w->w_pri = WorkList[i].w_pri; 297538032Speter w->w_ctime = WorkList[i].w_ctime; 297690792Sgshapiro w->w_mtime = WorkList[i].w_mtime; 297738032Speter w->w_next = WorkQ; 297838032Speter WorkQ = w; 297938032Speter } 298038032Speter if (WorkList != NULL) 298190792Sgshapiro sm_free(WorkList); /* XXX */ 298238032Speter WorkList = NULL; 298338032Speter WorkListSize = 0; 298490792Sgshapiro WorkListCount = 0; 298538032Speter 298638032Speter if (tTd(40, 1)) 298738032Speter { 298838032Speter for (w = WorkQ; w != NULL; w = w->w_next) 298964562Sgshapiro { 299064562Sgshapiro if (w->w_host != NULL) 299190792Sgshapiro sm_dprintf("%22s: pri=%ld %s\n", 299264562Sgshapiro w->w_name, w->w_pri, w->w_host); 299364562Sgshapiro else 299490792Sgshapiro sm_dprintf("%32s: pri=%ld\n", 299564562Sgshapiro w->w_name, w->w_pri); 299664562Sgshapiro } 299738032Speter } 299838032Speter 299990792Sgshapiro return wc; /* return number of WorkQ items */ 300038032Speter} 300190792Sgshapiro/* 300238032Speter** GROW_WLIST -- make the work list larger 300338032Speter** 300438032Speter** Parameters: 300590792Sgshapiro** qgrp -- the index for the queue group. 300690792Sgshapiro** qdir -- the index for the queue directory. 300738032Speter** 300838032Speter** Returns: 300938032Speter** none. 301038032Speter** 301138032Speter** Side Effects: 301238032Speter** Adds another QUEUESEGSIZE entries to WorkList if possible. 301338032Speter** It can fail if there isn't enough memory, so WorkListSize 301438032Speter** should be checked again upon return. 301538032Speter*/ 301638032Speter 301764562Sgshapirostatic void 301890792Sgshapirogrow_wlist(qgrp, qdir) 301990792Sgshapiro int qgrp; 302090792Sgshapiro int qdir; 302138032Speter{ 302238032Speter if (tTd(41, 1)) 302390792Sgshapiro sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); 302438032Speter if (WorkList == NULL) 302538032Speter { 302664562Sgshapiro WorkList = (WORK *) xalloc((sizeof *WorkList) * 302764562Sgshapiro (QUEUESEGSIZE + 1)); 302838032Speter WorkListSize = QUEUESEGSIZE; 302938032Speter } 303038032Speter else 303138032Speter { 303238032Speter int newsize = WorkListSize + QUEUESEGSIZE; 303390792Sgshapiro WORK *newlist = (WORK *) sm_realloc((char *) WorkList, 303490792Sgshapiro (unsigned) sizeof(WORK) * (newsize + 1)); 303538032Speter 303638032Speter if (newlist != NULL) 303738032Speter { 303838032Speter WorkListSize = newsize; 303938032Speter WorkList = newlist; 304038032Speter if (LogLevel > 1) 304138032Speter { 304264562Sgshapiro sm_syslog(LOG_INFO, NOQID, 304364562Sgshapiro "grew WorkList for %s to %d", 304490792Sgshapiro qid_printqueue(qgrp, qdir), 304564562Sgshapiro WorkListSize); 304638032Speter } 304738032Speter } 304838032Speter else if (LogLevel > 0) 304938032Speter { 305038032Speter sm_syslog(LOG_ALERT, NOQID, 305164562Sgshapiro "FAILED to grow WorkList for %s to %d", 305290792Sgshapiro qid_printqueue(qgrp, qdir), newsize); 305338032Speter } 305438032Speter } 305538032Speter if (tTd(41, 1)) 305690792Sgshapiro sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); 305738032Speter} 305890792Sgshapiro/* 305938032Speter** WORKCMPF0 -- simple priority-only compare function. 306038032Speter** 306138032Speter** Parameters: 306238032Speter** a -- the first argument. 306338032Speter** b -- the second argument. 306438032Speter** 306538032Speter** Returns: 306638032Speter** -1 if a < b 306738032Speter** 0 if a == b 306838032Speter** +1 if a > b 306938032Speter** 307038032Speter*/ 307138032Speter 307264562Sgshapirostatic int 307338032Speterworkcmpf0(a, b) 307438032Speter register WORK *a; 307538032Speter register WORK *b; 307638032Speter{ 307738032Speter long pa = a->w_pri; 307838032Speter long pb = b->w_pri; 307938032Speter 308038032Speter if (pa == pb) 308138032Speter return 0; 308238032Speter else if (pa > pb) 308338032Speter return 1; 308438032Speter else 308538032Speter return -1; 308638032Speter} 308790792Sgshapiro/* 308838032Speter** WORKCMPF1 -- first compare function for ordering work based on host name. 308938032Speter** 309038032Speter** Sorts on host name, lock status, and priority in that order. 309138032Speter** 309238032Speter** Parameters: 309338032Speter** a -- the first argument. 309438032Speter** b -- the second argument. 309538032Speter** 309638032Speter** Returns: 309738032Speter** <0 if a < b 309838032Speter** 0 if a == b 309938032Speter** >0 if a > b 310038032Speter** 310138032Speter*/ 310238032Speter 310364562Sgshapirostatic int 310438032Speterworkcmpf1(a, b) 310538032Speter register WORK *a; 310638032Speter register WORK *b; 310738032Speter{ 310838032Speter int i; 310938032Speter 311038032Speter /* host name */ 311138032Speter if (a->w_host != NULL && b->w_host == NULL) 311238032Speter return 1; 311338032Speter else if (a->w_host == NULL && b->w_host != NULL) 311438032Speter return -1; 311538032Speter if (a->w_host != NULL && b->w_host != NULL && 311638032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 311738032Speter return i; 311838032Speter 311938032Speter /* lock status */ 312038032Speter if (a->w_lock != b->w_lock) 312138032Speter return b->w_lock - a->w_lock; 312238032Speter 312338032Speter /* job priority */ 312473188Sgshapiro return workcmpf0(a, b); 312538032Speter} 312690792Sgshapiro/* 312738032Speter** WORKCMPF2 -- second compare function for ordering work based on host name. 312838032Speter** 312938032Speter** Sorts on lock status, host name, and priority in that order. 313038032Speter** 313138032Speter** Parameters: 313238032Speter** a -- the first argument. 313338032Speter** b -- the second argument. 313438032Speter** 313538032Speter** Returns: 313638032Speter** <0 if a < b 313738032Speter** 0 if a == b 313838032Speter** >0 if a > b 313938032Speter** 314038032Speter*/ 314138032Speter 314264562Sgshapirostatic int 314338032Speterworkcmpf2(a, b) 314438032Speter register WORK *a; 314538032Speter register WORK *b; 314638032Speter{ 314738032Speter int i; 314838032Speter 314938032Speter /* lock status */ 315038032Speter if (a->w_lock != b->w_lock) 315138032Speter return a->w_lock - b->w_lock; 315238032Speter 315338032Speter /* host name */ 315438032Speter if (a->w_host != NULL && b->w_host == NULL) 315538032Speter return 1; 315638032Speter else if (a->w_host == NULL && b->w_host != NULL) 315738032Speter return -1; 315838032Speter if (a->w_host != NULL && b->w_host != NULL && 315938032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 316038032Speter return i; 316138032Speter 316238032Speter /* job priority */ 316373188Sgshapiro return workcmpf0(a, b); 316438032Speter} 316590792Sgshapiro/* 316638032Speter** WORKCMPF3 -- simple submission-time-only compare function. 316738032Speter** 316838032Speter** Parameters: 316938032Speter** a -- the first argument. 317038032Speter** b -- the second argument. 317138032Speter** 317238032Speter** Returns: 317338032Speter** -1 if a < b 317438032Speter** 0 if a == b 317538032Speter** +1 if a > b 317638032Speter** 317738032Speter*/ 317838032Speter 317964562Sgshapirostatic int 318038032Speterworkcmpf3(a, b) 318138032Speter register WORK *a; 318238032Speter register WORK *b; 318338032Speter{ 318438032Speter if (a->w_ctime > b->w_ctime) 318538032Speter return 1; 318638032Speter else if (a->w_ctime < b->w_ctime) 318738032Speter return -1; 318838032Speter else 318938032Speter return 0; 319038032Speter} 319190792Sgshapiro/* 319264562Sgshapiro** WORKCMPF4 -- compare based on file name 319364562Sgshapiro** 319464562Sgshapiro** Parameters: 319564562Sgshapiro** a -- the first argument. 319664562Sgshapiro** b -- the second argument. 319764562Sgshapiro** 319864562Sgshapiro** Returns: 319964562Sgshapiro** -1 if a < b 320064562Sgshapiro** 0 if a == b 320164562Sgshapiro** +1 if a > b 320264562Sgshapiro** 320364562Sgshapiro*/ 320464562Sgshapiro 320564562Sgshapirostatic int 320664562Sgshapiroworkcmpf4(a, b) 320764562Sgshapiro register WORK *a; 320864562Sgshapiro register WORK *b; 320964562Sgshapiro{ 321064562Sgshapiro return strcmp(a->w_name, b->w_name); 321164562Sgshapiro} 321290792Sgshapiro/* 321390792Sgshapiro** WORKCMPF5 -- compare based on assigned random number 321490792Sgshapiro** 321590792Sgshapiro** Parameters: 321690792Sgshapiro** a -- the first argument (ignored). 321790792Sgshapiro** b -- the second argument (ignored). 321890792Sgshapiro** 321990792Sgshapiro** Returns: 322090792Sgshapiro** randomly 1/-1 322190792Sgshapiro*/ 322290792Sgshapiro 322390792Sgshapiro/* ARGSUSED0 */ 322490792Sgshapirostatic int 322590792Sgshapiroworkcmpf5(a, b) 322690792Sgshapiro register WORK *a; 322790792Sgshapiro register WORK *b; 322890792Sgshapiro{ 322990792Sgshapiro return (get_rand_mod(2)) ? 1 : -1; 323090792Sgshapiro} 323190792Sgshapiro/* 323290792Sgshapiro** WORKCMPF6 -- simple modification-time-only compare function. 323390792Sgshapiro** 323490792Sgshapiro** Parameters: 323590792Sgshapiro** a -- the first argument. 323690792Sgshapiro** b -- the second argument. 323790792Sgshapiro** 323890792Sgshapiro** Returns: 323990792Sgshapiro** -1 if a < b 324090792Sgshapiro** 0 if a == b 324190792Sgshapiro** +1 if a > b 324290792Sgshapiro** 324390792Sgshapiro*/ 324490792Sgshapiro 324590792Sgshapirostatic int 324690792Sgshapiroworkcmpf6(a, b) 324790792Sgshapiro register WORK *a; 324890792Sgshapiro register WORK *b; 324990792Sgshapiro{ 325090792Sgshapiro if (a->w_mtime > b->w_mtime) 325190792Sgshapiro return 1; 325290792Sgshapiro else if (a->w_mtime < b->w_mtime) 325390792Sgshapiro return -1; 325490792Sgshapiro else 325590792Sgshapiro return 0; 325690792Sgshapiro} 325790792Sgshapiro#if _FFR_RHS 325890792Sgshapiro/* 325990792Sgshapiro** WORKCMPF7 -- compare function for ordering work based on shuffled host name. 326090792Sgshapiro** 326190792Sgshapiro** Sorts on lock status, host name, and priority in that order. 326290792Sgshapiro** 326390792Sgshapiro** Parameters: 326490792Sgshapiro** a -- the first argument. 326590792Sgshapiro** b -- the second argument. 326690792Sgshapiro** 326790792Sgshapiro** Returns: 326890792Sgshapiro** <0 if a < b 326990792Sgshapiro** 0 if a == b 327090792Sgshapiro** >0 if a > b 327190792Sgshapiro** 327290792Sgshapiro*/ 327390792Sgshapiro 327490792Sgshapirostatic int 327590792Sgshapiroworkcmpf7(a, b) 327690792Sgshapiro register WORK *a; 327790792Sgshapiro register WORK *b; 327890792Sgshapiro{ 327990792Sgshapiro int i; 328090792Sgshapiro 328190792Sgshapiro /* lock status */ 328290792Sgshapiro if (a->w_lock != b->w_lock) 328390792Sgshapiro return a->w_lock - b->w_lock; 328490792Sgshapiro 328590792Sgshapiro /* host name */ 328690792Sgshapiro if (a->w_host != NULL && b->w_host == NULL) 328790792Sgshapiro return 1; 328890792Sgshapiro else if (a->w_host == NULL && b->w_host != NULL) 328990792Sgshapiro return -1; 329090792Sgshapiro if (a->w_host != NULL && b->w_host != NULL && 329190792Sgshapiro (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0) 329290792Sgshapiro return i; 329390792Sgshapiro 329490792Sgshapiro /* job priority */ 329590792Sgshapiro return workcmpf0(a, b); 329690792Sgshapiro} 329790792Sgshapiro#endif /* _FFR_RHS */ 329890792Sgshapiro/* 329964562Sgshapiro** STRREV -- reverse string 330064562Sgshapiro** 330164562Sgshapiro** Returns a pointer to a new string that is the reverse of 330264562Sgshapiro** the string pointed to by fwd. The space for the new 330364562Sgshapiro** string is obtained using xalloc(). 330464562Sgshapiro** 330564562Sgshapiro** Parameters: 330664562Sgshapiro** fwd -- the string to reverse. 330764562Sgshapiro** 330864562Sgshapiro** Returns: 330964562Sgshapiro** the reversed string. 331064562Sgshapiro*/ 331164562Sgshapiro 331264562Sgshapirostatic char * 331364562Sgshapirostrrev(fwd) 331464562Sgshapiro char *fwd; 331564562Sgshapiro{ 331664562Sgshapiro char *rev = NULL; 331764562Sgshapiro int len, cnt; 331864562Sgshapiro 331964562Sgshapiro len = strlen(fwd); 332064562Sgshapiro rev = xalloc(len + 1); 332164562Sgshapiro for (cnt = 0; cnt < len; ++cnt) 332264562Sgshapiro rev[cnt] = fwd[len - cnt - 1]; 332364562Sgshapiro rev[len] = '\0'; 332464562Sgshapiro return rev; 332564562Sgshapiro} 332690792Sgshapiro 332790792Sgshapiro#if _FFR_RHS 332890792Sgshapiro 332990792Sgshapiro#define NASCII 128 333090792Sgshapiro#define NCHAR 256 333190792Sgshapiro 333290792Sgshapirostatic unsigned char ShuffledAlphabet[NCHAR]; 333390792Sgshapiro 333490792Sgshapirovoid 333590792Sgshapiroinit_shuffle_alphabet() 333690792Sgshapiro{ 333790792Sgshapiro static bool init = false; 333890792Sgshapiro int i; 333990792Sgshapiro 334090792Sgshapiro if (init) 334190792Sgshapiro return; 334290792Sgshapiro 334390792Sgshapiro /* fill the ShuffledAlphabet */ 334490792Sgshapiro for (i = 0; i < NCHAR; i++) 334590792Sgshapiro ShuffledAlphabet[i] = i; 334690792Sgshapiro 334790792Sgshapiro /* mix it */ 334890792Sgshapiro for (i = 1; i < NCHAR; i++) 334990792Sgshapiro { 335090792Sgshapiro register int j = get_random() % NCHAR; 335190792Sgshapiro register int tmp; 335290792Sgshapiro 335390792Sgshapiro tmp = ShuffledAlphabet[j]; 335490792Sgshapiro ShuffledAlphabet[j] = ShuffledAlphabet[i]; 335590792Sgshapiro ShuffledAlphabet[i] = tmp; 335690792Sgshapiro } 335790792Sgshapiro 335890792Sgshapiro /* make it case insensitive */ 335990792Sgshapiro for (i = 'A'; i <= 'Z'; i++) 336090792Sgshapiro ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A']; 336190792Sgshapiro 336290792Sgshapiro /* fill the upper part */ 336390792Sgshapiro for (i = 0; i < NCHAR; i++) 336490792Sgshapiro ShuffledAlphabet[i + NCHAR] = ShuffledAlphabet[i]; 336590792Sgshapiro init = true; 336690792Sgshapiro} 336790792Sgshapiro 336890792Sgshapirostatic int 336990792Sgshapirosm_strshufflecmp(a, b) 337090792Sgshapiro char *a; 337190792Sgshapiro char *b; 337290792Sgshapiro{ 337390792Sgshapiro const unsigned char *us1 = (const unsigned char *) a; 337490792Sgshapiro const unsigned char *us2 = (const unsigned char *) b; 337590792Sgshapiro 337690792Sgshapiro while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++]) 337790792Sgshapiro { 337890792Sgshapiro if (*us1++ == '\0') 337990792Sgshapiro return 0; 338090792Sgshapiro } 338190792Sgshapiro return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]); 338290792Sgshapiro} 338390792Sgshapiro#endif /* _FFR_RHS */ 338490792Sgshapiro 338590792Sgshapiro/* 338638032Speter** DOWORK -- do a work request. 338738032Speter** 338838032Speter** Parameters: 338990792Sgshapiro** qgrp -- the index of the queue group for the job. 339090792Sgshapiro** qdir -- the index of the queue directory for the job. 339138032Speter** id -- the ID of the job to run. 339238032Speter** forkflag -- if set, run this in background. 339338032Speter** requeueflag -- if set, reinstantiate the queue quickly. 339438032Speter** This is used when expanding aliases in the queue. 339538032Speter** If forkflag is also set, it doesn't wait for the 339638032Speter** child. 339738032Speter** e - the envelope in which to run it. 339838032Speter** 339938032Speter** Returns: 340038032Speter** process id of process that is running the queue job. 340138032Speter** 340238032Speter** Side Effects: 340338032Speter** The work request is satisfied if possible. 340438032Speter*/ 340538032Speter 340638032Speterpid_t 340790792Sgshapirodowork(qgrp, qdir, id, forkflag, requeueflag, e) 340890792Sgshapiro int qgrp; 340990792Sgshapiro int qdir; 341038032Speter char *id; 341138032Speter bool forkflag; 341238032Speter bool requeueflag; 341338032Speter register ENVELOPE *e; 341438032Speter{ 341538032Speter register pid_t pid; 341690792Sgshapiro SM_RPOOL_T *rpool; 341738032Speter 341838032Speter if (tTd(40, 1)) 341990792Sgshapiro sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id); 342038032Speter 342138032Speter /* 342238032Speter ** Fork for work. 342338032Speter */ 342438032Speter 342538032Speter if (forkflag) 342638032Speter { 342764562Sgshapiro /* 342864562Sgshapiro ** Since the delivery may happen in a child and the 342964562Sgshapiro ** parent does not wait, the parent may close the 343064562Sgshapiro ** maps thereby removing any shared memory used by 343164562Sgshapiro ** the map. Therefore, close the maps now so the 343264562Sgshapiro ** child will dynamically open them if necessary. 343364562Sgshapiro */ 343464562Sgshapiro 343590792Sgshapiro closemaps(false); 343664562Sgshapiro 343738032Speter pid = fork(); 343838032Speter if (pid < 0) 343938032Speter { 344038032Speter syserr("dowork: cannot fork"); 344138032Speter return 0; 344238032Speter } 344338032Speter else if (pid > 0) 344438032Speter { 344538032Speter /* parent -- clean out connection cache */ 344690792Sgshapiro mci_flush(false, NULL); 344738032Speter } 344838032Speter else 344938032Speter { 345090792Sgshapiro /* 345190792Sgshapiro ** Initialize exception stack and default exception 345290792Sgshapiro ** handler for child process. 345390792Sgshapiro */ 345490792Sgshapiro 345590792Sgshapiro /* Reset global flags */ 345690792Sgshapiro RestartRequest = NULL; 345790792Sgshapiro RestartWorkGroup = false; 345890792Sgshapiro ShutdownRequest = NULL; 345990792Sgshapiro PendingSignal = 0; 346090792Sgshapiro CurrentPid = getpid(); 346190792Sgshapiro sm_exc_newthread(fatal_error); 346290792Sgshapiro 346390792Sgshapiro /* 346490792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 346590792Sgshapiro */ 346690792Sgshapiro 346790792Sgshapiro if (OpMode == MD_SMTP || 346890792Sgshapiro OpMode == MD_DAEMON || 346990792Sgshapiro MaxQueueChildren > 0) 347090792Sgshapiro { 347190792Sgshapiro proc_list_clear(); 347290792Sgshapiro sm_releasesignal(SIGCHLD); 347390792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 347490792Sgshapiro } 347590792Sgshapiro 347638032Speter /* child -- error messages to the transcript */ 347790792Sgshapiro QuickAbort = OnlyOneError = false; 347838032Speter } 347938032Speter } 348038032Speter else 348138032Speter { 348238032Speter pid = 0; 348338032Speter } 348438032Speter 348538032Speter if (pid == 0) 348638032Speter { 348738032Speter /* 348838032Speter ** CHILD 348938032Speter ** Lock the control file to avoid duplicate deliveries. 349038032Speter ** Then run the file as though we had just read it. 349138032Speter ** We save an idea of the temporary name so we 349238032Speter ** can recover on interrupt. 349338032Speter */ 349438032Speter 349590792Sgshapiro if (forkflag) 349690792Sgshapiro { 349790792Sgshapiro /* Reset global flags */ 349890792Sgshapiro RestartRequest = NULL; 349990792Sgshapiro RestartWorkGroup = false; 350090792Sgshapiro ShutdownRequest = NULL; 350190792Sgshapiro PendingSignal = 0; 350290792Sgshapiro } 350377349Sgshapiro 350438032Speter /* set basic modes, etc. */ 350590792Sgshapiro sm_clear_events(); 350664562Sgshapiro clearstats(); 350790792Sgshapiro rpool = sm_rpool_new_x(NULL); 350890792Sgshapiro clearenvelope(e, false, rpool); 350938032Speter e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 351064562Sgshapiro set_delivery_mode(SM_DELIVER, e); 351138032Speter e->e_errormode = EM_MAIL; 351238032Speter e->e_id = id; 351390792Sgshapiro e->e_qgrp = qgrp; 351490792Sgshapiro e->e_qdir = qdir; 351590792Sgshapiro GrabTo = UseErrorsTo = false; 351638032Speter ExitStat = EX_OK; 351738032Speter if (forkflag) 351838032Speter { 351938032Speter disconnect(1, e); 352090792Sgshapiro set_op_mode(MD_QUEUERUN); 352138032Speter } 352290792Sgshapiro sm_setproctitle(true, e, "%s from queue", qid_printname(e)); 352338032Speter if (LogLevel > 76) 352490792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d", 352590792Sgshapiro (int) CurrentPid); 352638032Speter 352738032Speter /* don't use the headers from sendmail.cf... */ 352838032Speter e->e_header = NULL; 352938032Speter 353038032Speter /* read the queue control file -- return if locked */ 353190792Sgshapiro if (!readqf(e, false)) 353238032Speter { 353338032Speter if (tTd(40, 4) && e->e_id != NULL) 353490792Sgshapiro sm_dprintf("readqf(%s) failed\n", 353564562Sgshapiro qid_printname(e)); 353638032Speter e->e_id = NULL; 353738032Speter if (forkflag) 353890792Sgshapiro finis(false, true, EX_OK); 353938032Speter else 354090792Sgshapiro { 354190792Sgshapiro /* adding this frees 8 bytes */ 354290792Sgshapiro clearenvelope(e, false, rpool); 354390792Sgshapiro 354490792Sgshapiro /* adding this frees 12 bytes */ 354590792Sgshapiro sm_rpool_free(rpool); 354690792Sgshapiro e->e_rpool = NULL; 354738032Speter return 0; 354890792Sgshapiro } 354938032Speter } 355038032Speter 355138032Speter e->e_flags |= EF_INQUEUE; 355290792Sgshapiro eatheader(e, requeueflag, true); 355338032Speter 355438032Speter if (requeueflag) 355590792Sgshapiro queueup(e, false, false); 355638032Speter 355738032Speter /* do the delivery */ 355838032Speter sendall(e, SM_DELIVER); 355938032Speter 356038032Speter /* finish up and exit */ 356138032Speter if (forkflag) 356290792Sgshapiro finis(true, true, ExitStat); 356338032Speter else 356490792Sgshapiro { 356590792Sgshapiro dropenvelope(e, true, false); 356690792Sgshapiro sm_rpool_free(rpool); 356790792Sgshapiro e->e_rpool = NULL; 356890792Sgshapiro } 356938032Speter } 357038032Speter e->e_id = NULL; 357138032Speter return pid; 357238032Speter} 357390792Sgshapiro 357490792Sgshapiro/* 357590792Sgshapiro** DOWORKLIST -- process a list of envelopes as work requests 357690792Sgshapiro** 357790792Sgshapiro** Similar to dowork(), except that after forking, it processes an 357890792Sgshapiro** envelope and its siblings, treating each envelope as a work request. 357990792Sgshapiro** 358090792Sgshapiro** Parameters: 358190792Sgshapiro** el -- envelope to be processed including its siblings. 358290792Sgshapiro** forkflag -- if set, run this in background. 358390792Sgshapiro** requeueflag -- if set, reinstantiate the queue quickly. 358490792Sgshapiro** This is used when expanding aliases in the queue. 358590792Sgshapiro** If forkflag is also set, it doesn't wait for the 358690792Sgshapiro** child. 358790792Sgshapiro** 358890792Sgshapiro** Returns: 358990792Sgshapiro** process id of process that is running the queue job. 359090792Sgshapiro** 359190792Sgshapiro** Side Effects: 359290792Sgshapiro** The work request is satisfied if possible. 359390792Sgshapiro*/ 359490792Sgshapiro 359590792Sgshapiropid_t 359690792Sgshapirodoworklist(el, forkflag, requeueflag) 359790792Sgshapiro ENVELOPE *el; 359890792Sgshapiro bool forkflag; 359990792Sgshapiro bool requeueflag; 360090792Sgshapiro{ 360190792Sgshapiro register pid_t pid; 360290792Sgshapiro ENVELOPE *ei; 360390792Sgshapiro 360490792Sgshapiro if (tTd(40, 1)) 360590792Sgshapiro sm_dprintf("doworklist()\n"); 360690792Sgshapiro 360790792Sgshapiro /* 360890792Sgshapiro ** Fork for work. 360990792Sgshapiro */ 361090792Sgshapiro 361190792Sgshapiro if (forkflag) 361290792Sgshapiro { 361390792Sgshapiro /* 361490792Sgshapiro ** Since the delivery may happen in a child and the 361590792Sgshapiro ** parent does not wait, the parent may close the 361690792Sgshapiro ** maps thereby removing any shared memory used by 361790792Sgshapiro ** the map. Therefore, close the maps now so the 361890792Sgshapiro ** child will dynamically open them if necessary. 361990792Sgshapiro */ 362090792Sgshapiro 362190792Sgshapiro closemaps(false); 362290792Sgshapiro 362390792Sgshapiro pid = fork(); 362490792Sgshapiro if (pid < 0) 362590792Sgshapiro { 362690792Sgshapiro syserr("doworklist: cannot fork"); 362790792Sgshapiro return 0; 362890792Sgshapiro } 362990792Sgshapiro else if (pid > 0) 363090792Sgshapiro { 363190792Sgshapiro /* parent -- clean out connection cache */ 363290792Sgshapiro mci_flush(false, NULL); 363390792Sgshapiro } 363490792Sgshapiro else 363590792Sgshapiro { 363690792Sgshapiro /* 363790792Sgshapiro ** Initialize exception stack and default exception 363890792Sgshapiro ** handler for child process. 363990792Sgshapiro */ 364090792Sgshapiro 364190792Sgshapiro /* Reset global flags */ 364290792Sgshapiro RestartRequest = NULL; 364390792Sgshapiro RestartWorkGroup = false; 364490792Sgshapiro ShutdownRequest = NULL; 364590792Sgshapiro PendingSignal = 0; 364690792Sgshapiro CurrentPid = getpid(); 364790792Sgshapiro sm_exc_newthread(fatal_error); 364890792Sgshapiro 364990792Sgshapiro /* 365090792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 365190792Sgshapiro */ 365290792Sgshapiro 365390792Sgshapiro if (OpMode == MD_SMTP || 365490792Sgshapiro OpMode == MD_DAEMON || 365590792Sgshapiro MaxQueueChildren > 0) 365690792Sgshapiro { 365790792Sgshapiro proc_list_clear(); 365890792Sgshapiro sm_releasesignal(SIGCHLD); 365990792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 366090792Sgshapiro } 366190792Sgshapiro 366290792Sgshapiro /* child -- error messages to the transcript */ 366390792Sgshapiro QuickAbort = OnlyOneError = false; 366490792Sgshapiro } 366590792Sgshapiro } 366690792Sgshapiro else 366790792Sgshapiro { 366890792Sgshapiro pid = 0; 366990792Sgshapiro } 367090792Sgshapiro 367190792Sgshapiro if (pid != 0) 367290792Sgshapiro return pid; 367390792Sgshapiro 367490792Sgshapiro /* 367590792Sgshapiro ** IN CHILD 367690792Sgshapiro ** Lock the control file to avoid duplicate deliveries. 367790792Sgshapiro ** Then run the file as though we had just read it. 367890792Sgshapiro ** We save an idea of the temporary name so we 367990792Sgshapiro ** can recover on interrupt. 368090792Sgshapiro */ 368190792Sgshapiro 368290792Sgshapiro if (forkflag) 368390792Sgshapiro { 368490792Sgshapiro /* Reset global flags */ 368590792Sgshapiro RestartRequest = NULL; 368690792Sgshapiro RestartWorkGroup = false; 368790792Sgshapiro ShutdownRequest = NULL; 368890792Sgshapiro PendingSignal = 0; 368990792Sgshapiro } 369090792Sgshapiro 369190792Sgshapiro /* set basic modes, etc. */ 369290792Sgshapiro sm_clear_events(); 369390792Sgshapiro clearstats(); 369490792Sgshapiro GrabTo = UseErrorsTo = false; 369590792Sgshapiro ExitStat = EX_OK; 369690792Sgshapiro if (forkflag) 369790792Sgshapiro { 369890792Sgshapiro disconnect(1, el); 369990792Sgshapiro set_op_mode(MD_QUEUERUN); 370090792Sgshapiro } 370190792Sgshapiro if (LogLevel > 76) 370290792Sgshapiro sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d", 370390792Sgshapiro (int) CurrentPid); 370490792Sgshapiro 370590792Sgshapiro for (ei = el; ei != NULL; ei = ei->e_sibling) 370690792Sgshapiro { 370790792Sgshapiro ENVELOPE e; 370890792Sgshapiro SM_RPOOL_T *rpool; 370990792Sgshapiro 371090792Sgshapiro if (WILL_BE_QUEUED(ei->e_sendmode)) 371190792Sgshapiro continue; 371290792Sgshapiro#if _FFR_QUARANTINE 371390792Sgshapiro else if (QueueMode != QM_QUARANTINE && 371490792Sgshapiro ei->e_quarmsg != NULL) 371590792Sgshapiro continue; 371690792Sgshapiro#endif /* _FFR_QUARANTINE */ 371790792Sgshapiro 371890792Sgshapiro rpool = sm_rpool_new_x(NULL); 371990792Sgshapiro clearenvelope(&e, true, rpool); 372090792Sgshapiro e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 372190792Sgshapiro set_delivery_mode(SM_DELIVER, &e); 372290792Sgshapiro e.e_errormode = EM_MAIL; 372390792Sgshapiro e.e_id = ei->e_id; 372490792Sgshapiro e.e_qgrp = ei->e_qgrp; 372590792Sgshapiro e.e_qdir = ei->e_qdir; 372690792Sgshapiro openxscript(&e); 372790792Sgshapiro sm_setproctitle(true, &e, "%s from queue", qid_printname(&e)); 372890792Sgshapiro 372990792Sgshapiro /* don't use the headers from sendmail.cf... */ 373090792Sgshapiro e.e_header = NULL; 373190792Sgshapiro CurEnv = &e; 373290792Sgshapiro 373390792Sgshapiro /* read the queue control file -- return if locked */ 373490792Sgshapiro if (readqf(&e, false)) 373590792Sgshapiro { 373690792Sgshapiro e.e_flags |= EF_INQUEUE; 373790792Sgshapiro eatheader(&e, requeueflag, true); 373890792Sgshapiro 373990792Sgshapiro if (requeueflag) 374090792Sgshapiro queueup(&e, false, false); 374190792Sgshapiro 374290792Sgshapiro /* do the delivery */ 374390792Sgshapiro sendall(&e, SM_DELIVER); 374490792Sgshapiro dropenvelope(&e, true, false); 374590792Sgshapiro } 374690792Sgshapiro else 374790792Sgshapiro { 374890792Sgshapiro if (tTd(40, 4) && e.e_id != NULL) 374990792Sgshapiro sm_dprintf("readqf(%s) failed\n", 375090792Sgshapiro qid_printname(&e)); 375190792Sgshapiro } 375290792Sgshapiro sm_rpool_free(rpool); 375390792Sgshapiro ei->e_id = NULL; 375490792Sgshapiro } 375590792Sgshapiro 375690792Sgshapiro /* restore CurEnv */ 375790792Sgshapiro CurEnv = el; 375890792Sgshapiro 375990792Sgshapiro /* finish up and exit */ 376090792Sgshapiro if (forkflag) 376190792Sgshapiro finis(true, true, ExitStat); 376290792Sgshapiro return 0; 376390792Sgshapiro} 376490792Sgshapiro/* 376538032Speter** READQF -- read queue file and set up environment. 376638032Speter** 376738032Speter** Parameters: 376838032Speter** e -- the envelope of the job to run. 376990792Sgshapiro** openonly -- only open the qf (returned as e_lockfp) 377038032Speter** 377138032Speter** Returns: 377290792Sgshapiro** true if it successfully read the queue file. 377390792Sgshapiro** false otherwise. 377438032Speter** 377538032Speter** Side Effects: 377638032Speter** The queue file is returned locked. 377738032Speter*/ 377838032Speter 377964562Sgshapirostatic bool 378090792Sgshapiroreadqf(e, openonly) 378138032Speter register ENVELOPE *e; 378290792Sgshapiro bool openonly; 378338032Speter{ 378490792Sgshapiro register SM_FILE_T *qfp; 378538032Speter ADDRESS *ctladdr; 378677349Sgshapiro struct stat st, stf; 378738032Speter char *bp; 378838032Speter int qfver = 0; 378938032Speter long hdrsize = 0; 379038032Speter register char *p; 379190792Sgshapiro char *frcpt = NULL; 379238032Speter char *orcpt = NULL; 379390792Sgshapiro bool nomore = false; 379490792Sgshapiro bool bogus = false; 379564562Sgshapiro MODE_T qsafe; 379694334Sgshapiro char *err; 379764562Sgshapiro char qf[MAXPATHLEN]; 379838032Speter char buf[MAXLINE]; 379938032Speter 380038032Speter /* 380138032Speter ** Read and process the file. 380238032Speter */ 380338032Speter 380490792Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof qf); 380590792Sgshapiro qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR, NULL); 380638032Speter if (qfp == NULL) 380738032Speter { 380864562Sgshapiro int save_errno = errno; 380964562Sgshapiro 381038032Speter if (tTd(40, 8)) 381190792Sgshapiro sm_dprintf("readqf(%s): sm_io_open failure (%s)\n", 381290792Sgshapiro qf, sm_errstring(errno)); 381364562Sgshapiro errno = save_errno; 381464562Sgshapiro if (errno != ENOENT 381564562Sgshapiro ) 381638032Speter syserr("readqf: no control file %s", qf); 381790792Sgshapiro RELEASE_QUEUE; 381890792Sgshapiro return false; 381938032Speter } 382038032Speter 382190792Sgshapiro if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL, 382290792Sgshapiro LOCK_EX|LOCK_NB)) 382338032Speter { 382438032Speter /* being processed by another queuer */ 382564562Sgshapiro if (Verbose) 382690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 382790792Sgshapiro "%s: locked\n", e->e_id); 382864562Sgshapiro if (tTd(40, 8)) 382990792Sgshapiro sm_dprintf("%s: locked\n", e->e_id); 383038032Speter if (LogLevel > 19) 383138032Speter sm_syslog(LOG_DEBUG, e->e_id, "locked"); 383290792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 383390792Sgshapiro RELEASE_QUEUE; 383490792Sgshapiro return false; 383538032Speter } 383638032Speter 383738032Speter /* 383877349Sgshapiro ** Prevent locking race condition. 383977349Sgshapiro ** 384077349Sgshapiro ** Process A: readqf(): qfp = fopen(qffile) 384177349Sgshapiro ** Process B: queueup(): rename(tf, qf) 384277349Sgshapiro ** Process B: unlocks(tf) 384377349Sgshapiro ** Process A: lockfile(qf); 384477349Sgshapiro ** 384577349Sgshapiro ** Process A (us) has the old qf file (before the rename deleted 384677349Sgshapiro ** the directory entry) and will be delivering based on old data. 384777349Sgshapiro ** This can lead to multiple deliveries of the same recipients. 384877349Sgshapiro ** 384977349Sgshapiro ** Catch this by checking if the underlying qf file has changed 385077349Sgshapiro ** *after* acquiring our lock and if so, act as though the file 385177349Sgshapiro ** was still locked (i.e., just return like the lockfile() case 385277349Sgshapiro ** above. 385338032Speter */ 385438032Speter 385577349Sgshapiro if (stat(qf, &stf) < 0 || 385690792Sgshapiro fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0) 385738032Speter { 385838032Speter /* must have been being processed by someone else */ 385938032Speter if (tTd(40, 8)) 386090792Sgshapiro sm_dprintf("readqf(%s): [f]stat failure (%s)\n", 386190792Sgshapiro qf, sm_errstring(errno)); 386290792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 386390792Sgshapiro RELEASE_QUEUE; 386490792Sgshapiro return false; 386538032Speter } 386638032Speter 386777349Sgshapiro if (st.st_nlink != stf.st_nlink || 386877349Sgshapiro st.st_dev != stf.st_dev || 386990792Sgshapiro ST_INODE(st) != ST_INODE(stf) || 387090792Sgshapiro#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 387177349Sgshapiro st.st_gen != stf.st_gen || 387290792Sgshapiro#endif /* HAS_ST_GEN && 0 */ 387377349Sgshapiro st.st_uid != stf.st_uid || 387477349Sgshapiro st.st_gid != stf.st_gid || 387577349Sgshapiro st.st_size != stf.st_size) 387677349Sgshapiro { 387777349Sgshapiro /* changed after opened */ 387877349Sgshapiro if (Verbose) 387990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 388090792Sgshapiro "%s: changed\n", e->e_id); 388177349Sgshapiro if (tTd(40, 8)) 388290792Sgshapiro sm_dprintf("%s: changed\n", e->e_id); 388377349Sgshapiro if (LogLevel > 19) 388477349Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "changed"); 388590792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 388690792Sgshapiro RELEASE_QUEUE; 388790792Sgshapiro return false; 388877349Sgshapiro } 388977349Sgshapiro 389077349Sgshapiro /* 389177349Sgshapiro ** Check the queue file for plausibility to avoid attacks. 389277349Sgshapiro */ 389377349Sgshapiro 389464562Sgshapiro qsafe = S_IWOTH|S_IWGRP; 389564562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 389664562Sgshapiro qsafe &= ~S_IWGRP; 389764562Sgshapiro 389890792Sgshapiro bogus = st.st_uid != geteuid() && 389990792Sgshapiro st.st_uid != TrustedUid && 390090792Sgshapiro geteuid() != RealUid; 390190792Sgshapiro 390290792Sgshapiro /* 390390792Sgshapiro ** If this qf file results from a set-group-ID binary, then 390490792Sgshapiro ** we check whether the directory is group-writable, 390590792Sgshapiro ** the queue file mode contains the group-writable bit, and 390690792Sgshapiro ** the groups are the same. 390790792Sgshapiro ** Notice: this requires that the set-group-ID binary is used to 390890792Sgshapiro ** run the queue! 390990792Sgshapiro */ 391090792Sgshapiro 391190792Sgshapiro if (bogus && st.st_gid == getegid() && UseMSP) 391238032Speter { 391390792Sgshapiro char delim; 391490792Sgshapiro struct stat dst; 391590792Sgshapiro 391690792Sgshapiro bp = SM_LAST_DIR_DELIM(qf); 391790792Sgshapiro if (bp == NULL) 391890792Sgshapiro delim = '\0'; 391990792Sgshapiro else 392090792Sgshapiro { 392190792Sgshapiro delim = *bp; 392290792Sgshapiro *bp = '\0'; 392390792Sgshapiro } 392490792Sgshapiro if (stat(delim == '\0' ? "." : qf, &dst) < 0) 392590792Sgshapiro syserr("readqf: cannot stat directory %s", 392690792Sgshapiro delim == '\0' ? "." : qf); 392790792Sgshapiro else 392890792Sgshapiro { 392990792Sgshapiro bogus = !(bitset(S_IWGRP, QueueFileMode) && 393090792Sgshapiro bitset(S_IWGRP, dst.st_mode) && 393190792Sgshapiro dst.st_gid == st.st_gid); 393290792Sgshapiro } 393390792Sgshapiro if (delim != '\0') 393490792Sgshapiro *bp = delim; 393590792Sgshapiro } 393690792Sgshapiro if (!bogus) 393790792Sgshapiro bogus = bitset(qsafe, st.st_mode); 393890792Sgshapiro if (bogus) 393990792Sgshapiro { 394038032Speter if (LogLevel > 0) 394138032Speter { 394238032Speter sm_syslog(LOG_ALERT, e->e_id, 394390792Sgshapiro "bogus queue file, uid=%d, gid=%d, mode=%o", 394490792Sgshapiro st.st_uid, st.st_gid, st.st_mode); 394538032Speter } 394638032Speter if (tTd(40, 8)) 394790792Sgshapiro sm_dprintf("readqf(%s): bogus file\n", qf); 394890792Sgshapiro e->e_flags |= EF_INQUEUE; 394990792Sgshapiro if (!openonly) 395090792Sgshapiro loseqfile(e, "bogus file uid/gid in mqueue"); 395190792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 395290792Sgshapiro RELEASE_QUEUE; 395390792Sgshapiro return false; 395438032Speter } 395538032Speter 395638032Speter if (st.st_size == 0) 395738032Speter { 395838032Speter /* must be a bogus file -- if also old, just remove it */ 395990792Sgshapiro if (!openonly && st.st_ctime + 10 * 60 < curtime()) 396038032Speter { 396190792Sgshapiro (void) xunlink(queuename(e, DATAFL_LETTER)); 396290792Sgshapiro (void) xunlink(queuename(e, ANYQFL_LETTER)); 396338032Speter } 396490792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 396590792Sgshapiro RELEASE_QUEUE; 396690792Sgshapiro return false; 396738032Speter } 396838032Speter 396938032Speter if (st.st_nlink == 0) 397038032Speter { 397138032Speter /* 397238032Speter ** Race condition -- we got a file just as it was being 397338032Speter ** unlinked. Just assume it is zero length. 397438032Speter */ 397538032Speter 397690792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 397790792Sgshapiro RELEASE_QUEUE; 397890792Sgshapiro return false; 397938032Speter } 398038032Speter 398190792Sgshapiro#if _FFR_TRUSTED_QF 398290792Sgshapiro /* 398390792Sgshapiro ** If we don't own the file mark it as unsafe. 398490792Sgshapiro ** However, allow TrustedUser to own it as well 398590792Sgshapiro ** in case TrustedUser manipulates the queue. 398690792Sgshapiro */ 398790792Sgshapiro 398890792Sgshapiro if (st.st_uid != geteuid() && st.st_uid != TrustedUid) 398990792Sgshapiro e->e_flags |= EF_UNSAFE; 399090792Sgshapiro#else /* _FFR_TRUSTED_QF */ 399190792Sgshapiro /* If we don't own the file mark it as unsafe */ 399290792Sgshapiro if (st.st_uid != geteuid()) 399390792Sgshapiro e->e_flags |= EF_UNSAFE; 399490792Sgshapiro#endif /* _FFR_TRUSTED_QF */ 399590792Sgshapiro 399638032Speter /* good file -- save this lock */ 399738032Speter e->e_lockfp = qfp; 399838032Speter 399990792Sgshapiro /* Just wanted the open file */ 400090792Sgshapiro if (openonly) 400190792Sgshapiro return true; 400290792Sgshapiro 400338032Speter /* do basic system initialization */ 400438032Speter initsys(e); 400590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 400638032Speter 400738032Speter LineNumber = 0; 400838032Speter e->e_flags |= EF_GLOBALERRS; 400990792Sgshapiro set_op_mode(MD_QUEUERUN); 401038032Speter ctladdr = NULL; 401190792Sgshapiro#if _FFR_QUARANTINE 401290792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 401390792Sgshapiro#endif /* _FFR_QUARANTINE */ 401490792Sgshapiro e->e_dfqgrp = e->e_qgrp; 401590792Sgshapiro e->e_dfqdir = e->e_qdir; 401690792Sgshapiro#if _FFR_QUEUE_MACRO 401790792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{queue}"), 401890792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 401990792Sgshapiro#endif /* _FFR_QUEUE_MACRO */ 402038032Speter e->e_dfino = -1; 402138032Speter e->e_msgsize = -1; 402290792Sgshapiro#if _FFR_QUEUEDELAY 402364562Sgshapiro e->e_queuealg = QD_LINEAR; 402464562Sgshapiro e->e_queuedelay = (time_t) 0; 402590792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 402638032Speter while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) 402738032Speter { 402890792Sgshapiro unsigned long qflags; 402938032Speter ADDRESS *q; 403090792Sgshapiro int r; 403171345Sgshapiro time_t now; 403238032Speter auto char *ep; 403338032Speter 403438032Speter if (tTd(40, 4)) 403590792Sgshapiro sm_dprintf("+++++ %s\n", bp); 403638032Speter if (nomore) 403738032Speter { 403838032Speter /* hack attack */ 403990792Sgshapiro hackattack: 404090792Sgshapiro syserr("SECURITY ALERT: extra or bogus data in queue file: %s", 404190792Sgshapiro bp); 404294334Sgshapiro err = "bogus queue line"; 404394334Sgshapiro goto fail; 404438032Speter } 404538032Speter switch (bp[0]) 404638032Speter { 404790792Sgshapiro case 'A': /* AUTH= parameter */ 404890792Sgshapiro if (!xtextok(&bp[1])) 404990792Sgshapiro goto hackattack; 405090792Sgshapiro e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 405190792Sgshapiro break; 405238032Speter 405390792Sgshapiro case 'B': /* body type */ 405490792Sgshapiro r = check_bodytype(&bp[1]); 405590792Sgshapiro if (!BODYTYPE_VALID(r)) 405690792Sgshapiro goto hackattack; 405790792Sgshapiro e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 405890792Sgshapiro break; 405990792Sgshapiro 406038032Speter case 'C': /* specify controlling user */ 406190792Sgshapiro ctladdr = setctluser(&bp[1], qfver, e); 406238032Speter break; 406338032Speter 406490792Sgshapiro case 'D': /* data file name */ 406590792Sgshapiro /* obsolete -- ignore */ 406638032Speter break; 406738032Speter 406890792Sgshapiro case 'd': /* data file directory name */ 406938032Speter { 407090792Sgshapiro int qgrp, qdir; 407190792Sgshapiro 407290792Sgshapiro#if _FFR_MSP_PARANOIA 407390792Sgshapiro /* forbid queue groups in MSP? */ 407490792Sgshapiro if (UseMSP) 407590792Sgshapiro goto hackattack; 407690792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 407790792Sgshapiro for (qgrp = 0; 407890792Sgshapiro qgrp < NumQueue && Queue[qgrp] != NULL; 407990792Sgshapiro ++qgrp) 408038032Speter { 408190792Sgshapiro for (qdir = 0; 408290792Sgshapiro qdir < Queue[qgrp]->qg_numqueues; 408390792Sgshapiro ++qdir) 408438032Speter { 408590792Sgshapiro if (strcmp(&bp[1], 408690792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name) 408790792Sgshapiro == 0) 408890792Sgshapiro { 408990792Sgshapiro e->e_dfqgrp = qgrp; 409090792Sgshapiro e->e_dfqdir = qdir; 409190792Sgshapiro goto done; 409290792Sgshapiro } 409338032Speter } 409438032Speter } 409594334Sgshapiro err = "bogus queue file directory"; 409694334Sgshapiro goto fail; 409790792Sgshapiro done: 409890792Sgshapiro break; 409938032Speter } 410038032Speter 410138032Speter case 'E': /* specify error recipient */ 410238032Speter /* no longer used */ 410338032Speter break; 410438032Speter 410590792Sgshapiro case 'F': /* flag bits */ 410690792Sgshapiro if (strncmp(bp, "From ", 5) == 0) 410790792Sgshapiro { 410890792Sgshapiro /* we are being spoofed! */ 410990792Sgshapiro syserr("SECURITY ALERT: bogus qf line %s", bp); 411094334Sgshapiro err = "bogus queue line"; 411194334Sgshapiro goto fail; 411290792Sgshapiro } 411390792Sgshapiro for (p = &bp[1]; *p != '\0'; p++) 411490792Sgshapiro { 411590792Sgshapiro switch (*p) 411690792Sgshapiro { 411790792Sgshapiro case '8': /* has 8 bit data */ 411890792Sgshapiro e->e_flags |= EF_HAS8BIT; 411990792Sgshapiro break; 412038032Speter 412190792Sgshapiro case 'b': /* delete Bcc: header */ 412290792Sgshapiro e->e_flags |= EF_DELETE_BCC; 412390792Sgshapiro break; 412438032Speter 412590792Sgshapiro case 'd': /* envelope has DSN RET= */ 412690792Sgshapiro e->e_flags |= EF_RET_PARAM; 412790792Sgshapiro break; 412838032Speter 412990792Sgshapiro case 'n': /* don't return body */ 413090792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 413190792Sgshapiro break; 413290792Sgshapiro 413390792Sgshapiro case 'r': /* response */ 413490792Sgshapiro e->e_flags |= EF_RESPONSE; 413590792Sgshapiro break; 413690792Sgshapiro 413790792Sgshapiro case 's': /* split */ 413890792Sgshapiro e->e_flags |= EF_SPLIT; 413990792Sgshapiro break; 414090792Sgshapiro 414190792Sgshapiro case 'w': /* warning sent */ 414290792Sgshapiro e->e_flags |= EF_WARNING; 414390792Sgshapiro break; 414490792Sgshapiro } 414590792Sgshapiro } 414638032Speter break; 414738032Speter 414890792Sgshapiro#if _FFR_QUEUEDELAY 414990792Sgshapiro case 'G': /* queue delay algorithm */ 415090792Sgshapiro e->e_queuealg = atoi(&buf[1]); 415138032Speter break; 415290792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 415338032Speter 415490792Sgshapiro#if _FFR_QUARANTINE 415590792Sgshapiro case 'q': /* quarantine reason */ 415690792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 415790792Sgshapiro macdefine(&e->e_macro, A_PERM, 415890792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 415938032Speter break; 416090792Sgshapiro#endif /* _FFR_QUARANTINE */ 416138032Speter 416290792Sgshapiro case 'H': /* header */ 416394334Sgshapiro 416494334Sgshapiro /* 416594334Sgshapiro ** count size before chompheader() destroys the line. 416694334Sgshapiro ** this isn't accurate due to macro expansion, but 416794334Sgshapiro ** better than before. "+3" to skip H?? at least. 416894334Sgshapiro */ 416994334Sgshapiro 417094334Sgshapiro hdrsize += strlen(bp + 3); 417190792Sgshapiro (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); 417238032Speter break; 417338032Speter 417438032Speter case 'I': /* data file's inode number */ 417538032Speter /* regenerated below */ 417638032Speter break; 417738032Speter 417880785Sgshapiro case 'K': /* time of last delivery attempt */ 417938032Speter e->e_dtime = atol(&buf[1]); 418038032Speter break; 418138032Speter 418290792Sgshapiro case 'L': /* Solaris Content-Length: */ 418390792Sgshapiro case 'M': /* message */ 418490792Sgshapiro /* ignore this; we want a new message next time */ 418564562Sgshapiro break; 418664562Sgshapiro 418738032Speter case 'N': /* number of delivery attempts */ 418838032Speter e->e_ntries = atoi(&buf[1]); 418938032Speter 419038032Speter /* if this has been tried recently, let it be */ 419171345Sgshapiro now = curtime(); 419271345Sgshapiro if (e->e_ntries > 0 && e->e_dtime <= now && 419371345Sgshapiro now < e->e_dtime + queuedelay(e)) 419438032Speter { 419564562Sgshapiro char *howlong; 419638032Speter 419790792Sgshapiro howlong = pintvl(now - e->e_dtime, true); 419864562Sgshapiro if (Verbose) 419990792Sgshapiro (void) sm_io_fprintf(smioout, 420090792Sgshapiro SM_TIME_DEFAULT, 420190792Sgshapiro "%s: too young (%s)\n", 420290792Sgshapiro e->e_id, howlong); 420364562Sgshapiro if (tTd(40, 8)) 420490792Sgshapiro sm_dprintf("%s: too young (%s)\n", 420538032Speter e->e_id, howlong); 420638032Speter if (LogLevel > 19) 420738032Speter sm_syslog(LOG_DEBUG, e->e_id, 420864562Sgshapiro "too young (%s)", 420964562Sgshapiro howlong); 421038032Speter e->e_id = NULL; 421138032Speter unlockqueue(e); 421290792Sgshapiro RELEASE_QUEUE; 421390792Sgshapiro return false; 421438032Speter } 421590792Sgshapiro macdefine(&e->e_macro, A_TEMP, 421690792Sgshapiro macid("{ntries}"), &buf[1]); 421764562Sgshapiro 421890792Sgshapiro#if NAMED_BIND 421964562Sgshapiro /* adjust BIND parameters immediately */ 422064562Sgshapiro if (e->e_ntries == 0) 422164562Sgshapiro { 422264562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 422364562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 422464562Sgshapiro } 422564562Sgshapiro else 422664562Sgshapiro { 422764562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_NORMAL]; 422864562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL]; 422964562Sgshapiro } 423090792Sgshapiro#endif /* NAMED_BIND */ 423138032Speter break; 423238032Speter 423338032Speter case 'P': /* message priority */ 423438032Speter e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 423538032Speter break; 423638032Speter 423790792Sgshapiro case 'Q': /* original recipient */ 423890792Sgshapiro orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 423990792Sgshapiro break; 424090792Sgshapiro 424198841Sgshapiro case 'r': /* final recipient */ 424290792Sgshapiro frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 424390792Sgshapiro break; 424490792Sgshapiro 424590792Sgshapiro case 'R': /* specify recipient */ 424690792Sgshapiro p = bp; 424790792Sgshapiro qflags = 0; 424890792Sgshapiro if (qfver >= 1) 424938032Speter { 425090792Sgshapiro /* get flag bits */ 425190792Sgshapiro while (*++p != '\0' && *p != ':') 425238032Speter { 425390792Sgshapiro switch (*p) 425490792Sgshapiro { 425590792Sgshapiro case 'N': 425690792Sgshapiro qflags |= QHASNOTIFY; 425790792Sgshapiro break; 425838032Speter 425990792Sgshapiro case 'S': 426090792Sgshapiro qflags |= QPINGONSUCCESS; 426190792Sgshapiro break; 426238032Speter 426390792Sgshapiro case 'F': 426490792Sgshapiro qflags |= QPINGONFAILURE; 426590792Sgshapiro break; 426638032Speter 426790792Sgshapiro case 'D': 426890792Sgshapiro qflags |= QPINGONDELAY; 426990792Sgshapiro break; 427038032Speter 427190792Sgshapiro case 'P': 427290792Sgshapiro qflags |= QPRIMARY; 427390792Sgshapiro break; 427438032Speter 427590792Sgshapiro case 'A': 427690792Sgshapiro if (ctladdr != NULL) 427790792Sgshapiro ctladdr->q_flags |= QALIAS; 427890792Sgshapiro break; 427990792Sgshapiro 428090792Sgshapiro default: /* ignore or complain? */ 428190792Sgshapiro break; 428290792Sgshapiro } 428338032Speter } 428438032Speter } 428590792Sgshapiro else 428690792Sgshapiro qflags |= QPRIMARY; 428790792Sgshapiro q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e, 428890792Sgshapiro true); 428990792Sgshapiro if (q != NULL) 429090792Sgshapiro { 429194334Sgshapiro /* make sure we keep the current qgrp */ 429294334Sgshapiro if (ISVALIDQGRP(e->e_qgrp)) 429394334Sgshapiro q->q_qgrp = e->e_qgrp; 429490792Sgshapiro q->q_alias = ctladdr; 429590792Sgshapiro if (qfver >= 1) 429690792Sgshapiro q->q_flags &= ~Q_PINGFLAGS; 429790792Sgshapiro q->q_flags |= qflags; 429890792Sgshapiro q->q_finalrcpt = frcpt; 429990792Sgshapiro q->q_orcpt = orcpt; 430090792Sgshapiro (void) recipient(q, &e->e_sendqueue, 0, e); 430190792Sgshapiro } 430290792Sgshapiro frcpt = NULL; 430390792Sgshapiro orcpt = NULL; 430438032Speter break; 430538032Speter 430690792Sgshapiro case 'S': /* sender */ 430790792Sgshapiro setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]), 430890792Sgshapiro e, NULL, '\0', true); 430938032Speter break; 431038032Speter 431190792Sgshapiro case 'T': /* init time */ 431290792Sgshapiro e->e_ctime = atol(&bp[1]); 431364562Sgshapiro break; 431464562Sgshapiro 431590792Sgshapiro case 'V': /* queue file version number */ 431690792Sgshapiro qfver = atoi(&bp[1]); 431790792Sgshapiro if (queuedelay_qfver_unsupported(qfver)) 431890792Sgshapiro syserr("queue file version %d not supported: %s", 431990792Sgshapiro qfver, 432090792Sgshapiro "sendmail not compiled with _FFR_QUEUEDELAY"); 432190792Sgshapiro if (qfver <= QF_VERSION) 432290792Sgshapiro break; 432390792Sgshapiro syserr("Version number in queue file (%d) greater than max (%d)", 432490792Sgshapiro qfver, QF_VERSION); 432594334Sgshapiro err = "unsupported queue file version"; 432694334Sgshapiro goto fail; 432790792Sgshapiro /* NOTREACHED */ 432890792Sgshapiro break; 432990792Sgshapiro 433090792Sgshapiro#if _FFR_QUEUEDELAY 433190792Sgshapiro case 'Y': /* current delay */ 433290792Sgshapiro e->e_queuedelay = (time_t) atol(&buf[1]); 433390792Sgshapiro break; 433490792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 433590792Sgshapiro 433690792Sgshapiro case 'Z': /* original envelope id from ESMTP */ 433790792Sgshapiro e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 433890792Sgshapiro macdefine(&e->e_macro, A_PERM, 433990792Sgshapiro macid("{dsn_envid}"), e->e_envid); 434090792Sgshapiro break; 434190792Sgshapiro 434290792Sgshapiro case '!': /* deliver by */ 434390792Sgshapiro 434490792Sgshapiro /* format: flag (1 char) space long-integer */ 434590792Sgshapiro e->e_dlvr_flag = buf[1]; 434690792Sgshapiro e->e_deliver_by = strtol(&buf[3], NULL, 10); 434790792Sgshapiro 434838032Speter case '$': /* define macro */ 434964562Sgshapiro { 435064562Sgshapiro char *p; 435164562Sgshapiro 435290792Sgshapiro /* XXX elimate p? */ 435390792Sgshapiro r = macid_parse(&bp[1], &ep); 435490792Sgshapiro if (r == 0) 435571345Sgshapiro break; 435690792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, ep); 435790792Sgshapiro macdefine(&e->e_macro, A_PERM, r, p); 435864562Sgshapiro } 435938032Speter break; 436038032Speter 436138032Speter case '.': /* terminate file */ 436290792Sgshapiro nomore = true; 436338032Speter break; 436438032Speter 436538032Speter default: 436638032Speter syserr("readqf: %s: line %d: bad line \"%s\"", 436738032Speter qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); 436894334Sgshapiro err = "unrecognized line"; 436994334Sgshapiro goto fail; 437038032Speter } 437138032Speter 437238032Speter if (bp != buf) 437390792Sgshapiro sm_free(bp); /* XXX */ 437438032Speter } 437538032Speter 437638032Speter /* 437738032Speter ** If we haven't read any lines, this queue file is empty. 437838032Speter ** Arrange to remove it without referencing any null pointers. 437938032Speter */ 438038032Speter 438138032Speter if (LineNumber == 0) 438238032Speter { 438338032Speter errno = 0; 438490792Sgshapiro e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE; 438590792Sgshapiro RELEASE_QUEUE; 438690792Sgshapiro return true; 438738032Speter } 438838032Speter 438990792Sgshapiro /* Check to make sure we have a complete queue file read */ 439090792Sgshapiro if (!nomore) 439190792Sgshapiro { 439290792Sgshapiro syserr("readqf: %s: incomplete queue file read", qf); 439390792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 439490792Sgshapiro RELEASE_QUEUE; 439590792Sgshapiro return false; 439690792Sgshapiro } 439790792Sgshapiro 439864562Sgshapiro /* possibly set ${dsn_ret} macro */ 439964562Sgshapiro if (bitset(EF_RET_PARAM, e->e_flags)) 440064562Sgshapiro { 440164562Sgshapiro if (bitset(EF_NO_BODY_RETN, e->e_flags)) 440290792Sgshapiro macdefine(&e->e_macro, A_PERM, 440390792Sgshapiro macid("{dsn_ret}"), "hdrs"); 440464562Sgshapiro else 440590792Sgshapiro macdefine(&e->e_macro, A_PERM, 440690792Sgshapiro macid("{dsn_ret}"), "full"); 440764562Sgshapiro } 440864562Sgshapiro 440938032Speter /* 441038032Speter ** Arrange to read the data file. 441138032Speter */ 441238032Speter 441390792Sgshapiro p = queuename(e, DATAFL_LETTER); 441490792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY, 441590792Sgshapiro NULL); 441638032Speter if (e->e_dfp == NULL) 441738032Speter { 441838032Speter syserr("readqf: cannot open %s", p); 441938032Speter } 442038032Speter else 442138032Speter { 442238032Speter e->e_flags |= EF_HAS_DF; 442390792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st) 442490792Sgshapiro >= 0) 442538032Speter { 442638032Speter e->e_msgsize = st.st_size + hdrsize; 442738032Speter e->e_dfdev = st.st_dev; 442890792Sgshapiro e->e_dfino = ST_INODE(st); 442998121Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", 443098121Sgshapiro e->e_msgsize); 443198121Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), 443298121Sgshapiro buf); 443338032Speter } 443438032Speter } 443538032Speter 443690792Sgshapiro RELEASE_QUEUE; 443790792Sgshapiro return true; 443894334Sgshapiro 443994334Sgshapiro fail: 444094334Sgshapiro /* 444194334Sgshapiro ** There was some error reading the qf file (reason is in err var.) 444294334Sgshapiro ** Cleanup: 444394334Sgshapiro ** close file; clear e_lockfp since it is the same as qfp, 444494334Sgshapiro ** hence it is invalid (as file) after qfp is closed; 444594334Sgshapiro ** the qf file is on disk, so set the flag to avoid calling 444694334Sgshapiro ** queueup() with bogus data. 444794334Sgshapiro */ 444894334Sgshapiro 444994334Sgshapiro if (qfp != NULL) 445094334Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 445194334Sgshapiro e->e_lockfp = NULL; 445294334Sgshapiro e->e_flags |= EF_INQUEUE; 445394334Sgshapiro loseqfile(e, err); 445494334Sgshapiro RELEASE_QUEUE; 445594334Sgshapiro return false; 445638032Speter} 445790792Sgshapiro/* 445864562Sgshapiro** PRTSTR -- print a string, "unprintable" characters are shown as \oct 445964562Sgshapiro** 446064562Sgshapiro** Parameters: 446164562Sgshapiro** s -- string to print 446264562Sgshapiro** ml -- maximum length of output 446364562Sgshapiro** 446464562Sgshapiro** Returns: 446590792Sgshapiro** number of entries 446664562Sgshapiro** 446764562Sgshapiro** Side Effects: 446864562Sgshapiro** Prints a string on stdout. 446964562Sgshapiro*/ 447064562Sgshapiro 447164562Sgshapirostatic void 447264562Sgshapiroprtstr(s, ml) 447364562Sgshapiro char *s; 447464562Sgshapiro int ml; 447564562Sgshapiro{ 447690792Sgshapiro int c; 447764562Sgshapiro 447864562Sgshapiro if (s == NULL) 447964562Sgshapiro return; 448064562Sgshapiro while (ml-- > 0 && ((c = *s++) != '\0')) 448164562Sgshapiro { 448264562Sgshapiro if (c == '\\') 448364562Sgshapiro { 448464562Sgshapiro if (ml-- > 0) 448564562Sgshapiro { 448690792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 448790792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 448864562Sgshapiro } 448964562Sgshapiro } 449064562Sgshapiro else if (isascii(c) && isprint(c)) 449190792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 449264562Sgshapiro else 449364562Sgshapiro { 449464562Sgshapiro if ((ml -= 3) > 0) 449590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 449690792Sgshapiro "\\%03o", c & 0xFF); 449764562Sgshapiro } 449864562Sgshapiro } 449964562Sgshapiro} 450090792Sgshapiro/* 450190792Sgshapiro** PRINTNQE -- print out number of entries in the mail queue 450290792Sgshapiro** 450390792Sgshapiro** Parameters: 450490792Sgshapiro** out -- output file pointer. 450590792Sgshapiro** prefix -- string to output in front of each line. 450690792Sgshapiro** 450790792Sgshapiro** Returns: 450890792Sgshapiro** none. 450990792Sgshapiro*/ 451090792Sgshapiro 451190792Sgshapirovoid 451290792Sgshapiroprintnqe(out, prefix) 451390792Sgshapiro SM_FILE_T *out; 451490792Sgshapiro char *prefix; 451590792Sgshapiro{ 451690792Sgshapiro#if SM_CONF_SHM 451790792Sgshapiro int i, k = 0, nrequests = 0; 451890792Sgshapiro bool unknown = false; 451990792Sgshapiro 452090792Sgshapiro if (ShmId == SM_SHM_NO_ID) 452190792Sgshapiro { 452290792Sgshapiro if (prefix == NULL) 452390792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 452490792Sgshapiro "Data unavailable: shared memory not updated\n"); 452590792Sgshapiro else 452690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 452790792Sgshapiro "%sNOTCONFIGURED:-1\r\n", prefix); 452890792Sgshapiro return; 452990792Sgshapiro } 453090792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 453190792Sgshapiro { 453290792Sgshapiro int j; 453390792Sgshapiro 453490792Sgshapiro k++; 453590792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 453690792Sgshapiro { 453790792Sgshapiro int n; 453890792Sgshapiro 453990792Sgshapiro if (StopRequest) 454090792Sgshapiro stop_sendmail(); 454190792Sgshapiro 454290792Sgshapiro n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx); 454390792Sgshapiro if (prefix != NULL) 454490792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 454590792Sgshapiro "%s%s:%d\r\n", 454690792Sgshapiro prefix, qid_printqueue(i, j), n); 454790792Sgshapiro else if (n < 0) 454890792Sgshapiro { 454990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 455090792Sgshapiro "%s: unknown number of entries\n", 455190792Sgshapiro qid_printqueue(i, j)); 455290792Sgshapiro unknown = true; 455390792Sgshapiro } 455490792Sgshapiro else if (n == 0) 455590792Sgshapiro { 455690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 455790792Sgshapiro "%s is empty\n", 455890792Sgshapiro qid_printqueue(i, j)); 455990792Sgshapiro } 456090792Sgshapiro else if (n > 0) 456190792Sgshapiro { 456290792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 456390792Sgshapiro "%s: entries=%d\n", 456490792Sgshapiro qid_printqueue(i, j), n); 456590792Sgshapiro nrequests += n; 456690792Sgshapiro k++; 456790792Sgshapiro } 456890792Sgshapiro } 456990792Sgshapiro } 457090792Sgshapiro if (prefix == NULL && k > 1) 457190792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 457290792Sgshapiro "\t\tTotal requests: %d%s\n", 457390792Sgshapiro nrequests, unknown ? " (about)" : ""); 457490792Sgshapiro#else /* SM_CONF_SHM */ 457590792Sgshapiro if (prefix == NULL) 457690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 457790792Sgshapiro "Data unavailable without shared memory support\n"); 457890792Sgshapiro else 457990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 458090792Sgshapiro "%sNOTAVAILABLE:-1\r\n", prefix); 458190792Sgshapiro#endif /* SM_CONF_SHM */ 458290792Sgshapiro} 458390792Sgshapiro/* 458438032Speter** PRINTQUEUE -- print out a representation of the mail queue 458538032Speter** 458638032Speter** Parameters: 458738032Speter** none. 458838032Speter** 458938032Speter** Returns: 459038032Speter** none. 459138032Speter** 459238032Speter** Side Effects: 459338032Speter** Prints a listing of the mail queue on the standard output. 459438032Speter*/ 459538032Speter 459638032Spetervoid 459738032Speterprintqueue() 459838032Speter{ 459990792Sgshapiro int i, k = 0, nrequests = 0; 460064562Sgshapiro 460190792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 460277349Sgshapiro { 460390792Sgshapiro int j; 460490792Sgshapiro 460590792Sgshapiro k++; 460690792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 460790792Sgshapiro { 460890792Sgshapiro if (StopRequest) 460990792Sgshapiro stop_sendmail(); 461090792Sgshapiro nrequests += print_single_queue(i, j); 461190792Sgshapiro k++; 461290792Sgshapiro } 461377349Sgshapiro } 461490792Sgshapiro if (k > 1) 461590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 461690792Sgshapiro "\t\tTotal requests: %d\n", 461790792Sgshapiro nrequests); 461864562Sgshapiro} 461990792Sgshapiro/* 462064562Sgshapiro** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue 462164562Sgshapiro** 462264562Sgshapiro** Parameters: 462390792Sgshapiro** qgrp -- the index of the queue group. 462490792Sgshapiro** qdir -- the queue directory. 462564562Sgshapiro** 462664562Sgshapiro** Returns: 462790792Sgshapiro** number of requests in mail queue. 462864562Sgshapiro** 462964562Sgshapiro** Side Effects: 463064562Sgshapiro** Prints a listing of the mail queue on the standard output. 463164562Sgshapiro*/ 463264562Sgshapiro 463390792Sgshapiroint 463490792Sgshapiroprint_single_queue(qgrp, qdir) 463590792Sgshapiro int qgrp; 463690792Sgshapiro int qdir; 463764562Sgshapiro{ 463838032Speter register WORK *w; 463990792Sgshapiro SM_FILE_T *f; 464038032Speter int nrequests; 464164562Sgshapiro char qd[MAXPATHLEN]; 464264562Sgshapiro char qddf[MAXPATHLEN]; 464338032Speter char buf[MAXLINE]; 464438032Speter 464590792Sgshapiro if (qdir == NOQDIR) 464664562Sgshapiro { 464790792Sgshapiro (void) sm_strlcpy(qd, ".", sizeof qd); 464890792Sgshapiro (void) sm_strlcpy(qddf, ".", sizeof qddf); 464964562Sgshapiro } 465064562Sgshapiro else 465164562Sgshapiro { 465290792Sgshapiro (void) sm_strlcpyn(qd, sizeof qd, 2, 465390792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 465490792Sgshapiro (bitset(QP_SUBQF, 465590792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 465690792Sgshapiro ? "/qf" : "")); 465790792Sgshapiro (void) sm_strlcpyn(qddf, sizeof qddf, 2, 465890792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 465990792Sgshapiro (bitset(QP_SUBDF, 466090792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 466190792Sgshapiro ? "/df" : "")); 466264562Sgshapiro } 466364562Sgshapiro 466438032Speter /* 466538032Speter ** Check for permission to print the queue 466638032Speter */ 466738032Speter 466838032Speter if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 466938032Speter { 467038032Speter struct stat st; 467190792Sgshapiro#ifdef NGROUPS_MAX 467238032Speter int n; 467338032Speter extern GIDSET_T InitialGidSet[NGROUPS_MAX]; 467490792Sgshapiro#endif /* NGROUPS_MAX */ 467538032Speter 467664562Sgshapiro if (stat(qd, &st) < 0) 467738032Speter { 467890792Sgshapiro syserr("Cannot stat %s", 467990792Sgshapiro qid_printqueue(qgrp, qdir)); 468064562Sgshapiro return 0; 468138032Speter } 468290792Sgshapiro#ifdef NGROUPS_MAX 468338032Speter n = NGROUPS_MAX; 468438032Speter while (--n >= 0) 468538032Speter { 468638032Speter if (InitialGidSet[n] == st.st_gid) 468738032Speter break; 468838032Speter } 468938032Speter if (n < 0 && RealGid != st.st_gid) 469090792Sgshapiro#else /* NGROUPS_MAX */ 469138032Speter if (RealGid != st.st_gid) 469290792Sgshapiro#endif /* NGROUPS_MAX */ 469338032Speter { 469438032Speter usrerr("510 You are not permitted to see the queue"); 469538032Speter setstat(EX_NOPERM); 469664562Sgshapiro return 0; 469738032Speter } 469838032Speter } 469938032Speter 470038032Speter /* 470138032Speter ** Read and order the queue. 470238032Speter */ 470338032Speter 470490792Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL); 470590792Sgshapiro (void) sortq(Queue[qgrp]->qg_maxlist); 470638032Speter 470738032Speter /* 470838032Speter ** Print the work list that we have read. 470938032Speter */ 471038032Speter 471138032Speter /* first see if there is anything */ 471238032Speter if (nrequests <= 0) 471338032Speter { 471490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n", 471590792Sgshapiro qid_printqueue(qgrp, qdir)); 471664562Sgshapiro return 0; 471738032Speter } 471838032Speter 471990792Sgshapiro sm_getla(); /* get load average */ 472038032Speter 472190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s", 472290792Sgshapiro qid_printqueue(qgrp, qdir), 472390792Sgshapiro nrequests, nrequests == 1 ? "" : "s"); 472438032Speter if (MaxQueueRun > 0 && nrequests > MaxQueueRun) 472590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 472690792Sgshapiro ", only %d printed", MaxQueueRun); 472738032Speter if (Verbose) 472890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 472990792Sgshapiro ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n"); 473038032Speter else 473190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 473290792Sgshapiro ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n"); 473338032Speter for (w = WorkQ; w != NULL; w = w->w_next) 473438032Speter { 473538032Speter struct stat st; 473638032Speter auto time_t submittime = 0; 473738032Speter long dfsize; 473838032Speter int flags = 0; 473938032Speter int qfver; 474090792Sgshapiro#if _FFR_QUARANTINE 474190792Sgshapiro char quarmsg[MAXLINE]; 474290792Sgshapiro#endif /* _FFR_QUARANTINE */ 474338032Speter char statmsg[MAXLINE]; 474438032Speter char bodytype[MAXNAME + 1]; 474564562Sgshapiro char qf[MAXPATHLEN]; 474638032Speter 474777349Sgshapiro if (StopRequest) 474877349Sgshapiro stop_sendmail(); 474977349Sgshapiro 475090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s", 475190792Sgshapiro w->w_name + 2); 475290792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", w->w_name); 475390792Sgshapiro f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY, 475490792Sgshapiro NULL); 475538032Speter if (f == NULL) 475638032Speter { 475790792Sgshapiro if (errno == EPERM) 475890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 475990792Sgshapiro " (permission denied)\n"); 476090792Sgshapiro else if (errno == ENOENT) 476190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 476290792Sgshapiro " (job completed)\n"); 476390792Sgshapiro else 476490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 476590792Sgshapiro " (%s)\n", 476690792Sgshapiro sm_errstring(errno)); 476738032Speter errno = 0; 476838032Speter continue; 476938032Speter } 477090792Sgshapiro w->w_name[0] = DATAFL_LETTER; 477190792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qddf, "/", w->w_name); 477264562Sgshapiro if (stat(qf, &st) >= 0) 477338032Speter dfsize = st.st_size; 477438032Speter else 477590792Sgshapiro { 477690792Sgshapiro ENVELOPE e; 477790792Sgshapiro 477890792Sgshapiro /* 477990792Sgshapiro ** Maybe the df file can't be statted because 478090792Sgshapiro ** it is in a different directory than the qf file. 478190792Sgshapiro ** In order to find out, we must read the qf file. 478290792Sgshapiro */ 478390792Sgshapiro 478490792Sgshapiro newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL)); 478590792Sgshapiro e.e_id = w->w_name + 2; 478690792Sgshapiro e.e_qgrp = qgrp; 478790792Sgshapiro e.e_qdir = qdir; 478838032Speter dfsize = -1; 478990792Sgshapiro if (readqf(&e, false)) 479090792Sgshapiro { 479190792Sgshapiro char *df = queuename(&e, DATAFL_LETTER); 479290792Sgshapiro if (stat(df, &st) >= 0) 479390792Sgshapiro dfsize = st.st_size; 479490792Sgshapiro } 479590792Sgshapiro if (e.e_lockfp != NULL) 479690792Sgshapiro { 479790792Sgshapiro (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT); 479890792Sgshapiro e.e_lockfp = NULL; 479990792Sgshapiro } 480090792Sgshapiro clearenvelope(&e, false, e.e_rpool); 480190792Sgshapiro sm_rpool_free(e.e_rpool); 480290792Sgshapiro } 480338032Speter if (w->w_lock) 480490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*"); 480590792Sgshapiro#if _FFR_QUARANTINE 480690792Sgshapiro else if (QueueMode == QM_LOST) 480790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?"); 480890792Sgshapiro#endif /* _FFR_QUARANTINE */ 480938032Speter else if (w->w_tooyoung) 481090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-"); 481138032Speter else if (shouldqueue(w->w_pri, w->w_ctime)) 481290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X"); 481338032Speter else 481490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " "); 481590792Sgshapiro 481638032Speter errno = 0; 481738032Speter 481890792Sgshapiro#if _FFR_QUARANTINE 481990792Sgshapiro quarmsg[0] = '\0'; 482090792Sgshapiro#endif /* _FFR_QUARANTINE */ 482138032Speter statmsg[0] = bodytype[0] = '\0'; 482238032Speter qfver = 0; 482390792Sgshapiro while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 482438032Speter { 482538032Speter register int i; 482638032Speter register char *p; 482738032Speter 482877349Sgshapiro if (StopRequest) 482977349Sgshapiro stop_sendmail(); 483077349Sgshapiro 483190792Sgshapiro fixcrlf(buf, true); 483238032Speter switch (buf[0]) 483338032Speter { 483438032Speter case 'V': /* queue file version */ 483538032Speter qfver = atoi(&buf[1]); 483638032Speter break; 483738032Speter 483838032Speter case 'M': /* error message */ 483938032Speter if ((i = strlen(&buf[1])) >= sizeof statmsg) 484038032Speter i = sizeof statmsg - 1; 484164562Sgshapiro memmove(statmsg, &buf[1], i); 484238032Speter statmsg[i] = '\0'; 484338032Speter break; 484438032Speter 484590792Sgshapiro#if _FFR_QUARANTINE 484690792Sgshapiro case 'q': /* quarantine reason */ 484790792Sgshapiro if ((i = strlen(&buf[1])) >= sizeof quarmsg) 484890792Sgshapiro i = sizeof quarmsg - 1; 484990792Sgshapiro memmove(quarmsg, &buf[1], i); 485090792Sgshapiro quarmsg[i] = '\0'; 485190792Sgshapiro break; 485290792Sgshapiro#endif /* _FFR_QUARANTINE */ 485390792Sgshapiro 485438032Speter case 'B': /* body type */ 485538032Speter if ((i = strlen(&buf[1])) >= sizeof bodytype) 485638032Speter i = sizeof bodytype - 1; 485764562Sgshapiro memmove(bodytype, &buf[1], i); 485838032Speter bodytype[i] = '\0'; 485938032Speter break; 486038032Speter 486138032Speter case 'S': /* sender name */ 486238032Speter if (Verbose) 486364562Sgshapiro { 486490792Sgshapiro (void) sm_io_fprintf(smioout, 486590792Sgshapiro SM_TIME_DEFAULT, 486690792Sgshapiro "%8ld %10ld%c%.12s ", 486790792Sgshapiro dfsize, 486890792Sgshapiro w->w_pri, 486990792Sgshapiro bitset(EF_WARNING, flags) 487090792Sgshapiro ? '+' : ' ', 487190792Sgshapiro ctime(&submittime) + 4); 487264562Sgshapiro prtstr(&buf[1], 78); 487364562Sgshapiro } 487438032Speter else 487564562Sgshapiro { 487690792Sgshapiro (void) sm_io_fprintf(smioout, 487790792Sgshapiro SM_TIME_DEFAULT, 487890792Sgshapiro "%8ld %.16s ", 487990792Sgshapiro dfsize, 488090792Sgshapiro ctime(&submittime)); 488190792Sgshapiro prtstr(&buf[1], 39); 488264562Sgshapiro } 488390792Sgshapiro#if _FFR_QUARANTINE 488490792Sgshapiro if (quarmsg[0] != '\0') 488590792Sgshapiro { 488690792Sgshapiro (void) sm_io_fprintf(smioout, 488790792Sgshapiro SM_TIME_DEFAULT, 488890792Sgshapiro "\n QUARANTINE: %.*s", 488990792Sgshapiro Verbose ? 100 : 60, 489090792Sgshapiro quarmsg); 489190792Sgshapiro quarmsg[0] = '\0'; 489290792Sgshapiro } 489390792Sgshapiro#endif /* _FFR_QUARANTINE */ 489438032Speter if (statmsg[0] != '\0' || bodytype[0] != '\0') 489538032Speter { 489690792Sgshapiro (void) sm_io_fprintf(smioout, 489790792Sgshapiro SM_TIME_DEFAULT, 489890792Sgshapiro "\n %10.10s", 489990792Sgshapiro bodytype); 490038032Speter if (statmsg[0] != '\0') 490190792Sgshapiro (void) sm_io_fprintf(smioout, 490290792Sgshapiro SM_TIME_DEFAULT, 490390792Sgshapiro " (%.*s)", 490490792Sgshapiro Verbose ? 100 : 60, 490590792Sgshapiro statmsg); 490690792Sgshapiro statmsg[0] = '\0'; 490738032Speter } 490838032Speter break; 490938032Speter 491038032Speter case 'C': /* controlling user */ 491138032Speter if (Verbose) 491290792Sgshapiro (void) sm_io_fprintf(smioout, 491390792Sgshapiro SM_TIME_DEFAULT, 491490792Sgshapiro "\n\t\t\t\t\t\t(---%.64s---)", 491590792Sgshapiro &buf[1]); 491638032Speter break; 491738032Speter 491838032Speter case 'R': /* recipient name */ 491938032Speter p = &buf[1]; 492038032Speter if (qfver >= 1) 492138032Speter { 492238032Speter p = strchr(p, ':'); 492338032Speter if (p == NULL) 492438032Speter break; 492538032Speter p++; 492638032Speter } 492738032Speter if (Verbose) 492864562Sgshapiro { 492990792Sgshapiro (void) sm_io_fprintf(smioout, 493090792Sgshapiro SM_TIME_DEFAULT, 493190792Sgshapiro "\n\t\t\t\t\t\t"); 493290792Sgshapiro prtstr(p, 71); 493364562Sgshapiro } 493438032Speter else 493564562Sgshapiro { 493690792Sgshapiro (void) sm_io_fprintf(smioout, 493790792Sgshapiro SM_TIME_DEFAULT, 493890792Sgshapiro "\n\t\t\t\t\t "); 493990792Sgshapiro prtstr(p, 38); 494064562Sgshapiro } 494190792Sgshapiro if (Verbose && statmsg[0] != '\0') 494290792Sgshapiro { 494390792Sgshapiro (void) sm_io_fprintf(smioout, 494490792Sgshapiro SM_TIME_DEFAULT, 494590792Sgshapiro "\n\t\t (%.100s)", 494690792Sgshapiro statmsg); 494790792Sgshapiro statmsg[0] = '\0'; 494890792Sgshapiro } 494938032Speter break; 495038032Speter 495138032Speter case 'T': /* creation time */ 495238032Speter submittime = atol(&buf[1]); 495338032Speter break; 495438032Speter 495538032Speter case 'F': /* flag bits */ 495638032Speter for (p = &buf[1]; *p != '\0'; p++) 495738032Speter { 495838032Speter switch (*p) 495938032Speter { 496038032Speter case 'w': 496138032Speter flags |= EF_WARNING; 496238032Speter break; 496338032Speter } 496438032Speter } 496538032Speter } 496638032Speter } 496738032Speter if (submittime == (time_t) 0) 496890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 496990792Sgshapiro " (no control file)"); 497090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 497190792Sgshapiro (void) sm_io_close(f, SM_TIME_DEFAULT); 497238032Speter } 497364562Sgshapiro return nrequests; 497438032Speter} 497590792Sgshapiro 497690792Sgshapiro#if _FFR_QUARANTINE 497790792Sgshapiro/* 497890792Sgshapiro** QUEUE_LETTER -- get the proper queue letter for the current QueueMode. 497990792Sgshapiro** 498090792Sgshapiro** Parameters: 498190792Sgshapiro** e -- envelope to build it in/from. 498290792Sgshapiro** type -- the file type, used as the first character 498390792Sgshapiro** of the file name. 498490792Sgshapiro** 498590792Sgshapiro** Returns: 498690792Sgshapiro** the letter to use 498790792Sgshapiro*/ 498890792Sgshapiro 498990792Sgshapirostatic char 499090792Sgshapiroqueue_letter(e, type) 499190792Sgshapiro ENVELOPE *e; 499290792Sgshapiro int type; 499390792Sgshapiro{ 499490792Sgshapiro /* Change type according to QueueMode */ 499590792Sgshapiro if (type == ANYQFL_LETTER) 499690792Sgshapiro { 499790792Sgshapiro if (e->e_quarmsg != NULL) 499890792Sgshapiro type = QUARQF_LETTER; 499990792Sgshapiro else 500090792Sgshapiro { 500190792Sgshapiro switch (QueueMode) 500290792Sgshapiro { 500390792Sgshapiro case QM_NORMAL: 500490792Sgshapiro type = NORMQF_LETTER; 500590792Sgshapiro break; 500690792Sgshapiro 500790792Sgshapiro case QM_QUARANTINE: 500890792Sgshapiro type = QUARQF_LETTER; 500990792Sgshapiro break; 501090792Sgshapiro 501190792Sgshapiro case QM_LOST: 501290792Sgshapiro type = LOSEQF_LETTER; 501390792Sgshapiro break; 501490792Sgshapiro 501590792Sgshapiro default: 501690792Sgshapiro /* should never happen */ 501790792Sgshapiro abort(); 501890792Sgshapiro /* NOTREACHED */ 501990792Sgshapiro } 502090792Sgshapiro } 502190792Sgshapiro } 502290792Sgshapiro return type; 502390792Sgshapiro} 502490792Sgshapiro#endif /* _FFR_QUARANTINE */ 502590792Sgshapiro 502690792Sgshapiro/* 502738032Speter** QUEUENAME -- build a file name in the queue directory for this envelope. 502838032Speter** 502938032Speter** Parameters: 503038032Speter** e -- envelope to build it in/from. 503138032Speter** type -- the file type, used as the first character 503238032Speter** of the file name. 503338032Speter** 503438032Speter** Returns: 503564562Sgshapiro** a pointer to the queue name (in a static buffer). 503638032Speter** 503738032Speter** Side Effects: 503864562Sgshapiro** If no id code is already assigned, queuename() will 503964562Sgshapiro** assign an id code with assign_queueid(). If no queue 504064562Sgshapiro** directory is assigned, one will be set with setnewqueue(). 504138032Speter*/ 504238032Speter 504338032Speterchar * 504438032Speterqueuename(e, type) 504538032Speter register ENVELOPE *e; 504638032Speter int type; 504738032Speter{ 504890792Sgshapiro int qd, qg; 504990792Sgshapiro char *sub = "/"; 505090792Sgshapiro char pref[3]; 505164562Sgshapiro static char buf[MAXPATHLEN]; 505238032Speter 505364562Sgshapiro /* Assign an ID if needed */ 505438032Speter if (e->e_id == NULL) 505564562Sgshapiro assign_queueid(e); 505664562Sgshapiro 505790792Sgshapiro#if _FFR_QUARANTINE 505890792Sgshapiro type = queue_letter(e, type); 505990792Sgshapiro#endif /* _FFR_QUARANTINE */ 506064562Sgshapiro 506190792Sgshapiro /* begin of filename */ 506290792Sgshapiro pref[0] = (char) type; 506390792Sgshapiro pref[1] = 'f'; 506490792Sgshapiro pref[2] = '\0'; 506590792Sgshapiro 506690792Sgshapiro /* Assign a queue group/directory if needed */ 506790792Sgshapiro if (type == XSCRPT_LETTER) 506890792Sgshapiro { 506990792Sgshapiro /* 507090792Sgshapiro ** We don't want to call setnewqueue() if we are fetching 507190792Sgshapiro ** the pathname of the transcript file, because setnewqueue 507290792Sgshapiro ** chooses a queue, and sometimes we need to write to the 507390792Sgshapiro ** transcript file before we have gathered enough information 507490792Sgshapiro ** to choose a queue. 507590792Sgshapiro */ 507690792Sgshapiro 507790792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 507890792Sgshapiro { 507990792Sgshapiro if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR) 508090792Sgshapiro { 508190792Sgshapiro e->e_xfqgrp = e->e_qgrp; 508290792Sgshapiro e->e_xfqdir = e->e_qdir; 508390792Sgshapiro } 508490792Sgshapiro else 508590792Sgshapiro { 508690792Sgshapiro e->e_xfqgrp = 0; 508790792Sgshapiro if (Queue[e->e_xfqgrp]->qg_numqueues <= 1) 508890792Sgshapiro e->e_xfqdir = 0; 508990792Sgshapiro else 509090792Sgshapiro { 509190792Sgshapiro e->e_xfqdir = get_rand_mod( 509290792Sgshapiro Queue[e->e_xfqgrp]->qg_numqueues); 509390792Sgshapiro } 509490792Sgshapiro } 509590792Sgshapiro } 509690792Sgshapiro qd = e->e_xfqdir; 509790792Sgshapiro qg = e->e_xfqgrp; 509890792Sgshapiro } 509964562Sgshapiro else 510038032Speter { 510190792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 510290792Sgshapiro setnewqueue(e); 510390792Sgshapiro if (type == DATAFL_LETTER) 510490792Sgshapiro { 510590792Sgshapiro qd = e->e_dfqdir; 510690792Sgshapiro qg = e->e_dfqgrp; 510790792Sgshapiro } 510890792Sgshapiro else 510990792Sgshapiro { 511090792Sgshapiro qd = e->e_qdir; 511190792Sgshapiro qg = e->e_qgrp; 511290792Sgshapiro } 511390792Sgshapiro } 511490792Sgshapiro 511594334Sgshapiro /* xf files always have a valid qd and qg picked above */ 511694334Sgshapiro if (e->e_qdir == NOQDIR && type != XSCRPT_LETTER) 511790792Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id); 511890792Sgshapiro else 511990792Sgshapiro { 512064562Sgshapiro switch (type) 512164562Sgshapiro { 512290792Sgshapiro case DATAFL_LETTER: 512390792Sgshapiro if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 512490792Sgshapiro sub = "/df/"; 512564562Sgshapiro break; 512638032Speter 512790792Sgshapiro#if _FFR_QUARANTINE 512890792Sgshapiro case QUARQF_LETTER: 512990792Sgshapiro#endif /* _FFR_QUARANTINE */ 513071345Sgshapiro case TEMPQF_LETTER: 513190792Sgshapiro case NEWQFL_LETTER: 513271345Sgshapiro case LOSEQF_LETTER: 513390792Sgshapiro case NORMQF_LETTER: 513490792Sgshapiro if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 513590792Sgshapiro sub = "/qf/"; 513664562Sgshapiro break; 513764562Sgshapiro 513890792Sgshapiro case XSCRPT_LETTER: 513990792Sgshapiro if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 514090792Sgshapiro sub = "/xf/"; 514164562Sgshapiro break; 514290792Sgshapiro 514390792Sgshapiro default: 514490792Sgshapiro sm_abort("queuename: bad queue file type %d", type); 514538032Speter } 514638032Speter 514790792Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 4, 514890792Sgshapiro Queue[qg]->qg_qpaths[qd].qp_name, 514990792Sgshapiro sub, pref, e->e_id); 515064562Sgshapiro } 515138032Speter 515264562Sgshapiro if (tTd(7, 2)) 515390792Sgshapiro sm_dprintf("queuename: %s\n", buf); 515464562Sgshapiro return buf; 515564562Sgshapiro} 515690792Sgshapiro/* 515764562Sgshapiro** ASSIGN_QUEUEID -- assign a queue ID for this envelope. 515864562Sgshapiro** 515964562Sgshapiro** Assigns an id code if one does not already exist. 516064562Sgshapiro** This code assumes that nothing will remain in the queue for 516164562Sgshapiro** longer than 60 years. It is critical that files with the given 516290792Sgshapiro** name do not already exist in the queue. 516390792Sgshapiro** [No longer initializes e_qdir to NOQDIR.] 516464562Sgshapiro** 516564562Sgshapiro** Parameters: 516664562Sgshapiro** e -- envelope to set it in. 516764562Sgshapiro** 516864562Sgshapiro** Returns: 516964562Sgshapiro** none. 517064562Sgshapiro*/ 517138032Speter 517277349Sgshapirostatic const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"; 517377349Sgshapiro# define QIC_LEN 60 517490792Sgshapiro# define queuenextid() CurrentPid 517538032Speter 517690792Sgshapiro 517764562Sgshapirovoid 517864562Sgshapiroassign_queueid(e) 517964562Sgshapiro register ENVELOPE *e; 518064562Sgshapiro{ 518190792Sgshapiro pid_t pid = queuenextid(); 518290792Sgshapiro static int cX = 0; 518364562Sgshapiro static long random_offset; 518464562Sgshapiro struct tm *tm; 518564562Sgshapiro char idbuf[MAXQFNAME - 2]; 518690792Sgshapiro int seq; 518738032Speter 518864562Sgshapiro if (e->e_id != NULL) 518964562Sgshapiro return; 519038032Speter 519164562Sgshapiro /* see if we need to get a new base time/pid */ 519290792Sgshapiro if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 || 519390792Sgshapiro LastQueuePid != pid) 519464562Sgshapiro { 519564562Sgshapiro time_t then = LastQueueTime; 519664562Sgshapiro 519764562Sgshapiro /* if the first time through, pick a random offset */ 519864562Sgshapiro if (LastQueueTime == 0) 519964562Sgshapiro random_offset = get_random(); 520064562Sgshapiro 520164562Sgshapiro while ((LastQueueTime = curtime()) == then && 520264562Sgshapiro LastQueuePid == pid) 520338032Speter { 520464562Sgshapiro (void) sleep(1); 520538032Speter } 520690792Sgshapiro LastQueuePid = queuenextid(); 520764562Sgshapiro cX = 0; 520838032Speter } 520990792Sgshapiro 521090792Sgshapiro /* 521190792Sgshapiro ** Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1. 521290792Sgshapiro ** This lets us generate up to QIC_LEN*QIC_LEN unique queue ids 521390792Sgshapiro ** per second, per process. With envelope splitting, 521490792Sgshapiro ** a single message can consume many queue ids. 521590792Sgshapiro */ 521690792Sgshapiro 521790792Sgshapiro seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN)); 521890792Sgshapiro ++cX; 521964562Sgshapiro if (tTd(7, 50)) 522090792Sgshapiro sm_dprintf("assign_queueid: random_offset = %ld (%d)\n", 522190792Sgshapiro random_offset, seq); 522238032Speter 522364562Sgshapiro tm = gmtime(&LastQueueTime); 522477349Sgshapiro idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN]; 522577349Sgshapiro idbuf[1] = QueueIdChars[tm->tm_mon]; 522677349Sgshapiro idbuf[2] = QueueIdChars[tm->tm_mday]; 522777349Sgshapiro idbuf[3] = QueueIdChars[tm->tm_hour]; 522877349Sgshapiro idbuf[4] = QueueIdChars[tm->tm_min]; 522977349Sgshapiro idbuf[5] = QueueIdChars[tm->tm_sec]; 523090792Sgshapiro idbuf[6] = QueueIdChars[seq / QIC_LEN]; 523190792Sgshapiro idbuf[7] = QueueIdChars[seq % QIC_LEN]; 523290792Sgshapiro (void) sm_snprintf(&idbuf[8], sizeof idbuf - 8, "%06d", 523390792Sgshapiro (int) LastQueuePid); 523490792Sgshapiro e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf); 523590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 523690792Sgshapiro#if 0 523790792Sgshapiro /* XXX: inherited from MainEnvelope */ 523890792Sgshapiro e->e_qgrp = NOQGRP; /* too early to do anything else */ 523990792Sgshapiro e->e_qdir = NOQDIR; 524090792Sgshapiro e->e_xfqgrp = NOQGRP; 524190792Sgshapiro#endif /* 0 */ 524290792Sgshapiro#if _FFR_QUARANTINE 524390792Sgshapiro /* New ID means it's not on disk yet */ 524490792Sgshapiro e->e_qfletter = '\0'; 524590792Sgshapiro#endif /* _FFR_QUARANTINE */ 524664562Sgshapiro if (tTd(7, 1)) 524790792Sgshapiro sm_dprintf("assign_queueid: assigned id %s, e=%p\n", 524890792Sgshapiro e->e_id, e); 524964562Sgshapiro if (LogLevel > 93) 525064562Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); 525138032Speter} 525290792Sgshapiro/* 525364562Sgshapiro** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second 525464562Sgshapiro** 525564562Sgshapiro** Make sure one PID can't be used by two processes in any one second. 525664562Sgshapiro** 525764562Sgshapiro** If the system rotates PIDs fast enough, may get the 525864562Sgshapiro** same pid in the same second for two distinct processes. 525964562Sgshapiro** This will interfere with the queue file naming system. 526064562Sgshapiro** 526164562Sgshapiro** Parameters: 526264562Sgshapiro** none 526364562Sgshapiro** 526464562Sgshapiro** Returns: 526564562Sgshapiro** none 526664562Sgshapiro*/ 526790792Sgshapiro 526864562Sgshapirovoid 526964562Sgshapirosync_queue_time() 527064562Sgshapiro{ 527190792Sgshapiro#if FAST_PID_RECYCLE 527264562Sgshapiro if (OpMode != MD_TEST && 527364562Sgshapiro OpMode != MD_VERIFY && 527464562Sgshapiro LastQueueTime > 0 && 527590792Sgshapiro LastQueuePid == CurrentPid && 527664562Sgshapiro curtime() == LastQueueTime) 527764562Sgshapiro (void) sleep(1); 527890792Sgshapiro#endif /* FAST_PID_RECYCLE */ 527964562Sgshapiro} 528090792Sgshapiro/* 528138032Speter** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 528238032Speter** 528338032Speter** Parameters: 528438032Speter** e -- the envelope to unlock. 528538032Speter** 528638032Speter** Returns: 528738032Speter** none 528838032Speter** 528938032Speter** Side Effects: 529038032Speter** unlocks the queue for `e'. 529138032Speter*/ 529238032Speter 529338032Spetervoid 529438032Speterunlockqueue(e) 529538032Speter ENVELOPE *e; 529638032Speter{ 529738032Speter if (tTd(51, 4)) 529890792Sgshapiro sm_dprintf("unlockqueue(%s)\n", 529938032Speter e->e_id == NULL ? "NOQUEUE" : e->e_id); 530038032Speter 530164562Sgshapiro 530238032Speter /* if there is a lock file in the envelope, close it */ 530338032Speter if (e->e_lockfp != NULL) 530490792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 530538032Speter e->e_lockfp = NULL; 530638032Speter 530738032Speter /* don't create a queue id if we don't already have one */ 530838032Speter if (e->e_id == NULL) 530938032Speter return; 531038032Speter 531138032Speter /* remove the transcript */ 531238032Speter if (LogLevel > 87) 531338032Speter sm_syslog(LOG_DEBUG, e->e_id, "unlock"); 531438032Speter if (!tTd(51, 104)) 531590792Sgshapiro (void) xunlink(queuename(e, XSCRPT_LETTER)); 531638032Speter} 531790792Sgshapiro/* 531838032Speter** SETCTLUSER -- create a controlling address 531938032Speter** 532038032Speter** Create a fake "address" given only a local login name; this is 532138032Speter** used as a "controlling user" for future recipient addresses. 532238032Speter** 532338032Speter** Parameters: 532438032Speter** user -- the user name of the controlling user. 532590792Sgshapiro** qfver -- the version stamp of this queue file. 532690792Sgshapiro** e -- envelope 532738032Speter** 532838032Speter** Returns: 532990792Sgshapiro** An address descriptor for the controlling user, 533090792Sgshapiro** using storage allocated from e->e_rpool. 533138032Speter** 533238032Speter*/ 533338032Speter 533464562Sgshapirostatic ADDRESS * 533590792Sgshapirosetctluser(user, qfver, e) 533638032Speter char *user; 533738032Speter int qfver; 533890792Sgshapiro ENVELOPE *e; 533938032Speter{ 534038032Speter register ADDRESS *a; 534138032Speter struct passwd *pw; 534238032Speter char *p; 534338032Speter 534438032Speter /* 534538032Speter ** See if this clears our concept of controlling user. 534638032Speter */ 534738032Speter 534838032Speter if (user == NULL || *user == '\0') 534938032Speter return NULL; 535038032Speter 535138032Speter /* 535238032Speter ** Set up addr fields for controlling user. 535338032Speter */ 535438032Speter 535590792Sgshapiro a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a); 535664562Sgshapiro memset((char *) a, '\0', sizeof *a); 535738032Speter 535890792Sgshapiro if (*user == ':') 535938032Speter { 536038032Speter p = &user[1]; 536190792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, p); 536238032Speter } 536338032Speter else 536438032Speter { 536538032Speter p = strtok(user, ":"); 536690792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, user); 536738032Speter if (qfver >= 2) 536838032Speter { 536938032Speter if ((p = strtok(NULL, ":")) != NULL) 537038032Speter a->q_uid = atoi(p); 537138032Speter if ((p = strtok(NULL, ":")) != NULL) 537238032Speter a->q_gid = atoi(p); 537338032Speter if ((p = strtok(NULL, ":")) != NULL) 537480785Sgshapiro { 537580785Sgshapiro char *o; 537680785Sgshapiro 537738032Speter a->q_flags |= QGOODUID; 537880785Sgshapiro 537980785Sgshapiro /* if there is another ':': restore it */ 538080785Sgshapiro if ((o = strtok(NULL, ":")) != NULL && o > p) 538180785Sgshapiro o[-1] = ':'; 538280785Sgshapiro } 538338032Speter } 538438032Speter else if ((pw = sm_getpwnam(user)) != NULL) 538538032Speter { 538666494Sgshapiro if (*pw->pw_dir == '\0') 538766494Sgshapiro a->q_home = NULL; 538866494Sgshapiro else if (strcmp(pw->pw_dir, "/") == 0) 538938032Speter a->q_home = ""; 539038032Speter else 539190792Sgshapiro a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir); 539238032Speter a->q_uid = pw->pw_uid; 539338032Speter a->q_gid = pw->pw_gid; 539438032Speter a->q_flags |= QGOODUID; 539538032Speter } 539638032Speter } 539738032Speter 539864562Sgshapiro a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 539938032Speter a->q_mailer = LocalMailer; 540038032Speter if (p == NULL) 540190792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); 540238032Speter else 540390792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p); 540438032Speter return a; 540538032Speter} 540690792Sgshapiro/* 540790792Sgshapiro** LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know 540838032Speter** 540938032Speter** Parameters: 541038032Speter** e -- the envelope (e->e_id will be used). 541138032Speter** why -- reported to whomever can hear. 541238032Speter** 541338032Speter** Returns: 541438032Speter** none. 541538032Speter*/ 541638032Speter 541738032Spetervoid 541838032Speterloseqfile(e, why) 541938032Speter register ENVELOPE *e; 542038032Speter char *why; 542138032Speter{ 542290792Sgshapiro bool loseit = true; 542338032Speter char *p; 542464562Sgshapiro char buf[MAXPATHLEN]; 542538032Speter 542638032Speter if (e == NULL || e->e_id == NULL) 542738032Speter return; 542890792Sgshapiro p = queuename(e, ANYQFL_LETTER); 542990792Sgshapiro if (sm_strlcpy(buf, p, sizeof buf) >= sizeof buf) 543038032Speter return; 543190792Sgshapiro if (!bitset(EF_INQUEUE, e->e_flags)) 543290792Sgshapiro queueup(e, false, true); 543390792Sgshapiro#if _FFR_QUARANTINE 543490792Sgshapiro else if (QueueMode == QM_LOST) 543590792Sgshapiro loseit = false; 543690792Sgshapiro#endif /* _FFR_QUARANTINE */ 543790792Sgshapiro 543890792Sgshapiro /* if already lost, no need to re-lose */ 543990792Sgshapiro if (loseit) 544090792Sgshapiro { 544190792Sgshapiro p = queuename(e, LOSEQF_LETTER); 544290792Sgshapiro if (rename(buf, p) < 0) 544390792Sgshapiro syserr("cannot rename(%s, %s), uid=%d", 544498121Sgshapiro buf, p, (int) geteuid()); 544590792Sgshapiro else if (LogLevel > 0) 544690792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 544790792Sgshapiro "Losing %s: %s", buf, why); 544890792Sgshapiro } 544990792Sgshapiro if (e->e_dfp != NULL) 545090792Sgshapiro { 545190792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 545290792Sgshapiro e->e_dfp = NULL; 545390792Sgshapiro } 545490792Sgshapiro e->e_flags &= ~EF_HAS_DF; 545538032Speter} 545690792Sgshapiro/* 545790792Sgshapiro** NAME2QID -- translate a queue group name to a queue group id 545890792Sgshapiro** 545990792Sgshapiro** Parameters: 546090792Sgshapiro** queuename -- name of queue group. 546190792Sgshapiro** 546290792Sgshapiro** Returns: 546390792Sgshapiro** queue group id if found. 546490792Sgshapiro** NOQGRP otherwise. 546590792Sgshapiro*/ 546690792Sgshapiro 546790792Sgshapiroint 546890792Sgshapironame2qid(queuename) 546990792Sgshapiro char *queuename; 547090792Sgshapiro{ 547190792Sgshapiro register STAB *s; 547290792Sgshapiro 547390792Sgshapiro s = stab(queuename, ST_QUEUE, ST_FIND); 547490792Sgshapiro if (s == NULL) 547590792Sgshapiro return NOQGRP; 547690792Sgshapiro return s->s_quegrp->qg_index; 547790792Sgshapiro} 547890792Sgshapiro/* 547964562Sgshapiro** QID_PRINTNAME -- create externally printable version of queue id 548064562Sgshapiro** 548164562Sgshapiro** Parameters: 548264562Sgshapiro** e -- the envelope. 548364562Sgshapiro** 548464562Sgshapiro** Returns: 548564562Sgshapiro** a printable version 548664562Sgshapiro*/ 548764562Sgshapiro 548864562Sgshapirochar * 548964562Sgshapiroqid_printname(e) 549064562Sgshapiro ENVELOPE *e; 549164562Sgshapiro{ 549264562Sgshapiro char *id; 549364562Sgshapiro static char idbuf[MAXQFNAME + 34]; 549464562Sgshapiro 549564562Sgshapiro if (e == NULL) 549664562Sgshapiro return ""; 549764562Sgshapiro 549864562Sgshapiro if (e->e_id == NULL) 549964562Sgshapiro id = ""; 550064562Sgshapiro else 550164562Sgshapiro id = e->e_id; 550264562Sgshapiro 550390792Sgshapiro if (e->e_qdir == NOQDIR) 550464562Sgshapiro return id; 550564562Sgshapiro 550690792Sgshapiro (void) sm_snprintf(idbuf, sizeof idbuf, "%.32s/%s", 550790792Sgshapiro Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name, 550890792Sgshapiro id); 550964562Sgshapiro return idbuf; 551064562Sgshapiro} 551190792Sgshapiro/* 551290792Sgshapiro** QID_PRINTQUEUE -- create full version of queue directory for data files 551364562Sgshapiro** 551464562Sgshapiro** Parameters: 551590792Sgshapiro** qgrp -- index in queue group. 551690792Sgshapiro** qdir -- the short version of the queue directory 551764562Sgshapiro** 551864562Sgshapiro** Returns: 551990792Sgshapiro** the full pathname to the queue (might point to a static var) 552064562Sgshapiro*/ 552164562Sgshapiro 552264562Sgshapirochar * 552390792Sgshapiroqid_printqueue(qgrp, qdir) 552490792Sgshapiro int qgrp; 552590792Sgshapiro int qdir; 552664562Sgshapiro{ 552764562Sgshapiro char *subdir; 552864562Sgshapiro static char dir[MAXPATHLEN]; 552964562Sgshapiro 553090792Sgshapiro if (qdir == NOQDIR) 553190792Sgshapiro return Queue[qgrp]->qg_qdir; 553264562Sgshapiro 553390792Sgshapiro if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0) 553464562Sgshapiro subdir = NULL; 553564562Sgshapiro else 553690792Sgshapiro subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name; 553764562Sgshapiro 553890792Sgshapiro (void) sm_strlcpyn(dir, sizeof dir, 4, 553990792Sgshapiro Queue[qgrp]->qg_qdir, 554064562Sgshapiro subdir == NULL ? "" : "/", 554164562Sgshapiro subdir == NULL ? "" : subdir, 554290792Sgshapiro (bitset(QP_SUBDF, 554390792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 554490792Sgshapiro ? "/df" : "")); 554564562Sgshapiro return dir; 554664562Sgshapiro} 554790792Sgshapiro 554890792Sgshapiro/* 554990792Sgshapiro** PICKQDIR -- Pick a queue directory from a queue group 555064562Sgshapiro** 555190792Sgshapiro** Parameters: 555290792Sgshapiro** qg -- queue group 555390792Sgshapiro** fsize -- file size in bytes 555490792Sgshapiro** e -- envelope, or NULL 555564562Sgshapiro** 555690792Sgshapiro** Result: 555790792Sgshapiro** NOQDIR if no queue directory in qg has enough free space to 555890792Sgshapiro** hold a file of size 'fsize', otherwise the index of 555990792Sgshapiro** a randomly selected queue directory which resides on a 556090792Sgshapiro** file system with enough disk space. 556190792Sgshapiro** XXX This could be extended to select a queuedir with 556290792Sgshapiro** a few (the fewest?) number of entries. That data 556390792Sgshapiro** is available if shared memory is used. 556464562Sgshapiro** 556590792Sgshapiro** Side Effects: 556690792Sgshapiro** If the request fails and e != NULL then sm_syslog is called. 556790792Sgshapiro*/ 556890792Sgshapiro 556990792Sgshapiroint 557090792Sgshapiropickqdir(qg, fsize, e) 557190792Sgshapiro QUEUEGRP *qg; 557290792Sgshapiro long fsize; 557390792Sgshapiro ENVELOPE *e; 557490792Sgshapiro{ 557590792Sgshapiro int qdir; 557690792Sgshapiro int i; 557790792Sgshapiro long avail = 0; 557890792Sgshapiro 557990792Sgshapiro /* Pick a random directory, as a starting point. */ 558090792Sgshapiro if (qg->qg_numqueues <= 1) 558190792Sgshapiro qdir = 0; 558290792Sgshapiro else 558390792Sgshapiro qdir = get_rand_mod(qg->qg_numqueues); 558490792Sgshapiro 558590792Sgshapiro if (MinBlocksFree <= 0 && fsize <= 0) 558690792Sgshapiro return qdir; 558790792Sgshapiro 558890792Sgshapiro /* 558990792Sgshapiro ** Now iterate over the queue directories, 559090792Sgshapiro ** looking for a directory with enough space for this message. 559190792Sgshapiro */ 559290792Sgshapiro 559390792Sgshapiro i = qdir; 559490792Sgshapiro do 559590792Sgshapiro { 559690792Sgshapiro QPATHS *qp = &qg->qg_qpaths[i]; 559790792Sgshapiro long needed = 0; 559890792Sgshapiro long fsavail = 0; 559990792Sgshapiro 560090792Sgshapiro if (fsize > 0) 560190792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx) 560290792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx) 560390792Sgshapiro > 0) ? 1 : 0); 560490792Sgshapiro if (MinBlocksFree > 0) 560590792Sgshapiro needed += MinBlocksFree; 560690792Sgshapiro fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx); 560790792Sgshapiro#if SM_CONF_SHM 560890792Sgshapiro if (fsavail <= 0) 560990792Sgshapiro { 561090792Sgshapiro long blksize; 561190792Sgshapiro 561290792Sgshapiro /* 561390792Sgshapiro ** might be not correctly updated, 561490792Sgshapiro ** let's try to get the info directly. 561590792Sgshapiro */ 561690792Sgshapiro 561790792Sgshapiro fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx), 561890792Sgshapiro &blksize); 561990792Sgshapiro if (fsavail < 0) 562090792Sgshapiro fsavail = 0; 562190792Sgshapiro } 562290792Sgshapiro#endif /* SM_CONF_SHM */ 562390792Sgshapiro if (needed <= fsavail) 562490792Sgshapiro return i; 562590792Sgshapiro if (avail < fsavail) 562690792Sgshapiro avail = fsavail; 562790792Sgshapiro 562890792Sgshapiro if (qg->qg_numqueues > 0) 562990792Sgshapiro i = (i + 1) % qg->qg_numqueues; 563090792Sgshapiro } while (i != qdir); 563190792Sgshapiro 563290792Sgshapiro if (e != NULL && LogLevel > 0) 563390792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 563490792Sgshapiro "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld", 563590792Sgshapiro CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, 563690792Sgshapiro fsize, MinBlocksFree, 563790792Sgshapiro qg->qg_qdir, avail); 563890792Sgshapiro return NOQDIR; 563990792Sgshapiro} 564090792Sgshapiro/* 564190792Sgshapiro** SETNEWQUEUE -- Sets a new queue group and directory 564290792Sgshapiro** 564390792Sgshapiro** Assign a queue group and directory to an envelope and store the 564490792Sgshapiro** directory in e->e_qdir. 564590792Sgshapiro** 564664562Sgshapiro** Parameters: 564764562Sgshapiro** e -- envelope to assign a queue for. 564864562Sgshapiro** 564964562Sgshapiro** Returns: 565090792Sgshapiro** true if successful 565190792Sgshapiro** false otherwise 565290792Sgshapiro** 565390792Sgshapiro** Side Effects: 565490792Sgshapiro** On success, e->e_qgrp and e->e_qdir are non-negative. 565590792Sgshapiro** On failure (not enough disk space), 565690792Sgshapiro** e->qgrp = NOQGRP, e->e_qdir = NOQDIR 565790792Sgshapiro** and usrerr() is invoked (which could raise an exception). 565864562Sgshapiro*/ 565964562Sgshapiro 566090792Sgshapirobool 566164562Sgshapirosetnewqueue(e) 566264562Sgshapiro ENVELOPE *e; 566364562Sgshapiro{ 566464562Sgshapiro if (tTd(41, 20)) 566590792Sgshapiro sm_dprintf("setnewqueue: called\n"); 566664562Sgshapiro 566790792Sgshapiro /* not set somewhere else */ 566890792Sgshapiro if (e->e_qgrp == NOQGRP) 566964562Sgshapiro { 5670102528Sgshapiro ADDRESS *q; 5671102528Sgshapiro 567290792Sgshapiro /* 5673102528Sgshapiro ** Use the queue group of the "first" recipient, as set by 567490792Sgshapiro ** the "queuegroup" rule set. If that is not defined, then 567590792Sgshapiro ** use the queue group of the mailer of the first recipient. 567690792Sgshapiro ** If that is not defined either, then use the default 567790792Sgshapiro ** queue group. 5678102528Sgshapiro ** Notice: "first" depends on the sorting of sendqueue 5679102528Sgshapiro ** in recipient(). 5680102528Sgshapiro ** To avoid problems with "bad" recipients look 5681102528Sgshapiro ** for a valid address first. 568290792Sgshapiro */ 568390792Sgshapiro 5684102528Sgshapiro q = e->e_sendqueue; 5685102528Sgshapiro while (q != NULL && 5686102528Sgshapiro (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state))) 5687102528Sgshapiro { 5688102528Sgshapiro q = q->q_next; 5689102528Sgshapiro } 5690102528Sgshapiro if (q == NULL) 569190792Sgshapiro e->e_qgrp = 0; 5692102528Sgshapiro else if (q->q_qgrp >= 0) 5693102528Sgshapiro e->e_qgrp = q->q_qgrp; 5694102528Sgshapiro else if (q->q_mailer != NULL && 5695102528Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 5696102528Sgshapiro e->e_qgrp = q->q_mailer->m_qgrp; 569790792Sgshapiro else 569890792Sgshapiro e->e_qgrp = 0; 569990792Sgshapiro e->e_dfqgrp = e->e_qgrp; 570090792Sgshapiro } 570190792Sgshapiro 570290792Sgshapiro if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir)) 570390792Sgshapiro { 570464562Sgshapiro if (tTd(41, 20)) 570590792Sgshapiro sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n", 570690792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 570790792Sgshapiro return true; 570864562Sgshapiro } 570964562Sgshapiro 571090792Sgshapiro filesys_update(); 571190792Sgshapiro e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e); 571290792Sgshapiro if (e->e_qdir == NOQDIR) 571364562Sgshapiro { 571490792Sgshapiro e->e_qgrp = NOQGRP; 571590792Sgshapiro if (!bitset(EF_FATALERRS, e->e_flags)) 571690792Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 571790792Sgshapiro e->e_flags |= EF_FATALERRS; 571890792Sgshapiro return false; 571964562Sgshapiro } 572064562Sgshapiro 572164562Sgshapiro if (tTd(41, 3)) 572290792Sgshapiro sm_dprintf("setnewqueue: Assigned queue directory %s\n", 572390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 572490792Sgshapiro 572590792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 572690792Sgshapiro { 572790792Sgshapiro e->e_xfqgrp = e->e_qgrp; 572890792Sgshapiro e->e_xfqdir = e->e_qdir; 572990792Sgshapiro } 573090792Sgshapiro e->e_dfqdir = e->e_qdir; 573190792Sgshapiro return true; 573264562Sgshapiro} 573390792Sgshapiro/* 573464562Sgshapiro** CHKQDIR -- check a queue directory 573564562Sgshapiro** 573664562Sgshapiro** Parameters: 573764562Sgshapiro** name -- name of queue directory 573864562Sgshapiro** sff -- flags for safefile() 573964562Sgshapiro** 574064562Sgshapiro** Returns: 574164562Sgshapiro** is it a queue directory? 574264562Sgshapiro*/ 574364562Sgshapiro 574464562Sgshapirostatic bool 574564562Sgshapirochkqdir(name, sff) 574664562Sgshapiro char *name; 574764562Sgshapiro long sff; 574864562Sgshapiro{ 574964562Sgshapiro struct stat statb; 575064562Sgshapiro int i; 575164562Sgshapiro 575266494Sgshapiro /* skip over . and .. directories */ 575366494Sgshapiro if (name[0] == '.' && 575477349Sgshapiro (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) 575590792Sgshapiro return false; 575690792Sgshapiro#if HASLSTAT 575764562Sgshapiro if (lstat(name, &statb) < 0) 575890792Sgshapiro#else /* HASLSTAT */ 575964562Sgshapiro if (stat(name, &statb) < 0) 576090792Sgshapiro#endif /* HASLSTAT */ 576164562Sgshapiro { 576264562Sgshapiro if (tTd(41, 2)) 576390792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 576490792Sgshapiro name, sm_errstring(errno)); 576590792Sgshapiro return false; 576664562Sgshapiro } 576790792Sgshapiro#if HASLSTAT 576864562Sgshapiro if (S_ISLNK(statb.st_mode)) 576964562Sgshapiro { 577064562Sgshapiro /* 577164562Sgshapiro ** For a symlink we need to make sure the 577264562Sgshapiro ** target is a directory 577364562Sgshapiro */ 577490792Sgshapiro 577564562Sgshapiro if (stat(name, &statb) < 0) 577664562Sgshapiro { 577764562Sgshapiro if (tTd(41, 2)) 577890792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 577990792Sgshapiro name, sm_errstring(errno)); 578090792Sgshapiro return false; 578164562Sgshapiro } 578264562Sgshapiro } 578390792Sgshapiro#endif /* HASLSTAT */ 578464562Sgshapiro 578564562Sgshapiro if (!S_ISDIR(statb.st_mode)) 578664562Sgshapiro { 578764562Sgshapiro if (tTd(41, 2)) 578890792Sgshapiro sm_dprintf("chkqdir: \"%s\": Not a directory\n", 578964562Sgshapiro name); 579090792Sgshapiro return false; 579164562Sgshapiro } 579264562Sgshapiro 579364562Sgshapiro /* Print a warning if unsafe (but still use it) */ 579490792Sgshapiro /* XXX do this only if we want the warning? */ 579564562Sgshapiro i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0); 579698121Sgshapiro if (i != 0) 579798121Sgshapiro { 579898121Sgshapiro if (tTd(41, 2)) 579998121Sgshapiro sm_dprintf("chkqdir: \"%s\": Not safe: %s\n", 580098121Sgshapiro name, sm_errstring(i)); 580198121Sgshapiro#if _FFR_CHK_QUEUE 580298121Sgshapiro if (LogLevel > 8) 580398121Sgshapiro sm_syslog(LOG_WARNING, NOQID, 580498121Sgshapiro "queue directory \"%s\": Not safe: %s", 580598121Sgshapiro name, sm_errstring(i)); 580698121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 580798121Sgshapiro } 580890792Sgshapiro return true; 580964562Sgshapiro} 581090792Sgshapiro/* 581164562Sgshapiro** MULTIQUEUE_CACHE -- cache a list of paths to queues. 581264562Sgshapiro** 581364562Sgshapiro** Each potential queue is checked as the cache is built. 581464562Sgshapiro** Thereafter, each is blindly trusted. 581564562Sgshapiro** Note that we can be called again after a timeout to rebuild 581664562Sgshapiro** (although code for that is not ready yet). 581764562Sgshapiro** 581864562Sgshapiro** Parameters: 581990792Sgshapiro** basedir -- base of all queue directories. 582090792Sgshapiro** blen -- strlen(basedir). 582190792Sgshapiro** qg -- queue group. 582290792Sgshapiro** qn -- number of queue directories already cached. 582390792Sgshapiro** phash -- pointer to hash value over queue dirs. 582490792Sgshapiro#if SM_CONF_SHM 582590792Sgshapiro** only used if shared memory is active. 582690792Sgshapiro#endif * SM_CONF_SHM * 582764562Sgshapiro** 582864562Sgshapiro** Returns: 582990792Sgshapiro** new number of queue directories. 583064562Sgshapiro*/ 583164562Sgshapiro 583290792Sgshapiro#define INITIAL_SLOTS 20 583390792Sgshapiro#define ADD_SLOTS 10 583490792Sgshapiro 583590792Sgshapirostatic int 583690792Sgshapiromultiqueue_cache(basedir, blen, qg, qn, phash) 583790792Sgshapiro char *basedir; 583890792Sgshapiro int blen; 583990792Sgshapiro QUEUEGRP *qg; 584090792Sgshapiro int qn; 584190792Sgshapiro unsigned int *phash; 584264562Sgshapiro{ 584364562Sgshapiro char *cp; 584464562Sgshapiro int i, len; 584564562Sgshapiro int slotsleft = 0; 584664562Sgshapiro long sff = SFF_ANYFILE; 584764562Sgshapiro char qpath[MAXPATHLEN]; 584864562Sgshapiro char subdir[MAXPATHLEN]; 584990792Sgshapiro char prefix[MAXPATHLEN]; /* dir relative to basedir */ 585064562Sgshapiro 585164562Sgshapiro if (tTd(41, 20)) 585290792Sgshapiro sm_dprintf("multiqueue_cache: called\n"); 585364562Sgshapiro 585490792Sgshapiro /* Initialize to current directory */ 585590792Sgshapiro prefix[0] = '.'; 585690792Sgshapiro prefix[1] = '\0'; 585790792Sgshapiro if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL) 585864562Sgshapiro { 585990792Sgshapiro for (i = 0; i < qg->qg_numqueues; i++) 586064562Sgshapiro { 586190792Sgshapiro if (qg->qg_qpaths[i].qp_name != NULL) 586290792Sgshapiro (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */ 586364562Sgshapiro } 586490792Sgshapiro (void) sm_free((char *) qg->qg_qpaths); /* XXX */ 586590792Sgshapiro qg->qg_qpaths = NULL; 586690792Sgshapiro qg->qg_numqueues = 0; 586764562Sgshapiro } 586864562Sgshapiro 586964562Sgshapiro /* If running as root, allow safedirpath() checks to use privs */ 587064562Sgshapiro if (RunAsUid == 0) 587164562Sgshapiro sff |= SFF_ROOTOK; 587298121Sgshapiro#if _FFR_CHK_QUEUE 587398121Sgshapiro sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES; 587498121Sgshapiro if (!UseMSP) 587598121Sgshapiro sff |= SFF_NOGWFILES; 587698121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 587764562Sgshapiro 587890792Sgshapiro if (!SM_IS_DIR_START(qg->qg_qdir)) 587990792Sgshapiro { 588090792Sgshapiro /* 588190792Sgshapiro ** XXX we could add basedir, but then we have to realloc() 588290792Sgshapiro ** the string... Maybe another time. 588390792Sgshapiro */ 588490792Sgshapiro 588590792Sgshapiro syserr("QueuePath %s not absolute", qg->qg_qdir); 588690792Sgshapiro ExitStat = EX_CONFIG; 588790792Sgshapiro return qn; 588890792Sgshapiro } 588990792Sgshapiro 589090792Sgshapiro /* qpath: directory of current workgroup */ 589190792Sgshapiro len = sm_strlcpy(qpath, qg->qg_qdir, sizeof qpath); 589290792Sgshapiro if (len >= sizeof qpath) 589390792Sgshapiro { 589490792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 589590792Sgshapiro qg->qg_qdir, (int) sizeof qpath); 589690792Sgshapiro ExitStat = EX_CONFIG; 589790792Sgshapiro return qn; 589890792Sgshapiro } 589990792Sgshapiro 590090792Sgshapiro /* begin of qpath must be same as basedir */ 590190792Sgshapiro if (strncmp(basedir, qpath, blen) != 0 && 590290792Sgshapiro (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1)) 590390792Sgshapiro { 590490792Sgshapiro syserr("QueuePath %s not subpath of QueueDirectory %s", 590590792Sgshapiro qpath, basedir); 590690792Sgshapiro ExitStat = EX_CONFIG; 590790792Sgshapiro return qn; 590890792Sgshapiro } 590990792Sgshapiro 591090792Sgshapiro /* Do we have a nested subdirectory? */ 591190792Sgshapiro if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL) 591290792Sgshapiro { 591390792Sgshapiro 591490792Sgshapiro /* Copy subdirectory into prefix for later use */ 591590792Sgshapiro if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof prefix) >= 591690792Sgshapiro sizeof prefix) 591790792Sgshapiro { 591890792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 591990792Sgshapiro qg->qg_qdir, (int) sizeof qpath); 592090792Sgshapiro ExitStat = EX_CONFIG; 592190792Sgshapiro return qn; 592290792Sgshapiro } 592390792Sgshapiro cp = SM_LAST_DIR_DELIM(prefix); 592490792Sgshapiro SM_ASSERT(cp != NULL); 592590792Sgshapiro *cp = '\0'; /* cut off trailing / */ 592690792Sgshapiro } 592790792Sgshapiro 592890792Sgshapiro /* This is guaranteed by the basedir check above */ 592990792Sgshapiro SM_ASSERT(len >= blen - 1); 593090792Sgshapiro cp = &qpath[len - 1]; 593164562Sgshapiro if (*cp == '*') 593264562Sgshapiro { 593390792Sgshapiro register DIR *dp; 593490792Sgshapiro register struct dirent *d; 593590792Sgshapiro int off; 593690792Sgshapiro char *delim; 593790792Sgshapiro char relpath[MAXPATHLEN]; 593890792Sgshapiro 593990792Sgshapiro *cp = '\0'; /* Overwrite wildcard */ 594090792Sgshapiro if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL) 594164562Sgshapiro { 594264562Sgshapiro syserr("QueueDirectory: can not wildcard relative path"); 594364562Sgshapiro if (tTd(41, 2)) 594490792Sgshapiro sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n", 594571345Sgshapiro qpath); 594664562Sgshapiro ExitStat = EX_CONFIG; 594790792Sgshapiro return qn; 594864562Sgshapiro } 594964562Sgshapiro if (cp == qpath) 595064562Sgshapiro { 595164562Sgshapiro /* 595264562Sgshapiro ** Special case of top level wildcard, like /foo* 595390792Sgshapiro ** Change to //foo* 595464562Sgshapiro */ 595564562Sgshapiro 595690792Sgshapiro (void) sm_strlcpy(qpath + 1, qpath, sizeof qpath - 1); 595764562Sgshapiro ++cp; 595864562Sgshapiro } 595990792Sgshapiro delim = cp; 596090792Sgshapiro *(cp++) = '\0'; /* Replace / with \0 */ 596190792Sgshapiro len = strlen(cp); /* Last component of queue directory */ 596264562Sgshapiro 596390792Sgshapiro /* 596490792Sgshapiro ** Path relative to basedir, with trailing / 596590792Sgshapiro ** It will be modified below to specify the subdirectories 596690792Sgshapiro ** so they can be opened without chdir(). 596790792Sgshapiro */ 596890792Sgshapiro 596990792Sgshapiro off = sm_strlcpyn(relpath, sizeof relpath, 2, prefix, "/"); 597090792Sgshapiro SM_ASSERT(off < sizeof relpath); 597190792Sgshapiro 597264562Sgshapiro if (tTd(41, 2)) 597390792Sgshapiro sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n", 597490792Sgshapiro relpath, cp); 597564562Sgshapiro 597690792Sgshapiro /* It is always basedir: we don't need to store it per group */ 597790792Sgshapiro /* XXX: optimize this! -> one more global? */ 597890792Sgshapiro qg->qg_qdir = newstr(basedir); 597990792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */ 598064562Sgshapiro 598164562Sgshapiro /* 598264562Sgshapiro ** XXX Should probably wrap this whole loop in a timeout 598364562Sgshapiro ** in case some wag decides to NFS mount the queues. 598464562Sgshapiro */ 598564562Sgshapiro 598690792Sgshapiro /* Test path to get warning messages. */ 598790792Sgshapiro if (qn == 0) 598864562Sgshapiro { 598990792Sgshapiro /* XXX qg_runasuid and qg_runasgid for specials? */ 599090792Sgshapiro i = safedirpath(basedir, RunAsUid, RunAsGid, NULL, 599190792Sgshapiro sff, 0, 0); 599290792Sgshapiro if (i != 0 && tTd(41, 2)) 599390792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", 599490792Sgshapiro basedir, sm_errstring(i)); 599564562Sgshapiro } 599664562Sgshapiro 599790792Sgshapiro if ((dp = opendir(prefix)) == NULL) 599864562Sgshapiro { 599990792Sgshapiro syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix); 600064562Sgshapiro if (tTd(41, 2)) 600190792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n", 600290792Sgshapiro qg->qg_qdir, prefix, 600390792Sgshapiro sm_errstring(errno)); 600464562Sgshapiro ExitStat = EX_CONFIG; 600590792Sgshapiro return qn; 600664562Sgshapiro } 600764562Sgshapiro while ((d = readdir(dp)) != NULL) 600864562Sgshapiro { 600990792Sgshapiro i = strlen(d->d_name); 601090792Sgshapiro if (i < len || strncmp(d->d_name, cp, len) != 0) 601164562Sgshapiro { 601264562Sgshapiro if (tTd(41, 5)) 601390792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\", skipped\n", 601464562Sgshapiro d->d_name); 601564562Sgshapiro continue; 601664562Sgshapiro } 601790792Sgshapiro 601890792Sgshapiro /* Create relative pathname: prefix + local directory */ 601990792Sgshapiro i = sizeof(relpath) - off; 602090792Sgshapiro if (sm_strlcpy(relpath + off, d->d_name, i) >= i) 602190792Sgshapiro continue; /* way too long */ 602290792Sgshapiro 602390792Sgshapiro if (!chkqdir(relpath, sff)) 602464562Sgshapiro continue; 602564562Sgshapiro 602690792Sgshapiro if (qg->qg_qpaths == NULL) 602764562Sgshapiro { 602890792Sgshapiro slotsleft = INITIAL_SLOTS; 602990792Sgshapiro qg->qg_qpaths = (QPATHS *)xalloc((sizeof *qg->qg_qpaths) * 603090792Sgshapiro slotsleft); 603190792Sgshapiro qg->qg_numqueues = 0; 603264562Sgshapiro } 603364562Sgshapiro else if (slotsleft < 1) 603464562Sgshapiro { 603590792Sgshapiro qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths, 603690792Sgshapiro (sizeof *qg->qg_qpaths) * 603790792Sgshapiro (qg->qg_numqueues + 603890792Sgshapiro ADD_SLOTS)); 603990792Sgshapiro if (qg->qg_qpaths == NULL) 604064562Sgshapiro { 604164562Sgshapiro (void) closedir(dp); 604290792Sgshapiro return qn; 604364562Sgshapiro } 604490792Sgshapiro slotsleft += ADD_SLOTS; 604564562Sgshapiro } 604664562Sgshapiro 604764562Sgshapiro /* check subdirs */ 604890792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB; 604964562Sgshapiro 605090792Sgshapiro#define CHKRSUBDIR(name, flag) \ 605190792Sgshapiro (void) sm_strlcpyn(subdir, sizeof subdir, 3, relpath, "/", name); \ 605290792Sgshapiro if (chkqdir(subdir, sff)) \ 605390792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \ 605490792Sgshapiro else 605564562Sgshapiro 605664562Sgshapiro 605790792Sgshapiro CHKRSUBDIR("qf", QP_SUBQF); 605890792Sgshapiro CHKRSUBDIR("df", QP_SUBDF); 605990792Sgshapiro CHKRSUBDIR("xf", QP_SUBXF); 606090792Sgshapiro 606164562Sgshapiro /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */ 606264562Sgshapiro /* maybe even - 17 (subdirs) */ 606390792Sgshapiro 606490792Sgshapiro if (prefix[0] != '.') 606590792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 606690792Sgshapiro newstr(relpath); 606790792Sgshapiro else 606890792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 606990792Sgshapiro newstr(d->d_name); 607090792Sgshapiro 607164562Sgshapiro if (tTd(41, 2)) 607290792Sgshapiro sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", 607390792Sgshapiro qg->qg_numqueues, relpath, 607490792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs); 607590792Sgshapiro#if SM_CONF_SHM 607690792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn; 607790792Sgshapiro *phash = hash_q(relpath, *phash); 607890792Sgshapiro#endif /* SM_CONF_SHM */ 607990792Sgshapiro qg->qg_numqueues++; 608090792Sgshapiro ++qn; 608164562Sgshapiro slotsleft--; 608264562Sgshapiro } 608364562Sgshapiro (void) closedir(dp); 608490792Sgshapiro 608590792Sgshapiro /* undo damage */ 608690792Sgshapiro *delim = '/'; 608764562Sgshapiro } 608890792Sgshapiro if (qg->qg_numqueues == 0) 608964562Sgshapiro { 609090792Sgshapiro qg->qg_qpaths = (QPATHS *) xalloc(sizeof *qg->qg_qpaths); 609164562Sgshapiro 609264562Sgshapiro /* test path to get warning messages */ 609390792Sgshapiro i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0); 609490792Sgshapiro if (i == ENOENT) 609564562Sgshapiro { 609690792Sgshapiro syserr("can not opendir(%s)", qpath); 609764562Sgshapiro if (tTd(41, 2)) 609890792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", 609990792Sgshapiro qpath, sm_errstring(i)); 610064562Sgshapiro ExitStat = EX_CONFIG; 610190792Sgshapiro return qn; 610264562Sgshapiro } 610364562Sgshapiro 610490792Sgshapiro qg->qg_qpaths[0].qp_subdirs = QP_NOSUB; 610590792Sgshapiro qg->qg_numqueues = 1; 610690792Sgshapiro 610764562Sgshapiro /* check subdirs */ 610890792Sgshapiro#define CHKSUBDIR(name, flag) \ 610990792Sgshapiro (void) sm_strlcpyn(subdir, sizeof subdir, 3, qg->qg_qdir, "/", name); \ 611090792Sgshapiro if (chkqdir(subdir, sff)) \ 611190792Sgshapiro qg->qg_qpaths[0].qp_subdirs |= flag; \ 611290792Sgshapiro else 611364562Sgshapiro 611490792Sgshapiro CHKSUBDIR("qf", QP_SUBQF); 611590792Sgshapiro CHKSUBDIR("df", QP_SUBDF); 611690792Sgshapiro CHKSUBDIR("xf", QP_SUBXF); 611764562Sgshapiro 611890792Sgshapiro if (qg->qg_qdir[blen - 1] != '\0' && 611990792Sgshapiro qg->qg_qdir[blen] != '\0') 612090792Sgshapiro { 612190792Sgshapiro /* 612290792Sgshapiro ** Copy the last component into qpaths and 612390792Sgshapiro ** cut off qdir 612490792Sgshapiro */ 612590792Sgshapiro 612690792Sgshapiro qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen); 612790792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; 612890792Sgshapiro } 612990792Sgshapiro else 613090792Sgshapiro qg->qg_qpaths[0].qp_name = newstr("."); 613190792Sgshapiro 613290792Sgshapiro#if SM_CONF_SHM 613390792Sgshapiro qg->qg_qpaths[0].qp_idx = qn; 613490792Sgshapiro *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash); 613590792Sgshapiro#endif /* SM_CONF_SHM */ 613690792Sgshapiro ++qn; 613764562Sgshapiro } 613890792Sgshapiro return qn; 613964562Sgshapiro} 614064562Sgshapiro 614190792Sgshapiro/* 614290792Sgshapiro** FILESYS_FIND -- find entry in FileSys table, or add new one 614390792Sgshapiro** 614490792Sgshapiro** Given the pathname of a directory, determine the file system 614590792Sgshapiro** in which that directory resides, and return a pointer to the 614690792Sgshapiro** entry in the FileSys table that describes the file system. 614790792Sgshapiro** A new entry is added if necessary (and requested). 614890792Sgshapiro** If the directory does not exist, -1 is returned. 614990792Sgshapiro** 615090792Sgshapiro** Parameters: 615190792Sgshapiro** path -- pathname of directory 615290792Sgshapiro** add -- add to structure if not found. 615390792Sgshapiro** 615490792Sgshapiro** Returns: 615590792Sgshapiro** >=0: found: index in file system table 615690792Sgshapiro** <0: some error, i.e., 615790792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 615890792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 615990792Sgshapiro** FSF_NOT_FOUND: not in list 616090792Sgshapiro*/ 616190792Sgshapiro 616290792Sgshapirostatic short filesys_find __P((char *, bool)); 616390792Sgshapiro 616490792Sgshapiro#define FSF_NOT_FOUND (-1) 616590792Sgshapiro#define FSF_STAT_FAIL (-2) 616690792Sgshapiro#define FSF_TOO_MANY (-3) 616790792Sgshapiro 616890792Sgshapirostatic short 616990792Sgshapirofilesys_find(path, add) 617090792Sgshapiro char *path; 617190792Sgshapiro bool add; 617290792Sgshapiro{ 617390792Sgshapiro struct stat st; 617490792Sgshapiro short i; 617590792Sgshapiro 617690792Sgshapiro if (stat(path, &st) < 0) 617790792Sgshapiro { 617890792Sgshapiro syserr("cannot stat queue directory %s", path); 617990792Sgshapiro return FSF_STAT_FAIL; 618090792Sgshapiro } 618190792Sgshapiro for (i = 0; i < NumFileSys; ++i) 618290792Sgshapiro { 618390792Sgshapiro if (FILE_SYS_DEV(i) == st.st_dev) 618490792Sgshapiro return i; 618590792Sgshapiro } 618690792Sgshapiro if (i >= MAXFILESYS) 618790792Sgshapiro { 618890792Sgshapiro syserr("too many queue file systems (%d max)", MAXFILESYS); 618990792Sgshapiro return FSF_TOO_MANY; 619090792Sgshapiro } 619190792Sgshapiro if (!add) 619290792Sgshapiro return FSF_NOT_FOUND; 619390792Sgshapiro 619490792Sgshapiro ++NumFileSys; 619590792Sgshapiro FILE_SYS_NAME(i) = path; 619690792Sgshapiro FILE_SYS_DEV(i) = st.st_dev; 619790792Sgshapiro FILE_SYS_AVAIL(i) = 0; 619890792Sgshapiro FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */ 619990792Sgshapiro return i; 620090792Sgshapiro} 620190792Sgshapiro 620290792Sgshapiro/* 620390792Sgshapiro** FILESYS_SETUP -- set up mapping from queue directories to file systems 620490792Sgshapiro** 620590792Sgshapiro** This data structure is used to efficiently check the amount of 620690792Sgshapiro** free space available in a set of queue directories. 620790792Sgshapiro** 620890792Sgshapiro** Parameters: 620990792Sgshapiro** add -- initialize structure if necessary. 621090792Sgshapiro** 621190792Sgshapiro** Returns: 621290792Sgshapiro** 0: success 621390792Sgshapiro** <0: some error, i.e., 621490792Sgshapiro** FSF_NOT_FOUND: not in list 621590792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 621690792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 621790792Sgshapiro*/ 621890792Sgshapiro 621990792Sgshapirostatic int filesys_setup __P((bool)); 622090792Sgshapiro 622190792Sgshapirostatic int 622290792Sgshapirofilesys_setup(add) 622390792Sgshapiro bool add; 622490792Sgshapiro{ 622590792Sgshapiro int i, j; 622690792Sgshapiro short fs; 622790792Sgshapiro int ret; 622890792Sgshapiro 622990792Sgshapiro ret = 0; 623090792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 623190792Sgshapiro { 623290792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; ++j) 623390792Sgshapiro { 623490792Sgshapiro QPATHS *qp = &Queue[i]->qg_qpaths[j]; 623590792Sgshapiro 623690792Sgshapiro fs = filesys_find(qp->qp_name, add); 623790792Sgshapiro if (fs >= 0) 623890792Sgshapiro qp->qp_fsysidx = fs; 623990792Sgshapiro else 624090792Sgshapiro qp->qp_fsysidx = 0; 624190792Sgshapiro if (fs < ret) 624290792Sgshapiro ret = fs; 624390792Sgshapiro } 624490792Sgshapiro } 624590792Sgshapiro return ret; 624690792Sgshapiro} 624790792Sgshapiro 624890792Sgshapiro/* 624990792Sgshapiro** FILESYS_UPDATE -- update amount of free space on all file systems 625090792Sgshapiro** 625190792Sgshapiro** The FileSys table is used to cache the amount of free space 625290792Sgshapiro** available on all queue directory file systems. 625390792Sgshapiro** This function updates the cached information if it has expired. 625490792Sgshapiro** 625590792Sgshapiro** Parameters: 625690792Sgshapiro** none. 625790792Sgshapiro** 625890792Sgshapiro** Returns: 625990792Sgshapiro** none. 626090792Sgshapiro** 626190792Sgshapiro** Side Effects: 626290792Sgshapiro** Updates FileSys table. 626390792Sgshapiro*/ 626490792Sgshapiro 626590792Sgshapirovoid 626690792Sgshapirofilesys_update() 626790792Sgshapiro{ 626890792Sgshapiro int i; 626990792Sgshapiro long avail, blksize; 627090792Sgshapiro time_t now; 627190792Sgshapiro static time_t nextupdate = 0; 627290792Sgshapiro 627390792Sgshapiro#if SM_CONF_SHM 627490792Sgshapiro /* only the daemon updates this structure */ 627590792Sgshapiro if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid) 627690792Sgshapiro return; 627790792Sgshapiro#endif /* SM_CONF_SHM */ 627890792Sgshapiro now = curtime(); 627990792Sgshapiro if (now < nextupdate) 628090792Sgshapiro return; 628190792Sgshapiro nextupdate = now + FILESYS_UPDATE_INTERVAL; 628290792Sgshapiro for (i = 0; i < NumFileSys; ++i) 628390792Sgshapiro { 628490792Sgshapiro FILESYS *fs = &FILE_SYS(i); 628590792Sgshapiro 628690792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 628790792Sgshapiro if (avail < 0 || blksize <= 0) 628890792Sgshapiro { 628990792Sgshapiro if (LogLevel > 5) 629090792Sgshapiro sm_syslog(LOG_ERR, NOQID, 629190792Sgshapiro "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld", 629290792Sgshapiro sm_errstring(errno), 629390792Sgshapiro FILE_SYS_NAME(i), avail, blksize); 629490792Sgshapiro fs->fs_avail = 0; 629590792Sgshapiro fs->fs_blksize = 1024; /* avoid divide by zero */ 629690792Sgshapiro nextupdate = now + 2; /* let's do this soon again */ 629790792Sgshapiro } 629890792Sgshapiro else 629990792Sgshapiro { 630090792Sgshapiro fs->fs_avail = avail; 630190792Sgshapiro fs->fs_blksize = blksize; 630290792Sgshapiro } 630390792Sgshapiro } 630490792Sgshapiro} 630590792Sgshapiro 630690792Sgshapiro#if _FFR_ANY_FREE_FS 630790792Sgshapiro/* 630890792Sgshapiro** FILESYS_FREE -- check whether there is at least one fs with enough space. 630990792Sgshapiro** 631090792Sgshapiro** Parameters: 631190792Sgshapiro** fsize -- file size in bytes 631290792Sgshapiro** 631390792Sgshapiro** Returns: 631490792Sgshapiro** true iff there is one fs with more than fsize bytes free. 631590792Sgshapiro*/ 631690792Sgshapiro 631790792Sgshapirobool 631890792Sgshapirofilesys_free(fsize) 631990792Sgshapiro long fsize; 632090792Sgshapiro{ 632190792Sgshapiro int i; 632290792Sgshapiro 632390792Sgshapiro if (fsize <= 0) 632490792Sgshapiro return true; 632590792Sgshapiro for (i = 0; i < NumFileSys; ++i) 632690792Sgshapiro { 632790792Sgshapiro long needed = 0; 632890792Sgshapiro 632990792Sgshapiro if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0) 633090792Sgshapiro continue; 633190792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(i) 633290792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(i) 633390792Sgshapiro > 0) ? 1 : 0) 633490792Sgshapiro + MinBlocksFree; 633590792Sgshapiro if (needed <= FILE_SYS_AVAIL(i)) 633690792Sgshapiro return true; 633790792Sgshapiro } 633890792Sgshapiro return false; 633990792Sgshapiro} 634090792Sgshapiro#endif /* _FFR_ANY_FREE_FS */ 634190792Sgshapiro 634290792Sgshapiro#if _FFR_CONTROL_MSTAT 634390792Sgshapiro/* 634490792Sgshapiro** DISK_STATUS -- show amount of free space in queue directories 634590792Sgshapiro** 634690792Sgshapiro** Parameters: 634790792Sgshapiro** out -- output file pointer. 634890792Sgshapiro** prefix -- string to output in front of each line. 634990792Sgshapiro** 635090792Sgshapiro** Returns: 635190792Sgshapiro** none. 635290792Sgshapiro*/ 635390792Sgshapiro 635490792Sgshapirovoid 635590792Sgshapirodisk_status(out, prefix) 635690792Sgshapiro SM_FILE_T *out; 635790792Sgshapiro char *prefix; 635890792Sgshapiro{ 635990792Sgshapiro int i; 636090792Sgshapiro long avail, blksize; 636190792Sgshapiro long free; 636290792Sgshapiro 636390792Sgshapiro for (i = 0; i < NumFileSys; ++i) 636490792Sgshapiro { 636590792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 636690792Sgshapiro if (avail >= 0 && blksize > 0) 636790792Sgshapiro { 636890792Sgshapiro free = (long)((double) avail * 636990792Sgshapiro ((double) blksize / 1024)); 637090792Sgshapiro } 637190792Sgshapiro else 637290792Sgshapiro free = -1; 637390792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 637490792Sgshapiro "%s%d/%s/%ld\r\n", 637590792Sgshapiro prefix, i, 637690792Sgshapiro FILE_SYS_NAME(i), 637790792Sgshapiro free); 637890792Sgshapiro } 637990792Sgshapiro} 638090792Sgshapiro#endif /* _FFR_CONTROL_MSTAT */ 638190792Sgshapiro 638290792Sgshapiro#if SM_CONF_SHM 638390792Sgshapiro/* 638490792Sgshapiro** UPD_QS -- update information about queue when adding/deleting an entry 638590792Sgshapiro** 638690792Sgshapiro** Parameters: 638790792Sgshapiro** e -- envelope. 638890792Sgshapiro** delete -- delete/add entry. 638990792Sgshapiro** avail -- update the space available as well. 639090792Sgshapiro** 639190792Sgshapiro** Returns: 639290792Sgshapiro** none. 639390792Sgshapiro** 639490792Sgshapiro** Side Effects: 639590792Sgshapiro** Modifies available space in filesystem. 639690792Sgshapiro** Changes number of entries in queue directory. 639790792Sgshapiro*/ 639890792Sgshapiro 639990792Sgshapirovoid 640090792Sgshapiroupd_qs(e, delete, avail) 640190792Sgshapiro ENVELOPE *e; 640290792Sgshapiro bool delete; 640390792Sgshapiro bool avail; 640490792Sgshapiro{ 640590792Sgshapiro short fidx; 640690792Sgshapiro int idx; 640790792Sgshapiro long s; 640890792Sgshapiro 640990792Sgshapiro if (ShmId == SM_SHM_NO_ID || e == NULL) 641090792Sgshapiro return; 641190792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 641290792Sgshapiro return; 641390792Sgshapiro idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx; 641490792Sgshapiro 641590792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 641690792Sgshapiro if (QSHM_ENTRIES(idx) >= 0) 641790792Sgshapiro { 641890792Sgshapiro if (delete) 641990792Sgshapiro --QSHM_ENTRIES(idx); 642090792Sgshapiro else 642190792Sgshapiro ++QSHM_ENTRIES(idx); 642290792Sgshapiro } 642390792Sgshapiro 642490792Sgshapiro fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx; 642590792Sgshapiro if (fidx < 0) 642690792Sgshapiro return; 642790792Sgshapiro 642890792Sgshapiro /* update available space also? (might be loseqfile) */ 642990792Sgshapiro if (!avail) 643090792Sgshapiro return; 643190792Sgshapiro 643290792Sgshapiro /* convert size to blocks; this causes rounding errors */ 643390792Sgshapiro s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx); 643490792Sgshapiro if (s == 0) 643590792Sgshapiro return; 643690792Sgshapiro 643790792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 643890792Sgshapiro if (delete) 643990792Sgshapiro FILE_SYS_AVAIL(fidx) += s; 644090792Sgshapiro else 644190792Sgshapiro FILE_SYS_AVAIL(fidx) -= s; 644290792Sgshapiro 644390792Sgshapiro} 644494334Sgshapiro 644594334Sgshapiro#if _FFR_SELECT_SHM 644694334Sgshapiro 644794334Sgshapirostatic bool write_key_file __P((char *, long)); 644894334Sgshapirostatic long read_key_file __P((char *, long)); 644994334Sgshapiro 645090792Sgshapiro/* 645194334Sgshapiro** WRITE_KEY_FILE -- record some key into a file. 645294334Sgshapiro** 645394334Sgshapiro** Parameters: 645494334Sgshapiro** keypath -- file name. 645594334Sgshapiro** key -- key to write. 645694334Sgshapiro** 645794334Sgshapiro** Returns: 645894334Sgshapiro** true iff file could be written. 645994334Sgshapiro** 646094334Sgshapiro** Side Effects: 646194334Sgshapiro** writes file. 646294334Sgshapiro*/ 646394334Sgshapiro 646494334Sgshapirostatic bool 646594334Sgshapirowrite_key_file(keypath, key) 646694334Sgshapiro char *keypath; 646794334Sgshapiro long key; 646894334Sgshapiro{ 646994334Sgshapiro bool ok; 647094334Sgshapiro long sff; 647194334Sgshapiro SM_FILE_T *keyf; 647294334Sgshapiro 647394334Sgshapiro ok = false; 647494334Sgshapiro if (keypath == NULL || *keypath == '\0') 647594334Sgshapiro return ok; 647694334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; 647794334Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 647894334Sgshapiro sff |= SFF_OPENASROOT; 647994334Sgshapiro keyf = safefopen(keypath, O_WRONLY|O_TRUNC, 0644, sff); 648094334Sgshapiro if (keyf == NULL) 648194334Sgshapiro { 648294334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", 648394334Sgshapiro keypath, sm_errstring(errno)); 648494334Sgshapiro } 648594334Sgshapiro else 648694334Sgshapiro { 648794334Sgshapiro ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) != 648894334Sgshapiro SM_IO_EOF; 648994334Sgshapiro ok = ok && (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF); 649094334Sgshapiro } 649194334Sgshapiro return ok; 649294334Sgshapiro} 649394334Sgshapiro 649494334Sgshapiro/* 649594334Sgshapiro** READ_KEY_FILE -- read a key from a file. 649694334Sgshapiro** 649794334Sgshapiro** Parameters: 649894334Sgshapiro** keypath -- file name. 649994334Sgshapiro** key -- default key. 650094334Sgshapiro** 650194334Sgshapiro** Returns: 650294334Sgshapiro** key. 650394334Sgshapiro*/ 650494334Sgshapiro 650594334Sgshapirostatic long 650694334Sgshapiroread_key_file(keypath, key) 650794334Sgshapiro char *keypath; 650894334Sgshapiro long key; 650994334Sgshapiro{ 651094334Sgshapiro int r; 651194334Sgshapiro long sff, n; 651294334Sgshapiro SM_FILE_T *keyf; 651394334Sgshapiro 651494334Sgshapiro if (keypath == NULL || *keypath == '\0') 651594334Sgshapiro return key; 651694334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY; 651794334Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 651894334Sgshapiro sff |= SFF_OPENASROOT; 651994334Sgshapiro keyf = safefopen(keypath, O_RDONLY, 0644, sff); 652094334Sgshapiro if (keyf == NULL) 652194334Sgshapiro { 652294334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s", 652394334Sgshapiro keypath, sm_errstring(errno)); 652494334Sgshapiro } 652594334Sgshapiro else 652694334Sgshapiro { 652794334Sgshapiro r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n); 652894334Sgshapiro if (r == 1) 652994334Sgshapiro key = n; 653094334Sgshapiro (void) sm_io_close(keyf, SM_TIME_DEFAULT); 653194334Sgshapiro } 653294334Sgshapiro return key; 653394334Sgshapiro} 653494334Sgshapiro#endif /* _FFR_SELECT_SHM */ 653594334Sgshapiro 653694334Sgshapiro/* 653790792Sgshapiro** INIT_SHM -- initialize shared memory structure 653890792Sgshapiro** 653990792Sgshapiro** Initialize or attach to shared memory segment. 654090792Sgshapiro** Currently it is not a fatal error if this doesn't work. 654190792Sgshapiro** However, it causes us to have a "fallback" storage location 654290792Sgshapiro** for everything that is supposed to be in the shared memory, 654390792Sgshapiro** which makes the code slightly ugly. 654490792Sgshapiro** 654590792Sgshapiro** Parameters: 654690792Sgshapiro** qn -- number of queue directories. 654790792Sgshapiro** owner -- owner of shared memory. 654890792Sgshapiro** hash -- identifies data that is stored in shared memory. 654990792Sgshapiro** 655090792Sgshapiro** Returns: 655190792Sgshapiro** none. 655290792Sgshapiro*/ 655390792Sgshapiro 655490792Sgshapirostatic void init_shm __P((int, bool, unsigned int)); 655590792Sgshapiro 655690792Sgshapirostatic void 655790792Sgshapiroinit_shm(qn, owner, hash) 655890792Sgshapiro int qn; 655990792Sgshapiro bool owner; 656090792Sgshapiro unsigned int hash; 656190792Sgshapiro{ 656290792Sgshapiro int i; 656394334Sgshapiro#if _FFR_SELECT_SHM 656494334Sgshapiro bool keyselect; 656594334Sgshapiro#endif /* _FFR_SELECT_SHM */ 656690792Sgshapiro 656790792Sgshapiro PtrFileSys = &FileSys[0]; 656890792Sgshapiro PNumFileSys = &Numfilesys; 656994334Sgshapiro#if _FFR_SELECT_SHM 657094334Sgshapiro/* if this "key" is specified: select one yourself */ 657194334Sgshapiro# define SEL_SHM_KEY ((key_t) -1) 657294334Sgshapiro# define FIRST_SHM_KEY 25 657394334Sgshapiro#endif /* _FFR_SELECT_SHM */ 657490792Sgshapiro 657590792Sgshapiro /* This allows us to disable shared memory at runtime. */ 657690792Sgshapiro if (ShmKey != 0) 657790792Sgshapiro { 657890792Sgshapiro int count; 657990792Sgshapiro int save_errno; 658090792Sgshapiro size_t shms; 658190792Sgshapiro 658290792Sgshapiro count = 0; 658390792Sgshapiro shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T); 658494334Sgshapiro#if _FFR_SELECT_SHM 658594334Sgshapiro keyselect = ShmKey == SEL_SHM_KEY; 658694334Sgshapiro if (keyselect) 658794334Sgshapiro { 658894334Sgshapiro if (owner) 658994334Sgshapiro ShmKey = FIRST_SHM_KEY; 659094334Sgshapiro else 659194334Sgshapiro { 659294334Sgshapiro ShmKey = read_key_file(ShmKeyFile, ShmKey); 659394334Sgshapiro keyselect = false; 659494334Sgshapiro if (ShmKey == SEL_SHM_KEY) 659594334Sgshapiro goto error; 659694334Sgshapiro } 659794334Sgshapiro } 659894334Sgshapiro#endif /* _FFR_SELECT_SHM */ 659990792Sgshapiro for (;;) 660090792Sgshapiro { 660190792Sgshapiro /* XXX: maybe allow read access for group? */ 660290792Sgshapiro Pshm = sm_shmstart(ShmKey, shms, SHM_R|SHM_W, &ShmId, 660390792Sgshapiro owner); 660490792Sgshapiro save_errno = errno; 660590792Sgshapiro if (Pshm != NULL || save_errno != EEXIST) 660690792Sgshapiro break; 660790792Sgshapiro if (++count >= 3) 660894334Sgshapiro { 660994334Sgshapiro#if _FFR_SELECT_SHM 661094334Sgshapiro if (keyselect) 661194334Sgshapiro { 661294334Sgshapiro ++ShmKey; 661394334Sgshapiro 661494334Sgshapiro /* back where we started? */ 661594334Sgshapiro if (ShmKey == SEL_SHM_KEY) 661694334Sgshapiro break; 661794334Sgshapiro continue; 661894334Sgshapiro } 661994334Sgshapiro#endif /* _FFR_SELECT_SHM */ 662090792Sgshapiro break; 662194334Sgshapiro } 662294334Sgshapiro#if _FFR_SELECT_SHM 662394334Sgshapiro /* only sleep if we are at the first key */ 662494334Sgshapiro if (!keyselect || ShmKey == SEL_SHM_KEY) 662594334Sgshapiro#endif /* _FFR_SELECT_SHM */ 662690792Sgshapiro sleep(count); 662790792Sgshapiro } 662890792Sgshapiro if (Pshm != NULL) 662990792Sgshapiro { 663090792Sgshapiro int *p; 663190792Sgshapiro 663294334Sgshapiro#if _FFR_SELECT_SHM 663394334Sgshapiro if (keyselect) 663494334Sgshapiro (void) write_key_file(ShmKeyFile, (long) ShmKey); 663594334Sgshapiro#endif /* _FFR_SELECT_SHM */ 663690792Sgshapiro p = (int *) Pshm; 663790792Sgshapiro if (owner) 663890792Sgshapiro { 663990792Sgshapiro *p = (int) shms; 664090792Sgshapiro *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid; 664190792Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 664290792Sgshapiro *p = hash; 664390792Sgshapiro } 664490792Sgshapiro else 664590792Sgshapiro { 664690792Sgshapiro if (*p != (int) shms) 664790792Sgshapiro { 664890792Sgshapiro save_errno = EINVAL; 664990792Sgshapiro cleanup_shm(false); 665090792Sgshapiro goto error; 665190792Sgshapiro } 665290792Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 665390792Sgshapiro if (*p != (int) hash) 665490792Sgshapiro { 665590792Sgshapiro save_errno = EINVAL; 665690792Sgshapiro cleanup_shm(false); 665790792Sgshapiro goto error; 665890792Sgshapiro } 665990792Sgshapiro 666090792Sgshapiro /* 666190792Sgshapiro ** XXX how to check the pid? 666290792Sgshapiro ** Read it from the pid-file? That does 666390792Sgshapiro ** not need to exist. 666490792Sgshapiro ** We could disable shm if we can't confirm 666590792Sgshapiro ** that it is the right one. 666690792Sgshapiro */ 666790792Sgshapiro } 666890792Sgshapiro 666990792Sgshapiro PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm); 667090792Sgshapiro PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm); 667190792Sgshapiro QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm); 667290792Sgshapiro PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm); 667390792Sgshapiro *PRSATmpCnt = 0; 667490792Sgshapiro if (owner) 667590792Sgshapiro { 667690792Sgshapiro /* initialize values in shared memory */ 667790792Sgshapiro NumFileSys = 0; 667890792Sgshapiro for (i = 0; i < qn; i++) 667990792Sgshapiro QShm[i].qs_entries = -1; 668090792Sgshapiro } 668190792Sgshapiro return; 668290792Sgshapiro } 668390792Sgshapiro error: 668490792Sgshapiro if (LogLevel > (owner ? 8 : 11)) 668590792Sgshapiro { 668690792Sgshapiro sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID, 668790792Sgshapiro "can't %s shared memory, key=%ld: %s", 668890792Sgshapiro owner ? "initialize" : "attach to", 668990792Sgshapiro (long) ShmKey, sm_errstring(save_errno)); 669090792Sgshapiro } 669190792Sgshapiro } 669290792Sgshapiro} 669390792Sgshapiro#endif /* SM_CONF_SHM */ 669490792Sgshapiro 669590792Sgshapiro/* 669690792Sgshapiro** SETUP_QUEUES -- setup all queue groups 669790792Sgshapiro** 669890792Sgshapiro** Parameters: 669990792Sgshapiro** owner -- owner of shared memory. 670090792Sgshapiro** 670190792Sgshapiro** Returns: 670290792Sgshapiro** none. 670390792Sgshapiro** 670490792Sgshapiro#if SM_CONF_SHM 670590792Sgshapiro** Side Effects: 670690792Sgshapiro** attaches shared memory. 670790792Sgshapiro#endif * SM_CONF_SHM * 670890792Sgshapiro*/ 670990792Sgshapiro 671090792Sgshapirovoid 671190792Sgshapirosetup_queues(owner) 671290792Sgshapiro bool owner; 671390792Sgshapiro{ 671490792Sgshapiro int i, qn, len; 671590792Sgshapiro unsigned int hashval; 671694334Sgshapiro time_t now; 671790792Sgshapiro char basedir[MAXPATHLEN]; 671890792Sgshapiro struct stat st; 671990792Sgshapiro 672090792Sgshapiro /* 672190792Sgshapiro ** Determine basedir for all queue directories. 672290792Sgshapiro ** All queue directories must be (first level) subdirectories 672390792Sgshapiro ** of the basedir. The basedir is the QueueDir 672490792Sgshapiro ** without wildcards, but with trailing / 672590792Sgshapiro */ 672690792Sgshapiro 672790792Sgshapiro hashval = 0; 672890792Sgshapiro errno = 0; 672990792Sgshapiro len = sm_strlcpy(basedir, QueueDir, sizeof basedir); 673090792Sgshapiro if (len >= sizeof basedir) 673190792Sgshapiro { 673290792Sgshapiro syserr("QueueDirectory: path too long: %d, max %d", 673390792Sgshapiro len, (int) sizeof basedir); 673490792Sgshapiro ExitStat = EX_CONFIG; 673590792Sgshapiro return; 673690792Sgshapiro } 673790792Sgshapiro SM_ASSERT(len > 0); 673890792Sgshapiro if (basedir[len - 1] == '*') 673990792Sgshapiro { 674090792Sgshapiro char *cp; 674190792Sgshapiro 674290792Sgshapiro cp = SM_LAST_DIR_DELIM(basedir); 674390792Sgshapiro if (cp == NULL) 674490792Sgshapiro { 674590792Sgshapiro syserr("QueueDirectory: can not wildcard relative path \"%s\"", 674690792Sgshapiro QueueDir); 674790792Sgshapiro if (tTd(41, 2)) 674890792Sgshapiro sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n", 674990792Sgshapiro QueueDir); 675090792Sgshapiro ExitStat = EX_CONFIG; 675190792Sgshapiro return; 675290792Sgshapiro } 675390792Sgshapiro 675490792Sgshapiro /* cut off wildcard pattern */ 675590792Sgshapiro *++cp = '\0'; 675690792Sgshapiro len = cp - basedir; 675790792Sgshapiro } 675890792Sgshapiro else if (!SM_IS_DIR_DELIM(basedir[len - 1])) 675990792Sgshapiro { 676090792Sgshapiro /* append trailing slash since it is a directory */ 676190792Sgshapiro basedir[len] = '/'; 676290792Sgshapiro basedir[++len] = '\0'; 676390792Sgshapiro } 676490792Sgshapiro 676590792Sgshapiro /* len counts up to the last directory delimiter */ 676690792Sgshapiro SM_ASSERT(basedir[len - 1] == '/'); 676790792Sgshapiro 676890792Sgshapiro if (chdir(basedir) < 0) 676990792Sgshapiro { 677090792Sgshapiro int save_errno = errno; 677190792Sgshapiro 677290792Sgshapiro syserr("can not chdir(%s)", basedir); 677390792Sgshapiro if (save_errno == EACCES) 677490792Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 677590792Sgshapiro "Program mode requires special privileges, e.g., root or TrustedUser.\n"); 677690792Sgshapiro if (tTd(41, 2)) 677790792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 677890792Sgshapiro basedir, sm_errstring(errno)); 677990792Sgshapiro ExitStat = EX_CONFIG; 678090792Sgshapiro return; 678190792Sgshapiro } 678290792Sgshapiro#if SM_CONF_SHM 678390792Sgshapiro hashval = hash_q(basedir, hashval); 678490792Sgshapiro#endif /* SM_CONF_SHM */ 678590792Sgshapiro 678694334Sgshapiro /* initialize for queue runs */ 678794334Sgshapiro DoQueueRun = false; 678894334Sgshapiro now = curtime(); 678994334Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 679094334Sgshapiro Queue[i]->qg_nextrun = now; 679190792Sgshapiro 679290792Sgshapiro 679390792Sgshapiro if (UseMSP && OpMode != MD_TEST) 679490792Sgshapiro { 679590792Sgshapiro long sff = SFF_CREAT; 679690792Sgshapiro 679790792Sgshapiro if (stat(".", &st) < 0) 679890792Sgshapiro { 679990792Sgshapiro syserr("can not stat(%s)", basedir); 680090792Sgshapiro if (tTd(41, 2)) 680190792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 680290792Sgshapiro basedir, sm_errstring(errno)); 680390792Sgshapiro ExitStat = EX_CONFIG; 680490792Sgshapiro return; 680590792Sgshapiro } 680690792Sgshapiro if (RunAsUid == 0) 680790792Sgshapiro sff |= SFF_ROOTOK; 680890792Sgshapiro 680990792Sgshapiro /* 681090792Sgshapiro ** Check queue directory permissions. 681190792Sgshapiro ** Can we write to a group writable queue directory? 681290792Sgshapiro */ 681390792Sgshapiro 681490792Sgshapiro if (bitset(S_IWGRP, QueueFileMode) && 681590792Sgshapiro bitset(S_IWGRP, st.st_mode) && 681690792Sgshapiro safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff, 681790792Sgshapiro QueueFileMode, NULL) != 0) 681890792Sgshapiro { 681990792Sgshapiro syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)", 682090792Sgshapiro basedir, (int) RunAsGid, (int) st.st_gid); 682190792Sgshapiro } 682290792Sgshapiro if (bitset(S_IWOTH|S_IXOTH, st.st_mode)) 682390792Sgshapiro { 682490792Sgshapiro#if _FFR_MSP_PARANOIA 682590792Sgshapiro syserr("dangerous permissions=%o on queue directory %s", 682690792Sgshapiro (int) st.st_mode, basedir); 682790792Sgshapiro#else /* _FFR_MSP_PARANOIA */ 682890792Sgshapiro if (LogLevel > 0) 682990792Sgshapiro sm_syslog(LOG_ERR, NOQID, 683090792Sgshapiro "dangerous permissions=%o on queue directory %s", 683190792Sgshapiro (int) st.st_mode, basedir); 683290792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 683390792Sgshapiro } 683490792Sgshapiro#if _FFR_MSP_PARANOIA 683590792Sgshapiro if (NumQueue > 1) 683690792Sgshapiro syserr("can not use multiple queues for MSP"); 683790792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 683890792Sgshapiro } 683990792Sgshapiro 684090792Sgshapiro /* initial number of queue directories */ 684190792Sgshapiro qn = 0; 684290792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 684390792Sgshapiro qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval); 684490792Sgshapiro 684590792Sgshapiro#if SM_CONF_SHM 684690792Sgshapiro init_shm(qn, owner, hashval); 684790792Sgshapiro i = filesys_setup(owner || ShmId == SM_SHM_NO_ID); 684890792Sgshapiro if (i == FSF_NOT_FOUND) 684990792Sgshapiro { 685090792Sgshapiro /* 685190792Sgshapiro ** We didn't get the right filesystem data 685290792Sgshapiro ** This may happen if we don't have the right shared memory. 685390792Sgshapiro ** So let's do this without shared memory. 685490792Sgshapiro */ 685590792Sgshapiro 685690792Sgshapiro SM_ASSERT(!owner); 685790792Sgshapiro cleanup_shm(false); /* release shared memory */ 685890792Sgshapiro i = filesys_setup(false); 685990792Sgshapiro if (i < 0) 686090792Sgshapiro syserr("filesys_setup failed twice, result=%d", i); 686190792Sgshapiro else if (LogLevel > 8) 686290792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 686390792Sgshapiro "shared memory does not contain expected data, ignored"); 686490792Sgshapiro } 686590792Sgshapiro#else /* SM_CONF_SHM */ 686690792Sgshapiro i = filesys_setup(true); 686790792Sgshapiro#endif /* SM_CONF_SHM */ 686890792Sgshapiro if (i < 0) 686990792Sgshapiro ExitStat = EX_CONFIG; 687090792Sgshapiro} 687190792Sgshapiro 687290792Sgshapiro#if SM_CONF_SHM 687390792Sgshapiro/* 687490792Sgshapiro** CLEANUP_SHM -- do some cleanup work for shared memory etc 687590792Sgshapiro** 687690792Sgshapiro** Parameters: 687790792Sgshapiro** owner -- owner of shared memory? 687890792Sgshapiro** 687990792Sgshapiro** Returns: 688090792Sgshapiro** none. 688190792Sgshapiro** 688290792Sgshapiro** Side Effects: 688390792Sgshapiro** detaches shared memory. 688490792Sgshapiro*/ 688590792Sgshapiro 688690792Sgshapirovoid 688790792Sgshapirocleanup_shm(owner) 688890792Sgshapiro bool owner; 688990792Sgshapiro{ 689090792Sgshapiro if (ShmId != SM_SHM_NO_ID) 689190792Sgshapiro { 689290792Sgshapiro if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8) 689398121Sgshapiro sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s", 689490792Sgshapiro sm_errstring(errno)); 689590792Sgshapiro Pshm = NULL; 689690792Sgshapiro ShmId = SM_SHM_NO_ID; 689790792Sgshapiro } 689890792Sgshapiro} 689990792Sgshapiro#endif /* SM_CONF_SHM */ 690090792Sgshapiro 690190792Sgshapiro/* 690290792Sgshapiro** CLEANUP_QUEUES -- do some cleanup work for queues 690390792Sgshapiro** 690490792Sgshapiro** Parameters: 690590792Sgshapiro** none. 690690792Sgshapiro** 690790792Sgshapiro** Returns: 690890792Sgshapiro** none. 690990792Sgshapiro** 691090792Sgshapiro*/ 691190792Sgshapiro 691290792Sgshapirovoid 691390792Sgshapirocleanup_queues() 691490792Sgshapiro{ 691590792Sgshapiro sync_queue_time(); 691690792Sgshapiro} 691790792Sgshapiro/* 691890792Sgshapiro** SET_DEF_QUEUEVAL -- set default values for a queue group. 691990792Sgshapiro** 692090792Sgshapiro** Parameters: 692190792Sgshapiro** qg -- queue group 692290792Sgshapiro** all -- set all values (true for default group)? 692390792Sgshapiro** 692490792Sgshapiro** Returns: 692590792Sgshapiro** none. 692690792Sgshapiro** 692790792Sgshapiro** Side Effects: 692890792Sgshapiro** sets default values for the queue group. 692990792Sgshapiro*/ 693090792Sgshapiro 693190792Sgshapirovoid 693290792Sgshapiroset_def_queueval(qg, all) 693390792Sgshapiro QUEUEGRP *qg; 693490792Sgshapiro bool all; 693590792Sgshapiro{ 693690792Sgshapiro if (bitnset(QD_DEFINED, qg->qg_flags)) 693790792Sgshapiro return; 693890792Sgshapiro if (all) 693990792Sgshapiro qg->qg_qdir = QueueDir; 694094334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 694190792Sgshapiro qg->qg_sortorder = QueueSortOrder; 694294334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 694390792Sgshapiro qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1; 694490792Sgshapiro qg->qg_nice = NiceQueueRun; 694590792Sgshapiro} 694690792Sgshapiro/* 694790792Sgshapiro** MAKEQUEUE -- define a new queue. 694890792Sgshapiro** 694990792Sgshapiro** Parameters: 695090792Sgshapiro** line -- description of queue. This is in labeled fields. 695190792Sgshapiro** The fields are: 695290792Sgshapiro** F -- the flags associated with the queue 695390792Sgshapiro** I -- the interval between running the queue 695490792Sgshapiro** J -- the maximum # of jobs in work list 695590792Sgshapiro** [M -- the maximum # of jobs in a queue run] 695690792Sgshapiro** N -- the niceness at which to run 695790792Sgshapiro** P -- the path to the queue 695890792Sgshapiro** S -- the queue sorting order 695990792Sgshapiro** R -- number of parallel queue runners 696090792Sgshapiro** r -- max recipients per envelope 696190792Sgshapiro** The first word is the canonical name of the queue. 696290792Sgshapiro** qdef -- this is a 'Q' definition from .cf 696390792Sgshapiro** 696490792Sgshapiro** Returns: 696590792Sgshapiro** none. 696690792Sgshapiro** 696790792Sgshapiro** Side Effects: 696890792Sgshapiro** enters the queue into the queue table. 696990792Sgshapiro*/ 697090792Sgshapiro 697190792Sgshapirovoid 697290792Sgshapiromakequeue(line, qdef) 697390792Sgshapiro char *line; 697490792Sgshapiro bool qdef; 697590792Sgshapiro{ 697690792Sgshapiro register char *p; 697790792Sgshapiro register QUEUEGRP *qg; 697890792Sgshapiro register STAB *s; 697990792Sgshapiro int i; 698090792Sgshapiro char fcode; 698190792Sgshapiro 698290792Sgshapiro /* allocate a queue and set up defaults */ 698390792Sgshapiro qg = (QUEUEGRP *) xalloc(sizeof *qg); 698490792Sgshapiro memset((char *) qg, '\0', sizeof *qg); 698590792Sgshapiro 698690792Sgshapiro if (line[0] == '\0') 698790792Sgshapiro { 698890792Sgshapiro syserr("name required for queue"); 698990792Sgshapiro return; 699090792Sgshapiro } 699190792Sgshapiro 699290792Sgshapiro /* collect the queue name */ 699390792Sgshapiro for (p = line; 699490792Sgshapiro *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 699590792Sgshapiro p++) 699690792Sgshapiro continue; 699790792Sgshapiro if (*p != '\0') 699890792Sgshapiro *p++ = '\0'; 699990792Sgshapiro qg->qg_name = newstr(line); 700090792Sgshapiro 700190792Sgshapiro /* set default values, can be overridden below */ 700290792Sgshapiro set_def_queueval(qg, false); 700390792Sgshapiro 700490792Sgshapiro /* now scan through and assign info from the fields */ 700590792Sgshapiro while (*p != '\0') 700690792Sgshapiro { 700790792Sgshapiro auto char *delimptr; 700890792Sgshapiro 700990792Sgshapiro while (*p != '\0' && 701090792Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 701190792Sgshapiro p++; 701290792Sgshapiro 701390792Sgshapiro /* p now points to field code */ 701490792Sgshapiro fcode = *p; 701590792Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 701690792Sgshapiro p++; 701790792Sgshapiro if (*p++ != '=') 701890792Sgshapiro { 701990792Sgshapiro syserr("queue %s: `=' expected", qg->qg_name); 702090792Sgshapiro return; 702190792Sgshapiro } 702290792Sgshapiro while (isascii(*p) && isspace(*p)) 702390792Sgshapiro p++; 702490792Sgshapiro 702590792Sgshapiro /* p now points to the field body */ 702690792Sgshapiro p = munchstring(p, &delimptr, ','); 702790792Sgshapiro 702890792Sgshapiro /* install the field into the queue struct */ 702990792Sgshapiro switch (fcode) 703090792Sgshapiro { 703190792Sgshapiro case 'P': /* pathname */ 703290792Sgshapiro if (*p == '\0') 703390792Sgshapiro syserr("queue %s: empty path name", 703490792Sgshapiro qg->qg_name); 703590792Sgshapiro else 703690792Sgshapiro qg->qg_qdir = newstr(p); 703790792Sgshapiro break; 703890792Sgshapiro 703990792Sgshapiro case 'F': /* flags */ 704090792Sgshapiro for (; *p != '\0'; p++) 704190792Sgshapiro if (!(isascii(*p) && isspace(*p))) 704290792Sgshapiro setbitn(*p, qg->qg_flags); 704390792Sgshapiro break; 704490792Sgshapiro 704590792Sgshapiro /* 704690792Sgshapiro ** Do we need two intervals here: 704790792Sgshapiro ** One for persistent queue runners, 704890792Sgshapiro ** one for "normal" queue runs? 704990792Sgshapiro */ 705090792Sgshapiro 705190792Sgshapiro case 'I': /* interval between running the queue */ 705290792Sgshapiro qg->qg_queueintvl = convtime(p, 'm'); 705390792Sgshapiro break; 705490792Sgshapiro 705590792Sgshapiro case 'N': /* run niceness */ 705690792Sgshapiro qg->qg_nice = atoi(p); 705790792Sgshapiro break; 705890792Sgshapiro 705990792Sgshapiro case 'R': /* maximum # of runners for the group */ 706090792Sgshapiro i = atoi(p); 706190792Sgshapiro 706290792Sgshapiro /* can't have more runners than allowed total */ 706390792Sgshapiro if (MaxQueueChildren > 0 && i > MaxQueueChildren) 706490792Sgshapiro { 706590792Sgshapiro qg->qg_maxqrun = MaxQueueChildren; 706690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 706790792Sgshapiro "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n", 706890792Sgshapiro qg->qg_name, i, 706990792Sgshapiro MaxQueueChildren); 707090792Sgshapiro } 707190792Sgshapiro else 707290792Sgshapiro qg->qg_maxqrun = i; 707390792Sgshapiro break; 707490792Sgshapiro 707590792Sgshapiro case 'J': /* maximum # of jobs in work list */ 707690792Sgshapiro qg->qg_maxlist = atoi(p); 707790792Sgshapiro break; 707890792Sgshapiro 707990792Sgshapiro case 'r': /* max recipients per envelope */ 708090792Sgshapiro qg->qg_maxrcpt = atoi(p); 708190792Sgshapiro break; 708290792Sgshapiro 708394334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 708490792Sgshapiro case 'S': /* queue sorting order */ 708590792Sgshapiro switch (*p) 708690792Sgshapiro { 708790792Sgshapiro case 'h': /* Host first */ 708890792Sgshapiro case 'H': 708990792Sgshapiro qg->qg_sortorder = QSO_BYHOST; 709090792Sgshapiro break; 709190792Sgshapiro 709290792Sgshapiro case 'p': /* Priority order */ 709390792Sgshapiro case 'P': 709490792Sgshapiro qg->qg_sortorder = QSO_BYPRIORITY; 709590792Sgshapiro break; 709690792Sgshapiro 709790792Sgshapiro case 't': /* Submission time */ 709890792Sgshapiro case 'T': 709990792Sgshapiro qg->qg_sortorder = QSO_BYTIME; 710090792Sgshapiro break; 710190792Sgshapiro 710290792Sgshapiro case 'f': /* File name */ 710390792Sgshapiro case 'F': 710490792Sgshapiro qg->qg_sortorder = QSO_BYFILENAME; 710590792Sgshapiro break; 710690792Sgshapiro 710790792Sgshapiro case 'm': /* Modification time */ 710890792Sgshapiro case 'M': 710994334Sgshapiro qg->qg_sortorder = QSO_BYMODTIME; 711090792Sgshapiro break; 711190792Sgshapiro 711294334Sgshapiro case 'r': /* Random */ 711394334Sgshapiro case 'R': 711494334Sgshapiro qg->qg_sortorder = QSO_RANDOM; 711594334Sgshapiro break; 711694334Sgshapiro 711794334Sgshapiro# if _FFR_RHS 711894334Sgshapiro case 's': /* Shuffled host name */ 711994334Sgshapiro case 'S': 712094334Sgshapiro qg->qg_sortorder = QSO_BYSHUFFLE; 712194334Sgshapiro break; 712294334Sgshapiro# endif /* _FFR_RHS */ 712394334Sgshapiro 712490792Sgshapiro default: 712590792Sgshapiro syserr("Invalid queue sort order \"%s\"", p); 712690792Sgshapiro } 712790792Sgshapiro break; 712894334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 712990792Sgshapiro 713090792Sgshapiro default: 713190792Sgshapiro syserr("Q%s: unknown queue equate %c=", 713290792Sgshapiro qg->qg_name, fcode); 713390792Sgshapiro break; 713490792Sgshapiro } 713590792Sgshapiro 713690792Sgshapiro p = delimptr; 713790792Sgshapiro } 713890792Sgshapiro 713990792Sgshapiro#if !HASNICE 714090792Sgshapiro if (qg->qg_nice != NiceQueueRun) 714190792Sgshapiro { 714290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 714390792Sgshapiro "Q%s: Warning: N= set on system that doesn't support nice()\n", 714490792Sgshapiro qg->qg_name); 714590792Sgshapiro } 714690792Sgshapiro#endif /* !HASNICE */ 714790792Sgshapiro 714890792Sgshapiro /* do some rationality checking */ 714990792Sgshapiro if (NumQueue >= MAXQUEUEGROUPS) 715090792Sgshapiro { 715190792Sgshapiro syserr("too many queue groups defined (%d max)", 715290792Sgshapiro MAXQUEUEGROUPS); 715390792Sgshapiro return; 715490792Sgshapiro } 715590792Sgshapiro 715690792Sgshapiro if (qg->qg_qdir == NULL) 715790792Sgshapiro { 715890792Sgshapiro if (QueueDir == NULL || *QueueDir == '\0') 715990792Sgshapiro { 716090792Sgshapiro syserr("QueueDir must be defined before queue groups"); 716190792Sgshapiro return; 716290792Sgshapiro } 716390792Sgshapiro qg->qg_qdir = newstr(QueueDir); 716490792Sgshapiro } 716590792Sgshapiro 716690792Sgshapiro if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags)) 716790792Sgshapiro { 716890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 716990792Sgshapiro "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n", 717090792Sgshapiro qg->qg_name, qg->qg_maxqrun, QD_FORK); 717190792Sgshapiro } 717290792Sgshapiro 717390792Sgshapiro /* enter the queue into the symbol table */ 717490792Sgshapiro if (tTd(37, 8)) 717590792Sgshapiro sm_syslog(LOG_INFO, NOQID, 717690792Sgshapiro "Adding %s to stab, path: %s", qg->qg_name, 717790792Sgshapiro qg->qg_qdir); 717890792Sgshapiro s = stab(qg->qg_name, ST_QUEUE, ST_ENTER); 717990792Sgshapiro if (s->s_quegrp != NULL) 718090792Sgshapiro { 718190792Sgshapiro i = s->s_quegrp->qg_index; 718290792Sgshapiro 718390792Sgshapiro /* XXX what about the pointers inside this struct? */ 718490792Sgshapiro sm_free(s->s_quegrp); /* XXX */ 718590792Sgshapiro } 718690792Sgshapiro else 718790792Sgshapiro i = NumQueue++; 718890792Sgshapiro Queue[i] = s->s_quegrp = qg; 718990792Sgshapiro qg->qg_index = i; 719090792Sgshapiro 719190792Sgshapiro /* set default value for max queue runners */ 719290792Sgshapiro if (qg->qg_maxqrun < 0) 719390792Sgshapiro { 719490792Sgshapiro if (MaxRunnersPerQueue > 0) 719590792Sgshapiro qg->qg_maxqrun = MaxRunnersPerQueue; 719690792Sgshapiro else 719790792Sgshapiro qg->qg_maxqrun = 1; 719890792Sgshapiro } 719990792Sgshapiro if (qdef) 720090792Sgshapiro setbitn(QD_DEFINED, qg->qg_flags); 720190792Sgshapiro} 720290792Sgshapiro#if 0 720390792Sgshapiro/* 720464562Sgshapiro** HASHFQN -- calculate a hash value for a fully qualified host name 720564562Sgshapiro** 720664562Sgshapiro** Arguments: 720764562Sgshapiro** fqn -- an all lower-case host.domain string 720864562Sgshapiro** buckets -- the number of buckets (queue directories) 720964562Sgshapiro** 721064562Sgshapiro** Returns: 721164562Sgshapiro** a bucket number (signed integer) 721264562Sgshapiro** -1 on error 721364562Sgshapiro** 721464562Sgshapiro** Contributed by Exactis.com, Inc. 721564562Sgshapiro*/ 721664562Sgshapiro 721764562Sgshapiroint 721864562Sgshapirohashfqn(fqn, buckets) 721964562Sgshapiro register char *fqn; 722064562Sgshapiro int buckets; 722164562Sgshapiro{ 722264562Sgshapiro register char *p; 722364562Sgshapiro register int h = 0, hash, cnt; 722464562Sgshapiro 722564562Sgshapiro if (fqn == NULL) 722664562Sgshapiro return -1; 722764562Sgshapiro 722864562Sgshapiro /* 722964562Sgshapiro ** A variation on the gdb hash 723064562Sgshapiro ** This is the best as of Feb 19, 1996 --bcx 723164562Sgshapiro */ 723264562Sgshapiro 723364562Sgshapiro p = fqn; 723464562Sgshapiro h = 0x238F13AF * strlen(p); 723564562Sgshapiro for (cnt = 0; *p != 0; ++p, cnt++) 723664562Sgshapiro { 723764562Sgshapiro h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF; 723864562Sgshapiro } 723964562Sgshapiro h = (1103515243 * h + 12345) & 0x7FFFFFFF; 724064562Sgshapiro if (buckets < 2) 724164562Sgshapiro hash = 0; 724264562Sgshapiro else 724364562Sgshapiro hash = (h % buckets); 724464562Sgshapiro 724564562Sgshapiro return hash; 724664562Sgshapiro} 724790792Sgshapiro#endif /* 0 */ 724864562Sgshapiro 724990792Sgshapiro#if _FFR_QUEUEDELAY 725090792Sgshapiro/* 725164562Sgshapiro** QUEUEDELAY -- compute queue delay time 725264562Sgshapiro** 725364562Sgshapiro** Parameters: 725464562Sgshapiro** e -- the envelope to queue up. 725564562Sgshapiro** 725664562Sgshapiro** Returns: 725764562Sgshapiro** queue delay time 725864562Sgshapiro** 725964562Sgshapiro** Side Effects: 726064562Sgshapiro** may change e_queuedelay 726164562Sgshapiro*/ 726264562Sgshapiro 726364562Sgshapirostatic time_t 726464562Sgshapiroqueuedelay(e) 726564562Sgshapiro ENVELOPE *e; 726664562Sgshapiro{ 726764562Sgshapiro time_t qd; 726864562Sgshapiro 726964562Sgshapiro if (e->e_queuealg == QD_EXP) 727064562Sgshapiro { 727164562Sgshapiro if (e->e_queuedelay == 0) 727264562Sgshapiro e->e_queuedelay = QueueInitDelay; 727364562Sgshapiro else 727464562Sgshapiro { 727564562Sgshapiro e->e_queuedelay *= 2; 727664562Sgshapiro if (e->e_queuedelay > QueueMaxDelay) 727764562Sgshapiro e->e_queuedelay = QueueMaxDelay; 727864562Sgshapiro } 727964562Sgshapiro qd = e->e_queuedelay; 728064562Sgshapiro } 728164562Sgshapiro else 728264562Sgshapiro qd = MinQueueAge; 728364562Sgshapiro return qd; 728464562Sgshapiro} 728590792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 728690792Sgshapiro 728790792Sgshapiro/* 728890792Sgshapiro** A structure for sorting Queue according to maxqrun without 728990792Sgshapiro** screwing up Queue itself. 729090792Sgshapiro*/ 729190792Sgshapiro 729290792Sgshapirostruct sortqgrp 729390792Sgshapiro{ 729490792Sgshapiro int sg_idx; /* original index */ 729590792Sgshapiro int sg_maxqrun; /* max queue runners */ 729690792Sgshapiro}; 729790792Sgshapirotypedef struct sortqgrp SORTQGRP_T; 729890792Sgshapirostatic int cmpidx __P((const void *, const void *)); 729990792Sgshapiro 730090792Sgshapirostatic int 730190792Sgshapirocmpidx(a, b) 730290792Sgshapiro const void *a; 730390792Sgshapiro const void *b; 730490792Sgshapiro{ 730590792Sgshapiro /* The sort is highest to lowest, so the comparison is reversed */ 730690792Sgshapiro if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun) 730790792Sgshapiro return 1; 730890792Sgshapiro else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun) 730990792Sgshapiro return -1; 731090792Sgshapiro else 731190792Sgshapiro return 0; 731290792Sgshapiro} 731390792Sgshapiro 731490792Sgshapiro/* 731590792Sgshapiro** MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren 731690792Sgshapiro** 731790792Sgshapiro** Take the now defined queue groups and assign them to work groups. 731890792Sgshapiro** This is done to balance out the number of concurrently active 731990792Sgshapiro** queue runners such that MaxQueueChildren is not exceeded. This may 732090792Sgshapiro** result in more than one queue group per work group. In such a case 732190792Sgshapiro** the number of running queue groups in that work group will have no 732290792Sgshapiro** more than the work group maximum number of runners (a "fair" portion 732390792Sgshapiro** of MaxQueueRunners). All queue groups within a work group will get a 732490792Sgshapiro** chance at running. 732590792Sgshapiro** 732690792Sgshapiro** Parameters: 732790792Sgshapiro** none. 732890792Sgshapiro** 732990792Sgshapiro** Returns: 733090792Sgshapiro** nothing. 733190792Sgshapiro** 733290792Sgshapiro** Side Effects: 733390792Sgshapiro** Sets up WorkGrp structure. 733490792Sgshapiro*/ 733590792Sgshapiro 733690792Sgshapirovoid 733790792Sgshapiromakeworkgroups() 733890792Sgshapiro{ 733990792Sgshapiro int i, j, total_runners = 0; 734090792Sgshapiro int dir; 734190792Sgshapiro SORTQGRP_T si[MAXQUEUEGROUPS + 1]; 734290792Sgshapiro 734390792Sgshapiro if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0) 734490792Sgshapiro { 734590792Sgshapiro /* 734690792Sgshapiro ** There is only the "mqueue" queue group (a default) 734790792Sgshapiro ** containing all of the queues. We want to provide to 734890792Sgshapiro ** this queue group the maximum allowable queue runners. 734990792Sgshapiro ** To match older behavior (8.10/8.11) we'll try for 735090792Sgshapiro ** 1 runner per queue capping it at MaxQueueChildren. 735190792Sgshapiro ** So if there are N queues, then there will be N runners 735290792Sgshapiro ** for the "mqueue" queue group (where N is kept less than 735390792Sgshapiro ** MaxQueueChildren). 735490792Sgshapiro */ 735590792Sgshapiro 735690792Sgshapiro NumWorkGroups = 1; 735790792Sgshapiro WorkGrp[0].wg_numqgrp = 1; 735890792Sgshapiro WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *)); 735990792Sgshapiro WorkGrp[0].wg_qgs[0] = Queue[0]; 736090792Sgshapiro if (MaxQueueChildren > 0 && 736190792Sgshapiro Queue[0]->qg_numqueues > MaxQueueChildren) 736290792Sgshapiro WorkGrp[0].wg_runners = MaxQueueChildren; 736390792Sgshapiro else 736490792Sgshapiro WorkGrp[0].wg_runners = Queue[0]->qg_numqueues; 736590792Sgshapiro 736690792Sgshapiro Queue[0]->qg_wgrp = 0; 736790792Sgshapiro 736890792Sgshapiro /* can't have more runners than allowed total */ 736990792Sgshapiro if (MaxQueueChildren > 0 && 737090792Sgshapiro Queue[0]->qg_maxqrun > MaxQueueChildren) 737190792Sgshapiro Queue[0]->qg_maxqrun = MaxQueueChildren; 737290792Sgshapiro WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun; 737390792Sgshapiro WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl; 737490792Sgshapiro return; 737590792Sgshapiro } 737690792Sgshapiro 737790792Sgshapiro for (i = 0; i < NumQueue; i++) 737890792Sgshapiro { 737990792Sgshapiro si[i].sg_maxqrun = Queue[i]->qg_maxqrun; 738090792Sgshapiro si[i].sg_idx = i; 738190792Sgshapiro } 738290792Sgshapiro qsort(si, NumQueue, sizeof(si[0]), cmpidx); 738390792Sgshapiro 738490792Sgshapiro NumWorkGroups = 0; 738590792Sgshapiro for (i = 0; i < NumQueue; i++) 738690792Sgshapiro { 738790792Sgshapiro total_runners += si[i].sg_maxqrun; 738890792Sgshapiro if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren) 738990792Sgshapiro NumWorkGroups++; 739090792Sgshapiro else 739190792Sgshapiro break; 739290792Sgshapiro } 739390792Sgshapiro 739490792Sgshapiro if (NumWorkGroups < 1) 739590792Sgshapiro NumWorkGroups = 1; /* gotta have one at least */ 739690792Sgshapiro else if (NumWorkGroups > MAXWORKGROUPS) 739790792Sgshapiro NumWorkGroups = MAXWORKGROUPS; /* the limit */ 739890792Sgshapiro 739990792Sgshapiro /* 740090792Sgshapiro ** We now know the number of work groups to pack the queue groups 740190792Sgshapiro ** into. The queue groups in 'Queue' are sorted from highest 740290792Sgshapiro ** to lowest for the number of runners per queue group. 740390792Sgshapiro ** We put the queue groups with the largest number of runners 740490792Sgshapiro ** into work groups first. Then the smaller ones are fitted in 740590792Sgshapiro ** where it looks best. 740690792Sgshapiro */ 740790792Sgshapiro 740890792Sgshapiro j = 0; 740990792Sgshapiro dir = 1; 741090792Sgshapiro for (i = 0; i < NumQueue; i++) 741190792Sgshapiro { 741290792Sgshapiro /* a to-and-fro packing scheme, continue from last position */ 741390792Sgshapiro if (j >= NumWorkGroups) 741490792Sgshapiro { 741590792Sgshapiro dir = -1; 741690792Sgshapiro j = NumWorkGroups - 1; 741790792Sgshapiro } 741890792Sgshapiro else if (j < 0) 741990792Sgshapiro { 742090792Sgshapiro j = 0; 742190792Sgshapiro dir = 1; 742290792Sgshapiro } 742390792Sgshapiro 742490792Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 742594334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) * 742694334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 742794334Sgshapiro else 742894334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs, 742994334Sgshapiro sizeof(QUEUEGRP *) * 743094334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 743194334Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 743290792Sgshapiro { 743394334Sgshapiro syserr("!cannot allocate memory for work queues, need %d bytes", 743490792Sgshapiro (int) (sizeof(QUEUEGRP *) * 743590792Sgshapiro (WorkGrp[j].wg_numqgrp + 1))); 743690792Sgshapiro } 743790792Sgshapiro 743890792Sgshapiro WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[si[i].sg_idx]; 743990792Sgshapiro WorkGrp[j].wg_numqgrp++; 744090792Sgshapiro WorkGrp[j].wg_runners += Queue[i]->qg_maxqrun; 744190792Sgshapiro Queue[si[i].sg_idx]->qg_wgrp = j; 744290792Sgshapiro 744390792Sgshapiro if (WorkGrp[j].wg_maxact == 0) 744490792Sgshapiro { 744590792Sgshapiro /* can't have more runners than allowed total */ 744690792Sgshapiro if (MaxQueueChildren > 0 && 744790792Sgshapiro Queue[i]->qg_maxqrun > MaxQueueChildren) 744890792Sgshapiro Queue[i]->qg_maxqrun = MaxQueueChildren; 744990792Sgshapiro WorkGrp[j].wg_maxact = Queue[i]->qg_maxqrun; 745090792Sgshapiro } 745190792Sgshapiro 745290792Sgshapiro /* 745390792Sgshapiro ** XXX: must wg_lowqintvl be the GCD? 745490792Sgshapiro ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for 745590792Sgshapiro ** qg2 occur? 745690792Sgshapiro */ 745790792Sgshapiro 745890792Sgshapiro /* keep track of the lowest interval for a persistent runner */ 745990792Sgshapiro if (Queue[si[i].sg_idx]->qg_queueintvl > 0 && 746090792Sgshapiro WorkGrp[j].wg_lowqintvl < Queue[si[i].sg_idx]->qg_queueintvl) 746190792Sgshapiro WorkGrp[j].wg_lowqintvl = Queue[si[i].sg_idx]->qg_queueintvl; 746290792Sgshapiro j += dir; 746390792Sgshapiro } 746490792Sgshapiro if (tTd(41, 9)) 746590792Sgshapiro { 746690792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 746790792Sgshapiro { 746890792Sgshapiro sm_dprintf("Workgroup[%d]=", i); 746990792Sgshapiro for (j = 0; j < WorkGrp[i].wg_numqgrp; j++) 747090792Sgshapiro { 747190792Sgshapiro sm_dprintf("%s, ", 747290792Sgshapiro WorkGrp[i].wg_qgs[j]->qg_name); 747390792Sgshapiro } 747490792Sgshapiro sm_dprintf("\n"); 747590792Sgshapiro } 747690792Sgshapiro } 747790792Sgshapiro} 747890792Sgshapiro 747990792Sgshapiro/* 748090792Sgshapiro** DUP_DF -- duplicate envelope data file 748190792Sgshapiro** 748290792Sgshapiro** Copy the data file from the 'old' envelope to the 'new' envelope 748390792Sgshapiro** in the most efficient way possible. 748490792Sgshapiro** 748590792Sgshapiro** Create a hard link from the 'old' data file to the 'new' data file. 748690792Sgshapiro** If the old and new queue directories are on different file systems, 748790792Sgshapiro** then the new data file link is created in the old queue directory, 748890792Sgshapiro** and the new queue file will contain a 'd' record pointing to the 748990792Sgshapiro** directory containing the new data file. 749090792Sgshapiro** 749190792Sgshapiro** Parameters: 749290792Sgshapiro** old -- old envelope. 749390792Sgshapiro** new -- new envelope. 749490792Sgshapiro** 749590792Sgshapiro** Results: 749690792Sgshapiro** Returns true on success, false on failure. 749790792Sgshapiro** 749890792Sgshapiro** Side Effects: 749990792Sgshapiro** On success, the new data file is created. 750090792Sgshapiro** On fatal failure, EF_FATALERRS is set in old->e_flags. 750190792Sgshapiro*/ 750290792Sgshapiro 750390792Sgshapirostatic bool dup_df __P((ENVELOPE *, ENVELOPE *)); 750490792Sgshapiro 750590792Sgshapirostatic bool 750690792Sgshapirodup_df(old, new) 750790792Sgshapiro ENVELOPE *old; 750890792Sgshapiro ENVELOPE *new; 750990792Sgshapiro{ 751090792Sgshapiro int ofs, nfs, r; 751190792Sgshapiro char opath[MAXPATHLEN]; 751290792Sgshapiro char npath[MAXPATHLEN]; 751390792Sgshapiro 751494334Sgshapiro if (!bitset(EF_HAS_DF, old->e_flags)) 751594334Sgshapiro { 751694334Sgshapiro /* 751794334Sgshapiro ** this can happen if: SuperSafe != True 751894334Sgshapiro ** and a bounce mail is sent that is split. 751994334Sgshapiro */ 752094334Sgshapiro 752194334Sgshapiro queueup(old, false, true); 752294334Sgshapiro } 752390792Sgshapiro SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir)); 752490792Sgshapiro SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir)); 752590792Sgshapiro 752690792Sgshapiro (void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof opath); 752790792Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath); 752890792Sgshapiro 752990792Sgshapiro if (old->e_dfp != NULL) 753090792Sgshapiro { 753190792Sgshapiro r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL); 753290792Sgshapiro if (r < 0 && errno != EINVAL) 753390792Sgshapiro { 753490792Sgshapiro syserr("@can't commit %s", opath); 753590792Sgshapiro old->e_flags |= EF_FATALERRS; 753690792Sgshapiro return false; 753790792Sgshapiro } 753890792Sgshapiro } 753990792Sgshapiro 754090792Sgshapiro /* 754190792Sgshapiro ** Attempt to create a hard link, if we think both old and new 754290792Sgshapiro ** are on the same file system, otherwise copy the file. 754390792Sgshapiro ** 754490792Sgshapiro ** Don't waste time attempting a hard link unless old and new 754590792Sgshapiro ** are on the same file system. 754690792Sgshapiro */ 754790792Sgshapiro 754890792Sgshapiro ofs = Queue[old->e_qgrp]->qg_qpaths[old->e_qdir].qp_fsysidx; 754990792Sgshapiro nfs = Queue[new->e_qgrp]->qg_qpaths[new->e_qdir].qp_fsysidx; 755090792Sgshapiro if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs)) 755190792Sgshapiro { 755290792Sgshapiro if (link(opath, npath) == 0) 755390792Sgshapiro { 755490792Sgshapiro new->e_flags |= EF_HAS_DF; 755590792Sgshapiro SYNC_DIR(npath, true); 755690792Sgshapiro return true; 755790792Sgshapiro } 755890792Sgshapiro goto error; 755990792Sgshapiro } 756090792Sgshapiro 756190792Sgshapiro /* 756290792Sgshapiro ** Can't link across queue directories, so try to create a hard 756390792Sgshapiro ** link in the same queue directory as the old df file. 756490792Sgshapiro ** The qf file will refer to the new df file using a 'd' record. 756590792Sgshapiro */ 756690792Sgshapiro 756790792Sgshapiro new->e_dfqgrp = old->e_dfqgrp; 756890792Sgshapiro new->e_dfqdir = old->e_dfqdir; 756990792Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath); 757090792Sgshapiro if (link(opath, npath) == 0) 757190792Sgshapiro { 757290792Sgshapiro new->e_flags |= EF_HAS_DF; 757390792Sgshapiro SYNC_DIR(npath, true); 757490792Sgshapiro return true; 757590792Sgshapiro } 757690792Sgshapiro 757790792Sgshapiro error: 757890792Sgshapiro if (LogLevel > 0) 757990792Sgshapiro sm_syslog(LOG_ERR, old->e_id, 758090792Sgshapiro "dup_df: can't link %s to %s, error=%s, envelope splitting failed", 758190792Sgshapiro opath, npath, sm_errstring(errno)); 758290792Sgshapiro return false; 758390792Sgshapiro} 758490792Sgshapiro 758590792Sgshapiro/* 758690792Sgshapiro** SPLIT_ENV -- Allocate a new envelope based on a given envelope. 758790792Sgshapiro** 758890792Sgshapiro** Parameters: 758990792Sgshapiro** e -- envelope. 759090792Sgshapiro** sendqueue -- sendqueue for new envelope. 759190792Sgshapiro** qgrp -- index of queue group. 759290792Sgshapiro** qdir -- queue directory. 759390792Sgshapiro** 759490792Sgshapiro** Results: 759590792Sgshapiro** new envelope. 759690792Sgshapiro** 759790792Sgshapiro*/ 759890792Sgshapiro 759990792Sgshapirostatic ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int)); 760090792Sgshapiro 760190792Sgshapirostatic ENVELOPE * 760290792Sgshapirosplit_env(e, sendqueue, qgrp, qdir) 760390792Sgshapiro ENVELOPE *e; 760490792Sgshapiro ADDRESS *sendqueue; 760590792Sgshapiro int qgrp; 760690792Sgshapiro int qdir; 760790792Sgshapiro{ 760890792Sgshapiro ENVELOPE *ee; 760990792Sgshapiro 761090792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof *ee); 761190792Sgshapiro STRUCTCOPY(*e, *ee); 761290792Sgshapiro ee->e_message = NULL; /* XXX use original message? */ 761390792Sgshapiro ee->e_id = NULL; 761490792Sgshapiro assign_queueid(ee); 761590792Sgshapiro ee->e_sendqueue = sendqueue; 761690792Sgshapiro ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS 761790792Sgshapiro |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF); 761890792Sgshapiro ee->e_flags |= EF_NORECEIPT; /* XXX really? */ 761990792Sgshapiro ee->e_from.q_state = QS_SENDER; 762090792Sgshapiro ee->e_dfp = NULL; 762190792Sgshapiro ee->e_lockfp = NULL; 762290792Sgshapiro if (e->e_xfp != NULL) 762390792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 762494334Sgshapiro 762594334Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 762694334Sgshapiro if (ee->e_xfp == NULL) 762794334Sgshapiro openxscript(ee); 762894334Sgshapiro 762990792Sgshapiro ee->e_qgrp = ee->e_dfqgrp = qgrp; 763090792Sgshapiro ee->e_qdir = ee->e_dfqdir = qdir; 763190792Sgshapiro ee->e_errormode = EM_MAIL; 763290792Sgshapiro ee->e_statmsg = NULL; 763390792Sgshapiro#if _FFR_QUARANTINE 763490792Sgshapiro if (e->e_quarmsg != NULL) 763590792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 763690792Sgshapiro e->e_quarmsg); 763790792Sgshapiro#endif /* _FFR_QUARANTINE */ 763890792Sgshapiro 763990792Sgshapiro /* 764090792Sgshapiro ** XXX Not sure if this copying is necessary. 764194334Sgshapiro ** sendall() does this copying, but I (dm) don't know if that is 764290792Sgshapiro ** because of the storage management discipline we were using 764390792Sgshapiro ** before rpools were introduced, or if it is because these lists 764490792Sgshapiro ** can be modified later. 764590792Sgshapiro */ 764690792Sgshapiro 764790792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 764890792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool); 764990792Sgshapiro 765090792Sgshapiro return ee; 765190792Sgshapiro} 765290792Sgshapiro 765390792Sgshapiro/* return values from split functions, check also below! */ 765490792Sgshapiro#define SM_SPLIT_FAIL (0) 765590792Sgshapiro#define SM_SPLIT_NONE (1) 765690792Sgshapiro#define SM_SPLIT_NEW(n) (1 + (n)) 765790792Sgshapiro 765890792Sgshapiro/* 765990792Sgshapiro** SPLIT_ACROSS_QUEUE_GROUPS 766090792Sgshapiro** 766190792Sgshapiro** This function splits an envelope across multiple queue groups 766290792Sgshapiro** based on the queue group of each recipient. 766390792Sgshapiro** 766490792Sgshapiro** Parameters: 766590792Sgshapiro** e -- envelope. 766690792Sgshapiro** 766790792Sgshapiro** Results: 766890792Sgshapiro** SM_SPLIT_FAIL on failure 766990792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 767090792Sgshapiro** or 1 + the number of additional envelopes created. 767190792Sgshapiro** 767290792Sgshapiro** Side Effects: 767390792Sgshapiro** On success, e->e_sibling points to a list of zero or more 767490792Sgshapiro** additional envelopes, and the associated data files exist 767590792Sgshapiro** on disk. But the queue files are not created. 767690792Sgshapiro** 767790792Sgshapiro** On failure, e->e_sibling is not changed. 767890792Sgshapiro** The order of recipients in e->e_sendqueue is permuted. 767990792Sgshapiro** Abandoned data files for additional envelopes that failed 768090792Sgshapiro** to be created may exist on disk. 768190792Sgshapiro*/ 768290792Sgshapiro 768390792Sgshapirostatic int q_qgrp_compare __P((const void *, const void *)); 768490792Sgshapirostatic int e_filesys_compare __P((const void *, const void *)); 768590792Sgshapiro 768690792Sgshapirostatic int 768790792Sgshapiroq_qgrp_compare(p1, p2) 768890792Sgshapiro const void *p1; 768990792Sgshapiro const void *p2; 769090792Sgshapiro{ 769190792Sgshapiro ADDRESS **pq1 = (ADDRESS **) p1; 769290792Sgshapiro ADDRESS **pq2 = (ADDRESS **) p2; 769390792Sgshapiro 769490792Sgshapiro return (*pq1)->q_qgrp - (*pq2)->q_qgrp; 769590792Sgshapiro} 769690792Sgshapiro 769790792Sgshapirostatic int 769890792Sgshapiroe_filesys_compare(p1, p2) 769990792Sgshapiro const void *p1; 770090792Sgshapiro const void *p2; 770190792Sgshapiro{ 770290792Sgshapiro ENVELOPE **pe1 = (ENVELOPE **) p1; 770390792Sgshapiro ENVELOPE **pe2 = (ENVELOPE **) p2; 770490792Sgshapiro int fs1, fs2; 770590792Sgshapiro 770690792Sgshapiro fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx; 770790792Sgshapiro fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx; 770890792Sgshapiro if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2)) 770990792Sgshapiro return -1; 771090792Sgshapiro if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2)) 771190792Sgshapiro return 1; 771290792Sgshapiro return 0; 771390792Sgshapiro} 771490792Sgshapiro 771590792Sgshapirostatic int 771690792Sgshapirosplit_across_queue_groups(e) 771790792Sgshapiro ENVELOPE *e; 771890792Sgshapiro{ 771990792Sgshapiro int naddrs, nsplits, i; 7720102528Sgshapiro bool changed; 772190792Sgshapiro char **pvp; 772290792Sgshapiro ADDRESS *q, **addrs; 772390792Sgshapiro ENVELOPE *ee, *es; 772490792Sgshapiro ENVELOPE *splits[MAXQUEUEGROUPS]; 772590792Sgshapiro char pvpbuf[PSBUFSIZE]; 772690792Sgshapiro 772790792Sgshapiro SM_REQUIRE(ISVALIDQGRP(e->e_qgrp)); 772890792Sgshapiro 772990792Sgshapiro /* Count addresses and assign queue groups. */ 773090792Sgshapiro naddrs = 0; 7731102528Sgshapiro changed = false; 773290792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 773390792Sgshapiro { 773490792Sgshapiro if (QS_IS_DEAD(q->q_state)) 773590792Sgshapiro continue; 773690792Sgshapiro ++naddrs; 773790792Sgshapiro 773890792Sgshapiro /* bad addresses and those already sent stay put */ 773990792Sgshapiro if (QS_IS_BADADDR(q->q_state) || 774090792Sgshapiro QS_IS_SENT(q->q_state)) 774190792Sgshapiro q->q_qgrp = e->e_qgrp; 774290792Sgshapiro else if (!ISVALIDQGRP(q->q_qgrp)) 774390792Sgshapiro { 774490792Sgshapiro /* call ruleset which should return a queue group */ 774590792Sgshapiro i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp, 774690792Sgshapiro pvpbuf, sizeof(pvpbuf)); 774790792Sgshapiro if (i == EX_OK && 774890792Sgshapiro pvp != NULL && pvp[0] != NULL && 774990792Sgshapiro (pvp[0][0] & 0377) == CANONNET && 775090792Sgshapiro pvp[1] != NULL && pvp[1][0] != '\0') 775190792Sgshapiro { 775290792Sgshapiro i = name2qid(pvp[1]); 775390792Sgshapiro if (ISVALIDQGRP(i)) 775490792Sgshapiro { 775590792Sgshapiro q->q_qgrp = i; 7756102528Sgshapiro changed = true; 775790792Sgshapiro if (tTd(20, 4)) 775890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 775990792Sgshapiro "queue group name %s -> %d", 776090792Sgshapiro pvp[1], i); 776190792Sgshapiro continue; 776290792Sgshapiro } 776390792Sgshapiro else if (LogLevel > 10) 776490792Sgshapiro sm_syslog(LOG_INFO, NOQID, 776590792Sgshapiro "can't find queue group name %s, selection ignored", 776690792Sgshapiro pvp[1]); 776790792Sgshapiro } 776890792Sgshapiro if (q->q_mailer != NULL && 776990792Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 7770102528Sgshapiro { 7771102528Sgshapiro changed = true; 777290792Sgshapiro q->q_qgrp = q->q_mailer->m_qgrp; 7773102528Sgshapiro } 777494334Sgshapiro else if (ISVALIDQGRP(e->e_qgrp)) 777594334Sgshapiro q->q_qgrp = e->e_qgrp; 777690792Sgshapiro else 777790792Sgshapiro q->q_qgrp = 0; 777890792Sgshapiro } 777990792Sgshapiro } 778090792Sgshapiro 778190792Sgshapiro /* only one address? nothing to split. */ 7782102528Sgshapiro if (naddrs <= 1 && !changed) 778390792Sgshapiro return SM_SPLIT_NONE; 778490792Sgshapiro 778590792Sgshapiro /* sort the addresses by queue group */ 778690792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *)); 778790792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 778890792Sgshapiro { 778990792Sgshapiro if (QS_IS_DEAD(q->q_state)) 779090792Sgshapiro continue; 779190792Sgshapiro addrs[i++] = q; 779290792Sgshapiro } 779390792Sgshapiro qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare); 779490792Sgshapiro 779590792Sgshapiro /* split into multiple envelopes, by queue group */ 779690792Sgshapiro nsplits = 0; 779790792Sgshapiro es = NULL; 779890792Sgshapiro e->e_sendqueue = NULL; 779990792Sgshapiro for (i = 0; i < naddrs; ++i) 780090792Sgshapiro { 780190792Sgshapiro if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp) 780290792Sgshapiro addrs[i]->q_next = NULL; 780390792Sgshapiro else 780490792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 780590792Sgshapiro 780690792Sgshapiro /* same queue group as original envelope? */ 780790792Sgshapiro if (addrs[i]->q_qgrp == e->e_qgrp) 780890792Sgshapiro { 780990792Sgshapiro if (e->e_sendqueue == NULL) 781090792Sgshapiro e->e_sendqueue = addrs[i]; 781190792Sgshapiro continue; 781290792Sgshapiro } 781390792Sgshapiro 781490792Sgshapiro /* different queue group than original envelope */ 781590792Sgshapiro if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp) 781690792Sgshapiro { 781790792Sgshapiro ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR); 781890792Sgshapiro es = ee; 781990792Sgshapiro splits[nsplits++] = ee; 782090792Sgshapiro } 782190792Sgshapiro } 782290792Sgshapiro 782390792Sgshapiro /* no splits? return right now. */ 782490792Sgshapiro if (nsplits <= 0) 782590792Sgshapiro return SM_SPLIT_NONE; 782690792Sgshapiro 782790792Sgshapiro /* assign a queue directory to each additional envelope */ 782890792Sgshapiro for (i = 0; i < nsplits; ++i) 782990792Sgshapiro { 783090792Sgshapiro es = splits[i]; 783190792Sgshapiro#if 0 783290792Sgshapiro es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es); 783390792Sgshapiro#endif /* 0 */ 783490792Sgshapiro if (!setnewqueue(es)) 783590792Sgshapiro goto failure; 783690792Sgshapiro } 783790792Sgshapiro 783890792Sgshapiro /* sort the additional envelopes by queue file system */ 783990792Sgshapiro qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare); 784090792Sgshapiro 784190792Sgshapiro /* create data files for each additional envelope */ 784290792Sgshapiro if (!dup_df(e, splits[0])) 784390792Sgshapiro { 784490792Sgshapiro i = 0; 784590792Sgshapiro goto failure; 784690792Sgshapiro } 784790792Sgshapiro for (i = 1; i < nsplits; ++i) 784890792Sgshapiro { 784990792Sgshapiro /* copy or link to the previous data file */ 785090792Sgshapiro if (!dup_df(splits[i - 1], splits[i])) 785190792Sgshapiro goto failure; 785290792Sgshapiro } 785390792Sgshapiro 785490792Sgshapiro /* success: prepend the new envelopes to the e->e_sibling list */ 785590792Sgshapiro for (i = 0; i < nsplits; ++i) 785690792Sgshapiro { 785790792Sgshapiro es = splits[i]; 785890792Sgshapiro es->e_sibling = e->e_sibling; 785990792Sgshapiro e->e_sibling = es; 786090792Sgshapiro } 786190792Sgshapiro return SM_SPLIT_NEW(nsplits); 786290792Sgshapiro 786390792Sgshapiro /* failure: clean up */ 786490792Sgshapiro failure: 786590792Sgshapiro if (i > 0) 786690792Sgshapiro { 786790792Sgshapiro int j; 786890792Sgshapiro 786990792Sgshapiro for (j = 0; j < i; j++) 787090792Sgshapiro (void) unlink(queuename(splits[j], DATAFL_LETTER)); 787190792Sgshapiro } 787290792Sgshapiro e->e_sendqueue = addrs[0]; 787390792Sgshapiro for (i = 0; i < naddrs - 1; ++i) 787490792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 787590792Sgshapiro addrs[naddrs - 1]->q_next = NULL; 787690792Sgshapiro return SM_SPLIT_FAIL; 787790792Sgshapiro} 787890792Sgshapiro 787990792Sgshapiro/* 788090792Sgshapiro** SPLIT_WITHIN_QUEUE 788190792Sgshapiro** 788290792Sgshapiro** Split an envelope with multiple recipients into several 788390792Sgshapiro** envelopes within the same queue directory, if the number of 788490792Sgshapiro** recipients exceeds the limit for the queue group. 788590792Sgshapiro** 788690792Sgshapiro** Parameters: 788790792Sgshapiro** e -- envelope. 788890792Sgshapiro** 788990792Sgshapiro** Results: 789090792Sgshapiro** SM_SPLIT_FAIL on failure 789190792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 789290792Sgshapiro** or 1 + the number of additional envelopes created. 789390792Sgshapiro*/ 789490792Sgshapiro 789590792Sgshapiro#define SPLIT_LOG_LEVEL 8 789690792Sgshapiro 789790792Sgshapirostatic int split_within_queue __P((ENVELOPE *)); 789890792Sgshapiro 789990792Sgshapirostatic int 790090792Sgshapirosplit_within_queue(e) 790190792Sgshapiro ENVELOPE *e; 790290792Sgshapiro{ 790390792Sgshapiro int maxrcpt, nrcpt, ndead, nsplit, i; 790490792Sgshapiro int j, l; 790590792Sgshapiro char *lsplits; 790690792Sgshapiro ADDRESS *q, **addrs; 790790792Sgshapiro ENVELOPE *ee, *firstsibling; 790890792Sgshapiro 790990792Sgshapiro if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags)) 791090792Sgshapiro return SM_SPLIT_NONE; 791190792Sgshapiro 791290792Sgshapiro /* don't bother if there is no recipient limit */ 791390792Sgshapiro maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt; 791490792Sgshapiro if (maxrcpt <= 0) 791590792Sgshapiro return SM_SPLIT_NONE; 791690792Sgshapiro 791790792Sgshapiro /* count recipients */ 791890792Sgshapiro nrcpt = 0; 791990792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 792090792Sgshapiro { 792190792Sgshapiro if (QS_IS_DEAD(q->q_state)) 792290792Sgshapiro continue; 792390792Sgshapiro ++nrcpt; 792490792Sgshapiro } 792590792Sgshapiro if (nrcpt <= maxrcpt) 792690792Sgshapiro return SM_SPLIT_NONE; 792790792Sgshapiro 792890792Sgshapiro /* 792990792Sgshapiro ** Preserve the recipient list 793090792Sgshapiro ** so that we can restore it in case of error. 793190792Sgshapiro ** (But we discard dead addresses.) 793290792Sgshapiro */ 793390792Sgshapiro 793490792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *)); 793590792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 793690792Sgshapiro { 793790792Sgshapiro if (QS_IS_DEAD(q->q_state)) 793890792Sgshapiro continue; 793990792Sgshapiro addrs[i++] = q; 794090792Sgshapiro } 794190792Sgshapiro 794290792Sgshapiro /* 794390792Sgshapiro ** Partition the recipient list so that bad and sent addresses 794490792Sgshapiro ** come first. These will go with the original envelope, and 794590792Sgshapiro ** do not count towards the maxrcpt limit. 794690792Sgshapiro ** addrs[] does not contain QS_IS_DEAD() addresses. 794790792Sgshapiro */ 794890792Sgshapiro 794990792Sgshapiro ndead = 0; 795090792Sgshapiro for (i = 0; i < nrcpt; ++i) 795190792Sgshapiro { 795290792Sgshapiro if (QS_IS_BADADDR(addrs[i]->q_state) || 795390792Sgshapiro QS_IS_SENT(addrs[i]->q_state) || 795490792Sgshapiro QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */ 795590792Sgshapiro { 795690792Sgshapiro if (i > ndead) 795790792Sgshapiro { 795890792Sgshapiro ADDRESS *tmp = addrs[i]; 795990792Sgshapiro 796090792Sgshapiro addrs[i] = addrs[ndead]; 796190792Sgshapiro addrs[ndead] = tmp; 796290792Sgshapiro } 796390792Sgshapiro ++ndead; 796490792Sgshapiro } 796590792Sgshapiro } 796690792Sgshapiro 796790792Sgshapiro /* Check if no splitting required. */ 796890792Sgshapiro if (nrcpt - ndead <= maxrcpt) 796990792Sgshapiro return SM_SPLIT_NONE; 797090792Sgshapiro 797190792Sgshapiro /* fix links */ 797290792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 797390792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 797490792Sgshapiro addrs[nrcpt - 1]->q_next = NULL; 797590792Sgshapiro e->e_sendqueue = addrs[0]; 797690792Sgshapiro 797790792Sgshapiro /* prepare buffer for logging */ 797890792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL) 797990792Sgshapiro { 798090792Sgshapiro l = MAXLINE; 798190792Sgshapiro lsplits = sm_malloc(l); 798290792Sgshapiro if (lsplits != NULL) 798390792Sgshapiro *lsplits = '\0'; 798490792Sgshapiro j = 0; 798590792Sgshapiro } 798690792Sgshapiro else 798790792Sgshapiro { 798890792Sgshapiro /* get rid of stupid compiler warnings */ 798990792Sgshapiro lsplits = NULL; 799090792Sgshapiro j = l = 0; 799190792Sgshapiro } 799290792Sgshapiro 799390792Sgshapiro /* split the envelope */ 799490792Sgshapiro firstsibling = e->e_sibling; 799590792Sgshapiro i = maxrcpt + ndead; 799690792Sgshapiro nsplit = 0; 799790792Sgshapiro for (;;) 799890792Sgshapiro { 799990792Sgshapiro addrs[i - 1]->q_next = NULL; 800090792Sgshapiro ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir); 800190792Sgshapiro if (!dup_df(e, ee)) 800290792Sgshapiro { 800390792Sgshapiro 800490792Sgshapiro ee = firstsibling; 800590792Sgshapiro while (ee != NULL) 800690792Sgshapiro { 800790792Sgshapiro (void) unlink(queuename(ee, DATAFL_LETTER)); 800890792Sgshapiro ee = ee->e_sibling; 800990792Sgshapiro } 801090792Sgshapiro 801190792Sgshapiro /* Error. Restore e's sibling & recipient lists. */ 801290792Sgshapiro e->e_sibling = firstsibling; 801390792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 801490792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 801590792Sgshapiro return SM_SPLIT_FAIL; 801690792Sgshapiro } 801790792Sgshapiro 801890792Sgshapiro /* prepend the new envelope to e->e_sibling */ 801990792Sgshapiro ee->e_sibling = e->e_sibling; 802090792Sgshapiro e->e_sibling = ee; 802190792Sgshapiro ++nsplit; 802290792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 802390792Sgshapiro { 802490792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 802590792Sgshapiro { 802690792Sgshapiro char *p; 802790792Sgshapiro 802890792Sgshapiro l += MAXLINE; 802990792Sgshapiro p = sm_realloc(lsplits, l); 803090792Sgshapiro if (p == NULL) 803190792Sgshapiro { 803290792Sgshapiro /* let's try to get this done */ 803390792Sgshapiro sm_free(lsplits); 803490792Sgshapiro lsplits = NULL; 803590792Sgshapiro } 803690792Sgshapiro else 803790792Sgshapiro lsplits = p; 803890792Sgshapiro } 803990792Sgshapiro if (lsplits != NULL) 804090792Sgshapiro { 804190792Sgshapiro if (j == 0) 804290792Sgshapiro j += sm_strlcat(lsplits + j, 804390792Sgshapiro ee->e_id, 804490792Sgshapiro l - j); 804590792Sgshapiro else 804690792Sgshapiro j += sm_strlcat2(lsplits + j, 804790792Sgshapiro "; ", 804890792Sgshapiro ee->e_id, 804990792Sgshapiro l - j); 805090792Sgshapiro SM_ASSERT(j < l); 805190792Sgshapiro } 805290792Sgshapiro } 805390792Sgshapiro if (nrcpt - i <= maxrcpt) 805490792Sgshapiro break; 805590792Sgshapiro i += maxrcpt; 805690792Sgshapiro } 805790792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && nsplit > 0) 805890792Sgshapiro { 805990792Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, 806090792Sgshapiro "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s", 806190792Sgshapiro maxrcpt, nrcpt - ndead, nsplit, 806290792Sgshapiro nsplit > 1 ? "s" : "", lsplits); 806390792Sgshapiro sm_free(lsplits); 806490792Sgshapiro } 806590792Sgshapiro return SM_SPLIT_NEW(nsplit); 806690792Sgshapiro} 806790792Sgshapiro/* 806890792Sgshapiro** SPLIT_BY_RECIPIENT 806990792Sgshapiro** 807090792Sgshapiro** Split an envelope with multiple recipients into multiple 807190792Sgshapiro** envelopes as required by the sendmail configuration. 807290792Sgshapiro** 807390792Sgshapiro** Parameters: 807490792Sgshapiro** e -- envelope. 807590792Sgshapiro** 807690792Sgshapiro** Results: 807790792Sgshapiro** Returns true on success, false on failure. 807890792Sgshapiro** 807990792Sgshapiro** Side Effects: 808090792Sgshapiro** see split_across_queue_groups(), split_within_queue(e) 808190792Sgshapiro*/ 808290792Sgshapiro 808390792Sgshapirobool 808490792Sgshapirosplit_by_recipient(e) 808590792Sgshapiro ENVELOPE *e; 808690792Sgshapiro{ 808790792Sgshapiro int split, n, i, j, l; 808890792Sgshapiro char *lsplits; 808990792Sgshapiro ENVELOPE *ee, *next, *firstsibling; 809090792Sgshapiro 809190792Sgshapiro if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) || 809290792Sgshapiro bitset(EF_SPLIT, e->e_flags)) 809390792Sgshapiro return true; 809490792Sgshapiro n = split_across_queue_groups(e); 809590792Sgshapiro if (n == SM_SPLIT_FAIL) 809690792Sgshapiro return false; 809790792Sgshapiro firstsibling = ee = e->e_sibling; 809890792Sgshapiro if (n > 1 && LogLevel > SPLIT_LOG_LEVEL) 809990792Sgshapiro { 810090792Sgshapiro l = MAXLINE; 810190792Sgshapiro lsplits = sm_malloc(l); 810290792Sgshapiro if (lsplits != NULL) 810390792Sgshapiro *lsplits = '\0'; 810490792Sgshapiro j = 0; 810590792Sgshapiro } 810690792Sgshapiro else 810790792Sgshapiro { 810890792Sgshapiro /* get rid of stupid compiler warnings */ 810990792Sgshapiro lsplits = NULL; 811090792Sgshapiro j = l = 0; 811190792Sgshapiro } 811290792Sgshapiro for (i = 1; i < n; ++i) 811390792Sgshapiro { 811490792Sgshapiro next = ee->e_sibling; 811590792Sgshapiro if (split_within_queue(ee) == SM_SPLIT_FAIL) 811690792Sgshapiro { 811790792Sgshapiro e->e_sibling = firstsibling; 811890792Sgshapiro return false; 811990792Sgshapiro } 812090792Sgshapiro ee->e_flags |= EF_SPLIT; 812190792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 812290792Sgshapiro { 812390792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 812490792Sgshapiro { 812590792Sgshapiro char *p; 812690792Sgshapiro 812790792Sgshapiro l += MAXLINE; 812890792Sgshapiro p = sm_realloc(lsplits, l); 812990792Sgshapiro if (p == NULL) 813090792Sgshapiro { 813190792Sgshapiro /* let's try to get this done */ 813290792Sgshapiro sm_free(lsplits); 813390792Sgshapiro lsplits = NULL; 813490792Sgshapiro } 813590792Sgshapiro else 813690792Sgshapiro lsplits = p; 813790792Sgshapiro } 813890792Sgshapiro if (lsplits != NULL) 813990792Sgshapiro { 814090792Sgshapiro if (j == 0) 814190792Sgshapiro j += sm_strlcat(lsplits + j, 814290792Sgshapiro ee->e_id, l - j); 814390792Sgshapiro else 814490792Sgshapiro j += sm_strlcat2(lsplits + j, "; ", 814590792Sgshapiro ee->e_id, l - j); 814690792Sgshapiro SM_ASSERT(j < l); 814790792Sgshapiro } 814890792Sgshapiro } 814990792Sgshapiro ee = next; 815090792Sgshapiro } 815190792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1) 815290792Sgshapiro { 815390792Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s", 815490792Sgshapiro n - 1, n > 2 ? "s" : "", lsplits); 815590792Sgshapiro sm_free(lsplits); 815690792Sgshapiro } 815790792Sgshapiro split = split_within_queue(e) != SM_SPLIT_FAIL; 815890792Sgshapiro if (split) 815990792Sgshapiro e->e_flags |= EF_SPLIT; 816090792Sgshapiro return split; 816190792Sgshapiro} 816290792Sgshapiro 816390792Sgshapiro#if _FFR_QUARANTINE 816490792Sgshapiro/* 816590792Sgshapiro** QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope 816690792Sgshapiro** 816790792Sgshapiro** Add/remove quarantine reason and requeue appropriately. 816890792Sgshapiro** 816990792Sgshapiro** Parameters: 817090792Sgshapiro** qgrp -- queue group for the item 817190792Sgshapiro** qdir -- queue directory in the given queue group 817290792Sgshapiro** e -- envelope information for the item 817390792Sgshapiro** reason -- quarantine reason, NULL means unquarantine. 817490792Sgshapiro** 817590792Sgshapiro** Results: 817690792Sgshapiro** true if item changed, false otherwise 817790792Sgshapiro** 817890792Sgshapiro** Side Effects: 817990792Sgshapiro** Changes quarantine tag in queue file and renames it. 818090792Sgshapiro*/ 818190792Sgshapiro 818290792Sgshapirostatic bool 818390792Sgshapiroquarantine_queue_item(qgrp, qdir, e, reason) 818490792Sgshapiro int qgrp; 818590792Sgshapiro int qdir; 818690792Sgshapiro ENVELOPE *e; 818790792Sgshapiro char *reason; 818890792Sgshapiro{ 818990792Sgshapiro bool dirty = false; 819090792Sgshapiro bool failing = false; 819190792Sgshapiro bool foundq = false; 819290792Sgshapiro bool finished = false; 819390792Sgshapiro int fd; 819490792Sgshapiro int flags; 819590792Sgshapiro int oldtype; 819690792Sgshapiro int newtype; 819790792Sgshapiro int save_errno; 819890792Sgshapiro MODE_T oldumask = 0; 819990792Sgshapiro SM_FILE_T *oldqfp, *tempqfp; 820090792Sgshapiro char *bp; 820190792Sgshapiro char oldqf[MAXPATHLEN]; 820290792Sgshapiro char tempqf[MAXPATHLEN]; 820390792Sgshapiro char newqf[MAXPATHLEN]; 820490792Sgshapiro char buf[MAXLINE]; 820590792Sgshapiro 820690792Sgshapiro oldtype = queue_letter(e, ANYQFL_LETTER); 820790792Sgshapiro (void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof oldqf); 820890792Sgshapiro (void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof tempqf); 820990792Sgshapiro 821090792Sgshapiro /* 821190792Sgshapiro ** Instead of duplicating all the open 821290792Sgshapiro ** and lock code here, tell readqf() to 821390792Sgshapiro ** do that work and return the open 821490792Sgshapiro ** file pointer in e_lockfp. Note that 821590792Sgshapiro ** we must release the locks properly when 821690792Sgshapiro ** we are done. 821790792Sgshapiro */ 821890792Sgshapiro 821990792Sgshapiro if (!readqf(e, true)) 822090792Sgshapiro { 822190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 822290792Sgshapiro "Skipping %s\n", qid_printname(e)); 822390792Sgshapiro return false; 822490792Sgshapiro } 822590792Sgshapiro oldqfp = e->e_lockfp; 822690792Sgshapiro 822790792Sgshapiro /* open the new queue file */ 822890792Sgshapiro flags = O_CREAT|O_WRONLY|O_EXCL; 822990792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 823090792Sgshapiro oldumask = umask(002); 823190792Sgshapiro fd = open(tempqf, flags, QueueFileMode); 823290792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 823390792Sgshapiro (void) umask(oldumask); 823490792Sgshapiro RELEASE_QUEUE; 823590792Sgshapiro 823690792Sgshapiro if (fd < 0) 823790792Sgshapiro { 823890792Sgshapiro save_errno = errno; 823990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 824090792Sgshapiro "Skipping %s: Could not open %s: %s\n", 824190792Sgshapiro qid_printname(e), tempqf, 824290792Sgshapiro sm_errstring(save_errno)); 824390792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 824490792Sgshapiro return false; 824590792Sgshapiro } 824690792Sgshapiro if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB)) 824790792Sgshapiro { 824890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 824990792Sgshapiro "Skipping %s: Could not lock %s\n", 825090792Sgshapiro qid_printname(e), tempqf); 825190792Sgshapiro (void) close(fd); 825290792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 825390792Sgshapiro return false; 825490792Sgshapiro } 825590792Sgshapiro 825690792Sgshapiro tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd, 825790792Sgshapiro SM_IO_WRONLY, NULL); 825890792Sgshapiro if (tempqfp == NULL) 825990792Sgshapiro { 826090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 826190792Sgshapiro "Skipping %s: Could not lock %s\n", 826290792Sgshapiro qid_printname(e), tempqf); 826390792Sgshapiro (void) close(fd); 826490792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 826590792Sgshapiro return false; 826690792Sgshapiro } 826790792Sgshapiro 826890792Sgshapiro /* Copy the data over, changing the quarantine reason */ 826990792Sgshapiro while ((bp = fgetfolded(buf, sizeof buf, oldqfp)) != NULL) 827090792Sgshapiro { 827190792Sgshapiro if (tTd(40, 4)) 827290792Sgshapiro sm_dprintf("+++++ %s\n", bp); 827390792Sgshapiro switch (bp[0]) 827490792Sgshapiro { 827590792Sgshapiro case 'q': /* quarantine reason */ 827690792Sgshapiro foundq = true; 827790792Sgshapiro if (reason == NULL) 827890792Sgshapiro { 827990792Sgshapiro if (Verbose) 828090792Sgshapiro { 828190792Sgshapiro (void) sm_io_fprintf(smioout, 828290792Sgshapiro SM_TIME_DEFAULT, 828390792Sgshapiro "%s: Removed quarantine of \"%s\"\n", 828490792Sgshapiro e->e_id, &bp[1]); 828590792Sgshapiro } 828690792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "unquarantine"); 828790792Sgshapiro dirty = true; 828890792Sgshapiro continue; 828990792Sgshapiro } 829090792Sgshapiro else if (strcmp(reason, &bp[1]) == 0) 829190792Sgshapiro { 829290792Sgshapiro if (Verbose) 829390792Sgshapiro { 829490792Sgshapiro (void) sm_io_fprintf(smioout, 829590792Sgshapiro SM_TIME_DEFAULT, 829690792Sgshapiro "%s: Already quarantined with \"%s\"\n", 829790792Sgshapiro e->e_id, reason); 829890792Sgshapiro } 829990792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 830090792Sgshapiro "q%s\n", reason); 830190792Sgshapiro } 830290792Sgshapiro else 830390792Sgshapiro { 830490792Sgshapiro if (Verbose) 830590792Sgshapiro { 830690792Sgshapiro (void) sm_io_fprintf(smioout, 830790792Sgshapiro SM_TIME_DEFAULT, 830890792Sgshapiro "%s: Quarantine changed from \"%s\" to \"%s\"\n", 830990792Sgshapiro e->e_id, &bp[1], 831090792Sgshapiro reason); 831190792Sgshapiro } 831290792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 831390792Sgshapiro "q%s\n", reason); 831490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 831590792Sgshapiro reason); 831690792Sgshapiro dirty = true; 831790792Sgshapiro } 831890792Sgshapiro break; 831990792Sgshapiro 832098121Sgshapiro case 'S': 832190792Sgshapiro /* 832290792Sgshapiro ** If we are quarantining an unquarantined item, 832390792Sgshapiro ** need to put in a new 'q' line before it's 832490792Sgshapiro ** too late. 832590792Sgshapiro */ 832690792Sgshapiro 832790792Sgshapiro if (!foundq && reason != NULL) 832890792Sgshapiro { 832990792Sgshapiro if (Verbose) 833090792Sgshapiro { 833190792Sgshapiro (void) sm_io_fprintf(smioout, 833290792Sgshapiro SM_TIME_DEFAULT, 833390792Sgshapiro "%s: Quarantined with \"%s\"\n", 833490792Sgshapiro e->e_id, reason); 833590792Sgshapiro } 833690792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 833790792Sgshapiro "q%s\n", reason); 833890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 833990792Sgshapiro reason); 834090792Sgshapiro foundq = true; 834190792Sgshapiro dirty = true; 834290792Sgshapiro } 834390792Sgshapiro 834490792Sgshapiro /* Copy the line to the new file */ 834590792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 834690792Sgshapiro "%s\n", bp); 834790792Sgshapiro break; 834890792Sgshapiro 834990792Sgshapiro case '.': 835090792Sgshapiro finished = true; 835190792Sgshapiro /* FALLTHROUGH */ 835290792Sgshapiro 835390792Sgshapiro default: 835490792Sgshapiro /* Copy the line to the new file */ 835590792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 835690792Sgshapiro "%s\n", bp); 835790792Sgshapiro break; 835890792Sgshapiro } 835990792Sgshapiro } 836090792Sgshapiro 836190792Sgshapiro /* Make sure we read the whole old file */ 836290792Sgshapiro errno = sm_io_error(tempqfp); 836390792Sgshapiro if (errno != 0 && errno != SM_IO_EOF) 836490792Sgshapiro { 836590792Sgshapiro save_errno = errno; 836690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 836790792Sgshapiro "Skipping %s: Error reading %s: %s\n", 836890792Sgshapiro qid_printname(e), oldqf, 836990792Sgshapiro sm_errstring(save_errno)); 837090792Sgshapiro failing = true; 837190792Sgshapiro } 837290792Sgshapiro 837390792Sgshapiro if (!failing && !finished) 837490792Sgshapiro { 837590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 837690792Sgshapiro "Skipping %s: Incomplete file: %s\n", 837790792Sgshapiro qid_printname(e), oldqf); 837890792Sgshapiro failing = true; 837990792Sgshapiro } 838090792Sgshapiro 838190792Sgshapiro /* Check if we actually changed anything or we can just bail now */ 838290792Sgshapiro if (!dirty) 838390792Sgshapiro { 838490792Sgshapiro /* pretend we failed, even though we technically didn't */ 838590792Sgshapiro failing = true; 838690792Sgshapiro } 838790792Sgshapiro 838890792Sgshapiro /* Make sure we wrote things out safely */ 838990792Sgshapiro if (!failing && 839090792Sgshapiro (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 || 839190792Sgshapiro ((SuperSafe == SAFE_REALLY || SuperSafe == SAFE_INTERACTIVE) && 839290792Sgshapiro fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) || 839390792Sgshapiro ((errno = sm_io_error(tempqfp)) != 0))) 839490792Sgshapiro { 839590792Sgshapiro save_errno = errno; 839690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 839790792Sgshapiro "Skipping %s: Error writing %s: %s\n", 839890792Sgshapiro qid_printname(e), tempqf, 839990792Sgshapiro sm_errstring(save_errno)); 840090792Sgshapiro failing = true; 840190792Sgshapiro } 840290792Sgshapiro 840390792Sgshapiro 840490792Sgshapiro /* Figure out the new filename */ 840590792Sgshapiro newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER); 840690792Sgshapiro if (oldtype == newtype) 840790792Sgshapiro { 840890792Sgshapiro /* going to rename tempqf to oldqf */ 840990792Sgshapiro (void) sm_strlcpy(newqf, oldqf, sizeof newqf); 841090792Sgshapiro } 841190792Sgshapiro else 841290792Sgshapiro { 841390792Sgshapiro /* going to rename tempqf to new name based on newtype */ 841490792Sgshapiro (void) sm_strlcpy(newqf, queuename(e, newtype), sizeof newqf); 841590792Sgshapiro } 841690792Sgshapiro 841790792Sgshapiro save_errno = 0; 841890792Sgshapiro 841990792Sgshapiro /* rename tempqf to newqf */ 842090792Sgshapiro if (!failing && 842190792Sgshapiro rename(tempqf, newqf) < 0) 842290792Sgshapiro save_errno = (errno == 0) ? EINVAL : errno; 842390792Sgshapiro 842490792Sgshapiro /* Check rename() success */ 842590792Sgshapiro if (!failing && save_errno != 0) 842690792Sgshapiro { 842790792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, 842890792Sgshapiro "quarantine_queue_item: rename(%s, %s): %s", 842990792Sgshapiro tempqf, newqf, sm_errstring(save_errno)); 843090792Sgshapiro 843190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 843290792Sgshapiro "Error renaming %s to %s: %s\n", 843390792Sgshapiro tempqf, newqf, 843490792Sgshapiro sm_errstring(save_errno)); 843590792Sgshapiro if (oldtype == newtype) 843690792Sgshapiro { 843790792Sgshapiro /* 843890792Sgshapiro ** Bail here since we don't know the state of 843990792Sgshapiro ** the filesystem and may need to keep tempqf 844090792Sgshapiro ** for the user to rescue us. 844190792Sgshapiro */ 844290792Sgshapiro 844390792Sgshapiro RELEASE_QUEUE; 844490792Sgshapiro errno = save_errno; 844590792Sgshapiro syserr("!452 Error renaming control file %s", tempqf); 844690792Sgshapiro /* NOTREACHED */ 844790792Sgshapiro } 844890792Sgshapiro else 844990792Sgshapiro { 845090792Sgshapiro /* remove new file (if rename() half completed) */ 845190792Sgshapiro if (xunlink(newqf) < 0) 845290792Sgshapiro { 845390792Sgshapiro save_errno = errno; 845490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 845590792Sgshapiro "Error removing %s: %s\n", 845690792Sgshapiro newqf, 845790792Sgshapiro sm_errstring(save_errno)); 845890792Sgshapiro } 845990792Sgshapiro 846090792Sgshapiro /* tempqf removed below */ 846190792Sgshapiro failing = true; 846290792Sgshapiro } 846390792Sgshapiro 846490792Sgshapiro } 846590792Sgshapiro 846690792Sgshapiro /* If changing file types, need to remove old type */ 846790792Sgshapiro if (!failing && oldtype != newtype) 846890792Sgshapiro { 846990792Sgshapiro if (xunlink(oldqf) < 0) 847090792Sgshapiro { 847190792Sgshapiro save_errno = errno; 847290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 847390792Sgshapiro "Error removing %s: %s\n", 847490792Sgshapiro oldqf, sm_errstring(save_errno)); 847590792Sgshapiro } 847690792Sgshapiro } 847790792Sgshapiro 847890792Sgshapiro /* see if anything above failed */ 847990792Sgshapiro if (failing) 848090792Sgshapiro { 848190792Sgshapiro /* Something failed: remove new file, old file still there */ 848290792Sgshapiro (void) xunlink(tempqf); 848390792Sgshapiro } 848490792Sgshapiro 848590792Sgshapiro /* 848690792Sgshapiro ** fsync() after file operations to make sure metadata is 848790792Sgshapiro ** written to disk on filesystems in which renames are 848890792Sgshapiro ** not guaranteed. It's ok if they fail, mail won't be lost. 848990792Sgshapiro */ 849090792Sgshapiro 849190792Sgshapiro if (SuperSafe != SAFE_NO) 849290792Sgshapiro { 849390792Sgshapiro /* for soft-updates */ 849490792Sgshapiro (void) fsync(sm_io_getinfo(tempqfp, 849590792Sgshapiro SM_IO_WHAT_FD, NULL)); 849690792Sgshapiro 849790792Sgshapiro if (!failing) 849890792Sgshapiro { 849990792Sgshapiro /* for soft-updates */ 850090792Sgshapiro (void) fsync(sm_io_getinfo(oldqfp, 850190792Sgshapiro SM_IO_WHAT_FD, NULL)); 850290792Sgshapiro } 850390792Sgshapiro 850490792Sgshapiro /* for other odd filesystems */ 850590792Sgshapiro SYNC_DIR(tempqf, false); 850690792Sgshapiro } 850790792Sgshapiro 850890792Sgshapiro /* Close up shop */ 850990792Sgshapiro RELEASE_QUEUE; 851090792Sgshapiro if (tempqfp != NULL) 851190792Sgshapiro (void) sm_io_close(tempqfp, SM_TIME_DEFAULT); 851290792Sgshapiro if (oldqfp != NULL) 851390792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 851490792Sgshapiro 851590792Sgshapiro /* All went well */ 851690792Sgshapiro return !failing; 851790792Sgshapiro} 851890792Sgshapiro 851990792Sgshapiro/* 852090792Sgshapiro** QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue 852190792Sgshapiro** 852290792Sgshapiro** Read all matching queue items, add/remove quarantine 852390792Sgshapiro** reason, and requeue appropriately. 852490792Sgshapiro** 852590792Sgshapiro** Parameters: 852690792Sgshapiro** reason -- quarantine reason, "." means unquarantine. 852790792Sgshapiro** qgrplimit -- limit to single queue group unless NOQGRP 852890792Sgshapiro** 852990792Sgshapiro** Results: 853090792Sgshapiro** none. 853190792Sgshapiro** 853290792Sgshapiro** Side Effects: 853390792Sgshapiro** Lots of changes to the queue. 853490792Sgshapiro*/ 853590792Sgshapiro 853690792Sgshapirovoid 853790792Sgshapiroquarantine_queue(reason, qgrplimit) 853890792Sgshapiro char *reason; 853990792Sgshapiro int qgrplimit; 854090792Sgshapiro{ 854190792Sgshapiro int changed = 0; 854290792Sgshapiro int qgrp; 854390792Sgshapiro 854490792Sgshapiro /* Convert internal representation of unquarantine */ 854590792Sgshapiro if (reason != NULL && reason[0] == '.' && reason[1] == '\0') 854690792Sgshapiro reason = NULL; 854790792Sgshapiro 854890792Sgshapiro if (reason != NULL) 854990792Sgshapiro { 855090792Sgshapiro /* clean it */ 855190792Sgshapiro reason = newstr(denlstring(reason, true, true)); 855290792Sgshapiro } 855390792Sgshapiro 855490792Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 855590792Sgshapiro { 855690792Sgshapiro int qdir; 855790792Sgshapiro 855890792Sgshapiro if (qgrplimit != NOQGRP && qgrplimit != qgrp) 855990792Sgshapiro continue; 856090792Sgshapiro 856190792Sgshapiro for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++) 856290792Sgshapiro { 856390792Sgshapiro int i; 856490792Sgshapiro int nrequests; 856590792Sgshapiro 856690792Sgshapiro if (StopRequest) 856790792Sgshapiro stop_sendmail(); 856890792Sgshapiro 856990792Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL); 857090792Sgshapiro 857190792Sgshapiro /* first see if there is anything */ 857290792Sgshapiro if (nrequests <= 0) 857390792Sgshapiro { 857490792Sgshapiro if (Verbose) 857590792Sgshapiro { 857690792Sgshapiro (void) sm_io_fprintf(smioout, 857790792Sgshapiro SM_TIME_DEFAULT, "%s: no matches\n", 857890792Sgshapiro qid_printqueue(qgrp, qdir)); 857990792Sgshapiro } 858090792Sgshapiro continue; 858190792Sgshapiro } 858290792Sgshapiro 858390792Sgshapiro if (Verbose) 858490792Sgshapiro { 858590792Sgshapiro (void) sm_io_fprintf(smioout, 858690792Sgshapiro SM_TIME_DEFAULT, "Processing %s:\n", 858790792Sgshapiro qid_printqueue(qgrp, qdir)); 858890792Sgshapiro } 858990792Sgshapiro 859090792Sgshapiro for (i = 0; i < WorkListCount; i++) 859190792Sgshapiro { 859290792Sgshapiro ENVELOPE e; 859390792Sgshapiro 859490792Sgshapiro if (StopRequest) 859590792Sgshapiro stop_sendmail(); 859690792Sgshapiro 859790792Sgshapiro /* setup envelope */ 859890792Sgshapiro clearenvelope(&e, true, sm_rpool_new_x(NULL)); 859990792Sgshapiro e.e_id = WorkList[i].w_name + 2; 860090792Sgshapiro e.e_qgrp = qgrp; 860190792Sgshapiro e.e_qdir = qdir; 860290792Sgshapiro 860390792Sgshapiro if (tTd(70, 101)) 860490792Sgshapiro { 860590792Sgshapiro sm_io_fprintf(smioout, SM_TIME_DEFAULT, 860690792Sgshapiro "Would do %s\n", e.e_id); 860790792Sgshapiro changed++; 860890792Sgshapiro } 860990792Sgshapiro else if (quarantine_queue_item(qgrp, qdir, 861090792Sgshapiro &e, reason)) 861190792Sgshapiro changed++; 861290792Sgshapiro 861390792Sgshapiro /* clean up */ 861490792Sgshapiro sm_rpool_free(e.e_rpool); 861590792Sgshapiro e.e_rpool = NULL; 861690792Sgshapiro } 861790792Sgshapiro if (WorkList != NULL) 861890792Sgshapiro sm_free(WorkList); /* XXX */ 861990792Sgshapiro WorkList = NULL; 862090792Sgshapiro WorkListSize = 0; 862190792Sgshapiro WorkListCount = 0; 862290792Sgshapiro } 862390792Sgshapiro } 862490792Sgshapiro if (Verbose) 862590792Sgshapiro { 862690792Sgshapiro if (changed == 0) 862790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 862890792Sgshapiro "No changes\n"); 862990792Sgshapiro else 863090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 863190792Sgshapiro "%d change%s\n", 863290792Sgshapiro changed, 863390792Sgshapiro changed == 1 ? "" : "s"); 863490792Sgshapiro } 863590792Sgshapiro} 863690792Sgshapiro#endif /* _FFR_QUARANTINE */ 8637