queue.c revision 363466
138032Speter/* 2285229Sgshapiro * Copyright (c) 1998-2009, 2011, 2012, 2014 Proofpoint, 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> 15147078Sgshapiro#include <sm/sem.h> 1664562Sgshapiro 17266527SgshapiroSM_RCSID("@(#)$Id: queue.c,v 8.1000 2013-11-22 20:51:56 ca Exp $") 1838032Speter 1990792Sgshapiro#include <dirent.h> 2038032Speter 2190792Sgshapiro# define RELEASE_QUEUE (void) 0 2290792Sgshapiro# define ST_INODE(st) (st).st_ino 2390792Sgshapiro 24120256Sgshapiro# define sm_file_exists(errno) ((errno) == EEXIST) 2590792Sgshapiro 26125820Sgshapiro# if HASFLOCK && defined(O_EXLOCK) 27125820Sgshapiro# define SM_OPEN_EXLOCK 1 28125820Sgshapiro# define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK) 29363466Sgshapiro# else 30125820Sgshapiro# define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL) 31363466Sgshapiro# endif 32120256Sgshapiro 33125820Sgshapiro#ifndef SM_OPEN_EXLOCK 34125820Sgshapiro# define SM_OPEN_EXLOCK 0 35363466Sgshapiro#endif 36125820Sgshapiro 3790792Sgshapiro/* 3890792Sgshapiro** Historical notes: 39120256Sgshapiro** QF_VERSION == 4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY 40120256Sgshapiro** QF_VERSION == 5 was sendmail 8.10/8.11 with _FFR_QUEUEDELAY 41132943Sgshapiro** QF_VERSION == 6 was sendmail 8.12 without _FFR_QUEUEDELAY 42132943Sgshapiro** QF_VERSION == 7 was sendmail 8.12 with _FFR_QUEUEDELAY 43132943Sgshapiro** QF_VERSION == 8 is sendmail 8.13 4490792Sgshapiro*/ 4590792Sgshapiro 46132943Sgshapiro#define QF_VERSION 8 /* version number of this queue format */ 47132943Sgshapiro 4890792Sgshapirostatic char queue_letter __P((ENVELOPE *, int)); 4990792Sgshapirostatic bool quarantine_queue_item __P((int, int, ENVELOPE *, char *)); 5064562Sgshapiro 5190792Sgshapiro/* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */ 5290792Sgshapiro 5338032Speter/* 5438032Speter** Work queue. 5538032Speter*/ 5638032Speter 5738032Speterstruct work 5838032Speter{ 5938032Speter char *w_name; /* name of control file */ 6038032Speter char *w_host; /* name of recipient host */ 6138032Speter bool w_lock; /* is message locked? */ 6238032Speter bool w_tooyoung; /* is it too young to run? */ 6338032Speter long w_pri; /* priority of message, see below */ 6490792Sgshapiro time_t w_ctime; /* creation time */ 6590792Sgshapiro time_t w_mtime; /* modification time */ 6690792Sgshapiro int w_qgrp; /* queue group located in */ 6790792Sgshapiro int w_qdir; /* queue directory located in */ 6838032Speter struct work *w_next; /* next in queue */ 6938032Speter}; 7038032Speter 7138032Spetertypedef struct work WORK; 7238032Speter 7390792Sgshapirostatic WORK *WorkQ; /* queue of things to be done */ 7490792Sgshapirostatic int NumWorkGroups; /* number of work groups */ 75120256Sgshapirostatic time_t Current_LA_time = 0; 7638032Speter 77120256Sgshapiro/* Get new load average every 30 seconds. */ 78120256Sgshapiro#define GET_NEW_LA_TIME 30 79120256Sgshapiro 80120256Sgshapiro#define SM_GET_LA(now) \ 81120256Sgshapiro do \ 82120256Sgshapiro { \ 83120256Sgshapiro now = curtime(); \ 84120256Sgshapiro if (Current_LA_time < now - GET_NEW_LA_TIME) \ 85120256Sgshapiro { \ 86120256Sgshapiro sm_getla(); \ 87120256Sgshapiro Current_LA_time = now; \ 88120256Sgshapiro } \ 89120256Sgshapiro } while (0) 90120256Sgshapiro 9190792Sgshapiro/* 9294334Sgshapiro** DoQueueRun indicates that a queue run is needed. 9394334Sgshapiro** Notice: DoQueueRun is modified in a signal handler! 9490792Sgshapiro*/ 9590792Sgshapiro 96111823Sgshapirostatic bool volatile DoQueueRun; /* non-interrupt time queue run needed */ 9790792Sgshapiro 9890792Sgshapiro/* 9990792Sgshapiro** Work group definition structure. 10090792Sgshapiro** Each work group contains one or more queue groups. This is done 10190792Sgshapiro** to manage the number of queue group runners active at the same time 10290792Sgshapiro** to be within the constraints of MaxQueueChildren (if it is set). 10390792Sgshapiro** The number of queue groups that can be run on the next work run 10490792Sgshapiro** is kept track of. The queue groups are run in a round robin. 10590792Sgshapiro*/ 10690792Sgshapiro 10790792Sgshapirostruct workgrp 10890792Sgshapiro{ 10990792Sgshapiro int wg_numqgrp; /* number of queue groups in work grp */ 11090792Sgshapiro int wg_runners; /* total runners */ 11190792Sgshapiro int wg_curqgrp; /* current queue group */ 11290792Sgshapiro QUEUEGRP **wg_qgs; /* array of queue groups */ 11390792Sgshapiro int wg_maxact; /* max # of active runners */ 11490792Sgshapiro time_t wg_lowqintvl; /* lowest queue interval */ 11590792Sgshapiro int wg_restart; /* needs restarting? */ 11690792Sgshapiro int wg_restartcnt; /* count of times restarted */ 11790792Sgshapiro}; 11890792Sgshapiro 11990792Sgshapirotypedef struct workgrp WORKGRP; 12090792Sgshapiro 12190792Sgshapirostatic WORKGRP volatile WorkGrp[MAXWORKGROUPS + 1]; /* work groups */ 12290792Sgshapiro 12390792Sgshapiro#if SM_HEAP_CHECK 12490792Sgshapirostatic SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q", 12590792Sgshapiro "@(#)$Debug: leak_q - trace memory leaks during queue processing $"); 126363466Sgshapiro#endif 12790792Sgshapiro 12890792Sgshapirostatic void grow_wlist __P((int, int)); 12990792Sgshapirostatic int multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *)); 130203004Sgshapirostatic int gatherq __P((int, int, bool, bool *, bool *, int *)); 13190792Sgshapirostatic int sortq __P((int)); 13290792Sgshapirostatic void printctladdr __P((ADDRESS *, SM_FILE_T *)); 13390792Sgshapirostatic bool readqf __P((ENVELOPE *, bool)); 13490792Sgshapirostatic void restart_work_group __P((int)); 13590792Sgshapirostatic void runner_work __P((ENVELOPE *, int, bool, int, int)); 13694334Sgshapirostatic void schedule_queue_runs __P((bool, int, bool)); 13764562Sgshapirostatic char *strrev __P((char *)); 13890792Sgshapirostatic ADDRESS *setctluser __P((char *, int, ENVELOPE *)); 13990792Sgshapiro#if _FFR_RHS 14090792Sgshapirostatic int sm_strshufflecmp __P((char *, char *)); 14190792Sgshapirostatic void init_shuffle_alphabet __P(()); 142363466Sgshapiro#endif 143168515Sgshapiro 144168515Sgshapiro/* 145168515Sgshapiro** Note: workcmpf?() don't use a prototype because it will cause a conflict 146168515Sgshapiro** with the qsort() call (which expects something like 147168515Sgshapiro** int (*compar)(const void *, const void *), not (WORK *, WORK *)) 148168515Sgshapiro*/ 149168515Sgshapiro 15064562Sgshapirostatic int workcmpf0(); 15164562Sgshapirostatic int workcmpf1(); 15264562Sgshapirostatic int workcmpf2(); 15364562Sgshapirostatic int workcmpf3(); 15464562Sgshapirostatic int workcmpf4(); 155110560Sgshapirostatic int randi = 3; /* index for workcmpf5() */ 15690792Sgshapirostatic int workcmpf5(); 15790792Sgshapirostatic int workcmpf6(); 15890792Sgshapiro#if _FFR_RHS 15990792Sgshapirostatic int workcmpf7(); 160363466Sgshapiro#endif 16138032Speter 16290792Sgshapiro#if RANDOMSHIFT 16390792Sgshapiro# define get_rand_mod(m) ((get_random() >> RANDOMSHIFT) % (m)) 164363466Sgshapiro#else 16590792Sgshapiro# define get_rand_mod(m) (get_random() % (m)) 166363466Sgshapiro#endif 16790792Sgshapiro 16880785Sgshapiro/* 16990792Sgshapiro** File system definition. 17090792Sgshapiro** Used to keep track of how much free space is available 17190792Sgshapiro** on a file system in which one or more queue directories reside. 17290792Sgshapiro*/ 17390792Sgshapiro 17490792Sgshapirotypedef struct filesys_shared FILESYS; 17590792Sgshapiro 17690792Sgshapirostruct filesys_shared 17790792Sgshapiro{ 17890792Sgshapiro dev_t fs_dev; /* unique device id */ 17990792Sgshapiro long fs_avail; /* number of free blocks available */ 18090792Sgshapiro long fs_blksize; /* block size, in bytes */ 18190792Sgshapiro}; 18290792Sgshapiro 18390792Sgshapiro/* probably kept in shared memory */ 18490792Sgshapirostatic FILESYS FileSys[MAXFILESYS]; /* queue file systems */ 185168515Sgshapirostatic const char *FSPath[MAXFILESYS]; /* pathnames for file systems */ 18690792Sgshapiro 18790792Sgshapiro#if SM_CONF_SHM 188363466Sgshapiro# include <ratectrl.h> 18990792Sgshapiro 19090792Sgshapiro/* 19190792Sgshapiro** Shared memory data 19290792Sgshapiro** 19390792Sgshapiro** Current layout: 19490792Sgshapiro** size -- size of shared memory segment 19590792Sgshapiro** pid -- pid of owner, should be a unique id to avoid misinterpretations 19690792Sgshapiro** by other processes. 19790792Sgshapiro** tag -- should be a unique id to avoid misinterpretations by others. 19890792Sgshapiro** idea: hash over configuration data that will be stored here. 19990792Sgshapiro** NumFileSys -- number of file systems. 200223067Sgshapiro** FileSys -- (array of) structure for used file systems. 20190792Sgshapiro** RSATmpCnt -- counter for number of uses of ephemeral RSA key. 202363466Sgshapiro** [OCC -- ...] 20390792Sgshapiro** QShm -- (array of) structure for information about queue directories. 204363466Sgshapiro** this must be last as the size is depending on the config. 20590792Sgshapiro*/ 20690792Sgshapiro 20790792Sgshapiro/* 20890792Sgshapiro** Queue data in shared memory 20990792Sgshapiro*/ 21090792Sgshapiro 21190792Sgshapirotypedef struct queue_shared QUEUE_SHM_T; 21290792Sgshapiro 21390792Sgshapirostruct queue_shared 21490792Sgshapiro{ 21590792Sgshapiro int qs_entries; /* number of entries */ 21690792Sgshapiro /* XXX more to follow? */ 21790792Sgshapiro}; 21890792Sgshapiro 21990792Sgshapirostatic void *Pshm; /* pointer to shared memory */ 22090792Sgshapirostatic FILESYS *PtrFileSys; /* pointer to queue file system array */ 22190792Sgshapiroint ShmId = SM_SHM_NO_ID; /* shared memory id */ 22290792Sgshapirostatic QUEUE_SHM_T *QShm; /* pointer to shared queue data */ 223110560Sgshapirostatic size_t shms; 22490792Sgshapiro 22590792Sgshapiro# define SHM_OFF_PID(p) (((char *) (p)) + sizeof(int)) 22690792Sgshapiro# define SHM_OFF_TAG(p) (((char *) (p)) + sizeof(pid_t) + sizeof(int)) 22790792Sgshapiro# define SHM_OFF_HEAD (sizeof(pid_t) + sizeof(int) * 2) 22890792Sgshapiro 22990792Sgshapiro/* how to access FileSys */ 23090792Sgshapiro# define FILE_SYS(i) (PtrFileSys[i]) 23190792Sgshapiro 23290792Sgshapiro/* first entry is a tag, for now just the size */ 23390792Sgshapiro# define OFF_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD) 23490792Sgshapiro 23590792Sgshapiro/* offset for PNumFileSys */ 23690792Sgshapiro# define OFF_NUM_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys)) 23790792Sgshapiro 23890792Sgshapiro/* offset for PRSATmpCnt */ 23990792Sgshapiro# define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int)) 24090792Sgshapiroint *PRSATmpCnt; 24190792Sgshapiro 242363466Sgshapiro# if _FFR_OCC 243363466Sgshapiro# define OFF_OCC_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2) 244363466Sgshapiro# define OCC_SIZE (sizeof(CHash_T) * CPMHSIZE) 245363466Sgshapirostatic CHash_T *occ = NULL; 246363466Sgshapiro# else 247363466Sgshapiro# define OCC_SIZE 0 248363466Sgshapiro# endif 249363466Sgshapiro 25090792Sgshapiro/* offset for queue_shm */ 251363466Sgshapiro# define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2 + OCC_SIZE) 25290792Sgshapiro 253112810Sgshapiro# define QSHM_ENTRIES(i) QShm[i].qs_entries 25490792Sgshapiro 25590792Sgshapiro/* basic size of shared memory segment */ 256363466Sgshapiro# define SM_T_SIZE (SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2 + OCC_SIZE) 25790792Sgshapiro 25890792Sgshapirostatic unsigned int hash_q __P((char *, unsigned int)); 25990792Sgshapiro 26090792Sgshapiro/* 26190792Sgshapiro** HASH_Q -- simple hash function 26290792Sgshapiro** 26390792Sgshapiro** Parameters: 26490792Sgshapiro** p -- string to hash. 26590792Sgshapiro** h -- hash start value (from previous run). 26690792Sgshapiro** 26790792Sgshapiro** Returns: 26890792Sgshapiro** hash value. 26990792Sgshapiro*/ 27090792Sgshapiro 27190792Sgshapirostatic unsigned int 27290792Sgshapirohash_q(p, h) 27390792Sgshapiro char *p; 27490792Sgshapiro unsigned int h; 27590792Sgshapiro{ 27690792Sgshapiro int c, d; 27790792Sgshapiro 27890792Sgshapiro while (*p != '\0') 27990792Sgshapiro { 28090792Sgshapiro d = *p++; 28190792Sgshapiro c = d; 28290792Sgshapiro c ^= c<<6; 28390792Sgshapiro h += (c<<11) ^ (c>>1); 28490792Sgshapiro h ^= (d<<14) + (d<<7) + (d<<4) + d; 28590792Sgshapiro } 28690792Sgshapiro return h; 28790792Sgshapiro} 28890792Sgshapiro 28990792Sgshapiro#else /* SM_CONF_SHM */ 29090792Sgshapiro# define FILE_SYS(i) FileSys[i] 29190792Sgshapiro#endif /* SM_CONF_SHM */ 29290792Sgshapiro 29390792Sgshapiro/* access to the various components of file system data */ 29490792Sgshapiro#define FILE_SYS_NAME(i) FSPath[i] 29590792Sgshapiro#define FILE_SYS_AVAIL(i) FILE_SYS(i).fs_avail 29690792Sgshapiro#define FILE_SYS_BLKSIZE(i) FILE_SYS(i).fs_blksize 29790792Sgshapiro#define FILE_SYS_DEV(i) FILE_SYS(i).fs_dev 29890792Sgshapiro 299110560Sgshapiro 30090792Sgshapiro/* 30180785Sgshapiro** Current qf file field assignments: 30280785Sgshapiro** 30380785Sgshapiro** A AUTH= parameter 30480785Sgshapiro** B body type 30580785Sgshapiro** C controlling user 306363466Sgshapiro** D data file name (obsolete) 30790792Sgshapiro** d data file directory name (added in 8.12) 30880785Sgshapiro** E error recipient 30980785Sgshapiro** F flag bits 31080785Sgshapiro** H header 31180785Sgshapiro** I data file's inode number 31280785Sgshapiro** K time of last delivery attempt 31380785Sgshapiro** L Solaris Content-Length: header (obsolete) 31498841Sgshapiro** M message 31580785Sgshapiro** N number of delivery attempts 31680785Sgshapiro** P message priority 317132943Sgshapiro** q quarantine reason 31880785Sgshapiro** Q original recipient (ORCPT=) 31990792Sgshapiro** r final recipient (Final-Recipient: DSN field) 32080785Sgshapiro** R recipient 32180785Sgshapiro** S sender 32280785Sgshapiro** T init time 32380785Sgshapiro** V queue file version 32490792Sgshapiro** X free (was: character set if _FFR_SAVE_CHARSET) 32580785Sgshapiro** Z original envelope id from ESMTP 32690792Sgshapiro** ! deliver by (added in 8.12) 32780785Sgshapiro** $ define macro 32880785Sgshapiro** . terminate file 32980785Sgshapiro*/ 33080785Sgshapiro 33190792Sgshapiro/* 33238032Speter** QUEUEUP -- queue a message up for future transmission. 33338032Speter** 33438032Speter** Parameters: 33538032Speter** e -- the envelope to queue up. 33690792Sgshapiro** announce -- if true, tell when you are queueing up. 33790792Sgshapiro** msync -- if true, then fsync() if SuperSafe interactive mode. 33838032Speter** 33938032Speter** Returns: 34038032Speter** none. 34138032Speter** 34238032Speter** Side Effects: 34390792Sgshapiro** The current request is saved in a control file. 34438032Speter** The queue file is left locked. 34538032Speter*/ 34638032Speter 34738032Spetervoid 34890792Sgshapiroqueueup(e, announce, msync) 34938032Speter register ENVELOPE *e; 35038032Speter bool announce; 35190792Sgshapiro bool msync; 35238032Speter{ 35390792Sgshapiro register SM_FILE_T *tfp; 35438032Speter register HDR *h; 35538032Speter register ADDRESS *q; 35664562Sgshapiro int tfd = -1; 35738032Speter int i; 35838032Speter bool newid; 35938032Speter register char *p; 36038032Speter MAILER nullmailer; 36138032Speter MCI mcibuf; 36290792Sgshapiro char qf[MAXPATHLEN]; 36364562Sgshapiro char tf[MAXPATHLEN]; 36490792Sgshapiro char df[MAXPATHLEN]; 36538032Speter char buf[MAXLINE]; 36638032Speter 36738032Speter /* 36838032Speter ** Create control file. 36938032Speter */ 37038032Speter 371125820Sgshapiro#define OPEN_TF do \ 372125820Sgshapiro { \ 373125820Sgshapiro MODE_T oldumask = 0; \ 374125820Sgshapiro \ 375125820Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) \ 376125820Sgshapiro oldumask = umask(002); \ 377125820Sgshapiro tfd = open(tf, TF_OPEN_FLAGS, QueueFileMode); \ 378125820Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) \ 379125820Sgshapiro (void) umask(oldumask); \ 380125820Sgshapiro } while (0) 381125820Sgshapiro 382125820Sgshapiro 38338032Speter newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); 384168515Sgshapiro (void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof(tf)); 38538032Speter tfp = e->e_lockfp; 386120256Sgshapiro if (tfp == NULL && newid) 387120256Sgshapiro { 388120256Sgshapiro /* 389120256Sgshapiro ** open qf file directly: this will give an error if the file 390120256Sgshapiro ** already exists and hence prevent problems if a queue-id 391120256Sgshapiro ** is reused (e.g., because the clock is set back). 392120256Sgshapiro */ 39338032Speter 394168515Sgshapiro (void) sm_strlcpy(tf, queuename(e, ANYQFL_LETTER), sizeof(tf)); 395125820Sgshapiro OPEN_TF; 396120256Sgshapiro if (tfd < 0 || 397125820Sgshapiro#if !SM_OPEN_EXLOCK 398120256Sgshapiro !lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB) || 399363466Sgshapiro#endif 400120256Sgshapiro (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 401132943Sgshapiro (void *) &tfd, SM_IO_WRONLY, 402120256Sgshapiro NULL)) == NULL) 403120256Sgshapiro { 404120256Sgshapiro int save_errno = errno; 405120256Sgshapiro 406120256Sgshapiro printopenfds(true); 407120256Sgshapiro errno = save_errno; 408285229Sgshapiro syserr("!queueup: cannot create queue file %s, euid=%ld, fd=%d, fp=%p", 409363466Sgshapiro tf, (long) geteuid(), tfd, (void *)tfp); 410120256Sgshapiro /* NOTREACHED */ 411120256Sgshapiro } 412120256Sgshapiro e->e_lockfp = tfp; 413147078Sgshapiro upd_qs(e, 1, 0, "queueup"); 414120256Sgshapiro } 415120256Sgshapiro 41690792Sgshapiro /* if newid, write the queue file directly (instead of temp file) */ 41738032Speter if (!newid) 41838032Speter { 41938032Speter /* get a locked tf file */ 42038032Speter for (i = 0; i < 128; i++) 42138032Speter { 42264562Sgshapiro if (tfd < 0) 42338032Speter { 424125820Sgshapiro OPEN_TF; 42564562Sgshapiro if (tfd < 0) 42664562Sgshapiro { 42764562Sgshapiro if (errno != EEXIST) 42864562Sgshapiro break; 42964562Sgshapiro if (LogLevel > 0 && (i % 32) == 0) 43064562Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 431285229Sgshapiro "queueup: cannot create %s, euid=%ld: %s", 432285229Sgshapiro tf, (long) geteuid(), 43390792Sgshapiro sm_errstring(errno)); 43464562Sgshapiro } 435125820Sgshapiro#if SM_OPEN_EXLOCK 436125820Sgshapiro else 437125820Sgshapiro break; 438363466Sgshapiro#endif 43938032Speter } 44064562Sgshapiro if (tfd >= 0) 44138032Speter { 442125820Sgshapiro#if SM_OPEN_EXLOCK 443125820Sgshapiro /* file is locked by open() */ 444125820Sgshapiro break; 445363466Sgshapiro#else 44664562Sgshapiro if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB)) 44738032Speter break; 448125820Sgshapiro else 449363466Sgshapiro#endif 450125820Sgshapiro if (LogLevel > 0 && (i % 32) == 0) 45138032Speter sm_syslog(LOG_ALERT, e->e_id, 45264562Sgshapiro "queueup: cannot lock %s: %s", 45390792Sgshapiro tf, sm_errstring(errno)); 45464562Sgshapiro if ((i % 32) == 31) 45564562Sgshapiro { 45664562Sgshapiro (void) close(tfd); 45764562Sgshapiro tfd = -1; 45864562Sgshapiro } 45938032Speter } 46038032Speter 46138032Speter if ((i % 32) == 31) 46238032Speter { 46338032Speter /* save the old temp file away */ 46464562Sgshapiro (void) rename(tf, queuename(e, TEMPQF_LETTER)); 46538032Speter } 46638032Speter else 46764562Sgshapiro (void) sleep(i % 32); 46838032Speter } 46990792Sgshapiro if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 470120256Sgshapiro (void *) &tfd, SM_IO_WRONLY_B, 47190792Sgshapiro NULL)) == NULL) 47238032Speter { 47364562Sgshapiro int save_errno = errno; 47464562Sgshapiro 47590792Sgshapiro printopenfds(true); 47664562Sgshapiro errno = save_errno; 477285229Sgshapiro syserr("!queueup: cannot create queue temp file %s, uid=%ld", 478285229Sgshapiro tf, (long) geteuid()); 47938032Speter } 48038032Speter } 48138032Speter 48238032Speter if (tTd(40, 1)) 48390792Sgshapiro sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n", 48490792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 48590792Sgshapiro queuename(e, ANYQFL_LETTER), 48690792Sgshapiro newid ? " (new id)" : ""); 48738032Speter if (tTd(40, 3)) 48838032Speter { 48990792Sgshapiro sm_dprintf(" e_flags="); 49038032Speter printenvflags(e); 49138032Speter } 49238032Speter if (tTd(40, 32)) 49338032Speter { 49490792Sgshapiro sm_dprintf(" sendq="); 495132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 49638032Speter } 49738032Speter if (tTd(40, 9)) 49838032Speter { 49990792Sgshapiro sm_dprintf(" tfp="); 50090792Sgshapiro dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false); 50190792Sgshapiro sm_dprintf(" lockfp="); 50238032Speter if (e->e_lockfp == NULL) 50390792Sgshapiro sm_dprintf("NULL\n"); 50438032Speter else 50590792Sgshapiro dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL), 50690792Sgshapiro true, false); 50738032Speter } 50838032Speter 50938032Speter /* 51038032Speter ** If there is no data file yet, create one. 51138032Speter */ 51238032Speter 513168515Sgshapiro (void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof(df)); 51464562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags)) 51538032Speter { 51690792Sgshapiro if (e->e_dfp != NULL && 51790792Sgshapiro SuperSafe != SAFE_REALLY && 518132943Sgshapiro SuperSafe != SAFE_REALLY_POSTMILTER && 51990792Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 52090792Sgshapiro errno != EINVAL) 52190792Sgshapiro { 522285229Sgshapiro syserr("!queueup: cannot commit data file %s, uid=%ld", 523285229Sgshapiro queuename(e, DATAFL_LETTER), (long) geteuid()); 52490792Sgshapiro } 52590792Sgshapiro if (e->e_dfp != NULL && 52690792Sgshapiro SuperSafe == SAFE_INTERACTIVE && msync) 52790792Sgshapiro { 52890792Sgshapiro if (tTd(40,32)) 52990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 53090792Sgshapiro "queueup: fsync(e->e_dfp)"); 53190792Sgshapiro 53290792Sgshapiro if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, 53390792Sgshapiro NULL)) < 0) 53490792Sgshapiro { 53590792Sgshapiro if (newid) 53690792Sgshapiro syserr("!552 Error writing data file %s", 53790792Sgshapiro df); 53890792Sgshapiro else 53990792Sgshapiro syserr("!452 Error writing data file %s", 54090792Sgshapiro df); 54190792Sgshapiro } 54290792Sgshapiro } 54364562Sgshapiro } 54464562Sgshapiro else 54564562Sgshapiro { 54664562Sgshapiro int dfd; 54790792Sgshapiro MODE_T oldumask = 0; 54890792Sgshapiro register SM_FILE_T *dfp = NULL; 54938032Speter struct stat stbuf; 55038032Speter 55190792Sgshapiro if (e->e_dfp != NULL && 55290792Sgshapiro sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 55364562Sgshapiro syserr("committing over bf file"); 55464562Sgshapiro 55590792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 55690792Sgshapiro oldumask = umask(002); 557120256Sgshapiro dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC|QF_O_EXTRA, 558120256Sgshapiro QueueFileMode); 55990792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 56090792Sgshapiro (void) umask(oldumask); 56190792Sgshapiro if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 562120256Sgshapiro (void *) &dfd, SM_IO_WRONLY_B, 56390792Sgshapiro NULL)) == NULL) 564285229Sgshapiro syserr("!queueup: cannot create data temp file %s, uid=%ld", 565285229Sgshapiro df, (long) geteuid()); 56664562Sgshapiro if (fstat(dfd, &stbuf) < 0) 56738032Speter e->e_dfino = -1; 56838032Speter else 56938032Speter { 57038032Speter e->e_dfdev = stbuf.st_dev; 57190792Sgshapiro e->e_dfino = ST_INODE(stbuf); 57238032Speter } 57338032Speter e->e_flags |= EF_HAS_DF; 574168515Sgshapiro memset(&mcibuf, '\0', sizeof(mcibuf)); 57538032Speter mcibuf.mci_out = dfp; 57638032Speter mcibuf.mci_mailer = FileMailer; 57738032Speter (*e->e_putbody)(&mcibuf, e, NULL); 57890792Sgshapiro 57990792Sgshapiro if (SuperSafe == SAFE_REALLY || 580132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER || 58190792Sgshapiro (SuperSafe == SAFE_INTERACTIVE && msync)) 58290792Sgshapiro { 58390792Sgshapiro if (tTd(40,32)) 58490792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 58590792Sgshapiro "queueup: fsync(dfp)"); 58690792Sgshapiro 58790792Sgshapiro if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0) 58890792Sgshapiro { 58990792Sgshapiro if (newid) 59090792Sgshapiro syserr("!552 Error writing data file %s", 59190792Sgshapiro df); 59290792Sgshapiro else 59390792Sgshapiro syserr("!452 Error writing data file %s", 59490792Sgshapiro df); 59590792Sgshapiro } 59690792Sgshapiro } 59790792Sgshapiro 59890792Sgshapiro if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0) 599285229Sgshapiro syserr("!queueup: cannot save data temp file %s, uid=%ld", 600285229Sgshapiro df, (long) geteuid()); 60138032Speter e->e_putbody = putbody; 60238032Speter } 60338032Speter 60438032Speter /* 60538032Speter ** Output future work requests. 60638032Speter ** Priority and creation time should be first, since 60790792Sgshapiro ** they are required by gatherq. 60838032Speter */ 60938032Speter 61038032Speter /* output queue version number (must be first!) */ 61190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION); 61238032Speter 61338032Speter /* output creation time */ 61490792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime); 61538032Speter 61638032Speter /* output last delivery time */ 61790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime); 61838032Speter 61938032Speter /* output number of delivery attempts */ 62090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries); 62138032Speter 62238032Speter /* output message priority */ 62390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority); 62438032Speter 62590792Sgshapiro /* 62690792Sgshapiro ** If data file is in a different directory than the queue file, 62790792Sgshapiro ** output a "d" record naming the directory of the data file. 62890792Sgshapiro */ 62990792Sgshapiro 63090792Sgshapiro if (e->e_dfqgrp != e->e_qgrp) 63190792Sgshapiro { 63290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n", 63390792Sgshapiro Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name); 63490792Sgshapiro } 63590792Sgshapiro 63638032Speter /* output inode number of data file */ 63738032Speter if (e->e_dfino != -1) 63838032Speter { 63990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n", 64090792Sgshapiro (long) major(e->e_dfdev), 64190792Sgshapiro (long) minor(e->e_dfdev), 64290792Sgshapiro (ULONGLONG_T) e->e_dfino); 64338032Speter } 64438032Speter 64538032Speter /* output body type */ 64638032Speter if (e->e_bodytype != NULL) 64790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n", 64890792Sgshapiro denlstring(e->e_bodytype, true, false)); 64938032Speter 65090792Sgshapiro /* quarantine reason */ 65190792Sgshapiro if (e->e_quarmsg != NULL) 65290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n", 65390792Sgshapiro denlstring(e->e_quarmsg, true, false)); 65438032Speter 65538032Speter /* message from envelope, if it exists */ 65638032Speter if (e->e_message != NULL) 65790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", 65890792Sgshapiro denlstring(e->e_message, true, false)); 65938032Speter 66038032Speter /* send various flag bits through */ 66138032Speter p = buf; 66238032Speter if (bitset(EF_WARNING, e->e_flags)) 66338032Speter *p++ = 'w'; 66438032Speter if (bitset(EF_RESPONSE, e->e_flags)) 66538032Speter *p++ = 'r'; 66638032Speter if (bitset(EF_HAS8BIT, e->e_flags)) 66738032Speter *p++ = '8'; 66838032Speter if (bitset(EF_DELETE_BCC, e->e_flags)) 66938032Speter *p++ = 'b'; 67038032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 67138032Speter *p++ = 'd'; 67238032Speter if (bitset(EF_NO_BODY_RETN, e->e_flags)) 67338032Speter *p++ = 'n'; 67490792Sgshapiro if (bitset(EF_SPLIT, e->e_flags)) 67590792Sgshapiro *p++ = 's'; 676363466Sgshapiro#if _FFR_EAI 677363466Sgshapiro if (e->e_smtputf8) 678363466Sgshapiro *p++ = 'e'; 679363466Sgshapiro#endif 68038032Speter *p++ = '\0'; 68138032Speter if (buf[0] != '\0') 68290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf); 68338032Speter 68464562Sgshapiro /* save $={persistentMacros} macro values */ 68590792Sgshapiro queueup_macros(macid("{persistentMacros}"), tfp, e); 68638032Speter 68738032Speter /* output name of sender */ 68838032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 68938032Speter p = e->e_sender; 69038032Speter else 69138032Speter p = e->e_from.q_paddr; 69290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n", 69390792Sgshapiro denlstring(p, true, false)); 69438032Speter 69538032Speter /* output ESMTP-supplied "original" information */ 69638032Speter if (e->e_envid != NULL) 69790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n", 69890792Sgshapiro denlstring(e->e_envid, true, false)); 69938032Speter 70064562Sgshapiro /* output AUTH= parameter */ 70164562Sgshapiro if (e->e_auth_param != NULL) 70290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n", 70390792Sgshapiro denlstring(e->e_auth_param, true, false)); 70490792Sgshapiro if (e->e_dlvr_flag != 0) 70590792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n", 70690792Sgshapiro (char) e->e_dlvr_flag, e->e_deliver_by); 70764562Sgshapiro 70838032Speter /* output list of recipient addresses */ 70938032Speter printctladdr(NULL, NULL); 71038032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 71138032Speter { 712363466Sgshapiro q->q_flags &= ~QQUEUED; 71364562Sgshapiro if (!QS_IS_UNDELIVERED(q->q_state)) 71438032Speter continue; 71564562Sgshapiro 71690792Sgshapiro /* message for this recipient, if it exists */ 71790792Sgshapiro if (q->q_message != NULL) 71890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", 71990792Sgshapiro denlstring(q->q_message, true, 72090792Sgshapiro false)); 72190792Sgshapiro 72238032Speter printctladdr(q, tfp); 72338032Speter if (q->q_orcpt != NULL) 72490792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n", 72590792Sgshapiro denlstring(q->q_orcpt, true, 72690792Sgshapiro false)); 72790792Sgshapiro if (q->q_finalrcpt != NULL) 72890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n", 72990792Sgshapiro denlstring(q->q_finalrcpt, true, 73090792Sgshapiro false)); 73190792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R'); 73238032Speter if (bitset(QPRIMARY, q->q_flags)) 73390792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P'); 73438032Speter if (bitset(QHASNOTIFY, q->q_flags)) 73590792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N'); 73638032Speter if (bitset(QPINGONSUCCESS, q->q_flags)) 73790792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S'); 73838032Speter if (bitset(QPINGONFAILURE, q->q_flags)) 73990792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F'); 74038032Speter if (bitset(QPINGONDELAY, q->q_flags)) 74190792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D'); 742285229Sgshapiro if (bitset(QINTBCC, q->q_flags)) 743285229Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'B'); 74471345Sgshapiro if (q->q_alias != NULL && 74571345Sgshapiro bitset(QALIAS, q->q_alias->q_flags)) 74690792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A'); 747285229Sgshapiro 748285229Sgshapiro /* _FFR_RCPTFLAGS */ 749285229Sgshapiro if (bitset(QDYNMAILER, q->q_flags)) 750285229Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, QDYNMAILFLG); 75190792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':'); 75290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n", 75390792Sgshapiro denlstring(q->q_paddr, true, false)); 75438032Speter if (announce) 75538032Speter { 75690792Sgshapiro char *tag = "queued"; 75790792Sgshapiro 75890792Sgshapiro if (e->e_quarmsg != NULL) 75990792Sgshapiro tag = "quarantined"; 76090792Sgshapiro 76138032Speter e->e_to = q->q_paddr; 762285229Sgshapiro message("%s", tag); 76338032Speter if (LogLevel > 8) 76464562Sgshapiro logdelivery(q->q_mailer, NULL, q->q_status, 765285229Sgshapiro tag, NULL, (time_t) 0, e, q, EX_OK); 76638032Speter e->e_to = NULL; 76738032Speter } 768363466Sgshapiro 769363466Sgshapiro /* 770363466Sgshapiro ** This is only "valid" when the msg is safely in the queue, 771363466Sgshapiro ** i.e., EF_INQUEUE needs to be set. 772363466Sgshapiro */ 773363466Sgshapiro 774363466Sgshapiro q->q_flags |= QQUEUED; 775363466Sgshapiro 77638032Speter if (tTd(40, 1)) 77738032Speter { 77890792Sgshapiro sm_dprintf("queueing "); 779132943Sgshapiro printaddr(sm_debug_file(), q, false); 78038032Speter } 78138032Speter } 78238032Speter 78338032Speter /* 78438032Speter ** Output headers for this message. 78538032Speter ** Expand macros completely here. Queue run will deal with 78638032Speter ** everything as absolute headers. 78738032Speter ** All headers that must be relative to the recipient 78838032Speter ** can be cracked later. 78938032Speter ** We set up a "null mailer" -- i.e., a mailer that will have 79038032Speter ** no effect on the addresses as they are output. 79138032Speter */ 79238032Speter 793168515Sgshapiro memset((char *) &nullmailer, '\0', sizeof(nullmailer)); 79438032Speter nullmailer.m_re_rwset = nullmailer.m_rh_rwset = 79538032Speter nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; 79638032Speter nullmailer.m_eol = "\n"; 797168515Sgshapiro memset(&mcibuf, '\0', sizeof(mcibuf)); 79838032Speter mcibuf.mci_mailer = &nullmailer; 79938032Speter mcibuf.mci_out = tfp; 80038032Speter 80190792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', "\201f"); 80238032Speter for (h = e->e_header; h != NULL; h = h->h_link) 80338032Speter { 80443730Speter if (h->h_value == NULL) 80538032Speter continue; 80638032Speter 80738032Speter /* don't output resent headers on non-resent messages */ 80864562Sgshapiro if (bitset(H_RESENT, h->h_flags) && 80964562Sgshapiro !bitset(EF_RESENT, e->e_flags)) 81038032Speter continue; 81138032Speter 81238032Speter /* expand macros; if null, don't output header at all */ 81338032Speter if (bitset(H_DEFAULT, h->h_flags)) 81438032Speter { 815168515Sgshapiro (void) expand(h->h_value, buf, sizeof(buf), e); 81638032Speter if (buf[0] == '\0') 81738032Speter continue; 818168515Sgshapiro if (buf[0] == ' ' && buf[1] == '\0') 819168515Sgshapiro continue; 82038032Speter } 82138032Speter 82238032Speter /* output this header */ 82390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?"); 82438032Speter 82564562Sgshapiro /* output conditional macro if present */ 82664562Sgshapiro if (h->h_macro != '\0') 82738032Speter { 82864562Sgshapiro if (bitset(0200, h->h_macro)) 82990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, 83090792Sgshapiro "${%s}", 83190792Sgshapiro macname(bitidx(h->h_macro))); 83264562Sgshapiro else 83390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, 83490792Sgshapiro "$%c", h->h_macro); 83564562Sgshapiro } 83664562Sgshapiro else if (!bitzerop(h->h_mflags) && 83764562Sgshapiro bitset(H_CHECK|H_ACHECK, h->h_flags)) 83864562Sgshapiro { 83938032Speter int j; 84038032Speter 84164562Sgshapiro /* if conditional, output the set of conditions */ 84238032Speter for (j = '\0'; j <= '\177'; j++) 84338032Speter if (bitnset(j, h->h_mflags)) 84490792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 84590792Sgshapiro j); 84638032Speter } 84790792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?'); 84838032Speter 84938032Speter /* output the header: expand macros, convert addresses */ 85064562Sgshapiro if (bitset(H_DEFAULT, h->h_flags) && 85164562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 85238032Speter { 853168515Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n", 85490792Sgshapiro h->h_field, 85590792Sgshapiro denlstring(buf, false, true)); 85638032Speter } 85764562Sgshapiro else if (bitset(H_FROM|H_RCPT, h->h_flags) && 85864562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 85938032Speter { 86038032Speter bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 86190792Sgshapiro SM_FILE_T *savetrace = TrafficLogFile; 86238032Speter 86338032Speter TrafficLogFile = NULL; 86438032Speter 86538032Speter if (bitset(H_FROM, h->h_flags)) 86690792Sgshapiro oldstyle = false; 867173340Sgshapiro commaize(h, h->h_value, oldstyle, &mcibuf, e, 868173340Sgshapiro PXLF_HEADER); 86938032Speter 87038032Speter TrafficLogFile = savetrace; 87138032Speter } 87238032Speter else 87338032Speter { 874168515Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n", 87590792Sgshapiro h->h_field, 87690792Sgshapiro denlstring(h->h_value, false, 87790792Sgshapiro true)); 87838032Speter } 87938032Speter } 88038032Speter 88138032Speter /* 88238032Speter ** Clean up. 88338032Speter ** 88438032Speter ** Write a terminator record -- this is to prevent 88538032Speter ** scurrilous crackers from appending any data. 88638032Speter */ 88738032Speter 88890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n"); 88938032Speter 89090792Sgshapiro if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 || 89190792Sgshapiro ((SuperSafe == SAFE_REALLY || 892132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER || 89390792Sgshapiro (SuperSafe == SAFE_INTERACTIVE && msync)) && 89490792Sgshapiro fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) || 89590792Sgshapiro sm_io_error(tfp)) 89638032Speter { 89738032Speter if (newid) 89838032Speter syserr("!552 Error writing control file %s", tf); 89938032Speter else 90038032Speter syserr("!452 Error writing control file %s", tf); 90138032Speter } 90238032Speter 90338032Speter if (!newid) 90438032Speter { 90590792Sgshapiro char new = queue_letter(e, ANYQFL_LETTER); 90690792Sgshapiro 90790792Sgshapiro /* rename (locked) tf to be (locked) [qh]f */ 90890792Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), 909168515Sgshapiro sizeof(qf)); 91038032Speter if (rename(tf, qf) < 0) 911285229Sgshapiro syserr("cannot rename(%s, %s), uid=%ld", 912285229Sgshapiro tf, qf, (long) geteuid()); 91390792Sgshapiro else 91490792Sgshapiro { 91590792Sgshapiro /* 91690792Sgshapiro ** Check if type has changed and only 91790792Sgshapiro ** remove the old item if the rename above 91890792Sgshapiro ** succeeded. 91990792Sgshapiro */ 92090792Sgshapiro 92190792Sgshapiro if (e->e_qfletter != '\0' && 92290792Sgshapiro e->e_qfletter != new) 92390792Sgshapiro { 92490792Sgshapiro if (tTd(40, 5)) 92590792Sgshapiro { 92690792Sgshapiro sm_dprintf("type changed from %c to %c\n", 92790792Sgshapiro e->e_qfletter, new); 92890792Sgshapiro } 92990792Sgshapiro 93090792Sgshapiro if (unlink(queuename(e, e->e_qfletter)) < 0) 93190792Sgshapiro { 93290792Sgshapiro /* XXX: something more drastic? */ 93390792Sgshapiro if (LogLevel > 0) 93490792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 93590792Sgshapiro "queueup: unlink(%s) failed: %s", 93690792Sgshapiro queuename(e, e->e_qfletter), 93790792Sgshapiro sm_errstring(errno)); 93890792Sgshapiro } 93990792Sgshapiro } 94090792Sgshapiro } 94190792Sgshapiro e->e_qfletter = new; 94290792Sgshapiro 94364562Sgshapiro /* 94490792Sgshapiro ** fsync() after renaming to make sure metadata is 94590792Sgshapiro ** written to disk on filesystems in which renames are 94690792Sgshapiro ** not guaranteed. 94764562Sgshapiro */ 94864562Sgshapiro 94990792Sgshapiro if (SuperSafe != SAFE_NO) 95090792Sgshapiro { 95190792Sgshapiro /* for softupdates */ 95290792Sgshapiro if (tfd >= 0 && fsync(tfd) < 0) 95390792Sgshapiro { 95490792Sgshapiro syserr("!queueup: cannot fsync queue temp file %s", 95590792Sgshapiro tf); 95690792Sgshapiro } 95790792Sgshapiro SYNC_DIR(qf, true); 95890792Sgshapiro } 95964562Sgshapiro 96090792Sgshapiro /* close and unlock old (locked) queue file */ 96138032Speter if (e->e_lockfp != NULL) 96290792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 96338032Speter e->e_lockfp = tfp; 96490792Sgshapiro 96590792Sgshapiro /* save log info */ 96690792Sgshapiro if (LogLevel > 79) 96790792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf); 96838032Speter } 96938032Speter else 97090792Sgshapiro { 97190792Sgshapiro /* save log info */ 97290792Sgshapiro if (LogLevel > 79) 97390792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf); 97490792Sgshapiro 97590792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 97690792Sgshapiro } 97790792Sgshapiro 97838032Speter errno = 0; 97938032Speter e->e_flags |= EF_INQUEUE; 98038032Speter 98138032Speter if (tTd(40, 1)) 98290792Sgshapiro sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id); 98338032Speter return; 98438032Speter} 98538032Speter 98690792Sgshapiro/* 98790792Sgshapiro** PRINTCTLADDR -- print control address to file. 98890792Sgshapiro** 98990792Sgshapiro** Parameters: 99090792Sgshapiro** a -- address. 99190792Sgshapiro** tfp -- file pointer. 99290792Sgshapiro** 99390792Sgshapiro** Returns: 99490792Sgshapiro** none. 99590792Sgshapiro** 99690792Sgshapiro** Side Effects: 99790792Sgshapiro** The control address (if changed) is printed to the file. 99890792Sgshapiro** The last control address and uid are saved. 99990792Sgshapiro*/ 100090792Sgshapiro 100164562Sgshapirostatic void 100238032Speterprintctladdr(a, tfp) 100338032Speter register ADDRESS *a; 100490792Sgshapiro SM_FILE_T *tfp; 100538032Speter{ 100664562Sgshapiro char *user; 100738032Speter register ADDRESS *q; 100838032Speter uid_t uid; 100938032Speter gid_t gid; 101038032Speter static ADDRESS *lastctladdr = NULL; 101138032Speter static uid_t lastuid; 101238032Speter 101338032Speter /* initialization */ 101438032Speter if (a == NULL || a->q_alias == NULL || tfp == NULL) 101538032Speter { 101638032Speter if (lastctladdr != NULL && tfp != NULL) 101790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n"); 101838032Speter lastctladdr = NULL; 101938032Speter lastuid = 0; 102038032Speter return; 102138032Speter } 102238032Speter 102338032Speter /* find the active uid */ 102438032Speter q = getctladdr(a); 102538032Speter if (q == NULL) 102638032Speter { 102764562Sgshapiro user = NULL; 102838032Speter uid = 0; 102938032Speter gid = 0; 103038032Speter } 103138032Speter else 103238032Speter { 103364562Sgshapiro user = q->q_ruser != NULL ? q->q_ruser : q->q_user; 103438032Speter uid = q->q_uid; 103538032Speter gid = q->q_gid; 103638032Speter } 103738032Speter a = a->q_alias; 103838032Speter 103938032Speter /* check to see if this is the same as last time */ 104038032Speter if (lastctladdr != NULL && uid == lastuid && 104138032Speter strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) 104238032Speter return; 104338032Speter lastuid = uid; 104438032Speter lastctladdr = a; 104538032Speter 104664562Sgshapiro if (uid == 0 || user == NULL || user[0] == '\0') 104790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C"); 104838032Speter else 104990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld", 105090792Sgshapiro denlstring(user, true, false), (long) uid, 105190792Sgshapiro (long) gid); 105290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n", 105390792Sgshapiro denlstring(a->q_paddr, true, false)); 105438032Speter} 105590792Sgshapiro 105690792Sgshapiro/* 105790792Sgshapiro** RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process 105890792Sgshapiro** 105990792Sgshapiro** This propagates the signal to the child processes that are queue 106090792Sgshapiro** runners. This is for a queue runner "cleanup". After all of the 106190792Sgshapiro** child queue runner processes are signaled (it should be SIGTERM 106290792Sgshapiro** being the sig) then the old signal handler (Oldsh) is called 106390792Sgshapiro** to handle any cleanup set for this process (provided it is not 106490792Sgshapiro** SIG_DFL or SIG_IGN). The signal may not be handled immediately 106590792Sgshapiro** if the BlockOldsh flag is set. If the current process doesn't 106690792Sgshapiro** have a parent then handle the signal immediately, regardless of 106790792Sgshapiro** BlockOldsh. 106890792Sgshapiro** 106990792Sgshapiro** Parameters: 107090792Sgshapiro** sig -- the signal number being sent 107190792Sgshapiro** 107290792Sgshapiro** Returns: 107390792Sgshapiro** none. 107490792Sgshapiro** 107590792Sgshapiro** Side Effects: 107690792Sgshapiro** Sets the NoMoreRunners boolean to true to stop more runners 107790792Sgshapiro** from being started in runqueue(). 107890792Sgshapiro** 107990792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 108090792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 108190792Sgshapiro** DOING. 108290792Sgshapiro*/ 108390792Sgshapiro 108490792Sgshapirostatic bool volatile NoMoreRunners = false; 108590792Sgshapirostatic sigfunc_t Oldsh_term = SIG_DFL; 108690792Sgshapirostatic sigfunc_t Oldsh_hup = SIG_DFL; 108790792Sgshapirostatic sigfunc_t volatile Oldsh = SIG_DFL; 108890792Sgshapirostatic bool BlockOldsh = false; 108990792Sgshapirostatic int volatile Oldsig = 0; 109090792Sgshapirostatic SIGFUNC_DECL runners_sigterm __P((int)); 109190792Sgshapirostatic SIGFUNC_DECL runners_sighup __P((int)); 109290792Sgshapiro 109390792Sgshapirostatic SIGFUNC_DECL 109490792Sgshapirorunners_sigterm(sig) 109590792Sgshapiro int sig; 109690792Sgshapiro{ 109790792Sgshapiro int save_errno = errno; 109890792Sgshapiro 109990792Sgshapiro FIX_SYSV_SIGNAL(sig, runners_sigterm); 110090792Sgshapiro errno = save_errno; 110190792Sgshapiro CHECK_CRITICAL(sig); 110290792Sgshapiro NoMoreRunners = true; 110390792Sgshapiro Oldsh = Oldsh_term; 110490792Sgshapiro Oldsig = sig; 110590792Sgshapiro proc_list_signal(PROC_QUEUE, sig); 110690792Sgshapiro 110790792Sgshapiro if (!BlockOldsh || getppid() <= 1) 110890792Sgshapiro { 110990792Sgshapiro /* Check that a valid 'old signal handler' is callable */ 111090792Sgshapiro if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN && 111190792Sgshapiro Oldsh_term != runners_sigterm) 111290792Sgshapiro (*Oldsh_term)(sig); 111390792Sgshapiro } 111490792Sgshapiro errno = save_errno; 111590792Sgshapiro return SIGFUNC_RETURN; 111690792Sgshapiro} 111790792Sgshapiro/* 111890792Sgshapiro** RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process 111990792Sgshapiro** 112090792Sgshapiro** This propagates the signal to the child processes that are queue 112190792Sgshapiro** runners. This is for a queue runner "cleanup". After all of the 112290792Sgshapiro** child queue runner processes are signaled (it should be SIGHUP 112390792Sgshapiro** being the sig) then the old signal handler (Oldsh) is called to 112490792Sgshapiro** handle any cleanup set for this process (provided it is not SIG_DFL 112590792Sgshapiro** or SIG_IGN). The signal may not be handled immediately if the 112690792Sgshapiro** BlockOldsh flag is set. If the current process doesn't have 112790792Sgshapiro** a parent then handle the signal immediately, regardless of 112890792Sgshapiro** BlockOldsh. 112990792Sgshapiro** 113090792Sgshapiro** Parameters: 113190792Sgshapiro** sig -- the signal number being sent 113290792Sgshapiro** 113390792Sgshapiro** Returns: 113490792Sgshapiro** none. 113590792Sgshapiro** 113690792Sgshapiro** Side Effects: 113790792Sgshapiro** Sets the NoMoreRunners boolean to true to stop more runners 113890792Sgshapiro** from being started in runqueue(). 113990792Sgshapiro** 114090792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 114190792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 114290792Sgshapiro** DOING. 114390792Sgshapiro*/ 114490792Sgshapiro 114590792Sgshapirostatic SIGFUNC_DECL 114690792Sgshapirorunners_sighup(sig) 114790792Sgshapiro int sig; 114890792Sgshapiro{ 114990792Sgshapiro int save_errno = errno; 115090792Sgshapiro 115190792Sgshapiro FIX_SYSV_SIGNAL(sig, runners_sighup); 115290792Sgshapiro errno = save_errno; 115390792Sgshapiro CHECK_CRITICAL(sig); 115490792Sgshapiro NoMoreRunners = true; 115590792Sgshapiro Oldsh = Oldsh_hup; 115690792Sgshapiro Oldsig = sig; 115790792Sgshapiro proc_list_signal(PROC_QUEUE, sig); 115890792Sgshapiro 115990792Sgshapiro if (!BlockOldsh || getppid() <= 1) 116090792Sgshapiro { 116190792Sgshapiro /* Check that a valid 'old signal handler' is callable */ 116290792Sgshapiro if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN && 116390792Sgshapiro Oldsh_hup != runners_sighup) 116490792Sgshapiro (*Oldsh_hup)(sig); 116590792Sgshapiro } 116690792Sgshapiro errno = save_errno; 116790792Sgshapiro return SIGFUNC_RETURN; 116890792Sgshapiro} 116990792Sgshapiro/* 117090792Sgshapiro** MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart 117190792Sgshapiro** 117290792Sgshapiro** Sets a workgroup for restarting. 117390792Sgshapiro** 117490792Sgshapiro** Parameters: 117590792Sgshapiro** wgrp -- the work group id to restart. 117690792Sgshapiro** reason -- why (signal?), -1 to turn off restart 117790792Sgshapiro** 117890792Sgshapiro** Returns: 117990792Sgshapiro** none. 118090792Sgshapiro** 118190792Sgshapiro** Side effects: 118290792Sgshapiro** May set global RestartWorkGroup to true. 118390792Sgshapiro** 118490792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 118590792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 118690792Sgshapiro** DOING. 118790792Sgshapiro*/ 118890792Sgshapiro 118990792Sgshapirovoid 119090792Sgshapiromark_work_group_restart(wgrp, reason) 119190792Sgshapiro int wgrp; 119290792Sgshapiro int reason; 119390792Sgshapiro{ 119490792Sgshapiro if (wgrp < 0 || wgrp > NumWorkGroups) 119590792Sgshapiro return; 119690792Sgshapiro 119790792Sgshapiro WorkGrp[wgrp].wg_restart = reason; 119890792Sgshapiro if (reason >= 0) 119990792Sgshapiro RestartWorkGroup = true; 120090792Sgshapiro} 120190792Sgshapiro/* 120290792Sgshapiro** RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart 120390792Sgshapiro** 120490792Sgshapiro** Restart any workgroup marked as needing a restart provided more 120590792Sgshapiro** runners are allowed. 120690792Sgshapiro** 120790792Sgshapiro** Parameters: 120890792Sgshapiro** none. 120990792Sgshapiro** 121090792Sgshapiro** Returns: 121190792Sgshapiro** none. 121290792Sgshapiro** 121390792Sgshapiro** Side effects: 121490792Sgshapiro** Sets global RestartWorkGroup to false. 121590792Sgshapiro*/ 121690792Sgshapiro 121790792Sgshapirovoid 121890792Sgshapirorestart_marked_work_groups() 121990792Sgshapiro{ 122090792Sgshapiro int i; 122190792Sgshapiro int wasblocked; 122290792Sgshapiro 122390792Sgshapiro if (NoMoreRunners) 122490792Sgshapiro return; 122590792Sgshapiro 122690792Sgshapiro /* Block SIGCHLD so reapchild() doesn't mess with us */ 122790792Sgshapiro wasblocked = sm_blocksignal(SIGCHLD); 122890792Sgshapiro 122990792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 123090792Sgshapiro { 123190792Sgshapiro if (WorkGrp[i].wg_restart >= 0) 123290792Sgshapiro { 123390792Sgshapiro if (LogLevel > 8) 123490792Sgshapiro sm_syslog(LOG_ERR, NOQID, 123590792Sgshapiro "restart queue runner=%d due to signal 0x%x", 123690792Sgshapiro i, WorkGrp[i].wg_restart); 123790792Sgshapiro restart_work_group(i); 123890792Sgshapiro } 123990792Sgshapiro } 124090792Sgshapiro RestartWorkGroup = false; 124190792Sgshapiro 124290792Sgshapiro if (wasblocked == 0) 124390792Sgshapiro (void) sm_releasesignal(SIGCHLD); 124490792Sgshapiro} 124590792Sgshapiro/* 124690792Sgshapiro** RESTART_WORK_GROUP -- restart a specific work group 124790792Sgshapiro** 124890792Sgshapiro** Restart a specific workgroup provided more runners are allowed. 124990792Sgshapiro** If the requested work group has been restarted too many times log 125090792Sgshapiro** this and refuse to restart. 125190792Sgshapiro** 125290792Sgshapiro** Parameters: 125390792Sgshapiro** wgrp -- the work group id to restart 125490792Sgshapiro** 125590792Sgshapiro** Returns: 125690792Sgshapiro** none. 125790792Sgshapiro** 125890792Sgshapiro** Side Effects: 125990792Sgshapiro** starts another process doing the work of wgrp 126090792Sgshapiro*/ 126190792Sgshapiro 126290792Sgshapiro#define MAX_PERSIST_RESTART 10 /* max allowed number of restarts */ 126390792Sgshapiro 126490792Sgshapirostatic void 126590792Sgshapirorestart_work_group(wgrp) 126690792Sgshapiro int wgrp; 126790792Sgshapiro{ 126890792Sgshapiro if (NoMoreRunners || 126990792Sgshapiro wgrp < 0 || wgrp > NumWorkGroups) 127090792Sgshapiro return; 127190792Sgshapiro 127290792Sgshapiro WorkGrp[wgrp].wg_restart = -1; 127390792Sgshapiro if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART) 127490792Sgshapiro { 127590792Sgshapiro /* avoid overflow; increment here */ 127690792Sgshapiro WorkGrp[wgrp].wg_restartcnt++; 1277110560Sgshapiro (void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL); 127890792Sgshapiro } 127990792Sgshapiro else 128090792Sgshapiro { 128190792Sgshapiro sm_syslog(LOG_ERR, NOQID, 128290792Sgshapiro "ERROR: persistent queue runner=%d restarted too many times, queue runner lost", 128390792Sgshapiro wgrp); 128490792Sgshapiro } 128590792Sgshapiro} 128690792Sgshapiro/* 128790792Sgshapiro** SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group. 128890792Sgshapiro** 128990792Sgshapiro** Parameters: 129090792Sgshapiro** runall -- schedule even if individual bit is not set. 129190792Sgshapiro** wgrp -- the work group id to schedule. 129294334Sgshapiro** didit -- the queue run was performed for this work group. 129390792Sgshapiro** 129490792Sgshapiro** Returns: 129590792Sgshapiro** nothing 129690792Sgshapiro*/ 129790792Sgshapiro 129890792Sgshapiro#define INCR_MOD(v, m) if (++v >= m) \ 129990792Sgshapiro v = 0; \ 130090792Sgshapiro else 130190792Sgshapiro 130290792Sgshapirostatic void 130394334Sgshapiroschedule_queue_runs(runall, wgrp, didit) 130490792Sgshapiro bool runall; 130590792Sgshapiro int wgrp; 130694334Sgshapiro bool didit; 130790792Sgshapiro{ 130890792Sgshapiro int qgrp, cgrp, endgrp; 130994334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 131094334Sgshapiro time_t lastsched; 131194334Sgshapiro bool sched; 1312363466Sgshapiro#endif 131394334Sgshapiro time_t now; 131494334Sgshapiro time_t minqintvl; 131590792Sgshapiro 131690792Sgshapiro /* 131790792Sgshapiro ** This is a bit ugly since we have to duplicate the 131890792Sgshapiro ** code that "walks" through a work queue group. 131990792Sgshapiro */ 132090792Sgshapiro 132194334Sgshapiro now = curtime(); 132294334Sgshapiro minqintvl = 0; 132390792Sgshapiro cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp; 132490792Sgshapiro do 132590792Sgshapiro { 132690792Sgshapiro time_t qintvl; 132790792Sgshapiro 132894334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 132994334Sgshapiro lastsched = 0; 133094334Sgshapiro sched = false; 1331363466Sgshapiro#endif 133290792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index; 133390792Sgshapiro if (Queue[qgrp]->qg_queueintvl > 0) 133490792Sgshapiro qintvl = Queue[qgrp]->qg_queueintvl; 133590792Sgshapiro else if (QueueIntvl > 0) 133690792Sgshapiro qintvl = QueueIntvl; 133790792Sgshapiro else 133890792Sgshapiro qintvl = (time_t) 0; 133990792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 134094334Sgshapiro lastsched = Queue[qgrp]->qg_nextrun; 1341363466Sgshapiro#endif 134294334Sgshapiro if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0) 134394334Sgshapiro { 134494334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 134594334Sgshapiro sched = true; 1346363466Sgshapiro#endif 134794334Sgshapiro if (minqintvl == 0 || qintvl < minqintvl) 134894334Sgshapiro minqintvl = qintvl; 134994334Sgshapiro 135094334Sgshapiro /* 135194334Sgshapiro ** Only set a new time if a queue run was performed 135294334Sgshapiro ** for this queue group. If the queue was not run, 135394334Sgshapiro ** we could starve it by setting a new time on each 135494334Sgshapiro ** call. 135594334Sgshapiro */ 135694334Sgshapiro 135794334Sgshapiro if (didit) 135894334Sgshapiro Queue[qgrp]->qg_nextrun += qintvl; 135994334Sgshapiro } 136094334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 136190792Sgshapiro if (tTd(69, 10)) 136290792Sgshapiro sm_syslog(LOG_INFO, NOQID, 136394334Sgshapiro "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d", 1364363466Sgshapiro wgrp, cgrp, qgrp, 1365363466Sgshapiro (long) Queue[qgrp]->qg_queueintvl, 1366363466Sgshapiro (long) QueueIntvl, runall, (long) lastsched, 1367363466Sgshapiro (long) Queue[qgrp]->qg_nextrun, sched); 136890792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 136990792Sgshapiro INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp); 137090792Sgshapiro } while (endgrp != cgrp); 137194334Sgshapiro if (minqintvl > 0) 137294334Sgshapiro (void) sm_setevent(minqintvl, runqueueevent, 0); 137390792Sgshapiro} 137494334Sgshapiro 137594334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA 137690792Sgshapiro/* 137794334Sgshapiro** CHECKQUEUERUNNER -- check whether a queue group hasn't been run. 137894334Sgshapiro** 137994334Sgshapiro** Use this if events may get lost and hence queue runners may not 138094334Sgshapiro** be started and mail will pile up in a queue. 138194334Sgshapiro** 138294334Sgshapiro** Parameters: 138394334Sgshapiro** none. 138494334Sgshapiro** 138594334Sgshapiro** Returns: 138694334Sgshapiro** true if a queue run is necessary. 138794334Sgshapiro** 138894334Sgshapiro** Side Effects: 138994334Sgshapiro** may schedule a queue run. 139094334Sgshapiro*/ 139194334Sgshapiro 139294334Sgshapirobool 139394334Sgshapirocheckqueuerunner() 139494334Sgshapiro{ 139594334Sgshapiro int qgrp; 139694334Sgshapiro time_t now, minqintvl; 139794334Sgshapiro 139894334Sgshapiro now = curtime(); 139994334Sgshapiro minqintvl = 0; 140094334Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 140194334Sgshapiro { 140294334Sgshapiro time_t qintvl; 140394334Sgshapiro 140494334Sgshapiro if (Queue[qgrp]->qg_queueintvl > 0) 140594334Sgshapiro qintvl = Queue[qgrp]->qg_queueintvl; 140694334Sgshapiro else if (QueueIntvl > 0) 140794334Sgshapiro qintvl = QueueIntvl; 140894334Sgshapiro else 140994334Sgshapiro qintvl = (time_t) 0; 141094334Sgshapiro if (Queue[qgrp]->qg_nextrun <= now - qintvl) 141194334Sgshapiro { 141294334Sgshapiro if (minqintvl == 0 || qintvl < minqintvl) 141394334Sgshapiro minqintvl = qintvl; 141494334Sgshapiro if (LogLevel > 1) 141594334Sgshapiro sm_syslog(LOG_WARNING, NOQID, 141694334Sgshapiro "checkqueuerunner: queue %d should have been run at %s, queue interval %ld", 141794334Sgshapiro qgrp, 141894334Sgshapiro arpadate(ctime(&Queue[qgrp]->qg_nextrun)), 1419363466Sgshapiro (long) qintvl); 142094334Sgshapiro } 142194334Sgshapiro } 142294334Sgshapiro if (minqintvl > 0) 142394334Sgshapiro { 142494334Sgshapiro (void) sm_setevent(minqintvl, runqueueevent, 0); 142594334Sgshapiro return true; 142694334Sgshapiro } 142794334Sgshapiro return false; 142894334Sgshapiro} 142994334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */ 143094334Sgshapiro 143194334Sgshapiro/* 143238032Speter** RUNQUEUE -- run the jobs in the queue. 143338032Speter** 143438032Speter** Gets the stuff out of the queue in some presumably logical 143538032Speter** order and processes them. 143638032Speter** 143738032Speter** Parameters: 143890792Sgshapiro** forkflag -- true if the queue scanning should be done in 143938032Speter** a child process. We double-fork so it is not our 144038032Speter** child and we don't have to clean up after it. 144190792Sgshapiro** false can be ignored if we have multiple queues. 144290792Sgshapiro** verbose -- if true, print out status information. 144390792Sgshapiro** persistent -- persistent queue runner? 144490792Sgshapiro** runall -- run all groups or only a subset (DoQueueRun)? 144538032Speter** 144638032Speter** Returns: 144790792Sgshapiro** true if the queue run successfully began. 144838032Speter** 144938032Speter** Side Effects: 145090792Sgshapiro** runs things in the mail queue using run_work_group(). 145190792Sgshapiro** maybe schedules next queue run. 145238032Speter*/ 145338032Speter 145464562Sgshapirostatic ENVELOPE QueueEnvelope; /* the queue run envelope */ 145564562Sgshapirostatic time_t LastQueueTime = 0; /* last time a queue ID assigned */ 145664562Sgshapirostatic pid_t LastQueuePid = -1; /* last PID which had a queue ID */ 145738032Speter 145864562Sgshapiro/* values for qp_supdirs */ 145964562Sgshapiro#define QP_NOSUB 0x0000 /* No subdirectories */ 146064562Sgshapiro#define QP_SUBDF 0x0001 /* "df" subdirectory */ 146164562Sgshapiro#define QP_SUBQF 0x0002 /* "qf" subdirectory */ 146264562Sgshapiro#define QP_SUBXF 0x0004 /* "xf" subdirectory */ 146364562Sgshapiro 146438032Speterbool 146590792Sgshapirorunqueue(forkflag, verbose, persistent, runall) 146638032Speter bool forkflag; 146738032Speter bool verbose; 146890792Sgshapiro bool persistent; 146990792Sgshapiro bool runall; 147038032Speter{ 147164562Sgshapiro int i; 147290792Sgshapiro bool ret = true; 147364562Sgshapiro static int curnum = 0; 147490792Sgshapiro sigfunc_t cursh; 147590792Sgshapiro#if SM_HEAP_CHECK 147690792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 147764562Sgshapiro 147890792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 147990792Sgshapiro { 148090792Sgshapiro oldgroup = sm_heap_group(); 148190792Sgshapiro sm_heap_newgroup(); 148290792Sgshapiro sm_dprintf("runqueue() heap group #%d\n", sm_heap_group()); 148390792Sgshapiro } 148490792Sgshapiro#endif /* SM_HEAP_CHECK */ 148571345Sgshapiro 148690792Sgshapiro /* queue run has been started, don't do any more this time */ 148794334Sgshapiro DoQueueRun = false; 148871345Sgshapiro 148990792Sgshapiro /* more than one queue or more than one directory per queue */ 149090792Sgshapiro if (!forkflag && !verbose && 149190792Sgshapiro (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 || 149290792Sgshapiro WorkGrp[0].wg_numqgrp > 1)) 149390792Sgshapiro forkflag = true; 149464562Sgshapiro 149590792Sgshapiro /* 149690792Sgshapiro ** For controlling queue runners via signals sent to this process. 149790792Sgshapiro ** Oldsh* will get called too by runners_sig* (if it is not SIG_IGN 149890792Sgshapiro ** or SIG_DFL) to preserve cleanup behavior. Now that this process 149990792Sgshapiro ** will have children (and perhaps grandchildren) this handler will 150090792Sgshapiro ** be left in place. This is because this process, once it has 150190792Sgshapiro ** finished spinning off queue runners, may go back to doing something 150290792Sgshapiro ** else (like being a daemon). And we still want on a SIG{TERM,HUP} to 150390792Sgshapiro ** clean up the child queue runners. Only install 'runners_sig*' once 150490792Sgshapiro ** else we'll get stuck looping forever. 150590792Sgshapiro */ 150690792Sgshapiro 150790792Sgshapiro cursh = sm_signal(SIGTERM, runners_sigterm); 150890792Sgshapiro if (cursh != runners_sigterm) 150990792Sgshapiro Oldsh_term = cursh; 151090792Sgshapiro cursh = sm_signal(SIGHUP, runners_sighup); 151190792Sgshapiro if (cursh != runners_sighup) 151290792Sgshapiro Oldsh_hup = cursh; 151390792Sgshapiro 151490792Sgshapiro for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++) 151564562Sgshapiro { 1516110560Sgshapiro int rwgflags = RWG_NONE; 1517244833Sgshapiro int wasblocked; 1518110560Sgshapiro 151964562Sgshapiro /* 152090792Sgshapiro ** If MaxQueueChildren active then test whether the start 152190792Sgshapiro ** of the next queue group's additional queue runners (maximum) 152290792Sgshapiro ** will result in MaxQueueChildren being exceeded. 152390792Sgshapiro ** 152490792Sgshapiro ** Note: do not use continue; even though another workgroup 152590792Sgshapiro ** may have fewer queue runners, this would be "unfair", 152690792Sgshapiro ** i.e., this work group might "starve" then. 152764562Sgshapiro */ 152864562Sgshapiro 152990792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 153090792Sgshapiro if (tTd(69, 10)) 153190792Sgshapiro sm_syslog(LOG_INFO, NOQID, 153290792Sgshapiro "rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d", 153390792Sgshapiro curnum, MaxQueueChildren, CurRunners, 153490792Sgshapiro WorkGrp[curnum].wg_maxact); 153590792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 153690792Sgshapiro if (MaxQueueChildren > 0 && 153790792Sgshapiro CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren) 153890792Sgshapiro break; 153964562Sgshapiro 154064562Sgshapiro /* 154190792Sgshapiro ** Pick up where we left off (curnum), in case we 154290792Sgshapiro ** used up all the children last time without finishing. 154390792Sgshapiro ** This give a round-robin fairness to queue runs. 1544102528Sgshapiro ** 1545102528Sgshapiro ** Increment CurRunners before calling run_work_group() 1546102528Sgshapiro ** to avoid a "race condition" with proc_list_drop() which 1547102528Sgshapiro ** decrements CurRunners if the queue runners terminate. 1548102528Sgshapiro ** Notice: CurRunners is an upper limit, in some cases 1549102528Sgshapiro ** (too few jobs in the queue) this value is larger than 1550102528Sgshapiro ** the actual number of queue runners. The discrepancy can 1551102528Sgshapiro ** increase if some queue runners "hang" for a long time. 155290792Sgshapiro */ 155390792Sgshapiro 1554244833Sgshapiro /* don't let proc_list_drop() change CurRunners */ 1555244833Sgshapiro wasblocked = sm_blocksignal(SIGCHLD); 1556102528Sgshapiro CurRunners += WorkGrp[curnum].wg_maxact; 1557244833Sgshapiro if (wasblocked == 0) 1558244833Sgshapiro (void) sm_releasesignal(SIGCHLD); 1559110560Sgshapiro if (forkflag) 1560110560Sgshapiro rwgflags |= RWG_FORK; 1561110560Sgshapiro if (verbose) 1562110560Sgshapiro rwgflags |= RWG_VERBOSE; 1563110560Sgshapiro if (persistent) 1564110560Sgshapiro rwgflags |= RWG_PERSISTENT; 1565110560Sgshapiro if (runall) 1566110560Sgshapiro rwgflags |= RWG_RUNALL; 1567110560Sgshapiro ret = run_work_group(curnum, rwgflags); 156890792Sgshapiro 156990792Sgshapiro /* 157064562Sgshapiro ** Failure means a message was printed for ETRN 157164562Sgshapiro ** and subsequent queues are likely to fail as well. 1572102528Sgshapiro ** Decrement CurRunners in that case because 1573102528Sgshapiro ** none have been started. 157464562Sgshapiro */ 157564562Sgshapiro 157664562Sgshapiro if (!ret) 1577102528Sgshapiro { 1578244833Sgshapiro /* don't let proc_list_drop() change CurRunners */ 1579244833Sgshapiro wasblocked = sm_blocksignal(SIGCHLD); 1580102528Sgshapiro CurRunners -= WorkGrp[curnum].wg_maxact; 1581244833Sgshapiro CHK_CUR_RUNNERS("runqueue", curnum, 1582244833Sgshapiro WorkGrp[curnum].wg_maxact); 1583244833Sgshapiro if (wasblocked == 0) 1584244833Sgshapiro (void) sm_releasesignal(SIGCHLD); 158564562Sgshapiro break; 1586102528Sgshapiro } 158764562Sgshapiro 158890792Sgshapiro if (!persistent) 158994334Sgshapiro schedule_queue_runs(runall, curnum, true); 159090792Sgshapiro INCR_MOD(curnum, NumWorkGroups); 159164562Sgshapiro } 159290792Sgshapiro 159390792Sgshapiro /* schedule left over queue runs */ 159490792Sgshapiro if (i < NumWorkGroups && !NoMoreRunners && !persistent) 159590792Sgshapiro { 159690792Sgshapiro int h; 159790792Sgshapiro 159890792Sgshapiro for (h = curnum; i < NumWorkGroups; i++) 159990792Sgshapiro { 160094334Sgshapiro schedule_queue_runs(runall, h, false); 160190792Sgshapiro INCR_MOD(h, NumWorkGroups); 160290792Sgshapiro } 160390792Sgshapiro } 160490792Sgshapiro 160590792Sgshapiro 160690792Sgshapiro#if SM_HEAP_CHECK 160790792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 160890792Sgshapiro sm_heap_setgroup(oldgroup); 1609363466Sgshapiro#endif 161064562Sgshapiro return ret; 161164562Sgshapiro} 1612132943Sgshapiro 1613132943Sgshapiro#if _FFR_SKIP_DOMAINS 161490792Sgshapiro/* 1615132943Sgshapiro** SKIP_DOMAINS -- Skip 'skip' number of domains in the WorkQ. 1616132943Sgshapiro** 1617132943Sgshapiro** Added by Stephen Frost <sfrost@snowman.net> to support 1618132943Sgshapiro** having each runner process every N'th domain instead of 1619132943Sgshapiro** every N'th message. 1620132943Sgshapiro** 1621132943Sgshapiro** Parameters: 1622132943Sgshapiro** skip -- number of domains in WorkQ to skip. 1623132943Sgshapiro** 1624132943Sgshapiro** Returns: 1625132943Sgshapiro** total number of messages skipped. 1626132943Sgshapiro** 1627132943Sgshapiro** Side Effects: 1628132943Sgshapiro** may change WorkQ 1629132943Sgshapiro*/ 1630132943Sgshapiro 1631132943Sgshapirostatic int 1632132943Sgshapiroskip_domains(skip) 1633132943Sgshapiro int skip; 1634132943Sgshapiro{ 1635132943Sgshapiro int n, seqjump; 1636132943Sgshapiro 1637132943Sgshapiro for (n = 0, seqjump = 0; n < skip && WorkQ != NULL; seqjump++) 1638132943Sgshapiro { 1639132943Sgshapiro if (WorkQ->w_next != NULL) 1640132943Sgshapiro { 1641132943Sgshapiro if (WorkQ->w_host != NULL && 1642132943Sgshapiro WorkQ->w_next->w_host != NULL) 1643132943Sgshapiro { 1644132943Sgshapiro if (sm_strcasecmp(WorkQ->w_host, 1645132943Sgshapiro WorkQ->w_next->w_host) != 0) 1646132943Sgshapiro n++; 1647132943Sgshapiro } 1648132943Sgshapiro else 1649132943Sgshapiro { 1650132943Sgshapiro if ((WorkQ->w_host != NULL && 1651132943Sgshapiro WorkQ->w_next->w_host == NULL) || 1652132943Sgshapiro (WorkQ->w_host == NULL && 1653132943Sgshapiro WorkQ->w_next->w_host != NULL)) 1654132943Sgshapiro n++; 1655132943Sgshapiro } 1656132943Sgshapiro } 1657132943Sgshapiro WorkQ = WorkQ->w_next; 1658132943Sgshapiro } 1659132943Sgshapiro return seqjump; 1660132943Sgshapiro} 1661132943Sgshapiro#endif /* _FFR_SKIP_DOMAINS */ 1662132943Sgshapiro 1663132943Sgshapiro/* 166490792Sgshapiro** RUNNER_WORK -- have a queue runner do its work 166564562Sgshapiro** 166690792Sgshapiro** Have a queue runner do its work a list of entries. 166790792Sgshapiro** When work isn't directly being done then this process can take a signal 166890792Sgshapiro** and terminate immediately (in a clean fashion of course). 166990792Sgshapiro** When work is directly being done, it's not to be interrupted 167090792Sgshapiro** immediately: the work should be allowed to finish at a clean point 167190792Sgshapiro** before termination (in a clean fashion of course). 167290792Sgshapiro** 167390792Sgshapiro** Parameters: 167490792Sgshapiro** e -- envelope. 167590792Sgshapiro** sequenceno -- 'th process to run WorkQ. 167690792Sgshapiro** didfork -- did the calling process fork()? 167790792Sgshapiro** skip -- process only each skip'th item. 167890792Sgshapiro** njobs -- number of jobs in WorkQ. 167990792Sgshapiro** 168090792Sgshapiro** Returns: 168190792Sgshapiro** none. 168290792Sgshapiro** 168390792Sgshapiro** Side Effects: 168490792Sgshapiro** runs things in the mail queue. 168590792Sgshapiro*/ 168690792Sgshapiro 168790792Sgshapirostatic void 168890792Sgshapirorunner_work(e, sequenceno, didfork, skip, njobs) 168990792Sgshapiro register ENVELOPE *e; 169090792Sgshapiro int sequenceno; 169190792Sgshapiro bool didfork; 169290792Sgshapiro int skip; 169390792Sgshapiro int njobs; 169490792Sgshapiro{ 1695132943Sgshapiro int n, seqjump; 169690792Sgshapiro WORK *w; 1697120256Sgshapiro time_t now; 169890792Sgshapiro 1699120256Sgshapiro SM_GET_LA(now); 170090792Sgshapiro 170190792Sgshapiro /* 170290792Sgshapiro ** Here we temporarily block the second calling of the handlers. 170390792Sgshapiro ** This allows us to handle the signal without terminating in the 170490792Sgshapiro ** middle of direct work. If a signal does come, the test for 170590792Sgshapiro ** NoMoreRunners will find it. 170690792Sgshapiro */ 170790792Sgshapiro 170890792Sgshapiro BlockOldsh = true; 1709132943Sgshapiro seqjump = skip; 171090792Sgshapiro 171190792Sgshapiro /* process them once at a time */ 171290792Sgshapiro while (WorkQ != NULL) 171390792Sgshapiro { 171490792Sgshapiro#if SM_HEAP_CHECK 171590792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 171690792Sgshapiro 171790792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 171890792Sgshapiro { 171990792Sgshapiro oldgroup = sm_heap_group(); 172090792Sgshapiro sm_heap_newgroup(); 172190792Sgshapiro sm_dprintf("run_queue_group() heap group #%d\n", 172290792Sgshapiro sm_heap_group()); 172390792Sgshapiro } 172490792Sgshapiro#endif /* SM_HEAP_CHECK */ 172590792Sgshapiro 172690792Sgshapiro /* do no more work */ 172790792Sgshapiro if (NoMoreRunners) 172890792Sgshapiro { 172990792Sgshapiro /* Check that a valid signal handler is callable */ 173090792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 173190792Sgshapiro Oldsh != runners_sighup && 173290792Sgshapiro Oldsh != runners_sigterm) 173390792Sgshapiro (*Oldsh)(Oldsig); 173490792Sgshapiro break; 173590792Sgshapiro } 173690792Sgshapiro 173790792Sgshapiro w = WorkQ; /* assign current work item */ 173890792Sgshapiro 173990792Sgshapiro /* 174090792Sgshapiro ** Set the head of the WorkQ to the next work item. 174190792Sgshapiro ** It is set 'skip' ahead (the number of parallel queue 174290792Sgshapiro ** runners working on WorkQ together) since each runner 174390792Sgshapiro ** works on every 'skip'th (N-th) item. 1744132943Sgshapiro#if _FFR_SKIP_DOMAINS 1745132943Sgshapiro ** In the case of the BYHOST Queue Sort Order, the 'item' 1746132943Sgshapiro ** is a domain, so we work on every 'skip'th (N-th) domain. 1747363466Sgshapiro#endif 174890792Sgshapiro */ 174990792Sgshapiro 1750132943Sgshapiro#if _FFR_SKIP_DOMAINS 1751132943Sgshapiro if (QueueSortOrder == QSO_BYHOST) 1752132943Sgshapiro { 1753132943Sgshapiro seqjump = 1; 1754132943Sgshapiro if (WorkQ->w_next != NULL) 1755132943Sgshapiro { 1756132943Sgshapiro if (WorkQ->w_host != NULL && 1757132943Sgshapiro WorkQ->w_next->w_host != NULL) 1758132943Sgshapiro { 1759132943Sgshapiro if (sm_strcasecmp(WorkQ->w_host, 1760132943Sgshapiro WorkQ->w_next->w_host) 1761132943Sgshapiro != 0) 1762132943Sgshapiro seqjump = skip_domains(skip); 1763132943Sgshapiro else 1764132943Sgshapiro WorkQ = WorkQ->w_next; 1765132943Sgshapiro } 1766132943Sgshapiro else 1767132943Sgshapiro { 1768132943Sgshapiro if ((WorkQ->w_host != NULL && 1769132943Sgshapiro WorkQ->w_next->w_host == NULL) || 1770132943Sgshapiro (WorkQ->w_host == NULL && 1771132943Sgshapiro WorkQ->w_next->w_host != NULL)) 1772132943Sgshapiro seqjump = skip_domains(skip); 1773132943Sgshapiro else 1774132943Sgshapiro WorkQ = WorkQ->w_next; 1775132943Sgshapiro } 1776132943Sgshapiro } 1777132943Sgshapiro else 1778132943Sgshapiro WorkQ = WorkQ->w_next; 1779132943Sgshapiro } 1780132943Sgshapiro else 1781132943Sgshapiro#endif /* _FFR_SKIP_DOMAINS */ 1782132943Sgshapiro { 1783132943Sgshapiro for (n = 0; n < skip && WorkQ != NULL; n++) 1784132943Sgshapiro WorkQ = WorkQ->w_next; 1785132943Sgshapiro } 1786132943Sgshapiro 178790792Sgshapiro e->e_to = NULL; 178890792Sgshapiro 178990792Sgshapiro /* 179090792Sgshapiro ** Ignore jobs that are too expensive for the moment. 179190792Sgshapiro ** 179290792Sgshapiro ** Get new load average every GET_NEW_LA_TIME seconds. 179390792Sgshapiro */ 179490792Sgshapiro 1795120256Sgshapiro SM_GET_LA(now); 1796120256Sgshapiro if (shouldqueue(WkRecipFact, Current_LA_time)) 179790792Sgshapiro { 179890792Sgshapiro char *msg = "Aborting queue run: load average too high"; 179990792Sgshapiro 180090792Sgshapiro if (Verbose) 180190792Sgshapiro message("%s", msg); 180290792Sgshapiro if (LogLevel > 8) 180390792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 180490792Sgshapiro break; 180590792Sgshapiro } 180690792Sgshapiro if (shouldqueue(w->w_pri, w->w_ctime)) 180790792Sgshapiro { 180890792Sgshapiro if (Verbose) 1809285229Sgshapiro message("%s", ""); 181090792Sgshapiro if (QueueSortOrder == QSO_BYPRIORITY) 181190792Sgshapiro { 181290792Sgshapiro if (Verbose) 181390792Sgshapiro message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", 181490792Sgshapiro qid_printqueue(w->w_qgrp, 181590792Sgshapiro w->w_qdir), 181690792Sgshapiro w->w_name + 2, sequenceno, 181790792Sgshapiro njobs); 181890792Sgshapiro if (LogLevel > 8) 181990792Sgshapiro sm_syslog(LOG_INFO, NOQID, 182090792Sgshapiro "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", 182190792Sgshapiro qid_printqueue(w->w_qgrp, 182290792Sgshapiro w->w_qdir), 182390792Sgshapiro w->w_name + 2, w->w_pri, 182490792Sgshapiro CurrentLA, sequenceno, 182590792Sgshapiro njobs); 182690792Sgshapiro break; 182790792Sgshapiro } 182890792Sgshapiro else if (Verbose) 182990792Sgshapiro message("Skipping %s/%s (sequence %d of %d)", 183090792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 183190792Sgshapiro w->w_name + 2, sequenceno, njobs); 183290792Sgshapiro } 183390792Sgshapiro else 183490792Sgshapiro { 183590792Sgshapiro if (Verbose) 183690792Sgshapiro { 1837285229Sgshapiro message("%s", ""); 183890792Sgshapiro message("Running %s/%s (sequence %d of %d)", 183990792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 184090792Sgshapiro w->w_name + 2, sequenceno, njobs); 184190792Sgshapiro } 184290792Sgshapiro if (didfork && MaxQueueChildren > 0) 184390792Sgshapiro { 184490792Sgshapiro sm_blocksignal(SIGCHLD); 184590792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 184690792Sgshapiro } 184790792Sgshapiro if (tTd(63, 100)) 184890792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 184990792Sgshapiro "runqueue %s dowork(%s)", 185090792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 185190792Sgshapiro w->w_name + 2); 185290792Sgshapiro 185390792Sgshapiro (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2, 1854111823Sgshapiro ForkQueueRuns, false, e); 185590792Sgshapiro errno = 0; 185690792Sgshapiro } 185790792Sgshapiro sm_free(w->w_name); /* XXX */ 185890792Sgshapiro if (w->w_host != NULL) 185990792Sgshapiro sm_free(w->w_host); /* XXX */ 186090792Sgshapiro sm_free((char *) w); /* XXX */ 1861132943Sgshapiro sequenceno += seqjump; /* next sequence number */ 186290792Sgshapiro#if SM_HEAP_CHECK 186390792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 186490792Sgshapiro sm_heap_setgroup(oldgroup); 1865363466Sgshapiro#endif 1866363466Sgshapiro#if _FFR_TESTS 1867363466Sgshapiro if (tTd(76, 101)) 1868363466Sgshapiro { 1869363466Sgshapiro int sl; 1870363466Sgshapiro 1871363466Sgshapiro sl = tTdlevel(76) - 100; 1872363466Sgshapiro sm_dprintf("run_work_group: sleep=%d\n", sl); 1873363466Sgshapiro sleep(sl); 1874363466Sgshapiro } 1875363466Sgshapiro#endif 187690792Sgshapiro } 187790792Sgshapiro 187890792Sgshapiro BlockOldsh = false; 187990792Sgshapiro 188090792Sgshapiro /* check the signals didn't happen during the revert */ 188190792Sgshapiro if (NoMoreRunners) 188290792Sgshapiro { 188390792Sgshapiro /* Check that a valid signal handler is callable */ 188490792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 188590792Sgshapiro Oldsh != runners_sighup && Oldsh != runners_sigterm) 188690792Sgshapiro (*Oldsh)(Oldsig); 188790792Sgshapiro } 188890792Sgshapiro 188990792Sgshapiro Oldsh = SIG_DFL; /* after the NoMoreRunners check */ 189090792Sgshapiro} 189190792Sgshapiro/* 189290792Sgshapiro** RUN_WORK_GROUP -- run the jobs in a queue group from a work group. 189390792Sgshapiro** 189464562Sgshapiro** Gets the stuff out of the queue in some presumably logical 189564562Sgshapiro** order and processes them. 189664562Sgshapiro** 189764562Sgshapiro** Parameters: 189890792Sgshapiro** wgrp -- work group to process. 1899110560Sgshapiro** flags -- RWG_* flags 190064562Sgshapiro** 190164562Sgshapiro** Returns: 190290792Sgshapiro** true if the queue run successfully began. 190364562Sgshapiro** 190464562Sgshapiro** Side Effects: 190564562Sgshapiro** runs things in the mail queue. 190664562Sgshapiro*/ 190764562Sgshapiro 190890792Sgshapiro/* Minimum sleep time for persistent queue runners */ 190990792Sgshapiro#define MIN_SLEEP_TIME 5 191090792Sgshapiro 191190792Sgshapirobool 1912110560Sgshapirorun_work_group(wgrp, flags) 191390792Sgshapiro int wgrp; 1914110560Sgshapiro int flags; 191564562Sgshapiro{ 191638032Speter register ENVELOPE *e; 191790792Sgshapiro int njobs, qdir; 191890792Sgshapiro int sequenceno = 1; 191990792Sgshapiro int qgrp, endgrp, h, i; 1920120256Sgshapiro time_t now; 192190792Sgshapiro bool full, more; 192290792Sgshapiro SM_RPOOL_T *rpool; 192338032Speter extern ENVELOPE BlankEnvelope; 192490792Sgshapiro extern SIGFUNC_DECL reapchild __P((int)); 192538032Speter 192690792Sgshapiro if (wgrp < 0) 192790792Sgshapiro return false; 192890792Sgshapiro 192938032Speter /* 193038032Speter ** If no work will ever be selected, don't even bother reading 193138032Speter ** the queue. 193238032Speter */ 193338032Speter 1934120256Sgshapiro SM_GET_LA(now); 193538032Speter 1936110560Sgshapiro if (!bitset(RWG_PERSISTENT, flags) && 1937120256Sgshapiro shouldqueue(WkRecipFact, Current_LA_time)) 193838032Speter { 193938032Speter char *msg = "Skipping queue run -- load average too high"; 194038032Speter 1941110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 194238032Speter message("458 %s\n", msg); 194338032Speter if (LogLevel > 8) 194490792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 194590792Sgshapiro return false; 194638032Speter } 194738032Speter 194838032Speter /* 194938032Speter ** See if we already have too many children. 195038032Speter */ 195138032Speter 1952110560Sgshapiro if (bitset(RWG_FORK, flags) && 1953110560Sgshapiro WorkGrp[wgrp].wg_lowqintvl > 0 && 1954110560Sgshapiro !bitset(RWG_PERSISTENT, flags) && 195538032Speter MaxChildren > 0 && CurChildren >= MaxChildren) 195638032Speter { 195764562Sgshapiro char *msg = "Skipping queue run -- too many children"; 195864562Sgshapiro 1959110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 196064562Sgshapiro message("458 %s (%d)\n", msg, CurChildren); 196164562Sgshapiro if (LogLevel > 8) 196290792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)", 196364562Sgshapiro msg, CurChildren); 196490792Sgshapiro return false; 196538032Speter } 196638032Speter 196738032Speter /* 196838032Speter ** See if we want to go off and do other useful work. 196938032Speter */ 197038032Speter 1971110560Sgshapiro if (bitset(RWG_FORK, flags)) 197238032Speter { 197338032Speter pid_t pid; 197438032Speter 197590792Sgshapiro (void) sm_blocksignal(SIGCHLD); 197690792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 197738032Speter 197838032Speter pid = dofork(); 197938032Speter if (pid == -1) 198038032Speter { 198138032Speter const char *msg = "Skipping queue run -- fork() failed"; 198290792Sgshapiro const char *err = sm_errstring(errno); 198338032Speter 1984110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 198538032Speter message("458 %s: %s\n", msg, err); 198638032Speter if (LogLevel > 8) 198790792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s", 198864562Sgshapiro msg, err); 198990792Sgshapiro (void) sm_releasesignal(SIGCHLD); 199090792Sgshapiro return false; 199138032Speter } 199238032Speter if (pid != 0) 199338032Speter { 199438032Speter /* parent -- pick up intermediate zombie */ 199590792Sgshapiro (void) sm_blocksignal(SIGALRM); 199690792Sgshapiro 199790792Sgshapiro /* wgrp only used when queue runners are persistent */ 199890792Sgshapiro proc_list_add(pid, "Queue runner", PROC_QUEUE, 199990792Sgshapiro WorkGrp[wgrp].wg_maxact, 2000132943Sgshapiro bitset(RWG_PERSISTENT, flags) ? wgrp : -1, 2001132943Sgshapiro NULL); 200290792Sgshapiro (void) sm_releasesignal(SIGALRM); 200390792Sgshapiro (void) sm_releasesignal(SIGCHLD); 200490792Sgshapiro return true; 200538032Speter } 200690792Sgshapiro 200764562Sgshapiro /* child -- clean up signals */ 200877349Sgshapiro 200977349Sgshapiro /* Reset global flags */ 201077349Sgshapiro RestartRequest = NULL; 201190792Sgshapiro RestartWorkGroup = false; 201277349Sgshapiro ShutdownRequest = NULL; 201377349Sgshapiro PendingSignal = 0; 201490792Sgshapiro CurrentPid = getpid(); 2015132943Sgshapiro close_sendmail_pid(); 201677349Sgshapiro 201790792Sgshapiro /* 201890792Sgshapiro ** Initialize exception stack and default exception 201990792Sgshapiro ** handler for child process. 202090792Sgshapiro */ 202190792Sgshapiro 202290792Sgshapiro sm_exc_newthread(fatal_error); 202342575Speter clrcontrol(); 202438032Speter proc_list_clear(); 202542575Speter 202642575Speter /* Add parent process as first child item */ 202790792Sgshapiro proc_list_add(CurrentPid, "Queue runner child process", 2028132943Sgshapiro PROC_QUEUE_CHILD, 0, -1, NULL); 202990792Sgshapiro (void) sm_releasesignal(SIGCHLD); 203090792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 203190792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 203290792Sgshapiro (void) sm_signal(SIGTERM, intsig); 203338032Speter } 203438032Speter 203538032Speter /* 203638032Speter ** Release any resources used by the daemon code. 203738032Speter */ 203838032Speter 203938032Speter clrdaemon(); 204038032Speter 204138032Speter /* force it to run expensive jobs */ 204290792Sgshapiro NoConnect = false; 204338032Speter 204438032Speter /* drop privileges */ 204538032Speter if (geteuid() == (uid_t) 0) 204690792Sgshapiro (void) drop_privileges(false); 204738032Speter 204838032Speter /* 204938032Speter ** Create ourselves an envelope 205038032Speter */ 205138032Speter 205238032Speter CurEnv = &QueueEnvelope; 205390792Sgshapiro rpool = sm_rpool_new_x(NULL); 205490792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 205538032Speter e->e_flags = BlankEnvelope.e_flags; 205673188Sgshapiro e->e_parent = NULL; 205738032Speter 205838032Speter /* make sure we have disconnected from parent */ 2059110560Sgshapiro if (bitset(RWG_FORK, flags)) 206038032Speter { 206138032Speter disconnect(1, e); 206290792Sgshapiro QuickAbort = false; 206338032Speter } 206438032Speter 206538032Speter /* 206638032Speter ** If we are running part of the queue, always ignore stored 206738032Speter ** host status. 206838032Speter */ 206938032Speter 207038032Speter if (QueueLimitId != NULL || QueueLimitSender != NULL || 207190792Sgshapiro QueueLimitQuarantine != NULL || 207238032Speter QueueLimitRecipient != NULL) 207338032Speter { 207490792Sgshapiro IgnoreHostStatus = true; 207538032Speter MinQueueAge = 0; 2076244833Sgshapiro MaxQueueAge = 0; 207738032Speter } 207838032Speter 207938032Speter /* 208090792Sgshapiro ** Here is where we choose the queue group from the work group. 208190792Sgshapiro ** The caller of the "domorework" label must setup a new envelope. 208290792Sgshapiro */ 208390792Sgshapiro 208490792Sgshapiro endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */ 208590792Sgshapiro 208690792Sgshapiro domorework: 208790792Sgshapiro 208890792Sgshapiro /* 208990792Sgshapiro ** Run a queue group if: 2090110560Sgshapiro ** RWG_RUNALL bit is set or the bit for this group is set. 209190792Sgshapiro */ 209290792Sgshapiro 209394334Sgshapiro now = curtime(); 209490792Sgshapiro for (;;) 209590792Sgshapiro { 209690792Sgshapiro /* 209790792Sgshapiro ** Find the next queue group within the work group that 209890792Sgshapiro ** has been marked as needing a run. 209990792Sgshapiro */ 210090792Sgshapiro 210190792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index; 210290792Sgshapiro WorkGrp[wgrp].wg_curqgrp++; /* advance */ 210390792Sgshapiro WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */ 2104110560Sgshapiro if (bitset(RWG_RUNALL, flags) || 210594334Sgshapiro (Queue[qgrp]->qg_nextrun <= now && 210694334Sgshapiro Queue[qgrp]->qg_nextrun != (time_t) -1)) 210790792Sgshapiro break; 210890792Sgshapiro if (endgrp == WorkGrp[wgrp].wg_curqgrp) 210990792Sgshapiro { 211090792Sgshapiro e->e_id = NULL; 2111110560Sgshapiro if (bitset(RWG_FORK, flags)) 211290792Sgshapiro finis(true, true, ExitStat); 211390792Sgshapiro return true; /* we're done */ 211490792Sgshapiro } 211590792Sgshapiro } 211690792Sgshapiro 211790792Sgshapiro qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */ 211890792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 211990792Sgshapiro if (tTd(69, 12)) 212090792Sgshapiro sm_syslog(LOG_INFO, NOQID, 212190792Sgshapiro "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d", 212290792Sgshapiro wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir), 212390792Sgshapiro WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp); 212490792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 212590792Sgshapiro 212690792Sgshapiro#if HASNICE 212790792Sgshapiro /* tweak niceness of queue runs */ 212890792Sgshapiro if (Queue[qgrp]->qg_nice > 0) 212990792Sgshapiro (void) nice(Queue[qgrp]->qg_nice); 2130363466Sgshapiro#endif 213190792Sgshapiro 213290792Sgshapiro /* XXX running queue group... */ 213390792Sgshapiro sm_setproctitle(true, CurEnv, "running queue: %s", 213490792Sgshapiro qid_printqueue(qgrp, qdir)); 213590792Sgshapiro 213690792Sgshapiro if (LogLevel > 69 || tTd(63, 99)) 213790792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 213890792Sgshapiro "runqueue %s, pid=%d, forkflag=%d", 213990792Sgshapiro qid_printqueue(qgrp, qdir), (int) CurrentPid, 2140110560Sgshapiro bitset(RWG_FORK, flags)); 214190792Sgshapiro 214290792Sgshapiro /* 214338032Speter ** Start making passes through the queue. 214438032Speter ** First, read and sort the entire queue. 214538032Speter ** Then, process the work in that order. 214638032Speter ** But if you take too long, start over. 214738032Speter */ 214838032Speter 214990792Sgshapiro for (i = 0; i < Queue[qgrp]->qg_numqueues; i++) 215090792Sgshapiro { 2151203004Sgshapiro (void) gatherq(qgrp, qdir, false, &full, &more, &h); 215290792Sgshapiro#if SM_CONF_SHM 215390792Sgshapiro if (ShmId != SM_SHM_NO_ID) 215490792Sgshapiro QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h; 2155363466Sgshapiro#endif 215690792Sgshapiro /* If there are no more items in this queue advance */ 215790792Sgshapiro if (!more) 215890792Sgshapiro { 215990792Sgshapiro /* A round-robin advance */ 216090792Sgshapiro qdir++; 216190792Sgshapiro qdir %= Queue[qgrp]->qg_numqueues; 216290792Sgshapiro } 216390792Sgshapiro 216490792Sgshapiro /* Has the WorkList reached the limit? */ 216590792Sgshapiro if (full) 216690792Sgshapiro break; /* don't try to gather more */ 216790792Sgshapiro } 216890792Sgshapiro 216938032Speter /* order the existing work requests */ 217090792Sgshapiro njobs = sortq(Queue[qgrp]->qg_maxlist); 217190792Sgshapiro Queue[qgrp]->qg_curnum = qdir; /* update */ 217238032Speter 217364562Sgshapiro 217490792Sgshapiro if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags)) 217538032Speter { 217690792Sgshapiro int loop, maxrunners; 217790792Sgshapiro pid_t pid; 217838032Speter 217938032Speter /* 218090792Sgshapiro ** For this WorkQ we want to fork off N children (maxrunners) 218190792Sgshapiro ** at this point. Each child has a copy of WorkQ. Each child 218290792Sgshapiro ** will process every N-th item. The parent will wait for all 218390792Sgshapiro ** of the children to finish before moving on to the next 218490792Sgshapiro ** queue group within the work group. This saves us forking 218590792Sgshapiro ** a new runner-child for each work item. 218690792Sgshapiro ** It's valid for qg_maxqrun == 0 since this may be an 218790792Sgshapiro ** explicit "don't run this queue" setting. 218838032Speter */ 218938032Speter 219090792Sgshapiro maxrunners = Queue[qgrp]->qg_maxqrun; 219190792Sgshapiro 2192173340Sgshapiro /* 2193173340Sgshapiro ** If no runners are configured for this group but 2194173340Sgshapiro ** the queue is "forced" then lets use 1 runner. 2195173340Sgshapiro */ 2196173340Sgshapiro 2197173340Sgshapiro if (maxrunners == 0 && bitset(RWG_FORCE, flags)) 2198173340Sgshapiro maxrunners = 1; 2199173340Sgshapiro 220090792Sgshapiro /* No need to have more runners then there are jobs */ 220190792Sgshapiro if (maxrunners > njobs) 220290792Sgshapiro maxrunners = njobs; 220390792Sgshapiro for (loop = 0; loop < maxrunners; loop++) 220438032Speter { 220590792Sgshapiro /* 220690792Sgshapiro ** Since the delivery may happen in a child and the 220790792Sgshapiro ** parent does not wait, the parent may close the 220890792Sgshapiro ** maps thereby removing any shared memory used by 220990792Sgshapiro ** the map. Therefore, close the maps now so the 221090792Sgshapiro ** child will dynamically open them if necessary. 221190792Sgshapiro */ 221290792Sgshapiro 221390792Sgshapiro closemaps(false); 221490792Sgshapiro 221590792Sgshapiro pid = fork(); 221690792Sgshapiro if (pid < 0) 221790792Sgshapiro { 221890792Sgshapiro syserr("run_work_group: cannot fork"); 2219120256Sgshapiro return false; 222090792Sgshapiro } 222190792Sgshapiro else if (pid > 0) 222290792Sgshapiro { 222390792Sgshapiro /* parent -- clean out connection cache */ 222490792Sgshapiro mci_flush(false, NULL); 2225132943Sgshapiro#if _FFR_SKIP_DOMAINS 2226132943Sgshapiro if (QueueSortOrder == QSO_BYHOST) 2227132943Sgshapiro { 2228132943Sgshapiro sequenceno += skip_domains(1); 2229132943Sgshapiro } 2230132943Sgshapiro else 2231132943Sgshapiro#endif /* _FFR_SKIP_DOMAINS */ 2232132943Sgshapiro { 2233132943Sgshapiro /* for the skip */ 2234132943Sgshapiro WorkQ = WorkQ->w_next; 2235132943Sgshapiro sequenceno++; 2236132943Sgshapiro } 223790792Sgshapiro proc_list_add(pid, "Queue child runner process", 2238132943Sgshapiro PROC_QUEUE_CHILD, 0, -1, NULL); 223990792Sgshapiro 224090792Sgshapiro /* No additional work, no additional runners */ 224190792Sgshapiro if (WorkQ == NULL) 224290792Sgshapiro break; 224390792Sgshapiro } 224490792Sgshapiro else 224590792Sgshapiro { 224690792Sgshapiro /* child -- Reset global flags */ 224790792Sgshapiro RestartRequest = NULL; 224890792Sgshapiro RestartWorkGroup = false; 224990792Sgshapiro ShutdownRequest = NULL; 225090792Sgshapiro PendingSignal = 0; 225190792Sgshapiro CurrentPid = getpid(); 2252132943Sgshapiro close_sendmail_pid(); 225390792Sgshapiro 225490792Sgshapiro /* 225590792Sgshapiro ** Initialize exception stack and default 225690792Sgshapiro ** exception handler for child process. 225790792Sgshapiro ** When fork()'d the child now has a private 225890792Sgshapiro ** copy of WorkQ at its current position. 225990792Sgshapiro */ 226090792Sgshapiro 226190792Sgshapiro sm_exc_newthread(fatal_error); 226290792Sgshapiro 226390792Sgshapiro /* 226490792Sgshapiro ** SMTP processes (whether -bd or -bs) set 226590792Sgshapiro ** SIGCHLD to reapchild to collect 226690792Sgshapiro ** children status. However, at delivery 226790792Sgshapiro ** time, that status must be collected 226890792Sgshapiro ** by sm_wait() to be dealt with properly 226990792Sgshapiro ** (check success of delivery based 227090792Sgshapiro ** on status code, etc). Therefore, if we 227190792Sgshapiro ** are an SMTP process, reset SIGCHLD 227290792Sgshapiro ** back to the default so reapchild 227390792Sgshapiro ** doesn't collect status before 227490792Sgshapiro ** sm_wait(). 227590792Sgshapiro */ 227690792Sgshapiro 227790792Sgshapiro if (OpMode == MD_SMTP || 227890792Sgshapiro OpMode == MD_DAEMON || 227990792Sgshapiro MaxQueueChildren > 0) 228090792Sgshapiro { 228190792Sgshapiro proc_list_clear(); 228290792Sgshapiro sm_releasesignal(SIGCHLD); 228390792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 228490792Sgshapiro } 228590792Sgshapiro 228690792Sgshapiro /* child -- error messages to the transcript */ 228790792Sgshapiro QuickAbort = OnlyOneError = false; 228890792Sgshapiro runner_work(e, sequenceno, true, 228990792Sgshapiro maxrunners, njobs); 229090792Sgshapiro 229190792Sgshapiro /* This child is done */ 229290792Sgshapiro finis(true, true, ExitStat); 229390792Sgshapiro /* NOTREACHED */ 229490792Sgshapiro } 229538032Speter } 229690792Sgshapiro 229790792Sgshapiro sm_releasesignal(SIGCHLD); 229890792Sgshapiro 229990792Sgshapiro /* 230090792Sgshapiro ** Wait until all of the runners have completed before 230190792Sgshapiro ** seeing if there is another queue group in the 230290792Sgshapiro ** work group to process. 230390792Sgshapiro ** XXX Future enhancement: don't wait() for all children 230490792Sgshapiro ** here, just go ahead and make sure that overall the number 230590792Sgshapiro ** of children is not exceeded. 230690792Sgshapiro */ 230790792Sgshapiro 230890792Sgshapiro while (CurChildren > 0) 230938032Speter { 231090792Sgshapiro int status; 231190792Sgshapiro pid_t ret; 231238032Speter 231390792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 231490792Sgshapiro continue; 231590792Sgshapiro proc_list_drop(ret, status, NULL); 231638032Speter } 231790792Sgshapiro } 2318110560Sgshapiro else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags)) 231990792Sgshapiro { 232090792Sgshapiro /* 232190792Sgshapiro ** When current process will not fork children to do the work, 232290792Sgshapiro ** it will do the work itself. The 'skip' will be 1 since 232390792Sgshapiro ** there are no child runners to divide the work across. 232490792Sgshapiro */ 232590792Sgshapiro 232690792Sgshapiro runner_work(e, sequenceno, false, 1, njobs); 232790792Sgshapiro } 232890792Sgshapiro 232990792Sgshapiro /* free memory allocated by newenvelope() above */ 233090792Sgshapiro sm_rpool_free(rpool); 233190792Sgshapiro QueueEnvelope.e_rpool = NULL; 233290792Sgshapiro 233390792Sgshapiro /* Are there still more queues in the work group to process? */ 233490792Sgshapiro if (endgrp != WorkGrp[wgrp].wg_curqgrp) 233590792Sgshapiro { 233690792Sgshapiro rpool = sm_rpool_new_x(NULL); 233790792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 233890792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 233990792Sgshapiro goto domorework; 234090792Sgshapiro } 234190792Sgshapiro 234290792Sgshapiro /* No more queues in work group to process. Now check persistent. */ 2343110560Sgshapiro if (bitset(RWG_PERSISTENT, flags)) 234490792Sgshapiro { 234590792Sgshapiro sequenceno = 1; 2346244833Sgshapiro sm_setproctitle(true, NULL, "running queue: %s", 234790792Sgshapiro qid_printqueue(qgrp, qdir)); 234890792Sgshapiro 234990792Sgshapiro /* 235090792Sgshapiro ** close bogus maps, i.e., maps which caused a tempfail, 235190792Sgshapiro ** so we get fresh map connections on the next lookup. 235290792Sgshapiro ** closemaps() is also called when children are started. 235390792Sgshapiro */ 235490792Sgshapiro 235590792Sgshapiro closemaps(true); 235690792Sgshapiro 235790792Sgshapiro /* Close any cached connections. */ 235890792Sgshapiro mci_flush(true, NULL); 235990792Sgshapiro 236090792Sgshapiro /* Clean out expired related entries. */ 236190792Sgshapiro rmexpstab(); 236290792Sgshapiro 236390792Sgshapiro#if NAMED_BIND 2364132943Sgshapiro /* Update MX records for FallbackMX. */ 2365132943Sgshapiro if (FallbackMX != NULL) 2366132943Sgshapiro (void) getfallbackmxrr(FallbackMX); 2367363466Sgshapiro#endif 236890792Sgshapiro 236990792Sgshapiro#if USERDB 237090792Sgshapiro /* close UserDatabase */ 237190792Sgshapiro _udbx_close(); 2372363466Sgshapiro#endif 237390792Sgshapiro 237490792Sgshapiro#if SM_HEAP_CHECK 237590792Sgshapiro if (sm_debug_active(&SmHeapCheck, 2) 237690792Sgshapiro && access("memdump", F_OK) == 0 237790792Sgshapiro ) 237838032Speter { 237990792Sgshapiro SM_FILE_T *out; 238090792Sgshapiro 238190792Sgshapiro remove("memdump"); 238290792Sgshapiro out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, 238390792Sgshapiro "memdump.out", SM_IO_APPEND, NULL); 238490792Sgshapiro if (out != NULL) 238538032Speter { 238690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n"); 238790792Sgshapiro sm_heap_report(out, 238890792Sgshapiro sm_debug_level(&SmHeapCheck) - 1); 238990792Sgshapiro (void) sm_io_close(out, SM_TIME_DEFAULT); 239038032Speter } 239138032Speter } 239290792Sgshapiro#endif /* SM_HEAP_CHECK */ 239390792Sgshapiro 239490792Sgshapiro /* let me rest for a second to catch my breath */ 239590792Sgshapiro if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME) 239690792Sgshapiro sleep(MIN_SLEEP_TIME); 239790792Sgshapiro else if (WorkGrp[wgrp].wg_lowqintvl <= 0) 239890792Sgshapiro sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME); 239938032Speter else 240090792Sgshapiro sleep(WorkGrp[wgrp].wg_lowqintvl); 240138032Speter 240290792Sgshapiro /* 240390792Sgshapiro ** Get the LA outside the WorkQ loop if necessary. 240490792Sgshapiro ** In a persistent queue runner the code is repeated over 240590792Sgshapiro ** and over but gatherq() may ignore entries due to 240690792Sgshapiro ** shouldqueue() (do we really have to do this twice?). 240790792Sgshapiro ** Hence the queue runners would just idle around when once 240890792Sgshapiro ** CurrentLA caused all entries in a queue to be ignored. 240990792Sgshapiro */ 241064562Sgshapiro 2411120256Sgshapiro if (njobs == 0) 2412120256Sgshapiro SM_GET_LA(now); 241390792Sgshapiro rpool = sm_rpool_new_x(NULL); 241490792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 241590792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 241690792Sgshapiro goto domorework; 241738032Speter } 241838032Speter 241938032Speter /* exit without the usual cleanup */ 242038032Speter e->e_id = NULL; 2421110560Sgshapiro if (bitset(RWG_FORK, flags)) 242290792Sgshapiro finis(true, true, ExitStat); 242364562Sgshapiro /* NOTREACHED */ 242490792Sgshapiro return true; 242538032Speter} 242638032Speter 242738032Speter/* 242890792Sgshapiro** DOQUEUERUN -- do a queue run? 242990792Sgshapiro*/ 243090792Sgshapiro 243190792Sgshapirobool 243290792Sgshapirodoqueuerun() 243390792Sgshapiro{ 243494334Sgshapiro return DoQueueRun; 243590792Sgshapiro} 243690792Sgshapiro 243790792Sgshapiro/* 243894334Sgshapiro** RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done. 243977349Sgshapiro** 244077349Sgshapiro** Parameters: 244194334Sgshapiro** none. 244277349Sgshapiro** 244377349Sgshapiro** Returns: 244477349Sgshapiro** none. 244577349Sgshapiro** 244690792Sgshapiro** Side Effects: 244790792Sgshapiro** The invocation of this function via an alarm may interrupt 244890792Sgshapiro** a set of actions. Thus errno may be set in that context. 244990792Sgshapiro** We need to restore errno at the end of this function to ensure 245090792Sgshapiro** that any work done here that sets errno doesn't return a 245190792Sgshapiro** misleading/false errno value. Errno may be EINTR upon entry to 245290792Sgshapiro** this function because of non-restartable/continuable system 245390792Sgshapiro** API was active. Iff this is true we will override errno as 245490792Sgshapiro** a timeout (as a more accurate error message). 245590792Sgshapiro** 245677349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 245777349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 245877349Sgshapiro** DOING. 245938032Speter*/ 246038032Speter 246190792Sgshapirovoid 2462141858Sgshapirorunqueueevent(ignore) 2463141858Sgshapiro int ignore; 246438032Speter{ 246590792Sgshapiro int save_errno = errno; 246690792Sgshapiro 246790792Sgshapiro /* 246890792Sgshapiro ** Set the general bit that we want a queue run, 246990792Sgshapiro ** tested in doqueuerun() 247090792Sgshapiro */ 247190792Sgshapiro 247294334Sgshapiro DoQueueRun = true; 247394334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 247494334Sgshapiro if (tTd(69, 10)) 247594334Sgshapiro sm_syslog(LOG_INFO, NOQID, "rqe: done"); 2476363466Sgshapiro#endif 247790792Sgshapiro 247890792Sgshapiro errno = save_errno; 247990792Sgshapiro if (errno == EINTR) 248090792Sgshapiro errno = ETIMEDOUT; 248138032Speter} 248290792Sgshapiro/* 248390792Sgshapiro** GATHERQ -- gather messages from the message queue(s) the work queue. 248438032Speter** 248538032Speter** Parameters: 248690792Sgshapiro** qgrp -- the index of the queue group. 248790792Sgshapiro** qdir -- the index of the queue directory. 248838032Speter** doall -- if set, include everything in the queue (even 248938032Speter** the jobs that cannot be run because the load 249090792Sgshapiro** average is too high, or MaxQueueRun is reached). 249190792Sgshapiro** Otherwise, exclude those jobs. 249290792Sgshapiro** full -- (optional) to be set 'true' if WorkList is full 249390792Sgshapiro** more -- (optional) to be set 'true' if there are still more 249490792Sgshapiro** messages in this queue not added to WorkList 2495203004Sgshapiro** pnentries -- (optional) total nuber of entries in queue 249638032Speter** 249738032Speter** Returns: 249838032Speter** The number of request in the queue (not necessarily 249990792Sgshapiro** the number of requests in WorkList however). 250038032Speter** 250138032Speter** Side Effects: 250290792Sgshapiro** prepares available work into WorkList 250338032Speter*/ 250438032Speter 250590792Sgshapiro#define NEED_P 0001 /* 'P': priority */ 250690792Sgshapiro#define NEED_T 0002 /* 'T': time */ 250790792Sgshapiro#define NEED_R 0004 /* 'R': recipient */ 250890792Sgshapiro#define NEED_S 0010 /* 'S': sender */ 250990792Sgshapiro#define NEED_H 0020 /* host */ 2510132943Sgshapiro#define HAS_QUARANTINE 0040 /* has an unexpected 'q' line */ 2511132943Sgshapiro#define NEED_QUARANTINE 0100 /* 'q': reason */ 251238032Speter 251390792Sgshapirostatic WORK *WorkList = NULL; /* list of unsort work */ 251490792Sgshapirostatic int WorkListSize = 0; /* current max size of WorkList */ 251590792Sgshapirostatic int WorkListCount = 0; /* # of work items in WorkList */ 251638032Speter 251764562Sgshapirostatic int 2518203004Sgshapirogatherq(qgrp, qdir, doall, full, more, pnentries) 251990792Sgshapiro int qgrp; 252090792Sgshapiro int qdir; 252138032Speter bool doall; 252290792Sgshapiro bool *full; 252390792Sgshapiro bool *more; 2524203004Sgshapiro int *pnentries; 252538032Speter{ 252638032Speter register struct dirent *d; 252738032Speter register WORK *w; 252838032Speter register char *p; 252938032Speter DIR *f; 2530203004Sgshapiro int i, num_ent, wn, nentries; 253138032Speter QUEUE_CHAR *check; 253264562Sgshapiro char qd[MAXPATHLEN]; 253364562Sgshapiro char qf[MAXPATHLEN]; 253464562Sgshapiro 253590792Sgshapiro wn = WorkListCount - 1; 253690792Sgshapiro num_ent = 0; 2537203004Sgshapiro nentries = 0; 253890792Sgshapiro if (qdir == NOQDIR) 2539168515Sgshapiro (void) sm_strlcpy(qd, ".", sizeof(qd)); 254064562Sgshapiro else 2541168515Sgshapiro (void) sm_strlcpyn(qd, sizeof(qd), 2, 254290792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 254390792Sgshapiro (bitset(QP_SUBQF, 254490792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 254590792Sgshapiro ? "/qf" : "")); 254664562Sgshapiro 254738032Speter if (tTd(41, 1)) 254838032Speter { 2549363466Sgshapiro sm_dprintf("gatherq: %s\n", qd); 255038032Speter 255138032Speter check = QueueLimitId; 255238032Speter while (check != NULL) 255338032Speter { 255490792Sgshapiro sm_dprintf("\tQueueLimitId = %s%s\n", 255590792Sgshapiro check->queue_negate ? "!" : "", 255664562Sgshapiro check->queue_match); 255738032Speter check = check->queue_next; 255838032Speter } 255938032Speter 256038032Speter check = QueueLimitSender; 256138032Speter while (check != NULL) 256238032Speter { 256390792Sgshapiro sm_dprintf("\tQueueLimitSender = %s%s\n", 256490792Sgshapiro check->queue_negate ? "!" : "", 256564562Sgshapiro check->queue_match); 256638032Speter check = check->queue_next; 256738032Speter } 256838032Speter 256938032Speter check = QueueLimitRecipient; 257038032Speter while (check != NULL) 257138032Speter { 257290792Sgshapiro sm_dprintf("\tQueueLimitRecipient = %s%s\n", 257390792Sgshapiro check->queue_negate ? "!" : "", 257464562Sgshapiro check->queue_match); 257538032Speter check = check->queue_next; 257638032Speter } 257738032Speter 257890792Sgshapiro if (QueueMode == QM_QUARANTINE) 257990792Sgshapiro { 258090792Sgshapiro check = QueueLimitQuarantine; 258190792Sgshapiro while (check != NULL) 258290792Sgshapiro { 258390792Sgshapiro sm_dprintf("\tQueueLimitQuarantine = %s%s\n", 258490792Sgshapiro check->queue_negate ? "!" : "", 258590792Sgshapiro check->queue_match); 258690792Sgshapiro check = check->queue_next; 258790792Sgshapiro } 258890792Sgshapiro } 258938032Speter } 259038032Speter 259138032Speter /* open the queue directory */ 259264562Sgshapiro f = opendir(qd); 259338032Speter if (f == NULL) 259438032Speter { 259590792Sgshapiro syserr("gatherq: cannot open \"%s\"", 259690792Sgshapiro qid_printqueue(qgrp, qdir)); 259790792Sgshapiro if (full != NULL) 259890792Sgshapiro *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0; 259990792Sgshapiro if (more != NULL) 260090792Sgshapiro *more = false; 260164562Sgshapiro return 0; 260238032Speter } 260338032Speter 260438032Speter /* 260538032Speter ** Read the work directory. 260638032Speter */ 260738032Speter 260838032Speter while ((d = readdir(f)) != NULL) 260938032Speter { 261090792Sgshapiro SM_FILE_T *cf; 261138032Speter int qfver = 0; 261238032Speter char lbuf[MAXNAME + 1]; 261364562Sgshapiro struct stat sbuf; 261438032Speter 261538032Speter if (tTd(41, 50)) 261690792Sgshapiro sm_dprintf("gatherq: checking %s..", d->d_name); 261738032Speter 261838032Speter /* is this an interesting entry? */ 261990792Sgshapiro if (!(((QueueMode == QM_NORMAL && 262090792Sgshapiro d->d_name[0] == NORMQF_LETTER) || 262190792Sgshapiro (QueueMode == QM_QUARANTINE && 262290792Sgshapiro d->d_name[0] == QUARQF_LETTER) || 262390792Sgshapiro (QueueMode == QM_LOST && 262490792Sgshapiro d->d_name[0] == LOSEQF_LETTER)) && 262590792Sgshapiro d->d_name[1] == 'f')) 262690792Sgshapiro { 262790792Sgshapiro if (tTd(41, 50)) 262890792Sgshapiro sm_dprintf(" skipping\n"); 262938032Speter continue; 263090792Sgshapiro } 263190792Sgshapiro if (tTd(41, 50)) 263290792Sgshapiro sm_dprintf("\n"); 263338032Speter 263464562Sgshapiro if (strlen(d->d_name) >= MAXQFNAME) 263542575Speter { 263642575Speter if (Verbose) 263790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 263890792Sgshapiro "gatherq: %s too long, %d max characters\n", 263990792Sgshapiro d->d_name, MAXQFNAME); 264042575Speter if (LogLevel > 0) 264142575Speter sm_syslog(LOG_ALERT, NOQID, 264290792Sgshapiro "gatherq: %s too long, %d max characters", 264364562Sgshapiro d->d_name, MAXQFNAME); 264438032Speter continue; 264542575Speter } 264638032Speter 2647203004Sgshapiro ++nentries; 264838032Speter check = QueueLimitId; 264938032Speter while (check != NULL) 265038032Speter { 265194334Sgshapiro if (strcontainedin(false, check->queue_match, 265290792Sgshapiro d->d_name) != check->queue_negate) 265338032Speter break; 265438032Speter else 265538032Speter check = check->queue_next; 265638032Speter } 265738032Speter if (QueueLimitId != NULL && check == NULL) 265838032Speter continue; 265938032Speter 266064562Sgshapiro /* grow work list if necessary */ 266138032Speter if (++wn >= MaxQueueRun && MaxQueueRun > 0) 266238032Speter { 266338032Speter if (wn == MaxQueueRun && LogLevel > 0) 266464562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 266564562Sgshapiro "WorkList for %s maxed out at %d", 266690792Sgshapiro qid_printqueue(qgrp, qdir), 266764562Sgshapiro MaxQueueRun); 266890792Sgshapiro if (doall) 266990792Sgshapiro continue; /* just count entries */ 267090792Sgshapiro break; 267138032Speter } 267238032Speter if (wn >= WorkListSize) 267338032Speter { 267490792Sgshapiro grow_wlist(qgrp, qdir); 267538032Speter if (wn >= WorkListSize) 267638032Speter continue; 267738032Speter } 267890792Sgshapiro SM_ASSERT(wn >= 0); 267964562Sgshapiro w = &WorkList[wn]; 268038032Speter 2681168515Sgshapiro (void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", d->d_name); 268264562Sgshapiro if (stat(qf, &sbuf) < 0) 268364562Sgshapiro { 268464562Sgshapiro if (errno != ENOENT) 268564562Sgshapiro sm_syslog(LOG_INFO, NOQID, 268690792Sgshapiro "gatherq: can't stat %s/%s", 268790792Sgshapiro qid_printqueue(qgrp, qdir), 268890792Sgshapiro d->d_name); 268964562Sgshapiro wn--; 269064562Sgshapiro continue; 269164562Sgshapiro } 269264562Sgshapiro if (!bitset(S_IFREG, sbuf.st_mode)) 269364562Sgshapiro { 269464562Sgshapiro /* Yikes! Skip it or we will hang on open! */ 269590792Sgshapiro if (!((d->d_name[0] == DATAFL_LETTER || 269690792Sgshapiro d->d_name[0] == NORMQF_LETTER || 269790792Sgshapiro d->d_name[0] == QUARQF_LETTER || 269890792Sgshapiro d->d_name[0] == LOSEQF_LETTER || 269990792Sgshapiro d->d_name[0] == XSCRPT_LETTER) && 270090792Sgshapiro d->d_name[1] == 'f' && d->d_name[2] == '\0')) 270190792Sgshapiro syserr("gatherq: %s/%s is not a regular file", 270290792Sgshapiro qid_printqueue(qgrp, qdir), d->d_name); 270364562Sgshapiro wn--; 270464562Sgshapiro continue; 270564562Sgshapiro } 270664562Sgshapiro 270764562Sgshapiro /* avoid work if possible */ 270890792Sgshapiro if ((QueueSortOrder == QSO_BYFILENAME || 270990792Sgshapiro QueueSortOrder == QSO_BYMODTIME || 2710161389Sgshapiro QueueSortOrder == QSO_NONE || 271190792Sgshapiro QueueSortOrder == QSO_RANDOM) && 271290792Sgshapiro QueueLimitQuarantine == NULL && 271366494Sgshapiro QueueLimitSender == NULL && 271466494Sgshapiro QueueLimitRecipient == NULL) 271564562Sgshapiro { 271690792Sgshapiro w->w_qgrp = qgrp; 271790792Sgshapiro w->w_qdir = qdir; 271864562Sgshapiro w->w_name = newstr(d->d_name); 271964562Sgshapiro w->w_host = NULL; 272090792Sgshapiro w->w_lock = w->w_tooyoung = false; 272164562Sgshapiro w->w_pri = 0; 272264562Sgshapiro w->w_ctime = 0; 272390792Sgshapiro w->w_mtime = sbuf.st_mtime; 272490792Sgshapiro ++num_ent; 272564562Sgshapiro continue; 272664562Sgshapiro } 272764562Sgshapiro 272864562Sgshapiro /* open control file */ 2729120256Sgshapiro cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B, 273090792Sgshapiro NULL); 273190792Sgshapiro if (cf == NULL && OpMode != MD_PRINT) 273238032Speter { 273338032Speter /* this may be some random person sending hir msgs */ 273438032Speter if (tTd(41, 2)) 273590792Sgshapiro sm_dprintf("gatherq: cannot open %s: %s\n", 273690792Sgshapiro d->d_name, sm_errstring(errno)); 273738032Speter errno = 0; 273838032Speter wn--; 273938032Speter continue; 274038032Speter } 274190792Sgshapiro w->w_qgrp = qgrp; 274290792Sgshapiro w->w_qdir = qdir; 274338032Speter w->w_name = newstr(d->d_name); 274438032Speter w->w_host = NULL; 274590792Sgshapiro if (cf != NULL) 274690792Sgshapiro { 274790792Sgshapiro w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD, 274890792Sgshapiro NULL), 274990792Sgshapiro w->w_name, NULL, 275090792Sgshapiro LOCK_SH|LOCK_NB); 275190792Sgshapiro } 275290792Sgshapiro w->w_tooyoung = false; 275338032Speter 275438032Speter /* make sure jobs in creation don't clog queue */ 275538032Speter w->w_pri = 0x7fffffff; 275638032Speter w->w_ctime = 0; 275790792Sgshapiro w->w_mtime = sbuf.st_mtime; 275838032Speter 275938032Speter /* extract useful information */ 276090792Sgshapiro i = NEED_P|NEED_T; 276190792Sgshapiro if (QueueSortOrder == QSO_BYHOST 276290792Sgshapiro#if _FFR_RHS 276390792Sgshapiro || QueueSortOrder == QSO_BYSHUFFLE 2764363466Sgshapiro#endif 276590792Sgshapiro ) 276671345Sgshapiro { 276771345Sgshapiro /* need w_host set for host sort order */ 276871345Sgshapiro i |= NEED_H; 276971345Sgshapiro } 277038032Speter if (QueueLimitSender != NULL) 277138032Speter i |= NEED_S; 277264562Sgshapiro if (QueueLimitRecipient != NULL) 277338032Speter i |= NEED_R; 277490792Sgshapiro if (QueueLimitQuarantine != NULL) 277590792Sgshapiro i |= NEED_QUARANTINE; 277690792Sgshapiro while (cf != NULL && i != 0 && 277790792Sgshapiro sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf, 2778249729Sgshapiro sizeof(lbuf)) >= 0) 277938032Speter { 278038032Speter int c; 278138032Speter time_t age; 278238032Speter 278338032Speter p = strchr(lbuf, '\n'); 278438032Speter if (p != NULL) 278538032Speter *p = '\0'; 278638032Speter else 278738032Speter { 278838032Speter /* flush rest of overly long line */ 278990792Sgshapiro while ((c = sm_io_getc(cf, SM_TIME_DEFAULT)) 279090792Sgshapiro != SM_IO_EOF && c != '\n') 279138032Speter continue; 279238032Speter } 279338032Speter 279438032Speter switch (lbuf[0]) 279538032Speter { 279638032Speter case 'V': 279738032Speter qfver = atoi(&lbuf[1]); 279838032Speter break; 279938032Speter 280038032Speter case 'P': 280138032Speter w->w_pri = atol(&lbuf[1]); 280238032Speter i &= ~NEED_P; 280338032Speter break; 280438032Speter 280538032Speter case 'T': 280638032Speter w->w_ctime = atol(&lbuf[1]); 280738032Speter i &= ~NEED_T; 280838032Speter break; 280938032Speter 281090792Sgshapiro case 'q': 281190792Sgshapiro if (QueueMode != QM_QUARANTINE && 281290792Sgshapiro QueueMode != QM_LOST) 281390792Sgshapiro { 281490792Sgshapiro if (tTd(41, 49)) 281590792Sgshapiro sm_dprintf("%s not marked as quarantined but has a 'q' line\n", 281690792Sgshapiro w->w_name); 281790792Sgshapiro i |= HAS_QUARANTINE; 281890792Sgshapiro } 281990792Sgshapiro else if (QueueMode == QM_QUARANTINE) 282090792Sgshapiro { 282190792Sgshapiro if (QueueLimitQuarantine == NULL) 282290792Sgshapiro { 282390792Sgshapiro i &= ~NEED_QUARANTINE; 282490792Sgshapiro break; 282590792Sgshapiro } 282690792Sgshapiro p = &lbuf[1]; 282790792Sgshapiro check = QueueLimitQuarantine; 282890792Sgshapiro while (check != NULL) 282990792Sgshapiro { 283090792Sgshapiro if (strcontainedin(false, 283190792Sgshapiro check->queue_match, 283290792Sgshapiro p) != 283390792Sgshapiro check->queue_negate) 283490792Sgshapiro break; 283590792Sgshapiro else 283690792Sgshapiro check = check->queue_next; 283790792Sgshapiro } 283890792Sgshapiro if (check != NULL) 283990792Sgshapiro i &= ~NEED_QUARANTINE; 284090792Sgshapiro } 284190792Sgshapiro break; 284290792Sgshapiro 284338032Speter case 'R': 284438032Speter if (w->w_host == NULL && 284538032Speter (p = strrchr(&lbuf[1], '@')) != NULL) 284664562Sgshapiro { 284790792Sgshapiro#if _FFR_RHS 284890792Sgshapiro if (QueueSortOrder == QSO_BYSHUFFLE) 284990792Sgshapiro w->w_host = newstr(&p[1]); 285090792Sgshapiro else 2851363466Sgshapiro#endif 285290792Sgshapiro w->w_host = strrev(&p[1]); 285364562Sgshapiro makelower(w->w_host); 285471345Sgshapiro i &= ~NEED_H; 285564562Sgshapiro } 285638032Speter if (QueueLimitRecipient == NULL) 285738032Speter { 285838032Speter i &= ~NEED_R; 285938032Speter break; 286038032Speter } 286138032Speter if (qfver > 0) 286238032Speter { 286338032Speter p = strchr(&lbuf[1], ':'); 286438032Speter if (p == NULL) 286538032Speter p = &lbuf[1]; 2866120256Sgshapiro else 2867120256Sgshapiro ++p; /* skip over ':' */ 286838032Speter } 286938032Speter else 287038032Speter p = &lbuf[1]; 287138032Speter check = QueueLimitRecipient; 287238032Speter while (check != NULL) 287338032Speter { 287490792Sgshapiro if (strcontainedin(true, 287590792Sgshapiro check->queue_match, 287690792Sgshapiro p) != 287790792Sgshapiro check->queue_negate) 287838032Speter break; 287938032Speter else 288038032Speter check = check->queue_next; 288138032Speter } 288238032Speter if (check != NULL) 288338032Speter i &= ~NEED_R; 288438032Speter break; 288538032Speter 288638032Speter case 'S': 288764562Sgshapiro check = QueueLimitSender; 288864562Sgshapiro while (check != NULL) 288964562Sgshapiro { 289090792Sgshapiro if (strcontainedin(true, 289190792Sgshapiro check->queue_match, 289290792Sgshapiro &lbuf[1]) != 289390792Sgshapiro check->queue_negate) 289464562Sgshapiro break; 289564562Sgshapiro else 289664562Sgshapiro check = check->queue_next; 289764562Sgshapiro } 289864562Sgshapiro if (check != NULL) 289964562Sgshapiro i &= ~NEED_S; 290038032Speter break; 290138032Speter 290238032Speter case 'K': 2903203004Sgshapiro if (MaxQueueAge > 0) 2904203004Sgshapiro { 2905244833Sgshapiro time_t lasttry, delay; 2906203004Sgshapiro 2907203004Sgshapiro lasttry = (time_t) atol(&lbuf[1]); 2908203004Sgshapiro delay = MIN(lasttry - w->w_ctime, 2909203004Sgshapiro MaxQueueAge); 2910203004Sgshapiro age = curtime() - lasttry; 2911203004Sgshapiro if (age < delay) 2912203004Sgshapiro w->w_tooyoung = true; 2913203004Sgshapiro break; 2914203004Sgshapiro } 2915203004Sgshapiro 291638032Speter age = curtime() - (time_t) atol(&lbuf[1]); 291738032Speter if (age >= 0 && MinQueueAge > 0 && 291838032Speter age < MinQueueAge) 291990792Sgshapiro w->w_tooyoung = true; 292038032Speter break; 292138032Speter 292238032Speter case 'N': 292338032Speter if (atol(&lbuf[1]) == 0) 292490792Sgshapiro w->w_tooyoung = false; 292538032Speter break; 292638032Speter } 292738032Speter } 292890792Sgshapiro if (cf != NULL) 292990792Sgshapiro (void) sm_io_close(cf, SM_TIME_DEFAULT); 293038032Speter 2931157001Sgshapiro if ((!doall && (shouldqueue(w->w_pri, w->w_ctime) || 2932157001Sgshapiro w->w_tooyoung)) || 293390792Sgshapiro bitset(HAS_QUARANTINE, i) || 293490792Sgshapiro bitset(NEED_QUARANTINE, i) || 293538032Speter bitset(NEED_R|NEED_S, i)) 293638032Speter { 293738032Speter /* don't even bother sorting this job in */ 293838032Speter if (tTd(41, 49)) 293990792Sgshapiro sm_dprintf("skipping %s (%x)\n", w->w_name, i); 294090792Sgshapiro sm_free(w->w_name); /* XXX */ 294190792Sgshapiro if (w->w_host != NULL) 294290792Sgshapiro sm_free(w->w_host); /* XXX */ 294338032Speter wn--; 294438032Speter } 294590792Sgshapiro else 294690792Sgshapiro ++num_ent; 294738032Speter } 294838032Speter (void) closedir(f); 294938032Speter wn++; 295038032Speter 295190792Sgshapiro i = wn - WorkListCount; 295290792Sgshapiro WorkListCount += SM_MIN(num_ent, WorkListSize); 295390792Sgshapiro 295490792Sgshapiro if (more != NULL) 295590792Sgshapiro *more = WorkListCount < wn; 295690792Sgshapiro 295790792Sgshapiro if (full != NULL) 295890792Sgshapiro *full = (wn >= MaxQueueRun && MaxQueueRun > 0) || 295990792Sgshapiro (WorkList == NULL && wn > 0); 296090792Sgshapiro 2961203004Sgshapiro if (pnentries != NULL) 2962203004Sgshapiro *pnentries = nentries; 296390792Sgshapiro return i; 296490792Sgshapiro} 296590792Sgshapiro/* 296690792Sgshapiro** SORTQ -- sort the work list 296790792Sgshapiro** 296890792Sgshapiro** First the old WorkQ is cleared away. Then the WorkList is sorted 296990792Sgshapiro** for all items so that important (higher sorting value) items are not 2970261194Sgshapiro** truncated off. Then the most important items are moved from 297190792Sgshapiro** WorkList to WorkQ. The lower count of 'max' or MaxListCount items 297290792Sgshapiro** are moved. 297390792Sgshapiro** 297490792Sgshapiro** Parameters: 297590792Sgshapiro** max -- maximum number of items to be placed in WorkQ 297690792Sgshapiro** 297790792Sgshapiro** Returns: 297890792Sgshapiro** the number of items in WorkQ 297990792Sgshapiro** 298090792Sgshapiro** Side Effects: 298190792Sgshapiro** WorkQ gets released and filled with new work. WorkList 298290792Sgshapiro** gets released. Work items get sorted in order. 298390792Sgshapiro*/ 298490792Sgshapiro 298590792Sgshapirostatic int 298690792Sgshapirosortq(max) 298790792Sgshapiro int max; 298890792Sgshapiro{ 298990792Sgshapiro register int i; /* local counter */ 299090792Sgshapiro register WORK *w; /* tmp item pointer */ 299190792Sgshapiro int wc = WorkListCount; /* trim size for WorkQ */ 299290792Sgshapiro 299390792Sgshapiro if (WorkQ != NULL) 299490792Sgshapiro { 2995132943Sgshapiro WORK *nw; 2996132943Sgshapiro 299790792Sgshapiro /* Clear out old WorkQ. */ 2998132943Sgshapiro for (w = WorkQ; w != NULL; w = nw) 299990792Sgshapiro { 3000132943Sgshapiro nw = w->w_next; 300190792Sgshapiro sm_free(w->w_name); /* XXX */ 300290792Sgshapiro if (w->w_host != NULL) 300390792Sgshapiro sm_free(w->w_host); /* XXX */ 300490792Sgshapiro sm_free((char *) w); /* XXX */ 300590792Sgshapiro } 300690792Sgshapiro WorkQ = NULL; 300790792Sgshapiro } 300890792Sgshapiro 300990792Sgshapiro if (WorkList == NULL || wc <= 0) 301064562Sgshapiro return 0; 301138032Speter 301290792Sgshapiro /* 301390792Sgshapiro ** The sort now takes place using all of the items in WorkList. 301490792Sgshapiro ** The list gets trimmed to the most important items after the sort. 301590792Sgshapiro ** If the trim were to happen before the sort then one or more 301690792Sgshapiro ** important items might get truncated off -- not what we want. 301790792Sgshapiro */ 301890792Sgshapiro 301964562Sgshapiro if (QueueSortOrder == QSO_BYHOST) 302038032Speter { 302138032Speter /* 302238032Speter ** Sort the work directory for the first time, 302338032Speter ** based on host name, lock status, and priority. 302438032Speter */ 302538032Speter 3026168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf1); 302738032Speter 302838032Speter /* 302938032Speter ** If one message to host is locked, "lock" all messages 303038032Speter ** to that host. 303138032Speter */ 303238032Speter 303338032Speter i = 0; 303438032Speter while (i < wc) 303538032Speter { 303638032Speter if (!WorkList[i].w_lock) 303738032Speter { 303838032Speter i++; 303938032Speter continue; 304038032Speter } 304138032Speter w = &WorkList[i]; 304238032Speter while (++i < wc) 304338032Speter { 304438032Speter if (WorkList[i].w_host == NULL && 304538032Speter w->w_host == NULL) 304690792Sgshapiro WorkList[i].w_lock = true; 304738032Speter else if (WorkList[i].w_host != NULL && 304838032Speter w->w_host != NULL && 304990792Sgshapiro sm_strcasecmp(WorkList[i].w_host, 305090792Sgshapiro w->w_host) == 0) 305190792Sgshapiro WorkList[i].w_lock = true; 305238032Speter else 305338032Speter break; 305438032Speter } 305538032Speter } 305638032Speter 305738032Speter /* 305838032Speter ** Sort the work directory for the second time, 305938032Speter ** based on lock status, host name, and priority. 306038032Speter */ 306138032Speter 3062168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf2); 306338032Speter } 306464562Sgshapiro else if (QueueSortOrder == QSO_BYTIME) 306538032Speter { 306638032Speter /* 306738032Speter ** Simple sort based on submission time only. 306838032Speter */ 306938032Speter 3070168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf3); 307138032Speter } 307264562Sgshapiro else if (QueueSortOrder == QSO_BYFILENAME) 307364562Sgshapiro { 307464562Sgshapiro /* 307590792Sgshapiro ** Sort based on queue filename. 307664562Sgshapiro */ 307764562Sgshapiro 3078168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf4); 307964562Sgshapiro } 308090792Sgshapiro else if (QueueSortOrder == QSO_RANDOM) 308190792Sgshapiro { 308290792Sgshapiro /* 3083110560Sgshapiro ** Sort randomly. To avoid problems with an instable sort, 3084110560Sgshapiro ** use a random index into the queue file name to start 3085110560Sgshapiro ** comparison. 308690792Sgshapiro */ 308790792Sgshapiro 3088110560Sgshapiro randi = get_rand_mod(MAXQFNAME); 3089110560Sgshapiro if (randi < 2) 3090110560Sgshapiro randi = 3; 3091168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf5); 309290792Sgshapiro } 309390792Sgshapiro else if (QueueSortOrder == QSO_BYMODTIME) 309490792Sgshapiro { 309590792Sgshapiro /* 309690792Sgshapiro ** Simple sort based on modification time of queue file. 309790792Sgshapiro ** This puts the oldest items first. 309890792Sgshapiro */ 309990792Sgshapiro 3100168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf6); 310190792Sgshapiro } 310290792Sgshapiro#if _FFR_RHS 310390792Sgshapiro else if (QueueSortOrder == QSO_BYSHUFFLE) 310490792Sgshapiro { 310590792Sgshapiro /* 310690792Sgshapiro ** Simple sort based on shuffled host name. 310790792Sgshapiro */ 310890792Sgshapiro 310990792Sgshapiro init_shuffle_alphabet(); 3110168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf7); 311190792Sgshapiro } 311290792Sgshapiro#endif /* _FFR_RHS */ 3113132943Sgshapiro else if (QueueSortOrder == QSO_BYPRIORITY) 311438032Speter { 311538032Speter /* 311638032Speter ** Simple sort based on queue priority only. 311738032Speter */ 311838032Speter 3119168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf0); 312038032Speter } 3121132943Sgshapiro /* else don't sort at all */ 312238032Speter 3123157001Sgshapiro /* Check if the per queue group item limit will be exceeded */ 3124157001Sgshapiro if (wc > max && max > 0) 3125157001Sgshapiro wc = max; 3126157001Sgshapiro 312738032Speter /* 312838032Speter ** Convert the work list into canonical form. 312938032Speter ** Should be turning it into a list of envelopes here perhaps. 313090792Sgshapiro ** Only take the most important items up to the per queue group 313190792Sgshapiro ** maximum. 313238032Speter */ 313338032Speter 313438032Speter for (i = wc; --i >= 0; ) 313538032Speter { 3136168515Sgshapiro w = (WORK *) xalloc(sizeof(*w)); 313790792Sgshapiro w->w_qgrp = WorkList[i].w_qgrp; 313890792Sgshapiro w->w_qdir = WorkList[i].w_qdir; 313938032Speter w->w_name = WorkList[i].w_name; 314038032Speter w->w_host = WorkList[i].w_host; 314138032Speter w->w_lock = WorkList[i].w_lock; 314238032Speter w->w_tooyoung = WorkList[i].w_tooyoung; 314338032Speter w->w_pri = WorkList[i].w_pri; 314438032Speter w->w_ctime = WorkList[i].w_ctime; 314590792Sgshapiro w->w_mtime = WorkList[i].w_mtime; 314638032Speter w->w_next = WorkQ; 314738032Speter WorkQ = w; 314838032Speter } 3149132943Sgshapiro 3150132943Sgshapiro /* free the rest of the list */ 3151132943Sgshapiro for (i = WorkListCount; --i >= wc; ) 3152132943Sgshapiro { 3153132943Sgshapiro sm_free(WorkList[i].w_name); 3154132943Sgshapiro if (WorkList[i].w_host != NULL) 3155132943Sgshapiro sm_free(WorkList[i].w_host); 3156132943Sgshapiro } 3157132943Sgshapiro 315838032Speter if (WorkList != NULL) 315990792Sgshapiro sm_free(WorkList); /* XXX */ 316038032Speter WorkList = NULL; 316138032Speter WorkListSize = 0; 316290792Sgshapiro WorkListCount = 0; 316338032Speter 316438032Speter if (tTd(40, 1)) 316538032Speter { 316638032Speter for (w = WorkQ; w != NULL; w = w->w_next) 316764562Sgshapiro { 316864562Sgshapiro if (w->w_host != NULL) 316990792Sgshapiro sm_dprintf("%22s: pri=%ld %s\n", 317064562Sgshapiro w->w_name, w->w_pri, w->w_host); 317164562Sgshapiro else 317290792Sgshapiro sm_dprintf("%32s: pri=%ld\n", 317364562Sgshapiro w->w_name, w->w_pri); 317464562Sgshapiro } 317538032Speter } 317638032Speter 317790792Sgshapiro return wc; /* return number of WorkQ items */ 317838032Speter} 317990792Sgshapiro/* 318038032Speter** GROW_WLIST -- make the work list larger 318138032Speter** 318238032Speter** Parameters: 318390792Sgshapiro** qgrp -- the index for the queue group. 318490792Sgshapiro** qdir -- the index for the queue directory. 318538032Speter** 318638032Speter** Returns: 318738032Speter** none. 318838032Speter** 318938032Speter** Side Effects: 319038032Speter** Adds another QUEUESEGSIZE entries to WorkList if possible. 319138032Speter** It can fail if there isn't enough memory, so WorkListSize 319238032Speter** should be checked again upon return. 319338032Speter*/ 319438032Speter 319564562Sgshapirostatic void 319690792Sgshapirogrow_wlist(qgrp, qdir) 319790792Sgshapiro int qgrp; 319890792Sgshapiro int qdir; 319938032Speter{ 320038032Speter if (tTd(41, 1)) 320190792Sgshapiro sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); 320238032Speter if (WorkList == NULL) 320338032Speter { 3204168515Sgshapiro WorkList = (WORK *) xalloc((sizeof(*WorkList)) * 320564562Sgshapiro (QUEUESEGSIZE + 1)); 320638032Speter WorkListSize = QUEUESEGSIZE; 320738032Speter } 320838032Speter else 320938032Speter { 321038032Speter int newsize = WorkListSize + QUEUESEGSIZE; 321190792Sgshapiro WORK *newlist = (WORK *) sm_realloc((char *) WorkList, 321290792Sgshapiro (unsigned) sizeof(WORK) * (newsize + 1)); 321338032Speter 321438032Speter if (newlist != NULL) 321538032Speter { 321638032Speter WorkListSize = newsize; 321738032Speter WorkList = newlist; 321838032Speter if (LogLevel > 1) 321938032Speter { 322064562Sgshapiro sm_syslog(LOG_INFO, NOQID, 322164562Sgshapiro "grew WorkList for %s to %d", 322290792Sgshapiro qid_printqueue(qgrp, qdir), 322364562Sgshapiro WorkListSize); 322438032Speter } 322538032Speter } 322638032Speter else if (LogLevel > 0) 322738032Speter { 322838032Speter sm_syslog(LOG_ALERT, NOQID, 322964562Sgshapiro "FAILED to grow WorkList for %s to %d", 323090792Sgshapiro qid_printqueue(qgrp, qdir), newsize); 323138032Speter } 323238032Speter } 323338032Speter if (tTd(41, 1)) 323490792Sgshapiro sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); 323538032Speter} 323690792Sgshapiro/* 323738032Speter** WORKCMPF0 -- simple priority-only compare function. 323838032Speter** 323938032Speter** Parameters: 324038032Speter** a -- the first argument. 324138032Speter** b -- the second argument. 324238032Speter** 324338032Speter** Returns: 324438032Speter** -1 if a < b 324538032Speter** 0 if a == b 324638032Speter** +1 if a > b 324738032Speter** 324838032Speter*/ 324938032Speter 325064562Sgshapirostatic int 325138032Speterworkcmpf0(a, b) 325238032Speter register WORK *a; 325338032Speter register WORK *b; 325438032Speter{ 325538032Speter long pa = a->w_pri; 325638032Speter long pb = b->w_pri; 325738032Speter 325838032Speter if (pa == pb) 325938032Speter return 0; 326038032Speter else if (pa > pb) 326138032Speter return 1; 326238032Speter else 326338032Speter return -1; 326438032Speter} 326590792Sgshapiro/* 326638032Speter** WORKCMPF1 -- first compare function for ordering work based on host name. 326738032Speter** 326838032Speter** Sorts on host name, lock status, and priority in that order. 326938032Speter** 327038032Speter** Parameters: 327138032Speter** a -- the first argument. 327238032Speter** b -- the second argument. 327338032Speter** 327438032Speter** Returns: 327538032Speter** <0 if a < b 327638032Speter** 0 if a == b 327738032Speter** >0 if a > b 327838032Speter** 327938032Speter*/ 328038032Speter 328164562Sgshapirostatic int 328238032Speterworkcmpf1(a, b) 328338032Speter register WORK *a; 328438032Speter register WORK *b; 328538032Speter{ 328638032Speter int i; 328738032Speter 328838032Speter /* host name */ 328938032Speter if (a->w_host != NULL && b->w_host == NULL) 329038032Speter return 1; 329138032Speter else if (a->w_host == NULL && b->w_host != NULL) 329238032Speter return -1; 329338032Speter if (a->w_host != NULL && b->w_host != NULL && 329438032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 329538032Speter return i; 329638032Speter 329738032Speter /* lock status */ 329838032Speter if (a->w_lock != b->w_lock) 329938032Speter return b->w_lock - a->w_lock; 330038032Speter 330138032Speter /* job priority */ 330273188Sgshapiro return workcmpf0(a, b); 330338032Speter} 330490792Sgshapiro/* 330538032Speter** WORKCMPF2 -- second compare function for ordering work based on host name. 330638032Speter** 330738032Speter** Sorts on lock status, host name, and priority in that order. 330838032Speter** 330938032Speter** Parameters: 331038032Speter** a -- the first argument. 331138032Speter** b -- the second argument. 331238032Speter** 331338032Speter** Returns: 331438032Speter** <0 if a < b 331538032Speter** 0 if a == b 331638032Speter** >0 if a > b 331738032Speter** 331838032Speter*/ 331938032Speter 332064562Sgshapirostatic int 332138032Speterworkcmpf2(a, b) 332238032Speter register WORK *a; 332338032Speter register WORK *b; 332438032Speter{ 332538032Speter int i; 332638032Speter 332738032Speter /* lock status */ 332838032Speter if (a->w_lock != b->w_lock) 332938032Speter return a->w_lock - b->w_lock; 333038032Speter 333138032Speter /* host name */ 333238032Speter if (a->w_host != NULL && b->w_host == NULL) 333338032Speter return 1; 333438032Speter else if (a->w_host == NULL && b->w_host != NULL) 333538032Speter return -1; 333638032Speter if (a->w_host != NULL && b->w_host != NULL && 333738032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 333838032Speter return i; 333938032Speter 334038032Speter /* job priority */ 334173188Sgshapiro return workcmpf0(a, b); 334238032Speter} 334390792Sgshapiro/* 334438032Speter** WORKCMPF3 -- simple submission-time-only compare function. 334538032Speter** 334638032Speter** Parameters: 334738032Speter** a -- the first argument. 334838032Speter** b -- the second argument. 334938032Speter** 335038032Speter** Returns: 335138032Speter** -1 if a < b 335238032Speter** 0 if a == b 335338032Speter** +1 if a > b 335438032Speter** 335538032Speter*/ 335638032Speter 335764562Sgshapirostatic int 335838032Speterworkcmpf3(a, b) 335938032Speter register WORK *a; 336038032Speter register WORK *b; 336138032Speter{ 336238032Speter if (a->w_ctime > b->w_ctime) 336338032Speter return 1; 336438032Speter else if (a->w_ctime < b->w_ctime) 336538032Speter return -1; 336638032Speter else 336738032Speter return 0; 336838032Speter} 336990792Sgshapiro/* 337064562Sgshapiro** WORKCMPF4 -- compare based on file name 337164562Sgshapiro** 337264562Sgshapiro** Parameters: 337364562Sgshapiro** a -- the first argument. 337464562Sgshapiro** b -- the second argument. 337564562Sgshapiro** 337664562Sgshapiro** Returns: 337764562Sgshapiro** -1 if a < b 337864562Sgshapiro** 0 if a == b 337964562Sgshapiro** +1 if a > b 338064562Sgshapiro** 338164562Sgshapiro*/ 338264562Sgshapiro 338364562Sgshapirostatic int 338464562Sgshapiroworkcmpf4(a, b) 338564562Sgshapiro register WORK *a; 338664562Sgshapiro register WORK *b; 338764562Sgshapiro{ 338864562Sgshapiro return strcmp(a->w_name, b->w_name); 338964562Sgshapiro} 339090792Sgshapiro/* 339190792Sgshapiro** WORKCMPF5 -- compare based on assigned random number 339290792Sgshapiro** 339390792Sgshapiro** Parameters: 3394203004Sgshapiro** a -- the first argument. 3395203004Sgshapiro** b -- the second argument. 339690792Sgshapiro** 339790792Sgshapiro** Returns: 339890792Sgshapiro** randomly 1/-1 339990792Sgshapiro*/ 340090792Sgshapiro 340190792Sgshapiro/* ARGSUSED0 */ 340290792Sgshapirostatic int 340390792Sgshapiroworkcmpf5(a, b) 340490792Sgshapiro register WORK *a; 340590792Sgshapiro register WORK *b; 340690792Sgshapiro{ 3407110560Sgshapiro if (strlen(a->w_name) < randi || strlen(b->w_name) < randi) 3408110560Sgshapiro return -1; 3409110560Sgshapiro return a->w_name[randi] - b->w_name[randi]; 341090792Sgshapiro} 341190792Sgshapiro/* 341290792Sgshapiro** WORKCMPF6 -- simple modification-time-only compare function. 341390792Sgshapiro** 341490792Sgshapiro** Parameters: 341590792Sgshapiro** a -- the first argument. 341690792Sgshapiro** b -- the second argument. 341790792Sgshapiro** 341890792Sgshapiro** Returns: 341990792Sgshapiro** -1 if a < b 342090792Sgshapiro** 0 if a == b 342190792Sgshapiro** +1 if a > b 342290792Sgshapiro** 342390792Sgshapiro*/ 342490792Sgshapiro 342590792Sgshapirostatic int 342690792Sgshapiroworkcmpf6(a, b) 342790792Sgshapiro register WORK *a; 342890792Sgshapiro register WORK *b; 342990792Sgshapiro{ 343090792Sgshapiro if (a->w_mtime > b->w_mtime) 343190792Sgshapiro return 1; 343290792Sgshapiro else if (a->w_mtime < b->w_mtime) 343390792Sgshapiro return -1; 343490792Sgshapiro else 343590792Sgshapiro return 0; 343690792Sgshapiro} 343790792Sgshapiro#if _FFR_RHS 343890792Sgshapiro/* 343990792Sgshapiro** WORKCMPF7 -- compare function for ordering work based on shuffled host name. 344090792Sgshapiro** 344190792Sgshapiro** Sorts on lock status, host name, and priority in that order. 344290792Sgshapiro** 344390792Sgshapiro** Parameters: 344490792Sgshapiro** a -- the first argument. 344590792Sgshapiro** b -- the second argument. 344690792Sgshapiro** 344790792Sgshapiro** Returns: 344890792Sgshapiro** <0 if a < b 344990792Sgshapiro** 0 if a == b 345090792Sgshapiro** >0 if a > b 345190792Sgshapiro** 345290792Sgshapiro*/ 345390792Sgshapiro 345490792Sgshapirostatic int 345590792Sgshapiroworkcmpf7(a, b) 345690792Sgshapiro register WORK *a; 345790792Sgshapiro register WORK *b; 345890792Sgshapiro{ 345990792Sgshapiro int i; 346090792Sgshapiro 346190792Sgshapiro /* lock status */ 346290792Sgshapiro if (a->w_lock != b->w_lock) 346390792Sgshapiro return a->w_lock - b->w_lock; 346490792Sgshapiro 346590792Sgshapiro /* host name */ 346690792Sgshapiro if (a->w_host != NULL && b->w_host == NULL) 346790792Sgshapiro return 1; 346890792Sgshapiro else if (a->w_host == NULL && b->w_host != NULL) 346990792Sgshapiro return -1; 347090792Sgshapiro if (a->w_host != NULL && b->w_host != NULL && 347190792Sgshapiro (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0) 347290792Sgshapiro return i; 347390792Sgshapiro 347490792Sgshapiro /* job priority */ 347590792Sgshapiro return workcmpf0(a, b); 347690792Sgshapiro} 347790792Sgshapiro#endif /* _FFR_RHS */ 347890792Sgshapiro/* 347964562Sgshapiro** STRREV -- reverse string 348064562Sgshapiro** 348164562Sgshapiro** Returns a pointer to a new string that is the reverse of 348264562Sgshapiro** the string pointed to by fwd. The space for the new 348364562Sgshapiro** string is obtained using xalloc(). 348464562Sgshapiro** 348564562Sgshapiro** Parameters: 348664562Sgshapiro** fwd -- the string to reverse. 348764562Sgshapiro** 348864562Sgshapiro** Returns: 348964562Sgshapiro** the reversed string. 349064562Sgshapiro*/ 349164562Sgshapiro 349264562Sgshapirostatic char * 349364562Sgshapirostrrev(fwd) 349464562Sgshapiro char *fwd; 349564562Sgshapiro{ 349664562Sgshapiro char *rev = NULL; 349764562Sgshapiro int len, cnt; 349864562Sgshapiro 349964562Sgshapiro len = strlen(fwd); 350064562Sgshapiro rev = xalloc(len + 1); 350164562Sgshapiro for (cnt = 0; cnt < len; ++cnt) 350264562Sgshapiro rev[cnt] = fwd[len - cnt - 1]; 350364562Sgshapiro rev[len] = '\0'; 350464562Sgshapiro return rev; 350564562Sgshapiro} 350690792Sgshapiro 350790792Sgshapiro#if _FFR_RHS 350890792Sgshapiro 3509112810Sgshapiro# define NASCII 128 3510112810Sgshapiro# define NCHAR 256 351190792Sgshapiro 351290792Sgshapirostatic unsigned char ShuffledAlphabet[NCHAR]; 351390792Sgshapiro 351490792Sgshapirovoid 351590792Sgshapiroinit_shuffle_alphabet() 351690792Sgshapiro{ 351790792Sgshapiro static bool init = false; 351890792Sgshapiro int i; 351990792Sgshapiro 352090792Sgshapiro if (init) 352190792Sgshapiro return; 352290792Sgshapiro 352390792Sgshapiro /* fill the ShuffledAlphabet */ 3524157001Sgshapiro for (i = 0; i < NASCII; i++) 352590792Sgshapiro ShuffledAlphabet[i] = i; 352690792Sgshapiro 352790792Sgshapiro /* mix it */ 3528157001Sgshapiro for (i = 1; i < NASCII; i++) 352990792Sgshapiro { 3530157001Sgshapiro register int j = get_random() % NASCII; 353190792Sgshapiro register int tmp; 353290792Sgshapiro 353390792Sgshapiro tmp = ShuffledAlphabet[j]; 353490792Sgshapiro ShuffledAlphabet[j] = ShuffledAlphabet[i]; 353590792Sgshapiro ShuffledAlphabet[i] = tmp; 353690792Sgshapiro } 353790792Sgshapiro 353890792Sgshapiro /* make it case insensitive */ 353990792Sgshapiro for (i = 'A'; i <= 'Z'; i++) 354090792Sgshapiro ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A']; 354190792Sgshapiro 354290792Sgshapiro /* fill the upper part */ 3543157001Sgshapiro for (i = 0; i < NASCII; i++) 3544157001Sgshapiro ShuffledAlphabet[i + NASCII] = ShuffledAlphabet[i]; 354590792Sgshapiro init = true; 354690792Sgshapiro} 354790792Sgshapiro 354890792Sgshapirostatic int 354990792Sgshapirosm_strshufflecmp(a, b) 355090792Sgshapiro char *a; 355190792Sgshapiro char *b; 355290792Sgshapiro{ 355390792Sgshapiro const unsigned char *us1 = (const unsigned char *) a; 355490792Sgshapiro const unsigned char *us2 = (const unsigned char *) b; 355590792Sgshapiro 355690792Sgshapiro while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++]) 355790792Sgshapiro { 355890792Sgshapiro if (*us1++ == '\0') 355990792Sgshapiro return 0; 356090792Sgshapiro } 356190792Sgshapiro return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]); 356290792Sgshapiro} 356390792Sgshapiro#endif /* _FFR_RHS */ 356490792Sgshapiro 356590792Sgshapiro/* 356638032Speter** DOWORK -- do a work request. 356738032Speter** 356838032Speter** Parameters: 356990792Sgshapiro** qgrp -- the index of the queue group for the job. 357090792Sgshapiro** qdir -- the index of the queue directory for the job. 357138032Speter** id -- the ID of the job to run. 357238032Speter** forkflag -- if set, run this in background. 357338032Speter** requeueflag -- if set, reinstantiate the queue quickly. 357438032Speter** This is used when expanding aliases in the queue. 357538032Speter** If forkflag is also set, it doesn't wait for the 357638032Speter** child. 357738032Speter** e - the envelope in which to run it. 357838032Speter** 357938032Speter** Returns: 358038032Speter** process id of process that is running the queue job. 358138032Speter** 358238032Speter** Side Effects: 358338032Speter** The work request is satisfied if possible. 358438032Speter*/ 358538032Speter 358638032Speterpid_t 358790792Sgshapirodowork(qgrp, qdir, id, forkflag, requeueflag, e) 358890792Sgshapiro int qgrp; 358990792Sgshapiro int qdir; 359038032Speter char *id; 359138032Speter bool forkflag; 359238032Speter bool requeueflag; 359338032Speter register ENVELOPE *e; 359438032Speter{ 359538032Speter register pid_t pid; 359690792Sgshapiro SM_RPOOL_T *rpool; 359738032Speter 359838032Speter if (tTd(40, 1)) 359990792Sgshapiro sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id); 360038032Speter 360138032Speter /* 360238032Speter ** Fork for work. 360338032Speter */ 360438032Speter 360538032Speter if (forkflag) 360638032Speter { 360764562Sgshapiro /* 360864562Sgshapiro ** Since the delivery may happen in a child and the 360964562Sgshapiro ** parent does not wait, the parent may close the 361064562Sgshapiro ** maps thereby removing any shared memory used by 361164562Sgshapiro ** the map. Therefore, close the maps now so the 361264562Sgshapiro ** child will dynamically open them if necessary. 361364562Sgshapiro */ 361464562Sgshapiro 361590792Sgshapiro closemaps(false); 361664562Sgshapiro 361738032Speter pid = fork(); 361838032Speter if (pid < 0) 361938032Speter { 362038032Speter syserr("dowork: cannot fork"); 362138032Speter return 0; 362238032Speter } 362338032Speter else if (pid > 0) 362438032Speter { 362538032Speter /* parent -- clean out connection cache */ 362690792Sgshapiro mci_flush(false, NULL); 362738032Speter } 362838032Speter else 362938032Speter { 363090792Sgshapiro /* 363190792Sgshapiro ** Initialize exception stack and default exception 363290792Sgshapiro ** handler for child process. 363390792Sgshapiro */ 363490792Sgshapiro 363590792Sgshapiro /* Reset global flags */ 363690792Sgshapiro RestartRequest = NULL; 363790792Sgshapiro RestartWorkGroup = false; 363890792Sgshapiro ShutdownRequest = NULL; 363990792Sgshapiro PendingSignal = 0; 364090792Sgshapiro CurrentPid = getpid(); 364190792Sgshapiro sm_exc_newthread(fatal_error); 364290792Sgshapiro 364390792Sgshapiro /* 364490792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 364590792Sgshapiro */ 364690792Sgshapiro 364790792Sgshapiro if (OpMode == MD_SMTP || 364890792Sgshapiro OpMode == MD_DAEMON || 364990792Sgshapiro MaxQueueChildren > 0) 365090792Sgshapiro { 365190792Sgshapiro proc_list_clear(); 365290792Sgshapiro sm_releasesignal(SIGCHLD); 365390792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 365490792Sgshapiro } 365590792Sgshapiro 365638032Speter /* child -- error messages to the transcript */ 365790792Sgshapiro QuickAbort = OnlyOneError = false; 365838032Speter } 365938032Speter } 366038032Speter else 366138032Speter { 366238032Speter pid = 0; 366338032Speter } 366438032Speter 366538032Speter if (pid == 0) 366638032Speter { 366738032Speter /* 366838032Speter ** CHILD 366938032Speter ** Lock the control file to avoid duplicate deliveries. 367038032Speter ** Then run the file as though we had just read it. 367138032Speter ** We save an idea of the temporary name so we 367238032Speter ** can recover on interrupt. 367338032Speter */ 367438032Speter 367590792Sgshapiro if (forkflag) 367690792Sgshapiro { 367790792Sgshapiro /* Reset global flags */ 367890792Sgshapiro RestartRequest = NULL; 367990792Sgshapiro RestartWorkGroup = false; 368090792Sgshapiro ShutdownRequest = NULL; 368190792Sgshapiro PendingSignal = 0; 368290792Sgshapiro } 368377349Sgshapiro 368438032Speter /* set basic modes, etc. */ 368590792Sgshapiro sm_clear_events(); 368664562Sgshapiro clearstats(); 368790792Sgshapiro rpool = sm_rpool_new_x(NULL); 368890792Sgshapiro clearenvelope(e, false, rpool); 368938032Speter e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 369064562Sgshapiro set_delivery_mode(SM_DELIVER, e); 369138032Speter e->e_errormode = EM_MAIL; 369238032Speter e->e_id = id; 369390792Sgshapiro e->e_qgrp = qgrp; 369490792Sgshapiro e->e_qdir = qdir; 369590792Sgshapiro GrabTo = UseErrorsTo = false; 369638032Speter ExitStat = EX_OK; 369738032Speter if (forkflag) 369838032Speter { 369938032Speter disconnect(1, e); 370090792Sgshapiro set_op_mode(MD_QUEUERUN); 370138032Speter } 370290792Sgshapiro sm_setproctitle(true, e, "%s from queue", qid_printname(e)); 370338032Speter if (LogLevel > 76) 370490792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d", 370590792Sgshapiro (int) CurrentPid); 370638032Speter 370738032Speter /* don't use the headers from sendmail.cf... */ 370838032Speter e->e_header = NULL; 370938032Speter 371038032Speter /* read the queue control file -- return if locked */ 371190792Sgshapiro if (!readqf(e, false)) 371238032Speter { 371338032Speter if (tTd(40, 4) && e->e_id != NULL) 371490792Sgshapiro sm_dprintf("readqf(%s) failed\n", 371564562Sgshapiro qid_printname(e)); 371638032Speter e->e_id = NULL; 371738032Speter if (forkflag) 371890792Sgshapiro finis(false, true, EX_OK); 371938032Speter else 372090792Sgshapiro { 372190792Sgshapiro /* adding this frees 8 bytes */ 372290792Sgshapiro clearenvelope(e, false, rpool); 372390792Sgshapiro 372490792Sgshapiro /* adding this frees 12 bytes */ 372590792Sgshapiro sm_rpool_free(rpool); 372690792Sgshapiro e->e_rpool = NULL; 372738032Speter return 0; 372890792Sgshapiro } 372938032Speter } 373038032Speter 373138032Speter e->e_flags |= EF_INQUEUE; 373290792Sgshapiro eatheader(e, requeueflag, true); 373338032Speter 373438032Speter if (requeueflag) 373590792Sgshapiro queueup(e, false, false); 373638032Speter 373738032Speter /* do the delivery */ 373838032Speter sendall(e, SM_DELIVER); 373938032Speter 374038032Speter /* finish up and exit */ 374138032Speter if (forkflag) 374290792Sgshapiro finis(true, true, ExitStat); 374338032Speter else 374490792Sgshapiro { 3745203004Sgshapiro (void) dropenvelope(e, true, false); 374690792Sgshapiro sm_rpool_free(rpool); 374790792Sgshapiro e->e_rpool = NULL; 3748244833Sgshapiro e->e_message = NULL; 374990792Sgshapiro } 375038032Speter } 375138032Speter e->e_id = NULL; 375238032Speter return pid; 375338032Speter} 375490792Sgshapiro 375590792Sgshapiro/* 375690792Sgshapiro** DOWORKLIST -- process a list of envelopes as work requests 375790792Sgshapiro** 375890792Sgshapiro** Similar to dowork(), except that after forking, it processes an 375990792Sgshapiro** envelope and its siblings, treating each envelope as a work request. 376090792Sgshapiro** 376190792Sgshapiro** Parameters: 376290792Sgshapiro** el -- envelope to be processed including its siblings. 376390792Sgshapiro** forkflag -- if set, run this in background. 376490792Sgshapiro** requeueflag -- if set, reinstantiate the queue quickly. 376590792Sgshapiro** This is used when expanding aliases in the queue. 376690792Sgshapiro** If forkflag is also set, it doesn't wait for the 376790792Sgshapiro** child. 376890792Sgshapiro** 376990792Sgshapiro** Returns: 377090792Sgshapiro** process id of process that is running the queue job. 377190792Sgshapiro** 377290792Sgshapiro** Side Effects: 377390792Sgshapiro** The work request is satisfied if possible. 377490792Sgshapiro*/ 377590792Sgshapiro 377690792Sgshapiropid_t 377790792Sgshapirodoworklist(el, forkflag, requeueflag) 377890792Sgshapiro ENVELOPE *el; 377990792Sgshapiro bool forkflag; 378090792Sgshapiro bool requeueflag; 378190792Sgshapiro{ 378290792Sgshapiro register pid_t pid; 378390792Sgshapiro ENVELOPE *ei; 378490792Sgshapiro 378590792Sgshapiro if (tTd(40, 1)) 378690792Sgshapiro sm_dprintf("doworklist()\n"); 378790792Sgshapiro 378890792Sgshapiro /* 378990792Sgshapiro ** Fork for work. 379090792Sgshapiro */ 379190792Sgshapiro 379290792Sgshapiro if (forkflag) 379390792Sgshapiro { 379490792Sgshapiro /* 379590792Sgshapiro ** Since the delivery may happen in a child and the 379690792Sgshapiro ** parent does not wait, the parent may close the 379790792Sgshapiro ** maps thereby removing any shared memory used by 379890792Sgshapiro ** the map. Therefore, close the maps now so the 379990792Sgshapiro ** child will dynamically open them if necessary. 380090792Sgshapiro */ 380190792Sgshapiro 380290792Sgshapiro closemaps(false); 380390792Sgshapiro 380490792Sgshapiro pid = fork(); 380590792Sgshapiro if (pid < 0) 380690792Sgshapiro { 380790792Sgshapiro syserr("doworklist: cannot fork"); 380890792Sgshapiro return 0; 380990792Sgshapiro } 381090792Sgshapiro else if (pid > 0) 381190792Sgshapiro { 381290792Sgshapiro /* parent -- clean out connection cache */ 381390792Sgshapiro mci_flush(false, NULL); 381490792Sgshapiro } 381590792Sgshapiro else 381690792Sgshapiro { 381790792Sgshapiro /* 381890792Sgshapiro ** Initialize exception stack and default exception 381990792Sgshapiro ** handler for child process. 382090792Sgshapiro */ 382190792Sgshapiro 382290792Sgshapiro /* Reset global flags */ 382390792Sgshapiro RestartRequest = NULL; 382490792Sgshapiro RestartWorkGroup = false; 382590792Sgshapiro ShutdownRequest = NULL; 382690792Sgshapiro PendingSignal = 0; 382790792Sgshapiro CurrentPid = getpid(); 382890792Sgshapiro sm_exc_newthread(fatal_error); 382990792Sgshapiro 383090792Sgshapiro /* 383190792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 383290792Sgshapiro */ 383390792Sgshapiro 383490792Sgshapiro if (OpMode == MD_SMTP || 383590792Sgshapiro OpMode == MD_DAEMON || 383690792Sgshapiro MaxQueueChildren > 0) 383790792Sgshapiro { 383890792Sgshapiro proc_list_clear(); 383990792Sgshapiro sm_releasesignal(SIGCHLD); 384090792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 384190792Sgshapiro } 384290792Sgshapiro 384390792Sgshapiro /* child -- error messages to the transcript */ 384490792Sgshapiro QuickAbort = OnlyOneError = false; 384590792Sgshapiro } 384690792Sgshapiro } 384790792Sgshapiro else 384890792Sgshapiro { 384990792Sgshapiro pid = 0; 385090792Sgshapiro } 385190792Sgshapiro 385290792Sgshapiro if (pid != 0) 385390792Sgshapiro return pid; 385490792Sgshapiro 385590792Sgshapiro /* 385690792Sgshapiro ** IN CHILD 385790792Sgshapiro ** Lock the control file to avoid duplicate deliveries. 385890792Sgshapiro ** Then run the file as though we had just read it. 385990792Sgshapiro ** We save an idea of the temporary name so we 386090792Sgshapiro ** can recover on interrupt. 386190792Sgshapiro */ 386290792Sgshapiro 386390792Sgshapiro if (forkflag) 386490792Sgshapiro { 386590792Sgshapiro /* Reset global flags */ 386690792Sgshapiro RestartRequest = NULL; 386790792Sgshapiro RestartWorkGroup = false; 386890792Sgshapiro ShutdownRequest = NULL; 386990792Sgshapiro PendingSignal = 0; 387090792Sgshapiro } 387190792Sgshapiro 387290792Sgshapiro /* set basic modes, etc. */ 387390792Sgshapiro sm_clear_events(); 387490792Sgshapiro clearstats(); 387590792Sgshapiro GrabTo = UseErrorsTo = false; 387690792Sgshapiro ExitStat = EX_OK; 387790792Sgshapiro if (forkflag) 387890792Sgshapiro { 387990792Sgshapiro disconnect(1, el); 388090792Sgshapiro set_op_mode(MD_QUEUERUN); 388190792Sgshapiro } 388290792Sgshapiro if (LogLevel > 76) 388390792Sgshapiro sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d", 388490792Sgshapiro (int) CurrentPid); 388590792Sgshapiro 388690792Sgshapiro for (ei = el; ei != NULL; ei = ei->e_sibling) 388790792Sgshapiro { 388890792Sgshapiro ENVELOPE e; 388990792Sgshapiro SM_RPOOL_T *rpool; 389090792Sgshapiro 389190792Sgshapiro if (WILL_BE_QUEUED(ei->e_sendmode)) 389290792Sgshapiro continue; 389390792Sgshapiro else if (QueueMode != QM_QUARANTINE && 389490792Sgshapiro ei->e_quarmsg != NULL) 389590792Sgshapiro continue; 389690792Sgshapiro 389790792Sgshapiro rpool = sm_rpool_new_x(NULL); 389890792Sgshapiro clearenvelope(&e, true, rpool); 389990792Sgshapiro e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 390090792Sgshapiro set_delivery_mode(SM_DELIVER, &e); 390190792Sgshapiro e.e_errormode = EM_MAIL; 390290792Sgshapiro e.e_id = ei->e_id; 390390792Sgshapiro e.e_qgrp = ei->e_qgrp; 390490792Sgshapiro e.e_qdir = ei->e_qdir; 390590792Sgshapiro openxscript(&e); 390690792Sgshapiro sm_setproctitle(true, &e, "%s from queue", qid_printname(&e)); 390790792Sgshapiro 390890792Sgshapiro /* don't use the headers from sendmail.cf... */ 390990792Sgshapiro e.e_header = NULL; 391090792Sgshapiro CurEnv = &e; 391190792Sgshapiro 391290792Sgshapiro /* read the queue control file -- return if locked */ 391390792Sgshapiro if (readqf(&e, false)) 391490792Sgshapiro { 391590792Sgshapiro e.e_flags |= EF_INQUEUE; 391690792Sgshapiro eatheader(&e, requeueflag, true); 391790792Sgshapiro 391890792Sgshapiro if (requeueflag) 391990792Sgshapiro queueup(&e, false, false); 392090792Sgshapiro 392190792Sgshapiro /* do the delivery */ 392290792Sgshapiro sendall(&e, SM_DELIVER); 3923203004Sgshapiro (void) dropenvelope(&e, true, false); 392490792Sgshapiro } 392590792Sgshapiro else 392690792Sgshapiro { 392790792Sgshapiro if (tTd(40, 4) && e.e_id != NULL) 392890792Sgshapiro sm_dprintf("readqf(%s) failed\n", 392990792Sgshapiro qid_printname(&e)); 393090792Sgshapiro } 393190792Sgshapiro sm_rpool_free(rpool); 393290792Sgshapiro ei->e_id = NULL; 393390792Sgshapiro } 393490792Sgshapiro 393590792Sgshapiro /* restore CurEnv */ 393690792Sgshapiro CurEnv = el; 393790792Sgshapiro 393890792Sgshapiro /* finish up and exit */ 393990792Sgshapiro if (forkflag) 394090792Sgshapiro finis(true, true, ExitStat); 394190792Sgshapiro return 0; 394290792Sgshapiro} 394390792Sgshapiro/* 394438032Speter** READQF -- read queue file and set up environment. 394538032Speter** 394638032Speter** Parameters: 394738032Speter** e -- the envelope of the job to run. 394890792Sgshapiro** openonly -- only open the qf (returned as e_lockfp) 394938032Speter** 395038032Speter** Returns: 395190792Sgshapiro** true if it successfully read the queue file. 395290792Sgshapiro** false otherwise. 395338032Speter** 395438032Speter** Side Effects: 395538032Speter** The queue file is returned locked. 395638032Speter*/ 395738032Speter 395864562Sgshapirostatic bool 395990792Sgshapiroreadqf(e, openonly) 396038032Speter register ENVELOPE *e; 396190792Sgshapiro bool openonly; 396238032Speter{ 396390792Sgshapiro register SM_FILE_T *qfp; 396438032Speter ADDRESS *ctladdr; 396577349Sgshapiro struct stat st, stf; 396638032Speter char *bp; 396738032Speter int qfver = 0; 396838032Speter long hdrsize = 0; 396938032Speter register char *p; 397090792Sgshapiro char *frcpt = NULL; 397138032Speter char *orcpt = NULL; 397290792Sgshapiro bool nomore = false; 397390792Sgshapiro bool bogus = false; 397464562Sgshapiro MODE_T qsafe; 397594334Sgshapiro char *err; 397664562Sgshapiro char qf[MAXPATHLEN]; 397738032Speter char buf[MAXLINE]; 3978168515Sgshapiro int bufsize; 397938032Speter 398038032Speter /* 398138032Speter ** Read and process the file. 398238032Speter */ 398338032Speter 3984168515Sgshapiro SM_REQUIRE(e != NULL); 3985159609Sgshapiro bp = NULL; 3986168515Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof(qf)); 3987120256Sgshapiro qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR_B, NULL); 398838032Speter if (qfp == NULL) 398938032Speter { 399064562Sgshapiro int save_errno = errno; 399164562Sgshapiro 399238032Speter if (tTd(40, 8)) 399390792Sgshapiro sm_dprintf("readqf(%s): sm_io_open failure (%s)\n", 399490792Sgshapiro qf, sm_errstring(errno)); 399564562Sgshapiro errno = save_errno; 399664562Sgshapiro if (errno != ENOENT 399764562Sgshapiro ) 399838032Speter syserr("readqf: no control file %s", qf); 399990792Sgshapiro RELEASE_QUEUE; 400090792Sgshapiro return false; 400138032Speter } 400238032Speter 400390792Sgshapiro if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL, 400490792Sgshapiro LOCK_EX|LOCK_NB)) 400538032Speter { 400638032Speter /* being processed by another queuer */ 400764562Sgshapiro if (Verbose) 400890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 400990792Sgshapiro "%s: locked\n", e->e_id); 401064562Sgshapiro if (tTd(40, 8)) 401190792Sgshapiro sm_dprintf("%s: locked\n", e->e_id); 401238032Speter if (LogLevel > 19) 401338032Speter sm_syslog(LOG_DEBUG, e->e_id, "locked"); 401490792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 401590792Sgshapiro RELEASE_QUEUE; 401690792Sgshapiro return false; 401738032Speter } 401838032Speter 4019120256Sgshapiro RELEASE_QUEUE; 4020120256Sgshapiro 402138032Speter /* 402277349Sgshapiro ** Prevent locking race condition. 402377349Sgshapiro ** 402477349Sgshapiro ** Process A: readqf(): qfp = fopen(qffile) 402577349Sgshapiro ** Process B: queueup(): rename(tf, qf) 402677349Sgshapiro ** Process B: unlocks(tf) 402777349Sgshapiro ** Process A: lockfile(qf); 402877349Sgshapiro ** 402977349Sgshapiro ** Process A (us) has the old qf file (before the rename deleted 403077349Sgshapiro ** the directory entry) and will be delivering based on old data. 403177349Sgshapiro ** This can lead to multiple deliveries of the same recipients. 403277349Sgshapiro ** 403377349Sgshapiro ** Catch this by checking if the underlying qf file has changed 403477349Sgshapiro ** *after* acquiring our lock and if so, act as though the file 403577349Sgshapiro ** was still locked (i.e., just return like the lockfile() case 403677349Sgshapiro ** above. 403738032Speter */ 403838032Speter 403977349Sgshapiro if (stat(qf, &stf) < 0 || 404090792Sgshapiro fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0) 404138032Speter { 404238032Speter /* must have been being processed by someone else */ 404338032Speter if (tTd(40, 8)) 404490792Sgshapiro sm_dprintf("readqf(%s): [f]stat failure (%s)\n", 404590792Sgshapiro qf, sm_errstring(errno)); 404690792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 404790792Sgshapiro return false; 404838032Speter } 404938032Speter 405077349Sgshapiro if (st.st_nlink != stf.st_nlink || 405177349Sgshapiro st.st_dev != stf.st_dev || 405290792Sgshapiro ST_INODE(st) != ST_INODE(stf) || 405390792Sgshapiro#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 405477349Sgshapiro st.st_gen != stf.st_gen || 4055363466Sgshapiro#endif 405677349Sgshapiro st.st_uid != stf.st_uid || 405777349Sgshapiro st.st_gid != stf.st_gid || 405877349Sgshapiro st.st_size != stf.st_size) 405977349Sgshapiro { 406077349Sgshapiro /* changed after opened */ 406177349Sgshapiro if (Verbose) 406290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 406390792Sgshapiro "%s: changed\n", e->e_id); 406477349Sgshapiro if (tTd(40, 8)) 406590792Sgshapiro sm_dprintf("%s: changed\n", e->e_id); 406677349Sgshapiro if (LogLevel > 19) 406777349Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "changed"); 406890792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 406990792Sgshapiro return false; 407077349Sgshapiro } 407177349Sgshapiro 407277349Sgshapiro /* 407377349Sgshapiro ** Check the queue file for plausibility to avoid attacks. 407477349Sgshapiro */ 407577349Sgshapiro 407664562Sgshapiro qsafe = S_IWOTH|S_IWGRP; 407764562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 407864562Sgshapiro qsafe &= ~S_IWGRP; 407964562Sgshapiro 408090792Sgshapiro bogus = st.st_uid != geteuid() && 408190792Sgshapiro st.st_uid != TrustedUid && 408290792Sgshapiro geteuid() != RealUid; 408390792Sgshapiro 408490792Sgshapiro /* 408590792Sgshapiro ** If this qf file results from a set-group-ID binary, then 408690792Sgshapiro ** we check whether the directory is group-writable, 408790792Sgshapiro ** the queue file mode contains the group-writable bit, and 408890792Sgshapiro ** the groups are the same. 408990792Sgshapiro ** Notice: this requires that the set-group-ID binary is used to 409090792Sgshapiro ** run the queue! 409190792Sgshapiro */ 409290792Sgshapiro 409390792Sgshapiro if (bogus && st.st_gid == getegid() && UseMSP) 409438032Speter { 409590792Sgshapiro char delim; 409690792Sgshapiro struct stat dst; 409790792Sgshapiro 409890792Sgshapiro bp = SM_LAST_DIR_DELIM(qf); 409990792Sgshapiro if (bp == NULL) 410090792Sgshapiro delim = '\0'; 410190792Sgshapiro else 410290792Sgshapiro { 410390792Sgshapiro delim = *bp; 410490792Sgshapiro *bp = '\0'; 410590792Sgshapiro } 410690792Sgshapiro if (stat(delim == '\0' ? "." : qf, &dst) < 0) 410790792Sgshapiro syserr("readqf: cannot stat directory %s", 410890792Sgshapiro delim == '\0' ? "." : qf); 410990792Sgshapiro else 411090792Sgshapiro { 411190792Sgshapiro bogus = !(bitset(S_IWGRP, QueueFileMode) && 411290792Sgshapiro bitset(S_IWGRP, dst.st_mode) && 411390792Sgshapiro dst.st_gid == st.st_gid); 411490792Sgshapiro } 411590792Sgshapiro if (delim != '\0') 411690792Sgshapiro *bp = delim; 4117159609Sgshapiro bp = NULL; 411890792Sgshapiro } 411990792Sgshapiro if (!bogus) 412090792Sgshapiro bogus = bitset(qsafe, st.st_mode); 412190792Sgshapiro if (bogus) 412290792Sgshapiro { 412338032Speter if (LogLevel > 0) 412438032Speter { 412538032Speter sm_syslog(LOG_ALERT, e->e_id, 4126285229Sgshapiro "bogus queue file, uid=%ld, gid=%ld, mode=%o", 4127285229Sgshapiro (long) st.st_uid, (long) st.st_gid, 4128285229Sgshapiro (unsigned int) st.st_mode); 412938032Speter } 413038032Speter if (tTd(40, 8)) 413190792Sgshapiro sm_dprintf("readqf(%s): bogus file\n", qf); 413290792Sgshapiro e->e_flags |= EF_INQUEUE; 413390792Sgshapiro if (!openonly) 413490792Sgshapiro loseqfile(e, "bogus file uid/gid in mqueue"); 413590792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 413690792Sgshapiro return false; 413738032Speter } 413838032Speter 413938032Speter if (st.st_size == 0) 414038032Speter { 414138032Speter /* must be a bogus file -- if also old, just remove it */ 414290792Sgshapiro if (!openonly && st.st_ctime + 10 * 60 < curtime()) 414338032Speter { 414490792Sgshapiro (void) xunlink(queuename(e, DATAFL_LETTER)); 414590792Sgshapiro (void) xunlink(queuename(e, ANYQFL_LETTER)); 414638032Speter } 414790792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 414890792Sgshapiro return false; 414938032Speter } 415038032Speter 415138032Speter if (st.st_nlink == 0) 415238032Speter { 415338032Speter /* 415438032Speter ** Race condition -- we got a file just as it was being 415538032Speter ** unlinked. Just assume it is zero length. 415638032Speter */ 415738032Speter 415890792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 415990792Sgshapiro return false; 416038032Speter } 416138032Speter 416290792Sgshapiro#if _FFR_TRUSTED_QF 416390792Sgshapiro /* 416490792Sgshapiro ** If we don't own the file mark it as unsafe. 416590792Sgshapiro ** However, allow TrustedUser to own it as well 416690792Sgshapiro ** in case TrustedUser manipulates the queue. 416790792Sgshapiro */ 416890792Sgshapiro 416990792Sgshapiro if (st.st_uid != geteuid() && st.st_uid != TrustedUid) 417090792Sgshapiro e->e_flags |= EF_UNSAFE; 417190792Sgshapiro#else /* _FFR_TRUSTED_QF */ 417290792Sgshapiro /* If we don't own the file mark it as unsafe */ 417390792Sgshapiro if (st.st_uid != geteuid()) 417490792Sgshapiro e->e_flags |= EF_UNSAFE; 417590792Sgshapiro#endif /* _FFR_TRUSTED_QF */ 417690792Sgshapiro 417738032Speter /* good file -- save this lock */ 417838032Speter e->e_lockfp = qfp; 417938032Speter 418090792Sgshapiro /* Just wanted the open file */ 418190792Sgshapiro if (openonly) 418290792Sgshapiro return true; 418390792Sgshapiro 418438032Speter /* do basic system initialization */ 418538032Speter initsys(e); 418690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 418738032Speter 418838032Speter LineNumber = 0; 418938032Speter e->e_flags |= EF_GLOBALERRS; 419090792Sgshapiro set_op_mode(MD_QUEUERUN); 419138032Speter ctladdr = NULL; 419290792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 419390792Sgshapiro e->e_dfqgrp = e->e_qgrp; 419490792Sgshapiro e->e_dfqdir = e->e_qdir; 419590792Sgshapiro#if _FFR_QUEUE_MACRO 419690792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{queue}"), 419790792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 4198363466Sgshapiro#endif 419938032Speter e->e_dfino = -1; 420038032Speter e->e_msgsize = -1; 4201168515Sgshapiro while (bufsize = sizeof(buf), 4202168515Sgshapiro (bp = fgetfolded(buf, &bufsize, qfp)) != NULL) 420338032Speter { 420490792Sgshapiro unsigned long qflags; 420538032Speter ADDRESS *q; 420690792Sgshapiro int r; 420771345Sgshapiro time_t now; 420838032Speter auto char *ep; 420938032Speter 421038032Speter if (tTd(40, 4)) 421190792Sgshapiro sm_dprintf("+++++ %s\n", bp); 421238032Speter if (nomore) 421338032Speter { 421438032Speter /* hack attack */ 421590792Sgshapiro hackattack: 421690792Sgshapiro syserr("SECURITY ALERT: extra or bogus data in queue file: %s", 421790792Sgshapiro bp); 421894334Sgshapiro err = "bogus queue line"; 421994334Sgshapiro goto fail; 422038032Speter } 422138032Speter switch (bp[0]) 422238032Speter { 422390792Sgshapiro case 'A': /* AUTH= parameter */ 422490792Sgshapiro if (!xtextok(&bp[1])) 422590792Sgshapiro goto hackattack; 422690792Sgshapiro e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 422790792Sgshapiro break; 422838032Speter 422990792Sgshapiro case 'B': /* body type */ 423090792Sgshapiro r = check_bodytype(&bp[1]); 423190792Sgshapiro if (!BODYTYPE_VALID(r)) 423290792Sgshapiro goto hackattack; 423390792Sgshapiro e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 423490792Sgshapiro break; 423590792Sgshapiro 423638032Speter case 'C': /* specify controlling user */ 423790792Sgshapiro ctladdr = setctluser(&bp[1], qfver, e); 423838032Speter break; 423938032Speter 424090792Sgshapiro case 'D': /* data file name */ 424190792Sgshapiro /* obsolete -- ignore */ 424238032Speter break; 424338032Speter 424490792Sgshapiro case 'd': /* data file directory name */ 424538032Speter { 424690792Sgshapiro int qgrp, qdir; 424790792Sgshapiro 424890792Sgshapiro#if _FFR_MSP_PARANOIA 424990792Sgshapiro /* forbid queue groups in MSP? */ 425090792Sgshapiro if (UseMSP) 425190792Sgshapiro goto hackattack; 4252363466Sgshapiro#endif 425390792Sgshapiro for (qgrp = 0; 425490792Sgshapiro qgrp < NumQueue && Queue[qgrp] != NULL; 425590792Sgshapiro ++qgrp) 425638032Speter { 425790792Sgshapiro for (qdir = 0; 425890792Sgshapiro qdir < Queue[qgrp]->qg_numqueues; 425990792Sgshapiro ++qdir) 426038032Speter { 426190792Sgshapiro if (strcmp(&bp[1], 426290792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name) 426390792Sgshapiro == 0) 426490792Sgshapiro { 426590792Sgshapiro e->e_dfqgrp = qgrp; 426690792Sgshapiro e->e_dfqdir = qdir; 426790792Sgshapiro goto done; 426890792Sgshapiro } 426938032Speter } 427038032Speter } 427194334Sgshapiro err = "bogus queue file directory"; 427294334Sgshapiro goto fail; 427390792Sgshapiro done: 427490792Sgshapiro break; 427538032Speter } 427638032Speter 427738032Speter case 'E': /* specify error recipient */ 427838032Speter /* no longer used */ 427938032Speter break; 428038032Speter 428190792Sgshapiro case 'F': /* flag bits */ 428290792Sgshapiro if (strncmp(bp, "From ", 5) == 0) 428390792Sgshapiro { 428490792Sgshapiro /* we are being spoofed! */ 428590792Sgshapiro syserr("SECURITY ALERT: bogus qf line %s", bp); 428694334Sgshapiro err = "bogus queue line"; 428794334Sgshapiro goto fail; 428890792Sgshapiro } 428990792Sgshapiro for (p = &bp[1]; *p != '\0'; p++) 429090792Sgshapiro { 429190792Sgshapiro switch (*p) 429290792Sgshapiro { 429390792Sgshapiro case '8': /* has 8 bit data */ 429490792Sgshapiro e->e_flags |= EF_HAS8BIT; 429590792Sgshapiro break; 429638032Speter 429790792Sgshapiro case 'b': /* delete Bcc: header */ 429890792Sgshapiro e->e_flags |= EF_DELETE_BCC; 429990792Sgshapiro break; 430038032Speter 430190792Sgshapiro case 'd': /* envelope has DSN RET= */ 430290792Sgshapiro e->e_flags |= EF_RET_PARAM; 430390792Sgshapiro break; 430438032Speter 430590792Sgshapiro case 'n': /* don't return body */ 430690792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 430790792Sgshapiro break; 430890792Sgshapiro 430990792Sgshapiro case 'r': /* response */ 431090792Sgshapiro e->e_flags |= EF_RESPONSE; 431190792Sgshapiro break; 431290792Sgshapiro 431390792Sgshapiro case 's': /* split */ 431490792Sgshapiro e->e_flags |= EF_SPLIT; 431590792Sgshapiro break; 431690792Sgshapiro 431790792Sgshapiro case 'w': /* warning sent */ 431890792Sgshapiro e->e_flags |= EF_WARNING; 431990792Sgshapiro break; 4320363466Sgshapiro 4321363466Sgshapiro#if _FFR_EAI 4322363466Sgshapiro case 'e': /* message requires EAI */ 4323363466Sgshapiro e->e_smtputf8 = true; 4324363466Sgshapiro break; 4325363466Sgshapiro#endif /* _FFR_EAI */ 432690792Sgshapiro } 432790792Sgshapiro } 432838032Speter break; 432938032Speter 433090792Sgshapiro case 'q': /* quarantine reason */ 433190792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 433290792Sgshapiro macdefine(&e->e_macro, A_PERM, 433390792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 433438032Speter break; 433538032Speter 433690792Sgshapiro case 'H': /* header */ 433794334Sgshapiro 433894334Sgshapiro /* 433994334Sgshapiro ** count size before chompheader() destroys the line. 434094334Sgshapiro ** this isn't accurate due to macro expansion, but 4341120256Sgshapiro ** better than before. "-3" to skip H?? at least. 434294334Sgshapiro */ 434394334Sgshapiro 4344120256Sgshapiro hdrsize += strlen(bp) - 3; 434590792Sgshapiro (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); 434638032Speter break; 434738032Speter 434838032Speter case 'I': /* data file's inode number */ 434938032Speter /* regenerated below */ 435038032Speter break; 435138032Speter 435280785Sgshapiro case 'K': /* time of last delivery attempt */ 435338032Speter e->e_dtime = atol(&buf[1]); 435438032Speter break; 435538032Speter 435690792Sgshapiro case 'L': /* Solaris Content-Length: */ 435790792Sgshapiro case 'M': /* message */ 435890792Sgshapiro /* ignore this; we want a new message next time */ 435964562Sgshapiro break; 436064562Sgshapiro 436138032Speter case 'N': /* number of delivery attempts */ 436238032Speter e->e_ntries = atoi(&buf[1]); 436338032Speter 436438032Speter /* if this has been tried recently, let it be */ 436571345Sgshapiro now = curtime(); 436671345Sgshapiro if (e->e_ntries > 0 && e->e_dtime <= now && 4367132943Sgshapiro now < e->e_dtime + MinQueueAge) 436838032Speter { 436964562Sgshapiro char *howlong; 437038032Speter 437190792Sgshapiro howlong = pintvl(now - e->e_dtime, true); 437264562Sgshapiro if (Verbose) 437390792Sgshapiro (void) sm_io_fprintf(smioout, 437490792Sgshapiro SM_TIME_DEFAULT, 437590792Sgshapiro "%s: too young (%s)\n", 437690792Sgshapiro e->e_id, howlong); 437764562Sgshapiro if (tTd(40, 8)) 437890792Sgshapiro sm_dprintf("%s: too young (%s)\n", 437938032Speter e->e_id, howlong); 438038032Speter if (LogLevel > 19) 438138032Speter sm_syslog(LOG_DEBUG, e->e_id, 438264562Sgshapiro "too young (%s)", 438364562Sgshapiro howlong); 438438032Speter e->e_id = NULL; 438538032Speter unlockqueue(e); 4386168515Sgshapiro if (bp != buf) 4387168515Sgshapiro sm_free(bp); 438890792Sgshapiro return false; 438938032Speter } 439090792Sgshapiro macdefine(&e->e_macro, A_TEMP, 439190792Sgshapiro macid("{ntries}"), &buf[1]); 439264562Sgshapiro 439390792Sgshapiro#if NAMED_BIND 439464562Sgshapiro /* adjust BIND parameters immediately */ 439564562Sgshapiro if (e->e_ntries == 0) 439664562Sgshapiro { 439764562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 439864562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 439964562Sgshapiro } 440064562Sgshapiro else 440164562Sgshapiro { 440264562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_NORMAL]; 440364562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL]; 440464562Sgshapiro } 440590792Sgshapiro#endif /* NAMED_BIND */ 440638032Speter break; 440738032Speter 440838032Speter case 'P': /* message priority */ 440938032Speter e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 441038032Speter break; 441138032Speter 441290792Sgshapiro case 'Q': /* original recipient */ 441390792Sgshapiro orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 441490792Sgshapiro break; 441590792Sgshapiro 441698841Sgshapiro case 'r': /* final recipient */ 441790792Sgshapiro frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 441890792Sgshapiro break; 441990792Sgshapiro 442090792Sgshapiro case 'R': /* specify recipient */ 442190792Sgshapiro p = bp; 442290792Sgshapiro qflags = 0; 442390792Sgshapiro if (qfver >= 1) 442438032Speter { 442590792Sgshapiro /* get flag bits */ 442690792Sgshapiro while (*++p != '\0' && *p != ':') 442738032Speter { 442890792Sgshapiro switch (*p) 442990792Sgshapiro { 443090792Sgshapiro case 'N': 443190792Sgshapiro qflags |= QHASNOTIFY; 443290792Sgshapiro break; 443338032Speter 443490792Sgshapiro case 'S': 443590792Sgshapiro qflags |= QPINGONSUCCESS; 443690792Sgshapiro break; 443738032Speter 443890792Sgshapiro case 'F': 443990792Sgshapiro qflags |= QPINGONFAILURE; 444090792Sgshapiro break; 444138032Speter 444290792Sgshapiro case 'D': 444390792Sgshapiro qflags |= QPINGONDELAY; 444490792Sgshapiro break; 444538032Speter 444690792Sgshapiro case 'P': 444790792Sgshapiro qflags |= QPRIMARY; 444890792Sgshapiro break; 444938032Speter 445090792Sgshapiro case 'A': 445190792Sgshapiro if (ctladdr != NULL) 445290792Sgshapiro ctladdr->q_flags |= QALIAS; 445390792Sgshapiro break; 445490792Sgshapiro 4455285229Sgshapiro case 'B': 4456285229Sgshapiro qflags |= QINTBCC; 4457285229Sgshapiro break; 4458285229Sgshapiro 4459285229Sgshapiro case QDYNMAILFLG: 4460285229Sgshapiro qflags |= QDYNMAILER; 4461285229Sgshapiro break; 4462285229Sgshapiro 446390792Sgshapiro default: /* ignore or complain? */ 446490792Sgshapiro break; 446590792Sgshapiro } 446638032Speter } 446738032Speter } 446890792Sgshapiro else 446990792Sgshapiro qflags |= QPRIMARY; 4470120256Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), 4471285229Sgshapiro ((qflags & QINTBCC) != 0) ? "e b" : "e r"); 4472120256Sgshapiro if (*p != '\0') 4473120256Sgshapiro q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', 4474120256Sgshapiro NULL, e, true); 4475120256Sgshapiro else 4476120256Sgshapiro q = NULL; 447790792Sgshapiro if (q != NULL) 447890792Sgshapiro { 447994334Sgshapiro /* make sure we keep the current qgrp */ 448094334Sgshapiro if (ISVALIDQGRP(e->e_qgrp)) 448194334Sgshapiro q->q_qgrp = e->e_qgrp; 448290792Sgshapiro q->q_alias = ctladdr; 448390792Sgshapiro if (qfver >= 1) 448490792Sgshapiro q->q_flags &= ~Q_PINGFLAGS; 448590792Sgshapiro q->q_flags |= qflags; 448690792Sgshapiro q->q_finalrcpt = frcpt; 448790792Sgshapiro q->q_orcpt = orcpt; 4488285229Sgshapiro#if _FFR_RCPTFLAGS 4489285229Sgshapiro if (bitset(QDYNMAILER, qflags)) 4490285229Sgshapiro newmodmailer(q, QDYNMAILFLG); 4491285229Sgshapiro#endif 449290792Sgshapiro (void) recipient(q, &e->e_sendqueue, 0, e); 449390792Sgshapiro } 449490792Sgshapiro frcpt = NULL; 449590792Sgshapiro orcpt = NULL; 4496120256Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), 4497120256Sgshapiro NULL); 449838032Speter break; 449938032Speter 450090792Sgshapiro case 'S': /* sender */ 450190792Sgshapiro setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]), 450290792Sgshapiro e, NULL, '\0', true); 450338032Speter break; 450438032Speter 450590792Sgshapiro case 'T': /* init time */ 450690792Sgshapiro e->e_ctime = atol(&bp[1]); 450764562Sgshapiro break; 450864562Sgshapiro 450990792Sgshapiro case 'V': /* queue file version number */ 451090792Sgshapiro qfver = atoi(&bp[1]); 451190792Sgshapiro if (qfver <= QF_VERSION) 451290792Sgshapiro break; 451390792Sgshapiro syserr("Version number in queue file (%d) greater than max (%d)", 451490792Sgshapiro qfver, QF_VERSION); 451594334Sgshapiro err = "unsupported queue file version"; 451694334Sgshapiro goto fail; 451790792Sgshapiro /* NOTREACHED */ 451890792Sgshapiro break; 451990792Sgshapiro 452090792Sgshapiro case 'Z': /* original envelope id from ESMTP */ 452190792Sgshapiro e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 452290792Sgshapiro macdefine(&e->e_macro, A_PERM, 452390792Sgshapiro macid("{dsn_envid}"), e->e_envid); 452490792Sgshapiro break; 452590792Sgshapiro 452690792Sgshapiro case '!': /* deliver by */ 452790792Sgshapiro 452890792Sgshapiro /* format: flag (1 char) space long-integer */ 452990792Sgshapiro e->e_dlvr_flag = buf[1]; 453090792Sgshapiro e->e_deliver_by = strtol(&buf[3], NULL, 10); 453190792Sgshapiro 453238032Speter case '$': /* define macro */ 453364562Sgshapiro { 453490792Sgshapiro r = macid_parse(&bp[1], &ep); 453590792Sgshapiro if (r == 0) 453671345Sgshapiro break; 4537363466Sgshapiro macdefine(&e->e_macro, A_PERM, r, 4538363466Sgshapiro sm_rpool_strdup_x(e->e_rpool, ep)); 453964562Sgshapiro } 454038032Speter break; 454138032Speter 454238032Speter case '.': /* terminate file */ 454390792Sgshapiro nomore = true; 454438032Speter break; 454538032Speter 454638032Speter default: 454738032Speter syserr("readqf: %s: line %d: bad line \"%s\"", 454838032Speter qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); 454994334Sgshapiro err = "unrecognized line"; 455094334Sgshapiro goto fail; 455138032Speter } 455238032Speter 455338032Speter if (bp != buf) 4554168515Sgshapiro SM_FREE(bp); 455538032Speter } 455638032Speter 455738032Speter /* 455838032Speter ** If we haven't read any lines, this queue file is empty. 455938032Speter ** Arrange to remove it without referencing any null pointers. 456038032Speter */ 456138032Speter 456238032Speter if (LineNumber == 0) 456338032Speter { 456438032Speter errno = 0; 456590792Sgshapiro e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE; 456690792Sgshapiro return true; 456738032Speter } 456838032Speter 456990792Sgshapiro /* Check to make sure we have a complete queue file read */ 457090792Sgshapiro if (!nomore) 457190792Sgshapiro { 457290792Sgshapiro syserr("readqf: %s: incomplete queue file read", qf); 457390792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 457490792Sgshapiro return false; 457590792Sgshapiro } 4576182352Sgshapiro 4577168515Sgshapiro#if _FFR_QF_PARANOIA 4578168515Sgshapiro /* Check to make sure key fields were read */ 4579168515Sgshapiro if (e->e_from.q_mailer == NULL) 4580168515Sgshapiro { 4581168515Sgshapiro syserr("readqf: %s: sender not specified in queue file", qf); 4582168515Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 4583168515Sgshapiro return false; 4584168515Sgshapiro } 4585168515Sgshapiro /* other checks? */ 4586168515Sgshapiro#endif /* _FFR_QF_PARANOIA */ 458790792Sgshapiro 4588363466Sgshapiro#if _FFR_EAI 4589363466Sgshapiro /* 4590363466Sgshapiro ** If this message originates from something other than 4591363466Sgshapiro ** srvrsmtp.c, then it might use UTF8 addresses but not be 4592363466Sgshapiro ** marked. We'll just add the mark so we're sure that it 4593363466Sgshapiro ** either can be delivered or will be returned. 4594363466Sgshapiro */ 4595363466Sgshapiro 4596363466Sgshapiro if (!e->e_smtputf8) 4597363466Sgshapiro { 4598363466Sgshapiro ADDRESS *q; 4599363466Sgshapiro 4600363466Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 4601363466Sgshapiro if (!addr_is_ascii(q->q_paddr) && !e->e_smtputf8) 4602363466Sgshapiro e->e_smtputf8 = true; 4603363466Sgshapiro if (!addr_is_ascii(e->e_from.q_paddr) && !e->e_smtputf8) 4604363466Sgshapiro e->e_smtputf8 = true; 4605363466Sgshapiro } 4606363466Sgshapiro#endif /* _FFR_EAI */ 4607363466Sgshapiro 460864562Sgshapiro /* possibly set ${dsn_ret} macro */ 460964562Sgshapiro if (bitset(EF_RET_PARAM, e->e_flags)) 461064562Sgshapiro { 461164562Sgshapiro if (bitset(EF_NO_BODY_RETN, e->e_flags)) 461290792Sgshapiro macdefine(&e->e_macro, A_PERM, 461390792Sgshapiro macid("{dsn_ret}"), "hdrs"); 461464562Sgshapiro else 461590792Sgshapiro macdefine(&e->e_macro, A_PERM, 461690792Sgshapiro macid("{dsn_ret}"), "full"); 461764562Sgshapiro } 461864562Sgshapiro 461938032Speter /* 462038032Speter ** Arrange to read the data file. 462138032Speter */ 462238032Speter 462390792Sgshapiro p = queuename(e, DATAFL_LETTER); 4624120256Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY_B, 462590792Sgshapiro NULL); 462638032Speter if (e->e_dfp == NULL) 462738032Speter { 462838032Speter syserr("readqf: cannot open %s", p); 462938032Speter } 463038032Speter else 463138032Speter { 463238032Speter e->e_flags |= EF_HAS_DF; 463390792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st) 463490792Sgshapiro >= 0) 463538032Speter { 463638032Speter e->e_msgsize = st.st_size + hdrsize; 463738032Speter e->e_dfdev = st.st_dev; 463890792Sgshapiro e->e_dfino = ST_INODE(st); 4639168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%ld", 4640244833Sgshapiro PRT_NONNEGL(e->e_msgsize)); 464198121Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), 464298121Sgshapiro buf); 464338032Speter } 464438032Speter } 464538032Speter 464690792Sgshapiro return true; 464794334Sgshapiro 464894334Sgshapiro fail: 464994334Sgshapiro /* 465094334Sgshapiro ** There was some error reading the qf file (reason is in err var.) 465194334Sgshapiro ** Cleanup: 465294334Sgshapiro ** close file; clear e_lockfp since it is the same as qfp, 465394334Sgshapiro ** hence it is invalid (as file) after qfp is closed; 465494334Sgshapiro ** the qf file is on disk, so set the flag to avoid calling 465594334Sgshapiro ** queueup() with bogus data. 465694334Sgshapiro */ 465794334Sgshapiro 4658168515Sgshapiro if (bp != buf) 4659168515Sgshapiro SM_FREE(bp); 466094334Sgshapiro if (qfp != NULL) 466194334Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 466294334Sgshapiro e->e_lockfp = NULL; 466394334Sgshapiro e->e_flags |= EF_INQUEUE; 466494334Sgshapiro loseqfile(e, err); 466594334Sgshapiro return false; 466638032Speter} 466790792Sgshapiro/* 466864562Sgshapiro** PRTSTR -- print a string, "unprintable" characters are shown as \oct 466964562Sgshapiro** 467064562Sgshapiro** Parameters: 467164562Sgshapiro** s -- string to print 467264562Sgshapiro** ml -- maximum length of output 467364562Sgshapiro** 467464562Sgshapiro** Returns: 467590792Sgshapiro** number of entries 467664562Sgshapiro** 467764562Sgshapiro** Side Effects: 467864562Sgshapiro** Prints a string on stdout. 467964562Sgshapiro*/ 468064562Sgshapiro 4681168515Sgshapirostatic void prtstr __P((char *, int)); 4682168515Sgshapiro 4683285229Sgshapiro#if _FFR_BOUNCE_QUEUE 4684363466Sgshapiro# define IS_BOUNCE_QUEUE(i) ((i) == BounceQueue) 4685363466Sgshapiro# define SKIP_BOUNCE_QUEUE(i) \ 4686363466Sgshapiro if (IS_BOUNCE_QUEUE(i)) \ 4687285229Sgshapiro continue; 4688285229Sgshapiro#else 4689363466Sgshapiro# define IS_BOUNCE_QUEUE(i) false 4690363466Sgshapiro# define SKIP_BOUNCE_QUEUE(i) 4691285229Sgshapiro#endif 4692285229Sgshapiro 469364562Sgshapirostatic void 469464562Sgshapiroprtstr(s, ml) 469564562Sgshapiro char *s; 469664562Sgshapiro int ml; 469764562Sgshapiro{ 469890792Sgshapiro int c; 469964562Sgshapiro 470064562Sgshapiro if (s == NULL) 470164562Sgshapiro return; 470264562Sgshapiro while (ml-- > 0 && ((c = *s++) != '\0')) 470364562Sgshapiro { 470464562Sgshapiro if (c == '\\') 470564562Sgshapiro { 470664562Sgshapiro if (ml-- > 0) 470764562Sgshapiro { 470890792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 470990792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 471064562Sgshapiro } 471164562Sgshapiro } 471264562Sgshapiro else if (isascii(c) && isprint(c)) 471390792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 471464562Sgshapiro else 471564562Sgshapiro { 471664562Sgshapiro if ((ml -= 3) > 0) 471790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 471890792Sgshapiro "\\%03o", c & 0xFF); 471964562Sgshapiro } 472064562Sgshapiro } 472164562Sgshapiro} 472290792Sgshapiro/* 472390792Sgshapiro** PRINTNQE -- print out number of entries in the mail queue 472490792Sgshapiro** 472590792Sgshapiro** Parameters: 472690792Sgshapiro** out -- output file pointer. 472790792Sgshapiro** prefix -- string to output in front of each line. 472890792Sgshapiro** 472990792Sgshapiro** Returns: 473090792Sgshapiro** none. 473190792Sgshapiro*/ 473290792Sgshapiro 473390792Sgshapirovoid 473490792Sgshapiroprintnqe(out, prefix) 473590792Sgshapiro SM_FILE_T *out; 473690792Sgshapiro char *prefix; 473790792Sgshapiro{ 473890792Sgshapiro#if SM_CONF_SHM 473990792Sgshapiro int i, k = 0, nrequests = 0; 474090792Sgshapiro bool unknown = false; 474190792Sgshapiro 474290792Sgshapiro if (ShmId == SM_SHM_NO_ID) 474390792Sgshapiro { 474490792Sgshapiro if (prefix == NULL) 474590792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 474690792Sgshapiro "Data unavailable: shared memory not updated\n"); 474790792Sgshapiro else 474890792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 474990792Sgshapiro "%sNOTCONFIGURED:-1\r\n", prefix); 475090792Sgshapiro return; 475190792Sgshapiro } 475290792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 475390792Sgshapiro { 475490792Sgshapiro int j; 475590792Sgshapiro 4756363466Sgshapiro SKIP_BOUNCE_QUEUE(i) 475790792Sgshapiro k++; 475890792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 475990792Sgshapiro { 476090792Sgshapiro int n; 476190792Sgshapiro 476290792Sgshapiro if (StopRequest) 476390792Sgshapiro stop_sendmail(); 476490792Sgshapiro 476590792Sgshapiro n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx); 476690792Sgshapiro if (prefix != NULL) 476790792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 476890792Sgshapiro "%s%s:%d\r\n", 476990792Sgshapiro prefix, qid_printqueue(i, j), n); 477090792Sgshapiro else if (n < 0) 477190792Sgshapiro { 477290792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 477390792Sgshapiro "%s: unknown number of entries\n", 477490792Sgshapiro qid_printqueue(i, j)); 477590792Sgshapiro unknown = true; 477690792Sgshapiro } 477790792Sgshapiro else if (n == 0) 477890792Sgshapiro { 477990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 478090792Sgshapiro "%s is empty\n", 478190792Sgshapiro qid_printqueue(i, j)); 478290792Sgshapiro } 478390792Sgshapiro else if (n > 0) 478490792Sgshapiro { 478590792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 478690792Sgshapiro "%s: entries=%d\n", 478790792Sgshapiro qid_printqueue(i, j), n); 478890792Sgshapiro nrequests += n; 478990792Sgshapiro k++; 479090792Sgshapiro } 479190792Sgshapiro } 479290792Sgshapiro } 479390792Sgshapiro if (prefix == NULL && k > 1) 479490792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 479590792Sgshapiro "\t\tTotal requests: %d%s\n", 479690792Sgshapiro nrequests, unknown ? " (about)" : ""); 479790792Sgshapiro#else /* SM_CONF_SHM */ 479890792Sgshapiro if (prefix == NULL) 479990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 480090792Sgshapiro "Data unavailable without shared memory support\n"); 480190792Sgshapiro else 480290792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 480390792Sgshapiro "%sNOTAVAILABLE:-1\r\n", prefix); 480490792Sgshapiro#endif /* SM_CONF_SHM */ 480590792Sgshapiro} 480690792Sgshapiro/* 480738032Speter** PRINTQUEUE -- print out a representation of the mail queue 480838032Speter** 480938032Speter** Parameters: 481038032Speter** none. 481138032Speter** 481238032Speter** Returns: 481338032Speter** none. 481438032Speter** 481538032Speter** Side Effects: 481638032Speter** Prints a listing of the mail queue on the standard output. 481738032Speter*/ 481838032Speter 481938032Spetervoid 482038032Speterprintqueue() 482138032Speter{ 482290792Sgshapiro int i, k = 0, nrequests = 0; 482364562Sgshapiro 482490792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 482577349Sgshapiro { 482690792Sgshapiro int j; 482790792Sgshapiro 482890792Sgshapiro k++; 482990792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 483090792Sgshapiro { 483190792Sgshapiro if (StopRequest) 483290792Sgshapiro stop_sendmail(); 483390792Sgshapiro nrequests += print_single_queue(i, j); 483490792Sgshapiro k++; 483590792Sgshapiro } 483677349Sgshapiro } 483790792Sgshapiro if (k > 1) 483890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 483990792Sgshapiro "\t\tTotal requests: %d\n", 484090792Sgshapiro nrequests); 484164562Sgshapiro} 484290792Sgshapiro/* 484364562Sgshapiro** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue 484464562Sgshapiro** 484564562Sgshapiro** Parameters: 484690792Sgshapiro** qgrp -- the index of the queue group. 484790792Sgshapiro** qdir -- the queue directory. 484864562Sgshapiro** 484964562Sgshapiro** Returns: 485090792Sgshapiro** number of requests in mail queue. 485164562Sgshapiro** 485264562Sgshapiro** Side Effects: 485364562Sgshapiro** Prints a listing of the mail queue on the standard output. 485464562Sgshapiro*/ 485564562Sgshapiro 485690792Sgshapiroint 485790792Sgshapiroprint_single_queue(qgrp, qdir) 485890792Sgshapiro int qgrp; 485990792Sgshapiro int qdir; 486064562Sgshapiro{ 486138032Speter register WORK *w; 486290792Sgshapiro SM_FILE_T *f; 486338032Speter int nrequests; 486464562Sgshapiro char qd[MAXPATHLEN]; 486564562Sgshapiro char qddf[MAXPATHLEN]; 486638032Speter char buf[MAXLINE]; 486738032Speter 486890792Sgshapiro if (qdir == NOQDIR) 486964562Sgshapiro { 4870168515Sgshapiro (void) sm_strlcpy(qd, ".", sizeof(qd)); 4871168515Sgshapiro (void) sm_strlcpy(qddf, ".", sizeof(qddf)); 487264562Sgshapiro } 487364562Sgshapiro else 487464562Sgshapiro { 4875168515Sgshapiro (void) sm_strlcpyn(qd, sizeof(qd), 2, 487690792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 487790792Sgshapiro (bitset(QP_SUBQF, 487890792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 487990792Sgshapiro ? "/qf" : "")); 4880168515Sgshapiro (void) sm_strlcpyn(qddf, sizeof(qddf), 2, 488190792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 488290792Sgshapiro (bitset(QP_SUBDF, 488390792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 488490792Sgshapiro ? "/df" : "")); 488564562Sgshapiro } 488664562Sgshapiro 488738032Speter /* 488838032Speter ** Check for permission to print the queue 488938032Speter */ 489038032Speter 489138032Speter if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 489238032Speter { 489338032Speter struct stat st; 489490792Sgshapiro#ifdef NGROUPS_MAX 489538032Speter int n; 489638032Speter extern GIDSET_T InitialGidSet[NGROUPS_MAX]; 4897363466Sgshapiro#endif 489838032Speter 489964562Sgshapiro if (stat(qd, &st) < 0) 490038032Speter { 490190792Sgshapiro syserr("Cannot stat %s", 490290792Sgshapiro qid_printqueue(qgrp, qdir)); 490364562Sgshapiro return 0; 490438032Speter } 490590792Sgshapiro#ifdef NGROUPS_MAX 490638032Speter n = NGROUPS_MAX; 490738032Speter while (--n >= 0) 490838032Speter { 490938032Speter if (InitialGidSet[n] == st.st_gid) 491038032Speter break; 491138032Speter } 491238032Speter if (n < 0 && RealGid != st.st_gid) 491390792Sgshapiro#else /* NGROUPS_MAX */ 491438032Speter if (RealGid != st.st_gid) 491590792Sgshapiro#endif /* NGROUPS_MAX */ 491638032Speter { 491738032Speter usrerr("510 You are not permitted to see the queue"); 491838032Speter setstat(EX_NOPERM); 491964562Sgshapiro return 0; 492038032Speter } 492138032Speter } 492238032Speter 492338032Speter /* 492438032Speter ** Read and order the queue. 492538032Speter */ 492638032Speter 4927203004Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL, NULL); 492890792Sgshapiro (void) sortq(Queue[qgrp]->qg_maxlist); 492938032Speter 493038032Speter /* 493138032Speter ** Print the work list that we have read. 493238032Speter */ 493338032Speter 493438032Speter /* first see if there is anything */ 493538032Speter if (nrequests <= 0) 493638032Speter { 493790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n", 493890792Sgshapiro qid_printqueue(qgrp, qdir)); 493964562Sgshapiro return 0; 494038032Speter } 494138032Speter 494290792Sgshapiro sm_getla(); /* get load average */ 494338032Speter 494490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s", 494590792Sgshapiro qid_printqueue(qgrp, qdir), 494690792Sgshapiro nrequests, nrequests == 1 ? "" : "s"); 494738032Speter if (MaxQueueRun > 0 && nrequests > MaxQueueRun) 494890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 494990792Sgshapiro ", only %d printed", MaxQueueRun); 495038032Speter if (Verbose) 495190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 495290792Sgshapiro ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n"); 495338032Speter else 495490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 495590792Sgshapiro ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n"); 495638032Speter for (w = WorkQ; w != NULL; w = w->w_next) 495738032Speter { 495838032Speter struct stat st; 495938032Speter auto time_t submittime = 0; 496038032Speter long dfsize; 496138032Speter int flags = 0; 496238032Speter int qfver; 496390792Sgshapiro char quarmsg[MAXLINE]; 496438032Speter char statmsg[MAXLINE]; 496538032Speter char bodytype[MAXNAME + 1]; 496664562Sgshapiro char qf[MAXPATHLEN]; 496738032Speter 496877349Sgshapiro if (StopRequest) 496977349Sgshapiro stop_sendmail(); 497077349Sgshapiro 497190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s", 497290792Sgshapiro w->w_name + 2); 4973168515Sgshapiro (void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", w->w_name); 4974120256Sgshapiro f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B, 497590792Sgshapiro NULL); 497638032Speter if (f == NULL) 497738032Speter { 497890792Sgshapiro if (errno == EPERM) 497990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 498090792Sgshapiro " (permission denied)\n"); 498190792Sgshapiro else if (errno == ENOENT) 498290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 498390792Sgshapiro " (job completed)\n"); 498490792Sgshapiro else 498590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 498690792Sgshapiro " (%s)\n", 498790792Sgshapiro sm_errstring(errno)); 498838032Speter errno = 0; 498938032Speter continue; 499038032Speter } 499190792Sgshapiro w->w_name[0] = DATAFL_LETTER; 4992168515Sgshapiro (void) sm_strlcpyn(qf, sizeof(qf), 3, qddf, "/", w->w_name); 499364562Sgshapiro if (stat(qf, &st) >= 0) 499438032Speter dfsize = st.st_size; 499538032Speter else 499690792Sgshapiro { 499790792Sgshapiro ENVELOPE e; 499890792Sgshapiro 499990792Sgshapiro /* 500090792Sgshapiro ** Maybe the df file can't be statted because 500190792Sgshapiro ** it is in a different directory than the qf file. 500290792Sgshapiro ** In order to find out, we must read the qf file. 500390792Sgshapiro */ 500490792Sgshapiro 500590792Sgshapiro newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL)); 500690792Sgshapiro e.e_id = w->w_name + 2; 500790792Sgshapiro e.e_qgrp = qgrp; 500890792Sgshapiro e.e_qdir = qdir; 500938032Speter dfsize = -1; 501090792Sgshapiro if (readqf(&e, false)) 501190792Sgshapiro { 501290792Sgshapiro char *df = queuename(&e, DATAFL_LETTER); 501390792Sgshapiro if (stat(df, &st) >= 0) 501490792Sgshapiro dfsize = st.st_size; 501590792Sgshapiro } 501690792Sgshapiro if (e.e_lockfp != NULL) 501790792Sgshapiro { 501890792Sgshapiro (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT); 501990792Sgshapiro e.e_lockfp = NULL; 502090792Sgshapiro } 502190792Sgshapiro clearenvelope(&e, false, e.e_rpool); 502290792Sgshapiro sm_rpool_free(e.e_rpool); 502390792Sgshapiro } 502438032Speter if (w->w_lock) 502590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*"); 502690792Sgshapiro else if (QueueMode == QM_LOST) 502790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?"); 502838032Speter else if (w->w_tooyoung) 502990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-"); 503038032Speter else if (shouldqueue(w->w_pri, w->w_ctime)) 503190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X"); 503238032Speter else 503390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " "); 503490792Sgshapiro 503538032Speter errno = 0; 503638032Speter 503790792Sgshapiro quarmsg[0] = '\0'; 503838032Speter statmsg[0] = bodytype[0] = '\0'; 503938032Speter qfver = 0; 5040249729Sgshapiro while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0) 504138032Speter { 504238032Speter register int i; 504338032Speter register char *p; 504438032Speter 504577349Sgshapiro if (StopRequest) 504677349Sgshapiro stop_sendmail(); 504777349Sgshapiro 504890792Sgshapiro fixcrlf(buf, true); 504938032Speter switch (buf[0]) 505038032Speter { 505138032Speter case 'V': /* queue file version */ 505238032Speter qfver = atoi(&buf[1]); 505338032Speter break; 505438032Speter 505538032Speter case 'M': /* error message */ 5056168515Sgshapiro if ((i = strlen(&buf[1])) >= sizeof(statmsg)) 5057168515Sgshapiro i = sizeof(statmsg) - 1; 505864562Sgshapiro memmove(statmsg, &buf[1], i); 505938032Speter statmsg[i] = '\0'; 506038032Speter break; 506138032Speter 506290792Sgshapiro case 'q': /* quarantine reason */ 5063168515Sgshapiro if ((i = strlen(&buf[1])) >= sizeof(quarmsg)) 5064168515Sgshapiro i = sizeof(quarmsg) - 1; 506590792Sgshapiro memmove(quarmsg, &buf[1], i); 506690792Sgshapiro quarmsg[i] = '\0'; 506790792Sgshapiro break; 506890792Sgshapiro 506938032Speter case 'B': /* body type */ 5070168515Sgshapiro if ((i = strlen(&buf[1])) >= sizeof(bodytype)) 5071168515Sgshapiro i = sizeof(bodytype) - 1; 507264562Sgshapiro memmove(bodytype, &buf[1], i); 507338032Speter bodytype[i] = '\0'; 507438032Speter break; 507538032Speter 507638032Speter case 'S': /* sender name */ 507738032Speter if (Verbose) 507864562Sgshapiro { 507990792Sgshapiro (void) sm_io_fprintf(smioout, 508090792Sgshapiro SM_TIME_DEFAULT, 508190792Sgshapiro "%8ld %10ld%c%.12s ", 508290792Sgshapiro dfsize, 508390792Sgshapiro w->w_pri, 508490792Sgshapiro bitset(EF_WARNING, flags) 508590792Sgshapiro ? '+' : ' ', 508690792Sgshapiro ctime(&submittime) + 4); 508764562Sgshapiro prtstr(&buf[1], 78); 508864562Sgshapiro } 508938032Speter else 509064562Sgshapiro { 509190792Sgshapiro (void) sm_io_fprintf(smioout, 509290792Sgshapiro SM_TIME_DEFAULT, 509390792Sgshapiro "%8ld %.16s ", 509490792Sgshapiro dfsize, 509590792Sgshapiro ctime(&submittime)); 509690792Sgshapiro prtstr(&buf[1], 39); 509764562Sgshapiro } 5098132943Sgshapiro 509990792Sgshapiro if (quarmsg[0] != '\0') 510090792Sgshapiro { 510190792Sgshapiro (void) sm_io_fprintf(smioout, 510290792Sgshapiro SM_TIME_DEFAULT, 510390792Sgshapiro "\n QUARANTINE: %.*s", 510490792Sgshapiro Verbose ? 100 : 60, 510590792Sgshapiro quarmsg); 510690792Sgshapiro quarmsg[0] = '\0'; 510790792Sgshapiro } 5108132943Sgshapiro 510938032Speter if (statmsg[0] != '\0' || bodytype[0] != '\0') 511038032Speter { 511190792Sgshapiro (void) sm_io_fprintf(smioout, 511290792Sgshapiro SM_TIME_DEFAULT, 511390792Sgshapiro "\n %10.10s", 511490792Sgshapiro bodytype); 511538032Speter if (statmsg[0] != '\0') 511690792Sgshapiro (void) sm_io_fprintf(smioout, 511790792Sgshapiro SM_TIME_DEFAULT, 511890792Sgshapiro " (%.*s)", 511990792Sgshapiro Verbose ? 100 : 60, 512090792Sgshapiro statmsg); 512190792Sgshapiro statmsg[0] = '\0'; 512238032Speter } 512338032Speter break; 512438032Speter 512538032Speter case 'C': /* controlling user */ 512638032Speter if (Verbose) 512790792Sgshapiro (void) sm_io_fprintf(smioout, 512890792Sgshapiro SM_TIME_DEFAULT, 512990792Sgshapiro "\n\t\t\t\t\t\t(---%.64s---)", 513090792Sgshapiro &buf[1]); 513138032Speter break; 513238032Speter 513338032Speter case 'R': /* recipient name */ 513438032Speter p = &buf[1]; 513538032Speter if (qfver >= 1) 513638032Speter { 513738032Speter p = strchr(p, ':'); 513838032Speter if (p == NULL) 513938032Speter break; 514038032Speter p++; 514138032Speter } 514238032Speter if (Verbose) 514364562Sgshapiro { 514490792Sgshapiro (void) sm_io_fprintf(smioout, 514590792Sgshapiro SM_TIME_DEFAULT, 514690792Sgshapiro "\n\t\t\t\t\t\t"); 514790792Sgshapiro prtstr(p, 71); 514864562Sgshapiro } 514938032Speter else 515064562Sgshapiro { 515190792Sgshapiro (void) sm_io_fprintf(smioout, 515290792Sgshapiro SM_TIME_DEFAULT, 515390792Sgshapiro "\n\t\t\t\t\t "); 515490792Sgshapiro prtstr(p, 38); 515564562Sgshapiro } 515690792Sgshapiro if (Verbose && statmsg[0] != '\0') 515790792Sgshapiro { 515890792Sgshapiro (void) sm_io_fprintf(smioout, 515990792Sgshapiro SM_TIME_DEFAULT, 516090792Sgshapiro "\n\t\t (%.100s)", 516190792Sgshapiro statmsg); 516290792Sgshapiro statmsg[0] = '\0'; 516390792Sgshapiro } 516438032Speter break; 516538032Speter 516638032Speter case 'T': /* creation time */ 516738032Speter submittime = atol(&buf[1]); 516838032Speter break; 516938032Speter 517038032Speter case 'F': /* flag bits */ 517138032Speter for (p = &buf[1]; *p != '\0'; p++) 517238032Speter { 517338032Speter switch (*p) 517438032Speter { 517538032Speter case 'w': 517638032Speter flags |= EF_WARNING; 517738032Speter break; 517838032Speter } 517938032Speter } 518038032Speter } 518138032Speter } 518238032Speter if (submittime == (time_t) 0) 518390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 518490792Sgshapiro " (no control file)"); 518590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 518690792Sgshapiro (void) sm_io_close(f, SM_TIME_DEFAULT); 518738032Speter } 518864562Sgshapiro return nrequests; 518938032Speter} 519090792Sgshapiro 519190792Sgshapiro/* 519290792Sgshapiro** QUEUE_LETTER -- get the proper queue letter for the current QueueMode. 519390792Sgshapiro** 519490792Sgshapiro** Parameters: 519590792Sgshapiro** e -- envelope to build it in/from. 519690792Sgshapiro** type -- the file type, used as the first character 519790792Sgshapiro** of the file name. 519890792Sgshapiro** 519990792Sgshapiro** Returns: 520090792Sgshapiro** the letter to use 520190792Sgshapiro*/ 520290792Sgshapiro 520390792Sgshapirostatic char 520490792Sgshapiroqueue_letter(e, type) 520590792Sgshapiro ENVELOPE *e; 520690792Sgshapiro int type; 520790792Sgshapiro{ 520890792Sgshapiro /* Change type according to QueueMode */ 520990792Sgshapiro if (type == ANYQFL_LETTER) 521090792Sgshapiro { 521190792Sgshapiro if (e->e_quarmsg != NULL) 521290792Sgshapiro type = QUARQF_LETTER; 521390792Sgshapiro else 521490792Sgshapiro { 521590792Sgshapiro switch (QueueMode) 521690792Sgshapiro { 521790792Sgshapiro case QM_NORMAL: 521890792Sgshapiro type = NORMQF_LETTER; 521990792Sgshapiro break; 522090792Sgshapiro 522190792Sgshapiro case QM_QUARANTINE: 522290792Sgshapiro type = QUARQF_LETTER; 522390792Sgshapiro break; 522490792Sgshapiro 522590792Sgshapiro case QM_LOST: 522690792Sgshapiro type = LOSEQF_LETTER; 522790792Sgshapiro break; 522890792Sgshapiro 522990792Sgshapiro default: 523090792Sgshapiro /* should never happen */ 523190792Sgshapiro abort(); 523290792Sgshapiro /* NOTREACHED */ 523390792Sgshapiro } 523490792Sgshapiro } 523590792Sgshapiro } 523690792Sgshapiro return type; 523790792Sgshapiro} 523890792Sgshapiro 523990792Sgshapiro/* 524038032Speter** QUEUENAME -- build a file name in the queue directory for this envelope. 524138032Speter** 524238032Speter** Parameters: 524338032Speter** e -- envelope to build it in/from. 524438032Speter** type -- the file type, used as the first character 524538032Speter** of the file name. 524638032Speter** 524738032Speter** Returns: 524864562Sgshapiro** a pointer to the queue name (in a static buffer). 524938032Speter** 525038032Speter** Side Effects: 525164562Sgshapiro** If no id code is already assigned, queuename() will 525264562Sgshapiro** assign an id code with assign_queueid(). If no queue 525364562Sgshapiro** directory is assigned, one will be set with setnewqueue(). 525438032Speter*/ 525538032Speter 525638032Speterchar * 525738032Speterqueuename(e, type) 525838032Speter register ENVELOPE *e; 525938032Speter int type; 526038032Speter{ 526190792Sgshapiro int qd, qg; 526290792Sgshapiro char *sub = "/"; 526390792Sgshapiro char pref[3]; 526464562Sgshapiro static char buf[MAXPATHLEN]; 526538032Speter 526664562Sgshapiro /* Assign an ID if needed */ 526738032Speter if (e->e_id == NULL) 5268223067Sgshapiro { 5269223067Sgshapiro if (IntSig) 5270223067Sgshapiro return NULL; 527164562Sgshapiro assign_queueid(e); 5272223067Sgshapiro } 527390792Sgshapiro type = queue_letter(e, type); 527464562Sgshapiro 527590792Sgshapiro /* begin of filename */ 527690792Sgshapiro pref[0] = (char) type; 527790792Sgshapiro pref[1] = 'f'; 527890792Sgshapiro pref[2] = '\0'; 527990792Sgshapiro 528090792Sgshapiro /* Assign a queue group/directory if needed */ 528190792Sgshapiro if (type == XSCRPT_LETTER) 528290792Sgshapiro { 528390792Sgshapiro /* 528490792Sgshapiro ** We don't want to call setnewqueue() if we are fetching 528590792Sgshapiro ** the pathname of the transcript file, because setnewqueue 528690792Sgshapiro ** chooses a queue, and sometimes we need to write to the 528790792Sgshapiro ** transcript file before we have gathered enough information 528890792Sgshapiro ** to choose a queue. 528990792Sgshapiro */ 529090792Sgshapiro 529190792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 529290792Sgshapiro { 529390792Sgshapiro if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR) 529490792Sgshapiro { 529590792Sgshapiro e->e_xfqgrp = e->e_qgrp; 529690792Sgshapiro e->e_xfqdir = e->e_qdir; 529790792Sgshapiro } 529890792Sgshapiro else 529990792Sgshapiro { 530090792Sgshapiro e->e_xfqgrp = 0; 530190792Sgshapiro if (Queue[e->e_xfqgrp]->qg_numqueues <= 1) 530290792Sgshapiro e->e_xfqdir = 0; 530390792Sgshapiro else 530490792Sgshapiro { 530590792Sgshapiro e->e_xfqdir = get_rand_mod( 530690792Sgshapiro Queue[e->e_xfqgrp]->qg_numqueues); 530790792Sgshapiro } 530890792Sgshapiro } 530990792Sgshapiro } 531090792Sgshapiro qd = e->e_xfqdir; 531190792Sgshapiro qg = e->e_xfqgrp; 531290792Sgshapiro } 531364562Sgshapiro else 531438032Speter { 531590792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 5316223067Sgshapiro { 5317223067Sgshapiro if (IntSig) 5318223067Sgshapiro return NULL; 5319159609Sgshapiro (void) setnewqueue(e); 5320223067Sgshapiro } 532190792Sgshapiro if (type == DATAFL_LETTER) 532290792Sgshapiro { 532390792Sgshapiro qd = e->e_dfqdir; 532490792Sgshapiro qg = e->e_dfqgrp; 532590792Sgshapiro } 532690792Sgshapiro else 532790792Sgshapiro { 532890792Sgshapiro qd = e->e_qdir; 532990792Sgshapiro qg = e->e_qgrp; 533090792Sgshapiro } 533190792Sgshapiro } 533290792Sgshapiro 533394334Sgshapiro /* xf files always have a valid qd and qg picked above */ 5334159609Sgshapiro if ((qd == NOQDIR || qg == NOQGRP) && type != XSCRPT_LETTER) 5335168515Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, pref, e->e_id); 533690792Sgshapiro else 533790792Sgshapiro { 533864562Sgshapiro switch (type) 533964562Sgshapiro { 534090792Sgshapiro case DATAFL_LETTER: 534190792Sgshapiro if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 534290792Sgshapiro sub = "/df/"; 534364562Sgshapiro break; 534438032Speter 534590792Sgshapiro case QUARQF_LETTER: 534671345Sgshapiro case TEMPQF_LETTER: 534790792Sgshapiro case NEWQFL_LETTER: 534871345Sgshapiro case LOSEQF_LETTER: 534990792Sgshapiro case NORMQF_LETTER: 535090792Sgshapiro if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 535190792Sgshapiro sub = "/qf/"; 535264562Sgshapiro break; 535364562Sgshapiro 535490792Sgshapiro case XSCRPT_LETTER: 535590792Sgshapiro if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 535690792Sgshapiro sub = "/xf/"; 535764562Sgshapiro break; 535890792Sgshapiro 535990792Sgshapiro default: 5360223067Sgshapiro if (IntSig) 5361223067Sgshapiro return NULL; 536290792Sgshapiro sm_abort("queuename: bad queue file type %d", type); 536338032Speter } 536438032Speter 5365168515Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 4, 536690792Sgshapiro Queue[qg]->qg_qpaths[qd].qp_name, 536790792Sgshapiro sub, pref, e->e_id); 536864562Sgshapiro } 536938032Speter 537064562Sgshapiro if (tTd(7, 2)) 537190792Sgshapiro sm_dprintf("queuename: %s\n", buf); 537264562Sgshapiro return buf; 537364562Sgshapiro} 5374125820Sgshapiro 537590792Sgshapiro/* 5376125820Sgshapiro** INIT_QID_ALG -- Initialize the (static) parameters that are used to 5377125820Sgshapiro** generate a queue ID. 5378125820Sgshapiro** 5379125820Sgshapiro** This function is called by the daemon to reset 5380125820Sgshapiro** LastQueueTime and LastQueuePid which are used by assign_queueid(). 5381125820Sgshapiro** Otherwise the algorithm may cause problems because 5382125820Sgshapiro** LastQueueTime and LastQueuePid are set indirectly by main() 5383125820Sgshapiro** before the daemon process is started, hence LastQueuePid is not 5384125820Sgshapiro** the pid of the daemon and therefore a child of the daemon can 5385125820Sgshapiro** actually have the same pid as LastQueuePid which means the section 5386125820Sgshapiro** in assign_queueid(): 5387125820Sgshapiro** * see if we need to get a new base time/pid * 5388125820Sgshapiro** is NOT triggered which will cause the same queue id to be generated. 5389125820Sgshapiro** 5390125820Sgshapiro** Parameters: 5391125820Sgshapiro** none 5392125820Sgshapiro** 5393125820Sgshapiro** Returns: 5394125820Sgshapiro** none. 5395125820Sgshapiro*/ 5396125820Sgshapiro 5397125820Sgshapirovoid 5398125820Sgshapiroinit_qid_alg() 5399125820Sgshapiro{ 5400125820Sgshapiro LastQueueTime = 0; 5401125820Sgshapiro LastQueuePid = -1; 5402125820Sgshapiro} 5403125820Sgshapiro 5404125820Sgshapiro/* 540564562Sgshapiro** ASSIGN_QUEUEID -- assign a queue ID for this envelope. 540664562Sgshapiro** 540764562Sgshapiro** Assigns an id code if one does not already exist. 540864562Sgshapiro** This code assumes that nothing will remain in the queue for 540964562Sgshapiro** longer than 60 years. It is critical that files with the given 541090792Sgshapiro** name do not already exist in the queue. 541190792Sgshapiro** [No longer initializes e_qdir to NOQDIR.] 541264562Sgshapiro** 541364562Sgshapiro** Parameters: 541464562Sgshapiro** e -- envelope to set it in. 541564562Sgshapiro** 541664562Sgshapiro** Returns: 541764562Sgshapiro** none. 541864562Sgshapiro*/ 541938032Speter 5420125820Sgshapirostatic const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 542177349Sgshapiro# define QIC_LEN 60 5422125820Sgshapiro# define QIC_LEN_R 62 5423125820Sgshapiro 5424125820Sgshapiro/* 5425125820Sgshapiro** Note: the length is "officially" 60 because minutes and seconds are 5426125820Sgshapiro** usually only 0-59. However (Linux): 5427125820Sgshapiro** tm_sec The number of seconds after the minute, normally in 5428132943Sgshapiro** the range 0 to 59, but can be up to 61 to allow for 5429132943Sgshapiro** leap seconds. 5430125820Sgshapiro** Hence the real length of the string is 62 to take this into account. 5431125820Sgshapiro** Alternatively % QIC_LEN can (should) be used for access everywhere. 5432125820Sgshapiro*/ 5433125820Sgshapiro 543490792Sgshapiro# define queuenextid() CurrentPid 5435203004Sgshapiro#define QIC_LEN_SQR (QIC_LEN * QIC_LEN) 543638032Speter 543764562Sgshapirovoid 543864562Sgshapiroassign_queueid(e) 543964562Sgshapiro register ENVELOPE *e; 544064562Sgshapiro{ 544190792Sgshapiro pid_t pid = queuenextid(); 5442203004Sgshapiro static unsigned int cX = 0; 5443203004Sgshapiro static unsigned int random_offset; 544464562Sgshapiro struct tm *tm; 544564562Sgshapiro char idbuf[MAXQFNAME - 2]; 5446203004Sgshapiro unsigned int seq; 544738032Speter 544864562Sgshapiro if (e->e_id != NULL) 544964562Sgshapiro return; 545038032Speter 545164562Sgshapiro /* see if we need to get a new base time/pid */ 5452203004Sgshapiro if (cX >= QIC_LEN_SQR || LastQueueTime == 0 || LastQueuePid != pid) 545364562Sgshapiro { 545464562Sgshapiro time_t then = LastQueueTime; 545564562Sgshapiro 545664562Sgshapiro /* if the first time through, pick a random offset */ 545764562Sgshapiro if (LastQueueTime == 0) 5458203004Sgshapiro random_offset = ((unsigned int)get_random()) 5459203004Sgshapiro % QIC_LEN_SQR; 546064562Sgshapiro 546164562Sgshapiro while ((LastQueueTime = curtime()) == then && 546264562Sgshapiro LastQueuePid == pid) 546338032Speter { 546464562Sgshapiro (void) sleep(1); 546538032Speter } 546690792Sgshapiro LastQueuePid = queuenextid(); 546764562Sgshapiro cX = 0; 546838032Speter } 546990792Sgshapiro 547090792Sgshapiro /* 5471203004Sgshapiro ** Generate a new sequence number between 0 and QIC_LEN_SQR-1. 5472203004Sgshapiro ** This lets us generate up to QIC_LEN_SQR unique queue ids 547390792Sgshapiro ** per second, per process. With envelope splitting, 547490792Sgshapiro ** a single message can consume many queue ids. 547590792Sgshapiro */ 547690792Sgshapiro 5477203004Sgshapiro seq = (cX + random_offset) % QIC_LEN_SQR; 547890792Sgshapiro ++cX; 547964562Sgshapiro if (tTd(7, 50)) 5480203004Sgshapiro sm_dprintf("assign_queueid: random_offset=%u (%u)\n", 548190792Sgshapiro random_offset, seq); 548238032Speter 548364562Sgshapiro tm = gmtime(&LastQueueTime); 548477349Sgshapiro idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN]; 548577349Sgshapiro idbuf[1] = QueueIdChars[tm->tm_mon]; 548677349Sgshapiro idbuf[2] = QueueIdChars[tm->tm_mday]; 548777349Sgshapiro idbuf[3] = QueueIdChars[tm->tm_hour]; 5488125820Sgshapiro idbuf[4] = QueueIdChars[tm->tm_min % QIC_LEN_R]; 5489125820Sgshapiro idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R]; 549090792Sgshapiro idbuf[6] = QueueIdChars[seq / QIC_LEN]; 549190792Sgshapiro idbuf[7] = QueueIdChars[seq % QIC_LEN]; 5492168515Sgshapiro (void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d", 549390792Sgshapiro (int) LastQueuePid); 549490792Sgshapiro e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf); 549590792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 549690792Sgshapiro#if 0 549790792Sgshapiro /* XXX: inherited from MainEnvelope */ 549890792Sgshapiro e->e_qgrp = NOQGRP; /* too early to do anything else */ 549990792Sgshapiro e->e_qdir = NOQDIR; 550090792Sgshapiro e->e_xfqgrp = NOQGRP; 550190792Sgshapiro#endif /* 0 */ 5502132943Sgshapiro 550390792Sgshapiro /* New ID means it's not on disk yet */ 550490792Sgshapiro e->e_qfletter = '\0'; 5505132943Sgshapiro 550664562Sgshapiro if (tTd(7, 1)) 550790792Sgshapiro sm_dprintf("assign_queueid: assigned id %s, e=%p\n", 5508363466Sgshapiro e->e_id, (void *)e); 550964562Sgshapiro if (LogLevel > 93) 551064562Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); 551138032Speter} 551290792Sgshapiro/* 551364562Sgshapiro** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second 551464562Sgshapiro** 551564562Sgshapiro** Make sure one PID can't be used by two processes in any one second. 551664562Sgshapiro** 551764562Sgshapiro** If the system rotates PIDs fast enough, may get the 551864562Sgshapiro** same pid in the same second for two distinct processes. 551964562Sgshapiro** This will interfere with the queue file naming system. 552064562Sgshapiro** 552164562Sgshapiro** Parameters: 552264562Sgshapiro** none 552364562Sgshapiro** 552464562Sgshapiro** Returns: 552564562Sgshapiro** none 552664562Sgshapiro*/ 552790792Sgshapiro 552864562Sgshapirovoid 552964562Sgshapirosync_queue_time() 553064562Sgshapiro{ 553190792Sgshapiro#if FAST_PID_RECYCLE 553264562Sgshapiro if (OpMode != MD_TEST && 5533203004Sgshapiro OpMode != MD_CHECKCONFIG && 553464562Sgshapiro OpMode != MD_VERIFY && 553564562Sgshapiro LastQueueTime > 0 && 553690792Sgshapiro LastQueuePid == CurrentPid && 553764562Sgshapiro curtime() == LastQueueTime) 553864562Sgshapiro (void) sleep(1); 553990792Sgshapiro#endif /* FAST_PID_RECYCLE */ 554064562Sgshapiro} 554190792Sgshapiro/* 554238032Speter** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 554338032Speter** 554438032Speter** Parameters: 554538032Speter** e -- the envelope to unlock. 554638032Speter** 554738032Speter** Returns: 554838032Speter** none 554938032Speter** 555038032Speter** Side Effects: 555138032Speter** unlocks the queue for `e'. 555238032Speter*/ 555338032Speter 555438032Spetervoid 555538032Speterunlockqueue(e) 555638032Speter ENVELOPE *e; 555738032Speter{ 555838032Speter if (tTd(51, 4)) 555990792Sgshapiro sm_dprintf("unlockqueue(%s)\n", 556038032Speter e->e_id == NULL ? "NOQUEUE" : e->e_id); 556138032Speter 556264562Sgshapiro 556338032Speter /* if there is a lock file in the envelope, close it */ 556438032Speter if (e->e_lockfp != NULL) 556590792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 556638032Speter e->e_lockfp = NULL; 556738032Speter 556838032Speter /* don't create a queue id if we don't already have one */ 556938032Speter if (e->e_id == NULL) 557038032Speter return; 557138032Speter 557238032Speter /* remove the transcript */ 557338032Speter if (LogLevel > 87) 557438032Speter sm_syslog(LOG_DEBUG, e->e_id, "unlock"); 557538032Speter if (!tTd(51, 104)) 557690792Sgshapiro (void) xunlink(queuename(e, XSCRPT_LETTER)); 557738032Speter} 557890792Sgshapiro/* 557938032Speter** SETCTLUSER -- create a controlling address 558038032Speter** 558138032Speter** Create a fake "address" given only a local login name; this is 558238032Speter** used as a "controlling user" for future recipient addresses. 558338032Speter** 558438032Speter** Parameters: 558538032Speter** user -- the user name of the controlling user. 558690792Sgshapiro** qfver -- the version stamp of this queue file. 558790792Sgshapiro** e -- envelope 558838032Speter** 558938032Speter** Returns: 559090792Sgshapiro** An address descriptor for the controlling user, 559190792Sgshapiro** using storage allocated from e->e_rpool. 559238032Speter** 559338032Speter*/ 559438032Speter 559564562Sgshapirostatic ADDRESS * 559690792Sgshapirosetctluser(user, qfver, e) 559738032Speter char *user; 559838032Speter int qfver; 559990792Sgshapiro ENVELOPE *e; 560038032Speter{ 560138032Speter register ADDRESS *a; 560238032Speter struct passwd *pw; 560338032Speter char *p; 560438032Speter 560538032Speter /* 560638032Speter ** See if this clears our concept of controlling user. 560738032Speter */ 560838032Speter 560938032Speter if (user == NULL || *user == '\0') 561038032Speter return NULL; 561138032Speter 561238032Speter /* 561338032Speter ** Set up addr fields for controlling user. 561438032Speter */ 561538032Speter 5616168515Sgshapiro a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a)); 5617168515Sgshapiro memset((char *) a, '\0', sizeof(*a)); 561838032Speter 561990792Sgshapiro if (*user == ':') 562038032Speter { 562138032Speter p = &user[1]; 562290792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, p); 562338032Speter } 562438032Speter else 562538032Speter { 562638032Speter p = strtok(user, ":"); 562790792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, user); 562838032Speter if (qfver >= 2) 562938032Speter { 563038032Speter if ((p = strtok(NULL, ":")) != NULL) 563138032Speter a->q_uid = atoi(p); 563238032Speter if ((p = strtok(NULL, ":")) != NULL) 563338032Speter a->q_gid = atoi(p); 563438032Speter if ((p = strtok(NULL, ":")) != NULL) 563580785Sgshapiro { 563680785Sgshapiro char *o; 563780785Sgshapiro 563838032Speter a->q_flags |= QGOODUID; 563980785Sgshapiro 564080785Sgshapiro /* if there is another ':': restore it */ 564180785Sgshapiro if ((o = strtok(NULL, ":")) != NULL && o > p) 564280785Sgshapiro o[-1] = ':'; 564380785Sgshapiro } 564438032Speter } 564538032Speter else if ((pw = sm_getpwnam(user)) != NULL) 564638032Speter { 564766494Sgshapiro if (*pw->pw_dir == '\0') 564866494Sgshapiro a->q_home = NULL; 564966494Sgshapiro else if (strcmp(pw->pw_dir, "/") == 0) 565038032Speter a->q_home = ""; 565138032Speter else 565290792Sgshapiro a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir); 565338032Speter a->q_uid = pw->pw_uid; 565438032Speter a->q_gid = pw->pw_gid; 565538032Speter a->q_flags |= QGOODUID; 565638032Speter } 565738032Speter } 565838032Speter 565964562Sgshapiro a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 566038032Speter a->q_mailer = LocalMailer; 566138032Speter if (p == NULL) 566290792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); 566338032Speter else 566490792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p); 566538032Speter return a; 566638032Speter} 566790792Sgshapiro/* 566890792Sgshapiro** LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know 566938032Speter** 567038032Speter** Parameters: 567138032Speter** e -- the envelope (e->e_id will be used). 567238032Speter** why -- reported to whomever can hear. 567338032Speter** 567438032Speter** Returns: 567538032Speter** none. 567638032Speter*/ 567738032Speter 567838032Spetervoid 567938032Speterloseqfile(e, why) 568038032Speter register ENVELOPE *e; 568138032Speter char *why; 568238032Speter{ 568390792Sgshapiro bool loseit = true; 568438032Speter char *p; 568564562Sgshapiro char buf[MAXPATHLEN]; 568638032Speter 568738032Speter if (e == NULL || e->e_id == NULL) 568838032Speter return; 568990792Sgshapiro p = queuename(e, ANYQFL_LETTER); 5690168515Sgshapiro if (sm_strlcpy(buf, p, sizeof(buf)) >= sizeof(buf)) 569138032Speter return; 569290792Sgshapiro if (!bitset(EF_INQUEUE, e->e_flags)) 569390792Sgshapiro queueup(e, false, true); 569490792Sgshapiro else if (QueueMode == QM_LOST) 569590792Sgshapiro loseit = false; 569690792Sgshapiro 569790792Sgshapiro /* if already lost, no need to re-lose */ 569890792Sgshapiro if (loseit) 569990792Sgshapiro { 570090792Sgshapiro p = queuename(e, LOSEQF_LETTER); 570190792Sgshapiro if (rename(buf, p) < 0) 5702285229Sgshapiro syserr("cannot rename(%s, %s), uid=%ld", 5703285229Sgshapiro buf, p, (long) geteuid()); 570490792Sgshapiro else if (LogLevel > 0) 570590792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 570690792Sgshapiro "Losing %s: %s", buf, why); 570790792Sgshapiro } 570890792Sgshapiro if (e->e_dfp != NULL) 570990792Sgshapiro { 571090792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 571190792Sgshapiro e->e_dfp = NULL; 571290792Sgshapiro } 571390792Sgshapiro e->e_flags &= ~EF_HAS_DF; 571438032Speter} 571590792Sgshapiro/* 571690792Sgshapiro** NAME2QID -- translate a queue group name to a queue group id 571790792Sgshapiro** 571890792Sgshapiro** Parameters: 571990792Sgshapiro** queuename -- name of queue group. 572090792Sgshapiro** 572190792Sgshapiro** Returns: 572290792Sgshapiro** queue group id if found. 572390792Sgshapiro** NOQGRP otherwise. 572490792Sgshapiro*/ 572590792Sgshapiro 572690792Sgshapiroint 572790792Sgshapironame2qid(queuename) 572890792Sgshapiro char *queuename; 572990792Sgshapiro{ 573090792Sgshapiro register STAB *s; 573190792Sgshapiro 573290792Sgshapiro s = stab(queuename, ST_QUEUE, ST_FIND); 573390792Sgshapiro if (s == NULL) 573490792Sgshapiro return NOQGRP; 573590792Sgshapiro return s->s_quegrp->qg_index; 573690792Sgshapiro} 573790792Sgshapiro/* 573864562Sgshapiro** QID_PRINTNAME -- create externally printable version of queue id 573964562Sgshapiro** 574064562Sgshapiro** Parameters: 574164562Sgshapiro** e -- the envelope. 574264562Sgshapiro** 574364562Sgshapiro** Returns: 574464562Sgshapiro** a printable version 574564562Sgshapiro*/ 574664562Sgshapiro 574764562Sgshapirochar * 574864562Sgshapiroqid_printname(e) 574964562Sgshapiro ENVELOPE *e; 575064562Sgshapiro{ 575164562Sgshapiro char *id; 575264562Sgshapiro static char idbuf[MAXQFNAME + 34]; 575364562Sgshapiro 575464562Sgshapiro if (e == NULL) 575564562Sgshapiro return ""; 575664562Sgshapiro 575764562Sgshapiro if (e->e_id == NULL) 575864562Sgshapiro id = ""; 575964562Sgshapiro else 576064562Sgshapiro id = e->e_id; 576164562Sgshapiro 576290792Sgshapiro if (e->e_qdir == NOQDIR) 576364562Sgshapiro return id; 576464562Sgshapiro 5765168515Sgshapiro (void) sm_snprintf(idbuf, sizeof(idbuf), "%.32s/%s", 576690792Sgshapiro Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name, 576790792Sgshapiro id); 576864562Sgshapiro return idbuf; 576964562Sgshapiro} 577090792Sgshapiro/* 577190792Sgshapiro** QID_PRINTQUEUE -- create full version of queue directory for data files 577264562Sgshapiro** 577364562Sgshapiro** Parameters: 577490792Sgshapiro** qgrp -- index in queue group. 577590792Sgshapiro** qdir -- the short version of the queue directory 577664562Sgshapiro** 577764562Sgshapiro** Returns: 577890792Sgshapiro** the full pathname to the queue (might point to a static var) 577964562Sgshapiro*/ 578064562Sgshapiro 578164562Sgshapirochar * 578290792Sgshapiroqid_printqueue(qgrp, qdir) 578390792Sgshapiro int qgrp; 578490792Sgshapiro int qdir; 578564562Sgshapiro{ 578664562Sgshapiro char *subdir; 578764562Sgshapiro static char dir[MAXPATHLEN]; 578864562Sgshapiro 578990792Sgshapiro if (qdir == NOQDIR) 579090792Sgshapiro return Queue[qgrp]->qg_qdir; 579164562Sgshapiro 579290792Sgshapiro if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0) 579364562Sgshapiro subdir = NULL; 579464562Sgshapiro else 579590792Sgshapiro subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name; 579664562Sgshapiro 5797168515Sgshapiro (void) sm_strlcpyn(dir, sizeof(dir), 4, 579890792Sgshapiro Queue[qgrp]->qg_qdir, 579964562Sgshapiro subdir == NULL ? "" : "/", 580064562Sgshapiro subdir == NULL ? "" : subdir, 580190792Sgshapiro (bitset(QP_SUBDF, 580290792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 580390792Sgshapiro ? "/df" : "")); 580464562Sgshapiro return dir; 580564562Sgshapiro} 580690792Sgshapiro 580790792Sgshapiro/* 580890792Sgshapiro** PICKQDIR -- Pick a queue directory from a queue group 580964562Sgshapiro** 581090792Sgshapiro** Parameters: 581190792Sgshapiro** qg -- queue group 581290792Sgshapiro** fsize -- file size in bytes 581390792Sgshapiro** e -- envelope, or NULL 581464562Sgshapiro** 581590792Sgshapiro** Result: 581690792Sgshapiro** NOQDIR if no queue directory in qg has enough free space to 581790792Sgshapiro** hold a file of size 'fsize', otherwise the index of 581890792Sgshapiro** a randomly selected queue directory which resides on a 581990792Sgshapiro** file system with enough disk space. 582090792Sgshapiro** XXX This could be extended to select a queuedir with 582190792Sgshapiro** a few (the fewest?) number of entries. That data 582290792Sgshapiro** is available if shared memory is used. 582364562Sgshapiro** 582490792Sgshapiro** Side Effects: 582590792Sgshapiro** If the request fails and e != NULL then sm_syslog is called. 582690792Sgshapiro*/ 582790792Sgshapiro 582890792Sgshapiroint 582990792Sgshapiropickqdir(qg, fsize, e) 583090792Sgshapiro QUEUEGRP *qg; 583190792Sgshapiro long fsize; 583290792Sgshapiro ENVELOPE *e; 583390792Sgshapiro{ 583490792Sgshapiro int qdir; 583590792Sgshapiro int i; 583690792Sgshapiro long avail = 0; 583790792Sgshapiro 583890792Sgshapiro /* Pick a random directory, as a starting point. */ 583990792Sgshapiro if (qg->qg_numqueues <= 1) 584090792Sgshapiro qdir = 0; 584190792Sgshapiro else 584290792Sgshapiro qdir = get_rand_mod(qg->qg_numqueues); 584390792Sgshapiro 5844203004Sgshapiro#if _FFR_TESTS 5845203004Sgshapiro if (tTd(4, 101)) 5846203004Sgshapiro return NOQDIR; 5847363466Sgshapiro#endif 584890792Sgshapiro if (MinBlocksFree <= 0 && fsize <= 0) 584990792Sgshapiro return qdir; 585090792Sgshapiro 585190792Sgshapiro /* 585290792Sgshapiro ** Now iterate over the queue directories, 585390792Sgshapiro ** looking for a directory with enough space for this message. 585490792Sgshapiro */ 585590792Sgshapiro 585690792Sgshapiro i = qdir; 585790792Sgshapiro do 585890792Sgshapiro { 585990792Sgshapiro QPATHS *qp = &qg->qg_qpaths[i]; 586090792Sgshapiro long needed = 0; 586190792Sgshapiro long fsavail = 0; 586290792Sgshapiro 586390792Sgshapiro if (fsize > 0) 586490792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx) 586590792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx) 586690792Sgshapiro > 0) ? 1 : 0); 586790792Sgshapiro if (MinBlocksFree > 0) 586890792Sgshapiro needed += MinBlocksFree; 586990792Sgshapiro fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx); 587090792Sgshapiro#if SM_CONF_SHM 587190792Sgshapiro if (fsavail <= 0) 587290792Sgshapiro { 587390792Sgshapiro long blksize; 587490792Sgshapiro 587590792Sgshapiro /* 587690792Sgshapiro ** might be not correctly updated, 587790792Sgshapiro ** let's try to get the info directly. 587890792Sgshapiro */ 587990792Sgshapiro 588090792Sgshapiro fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx), 588190792Sgshapiro &blksize); 588290792Sgshapiro if (fsavail < 0) 588390792Sgshapiro fsavail = 0; 588490792Sgshapiro } 588590792Sgshapiro#endif /* SM_CONF_SHM */ 588690792Sgshapiro if (needed <= fsavail) 588790792Sgshapiro return i; 588890792Sgshapiro if (avail < fsavail) 588990792Sgshapiro avail = fsavail; 589090792Sgshapiro 589190792Sgshapiro if (qg->qg_numqueues > 0) 589290792Sgshapiro i = (i + 1) % qg->qg_numqueues; 589390792Sgshapiro } while (i != qdir); 589490792Sgshapiro 589590792Sgshapiro if (e != NULL && LogLevel > 0) 589690792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 589790792Sgshapiro "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld", 589890792Sgshapiro CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, 589990792Sgshapiro fsize, MinBlocksFree, 590090792Sgshapiro qg->qg_qdir, avail); 590190792Sgshapiro return NOQDIR; 590290792Sgshapiro} 590390792Sgshapiro/* 590490792Sgshapiro** SETNEWQUEUE -- Sets a new queue group and directory 590590792Sgshapiro** 590690792Sgshapiro** Assign a queue group and directory to an envelope and store the 590790792Sgshapiro** directory in e->e_qdir. 590890792Sgshapiro** 590964562Sgshapiro** Parameters: 591064562Sgshapiro** e -- envelope to assign a queue for. 591164562Sgshapiro** 591264562Sgshapiro** Returns: 591390792Sgshapiro** true if successful 591490792Sgshapiro** false otherwise 591590792Sgshapiro** 591690792Sgshapiro** Side Effects: 591790792Sgshapiro** On success, e->e_qgrp and e->e_qdir are non-negative. 591890792Sgshapiro** On failure (not enough disk space), 591990792Sgshapiro** e->qgrp = NOQGRP, e->e_qdir = NOQDIR 592090792Sgshapiro** and usrerr() is invoked (which could raise an exception). 592164562Sgshapiro*/ 592264562Sgshapiro 592390792Sgshapirobool 592464562Sgshapirosetnewqueue(e) 592564562Sgshapiro ENVELOPE *e; 592664562Sgshapiro{ 592764562Sgshapiro if (tTd(41, 20)) 592890792Sgshapiro sm_dprintf("setnewqueue: called\n"); 592964562Sgshapiro 593090792Sgshapiro /* not set somewhere else */ 593190792Sgshapiro if (e->e_qgrp == NOQGRP) 593264562Sgshapiro { 5933102528Sgshapiro ADDRESS *q; 5934102528Sgshapiro 593590792Sgshapiro /* 5936102528Sgshapiro ** Use the queue group of the "first" recipient, as set by 593790792Sgshapiro ** the "queuegroup" rule set. If that is not defined, then 593890792Sgshapiro ** use the queue group of the mailer of the first recipient. 593990792Sgshapiro ** If that is not defined either, then use the default 594090792Sgshapiro ** queue group. 5941102528Sgshapiro ** Notice: "first" depends on the sorting of sendqueue 5942102528Sgshapiro ** in recipient(). 5943102528Sgshapiro ** To avoid problems with "bad" recipients look 5944102528Sgshapiro ** for a valid address first. 594590792Sgshapiro */ 594690792Sgshapiro 5947102528Sgshapiro q = e->e_sendqueue; 5948102528Sgshapiro while (q != NULL && 5949102528Sgshapiro (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state))) 5950102528Sgshapiro { 5951102528Sgshapiro q = q->q_next; 5952102528Sgshapiro } 5953102528Sgshapiro if (q == NULL) 595490792Sgshapiro e->e_qgrp = 0; 5955102528Sgshapiro else if (q->q_qgrp >= 0) 5956102528Sgshapiro e->e_qgrp = q->q_qgrp; 5957102528Sgshapiro else if (q->q_mailer != NULL && 5958102528Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 5959102528Sgshapiro e->e_qgrp = q->q_mailer->m_qgrp; 596090792Sgshapiro else 596190792Sgshapiro e->e_qgrp = 0; 596290792Sgshapiro e->e_dfqgrp = e->e_qgrp; 596390792Sgshapiro } 596490792Sgshapiro 596590792Sgshapiro if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir)) 596690792Sgshapiro { 596764562Sgshapiro if (tTd(41, 20)) 596890792Sgshapiro sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n", 596990792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 597090792Sgshapiro return true; 597164562Sgshapiro } 597264562Sgshapiro 597390792Sgshapiro filesys_update(); 597490792Sgshapiro e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e); 597590792Sgshapiro if (e->e_qdir == NOQDIR) 597664562Sgshapiro { 597790792Sgshapiro e->e_qgrp = NOQGRP; 597890792Sgshapiro if (!bitset(EF_FATALERRS, e->e_flags)) 597990792Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 598090792Sgshapiro e->e_flags |= EF_FATALERRS; 598190792Sgshapiro return false; 598264562Sgshapiro } 598364562Sgshapiro 598464562Sgshapiro if (tTd(41, 3)) 598590792Sgshapiro sm_dprintf("setnewqueue: Assigned queue directory %s\n", 598690792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 598790792Sgshapiro 598890792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 598990792Sgshapiro { 599090792Sgshapiro e->e_xfqgrp = e->e_qgrp; 599190792Sgshapiro e->e_xfqdir = e->e_qdir; 599290792Sgshapiro } 599390792Sgshapiro e->e_dfqdir = e->e_qdir; 599490792Sgshapiro return true; 599564562Sgshapiro} 599690792Sgshapiro/* 599764562Sgshapiro** CHKQDIR -- check a queue directory 599864562Sgshapiro** 599964562Sgshapiro** Parameters: 600064562Sgshapiro** name -- name of queue directory 600164562Sgshapiro** sff -- flags for safefile() 600264562Sgshapiro** 600364562Sgshapiro** Returns: 600464562Sgshapiro** is it a queue directory? 600564562Sgshapiro*/ 600664562Sgshapiro 6007168515Sgshapirostatic bool chkqdir __P((char *, long)); 6008168515Sgshapiro 600964562Sgshapirostatic bool 601064562Sgshapirochkqdir(name, sff) 601164562Sgshapiro char *name; 601264562Sgshapiro long sff; 601364562Sgshapiro{ 601464562Sgshapiro struct stat statb; 601564562Sgshapiro int i; 601664562Sgshapiro 601766494Sgshapiro /* skip over . and .. directories */ 601866494Sgshapiro if (name[0] == '.' && 601977349Sgshapiro (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) 602090792Sgshapiro return false; 602190792Sgshapiro#if HASLSTAT 602264562Sgshapiro if (lstat(name, &statb) < 0) 6023363466Sgshapiro#else 602464562Sgshapiro if (stat(name, &statb) < 0) 6025363466Sgshapiro#endif 602664562Sgshapiro { 602764562Sgshapiro if (tTd(41, 2)) 602890792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 602990792Sgshapiro name, sm_errstring(errno)); 603090792Sgshapiro return false; 603164562Sgshapiro } 603290792Sgshapiro#if HASLSTAT 603364562Sgshapiro if (S_ISLNK(statb.st_mode)) 603464562Sgshapiro { 603564562Sgshapiro /* 603664562Sgshapiro ** For a symlink we need to make sure the 603764562Sgshapiro ** target is a directory 603864562Sgshapiro */ 603990792Sgshapiro 604064562Sgshapiro if (stat(name, &statb) < 0) 604164562Sgshapiro { 604264562Sgshapiro if (tTd(41, 2)) 604390792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 604490792Sgshapiro name, sm_errstring(errno)); 604590792Sgshapiro return false; 604664562Sgshapiro } 604764562Sgshapiro } 604890792Sgshapiro#endif /* HASLSTAT */ 604964562Sgshapiro 605064562Sgshapiro if (!S_ISDIR(statb.st_mode)) 605164562Sgshapiro { 605264562Sgshapiro if (tTd(41, 2)) 605390792Sgshapiro sm_dprintf("chkqdir: \"%s\": Not a directory\n", 605464562Sgshapiro name); 605590792Sgshapiro return false; 605664562Sgshapiro } 605764562Sgshapiro 605864562Sgshapiro /* Print a warning if unsafe (but still use it) */ 605990792Sgshapiro /* XXX do this only if we want the warning? */ 606064562Sgshapiro i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0); 606198121Sgshapiro if (i != 0) 606298121Sgshapiro { 606398121Sgshapiro if (tTd(41, 2)) 606498121Sgshapiro sm_dprintf("chkqdir: \"%s\": Not safe: %s\n", 606598121Sgshapiro name, sm_errstring(i)); 606698121Sgshapiro#if _FFR_CHK_QUEUE 606798121Sgshapiro if (LogLevel > 8) 606898121Sgshapiro sm_syslog(LOG_WARNING, NOQID, 606998121Sgshapiro "queue directory \"%s\": Not safe: %s", 607098121Sgshapiro name, sm_errstring(i)); 607198121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 607298121Sgshapiro } 607390792Sgshapiro return true; 607464562Sgshapiro} 607590792Sgshapiro/* 607664562Sgshapiro** MULTIQUEUE_CACHE -- cache a list of paths to queues. 607764562Sgshapiro** 607864562Sgshapiro** Each potential queue is checked as the cache is built. 607964562Sgshapiro** Thereafter, each is blindly trusted. 608064562Sgshapiro** Note that we can be called again after a timeout to rebuild 608164562Sgshapiro** (although code for that is not ready yet). 608264562Sgshapiro** 608364562Sgshapiro** Parameters: 608490792Sgshapiro** basedir -- base of all queue directories. 608590792Sgshapiro** blen -- strlen(basedir). 608690792Sgshapiro** qg -- queue group. 608790792Sgshapiro** qn -- number of queue directories already cached. 608890792Sgshapiro** phash -- pointer to hash value over queue dirs. 608990792Sgshapiro#if SM_CONF_SHM 609090792Sgshapiro** only used if shared memory is active. 609190792Sgshapiro#endif * SM_CONF_SHM * 609264562Sgshapiro** 609364562Sgshapiro** Returns: 609490792Sgshapiro** new number of queue directories. 609564562Sgshapiro*/ 609664562Sgshapiro 609790792Sgshapiro#define INITIAL_SLOTS 20 609890792Sgshapiro#define ADD_SLOTS 10 609990792Sgshapiro 610090792Sgshapirostatic int 610190792Sgshapiromultiqueue_cache(basedir, blen, qg, qn, phash) 610290792Sgshapiro char *basedir; 610390792Sgshapiro int blen; 610490792Sgshapiro QUEUEGRP *qg; 610590792Sgshapiro int qn; 610690792Sgshapiro unsigned int *phash; 610764562Sgshapiro{ 610864562Sgshapiro char *cp; 610964562Sgshapiro int i, len; 611064562Sgshapiro int slotsleft = 0; 611164562Sgshapiro long sff = SFF_ANYFILE; 611264562Sgshapiro char qpath[MAXPATHLEN]; 611364562Sgshapiro char subdir[MAXPATHLEN]; 611490792Sgshapiro char prefix[MAXPATHLEN]; /* dir relative to basedir */ 611564562Sgshapiro 611664562Sgshapiro if (tTd(41, 20)) 611790792Sgshapiro sm_dprintf("multiqueue_cache: called\n"); 611864562Sgshapiro 611990792Sgshapiro /* Initialize to current directory */ 612090792Sgshapiro prefix[0] = '.'; 612190792Sgshapiro prefix[1] = '\0'; 612290792Sgshapiro if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL) 612364562Sgshapiro { 612490792Sgshapiro for (i = 0; i < qg->qg_numqueues; i++) 612564562Sgshapiro { 612690792Sgshapiro if (qg->qg_qpaths[i].qp_name != NULL) 612790792Sgshapiro (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */ 612864562Sgshapiro } 612990792Sgshapiro (void) sm_free((char *) qg->qg_qpaths); /* XXX */ 613090792Sgshapiro qg->qg_qpaths = NULL; 613190792Sgshapiro qg->qg_numqueues = 0; 613264562Sgshapiro } 613364562Sgshapiro 613464562Sgshapiro /* If running as root, allow safedirpath() checks to use privs */ 613564562Sgshapiro if (RunAsUid == 0) 613664562Sgshapiro sff |= SFF_ROOTOK; 613798121Sgshapiro#if _FFR_CHK_QUEUE 613898121Sgshapiro sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES; 613998121Sgshapiro if (!UseMSP) 614098121Sgshapiro sff |= SFF_NOGWFILES; 6141363466Sgshapiro#endif 614264562Sgshapiro 614390792Sgshapiro if (!SM_IS_DIR_START(qg->qg_qdir)) 614490792Sgshapiro { 614590792Sgshapiro /* 614690792Sgshapiro ** XXX we could add basedir, but then we have to realloc() 614790792Sgshapiro ** the string... Maybe another time. 614890792Sgshapiro */ 614990792Sgshapiro 615090792Sgshapiro syserr("QueuePath %s not absolute", qg->qg_qdir); 615190792Sgshapiro ExitStat = EX_CONFIG; 615290792Sgshapiro return qn; 615390792Sgshapiro } 615490792Sgshapiro 615590792Sgshapiro /* qpath: directory of current workgroup */ 6156168515Sgshapiro len = sm_strlcpy(qpath, qg->qg_qdir, sizeof(qpath)); 6157168515Sgshapiro if (len >= sizeof(qpath)) 615890792Sgshapiro { 615990792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 6160168515Sgshapiro qg->qg_qdir, (int) sizeof(qpath)); 616190792Sgshapiro ExitStat = EX_CONFIG; 616290792Sgshapiro return qn; 616390792Sgshapiro } 616490792Sgshapiro 616590792Sgshapiro /* begin of qpath must be same as basedir */ 616690792Sgshapiro if (strncmp(basedir, qpath, blen) != 0 && 616790792Sgshapiro (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1)) 616890792Sgshapiro { 616990792Sgshapiro syserr("QueuePath %s not subpath of QueueDirectory %s", 617090792Sgshapiro qpath, basedir); 617190792Sgshapiro ExitStat = EX_CONFIG; 617290792Sgshapiro return qn; 617390792Sgshapiro } 617490792Sgshapiro 617590792Sgshapiro /* Do we have a nested subdirectory? */ 617690792Sgshapiro if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL) 617790792Sgshapiro { 617890792Sgshapiro 617990792Sgshapiro /* Copy subdirectory into prefix for later use */ 6180168515Sgshapiro if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof(prefix)) >= 6181168515Sgshapiro sizeof(prefix)) 618290792Sgshapiro { 618390792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 6184168515Sgshapiro qg->qg_qdir, (int) sizeof(qpath)); 618590792Sgshapiro ExitStat = EX_CONFIG; 618690792Sgshapiro return qn; 618790792Sgshapiro } 618890792Sgshapiro cp = SM_LAST_DIR_DELIM(prefix); 618990792Sgshapiro SM_ASSERT(cp != NULL); 619090792Sgshapiro *cp = '\0'; /* cut off trailing / */ 619190792Sgshapiro } 619290792Sgshapiro 619390792Sgshapiro /* This is guaranteed by the basedir check above */ 619490792Sgshapiro SM_ASSERT(len >= blen - 1); 619590792Sgshapiro cp = &qpath[len - 1]; 619664562Sgshapiro if (*cp == '*') 619764562Sgshapiro { 619890792Sgshapiro register DIR *dp; 619990792Sgshapiro register struct dirent *d; 620090792Sgshapiro int off; 620190792Sgshapiro char *delim; 620290792Sgshapiro char relpath[MAXPATHLEN]; 620390792Sgshapiro 620490792Sgshapiro *cp = '\0'; /* Overwrite wildcard */ 620590792Sgshapiro if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL) 620664562Sgshapiro { 620764562Sgshapiro syserr("QueueDirectory: can not wildcard relative path"); 620864562Sgshapiro if (tTd(41, 2)) 620990792Sgshapiro sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n", 621071345Sgshapiro qpath); 621164562Sgshapiro ExitStat = EX_CONFIG; 621290792Sgshapiro return qn; 621364562Sgshapiro } 621464562Sgshapiro if (cp == qpath) 621564562Sgshapiro { 621664562Sgshapiro /* 621764562Sgshapiro ** Special case of top level wildcard, like /foo* 621890792Sgshapiro ** Change to //foo* 621964562Sgshapiro */ 622064562Sgshapiro 6221168515Sgshapiro (void) sm_strlcpy(qpath + 1, qpath, sizeof(qpath) - 1); 622264562Sgshapiro ++cp; 622364562Sgshapiro } 622490792Sgshapiro delim = cp; 622590792Sgshapiro *(cp++) = '\0'; /* Replace / with \0 */ 622690792Sgshapiro len = strlen(cp); /* Last component of queue directory */ 622764562Sgshapiro 622890792Sgshapiro /* 622990792Sgshapiro ** Path relative to basedir, with trailing / 623090792Sgshapiro ** It will be modified below to specify the subdirectories 623190792Sgshapiro ** so they can be opened without chdir(). 623290792Sgshapiro */ 623390792Sgshapiro 6234168515Sgshapiro off = sm_strlcpyn(relpath, sizeof(relpath), 2, prefix, "/"); 6235168515Sgshapiro SM_ASSERT(off < sizeof(relpath)); 623690792Sgshapiro 623764562Sgshapiro if (tTd(41, 2)) 623890792Sgshapiro sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n", 623990792Sgshapiro relpath, cp); 624064562Sgshapiro 624190792Sgshapiro /* It is always basedir: we don't need to store it per group */ 624290792Sgshapiro /* XXX: optimize this! -> one more global? */ 624390792Sgshapiro qg->qg_qdir = newstr(basedir); 624490792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */ 624564562Sgshapiro 624664562Sgshapiro /* 624764562Sgshapiro ** XXX Should probably wrap this whole loop in a timeout 624864562Sgshapiro ** in case some wag decides to NFS mount the queues. 624964562Sgshapiro */ 625064562Sgshapiro 625190792Sgshapiro /* Test path to get warning messages. */ 625290792Sgshapiro if (qn == 0) 625364562Sgshapiro { 625490792Sgshapiro /* XXX qg_runasuid and qg_runasgid for specials? */ 625590792Sgshapiro i = safedirpath(basedir, RunAsUid, RunAsGid, NULL, 625690792Sgshapiro sff, 0, 0); 625790792Sgshapiro if (i != 0 && tTd(41, 2)) 625890792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", 625990792Sgshapiro basedir, sm_errstring(i)); 626064562Sgshapiro } 626164562Sgshapiro 626290792Sgshapiro if ((dp = opendir(prefix)) == NULL) 626364562Sgshapiro { 626490792Sgshapiro syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix); 626564562Sgshapiro if (tTd(41, 2)) 626690792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n", 626790792Sgshapiro qg->qg_qdir, prefix, 626890792Sgshapiro sm_errstring(errno)); 626964562Sgshapiro ExitStat = EX_CONFIG; 627090792Sgshapiro return qn; 627164562Sgshapiro } 627264562Sgshapiro while ((d = readdir(dp)) != NULL) 627364562Sgshapiro { 6274168515Sgshapiro /* Skip . and .. directories */ 6275168515Sgshapiro if (strcmp(d->d_name, ".") == 0 || 6276168515Sgshapiro strcmp(d->d_name, "..") == 0) 6277168515Sgshapiro continue; 6278168515Sgshapiro 627990792Sgshapiro i = strlen(d->d_name); 628090792Sgshapiro if (i < len || strncmp(d->d_name, cp, len) != 0) 628164562Sgshapiro { 628264562Sgshapiro if (tTd(41, 5)) 628390792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\", skipped\n", 628464562Sgshapiro d->d_name); 628564562Sgshapiro continue; 628664562Sgshapiro } 628790792Sgshapiro 628890792Sgshapiro /* Create relative pathname: prefix + local directory */ 628990792Sgshapiro i = sizeof(relpath) - off; 629090792Sgshapiro if (sm_strlcpy(relpath + off, d->d_name, i) >= i) 629190792Sgshapiro continue; /* way too long */ 629290792Sgshapiro 629390792Sgshapiro if (!chkqdir(relpath, sff)) 629464562Sgshapiro continue; 629564562Sgshapiro 629690792Sgshapiro if (qg->qg_qpaths == NULL) 629764562Sgshapiro { 629890792Sgshapiro slotsleft = INITIAL_SLOTS; 6299168515Sgshapiro qg->qg_qpaths = (QPATHS *)xalloc((sizeof(*qg->qg_qpaths)) * 630090792Sgshapiro slotsleft); 630190792Sgshapiro qg->qg_numqueues = 0; 630264562Sgshapiro } 630364562Sgshapiro else if (slotsleft < 1) 630464562Sgshapiro { 630590792Sgshapiro qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths, 6306168515Sgshapiro (sizeof(*qg->qg_qpaths)) * 630790792Sgshapiro (qg->qg_numqueues + 630890792Sgshapiro ADD_SLOTS)); 630990792Sgshapiro if (qg->qg_qpaths == NULL) 631064562Sgshapiro { 631164562Sgshapiro (void) closedir(dp); 631290792Sgshapiro return qn; 631364562Sgshapiro } 631490792Sgshapiro slotsleft += ADD_SLOTS; 631564562Sgshapiro } 631664562Sgshapiro 631764562Sgshapiro /* check subdirs */ 631890792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB; 631964562Sgshapiro 632090792Sgshapiro#define CHKRSUBDIR(name, flag) \ 6321168515Sgshapiro (void) sm_strlcpyn(subdir, sizeof(subdir), 3, relpath, "/", name); \ 632290792Sgshapiro if (chkqdir(subdir, sff)) \ 632390792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \ 632490792Sgshapiro else 632564562Sgshapiro 632664562Sgshapiro 632790792Sgshapiro CHKRSUBDIR("qf", QP_SUBQF); 632890792Sgshapiro CHKRSUBDIR("df", QP_SUBDF); 632990792Sgshapiro CHKRSUBDIR("xf", QP_SUBXF); 633090792Sgshapiro 633164562Sgshapiro /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */ 633264562Sgshapiro /* maybe even - 17 (subdirs) */ 633390792Sgshapiro 633490792Sgshapiro if (prefix[0] != '.') 633590792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 633690792Sgshapiro newstr(relpath); 633790792Sgshapiro else 633890792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 633990792Sgshapiro newstr(d->d_name); 634090792Sgshapiro 634164562Sgshapiro if (tTd(41, 2)) 634290792Sgshapiro sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", 634390792Sgshapiro qg->qg_numqueues, relpath, 634490792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs); 634590792Sgshapiro#if SM_CONF_SHM 634690792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn; 634790792Sgshapiro *phash = hash_q(relpath, *phash); 6348363466Sgshapiro#endif 634990792Sgshapiro qg->qg_numqueues++; 635090792Sgshapiro ++qn; 635164562Sgshapiro slotsleft--; 635264562Sgshapiro } 635364562Sgshapiro (void) closedir(dp); 635490792Sgshapiro 635590792Sgshapiro /* undo damage */ 635690792Sgshapiro *delim = '/'; 635764562Sgshapiro } 635890792Sgshapiro if (qg->qg_numqueues == 0) 635964562Sgshapiro { 6360168515Sgshapiro qg->qg_qpaths = (QPATHS *) xalloc(sizeof(*qg->qg_qpaths)); 636164562Sgshapiro 636264562Sgshapiro /* test path to get warning messages */ 636390792Sgshapiro i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0); 636490792Sgshapiro if (i == ENOENT) 636564562Sgshapiro { 636690792Sgshapiro syserr("can not opendir(%s)", qpath); 636764562Sgshapiro if (tTd(41, 2)) 636890792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", 636990792Sgshapiro qpath, sm_errstring(i)); 637064562Sgshapiro ExitStat = EX_CONFIG; 637190792Sgshapiro return qn; 637264562Sgshapiro } 637364562Sgshapiro 637490792Sgshapiro qg->qg_qpaths[0].qp_subdirs = QP_NOSUB; 637590792Sgshapiro qg->qg_numqueues = 1; 637690792Sgshapiro 637764562Sgshapiro /* check subdirs */ 637890792Sgshapiro#define CHKSUBDIR(name, flag) \ 6379168515Sgshapiro (void) sm_strlcpyn(subdir, sizeof(subdir), 3, qg->qg_qdir, "/", name); \ 638090792Sgshapiro if (chkqdir(subdir, sff)) \ 638190792Sgshapiro qg->qg_qpaths[0].qp_subdirs |= flag; \ 638290792Sgshapiro else 638364562Sgshapiro 638490792Sgshapiro CHKSUBDIR("qf", QP_SUBQF); 638590792Sgshapiro CHKSUBDIR("df", QP_SUBDF); 638690792Sgshapiro CHKSUBDIR("xf", QP_SUBXF); 638764562Sgshapiro 638890792Sgshapiro if (qg->qg_qdir[blen - 1] != '\0' && 638990792Sgshapiro qg->qg_qdir[blen] != '\0') 639090792Sgshapiro { 639190792Sgshapiro /* 639290792Sgshapiro ** Copy the last component into qpaths and 639390792Sgshapiro ** cut off qdir 639490792Sgshapiro */ 639590792Sgshapiro 639690792Sgshapiro qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen); 639790792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; 639890792Sgshapiro } 639990792Sgshapiro else 640090792Sgshapiro qg->qg_qpaths[0].qp_name = newstr("."); 640190792Sgshapiro 640290792Sgshapiro#if SM_CONF_SHM 640390792Sgshapiro qg->qg_qpaths[0].qp_idx = qn; 640490792Sgshapiro *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash); 6405363466Sgshapiro#endif 640690792Sgshapiro ++qn; 640764562Sgshapiro } 640890792Sgshapiro return qn; 640964562Sgshapiro} 641064562Sgshapiro 641190792Sgshapiro/* 641290792Sgshapiro** FILESYS_FIND -- find entry in FileSys table, or add new one 641390792Sgshapiro** 641490792Sgshapiro** Given the pathname of a directory, determine the file system 641590792Sgshapiro** in which that directory resides, and return a pointer to the 641690792Sgshapiro** entry in the FileSys table that describes the file system. 641790792Sgshapiro** A new entry is added if necessary (and requested). 641890792Sgshapiro** If the directory does not exist, -1 is returned. 641990792Sgshapiro** 642090792Sgshapiro** Parameters: 6421157001Sgshapiro** name -- name of directory (must be persistent!) 6422157001Sgshapiro** path -- pathname of directory (name plus maybe "/df") 642390792Sgshapiro** add -- add to structure if not found. 642490792Sgshapiro** 642590792Sgshapiro** Returns: 642690792Sgshapiro** >=0: found: index in file system table 642790792Sgshapiro** <0: some error, i.e., 642890792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 642990792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 643090792Sgshapiro** FSF_NOT_FOUND: not in list 643190792Sgshapiro*/ 643290792Sgshapiro 6433168515Sgshapirostatic short filesys_find __P((const char *, const char *, bool)); 643490792Sgshapiro 643590792Sgshapiro#define FSF_NOT_FOUND (-1) 643690792Sgshapiro#define FSF_STAT_FAIL (-2) 643790792Sgshapiro#define FSF_TOO_MANY (-3) 643890792Sgshapiro 643990792Sgshapirostatic short 6440157001Sgshapirofilesys_find(name, path, add) 6441168515Sgshapiro const char *name; 6442168515Sgshapiro const char *path; 644390792Sgshapiro bool add; 644490792Sgshapiro{ 644590792Sgshapiro struct stat st; 644690792Sgshapiro short i; 644790792Sgshapiro 644890792Sgshapiro if (stat(path, &st) < 0) 644990792Sgshapiro { 645090792Sgshapiro syserr("cannot stat queue directory %s", path); 645190792Sgshapiro return FSF_STAT_FAIL; 645290792Sgshapiro } 645390792Sgshapiro for (i = 0; i < NumFileSys; ++i) 645490792Sgshapiro { 645590792Sgshapiro if (FILE_SYS_DEV(i) == st.st_dev) 6456161389Sgshapiro { 6457161389Sgshapiro /* 6458161389Sgshapiro ** Make sure the file system (FS) name is set: 6459161389Sgshapiro ** even though the source code indicates that 6460161389Sgshapiro ** FILE_SYS_DEV() is only set below, it could be 6461161389Sgshapiro ** set via shared memory, hence we need to perform 6462161389Sgshapiro ** this check/assignment here. 6463161389Sgshapiro */ 6464161389Sgshapiro 6465161389Sgshapiro if (NULL == FILE_SYS_NAME(i)) 6466161389Sgshapiro FILE_SYS_NAME(i) = name; 646790792Sgshapiro return i; 6468161389Sgshapiro } 646990792Sgshapiro } 647090792Sgshapiro if (i >= MAXFILESYS) 647190792Sgshapiro { 647290792Sgshapiro syserr("too many queue file systems (%d max)", MAXFILESYS); 647390792Sgshapiro return FSF_TOO_MANY; 647490792Sgshapiro } 647590792Sgshapiro if (!add) 647690792Sgshapiro return FSF_NOT_FOUND; 647790792Sgshapiro 647890792Sgshapiro ++NumFileSys; 6479157001Sgshapiro FILE_SYS_NAME(i) = name; 648090792Sgshapiro FILE_SYS_DEV(i) = st.st_dev; 648190792Sgshapiro FILE_SYS_AVAIL(i) = 0; 648290792Sgshapiro FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */ 648390792Sgshapiro return i; 648490792Sgshapiro} 648590792Sgshapiro 648690792Sgshapiro/* 648790792Sgshapiro** FILESYS_SETUP -- set up mapping from queue directories to file systems 648890792Sgshapiro** 648990792Sgshapiro** This data structure is used to efficiently check the amount of 649090792Sgshapiro** free space available in a set of queue directories. 649190792Sgshapiro** 649290792Sgshapiro** Parameters: 649390792Sgshapiro** add -- initialize structure if necessary. 649490792Sgshapiro** 649590792Sgshapiro** Returns: 649690792Sgshapiro** 0: success 649790792Sgshapiro** <0: some error, i.e., 649890792Sgshapiro** FSF_NOT_FOUND: not in list 649990792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 650090792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 650190792Sgshapiro*/ 650290792Sgshapiro 650390792Sgshapirostatic int filesys_setup __P((bool)); 650490792Sgshapiro 650590792Sgshapirostatic int 650690792Sgshapirofilesys_setup(add) 650790792Sgshapiro bool add; 650890792Sgshapiro{ 650990792Sgshapiro int i, j; 651090792Sgshapiro short fs; 651190792Sgshapiro int ret; 651290792Sgshapiro 651390792Sgshapiro ret = 0; 651490792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 651590792Sgshapiro { 651690792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; ++j) 651790792Sgshapiro { 651890792Sgshapiro QPATHS *qp = &Queue[i]->qg_qpaths[j]; 6519157001Sgshapiro char qddf[MAXPATHLEN]; 652090792Sgshapiro 6521168515Sgshapiro (void) sm_strlcpyn(qddf, sizeof(qddf), 2, qp->qp_name, 6522157001Sgshapiro (bitset(QP_SUBDF, qp->qp_subdirs) 6523157001Sgshapiro ? "/df" : "")); 6524157001Sgshapiro fs = filesys_find(qp->qp_name, qddf, add); 652590792Sgshapiro if (fs >= 0) 652690792Sgshapiro qp->qp_fsysidx = fs; 652790792Sgshapiro else 652890792Sgshapiro qp->qp_fsysidx = 0; 652990792Sgshapiro if (fs < ret) 653090792Sgshapiro ret = fs; 653190792Sgshapiro } 653290792Sgshapiro } 653390792Sgshapiro return ret; 653490792Sgshapiro} 653590792Sgshapiro 653690792Sgshapiro/* 653790792Sgshapiro** FILESYS_UPDATE -- update amount of free space on all file systems 653890792Sgshapiro** 653990792Sgshapiro** The FileSys table is used to cache the amount of free space 654090792Sgshapiro** available on all queue directory file systems. 654190792Sgshapiro** This function updates the cached information if it has expired. 654290792Sgshapiro** 654390792Sgshapiro** Parameters: 654490792Sgshapiro** none. 654590792Sgshapiro** 654690792Sgshapiro** Returns: 654790792Sgshapiro** none. 654890792Sgshapiro** 654990792Sgshapiro** Side Effects: 655090792Sgshapiro** Updates FileSys table. 655190792Sgshapiro*/ 655290792Sgshapiro 655390792Sgshapirovoid 655490792Sgshapirofilesys_update() 655590792Sgshapiro{ 655690792Sgshapiro int i; 655790792Sgshapiro long avail, blksize; 655890792Sgshapiro time_t now; 655990792Sgshapiro static time_t nextupdate = 0; 656090792Sgshapiro 656190792Sgshapiro#if SM_CONF_SHM 6562168515Sgshapiro /* 6563168515Sgshapiro ** Only the daemon updates the shared memory, i.e., 6564168515Sgshapiro ** if shared memory is available but the pid is not the 6565168515Sgshapiro ** one of the daemon, then don't do anything. 6566168515Sgshapiro */ 6567168515Sgshapiro 6568161389Sgshapiro if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid) 656990792Sgshapiro return; 657090792Sgshapiro#endif /* SM_CONF_SHM */ 657190792Sgshapiro now = curtime(); 657290792Sgshapiro if (now < nextupdate) 657390792Sgshapiro return; 657490792Sgshapiro nextupdate = now + FILESYS_UPDATE_INTERVAL; 657590792Sgshapiro for (i = 0; i < NumFileSys; ++i) 657690792Sgshapiro { 657790792Sgshapiro FILESYS *fs = &FILE_SYS(i); 657890792Sgshapiro 657990792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 658090792Sgshapiro if (avail < 0 || blksize <= 0) 658190792Sgshapiro { 658290792Sgshapiro if (LogLevel > 5) 658390792Sgshapiro sm_syslog(LOG_ERR, NOQID, 658490792Sgshapiro "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld", 658590792Sgshapiro sm_errstring(errno), 658690792Sgshapiro FILE_SYS_NAME(i), avail, blksize); 658790792Sgshapiro fs->fs_avail = 0; 658890792Sgshapiro fs->fs_blksize = 1024; /* avoid divide by zero */ 658990792Sgshapiro nextupdate = now + 2; /* let's do this soon again */ 659090792Sgshapiro } 659190792Sgshapiro else 659290792Sgshapiro { 659390792Sgshapiro fs->fs_avail = avail; 659490792Sgshapiro fs->fs_blksize = blksize; 659590792Sgshapiro } 659690792Sgshapiro } 659790792Sgshapiro} 659890792Sgshapiro 659990792Sgshapiro#if _FFR_ANY_FREE_FS 660090792Sgshapiro/* 660190792Sgshapiro** FILESYS_FREE -- check whether there is at least one fs with enough space. 660290792Sgshapiro** 660390792Sgshapiro** Parameters: 660490792Sgshapiro** fsize -- file size in bytes 660590792Sgshapiro** 660690792Sgshapiro** Returns: 660790792Sgshapiro** true iff there is one fs with more than fsize bytes free. 660890792Sgshapiro*/ 660990792Sgshapiro 661090792Sgshapirobool 661190792Sgshapirofilesys_free(fsize) 661290792Sgshapiro long fsize; 661390792Sgshapiro{ 661490792Sgshapiro int i; 661590792Sgshapiro 661690792Sgshapiro if (fsize <= 0) 661790792Sgshapiro return true; 661890792Sgshapiro for (i = 0; i < NumFileSys; ++i) 661990792Sgshapiro { 662090792Sgshapiro long needed = 0; 662190792Sgshapiro 662290792Sgshapiro if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0) 662390792Sgshapiro continue; 662490792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(i) 662590792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(i) 662690792Sgshapiro > 0) ? 1 : 0) 662790792Sgshapiro + MinBlocksFree; 662890792Sgshapiro if (needed <= FILE_SYS_AVAIL(i)) 662990792Sgshapiro return true; 663090792Sgshapiro } 663190792Sgshapiro return false; 663290792Sgshapiro} 663390792Sgshapiro#endif /* _FFR_ANY_FREE_FS */ 663490792Sgshapiro 663590792Sgshapiro/* 663690792Sgshapiro** DISK_STATUS -- show amount of free space in queue directories 663790792Sgshapiro** 663890792Sgshapiro** Parameters: 663990792Sgshapiro** out -- output file pointer. 664090792Sgshapiro** prefix -- string to output in front of each line. 664190792Sgshapiro** 664290792Sgshapiro** Returns: 664390792Sgshapiro** none. 664490792Sgshapiro*/ 664590792Sgshapiro 664690792Sgshapirovoid 664790792Sgshapirodisk_status(out, prefix) 664890792Sgshapiro SM_FILE_T *out; 664990792Sgshapiro char *prefix; 665090792Sgshapiro{ 665190792Sgshapiro int i; 665290792Sgshapiro long avail, blksize; 665390792Sgshapiro long free; 665490792Sgshapiro 665590792Sgshapiro for (i = 0; i < NumFileSys; ++i) 665690792Sgshapiro { 665790792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 665890792Sgshapiro if (avail >= 0 && blksize > 0) 665990792Sgshapiro { 666090792Sgshapiro free = (long)((double) avail * 666190792Sgshapiro ((double) blksize / 1024)); 666290792Sgshapiro } 666390792Sgshapiro else 666490792Sgshapiro free = -1; 666590792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 666690792Sgshapiro "%s%d/%s/%ld\r\n", 666790792Sgshapiro prefix, i, 666890792Sgshapiro FILE_SYS_NAME(i), 666990792Sgshapiro free); 667090792Sgshapiro } 667190792Sgshapiro} 667290792Sgshapiro 667390792Sgshapiro#if SM_CONF_SHM 6674147078Sgshapiro 667590792Sgshapiro/* 6676147078Sgshapiro** INIT_SEM -- initialize semaphore system 6677147078Sgshapiro** 6678147078Sgshapiro** Parameters: 6679147078Sgshapiro** owner -- is this the owner of semaphores? 6680147078Sgshapiro** 6681147078Sgshapiro** Returns: 6682147078Sgshapiro** none. 6683147078Sgshapiro*/ 6684147078Sgshapiro 6685363466Sgshapiro#if _FFR_USE_SEM_LOCKING && SM_CONF_SEM 6686147078Sgshapirostatic int SemId = -1; /* Semaphore Id */ 6687147078Sgshapiroint SemKey = SM_SEM_KEY; 6688363466Sgshapiro# define SEM_LOCK(r) \ 6689363466Sgshapiro do \ 6690363466Sgshapiro { \ 6691363466Sgshapiro if (SemId >= 0) \ 6692363466Sgshapiro r = sm_sem_acq(SemId, 0, 1); \ 6693363466Sgshapiro } while (0) 6694363466Sgshapiro# define SEM_UNLOCK(r) \ 6695363466Sgshapiro do \ 6696363466Sgshapiro { \ 6697363466Sgshapiro if (SemId >= 0 && r >= 0) \ 6698363466Sgshapiro r = sm_sem_rel(SemId, 0, 1); \ 6699363466Sgshapiro } while (0) 6700363466Sgshapiro#else /* _FFR_USE_SEM_LOCKING && SM_CONF_SEM */ 6701363466Sgshapiro# define SEM_LOCK(r) 6702363466Sgshapiro# define SEM_UNLOCK(r) 6703363466Sgshapiro#endif /* _FFR_USE_SEM_LOCKING && SM_CONF_SEM */ 6704147078Sgshapiro 6705147078Sgshapirostatic void init_sem __P((bool)); 6706147078Sgshapiro 6707147078Sgshapirostatic void 6708147078Sgshapiroinit_sem(owner) 6709147078Sgshapiro bool owner; 6710147078Sgshapiro{ 6711147078Sgshapiro#if _FFR_USE_SEM_LOCKING 6712147078Sgshapiro#if SM_CONF_SEM 6713147078Sgshapiro SemId = sm_sem_start(SemKey, 1, 0, owner); 6714147078Sgshapiro if (SemId < 0) 6715147078Sgshapiro { 6716147078Sgshapiro sm_syslog(LOG_ERR, NOQID, 6717182352Sgshapiro "func=init_sem, sem_key=%ld, sm_sem_start=%d, error=%s", 6718182352Sgshapiro (long) SemKey, SemId, sm_errstring(-SemId)); 6719147078Sgshapiro return; 6720147078Sgshapiro } 6721203004Sgshapiro if (owner && RunAsUid != 0) 6722203004Sgshapiro { 6723203004Sgshapiro int r; 6724203004Sgshapiro 6725203004Sgshapiro r = sm_semsetowner(SemId, RunAsUid, RunAsGid, 0660); 6726203004Sgshapiro if (r != 0) 6727203004Sgshapiro sm_syslog(LOG_ERR, NOQID, 6728285229Sgshapiro "key=%ld, sm_semsetowner=%d, RunAsUid=%ld, RunAsGid=%ld", 6729285229Sgshapiro (long) SemKey, r, (long) RunAsUid, (long) RunAsGid); 6730203004Sgshapiro } 6731147078Sgshapiro#endif /* SM_CONF_SEM */ 6732147078Sgshapiro#endif /* _FFR_USE_SEM_LOCKING */ 6733147078Sgshapiro return; 6734147078Sgshapiro} 6735147078Sgshapiro 6736147078Sgshapiro/* 6737147078Sgshapiro** STOP_SEM -- stop semaphore system 6738147078Sgshapiro** 6739147078Sgshapiro** Parameters: 6740147078Sgshapiro** owner -- is this the owner of semaphores? 6741147078Sgshapiro** 6742147078Sgshapiro** Returns: 6743147078Sgshapiro** none. 6744147078Sgshapiro*/ 6745147078Sgshapiro 6746147078Sgshapirostatic void stop_sem __P((bool)); 6747147078Sgshapiro 6748147078Sgshapirostatic void 6749147078Sgshapirostop_sem(owner) 6750147078Sgshapiro bool owner; 6751147078Sgshapiro{ 6752147078Sgshapiro#if _FFR_USE_SEM_LOCKING 6753147078Sgshapiro#if SM_CONF_SEM 6754147078Sgshapiro if (owner && SemId >= 0) 6755147078Sgshapiro sm_sem_stop(SemId); 6756363466Sgshapiro#endif 6757147078Sgshapiro#endif /* _FFR_USE_SEM_LOCKING */ 6758147078Sgshapiro return; 6759147078Sgshapiro} 6760147078Sgshapiro 6761363466Sgshapiro# if _FFR_OCC 6762147078Sgshapiro/* 6763363466Sgshapiro** Todo: call occ_close() 6764363466Sgshapiro** when closing a connection to decrease #open connections (and rate!) 6765363466Sgshapiro** (currently done as hack in deliver()) 6766363466Sgshapiro** must also be done if connection couldn't be opened (see daemon.c: OCC_CLOSE) 6767363466Sgshapiro*/ 6768363466Sgshapiro 6769363466Sgshapiro/* 6770363466Sgshapiro** OCC_EXCEEDED -- is an outgoing connection limit exceeded? 6771363466Sgshapiro** 6772363466Sgshapiro** Parameters: 6773363466Sgshapiro** e -- envelope 6774363466Sgshapiro** mci -- mail connection information 6775363466Sgshapiro** host -- name of host 6776363466Sgshapiro** addr -- address of host 6777363466Sgshapiro** 6778363466Sgshapiro** Returns: 6779363466Sgshapiro** true iff an outgoing connection limit is exceeded 6780363466Sgshapiro*/ 6781363466Sgshapiro 6782363466Sgshapirobool 6783363466Sgshapiroocc_exceeded(e, mci, host, addr) 6784363466Sgshapiro ENVELOPE *e; 6785363466Sgshapiro MCI *mci; 6786363466Sgshapiro const char *host; 6787363466Sgshapiro SOCKADDR *addr; 6788363466Sgshapiro{ 6789363466Sgshapiro time_t now; 6790363466Sgshapiro bool exc; 6791363466Sgshapiro int r, ratelimit, conclimit; 6792363466Sgshapiro char *limit; /* allocated from e_rpool by rscheck(), no need to free() */ 6793363466Sgshapiro 6794363466Sgshapiro/* if necessary, some error checking for a number could be done here */ 6795363466Sgshapiro#define STR2INT(r, limit, val) \ 6796363466Sgshapiro do \ 6797363466Sgshapiro { \ 6798363466Sgshapiro if ((r) == EX_OK && (limit) != NULL) \ 6799363466Sgshapiro (val) = atoi((limit)); \ 6800363466Sgshapiro } while (0); 6801363466Sgshapiro 6802363466Sgshapiro if (occ == NULL || e == NULL) 6803363466Sgshapiro return false; 6804363466Sgshapiro ratelimit = conclimit = 0; 6805363466Sgshapiro limit = NULL; 6806363466Sgshapiro r = rscheck("oc_rate", host, anynet_ntoa(addr), e, RSF_ADDR, 6807363466Sgshapiro 12, NULL, NOQID, NULL, &limit); 6808363466Sgshapiro STR2INT(r, limit, ratelimit); 6809363466Sgshapiro limit = NULL; 6810363466Sgshapiro r = rscheck("oc_conc", host, anynet_ntoa(addr), e, RSF_ADDR, 6811363466Sgshapiro 12, NULL, NOQID, NULL, &limit); 6812363466Sgshapiro STR2INT(r, limit, conclimit); 6813363466Sgshapiro now = curtime(); 6814363466Sgshapiro 6815363466Sgshapiro /* lock occ: lock entire shared memory segment */ 6816363466Sgshapiro SEM_LOCK(r); 6817363466Sgshapiro exc = (bool) conn_limits(e, now, addr, SM_CLFL_EXC, occ, ratelimit, 6818363466Sgshapiro conclimit); 6819363466Sgshapiro SEM_UNLOCK(r); 6820363466Sgshapiro if (!exc && mci != NULL) 6821363466Sgshapiro mci->mci_flags |= MCIF_OCC_INCR; 6822363466Sgshapiro return exc; 6823363466Sgshapiro} 6824363466Sgshapiro 6825363466Sgshapiro/* 6826363466Sgshapiro** OCC_CLOSE -- "close" an outgoing connection: up connection status 6827363466Sgshapiro** 6828363466Sgshapiro** Parameters: 6829363466Sgshapiro** e -- envelope 6830363466Sgshapiro** mci -- mail connection information 6831363466Sgshapiro** host -- name of host 6832363466Sgshapiro** addr -- address of host 6833363466Sgshapiro** 6834363466Sgshapiro** Returns: 6835363466Sgshapiro** true after successful update 6836363466Sgshapiro*/ 6837363466Sgshapiro 6838363466Sgshapirobool 6839363466Sgshapiroocc_close(e, mci, host, addr) 6840363466Sgshapiro ENVELOPE *e; 6841363466Sgshapiro MCI *mci; 6842363466Sgshapiro const char *host; 6843363466Sgshapiro SOCKADDR *addr; 6844363466Sgshapiro{ 6845363466Sgshapiro time_t now; 6846363466Sgshapiro# if _FFR_USE_SEM_LOCKING && SM_CONF_SEM 6847363466Sgshapiro int r; 6848363466Sgshapiro# endif 6849363466Sgshapiro 6850363466Sgshapiro if (occ == NULL || e == NULL) 6851363466Sgshapiro return false; 6852363466Sgshapiro if (mci == NULL || mci->mci_state == MCIS_CLOSED || 6853363466Sgshapiro bitset(MCIF_CACHED, mci->mci_flags) || 6854363466Sgshapiro !bitset(MCIF_OCC_INCR, mci->mci_flags)) 6855363466Sgshapiro return false; 6856363466Sgshapiro mci->mci_flags &= ~MCIF_OCC_INCR; 6857363466Sgshapiro 6858363466Sgshapiro now = curtime(); 6859363466Sgshapiro 6860363466Sgshapiro /* lock occ: lock entire shared memory segment */ 6861363466Sgshapiro SEM_LOCK(r); 6862363466Sgshapiro (void) conn_limits(e, now, addr, SM_CLFL_EXC, occ, -1, -1); 6863363466Sgshapiro SEM_UNLOCK(r); 6864363466Sgshapiro return true; 6865363466Sgshapiro} 6866363466Sgshapiro# endif /* _FFR_OCC */ 6867363466Sgshapiro 6868363466Sgshapiro/* 686990792Sgshapiro** UPD_QS -- update information about queue when adding/deleting an entry 687090792Sgshapiro** 687190792Sgshapiro** Parameters: 687290792Sgshapiro** e -- envelope. 6873147078Sgshapiro** count -- add/remove entry (+1/0/-1: add/no change/remove) 6874147078Sgshapiro** space -- update the space available as well. 6875147078Sgshapiro** (>0/0/<0: add/no change/remove) 6876147078Sgshapiro** where -- caller (for logging) 687790792Sgshapiro** 687890792Sgshapiro** Returns: 687990792Sgshapiro** none. 688090792Sgshapiro** 688190792Sgshapiro** Side Effects: 688290792Sgshapiro** Modifies available space in filesystem. 688390792Sgshapiro** Changes number of entries in queue directory. 688490792Sgshapiro*/ 688590792Sgshapiro 688690792Sgshapirovoid 6887147078Sgshapiroupd_qs(e, count, space, where) 688890792Sgshapiro ENVELOPE *e; 6889147078Sgshapiro int count; 6890147078Sgshapiro int space; 6891147078Sgshapiro char *where; 689290792Sgshapiro{ 689390792Sgshapiro short fidx; 689490792Sgshapiro int idx; 6895147078Sgshapiro# if _FFR_USE_SEM_LOCKING 6896147078Sgshapiro int r; 6897363466Sgshapiro# endif 689890792Sgshapiro long s; 689990792Sgshapiro 690090792Sgshapiro if (ShmId == SM_SHM_NO_ID || e == NULL) 690190792Sgshapiro return; 690290792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 690390792Sgshapiro return; 690490792Sgshapiro idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx; 6905147078Sgshapiro if (tTd(73,2)) 6906147078Sgshapiro sm_dprintf("func=upd_qs, count=%d, space=%d, where=%s, idx=%d, entries=%d\n", 6907147078Sgshapiro count, space, where, idx, QSHM_ENTRIES(idx)); 690890792Sgshapiro 690990792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 6910147078Sgshapiro if (QSHM_ENTRIES(idx) >= 0 && count != 0) 691190792Sgshapiro { 6912363466Sgshapiro SEM_LOCK(r); 6913147078Sgshapiro QSHM_ENTRIES(idx) += count; 6914363466Sgshapiro SEM_UNLOCK(r); 691590792Sgshapiro } 691690792Sgshapiro 691790792Sgshapiro fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx; 691890792Sgshapiro if (fidx < 0) 691990792Sgshapiro return; 692090792Sgshapiro 692190792Sgshapiro /* update available space also? (might be loseqfile) */ 6922147078Sgshapiro if (space == 0) 692390792Sgshapiro return; 692490792Sgshapiro 692590792Sgshapiro /* convert size to blocks; this causes rounding errors */ 692690792Sgshapiro s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx); 692790792Sgshapiro if (s == 0) 692890792Sgshapiro return; 692990792Sgshapiro 693090792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 6931147078Sgshapiro if (space > 0) 693290792Sgshapiro FILE_SYS_AVAIL(fidx) += s; 693390792Sgshapiro else 693490792Sgshapiro FILE_SYS_AVAIL(fidx) -= s; 693590792Sgshapiro 693690792Sgshapiro} 693794334Sgshapiro 693894334Sgshapirostatic bool write_key_file __P((char *, long)); 693994334Sgshapirostatic long read_key_file __P((char *, long)); 694094334Sgshapiro 694190792Sgshapiro/* 694294334Sgshapiro** WRITE_KEY_FILE -- record some key into a file. 694394334Sgshapiro** 694494334Sgshapiro** Parameters: 694594334Sgshapiro** keypath -- file name. 694694334Sgshapiro** key -- key to write. 694794334Sgshapiro** 694894334Sgshapiro** Returns: 694994334Sgshapiro** true iff file could be written. 695094334Sgshapiro** 695194334Sgshapiro** Side Effects: 695294334Sgshapiro** writes file. 695394334Sgshapiro*/ 695494334Sgshapiro 695594334Sgshapirostatic bool 695694334Sgshapirowrite_key_file(keypath, key) 695794334Sgshapiro char *keypath; 695894334Sgshapiro long key; 695994334Sgshapiro{ 696094334Sgshapiro bool ok; 696194334Sgshapiro long sff; 696294334Sgshapiro SM_FILE_T *keyf; 696394334Sgshapiro 696494334Sgshapiro ok = false; 696594334Sgshapiro if (keypath == NULL || *keypath == '\0') 696694334Sgshapiro return ok; 696794334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; 696894334Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 696994334Sgshapiro sff |= SFF_OPENASROOT; 6970110560Sgshapiro keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff); 697194334Sgshapiro if (keyf == NULL) 697294334Sgshapiro { 697394334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", 697494334Sgshapiro keypath, sm_errstring(errno)); 697594334Sgshapiro } 697694334Sgshapiro else 697794334Sgshapiro { 6978157001Sgshapiro if (geteuid() == 0 && RunAsUid != 0) 6979157001Sgshapiro { 6980157001Sgshapiro# if HASFCHOWN 6981157001Sgshapiro int fd; 6982157001Sgshapiro 6983157001Sgshapiro fd = keyf->f_file; 6984157001Sgshapiro if (fd >= 0 && fchown(fd, RunAsUid, -1) < 0) 6985157001Sgshapiro { 6986157001Sgshapiro int err = errno; 6987157001Sgshapiro 6988157001Sgshapiro sm_syslog(LOG_ALERT, NOQID, 6989285229Sgshapiro "ownership change on %s to %ld failed: %s", 6990285229Sgshapiro keypath, (long) RunAsUid, sm_errstring(err)); 6991157001Sgshapiro } 6992157001Sgshapiro# endif /* HASFCHOWN */ 6993157001Sgshapiro } 699494334Sgshapiro ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) != 699594334Sgshapiro SM_IO_EOF; 6996110560Sgshapiro ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok; 699794334Sgshapiro } 699894334Sgshapiro return ok; 699994334Sgshapiro} 700094334Sgshapiro 700194334Sgshapiro/* 700294334Sgshapiro** READ_KEY_FILE -- read a key from a file. 700394334Sgshapiro** 700494334Sgshapiro** Parameters: 700594334Sgshapiro** keypath -- file name. 700694334Sgshapiro** key -- default key. 700794334Sgshapiro** 700894334Sgshapiro** Returns: 700994334Sgshapiro** key. 701094334Sgshapiro*/ 701194334Sgshapiro 701294334Sgshapirostatic long 701394334Sgshapiroread_key_file(keypath, key) 701494334Sgshapiro char *keypath; 701594334Sgshapiro long key; 701694334Sgshapiro{ 701794334Sgshapiro int r; 701894334Sgshapiro long sff, n; 701994334Sgshapiro SM_FILE_T *keyf; 702094334Sgshapiro 702194334Sgshapiro if (keypath == NULL || *keypath == '\0') 702294334Sgshapiro return key; 702394334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY; 7024110560Sgshapiro if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid)) 702594334Sgshapiro sff |= SFF_OPENASROOT; 7026110560Sgshapiro keyf = safefopen(keypath, O_RDONLY, FileMode, sff); 702794334Sgshapiro if (keyf == NULL) 702894334Sgshapiro { 702994334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s", 703094334Sgshapiro keypath, sm_errstring(errno)); 703194334Sgshapiro } 703294334Sgshapiro else 703394334Sgshapiro { 703494334Sgshapiro r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n); 703594334Sgshapiro if (r == 1) 703694334Sgshapiro key = n; 703794334Sgshapiro (void) sm_io_close(keyf, SM_TIME_DEFAULT); 703894334Sgshapiro } 703994334Sgshapiro return key; 704094334Sgshapiro} 704194334Sgshapiro 704294334Sgshapiro/* 704390792Sgshapiro** INIT_SHM -- initialize shared memory structure 704490792Sgshapiro** 704590792Sgshapiro** Initialize or attach to shared memory segment. 704690792Sgshapiro** Currently it is not a fatal error if this doesn't work. 704790792Sgshapiro** However, it causes us to have a "fallback" storage location 704890792Sgshapiro** for everything that is supposed to be in the shared memory, 704990792Sgshapiro** which makes the code slightly ugly. 705090792Sgshapiro** 705190792Sgshapiro** Parameters: 705290792Sgshapiro** qn -- number of queue directories. 705390792Sgshapiro** owner -- owner of shared memory. 705490792Sgshapiro** hash -- identifies data that is stored in shared memory. 705590792Sgshapiro** 705690792Sgshapiro** Returns: 705790792Sgshapiro** none. 705890792Sgshapiro*/ 705990792Sgshapiro 706090792Sgshapirostatic void init_shm __P((int, bool, unsigned int)); 706190792Sgshapiro 706290792Sgshapirostatic void 706390792Sgshapiroinit_shm(qn, owner, hash) 706490792Sgshapiro int qn; 706590792Sgshapiro bool owner; 706690792Sgshapiro unsigned int hash; 706790792Sgshapiro{ 706890792Sgshapiro int i; 7069147078Sgshapiro int count; 7070147078Sgshapiro int save_errno; 707194334Sgshapiro bool keyselect; 707290792Sgshapiro 707390792Sgshapiro PtrFileSys = &FileSys[0]; 707490792Sgshapiro PNumFileSys = &Numfilesys; 707594334Sgshapiro/* if this "key" is specified: select one yourself */ 7076168515Sgshapiro#define SEL_SHM_KEY ((key_t) -1) 7077168515Sgshapiro#define FIRST_SHM_KEY 25 707890792Sgshapiro 707990792Sgshapiro /* This allows us to disable shared memory at runtime. */ 7080147078Sgshapiro if (ShmKey == 0) 7081147078Sgshapiro return; 708290792Sgshapiro 7083147078Sgshapiro count = 0; 7084147078Sgshapiro shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T); 7085147078Sgshapiro keyselect = ShmKey == SEL_SHM_KEY; 7086147078Sgshapiro if (keyselect) 7087147078Sgshapiro { 7088147078Sgshapiro if (owner) 7089147078Sgshapiro ShmKey = FIRST_SHM_KEY; 7090147078Sgshapiro else 709194334Sgshapiro { 7092168515Sgshapiro errno = 0; 7093147078Sgshapiro ShmKey = read_key_file(ShmKeyFile, ShmKey); 7094147078Sgshapiro keyselect = false; 7095147078Sgshapiro if (ShmKey == SEL_SHM_KEY) 7096168515Sgshapiro { 7097168515Sgshapiro save_errno = (errno != 0) ? errno : EINVAL; 7098147078Sgshapiro goto error; 7099168515Sgshapiro } 710094334Sgshapiro } 7101147078Sgshapiro } 7102147078Sgshapiro for (;;) 7103147078Sgshapiro { 7104147078Sgshapiro /* allow read/write access for group? */ 7105147078Sgshapiro Pshm = sm_shmstart(ShmKey, shms, 7106147078Sgshapiro SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3), 7107147078Sgshapiro &ShmId, owner); 7108147078Sgshapiro save_errno = errno; 7109147078Sgshapiro if (Pshm != NULL || !sm_file_exists(save_errno)) 7110147078Sgshapiro break; 7111147078Sgshapiro if (++count >= 3) 711290792Sgshapiro { 7113147078Sgshapiro if (keyselect) 711494334Sgshapiro { 7115147078Sgshapiro ++ShmKey; 711694334Sgshapiro 7117147078Sgshapiro /* back where we started? */ 7118147078Sgshapiro if (ShmKey == SEL_SHM_KEY) 7119147078Sgshapiro break; 7120147078Sgshapiro continue; 7121147078Sgshapiro } 7122147078Sgshapiro break; 7123147078Sgshapiro } 7124168515Sgshapiro 7125147078Sgshapiro /* only sleep if we are at the first key */ 7126147078Sgshapiro if (!keyselect || ShmKey == SEL_SHM_KEY) 7127168515Sgshapiro sleep(count); 7128147078Sgshapiro } 7129147078Sgshapiro if (Pshm != NULL) 7130147078Sgshapiro { 7131147078Sgshapiro int *p; 713290792Sgshapiro 7133147078Sgshapiro if (keyselect) 7134147078Sgshapiro (void) write_key_file(ShmKeyFile, (long) ShmKey); 7135147078Sgshapiro if (owner && RunAsUid != 0) 7136147078Sgshapiro { 7137157001Sgshapiro i = sm_shmsetowner(ShmId, RunAsUid, RunAsGid, 0660); 7138147078Sgshapiro if (i != 0) 7139147078Sgshapiro sm_syslog(LOG_ERR, NOQID, 7140285229Sgshapiro "key=%ld, sm_shmsetowner=%d, RunAsUid=%ld, RunAsGid=%ld", 7141285229Sgshapiro (long) ShmKey, i, (long) RunAsUid, (long) RunAsGid); 7142147078Sgshapiro } 7143147078Sgshapiro p = (int *) Pshm; 7144147078Sgshapiro if (owner) 7145147078Sgshapiro { 7146147078Sgshapiro *p = (int) shms; 7147147078Sgshapiro *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid; 7148147078Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 7149147078Sgshapiro *p = hash; 7150147078Sgshapiro } 7151147078Sgshapiro else 7152147078Sgshapiro { 7153147078Sgshapiro if (*p != (int) shms) 715490792Sgshapiro { 7155147078Sgshapiro save_errno = EINVAL; 7156147078Sgshapiro cleanup_shm(false); 7157147078Sgshapiro goto error; 715890792Sgshapiro } 7159147078Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 7160147078Sgshapiro if (*p != (int) hash) 716190792Sgshapiro { 7162147078Sgshapiro save_errno = EINVAL; 7163147078Sgshapiro cleanup_shm(false); 7164147078Sgshapiro goto error; 716590792Sgshapiro } 716690792Sgshapiro 7167147078Sgshapiro /* 7168147078Sgshapiro ** XXX how to check the pid? 7169147078Sgshapiro ** Read it from the pid-file? That does 7170147078Sgshapiro ** not need to exist. 7171147078Sgshapiro ** We could disable shm if we can't confirm 7172147078Sgshapiro ** that it is the right one. 7173147078Sgshapiro */ 717490792Sgshapiro } 7175147078Sgshapiro 7176147078Sgshapiro PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm); 7177147078Sgshapiro PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm); 7178147078Sgshapiro QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm); 7179147078Sgshapiro PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm); 7180147078Sgshapiro *PRSATmpCnt = 0; 7181363466Sgshapiro# if _FFR_OCC 7182363466Sgshapiro occ = (CHash_T *) OFF_OCC_SHM(Pshm); 7183363466Sgshapiro# endif 7184147078Sgshapiro if (owner) 718590792Sgshapiro { 7186147078Sgshapiro /* initialize values in shared memory */ 7187147078Sgshapiro NumFileSys = 0; 7188147078Sgshapiro for (i = 0; i < qn; i++) 7189147078Sgshapiro QShm[i].qs_entries = -1; 7190363466Sgshapiro# if _FFR_OCC 7191363466Sgshapiro memset(occ, 0, OCC_SIZE); 7192363466Sgshapiro# endif 719390792Sgshapiro } 7194147078Sgshapiro init_sem(owner); 7195147078Sgshapiro return; 719690792Sgshapiro } 7197147078Sgshapiro error: 7198147078Sgshapiro if (LogLevel > (owner ? 8 : 11)) 7199147078Sgshapiro { 7200147078Sgshapiro sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID, 7201147078Sgshapiro "can't %s shared memory, key=%ld: %s", 7202147078Sgshapiro owner ? "initialize" : "attach to", 7203147078Sgshapiro (long) ShmKey, sm_errstring(save_errno)); 7204147078Sgshapiro } 720590792Sgshapiro} 720690792Sgshapiro#endif /* SM_CONF_SHM */ 720790792Sgshapiro 7208120256Sgshapiro 720990792Sgshapiro/* 7210168515Sgshapiro** SETUP_QUEUES -- set up all queue groups 721190792Sgshapiro** 721290792Sgshapiro** Parameters: 7213168515Sgshapiro** owner -- owner of shared memory? 721490792Sgshapiro** 721590792Sgshapiro** Returns: 721690792Sgshapiro** none. 721790792Sgshapiro** 721890792Sgshapiro#if SM_CONF_SHM 721990792Sgshapiro** Side Effects: 722090792Sgshapiro** attaches shared memory. 722190792Sgshapiro#endif * SM_CONF_SHM * 722290792Sgshapiro*/ 722390792Sgshapiro 722490792Sgshapirovoid 722590792Sgshapirosetup_queues(owner) 722690792Sgshapiro bool owner; 722790792Sgshapiro{ 722890792Sgshapiro int i, qn, len; 722990792Sgshapiro unsigned int hashval; 723094334Sgshapiro time_t now; 723190792Sgshapiro char basedir[MAXPATHLEN]; 723290792Sgshapiro struct stat st; 723390792Sgshapiro 723490792Sgshapiro /* 723590792Sgshapiro ** Determine basedir for all queue directories. 723690792Sgshapiro ** All queue directories must be (first level) subdirectories 723790792Sgshapiro ** of the basedir. The basedir is the QueueDir 723890792Sgshapiro ** without wildcards, but with trailing / 723990792Sgshapiro */ 724090792Sgshapiro 724190792Sgshapiro hashval = 0; 724290792Sgshapiro errno = 0; 7243168515Sgshapiro len = sm_strlcpy(basedir, QueueDir, sizeof(basedir)); 7244111823Sgshapiro 7245111823Sgshapiro /* Provide space for trailing '/' */ 7246168515Sgshapiro if (len >= sizeof(basedir) - 1) 724790792Sgshapiro { 724890792Sgshapiro syserr("QueueDirectory: path too long: %d, max %d", 7249168515Sgshapiro len, (int) sizeof(basedir) - 1); 725090792Sgshapiro ExitStat = EX_CONFIG; 725190792Sgshapiro return; 725290792Sgshapiro } 725390792Sgshapiro SM_ASSERT(len > 0); 725490792Sgshapiro if (basedir[len - 1] == '*') 725590792Sgshapiro { 725690792Sgshapiro char *cp; 725790792Sgshapiro 725890792Sgshapiro cp = SM_LAST_DIR_DELIM(basedir); 725990792Sgshapiro if (cp == NULL) 726090792Sgshapiro { 726190792Sgshapiro syserr("QueueDirectory: can not wildcard relative path \"%s\"", 726290792Sgshapiro QueueDir); 726390792Sgshapiro if (tTd(41, 2)) 726490792Sgshapiro sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n", 726590792Sgshapiro QueueDir); 726690792Sgshapiro ExitStat = EX_CONFIG; 726790792Sgshapiro return; 726890792Sgshapiro } 726990792Sgshapiro 727090792Sgshapiro /* cut off wildcard pattern */ 727190792Sgshapiro *++cp = '\0'; 727290792Sgshapiro len = cp - basedir; 727390792Sgshapiro } 727490792Sgshapiro else if (!SM_IS_DIR_DELIM(basedir[len - 1])) 727590792Sgshapiro { 727690792Sgshapiro /* append trailing slash since it is a directory */ 727790792Sgshapiro basedir[len] = '/'; 727890792Sgshapiro basedir[++len] = '\0'; 727990792Sgshapiro } 728090792Sgshapiro 728190792Sgshapiro /* len counts up to the last directory delimiter */ 728290792Sgshapiro SM_ASSERT(basedir[len - 1] == '/'); 728390792Sgshapiro 728490792Sgshapiro if (chdir(basedir) < 0) 728590792Sgshapiro { 728690792Sgshapiro int save_errno = errno; 728790792Sgshapiro 728890792Sgshapiro syserr("can not chdir(%s)", basedir); 728990792Sgshapiro if (save_errno == EACCES) 729090792Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 729190792Sgshapiro "Program mode requires special privileges, e.g., root or TrustedUser.\n"); 729290792Sgshapiro if (tTd(41, 2)) 729390792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 729490792Sgshapiro basedir, sm_errstring(errno)); 729590792Sgshapiro ExitStat = EX_CONFIG; 729690792Sgshapiro return; 729790792Sgshapiro } 729890792Sgshapiro#if SM_CONF_SHM 729990792Sgshapiro hashval = hash_q(basedir, hashval); 7300363466Sgshapiro#endif 730190792Sgshapiro 730294334Sgshapiro /* initialize for queue runs */ 730394334Sgshapiro DoQueueRun = false; 730494334Sgshapiro now = curtime(); 730594334Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 730694334Sgshapiro Queue[i]->qg_nextrun = now; 730790792Sgshapiro 730890792Sgshapiro 730990792Sgshapiro if (UseMSP && OpMode != MD_TEST) 731090792Sgshapiro { 731190792Sgshapiro long sff = SFF_CREAT; 731290792Sgshapiro 731390792Sgshapiro if (stat(".", &st) < 0) 731490792Sgshapiro { 731590792Sgshapiro syserr("can not stat(%s)", basedir); 731690792Sgshapiro if (tTd(41, 2)) 731790792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 731890792Sgshapiro basedir, sm_errstring(errno)); 731990792Sgshapiro ExitStat = EX_CONFIG; 732090792Sgshapiro return; 732190792Sgshapiro } 732290792Sgshapiro if (RunAsUid == 0) 732390792Sgshapiro sff |= SFF_ROOTOK; 732490792Sgshapiro 732590792Sgshapiro /* 732690792Sgshapiro ** Check queue directory permissions. 732790792Sgshapiro ** Can we write to a group writable queue directory? 732890792Sgshapiro */ 732990792Sgshapiro 733090792Sgshapiro if (bitset(S_IWGRP, QueueFileMode) && 733190792Sgshapiro bitset(S_IWGRP, st.st_mode) && 733290792Sgshapiro safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff, 733390792Sgshapiro QueueFileMode, NULL) != 0) 733490792Sgshapiro { 7335285229Sgshapiro syserr("can not write to queue directory %s (RunAsGid=%ld, required=%ld)", 7336285229Sgshapiro basedir, (long) RunAsGid, (long) st.st_gid); 733790792Sgshapiro } 733890792Sgshapiro if (bitset(S_IWOTH|S_IXOTH, st.st_mode)) 733990792Sgshapiro { 734090792Sgshapiro#if _FFR_MSP_PARANOIA 734190792Sgshapiro syserr("dangerous permissions=%o on queue directory %s", 7342285229Sgshapiro (unsigned int) st.st_mode, basedir); 7343363466Sgshapiro#else 734490792Sgshapiro if (LogLevel > 0) 734590792Sgshapiro sm_syslog(LOG_ERR, NOQID, 734690792Sgshapiro "dangerous permissions=%o on queue directory %s", 7347285229Sgshapiro (unsigned int) st.st_mode, basedir); 734890792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 734990792Sgshapiro } 735090792Sgshapiro#if _FFR_MSP_PARANOIA 735190792Sgshapiro if (NumQueue > 1) 735290792Sgshapiro syserr("can not use multiple queues for MSP"); 7353363466Sgshapiro#endif 735490792Sgshapiro } 735590792Sgshapiro 735690792Sgshapiro /* initial number of queue directories */ 735790792Sgshapiro qn = 0; 735890792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 735990792Sgshapiro qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval); 736090792Sgshapiro 736190792Sgshapiro#if SM_CONF_SHM 736290792Sgshapiro init_shm(qn, owner, hashval); 736390792Sgshapiro i = filesys_setup(owner || ShmId == SM_SHM_NO_ID); 736490792Sgshapiro if (i == FSF_NOT_FOUND) 736590792Sgshapiro { 736690792Sgshapiro /* 736790792Sgshapiro ** We didn't get the right filesystem data 736890792Sgshapiro ** This may happen if we don't have the right shared memory. 736990792Sgshapiro ** So let's do this without shared memory. 737090792Sgshapiro */ 737190792Sgshapiro 737290792Sgshapiro SM_ASSERT(!owner); 737390792Sgshapiro cleanup_shm(false); /* release shared memory */ 737490792Sgshapiro i = filesys_setup(false); 737590792Sgshapiro if (i < 0) 737690792Sgshapiro syserr("filesys_setup failed twice, result=%d", i); 737790792Sgshapiro else if (LogLevel > 8) 737890792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 737990792Sgshapiro "shared memory does not contain expected data, ignored"); 738090792Sgshapiro } 738190792Sgshapiro#else /* SM_CONF_SHM */ 738290792Sgshapiro i = filesys_setup(true); 738390792Sgshapiro#endif /* SM_CONF_SHM */ 738490792Sgshapiro if (i < 0) 738590792Sgshapiro ExitStat = EX_CONFIG; 738690792Sgshapiro} 738790792Sgshapiro 738890792Sgshapiro#if SM_CONF_SHM 738990792Sgshapiro/* 739090792Sgshapiro** CLEANUP_SHM -- do some cleanup work for shared memory etc 739190792Sgshapiro** 739290792Sgshapiro** Parameters: 739390792Sgshapiro** owner -- owner of shared memory? 739490792Sgshapiro** 739590792Sgshapiro** Returns: 739690792Sgshapiro** none. 739790792Sgshapiro** 739890792Sgshapiro** Side Effects: 739990792Sgshapiro** detaches shared memory. 740090792Sgshapiro*/ 740190792Sgshapiro 740290792Sgshapirovoid 740390792Sgshapirocleanup_shm(owner) 740490792Sgshapiro bool owner; 740590792Sgshapiro{ 740690792Sgshapiro if (ShmId != SM_SHM_NO_ID) 740790792Sgshapiro { 740890792Sgshapiro if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8) 740998121Sgshapiro sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s", 741090792Sgshapiro sm_errstring(errno)); 741190792Sgshapiro Pshm = NULL; 741290792Sgshapiro ShmId = SM_SHM_NO_ID; 741390792Sgshapiro } 7414147078Sgshapiro stop_sem(owner); 741590792Sgshapiro} 741690792Sgshapiro#endif /* SM_CONF_SHM */ 741790792Sgshapiro 741890792Sgshapiro/* 741990792Sgshapiro** CLEANUP_QUEUES -- do some cleanup work for queues 742090792Sgshapiro** 742190792Sgshapiro** Parameters: 742290792Sgshapiro** none. 742390792Sgshapiro** 742490792Sgshapiro** Returns: 742590792Sgshapiro** none. 742690792Sgshapiro** 742790792Sgshapiro*/ 742890792Sgshapiro 742990792Sgshapirovoid 743090792Sgshapirocleanup_queues() 743190792Sgshapiro{ 743290792Sgshapiro sync_queue_time(); 743390792Sgshapiro} 743490792Sgshapiro/* 743590792Sgshapiro** SET_DEF_QUEUEVAL -- set default values for a queue group. 743690792Sgshapiro** 743790792Sgshapiro** Parameters: 743890792Sgshapiro** qg -- queue group 743990792Sgshapiro** all -- set all values (true for default group)? 744090792Sgshapiro** 744190792Sgshapiro** Returns: 744290792Sgshapiro** none. 744390792Sgshapiro** 744490792Sgshapiro** Side Effects: 744590792Sgshapiro** sets default values for the queue group. 744690792Sgshapiro*/ 744790792Sgshapiro 744890792Sgshapirovoid 744990792Sgshapiroset_def_queueval(qg, all) 745090792Sgshapiro QUEUEGRP *qg; 745190792Sgshapiro bool all; 745290792Sgshapiro{ 745390792Sgshapiro if (bitnset(QD_DEFINED, qg->qg_flags)) 745490792Sgshapiro return; 745590792Sgshapiro if (all) 745690792Sgshapiro qg->qg_qdir = QueueDir; 745794334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 745890792Sgshapiro qg->qg_sortorder = QueueSortOrder; 7459363466Sgshapiro#endif 746090792Sgshapiro qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1; 746190792Sgshapiro qg->qg_nice = NiceQueueRun; 746290792Sgshapiro} 746390792Sgshapiro/* 746490792Sgshapiro** MAKEQUEUE -- define a new queue. 746590792Sgshapiro** 746690792Sgshapiro** Parameters: 746790792Sgshapiro** line -- description of queue. This is in labeled fields. 746890792Sgshapiro** The fields are: 746990792Sgshapiro** F -- the flags associated with the queue 747090792Sgshapiro** I -- the interval between running the queue 747190792Sgshapiro** J -- the maximum # of jobs in work list 747290792Sgshapiro** [M -- the maximum # of jobs in a queue run] 747390792Sgshapiro** N -- the niceness at which to run 747490792Sgshapiro** P -- the path to the queue 747590792Sgshapiro** S -- the queue sorting order 747690792Sgshapiro** R -- number of parallel queue runners 747790792Sgshapiro** r -- max recipients per envelope 747890792Sgshapiro** The first word is the canonical name of the queue. 747990792Sgshapiro** qdef -- this is a 'Q' definition from .cf 748090792Sgshapiro** 748190792Sgshapiro** Returns: 748290792Sgshapiro** none. 748390792Sgshapiro** 748490792Sgshapiro** Side Effects: 748590792Sgshapiro** enters the queue into the queue table. 748690792Sgshapiro*/ 748790792Sgshapiro 748890792Sgshapirovoid 748990792Sgshapiromakequeue(line, qdef) 749090792Sgshapiro char *line; 749190792Sgshapiro bool qdef; 749290792Sgshapiro{ 749390792Sgshapiro register char *p; 749490792Sgshapiro register QUEUEGRP *qg; 749590792Sgshapiro register STAB *s; 749690792Sgshapiro int i; 749790792Sgshapiro char fcode; 749890792Sgshapiro 749990792Sgshapiro /* allocate a queue and set up defaults */ 7500168515Sgshapiro qg = (QUEUEGRP *) xalloc(sizeof(*qg)); 7501168515Sgshapiro memset((char *) qg, '\0', sizeof(*qg)); 750290792Sgshapiro 750390792Sgshapiro if (line[0] == '\0') 750490792Sgshapiro { 750590792Sgshapiro syserr("name required for queue"); 750690792Sgshapiro return; 750790792Sgshapiro } 750890792Sgshapiro 750990792Sgshapiro /* collect the queue name */ 751090792Sgshapiro for (p = line; 7511363466Sgshapiro *p != '\0' && *p != ',' && !(SM_ISSPACE(*p)); 751290792Sgshapiro p++) 751390792Sgshapiro continue; 751490792Sgshapiro if (*p != '\0') 751590792Sgshapiro *p++ = '\0'; 751690792Sgshapiro qg->qg_name = newstr(line); 751790792Sgshapiro 751890792Sgshapiro /* set default values, can be overridden below */ 751990792Sgshapiro set_def_queueval(qg, false); 752090792Sgshapiro 752190792Sgshapiro /* now scan through and assign info from the fields */ 752290792Sgshapiro while (*p != '\0') 752390792Sgshapiro { 752490792Sgshapiro auto char *delimptr; 752590792Sgshapiro 7526363466Sgshapiro while (*p != '\0' && (*p == ',' || (SM_ISSPACE(*p)))) 752790792Sgshapiro p++; 752890792Sgshapiro 752990792Sgshapiro /* p now points to field code */ 753090792Sgshapiro fcode = *p; 753190792Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 753290792Sgshapiro p++; 753390792Sgshapiro if (*p++ != '=') 753490792Sgshapiro { 753590792Sgshapiro syserr("queue %s: `=' expected", qg->qg_name); 753690792Sgshapiro return; 753790792Sgshapiro } 7538363466Sgshapiro while (SM_ISSPACE(*p)) 753990792Sgshapiro p++; 754090792Sgshapiro 754190792Sgshapiro /* p now points to the field body */ 754290792Sgshapiro p = munchstring(p, &delimptr, ','); 754390792Sgshapiro 754490792Sgshapiro /* install the field into the queue struct */ 754590792Sgshapiro switch (fcode) 754690792Sgshapiro { 754790792Sgshapiro case 'P': /* pathname */ 754890792Sgshapiro if (*p == '\0') 754990792Sgshapiro syserr("queue %s: empty path name", 755090792Sgshapiro qg->qg_name); 755190792Sgshapiro else 755290792Sgshapiro qg->qg_qdir = newstr(p); 755390792Sgshapiro break; 755490792Sgshapiro 755590792Sgshapiro case 'F': /* flags */ 755690792Sgshapiro for (; *p != '\0'; p++) 7557363466Sgshapiro if (!(SM_ISSPACE(*p))) 755890792Sgshapiro setbitn(*p, qg->qg_flags); 755990792Sgshapiro break; 756090792Sgshapiro 756190792Sgshapiro /* 756290792Sgshapiro ** Do we need two intervals here: 756390792Sgshapiro ** One for persistent queue runners, 756490792Sgshapiro ** one for "normal" queue runs? 756590792Sgshapiro */ 756690792Sgshapiro 756790792Sgshapiro case 'I': /* interval between running the queue */ 756890792Sgshapiro qg->qg_queueintvl = convtime(p, 'm'); 756990792Sgshapiro break; 757090792Sgshapiro 757190792Sgshapiro case 'N': /* run niceness */ 757290792Sgshapiro qg->qg_nice = atoi(p); 757390792Sgshapiro break; 757490792Sgshapiro 757590792Sgshapiro case 'R': /* maximum # of runners for the group */ 757690792Sgshapiro i = atoi(p); 757790792Sgshapiro 757890792Sgshapiro /* can't have more runners than allowed total */ 757990792Sgshapiro if (MaxQueueChildren > 0 && i > MaxQueueChildren) 758090792Sgshapiro { 758190792Sgshapiro qg->qg_maxqrun = MaxQueueChildren; 758290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 758390792Sgshapiro "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n", 758490792Sgshapiro qg->qg_name, i, 758590792Sgshapiro MaxQueueChildren); 758690792Sgshapiro } 758790792Sgshapiro else 758890792Sgshapiro qg->qg_maxqrun = i; 758990792Sgshapiro break; 759090792Sgshapiro 759190792Sgshapiro case 'J': /* maximum # of jobs in work list */ 759290792Sgshapiro qg->qg_maxlist = atoi(p); 759390792Sgshapiro break; 759490792Sgshapiro 759590792Sgshapiro case 'r': /* max recipients per envelope */ 759690792Sgshapiro qg->qg_maxrcpt = atoi(p); 759790792Sgshapiro break; 759890792Sgshapiro 759994334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 760090792Sgshapiro case 'S': /* queue sorting order */ 760190792Sgshapiro switch (*p) 760290792Sgshapiro { 760390792Sgshapiro case 'h': /* Host first */ 760490792Sgshapiro case 'H': 760590792Sgshapiro qg->qg_sortorder = QSO_BYHOST; 760690792Sgshapiro break; 760790792Sgshapiro 760890792Sgshapiro case 'p': /* Priority order */ 760990792Sgshapiro case 'P': 761090792Sgshapiro qg->qg_sortorder = QSO_BYPRIORITY; 761190792Sgshapiro break; 761290792Sgshapiro 761390792Sgshapiro case 't': /* Submission time */ 761490792Sgshapiro case 'T': 761590792Sgshapiro qg->qg_sortorder = QSO_BYTIME; 761690792Sgshapiro break; 761790792Sgshapiro 761890792Sgshapiro case 'f': /* File name */ 761990792Sgshapiro case 'F': 762090792Sgshapiro qg->qg_sortorder = QSO_BYFILENAME; 762190792Sgshapiro break; 762290792Sgshapiro 762390792Sgshapiro case 'm': /* Modification time */ 762490792Sgshapiro case 'M': 762594334Sgshapiro qg->qg_sortorder = QSO_BYMODTIME; 762690792Sgshapiro break; 762790792Sgshapiro 762894334Sgshapiro case 'r': /* Random */ 762994334Sgshapiro case 'R': 763094334Sgshapiro qg->qg_sortorder = QSO_RANDOM; 763194334Sgshapiro break; 763294334Sgshapiro 763394334Sgshapiro# if _FFR_RHS 763494334Sgshapiro case 's': /* Shuffled host name */ 763594334Sgshapiro case 'S': 763694334Sgshapiro qg->qg_sortorder = QSO_BYSHUFFLE; 763794334Sgshapiro break; 763894334Sgshapiro# endif /* _FFR_RHS */ 763994334Sgshapiro 7640132943Sgshapiro case 'n': /* none */ 7641132943Sgshapiro case 'N': 7642132943Sgshapiro qg->qg_sortorder = QSO_NONE; 7643132943Sgshapiro break; 7644132943Sgshapiro 764590792Sgshapiro default: 764690792Sgshapiro syserr("Invalid queue sort order \"%s\"", p); 764790792Sgshapiro } 764890792Sgshapiro break; 764994334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 765090792Sgshapiro 765190792Sgshapiro default: 765290792Sgshapiro syserr("Q%s: unknown queue equate %c=", 765390792Sgshapiro qg->qg_name, fcode); 765490792Sgshapiro break; 765590792Sgshapiro } 765690792Sgshapiro 765790792Sgshapiro p = delimptr; 765890792Sgshapiro } 765990792Sgshapiro 766090792Sgshapiro#if !HASNICE 766190792Sgshapiro if (qg->qg_nice != NiceQueueRun) 766290792Sgshapiro { 766390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 766490792Sgshapiro "Q%s: Warning: N= set on system that doesn't support nice()\n", 766590792Sgshapiro qg->qg_name); 766690792Sgshapiro } 766790792Sgshapiro#endif /* !HASNICE */ 766890792Sgshapiro 766990792Sgshapiro /* do some rationality checking */ 767090792Sgshapiro if (NumQueue >= MAXQUEUEGROUPS) 767190792Sgshapiro { 767290792Sgshapiro syserr("too many queue groups defined (%d max)", 767390792Sgshapiro MAXQUEUEGROUPS); 767490792Sgshapiro return; 767590792Sgshapiro } 767690792Sgshapiro 767790792Sgshapiro if (qg->qg_qdir == NULL) 767890792Sgshapiro { 767990792Sgshapiro if (QueueDir == NULL || *QueueDir == '\0') 768090792Sgshapiro { 768190792Sgshapiro syserr("QueueDir must be defined before queue groups"); 768290792Sgshapiro return; 768390792Sgshapiro } 768490792Sgshapiro qg->qg_qdir = newstr(QueueDir); 768590792Sgshapiro } 768690792Sgshapiro 768790792Sgshapiro if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags)) 768890792Sgshapiro { 768990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 769090792Sgshapiro "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n", 769190792Sgshapiro qg->qg_name, qg->qg_maxqrun, QD_FORK); 769290792Sgshapiro } 769390792Sgshapiro 769490792Sgshapiro /* enter the queue into the symbol table */ 769590792Sgshapiro if (tTd(37, 8)) 769690792Sgshapiro sm_syslog(LOG_INFO, NOQID, 769790792Sgshapiro "Adding %s to stab, path: %s", qg->qg_name, 769890792Sgshapiro qg->qg_qdir); 769990792Sgshapiro s = stab(qg->qg_name, ST_QUEUE, ST_ENTER); 770090792Sgshapiro if (s->s_quegrp != NULL) 770190792Sgshapiro { 770290792Sgshapiro i = s->s_quegrp->qg_index; 770390792Sgshapiro 770490792Sgshapiro /* XXX what about the pointers inside this struct? */ 770590792Sgshapiro sm_free(s->s_quegrp); /* XXX */ 770690792Sgshapiro } 770790792Sgshapiro else 770890792Sgshapiro i = NumQueue++; 770990792Sgshapiro Queue[i] = s->s_quegrp = qg; 771090792Sgshapiro qg->qg_index = i; 771190792Sgshapiro 771290792Sgshapiro /* set default value for max queue runners */ 771390792Sgshapiro if (qg->qg_maxqrun < 0) 771490792Sgshapiro { 771590792Sgshapiro if (MaxRunnersPerQueue > 0) 771690792Sgshapiro qg->qg_maxqrun = MaxRunnersPerQueue; 771790792Sgshapiro else 771890792Sgshapiro qg->qg_maxqrun = 1; 771990792Sgshapiro } 772090792Sgshapiro if (qdef) 772190792Sgshapiro setbitn(QD_DEFINED, qg->qg_flags); 772290792Sgshapiro} 772390792Sgshapiro#if 0 772490792Sgshapiro/* 772564562Sgshapiro** HASHFQN -- calculate a hash value for a fully qualified host name 772664562Sgshapiro** 772764562Sgshapiro** Arguments: 772864562Sgshapiro** fqn -- an all lower-case host.domain string 772964562Sgshapiro** buckets -- the number of buckets (queue directories) 773064562Sgshapiro** 773164562Sgshapiro** Returns: 773264562Sgshapiro** a bucket number (signed integer) 773364562Sgshapiro** -1 on error 773464562Sgshapiro** 773564562Sgshapiro** Contributed by Exactis.com, Inc. 773664562Sgshapiro*/ 773764562Sgshapiro 773864562Sgshapiroint 773964562Sgshapirohashfqn(fqn, buckets) 774064562Sgshapiro register char *fqn; 774164562Sgshapiro int buckets; 774264562Sgshapiro{ 774364562Sgshapiro register char *p; 774464562Sgshapiro register int h = 0, hash, cnt; 774564562Sgshapiro 774664562Sgshapiro if (fqn == NULL) 774764562Sgshapiro return -1; 774864562Sgshapiro 774964562Sgshapiro /* 775064562Sgshapiro ** A variation on the gdb hash 775164562Sgshapiro ** This is the best as of Feb 19, 1996 --bcx 775264562Sgshapiro */ 775364562Sgshapiro 775464562Sgshapiro p = fqn; 775564562Sgshapiro h = 0x238F13AF * strlen(p); 775664562Sgshapiro for (cnt = 0; *p != 0; ++p, cnt++) 775764562Sgshapiro { 775864562Sgshapiro h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF; 775964562Sgshapiro } 776064562Sgshapiro h = (1103515243 * h + 12345) & 0x7FFFFFFF; 776164562Sgshapiro if (buckets < 2) 776264562Sgshapiro hash = 0; 776364562Sgshapiro else 776464562Sgshapiro hash = (h % buckets); 776564562Sgshapiro 776664562Sgshapiro return hash; 776764562Sgshapiro} 776890792Sgshapiro#endif /* 0 */ 776964562Sgshapiro 777090792Sgshapiro/* 777190792Sgshapiro** A structure for sorting Queue according to maxqrun without 777290792Sgshapiro** screwing up Queue itself. 777390792Sgshapiro*/ 777490792Sgshapiro 777590792Sgshapirostruct sortqgrp 777690792Sgshapiro{ 777790792Sgshapiro int sg_idx; /* original index */ 777890792Sgshapiro int sg_maxqrun; /* max queue runners */ 777990792Sgshapiro}; 778090792Sgshapirotypedef struct sortqgrp SORTQGRP_T; 778190792Sgshapirostatic int cmpidx __P((const void *, const void *)); 778290792Sgshapiro 778390792Sgshapirostatic int 778490792Sgshapirocmpidx(a, b) 778590792Sgshapiro const void *a; 778690792Sgshapiro const void *b; 778790792Sgshapiro{ 778890792Sgshapiro /* The sort is highest to lowest, so the comparison is reversed */ 778990792Sgshapiro if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun) 779090792Sgshapiro return 1; 779190792Sgshapiro else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun) 779290792Sgshapiro return -1; 779390792Sgshapiro else 779490792Sgshapiro return 0; 779590792Sgshapiro} 779690792Sgshapiro 779790792Sgshapiro/* 7798285229Sgshapiro** MAKEWORKGROUPS -- balance queue groups into work groups per MaxQueueChildren 779990792Sgshapiro** 780090792Sgshapiro** Take the now defined queue groups and assign them to work groups. 780190792Sgshapiro** This is done to balance out the number of concurrently active 780290792Sgshapiro** queue runners such that MaxQueueChildren is not exceeded. This may 780390792Sgshapiro** result in more than one queue group per work group. In such a case 780490792Sgshapiro** the number of running queue groups in that work group will have no 780590792Sgshapiro** more than the work group maximum number of runners (a "fair" portion 780690792Sgshapiro** of MaxQueueRunners). All queue groups within a work group will get a 780790792Sgshapiro** chance at running. 780890792Sgshapiro** 780990792Sgshapiro** Parameters: 781090792Sgshapiro** none. 781190792Sgshapiro** 781290792Sgshapiro** Returns: 781390792Sgshapiro** nothing. 781490792Sgshapiro** 781590792Sgshapiro** Side Effects: 781690792Sgshapiro** Sets up WorkGrp structure. 781790792Sgshapiro*/ 781890792Sgshapiro 781990792Sgshapirovoid 782090792Sgshapiromakeworkgroups() 782190792Sgshapiro{ 7822125820Sgshapiro int i, j, total_runners, dir, h; 782390792Sgshapiro SORTQGRP_T si[MAXQUEUEGROUPS + 1]; 782490792Sgshapiro 7825125820Sgshapiro total_runners = 0; 782690792Sgshapiro if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0) 782790792Sgshapiro { 782890792Sgshapiro /* 782990792Sgshapiro ** There is only the "mqueue" queue group (a default) 783090792Sgshapiro ** containing all of the queues. We want to provide to 783190792Sgshapiro ** this queue group the maximum allowable queue runners. 783290792Sgshapiro ** To match older behavior (8.10/8.11) we'll try for 783390792Sgshapiro ** 1 runner per queue capping it at MaxQueueChildren. 783490792Sgshapiro ** So if there are N queues, then there will be N runners 783590792Sgshapiro ** for the "mqueue" queue group (where N is kept less than 783690792Sgshapiro ** MaxQueueChildren). 783790792Sgshapiro */ 783890792Sgshapiro 783990792Sgshapiro NumWorkGroups = 1; 784090792Sgshapiro WorkGrp[0].wg_numqgrp = 1; 784190792Sgshapiro WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *)); 784290792Sgshapiro WorkGrp[0].wg_qgs[0] = Queue[0]; 784390792Sgshapiro if (MaxQueueChildren > 0 && 784490792Sgshapiro Queue[0]->qg_numqueues > MaxQueueChildren) 784590792Sgshapiro WorkGrp[0].wg_runners = MaxQueueChildren; 784690792Sgshapiro else 784790792Sgshapiro WorkGrp[0].wg_runners = Queue[0]->qg_numqueues; 784890792Sgshapiro 784990792Sgshapiro Queue[0]->qg_wgrp = 0; 785090792Sgshapiro 785190792Sgshapiro /* can't have more runners than allowed total */ 785290792Sgshapiro if (MaxQueueChildren > 0 && 785390792Sgshapiro Queue[0]->qg_maxqrun > MaxQueueChildren) 785490792Sgshapiro Queue[0]->qg_maxqrun = MaxQueueChildren; 785590792Sgshapiro WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun; 785690792Sgshapiro WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl; 785790792Sgshapiro return; 785890792Sgshapiro } 785990792Sgshapiro 786090792Sgshapiro for (i = 0; i < NumQueue; i++) 786190792Sgshapiro { 786290792Sgshapiro si[i].sg_maxqrun = Queue[i]->qg_maxqrun; 786390792Sgshapiro si[i].sg_idx = i; 7864363466Sgshapiro 7865363466Sgshapiro /* Hack to make sure BounceQueue ends up last */ 7866363466Sgshapiro if (IS_BOUNCE_QUEUE(i)) 7867363466Sgshapiro si[i].sg_maxqrun = INT_MIN; 786890792Sgshapiro } 786990792Sgshapiro qsort(si, NumQueue, sizeof(si[0]), cmpidx); 787090792Sgshapiro 787190792Sgshapiro NumWorkGroups = 0; 787290792Sgshapiro for (i = 0; i < NumQueue; i++) 787390792Sgshapiro { 7874363466Sgshapiro SKIP_BOUNCE_QUEUE(i) 787590792Sgshapiro total_runners += si[i].sg_maxqrun; 787690792Sgshapiro if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren) 787790792Sgshapiro NumWorkGroups++; 787890792Sgshapiro else 787990792Sgshapiro break; 788090792Sgshapiro } 788190792Sgshapiro 788290792Sgshapiro if (NumWorkGroups < 1) 788390792Sgshapiro NumWorkGroups = 1; /* gotta have one at least */ 788490792Sgshapiro else if (NumWorkGroups > MAXWORKGROUPS) 788590792Sgshapiro NumWorkGroups = MAXWORKGROUPS; /* the limit */ 788690792Sgshapiro 788790792Sgshapiro /* 788890792Sgshapiro ** We now know the number of work groups to pack the queue groups 788990792Sgshapiro ** into. The queue groups in 'Queue' are sorted from highest 789090792Sgshapiro ** to lowest for the number of runners per queue group. 789190792Sgshapiro ** We put the queue groups with the largest number of runners 789290792Sgshapiro ** into work groups first. Then the smaller ones are fitted in 789390792Sgshapiro ** where it looks best. 789490792Sgshapiro */ 789590792Sgshapiro 789690792Sgshapiro j = 0; 789790792Sgshapiro dir = 1; 789890792Sgshapiro for (i = 0; i < NumQueue; i++) 789990792Sgshapiro { 7900363466Sgshapiro h = si[i].sg_idx; 7901363466Sgshapiro if (tTd(41, 49)) 7902363466Sgshapiro sm_dprintf("sortqg: i=%d, j=%d, h=%d, skip=%d\n", 7903363466Sgshapiro i, j, h, IS_BOUNCE_QUEUE(h)); 7904363466Sgshapiro SKIP_BOUNCE_QUEUE(h); 7905285229Sgshapiro 790690792Sgshapiro /* a to-and-fro packing scheme, continue from last position */ 790790792Sgshapiro if (j >= NumWorkGroups) 790890792Sgshapiro { 790990792Sgshapiro dir = -1; 791090792Sgshapiro j = NumWorkGroups - 1; 791190792Sgshapiro } 791290792Sgshapiro else if (j < 0) 791390792Sgshapiro { 791490792Sgshapiro j = 0; 791590792Sgshapiro dir = 1; 791690792Sgshapiro } 791790792Sgshapiro 791890792Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 791994334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) * 792094334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 792194334Sgshapiro else 792294334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs, 792394334Sgshapiro sizeof(QUEUEGRP *) * 792494334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 792594334Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 792690792Sgshapiro { 792794334Sgshapiro syserr("!cannot allocate memory for work queues, need %d bytes", 792890792Sgshapiro (int) (sizeof(QUEUEGRP *) * 792990792Sgshapiro (WorkGrp[j].wg_numqgrp + 1))); 793090792Sgshapiro } 793190792Sgshapiro 7932125820Sgshapiro WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[h]; 793390792Sgshapiro WorkGrp[j].wg_numqgrp++; 7934125820Sgshapiro WorkGrp[j].wg_runners += Queue[h]->qg_maxqrun; 7935125820Sgshapiro Queue[h]->qg_wgrp = j; 793690792Sgshapiro 793790792Sgshapiro if (WorkGrp[j].wg_maxact == 0) 793890792Sgshapiro { 793990792Sgshapiro /* can't have more runners than allowed total */ 794090792Sgshapiro if (MaxQueueChildren > 0 && 7941125820Sgshapiro Queue[h]->qg_maxqrun > MaxQueueChildren) 7942125820Sgshapiro Queue[h]->qg_maxqrun = MaxQueueChildren; 7943125820Sgshapiro WorkGrp[j].wg_maxact = Queue[h]->qg_maxqrun; 794490792Sgshapiro } 794590792Sgshapiro 794690792Sgshapiro /* 794790792Sgshapiro ** XXX: must wg_lowqintvl be the GCD? 794890792Sgshapiro ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for 794990792Sgshapiro ** qg2 occur? 795090792Sgshapiro */ 795190792Sgshapiro 795290792Sgshapiro /* keep track of the lowest interval for a persistent runner */ 7953125820Sgshapiro if (Queue[h]->qg_queueintvl > 0 && 7954125820Sgshapiro WorkGrp[j].wg_lowqintvl < Queue[h]->qg_queueintvl) 7955125820Sgshapiro WorkGrp[j].wg_lowqintvl = Queue[h]->qg_queueintvl; 795690792Sgshapiro j += dir; 795790792Sgshapiro } 795890792Sgshapiro if (tTd(41, 9)) 795990792Sgshapiro { 796090792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 796190792Sgshapiro { 796290792Sgshapiro sm_dprintf("Workgroup[%d]=", i); 796390792Sgshapiro for (j = 0; j < WorkGrp[i].wg_numqgrp; j++) 796490792Sgshapiro { 796590792Sgshapiro sm_dprintf("%s, ", 796690792Sgshapiro WorkGrp[i].wg_qgs[j]->qg_name); 796790792Sgshapiro } 7968363466Sgshapiro if (tTd(41, 12)) 7969363466Sgshapiro sm_dprintf("lowqintvl=%d", 7970363466Sgshapiro (int) WorkGrp[i].wg_lowqintvl); 797190792Sgshapiro sm_dprintf("\n"); 797290792Sgshapiro } 797390792Sgshapiro } 797490792Sgshapiro} 797590792Sgshapiro 797690792Sgshapiro/* 797790792Sgshapiro** DUP_DF -- duplicate envelope data file 797890792Sgshapiro** 797990792Sgshapiro** Copy the data file from the 'old' envelope to the 'new' envelope 798090792Sgshapiro** in the most efficient way possible. 798190792Sgshapiro** 798290792Sgshapiro** Create a hard link from the 'old' data file to the 'new' data file. 798390792Sgshapiro** If the old and new queue directories are on different file systems, 798490792Sgshapiro** then the new data file link is created in the old queue directory, 798590792Sgshapiro** and the new queue file will contain a 'd' record pointing to the 798690792Sgshapiro** directory containing the new data file. 798790792Sgshapiro** 798890792Sgshapiro** Parameters: 798990792Sgshapiro** old -- old envelope. 799090792Sgshapiro** new -- new envelope. 799190792Sgshapiro** 799290792Sgshapiro** Results: 799390792Sgshapiro** Returns true on success, false on failure. 799490792Sgshapiro** 799590792Sgshapiro** Side Effects: 799690792Sgshapiro** On success, the new data file is created. 799790792Sgshapiro** On fatal failure, EF_FATALERRS is set in old->e_flags. 799890792Sgshapiro*/ 799990792Sgshapiro 800090792Sgshapirostatic bool dup_df __P((ENVELOPE *, ENVELOPE *)); 800190792Sgshapiro 800290792Sgshapirostatic bool 800390792Sgshapirodup_df(old, new) 800490792Sgshapiro ENVELOPE *old; 800590792Sgshapiro ENVELOPE *new; 800690792Sgshapiro{ 800790792Sgshapiro int ofs, nfs, r; 800890792Sgshapiro char opath[MAXPATHLEN]; 800990792Sgshapiro char npath[MAXPATHLEN]; 801090792Sgshapiro 801194334Sgshapiro if (!bitset(EF_HAS_DF, old->e_flags)) 801294334Sgshapiro { 801394334Sgshapiro /* 801494334Sgshapiro ** this can happen if: SuperSafe != True 801594334Sgshapiro ** and a bounce mail is sent that is split. 801694334Sgshapiro */ 801794334Sgshapiro 801894334Sgshapiro queueup(old, false, true); 801994334Sgshapiro } 802090792Sgshapiro SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir)); 802190792Sgshapiro SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir)); 802290792Sgshapiro 8023168515Sgshapiro (void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof(opath)); 8024168515Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath)); 802590792Sgshapiro 802690792Sgshapiro if (old->e_dfp != NULL) 802790792Sgshapiro { 802890792Sgshapiro r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL); 802990792Sgshapiro if (r < 0 && errno != EINVAL) 803090792Sgshapiro { 803190792Sgshapiro syserr("@can't commit %s", opath); 803290792Sgshapiro old->e_flags |= EF_FATALERRS; 803390792Sgshapiro return false; 803490792Sgshapiro } 803590792Sgshapiro } 803690792Sgshapiro 803790792Sgshapiro /* 803890792Sgshapiro ** Attempt to create a hard link, if we think both old and new 803990792Sgshapiro ** are on the same file system, otherwise copy the file. 804090792Sgshapiro ** 804190792Sgshapiro ** Don't waste time attempting a hard link unless old and new 804290792Sgshapiro ** are on the same file system. 804390792Sgshapiro */ 804490792Sgshapiro 8045157001Sgshapiro SM_REQUIRE(ISVALIDQGRP(old->e_dfqgrp) && ISVALIDQDIR(old->e_dfqdir)); 8046157001Sgshapiro SM_REQUIRE(ISVALIDQGRP(new->e_dfqgrp) && ISVALIDQDIR(new->e_dfqdir)); 8047157001Sgshapiro 8048157001Sgshapiro ofs = Queue[old->e_dfqgrp]->qg_qpaths[old->e_dfqdir].qp_fsysidx; 8049157001Sgshapiro nfs = Queue[new->e_dfqgrp]->qg_qpaths[new->e_dfqdir].qp_fsysidx; 805090792Sgshapiro if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs)) 805190792Sgshapiro { 805290792Sgshapiro if (link(opath, npath) == 0) 805390792Sgshapiro { 805490792Sgshapiro new->e_flags |= EF_HAS_DF; 805590792Sgshapiro SYNC_DIR(npath, true); 805690792Sgshapiro return true; 805790792Sgshapiro } 805890792Sgshapiro goto error; 805990792Sgshapiro } 806090792Sgshapiro 806190792Sgshapiro /* 806290792Sgshapiro ** Can't link across queue directories, so try to create a hard 806390792Sgshapiro ** link in the same queue directory as the old df file. 806490792Sgshapiro ** The qf file will refer to the new df file using a 'd' record. 806590792Sgshapiro */ 806690792Sgshapiro 806790792Sgshapiro new->e_dfqgrp = old->e_dfqgrp; 806890792Sgshapiro new->e_dfqdir = old->e_dfqdir; 8069168515Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath)); 807090792Sgshapiro if (link(opath, npath) == 0) 807190792Sgshapiro { 807290792Sgshapiro new->e_flags |= EF_HAS_DF; 807390792Sgshapiro SYNC_DIR(npath, true); 807490792Sgshapiro return true; 807590792Sgshapiro } 807690792Sgshapiro 807790792Sgshapiro error: 807890792Sgshapiro if (LogLevel > 0) 807990792Sgshapiro sm_syslog(LOG_ERR, old->e_id, 808090792Sgshapiro "dup_df: can't link %s to %s, error=%s, envelope splitting failed", 808190792Sgshapiro opath, npath, sm_errstring(errno)); 808290792Sgshapiro return false; 808390792Sgshapiro} 808490792Sgshapiro 808590792Sgshapiro/* 808690792Sgshapiro** SPLIT_ENV -- Allocate a new envelope based on a given envelope. 808790792Sgshapiro** 808890792Sgshapiro** Parameters: 808990792Sgshapiro** e -- envelope. 809090792Sgshapiro** sendqueue -- sendqueue for new envelope. 809190792Sgshapiro** qgrp -- index of queue group. 809290792Sgshapiro** qdir -- queue directory. 809390792Sgshapiro** 809490792Sgshapiro** Results: 809590792Sgshapiro** new envelope. 809690792Sgshapiro** 809790792Sgshapiro*/ 809890792Sgshapiro 809990792Sgshapirostatic ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int)); 810090792Sgshapiro 810190792Sgshapirostatic ENVELOPE * 810290792Sgshapirosplit_env(e, sendqueue, qgrp, qdir) 810390792Sgshapiro ENVELOPE *e; 810490792Sgshapiro ADDRESS *sendqueue; 810590792Sgshapiro int qgrp; 810690792Sgshapiro int qdir; 810790792Sgshapiro{ 810890792Sgshapiro ENVELOPE *ee; 810990792Sgshapiro 8110168515Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof(*ee)); 811190792Sgshapiro STRUCTCOPY(*e, *ee); 811290792Sgshapiro ee->e_message = NULL; /* XXX use original message? */ 811390792Sgshapiro ee->e_id = NULL; 811490792Sgshapiro assign_queueid(ee); 811590792Sgshapiro ee->e_sendqueue = sendqueue; 811690792Sgshapiro ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS 811790792Sgshapiro |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF); 811890792Sgshapiro ee->e_flags |= EF_NORECEIPT; /* XXX really? */ 811990792Sgshapiro ee->e_from.q_state = QS_SENDER; 812090792Sgshapiro ee->e_dfp = NULL; 812190792Sgshapiro ee->e_lockfp = NULL; 812290792Sgshapiro if (e->e_xfp != NULL) 812390792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 812494334Sgshapiro 812594334Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 812694334Sgshapiro if (ee->e_xfp == NULL) 812794334Sgshapiro openxscript(ee); 812894334Sgshapiro 812990792Sgshapiro ee->e_qgrp = ee->e_dfqgrp = qgrp; 813090792Sgshapiro ee->e_qdir = ee->e_dfqdir = qdir; 813190792Sgshapiro ee->e_errormode = EM_MAIL; 813290792Sgshapiro ee->e_statmsg = NULL; 813390792Sgshapiro if (e->e_quarmsg != NULL) 813490792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 813590792Sgshapiro e->e_quarmsg); 813690792Sgshapiro 813790792Sgshapiro /* 813890792Sgshapiro ** XXX Not sure if this copying is necessary. 813994334Sgshapiro ** sendall() does this copying, but I (dm) don't know if that is 814090792Sgshapiro ** because of the storage management discipline we were using 814190792Sgshapiro ** before rpools were introduced, or if it is because these lists 814290792Sgshapiro ** can be modified later. 814390792Sgshapiro */ 814490792Sgshapiro 814590792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 814690792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool); 814790792Sgshapiro 814890792Sgshapiro return ee; 814990792Sgshapiro} 815090792Sgshapiro 815190792Sgshapiro/* return values from split functions, check also below! */ 815290792Sgshapiro#define SM_SPLIT_FAIL (0) 815390792Sgshapiro#define SM_SPLIT_NONE (1) 815490792Sgshapiro#define SM_SPLIT_NEW(n) (1 + (n)) 815590792Sgshapiro 815690792Sgshapiro/* 815790792Sgshapiro** SPLIT_ACROSS_QUEUE_GROUPS 815890792Sgshapiro** 815990792Sgshapiro** This function splits an envelope across multiple queue groups 816090792Sgshapiro** based on the queue group of each recipient. 816190792Sgshapiro** 816290792Sgshapiro** Parameters: 816390792Sgshapiro** e -- envelope. 816490792Sgshapiro** 816590792Sgshapiro** Results: 816690792Sgshapiro** SM_SPLIT_FAIL on failure 816790792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 816890792Sgshapiro** or 1 + the number of additional envelopes created. 816990792Sgshapiro** 817090792Sgshapiro** Side Effects: 817190792Sgshapiro** On success, e->e_sibling points to a list of zero or more 817290792Sgshapiro** additional envelopes, and the associated data files exist 817390792Sgshapiro** on disk. But the queue files are not created. 817490792Sgshapiro** 817590792Sgshapiro** On failure, e->e_sibling is not changed. 817690792Sgshapiro** The order of recipients in e->e_sendqueue is permuted. 817790792Sgshapiro** Abandoned data files for additional envelopes that failed 817890792Sgshapiro** to be created may exist on disk. 817990792Sgshapiro*/ 818090792Sgshapiro 818190792Sgshapirostatic int q_qgrp_compare __P((const void *, const void *)); 818290792Sgshapirostatic int e_filesys_compare __P((const void *, const void *)); 818390792Sgshapiro 818490792Sgshapirostatic int 818590792Sgshapiroq_qgrp_compare(p1, p2) 818690792Sgshapiro const void *p1; 818790792Sgshapiro const void *p2; 818890792Sgshapiro{ 818990792Sgshapiro ADDRESS **pq1 = (ADDRESS **) p1; 819090792Sgshapiro ADDRESS **pq2 = (ADDRESS **) p2; 819190792Sgshapiro 819290792Sgshapiro return (*pq1)->q_qgrp - (*pq2)->q_qgrp; 819390792Sgshapiro} 819490792Sgshapiro 819590792Sgshapirostatic int 819690792Sgshapiroe_filesys_compare(p1, p2) 819790792Sgshapiro const void *p1; 819890792Sgshapiro const void *p2; 819990792Sgshapiro{ 820090792Sgshapiro ENVELOPE **pe1 = (ENVELOPE **) p1; 820190792Sgshapiro ENVELOPE **pe2 = (ENVELOPE **) p2; 820290792Sgshapiro int fs1, fs2; 820390792Sgshapiro 820490792Sgshapiro fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx; 820590792Sgshapiro fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx; 820690792Sgshapiro if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2)) 820790792Sgshapiro return -1; 820890792Sgshapiro if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2)) 820990792Sgshapiro return 1; 821090792Sgshapiro return 0; 821190792Sgshapiro} 821290792Sgshapiro 8213168515Sgshapirostatic int split_across_queue_groups __P((ENVELOPE *)); 821490792Sgshapirostatic int 821590792Sgshapirosplit_across_queue_groups(e) 821690792Sgshapiro ENVELOPE *e; 821790792Sgshapiro{ 821890792Sgshapiro int naddrs, nsplits, i; 8219102528Sgshapiro bool changed; 822090792Sgshapiro char **pvp; 822190792Sgshapiro ADDRESS *q, **addrs; 822290792Sgshapiro ENVELOPE *ee, *es; 822390792Sgshapiro ENVELOPE *splits[MAXQUEUEGROUPS]; 822490792Sgshapiro char pvpbuf[PSBUFSIZE]; 822590792Sgshapiro 822690792Sgshapiro SM_REQUIRE(ISVALIDQGRP(e->e_qgrp)); 822790792Sgshapiro 822890792Sgshapiro /* Count addresses and assign queue groups. */ 822990792Sgshapiro naddrs = 0; 8230102528Sgshapiro changed = false; 823190792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 823290792Sgshapiro { 823390792Sgshapiro if (QS_IS_DEAD(q->q_state)) 823490792Sgshapiro continue; 823590792Sgshapiro ++naddrs; 823690792Sgshapiro 823790792Sgshapiro /* bad addresses and those already sent stay put */ 823890792Sgshapiro if (QS_IS_BADADDR(q->q_state) || 823990792Sgshapiro QS_IS_SENT(q->q_state)) 824090792Sgshapiro q->q_qgrp = e->e_qgrp; 824190792Sgshapiro else if (!ISVALIDQGRP(q->q_qgrp)) 824290792Sgshapiro { 824390792Sgshapiro /* call ruleset which should return a queue group */ 824490792Sgshapiro i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp, 824590792Sgshapiro pvpbuf, sizeof(pvpbuf)); 824690792Sgshapiro if (i == EX_OK && 824790792Sgshapiro pvp != NULL && pvp[0] != NULL && 824890792Sgshapiro (pvp[0][0] & 0377) == CANONNET && 824990792Sgshapiro pvp[1] != NULL && pvp[1][0] != '\0') 825090792Sgshapiro { 825190792Sgshapiro i = name2qid(pvp[1]); 825290792Sgshapiro if (ISVALIDQGRP(i)) 825390792Sgshapiro { 825490792Sgshapiro q->q_qgrp = i; 8255102528Sgshapiro changed = true; 825690792Sgshapiro if (tTd(20, 4)) 825790792Sgshapiro sm_syslog(LOG_INFO, NOQID, 825890792Sgshapiro "queue group name %s -> %d", 825990792Sgshapiro pvp[1], i); 826090792Sgshapiro continue; 826190792Sgshapiro } 826290792Sgshapiro else if (LogLevel > 10) 826390792Sgshapiro sm_syslog(LOG_INFO, NOQID, 826490792Sgshapiro "can't find queue group name %s, selection ignored", 826590792Sgshapiro pvp[1]); 826690792Sgshapiro } 826790792Sgshapiro if (q->q_mailer != NULL && 826890792Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 8269102528Sgshapiro { 8270102528Sgshapiro changed = true; 827190792Sgshapiro q->q_qgrp = q->q_mailer->m_qgrp; 8272102528Sgshapiro } 827394334Sgshapiro else if (ISVALIDQGRP(e->e_qgrp)) 827494334Sgshapiro q->q_qgrp = e->e_qgrp; 827590792Sgshapiro else 827690792Sgshapiro q->q_qgrp = 0; 827790792Sgshapiro } 827890792Sgshapiro } 827990792Sgshapiro 828090792Sgshapiro /* only one address? nothing to split. */ 8281102528Sgshapiro if (naddrs <= 1 && !changed) 828290792Sgshapiro return SM_SPLIT_NONE; 828390792Sgshapiro 828490792Sgshapiro /* sort the addresses by queue group */ 828590792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *)); 828690792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 828790792Sgshapiro { 828890792Sgshapiro if (QS_IS_DEAD(q->q_state)) 828990792Sgshapiro continue; 829090792Sgshapiro addrs[i++] = q; 829190792Sgshapiro } 829290792Sgshapiro qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare); 829390792Sgshapiro 829490792Sgshapiro /* split into multiple envelopes, by queue group */ 829590792Sgshapiro nsplits = 0; 829690792Sgshapiro es = NULL; 829790792Sgshapiro e->e_sendqueue = NULL; 829890792Sgshapiro for (i = 0; i < naddrs; ++i) 829990792Sgshapiro { 830090792Sgshapiro if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp) 830190792Sgshapiro addrs[i]->q_next = NULL; 830290792Sgshapiro else 830390792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 830490792Sgshapiro 830590792Sgshapiro /* same queue group as original envelope? */ 830690792Sgshapiro if (addrs[i]->q_qgrp == e->e_qgrp) 830790792Sgshapiro { 830890792Sgshapiro if (e->e_sendqueue == NULL) 830990792Sgshapiro e->e_sendqueue = addrs[i]; 831090792Sgshapiro continue; 831190792Sgshapiro } 831290792Sgshapiro 831390792Sgshapiro /* different queue group than original envelope */ 831490792Sgshapiro if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp) 831590792Sgshapiro { 831690792Sgshapiro ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR); 831790792Sgshapiro es = ee; 831890792Sgshapiro splits[nsplits++] = ee; 831990792Sgshapiro } 832090792Sgshapiro } 832190792Sgshapiro 832290792Sgshapiro /* no splits? return right now. */ 832390792Sgshapiro if (nsplits <= 0) 832490792Sgshapiro return SM_SPLIT_NONE; 832590792Sgshapiro 832690792Sgshapiro /* assign a queue directory to each additional envelope */ 832790792Sgshapiro for (i = 0; i < nsplits; ++i) 832890792Sgshapiro { 832990792Sgshapiro es = splits[i]; 833090792Sgshapiro#if 0 833190792Sgshapiro es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es); 8332363466Sgshapiro#endif 833390792Sgshapiro if (!setnewqueue(es)) 833490792Sgshapiro goto failure; 833590792Sgshapiro } 833690792Sgshapiro 833790792Sgshapiro /* sort the additional envelopes by queue file system */ 833890792Sgshapiro qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare); 833990792Sgshapiro 834090792Sgshapiro /* create data files for each additional envelope */ 834190792Sgshapiro if (!dup_df(e, splits[0])) 834290792Sgshapiro { 834390792Sgshapiro i = 0; 834490792Sgshapiro goto failure; 834590792Sgshapiro } 834690792Sgshapiro for (i = 1; i < nsplits; ++i) 834790792Sgshapiro { 834890792Sgshapiro /* copy or link to the previous data file */ 834990792Sgshapiro if (!dup_df(splits[i - 1], splits[i])) 835090792Sgshapiro goto failure; 835190792Sgshapiro } 835290792Sgshapiro 835390792Sgshapiro /* success: prepend the new envelopes to the e->e_sibling list */ 835490792Sgshapiro for (i = 0; i < nsplits; ++i) 835590792Sgshapiro { 835690792Sgshapiro es = splits[i]; 835790792Sgshapiro es->e_sibling = e->e_sibling; 835890792Sgshapiro e->e_sibling = es; 835990792Sgshapiro } 836090792Sgshapiro return SM_SPLIT_NEW(nsplits); 836190792Sgshapiro 836290792Sgshapiro /* failure: clean up */ 836390792Sgshapiro failure: 836490792Sgshapiro if (i > 0) 836590792Sgshapiro { 836690792Sgshapiro int j; 836790792Sgshapiro 836890792Sgshapiro for (j = 0; j < i; j++) 836990792Sgshapiro (void) unlink(queuename(splits[j], DATAFL_LETTER)); 837090792Sgshapiro } 837190792Sgshapiro e->e_sendqueue = addrs[0]; 837290792Sgshapiro for (i = 0; i < naddrs - 1; ++i) 837390792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 837490792Sgshapiro addrs[naddrs - 1]->q_next = NULL; 837590792Sgshapiro return SM_SPLIT_FAIL; 837690792Sgshapiro} 837790792Sgshapiro 837890792Sgshapiro/* 837990792Sgshapiro** SPLIT_WITHIN_QUEUE 838090792Sgshapiro** 838190792Sgshapiro** Split an envelope with multiple recipients into several 838290792Sgshapiro** envelopes within the same queue directory, if the number of 838390792Sgshapiro** recipients exceeds the limit for the queue group. 838490792Sgshapiro** 838590792Sgshapiro** Parameters: 838690792Sgshapiro** e -- envelope. 838790792Sgshapiro** 838890792Sgshapiro** Results: 838990792Sgshapiro** SM_SPLIT_FAIL on failure 839090792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 839190792Sgshapiro** or 1 + the number of additional envelopes created. 839290792Sgshapiro*/ 839390792Sgshapiro 839490792Sgshapiro#define SPLIT_LOG_LEVEL 8 839590792Sgshapiro 839690792Sgshapirostatic int split_within_queue __P((ENVELOPE *)); 839790792Sgshapiro 839890792Sgshapirostatic int 839990792Sgshapirosplit_within_queue(e) 840090792Sgshapiro ENVELOPE *e; 840190792Sgshapiro{ 840290792Sgshapiro int maxrcpt, nrcpt, ndead, nsplit, i; 840390792Sgshapiro int j, l; 840490792Sgshapiro char *lsplits; 840590792Sgshapiro ADDRESS *q, **addrs; 840690792Sgshapiro ENVELOPE *ee, *firstsibling; 840790792Sgshapiro 840890792Sgshapiro if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags)) 840990792Sgshapiro return SM_SPLIT_NONE; 841090792Sgshapiro 841190792Sgshapiro /* don't bother if there is no recipient limit */ 841290792Sgshapiro maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt; 841390792Sgshapiro if (maxrcpt <= 0) 841490792Sgshapiro return SM_SPLIT_NONE; 841590792Sgshapiro 841690792Sgshapiro /* count recipients */ 841790792Sgshapiro nrcpt = 0; 841890792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 841990792Sgshapiro { 842090792Sgshapiro if (QS_IS_DEAD(q->q_state)) 842190792Sgshapiro continue; 842290792Sgshapiro ++nrcpt; 842390792Sgshapiro } 842490792Sgshapiro if (nrcpt <= maxrcpt) 842590792Sgshapiro return SM_SPLIT_NONE; 842690792Sgshapiro 842790792Sgshapiro /* 842890792Sgshapiro ** Preserve the recipient list 842990792Sgshapiro ** so that we can restore it in case of error. 843090792Sgshapiro ** (But we discard dead addresses.) 843190792Sgshapiro */ 843290792Sgshapiro 843390792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *)); 843490792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 843590792Sgshapiro { 843690792Sgshapiro if (QS_IS_DEAD(q->q_state)) 843790792Sgshapiro continue; 843890792Sgshapiro addrs[i++] = q; 843990792Sgshapiro } 844090792Sgshapiro 844190792Sgshapiro /* 844290792Sgshapiro ** Partition the recipient list so that bad and sent addresses 844390792Sgshapiro ** come first. These will go with the original envelope, and 844490792Sgshapiro ** do not count towards the maxrcpt limit. 844590792Sgshapiro ** addrs[] does not contain QS_IS_DEAD() addresses. 844690792Sgshapiro */ 844790792Sgshapiro 844890792Sgshapiro ndead = 0; 844990792Sgshapiro for (i = 0; i < nrcpt; ++i) 845090792Sgshapiro { 845190792Sgshapiro if (QS_IS_BADADDR(addrs[i]->q_state) || 845290792Sgshapiro QS_IS_SENT(addrs[i]->q_state) || 845390792Sgshapiro QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */ 845490792Sgshapiro { 845590792Sgshapiro if (i > ndead) 845690792Sgshapiro { 845790792Sgshapiro ADDRESS *tmp = addrs[i]; 845890792Sgshapiro 845990792Sgshapiro addrs[i] = addrs[ndead]; 846090792Sgshapiro addrs[ndead] = tmp; 846190792Sgshapiro } 846290792Sgshapiro ++ndead; 846390792Sgshapiro } 846490792Sgshapiro } 846590792Sgshapiro 846690792Sgshapiro /* Check if no splitting required. */ 846790792Sgshapiro if (nrcpt - ndead <= maxrcpt) 846890792Sgshapiro return SM_SPLIT_NONE; 846990792Sgshapiro 847090792Sgshapiro /* fix links */ 847190792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 847290792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 847390792Sgshapiro addrs[nrcpt - 1]->q_next = NULL; 847490792Sgshapiro e->e_sendqueue = addrs[0]; 847590792Sgshapiro 847690792Sgshapiro /* prepare buffer for logging */ 847790792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL) 847890792Sgshapiro { 847990792Sgshapiro l = MAXLINE; 848090792Sgshapiro lsplits = sm_malloc(l); 848190792Sgshapiro if (lsplits != NULL) 848290792Sgshapiro *lsplits = '\0'; 848390792Sgshapiro j = 0; 848490792Sgshapiro } 848590792Sgshapiro else 848690792Sgshapiro { 848790792Sgshapiro /* get rid of stupid compiler warnings */ 848890792Sgshapiro lsplits = NULL; 848990792Sgshapiro j = l = 0; 849090792Sgshapiro } 849190792Sgshapiro 849290792Sgshapiro /* split the envelope */ 849390792Sgshapiro firstsibling = e->e_sibling; 849490792Sgshapiro i = maxrcpt + ndead; 849590792Sgshapiro nsplit = 0; 849690792Sgshapiro for (;;) 849790792Sgshapiro { 849890792Sgshapiro addrs[i - 1]->q_next = NULL; 849990792Sgshapiro ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir); 850090792Sgshapiro if (!dup_df(e, ee)) 850190792Sgshapiro { 850290792Sgshapiro 850390792Sgshapiro ee = firstsibling; 850490792Sgshapiro while (ee != NULL) 850590792Sgshapiro { 850690792Sgshapiro (void) unlink(queuename(ee, DATAFL_LETTER)); 850790792Sgshapiro ee = ee->e_sibling; 850890792Sgshapiro } 850990792Sgshapiro 851090792Sgshapiro /* Error. Restore e's sibling & recipient lists. */ 851190792Sgshapiro e->e_sibling = firstsibling; 851290792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 851390792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 8514110560Sgshapiro if (lsplits != NULL) 8515110560Sgshapiro sm_free(lsplits); 851690792Sgshapiro return SM_SPLIT_FAIL; 851790792Sgshapiro } 851890792Sgshapiro 851990792Sgshapiro /* prepend the new envelope to e->e_sibling */ 852090792Sgshapiro ee->e_sibling = e->e_sibling; 852190792Sgshapiro e->e_sibling = ee; 852290792Sgshapiro ++nsplit; 852390792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 852490792Sgshapiro { 852590792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 852690792Sgshapiro { 852790792Sgshapiro char *p; 852890792Sgshapiro 852990792Sgshapiro l += MAXLINE; 853090792Sgshapiro p = sm_realloc(lsplits, l); 853190792Sgshapiro if (p == NULL) 853290792Sgshapiro { 853390792Sgshapiro /* let's try to get this done */ 853490792Sgshapiro sm_free(lsplits); 853590792Sgshapiro lsplits = NULL; 853690792Sgshapiro } 853790792Sgshapiro else 853890792Sgshapiro lsplits = p; 853990792Sgshapiro } 854090792Sgshapiro if (lsplits != NULL) 854190792Sgshapiro { 854290792Sgshapiro if (j == 0) 854390792Sgshapiro j += sm_strlcat(lsplits + j, 854490792Sgshapiro ee->e_id, 854590792Sgshapiro l - j); 854690792Sgshapiro else 854790792Sgshapiro j += sm_strlcat2(lsplits + j, 854890792Sgshapiro "; ", 854990792Sgshapiro ee->e_id, 855090792Sgshapiro l - j); 855190792Sgshapiro SM_ASSERT(j < l); 855290792Sgshapiro } 855390792Sgshapiro } 855490792Sgshapiro if (nrcpt - i <= maxrcpt) 855590792Sgshapiro break; 855690792Sgshapiro i += maxrcpt; 855790792Sgshapiro } 8558110560Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 855990792Sgshapiro { 8560110560Sgshapiro if (nsplit > 0) 8561110560Sgshapiro { 8562110560Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, 8563110560Sgshapiro "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s", 8564110560Sgshapiro maxrcpt, nrcpt - ndead, nsplit, 8565110560Sgshapiro nsplit > 1 ? "s" : "", lsplits); 8566110560Sgshapiro } 856790792Sgshapiro sm_free(lsplits); 856890792Sgshapiro } 856990792Sgshapiro return SM_SPLIT_NEW(nsplit); 857090792Sgshapiro} 857190792Sgshapiro/* 857290792Sgshapiro** SPLIT_BY_RECIPIENT 857390792Sgshapiro** 857490792Sgshapiro** Split an envelope with multiple recipients into multiple 857590792Sgshapiro** envelopes as required by the sendmail configuration. 857690792Sgshapiro** 857790792Sgshapiro** Parameters: 857890792Sgshapiro** e -- envelope. 857990792Sgshapiro** 858090792Sgshapiro** Results: 858190792Sgshapiro** Returns true on success, false on failure. 858290792Sgshapiro** 858390792Sgshapiro** Side Effects: 858490792Sgshapiro** see split_across_queue_groups(), split_within_queue(e) 858590792Sgshapiro*/ 858690792Sgshapiro 858790792Sgshapirobool 858890792Sgshapirosplit_by_recipient(e) 858990792Sgshapiro ENVELOPE *e; 859090792Sgshapiro{ 859190792Sgshapiro int split, n, i, j, l; 859290792Sgshapiro char *lsplits; 859390792Sgshapiro ENVELOPE *ee, *next, *firstsibling; 859490792Sgshapiro 859590792Sgshapiro if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) || 859690792Sgshapiro bitset(EF_SPLIT, e->e_flags)) 859790792Sgshapiro return true; 859890792Sgshapiro n = split_across_queue_groups(e); 859990792Sgshapiro if (n == SM_SPLIT_FAIL) 860090792Sgshapiro return false; 860190792Sgshapiro firstsibling = ee = e->e_sibling; 860290792Sgshapiro if (n > 1 && LogLevel > SPLIT_LOG_LEVEL) 860390792Sgshapiro { 860490792Sgshapiro l = MAXLINE; 860590792Sgshapiro lsplits = sm_malloc(l); 860690792Sgshapiro if (lsplits != NULL) 860790792Sgshapiro *lsplits = '\0'; 860890792Sgshapiro j = 0; 860990792Sgshapiro } 861090792Sgshapiro else 861190792Sgshapiro { 861290792Sgshapiro /* get rid of stupid compiler warnings */ 861390792Sgshapiro lsplits = NULL; 861490792Sgshapiro j = l = 0; 861590792Sgshapiro } 861690792Sgshapiro for (i = 1; i < n; ++i) 861790792Sgshapiro { 861890792Sgshapiro next = ee->e_sibling; 861990792Sgshapiro if (split_within_queue(ee) == SM_SPLIT_FAIL) 862090792Sgshapiro { 862190792Sgshapiro e->e_sibling = firstsibling; 8622363466Sgshapiro SM_FREE(lsplits); 862390792Sgshapiro return false; 862490792Sgshapiro } 862590792Sgshapiro ee->e_flags |= EF_SPLIT; 862690792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 862790792Sgshapiro { 862890792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 862990792Sgshapiro { 863090792Sgshapiro char *p; 863190792Sgshapiro 863290792Sgshapiro l += MAXLINE; 863390792Sgshapiro p = sm_realloc(lsplits, l); 863490792Sgshapiro if (p == NULL) 863590792Sgshapiro { 863690792Sgshapiro /* let's try to get this done */ 8637363466Sgshapiro SM_FREE(lsplits); 863890792Sgshapiro } 863990792Sgshapiro else 864090792Sgshapiro lsplits = p; 864190792Sgshapiro } 864290792Sgshapiro if (lsplits != NULL) 864390792Sgshapiro { 864490792Sgshapiro if (j == 0) 864590792Sgshapiro j += sm_strlcat(lsplits + j, 864690792Sgshapiro ee->e_id, l - j); 864790792Sgshapiro else 864890792Sgshapiro j += sm_strlcat2(lsplits + j, "; ", 864990792Sgshapiro ee->e_id, l - j); 865090792Sgshapiro SM_ASSERT(j < l); 865190792Sgshapiro } 865290792Sgshapiro } 865390792Sgshapiro ee = next; 865490792Sgshapiro } 865590792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1) 865690792Sgshapiro { 865790792Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s", 865890792Sgshapiro n - 1, n > 2 ? "s" : "", lsplits); 8659363466Sgshapiro SM_FREE(lsplits); 866090792Sgshapiro } 866190792Sgshapiro split = split_within_queue(e) != SM_SPLIT_FAIL; 866290792Sgshapiro if (split) 866390792Sgshapiro e->e_flags |= EF_SPLIT; 866490792Sgshapiro return split; 866590792Sgshapiro} 866690792Sgshapiro 866790792Sgshapiro/* 866890792Sgshapiro** QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope 866990792Sgshapiro** 867090792Sgshapiro** Add/remove quarantine reason and requeue appropriately. 867190792Sgshapiro** 867290792Sgshapiro** Parameters: 867390792Sgshapiro** qgrp -- queue group for the item 867490792Sgshapiro** qdir -- queue directory in the given queue group 867590792Sgshapiro** e -- envelope information for the item 867690792Sgshapiro** reason -- quarantine reason, NULL means unquarantine. 867790792Sgshapiro** 867890792Sgshapiro** Results: 867990792Sgshapiro** true if item changed, false otherwise 868090792Sgshapiro** 868190792Sgshapiro** Side Effects: 868290792Sgshapiro** Changes quarantine tag in queue file and renames it. 868390792Sgshapiro*/ 868490792Sgshapiro 868590792Sgshapirostatic bool 868690792Sgshapiroquarantine_queue_item(qgrp, qdir, e, reason) 868790792Sgshapiro int qgrp; 868890792Sgshapiro int qdir; 868990792Sgshapiro ENVELOPE *e; 869090792Sgshapiro char *reason; 869190792Sgshapiro{ 869290792Sgshapiro bool dirty = false; 869390792Sgshapiro bool failing = false; 869490792Sgshapiro bool foundq = false; 869590792Sgshapiro bool finished = false; 869690792Sgshapiro int fd; 869790792Sgshapiro int flags; 869890792Sgshapiro int oldtype; 869990792Sgshapiro int newtype; 870090792Sgshapiro int save_errno; 870190792Sgshapiro MODE_T oldumask = 0; 870290792Sgshapiro SM_FILE_T *oldqfp, *tempqfp; 870390792Sgshapiro char *bp; 8704168515Sgshapiro int bufsize; 870590792Sgshapiro char oldqf[MAXPATHLEN]; 870690792Sgshapiro char tempqf[MAXPATHLEN]; 870790792Sgshapiro char newqf[MAXPATHLEN]; 870890792Sgshapiro char buf[MAXLINE]; 870990792Sgshapiro 871090792Sgshapiro oldtype = queue_letter(e, ANYQFL_LETTER); 8711168515Sgshapiro (void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof(oldqf)); 8712168515Sgshapiro (void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof(tempqf)); 871390792Sgshapiro 871490792Sgshapiro /* 871590792Sgshapiro ** Instead of duplicating all the open 871690792Sgshapiro ** and lock code here, tell readqf() to 871790792Sgshapiro ** do that work and return the open 871890792Sgshapiro ** file pointer in e_lockfp. Note that 871990792Sgshapiro ** we must release the locks properly when 872090792Sgshapiro ** we are done. 872190792Sgshapiro */ 872290792Sgshapiro 872390792Sgshapiro if (!readqf(e, true)) 872490792Sgshapiro { 872590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 872690792Sgshapiro "Skipping %s\n", qid_printname(e)); 872790792Sgshapiro return false; 872890792Sgshapiro } 872990792Sgshapiro oldqfp = e->e_lockfp; 873090792Sgshapiro 873190792Sgshapiro /* open the new queue file */ 873290792Sgshapiro flags = O_CREAT|O_WRONLY|O_EXCL; 873390792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 873490792Sgshapiro oldumask = umask(002); 873590792Sgshapiro fd = open(tempqf, flags, QueueFileMode); 873690792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 873790792Sgshapiro (void) umask(oldumask); 873890792Sgshapiro RELEASE_QUEUE; 873990792Sgshapiro 874090792Sgshapiro if (fd < 0) 874190792Sgshapiro { 874290792Sgshapiro save_errno = errno; 874390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 874490792Sgshapiro "Skipping %s: Could not open %s: %s\n", 874590792Sgshapiro qid_printname(e), tempqf, 874690792Sgshapiro sm_errstring(save_errno)); 874790792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 874890792Sgshapiro return false; 874990792Sgshapiro } 875090792Sgshapiro if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB)) 875190792Sgshapiro { 875290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 875390792Sgshapiro "Skipping %s: Could not lock %s\n", 875490792Sgshapiro qid_printname(e), tempqf); 875590792Sgshapiro (void) close(fd); 875690792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 875790792Sgshapiro return false; 875890792Sgshapiro } 875990792Sgshapiro 876090792Sgshapiro tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd, 8761120256Sgshapiro SM_IO_WRONLY_B, NULL); 876290792Sgshapiro if (tempqfp == NULL) 876390792Sgshapiro { 876490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 876590792Sgshapiro "Skipping %s: Could not lock %s\n", 876690792Sgshapiro qid_printname(e), tempqf); 876790792Sgshapiro (void) close(fd); 876890792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 876990792Sgshapiro return false; 877090792Sgshapiro } 877190792Sgshapiro 877290792Sgshapiro /* Copy the data over, changing the quarantine reason */ 8773168515Sgshapiro while (bufsize = sizeof(buf), 8774168515Sgshapiro (bp = fgetfolded(buf, &bufsize, oldqfp)) != NULL) 877590792Sgshapiro { 877690792Sgshapiro if (tTd(40, 4)) 877790792Sgshapiro sm_dprintf("+++++ %s\n", bp); 877890792Sgshapiro switch (bp[0]) 877990792Sgshapiro { 878090792Sgshapiro case 'q': /* quarantine reason */ 878190792Sgshapiro foundq = true; 878290792Sgshapiro if (reason == NULL) 878390792Sgshapiro { 878490792Sgshapiro if (Verbose) 878590792Sgshapiro { 878690792Sgshapiro (void) sm_io_fprintf(smioout, 878790792Sgshapiro SM_TIME_DEFAULT, 878890792Sgshapiro "%s: Removed quarantine of \"%s\"\n", 878990792Sgshapiro e->e_id, &bp[1]); 879090792Sgshapiro } 879190792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "unquarantine"); 879290792Sgshapiro dirty = true; 879390792Sgshapiro } 879490792Sgshapiro else if (strcmp(reason, &bp[1]) == 0) 879590792Sgshapiro { 879690792Sgshapiro if (Verbose) 879790792Sgshapiro { 879890792Sgshapiro (void) sm_io_fprintf(smioout, 879990792Sgshapiro SM_TIME_DEFAULT, 880090792Sgshapiro "%s: Already quarantined with \"%s\"\n", 880190792Sgshapiro e->e_id, reason); 880290792Sgshapiro } 880390792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 880490792Sgshapiro "q%s\n", reason); 880590792Sgshapiro } 880690792Sgshapiro else 880790792Sgshapiro { 880890792Sgshapiro if (Verbose) 880990792Sgshapiro { 881090792Sgshapiro (void) sm_io_fprintf(smioout, 881190792Sgshapiro SM_TIME_DEFAULT, 881290792Sgshapiro "%s: Quarantine changed from \"%s\" to \"%s\"\n", 881390792Sgshapiro e->e_id, &bp[1], 881490792Sgshapiro reason); 881590792Sgshapiro } 881690792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 881790792Sgshapiro "q%s\n", reason); 881890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 881990792Sgshapiro reason); 882090792Sgshapiro dirty = true; 882190792Sgshapiro } 882290792Sgshapiro break; 882390792Sgshapiro 882498121Sgshapiro case 'S': 882590792Sgshapiro /* 882690792Sgshapiro ** If we are quarantining an unquarantined item, 882790792Sgshapiro ** need to put in a new 'q' line before it's 882890792Sgshapiro ** too late. 882990792Sgshapiro */ 883090792Sgshapiro 883190792Sgshapiro if (!foundq && reason != NULL) 883290792Sgshapiro { 883390792Sgshapiro if (Verbose) 883490792Sgshapiro { 883590792Sgshapiro (void) sm_io_fprintf(smioout, 883690792Sgshapiro SM_TIME_DEFAULT, 883790792Sgshapiro "%s: Quarantined with \"%s\"\n", 883890792Sgshapiro e->e_id, reason); 883990792Sgshapiro } 884090792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 884190792Sgshapiro "q%s\n", reason); 884290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 884390792Sgshapiro reason); 884490792Sgshapiro foundq = true; 884590792Sgshapiro dirty = true; 884690792Sgshapiro } 884790792Sgshapiro 884890792Sgshapiro /* Copy the line to the new file */ 884990792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 885090792Sgshapiro "%s\n", bp); 885190792Sgshapiro break; 885290792Sgshapiro 885390792Sgshapiro case '.': 885490792Sgshapiro finished = true; 885590792Sgshapiro /* FALLTHROUGH */ 885690792Sgshapiro 885790792Sgshapiro default: 885890792Sgshapiro /* Copy the line to the new file */ 885990792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 886090792Sgshapiro "%s\n", bp); 886190792Sgshapiro break; 886290792Sgshapiro } 8863168515Sgshapiro if (bp != buf) 8864168515Sgshapiro sm_free(bp); 886590792Sgshapiro } 886690792Sgshapiro 886790792Sgshapiro /* Make sure we read the whole old file */ 886890792Sgshapiro errno = sm_io_error(tempqfp); 886990792Sgshapiro if (errno != 0 && errno != SM_IO_EOF) 887090792Sgshapiro { 887190792Sgshapiro save_errno = errno; 887290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 887390792Sgshapiro "Skipping %s: Error reading %s: %s\n", 887490792Sgshapiro qid_printname(e), oldqf, 887590792Sgshapiro sm_errstring(save_errno)); 887690792Sgshapiro failing = true; 887790792Sgshapiro } 887890792Sgshapiro 887990792Sgshapiro if (!failing && !finished) 888090792Sgshapiro { 888190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 888290792Sgshapiro "Skipping %s: Incomplete file: %s\n", 888390792Sgshapiro qid_printname(e), oldqf); 888490792Sgshapiro failing = true; 888590792Sgshapiro } 888690792Sgshapiro 888790792Sgshapiro /* Check if we actually changed anything or we can just bail now */ 888890792Sgshapiro if (!dirty) 888990792Sgshapiro { 889090792Sgshapiro /* pretend we failed, even though we technically didn't */ 889190792Sgshapiro failing = true; 889290792Sgshapiro } 889390792Sgshapiro 889490792Sgshapiro /* Make sure we wrote things out safely */ 889590792Sgshapiro if (!failing && 889690792Sgshapiro (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 || 8897132943Sgshapiro ((SuperSafe == SAFE_REALLY || 8898132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER || 8899132943Sgshapiro SuperSafe == SAFE_INTERACTIVE) && 890090792Sgshapiro fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) || 890190792Sgshapiro ((errno = sm_io_error(tempqfp)) != 0))) 890290792Sgshapiro { 890390792Sgshapiro save_errno = errno; 890490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 890590792Sgshapiro "Skipping %s: Error writing %s: %s\n", 890690792Sgshapiro qid_printname(e), tempqf, 890790792Sgshapiro sm_errstring(save_errno)); 890890792Sgshapiro failing = true; 890990792Sgshapiro } 891090792Sgshapiro 891190792Sgshapiro 891290792Sgshapiro /* Figure out the new filename */ 891390792Sgshapiro newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER); 891490792Sgshapiro if (oldtype == newtype) 891590792Sgshapiro { 891690792Sgshapiro /* going to rename tempqf to oldqf */ 8917168515Sgshapiro (void) sm_strlcpy(newqf, oldqf, sizeof(newqf)); 891890792Sgshapiro } 891990792Sgshapiro else 892090792Sgshapiro { 892190792Sgshapiro /* going to rename tempqf to new name based on newtype */ 8922168515Sgshapiro (void) sm_strlcpy(newqf, queuename(e, newtype), sizeof(newqf)); 892390792Sgshapiro } 892490792Sgshapiro 892590792Sgshapiro save_errno = 0; 892690792Sgshapiro 892790792Sgshapiro /* rename tempqf to newqf */ 892890792Sgshapiro if (!failing && 892990792Sgshapiro rename(tempqf, newqf) < 0) 893090792Sgshapiro save_errno = (errno == 0) ? EINVAL : errno; 893190792Sgshapiro 893290792Sgshapiro /* Check rename() success */ 893390792Sgshapiro if (!failing && save_errno != 0) 893490792Sgshapiro { 893590792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, 893690792Sgshapiro "quarantine_queue_item: rename(%s, %s): %s", 893790792Sgshapiro tempqf, newqf, sm_errstring(save_errno)); 893890792Sgshapiro 893990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 894090792Sgshapiro "Error renaming %s to %s: %s\n", 894190792Sgshapiro tempqf, newqf, 894290792Sgshapiro sm_errstring(save_errno)); 894390792Sgshapiro if (oldtype == newtype) 894490792Sgshapiro { 894590792Sgshapiro /* 894690792Sgshapiro ** Bail here since we don't know the state of 894790792Sgshapiro ** the filesystem and may need to keep tempqf 894890792Sgshapiro ** for the user to rescue us. 894990792Sgshapiro */ 895090792Sgshapiro 895190792Sgshapiro RELEASE_QUEUE; 895290792Sgshapiro errno = save_errno; 895390792Sgshapiro syserr("!452 Error renaming control file %s", tempqf); 895490792Sgshapiro /* NOTREACHED */ 895590792Sgshapiro } 895690792Sgshapiro else 895790792Sgshapiro { 895890792Sgshapiro /* remove new file (if rename() half completed) */ 895990792Sgshapiro if (xunlink(newqf) < 0) 896090792Sgshapiro { 896190792Sgshapiro save_errno = errno; 896290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 896390792Sgshapiro "Error removing %s: %s\n", 896490792Sgshapiro newqf, 896590792Sgshapiro sm_errstring(save_errno)); 896690792Sgshapiro } 896790792Sgshapiro 896890792Sgshapiro /* tempqf removed below */ 896990792Sgshapiro failing = true; 897090792Sgshapiro } 897190792Sgshapiro 897290792Sgshapiro } 897390792Sgshapiro 897490792Sgshapiro /* If changing file types, need to remove old type */ 897590792Sgshapiro if (!failing && oldtype != newtype) 897690792Sgshapiro { 897790792Sgshapiro if (xunlink(oldqf) < 0) 897890792Sgshapiro { 897990792Sgshapiro save_errno = errno; 898090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 898190792Sgshapiro "Error removing %s: %s\n", 898290792Sgshapiro oldqf, sm_errstring(save_errno)); 898390792Sgshapiro } 898490792Sgshapiro } 898590792Sgshapiro 898690792Sgshapiro /* see if anything above failed */ 898790792Sgshapiro if (failing) 898890792Sgshapiro { 898990792Sgshapiro /* Something failed: remove new file, old file still there */ 899090792Sgshapiro (void) xunlink(tempqf); 899190792Sgshapiro } 899290792Sgshapiro 899390792Sgshapiro /* 899490792Sgshapiro ** fsync() after file operations to make sure metadata is 899590792Sgshapiro ** written to disk on filesystems in which renames are 899690792Sgshapiro ** not guaranteed. It's ok if they fail, mail won't be lost. 899790792Sgshapiro */ 899890792Sgshapiro 899990792Sgshapiro if (SuperSafe != SAFE_NO) 900090792Sgshapiro { 900190792Sgshapiro /* for soft-updates */ 900290792Sgshapiro (void) fsync(sm_io_getinfo(tempqfp, 900390792Sgshapiro SM_IO_WHAT_FD, NULL)); 900490792Sgshapiro 900590792Sgshapiro if (!failing) 900690792Sgshapiro { 900790792Sgshapiro /* for soft-updates */ 900890792Sgshapiro (void) fsync(sm_io_getinfo(oldqfp, 900990792Sgshapiro SM_IO_WHAT_FD, NULL)); 901090792Sgshapiro } 901190792Sgshapiro 901290792Sgshapiro /* for other odd filesystems */ 901390792Sgshapiro SYNC_DIR(tempqf, false); 901490792Sgshapiro } 901590792Sgshapiro 901690792Sgshapiro /* Close up shop */ 901790792Sgshapiro RELEASE_QUEUE; 901890792Sgshapiro if (tempqfp != NULL) 901990792Sgshapiro (void) sm_io_close(tempqfp, SM_TIME_DEFAULT); 902090792Sgshapiro if (oldqfp != NULL) 902190792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 902290792Sgshapiro 902390792Sgshapiro /* All went well */ 902490792Sgshapiro return !failing; 902590792Sgshapiro} 902690792Sgshapiro 902790792Sgshapiro/* 902890792Sgshapiro** QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue 902990792Sgshapiro** 903090792Sgshapiro** Read all matching queue items, add/remove quarantine 903190792Sgshapiro** reason, and requeue appropriately. 903290792Sgshapiro** 903390792Sgshapiro** Parameters: 903490792Sgshapiro** reason -- quarantine reason, "." means unquarantine. 903590792Sgshapiro** qgrplimit -- limit to single queue group unless NOQGRP 903690792Sgshapiro** 903790792Sgshapiro** Results: 903890792Sgshapiro** none. 903990792Sgshapiro** 904090792Sgshapiro** Side Effects: 904190792Sgshapiro** Lots of changes to the queue. 904290792Sgshapiro*/ 904390792Sgshapiro 904490792Sgshapirovoid 904590792Sgshapiroquarantine_queue(reason, qgrplimit) 904690792Sgshapiro char *reason; 904790792Sgshapiro int qgrplimit; 904890792Sgshapiro{ 904990792Sgshapiro int changed = 0; 905090792Sgshapiro int qgrp; 905190792Sgshapiro 905290792Sgshapiro /* Convert internal representation of unquarantine */ 905390792Sgshapiro if (reason != NULL && reason[0] == '.' && reason[1] == '\0') 905490792Sgshapiro reason = NULL; 905590792Sgshapiro 905690792Sgshapiro if (reason != NULL) 905790792Sgshapiro { 905890792Sgshapiro /* clean it */ 905990792Sgshapiro reason = newstr(denlstring(reason, true, true)); 906090792Sgshapiro } 906190792Sgshapiro 906290792Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 906390792Sgshapiro { 906490792Sgshapiro int qdir; 906590792Sgshapiro 906690792Sgshapiro if (qgrplimit != NOQGRP && qgrplimit != qgrp) 906790792Sgshapiro continue; 906890792Sgshapiro 906990792Sgshapiro for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++) 907090792Sgshapiro { 907190792Sgshapiro int i; 907290792Sgshapiro int nrequests; 907390792Sgshapiro 907490792Sgshapiro if (StopRequest) 907590792Sgshapiro stop_sendmail(); 907690792Sgshapiro 9077203004Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL, NULL); 907890792Sgshapiro 907990792Sgshapiro /* first see if there is anything */ 908090792Sgshapiro if (nrequests <= 0) 908190792Sgshapiro { 908290792Sgshapiro if (Verbose) 908390792Sgshapiro { 908490792Sgshapiro (void) sm_io_fprintf(smioout, 908590792Sgshapiro SM_TIME_DEFAULT, "%s: no matches\n", 908690792Sgshapiro qid_printqueue(qgrp, qdir)); 908790792Sgshapiro } 908890792Sgshapiro continue; 908990792Sgshapiro } 909090792Sgshapiro 909190792Sgshapiro if (Verbose) 909290792Sgshapiro { 909390792Sgshapiro (void) sm_io_fprintf(smioout, 909490792Sgshapiro SM_TIME_DEFAULT, "Processing %s:\n", 909590792Sgshapiro qid_printqueue(qgrp, qdir)); 909690792Sgshapiro } 909790792Sgshapiro 909890792Sgshapiro for (i = 0; i < WorkListCount; i++) 909990792Sgshapiro { 910090792Sgshapiro ENVELOPE e; 910190792Sgshapiro 910290792Sgshapiro if (StopRequest) 910390792Sgshapiro stop_sendmail(); 910490792Sgshapiro 910590792Sgshapiro /* setup envelope */ 910690792Sgshapiro clearenvelope(&e, true, sm_rpool_new_x(NULL)); 910790792Sgshapiro e.e_id = WorkList[i].w_name + 2; 910890792Sgshapiro e.e_qgrp = qgrp; 910990792Sgshapiro e.e_qdir = qdir; 911090792Sgshapiro 911190792Sgshapiro if (tTd(70, 101)) 911290792Sgshapiro { 911390792Sgshapiro sm_io_fprintf(smioout, SM_TIME_DEFAULT, 911490792Sgshapiro "Would do %s\n", e.e_id); 911590792Sgshapiro changed++; 911690792Sgshapiro } 911790792Sgshapiro else if (quarantine_queue_item(qgrp, qdir, 911890792Sgshapiro &e, reason)) 911990792Sgshapiro changed++; 912090792Sgshapiro 912190792Sgshapiro /* clean up */ 912290792Sgshapiro sm_rpool_free(e.e_rpool); 912390792Sgshapiro e.e_rpool = NULL; 912490792Sgshapiro } 912590792Sgshapiro if (WorkList != NULL) 912690792Sgshapiro sm_free(WorkList); /* XXX */ 912790792Sgshapiro WorkList = NULL; 912890792Sgshapiro WorkListSize = 0; 912990792Sgshapiro WorkListCount = 0; 913090792Sgshapiro } 913190792Sgshapiro } 913290792Sgshapiro if (Verbose) 913390792Sgshapiro { 913490792Sgshapiro if (changed == 0) 913590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 913690792Sgshapiro "No changes\n"); 913790792Sgshapiro else 913890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 913990792Sgshapiro "%d change%s\n", 914090792Sgshapiro changed, 914190792Sgshapiro changed == 1 ? "" : "s"); 914290792Sgshapiro } 914390792Sgshapiro} 9144