queue.c revision 98841
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 1698841SgshapiroSM_RCSID("@(#)$Id: queue.c,v 8.863.2.2 2002/06/25 21:34:31 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. 146690792Sgshapiro */ 146790792Sgshapiro 146890792Sgshapiro ret = run_work_group(curnum, forkflag, verbose, persistent, 146990792Sgshapiro runall); 147090792Sgshapiro 147190792Sgshapiro /* 147264562Sgshapiro ** Failure means a message was printed for ETRN 147364562Sgshapiro ** and subsequent queues are likely to fail as well. 147464562Sgshapiro */ 147564562Sgshapiro 147664562Sgshapiro if (!ret) 147764562Sgshapiro break; 147864562Sgshapiro 147990792Sgshapiro /* Success means the runner count needs to be updated. */ 148090792Sgshapiro CurRunners += WorkGrp[curnum].wg_maxact; 148190792Sgshapiro if (!persistent) 148294334Sgshapiro schedule_queue_runs(runall, curnum, true); 148390792Sgshapiro INCR_MOD(curnum, NumWorkGroups); 148464562Sgshapiro } 148590792Sgshapiro 148690792Sgshapiro /* schedule left over queue runs */ 148790792Sgshapiro if (i < NumWorkGroups && !NoMoreRunners && !persistent) 148890792Sgshapiro { 148990792Sgshapiro int h; 149090792Sgshapiro 149190792Sgshapiro for (h = curnum; i < NumWorkGroups; i++) 149290792Sgshapiro { 149394334Sgshapiro schedule_queue_runs(runall, h, false); 149490792Sgshapiro INCR_MOD(h, NumWorkGroups); 149590792Sgshapiro } 149690792Sgshapiro } 149790792Sgshapiro 149890792Sgshapiro 149990792Sgshapiro#if SM_HEAP_CHECK 150090792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 150190792Sgshapiro sm_heap_setgroup(oldgroup); 150290792Sgshapiro#endif /* SM_HEAP_CHECK */ 150364562Sgshapiro return ret; 150464562Sgshapiro} 150590792Sgshapiro/* 150690792Sgshapiro** RUNNER_WORK -- have a queue runner do its work 150764562Sgshapiro** 150890792Sgshapiro** Have a queue runner do its work a list of entries. 150990792Sgshapiro** When work isn't directly being done then this process can take a signal 151090792Sgshapiro** and terminate immediately (in a clean fashion of course). 151190792Sgshapiro** When work is directly being done, it's not to be interrupted 151290792Sgshapiro** immediately: the work should be allowed to finish at a clean point 151390792Sgshapiro** before termination (in a clean fashion of course). 151490792Sgshapiro** 151590792Sgshapiro** Parameters: 151690792Sgshapiro** e -- envelope. 151790792Sgshapiro** sequenceno -- 'th process to run WorkQ. 151890792Sgshapiro** didfork -- did the calling process fork()? 151990792Sgshapiro** skip -- process only each skip'th item. 152090792Sgshapiro** njobs -- number of jobs in WorkQ. 152190792Sgshapiro** 152290792Sgshapiro** Returns: 152390792Sgshapiro** none. 152490792Sgshapiro** 152590792Sgshapiro** Side Effects: 152690792Sgshapiro** runs things in the mail queue. 152790792Sgshapiro*/ 152890792Sgshapiro 152990792Sgshapiro/* Get new load average every 30 seconds. */ 153090792Sgshapiro#define GET_NEW_LA_TIME 30 153190792Sgshapiro 153290792Sgshapirostatic void 153390792Sgshapirorunner_work(e, sequenceno, didfork, skip, njobs) 153490792Sgshapiro register ENVELOPE *e; 153590792Sgshapiro int sequenceno; 153690792Sgshapiro bool didfork; 153790792Sgshapiro int skip; 153890792Sgshapiro int njobs; 153990792Sgshapiro{ 154090792Sgshapiro int n; 154190792Sgshapiro WORK *w; 154290792Sgshapiro time_t current_la_time, now; 154390792Sgshapiro 154490792Sgshapiro current_la_time = curtime(); 154590792Sgshapiro 154690792Sgshapiro /* 154790792Sgshapiro ** Here we temporarily block the second calling of the handlers. 154890792Sgshapiro ** This allows us to handle the signal without terminating in the 154990792Sgshapiro ** middle of direct work. If a signal does come, the test for 155090792Sgshapiro ** NoMoreRunners will find it. 155190792Sgshapiro */ 155290792Sgshapiro 155390792Sgshapiro BlockOldsh = true; 155490792Sgshapiro 155590792Sgshapiro /* process them once at a time */ 155690792Sgshapiro while (WorkQ != NULL) 155790792Sgshapiro { 155890792Sgshapiro#if SM_HEAP_CHECK 155990792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 156090792Sgshapiro 156190792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 156290792Sgshapiro { 156390792Sgshapiro oldgroup = sm_heap_group(); 156490792Sgshapiro sm_heap_newgroup(); 156590792Sgshapiro sm_dprintf("run_queue_group() heap group #%d\n", 156690792Sgshapiro sm_heap_group()); 156790792Sgshapiro } 156890792Sgshapiro#endif /* SM_HEAP_CHECK */ 156990792Sgshapiro 157090792Sgshapiro /* do no more work */ 157190792Sgshapiro if (NoMoreRunners) 157290792Sgshapiro { 157390792Sgshapiro /* Check that a valid signal handler is callable */ 157490792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 157590792Sgshapiro Oldsh != runners_sighup && 157690792Sgshapiro Oldsh != runners_sigterm) 157790792Sgshapiro (*Oldsh)(Oldsig); 157890792Sgshapiro break; 157990792Sgshapiro } 158090792Sgshapiro 158190792Sgshapiro w = WorkQ; /* assign current work item */ 158290792Sgshapiro 158390792Sgshapiro /* 158490792Sgshapiro ** Set the head of the WorkQ to the next work item. 158590792Sgshapiro ** It is set 'skip' ahead (the number of parallel queue 158690792Sgshapiro ** runners working on WorkQ together) since each runner 158790792Sgshapiro ** works on every 'skip'th (N-th) item. 158890792Sgshapiro */ 158990792Sgshapiro 159090792Sgshapiro for (n = 0; n < skip && WorkQ != NULL; n++) 159190792Sgshapiro WorkQ = WorkQ->w_next; 159290792Sgshapiro e->e_to = NULL; 159390792Sgshapiro 159490792Sgshapiro /* 159590792Sgshapiro ** Ignore jobs that are too expensive for the moment. 159690792Sgshapiro ** 159790792Sgshapiro ** Get new load average every GET_NEW_LA_TIME seconds. 159890792Sgshapiro */ 159990792Sgshapiro 160090792Sgshapiro now = curtime(); 160190792Sgshapiro if (current_la_time < now - GET_NEW_LA_TIME) 160290792Sgshapiro { 160390792Sgshapiro sm_getla(); 160490792Sgshapiro current_la_time = now; 160590792Sgshapiro } 160690792Sgshapiro if (shouldqueue(WkRecipFact, current_la_time)) 160790792Sgshapiro { 160890792Sgshapiro char *msg = "Aborting queue run: load average too high"; 160990792Sgshapiro 161090792Sgshapiro if (Verbose) 161190792Sgshapiro message("%s", msg); 161290792Sgshapiro if (LogLevel > 8) 161390792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 161490792Sgshapiro break; 161590792Sgshapiro } 161690792Sgshapiro if (shouldqueue(w->w_pri, w->w_ctime)) 161790792Sgshapiro { 161890792Sgshapiro if (Verbose) 161990792Sgshapiro message(EmptyString); 162090792Sgshapiro if (QueueSortOrder == QSO_BYPRIORITY) 162190792Sgshapiro { 162290792Sgshapiro if (Verbose) 162390792Sgshapiro message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", 162490792Sgshapiro qid_printqueue(w->w_qgrp, 162590792Sgshapiro w->w_qdir), 162690792Sgshapiro w->w_name + 2, sequenceno, 162790792Sgshapiro njobs); 162890792Sgshapiro if (LogLevel > 8) 162990792Sgshapiro sm_syslog(LOG_INFO, NOQID, 163090792Sgshapiro "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", 163190792Sgshapiro qid_printqueue(w->w_qgrp, 163290792Sgshapiro w->w_qdir), 163390792Sgshapiro w->w_name + 2, w->w_pri, 163490792Sgshapiro CurrentLA, sequenceno, 163590792Sgshapiro njobs); 163690792Sgshapiro break; 163790792Sgshapiro } 163890792Sgshapiro else if (Verbose) 163990792Sgshapiro message("Skipping %s/%s (sequence %d of %d)", 164090792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 164190792Sgshapiro w->w_name + 2, sequenceno, njobs); 164290792Sgshapiro } 164390792Sgshapiro else 164490792Sgshapiro { 164590792Sgshapiro if (Verbose) 164690792Sgshapiro { 164790792Sgshapiro message(EmptyString); 164890792Sgshapiro message("Running %s/%s (sequence %d of %d)", 164990792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 165090792Sgshapiro w->w_name + 2, sequenceno, njobs); 165190792Sgshapiro } 165290792Sgshapiro if (didfork && MaxQueueChildren > 0) 165390792Sgshapiro { 165490792Sgshapiro sm_blocksignal(SIGCHLD); 165590792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 165690792Sgshapiro } 165790792Sgshapiro if (tTd(63, 100)) 165890792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 165990792Sgshapiro "runqueue %s dowork(%s)", 166090792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 166190792Sgshapiro w->w_name + 2); 166290792Sgshapiro 166390792Sgshapiro (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2, 166490792Sgshapiro false, false, e); 166590792Sgshapiro errno = 0; 166690792Sgshapiro } 166790792Sgshapiro sm_free(w->w_name); /* XXX */ 166890792Sgshapiro if (w->w_host != NULL) 166990792Sgshapiro sm_free(w->w_host); /* XXX */ 167090792Sgshapiro sm_free((char *) w); /* XXX */ 167190792Sgshapiro sequenceno += skip; /* next sequence number */ 167290792Sgshapiro#if SM_HEAP_CHECK 167390792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 167490792Sgshapiro sm_heap_setgroup(oldgroup); 167590792Sgshapiro#endif /* SM_HEAP_CHECK */ 167690792Sgshapiro } 167790792Sgshapiro 167890792Sgshapiro BlockOldsh = false; 167990792Sgshapiro 168090792Sgshapiro /* check the signals didn't happen during the revert */ 168190792Sgshapiro if (NoMoreRunners) 168290792Sgshapiro { 168390792Sgshapiro /* Check that a valid signal handler is callable */ 168490792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 168590792Sgshapiro Oldsh != runners_sighup && Oldsh != runners_sigterm) 168690792Sgshapiro (*Oldsh)(Oldsig); 168790792Sgshapiro } 168890792Sgshapiro 168990792Sgshapiro Oldsh = SIG_DFL; /* after the NoMoreRunners check */ 169090792Sgshapiro} 169190792Sgshapiro/* 169290792Sgshapiro** RUN_WORK_GROUP -- run the jobs in a queue group from a work group. 169390792Sgshapiro** 169464562Sgshapiro** Gets the stuff out of the queue in some presumably logical 169564562Sgshapiro** order and processes them. 169664562Sgshapiro** 169764562Sgshapiro** Parameters: 169890792Sgshapiro** wgrp -- work group to process. 169990792Sgshapiro** forkflag -- true if the queue scanning should be done in 170064562Sgshapiro** a child process. We double-fork so it is not our 170164562Sgshapiro** child and we don't have to clean up after it. 170290792Sgshapiro** verbose -- if true, print out status information. 170390792Sgshapiro** persistent -- persistent queue runner? 170490792Sgshapiro** runall -- true: run all of the queue groups in this work group 170564562Sgshapiro** 170664562Sgshapiro** Returns: 170790792Sgshapiro** true if the queue run successfully began. 170864562Sgshapiro** 170964562Sgshapiro** Side Effects: 171064562Sgshapiro** runs things in the mail queue. 171164562Sgshapiro*/ 171264562Sgshapiro 171390792Sgshapiro/* Minimum sleep time for persistent queue runners */ 171490792Sgshapiro#define MIN_SLEEP_TIME 5 171590792Sgshapiro 171690792Sgshapirobool 171790792Sgshapirorun_work_group(wgrp, forkflag, verbose, persistent, runall) 171890792Sgshapiro int wgrp; 171964562Sgshapiro bool forkflag; 172064562Sgshapiro bool verbose; 172190792Sgshapiro bool persistent; 172290792Sgshapiro bool runall; 172364562Sgshapiro{ 172438032Speter register ENVELOPE *e; 172590792Sgshapiro int njobs, qdir; 172690792Sgshapiro int sequenceno = 1; 172790792Sgshapiro int qgrp, endgrp, h, i; 172894334Sgshapiro time_t current_la_time, now; 172990792Sgshapiro bool full, more; 173090792Sgshapiro SM_RPOOL_T *rpool; 173190792Sgshapiro extern void rmexpstab __P((void)); 173238032Speter extern ENVELOPE BlankEnvelope; 173390792Sgshapiro extern SIGFUNC_DECL reapchild __P((int)); 173438032Speter 173590792Sgshapiro if (wgrp < 0) 173690792Sgshapiro return false; 173790792Sgshapiro 173838032Speter /* 173938032Speter ** If no work will ever be selected, don't even bother reading 174038032Speter ** the queue. 174138032Speter */ 174238032Speter 174390792Sgshapiro sm_getla(); /* get load average */ 174438032Speter current_la_time = curtime(); 174538032Speter 174690792Sgshapiro if (!persistent && shouldqueue(WkRecipFact, current_la_time)) 174738032Speter { 174838032Speter char *msg = "Skipping queue run -- load average too high"; 174938032Speter 175038032Speter if (verbose) 175138032Speter message("458 %s\n", msg); 175238032Speter if (LogLevel > 8) 175390792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 175490792Sgshapiro return false; 175538032Speter } 175638032Speter 175738032Speter /* 175838032Speter ** See if we already have too many children. 175938032Speter */ 176038032Speter 176190792Sgshapiro if (forkflag && WorkGrp[wgrp].wg_lowqintvl > 0 && !persistent && 176238032Speter MaxChildren > 0 && CurChildren >= MaxChildren) 176338032Speter { 176464562Sgshapiro char *msg = "Skipping queue run -- too many children"; 176564562Sgshapiro 176664562Sgshapiro if (verbose) 176764562Sgshapiro message("458 %s (%d)\n", msg, CurChildren); 176864562Sgshapiro if (LogLevel > 8) 176990792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)", 177064562Sgshapiro msg, CurChildren); 177190792Sgshapiro return false; 177238032Speter } 177338032Speter 177438032Speter /* 177538032Speter ** See if we want to go off and do other useful work. 177638032Speter */ 177738032Speter 177838032Speter if (forkflag) 177938032Speter { 178038032Speter pid_t pid; 178138032Speter 178290792Sgshapiro (void) sm_blocksignal(SIGCHLD); 178390792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 178438032Speter 178538032Speter pid = dofork(); 178638032Speter if (pid == -1) 178738032Speter { 178838032Speter const char *msg = "Skipping queue run -- fork() failed"; 178990792Sgshapiro const char *err = sm_errstring(errno); 179038032Speter 179138032Speter if (verbose) 179238032Speter message("458 %s: %s\n", msg, err); 179338032Speter if (LogLevel > 8) 179490792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s", 179564562Sgshapiro msg, err); 179690792Sgshapiro (void) sm_releasesignal(SIGCHLD); 179790792Sgshapiro return false; 179838032Speter } 179938032Speter if (pid != 0) 180038032Speter { 180138032Speter /* parent -- pick up intermediate zombie */ 180290792Sgshapiro (void) sm_blocksignal(SIGALRM); 180390792Sgshapiro 180490792Sgshapiro /* wgrp only used when queue runners are persistent */ 180590792Sgshapiro proc_list_add(pid, "Queue runner", PROC_QUEUE, 180690792Sgshapiro WorkGrp[wgrp].wg_maxact, 180790792Sgshapiro persistent ? wgrp : -1); 180890792Sgshapiro (void) sm_releasesignal(SIGALRM); 180990792Sgshapiro (void) sm_releasesignal(SIGCHLD); 181090792Sgshapiro return true; 181138032Speter } 181290792Sgshapiro 181364562Sgshapiro /* child -- clean up signals */ 181477349Sgshapiro 181577349Sgshapiro /* Reset global flags */ 181677349Sgshapiro RestartRequest = NULL; 181790792Sgshapiro RestartWorkGroup = false; 181877349Sgshapiro ShutdownRequest = NULL; 181977349Sgshapiro PendingSignal = 0; 182090792Sgshapiro CurrentPid = getpid(); 182177349Sgshapiro 182290792Sgshapiro /* 182390792Sgshapiro ** Initialize exception stack and default exception 182490792Sgshapiro ** handler for child process. 182590792Sgshapiro */ 182690792Sgshapiro 182790792Sgshapiro sm_exc_newthread(fatal_error); 182842575Speter clrcontrol(); 182938032Speter proc_list_clear(); 183042575Speter 183142575Speter /* Add parent process as first child item */ 183290792Sgshapiro proc_list_add(CurrentPid, "Queue runner child process", 183390792Sgshapiro PROC_QUEUE_CHILD, 0, -1); 183490792Sgshapiro (void) sm_releasesignal(SIGCHLD); 183590792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 183690792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 183790792Sgshapiro (void) sm_signal(SIGTERM, intsig); 183838032Speter } 183938032Speter 184038032Speter /* 184138032Speter ** Release any resources used by the daemon code. 184238032Speter */ 184338032Speter 184438032Speter clrdaemon(); 184538032Speter 184638032Speter /* force it to run expensive jobs */ 184790792Sgshapiro NoConnect = false; 184838032Speter 184938032Speter /* drop privileges */ 185038032Speter if (geteuid() == (uid_t) 0) 185190792Sgshapiro (void) drop_privileges(false); 185238032Speter 185338032Speter /* 185438032Speter ** Create ourselves an envelope 185538032Speter */ 185638032Speter 185738032Speter CurEnv = &QueueEnvelope; 185890792Sgshapiro rpool = sm_rpool_new_x(NULL); 185990792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 186038032Speter e->e_flags = BlankEnvelope.e_flags; 186173188Sgshapiro e->e_parent = NULL; 186238032Speter 186338032Speter /* make sure we have disconnected from parent */ 186438032Speter if (forkflag) 186538032Speter { 186638032Speter disconnect(1, e); 186790792Sgshapiro QuickAbort = false; 186838032Speter } 186938032Speter 187038032Speter /* 187138032Speter ** If we are running part of the queue, always ignore stored 187238032Speter ** host status. 187338032Speter */ 187438032Speter 187538032Speter if (QueueLimitId != NULL || QueueLimitSender != NULL || 187690792Sgshapiro#if _FFR_QUARANTINE 187790792Sgshapiro QueueLimitQuarantine != NULL || 187890792Sgshapiro#endif /* _FFR_QUARANTINE */ 187938032Speter QueueLimitRecipient != NULL) 188038032Speter { 188190792Sgshapiro IgnoreHostStatus = true; 188238032Speter MinQueueAge = 0; 188338032Speter } 188438032Speter 188538032Speter /* 188690792Sgshapiro ** Here is where we choose the queue group from the work group. 188790792Sgshapiro ** The caller of the "domorework" label must setup a new envelope. 188890792Sgshapiro */ 188990792Sgshapiro 189090792Sgshapiro endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */ 189190792Sgshapiro 189290792Sgshapiro domorework: 189390792Sgshapiro 189490792Sgshapiro /* 189590792Sgshapiro ** Run a queue group if: 189690792Sgshapiro ** runall is set or the bit for this group is set. 189790792Sgshapiro */ 189890792Sgshapiro 189994334Sgshapiro now = curtime(); 190090792Sgshapiro for (;;) 190190792Sgshapiro { 190290792Sgshapiro /* 190390792Sgshapiro ** Find the next queue group within the work group that 190490792Sgshapiro ** has been marked as needing a run. 190590792Sgshapiro */ 190690792Sgshapiro 190790792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index; 190890792Sgshapiro WorkGrp[wgrp].wg_curqgrp++; /* advance */ 190990792Sgshapiro WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */ 191094334Sgshapiro if (runall || 191194334Sgshapiro (Queue[qgrp]->qg_nextrun <= now && 191294334Sgshapiro Queue[qgrp]->qg_nextrun != (time_t) -1)) 191390792Sgshapiro break; 191490792Sgshapiro if (endgrp == WorkGrp[wgrp].wg_curqgrp) 191590792Sgshapiro { 191690792Sgshapiro e->e_id = NULL; 191790792Sgshapiro if (forkflag) 191890792Sgshapiro finis(true, true, ExitStat); 191990792Sgshapiro return true; /* we're done */ 192090792Sgshapiro } 192190792Sgshapiro } 192290792Sgshapiro 192390792Sgshapiro qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */ 192490792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 192590792Sgshapiro if (tTd(69, 12)) 192690792Sgshapiro sm_syslog(LOG_INFO, NOQID, 192790792Sgshapiro "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d", 192890792Sgshapiro wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir), 192990792Sgshapiro WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp); 193090792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 193190792Sgshapiro 193290792Sgshapiro#if HASNICE 193390792Sgshapiro /* tweak niceness of queue runs */ 193490792Sgshapiro if (Queue[qgrp]->qg_nice > 0) 193590792Sgshapiro (void) nice(Queue[qgrp]->qg_nice); 193690792Sgshapiro#endif /* HASNICE */ 193790792Sgshapiro 193890792Sgshapiro /* XXX running queue group... */ 193990792Sgshapiro sm_setproctitle(true, CurEnv, "running queue: %s", 194090792Sgshapiro qid_printqueue(qgrp, qdir)); 194190792Sgshapiro 194290792Sgshapiro if (LogLevel > 69 || tTd(63, 99)) 194390792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 194490792Sgshapiro "runqueue %s, pid=%d, forkflag=%d", 194590792Sgshapiro qid_printqueue(qgrp, qdir), (int) CurrentPid, 194690792Sgshapiro forkflag); 194790792Sgshapiro 194890792Sgshapiro /* 194938032Speter ** Start making passes through the queue. 195038032Speter ** First, read and sort the entire queue. 195138032Speter ** Then, process the work in that order. 195238032Speter ** But if you take too long, start over. 195338032Speter */ 195438032Speter 195590792Sgshapiro for (i = 0; i < Queue[qgrp]->qg_numqueues; i++) 195690792Sgshapiro { 195790792Sgshapiro h = gatherq(qgrp, qdir, false, &full, &more); 195890792Sgshapiro#if SM_CONF_SHM 195990792Sgshapiro if (ShmId != SM_SHM_NO_ID) 196090792Sgshapiro QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h; 196190792Sgshapiro#endif /* SM_CONF_SHM */ 196290792Sgshapiro /* If there are no more items in this queue advance */ 196390792Sgshapiro if (!more) 196490792Sgshapiro { 196590792Sgshapiro /* A round-robin advance */ 196690792Sgshapiro qdir++; 196790792Sgshapiro qdir %= Queue[qgrp]->qg_numqueues; 196890792Sgshapiro } 196990792Sgshapiro 197090792Sgshapiro /* Has the WorkList reached the limit? */ 197190792Sgshapiro if (full) 197290792Sgshapiro break; /* don't try to gather more */ 197390792Sgshapiro } 197490792Sgshapiro 197538032Speter /* order the existing work requests */ 197690792Sgshapiro njobs = sortq(Queue[qgrp]->qg_maxlist); 197790792Sgshapiro Queue[qgrp]->qg_curnum = qdir; /* update */ 197838032Speter 197964562Sgshapiro 198090792Sgshapiro if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags)) 198138032Speter { 198290792Sgshapiro int loop, maxrunners; 198390792Sgshapiro pid_t pid; 198438032Speter 198538032Speter /* 198690792Sgshapiro ** For this WorkQ we want to fork off N children (maxrunners) 198790792Sgshapiro ** at this point. Each child has a copy of WorkQ. Each child 198890792Sgshapiro ** will process every N-th item. The parent will wait for all 198990792Sgshapiro ** of the children to finish before moving on to the next 199090792Sgshapiro ** queue group within the work group. This saves us forking 199190792Sgshapiro ** a new runner-child for each work item. 199290792Sgshapiro ** It's valid for qg_maxqrun == 0 since this may be an 199390792Sgshapiro ** explicit "don't run this queue" setting. 199438032Speter */ 199538032Speter 199690792Sgshapiro maxrunners = Queue[qgrp]->qg_maxqrun; 199790792Sgshapiro 199890792Sgshapiro /* No need to have more runners then there are jobs */ 199990792Sgshapiro if (maxrunners > njobs) 200090792Sgshapiro maxrunners = njobs; 200190792Sgshapiro for (loop = 0; loop < maxrunners; loop++) 200238032Speter { 200390792Sgshapiro /* 200490792Sgshapiro ** Since the delivery may happen in a child and the 200590792Sgshapiro ** parent does not wait, the parent may close the 200690792Sgshapiro ** maps thereby removing any shared memory used by 200790792Sgshapiro ** the map. Therefore, close the maps now so the 200890792Sgshapiro ** child will dynamically open them if necessary. 200990792Sgshapiro */ 201090792Sgshapiro 201190792Sgshapiro closemaps(false); 201290792Sgshapiro 201390792Sgshapiro pid = fork(); 201490792Sgshapiro if (pid < 0) 201590792Sgshapiro { 201690792Sgshapiro syserr("run_work_group: cannot fork"); 201790792Sgshapiro return 0; 201890792Sgshapiro } 201990792Sgshapiro else if (pid > 0) 202090792Sgshapiro { 202190792Sgshapiro /* parent -- clean out connection cache */ 202290792Sgshapiro mci_flush(false, NULL); 202390792Sgshapiro WorkQ = WorkQ->w_next; /* for the skip */ 202490792Sgshapiro sequenceno++; 202590792Sgshapiro proc_list_add(pid, "Queue child runner process", 202690792Sgshapiro PROC_QUEUE_CHILD, 0, -1); 202790792Sgshapiro 202890792Sgshapiro /* No additional work, no additional runners */ 202990792Sgshapiro if (WorkQ == NULL) 203090792Sgshapiro break; 203190792Sgshapiro } 203290792Sgshapiro else 203390792Sgshapiro { 203490792Sgshapiro /* child -- Reset global flags */ 203590792Sgshapiro RestartRequest = NULL; 203690792Sgshapiro RestartWorkGroup = false; 203790792Sgshapiro ShutdownRequest = NULL; 203890792Sgshapiro PendingSignal = 0; 203990792Sgshapiro CurrentPid = getpid(); 204090792Sgshapiro 204190792Sgshapiro /* 204290792Sgshapiro ** Initialize exception stack and default 204390792Sgshapiro ** exception handler for child process. 204490792Sgshapiro ** When fork()'d the child now has a private 204590792Sgshapiro ** copy of WorkQ at its current position. 204690792Sgshapiro */ 204790792Sgshapiro 204890792Sgshapiro sm_exc_newthread(fatal_error); 204990792Sgshapiro 205090792Sgshapiro /* 205190792Sgshapiro ** SMTP processes (whether -bd or -bs) set 205290792Sgshapiro ** SIGCHLD to reapchild to collect 205390792Sgshapiro ** children status. However, at delivery 205490792Sgshapiro ** time, that status must be collected 205590792Sgshapiro ** by sm_wait() to be dealt with properly 205690792Sgshapiro ** (check success of delivery based 205790792Sgshapiro ** on status code, etc). Therefore, if we 205890792Sgshapiro ** are an SMTP process, reset SIGCHLD 205990792Sgshapiro ** back to the default so reapchild 206090792Sgshapiro ** doesn't collect status before 206190792Sgshapiro ** sm_wait(). 206290792Sgshapiro */ 206390792Sgshapiro 206490792Sgshapiro if (OpMode == MD_SMTP || 206590792Sgshapiro OpMode == MD_DAEMON || 206690792Sgshapiro MaxQueueChildren > 0) 206790792Sgshapiro { 206890792Sgshapiro proc_list_clear(); 206990792Sgshapiro sm_releasesignal(SIGCHLD); 207090792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 207190792Sgshapiro } 207290792Sgshapiro 207390792Sgshapiro /* child -- error messages to the transcript */ 207490792Sgshapiro QuickAbort = OnlyOneError = false; 207590792Sgshapiro runner_work(e, sequenceno, true, 207690792Sgshapiro maxrunners, njobs); 207790792Sgshapiro 207890792Sgshapiro /* This child is done */ 207990792Sgshapiro finis(true, true, ExitStat); 208090792Sgshapiro /* NOTREACHED */ 208190792Sgshapiro } 208238032Speter } 208390792Sgshapiro 208490792Sgshapiro sm_releasesignal(SIGCHLD); 208590792Sgshapiro 208690792Sgshapiro /* 208790792Sgshapiro ** Wait until all of the runners have completed before 208890792Sgshapiro ** seeing if there is another queue group in the 208990792Sgshapiro ** work group to process. 209090792Sgshapiro ** XXX Future enhancement: don't wait() for all children 209190792Sgshapiro ** here, just go ahead and make sure that overall the number 209290792Sgshapiro ** of children is not exceeded. 209390792Sgshapiro */ 209490792Sgshapiro 209590792Sgshapiro while (CurChildren > 0) 209638032Speter { 209790792Sgshapiro int status; 209890792Sgshapiro pid_t ret; 209938032Speter 210090792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 210190792Sgshapiro continue; 210290792Sgshapiro proc_list_drop(ret, status, NULL); 210338032Speter } 210490792Sgshapiro } 210590792Sgshapiro else 210690792Sgshapiro { 210790792Sgshapiro /* 210890792Sgshapiro ** When current process will not fork children to do the work, 210990792Sgshapiro ** it will do the work itself. The 'skip' will be 1 since 211090792Sgshapiro ** there are no child runners to divide the work across. 211190792Sgshapiro */ 211290792Sgshapiro 211390792Sgshapiro runner_work(e, sequenceno, false, 1, njobs); 211490792Sgshapiro } 211590792Sgshapiro 211690792Sgshapiro /* free memory allocated by newenvelope() above */ 211790792Sgshapiro sm_rpool_free(rpool); 211890792Sgshapiro QueueEnvelope.e_rpool = NULL; 211990792Sgshapiro 212090792Sgshapiro /* Are there still more queues in the work group to process? */ 212190792Sgshapiro if (endgrp != WorkGrp[wgrp].wg_curqgrp) 212290792Sgshapiro { 212390792Sgshapiro rpool = sm_rpool_new_x(NULL); 212490792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 212590792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 212690792Sgshapiro goto domorework; 212790792Sgshapiro } 212890792Sgshapiro 212990792Sgshapiro /* No more queues in work group to process. Now check persistent. */ 213090792Sgshapiro if (persistent) 213190792Sgshapiro { 213290792Sgshapiro sequenceno = 1; 213390792Sgshapiro sm_setproctitle(true, CurEnv, "running queue: %s", 213490792Sgshapiro qid_printqueue(qgrp, qdir)); 213590792Sgshapiro 213690792Sgshapiro /* 213790792Sgshapiro ** close bogus maps, i.e., maps which caused a tempfail, 213890792Sgshapiro ** so we get fresh map connections on the next lookup. 213990792Sgshapiro ** closemaps() is also called when children are started. 214090792Sgshapiro */ 214190792Sgshapiro 214290792Sgshapiro closemaps(true); 214390792Sgshapiro 214490792Sgshapiro /* Close any cached connections. */ 214590792Sgshapiro mci_flush(true, NULL); 214690792Sgshapiro 214790792Sgshapiro /* Clean out expired related entries. */ 214890792Sgshapiro rmexpstab(); 214990792Sgshapiro 215090792Sgshapiro#if NAMED_BIND 215190792Sgshapiro /* Update MX records for FallBackMX. */ 215290792Sgshapiro if (FallBackMX != NULL) 215390792Sgshapiro (void) getfallbackmxrr(FallBackMX); 215490792Sgshapiro#endif /* NAMED_BIND */ 215590792Sgshapiro 215690792Sgshapiro#if USERDB 215790792Sgshapiro /* close UserDatabase */ 215890792Sgshapiro _udbx_close(); 215990792Sgshapiro#endif /* USERDB */ 216090792Sgshapiro 216190792Sgshapiro#if SM_HEAP_CHECK 216290792Sgshapiro if (sm_debug_active(&SmHeapCheck, 2) 216390792Sgshapiro && access("memdump", F_OK) == 0 216490792Sgshapiro ) 216538032Speter { 216690792Sgshapiro SM_FILE_T *out; 216790792Sgshapiro 216890792Sgshapiro remove("memdump"); 216990792Sgshapiro out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, 217090792Sgshapiro "memdump.out", SM_IO_APPEND, NULL); 217190792Sgshapiro if (out != NULL) 217238032Speter { 217390792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n"); 217490792Sgshapiro sm_heap_report(out, 217590792Sgshapiro sm_debug_level(&SmHeapCheck) - 1); 217690792Sgshapiro (void) sm_io_close(out, SM_TIME_DEFAULT); 217738032Speter } 217838032Speter } 217990792Sgshapiro#endif /* SM_HEAP_CHECK */ 218090792Sgshapiro 218190792Sgshapiro /* let me rest for a second to catch my breath */ 218290792Sgshapiro if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME) 218390792Sgshapiro sleep(MIN_SLEEP_TIME); 218490792Sgshapiro else if (WorkGrp[wgrp].wg_lowqintvl <= 0) 218590792Sgshapiro sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME); 218638032Speter else 218790792Sgshapiro sleep(WorkGrp[wgrp].wg_lowqintvl); 218838032Speter 218990792Sgshapiro /* 219090792Sgshapiro ** Get the LA outside the WorkQ loop if necessary. 219190792Sgshapiro ** In a persistent queue runner the code is repeated over 219290792Sgshapiro ** and over but gatherq() may ignore entries due to 219390792Sgshapiro ** shouldqueue() (do we really have to do this twice?). 219490792Sgshapiro ** Hence the queue runners would just idle around when once 219590792Sgshapiro ** CurrentLA caused all entries in a queue to be ignored. 219690792Sgshapiro */ 219764562Sgshapiro 219890792Sgshapiro now = curtime(); 219990792Sgshapiro if (njobs == 0 && current_la_time < now - GET_NEW_LA_TIME) 220090792Sgshapiro { 220190792Sgshapiro sm_getla(); 220290792Sgshapiro current_la_time = now; 220338032Speter } 220490792Sgshapiro rpool = sm_rpool_new_x(NULL); 220590792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 220690792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 220790792Sgshapiro goto domorework; 220838032Speter } 220938032Speter 221038032Speter /* exit without the usual cleanup */ 221138032Speter e->e_id = NULL; 221264562Sgshapiro if (forkflag) 221390792Sgshapiro finis(true, true, ExitStat); 221464562Sgshapiro /* NOTREACHED */ 221590792Sgshapiro return true; 221638032Speter} 221738032Speter 221838032Speter/* 221990792Sgshapiro** DOQUEUERUN -- do a queue run? 222090792Sgshapiro*/ 222190792Sgshapiro 222290792Sgshapirobool 222390792Sgshapirodoqueuerun() 222490792Sgshapiro{ 222594334Sgshapiro return DoQueueRun; 222690792Sgshapiro} 222790792Sgshapiro 222890792Sgshapiro/* 222994334Sgshapiro** RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done. 223077349Sgshapiro** 223177349Sgshapiro** Parameters: 223294334Sgshapiro** none. 223377349Sgshapiro** 223477349Sgshapiro** Returns: 223577349Sgshapiro** none. 223677349Sgshapiro** 223790792Sgshapiro** Side Effects: 223890792Sgshapiro** The invocation of this function via an alarm may interrupt 223990792Sgshapiro** a set of actions. Thus errno may be set in that context. 224090792Sgshapiro** We need to restore errno at the end of this function to ensure 224190792Sgshapiro** that any work done here that sets errno doesn't return a 224290792Sgshapiro** misleading/false errno value. Errno may be EINTR upon entry to 224390792Sgshapiro** this function because of non-restartable/continuable system 224490792Sgshapiro** API was active. Iff this is true we will override errno as 224590792Sgshapiro** a timeout (as a more accurate error message). 224690792Sgshapiro** 224777349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 224877349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 224977349Sgshapiro** DOING. 225038032Speter*/ 225138032Speter 225290792Sgshapirovoid 225394334Sgshapirorunqueueevent() 225438032Speter{ 225590792Sgshapiro int save_errno = errno; 225690792Sgshapiro 225790792Sgshapiro /* 225890792Sgshapiro ** Set the general bit that we want a queue run, 225990792Sgshapiro ** tested in doqueuerun() 226090792Sgshapiro */ 226190792Sgshapiro 226294334Sgshapiro DoQueueRun = true; 226394334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 226494334Sgshapiro if (tTd(69, 10)) 226594334Sgshapiro sm_syslog(LOG_INFO, NOQID, "rqe: done"); 226694334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 226790792Sgshapiro 226890792Sgshapiro errno = save_errno; 226990792Sgshapiro if (errno == EINTR) 227090792Sgshapiro errno = ETIMEDOUT; 227138032Speter} 227290792Sgshapiro/* 227390792Sgshapiro** GATHERQ -- gather messages from the message queue(s) the work queue. 227438032Speter** 227538032Speter** Parameters: 227690792Sgshapiro** qgrp -- the index of the queue group. 227790792Sgshapiro** qdir -- the index of the queue directory. 227838032Speter** doall -- if set, include everything in the queue (even 227938032Speter** the jobs that cannot be run because the load 228090792Sgshapiro** average is too high, or MaxQueueRun is reached). 228190792Sgshapiro** Otherwise, exclude those jobs. 228290792Sgshapiro** full -- (optional) to be set 'true' if WorkList is full 228390792Sgshapiro** more -- (optional) to be set 'true' if there are still more 228490792Sgshapiro** messages in this queue not added to WorkList 228538032Speter** 228638032Speter** Returns: 228738032Speter** The number of request in the queue (not necessarily 228890792Sgshapiro** the number of requests in WorkList however). 228938032Speter** 229038032Speter** Side Effects: 229190792Sgshapiro** prepares available work into WorkList 229238032Speter*/ 229338032Speter 229490792Sgshapiro#define NEED_P 0001 /* 'P': priority */ 229590792Sgshapiro#define NEED_T 0002 /* 'T': time */ 229690792Sgshapiro#define NEED_R 0004 /* 'R': recipient */ 229790792Sgshapiro#define NEED_S 0010 /* 'S': sender */ 229890792Sgshapiro#define NEED_H 0020 /* host */ 229990792Sgshapiro#if _FFR_QUARANTINE 230090792Sgshapiro# define HAS_QUARANTINE 0040 /* has an unexpected 'q' line */ 230190792Sgshapiro# define NEED_QUARANTINE 0100 /* 'q': reason */ 230290792Sgshapiro#endif /* _FFR_QUARANTINE */ 230338032Speter 230490792Sgshapirostatic WORK *WorkList = NULL; /* list of unsort work */ 230590792Sgshapirostatic int WorkListSize = 0; /* current max size of WorkList */ 230690792Sgshapirostatic int WorkListCount = 0; /* # of work items in WorkList */ 230738032Speter 230864562Sgshapirostatic int 230990792Sgshapirogatherq(qgrp, qdir, doall, full, more) 231090792Sgshapiro int qgrp; 231190792Sgshapiro int qdir; 231238032Speter bool doall; 231390792Sgshapiro bool *full; 231490792Sgshapiro bool *more; 231538032Speter{ 231638032Speter register struct dirent *d; 231738032Speter register WORK *w; 231838032Speter register char *p; 231938032Speter DIR *f; 232090792Sgshapiro int i, num_ent; 232190792Sgshapiro int wn; 232238032Speter QUEUE_CHAR *check; 232364562Sgshapiro char qd[MAXPATHLEN]; 232464562Sgshapiro char qf[MAXPATHLEN]; 232564562Sgshapiro 232690792Sgshapiro wn = WorkListCount - 1; 232790792Sgshapiro num_ent = 0; 232890792Sgshapiro if (qdir == NOQDIR) 232990792Sgshapiro (void) sm_strlcpy(qd, ".", sizeof qd); 233064562Sgshapiro else 233190792Sgshapiro (void) sm_strlcpyn(qd, sizeof qd, 2, 233290792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 233390792Sgshapiro (bitset(QP_SUBQF, 233490792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 233590792Sgshapiro ? "/qf" : "")); 233664562Sgshapiro 233738032Speter if (tTd(41, 1)) 233838032Speter { 233990792Sgshapiro sm_dprintf("gatherq:\n"); 234038032Speter 234138032Speter check = QueueLimitId; 234238032Speter while (check != NULL) 234338032Speter { 234490792Sgshapiro sm_dprintf("\tQueueLimitId = %s%s\n", 234590792Sgshapiro check->queue_negate ? "!" : "", 234664562Sgshapiro check->queue_match); 234738032Speter check = check->queue_next; 234838032Speter } 234938032Speter 235038032Speter check = QueueLimitSender; 235138032Speter while (check != NULL) 235238032Speter { 235390792Sgshapiro sm_dprintf("\tQueueLimitSender = %s%s\n", 235490792Sgshapiro check->queue_negate ? "!" : "", 235564562Sgshapiro check->queue_match); 235638032Speter check = check->queue_next; 235738032Speter } 235838032Speter 235938032Speter check = QueueLimitRecipient; 236038032Speter while (check != NULL) 236138032Speter { 236290792Sgshapiro sm_dprintf("\tQueueLimitRecipient = %s%s\n", 236390792Sgshapiro check->queue_negate ? "!" : "", 236464562Sgshapiro check->queue_match); 236538032Speter check = check->queue_next; 236638032Speter } 236738032Speter 236890792Sgshapiro#if _FFR_QUARANTINE 236990792Sgshapiro if (QueueMode == QM_QUARANTINE) 237090792Sgshapiro { 237190792Sgshapiro check = QueueLimitQuarantine; 237290792Sgshapiro while (check != NULL) 237390792Sgshapiro { 237490792Sgshapiro sm_dprintf("\tQueueLimitQuarantine = %s%s\n", 237590792Sgshapiro check->queue_negate ? "!" : "", 237690792Sgshapiro check->queue_match); 237790792Sgshapiro check = check->queue_next; 237890792Sgshapiro } 237990792Sgshapiro } 238090792Sgshapiro#endif /* _FFR_QUARANTINE */ 238138032Speter } 238238032Speter 238338032Speter /* open the queue directory */ 238464562Sgshapiro f = opendir(qd); 238538032Speter if (f == NULL) 238638032Speter { 238790792Sgshapiro syserr("gatherq: cannot open \"%s\"", 238890792Sgshapiro qid_printqueue(qgrp, qdir)); 238990792Sgshapiro if (full != NULL) 239090792Sgshapiro *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0; 239190792Sgshapiro if (more != NULL) 239290792Sgshapiro *more = false; 239364562Sgshapiro return 0; 239438032Speter } 239538032Speter 239638032Speter /* 239738032Speter ** Read the work directory. 239838032Speter */ 239938032Speter 240038032Speter while ((d = readdir(f)) != NULL) 240138032Speter { 240290792Sgshapiro SM_FILE_T *cf; 240338032Speter int qfver = 0; 240438032Speter char lbuf[MAXNAME + 1]; 240564562Sgshapiro struct stat sbuf; 240638032Speter 240738032Speter if (tTd(41, 50)) 240890792Sgshapiro sm_dprintf("gatherq: checking %s..", d->d_name); 240938032Speter 241038032Speter /* is this an interesting entry? */ 241190792Sgshapiro#if _FFR_QUARANTINE 241290792Sgshapiro if (!(((QueueMode == QM_NORMAL && 241390792Sgshapiro d->d_name[0] == NORMQF_LETTER) || 241490792Sgshapiro (QueueMode == QM_QUARANTINE && 241590792Sgshapiro d->d_name[0] == QUARQF_LETTER) || 241690792Sgshapiro (QueueMode == QM_LOST && 241790792Sgshapiro d->d_name[0] == LOSEQF_LETTER)) && 241890792Sgshapiro d->d_name[1] == 'f')) 241990792Sgshapiro#else /* _FFR_QUARANTINE */ 242090792Sgshapiro if (d->d_name[0] != NORMQF_LETTER || d->d_name[1] != 'f') 242190792Sgshapiro#endif /* _FFR_QUARANTINE */ 242290792Sgshapiro { 242390792Sgshapiro if (tTd(41, 50)) 242490792Sgshapiro sm_dprintf(" skipping\n"); 242538032Speter continue; 242690792Sgshapiro } 242790792Sgshapiro if (tTd(41, 50)) 242890792Sgshapiro sm_dprintf("\n"); 242938032Speter 243064562Sgshapiro if (strlen(d->d_name) >= MAXQFNAME) 243142575Speter { 243242575Speter if (Verbose) 243390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 243490792Sgshapiro "gatherq: %s too long, %d max characters\n", 243590792Sgshapiro d->d_name, MAXQFNAME); 243642575Speter if (LogLevel > 0) 243742575Speter sm_syslog(LOG_ALERT, NOQID, 243890792Sgshapiro "gatherq: %s too long, %d max characters", 243964562Sgshapiro d->d_name, MAXQFNAME); 244038032Speter continue; 244142575Speter } 244238032Speter 244338032Speter check = QueueLimitId; 244438032Speter while (check != NULL) 244538032Speter { 244694334Sgshapiro if (strcontainedin(false, check->queue_match, 244790792Sgshapiro d->d_name) != check->queue_negate) 244838032Speter break; 244938032Speter else 245038032Speter check = check->queue_next; 245138032Speter } 245238032Speter if (QueueLimitId != NULL && check == NULL) 245338032Speter continue; 245438032Speter 245564562Sgshapiro /* grow work list if necessary */ 245638032Speter if (++wn >= MaxQueueRun && MaxQueueRun > 0) 245738032Speter { 245838032Speter if (wn == MaxQueueRun && LogLevel > 0) 245964562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 246064562Sgshapiro "WorkList for %s maxed out at %d", 246190792Sgshapiro qid_printqueue(qgrp, qdir), 246264562Sgshapiro MaxQueueRun); 246390792Sgshapiro if (doall) 246490792Sgshapiro continue; /* just count entries */ 246590792Sgshapiro break; 246638032Speter } 246738032Speter if (wn >= WorkListSize) 246838032Speter { 246990792Sgshapiro grow_wlist(qgrp, qdir); 247038032Speter if (wn >= WorkListSize) 247138032Speter continue; 247238032Speter } 247390792Sgshapiro SM_ASSERT(wn >= 0); 247464562Sgshapiro w = &WorkList[wn]; 247538032Speter 247690792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", d->d_name); 247764562Sgshapiro if (stat(qf, &sbuf) < 0) 247864562Sgshapiro { 247964562Sgshapiro if (errno != ENOENT) 248064562Sgshapiro sm_syslog(LOG_INFO, NOQID, 248190792Sgshapiro "gatherq: can't stat %s/%s", 248290792Sgshapiro qid_printqueue(qgrp, qdir), 248390792Sgshapiro d->d_name); 248464562Sgshapiro wn--; 248564562Sgshapiro continue; 248664562Sgshapiro } 248764562Sgshapiro if (!bitset(S_IFREG, sbuf.st_mode)) 248864562Sgshapiro { 248964562Sgshapiro /* Yikes! Skip it or we will hang on open! */ 249090792Sgshapiro if (!((d->d_name[0] == DATAFL_LETTER || 249190792Sgshapiro d->d_name[0] == NORMQF_LETTER || 249290792Sgshapiro#if _FFR_QUARANTINE 249390792Sgshapiro d->d_name[0] == QUARQF_LETTER || 249490792Sgshapiro d->d_name[0] == LOSEQF_LETTER || 249590792Sgshapiro#endif /* _FFR_QUARANTINE */ 249690792Sgshapiro d->d_name[0] == XSCRPT_LETTER) && 249790792Sgshapiro d->d_name[1] == 'f' && d->d_name[2] == '\0')) 249890792Sgshapiro syserr("gatherq: %s/%s is not a regular file", 249990792Sgshapiro qid_printqueue(qgrp, qdir), d->d_name); 250064562Sgshapiro wn--; 250164562Sgshapiro continue; 250264562Sgshapiro } 250364562Sgshapiro 250464562Sgshapiro /* avoid work if possible */ 250590792Sgshapiro if ((QueueSortOrder == QSO_BYFILENAME || 250690792Sgshapiro QueueSortOrder == QSO_BYMODTIME || 250790792Sgshapiro QueueSortOrder == QSO_RANDOM) && 250890792Sgshapiro#if _FFR_QUARANTINE 250990792Sgshapiro QueueLimitQuarantine == NULL && 251090792Sgshapiro#endif /* _FFR_QUARANTINE */ 251166494Sgshapiro QueueLimitSender == NULL && 251266494Sgshapiro QueueLimitRecipient == NULL) 251364562Sgshapiro { 251490792Sgshapiro w->w_qgrp = qgrp; 251590792Sgshapiro w->w_qdir = qdir; 251664562Sgshapiro w->w_name = newstr(d->d_name); 251764562Sgshapiro w->w_host = NULL; 251890792Sgshapiro w->w_lock = w->w_tooyoung = false; 251964562Sgshapiro w->w_pri = 0; 252064562Sgshapiro w->w_ctime = 0; 252190792Sgshapiro w->w_mtime = sbuf.st_mtime; 252290792Sgshapiro ++num_ent; 252364562Sgshapiro continue; 252464562Sgshapiro } 252564562Sgshapiro 252664562Sgshapiro /* open control file */ 252790792Sgshapiro cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY, 252890792Sgshapiro NULL); 252990792Sgshapiro if (cf == NULL && OpMode != MD_PRINT) 253038032Speter { 253138032Speter /* this may be some random person sending hir msgs */ 253238032Speter if (tTd(41, 2)) 253390792Sgshapiro sm_dprintf("gatherq: cannot open %s: %s\n", 253490792Sgshapiro d->d_name, sm_errstring(errno)); 253538032Speter errno = 0; 253638032Speter wn--; 253738032Speter continue; 253838032Speter } 253990792Sgshapiro w->w_qgrp = qgrp; 254090792Sgshapiro w->w_qdir = qdir; 254138032Speter w->w_name = newstr(d->d_name); 254238032Speter w->w_host = NULL; 254390792Sgshapiro if (cf != NULL) 254490792Sgshapiro { 254590792Sgshapiro w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD, 254690792Sgshapiro NULL), 254790792Sgshapiro w->w_name, NULL, 254890792Sgshapiro LOCK_SH|LOCK_NB); 254990792Sgshapiro } 255090792Sgshapiro w->w_tooyoung = false; 255138032Speter 255238032Speter /* make sure jobs in creation don't clog queue */ 255338032Speter w->w_pri = 0x7fffffff; 255438032Speter w->w_ctime = 0; 255590792Sgshapiro w->w_mtime = sbuf.st_mtime; 255638032Speter 255738032Speter /* extract useful information */ 255890792Sgshapiro i = NEED_P|NEED_T; 255990792Sgshapiro if (QueueSortOrder == QSO_BYHOST 256090792Sgshapiro#if _FFR_RHS 256190792Sgshapiro || QueueSortOrder == QSO_BYSHUFFLE 256290792Sgshapiro#endif /* _FFR_RHS */ 256390792Sgshapiro ) 256471345Sgshapiro { 256571345Sgshapiro /* need w_host set for host sort order */ 256671345Sgshapiro i |= NEED_H; 256771345Sgshapiro } 256838032Speter if (QueueLimitSender != NULL) 256938032Speter i |= NEED_S; 257064562Sgshapiro if (QueueLimitRecipient != NULL) 257138032Speter i |= NEED_R; 257290792Sgshapiro#if _FFR_QUARANTINE 257390792Sgshapiro if (QueueLimitQuarantine != NULL) 257490792Sgshapiro i |= NEED_QUARANTINE; 257590792Sgshapiro#endif /* _FFR_QUARANTINE */ 257690792Sgshapiro while (cf != NULL && i != 0 && 257790792Sgshapiro sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf, 257890792Sgshapiro sizeof lbuf) != NULL) 257938032Speter { 258038032Speter int c; 258138032Speter time_t age; 258238032Speter 258338032Speter p = strchr(lbuf, '\n'); 258438032Speter if (p != NULL) 258538032Speter *p = '\0'; 258638032Speter else 258738032Speter { 258838032Speter /* flush rest of overly long line */ 258990792Sgshapiro while ((c = sm_io_getc(cf, SM_TIME_DEFAULT)) 259090792Sgshapiro != SM_IO_EOF && c != '\n') 259138032Speter continue; 259238032Speter } 259338032Speter 259438032Speter switch (lbuf[0]) 259538032Speter { 259638032Speter case 'V': 259738032Speter qfver = atoi(&lbuf[1]); 259838032Speter break; 259938032Speter 260038032Speter case 'P': 260138032Speter w->w_pri = atol(&lbuf[1]); 260238032Speter i &= ~NEED_P; 260338032Speter break; 260438032Speter 260538032Speter case 'T': 260638032Speter w->w_ctime = atol(&lbuf[1]); 260738032Speter i &= ~NEED_T; 260838032Speter break; 260938032Speter 261090792Sgshapiro#if _FFR_QUARANTINE 261190792Sgshapiro case 'q': 261290792Sgshapiro if (QueueMode != QM_QUARANTINE && 261390792Sgshapiro QueueMode != QM_LOST) 261490792Sgshapiro { 261590792Sgshapiro if (tTd(41, 49)) 261690792Sgshapiro sm_dprintf("%s not marked as quarantined but has a 'q' line\n", 261790792Sgshapiro w->w_name); 261890792Sgshapiro i |= HAS_QUARANTINE; 261990792Sgshapiro } 262090792Sgshapiro else if (QueueMode == QM_QUARANTINE) 262190792Sgshapiro { 262290792Sgshapiro if (QueueLimitQuarantine == NULL) 262390792Sgshapiro { 262490792Sgshapiro i &= ~NEED_QUARANTINE; 262590792Sgshapiro break; 262690792Sgshapiro } 262790792Sgshapiro p = &lbuf[1]; 262890792Sgshapiro check = QueueLimitQuarantine; 262990792Sgshapiro while (check != NULL) 263090792Sgshapiro { 263190792Sgshapiro if (strcontainedin(false, 263290792Sgshapiro check->queue_match, 263390792Sgshapiro p) != 263490792Sgshapiro check->queue_negate) 263590792Sgshapiro break; 263690792Sgshapiro else 263790792Sgshapiro check = check->queue_next; 263890792Sgshapiro } 263990792Sgshapiro if (check != NULL) 264090792Sgshapiro i &= ~NEED_QUARANTINE; 264190792Sgshapiro } 264290792Sgshapiro break; 264390792Sgshapiro#endif /* _FFR_QUARANTINE */ 264490792Sgshapiro 264538032Speter case 'R': 264638032Speter if (w->w_host == NULL && 264738032Speter (p = strrchr(&lbuf[1], '@')) != NULL) 264864562Sgshapiro { 264990792Sgshapiro#if _FFR_RHS 265090792Sgshapiro if (QueueSortOrder == QSO_BYSHUFFLE) 265190792Sgshapiro w->w_host = newstr(&p[1]); 265290792Sgshapiro else 265390792Sgshapiro#endif /* _FFR_RHS */ 265490792Sgshapiro w->w_host = strrev(&p[1]); 265564562Sgshapiro makelower(w->w_host); 265671345Sgshapiro i &= ~NEED_H; 265764562Sgshapiro } 265838032Speter if (QueueLimitRecipient == NULL) 265938032Speter { 266038032Speter i &= ~NEED_R; 266138032Speter break; 266238032Speter } 266338032Speter if (qfver > 0) 266438032Speter { 266538032Speter p = strchr(&lbuf[1], ':'); 266638032Speter if (p == NULL) 266738032Speter p = &lbuf[1]; 266838032Speter } 266938032Speter else 267038032Speter p = &lbuf[1]; 267138032Speter check = QueueLimitRecipient; 267238032Speter while (check != NULL) 267338032Speter { 267490792Sgshapiro if (strcontainedin(true, 267590792Sgshapiro check->queue_match, 267690792Sgshapiro p) != 267790792Sgshapiro check->queue_negate) 267838032Speter break; 267938032Speter else 268038032Speter check = check->queue_next; 268138032Speter } 268238032Speter if (check != NULL) 268338032Speter i &= ~NEED_R; 268438032Speter break; 268538032Speter 268638032Speter case 'S': 268764562Sgshapiro check = QueueLimitSender; 268864562Sgshapiro while (check != NULL) 268964562Sgshapiro { 269090792Sgshapiro if (strcontainedin(true, 269190792Sgshapiro check->queue_match, 269290792Sgshapiro &lbuf[1]) != 269390792Sgshapiro check->queue_negate) 269464562Sgshapiro break; 269564562Sgshapiro else 269664562Sgshapiro check = check->queue_next; 269764562Sgshapiro } 269864562Sgshapiro if (check != NULL) 269964562Sgshapiro i &= ~NEED_S; 270038032Speter break; 270138032Speter 270238032Speter case 'K': 270338032Speter age = curtime() - (time_t) atol(&lbuf[1]); 270438032Speter if (age >= 0 && MinQueueAge > 0 && 270538032Speter age < MinQueueAge) 270690792Sgshapiro w->w_tooyoung = true; 270738032Speter break; 270838032Speter 270938032Speter case 'N': 271038032Speter if (atol(&lbuf[1]) == 0) 271190792Sgshapiro w->w_tooyoung = false; 271238032Speter break; 271364562Sgshapiro 271490792Sgshapiro#if _FFR_QUEUEDELAY 271564562Sgshapiro/* 271664562Sgshapiro case 'G': 271764562Sgshapiro queuealg = atoi(lbuf[1]); 271864562Sgshapiro break; 271964562Sgshapiro case 'Y': 272064562Sgshapiro queuedelay = (time_t) atol(&lbuf[1]); 272164562Sgshapiro break; 272264562Sgshapiro*/ 272390792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 272438032Speter } 272538032Speter } 272690792Sgshapiro if (cf != NULL) 272790792Sgshapiro (void) sm_io_close(cf, SM_TIME_DEFAULT); 272838032Speter 272938032Speter if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) || 273090792Sgshapiro#if _FFR_QUARANTINE 273190792Sgshapiro bitset(HAS_QUARANTINE, i) || 273290792Sgshapiro bitset(NEED_QUARANTINE, i) || 273390792Sgshapiro#endif /* _FFR_QUARANTINE */ 273438032Speter bitset(NEED_R|NEED_S, i)) 273538032Speter { 273638032Speter /* don't even bother sorting this job in */ 273738032Speter if (tTd(41, 49)) 273890792Sgshapiro sm_dprintf("skipping %s (%x)\n", w->w_name, i); 273990792Sgshapiro sm_free(w->w_name); /* XXX */ 274090792Sgshapiro if (w->w_host != NULL) 274190792Sgshapiro sm_free(w->w_host); /* XXX */ 274238032Speter wn--; 274338032Speter } 274490792Sgshapiro else 274590792Sgshapiro ++num_ent; 274638032Speter } 274738032Speter (void) closedir(f); 274838032Speter wn++; 274938032Speter 275090792Sgshapiro i = wn - WorkListCount; 275190792Sgshapiro WorkListCount += SM_MIN(num_ent, WorkListSize); 275290792Sgshapiro 275390792Sgshapiro if (more != NULL) 275490792Sgshapiro *more = WorkListCount < wn; 275590792Sgshapiro 275690792Sgshapiro if (full != NULL) 275790792Sgshapiro *full = (wn >= MaxQueueRun && MaxQueueRun > 0) || 275890792Sgshapiro (WorkList == NULL && wn > 0); 275990792Sgshapiro 276090792Sgshapiro return i; 276190792Sgshapiro} 276290792Sgshapiro/* 276390792Sgshapiro** SORTQ -- sort the work list 276490792Sgshapiro** 276590792Sgshapiro** First the old WorkQ is cleared away. Then the WorkList is sorted 276690792Sgshapiro** for all items so that important (higher sorting value) items are not 276790792Sgshapiro** trunctated off. Then the most important items are moved from 276890792Sgshapiro** WorkList to WorkQ. The lower count of 'max' or MaxListCount items 276990792Sgshapiro** are moved. 277090792Sgshapiro** 277190792Sgshapiro** Parameters: 277290792Sgshapiro** max -- maximum number of items to be placed in WorkQ 277390792Sgshapiro** 277490792Sgshapiro** Returns: 277590792Sgshapiro** the number of items in WorkQ 277690792Sgshapiro** 277790792Sgshapiro** Side Effects: 277890792Sgshapiro** WorkQ gets released and filled with new work. WorkList 277990792Sgshapiro** gets released. Work items get sorted in order. 278090792Sgshapiro*/ 278190792Sgshapiro 278290792Sgshapirostatic int 278390792Sgshapirosortq(max) 278490792Sgshapiro int max; 278590792Sgshapiro{ 278690792Sgshapiro register int i; /* local counter */ 278790792Sgshapiro register WORK *w; /* tmp item pointer */ 278890792Sgshapiro int wc = WorkListCount; /* trim size for WorkQ */ 278990792Sgshapiro 279090792Sgshapiro if (WorkQ != NULL) 279190792Sgshapiro { 279290792Sgshapiro /* Clear out old WorkQ. */ 279390792Sgshapiro for (w = WorkQ; w != NULL; ) 279490792Sgshapiro { 279590792Sgshapiro register WORK *nw = w->w_next; 279690792Sgshapiro 279790792Sgshapiro WorkQ = nw; 279890792Sgshapiro sm_free(w->w_name); /* XXX */ 279990792Sgshapiro if (w->w_host != NULL) 280090792Sgshapiro sm_free(w->w_host); /* XXX */ 280190792Sgshapiro sm_free((char *) w); /* XXX */ 280290792Sgshapiro w = nw; 280390792Sgshapiro } 280490792Sgshapiro sm_free((char *) WorkQ); 280590792Sgshapiro WorkQ = NULL; 280690792Sgshapiro } 280790792Sgshapiro 280890792Sgshapiro if (WorkList == NULL || wc <= 0) 280964562Sgshapiro return 0; 281038032Speter 281190792Sgshapiro /* Check if the per queue group item limit will be exceeded */ 281290792Sgshapiro if (wc > max && max > 0) 281390792Sgshapiro wc = max; 281490792Sgshapiro 281590792Sgshapiro /* 281690792Sgshapiro ** The sort now takes place using all of the items in WorkList. 281790792Sgshapiro ** The list gets trimmed to the most important items after the sort. 281890792Sgshapiro ** If the trim were to happen before the sort then one or more 281990792Sgshapiro ** important items might get truncated off -- not what we want. 282090792Sgshapiro */ 282190792Sgshapiro 282264562Sgshapiro if (QueueSortOrder == QSO_BYHOST) 282338032Speter { 282438032Speter /* 282538032Speter ** Sort the work directory for the first time, 282638032Speter ** based on host name, lock status, and priority. 282738032Speter */ 282838032Speter 282938032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1); 283038032Speter 283138032Speter /* 283238032Speter ** If one message to host is locked, "lock" all messages 283338032Speter ** to that host. 283438032Speter */ 283538032Speter 283638032Speter i = 0; 283738032Speter while (i < wc) 283838032Speter { 283938032Speter if (!WorkList[i].w_lock) 284038032Speter { 284138032Speter i++; 284238032Speter continue; 284338032Speter } 284438032Speter w = &WorkList[i]; 284538032Speter while (++i < wc) 284638032Speter { 284738032Speter if (WorkList[i].w_host == NULL && 284838032Speter w->w_host == NULL) 284990792Sgshapiro WorkList[i].w_lock = true; 285038032Speter else if (WorkList[i].w_host != NULL && 285138032Speter w->w_host != NULL && 285290792Sgshapiro sm_strcasecmp(WorkList[i].w_host, 285390792Sgshapiro w->w_host) == 0) 285490792Sgshapiro WorkList[i].w_lock = true; 285538032Speter else 285638032Speter break; 285738032Speter } 285838032Speter } 285938032Speter 286038032Speter /* 286138032Speter ** Sort the work directory for the second time, 286238032Speter ** based on lock status, host name, and priority. 286338032Speter */ 286438032Speter 286538032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2); 286638032Speter } 286764562Sgshapiro else if (QueueSortOrder == QSO_BYTIME) 286838032Speter { 286938032Speter /* 287038032Speter ** Simple sort based on submission time only. 287138032Speter */ 287238032Speter 287338032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3); 287438032Speter } 287564562Sgshapiro else if (QueueSortOrder == QSO_BYFILENAME) 287664562Sgshapiro { 287764562Sgshapiro /* 287890792Sgshapiro ** Sort based on queue filename. 287964562Sgshapiro */ 288064562Sgshapiro 288164562Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4); 288264562Sgshapiro } 288390792Sgshapiro else if (QueueSortOrder == QSO_RANDOM) 288490792Sgshapiro { 288590792Sgshapiro /* 288690792Sgshapiro ** Sort randomly. 288790792Sgshapiro ** workcmpf5() returns a random 1 or -1. 288890792Sgshapiro ** As long as nobody does a verification pass over the 288990792Sgshapiro ** sorted list, we should be golden. 289090792Sgshapiro */ 289190792Sgshapiro 289290792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf5); 289390792Sgshapiro } 289490792Sgshapiro else if (QueueSortOrder == QSO_BYMODTIME) 289590792Sgshapiro { 289690792Sgshapiro /* 289790792Sgshapiro ** Simple sort based on modification time of queue file. 289890792Sgshapiro ** This puts the oldest items first. 289990792Sgshapiro */ 290090792Sgshapiro 290190792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf6); 290290792Sgshapiro } 290390792Sgshapiro#if _FFR_RHS 290490792Sgshapiro else if (QueueSortOrder == QSO_BYSHUFFLE) 290590792Sgshapiro { 290690792Sgshapiro /* 290790792Sgshapiro ** Simple sort based on shuffled host name. 290890792Sgshapiro */ 290990792Sgshapiro 291090792Sgshapiro init_shuffle_alphabet(); 291190792Sgshapiro qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf7); 291290792Sgshapiro } 291390792Sgshapiro#endif /* _FFR_RHS */ 291438032Speter else 291538032Speter { 291638032Speter /* 291738032Speter ** Simple sort based on queue priority only. 291838032Speter */ 291938032Speter 292038032Speter qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0); 292138032Speter } 292238032Speter 292338032Speter /* 292438032Speter ** Convert the work list into canonical form. 292538032Speter ** Should be turning it into a list of envelopes here perhaps. 292690792Sgshapiro ** Only take the most important items up to the per queue group 292790792Sgshapiro ** maximum. 292838032Speter */ 292938032Speter 293038032Speter for (i = wc; --i >= 0; ) 293138032Speter { 293238032Speter w = (WORK *) xalloc(sizeof *w); 293390792Sgshapiro w->w_qgrp = WorkList[i].w_qgrp; 293490792Sgshapiro w->w_qdir = WorkList[i].w_qdir; 293538032Speter w->w_name = WorkList[i].w_name; 293638032Speter w->w_host = WorkList[i].w_host; 293738032Speter w->w_lock = WorkList[i].w_lock; 293838032Speter w->w_tooyoung = WorkList[i].w_tooyoung; 293938032Speter w->w_pri = WorkList[i].w_pri; 294038032Speter w->w_ctime = WorkList[i].w_ctime; 294190792Sgshapiro w->w_mtime = WorkList[i].w_mtime; 294238032Speter w->w_next = WorkQ; 294338032Speter WorkQ = w; 294438032Speter } 294538032Speter if (WorkList != NULL) 294690792Sgshapiro sm_free(WorkList); /* XXX */ 294738032Speter WorkList = NULL; 294838032Speter WorkListSize = 0; 294990792Sgshapiro WorkListCount = 0; 295038032Speter 295138032Speter if (tTd(40, 1)) 295238032Speter { 295338032Speter for (w = WorkQ; w != NULL; w = w->w_next) 295464562Sgshapiro { 295564562Sgshapiro if (w->w_host != NULL) 295690792Sgshapiro sm_dprintf("%22s: pri=%ld %s\n", 295764562Sgshapiro w->w_name, w->w_pri, w->w_host); 295864562Sgshapiro else 295990792Sgshapiro sm_dprintf("%32s: pri=%ld\n", 296064562Sgshapiro w->w_name, w->w_pri); 296164562Sgshapiro } 296238032Speter } 296338032Speter 296490792Sgshapiro return wc; /* return number of WorkQ items */ 296538032Speter} 296690792Sgshapiro/* 296738032Speter** GROW_WLIST -- make the work list larger 296838032Speter** 296938032Speter** Parameters: 297090792Sgshapiro** qgrp -- the index for the queue group. 297190792Sgshapiro** qdir -- the index for the queue directory. 297238032Speter** 297338032Speter** Returns: 297438032Speter** none. 297538032Speter** 297638032Speter** Side Effects: 297738032Speter** Adds another QUEUESEGSIZE entries to WorkList if possible. 297838032Speter** It can fail if there isn't enough memory, so WorkListSize 297938032Speter** should be checked again upon return. 298038032Speter*/ 298138032Speter 298264562Sgshapirostatic void 298390792Sgshapirogrow_wlist(qgrp, qdir) 298490792Sgshapiro int qgrp; 298590792Sgshapiro int qdir; 298638032Speter{ 298738032Speter if (tTd(41, 1)) 298890792Sgshapiro sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); 298938032Speter if (WorkList == NULL) 299038032Speter { 299164562Sgshapiro WorkList = (WORK *) xalloc((sizeof *WorkList) * 299264562Sgshapiro (QUEUESEGSIZE + 1)); 299338032Speter WorkListSize = QUEUESEGSIZE; 299438032Speter } 299538032Speter else 299638032Speter { 299738032Speter int newsize = WorkListSize + QUEUESEGSIZE; 299890792Sgshapiro WORK *newlist = (WORK *) sm_realloc((char *) WorkList, 299990792Sgshapiro (unsigned) sizeof(WORK) * (newsize + 1)); 300038032Speter 300138032Speter if (newlist != NULL) 300238032Speter { 300338032Speter WorkListSize = newsize; 300438032Speter WorkList = newlist; 300538032Speter if (LogLevel > 1) 300638032Speter { 300764562Sgshapiro sm_syslog(LOG_INFO, NOQID, 300864562Sgshapiro "grew WorkList for %s to %d", 300990792Sgshapiro qid_printqueue(qgrp, qdir), 301064562Sgshapiro WorkListSize); 301138032Speter } 301238032Speter } 301338032Speter else if (LogLevel > 0) 301438032Speter { 301538032Speter sm_syslog(LOG_ALERT, NOQID, 301664562Sgshapiro "FAILED to grow WorkList for %s to %d", 301790792Sgshapiro qid_printqueue(qgrp, qdir), newsize); 301838032Speter } 301938032Speter } 302038032Speter if (tTd(41, 1)) 302190792Sgshapiro sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); 302238032Speter} 302390792Sgshapiro/* 302438032Speter** WORKCMPF0 -- simple priority-only compare function. 302538032Speter** 302638032Speter** Parameters: 302738032Speter** a -- the first argument. 302838032Speter** b -- the second argument. 302938032Speter** 303038032Speter** Returns: 303138032Speter** -1 if a < b 303238032Speter** 0 if a == b 303338032Speter** +1 if a > b 303438032Speter** 303538032Speter*/ 303638032Speter 303764562Sgshapirostatic int 303838032Speterworkcmpf0(a, b) 303938032Speter register WORK *a; 304038032Speter register WORK *b; 304138032Speter{ 304238032Speter long pa = a->w_pri; 304338032Speter long pb = b->w_pri; 304438032Speter 304538032Speter if (pa == pb) 304638032Speter return 0; 304738032Speter else if (pa > pb) 304838032Speter return 1; 304938032Speter else 305038032Speter return -1; 305138032Speter} 305290792Sgshapiro/* 305338032Speter** WORKCMPF1 -- first compare function for ordering work based on host name. 305438032Speter** 305538032Speter** Sorts on host name, lock status, and priority in that order. 305638032Speter** 305738032Speter** Parameters: 305838032Speter** a -- the first argument. 305938032Speter** b -- the second argument. 306038032Speter** 306138032Speter** Returns: 306238032Speter** <0 if a < b 306338032Speter** 0 if a == b 306438032Speter** >0 if a > b 306538032Speter** 306638032Speter*/ 306738032Speter 306864562Sgshapirostatic int 306938032Speterworkcmpf1(a, b) 307038032Speter register WORK *a; 307138032Speter register WORK *b; 307238032Speter{ 307338032Speter int i; 307438032Speter 307538032Speter /* host name */ 307638032Speter if (a->w_host != NULL && b->w_host == NULL) 307738032Speter return 1; 307838032Speter else if (a->w_host == NULL && b->w_host != NULL) 307938032Speter return -1; 308038032Speter if (a->w_host != NULL && b->w_host != NULL && 308138032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 308238032Speter return i; 308338032Speter 308438032Speter /* lock status */ 308538032Speter if (a->w_lock != b->w_lock) 308638032Speter return b->w_lock - a->w_lock; 308738032Speter 308838032Speter /* job priority */ 308973188Sgshapiro return workcmpf0(a, b); 309038032Speter} 309190792Sgshapiro/* 309238032Speter** WORKCMPF2 -- second compare function for ordering work based on host name. 309338032Speter** 309438032Speter** Sorts on lock status, host name, and priority in that order. 309538032Speter** 309638032Speter** Parameters: 309738032Speter** a -- the first argument. 309838032Speter** b -- the second argument. 309938032Speter** 310038032Speter** Returns: 310138032Speter** <0 if a < b 310238032Speter** 0 if a == b 310338032Speter** >0 if a > b 310438032Speter** 310538032Speter*/ 310638032Speter 310764562Sgshapirostatic int 310838032Speterworkcmpf2(a, b) 310938032Speter register WORK *a; 311038032Speter register WORK *b; 311138032Speter{ 311238032Speter int i; 311338032Speter 311438032Speter /* lock status */ 311538032Speter if (a->w_lock != b->w_lock) 311638032Speter return a->w_lock - b->w_lock; 311738032Speter 311838032Speter /* host name */ 311938032Speter if (a->w_host != NULL && b->w_host == NULL) 312038032Speter return 1; 312138032Speter else if (a->w_host == NULL && b->w_host != NULL) 312238032Speter return -1; 312338032Speter if (a->w_host != NULL && b->w_host != NULL && 312438032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 312538032Speter return i; 312638032Speter 312738032Speter /* job priority */ 312873188Sgshapiro return workcmpf0(a, b); 312938032Speter} 313090792Sgshapiro/* 313138032Speter** WORKCMPF3 -- simple submission-time-only compare function. 313238032Speter** 313338032Speter** Parameters: 313438032Speter** a -- the first argument. 313538032Speter** b -- the second argument. 313638032Speter** 313738032Speter** Returns: 313838032Speter** -1 if a < b 313938032Speter** 0 if a == b 314038032Speter** +1 if a > b 314138032Speter** 314238032Speter*/ 314338032Speter 314464562Sgshapirostatic int 314538032Speterworkcmpf3(a, b) 314638032Speter register WORK *a; 314738032Speter register WORK *b; 314838032Speter{ 314938032Speter if (a->w_ctime > b->w_ctime) 315038032Speter return 1; 315138032Speter else if (a->w_ctime < b->w_ctime) 315238032Speter return -1; 315338032Speter else 315438032Speter return 0; 315538032Speter} 315690792Sgshapiro/* 315764562Sgshapiro** WORKCMPF4 -- compare based on file name 315864562Sgshapiro** 315964562Sgshapiro** Parameters: 316064562Sgshapiro** a -- the first argument. 316164562Sgshapiro** b -- the second argument. 316264562Sgshapiro** 316364562Sgshapiro** Returns: 316464562Sgshapiro** -1 if a < b 316564562Sgshapiro** 0 if a == b 316664562Sgshapiro** +1 if a > b 316764562Sgshapiro** 316864562Sgshapiro*/ 316964562Sgshapiro 317064562Sgshapirostatic int 317164562Sgshapiroworkcmpf4(a, b) 317264562Sgshapiro register WORK *a; 317364562Sgshapiro register WORK *b; 317464562Sgshapiro{ 317564562Sgshapiro return strcmp(a->w_name, b->w_name); 317664562Sgshapiro} 317790792Sgshapiro/* 317890792Sgshapiro** WORKCMPF5 -- compare based on assigned random number 317990792Sgshapiro** 318090792Sgshapiro** Parameters: 318190792Sgshapiro** a -- the first argument (ignored). 318290792Sgshapiro** b -- the second argument (ignored). 318390792Sgshapiro** 318490792Sgshapiro** Returns: 318590792Sgshapiro** randomly 1/-1 318690792Sgshapiro*/ 318790792Sgshapiro 318890792Sgshapiro/* ARGSUSED0 */ 318990792Sgshapirostatic int 319090792Sgshapiroworkcmpf5(a, b) 319190792Sgshapiro register WORK *a; 319290792Sgshapiro register WORK *b; 319390792Sgshapiro{ 319490792Sgshapiro return (get_rand_mod(2)) ? 1 : -1; 319590792Sgshapiro} 319690792Sgshapiro/* 319790792Sgshapiro** WORKCMPF6 -- simple modification-time-only compare function. 319890792Sgshapiro** 319990792Sgshapiro** Parameters: 320090792Sgshapiro** a -- the first argument. 320190792Sgshapiro** b -- the second argument. 320290792Sgshapiro** 320390792Sgshapiro** Returns: 320490792Sgshapiro** -1 if a < b 320590792Sgshapiro** 0 if a == b 320690792Sgshapiro** +1 if a > b 320790792Sgshapiro** 320890792Sgshapiro*/ 320990792Sgshapiro 321090792Sgshapirostatic int 321190792Sgshapiroworkcmpf6(a, b) 321290792Sgshapiro register WORK *a; 321390792Sgshapiro register WORK *b; 321490792Sgshapiro{ 321590792Sgshapiro if (a->w_mtime > b->w_mtime) 321690792Sgshapiro return 1; 321790792Sgshapiro else if (a->w_mtime < b->w_mtime) 321890792Sgshapiro return -1; 321990792Sgshapiro else 322090792Sgshapiro return 0; 322190792Sgshapiro} 322290792Sgshapiro#if _FFR_RHS 322390792Sgshapiro/* 322490792Sgshapiro** WORKCMPF7 -- compare function for ordering work based on shuffled host name. 322590792Sgshapiro** 322690792Sgshapiro** Sorts on lock status, host name, and priority in that order. 322790792Sgshapiro** 322890792Sgshapiro** Parameters: 322990792Sgshapiro** a -- the first argument. 323090792Sgshapiro** b -- the second argument. 323190792Sgshapiro** 323290792Sgshapiro** Returns: 323390792Sgshapiro** <0 if a < b 323490792Sgshapiro** 0 if a == b 323590792Sgshapiro** >0 if a > b 323690792Sgshapiro** 323790792Sgshapiro*/ 323890792Sgshapiro 323990792Sgshapirostatic int 324090792Sgshapiroworkcmpf7(a, b) 324190792Sgshapiro register WORK *a; 324290792Sgshapiro register WORK *b; 324390792Sgshapiro{ 324490792Sgshapiro int i; 324590792Sgshapiro 324690792Sgshapiro /* lock status */ 324790792Sgshapiro if (a->w_lock != b->w_lock) 324890792Sgshapiro return a->w_lock - b->w_lock; 324990792Sgshapiro 325090792Sgshapiro /* host name */ 325190792Sgshapiro if (a->w_host != NULL && b->w_host == NULL) 325290792Sgshapiro return 1; 325390792Sgshapiro else if (a->w_host == NULL && b->w_host != NULL) 325490792Sgshapiro return -1; 325590792Sgshapiro if (a->w_host != NULL && b->w_host != NULL && 325690792Sgshapiro (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0) 325790792Sgshapiro return i; 325890792Sgshapiro 325990792Sgshapiro /* job priority */ 326090792Sgshapiro return workcmpf0(a, b); 326190792Sgshapiro} 326290792Sgshapiro#endif /* _FFR_RHS */ 326390792Sgshapiro/* 326464562Sgshapiro** STRREV -- reverse string 326564562Sgshapiro** 326664562Sgshapiro** Returns a pointer to a new string that is the reverse of 326764562Sgshapiro** the string pointed to by fwd. The space for the new 326864562Sgshapiro** string is obtained using xalloc(). 326964562Sgshapiro** 327064562Sgshapiro** Parameters: 327164562Sgshapiro** fwd -- the string to reverse. 327264562Sgshapiro** 327364562Sgshapiro** Returns: 327464562Sgshapiro** the reversed string. 327564562Sgshapiro*/ 327664562Sgshapiro 327764562Sgshapirostatic char * 327864562Sgshapirostrrev(fwd) 327964562Sgshapiro char *fwd; 328064562Sgshapiro{ 328164562Sgshapiro char *rev = NULL; 328264562Sgshapiro int len, cnt; 328364562Sgshapiro 328464562Sgshapiro len = strlen(fwd); 328564562Sgshapiro rev = xalloc(len + 1); 328664562Sgshapiro for (cnt = 0; cnt < len; ++cnt) 328764562Sgshapiro rev[cnt] = fwd[len - cnt - 1]; 328864562Sgshapiro rev[len] = '\0'; 328964562Sgshapiro return rev; 329064562Sgshapiro} 329190792Sgshapiro 329290792Sgshapiro#if _FFR_RHS 329390792Sgshapiro 329490792Sgshapiro#define NASCII 128 329590792Sgshapiro#define NCHAR 256 329690792Sgshapiro 329790792Sgshapirostatic unsigned char ShuffledAlphabet[NCHAR]; 329890792Sgshapiro 329990792Sgshapirovoid 330090792Sgshapiroinit_shuffle_alphabet() 330190792Sgshapiro{ 330290792Sgshapiro static bool init = false; 330390792Sgshapiro int i; 330490792Sgshapiro 330590792Sgshapiro if (init) 330690792Sgshapiro return; 330790792Sgshapiro 330890792Sgshapiro /* fill the ShuffledAlphabet */ 330990792Sgshapiro for (i = 0; i < NCHAR; i++) 331090792Sgshapiro ShuffledAlphabet[i] = i; 331190792Sgshapiro 331290792Sgshapiro /* mix it */ 331390792Sgshapiro for (i = 1; i < NCHAR; i++) 331490792Sgshapiro { 331590792Sgshapiro register int j = get_random() % NCHAR; 331690792Sgshapiro register int tmp; 331790792Sgshapiro 331890792Sgshapiro tmp = ShuffledAlphabet[j]; 331990792Sgshapiro ShuffledAlphabet[j] = ShuffledAlphabet[i]; 332090792Sgshapiro ShuffledAlphabet[i] = tmp; 332190792Sgshapiro } 332290792Sgshapiro 332390792Sgshapiro /* make it case insensitive */ 332490792Sgshapiro for (i = 'A'; i <= 'Z'; i++) 332590792Sgshapiro ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A']; 332690792Sgshapiro 332790792Sgshapiro /* fill the upper part */ 332890792Sgshapiro for (i = 0; i < NCHAR; i++) 332990792Sgshapiro ShuffledAlphabet[i + NCHAR] = ShuffledAlphabet[i]; 333090792Sgshapiro init = true; 333190792Sgshapiro} 333290792Sgshapiro 333390792Sgshapirostatic int 333490792Sgshapirosm_strshufflecmp(a, b) 333590792Sgshapiro char *a; 333690792Sgshapiro char *b; 333790792Sgshapiro{ 333890792Sgshapiro const unsigned char *us1 = (const unsigned char *) a; 333990792Sgshapiro const unsigned char *us2 = (const unsigned char *) b; 334090792Sgshapiro 334190792Sgshapiro while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++]) 334290792Sgshapiro { 334390792Sgshapiro if (*us1++ == '\0') 334490792Sgshapiro return 0; 334590792Sgshapiro } 334690792Sgshapiro return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]); 334790792Sgshapiro} 334890792Sgshapiro#endif /* _FFR_RHS */ 334990792Sgshapiro 335090792Sgshapiro/* 335138032Speter** DOWORK -- do a work request. 335238032Speter** 335338032Speter** Parameters: 335490792Sgshapiro** qgrp -- the index of the queue group for the job. 335590792Sgshapiro** qdir -- the index of the queue directory for the job. 335638032Speter** id -- the ID of the job to run. 335738032Speter** forkflag -- if set, run this in background. 335838032Speter** requeueflag -- if set, reinstantiate the queue quickly. 335938032Speter** This is used when expanding aliases in the queue. 336038032Speter** If forkflag is also set, it doesn't wait for the 336138032Speter** child. 336238032Speter** e - the envelope in which to run it. 336338032Speter** 336438032Speter** Returns: 336538032Speter** process id of process that is running the queue job. 336638032Speter** 336738032Speter** Side Effects: 336838032Speter** The work request is satisfied if possible. 336938032Speter*/ 337038032Speter 337138032Speterpid_t 337290792Sgshapirodowork(qgrp, qdir, id, forkflag, requeueflag, e) 337390792Sgshapiro int qgrp; 337490792Sgshapiro int qdir; 337538032Speter char *id; 337638032Speter bool forkflag; 337738032Speter bool requeueflag; 337838032Speter register ENVELOPE *e; 337938032Speter{ 338038032Speter register pid_t pid; 338190792Sgshapiro SM_RPOOL_T *rpool; 338238032Speter 338338032Speter if (tTd(40, 1)) 338490792Sgshapiro sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id); 338538032Speter 338638032Speter /* 338738032Speter ** Fork for work. 338838032Speter */ 338938032Speter 339038032Speter if (forkflag) 339138032Speter { 339264562Sgshapiro /* 339364562Sgshapiro ** Since the delivery may happen in a child and the 339464562Sgshapiro ** parent does not wait, the parent may close the 339564562Sgshapiro ** maps thereby removing any shared memory used by 339664562Sgshapiro ** the map. Therefore, close the maps now so the 339764562Sgshapiro ** child will dynamically open them if necessary. 339864562Sgshapiro */ 339964562Sgshapiro 340090792Sgshapiro closemaps(false); 340164562Sgshapiro 340238032Speter pid = fork(); 340338032Speter if (pid < 0) 340438032Speter { 340538032Speter syserr("dowork: cannot fork"); 340638032Speter return 0; 340738032Speter } 340838032Speter else if (pid > 0) 340938032Speter { 341038032Speter /* parent -- clean out connection cache */ 341190792Sgshapiro mci_flush(false, NULL); 341238032Speter } 341338032Speter else 341438032Speter { 341590792Sgshapiro /* 341690792Sgshapiro ** Initialize exception stack and default exception 341790792Sgshapiro ** handler for child process. 341890792Sgshapiro */ 341990792Sgshapiro 342090792Sgshapiro /* Reset global flags */ 342190792Sgshapiro RestartRequest = NULL; 342290792Sgshapiro RestartWorkGroup = false; 342390792Sgshapiro ShutdownRequest = NULL; 342490792Sgshapiro PendingSignal = 0; 342590792Sgshapiro CurrentPid = getpid(); 342690792Sgshapiro sm_exc_newthread(fatal_error); 342790792Sgshapiro 342890792Sgshapiro /* 342990792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 343090792Sgshapiro */ 343190792Sgshapiro 343290792Sgshapiro if (OpMode == MD_SMTP || 343390792Sgshapiro OpMode == MD_DAEMON || 343490792Sgshapiro MaxQueueChildren > 0) 343590792Sgshapiro { 343690792Sgshapiro proc_list_clear(); 343790792Sgshapiro sm_releasesignal(SIGCHLD); 343890792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 343990792Sgshapiro } 344090792Sgshapiro 344138032Speter /* child -- error messages to the transcript */ 344290792Sgshapiro QuickAbort = OnlyOneError = false; 344338032Speter } 344438032Speter } 344538032Speter else 344638032Speter { 344738032Speter pid = 0; 344838032Speter } 344938032Speter 345038032Speter if (pid == 0) 345138032Speter { 345238032Speter /* 345338032Speter ** CHILD 345438032Speter ** Lock the control file to avoid duplicate deliveries. 345538032Speter ** Then run the file as though we had just read it. 345638032Speter ** We save an idea of the temporary name so we 345738032Speter ** can recover on interrupt. 345838032Speter */ 345938032Speter 346090792Sgshapiro if (forkflag) 346190792Sgshapiro { 346290792Sgshapiro /* Reset global flags */ 346390792Sgshapiro RestartRequest = NULL; 346490792Sgshapiro RestartWorkGroup = false; 346590792Sgshapiro ShutdownRequest = NULL; 346690792Sgshapiro PendingSignal = 0; 346790792Sgshapiro } 346877349Sgshapiro 346938032Speter /* set basic modes, etc. */ 347090792Sgshapiro sm_clear_events(); 347164562Sgshapiro clearstats(); 347290792Sgshapiro rpool = sm_rpool_new_x(NULL); 347390792Sgshapiro clearenvelope(e, false, rpool); 347438032Speter e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 347564562Sgshapiro set_delivery_mode(SM_DELIVER, e); 347638032Speter e->e_errormode = EM_MAIL; 347738032Speter e->e_id = id; 347890792Sgshapiro e->e_qgrp = qgrp; 347990792Sgshapiro e->e_qdir = qdir; 348090792Sgshapiro GrabTo = UseErrorsTo = false; 348138032Speter ExitStat = EX_OK; 348238032Speter if (forkflag) 348338032Speter { 348438032Speter disconnect(1, e); 348590792Sgshapiro set_op_mode(MD_QUEUERUN); 348638032Speter } 348790792Sgshapiro sm_setproctitle(true, e, "%s from queue", qid_printname(e)); 348838032Speter if (LogLevel > 76) 348990792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d", 349090792Sgshapiro (int) CurrentPid); 349138032Speter 349238032Speter /* don't use the headers from sendmail.cf... */ 349338032Speter e->e_header = NULL; 349438032Speter 349538032Speter /* read the queue control file -- return if locked */ 349690792Sgshapiro if (!readqf(e, false)) 349738032Speter { 349838032Speter if (tTd(40, 4) && e->e_id != NULL) 349990792Sgshapiro sm_dprintf("readqf(%s) failed\n", 350064562Sgshapiro qid_printname(e)); 350138032Speter e->e_id = NULL; 350238032Speter if (forkflag) 350390792Sgshapiro finis(false, true, EX_OK); 350438032Speter else 350590792Sgshapiro { 350690792Sgshapiro /* adding this frees 8 bytes */ 350790792Sgshapiro clearenvelope(e, false, rpool); 350890792Sgshapiro 350990792Sgshapiro /* adding this frees 12 bytes */ 351090792Sgshapiro sm_rpool_free(rpool); 351190792Sgshapiro e->e_rpool = NULL; 351238032Speter return 0; 351390792Sgshapiro } 351438032Speter } 351538032Speter 351638032Speter e->e_flags |= EF_INQUEUE; 351790792Sgshapiro eatheader(e, requeueflag, true); 351838032Speter 351938032Speter if (requeueflag) 352090792Sgshapiro queueup(e, false, false); 352138032Speter 352238032Speter /* do the delivery */ 352338032Speter sendall(e, SM_DELIVER); 352438032Speter 352538032Speter /* finish up and exit */ 352638032Speter if (forkflag) 352790792Sgshapiro finis(true, true, ExitStat); 352838032Speter else 352990792Sgshapiro { 353090792Sgshapiro dropenvelope(e, true, false); 353190792Sgshapiro sm_rpool_free(rpool); 353290792Sgshapiro e->e_rpool = NULL; 353390792Sgshapiro } 353438032Speter } 353538032Speter e->e_id = NULL; 353638032Speter return pid; 353738032Speter} 353890792Sgshapiro 353990792Sgshapiro/* 354090792Sgshapiro** DOWORKLIST -- process a list of envelopes as work requests 354190792Sgshapiro** 354290792Sgshapiro** Similar to dowork(), except that after forking, it processes an 354390792Sgshapiro** envelope and its siblings, treating each envelope as a work request. 354490792Sgshapiro** 354590792Sgshapiro** Parameters: 354690792Sgshapiro** el -- envelope to be processed including its siblings. 354790792Sgshapiro** forkflag -- if set, run this in background. 354890792Sgshapiro** requeueflag -- if set, reinstantiate the queue quickly. 354990792Sgshapiro** This is used when expanding aliases in the queue. 355090792Sgshapiro** If forkflag is also set, it doesn't wait for the 355190792Sgshapiro** child. 355290792Sgshapiro** 355390792Sgshapiro** Returns: 355490792Sgshapiro** process id of process that is running the queue job. 355590792Sgshapiro** 355690792Sgshapiro** Side Effects: 355790792Sgshapiro** The work request is satisfied if possible. 355890792Sgshapiro*/ 355990792Sgshapiro 356090792Sgshapiropid_t 356190792Sgshapirodoworklist(el, forkflag, requeueflag) 356290792Sgshapiro ENVELOPE *el; 356390792Sgshapiro bool forkflag; 356490792Sgshapiro bool requeueflag; 356590792Sgshapiro{ 356690792Sgshapiro register pid_t pid; 356790792Sgshapiro ENVELOPE *ei; 356890792Sgshapiro 356990792Sgshapiro if (tTd(40, 1)) 357090792Sgshapiro sm_dprintf("doworklist()\n"); 357190792Sgshapiro 357290792Sgshapiro /* 357390792Sgshapiro ** Fork for work. 357490792Sgshapiro */ 357590792Sgshapiro 357690792Sgshapiro if (forkflag) 357790792Sgshapiro { 357890792Sgshapiro /* 357990792Sgshapiro ** Since the delivery may happen in a child and the 358090792Sgshapiro ** parent does not wait, the parent may close the 358190792Sgshapiro ** maps thereby removing any shared memory used by 358290792Sgshapiro ** the map. Therefore, close the maps now so the 358390792Sgshapiro ** child will dynamically open them if necessary. 358490792Sgshapiro */ 358590792Sgshapiro 358690792Sgshapiro closemaps(false); 358790792Sgshapiro 358890792Sgshapiro pid = fork(); 358990792Sgshapiro if (pid < 0) 359090792Sgshapiro { 359190792Sgshapiro syserr("doworklist: cannot fork"); 359290792Sgshapiro return 0; 359390792Sgshapiro } 359490792Sgshapiro else if (pid > 0) 359590792Sgshapiro { 359690792Sgshapiro /* parent -- clean out connection cache */ 359790792Sgshapiro mci_flush(false, NULL); 359890792Sgshapiro } 359990792Sgshapiro else 360090792Sgshapiro { 360190792Sgshapiro /* 360290792Sgshapiro ** Initialize exception stack and default exception 360390792Sgshapiro ** handler for child process. 360490792Sgshapiro */ 360590792Sgshapiro 360690792Sgshapiro /* Reset global flags */ 360790792Sgshapiro RestartRequest = NULL; 360890792Sgshapiro RestartWorkGroup = false; 360990792Sgshapiro ShutdownRequest = NULL; 361090792Sgshapiro PendingSignal = 0; 361190792Sgshapiro CurrentPid = getpid(); 361290792Sgshapiro sm_exc_newthread(fatal_error); 361390792Sgshapiro 361490792Sgshapiro /* 361590792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 361690792Sgshapiro */ 361790792Sgshapiro 361890792Sgshapiro if (OpMode == MD_SMTP || 361990792Sgshapiro OpMode == MD_DAEMON || 362090792Sgshapiro MaxQueueChildren > 0) 362190792Sgshapiro { 362290792Sgshapiro proc_list_clear(); 362390792Sgshapiro sm_releasesignal(SIGCHLD); 362490792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 362590792Sgshapiro } 362690792Sgshapiro 362790792Sgshapiro /* child -- error messages to the transcript */ 362890792Sgshapiro QuickAbort = OnlyOneError = false; 362990792Sgshapiro } 363090792Sgshapiro } 363190792Sgshapiro else 363290792Sgshapiro { 363390792Sgshapiro pid = 0; 363490792Sgshapiro } 363590792Sgshapiro 363690792Sgshapiro if (pid != 0) 363790792Sgshapiro return pid; 363890792Sgshapiro 363990792Sgshapiro /* 364090792Sgshapiro ** IN CHILD 364190792Sgshapiro ** Lock the control file to avoid duplicate deliveries. 364290792Sgshapiro ** Then run the file as though we had just read it. 364390792Sgshapiro ** We save an idea of the temporary name so we 364490792Sgshapiro ** can recover on interrupt. 364590792Sgshapiro */ 364690792Sgshapiro 364790792Sgshapiro if (forkflag) 364890792Sgshapiro { 364990792Sgshapiro /* Reset global flags */ 365090792Sgshapiro RestartRequest = NULL; 365190792Sgshapiro RestartWorkGroup = false; 365290792Sgshapiro ShutdownRequest = NULL; 365390792Sgshapiro PendingSignal = 0; 365490792Sgshapiro } 365590792Sgshapiro 365690792Sgshapiro /* set basic modes, etc. */ 365790792Sgshapiro sm_clear_events(); 365890792Sgshapiro clearstats(); 365990792Sgshapiro GrabTo = UseErrorsTo = false; 366090792Sgshapiro ExitStat = EX_OK; 366190792Sgshapiro if (forkflag) 366290792Sgshapiro { 366390792Sgshapiro disconnect(1, el); 366490792Sgshapiro set_op_mode(MD_QUEUERUN); 366590792Sgshapiro } 366690792Sgshapiro if (LogLevel > 76) 366790792Sgshapiro sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d", 366890792Sgshapiro (int) CurrentPid); 366990792Sgshapiro 367090792Sgshapiro for (ei = el; ei != NULL; ei = ei->e_sibling) 367190792Sgshapiro { 367290792Sgshapiro ENVELOPE e; 367390792Sgshapiro SM_RPOOL_T *rpool; 367490792Sgshapiro 367590792Sgshapiro if (WILL_BE_QUEUED(ei->e_sendmode)) 367690792Sgshapiro continue; 367790792Sgshapiro#if _FFR_QUARANTINE 367890792Sgshapiro else if (QueueMode != QM_QUARANTINE && 367990792Sgshapiro ei->e_quarmsg != NULL) 368090792Sgshapiro continue; 368190792Sgshapiro#endif /* _FFR_QUARANTINE */ 368290792Sgshapiro 368390792Sgshapiro rpool = sm_rpool_new_x(NULL); 368490792Sgshapiro clearenvelope(&e, true, rpool); 368590792Sgshapiro e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 368690792Sgshapiro set_delivery_mode(SM_DELIVER, &e); 368790792Sgshapiro e.e_errormode = EM_MAIL; 368890792Sgshapiro e.e_id = ei->e_id; 368990792Sgshapiro e.e_qgrp = ei->e_qgrp; 369090792Sgshapiro e.e_qdir = ei->e_qdir; 369190792Sgshapiro openxscript(&e); 369290792Sgshapiro sm_setproctitle(true, &e, "%s from queue", qid_printname(&e)); 369390792Sgshapiro 369490792Sgshapiro /* don't use the headers from sendmail.cf... */ 369590792Sgshapiro e.e_header = NULL; 369690792Sgshapiro CurEnv = &e; 369790792Sgshapiro 369890792Sgshapiro /* read the queue control file -- return if locked */ 369990792Sgshapiro if (readqf(&e, false)) 370090792Sgshapiro { 370190792Sgshapiro e.e_flags |= EF_INQUEUE; 370290792Sgshapiro eatheader(&e, requeueflag, true); 370390792Sgshapiro 370490792Sgshapiro if (requeueflag) 370590792Sgshapiro queueup(&e, false, false); 370690792Sgshapiro 370790792Sgshapiro /* do the delivery */ 370890792Sgshapiro sendall(&e, SM_DELIVER); 370990792Sgshapiro dropenvelope(&e, true, false); 371090792Sgshapiro } 371190792Sgshapiro else 371290792Sgshapiro { 371390792Sgshapiro if (tTd(40, 4) && e.e_id != NULL) 371490792Sgshapiro sm_dprintf("readqf(%s) failed\n", 371590792Sgshapiro qid_printname(&e)); 371690792Sgshapiro } 371790792Sgshapiro sm_rpool_free(rpool); 371890792Sgshapiro ei->e_id = NULL; 371990792Sgshapiro } 372090792Sgshapiro 372190792Sgshapiro /* restore CurEnv */ 372290792Sgshapiro CurEnv = el; 372390792Sgshapiro 372490792Sgshapiro /* finish up and exit */ 372590792Sgshapiro if (forkflag) 372690792Sgshapiro finis(true, true, ExitStat); 372790792Sgshapiro return 0; 372890792Sgshapiro} 372990792Sgshapiro/* 373038032Speter** READQF -- read queue file and set up environment. 373138032Speter** 373238032Speter** Parameters: 373338032Speter** e -- the envelope of the job to run. 373490792Sgshapiro** openonly -- only open the qf (returned as e_lockfp) 373538032Speter** 373638032Speter** Returns: 373790792Sgshapiro** true if it successfully read the queue file. 373890792Sgshapiro** false otherwise. 373938032Speter** 374038032Speter** Side Effects: 374138032Speter** The queue file is returned locked. 374238032Speter*/ 374338032Speter 374464562Sgshapirostatic bool 374590792Sgshapiroreadqf(e, openonly) 374638032Speter register ENVELOPE *e; 374790792Sgshapiro bool openonly; 374838032Speter{ 374990792Sgshapiro register SM_FILE_T *qfp; 375038032Speter ADDRESS *ctladdr; 375177349Sgshapiro struct stat st, stf; 375238032Speter char *bp; 375338032Speter int qfver = 0; 375438032Speter long hdrsize = 0; 375538032Speter register char *p; 375690792Sgshapiro char *frcpt = NULL; 375738032Speter char *orcpt = NULL; 375890792Sgshapiro bool nomore = false; 375990792Sgshapiro bool bogus = false; 376064562Sgshapiro MODE_T qsafe; 376194334Sgshapiro char *err; 376264562Sgshapiro char qf[MAXPATHLEN]; 376338032Speter char buf[MAXLINE]; 376438032Speter 376538032Speter /* 376638032Speter ** Read and process the file. 376738032Speter */ 376838032Speter 376990792Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof qf); 377090792Sgshapiro qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR, NULL); 377138032Speter if (qfp == NULL) 377238032Speter { 377364562Sgshapiro int save_errno = errno; 377464562Sgshapiro 377538032Speter if (tTd(40, 8)) 377690792Sgshapiro sm_dprintf("readqf(%s): sm_io_open failure (%s)\n", 377790792Sgshapiro qf, sm_errstring(errno)); 377864562Sgshapiro errno = save_errno; 377964562Sgshapiro if (errno != ENOENT 378064562Sgshapiro ) 378138032Speter syserr("readqf: no control file %s", qf); 378290792Sgshapiro RELEASE_QUEUE; 378390792Sgshapiro return false; 378438032Speter } 378538032Speter 378690792Sgshapiro if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL, 378790792Sgshapiro LOCK_EX|LOCK_NB)) 378838032Speter { 378938032Speter /* being processed by another queuer */ 379064562Sgshapiro if (Verbose) 379190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 379290792Sgshapiro "%s: locked\n", e->e_id); 379364562Sgshapiro if (tTd(40, 8)) 379490792Sgshapiro sm_dprintf("%s: locked\n", e->e_id); 379538032Speter if (LogLevel > 19) 379638032Speter sm_syslog(LOG_DEBUG, e->e_id, "locked"); 379790792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 379890792Sgshapiro RELEASE_QUEUE; 379990792Sgshapiro return false; 380038032Speter } 380138032Speter 380238032Speter /* 380377349Sgshapiro ** Prevent locking race condition. 380477349Sgshapiro ** 380577349Sgshapiro ** Process A: readqf(): qfp = fopen(qffile) 380677349Sgshapiro ** Process B: queueup(): rename(tf, qf) 380777349Sgshapiro ** Process B: unlocks(tf) 380877349Sgshapiro ** Process A: lockfile(qf); 380977349Sgshapiro ** 381077349Sgshapiro ** Process A (us) has the old qf file (before the rename deleted 381177349Sgshapiro ** the directory entry) and will be delivering based on old data. 381277349Sgshapiro ** This can lead to multiple deliveries of the same recipients. 381377349Sgshapiro ** 381477349Sgshapiro ** Catch this by checking if the underlying qf file has changed 381577349Sgshapiro ** *after* acquiring our lock and if so, act as though the file 381677349Sgshapiro ** was still locked (i.e., just return like the lockfile() case 381777349Sgshapiro ** above. 381838032Speter */ 381938032Speter 382077349Sgshapiro if (stat(qf, &stf) < 0 || 382190792Sgshapiro fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0) 382238032Speter { 382338032Speter /* must have been being processed by someone else */ 382438032Speter if (tTd(40, 8)) 382590792Sgshapiro sm_dprintf("readqf(%s): [f]stat failure (%s)\n", 382690792Sgshapiro qf, sm_errstring(errno)); 382790792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 382890792Sgshapiro RELEASE_QUEUE; 382990792Sgshapiro return false; 383038032Speter } 383138032Speter 383277349Sgshapiro if (st.st_nlink != stf.st_nlink || 383377349Sgshapiro st.st_dev != stf.st_dev || 383490792Sgshapiro ST_INODE(st) != ST_INODE(stf) || 383590792Sgshapiro#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 383677349Sgshapiro st.st_gen != stf.st_gen || 383790792Sgshapiro#endif /* HAS_ST_GEN && 0 */ 383877349Sgshapiro st.st_uid != stf.st_uid || 383977349Sgshapiro st.st_gid != stf.st_gid || 384077349Sgshapiro st.st_size != stf.st_size) 384177349Sgshapiro { 384277349Sgshapiro /* changed after opened */ 384377349Sgshapiro if (Verbose) 384490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 384590792Sgshapiro "%s: changed\n", e->e_id); 384677349Sgshapiro if (tTd(40, 8)) 384790792Sgshapiro sm_dprintf("%s: changed\n", e->e_id); 384877349Sgshapiro if (LogLevel > 19) 384977349Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "changed"); 385090792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 385190792Sgshapiro RELEASE_QUEUE; 385290792Sgshapiro return false; 385377349Sgshapiro } 385477349Sgshapiro 385577349Sgshapiro /* 385677349Sgshapiro ** Check the queue file for plausibility to avoid attacks. 385777349Sgshapiro */ 385877349Sgshapiro 385964562Sgshapiro qsafe = S_IWOTH|S_IWGRP; 386064562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 386164562Sgshapiro qsafe &= ~S_IWGRP; 386264562Sgshapiro 386390792Sgshapiro bogus = st.st_uid != geteuid() && 386490792Sgshapiro st.st_uid != TrustedUid && 386590792Sgshapiro geteuid() != RealUid; 386690792Sgshapiro 386790792Sgshapiro /* 386890792Sgshapiro ** If this qf file results from a set-group-ID binary, then 386990792Sgshapiro ** we check whether the directory is group-writable, 387090792Sgshapiro ** the queue file mode contains the group-writable bit, and 387190792Sgshapiro ** the groups are the same. 387290792Sgshapiro ** Notice: this requires that the set-group-ID binary is used to 387390792Sgshapiro ** run the queue! 387490792Sgshapiro */ 387590792Sgshapiro 387690792Sgshapiro if (bogus && st.st_gid == getegid() && UseMSP) 387738032Speter { 387890792Sgshapiro char delim; 387990792Sgshapiro struct stat dst; 388090792Sgshapiro 388190792Sgshapiro bp = SM_LAST_DIR_DELIM(qf); 388290792Sgshapiro if (bp == NULL) 388390792Sgshapiro delim = '\0'; 388490792Sgshapiro else 388590792Sgshapiro { 388690792Sgshapiro delim = *bp; 388790792Sgshapiro *bp = '\0'; 388890792Sgshapiro } 388990792Sgshapiro if (stat(delim == '\0' ? "." : qf, &dst) < 0) 389090792Sgshapiro syserr("readqf: cannot stat directory %s", 389190792Sgshapiro delim == '\0' ? "." : qf); 389290792Sgshapiro else 389390792Sgshapiro { 389490792Sgshapiro bogus = !(bitset(S_IWGRP, QueueFileMode) && 389590792Sgshapiro bitset(S_IWGRP, dst.st_mode) && 389690792Sgshapiro dst.st_gid == st.st_gid); 389790792Sgshapiro } 389890792Sgshapiro if (delim != '\0') 389990792Sgshapiro *bp = delim; 390090792Sgshapiro } 390190792Sgshapiro if (!bogus) 390290792Sgshapiro bogus = bitset(qsafe, st.st_mode); 390390792Sgshapiro if (bogus) 390490792Sgshapiro { 390538032Speter if (LogLevel > 0) 390638032Speter { 390738032Speter sm_syslog(LOG_ALERT, e->e_id, 390890792Sgshapiro "bogus queue file, uid=%d, gid=%d, mode=%o", 390990792Sgshapiro st.st_uid, st.st_gid, st.st_mode); 391038032Speter } 391138032Speter if (tTd(40, 8)) 391290792Sgshapiro sm_dprintf("readqf(%s): bogus file\n", qf); 391390792Sgshapiro e->e_flags |= EF_INQUEUE; 391490792Sgshapiro if (!openonly) 391590792Sgshapiro loseqfile(e, "bogus file uid/gid in mqueue"); 391690792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 391790792Sgshapiro RELEASE_QUEUE; 391890792Sgshapiro return false; 391938032Speter } 392038032Speter 392138032Speter if (st.st_size == 0) 392238032Speter { 392338032Speter /* must be a bogus file -- if also old, just remove it */ 392490792Sgshapiro if (!openonly && st.st_ctime + 10 * 60 < curtime()) 392538032Speter { 392690792Sgshapiro (void) xunlink(queuename(e, DATAFL_LETTER)); 392790792Sgshapiro (void) xunlink(queuename(e, ANYQFL_LETTER)); 392838032Speter } 392990792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 393090792Sgshapiro RELEASE_QUEUE; 393190792Sgshapiro return false; 393238032Speter } 393338032Speter 393438032Speter if (st.st_nlink == 0) 393538032Speter { 393638032Speter /* 393738032Speter ** Race condition -- we got a file just as it was being 393838032Speter ** unlinked. Just assume it is zero length. 393938032Speter */ 394038032Speter 394190792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 394290792Sgshapiro RELEASE_QUEUE; 394390792Sgshapiro return false; 394438032Speter } 394538032Speter 394690792Sgshapiro#if _FFR_TRUSTED_QF 394790792Sgshapiro /* 394890792Sgshapiro ** If we don't own the file mark it as unsafe. 394990792Sgshapiro ** However, allow TrustedUser to own it as well 395090792Sgshapiro ** in case TrustedUser manipulates the queue. 395190792Sgshapiro */ 395290792Sgshapiro 395390792Sgshapiro if (st.st_uid != geteuid() && st.st_uid != TrustedUid) 395490792Sgshapiro e->e_flags |= EF_UNSAFE; 395590792Sgshapiro#else /* _FFR_TRUSTED_QF */ 395690792Sgshapiro /* If we don't own the file mark it as unsafe */ 395790792Sgshapiro if (st.st_uid != geteuid()) 395890792Sgshapiro e->e_flags |= EF_UNSAFE; 395990792Sgshapiro#endif /* _FFR_TRUSTED_QF */ 396090792Sgshapiro 396138032Speter /* good file -- save this lock */ 396238032Speter e->e_lockfp = qfp; 396338032Speter 396490792Sgshapiro /* Just wanted the open file */ 396590792Sgshapiro if (openonly) 396690792Sgshapiro return true; 396790792Sgshapiro 396838032Speter /* do basic system initialization */ 396938032Speter initsys(e); 397090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 397138032Speter 397238032Speter LineNumber = 0; 397338032Speter e->e_flags |= EF_GLOBALERRS; 397490792Sgshapiro set_op_mode(MD_QUEUERUN); 397538032Speter ctladdr = NULL; 397690792Sgshapiro#if _FFR_QUARANTINE 397790792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 397890792Sgshapiro#endif /* _FFR_QUARANTINE */ 397990792Sgshapiro e->e_dfqgrp = e->e_qgrp; 398090792Sgshapiro e->e_dfqdir = e->e_qdir; 398190792Sgshapiro#if _FFR_QUEUE_MACRO 398290792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{queue}"), 398390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 398490792Sgshapiro#endif /* _FFR_QUEUE_MACRO */ 398538032Speter e->e_dfino = -1; 398638032Speter e->e_msgsize = -1; 398790792Sgshapiro#if _FFR_QUEUEDELAY 398864562Sgshapiro e->e_queuealg = QD_LINEAR; 398964562Sgshapiro e->e_queuedelay = (time_t) 0; 399090792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 399138032Speter while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL) 399238032Speter { 399390792Sgshapiro unsigned long qflags; 399438032Speter ADDRESS *q; 399590792Sgshapiro int r; 399671345Sgshapiro time_t now; 399738032Speter auto char *ep; 399838032Speter 399938032Speter if (tTd(40, 4)) 400090792Sgshapiro sm_dprintf("+++++ %s\n", bp); 400138032Speter if (nomore) 400238032Speter { 400338032Speter /* hack attack */ 400490792Sgshapiro hackattack: 400590792Sgshapiro syserr("SECURITY ALERT: extra or bogus data in queue file: %s", 400690792Sgshapiro bp); 400794334Sgshapiro err = "bogus queue line"; 400894334Sgshapiro goto fail; 400938032Speter } 401038032Speter switch (bp[0]) 401138032Speter { 401290792Sgshapiro case 'A': /* AUTH= parameter */ 401390792Sgshapiro if (!xtextok(&bp[1])) 401490792Sgshapiro goto hackattack; 401590792Sgshapiro e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 401690792Sgshapiro break; 401738032Speter 401890792Sgshapiro case 'B': /* body type */ 401990792Sgshapiro r = check_bodytype(&bp[1]); 402090792Sgshapiro if (!BODYTYPE_VALID(r)) 402190792Sgshapiro goto hackattack; 402290792Sgshapiro e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 402390792Sgshapiro break; 402490792Sgshapiro 402538032Speter case 'C': /* specify controlling user */ 402690792Sgshapiro ctladdr = setctluser(&bp[1], qfver, e); 402738032Speter break; 402838032Speter 402990792Sgshapiro case 'D': /* data file name */ 403090792Sgshapiro /* obsolete -- ignore */ 403138032Speter break; 403238032Speter 403390792Sgshapiro case 'd': /* data file directory name */ 403438032Speter { 403590792Sgshapiro int qgrp, qdir; 403690792Sgshapiro 403790792Sgshapiro#if _FFR_MSP_PARANOIA 403890792Sgshapiro /* forbid queue groups in MSP? */ 403990792Sgshapiro if (UseMSP) 404090792Sgshapiro goto hackattack; 404190792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 404290792Sgshapiro for (qgrp = 0; 404390792Sgshapiro qgrp < NumQueue && Queue[qgrp] != NULL; 404490792Sgshapiro ++qgrp) 404538032Speter { 404690792Sgshapiro for (qdir = 0; 404790792Sgshapiro qdir < Queue[qgrp]->qg_numqueues; 404890792Sgshapiro ++qdir) 404938032Speter { 405090792Sgshapiro if (strcmp(&bp[1], 405190792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name) 405290792Sgshapiro == 0) 405390792Sgshapiro { 405490792Sgshapiro e->e_dfqgrp = qgrp; 405590792Sgshapiro e->e_dfqdir = qdir; 405690792Sgshapiro goto done; 405790792Sgshapiro } 405838032Speter } 405938032Speter } 406094334Sgshapiro err = "bogus queue file directory"; 406194334Sgshapiro goto fail; 406290792Sgshapiro done: 406390792Sgshapiro break; 406438032Speter } 406538032Speter 406638032Speter case 'E': /* specify error recipient */ 406738032Speter /* no longer used */ 406838032Speter break; 406938032Speter 407090792Sgshapiro case 'F': /* flag bits */ 407190792Sgshapiro if (strncmp(bp, "From ", 5) == 0) 407290792Sgshapiro { 407390792Sgshapiro /* we are being spoofed! */ 407490792Sgshapiro syserr("SECURITY ALERT: bogus qf line %s", bp); 407594334Sgshapiro err = "bogus queue line"; 407694334Sgshapiro goto fail; 407790792Sgshapiro } 407890792Sgshapiro for (p = &bp[1]; *p != '\0'; p++) 407990792Sgshapiro { 408090792Sgshapiro switch (*p) 408190792Sgshapiro { 408290792Sgshapiro case '8': /* has 8 bit data */ 408390792Sgshapiro e->e_flags |= EF_HAS8BIT; 408490792Sgshapiro break; 408538032Speter 408690792Sgshapiro case 'b': /* delete Bcc: header */ 408790792Sgshapiro e->e_flags |= EF_DELETE_BCC; 408890792Sgshapiro break; 408938032Speter 409090792Sgshapiro case 'd': /* envelope has DSN RET= */ 409190792Sgshapiro e->e_flags |= EF_RET_PARAM; 409290792Sgshapiro break; 409338032Speter 409490792Sgshapiro case 'n': /* don't return body */ 409590792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 409690792Sgshapiro break; 409790792Sgshapiro 409890792Sgshapiro case 'r': /* response */ 409990792Sgshapiro e->e_flags |= EF_RESPONSE; 410090792Sgshapiro break; 410190792Sgshapiro 410290792Sgshapiro case 's': /* split */ 410390792Sgshapiro e->e_flags |= EF_SPLIT; 410490792Sgshapiro break; 410590792Sgshapiro 410690792Sgshapiro case 'w': /* warning sent */ 410790792Sgshapiro e->e_flags |= EF_WARNING; 410890792Sgshapiro break; 410990792Sgshapiro } 411090792Sgshapiro } 411138032Speter break; 411238032Speter 411390792Sgshapiro#if _FFR_QUEUEDELAY 411490792Sgshapiro case 'G': /* queue delay algorithm */ 411590792Sgshapiro e->e_queuealg = atoi(&buf[1]); 411638032Speter break; 411790792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 411838032Speter 411990792Sgshapiro#if _FFR_QUARANTINE 412090792Sgshapiro case 'q': /* quarantine reason */ 412190792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 412290792Sgshapiro macdefine(&e->e_macro, A_PERM, 412390792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 412438032Speter break; 412590792Sgshapiro#endif /* _FFR_QUARANTINE */ 412638032Speter 412790792Sgshapiro case 'H': /* header */ 412894334Sgshapiro 412994334Sgshapiro /* 413094334Sgshapiro ** count size before chompheader() destroys the line. 413194334Sgshapiro ** this isn't accurate due to macro expansion, but 413294334Sgshapiro ** better than before. "+3" to skip H?? at least. 413394334Sgshapiro */ 413494334Sgshapiro 413594334Sgshapiro hdrsize += strlen(bp + 3); 413690792Sgshapiro (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); 413738032Speter break; 413838032Speter 413938032Speter case 'I': /* data file's inode number */ 414038032Speter /* regenerated below */ 414138032Speter break; 414238032Speter 414380785Sgshapiro case 'K': /* time of last delivery attempt */ 414438032Speter e->e_dtime = atol(&buf[1]); 414538032Speter break; 414638032Speter 414790792Sgshapiro case 'L': /* Solaris Content-Length: */ 414890792Sgshapiro case 'M': /* message */ 414990792Sgshapiro /* ignore this; we want a new message next time */ 415064562Sgshapiro break; 415164562Sgshapiro 415238032Speter case 'N': /* number of delivery attempts */ 415338032Speter e->e_ntries = atoi(&buf[1]); 415438032Speter 415538032Speter /* if this has been tried recently, let it be */ 415671345Sgshapiro now = curtime(); 415771345Sgshapiro if (e->e_ntries > 0 && e->e_dtime <= now && 415871345Sgshapiro now < e->e_dtime + queuedelay(e)) 415938032Speter { 416064562Sgshapiro char *howlong; 416138032Speter 416290792Sgshapiro howlong = pintvl(now - e->e_dtime, true); 416364562Sgshapiro if (Verbose) 416490792Sgshapiro (void) sm_io_fprintf(smioout, 416590792Sgshapiro SM_TIME_DEFAULT, 416690792Sgshapiro "%s: too young (%s)\n", 416790792Sgshapiro e->e_id, howlong); 416864562Sgshapiro if (tTd(40, 8)) 416990792Sgshapiro sm_dprintf("%s: too young (%s)\n", 417038032Speter e->e_id, howlong); 417138032Speter if (LogLevel > 19) 417238032Speter sm_syslog(LOG_DEBUG, e->e_id, 417364562Sgshapiro "too young (%s)", 417464562Sgshapiro howlong); 417538032Speter e->e_id = NULL; 417638032Speter unlockqueue(e); 417790792Sgshapiro RELEASE_QUEUE; 417890792Sgshapiro return false; 417938032Speter } 418090792Sgshapiro macdefine(&e->e_macro, A_TEMP, 418190792Sgshapiro macid("{ntries}"), &buf[1]); 418264562Sgshapiro 418390792Sgshapiro#if NAMED_BIND 418464562Sgshapiro /* adjust BIND parameters immediately */ 418564562Sgshapiro if (e->e_ntries == 0) 418664562Sgshapiro { 418764562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 418864562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 418964562Sgshapiro } 419064562Sgshapiro else 419164562Sgshapiro { 419264562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_NORMAL]; 419364562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL]; 419464562Sgshapiro } 419590792Sgshapiro#endif /* NAMED_BIND */ 419638032Speter break; 419738032Speter 419838032Speter case 'P': /* message priority */ 419938032Speter e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 420038032Speter break; 420138032Speter 420290792Sgshapiro case 'Q': /* original recipient */ 420390792Sgshapiro orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 420490792Sgshapiro break; 420590792Sgshapiro 420698841Sgshapiro case 'r': /* final recipient */ 420790792Sgshapiro frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 420890792Sgshapiro break; 420990792Sgshapiro 421090792Sgshapiro case 'R': /* specify recipient */ 421190792Sgshapiro p = bp; 421290792Sgshapiro qflags = 0; 421390792Sgshapiro if (qfver >= 1) 421438032Speter { 421590792Sgshapiro /* get flag bits */ 421690792Sgshapiro while (*++p != '\0' && *p != ':') 421738032Speter { 421890792Sgshapiro switch (*p) 421990792Sgshapiro { 422090792Sgshapiro case 'N': 422190792Sgshapiro qflags |= QHASNOTIFY; 422290792Sgshapiro break; 422338032Speter 422490792Sgshapiro case 'S': 422590792Sgshapiro qflags |= QPINGONSUCCESS; 422690792Sgshapiro break; 422738032Speter 422890792Sgshapiro case 'F': 422990792Sgshapiro qflags |= QPINGONFAILURE; 423090792Sgshapiro break; 423138032Speter 423290792Sgshapiro case 'D': 423390792Sgshapiro qflags |= QPINGONDELAY; 423490792Sgshapiro break; 423538032Speter 423690792Sgshapiro case 'P': 423790792Sgshapiro qflags |= QPRIMARY; 423890792Sgshapiro break; 423938032Speter 424090792Sgshapiro case 'A': 424190792Sgshapiro if (ctladdr != NULL) 424290792Sgshapiro ctladdr->q_flags |= QALIAS; 424390792Sgshapiro break; 424490792Sgshapiro 424590792Sgshapiro default: /* ignore or complain? */ 424690792Sgshapiro break; 424790792Sgshapiro } 424838032Speter } 424938032Speter } 425090792Sgshapiro else 425190792Sgshapiro qflags |= QPRIMARY; 425290792Sgshapiro q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e, 425390792Sgshapiro true); 425490792Sgshapiro if (q != NULL) 425590792Sgshapiro { 425694334Sgshapiro /* make sure we keep the current qgrp */ 425794334Sgshapiro if (ISVALIDQGRP(e->e_qgrp)) 425894334Sgshapiro q->q_qgrp = e->e_qgrp; 425990792Sgshapiro q->q_alias = ctladdr; 426090792Sgshapiro if (qfver >= 1) 426190792Sgshapiro q->q_flags &= ~Q_PINGFLAGS; 426290792Sgshapiro q->q_flags |= qflags; 426390792Sgshapiro q->q_finalrcpt = frcpt; 426490792Sgshapiro q->q_orcpt = orcpt; 426590792Sgshapiro (void) recipient(q, &e->e_sendqueue, 0, e); 426690792Sgshapiro } 426790792Sgshapiro frcpt = NULL; 426890792Sgshapiro orcpt = NULL; 426938032Speter break; 427038032Speter 427190792Sgshapiro case 'S': /* sender */ 427290792Sgshapiro setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]), 427390792Sgshapiro e, NULL, '\0', true); 427438032Speter break; 427538032Speter 427690792Sgshapiro case 'T': /* init time */ 427790792Sgshapiro e->e_ctime = atol(&bp[1]); 427864562Sgshapiro break; 427964562Sgshapiro 428090792Sgshapiro case 'V': /* queue file version number */ 428190792Sgshapiro qfver = atoi(&bp[1]); 428290792Sgshapiro if (queuedelay_qfver_unsupported(qfver)) 428390792Sgshapiro syserr("queue file version %d not supported: %s", 428490792Sgshapiro qfver, 428590792Sgshapiro "sendmail not compiled with _FFR_QUEUEDELAY"); 428690792Sgshapiro if (qfver <= QF_VERSION) 428790792Sgshapiro break; 428890792Sgshapiro syserr("Version number in queue file (%d) greater than max (%d)", 428990792Sgshapiro qfver, QF_VERSION); 429094334Sgshapiro err = "unsupported queue file version"; 429194334Sgshapiro goto fail; 429290792Sgshapiro /* NOTREACHED */ 429390792Sgshapiro break; 429490792Sgshapiro 429590792Sgshapiro#if _FFR_QUEUEDELAY 429690792Sgshapiro case 'Y': /* current delay */ 429790792Sgshapiro e->e_queuedelay = (time_t) atol(&buf[1]); 429890792Sgshapiro break; 429990792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 430090792Sgshapiro 430190792Sgshapiro case 'Z': /* original envelope id from ESMTP */ 430290792Sgshapiro e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 430390792Sgshapiro macdefine(&e->e_macro, A_PERM, 430490792Sgshapiro macid("{dsn_envid}"), e->e_envid); 430590792Sgshapiro break; 430690792Sgshapiro 430790792Sgshapiro case '!': /* deliver by */ 430890792Sgshapiro 430990792Sgshapiro /* format: flag (1 char) space long-integer */ 431090792Sgshapiro e->e_dlvr_flag = buf[1]; 431190792Sgshapiro e->e_deliver_by = strtol(&buf[3], NULL, 10); 431290792Sgshapiro 431338032Speter case '$': /* define macro */ 431464562Sgshapiro { 431564562Sgshapiro char *p; 431664562Sgshapiro 431790792Sgshapiro /* XXX elimate p? */ 431890792Sgshapiro r = macid_parse(&bp[1], &ep); 431990792Sgshapiro if (r == 0) 432071345Sgshapiro break; 432190792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, ep); 432290792Sgshapiro macdefine(&e->e_macro, A_PERM, r, p); 432364562Sgshapiro } 432438032Speter break; 432538032Speter 432638032Speter case '.': /* terminate file */ 432790792Sgshapiro nomore = true; 432838032Speter break; 432938032Speter 433038032Speter default: 433138032Speter syserr("readqf: %s: line %d: bad line \"%s\"", 433238032Speter qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); 433394334Sgshapiro err = "unrecognized line"; 433494334Sgshapiro goto fail; 433538032Speter } 433638032Speter 433738032Speter if (bp != buf) 433890792Sgshapiro sm_free(bp); /* XXX */ 433938032Speter } 434038032Speter 434138032Speter /* 434238032Speter ** If we haven't read any lines, this queue file is empty. 434338032Speter ** Arrange to remove it without referencing any null pointers. 434438032Speter */ 434538032Speter 434638032Speter if (LineNumber == 0) 434738032Speter { 434838032Speter errno = 0; 434990792Sgshapiro e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE; 435090792Sgshapiro RELEASE_QUEUE; 435190792Sgshapiro return true; 435238032Speter } 435338032Speter 435490792Sgshapiro /* Check to make sure we have a complete queue file read */ 435590792Sgshapiro if (!nomore) 435690792Sgshapiro { 435790792Sgshapiro syserr("readqf: %s: incomplete queue file read", qf); 435890792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 435990792Sgshapiro RELEASE_QUEUE; 436090792Sgshapiro return false; 436190792Sgshapiro } 436290792Sgshapiro 436364562Sgshapiro /* possibly set ${dsn_ret} macro */ 436464562Sgshapiro if (bitset(EF_RET_PARAM, e->e_flags)) 436564562Sgshapiro { 436664562Sgshapiro if (bitset(EF_NO_BODY_RETN, e->e_flags)) 436790792Sgshapiro macdefine(&e->e_macro, A_PERM, 436890792Sgshapiro macid("{dsn_ret}"), "hdrs"); 436964562Sgshapiro else 437090792Sgshapiro macdefine(&e->e_macro, A_PERM, 437190792Sgshapiro macid("{dsn_ret}"), "full"); 437264562Sgshapiro } 437364562Sgshapiro 437438032Speter /* 437538032Speter ** Arrange to read the data file. 437638032Speter */ 437738032Speter 437890792Sgshapiro p = queuename(e, DATAFL_LETTER); 437990792Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY, 438090792Sgshapiro NULL); 438138032Speter if (e->e_dfp == NULL) 438238032Speter { 438338032Speter syserr("readqf: cannot open %s", p); 438438032Speter } 438538032Speter else 438638032Speter { 438738032Speter e->e_flags |= EF_HAS_DF; 438890792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st) 438990792Sgshapiro >= 0) 439038032Speter { 439138032Speter e->e_msgsize = st.st_size + hdrsize; 439238032Speter e->e_dfdev = st.st_dev; 439390792Sgshapiro e->e_dfino = ST_INODE(st); 439498121Sgshapiro (void) sm_snprintf(buf, sizeof buf, "%ld", 439598121Sgshapiro e->e_msgsize); 439698121Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), 439798121Sgshapiro buf); 439838032Speter } 439938032Speter } 440038032Speter 440190792Sgshapiro RELEASE_QUEUE; 440290792Sgshapiro return true; 440394334Sgshapiro 440494334Sgshapiro fail: 440594334Sgshapiro /* 440694334Sgshapiro ** There was some error reading the qf file (reason is in err var.) 440794334Sgshapiro ** Cleanup: 440894334Sgshapiro ** close file; clear e_lockfp since it is the same as qfp, 440994334Sgshapiro ** hence it is invalid (as file) after qfp is closed; 441094334Sgshapiro ** the qf file is on disk, so set the flag to avoid calling 441194334Sgshapiro ** queueup() with bogus data. 441294334Sgshapiro */ 441394334Sgshapiro 441494334Sgshapiro if (qfp != NULL) 441594334Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 441694334Sgshapiro e->e_lockfp = NULL; 441794334Sgshapiro e->e_flags |= EF_INQUEUE; 441894334Sgshapiro loseqfile(e, err); 441994334Sgshapiro RELEASE_QUEUE; 442094334Sgshapiro return false; 442138032Speter} 442290792Sgshapiro/* 442364562Sgshapiro** PRTSTR -- print a string, "unprintable" characters are shown as \oct 442464562Sgshapiro** 442564562Sgshapiro** Parameters: 442664562Sgshapiro** s -- string to print 442764562Sgshapiro** ml -- maximum length of output 442864562Sgshapiro** 442964562Sgshapiro** Returns: 443090792Sgshapiro** number of entries 443164562Sgshapiro** 443264562Sgshapiro** Side Effects: 443364562Sgshapiro** Prints a string on stdout. 443464562Sgshapiro*/ 443564562Sgshapiro 443664562Sgshapirostatic void 443764562Sgshapiroprtstr(s, ml) 443864562Sgshapiro char *s; 443964562Sgshapiro int ml; 444064562Sgshapiro{ 444190792Sgshapiro int c; 444264562Sgshapiro 444364562Sgshapiro if (s == NULL) 444464562Sgshapiro return; 444564562Sgshapiro while (ml-- > 0 && ((c = *s++) != '\0')) 444664562Sgshapiro { 444764562Sgshapiro if (c == '\\') 444864562Sgshapiro { 444964562Sgshapiro if (ml-- > 0) 445064562Sgshapiro { 445190792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 445290792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 445364562Sgshapiro } 445464562Sgshapiro } 445564562Sgshapiro else if (isascii(c) && isprint(c)) 445690792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 445764562Sgshapiro else 445864562Sgshapiro { 445964562Sgshapiro if ((ml -= 3) > 0) 446090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 446190792Sgshapiro "\\%03o", c & 0xFF); 446264562Sgshapiro } 446364562Sgshapiro } 446464562Sgshapiro} 446590792Sgshapiro/* 446690792Sgshapiro** PRINTNQE -- print out number of entries in the mail queue 446790792Sgshapiro** 446890792Sgshapiro** Parameters: 446990792Sgshapiro** out -- output file pointer. 447090792Sgshapiro** prefix -- string to output in front of each line. 447190792Sgshapiro** 447290792Sgshapiro** Returns: 447390792Sgshapiro** none. 447490792Sgshapiro*/ 447590792Sgshapiro 447690792Sgshapirovoid 447790792Sgshapiroprintnqe(out, prefix) 447890792Sgshapiro SM_FILE_T *out; 447990792Sgshapiro char *prefix; 448090792Sgshapiro{ 448190792Sgshapiro#if SM_CONF_SHM 448290792Sgshapiro int i, k = 0, nrequests = 0; 448390792Sgshapiro bool unknown = false; 448490792Sgshapiro 448590792Sgshapiro if (ShmId == SM_SHM_NO_ID) 448690792Sgshapiro { 448790792Sgshapiro if (prefix == NULL) 448890792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 448990792Sgshapiro "Data unavailable: shared memory not updated\n"); 449090792Sgshapiro else 449190792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 449290792Sgshapiro "%sNOTCONFIGURED:-1\r\n", prefix); 449390792Sgshapiro return; 449490792Sgshapiro } 449590792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 449690792Sgshapiro { 449790792Sgshapiro int j; 449890792Sgshapiro 449990792Sgshapiro k++; 450090792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 450190792Sgshapiro { 450290792Sgshapiro int n; 450390792Sgshapiro 450490792Sgshapiro if (StopRequest) 450590792Sgshapiro stop_sendmail(); 450690792Sgshapiro 450790792Sgshapiro n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx); 450890792Sgshapiro if (prefix != NULL) 450990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 451090792Sgshapiro "%s%s:%d\r\n", 451190792Sgshapiro prefix, qid_printqueue(i, j), n); 451290792Sgshapiro else if (n < 0) 451390792Sgshapiro { 451490792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 451590792Sgshapiro "%s: unknown number of entries\n", 451690792Sgshapiro qid_printqueue(i, j)); 451790792Sgshapiro unknown = true; 451890792Sgshapiro } 451990792Sgshapiro else if (n == 0) 452090792Sgshapiro { 452190792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 452290792Sgshapiro "%s is empty\n", 452390792Sgshapiro qid_printqueue(i, j)); 452490792Sgshapiro } 452590792Sgshapiro else if (n > 0) 452690792Sgshapiro { 452790792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 452890792Sgshapiro "%s: entries=%d\n", 452990792Sgshapiro qid_printqueue(i, j), n); 453090792Sgshapiro nrequests += n; 453190792Sgshapiro k++; 453290792Sgshapiro } 453390792Sgshapiro } 453490792Sgshapiro } 453590792Sgshapiro if (prefix == NULL && k > 1) 453690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 453790792Sgshapiro "\t\tTotal requests: %d%s\n", 453890792Sgshapiro nrequests, unknown ? " (about)" : ""); 453990792Sgshapiro#else /* SM_CONF_SHM */ 454090792Sgshapiro if (prefix == NULL) 454190792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 454290792Sgshapiro "Data unavailable without shared memory support\n"); 454390792Sgshapiro else 454490792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 454590792Sgshapiro "%sNOTAVAILABLE:-1\r\n", prefix); 454690792Sgshapiro#endif /* SM_CONF_SHM */ 454790792Sgshapiro} 454890792Sgshapiro/* 454938032Speter** PRINTQUEUE -- print out a representation of the mail queue 455038032Speter** 455138032Speter** Parameters: 455238032Speter** none. 455338032Speter** 455438032Speter** Returns: 455538032Speter** none. 455638032Speter** 455738032Speter** Side Effects: 455838032Speter** Prints a listing of the mail queue on the standard output. 455938032Speter*/ 456038032Speter 456138032Spetervoid 456238032Speterprintqueue() 456338032Speter{ 456490792Sgshapiro int i, k = 0, nrequests = 0; 456564562Sgshapiro 456690792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 456777349Sgshapiro { 456890792Sgshapiro int j; 456990792Sgshapiro 457090792Sgshapiro k++; 457190792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 457290792Sgshapiro { 457390792Sgshapiro if (StopRequest) 457490792Sgshapiro stop_sendmail(); 457590792Sgshapiro nrequests += print_single_queue(i, j); 457690792Sgshapiro k++; 457790792Sgshapiro } 457877349Sgshapiro } 457990792Sgshapiro if (k > 1) 458090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 458190792Sgshapiro "\t\tTotal requests: %d\n", 458290792Sgshapiro nrequests); 458364562Sgshapiro} 458490792Sgshapiro/* 458564562Sgshapiro** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue 458664562Sgshapiro** 458764562Sgshapiro** Parameters: 458890792Sgshapiro** qgrp -- the index of the queue group. 458990792Sgshapiro** qdir -- the queue directory. 459064562Sgshapiro** 459164562Sgshapiro** Returns: 459290792Sgshapiro** number of requests in mail queue. 459364562Sgshapiro** 459464562Sgshapiro** Side Effects: 459564562Sgshapiro** Prints a listing of the mail queue on the standard output. 459664562Sgshapiro*/ 459764562Sgshapiro 459890792Sgshapiroint 459990792Sgshapiroprint_single_queue(qgrp, qdir) 460090792Sgshapiro int qgrp; 460190792Sgshapiro int qdir; 460264562Sgshapiro{ 460338032Speter register WORK *w; 460490792Sgshapiro SM_FILE_T *f; 460538032Speter int nrequests; 460664562Sgshapiro char qd[MAXPATHLEN]; 460764562Sgshapiro char qddf[MAXPATHLEN]; 460838032Speter char buf[MAXLINE]; 460938032Speter 461090792Sgshapiro if (qdir == NOQDIR) 461164562Sgshapiro { 461290792Sgshapiro (void) sm_strlcpy(qd, ".", sizeof qd); 461390792Sgshapiro (void) sm_strlcpy(qddf, ".", sizeof qddf); 461464562Sgshapiro } 461564562Sgshapiro else 461664562Sgshapiro { 461790792Sgshapiro (void) sm_strlcpyn(qd, sizeof qd, 2, 461890792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 461990792Sgshapiro (bitset(QP_SUBQF, 462090792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 462190792Sgshapiro ? "/qf" : "")); 462290792Sgshapiro (void) sm_strlcpyn(qddf, sizeof qddf, 2, 462390792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 462490792Sgshapiro (bitset(QP_SUBDF, 462590792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 462690792Sgshapiro ? "/df" : "")); 462764562Sgshapiro } 462864562Sgshapiro 462938032Speter /* 463038032Speter ** Check for permission to print the queue 463138032Speter */ 463238032Speter 463338032Speter if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 463438032Speter { 463538032Speter struct stat st; 463690792Sgshapiro#ifdef NGROUPS_MAX 463738032Speter int n; 463838032Speter extern GIDSET_T InitialGidSet[NGROUPS_MAX]; 463990792Sgshapiro#endif /* NGROUPS_MAX */ 464038032Speter 464164562Sgshapiro if (stat(qd, &st) < 0) 464238032Speter { 464390792Sgshapiro syserr("Cannot stat %s", 464490792Sgshapiro qid_printqueue(qgrp, qdir)); 464564562Sgshapiro return 0; 464638032Speter } 464790792Sgshapiro#ifdef NGROUPS_MAX 464838032Speter n = NGROUPS_MAX; 464938032Speter while (--n >= 0) 465038032Speter { 465138032Speter if (InitialGidSet[n] == st.st_gid) 465238032Speter break; 465338032Speter } 465438032Speter if (n < 0 && RealGid != st.st_gid) 465590792Sgshapiro#else /* NGROUPS_MAX */ 465638032Speter if (RealGid != st.st_gid) 465790792Sgshapiro#endif /* NGROUPS_MAX */ 465838032Speter { 465938032Speter usrerr("510 You are not permitted to see the queue"); 466038032Speter setstat(EX_NOPERM); 466164562Sgshapiro return 0; 466238032Speter } 466338032Speter } 466438032Speter 466538032Speter /* 466638032Speter ** Read and order the queue. 466738032Speter */ 466838032Speter 466990792Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL); 467090792Sgshapiro (void) sortq(Queue[qgrp]->qg_maxlist); 467138032Speter 467238032Speter /* 467338032Speter ** Print the work list that we have read. 467438032Speter */ 467538032Speter 467638032Speter /* first see if there is anything */ 467738032Speter if (nrequests <= 0) 467838032Speter { 467990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n", 468090792Sgshapiro qid_printqueue(qgrp, qdir)); 468164562Sgshapiro return 0; 468238032Speter } 468338032Speter 468490792Sgshapiro sm_getla(); /* get load average */ 468538032Speter 468690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s", 468790792Sgshapiro qid_printqueue(qgrp, qdir), 468890792Sgshapiro nrequests, nrequests == 1 ? "" : "s"); 468938032Speter if (MaxQueueRun > 0 && nrequests > MaxQueueRun) 469090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 469190792Sgshapiro ", only %d printed", MaxQueueRun); 469238032Speter if (Verbose) 469390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 469490792Sgshapiro ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n"); 469538032Speter else 469690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 469790792Sgshapiro ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n"); 469838032Speter for (w = WorkQ; w != NULL; w = w->w_next) 469938032Speter { 470038032Speter struct stat st; 470138032Speter auto time_t submittime = 0; 470238032Speter long dfsize; 470338032Speter int flags = 0; 470438032Speter int qfver; 470590792Sgshapiro#if _FFR_QUARANTINE 470690792Sgshapiro char quarmsg[MAXLINE]; 470790792Sgshapiro#endif /* _FFR_QUARANTINE */ 470838032Speter char statmsg[MAXLINE]; 470938032Speter char bodytype[MAXNAME + 1]; 471064562Sgshapiro char qf[MAXPATHLEN]; 471138032Speter 471277349Sgshapiro if (StopRequest) 471377349Sgshapiro stop_sendmail(); 471477349Sgshapiro 471590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s", 471690792Sgshapiro w->w_name + 2); 471790792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", w->w_name); 471890792Sgshapiro f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY, 471990792Sgshapiro NULL); 472038032Speter if (f == NULL) 472138032Speter { 472290792Sgshapiro if (errno == EPERM) 472390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 472490792Sgshapiro " (permission denied)\n"); 472590792Sgshapiro else if (errno == ENOENT) 472690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 472790792Sgshapiro " (job completed)\n"); 472890792Sgshapiro else 472990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 473090792Sgshapiro " (%s)\n", 473190792Sgshapiro sm_errstring(errno)); 473238032Speter errno = 0; 473338032Speter continue; 473438032Speter } 473590792Sgshapiro w->w_name[0] = DATAFL_LETTER; 473690792Sgshapiro (void) sm_strlcpyn(qf, sizeof qf, 3, qddf, "/", w->w_name); 473764562Sgshapiro if (stat(qf, &st) >= 0) 473838032Speter dfsize = st.st_size; 473938032Speter else 474090792Sgshapiro { 474190792Sgshapiro ENVELOPE e; 474290792Sgshapiro 474390792Sgshapiro /* 474490792Sgshapiro ** Maybe the df file can't be statted because 474590792Sgshapiro ** it is in a different directory than the qf file. 474690792Sgshapiro ** In order to find out, we must read the qf file. 474790792Sgshapiro */ 474890792Sgshapiro 474990792Sgshapiro newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL)); 475090792Sgshapiro e.e_id = w->w_name + 2; 475190792Sgshapiro e.e_qgrp = qgrp; 475290792Sgshapiro e.e_qdir = qdir; 475338032Speter dfsize = -1; 475490792Sgshapiro if (readqf(&e, false)) 475590792Sgshapiro { 475690792Sgshapiro char *df = queuename(&e, DATAFL_LETTER); 475790792Sgshapiro if (stat(df, &st) >= 0) 475890792Sgshapiro dfsize = st.st_size; 475990792Sgshapiro } 476090792Sgshapiro if (e.e_lockfp != NULL) 476190792Sgshapiro { 476290792Sgshapiro (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT); 476390792Sgshapiro e.e_lockfp = NULL; 476490792Sgshapiro } 476590792Sgshapiro clearenvelope(&e, false, e.e_rpool); 476690792Sgshapiro sm_rpool_free(e.e_rpool); 476790792Sgshapiro } 476838032Speter if (w->w_lock) 476990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*"); 477090792Sgshapiro#if _FFR_QUARANTINE 477190792Sgshapiro else if (QueueMode == QM_LOST) 477290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?"); 477390792Sgshapiro#endif /* _FFR_QUARANTINE */ 477438032Speter else if (w->w_tooyoung) 477590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-"); 477638032Speter else if (shouldqueue(w->w_pri, w->w_ctime)) 477790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X"); 477838032Speter else 477990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " "); 478090792Sgshapiro 478138032Speter errno = 0; 478238032Speter 478390792Sgshapiro#if _FFR_QUARANTINE 478490792Sgshapiro quarmsg[0] = '\0'; 478590792Sgshapiro#endif /* _FFR_QUARANTINE */ 478638032Speter statmsg[0] = bodytype[0] = '\0'; 478738032Speter qfver = 0; 478890792Sgshapiro while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL) 478938032Speter { 479038032Speter register int i; 479138032Speter register char *p; 479238032Speter 479377349Sgshapiro if (StopRequest) 479477349Sgshapiro stop_sendmail(); 479577349Sgshapiro 479690792Sgshapiro fixcrlf(buf, true); 479738032Speter switch (buf[0]) 479838032Speter { 479938032Speter case 'V': /* queue file version */ 480038032Speter qfver = atoi(&buf[1]); 480138032Speter break; 480238032Speter 480338032Speter case 'M': /* error message */ 480438032Speter if ((i = strlen(&buf[1])) >= sizeof statmsg) 480538032Speter i = sizeof statmsg - 1; 480664562Sgshapiro memmove(statmsg, &buf[1], i); 480738032Speter statmsg[i] = '\0'; 480838032Speter break; 480938032Speter 481090792Sgshapiro#if _FFR_QUARANTINE 481190792Sgshapiro case 'q': /* quarantine reason */ 481290792Sgshapiro if ((i = strlen(&buf[1])) >= sizeof quarmsg) 481390792Sgshapiro i = sizeof quarmsg - 1; 481490792Sgshapiro memmove(quarmsg, &buf[1], i); 481590792Sgshapiro quarmsg[i] = '\0'; 481690792Sgshapiro break; 481790792Sgshapiro#endif /* _FFR_QUARANTINE */ 481890792Sgshapiro 481938032Speter case 'B': /* body type */ 482038032Speter if ((i = strlen(&buf[1])) >= sizeof bodytype) 482138032Speter i = sizeof bodytype - 1; 482264562Sgshapiro memmove(bodytype, &buf[1], i); 482338032Speter bodytype[i] = '\0'; 482438032Speter break; 482538032Speter 482638032Speter case 'S': /* sender name */ 482738032Speter if (Verbose) 482864562Sgshapiro { 482990792Sgshapiro (void) sm_io_fprintf(smioout, 483090792Sgshapiro SM_TIME_DEFAULT, 483190792Sgshapiro "%8ld %10ld%c%.12s ", 483290792Sgshapiro dfsize, 483390792Sgshapiro w->w_pri, 483490792Sgshapiro bitset(EF_WARNING, flags) 483590792Sgshapiro ? '+' : ' ', 483690792Sgshapiro ctime(&submittime) + 4); 483764562Sgshapiro prtstr(&buf[1], 78); 483864562Sgshapiro } 483938032Speter else 484064562Sgshapiro { 484190792Sgshapiro (void) sm_io_fprintf(smioout, 484290792Sgshapiro SM_TIME_DEFAULT, 484390792Sgshapiro "%8ld %.16s ", 484490792Sgshapiro dfsize, 484590792Sgshapiro ctime(&submittime)); 484690792Sgshapiro prtstr(&buf[1], 39); 484764562Sgshapiro } 484890792Sgshapiro#if _FFR_QUARANTINE 484990792Sgshapiro if (quarmsg[0] != '\0') 485090792Sgshapiro { 485190792Sgshapiro (void) sm_io_fprintf(smioout, 485290792Sgshapiro SM_TIME_DEFAULT, 485390792Sgshapiro "\n QUARANTINE: %.*s", 485490792Sgshapiro Verbose ? 100 : 60, 485590792Sgshapiro quarmsg); 485690792Sgshapiro quarmsg[0] = '\0'; 485790792Sgshapiro } 485890792Sgshapiro#endif /* _FFR_QUARANTINE */ 485938032Speter if (statmsg[0] != '\0' || bodytype[0] != '\0') 486038032Speter { 486190792Sgshapiro (void) sm_io_fprintf(smioout, 486290792Sgshapiro SM_TIME_DEFAULT, 486390792Sgshapiro "\n %10.10s", 486490792Sgshapiro bodytype); 486538032Speter if (statmsg[0] != '\0') 486690792Sgshapiro (void) sm_io_fprintf(smioout, 486790792Sgshapiro SM_TIME_DEFAULT, 486890792Sgshapiro " (%.*s)", 486990792Sgshapiro Verbose ? 100 : 60, 487090792Sgshapiro statmsg); 487190792Sgshapiro statmsg[0] = '\0'; 487238032Speter } 487338032Speter break; 487438032Speter 487538032Speter case 'C': /* controlling user */ 487638032Speter if (Verbose) 487790792Sgshapiro (void) sm_io_fprintf(smioout, 487890792Sgshapiro SM_TIME_DEFAULT, 487990792Sgshapiro "\n\t\t\t\t\t\t(---%.64s---)", 488090792Sgshapiro &buf[1]); 488138032Speter break; 488238032Speter 488338032Speter case 'R': /* recipient name */ 488438032Speter p = &buf[1]; 488538032Speter if (qfver >= 1) 488638032Speter { 488738032Speter p = strchr(p, ':'); 488838032Speter if (p == NULL) 488938032Speter break; 489038032Speter p++; 489138032Speter } 489238032Speter if (Verbose) 489364562Sgshapiro { 489490792Sgshapiro (void) sm_io_fprintf(smioout, 489590792Sgshapiro SM_TIME_DEFAULT, 489690792Sgshapiro "\n\t\t\t\t\t\t"); 489790792Sgshapiro prtstr(p, 71); 489864562Sgshapiro } 489938032Speter else 490064562Sgshapiro { 490190792Sgshapiro (void) sm_io_fprintf(smioout, 490290792Sgshapiro SM_TIME_DEFAULT, 490390792Sgshapiro "\n\t\t\t\t\t "); 490490792Sgshapiro prtstr(p, 38); 490564562Sgshapiro } 490690792Sgshapiro if (Verbose && statmsg[0] != '\0') 490790792Sgshapiro { 490890792Sgshapiro (void) sm_io_fprintf(smioout, 490990792Sgshapiro SM_TIME_DEFAULT, 491090792Sgshapiro "\n\t\t (%.100s)", 491190792Sgshapiro statmsg); 491290792Sgshapiro statmsg[0] = '\0'; 491390792Sgshapiro } 491438032Speter break; 491538032Speter 491638032Speter case 'T': /* creation time */ 491738032Speter submittime = atol(&buf[1]); 491838032Speter break; 491938032Speter 492038032Speter case 'F': /* flag bits */ 492138032Speter for (p = &buf[1]; *p != '\0'; p++) 492238032Speter { 492338032Speter switch (*p) 492438032Speter { 492538032Speter case 'w': 492638032Speter flags |= EF_WARNING; 492738032Speter break; 492838032Speter } 492938032Speter } 493038032Speter } 493138032Speter } 493238032Speter if (submittime == (time_t) 0) 493390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 493490792Sgshapiro " (no control file)"); 493590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 493690792Sgshapiro (void) sm_io_close(f, SM_TIME_DEFAULT); 493738032Speter } 493864562Sgshapiro return nrequests; 493938032Speter} 494090792Sgshapiro 494190792Sgshapiro#if _FFR_QUARANTINE 494290792Sgshapiro/* 494390792Sgshapiro** QUEUE_LETTER -- get the proper queue letter for the current QueueMode. 494490792Sgshapiro** 494590792Sgshapiro** Parameters: 494690792Sgshapiro** e -- envelope to build it in/from. 494790792Sgshapiro** type -- the file type, used as the first character 494890792Sgshapiro** of the file name. 494990792Sgshapiro** 495090792Sgshapiro** Returns: 495190792Sgshapiro** the letter to use 495290792Sgshapiro*/ 495390792Sgshapiro 495490792Sgshapirostatic char 495590792Sgshapiroqueue_letter(e, type) 495690792Sgshapiro ENVELOPE *e; 495790792Sgshapiro int type; 495890792Sgshapiro{ 495990792Sgshapiro /* Change type according to QueueMode */ 496090792Sgshapiro if (type == ANYQFL_LETTER) 496190792Sgshapiro { 496290792Sgshapiro if (e->e_quarmsg != NULL) 496390792Sgshapiro type = QUARQF_LETTER; 496490792Sgshapiro else 496590792Sgshapiro { 496690792Sgshapiro switch (QueueMode) 496790792Sgshapiro { 496890792Sgshapiro case QM_NORMAL: 496990792Sgshapiro type = NORMQF_LETTER; 497090792Sgshapiro break; 497190792Sgshapiro 497290792Sgshapiro case QM_QUARANTINE: 497390792Sgshapiro type = QUARQF_LETTER; 497490792Sgshapiro break; 497590792Sgshapiro 497690792Sgshapiro case QM_LOST: 497790792Sgshapiro type = LOSEQF_LETTER; 497890792Sgshapiro break; 497990792Sgshapiro 498090792Sgshapiro default: 498190792Sgshapiro /* should never happen */ 498290792Sgshapiro abort(); 498390792Sgshapiro /* NOTREACHED */ 498490792Sgshapiro } 498590792Sgshapiro } 498690792Sgshapiro } 498790792Sgshapiro return type; 498890792Sgshapiro} 498990792Sgshapiro#endif /* _FFR_QUARANTINE */ 499090792Sgshapiro 499190792Sgshapiro/* 499238032Speter** QUEUENAME -- build a file name in the queue directory for this envelope. 499338032Speter** 499438032Speter** Parameters: 499538032Speter** e -- envelope to build it in/from. 499638032Speter** type -- the file type, used as the first character 499738032Speter** of the file name. 499838032Speter** 499938032Speter** Returns: 500064562Sgshapiro** a pointer to the queue name (in a static buffer). 500138032Speter** 500238032Speter** Side Effects: 500364562Sgshapiro** If no id code is already assigned, queuename() will 500464562Sgshapiro** assign an id code with assign_queueid(). If no queue 500564562Sgshapiro** directory is assigned, one will be set with setnewqueue(). 500638032Speter*/ 500738032Speter 500838032Speterchar * 500938032Speterqueuename(e, type) 501038032Speter register ENVELOPE *e; 501138032Speter int type; 501238032Speter{ 501390792Sgshapiro int qd, qg; 501490792Sgshapiro char *sub = "/"; 501590792Sgshapiro char pref[3]; 501664562Sgshapiro static char buf[MAXPATHLEN]; 501738032Speter 501864562Sgshapiro /* Assign an ID if needed */ 501938032Speter if (e->e_id == NULL) 502064562Sgshapiro assign_queueid(e); 502164562Sgshapiro 502290792Sgshapiro#if _FFR_QUARANTINE 502390792Sgshapiro type = queue_letter(e, type); 502490792Sgshapiro#endif /* _FFR_QUARANTINE */ 502564562Sgshapiro 502690792Sgshapiro /* begin of filename */ 502790792Sgshapiro pref[0] = (char) type; 502890792Sgshapiro pref[1] = 'f'; 502990792Sgshapiro pref[2] = '\0'; 503090792Sgshapiro 503190792Sgshapiro /* Assign a queue group/directory if needed */ 503290792Sgshapiro if (type == XSCRPT_LETTER) 503390792Sgshapiro { 503490792Sgshapiro /* 503590792Sgshapiro ** We don't want to call setnewqueue() if we are fetching 503690792Sgshapiro ** the pathname of the transcript file, because setnewqueue 503790792Sgshapiro ** chooses a queue, and sometimes we need to write to the 503890792Sgshapiro ** transcript file before we have gathered enough information 503990792Sgshapiro ** to choose a queue. 504090792Sgshapiro */ 504190792Sgshapiro 504290792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 504390792Sgshapiro { 504490792Sgshapiro if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR) 504590792Sgshapiro { 504690792Sgshapiro e->e_xfqgrp = e->e_qgrp; 504790792Sgshapiro e->e_xfqdir = e->e_qdir; 504890792Sgshapiro } 504990792Sgshapiro else 505090792Sgshapiro { 505190792Sgshapiro e->e_xfqgrp = 0; 505290792Sgshapiro if (Queue[e->e_xfqgrp]->qg_numqueues <= 1) 505390792Sgshapiro e->e_xfqdir = 0; 505490792Sgshapiro else 505590792Sgshapiro { 505690792Sgshapiro e->e_xfqdir = get_rand_mod( 505790792Sgshapiro Queue[e->e_xfqgrp]->qg_numqueues); 505890792Sgshapiro } 505990792Sgshapiro } 506090792Sgshapiro } 506190792Sgshapiro qd = e->e_xfqdir; 506290792Sgshapiro qg = e->e_xfqgrp; 506390792Sgshapiro } 506464562Sgshapiro else 506538032Speter { 506690792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 506790792Sgshapiro setnewqueue(e); 506890792Sgshapiro if (type == DATAFL_LETTER) 506990792Sgshapiro { 507090792Sgshapiro qd = e->e_dfqdir; 507190792Sgshapiro qg = e->e_dfqgrp; 507290792Sgshapiro } 507390792Sgshapiro else 507490792Sgshapiro { 507590792Sgshapiro qd = e->e_qdir; 507690792Sgshapiro qg = e->e_qgrp; 507790792Sgshapiro } 507890792Sgshapiro } 507990792Sgshapiro 508094334Sgshapiro /* xf files always have a valid qd and qg picked above */ 508194334Sgshapiro if (e->e_qdir == NOQDIR && type != XSCRPT_LETTER) 508290792Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id); 508390792Sgshapiro else 508490792Sgshapiro { 508564562Sgshapiro switch (type) 508664562Sgshapiro { 508790792Sgshapiro case DATAFL_LETTER: 508890792Sgshapiro if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 508990792Sgshapiro sub = "/df/"; 509064562Sgshapiro break; 509138032Speter 509290792Sgshapiro#if _FFR_QUARANTINE 509390792Sgshapiro case QUARQF_LETTER: 509490792Sgshapiro#endif /* _FFR_QUARANTINE */ 509571345Sgshapiro case TEMPQF_LETTER: 509690792Sgshapiro case NEWQFL_LETTER: 509771345Sgshapiro case LOSEQF_LETTER: 509890792Sgshapiro case NORMQF_LETTER: 509990792Sgshapiro if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 510090792Sgshapiro sub = "/qf/"; 510164562Sgshapiro break; 510264562Sgshapiro 510390792Sgshapiro case XSCRPT_LETTER: 510490792Sgshapiro if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 510590792Sgshapiro sub = "/xf/"; 510664562Sgshapiro break; 510790792Sgshapiro 510890792Sgshapiro default: 510990792Sgshapiro sm_abort("queuename: bad queue file type %d", type); 511038032Speter } 511138032Speter 511290792Sgshapiro (void) sm_strlcpyn(buf, sizeof buf, 4, 511390792Sgshapiro Queue[qg]->qg_qpaths[qd].qp_name, 511490792Sgshapiro sub, pref, e->e_id); 511564562Sgshapiro } 511638032Speter 511764562Sgshapiro if (tTd(7, 2)) 511890792Sgshapiro sm_dprintf("queuename: %s\n", buf); 511964562Sgshapiro return buf; 512064562Sgshapiro} 512190792Sgshapiro/* 512264562Sgshapiro** ASSIGN_QUEUEID -- assign a queue ID for this envelope. 512364562Sgshapiro** 512464562Sgshapiro** Assigns an id code if one does not already exist. 512564562Sgshapiro** This code assumes that nothing will remain in the queue for 512664562Sgshapiro** longer than 60 years. It is critical that files with the given 512790792Sgshapiro** name do not already exist in the queue. 512890792Sgshapiro** [No longer initializes e_qdir to NOQDIR.] 512964562Sgshapiro** 513064562Sgshapiro** Parameters: 513164562Sgshapiro** e -- envelope to set it in. 513264562Sgshapiro** 513364562Sgshapiro** Returns: 513464562Sgshapiro** none. 513564562Sgshapiro*/ 513638032Speter 513777349Sgshapirostatic const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"; 513877349Sgshapiro# define QIC_LEN 60 513990792Sgshapiro# define queuenextid() CurrentPid 514038032Speter 514190792Sgshapiro 514264562Sgshapirovoid 514364562Sgshapiroassign_queueid(e) 514464562Sgshapiro register ENVELOPE *e; 514564562Sgshapiro{ 514690792Sgshapiro pid_t pid = queuenextid(); 514790792Sgshapiro static int cX = 0; 514864562Sgshapiro static long random_offset; 514964562Sgshapiro struct tm *tm; 515064562Sgshapiro char idbuf[MAXQFNAME - 2]; 515190792Sgshapiro int seq; 515238032Speter 515364562Sgshapiro if (e->e_id != NULL) 515464562Sgshapiro return; 515538032Speter 515664562Sgshapiro /* see if we need to get a new base time/pid */ 515790792Sgshapiro if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 || 515890792Sgshapiro LastQueuePid != pid) 515964562Sgshapiro { 516064562Sgshapiro time_t then = LastQueueTime; 516164562Sgshapiro 516264562Sgshapiro /* if the first time through, pick a random offset */ 516364562Sgshapiro if (LastQueueTime == 0) 516464562Sgshapiro random_offset = get_random(); 516564562Sgshapiro 516664562Sgshapiro while ((LastQueueTime = curtime()) == then && 516764562Sgshapiro LastQueuePid == pid) 516838032Speter { 516964562Sgshapiro (void) sleep(1); 517038032Speter } 517190792Sgshapiro LastQueuePid = queuenextid(); 517264562Sgshapiro cX = 0; 517338032Speter } 517490792Sgshapiro 517590792Sgshapiro /* 517690792Sgshapiro ** Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1. 517790792Sgshapiro ** This lets us generate up to QIC_LEN*QIC_LEN unique queue ids 517890792Sgshapiro ** per second, per process. With envelope splitting, 517990792Sgshapiro ** a single message can consume many queue ids. 518090792Sgshapiro */ 518190792Sgshapiro 518290792Sgshapiro seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN)); 518390792Sgshapiro ++cX; 518464562Sgshapiro if (tTd(7, 50)) 518590792Sgshapiro sm_dprintf("assign_queueid: random_offset = %ld (%d)\n", 518690792Sgshapiro random_offset, seq); 518738032Speter 518864562Sgshapiro tm = gmtime(&LastQueueTime); 518977349Sgshapiro idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN]; 519077349Sgshapiro idbuf[1] = QueueIdChars[tm->tm_mon]; 519177349Sgshapiro idbuf[2] = QueueIdChars[tm->tm_mday]; 519277349Sgshapiro idbuf[3] = QueueIdChars[tm->tm_hour]; 519377349Sgshapiro idbuf[4] = QueueIdChars[tm->tm_min]; 519477349Sgshapiro idbuf[5] = QueueIdChars[tm->tm_sec]; 519590792Sgshapiro idbuf[6] = QueueIdChars[seq / QIC_LEN]; 519690792Sgshapiro idbuf[7] = QueueIdChars[seq % QIC_LEN]; 519790792Sgshapiro (void) sm_snprintf(&idbuf[8], sizeof idbuf - 8, "%06d", 519890792Sgshapiro (int) LastQueuePid); 519990792Sgshapiro e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf); 520090792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 520190792Sgshapiro#if 0 520290792Sgshapiro /* XXX: inherited from MainEnvelope */ 520390792Sgshapiro e->e_qgrp = NOQGRP; /* too early to do anything else */ 520490792Sgshapiro e->e_qdir = NOQDIR; 520590792Sgshapiro e->e_xfqgrp = NOQGRP; 520690792Sgshapiro#endif /* 0 */ 520790792Sgshapiro#if _FFR_QUARANTINE 520890792Sgshapiro /* New ID means it's not on disk yet */ 520990792Sgshapiro e->e_qfletter = '\0'; 521090792Sgshapiro#endif /* _FFR_QUARANTINE */ 521164562Sgshapiro if (tTd(7, 1)) 521290792Sgshapiro sm_dprintf("assign_queueid: assigned id %s, e=%p\n", 521390792Sgshapiro e->e_id, e); 521464562Sgshapiro if (LogLevel > 93) 521564562Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); 521638032Speter} 521790792Sgshapiro/* 521864562Sgshapiro** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second 521964562Sgshapiro** 522064562Sgshapiro** Make sure one PID can't be used by two processes in any one second. 522164562Sgshapiro** 522264562Sgshapiro** If the system rotates PIDs fast enough, may get the 522364562Sgshapiro** same pid in the same second for two distinct processes. 522464562Sgshapiro** This will interfere with the queue file naming system. 522564562Sgshapiro** 522664562Sgshapiro** Parameters: 522764562Sgshapiro** none 522864562Sgshapiro** 522964562Sgshapiro** Returns: 523064562Sgshapiro** none 523164562Sgshapiro*/ 523290792Sgshapiro 523364562Sgshapirovoid 523464562Sgshapirosync_queue_time() 523564562Sgshapiro{ 523690792Sgshapiro#if FAST_PID_RECYCLE 523764562Sgshapiro if (OpMode != MD_TEST && 523864562Sgshapiro OpMode != MD_VERIFY && 523964562Sgshapiro LastQueueTime > 0 && 524090792Sgshapiro LastQueuePid == CurrentPid && 524164562Sgshapiro curtime() == LastQueueTime) 524264562Sgshapiro (void) sleep(1); 524390792Sgshapiro#endif /* FAST_PID_RECYCLE */ 524464562Sgshapiro} 524590792Sgshapiro/* 524638032Speter** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 524738032Speter** 524838032Speter** Parameters: 524938032Speter** e -- the envelope to unlock. 525038032Speter** 525138032Speter** Returns: 525238032Speter** none 525338032Speter** 525438032Speter** Side Effects: 525538032Speter** unlocks the queue for `e'. 525638032Speter*/ 525738032Speter 525838032Spetervoid 525938032Speterunlockqueue(e) 526038032Speter ENVELOPE *e; 526138032Speter{ 526238032Speter if (tTd(51, 4)) 526390792Sgshapiro sm_dprintf("unlockqueue(%s)\n", 526438032Speter e->e_id == NULL ? "NOQUEUE" : e->e_id); 526538032Speter 526664562Sgshapiro 526738032Speter /* if there is a lock file in the envelope, close it */ 526838032Speter if (e->e_lockfp != NULL) 526990792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 527038032Speter e->e_lockfp = NULL; 527138032Speter 527238032Speter /* don't create a queue id if we don't already have one */ 527338032Speter if (e->e_id == NULL) 527438032Speter return; 527538032Speter 527638032Speter /* remove the transcript */ 527738032Speter if (LogLevel > 87) 527838032Speter sm_syslog(LOG_DEBUG, e->e_id, "unlock"); 527938032Speter if (!tTd(51, 104)) 528090792Sgshapiro (void) xunlink(queuename(e, XSCRPT_LETTER)); 528138032Speter} 528290792Sgshapiro/* 528338032Speter** SETCTLUSER -- create a controlling address 528438032Speter** 528538032Speter** Create a fake "address" given only a local login name; this is 528638032Speter** used as a "controlling user" for future recipient addresses. 528738032Speter** 528838032Speter** Parameters: 528938032Speter** user -- the user name of the controlling user. 529090792Sgshapiro** qfver -- the version stamp of this queue file. 529190792Sgshapiro** e -- envelope 529238032Speter** 529338032Speter** Returns: 529490792Sgshapiro** An address descriptor for the controlling user, 529590792Sgshapiro** using storage allocated from e->e_rpool. 529638032Speter** 529738032Speter*/ 529838032Speter 529964562Sgshapirostatic ADDRESS * 530090792Sgshapirosetctluser(user, qfver, e) 530138032Speter char *user; 530238032Speter int qfver; 530390792Sgshapiro ENVELOPE *e; 530438032Speter{ 530538032Speter register ADDRESS *a; 530638032Speter struct passwd *pw; 530738032Speter char *p; 530838032Speter 530938032Speter /* 531038032Speter ** See if this clears our concept of controlling user. 531138032Speter */ 531238032Speter 531338032Speter if (user == NULL || *user == '\0') 531438032Speter return NULL; 531538032Speter 531638032Speter /* 531738032Speter ** Set up addr fields for controlling user. 531838032Speter */ 531938032Speter 532090792Sgshapiro a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a); 532164562Sgshapiro memset((char *) a, '\0', sizeof *a); 532238032Speter 532390792Sgshapiro if (*user == ':') 532438032Speter { 532538032Speter p = &user[1]; 532690792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, p); 532738032Speter } 532838032Speter else 532938032Speter { 533038032Speter p = strtok(user, ":"); 533190792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, user); 533238032Speter if (qfver >= 2) 533338032Speter { 533438032Speter if ((p = strtok(NULL, ":")) != NULL) 533538032Speter a->q_uid = atoi(p); 533638032Speter if ((p = strtok(NULL, ":")) != NULL) 533738032Speter a->q_gid = atoi(p); 533838032Speter if ((p = strtok(NULL, ":")) != NULL) 533980785Sgshapiro { 534080785Sgshapiro char *o; 534180785Sgshapiro 534238032Speter a->q_flags |= QGOODUID; 534380785Sgshapiro 534480785Sgshapiro /* if there is another ':': restore it */ 534580785Sgshapiro if ((o = strtok(NULL, ":")) != NULL && o > p) 534680785Sgshapiro o[-1] = ':'; 534780785Sgshapiro } 534838032Speter } 534938032Speter else if ((pw = sm_getpwnam(user)) != NULL) 535038032Speter { 535166494Sgshapiro if (*pw->pw_dir == '\0') 535266494Sgshapiro a->q_home = NULL; 535366494Sgshapiro else if (strcmp(pw->pw_dir, "/") == 0) 535438032Speter a->q_home = ""; 535538032Speter else 535690792Sgshapiro a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir); 535738032Speter a->q_uid = pw->pw_uid; 535838032Speter a->q_gid = pw->pw_gid; 535938032Speter a->q_flags |= QGOODUID; 536038032Speter } 536138032Speter } 536238032Speter 536364562Sgshapiro a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 536438032Speter a->q_mailer = LocalMailer; 536538032Speter if (p == NULL) 536690792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); 536738032Speter else 536890792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p); 536938032Speter return a; 537038032Speter} 537190792Sgshapiro/* 537290792Sgshapiro** LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know 537338032Speter** 537438032Speter** Parameters: 537538032Speter** e -- the envelope (e->e_id will be used). 537638032Speter** why -- reported to whomever can hear. 537738032Speter** 537838032Speter** Returns: 537938032Speter** none. 538038032Speter*/ 538138032Speter 538238032Spetervoid 538338032Speterloseqfile(e, why) 538438032Speter register ENVELOPE *e; 538538032Speter char *why; 538638032Speter{ 538790792Sgshapiro bool loseit = true; 538838032Speter char *p; 538964562Sgshapiro char buf[MAXPATHLEN]; 539038032Speter 539138032Speter if (e == NULL || e->e_id == NULL) 539238032Speter return; 539390792Sgshapiro p = queuename(e, ANYQFL_LETTER); 539490792Sgshapiro if (sm_strlcpy(buf, p, sizeof buf) >= sizeof buf) 539538032Speter return; 539690792Sgshapiro if (!bitset(EF_INQUEUE, e->e_flags)) 539790792Sgshapiro queueup(e, false, true); 539890792Sgshapiro#if _FFR_QUARANTINE 539990792Sgshapiro else if (QueueMode == QM_LOST) 540090792Sgshapiro loseit = false; 540190792Sgshapiro#endif /* _FFR_QUARANTINE */ 540290792Sgshapiro 540390792Sgshapiro /* if already lost, no need to re-lose */ 540490792Sgshapiro if (loseit) 540590792Sgshapiro { 540690792Sgshapiro p = queuename(e, LOSEQF_LETTER); 540790792Sgshapiro if (rename(buf, p) < 0) 540890792Sgshapiro syserr("cannot rename(%s, %s), uid=%d", 540998121Sgshapiro buf, p, (int) geteuid()); 541090792Sgshapiro else if (LogLevel > 0) 541190792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 541290792Sgshapiro "Losing %s: %s", buf, why); 541390792Sgshapiro } 541490792Sgshapiro if (e->e_dfp != NULL) 541590792Sgshapiro { 541690792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 541790792Sgshapiro e->e_dfp = NULL; 541890792Sgshapiro } 541990792Sgshapiro e->e_flags &= ~EF_HAS_DF; 542038032Speter} 542190792Sgshapiro/* 542290792Sgshapiro** NAME2QID -- translate a queue group name to a queue group id 542390792Sgshapiro** 542490792Sgshapiro** Parameters: 542590792Sgshapiro** queuename -- name of queue group. 542690792Sgshapiro** 542790792Sgshapiro** Returns: 542890792Sgshapiro** queue group id if found. 542990792Sgshapiro** NOQGRP otherwise. 543090792Sgshapiro*/ 543190792Sgshapiro 543290792Sgshapiroint 543390792Sgshapironame2qid(queuename) 543490792Sgshapiro char *queuename; 543590792Sgshapiro{ 543690792Sgshapiro register STAB *s; 543790792Sgshapiro 543890792Sgshapiro s = stab(queuename, ST_QUEUE, ST_FIND); 543990792Sgshapiro if (s == NULL) 544090792Sgshapiro return NOQGRP; 544190792Sgshapiro return s->s_quegrp->qg_index; 544290792Sgshapiro} 544390792Sgshapiro/* 544464562Sgshapiro** QID_PRINTNAME -- create externally printable version of queue id 544564562Sgshapiro** 544664562Sgshapiro** Parameters: 544764562Sgshapiro** e -- the envelope. 544864562Sgshapiro** 544964562Sgshapiro** Returns: 545064562Sgshapiro** a printable version 545164562Sgshapiro*/ 545264562Sgshapiro 545364562Sgshapirochar * 545464562Sgshapiroqid_printname(e) 545564562Sgshapiro ENVELOPE *e; 545664562Sgshapiro{ 545764562Sgshapiro char *id; 545864562Sgshapiro static char idbuf[MAXQFNAME + 34]; 545964562Sgshapiro 546064562Sgshapiro if (e == NULL) 546164562Sgshapiro return ""; 546264562Sgshapiro 546364562Sgshapiro if (e->e_id == NULL) 546464562Sgshapiro id = ""; 546564562Sgshapiro else 546664562Sgshapiro id = e->e_id; 546764562Sgshapiro 546890792Sgshapiro if (e->e_qdir == NOQDIR) 546964562Sgshapiro return id; 547064562Sgshapiro 547190792Sgshapiro (void) sm_snprintf(idbuf, sizeof idbuf, "%.32s/%s", 547290792Sgshapiro Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name, 547390792Sgshapiro id); 547464562Sgshapiro return idbuf; 547564562Sgshapiro} 547690792Sgshapiro/* 547790792Sgshapiro** QID_PRINTQUEUE -- create full version of queue directory for data files 547864562Sgshapiro** 547964562Sgshapiro** Parameters: 548090792Sgshapiro** qgrp -- index in queue group. 548190792Sgshapiro** qdir -- the short version of the queue directory 548264562Sgshapiro** 548364562Sgshapiro** Returns: 548490792Sgshapiro** the full pathname to the queue (might point to a static var) 548564562Sgshapiro*/ 548664562Sgshapiro 548764562Sgshapirochar * 548890792Sgshapiroqid_printqueue(qgrp, qdir) 548990792Sgshapiro int qgrp; 549090792Sgshapiro int qdir; 549164562Sgshapiro{ 549264562Sgshapiro char *subdir; 549364562Sgshapiro static char dir[MAXPATHLEN]; 549464562Sgshapiro 549590792Sgshapiro if (qdir == NOQDIR) 549690792Sgshapiro return Queue[qgrp]->qg_qdir; 549764562Sgshapiro 549890792Sgshapiro if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0) 549964562Sgshapiro subdir = NULL; 550064562Sgshapiro else 550190792Sgshapiro subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name; 550264562Sgshapiro 550390792Sgshapiro (void) sm_strlcpyn(dir, sizeof dir, 4, 550490792Sgshapiro Queue[qgrp]->qg_qdir, 550564562Sgshapiro subdir == NULL ? "" : "/", 550664562Sgshapiro subdir == NULL ? "" : subdir, 550790792Sgshapiro (bitset(QP_SUBDF, 550890792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 550990792Sgshapiro ? "/df" : "")); 551064562Sgshapiro return dir; 551164562Sgshapiro} 551290792Sgshapiro 551390792Sgshapiro/* 551490792Sgshapiro** PICKQDIR -- Pick a queue directory from a queue group 551564562Sgshapiro** 551690792Sgshapiro** Parameters: 551790792Sgshapiro** qg -- queue group 551890792Sgshapiro** fsize -- file size in bytes 551990792Sgshapiro** e -- envelope, or NULL 552064562Sgshapiro** 552190792Sgshapiro** Result: 552290792Sgshapiro** NOQDIR if no queue directory in qg has enough free space to 552390792Sgshapiro** hold a file of size 'fsize', otherwise the index of 552490792Sgshapiro** a randomly selected queue directory which resides on a 552590792Sgshapiro** file system with enough disk space. 552690792Sgshapiro** XXX This could be extended to select a queuedir with 552790792Sgshapiro** a few (the fewest?) number of entries. That data 552890792Sgshapiro** is available if shared memory is used. 552964562Sgshapiro** 553090792Sgshapiro** Side Effects: 553190792Sgshapiro** If the request fails and e != NULL then sm_syslog is called. 553290792Sgshapiro*/ 553390792Sgshapiro 553490792Sgshapiroint 553590792Sgshapiropickqdir(qg, fsize, e) 553690792Sgshapiro QUEUEGRP *qg; 553790792Sgshapiro long fsize; 553890792Sgshapiro ENVELOPE *e; 553990792Sgshapiro{ 554090792Sgshapiro int qdir; 554190792Sgshapiro int i; 554290792Sgshapiro long avail = 0; 554390792Sgshapiro 554490792Sgshapiro /* Pick a random directory, as a starting point. */ 554590792Sgshapiro if (qg->qg_numqueues <= 1) 554690792Sgshapiro qdir = 0; 554790792Sgshapiro else 554890792Sgshapiro qdir = get_rand_mod(qg->qg_numqueues); 554990792Sgshapiro 555090792Sgshapiro if (MinBlocksFree <= 0 && fsize <= 0) 555190792Sgshapiro return qdir; 555290792Sgshapiro 555390792Sgshapiro /* 555490792Sgshapiro ** Now iterate over the queue directories, 555590792Sgshapiro ** looking for a directory with enough space for this message. 555690792Sgshapiro */ 555790792Sgshapiro 555890792Sgshapiro i = qdir; 555990792Sgshapiro do 556090792Sgshapiro { 556190792Sgshapiro QPATHS *qp = &qg->qg_qpaths[i]; 556290792Sgshapiro long needed = 0; 556390792Sgshapiro long fsavail = 0; 556490792Sgshapiro 556590792Sgshapiro if (fsize > 0) 556690792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx) 556790792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx) 556890792Sgshapiro > 0) ? 1 : 0); 556990792Sgshapiro if (MinBlocksFree > 0) 557090792Sgshapiro needed += MinBlocksFree; 557190792Sgshapiro fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx); 557290792Sgshapiro#if SM_CONF_SHM 557390792Sgshapiro if (fsavail <= 0) 557490792Sgshapiro { 557590792Sgshapiro long blksize; 557690792Sgshapiro 557790792Sgshapiro /* 557890792Sgshapiro ** might be not correctly updated, 557990792Sgshapiro ** let's try to get the info directly. 558090792Sgshapiro */ 558190792Sgshapiro 558290792Sgshapiro fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx), 558390792Sgshapiro &blksize); 558490792Sgshapiro if (fsavail < 0) 558590792Sgshapiro fsavail = 0; 558690792Sgshapiro } 558790792Sgshapiro#endif /* SM_CONF_SHM */ 558890792Sgshapiro if (needed <= fsavail) 558990792Sgshapiro return i; 559090792Sgshapiro if (avail < fsavail) 559190792Sgshapiro avail = fsavail; 559290792Sgshapiro 559390792Sgshapiro if (qg->qg_numqueues > 0) 559490792Sgshapiro i = (i + 1) % qg->qg_numqueues; 559590792Sgshapiro } while (i != qdir); 559690792Sgshapiro 559790792Sgshapiro if (e != NULL && LogLevel > 0) 559890792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 559990792Sgshapiro "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld", 560090792Sgshapiro CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, 560190792Sgshapiro fsize, MinBlocksFree, 560290792Sgshapiro qg->qg_qdir, avail); 560390792Sgshapiro return NOQDIR; 560490792Sgshapiro} 560590792Sgshapiro/* 560690792Sgshapiro** SETNEWQUEUE -- Sets a new queue group and directory 560790792Sgshapiro** 560890792Sgshapiro** Assign a queue group and directory to an envelope and store the 560990792Sgshapiro** directory in e->e_qdir. 561090792Sgshapiro** 561164562Sgshapiro** Parameters: 561264562Sgshapiro** e -- envelope to assign a queue for. 561364562Sgshapiro** 561464562Sgshapiro** Returns: 561590792Sgshapiro** true if successful 561690792Sgshapiro** false otherwise 561790792Sgshapiro** 561890792Sgshapiro** Side Effects: 561990792Sgshapiro** On success, e->e_qgrp and e->e_qdir are non-negative. 562090792Sgshapiro** On failure (not enough disk space), 562190792Sgshapiro** e->qgrp = NOQGRP, e->e_qdir = NOQDIR 562290792Sgshapiro** and usrerr() is invoked (which could raise an exception). 562364562Sgshapiro*/ 562464562Sgshapiro 562590792Sgshapirobool 562664562Sgshapirosetnewqueue(e) 562764562Sgshapiro ENVELOPE *e; 562864562Sgshapiro{ 562964562Sgshapiro if (tTd(41, 20)) 563090792Sgshapiro sm_dprintf("setnewqueue: called\n"); 563164562Sgshapiro 563290792Sgshapiro /* not set somewhere else */ 563390792Sgshapiro if (e->e_qgrp == NOQGRP) 563464562Sgshapiro { 563590792Sgshapiro /* 563690792Sgshapiro ** Use the queue group of the first recipient, as set by 563790792Sgshapiro ** the "queuegroup" rule set. If that is not defined, then 563890792Sgshapiro ** use the queue group of the mailer of the first recipient. 563990792Sgshapiro ** If that is not defined either, then use the default 564090792Sgshapiro ** queue group. 564190792Sgshapiro */ 564290792Sgshapiro 564390792Sgshapiro if (e->e_sendqueue == NULL) 564490792Sgshapiro e->e_qgrp = 0; 564590792Sgshapiro else if (e->e_sendqueue->q_qgrp >= 0) 564690792Sgshapiro e->e_qgrp = e->e_sendqueue->q_qgrp; 564790792Sgshapiro else if (e->e_sendqueue->q_mailer != NULL && 564890792Sgshapiro ISVALIDQGRP(e->e_sendqueue->q_mailer->m_qgrp)) 564990792Sgshapiro e->e_qgrp = e->e_sendqueue->q_mailer->m_qgrp; 565090792Sgshapiro else 565190792Sgshapiro e->e_qgrp = 0; 565290792Sgshapiro e->e_dfqgrp = e->e_qgrp; 565390792Sgshapiro } 565490792Sgshapiro 565590792Sgshapiro if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir)) 565690792Sgshapiro { 565764562Sgshapiro if (tTd(41, 20)) 565890792Sgshapiro sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n", 565990792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 566090792Sgshapiro return true; 566164562Sgshapiro } 566264562Sgshapiro 566390792Sgshapiro filesys_update(); 566490792Sgshapiro e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e); 566590792Sgshapiro if (e->e_qdir == NOQDIR) 566664562Sgshapiro { 566790792Sgshapiro e->e_qgrp = NOQGRP; 566890792Sgshapiro if (!bitset(EF_FATALERRS, e->e_flags)) 566990792Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 567090792Sgshapiro e->e_flags |= EF_FATALERRS; 567190792Sgshapiro return false; 567264562Sgshapiro } 567364562Sgshapiro 567464562Sgshapiro if (tTd(41, 3)) 567590792Sgshapiro sm_dprintf("setnewqueue: Assigned queue directory %s\n", 567690792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 567790792Sgshapiro 567890792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 567990792Sgshapiro { 568090792Sgshapiro e->e_xfqgrp = e->e_qgrp; 568190792Sgshapiro e->e_xfqdir = e->e_qdir; 568290792Sgshapiro } 568390792Sgshapiro e->e_dfqdir = e->e_qdir; 568490792Sgshapiro return true; 568564562Sgshapiro} 568690792Sgshapiro/* 568764562Sgshapiro** CHKQDIR -- check a queue directory 568864562Sgshapiro** 568964562Sgshapiro** Parameters: 569064562Sgshapiro** name -- name of queue directory 569164562Sgshapiro** sff -- flags for safefile() 569264562Sgshapiro** 569364562Sgshapiro** Returns: 569464562Sgshapiro** is it a queue directory? 569564562Sgshapiro*/ 569664562Sgshapiro 569764562Sgshapirostatic bool 569864562Sgshapirochkqdir(name, sff) 569964562Sgshapiro char *name; 570064562Sgshapiro long sff; 570164562Sgshapiro{ 570264562Sgshapiro struct stat statb; 570364562Sgshapiro int i; 570464562Sgshapiro 570566494Sgshapiro /* skip over . and .. directories */ 570666494Sgshapiro if (name[0] == '.' && 570777349Sgshapiro (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) 570890792Sgshapiro return false; 570990792Sgshapiro#if HASLSTAT 571064562Sgshapiro if (lstat(name, &statb) < 0) 571190792Sgshapiro#else /* HASLSTAT */ 571264562Sgshapiro if (stat(name, &statb) < 0) 571390792Sgshapiro#endif /* HASLSTAT */ 571464562Sgshapiro { 571564562Sgshapiro if (tTd(41, 2)) 571690792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 571790792Sgshapiro name, sm_errstring(errno)); 571890792Sgshapiro return false; 571964562Sgshapiro } 572090792Sgshapiro#if HASLSTAT 572164562Sgshapiro if (S_ISLNK(statb.st_mode)) 572264562Sgshapiro { 572364562Sgshapiro /* 572464562Sgshapiro ** For a symlink we need to make sure the 572564562Sgshapiro ** target is a directory 572664562Sgshapiro */ 572790792Sgshapiro 572864562Sgshapiro if (stat(name, &statb) < 0) 572964562Sgshapiro { 573064562Sgshapiro if (tTd(41, 2)) 573190792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 573290792Sgshapiro name, sm_errstring(errno)); 573390792Sgshapiro return false; 573464562Sgshapiro } 573564562Sgshapiro } 573690792Sgshapiro#endif /* HASLSTAT */ 573764562Sgshapiro 573864562Sgshapiro if (!S_ISDIR(statb.st_mode)) 573964562Sgshapiro { 574064562Sgshapiro if (tTd(41, 2)) 574190792Sgshapiro sm_dprintf("chkqdir: \"%s\": Not a directory\n", 574264562Sgshapiro name); 574390792Sgshapiro return false; 574464562Sgshapiro } 574564562Sgshapiro 574664562Sgshapiro /* Print a warning if unsafe (but still use it) */ 574790792Sgshapiro /* XXX do this only if we want the warning? */ 574864562Sgshapiro i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0); 574998121Sgshapiro if (i != 0) 575098121Sgshapiro { 575198121Sgshapiro if (tTd(41, 2)) 575298121Sgshapiro sm_dprintf("chkqdir: \"%s\": Not safe: %s\n", 575398121Sgshapiro name, sm_errstring(i)); 575498121Sgshapiro#if _FFR_CHK_QUEUE 575598121Sgshapiro if (LogLevel > 8) 575698121Sgshapiro sm_syslog(LOG_WARNING, NOQID, 575798121Sgshapiro "queue directory \"%s\": Not safe: %s", 575898121Sgshapiro name, sm_errstring(i)); 575998121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 576098121Sgshapiro } 576190792Sgshapiro return true; 576264562Sgshapiro} 576390792Sgshapiro/* 576464562Sgshapiro** MULTIQUEUE_CACHE -- cache a list of paths to queues. 576564562Sgshapiro** 576664562Sgshapiro** Each potential queue is checked as the cache is built. 576764562Sgshapiro** Thereafter, each is blindly trusted. 576864562Sgshapiro** Note that we can be called again after a timeout to rebuild 576964562Sgshapiro** (although code for that is not ready yet). 577064562Sgshapiro** 577164562Sgshapiro** Parameters: 577290792Sgshapiro** basedir -- base of all queue directories. 577390792Sgshapiro** blen -- strlen(basedir). 577490792Sgshapiro** qg -- queue group. 577590792Sgshapiro** qn -- number of queue directories already cached. 577690792Sgshapiro** phash -- pointer to hash value over queue dirs. 577790792Sgshapiro#if SM_CONF_SHM 577890792Sgshapiro** only used if shared memory is active. 577990792Sgshapiro#endif * SM_CONF_SHM * 578064562Sgshapiro** 578164562Sgshapiro** Returns: 578290792Sgshapiro** new number of queue directories. 578364562Sgshapiro*/ 578464562Sgshapiro 578590792Sgshapiro#define INITIAL_SLOTS 20 578690792Sgshapiro#define ADD_SLOTS 10 578790792Sgshapiro 578890792Sgshapirostatic int 578990792Sgshapiromultiqueue_cache(basedir, blen, qg, qn, phash) 579090792Sgshapiro char *basedir; 579190792Sgshapiro int blen; 579290792Sgshapiro QUEUEGRP *qg; 579390792Sgshapiro int qn; 579490792Sgshapiro unsigned int *phash; 579564562Sgshapiro{ 579664562Sgshapiro char *cp; 579764562Sgshapiro int i, len; 579864562Sgshapiro int slotsleft = 0; 579964562Sgshapiro long sff = SFF_ANYFILE; 580064562Sgshapiro char qpath[MAXPATHLEN]; 580164562Sgshapiro char subdir[MAXPATHLEN]; 580290792Sgshapiro char prefix[MAXPATHLEN]; /* dir relative to basedir */ 580364562Sgshapiro 580464562Sgshapiro if (tTd(41, 20)) 580590792Sgshapiro sm_dprintf("multiqueue_cache: called\n"); 580664562Sgshapiro 580790792Sgshapiro /* Initialize to current directory */ 580890792Sgshapiro prefix[0] = '.'; 580990792Sgshapiro prefix[1] = '\0'; 581090792Sgshapiro if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL) 581164562Sgshapiro { 581290792Sgshapiro for (i = 0; i < qg->qg_numqueues; i++) 581364562Sgshapiro { 581490792Sgshapiro if (qg->qg_qpaths[i].qp_name != NULL) 581590792Sgshapiro (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */ 581664562Sgshapiro } 581790792Sgshapiro (void) sm_free((char *) qg->qg_qpaths); /* XXX */ 581890792Sgshapiro qg->qg_qpaths = NULL; 581990792Sgshapiro qg->qg_numqueues = 0; 582064562Sgshapiro } 582164562Sgshapiro 582264562Sgshapiro /* If running as root, allow safedirpath() checks to use privs */ 582364562Sgshapiro if (RunAsUid == 0) 582464562Sgshapiro sff |= SFF_ROOTOK; 582598121Sgshapiro#if _FFR_CHK_QUEUE 582698121Sgshapiro sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES; 582798121Sgshapiro if (!UseMSP) 582898121Sgshapiro sff |= SFF_NOGWFILES; 582998121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 583064562Sgshapiro 583190792Sgshapiro if (!SM_IS_DIR_START(qg->qg_qdir)) 583290792Sgshapiro { 583390792Sgshapiro /* 583490792Sgshapiro ** XXX we could add basedir, but then we have to realloc() 583590792Sgshapiro ** the string... Maybe another time. 583690792Sgshapiro */ 583790792Sgshapiro 583890792Sgshapiro syserr("QueuePath %s not absolute", qg->qg_qdir); 583990792Sgshapiro ExitStat = EX_CONFIG; 584090792Sgshapiro return qn; 584190792Sgshapiro } 584290792Sgshapiro 584390792Sgshapiro /* qpath: directory of current workgroup */ 584490792Sgshapiro len = sm_strlcpy(qpath, qg->qg_qdir, sizeof qpath); 584590792Sgshapiro if (len >= sizeof qpath) 584690792Sgshapiro { 584790792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 584890792Sgshapiro qg->qg_qdir, (int) sizeof qpath); 584990792Sgshapiro ExitStat = EX_CONFIG; 585090792Sgshapiro return qn; 585190792Sgshapiro } 585290792Sgshapiro 585390792Sgshapiro /* begin of qpath must be same as basedir */ 585490792Sgshapiro if (strncmp(basedir, qpath, blen) != 0 && 585590792Sgshapiro (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1)) 585690792Sgshapiro { 585790792Sgshapiro syserr("QueuePath %s not subpath of QueueDirectory %s", 585890792Sgshapiro qpath, basedir); 585990792Sgshapiro ExitStat = EX_CONFIG; 586090792Sgshapiro return qn; 586190792Sgshapiro } 586290792Sgshapiro 586390792Sgshapiro /* Do we have a nested subdirectory? */ 586490792Sgshapiro if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL) 586590792Sgshapiro { 586690792Sgshapiro 586790792Sgshapiro /* Copy subdirectory into prefix for later use */ 586890792Sgshapiro if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof prefix) >= 586990792Sgshapiro sizeof prefix) 587090792Sgshapiro { 587190792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 587290792Sgshapiro qg->qg_qdir, (int) sizeof qpath); 587390792Sgshapiro ExitStat = EX_CONFIG; 587490792Sgshapiro return qn; 587590792Sgshapiro } 587690792Sgshapiro cp = SM_LAST_DIR_DELIM(prefix); 587790792Sgshapiro SM_ASSERT(cp != NULL); 587890792Sgshapiro *cp = '\0'; /* cut off trailing / */ 587990792Sgshapiro } 588090792Sgshapiro 588190792Sgshapiro /* This is guaranteed by the basedir check above */ 588290792Sgshapiro SM_ASSERT(len >= blen - 1); 588390792Sgshapiro cp = &qpath[len - 1]; 588464562Sgshapiro if (*cp == '*') 588564562Sgshapiro { 588690792Sgshapiro register DIR *dp; 588790792Sgshapiro register struct dirent *d; 588890792Sgshapiro int off; 588990792Sgshapiro char *delim; 589090792Sgshapiro char relpath[MAXPATHLEN]; 589190792Sgshapiro 589290792Sgshapiro *cp = '\0'; /* Overwrite wildcard */ 589390792Sgshapiro if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL) 589464562Sgshapiro { 589564562Sgshapiro syserr("QueueDirectory: can not wildcard relative path"); 589664562Sgshapiro if (tTd(41, 2)) 589790792Sgshapiro sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n", 589871345Sgshapiro qpath); 589964562Sgshapiro ExitStat = EX_CONFIG; 590090792Sgshapiro return qn; 590164562Sgshapiro } 590264562Sgshapiro if (cp == qpath) 590364562Sgshapiro { 590464562Sgshapiro /* 590564562Sgshapiro ** Special case of top level wildcard, like /foo* 590690792Sgshapiro ** Change to //foo* 590764562Sgshapiro */ 590864562Sgshapiro 590990792Sgshapiro (void) sm_strlcpy(qpath + 1, qpath, sizeof qpath - 1); 591064562Sgshapiro ++cp; 591164562Sgshapiro } 591290792Sgshapiro delim = cp; 591390792Sgshapiro *(cp++) = '\0'; /* Replace / with \0 */ 591490792Sgshapiro len = strlen(cp); /* Last component of queue directory */ 591564562Sgshapiro 591690792Sgshapiro /* 591790792Sgshapiro ** Path relative to basedir, with trailing / 591890792Sgshapiro ** It will be modified below to specify the subdirectories 591990792Sgshapiro ** so they can be opened without chdir(). 592090792Sgshapiro */ 592190792Sgshapiro 592290792Sgshapiro off = sm_strlcpyn(relpath, sizeof relpath, 2, prefix, "/"); 592390792Sgshapiro SM_ASSERT(off < sizeof relpath); 592490792Sgshapiro 592564562Sgshapiro if (tTd(41, 2)) 592690792Sgshapiro sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n", 592790792Sgshapiro relpath, cp); 592864562Sgshapiro 592990792Sgshapiro /* It is always basedir: we don't need to store it per group */ 593090792Sgshapiro /* XXX: optimize this! -> one more global? */ 593190792Sgshapiro qg->qg_qdir = newstr(basedir); 593290792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */ 593364562Sgshapiro 593464562Sgshapiro /* 593564562Sgshapiro ** XXX Should probably wrap this whole loop in a timeout 593664562Sgshapiro ** in case some wag decides to NFS mount the queues. 593764562Sgshapiro */ 593864562Sgshapiro 593990792Sgshapiro /* Test path to get warning messages. */ 594090792Sgshapiro if (qn == 0) 594164562Sgshapiro { 594290792Sgshapiro /* XXX qg_runasuid and qg_runasgid for specials? */ 594390792Sgshapiro i = safedirpath(basedir, RunAsUid, RunAsGid, NULL, 594490792Sgshapiro sff, 0, 0); 594590792Sgshapiro if (i != 0 && tTd(41, 2)) 594690792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", 594790792Sgshapiro basedir, sm_errstring(i)); 594864562Sgshapiro } 594964562Sgshapiro 595090792Sgshapiro if ((dp = opendir(prefix)) == NULL) 595164562Sgshapiro { 595290792Sgshapiro syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix); 595364562Sgshapiro if (tTd(41, 2)) 595490792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n", 595590792Sgshapiro qg->qg_qdir, prefix, 595690792Sgshapiro sm_errstring(errno)); 595764562Sgshapiro ExitStat = EX_CONFIG; 595890792Sgshapiro return qn; 595964562Sgshapiro } 596064562Sgshapiro while ((d = readdir(dp)) != NULL) 596164562Sgshapiro { 596290792Sgshapiro i = strlen(d->d_name); 596390792Sgshapiro if (i < len || strncmp(d->d_name, cp, len) != 0) 596464562Sgshapiro { 596564562Sgshapiro if (tTd(41, 5)) 596690792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\", skipped\n", 596764562Sgshapiro d->d_name); 596864562Sgshapiro continue; 596964562Sgshapiro } 597090792Sgshapiro 597190792Sgshapiro /* Create relative pathname: prefix + local directory */ 597290792Sgshapiro i = sizeof(relpath) - off; 597390792Sgshapiro if (sm_strlcpy(relpath + off, d->d_name, i) >= i) 597490792Sgshapiro continue; /* way too long */ 597590792Sgshapiro 597690792Sgshapiro if (!chkqdir(relpath, sff)) 597764562Sgshapiro continue; 597864562Sgshapiro 597990792Sgshapiro if (qg->qg_qpaths == NULL) 598064562Sgshapiro { 598190792Sgshapiro slotsleft = INITIAL_SLOTS; 598290792Sgshapiro qg->qg_qpaths = (QPATHS *)xalloc((sizeof *qg->qg_qpaths) * 598390792Sgshapiro slotsleft); 598490792Sgshapiro qg->qg_numqueues = 0; 598564562Sgshapiro } 598664562Sgshapiro else if (slotsleft < 1) 598764562Sgshapiro { 598890792Sgshapiro qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths, 598990792Sgshapiro (sizeof *qg->qg_qpaths) * 599090792Sgshapiro (qg->qg_numqueues + 599190792Sgshapiro ADD_SLOTS)); 599290792Sgshapiro if (qg->qg_qpaths == NULL) 599364562Sgshapiro { 599464562Sgshapiro (void) closedir(dp); 599590792Sgshapiro return qn; 599664562Sgshapiro } 599790792Sgshapiro slotsleft += ADD_SLOTS; 599864562Sgshapiro } 599964562Sgshapiro 600064562Sgshapiro /* check subdirs */ 600190792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB; 600264562Sgshapiro 600390792Sgshapiro#define CHKRSUBDIR(name, flag) \ 600490792Sgshapiro (void) sm_strlcpyn(subdir, sizeof subdir, 3, relpath, "/", name); \ 600590792Sgshapiro if (chkqdir(subdir, sff)) \ 600690792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \ 600790792Sgshapiro else 600864562Sgshapiro 600964562Sgshapiro 601090792Sgshapiro CHKRSUBDIR("qf", QP_SUBQF); 601190792Sgshapiro CHKRSUBDIR("df", QP_SUBDF); 601290792Sgshapiro CHKRSUBDIR("xf", QP_SUBXF); 601390792Sgshapiro 601464562Sgshapiro /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */ 601564562Sgshapiro /* maybe even - 17 (subdirs) */ 601690792Sgshapiro 601790792Sgshapiro if (prefix[0] != '.') 601890792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 601990792Sgshapiro newstr(relpath); 602090792Sgshapiro else 602190792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 602290792Sgshapiro newstr(d->d_name); 602390792Sgshapiro 602464562Sgshapiro if (tTd(41, 2)) 602590792Sgshapiro sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", 602690792Sgshapiro qg->qg_numqueues, relpath, 602790792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs); 602890792Sgshapiro#if SM_CONF_SHM 602990792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn; 603090792Sgshapiro *phash = hash_q(relpath, *phash); 603190792Sgshapiro#endif /* SM_CONF_SHM */ 603290792Sgshapiro qg->qg_numqueues++; 603390792Sgshapiro ++qn; 603464562Sgshapiro slotsleft--; 603564562Sgshapiro } 603664562Sgshapiro (void) closedir(dp); 603790792Sgshapiro 603890792Sgshapiro /* undo damage */ 603990792Sgshapiro *delim = '/'; 604064562Sgshapiro } 604190792Sgshapiro if (qg->qg_numqueues == 0) 604264562Sgshapiro { 604390792Sgshapiro qg->qg_qpaths = (QPATHS *) xalloc(sizeof *qg->qg_qpaths); 604464562Sgshapiro 604564562Sgshapiro /* test path to get warning messages */ 604690792Sgshapiro i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0); 604790792Sgshapiro if (i == ENOENT) 604864562Sgshapiro { 604990792Sgshapiro syserr("can not opendir(%s)", qpath); 605064562Sgshapiro if (tTd(41, 2)) 605190792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", 605290792Sgshapiro qpath, sm_errstring(i)); 605364562Sgshapiro ExitStat = EX_CONFIG; 605490792Sgshapiro return qn; 605564562Sgshapiro } 605664562Sgshapiro 605790792Sgshapiro qg->qg_qpaths[0].qp_subdirs = QP_NOSUB; 605890792Sgshapiro qg->qg_numqueues = 1; 605990792Sgshapiro 606064562Sgshapiro /* check subdirs */ 606190792Sgshapiro#define CHKSUBDIR(name, flag) \ 606290792Sgshapiro (void) sm_strlcpyn(subdir, sizeof subdir, 3, qg->qg_qdir, "/", name); \ 606390792Sgshapiro if (chkqdir(subdir, sff)) \ 606490792Sgshapiro qg->qg_qpaths[0].qp_subdirs |= flag; \ 606590792Sgshapiro else 606664562Sgshapiro 606790792Sgshapiro CHKSUBDIR("qf", QP_SUBQF); 606890792Sgshapiro CHKSUBDIR("df", QP_SUBDF); 606990792Sgshapiro CHKSUBDIR("xf", QP_SUBXF); 607064562Sgshapiro 607190792Sgshapiro if (qg->qg_qdir[blen - 1] != '\0' && 607290792Sgshapiro qg->qg_qdir[blen] != '\0') 607390792Sgshapiro { 607490792Sgshapiro /* 607590792Sgshapiro ** Copy the last component into qpaths and 607690792Sgshapiro ** cut off qdir 607790792Sgshapiro */ 607890792Sgshapiro 607990792Sgshapiro qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen); 608090792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; 608190792Sgshapiro } 608290792Sgshapiro else 608390792Sgshapiro qg->qg_qpaths[0].qp_name = newstr("."); 608490792Sgshapiro 608590792Sgshapiro#if SM_CONF_SHM 608690792Sgshapiro qg->qg_qpaths[0].qp_idx = qn; 608790792Sgshapiro *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash); 608890792Sgshapiro#endif /* SM_CONF_SHM */ 608990792Sgshapiro ++qn; 609064562Sgshapiro } 609190792Sgshapiro return qn; 609264562Sgshapiro} 609364562Sgshapiro 609490792Sgshapiro/* 609590792Sgshapiro** FILESYS_FIND -- find entry in FileSys table, or add new one 609690792Sgshapiro** 609790792Sgshapiro** Given the pathname of a directory, determine the file system 609890792Sgshapiro** in which that directory resides, and return a pointer to the 609990792Sgshapiro** entry in the FileSys table that describes the file system. 610090792Sgshapiro** A new entry is added if necessary (and requested). 610190792Sgshapiro** If the directory does not exist, -1 is returned. 610290792Sgshapiro** 610390792Sgshapiro** Parameters: 610490792Sgshapiro** path -- pathname of directory 610590792Sgshapiro** add -- add to structure if not found. 610690792Sgshapiro** 610790792Sgshapiro** Returns: 610890792Sgshapiro** >=0: found: index in file system table 610990792Sgshapiro** <0: some error, i.e., 611090792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 611190792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 611290792Sgshapiro** FSF_NOT_FOUND: not in list 611390792Sgshapiro*/ 611490792Sgshapiro 611590792Sgshapirostatic short filesys_find __P((char *, bool)); 611690792Sgshapiro 611790792Sgshapiro#define FSF_NOT_FOUND (-1) 611890792Sgshapiro#define FSF_STAT_FAIL (-2) 611990792Sgshapiro#define FSF_TOO_MANY (-3) 612090792Sgshapiro 612190792Sgshapirostatic short 612290792Sgshapirofilesys_find(path, add) 612390792Sgshapiro char *path; 612490792Sgshapiro bool add; 612590792Sgshapiro{ 612690792Sgshapiro struct stat st; 612790792Sgshapiro short i; 612890792Sgshapiro 612990792Sgshapiro if (stat(path, &st) < 0) 613090792Sgshapiro { 613190792Sgshapiro syserr("cannot stat queue directory %s", path); 613290792Sgshapiro return FSF_STAT_FAIL; 613390792Sgshapiro } 613490792Sgshapiro for (i = 0; i < NumFileSys; ++i) 613590792Sgshapiro { 613690792Sgshapiro if (FILE_SYS_DEV(i) == st.st_dev) 613790792Sgshapiro return i; 613890792Sgshapiro } 613990792Sgshapiro if (i >= MAXFILESYS) 614090792Sgshapiro { 614190792Sgshapiro syserr("too many queue file systems (%d max)", MAXFILESYS); 614290792Sgshapiro return FSF_TOO_MANY; 614390792Sgshapiro } 614490792Sgshapiro if (!add) 614590792Sgshapiro return FSF_NOT_FOUND; 614690792Sgshapiro 614790792Sgshapiro ++NumFileSys; 614890792Sgshapiro FILE_SYS_NAME(i) = path; 614990792Sgshapiro FILE_SYS_DEV(i) = st.st_dev; 615090792Sgshapiro FILE_SYS_AVAIL(i) = 0; 615190792Sgshapiro FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */ 615290792Sgshapiro return i; 615390792Sgshapiro} 615490792Sgshapiro 615590792Sgshapiro/* 615690792Sgshapiro** FILESYS_SETUP -- set up mapping from queue directories to file systems 615790792Sgshapiro** 615890792Sgshapiro** This data structure is used to efficiently check the amount of 615990792Sgshapiro** free space available in a set of queue directories. 616090792Sgshapiro** 616190792Sgshapiro** Parameters: 616290792Sgshapiro** add -- initialize structure if necessary. 616390792Sgshapiro** 616490792Sgshapiro** Returns: 616590792Sgshapiro** 0: success 616690792Sgshapiro** <0: some error, i.e., 616790792Sgshapiro** FSF_NOT_FOUND: not in list 616890792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 616990792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 617090792Sgshapiro*/ 617190792Sgshapiro 617290792Sgshapirostatic int filesys_setup __P((bool)); 617390792Sgshapiro 617490792Sgshapirostatic int 617590792Sgshapirofilesys_setup(add) 617690792Sgshapiro bool add; 617790792Sgshapiro{ 617890792Sgshapiro int i, j; 617990792Sgshapiro short fs; 618090792Sgshapiro int ret; 618190792Sgshapiro 618290792Sgshapiro ret = 0; 618390792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 618490792Sgshapiro { 618590792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; ++j) 618690792Sgshapiro { 618790792Sgshapiro QPATHS *qp = &Queue[i]->qg_qpaths[j]; 618890792Sgshapiro 618990792Sgshapiro fs = filesys_find(qp->qp_name, add); 619090792Sgshapiro if (fs >= 0) 619190792Sgshapiro qp->qp_fsysidx = fs; 619290792Sgshapiro else 619390792Sgshapiro qp->qp_fsysidx = 0; 619490792Sgshapiro if (fs < ret) 619590792Sgshapiro ret = fs; 619690792Sgshapiro } 619790792Sgshapiro } 619890792Sgshapiro return ret; 619990792Sgshapiro} 620090792Sgshapiro 620190792Sgshapiro/* 620290792Sgshapiro** FILESYS_UPDATE -- update amount of free space on all file systems 620390792Sgshapiro** 620490792Sgshapiro** The FileSys table is used to cache the amount of free space 620590792Sgshapiro** available on all queue directory file systems. 620690792Sgshapiro** This function updates the cached information if it has expired. 620790792Sgshapiro** 620890792Sgshapiro** Parameters: 620990792Sgshapiro** none. 621090792Sgshapiro** 621190792Sgshapiro** Returns: 621290792Sgshapiro** none. 621390792Sgshapiro** 621490792Sgshapiro** Side Effects: 621590792Sgshapiro** Updates FileSys table. 621690792Sgshapiro*/ 621790792Sgshapiro 621890792Sgshapirovoid 621990792Sgshapirofilesys_update() 622090792Sgshapiro{ 622190792Sgshapiro int i; 622290792Sgshapiro long avail, blksize; 622390792Sgshapiro time_t now; 622490792Sgshapiro static time_t nextupdate = 0; 622590792Sgshapiro 622690792Sgshapiro#if SM_CONF_SHM 622790792Sgshapiro /* only the daemon updates this structure */ 622890792Sgshapiro if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid) 622990792Sgshapiro return; 623090792Sgshapiro#endif /* SM_CONF_SHM */ 623190792Sgshapiro now = curtime(); 623290792Sgshapiro if (now < nextupdate) 623390792Sgshapiro return; 623490792Sgshapiro nextupdate = now + FILESYS_UPDATE_INTERVAL; 623590792Sgshapiro for (i = 0; i < NumFileSys; ++i) 623690792Sgshapiro { 623790792Sgshapiro FILESYS *fs = &FILE_SYS(i); 623890792Sgshapiro 623990792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 624090792Sgshapiro if (avail < 0 || blksize <= 0) 624190792Sgshapiro { 624290792Sgshapiro if (LogLevel > 5) 624390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 624490792Sgshapiro "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld", 624590792Sgshapiro sm_errstring(errno), 624690792Sgshapiro FILE_SYS_NAME(i), avail, blksize); 624790792Sgshapiro fs->fs_avail = 0; 624890792Sgshapiro fs->fs_blksize = 1024; /* avoid divide by zero */ 624990792Sgshapiro nextupdate = now + 2; /* let's do this soon again */ 625090792Sgshapiro } 625190792Sgshapiro else 625290792Sgshapiro { 625390792Sgshapiro fs->fs_avail = avail; 625490792Sgshapiro fs->fs_blksize = blksize; 625590792Sgshapiro } 625690792Sgshapiro } 625790792Sgshapiro} 625890792Sgshapiro 625990792Sgshapiro#if _FFR_ANY_FREE_FS 626090792Sgshapiro/* 626190792Sgshapiro** FILESYS_FREE -- check whether there is at least one fs with enough space. 626290792Sgshapiro** 626390792Sgshapiro** Parameters: 626490792Sgshapiro** fsize -- file size in bytes 626590792Sgshapiro** 626690792Sgshapiro** Returns: 626790792Sgshapiro** true iff there is one fs with more than fsize bytes free. 626890792Sgshapiro*/ 626990792Sgshapiro 627090792Sgshapirobool 627190792Sgshapirofilesys_free(fsize) 627290792Sgshapiro long fsize; 627390792Sgshapiro{ 627490792Sgshapiro int i; 627590792Sgshapiro 627690792Sgshapiro if (fsize <= 0) 627790792Sgshapiro return true; 627890792Sgshapiro for (i = 0; i < NumFileSys; ++i) 627990792Sgshapiro { 628090792Sgshapiro long needed = 0; 628190792Sgshapiro 628290792Sgshapiro if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0) 628390792Sgshapiro continue; 628490792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(i) 628590792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(i) 628690792Sgshapiro > 0) ? 1 : 0) 628790792Sgshapiro + MinBlocksFree; 628890792Sgshapiro if (needed <= FILE_SYS_AVAIL(i)) 628990792Sgshapiro return true; 629090792Sgshapiro } 629190792Sgshapiro return false; 629290792Sgshapiro} 629390792Sgshapiro#endif /* _FFR_ANY_FREE_FS */ 629490792Sgshapiro 629590792Sgshapiro#if _FFR_CONTROL_MSTAT 629690792Sgshapiro/* 629790792Sgshapiro** DISK_STATUS -- show amount of free space in queue directories 629890792Sgshapiro** 629990792Sgshapiro** Parameters: 630090792Sgshapiro** out -- output file pointer. 630190792Sgshapiro** prefix -- string to output in front of each line. 630290792Sgshapiro** 630390792Sgshapiro** Returns: 630490792Sgshapiro** none. 630590792Sgshapiro*/ 630690792Sgshapiro 630790792Sgshapirovoid 630890792Sgshapirodisk_status(out, prefix) 630990792Sgshapiro SM_FILE_T *out; 631090792Sgshapiro char *prefix; 631190792Sgshapiro{ 631290792Sgshapiro int i; 631390792Sgshapiro long avail, blksize; 631490792Sgshapiro long free; 631590792Sgshapiro 631690792Sgshapiro for (i = 0; i < NumFileSys; ++i) 631790792Sgshapiro { 631890792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 631990792Sgshapiro if (avail >= 0 && blksize > 0) 632090792Sgshapiro { 632190792Sgshapiro free = (long)((double) avail * 632290792Sgshapiro ((double) blksize / 1024)); 632390792Sgshapiro } 632490792Sgshapiro else 632590792Sgshapiro free = -1; 632690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 632790792Sgshapiro "%s%d/%s/%ld\r\n", 632890792Sgshapiro prefix, i, 632990792Sgshapiro FILE_SYS_NAME(i), 633090792Sgshapiro free); 633190792Sgshapiro } 633290792Sgshapiro} 633390792Sgshapiro#endif /* _FFR_CONTROL_MSTAT */ 633490792Sgshapiro 633590792Sgshapiro#if SM_CONF_SHM 633690792Sgshapiro/* 633790792Sgshapiro** UPD_QS -- update information about queue when adding/deleting an entry 633890792Sgshapiro** 633990792Sgshapiro** Parameters: 634090792Sgshapiro** e -- envelope. 634190792Sgshapiro** delete -- delete/add entry. 634290792Sgshapiro** avail -- update the space available as well. 634390792Sgshapiro** 634490792Sgshapiro** Returns: 634590792Sgshapiro** none. 634690792Sgshapiro** 634790792Sgshapiro** Side Effects: 634890792Sgshapiro** Modifies available space in filesystem. 634990792Sgshapiro** Changes number of entries in queue directory. 635090792Sgshapiro*/ 635190792Sgshapiro 635290792Sgshapirovoid 635390792Sgshapiroupd_qs(e, delete, avail) 635490792Sgshapiro ENVELOPE *e; 635590792Sgshapiro bool delete; 635690792Sgshapiro bool avail; 635790792Sgshapiro{ 635890792Sgshapiro short fidx; 635990792Sgshapiro int idx; 636090792Sgshapiro long s; 636190792Sgshapiro 636290792Sgshapiro if (ShmId == SM_SHM_NO_ID || e == NULL) 636390792Sgshapiro return; 636490792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 636590792Sgshapiro return; 636690792Sgshapiro idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx; 636790792Sgshapiro 636890792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 636990792Sgshapiro if (QSHM_ENTRIES(idx) >= 0) 637090792Sgshapiro { 637190792Sgshapiro if (delete) 637290792Sgshapiro --QSHM_ENTRIES(idx); 637390792Sgshapiro else 637490792Sgshapiro ++QSHM_ENTRIES(idx); 637590792Sgshapiro } 637690792Sgshapiro 637790792Sgshapiro fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx; 637890792Sgshapiro if (fidx < 0) 637990792Sgshapiro return; 638090792Sgshapiro 638190792Sgshapiro /* update available space also? (might be loseqfile) */ 638290792Sgshapiro if (!avail) 638390792Sgshapiro return; 638490792Sgshapiro 638590792Sgshapiro /* convert size to blocks; this causes rounding errors */ 638690792Sgshapiro s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx); 638790792Sgshapiro if (s == 0) 638890792Sgshapiro return; 638990792Sgshapiro 639090792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 639190792Sgshapiro if (delete) 639290792Sgshapiro FILE_SYS_AVAIL(fidx) += s; 639390792Sgshapiro else 639490792Sgshapiro FILE_SYS_AVAIL(fidx) -= s; 639590792Sgshapiro 639690792Sgshapiro} 639794334Sgshapiro 639894334Sgshapiro#if _FFR_SELECT_SHM 639994334Sgshapiro 640094334Sgshapirostatic bool write_key_file __P((char *, long)); 640194334Sgshapirostatic long read_key_file __P((char *, long)); 640294334Sgshapiro 640390792Sgshapiro/* 640494334Sgshapiro** WRITE_KEY_FILE -- record some key into a file. 640594334Sgshapiro** 640694334Sgshapiro** Parameters: 640794334Sgshapiro** keypath -- file name. 640894334Sgshapiro** key -- key to write. 640994334Sgshapiro** 641094334Sgshapiro** Returns: 641194334Sgshapiro** true iff file could be written. 641294334Sgshapiro** 641394334Sgshapiro** Side Effects: 641494334Sgshapiro** writes file. 641594334Sgshapiro*/ 641694334Sgshapiro 641794334Sgshapirostatic bool 641894334Sgshapirowrite_key_file(keypath, key) 641994334Sgshapiro char *keypath; 642094334Sgshapiro long key; 642194334Sgshapiro{ 642294334Sgshapiro bool ok; 642394334Sgshapiro long sff; 642494334Sgshapiro SM_FILE_T *keyf; 642594334Sgshapiro 642694334Sgshapiro ok = false; 642794334Sgshapiro if (keypath == NULL || *keypath == '\0') 642894334Sgshapiro return ok; 642994334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; 643094334Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 643194334Sgshapiro sff |= SFF_OPENASROOT; 643294334Sgshapiro keyf = safefopen(keypath, O_WRONLY|O_TRUNC, 0644, sff); 643394334Sgshapiro if (keyf == NULL) 643494334Sgshapiro { 643594334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", 643694334Sgshapiro keypath, sm_errstring(errno)); 643794334Sgshapiro } 643894334Sgshapiro else 643994334Sgshapiro { 644094334Sgshapiro ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) != 644194334Sgshapiro SM_IO_EOF; 644294334Sgshapiro ok = ok && (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF); 644394334Sgshapiro } 644494334Sgshapiro return ok; 644594334Sgshapiro} 644694334Sgshapiro 644794334Sgshapiro/* 644894334Sgshapiro** READ_KEY_FILE -- read a key from a file. 644994334Sgshapiro** 645094334Sgshapiro** Parameters: 645194334Sgshapiro** keypath -- file name. 645294334Sgshapiro** key -- default key. 645394334Sgshapiro** 645494334Sgshapiro** Returns: 645594334Sgshapiro** key. 645694334Sgshapiro*/ 645794334Sgshapiro 645894334Sgshapirostatic long 645994334Sgshapiroread_key_file(keypath, key) 646094334Sgshapiro char *keypath; 646194334Sgshapiro long key; 646294334Sgshapiro{ 646394334Sgshapiro int r; 646494334Sgshapiro long sff, n; 646594334Sgshapiro SM_FILE_T *keyf; 646694334Sgshapiro 646794334Sgshapiro if (keypath == NULL || *keypath == '\0') 646894334Sgshapiro return key; 646994334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY; 647094334Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 647194334Sgshapiro sff |= SFF_OPENASROOT; 647294334Sgshapiro keyf = safefopen(keypath, O_RDONLY, 0644, sff); 647394334Sgshapiro if (keyf == NULL) 647494334Sgshapiro { 647594334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s", 647694334Sgshapiro keypath, sm_errstring(errno)); 647794334Sgshapiro } 647894334Sgshapiro else 647994334Sgshapiro { 648094334Sgshapiro r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n); 648194334Sgshapiro if (r == 1) 648294334Sgshapiro key = n; 648394334Sgshapiro (void) sm_io_close(keyf, SM_TIME_DEFAULT); 648494334Sgshapiro } 648594334Sgshapiro return key; 648694334Sgshapiro} 648794334Sgshapiro#endif /* _FFR_SELECT_SHM */ 648894334Sgshapiro 648994334Sgshapiro/* 649090792Sgshapiro** INIT_SHM -- initialize shared memory structure 649190792Sgshapiro** 649290792Sgshapiro** Initialize or attach to shared memory segment. 649390792Sgshapiro** Currently it is not a fatal error if this doesn't work. 649490792Sgshapiro** However, it causes us to have a "fallback" storage location 649590792Sgshapiro** for everything that is supposed to be in the shared memory, 649690792Sgshapiro** which makes the code slightly ugly. 649790792Sgshapiro** 649890792Sgshapiro** Parameters: 649990792Sgshapiro** qn -- number of queue directories. 650090792Sgshapiro** owner -- owner of shared memory. 650190792Sgshapiro** hash -- identifies data that is stored in shared memory. 650290792Sgshapiro** 650390792Sgshapiro** Returns: 650490792Sgshapiro** none. 650590792Sgshapiro*/ 650690792Sgshapiro 650790792Sgshapirostatic void init_shm __P((int, bool, unsigned int)); 650890792Sgshapiro 650990792Sgshapirostatic void 651090792Sgshapiroinit_shm(qn, owner, hash) 651190792Sgshapiro int qn; 651290792Sgshapiro bool owner; 651390792Sgshapiro unsigned int hash; 651490792Sgshapiro{ 651590792Sgshapiro int i; 651694334Sgshapiro#if _FFR_SELECT_SHM 651794334Sgshapiro bool keyselect; 651894334Sgshapiro#endif /* _FFR_SELECT_SHM */ 651990792Sgshapiro 652090792Sgshapiro PtrFileSys = &FileSys[0]; 652190792Sgshapiro PNumFileSys = &Numfilesys; 652294334Sgshapiro#if _FFR_SELECT_SHM 652394334Sgshapiro/* if this "key" is specified: select one yourself */ 652494334Sgshapiro# define SEL_SHM_KEY ((key_t) -1) 652594334Sgshapiro# define FIRST_SHM_KEY 25 652694334Sgshapiro#endif /* _FFR_SELECT_SHM */ 652790792Sgshapiro 652890792Sgshapiro /* This allows us to disable shared memory at runtime. */ 652990792Sgshapiro if (ShmKey != 0) 653090792Sgshapiro { 653190792Sgshapiro int count; 653290792Sgshapiro int save_errno; 653390792Sgshapiro size_t shms; 653490792Sgshapiro 653590792Sgshapiro count = 0; 653690792Sgshapiro shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T); 653794334Sgshapiro#if _FFR_SELECT_SHM 653894334Sgshapiro keyselect = ShmKey == SEL_SHM_KEY; 653994334Sgshapiro if (keyselect) 654094334Sgshapiro { 654194334Sgshapiro if (owner) 654294334Sgshapiro ShmKey = FIRST_SHM_KEY; 654394334Sgshapiro else 654494334Sgshapiro { 654594334Sgshapiro ShmKey = read_key_file(ShmKeyFile, ShmKey); 654694334Sgshapiro keyselect = false; 654794334Sgshapiro if (ShmKey == SEL_SHM_KEY) 654894334Sgshapiro goto error; 654994334Sgshapiro } 655094334Sgshapiro } 655194334Sgshapiro#endif /* _FFR_SELECT_SHM */ 655290792Sgshapiro for (;;) 655390792Sgshapiro { 655490792Sgshapiro /* XXX: maybe allow read access for group? */ 655590792Sgshapiro Pshm = sm_shmstart(ShmKey, shms, SHM_R|SHM_W, &ShmId, 655690792Sgshapiro owner); 655790792Sgshapiro save_errno = errno; 655890792Sgshapiro if (Pshm != NULL || save_errno != EEXIST) 655990792Sgshapiro break; 656090792Sgshapiro if (++count >= 3) 656194334Sgshapiro { 656294334Sgshapiro#if _FFR_SELECT_SHM 656394334Sgshapiro if (keyselect) 656494334Sgshapiro { 656594334Sgshapiro ++ShmKey; 656694334Sgshapiro 656794334Sgshapiro /* back where we started? */ 656894334Sgshapiro if (ShmKey == SEL_SHM_KEY) 656994334Sgshapiro break; 657094334Sgshapiro continue; 657194334Sgshapiro } 657294334Sgshapiro#endif /* _FFR_SELECT_SHM */ 657390792Sgshapiro break; 657494334Sgshapiro } 657594334Sgshapiro#if _FFR_SELECT_SHM 657694334Sgshapiro /* only sleep if we are at the first key */ 657794334Sgshapiro if (!keyselect || ShmKey == SEL_SHM_KEY) 657894334Sgshapiro#endif /* _FFR_SELECT_SHM */ 657990792Sgshapiro sleep(count); 658090792Sgshapiro } 658190792Sgshapiro if (Pshm != NULL) 658290792Sgshapiro { 658390792Sgshapiro int *p; 658490792Sgshapiro 658594334Sgshapiro#if _FFR_SELECT_SHM 658694334Sgshapiro if (keyselect) 658794334Sgshapiro (void) write_key_file(ShmKeyFile, (long) ShmKey); 658894334Sgshapiro#endif /* _FFR_SELECT_SHM */ 658990792Sgshapiro p = (int *) Pshm; 659090792Sgshapiro if (owner) 659190792Sgshapiro { 659290792Sgshapiro *p = (int) shms; 659390792Sgshapiro *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid; 659490792Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 659590792Sgshapiro *p = hash; 659690792Sgshapiro } 659790792Sgshapiro else 659890792Sgshapiro { 659990792Sgshapiro if (*p != (int) shms) 660090792Sgshapiro { 660190792Sgshapiro save_errno = EINVAL; 660290792Sgshapiro cleanup_shm(false); 660390792Sgshapiro goto error; 660490792Sgshapiro } 660590792Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 660690792Sgshapiro if (*p != (int) hash) 660790792Sgshapiro { 660890792Sgshapiro save_errno = EINVAL; 660990792Sgshapiro cleanup_shm(false); 661090792Sgshapiro goto error; 661190792Sgshapiro } 661290792Sgshapiro 661390792Sgshapiro /* 661490792Sgshapiro ** XXX how to check the pid? 661590792Sgshapiro ** Read it from the pid-file? That does 661690792Sgshapiro ** not need to exist. 661790792Sgshapiro ** We could disable shm if we can't confirm 661890792Sgshapiro ** that it is the right one. 661990792Sgshapiro */ 662090792Sgshapiro } 662190792Sgshapiro 662290792Sgshapiro PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm); 662390792Sgshapiro PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm); 662490792Sgshapiro QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm); 662590792Sgshapiro PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm); 662690792Sgshapiro *PRSATmpCnt = 0; 662790792Sgshapiro if (owner) 662890792Sgshapiro { 662990792Sgshapiro /* initialize values in shared memory */ 663090792Sgshapiro NumFileSys = 0; 663190792Sgshapiro for (i = 0; i < qn; i++) 663290792Sgshapiro QShm[i].qs_entries = -1; 663390792Sgshapiro } 663490792Sgshapiro return; 663590792Sgshapiro } 663690792Sgshapiro error: 663790792Sgshapiro if (LogLevel > (owner ? 8 : 11)) 663890792Sgshapiro { 663990792Sgshapiro sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID, 664090792Sgshapiro "can't %s shared memory, key=%ld: %s", 664190792Sgshapiro owner ? "initialize" : "attach to", 664290792Sgshapiro (long) ShmKey, sm_errstring(save_errno)); 664390792Sgshapiro } 664490792Sgshapiro } 664590792Sgshapiro} 664690792Sgshapiro#endif /* SM_CONF_SHM */ 664790792Sgshapiro 664890792Sgshapiro/* 664990792Sgshapiro** SETUP_QUEUES -- setup all queue groups 665090792Sgshapiro** 665190792Sgshapiro** Parameters: 665290792Sgshapiro** owner -- owner of shared memory. 665390792Sgshapiro** 665490792Sgshapiro** Returns: 665590792Sgshapiro** none. 665690792Sgshapiro** 665790792Sgshapiro#if SM_CONF_SHM 665890792Sgshapiro** Side Effects: 665990792Sgshapiro** attaches shared memory. 666090792Sgshapiro#endif * SM_CONF_SHM * 666190792Sgshapiro*/ 666290792Sgshapiro 666390792Sgshapirovoid 666490792Sgshapirosetup_queues(owner) 666590792Sgshapiro bool owner; 666690792Sgshapiro{ 666790792Sgshapiro int i, qn, len; 666890792Sgshapiro unsigned int hashval; 666994334Sgshapiro time_t now; 667090792Sgshapiro char basedir[MAXPATHLEN]; 667190792Sgshapiro struct stat st; 667290792Sgshapiro 667390792Sgshapiro /* 667490792Sgshapiro ** Determine basedir for all queue directories. 667590792Sgshapiro ** All queue directories must be (first level) subdirectories 667690792Sgshapiro ** of the basedir. The basedir is the QueueDir 667790792Sgshapiro ** without wildcards, but with trailing / 667890792Sgshapiro */ 667990792Sgshapiro 668090792Sgshapiro hashval = 0; 668190792Sgshapiro errno = 0; 668290792Sgshapiro len = sm_strlcpy(basedir, QueueDir, sizeof basedir); 668390792Sgshapiro if (len >= sizeof basedir) 668490792Sgshapiro { 668590792Sgshapiro syserr("QueueDirectory: path too long: %d, max %d", 668690792Sgshapiro len, (int) sizeof basedir); 668790792Sgshapiro ExitStat = EX_CONFIG; 668890792Sgshapiro return; 668990792Sgshapiro } 669090792Sgshapiro SM_ASSERT(len > 0); 669190792Sgshapiro if (basedir[len - 1] == '*') 669290792Sgshapiro { 669390792Sgshapiro char *cp; 669490792Sgshapiro 669590792Sgshapiro cp = SM_LAST_DIR_DELIM(basedir); 669690792Sgshapiro if (cp == NULL) 669790792Sgshapiro { 669890792Sgshapiro syserr("QueueDirectory: can not wildcard relative path \"%s\"", 669990792Sgshapiro QueueDir); 670090792Sgshapiro if (tTd(41, 2)) 670190792Sgshapiro sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n", 670290792Sgshapiro QueueDir); 670390792Sgshapiro ExitStat = EX_CONFIG; 670490792Sgshapiro return; 670590792Sgshapiro } 670690792Sgshapiro 670790792Sgshapiro /* cut off wildcard pattern */ 670890792Sgshapiro *++cp = '\0'; 670990792Sgshapiro len = cp - basedir; 671090792Sgshapiro } 671190792Sgshapiro else if (!SM_IS_DIR_DELIM(basedir[len - 1])) 671290792Sgshapiro { 671390792Sgshapiro /* append trailing slash since it is a directory */ 671490792Sgshapiro basedir[len] = '/'; 671590792Sgshapiro basedir[++len] = '\0'; 671690792Sgshapiro } 671790792Sgshapiro 671890792Sgshapiro /* len counts up to the last directory delimiter */ 671990792Sgshapiro SM_ASSERT(basedir[len - 1] == '/'); 672090792Sgshapiro 672190792Sgshapiro if (chdir(basedir) < 0) 672290792Sgshapiro { 672390792Sgshapiro int save_errno = errno; 672490792Sgshapiro 672590792Sgshapiro syserr("can not chdir(%s)", basedir); 672690792Sgshapiro if (save_errno == EACCES) 672790792Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 672890792Sgshapiro "Program mode requires special privileges, e.g., root or TrustedUser.\n"); 672990792Sgshapiro if (tTd(41, 2)) 673090792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 673190792Sgshapiro basedir, sm_errstring(errno)); 673290792Sgshapiro ExitStat = EX_CONFIG; 673390792Sgshapiro return; 673490792Sgshapiro } 673590792Sgshapiro#if SM_CONF_SHM 673690792Sgshapiro hashval = hash_q(basedir, hashval); 673790792Sgshapiro#endif /* SM_CONF_SHM */ 673890792Sgshapiro 673994334Sgshapiro /* initialize for queue runs */ 674094334Sgshapiro DoQueueRun = false; 674194334Sgshapiro now = curtime(); 674294334Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 674394334Sgshapiro Queue[i]->qg_nextrun = now; 674490792Sgshapiro 674590792Sgshapiro 674690792Sgshapiro if (UseMSP && OpMode != MD_TEST) 674790792Sgshapiro { 674890792Sgshapiro long sff = SFF_CREAT; 674990792Sgshapiro 675090792Sgshapiro if (stat(".", &st) < 0) 675190792Sgshapiro { 675290792Sgshapiro syserr("can not stat(%s)", basedir); 675390792Sgshapiro if (tTd(41, 2)) 675490792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 675590792Sgshapiro basedir, sm_errstring(errno)); 675690792Sgshapiro ExitStat = EX_CONFIG; 675790792Sgshapiro return; 675890792Sgshapiro } 675990792Sgshapiro if (RunAsUid == 0) 676090792Sgshapiro sff |= SFF_ROOTOK; 676190792Sgshapiro 676290792Sgshapiro /* 676390792Sgshapiro ** Check queue directory permissions. 676490792Sgshapiro ** Can we write to a group writable queue directory? 676590792Sgshapiro */ 676690792Sgshapiro 676790792Sgshapiro if (bitset(S_IWGRP, QueueFileMode) && 676890792Sgshapiro bitset(S_IWGRP, st.st_mode) && 676990792Sgshapiro safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff, 677090792Sgshapiro QueueFileMode, NULL) != 0) 677190792Sgshapiro { 677290792Sgshapiro syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)", 677390792Sgshapiro basedir, (int) RunAsGid, (int) st.st_gid); 677490792Sgshapiro } 677590792Sgshapiro if (bitset(S_IWOTH|S_IXOTH, st.st_mode)) 677690792Sgshapiro { 677790792Sgshapiro#if _FFR_MSP_PARANOIA 677890792Sgshapiro syserr("dangerous permissions=%o on queue directory %s", 677990792Sgshapiro (int) st.st_mode, basedir); 678090792Sgshapiro#else /* _FFR_MSP_PARANOIA */ 678190792Sgshapiro if (LogLevel > 0) 678290792Sgshapiro sm_syslog(LOG_ERR, NOQID, 678390792Sgshapiro "dangerous permissions=%o on queue directory %s", 678490792Sgshapiro (int) st.st_mode, basedir); 678590792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 678690792Sgshapiro } 678790792Sgshapiro#if _FFR_MSP_PARANOIA 678890792Sgshapiro if (NumQueue > 1) 678990792Sgshapiro syserr("can not use multiple queues for MSP"); 679090792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 679190792Sgshapiro } 679290792Sgshapiro 679390792Sgshapiro /* initial number of queue directories */ 679490792Sgshapiro qn = 0; 679590792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 679690792Sgshapiro qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval); 679790792Sgshapiro 679890792Sgshapiro#if SM_CONF_SHM 679990792Sgshapiro init_shm(qn, owner, hashval); 680090792Sgshapiro i = filesys_setup(owner || ShmId == SM_SHM_NO_ID); 680190792Sgshapiro if (i == FSF_NOT_FOUND) 680290792Sgshapiro { 680390792Sgshapiro /* 680490792Sgshapiro ** We didn't get the right filesystem data 680590792Sgshapiro ** This may happen if we don't have the right shared memory. 680690792Sgshapiro ** So let's do this without shared memory. 680790792Sgshapiro */ 680890792Sgshapiro 680990792Sgshapiro SM_ASSERT(!owner); 681090792Sgshapiro cleanup_shm(false); /* release shared memory */ 681190792Sgshapiro i = filesys_setup(false); 681290792Sgshapiro if (i < 0) 681390792Sgshapiro syserr("filesys_setup failed twice, result=%d", i); 681490792Sgshapiro else if (LogLevel > 8) 681590792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 681690792Sgshapiro "shared memory does not contain expected data, ignored"); 681790792Sgshapiro } 681890792Sgshapiro#else /* SM_CONF_SHM */ 681990792Sgshapiro i = filesys_setup(true); 682090792Sgshapiro#endif /* SM_CONF_SHM */ 682190792Sgshapiro if (i < 0) 682290792Sgshapiro ExitStat = EX_CONFIG; 682390792Sgshapiro} 682490792Sgshapiro 682590792Sgshapiro#if SM_CONF_SHM 682690792Sgshapiro/* 682790792Sgshapiro** CLEANUP_SHM -- do some cleanup work for shared memory etc 682890792Sgshapiro** 682990792Sgshapiro** Parameters: 683090792Sgshapiro** owner -- owner of shared memory? 683190792Sgshapiro** 683290792Sgshapiro** Returns: 683390792Sgshapiro** none. 683490792Sgshapiro** 683590792Sgshapiro** Side Effects: 683690792Sgshapiro** detaches shared memory. 683790792Sgshapiro*/ 683890792Sgshapiro 683990792Sgshapirovoid 684090792Sgshapirocleanup_shm(owner) 684190792Sgshapiro bool owner; 684290792Sgshapiro{ 684390792Sgshapiro if (ShmId != SM_SHM_NO_ID) 684490792Sgshapiro { 684590792Sgshapiro if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8) 684698121Sgshapiro sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s", 684790792Sgshapiro sm_errstring(errno)); 684890792Sgshapiro Pshm = NULL; 684990792Sgshapiro ShmId = SM_SHM_NO_ID; 685090792Sgshapiro } 685190792Sgshapiro} 685290792Sgshapiro#endif /* SM_CONF_SHM */ 685390792Sgshapiro 685490792Sgshapiro/* 685590792Sgshapiro** CLEANUP_QUEUES -- do some cleanup work for queues 685690792Sgshapiro** 685790792Sgshapiro** Parameters: 685890792Sgshapiro** none. 685990792Sgshapiro** 686090792Sgshapiro** Returns: 686190792Sgshapiro** none. 686290792Sgshapiro** 686390792Sgshapiro*/ 686490792Sgshapiro 686590792Sgshapirovoid 686690792Sgshapirocleanup_queues() 686790792Sgshapiro{ 686890792Sgshapiro sync_queue_time(); 686990792Sgshapiro} 687090792Sgshapiro/* 687190792Sgshapiro** SET_DEF_QUEUEVAL -- set default values for a queue group. 687290792Sgshapiro** 687390792Sgshapiro** Parameters: 687490792Sgshapiro** qg -- queue group 687590792Sgshapiro** all -- set all values (true for default group)? 687690792Sgshapiro** 687790792Sgshapiro** Returns: 687890792Sgshapiro** none. 687990792Sgshapiro** 688090792Sgshapiro** Side Effects: 688190792Sgshapiro** sets default values for the queue group. 688290792Sgshapiro*/ 688390792Sgshapiro 688490792Sgshapirovoid 688590792Sgshapiroset_def_queueval(qg, all) 688690792Sgshapiro QUEUEGRP *qg; 688790792Sgshapiro bool all; 688890792Sgshapiro{ 688990792Sgshapiro if (bitnset(QD_DEFINED, qg->qg_flags)) 689090792Sgshapiro return; 689190792Sgshapiro if (all) 689290792Sgshapiro qg->qg_qdir = QueueDir; 689394334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 689490792Sgshapiro qg->qg_sortorder = QueueSortOrder; 689594334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 689690792Sgshapiro qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1; 689790792Sgshapiro qg->qg_nice = NiceQueueRun; 689890792Sgshapiro} 689990792Sgshapiro/* 690090792Sgshapiro** MAKEQUEUE -- define a new queue. 690190792Sgshapiro** 690290792Sgshapiro** Parameters: 690390792Sgshapiro** line -- description of queue. This is in labeled fields. 690490792Sgshapiro** The fields are: 690590792Sgshapiro** F -- the flags associated with the queue 690690792Sgshapiro** I -- the interval between running the queue 690790792Sgshapiro** J -- the maximum # of jobs in work list 690890792Sgshapiro** [M -- the maximum # of jobs in a queue run] 690990792Sgshapiro** N -- the niceness at which to run 691090792Sgshapiro** P -- the path to the queue 691190792Sgshapiro** S -- the queue sorting order 691290792Sgshapiro** R -- number of parallel queue runners 691390792Sgshapiro** r -- max recipients per envelope 691490792Sgshapiro** The first word is the canonical name of the queue. 691590792Sgshapiro** qdef -- this is a 'Q' definition from .cf 691690792Sgshapiro** 691790792Sgshapiro** Returns: 691890792Sgshapiro** none. 691990792Sgshapiro** 692090792Sgshapiro** Side Effects: 692190792Sgshapiro** enters the queue into the queue table. 692290792Sgshapiro*/ 692390792Sgshapiro 692490792Sgshapirovoid 692590792Sgshapiromakequeue(line, qdef) 692690792Sgshapiro char *line; 692790792Sgshapiro bool qdef; 692890792Sgshapiro{ 692990792Sgshapiro register char *p; 693090792Sgshapiro register QUEUEGRP *qg; 693190792Sgshapiro register STAB *s; 693290792Sgshapiro int i; 693390792Sgshapiro char fcode; 693490792Sgshapiro 693590792Sgshapiro /* allocate a queue and set up defaults */ 693690792Sgshapiro qg = (QUEUEGRP *) xalloc(sizeof *qg); 693790792Sgshapiro memset((char *) qg, '\0', sizeof *qg); 693890792Sgshapiro 693990792Sgshapiro if (line[0] == '\0') 694090792Sgshapiro { 694190792Sgshapiro syserr("name required for queue"); 694290792Sgshapiro return; 694390792Sgshapiro } 694490792Sgshapiro 694590792Sgshapiro /* collect the queue name */ 694690792Sgshapiro for (p = line; 694790792Sgshapiro *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 694890792Sgshapiro p++) 694990792Sgshapiro continue; 695090792Sgshapiro if (*p != '\0') 695190792Sgshapiro *p++ = '\0'; 695290792Sgshapiro qg->qg_name = newstr(line); 695390792Sgshapiro 695490792Sgshapiro /* set default values, can be overridden below */ 695590792Sgshapiro set_def_queueval(qg, false); 695690792Sgshapiro 695790792Sgshapiro /* now scan through and assign info from the fields */ 695890792Sgshapiro while (*p != '\0') 695990792Sgshapiro { 696090792Sgshapiro auto char *delimptr; 696190792Sgshapiro 696290792Sgshapiro while (*p != '\0' && 696390792Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 696490792Sgshapiro p++; 696590792Sgshapiro 696690792Sgshapiro /* p now points to field code */ 696790792Sgshapiro fcode = *p; 696890792Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 696990792Sgshapiro p++; 697090792Sgshapiro if (*p++ != '=') 697190792Sgshapiro { 697290792Sgshapiro syserr("queue %s: `=' expected", qg->qg_name); 697390792Sgshapiro return; 697490792Sgshapiro } 697590792Sgshapiro while (isascii(*p) && isspace(*p)) 697690792Sgshapiro p++; 697790792Sgshapiro 697890792Sgshapiro /* p now points to the field body */ 697990792Sgshapiro p = munchstring(p, &delimptr, ','); 698090792Sgshapiro 698190792Sgshapiro /* install the field into the queue struct */ 698290792Sgshapiro switch (fcode) 698390792Sgshapiro { 698490792Sgshapiro case 'P': /* pathname */ 698590792Sgshapiro if (*p == '\0') 698690792Sgshapiro syserr("queue %s: empty path name", 698790792Sgshapiro qg->qg_name); 698890792Sgshapiro else 698990792Sgshapiro qg->qg_qdir = newstr(p); 699090792Sgshapiro break; 699190792Sgshapiro 699290792Sgshapiro case 'F': /* flags */ 699390792Sgshapiro for (; *p != '\0'; p++) 699490792Sgshapiro if (!(isascii(*p) && isspace(*p))) 699590792Sgshapiro setbitn(*p, qg->qg_flags); 699690792Sgshapiro break; 699790792Sgshapiro 699890792Sgshapiro /* 699990792Sgshapiro ** Do we need two intervals here: 700090792Sgshapiro ** One for persistent queue runners, 700190792Sgshapiro ** one for "normal" queue runs? 700290792Sgshapiro */ 700390792Sgshapiro 700490792Sgshapiro case 'I': /* interval between running the queue */ 700590792Sgshapiro qg->qg_queueintvl = convtime(p, 'm'); 700690792Sgshapiro break; 700790792Sgshapiro 700890792Sgshapiro case 'N': /* run niceness */ 700990792Sgshapiro qg->qg_nice = atoi(p); 701090792Sgshapiro break; 701190792Sgshapiro 701290792Sgshapiro case 'R': /* maximum # of runners for the group */ 701390792Sgshapiro i = atoi(p); 701490792Sgshapiro 701590792Sgshapiro /* can't have more runners than allowed total */ 701690792Sgshapiro if (MaxQueueChildren > 0 && i > MaxQueueChildren) 701790792Sgshapiro { 701890792Sgshapiro qg->qg_maxqrun = MaxQueueChildren; 701990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 702090792Sgshapiro "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n", 702190792Sgshapiro qg->qg_name, i, 702290792Sgshapiro MaxQueueChildren); 702390792Sgshapiro } 702490792Sgshapiro else 702590792Sgshapiro qg->qg_maxqrun = i; 702690792Sgshapiro break; 702790792Sgshapiro 702890792Sgshapiro case 'J': /* maximum # of jobs in work list */ 702990792Sgshapiro qg->qg_maxlist = atoi(p); 703090792Sgshapiro break; 703190792Sgshapiro 703290792Sgshapiro case 'r': /* max recipients per envelope */ 703390792Sgshapiro qg->qg_maxrcpt = atoi(p); 703490792Sgshapiro break; 703590792Sgshapiro 703694334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 703790792Sgshapiro case 'S': /* queue sorting order */ 703890792Sgshapiro switch (*p) 703990792Sgshapiro { 704090792Sgshapiro case 'h': /* Host first */ 704190792Sgshapiro case 'H': 704290792Sgshapiro qg->qg_sortorder = QSO_BYHOST; 704390792Sgshapiro break; 704490792Sgshapiro 704590792Sgshapiro case 'p': /* Priority order */ 704690792Sgshapiro case 'P': 704790792Sgshapiro qg->qg_sortorder = QSO_BYPRIORITY; 704890792Sgshapiro break; 704990792Sgshapiro 705090792Sgshapiro case 't': /* Submission time */ 705190792Sgshapiro case 'T': 705290792Sgshapiro qg->qg_sortorder = QSO_BYTIME; 705390792Sgshapiro break; 705490792Sgshapiro 705590792Sgshapiro case 'f': /* File name */ 705690792Sgshapiro case 'F': 705790792Sgshapiro qg->qg_sortorder = QSO_BYFILENAME; 705890792Sgshapiro break; 705990792Sgshapiro 706090792Sgshapiro case 'm': /* Modification time */ 706190792Sgshapiro case 'M': 706294334Sgshapiro qg->qg_sortorder = QSO_BYMODTIME; 706390792Sgshapiro break; 706490792Sgshapiro 706594334Sgshapiro case 'r': /* Random */ 706694334Sgshapiro case 'R': 706794334Sgshapiro qg->qg_sortorder = QSO_RANDOM; 706894334Sgshapiro break; 706994334Sgshapiro 707094334Sgshapiro# if _FFR_RHS 707194334Sgshapiro case 's': /* Shuffled host name */ 707294334Sgshapiro case 'S': 707394334Sgshapiro qg->qg_sortorder = QSO_BYSHUFFLE; 707494334Sgshapiro break; 707594334Sgshapiro# endif /* _FFR_RHS */ 707694334Sgshapiro 707790792Sgshapiro default: 707890792Sgshapiro syserr("Invalid queue sort order \"%s\"", p); 707990792Sgshapiro } 708090792Sgshapiro break; 708194334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 708290792Sgshapiro 708390792Sgshapiro default: 708490792Sgshapiro syserr("Q%s: unknown queue equate %c=", 708590792Sgshapiro qg->qg_name, fcode); 708690792Sgshapiro break; 708790792Sgshapiro } 708890792Sgshapiro 708990792Sgshapiro p = delimptr; 709090792Sgshapiro } 709190792Sgshapiro 709290792Sgshapiro#if !HASNICE 709390792Sgshapiro if (qg->qg_nice != NiceQueueRun) 709490792Sgshapiro { 709590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 709690792Sgshapiro "Q%s: Warning: N= set on system that doesn't support nice()\n", 709790792Sgshapiro qg->qg_name); 709890792Sgshapiro } 709990792Sgshapiro#endif /* !HASNICE */ 710090792Sgshapiro 710190792Sgshapiro /* do some rationality checking */ 710290792Sgshapiro if (NumQueue >= MAXQUEUEGROUPS) 710390792Sgshapiro { 710490792Sgshapiro syserr("too many queue groups defined (%d max)", 710590792Sgshapiro MAXQUEUEGROUPS); 710690792Sgshapiro return; 710790792Sgshapiro } 710890792Sgshapiro 710990792Sgshapiro if (qg->qg_qdir == NULL) 711090792Sgshapiro { 711190792Sgshapiro if (QueueDir == NULL || *QueueDir == '\0') 711290792Sgshapiro { 711390792Sgshapiro syserr("QueueDir must be defined before queue groups"); 711490792Sgshapiro return; 711590792Sgshapiro } 711690792Sgshapiro qg->qg_qdir = newstr(QueueDir); 711790792Sgshapiro } 711890792Sgshapiro 711990792Sgshapiro if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags)) 712090792Sgshapiro { 712190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 712290792Sgshapiro "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n", 712390792Sgshapiro qg->qg_name, qg->qg_maxqrun, QD_FORK); 712490792Sgshapiro } 712590792Sgshapiro 712690792Sgshapiro /* enter the queue into the symbol table */ 712790792Sgshapiro if (tTd(37, 8)) 712890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 712990792Sgshapiro "Adding %s to stab, path: %s", qg->qg_name, 713090792Sgshapiro qg->qg_qdir); 713190792Sgshapiro s = stab(qg->qg_name, ST_QUEUE, ST_ENTER); 713290792Sgshapiro if (s->s_quegrp != NULL) 713390792Sgshapiro { 713490792Sgshapiro i = s->s_quegrp->qg_index; 713590792Sgshapiro 713690792Sgshapiro /* XXX what about the pointers inside this struct? */ 713790792Sgshapiro sm_free(s->s_quegrp); /* XXX */ 713890792Sgshapiro } 713990792Sgshapiro else 714090792Sgshapiro i = NumQueue++; 714190792Sgshapiro Queue[i] = s->s_quegrp = qg; 714290792Sgshapiro qg->qg_index = i; 714390792Sgshapiro 714490792Sgshapiro /* set default value for max queue runners */ 714590792Sgshapiro if (qg->qg_maxqrun < 0) 714690792Sgshapiro { 714790792Sgshapiro if (MaxRunnersPerQueue > 0) 714890792Sgshapiro qg->qg_maxqrun = MaxRunnersPerQueue; 714990792Sgshapiro else 715090792Sgshapiro qg->qg_maxqrun = 1; 715190792Sgshapiro } 715290792Sgshapiro if (qdef) 715390792Sgshapiro setbitn(QD_DEFINED, qg->qg_flags); 715490792Sgshapiro} 715590792Sgshapiro#if 0 715690792Sgshapiro/* 715764562Sgshapiro** HASHFQN -- calculate a hash value for a fully qualified host name 715864562Sgshapiro** 715964562Sgshapiro** Arguments: 716064562Sgshapiro** fqn -- an all lower-case host.domain string 716164562Sgshapiro** buckets -- the number of buckets (queue directories) 716264562Sgshapiro** 716364562Sgshapiro** Returns: 716464562Sgshapiro** a bucket number (signed integer) 716564562Sgshapiro** -1 on error 716664562Sgshapiro** 716764562Sgshapiro** Contributed by Exactis.com, Inc. 716864562Sgshapiro*/ 716964562Sgshapiro 717064562Sgshapiroint 717164562Sgshapirohashfqn(fqn, buckets) 717264562Sgshapiro register char *fqn; 717364562Sgshapiro int buckets; 717464562Sgshapiro{ 717564562Sgshapiro register char *p; 717664562Sgshapiro register int h = 0, hash, cnt; 717764562Sgshapiro 717864562Sgshapiro if (fqn == NULL) 717964562Sgshapiro return -1; 718064562Sgshapiro 718164562Sgshapiro /* 718264562Sgshapiro ** A variation on the gdb hash 718364562Sgshapiro ** This is the best as of Feb 19, 1996 --bcx 718464562Sgshapiro */ 718564562Sgshapiro 718664562Sgshapiro p = fqn; 718764562Sgshapiro h = 0x238F13AF * strlen(p); 718864562Sgshapiro for (cnt = 0; *p != 0; ++p, cnt++) 718964562Sgshapiro { 719064562Sgshapiro h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF; 719164562Sgshapiro } 719264562Sgshapiro h = (1103515243 * h + 12345) & 0x7FFFFFFF; 719364562Sgshapiro if (buckets < 2) 719464562Sgshapiro hash = 0; 719564562Sgshapiro else 719664562Sgshapiro hash = (h % buckets); 719764562Sgshapiro 719864562Sgshapiro return hash; 719964562Sgshapiro} 720090792Sgshapiro#endif /* 0 */ 720164562Sgshapiro 720290792Sgshapiro#if _FFR_QUEUEDELAY 720390792Sgshapiro/* 720464562Sgshapiro** QUEUEDELAY -- compute queue delay time 720564562Sgshapiro** 720664562Sgshapiro** Parameters: 720764562Sgshapiro** e -- the envelope to queue up. 720864562Sgshapiro** 720964562Sgshapiro** Returns: 721064562Sgshapiro** queue delay time 721164562Sgshapiro** 721264562Sgshapiro** Side Effects: 721364562Sgshapiro** may change e_queuedelay 721464562Sgshapiro*/ 721564562Sgshapiro 721664562Sgshapirostatic time_t 721764562Sgshapiroqueuedelay(e) 721864562Sgshapiro ENVELOPE *e; 721964562Sgshapiro{ 722064562Sgshapiro time_t qd; 722164562Sgshapiro 722264562Sgshapiro if (e->e_queuealg == QD_EXP) 722364562Sgshapiro { 722464562Sgshapiro if (e->e_queuedelay == 0) 722564562Sgshapiro e->e_queuedelay = QueueInitDelay; 722664562Sgshapiro else 722764562Sgshapiro { 722864562Sgshapiro e->e_queuedelay *= 2; 722964562Sgshapiro if (e->e_queuedelay > QueueMaxDelay) 723064562Sgshapiro e->e_queuedelay = QueueMaxDelay; 723164562Sgshapiro } 723264562Sgshapiro qd = e->e_queuedelay; 723364562Sgshapiro } 723464562Sgshapiro else 723564562Sgshapiro qd = MinQueueAge; 723664562Sgshapiro return qd; 723764562Sgshapiro} 723890792Sgshapiro#endif /* _FFR_QUEUEDELAY */ 723990792Sgshapiro 724090792Sgshapiro/* 724190792Sgshapiro** A structure for sorting Queue according to maxqrun without 724290792Sgshapiro** screwing up Queue itself. 724390792Sgshapiro*/ 724490792Sgshapiro 724590792Sgshapirostruct sortqgrp 724690792Sgshapiro{ 724790792Sgshapiro int sg_idx; /* original index */ 724890792Sgshapiro int sg_maxqrun; /* max queue runners */ 724990792Sgshapiro}; 725090792Sgshapirotypedef struct sortqgrp SORTQGRP_T; 725190792Sgshapirostatic int cmpidx __P((const void *, const void *)); 725290792Sgshapiro 725390792Sgshapirostatic int 725490792Sgshapirocmpidx(a, b) 725590792Sgshapiro const void *a; 725690792Sgshapiro const void *b; 725790792Sgshapiro{ 725890792Sgshapiro /* The sort is highest to lowest, so the comparison is reversed */ 725990792Sgshapiro if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun) 726090792Sgshapiro return 1; 726190792Sgshapiro else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun) 726290792Sgshapiro return -1; 726390792Sgshapiro else 726490792Sgshapiro return 0; 726590792Sgshapiro} 726690792Sgshapiro 726790792Sgshapiro/* 726890792Sgshapiro** MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren 726990792Sgshapiro** 727090792Sgshapiro** Take the now defined queue groups and assign them to work groups. 727190792Sgshapiro** This is done to balance out the number of concurrently active 727290792Sgshapiro** queue runners such that MaxQueueChildren is not exceeded. This may 727390792Sgshapiro** result in more than one queue group per work group. In such a case 727490792Sgshapiro** the number of running queue groups in that work group will have no 727590792Sgshapiro** more than the work group maximum number of runners (a "fair" portion 727690792Sgshapiro** of MaxQueueRunners). All queue groups within a work group will get a 727790792Sgshapiro** chance at running. 727890792Sgshapiro** 727990792Sgshapiro** Parameters: 728090792Sgshapiro** none. 728190792Sgshapiro** 728290792Sgshapiro** Returns: 728390792Sgshapiro** nothing. 728490792Sgshapiro** 728590792Sgshapiro** Side Effects: 728690792Sgshapiro** Sets up WorkGrp structure. 728790792Sgshapiro*/ 728890792Sgshapiro 728990792Sgshapirovoid 729090792Sgshapiromakeworkgroups() 729190792Sgshapiro{ 729290792Sgshapiro int i, j, total_runners = 0; 729390792Sgshapiro int dir; 729490792Sgshapiro SORTQGRP_T si[MAXQUEUEGROUPS + 1]; 729590792Sgshapiro 729690792Sgshapiro if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0) 729790792Sgshapiro { 729890792Sgshapiro /* 729990792Sgshapiro ** There is only the "mqueue" queue group (a default) 730090792Sgshapiro ** containing all of the queues. We want to provide to 730190792Sgshapiro ** this queue group the maximum allowable queue runners. 730290792Sgshapiro ** To match older behavior (8.10/8.11) we'll try for 730390792Sgshapiro ** 1 runner per queue capping it at MaxQueueChildren. 730490792Sgshapiro ** So if there are N queues, then there will be N runners 730590792Sgshapiro ** for the "mqueue" queue group (where N is kept less than 730690792Sgshapiro ** MaxQueueChildren). 730790792Sgshapiro */ 730890792Sgshapiro 730990792Sgshapiro NumWorkGroups = 1; 731090792Sgshapiro WorkGrp[0].wg_numqgrp = 1; 731190792Sgshapiro WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *)); 731290792Sgshapiro WorkGrp[0].wg_qgs[0] = Queue[0]; 731390792Sgshapiro if (MaxQueueChildren > 0 && 731490792Sgshapiro Queue[0]->qg_numqueues > MaxQueueChildren) 731590792Sgshapiro WorkGrp[0].wg_runners = MaxQueueChildren; 731690792Sgshapiro else 731790792Sgshapiro WorkGrp[0].wg_runners = Queue[0]->qg_numqueues; 731890792Sgshapiro 731990792Sgshapiro Queue[0]->qg_wgrp = 0; 732090792Sgshapiro 732190792Sgshapiro /* can't have more runners than allowed total */ 732290792Sgshapiro if (MaxQueueChildren > 0 && 732390792Sgshapiro Queue[0]->qg_maxqrun > MaxQueueChildren) 732490792Sgshapiro Queue[0]->qg_maxqrun = MaxQueueChildren; 732590792Sgshapiro WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun; 732690792Sgshapiro WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl; 732790792Sgshapiro return; 732890792Sgshapiro } 732990792Sgshapiro 733090792Sgshapiro for (i = 0; i < NumQueue; i++) 733190792Sgshapiro { 733290792Sgshapiro si[i].sg_maxqrun = Queue[i]->qg_maxqrun; 733390792Sgshapiro si[i].sg_idx = i; 733490792Sgshapiro } 733590792Sgshapiro qsort(si, NumQueue, sizeof(si[0]), cmpidx); 733690792Sgshapiro 733790792Sgshapiro NumWorkGroups = 0; 733890792Sgshapiro for (i = 0; i < NumQueue; i++) 733990792Sgshapiro { 734090792Sgshapiro total_runners += si[i].sg_maxqrun; 734190792Sgshapiro if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren) 734290792Sgshapiro NumWorkGroups++; 734390792Sgshapiro else 734490792Sgshapiro break; 734590792Sgshapiro } 734690792Sgshapiro 734790792Sgshapiro if (NumWorkGroups < 1) 734890792Sgshapiro NumWorkGroups = 1; /* gotta have one at least */ 734990792Sgshapiro else if (NumWorkGroups > MAXWORKGROUPS) 735090792Sgshapiro NumWorkGroups = MAXWORKGROUPS; /* the limit */ 735190792Sgshapiro 735290792Sgshapiro /* 735390792Sgshapiro ** We now know the number of work groups to pack the queue groups 735490792Sgshapiro ** into. The queue groups in 'Queue' are sorted from highest 735590792Sgshapiro ** to lowest for the number of runners per queue group. 735690792Sgshapiro ** We put the queue groups with the largest number of runners 735790792Sgshapiro ** into work groups first. Then the smaller ones are fitted in 735890792Sgshapiro ** where it looks best. 735990792Sgshapiro */ 736090792Sgshapiro 736190792Sgshapiro j = 0; 736290792Sgshapiro dir = 1; 736390792Sgshapiro for (i = 0; i < NumQueue; i++) 736490792Sgshapiro { 736590792Sgshapiro /* a to-and-fro packing scheme, continue from last position */ 736690792Sgshapiro if (j >= NumWorkGroups) 736790792Sgshapiro { 736890792Sgshapiro dir = -1; 736990792Sgshapiro j = NumWorkGroups - 1; 737090792Sgshapiro } 737190792Sgshapiro else if (j < 0) 737290792Sgshapiro { 737390792Sgshapiro j = 0; 737490792Sgshapiro dir = 1; 737590792Sgshapiro } 737690792Sgshapiro 737790792Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 737894334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) * 737994334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 738094334Sgshapiro else 738194334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs, 738294334Sgshapiro sizeof(QUEUEGRP *) * 738394334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 738494334Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 738590792Sgshapiro { 738694334Sgshapiro syserr("!cannot allocate memory for work queues, need %d bytes", 738790792Sgshapiro (int) (sizeof(QUEUEGRP *) * 738890792Sgshapiro (WorkGrp[j].wg_numqgrp + 1))); 738990792Sgshapiro } 739090792Sgshapiro 739190792Sgshapiro WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[si[i].sg_idx]; 739290792Sgshapiro WorkGrp[j].wg_numqgrp++; 739390792Sgshapiro WorkGrp[j].wg_runners += Queue[i]->qg_maxqrun; 739490792Sgshapiro Queue[si[i].sg_idx]->qg_wgrp = j; 739590792Sgshapiro 739690792Sgshapiro if (WorkGrp[j].wg_maxact == 0) 739790792Sgshapiro { 739890792Sgshapiro /* can't have more runners than allowed total */ 739990792Sgshapiro if (MaxQueueChildren > 0 && 740090792Sgshapiro Queue[i]->qg_maxqrun > MaxQueueChildren) 740190792Sgshapiro Queue[i]->qg_maxqrun = MaxQueueChildren; 740290792Sgshapiro WorkGrp[j].wg_maxact = Queue[i]->qg_maxqrun; 740390792Sgshapiro } 740490792Sgshapiro 740590792Sgshapiro /* 740690792Sgshapiro ** XXX: must wg_lowqintvl be the GCD? 740790792Sgshapiro ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for 740890792Sgshapiro ** qg2 occur? 740990792Sgshapiro */ 741090792Sgshapiro 741190792Sgshapiro /* keep track of the lowest interval for a persistent runner */ 741290792Sgshapiro if (Queue[si[i].sg_idx]->qg_queueintvl > 0 && 741390792Sgshapiro WorkGrp[j].wg_lowqintvl < Queue[si[i].sg_idx]->qg_queueintvl) 741490792Sgshapiro WorkGrp[j].wg_lowqintvl = Queue[si[i].sg_idx]->qg_queueintvl; 741590792Sgshapiro j += dir; 741690792Sgshapiro } 741790792Sgshapiro if (tTd(41, 9)) 741890792Sgshapiro { 741990792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 742090792Sgshapiro { 742190792Sgshapiro sm_dprintf("Workgroup[%d]=", i); 742290792Sgshapiro for (j = 0; j < WorkGrp[i].wg_numqgrp; j++) 742390792Sgshapiro { 742490792Sgshapiro sm_dprintf("%s, ", 742590792Sgshapiro WorkGrp[i].wg_qgs[j]->qg_name); 742690792Sgshapiro } 742790792Sgshapiro sm_dprintf("\n"); 742890792Sgshapiro } 742990792Sgshapiro } 743090792Sgshapiro} 743190792Sgshapiro 743290792Sgshapiro/* 743390792Sgshapiro** DUP_DF -- duplicate envelope data file 743490792Sgshapiro** 743590792Sgshapiro** Copy the data file from the 'old' envelope to the 'new' envelope 743690792Sgshapiro** in the most efficient way possible. 743790792Sgshapiro** 743890792Sgshapiro** Create a hard link from the 'old' data file to the 'new' data file. 743990792Sgshapiro** If the old and new queue directories are on different file systems, 744090792Sgshapiro** then the new data file link is created in the old queue directory, 744190792Sgshapiro** and the new queue file will contain a 'd' record pointing to the 744290792Sgshapiro** directory containing the new data file. 744390792Sgshapiro** 744490792Sgshapiro** Parameters: 744590792Sgshapiro** old -- old envelope. 744690792Sgshapiro** new -- new envelope. 744790792Sgshapiro** 744890792Sgshapiro** Results: 744990792Sgshapiro** Returns true on success, false on failure. 745090792Sgshapiro** 745190792Sgshapiro** Side Effects: 745290792Sgshapiro** On success, the new data file is created. 745390792Sgshapiro** On fatal failure, EF_FATALERRS is set in old->e_flags. 745490792Sgshapiro*/ 745590792Sgshapiro 745690792Sgshapirostatic bool dup_df __P((ENVELOPE *, ENVELOPE *)); 745790792Sgshapiro 745890792Sgshapirostatic bool 745990792Sgshapirodup_df(old, new) 746090792Sgshapiro ENVELOPE *old; 746190792Sgshapiro ENVELOPE *new; 746290792Sgshapiro{ 746390792Sgshapiro int ofs, nfs, r; 746490792Sgshapiro char opath[MAXPATHLEN]; 746590792Sgshapiro char npath[MAXPATHLEN]; 746690792Sgshapiro 746794334Sgshapiro if (!bitset(EF_HAS_DF, old->e_flags)) 746894334Sgshapiro { 746994334Sgshapiro /* 747094334Sgshapiro ** this can happen if: SuperSafe != True 747194334Sgshapiro ** and a bounce mail is sent that is split. 747294334Sgshapiro */ 747394334Sgshapiro 747494334Sgshapiro queueup(old, false, true); 747594334Sgshapiro } 747690792Sgshapiro SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir)); 747790792Sgshapiro SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir)); 747890792Sgshapiro 747990792Sgshapiro (void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof opath); 748090792Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath); 748190792Sgshapiro 748290792Sgshapiro if (old->e_dfp != NULL) 748390792Sgshapiro { 748490792Sgshapiro r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL); 748590792Sgshapiro if (r < 0 && errno != EINVAL) 748690792Sgshapiro { 748790792Sgshapiro syserr("@can't commit %s", opath); 748890792Sgshapiro old->e_flags |= EF_FATALERRS; 748990792Sgshapiro return false; 749090792Sgshapiro } 749190792Sgshapiro } 749290792Sgshapiro 749390792Sgshapiro /* 749490792Sgshapiro ** Attempt to create a hard link, if we think both old and new 749590792Sgshapiro ** are on the same file system, otherwise copy the file. 749690792Sgshapiro ** 749790792Sgshapiro ** Don't waste time attempting a hard link unless old and new 749890792Sgshapiro ** are on the same file system. 749990792Sgshapiro */ 750090792Sgshapiro 750190792Sgshapiro ofs = Queue[old->e_qgrp]->qg_qpaths[old->e_qdir].qp_fsysidx; 750290792Sgshapiro nfs = Queue[new->e_qgrp]->qg_qpaths[new->e_qdir].qp_fsysidx; 750390792Sgshapiro if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs)) 750490792Sgshapiro { 750590792Sgshapiro if (link(opath, npath) == 0) 750690792Sgshapiro { 750790792Sgshapiro new->e_flags |= EF_HAS_DF; 750890792Sgshapiro SYNC_DIR(npath, true); 750990792Sgshapiro return true; 751090792Sgshapiro } 751190792Sgshapiro goto error; 751290792Sgshapiro } 751390792Sgshapiro 751490792Sgshapiro /* 751590792Sgshapiro ** Can't link across queue directories, so try to create a hard 751690792Sgshapiro ** link in the same queue directory as the old df file. 751790792Sgshapiro ** The qf file will refer to the new df file using a 'd' record. 751890792Sgshapiro */ 751990792Sgshapiro 752090792Sgshapiro new->e_dfqgrp = old->e_dfqgrp; 752190792Sgshapiro new->e_dfqdir = old->e_dfqdir; 752290792Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath); 752390792Sgshapiro if (link(opath, npath) == 0) 752490792Sgshapiro { 752590792Sgshapiro new->e_flags |= EF_HAS_DF; 752690792Sgshapiro SYNC_DIR(npath, true); 752790792Sgshapiro return true; 752890792Sgshapiro } 752990792Sgshapiro 753090792Sgshapiro error: 753190792Sgshapiro if (LogLevel > 0) 753290792Sgshapiro sm_syslog(LOG_ERR, old->e_id, 753390792Sgshapiro "dup_df: can't link %s to %s, error=%s, envelope splitting failed", 753490792Sgshapiro opath, npath, sm_errstring(errno)); 753590792Sgshapiro return false; 753690792Sgshapiro} 753790792Sgshapiro 753890792Sgshapiro/* 753990792Sgshapiro** SPLIT_ENV -- Allocate a new envelope based on a given envelope. 754090792Sgshapiro** 754190792Sgshapiro** Parameters: 754290792Sgshapiro** e -- envelope. 754390792Sgshapiro** sendqueue -- sendqueue for new envelope. 754490792Sgshapiro** qgrp -- index of queue group. 754590792Sgshapiro** qdir -- queue directory. 754690792Sgshapiro** 754790792Sgshapiro** Results: 754890792Sgshapiro** new envelope. 754990792Sgshapiro** 755090792Sgshapiro*/ 755190792Sgshapiro 755290792Sgshapirostatic ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int)); 755390792Sgshapiro 755490792Sgshapirostatic ENVELOPE * 755590792Sgshapirosplit_env(e, sendqueue, qgrp, qdir) 755690792Sgshapiro ENVELOPE *e; 755790792Sgshapiro ADDRESS *sendqueue; 755890792Sgshapiro int qgrp; 755990792Sgshapiro int qdir; 756090792Sgshapiro{ 756190792Sgshapiro ENVELOPE *ee; 756290792Sgshapiro 756390792Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof *ee); 756490792Sgshapiro STRUCTCOPY(*e, *ee); 756590792Sgshapiro ee->e_message = NULL; /* XXX use original message? */ 756690792Sgshapiro ee->e_id = NULL; 756790792Sgshapiro assign_queueid(ee); 756890792Sgshapiro ee->e_sendqueue = sendqueue; 756990792Sgshapiro ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS 757090792Sgshapiro |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF); 757190792Sgshapiro ee->e_flags |= EF_NORECEIPT; /* XXX really? */ 757290792Sgshapiro ee->e_from.q_state = QS_SENDER; 757390792Sgshapiro ee->e_dfp = NULL; 757490792Sgshapiro ee->e_lockfp = NULL; 757590792Sgshapiro if (e->e_xfp != NULL) 757690792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 757794334Sgshapiro 757894334Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 757994334Sgshapiro if (ee->e_xfp == NULL) 758094334Sgshapiro openxscript(ee); 758194334Sgshapiro 758290792Sgshapiro ee->e_qgrp = ee->e_dfqgrp = qgrp; 758390792Sgshapiro ee->e_qdir = ee->e_dfqdir = qdir; 758490792Sgshapiro ee->e_errormode = EM_MAIL; 758590792Sgshapiro ee->e_statmsg = NULL; 758690792Sgshapiro#if _FFR_QUARANTINE 758790792Sgshapiro if (e->e_quarmsg != NULL) 758890792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 758990792Sgshapiro e->e_quarmsg); 759090792Sgshapiro#endif /* _FFR_QUARANTINE */ 759190792Sgshapiro 759290792Sgshapiro /* 759390792Sgshapiro ** XXX Not sure if this copying is necessary. 759494334Sgshapiro ** sendall() does this copying, but I (dm) don't know if that is 759590792Sgshapiro ** because of the storage management discipline we were using 759690792Sgshapiro ** before rpools were introduced, or if it is because these lists 759790792Sgshapiro ** can be modified later. 759890792Sgshapiro */ 759990792Sgshapiro 760090792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 760190792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool); 760290792Sgshapiro 760390792Sgshapiro return ee; 760490792Sgshapiro} 760590792Sgshapiro 760690792Sgshapiro/* return values from split functions, check also below! */ 760790792Sgshapiro#define SM_SPLIT_FAIL (0) 760890792Sgshapiro#define SM_SPLIT_NONE (1) 760990792Sgshapiro#define SM_SPLIT_NEW(n) (1 + (n)) 761090792Sgshapiro 761190792Sgshapiro/* 761290792Sgshapiro** SPLIT_ACROSS_QUEUE_GROUPS 761390792Sgshapiro** 761490792Sgshapiro** This function splits an envelope across multiple queue groups 761590792Sgshapiro** based on the queue group of each recipient. 761690792Sgshapiro** 761790792Sgshapiro** Parameters: 761890792Sgshapiro** e -- envelope. 761990792Sgshapiro** 762090792Sgshapiro** Results: 762190792Sgshapiro** SM_SPLIT_FAIL on failure 762290792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 762390792Sgshapiro** or 1 + the number of additional envelopes created. 762490792Sgshapiro** 762590792Sgshapiro** Side Effects: 762690792Sgshapiro** On success, e->e_sibling points to a list of zero or more 762790792Sgshapiro** additional envelopes, and the associated data files exist 762890792Sgshapiro** on disk. But the queue files are not created. 762990792Sgshapiro** 763090792Sgshapiro** On failure, e->e_sibling is not changed. 763190792Sgshapiro** The order of recipients in e->e_sendqueue is permuted. 763290792Sgshapiro** Abandoned data files for additional envelopes that failed 763390792Sgshapiro** to be created may exist on disk. 763490792Sgshapiro*/ 763590792Sgshapiro 763690792Sgshapirostatic int q_qgrp_compare __P((const void *, const void *)); 763790792Sgshapirostatic int e_filesys_compare __P((const void *, const void *)); 763890792Sgshapiro 763990792Sgshapirostatic int 764090792Sgshapiroq_qgrp_compare(p1, p2) 764190792Sgshapiro const void *p1; 764290792Sgshapiro const void *p2; 764390792Sgshapiro{ 764490792Sgshapiro ADDRESS **pq1 = (ADDRESS **) p1; 764590792Sgshapiro ADDRESS **pq2 = (ADDRESS **) p2; 764690792Sgshapiro 764790792Sgshapiro return (*pq1)->q_qgrp - (*pq2)->q_qgrp; 764890792Sgshapiro} 764990792Sgshapiro 765090792Sgshapirostatic int 765190792Sgshapiroe_filesys_compare(p1, p2) 765290792Sgshapiro const void *p1; 765390792Sgshapiro const void *p2; 765490792Sgshapiro{ 765590792Sgshapiro ENVELOPE **pe1 = (ENVELOPE **) p1; 765690792Sgshapiro ENVELOPE **pe2 = (ENVELOPE **) p2; 765790792Sgshapiro int fs1, fs2; 765890792Sgshapiro 765990792Sgshapiro fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx; 766090792Sgshapiro fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx; 766190792Sgshapiro if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2)) 766290792Sgshapiro return -1; 766390792Sgshapiro if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2)) 766490792Sgshapiro return 1; 766590792Sgshapiro return 0; 766690792Sgshapiro} 766790792Sgshapiro 766890792Sgshapirostatic int 766990792Sgshapirosplit_across_queue_groups(e) 767090792Sgshapiro ENVELOPE *e; 767190792Sgshapiro{ 767290792Sgshapiro int naddrs, nsplits, i; 767390792Sgshapiro char **pvp; 767490792Sgshapiro ADDRESS *q, **addrs; 767590792Sgshapiro ENVELOPE *ee, *es; 767690792Sgshapiro ENVELOPE *splits[MAXQUEUEGROUPS]; 767790792Sgshapiro char pvpbuf[PSBUFSIZE]; 767890792Sgshapiro 767990792Sgshapiro SM_REQUIRE(ISVALIDQGRP(e->e_qgrp)); 768090792Sgshapiro 768190792Sgshapiro /* Count addresses and assign queue groups. */ 768290792Sgshapiro naddrs = 0; 768390792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 768490792Sgshapiro { 768590792Sgshapiro if (QS_IS_DEAD(q->q_state)) 768690792Sgshapiro continue; 768790792Sgshapiro ++naddrs; 768890792Sgshapiro 768990792Sgshapiro /* bad addresses and those already sent stay put */ 769090792Sgshapiro if (QS_IS_BADADDR(q->q_state) || 769190792Sgshapiro QS_IS_SENT(q->q_state)) 769290792Sgshapiro q->q_qgrp = e->e_qgrp; 769390792Sgshapiro else if (!ISVALIDQGRP(q->q_qgrp)) 769490792Sgshapiro { 769590792Sgshapiro /* call ruleset which should return a queue group */ 769690792Sgshapiro i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp, 769790792Sgshapiro pvpbuf, sizeof(pvpbuf)); 769890792Sgshapiro if (i == EX_OK && 769990792Sgshapiro pvp != NULL && pvp[0] != NULL && 770090792Sgshapiro (pvp[0][0] & 0377) == CANONNET && 770190792Sgshapiro pvp[1] != NULL && pvp[1][0] != '\0') 770290792Sgshapiro { 770390792Sgshapiro i = name2qid(pvp[1]); 770490792Sgshapiro if (ISVALIDQGRP(i)) 770590792Sgshapiro { 770690792Sgshapiro q->q_qgrp = i; 770790792Sgshapiro if (tTd(20, 4)) 770890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 770990792Sgshapiro "queue group name %s -> %d", 771090792Sgshapiro pvp[1], i); 771190792Sgshapiro continue; 771290792Sgshapiro } 771390792Sgshapiro else if (LogLevel > 10) 771490792Sgshapiro sm_syslog(LOG_INFO, NOQID, 771590792Sgshapiro "can't find queue group name %s, selection ignored", 771690792Sgshapiro pvp[1]); 771790792Sgshapiro } 771890792Sgshapiro if (q->q_mailer != NULL && 771990792Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 772090792Sgshapiro q->q_qgrp = q->q_mailer->m_qgrp; 772194334Sgshapiro else if (ISVALIDQGRP(e->e_qgrp)) 772294334Sgshapiro q->q_qgrp = e->e_qgrp; 772390792Sgshapiro else 772490792Sgshapiro q->q_qgrp = 0; 772590792Sgshapiro } 772690792Sgshapiro } 772790792Sgshapiro 772890792Sgshapiro /* only one address? nothing to split. */ 772990792Sgshapiro if (naddrs <= 1) 773090792Sgshapiro return SM_SPLIT_NONE; 773190792Sgshapiro 773290792Sgshapiro /* sort the addresses by queue group */ 773390792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *)); 773490792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 773590792Sgshapiro { 773690792Sgshapiro if (QS_IS_DEAD(q->q_state)) 773790792Sgshapiro continue; 773890792Sgshapiro addrs[i++] = q; 773990792Sgshapiro } 774090792Sgshapiro qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare); 774190792Sgshapiro 774290792Sgshapiro /* split into multiple envelopes, by queue group */ 774390792Sgshapiro nsplits = 0; 774490792Sgshapiro es = NULL; 774590792Sgshapiro e->e_sendqueue = NULL; 774690792Sgshapiro for (i = 0; i < naddrs; ++i) 774790792Sgshapiro { 774890792Sgshapiro if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp) 774990792Sgshapiro addrs[i]->q_next = NULL; 775090792Sgshapiro else 775190792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 775290792Sgshapiro 775390792Sgshapiro /* same queue group as original envelope? */ 775490792Sgshapiro if (addrs[i]->q_qgrp == e->e_qgrp) 775590792Sgshapiro { 775690792Sgshapiro if (e->e_sendqueue == NULL) 775790792Sgshapiro e->e_sendqueue = addrs[i]; 775890792Sgshapiro continue; 775990792Sgshapiro } 776090792Sgshapiro 776190792Sgshapiro /* different queue group than original envelope */ 776290792Sgshapiro if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp) 776390792Sgshapiro { 776490792Sgshapiro ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR); 776590792Sgshapiro es = ee; 776690792Sgshapiro splits[nsplits++] = ee; 776790792Sgshapiro } 776890792Sgshapiro } 776990792Sgshapiro 777090792Sgshapiro /* no splits? return right now. */ 777190792Sgshapiro if (nsplits <= 0) 777290792Sgshapiro return SM_SPLIT_NONE; 777390792Sgshapiro 777490792Sgshapiro /* assign a queue directory to each additional envelope */ 777590792Sgshapiro for (i = 0; i < nsplits; ++i) 777690792Sgshapiro { 777790792Sgshapiro es = splits[i]; 777890792Sgshapiro#if 0 777990792Sgshapiro es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es); 778090792Sgshapiro#endif /* 0 */ 778190792Sgshapiro if (!setnewqueue(es)) 778290792Sgshapiro goto failure; 778390792Sgshapiro } 778490792Sgshapiro 778590792Sgshapiro /* sort the additional envelopes by queue file system */ 778690792Sgshapiro qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare); 778790792Sgshapiro 778890792Sgshapiro /* create data files for each additional envelope */ 778990792Sgshapiro if (!dup_df(e, splits[0])) 779090792Sgshapiro { 779190792Sgshapiro i = 0; 779290792Sgshapiro goto failure; 779390792Sgshapiro } 779490792Sgshapiro for (i = 1; i < nsplits; ++i) 779590792Sgshapiro { 779690792Sgshapiro /* copy or link to the previous data file */ 779790792Sgshapiro if (!dup_df(splits[i - 1], splits[i])) 779890792Sgshapiro goto failure; 779990792Sgshapiro } 780090792Sgshapiro 780190792Sgshapiro /* success: prepend the new envelopes to the e->e_sibling list */ 780290792Sgshapiro for (i = 0; i < nsplits; ++i) 780390792Sgshapiro { 780490792Sgshapiro es = splits[i]; 780590792Sgshapiro es->e_sibling = e->e_sibling; 780690792Sgshapiro e->e_sibling = es; 780790792Sgshapiro } 780890792Sgshapiro return SM_SPLIT_NEW(nsplits); 780990792Sgshapiro 781090792Sgshapiro /* failure: clean up */ 781190792Sgshapiro failure: 781290792Sgshapiro if (i > 0) 781390792Sgshapiro { 781490792Sgshapiro int j; 781590792Sgshapiro 781690792Sgshapiro for (j = 0; j < i; j++) 781790792Sgshapiro (void) unlink(queuename(splits[j], DATAFL_LETTER)); 781890792Sgshapiro } 781990792Sgshapiro e->e_sendqueue = addrs[0]; 782090792Sgshapiro for (i = 0; i < naddrs - 1; ++i) 782190792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 782290792Sgshapiro addrs[naddrs - 1]->q_next = NULL; 782390792Sgshapiro return SM_SPLIT_FAIL; 782490792Sgshapiro} 782590792Sgshapiro 782690792Sgshapiro/* 782790792Sgshapiro** SPLIT_WITHIN_QUEUE 782890792Sgshapiro** 782990792Sgshapiro** Split an envelope with multiple recipients into several 783090792Sgshapiro** envelopes within the same queue directory, if the number of 783190792Sgshapiro** recipients exceeds the limit for the queue group. 783290792Sgshapiro** 783390792Sgshapiro** Parameters: 783490792Sgshapiro** e -- envelope. 783590792Sgshapiro** 783690792Sgshapiro** Results: 783790792Sgshapiro** SM_SPLIT_FAIL on failure 783890792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 783990792Sgshapiro** or 1 + the number of additional envelopes created. 784090792Sgshapiro*/ 784190792Sgshapiro 784290792Sgshapiro#define SPLIT_LOG_LEVEL 8 784390792Sgshapiro 784490792Sgshapirostatic int split_within_queue __P((ENVELOPE *)); 784590792Sgshapiro 784690792Sgshapirostatic int 784790792Sgshapirosplit_within_queue(e) 784890792Sgshapiro ENVELOPE *e; 784990792Sgshapiro{ 785090792Sgshapiro int maxrcpt, nrcpt, ndead, nsplit, i; 785190792Sgshapiro int j, l; 785290792Sgshapiro char *lsplits; 785390792Sgshapiro ADDRESS *q, **addrs; 785490792Sgshapiro ENVELOPE *ee, *firstsibling; 785590792Sgshapiro 785690792Sgshapiro if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags)) 785790792Sgshapiro return SM_SPLIT_NONE; 785890792Sgshapiro 785990792Sgshapiro /* don't bother if there is no recipient limit */ 786090792Sgshapiro maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt; 786190792Sgshapiro if (maxrcpt <= 0) 786290792Sgshapiro return SM_SPLIT_NONE; 786390792Sgshapiro 786490792Sgshapiro /* count recipients */ 786590792Sgshapiro nrcpt = 0; 786690792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 786790792Sgshapiro { 786890792Sgshapiro if (QS_IS_DEAD(q->q_state)) 786990792Sgshapiro continue; 787090792Sgshapiro ++nrcpt; 787190792Sgshapiro } 787290792Sgshapiro if (nrcpt <= maxrcpt) 787390792Sgshapiro return SM_SPLIT_NONE; 787490792Sgshapiro 787590792Sgshapiro /* 787690792Sgshapiro ** Preserve the recipient list 787790792Sgshapiro ** so that we can restore it in case of error. 787890792Sgshapiro ** (But we discard dead addresses.) 787990792Sgshapiro */ 788090792Sgshapiro 788190792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *)); 788290792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 788390792Sgshapiro { 788490792Sgshapiro if (QS_IS_DEAD(q->q_state)) 788590792Sgshapiro continue; 788690792Sgshapiro addrs[i++] = q; 788790792Sgshapiro } 788890792Sgshapiro 788990792Sgshapiro /* 789090792Sgshapiro ** Partition the recipient list so that bad and sent addresses 789190792Sgshapiro ** come first. These will go with the original envelope, and 789290792Sgshapiro ** do not count towards the maxrcpt limit. 789390792Sgshapiro ** addrs[] does not contain QS_IS_DEAD() addresses. 789490792Sgshapiro */ 789590792Sgshapiro 789690792Sgshapiro ndead = 0; 789790792Sgshapiro for (i = 0; i < nrcpt; ++i) 789890792Sgshapiro { 789990792Sgshapiro if (QS_IS_BADADDR(addrs[i]->q_state) || 790090792Sgshapiro QS_IS_SENT(addrs[i]->q_state) || 790190792Sgshapiro QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */ 790290792Sgshapiro { 790390792Sgshapiro if (i > ndead) 790490792Sgshapiro { 790590792Sgshapiro ADDRESS *tmp = addrs[i]; 790690792Sgshapiro 790790792Sgshapiro addrs[i] = addrs[ndead]; 790890792Sgshapiro addrs[ndead] = tmp; 790990792Sgshapiro } 791090792Sgshapiro ++ndead; 791190792Sgshapiro } 791290792Sgshapiro } 791390792Sgshapiro 791490792Sgshapiro /* Check if no splitting required. */ 791590792Sgshapiro if (nrcpt - ndead <= maxrcpt) 791690792Sgshapiro return SM_SPLIT_NONE; 791790792Sgshapiro 791890792Sgshapiro /* fix links */ 791990792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 792090792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 792190792Sgshapiro addrs[nrcpt - 1]->q_next = NULL; 792290792Sgshapiro e->e_sendqueue = addrs[0]; 792390792Sgshapiro 792490792Sgshapiro /* prepare buffer for logging */ 792590792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL) 792690792Sgshapiro { 792790792Sgshapiro l = MAXLINE; 792890792Sgshapiro lsplits = sm_malloc(l); 792990792Sgshapiro if (lsplits != NULL) 793090792Sgshapiro *lsplits = '\0'; 793190792Sgshapiro j = 0; 793290792Sgshapiro } 793390792Sgshapiro else 793490792Sgshapiro { 793590792Sgshapiro /* get rid of stupid compiler warnings */ 793690792Sgshapiro lsplits = NULL; 793790792Sgshapiro j = l = 0; 793890792Sgshapiro } 793990792Sgshapiro 794090792Sgshapiro /* split the envelope */ 794190792Sgshapiro firstsibling = e->e_sibling; 794290792Sgshapiro i = maxrcpt + ndead; 794390792Sgshapiro nsplit = 0; 794490792Sgshapiro for (;;) 794590792Sgshapiro { 794690792Sgshapiro addrs[i - 1]->q_next = NULL; 794790792Sgshapiro ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir); 794890792Sgshapiro if (!dup_df(e, ee)) 794990792Sgshapiro { 795090792Sgshapiro 795190792Sgshapiro ee = firstsibling; 795290792Sgshapiro while (ee != NULL) 795390792Sgshapiro { 795490792Sgshapiro (void) unlink(queuename(ee, DATAFL_LETTER)); 795590792Sgshapiro ee = ee->e_sibling; 795690792Sgshapiro } 795790792Sgshapiro 795890792Sgshapiro /* Error. Restore e's sibling & recipient lists. */ 795990792Sgshapiro e->e_sibling = firstsibling; 796090792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 796190792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 796290792Sgshapiro return SM_SPLIT_FAIL; 796390792Sgshapiro } 796490792Sgshapiro 796590792Sgshapiro /* prepend the new envelope to e->e_sibling */ 796690792Sgshapiro ee->e_sibling = e->e_sibling; 796790792Sgshapiro e->e_sibling = ee; 796890792Sgshapiro ++nsplit; 796990792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 797090792Sgshapiro { 797190792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 797290792Sgshapiro { 797390792Sgshapiro char *p; 797490792Sgshapiro 797590792Sgshapiro l += MAXLINE; 797690792Sgshapiro p = sm_realloc(lsplits, l); 797790792Sgshapiro if (p == NULL) 797890792Sgshapiro { 797990792Sgshapiro /* let's try to get this done */ 798090792Sgshapiro sm_free(lsplits); 798190792Sgshapiro lsplits = NULL; 798290792Sgshapiro } 798390792Sgshapiro else 798490792Sgshapiro lsplits = p; 798590792Sgshapiro } 798690792Sgshapiro if (lsplits != NULL) 798790792Sgshapiro { 798890792Sgshapiro if (j == 0) 798990792Sgshapiro j += sm_strlcat(lsplits + j, 799090792Sgshapiro ee->e_id, 799190792Sgshapiro l - j); 799290792Sgshapiro else 799390792Sgshapiro j += sm_strlcat2(lsplits + j, 799490792Sgshapiro "; ", 799590792Sgshapiro ee->e_id, 799690792Sgshapiro l - j); 799790792Sgshapiro SM_ASSERT(j < l); 799890792Sgshapiro } 799990792Sgshapiro } 800090792Sgshapiro if (nrcpt - i <= maxrcpt) 800190792Sgshapiro break; 800290792Sgshapiro i += maxrcpt; 800390792Sgshapiro } 800490792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && nsplit > 0) 800590792Sgshapiro { 800690792Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, 800790792Sgshapiro "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s", 800890792Sgshapiro maxrcpt, nrcpt - ndead, nsplit, 800990792Sgshapiro nsplit > 1 ? "s" : "", lsplits); 801090792Sgshapiro sm_free(lsplits); 801190792Sgshapiro } 801290792Sgshapiro return SM_SPLIT_NEW(nsplit); 801390792Sgshapiro} 801490792Sgshapiro/* 801590792Sgshapiro** SPLIT_BY_RECIPIENT 801690792Sgshapiro** 801790792Sgshapiro** Split an envelope with multiple recipients into multiple 801890792Sgshapiro** envelopes as required by the sendmail configuration. 801990792Sgshapiro** 802090792Sgshapiro** Parameters: 802190792Sgshapiro** e -- envelope. 802290792Sgshapiro** 802390792Sgshapiro** Results: 802490792Sgshapiro** Returns true on success, false on failure. 802590792Sgshapiro** 802690792Sgshapiro** Side Effects: 802790792Sgshapiro** see split_across_queue_groups(), split_within_queue(e) 802890792Sgshapiro*/ 802990792Sgshapiro 803090792Sgshapirobool 803190792Sgshapirosplit_by_recipient(e) 803290792Sgshapiro ENVELOPE *e; 803390792Sgshapiro{ 803490792Sgshapiro int split, n, i, j, l; 803590792Sgshapiro char *lsplits; 803690792Sgshapiro ENVELOPE *ee, *next, *firstsibling; 803790792Sgshapiro 803890792Sgshapiro if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) || 803990792Sgshapiro bitset(EF_SPLIT, e->e_flags)) 804090792Sgshapiro return true; 804190792Sgshapiro n = split_across_queue_groups(e); 804290792Sgshapiro if (n == SM_SPLIT_FAIL) 804390792Sgshapiro return false; 804490792Sgshapiro firstsibling = ee = e->e_sibling; 804590792Sgshapiro if (n > 1 && LogLevel > SPLIT_LOG_LEVEL) 804690792Sgshapiro { 804790792Sgshapiro l = MAXLINE; 804890792Sgshapiro lsplits = sm_malloc(l); 804990792Sgshapiro if (lsplits != NULL) 805090792Sgshapiro *lsplits = '\0'; 805190792Sgshapiro j = 0; 805290792Sgshapiro } 805390792Sgshapiro else 805490792Sgshapiro { 805590792Sgshapiro /* get rid of stupid compiler warnings */ 805690792Sgshapiro lsplits = NULL; 805790792Sgshapiro j = l = 0; 805890792Sgshapiro } 805990792Sgshapiro for (i = 1; i < n; ++i) 806090792Sgshapiro { 806190792Sgshapiro next = ee->e_sibling; 806290792Sgshapiro if (split_within_queue(ee) == SM_SPLIT_FAIL) 806390792Sgshapiro { 806490792Sgshapiro e->e_sibling = firstsibling; 806590792Sgshapiro return false; 806690792Sgshapiro } 806790792Sgshapiro ee->e_flags |= EF_SPLIT; 806890792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 806990792Sgshapiro { 807090792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 807190792Sgshapiro { 807290792Sgshapiro char *p; 807390792Sgshapiro 807490792Sgshapiro l += MAXLINE; 807590792Sgshapiro p = sm_realloc(lsplits, l); 807690792Sgshapiro if (p == NULL) 807790792Sgshapiro { 807890792Sgshapiro /* let's try to get this done */ 807990792Sgshapiro sm_free(lsplits); 808090792Sgshapiro lsplits = NULL; 808190792Sgshapiro } 808290792Sgshapiro else 808390792Sgshapiro lsplits = p; 808490792Sgshapiro } 808590792Sgshapiro if (lsplits != NULL) 808690792Sgshapiro { 808790792Sgshapiro if (j == 0) 808890792Sgshapiro j += sm_strlcat(lsplits + j, 808990792Sgshapiro ee->e_id, l - j); 809090792Sgshapiro else 809190792Sgshapiro j += sm_strlcat2(lsplits + j, "; ", 809290792Sgshapiro ee->e_id, l - j); 809390792Sgshapiro SM_ASSERT(j < l); 809490792Sgshapiro } 809590792Sgshapiro } 809690792Sgshapiro ee = next; 809790792Sgshapiro } 809890792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1) 809990792Sgshapiro { 810090792Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s", 810190792Sgshapiro n - 1, n > 2 ? "s" : "", lsplits); 810290792Sgshapiro sm_free(lsplits); 810390792Sgshapiro } 810490792Sgshapiro split = split_within_queue(e) != SM_SPLIT_FAIL; 810590792Sgshapiro if (split) 810690792Sgshapiro e->e_flags |= EF_SPLIT; 810790792Sgshapiro return split; 810890792Sgshapiro} 810990792Sgshapiro 811090792Sgshapiro#if _FFR_QUARANTINE 811190792Sgshapiro/* 811290792Sgshapiro** QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope 811390792Sgshapiro** 811490792Sgshapiro** Add/remove quarantine reason and requeue appropriately. 811590792Sgshapiro** 811690792Sgshapiro** Parameters: 811790792Sgshapiro** qgrp -- queue group for the item 811890792Sgshapiro** qdir -- queue directory in the given queue group 811990792Sgshapiro** e -- envelope information for the item 812090792Sgshapiro** reason -- quarantine reason, NULL means unquarantine. 812190792Sgshapiro** 812290792Sgshapiro** Results: 812390792Sgshapiro** true if item changed, false otherwise 812490792Sgshapiro** 812590792Sgshapiro** Side Effects: 812690792Sgshapiro** Changes quarantine tag in queue file and renames it. 812790792Sgshapiro*/ 812890792Sgshapiro 812990792Sgshapirostatic bool 813090792Sgshapiroquarantine_queue_item(qgrp, qdir, e, reason) 813190792Sgshapiro int qgrp; 813290792Sgshapiro int qdir; 813390792Sgshapiro ENVELOPE *e; 813490792Sgshapiro char *reason; 813590792Sgshapiro{ 813690792Sgshapiro bool dirty = false; 813790792Sgshapiro bool failing = false; 813890792Sgshapiro bool foundq = false; 813990792Sgshapiro bool finished = false; 814090792Sgshapiro int fd; 814190792Sgshapiro int flags; 814290792Sgshapiro int oldtype; 814390792Sgshapiro int newtype; 814490792Sgshapiro int save_errno; 814590792Sgshapiro MODE_T oldumask = 0; 814690792Sgshapiro SM_FILE_T *oldqfp, *tempqfp; 814790792Sgshapiro char *bp; 814890792Sgshapiro char oldqf[MAXPATHLEN]; 814990792Sgshapiro char tempqf[MAXPATHLEN]; 815090792Sgshapiro char newqf[MAXPATHLEN]; 815190792Sgshapiro char buf[MAXLINE]; 815290792Sgshapiro 815390792Sgshapiro oldtype = queue_letter(e, ANYQFL_LETTER); 815490792Sgshapiro (void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof oldqf); 815590792Sgshapiro (void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof tempqf); 815690792Sgshapiro 815790792Sgshapiro /* 815890792Sgshapiro ** Instead of duplicating all the open 815990792Sgshapiro ** and lock code here, tell readqf() to 816090792Sgshapiro ** do that work and return the open 816190792Sgshapiro ** file pointer in e_lockfp. Note that 816290792Sgshapiro ** we must release the locks properly when 816390792Sgshapiro ** we are done. 816490792Sgshapiro */ 816590792Sgshapiro 816690792Sgshapiro if (!readqf(e, true)) 816790792Sgshapiro { 816890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 816990792Sgshapiro "Skipping %s\n", qid_printname(e)); 817090792Sgshapiro return false; 817190792Sgshapiro } 817290792Sgshapiro oldqfp = e->e_lockfp; 817390792Sgshapiro 817490792Sgshapiro /* open the new queue file */ 817590792Sgshapiro flags = O_CREAT|O_WRONLY|O_EXCL; 817690792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 817790792Sgshapiro oldumask = umask(002); 817890792Sgshapiro fd = open(tempqf, flags, QueueFileMode); 817990792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 818090792Sgshapiro (void) umask(oldumask); 818190792Sgshapiro RELEASE_QUEUE; 818290792Sgshapiro 818390792Sgshapiro if (fd < 0) 818490792Sgshapiro { 818590792Sgshapiro save_errno = errno; 818690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 818790792Sgshapiro "Skipping %s: Could not open %s: %s\n", 818890792Sgshapiro qid_printname(e), tempqf, 818990792Sgshapiro sm_errstring(save_errno)); 819090792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 819190792Sgshapiro return false; 819290792Sgshapiro } 819390792Sgshapiro if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB)) 819490792Sgshapiro { 819590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 819690792Sgshapiro "Skipping %s: Could not lock %s\n", 819790792Sgshapiro qid_printname(e), tempqf); 819890792Sgshapiro (void) close(fd); 819990792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 820090792Sgshapiro return false; 820190792Sgshapiro } 820290792Sgshapiro 820390792Sgshapiro tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd, 820490792Sgshapiro SM_IO_WRONLY, NULL); 820590792Sgshapiro if (tempqfp == NULL) 820690792Sgshapiro { 820790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 820890792Sgshapiro "Skipping %s: Could not lock %s\n", 820990792Sgshapiro qid_printname(e), tempqf); 821090792Sgshapiro (void) close(fd); 821190792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 821290792Sgshapiro return false; 821390792Sgshapiro } 821490792Sgshapiro 821590792Sgshapiro /* Copy the data over, changing the quarantine reason */ 821690792Sgshapiro while ((bp = fgetfolded(buf, sizeof buf, oldqfp)) != NULL) 821790792Sgshapiro { 821890792Sgshapiro if (tTd(40, 4)) 821990792Sgshapiro sm_dprintf("+++++ %s\n", bp); 822090792Sgshapiro switch (bp[0]) 822190792Sgshapiro { 822290792Sgshapiro case 'q': /* quarantine reason */ 822390792Sgshapiro foundq = true; 822490792Sgshapiro if (reason == NULL) 822590792Sgshapiro { 822690792Sgshapiro if (Verbose) 822790792Sgshapiro { 822890792Sgshapiro (void) sm_io_fprintf(smioout, 822990792Sgshapiro SM_TIME_DEFAULT, 823090792Sgshapiro "%s: Removed quarantine of \"%s\"\n", 823190792Sgshapiro e->e_id, &bp[1]); 823290792Sgshapiro } 823390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "unquarantine"); 823490792Sgshapiro dirty = true; 823590792Sgshapiro continue; 823690792Sgshapiro } 823790792Sgshapiro else if (strcmp(reason, &bp[1]) == 0) 823890792Sgshapiro { 823990792Sgshapiro if (Verbose) 824090792Sgshapiro { 824190792Sgshapiro (void) sm_io_fprintf(smioout, 824290792Sgshapiro SM_TIME_DEFAULT, 824390792Sgshapiro "%s: Already quarantined with \"%s\"\n", 824490792Sgshapiro e->e_id, reason); 824590792Sgshapiro } 824690792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 824790792Sgshapiro "q%s\n", reason); 824890792Sgshapiro } 824990792Sgshapiro else 825090792Sgshapiro { 825190792Sgshapiro if (Verbose) 825290792Sgshapiro { 825390792Sgshapiro (void) sm_io_fprintf(smioout, 825490792Sgshapiro SM_TIME_DEFAULT, 825590792Sgshapiro "%s: Quarantine changed from \"%s\" to \"%s\"\n", 825690792Sgshapiro e->e_id, &bp[1], 825790792Sgshapiro reason); 825890792Sgshapiro } 825990792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 826090792Sgshapiro "q%s\n", reason); 826190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 826290792Sgshapiro reason); 826390792Sgshapiro dirty = true; 826490792Sgshapiro } 826590792Sgshapiro break; 826690792Sgshapiro 826798121Sgshapiro case 'S': 826890792Sgshapiro /* 826990792Sgshapiro ** If we are quarantining an unquarantined item, 827090792Sgshapiro ** need to put in a new 'q' line before it's 827190792Sgshapiro ** too late. 827290792Sgshapiro */ 827390792Sgshapiro 827490792Sgshapiro if (!foundq && reason != NULL) 827590792Sgshapiro { 827690792Sgshapiro if (Verbose) 827790792Sgshapiro { 827890792Sgshapiro (void) sm_io_fprintf(smioout, 827990792Sgshapiro SM_TIME_DEFAULT, 828090792Sgshapiro "%s: Quarantined with \"%s\"\n", 828190792Sgshapiro e->e_id, reason); 828290792Sgshapiro } 828390792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 828490792Sgshapiro "q%s\n", reason); 828590792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 828690792Sgshapiro reason); 828790792Sgshapiro foundq = true; 828890792Sgshapiro dirty = true; 828990792Sgshapiro } 829090792Sgshapiro 829190792Sgshapiro /* Copy the line to the new file */ 829290792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 829390792Sgshapiro "%s\n", bp); 829490792Sgshapiro break; 829590792Sgshapiro 829690792Sgshapiro case '.': 829790792Sgshapiro finished = true; 829890792Sgshapiro /* FALLTHROUGH */ 829990792Sgshapiro 830090792Sgshapiro default: 830190792Sgshapiro /* Copy the line to the new file */ 830290792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 830390792Sgshapiro "%s\n", bp); 830490792Sgshapiro break; 830590792Sgshapiro } 830690792Sgshapiro } 830790792Sgshapiro 830890792Sgshapiro /* Make sure we read the whole old file */ 830990792Sgshapiro errno = sm_io_error(tempqfp); 831090792Sgshapiro if (errno != 0 && errno != SM_IO_EOF) 831190792Sgshapiro { 831290792Sgshapiro save_errno = errno; 831390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 831490792Sgshapiro "Skipping %s: Error reading %s: %s\n", 831590792Sgshapiro qid_printname(e), oldqf, 831690792Sgshapiro sm_errstring(save_errno)); 831790792Sgshapiro failing = true; 831890792Sgshapiro } 831990792Sgshapiro 832090792Sgshapiro if (!failing && !finished) 832190792Sgshapiro { 832290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 832390792Sgshapiro "Skipping %s: Incomplete file: %s\n", 832490792Sgshapiro qid_printname(e), oldqf); 832590792Sgshapiro failing = true; 832690792Sgshapiro } 832790792Sgshapiro 832890792Sgshapiro /* Check if we actually changed anything or we can just bail now */ 832990792Sgshapiro if (!dirty) 833090792Sgshapiro { 833190792Sgshapiro /* pretend we failed, even though we technically didn't */ 833290792Sgshapiro failing = true; 833390792Sgshapiro } 833490792Sgshapiro 833590792Sgshapiro /* Make sure we wrote things out safely */ 833690792Sgshapiro if (!failing && 833790792Sgshapiro (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 || 833890792Sgshapiro ((SuperSafe == SAFE_REALLY || SuperSafe == SAFE_INTERACTIVE) && 833990792Sgshapiro fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) || 834090792Sgshapiro ((errno = sm_io_error(tempqfp)) != 0))) 834190792Sgshapiro { 834290792Sgshapiro save_errno = errno; 834390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 834490792Sgshapiro "Skipping %s: Error writing %s: %s\n", 834590792Sgshapiro qid_printname(e), tempqf, 834690792Sgshapiro sm_errstring(save_errno)); 834790792Sgshapiro failing = true; 834890792Sgshapiro } 834990792Sgshapiro 835090792Sgshapiro 835190792Sgshapiro /* Figure out the new filename */ 835290792Sgshapiro newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER); 835390792Sgshapiro if (oldtype == newtype) 835490792Sgshapiro { 835590792Sgshapiro /* going to rename tempqf to oldqf */ 835690792Sgshapiro (void) sm_strlcpy(newqf, oldqf, sizeof newqf); 835790792Sgshapiro } 835890792Sgshapiro else 835990792Sgshapiro { 836090792Sgshapiro /* going to rename tempqf to new name based on newtype */ 836190792Sgshapiro (void) sm_strlcpy(newqf, queuename(e, newtype), sizeof newqf); 836290792Sgshapiro } 836390792Sgshapiro 836490792Sgshapiro save_errno = 0; 836590792Sgshapiro 836690792Sgshapiro /* rename tempqf to newqf */ 836790792Sgshapiro if (!failing && 836890792Sgshapiro rename(tempqf, newqf) < 0) 836990792Sgshapiro save_errno = (errno == 0) ? EINVAL : errno; 837090792Sgshapiro 837190792Sgshapiro /* Check rename() success */ 837290792Sgshapiro if (!failing && save_errno != 0) 837390792Sgshapiro { 837490792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, 837590792Sgshapiro "quarantine_queue_item: rename(%s, %s): %s", 837690792Sgshapiro tempqf, newqf, sm_errstring(save_errno)); 837790792Sgshapiro 837890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 837990792Sgshapiro "Error renaming %s to %s: %s\n", 838090792Sgshapiro tempqf, newqf, 838190792Sgshapiro sm_errstring(save_errno)); 838290792Sgshapiro if (oldtype == newtype) 838390792Sgshapiro { 838490792Sgshapiro /* 838590792Sgshapiro ** Bail here since we don't know the state of 838690792Sgshapiro ** the filesystem and may need to keep tempqf 838790792Sgshapiro ** for the user to rescue us. 838890792Sgshapiro */ 838990792Sgshapiro 839090792Sgshapiro RELEASE_QUEUE; 839190792Sgshapiro errno = save_errno; 839290792Sgshapiro syserr("!452 Error renaming control file %s", tempqf); 839390792Sgshapiro /* NOTREACHED */ 839490792Sgshapiro } 839590792Sgshapiro else 839690792Sgshapiro { 839790792Sgshapiro /* remove new file (if rename() half completed) */ 839890792Sgshapiro if (xunlink(newqf) < 0) 839990792Sgshapiro { 840090792Sgshapiro save_errno = errno; 840190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 840290792Sgshapiro "Error removing %s: %s\n", 840390792Sgshapiro newqf, 840490792Sgshapiro sm_errstring(save_errno)); 840590792Sgshapiro } 840690792Sgshapiro 840790792Sgshapiro /* tempqf removed below */ 840890792Sgshapiro failing = true; 840990792Sgshapiro } 841090792Sgshapiro 841190792Sgshapiro } 841290792Sgshapiro 841390792Sgshapiro /* If changing file types, need to remove old type */ 841490792Sgshapiro if (!failing && oldtype != newtype) 841590792Sgshapiro { 841690792Sgshapiro if (xunlink(oldqf) < 0) 841790792Sgshapiro { 841890792Sgshapiro save_errno = errno; 841990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 842090792Sgshapiro "Error removing %s: %s\n", 842190792Sgshapiro oldqf, sm_errstring(save_errno)); 842290792Sgshapiro } 842390792Sgshapiro } 842490792Sgshapiro 842590792Sgshapiro /* see if anything above failed */ 842690792Sgshapiro if (failing) 842790792Sgshapiro { 842890792Sgshapiro /* Something failed: remove new file, old file still there */ 842990792Sgshapiro (void) xunlink(tempqf); 843090792Sgshapiro } 843190792Sgshapiro 843290792Sgshapiro /* 843390792Sgshapiro ** fsync() after file operations to make sure metadata is 843490792Sgshapiro ** written to disk on filesystems in which renames are 843590792Sgshapiro ** not guaranteed. It's ok if they fail, mail won't be lost. 843690792Sgshapiro */ 843790792Sgshapiro 843890792Sgshapiro if (SuperSafe != SAFE_NO) 843990792Sgshapiro { 844090792Sgshapiro /* for soft-updates */ 844190792Sgshapiro (void) fsync(sm_io_getinfo(tempqfp, 844290792Sgshapiro SM_IO_WHAT_FD, NULL)); 844390792Sgshapiro 844490792Sgshapiro if (!failing) 844590792Sgshapiro { 844690792Sgshapiro /* for soft-updates */ 844790792Sgshapiro (void) fsync(sm_io_getinfo(oldqfp, 844890792Sgshapiro SM_IO_WHAT_FD, NULL)); 844990792Sgshapiro } 845090792Sgshapiro 845190792Sgshapiro /* for other odd filesystems */ 845290792Sgshapiro SYNC_DIR(tempqf, false); 845390792Sgshapiro } 845490792Sgshapiro 845590792Sgshapiro /* Close up shop */ 845690792Sgshapiro RELEASE_QUEUE; 845790792Sgshapiro if (tempqfp != NULL) 845890792Sgshapiro (void) sm_io_close(tempqfp, SM_TIME_DEFAULT); 845990792Sgshapiro if (oldqfp != NULL) 846090792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 846190792Sgshapiro 846290792Sgshapiro /* All went well */ 846390792Sgshapiro return !failing; 846490792Sgshapiro} 846590792Sgshapiro 846690792Sgshapiro/* 846790792Sgshapiro** QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue 846890792Sgshapiro** 846990792Sgshapiro** Read all matching queue items, add/remove quarantine 847090792Sgshapiro** reason, and requeue appropriately. 847190792Sgshapiro** 847290792Sgshapiro** Parameters: 847390792Sgshapiro** reason -- quarantine reason, "." means unquarantine. 847490792Sgshapiro** qgrplimit -- limit to single queue group unless NOQGRP 847590792Sgshapiro** 847690792Sgshapiro** Results: 847790792Sgshapiro** none. 847890792Sgshapiro** 847990792Sgshapiro** Side Effects: 848090792Sgshapiro** Lots of changes to the queue. 848190792Sgshapiro*/ 848290792Sgshapiro 848390792Sgshapirovoid 848490792Sgshapiroquarantine_queue(reason, qgrplimit) 848590792Sgshapiro char *reason; 848690792Sgshapiro int qgrplimit; 848790792Sgshapiro{ 848890792Sgshapiro int changed = 0; 848990792Sgshapiro int qgrp; 849090792Sgshapiro 849190792Sgshapiro /* Convert internal representation of unquarantine */ 849290792Sgshapiro if (reason != NULL && reason[0] == '.' && reason[1] == '\0') 849390792Sgshapiro reason = NULL; 849490792Sgshapiro 849590792Sgshapiro if (reason != NULL) 849690792Sgshapiro { 849790792Sgshapiro /* clean it */ 849890792Sgshapiro reason = newstr(denlstring(reason, true, true)); 849990792Sgshapiro } 850090792Sgshapiro 850190792Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 850290792Sgshapiro { 850390792Sgshapiro int qdir; 850490792Sgshapiro 850590792Sgshapiro if (qgrplimit != NOQGRP && qgrplimit != qgrp) 850690792Sgshapiro continue; 850790792Sgshapiro 850890792Sgshapiro for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++) 850990792Sgshapiro { 851090792Sgshapiro int i; 851190792Sgshapiro int nrequests; 851290792Sgshapiro 851390792Sgshapiro if (StopRequest) 851490792Sgshapiro stop_sendmail(); 851590792Sgshapiro 851690792Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL); 851790792Sgshapiro 851890792Sgshapiro /* first see if there is anything */ 851990792Sgshapiro if (nrequests <= 0) 852090792Sgshapiro { 852190792Sgshapiro if (Verbose) 852290792Sgshapiro { 852390792Sgshapiro (void) sm_io_fprintf(smioout, 852490792Sgshapiro SM_TIME_DEFAULT, "%s: no matches\n", 852590792Sgshapiro qid_printqueue(qgrp, qdir)); 852690792Sgshapiro } 852790792Sgshapiro continue; 852890792Sgshapiro } 852990792Sgshapiro 853090792Sgshapiro if (Verbose) 853190792Sgshapiro { 853290792Sgshapiro (void) sm_io_fprintf(smioout, 853390792Sgshapiro SM_TIME_DEFAULT, "Processing %s:\n", 853490792Sgshapiro qid_printqueue(qgrp, qdir)); 853590792Sgshapiro } 853690792Sgshapiro 853790792Sgshapiro for (i = 0; i < WorkListCount; i++) 853890792Sgshapiro { 853990792Sgshapiro ENVELOPE e; 854090792Sgshapiro 854190792Sgshapiro if (StopRequest) 854290792Sgshapiro stop_sendmail(); 854390792Sgshapiro 854490792Sgshapiro /* setup envelope */ 854590792Sgshapiro clearenvelope(&e, true, sm_rpool_new_x(NULL)); 854690792Sgshapiro e.e_id = WorkList[i].w_name + 2; 854790792Sgshapiro e.e_qgrp = qgrp; 854890792Sgshapiro e.e_qdir = qdir; 854990792Sgshapiro 855090792Sgshapiro if (tTd(70, 101)) 855190792Sgshapiro { 855290792Sgshapiro sm_io_fprintf(smioout, SM_TIME_DEFAULT, 855390792Sgshapiro "Would do %s\n", e.e_id); 855490792Sgshapiro changed++; 855590792Sgshapiro } 855690792Sgshapiro else if (quarantine_queue_item(qgrp, qdir, 855790792Sgshapiro &e, reason)) 855890792Sgshapiro changed++; 855990792Sgshapiro 856090792Sgshapiro /* clean up */ 856190792Sgshapiro sm_rpool_free(e.e_rpool); 856290792Sgshapiro e.e_rpool = NULL; 856390792Sgshapiro } 856490792Sgshapiro if (WorkList != NULL) 856590792Sgshapiro sm_free(WorkList); /* XXX */ 856690792Sgshapiro WorkList = NULL; 856790792Sgshapiro WorkListSize = 0; 856890792Sgshapiro WorkListCount = 0; 856990792Sgshapiro } 857090792Sgshapiro } 857190792Sgshapiro if (Verbose) 857290792Sgshapiro { 857390792Sgshapiro if (changed == 0) 857490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 857590792Sgshapiro "No changes\n"); 857690792Sgshapiro else 857790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 857890792Sgshapiro "%d change%s\n", 857990792Sgshapiro changed, 858090792Sgshapiro changed == 1 ? "" : "s"); 858190792Sgshapiro } 858290792Sgshapiro} 858390792Sgshapiro#endif /* _FFR_QUARANTINE */ 8584