queue.c revision 266527
138032Speter/* 2261194Sgshapiro * Copyright (c) 1998-2009, 2011, 2012 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) 29125820Sgshapiro# else /* HASFLOCK && defined(O_EXLOCK) */ 30125820Sgshapiro# define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL) 31125820Sgshapiro# endif /* HASFLOCK && defined(O_EXLOCK) */ 32120256Sgshapiro 33125820Sgshapiro#ifndef SM_OPEN_EXLOCK 34125820Sgshapiro# define SM_OPEN_EXLOCK 0 35125820Sgshapiro#endif /* ! SM_OPEN_EXLOCK */ 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 $"); 12690792Sgshapiro#endif /* SM_HEAP_CHECK */ 12790792Sgshapiro 12890792Sgshapiro/* 12990792Sgshapiro** We use EmptyString instead of "" to avoid 13090792Sgshapiro** 'zero-length format string' warnings from gcc 13190792Sgshapiro*/ 13290792Sgshapiro 13390792Sgshapirostatic const char EmptyString[] = ""; 13490792Sgshapiro 13590792Sgshapirostatic void grow_wlist __P((int, int)); 13690792Sgshapirostatic int multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *)); 137203004Sgshapirostatic int gatherq __P((int, int, bool, bool *, bool *, int *)); 13890792Sgshapirostatic int sortq __P((int)); 13990792Sgshapirostatic void printctladdr __P((ADDRESS *, SM_FILE_T *)); 14090792Sgshapirostatic bool readqf __P((ENVELOPE *, bool)); 14190792Sgshapirostatic void restart_work_group __P((int)); 14290792Sgshapirostatic void runner_work __P((ENVELOPE *, int, bool, int, int)); 14394334Sgshapirostatic void schedule_queue_runs __P((bool, int, bool)); 14464562Sgshapirostatic char *strrev __P((char *)); 14590792Sgshapirostatic ADDRESS *setctluser __P((char *, int, ENVELOPE *)); 14690792Sgshapiro#if _FFR_RHS 14790792Sgshapirostatic int sm_strshufflecmp __P((char *, char *)); 14890792Sgshapirostatic void init_shuffle_alphabet __P(()); 14990792Sgshapiro#endif /* _FFR_RHS */ 150168515Sgshapiro 151168515Sgshapiro/* 152168515Sgshapiro** Note: workcmpf?() don't use a prototype because it will cause a conflict 153168515Sgshapiro** with the qsort() call (which expects something like 154168515Sgshapiro** int (*compar)(const void *, const void *), not (WORK *, WORK *)) 155168515Sgshapiro*/ 156168515Sgshapiro 15764562Sgshapirostatic int workcmpf0(); 15864562Sgshapirostatic int workcmpf1(); 15964562Sgshapirostatic int workcmpf2(); 16064562Sgshapirostatic int workcmpf3(); 16164562Sgshapirostatic int workcmpf4(); 162110560Sgshapirostatic int randi = 3; /* index for workcmpf5() */ 16390792Sgshapirostatic int workcmpf5(); 16490792Sgshapirostatic int workcmpf6(); 16590792Sgshapiro#if _FFR_RHS 16690792Sgshapirostatic int workcmpf7(); 16790792Sgshapiro#endif /* _FFR_RHS */ 16838032Speter 16990792Sgshapiro#if RANDOMSHIFT 17090792Sgshapiro# define get_rand_mod(m) ((get_random() >> RANDOMSHIFT) % (m)) 17190792Sgshapiro#else /* RANDOMSHIFT */ 17290792Sgshapiro# define get_rand_mod(m) (get_random() % (m)) 17390792Sgshapiro#endif /* RANDOMSHIFT */ 17490792Sgshapiro 17580785Sgshapiro/* 17690792Sgshapiro** File system definition. 17790792Sgshapiro** Used to keep track of how much free space is available 17890792Sgshapiro** on a file system in which one or more queue directories reside. 17990792Sgshapiro*/ 18090792Sgshapiro 18190792Sgshapirotypedef struct filesys_shared FILESYS; 18290792Sgshapiro 18390792Sgshapirostruct filesys_shared 18490792Sgshapiro{ 18590792Sgshapiro dev_t fs_dev; /* unique device id */ 18690792Sgshapiro long fs_avail; /* number of free blocks available */ 18790792Sgshapiro long fs_blksize; /* block size, in bytes */ 18890792Sgshapiro}; 18990792Sgshapiro 19090792Sgshapiro/* probably kept in shared memory */ 19190792Sgshapirostatic FILESYS FileSys[MAXFILESYS]; /* queue file systems */ 192168515Sgshapirostatic const char *FSPath[MAXFILESYS]; /* pathnames for file systems */ 19390792Sgshapiro 19490792Sgshapiro#if SM_CONF_SHM 19590792Sgshapiro 19690792Sgshapiro/* 19790792Sgshapiro** Shared memory data 19890792Sgshapiro** 19990792Sgshapiro** Current layout: 20090792Sgshapiro** size -- size of shared memory segment 20190792Sgshapiro** pid -- pid of owner, should be a unique id to avoid misinterpretations 20290792Sgshapiro** by other processes. 20390792Sgshapiro** tag -- should be a unique id to avoid misinterpretations by others. 20490792Sgshapiro** idea: hash over configuration data that will be stored here. 20590792Sgshapiro** NumFileSys -- number of file systems. 206223067Sgshapiro** FileSys -- (array of) structure for used file systems. 20790792Sgshapiro** RSATmpCnt -- counter for number of uses of ephemeral RSA key. 20890792Sgshapiro** QShm -- (array of) structure for information about queue directories. 20990792Sgshapiro*/ 21090792Sgshapiro 21190792Sgshapiro/* 21290792Sgshapiro** Queue data in shared memory 21390792Sgshapiro*/ 21490792Sgshapiro 21590792Sgshapirotypedef struct queue_shared QUEUE_SHM_T; 21690792Sgshapiro 21790792Sgshapirostruct queue_shared 21890792Sgshapiro{ 21990792Sgshapiro int qs_entries; /* number of entries */ 22090792Sgshapiro /* XXX more to follow? */ 22190792Sgshapiro}; 22290792Sgshapiro 22390792Sgshapirostatic void *Pshm; /* pointer to shared memory */ 22490792Sgshapirostatic FILESYS *PtrFileSys; /* pointer to queue file system array */ 22590792Sgshapiroint ShmId = SM_SHM_NO_ID; /* shared memory id */ 22690792Sgshapirostatic QUEUE_SHM_T *QShm; /* pointer to shared queue data */ 227110560Sgshapirostatic size_t shms; 22890792Sgshapiro 22990792Sgshapiro# define SHM_OFF_PID(p) (((char *) (p)) + sizeof(int)) 23090792Sgshapiro# define SHM_OFF_TAG(p) (((char *) (p)) + sizeof(pid_t) + sizeof(int)) 23190792Sgshapiro# define SHM_OFF_HEAD (sizeof(pid_t) + sizeof(int) * 2) 23290792Sgshapiro 23390792Sgshapiro/* how to access FileSys */ 23490792Sgshapiro# define FILE_SYS(i) (PtrFileSys[i]) 23590792Sgshapiro 23690792Sgshapiro/* first entry is a tag, for now just the size */ 23790792Sgshapiro# define OFF_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD) 23890792Sgshapiro 23990792Sgshapiro/* offset for PNumFileSys */ 24090792Sgshapiro# define OFF_NUM_FILE_SYS(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys)) 24190792Sgshapiro 24290792Sgshapiro/* offset for PRSATmpCnt */ 24390792Sgshapiro# define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int)) 24490792Sgshapiroint *PRSATmpCnt; 24590792Sgshapiro 24690792Sgshapiro/* offset for queue_shm */ 24790792Sgshapiro# define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2) 24890792Sgshapiro 249112810Sgshapiro# define QSHM_ENTRIES(i) QShm[i].qs_entries 25090792Sgshapiro 25190792Sgshapiro/* basic size of shared memory segment */ 25290792Sgshapiro# define SM_T_SIZE (SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2) 25390792Sgshapiro 25490792Sgshapirostatic unsigned int hash_q __P((char *, unsigned int)); 25590792Sgshapiro 25690792Sgshapiro/* 25790792Sgshapiro** HASH_Q -- simple hash function 25890792Sgshapiro** 25990792Sgshapiro** Parameters: 26090792Sgshapiro** p -- string to hash. 26190792Sgshapiro** h -- hash start value (from previous run). 26290792Sgshapiro** 26390792Sgshapiro** Returns: 26490792Sgshapiro** hash value. 26590792Sgshapiro*/ 26690792Sgshapiro 26790792Sgshapirostatic unsigned int 26890792Sgshapirohash_q(p, h) 26990792Sgshapiro char *p; 27090792Sgshapiro unsigned int h; 27190792Sgshapiro{ 27290792Sgshapiro int c, d; 27390792Sgshapiro 27490792Sgshapiro while (*p != '\0') 27590792Sgshapiro { 27690792Sgshapiro d = *p++; 27790792Sgshapiro c = d; 27890792Sgshapiro c ^= c<<6; 27990792Sgshapiro h += (c<<11) ^ (c>>1); 28090792Sgshapiro h ^= (d<<14) + (d<<7) + (d<<4) + d; 28190792Sgshapiro } 28290792Sgshapiro return h; 28390792Sgshapiro} 28490792Sgshapiro 285110560Sgshapiro 28690792Sgshapiro#else /* SM_CONF_SHM */ 28790792Sgshapiro# define FILE_SYS(i) FileSys[i] 28890792Sgshapiro#endif /* SM_CONF_SHM */ 28990792Sgshapiro 29090792Sgshapiro/* access to the various components of file system data */ 29190792Sgshapiro#define FILE_SYS_NAME(i) FSPath[i] 29290792Sgshapiro#define FILE_SYS_AVAIL(i) FILE_SYS(i).fs_avail 29390792Sgshapiro#define FILE_SYS_BLKSIZE(i) FILE_SYS(i).fs_blksize 29490792Sgshapiro#define FILE_SYS_DEV(i) FILE_SYS(i).fs_dev 29590792Sgshapiro 296110560Sgshapiro 29790792Sgshapiro/* 29880785Sgshapiro** Current qf file field assignments: 29980785Sgshapiro** 30080785Sgshapiro** A AUTH= parameter 30180785Sgshapiro** B body type 30280785Sgshapiro** C controlling user 30380785Sgshapiro** D data file name 30490792Sgshapiro** d data file directory name (added in 8.12) 30580785Sgshapiro** E error recipient 30680785Sgshapiro** F flag bits 307132943Sgshapiro** G free (was: queue delay algorithm if _FFR_QUEUEDELAY) 30880785Sgshapiro** H header 30980785Sgshapiro** I data file's inode number 31080785Sgshapiro** K time of last delivery attempt 31180785Sgshapiro** L Solaris Content-Length: header (obsolete) 31298841Sgshapiro** M message 31380785Sgshapiro** N number of delivery attempts 31480785Sgshapiro** P message priority 315132943Sgshapiro** q quarantine reason 31680785Sgshapiro** Q original recipient (ORCPT=) 31790792Sgshapiro** r final recipient (Final-Recipient: DSN field) 31880785Sgshapiro** R recipient 31980785Sgshapiro** S sender 32080785Sgshapiro** T init time 32180785Sgshapiro** V queue file version 32290792Sgshapiro** X free (was: character set if _FFR_SAVE_CHARSET) 323132943Sgshapiro** Y free (was: current delay if _FFR_QUEUEDELAY) 32480785Sgshapiro** Z original envelope id from ESMTP 32590792Sgshapiro** ! deliver by (added in 8.12) 32680785Sgshapiro** $ define macro 32780785Sgshapiro** . terminate file 32880785Sgshapiro*/ 32980785Sgshapiro 33090792Sgshapiro/* 33138032Speter** QUEUEUP -- queue a message up for future transmission. 33238032Speter** 33338032Speter** Parameters: 33438032Speter** e -- the envelope to queue up. 33590792Sgshapiro** announce -- if true, tell when you are queueing up. 33690792Sgshapiro** msync -- if true, then fsync() if SuperSafe interactive mode. 33738032Speter** 33838032Speter** Returns: 33938032Speter** none. 34038032Speter** 34138032Speter** Side Effects: 34290792Sgshapiro** The current request is saved in a control file. 34338032Speter** The queue file is left locked. 34438032Speter*/ 34538032Speter 34638032Spetervoid 34790792Sgshapiroqueueup(e, announce, msync) 34838032Speter register ENVELOPE *e; 34938032Speter bool announce; 35090792Sgshapiro bool msync; 35138032Speter{ 35290792Sgshapiro register SM_FILE_T *tfp; 35338032Speter register HDR *h; 35438032Speter register ADDRESS *q; 35564562Sgshapiro int tfd = -1; 35638032Speter int i; 35738032Speter bool newid; 35838032Speter register char *p; 35938032Speter MAILER nullmailer; 36038032Speter MCI mcibuf; 36190792Sgshapiro char qf[MAXPATHLEN]; 36264562Sgshapiro char tf[MAXPATHLEN]; 36390792Sgshapiro char df[MAXPATHLEN]; 36438032Speter char buf[MAXLINE]; 36538032Speter 36638032Speter /* 36738032Speter ** Create control file. 36838032Speter */ 36938032Speter 370125820Sgshapiro#define OPEN_TF do \ 371125820Sgshapiro { \ 372125820Sgshapiro MODE_T oldumask = 0; \ 373125820Sgshapiro \ 374125820Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) \ 375125820Sgshapiro oldumask = umask(002); \ 376125820Sgshapiro tfd = open(tf, TF_OPEN_FLAGS, QueueFileMode); \ 377125820Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) \ 378125820Sgshapiro (void) umask(oldumask); \ 379125820Sgshapiro } while (0) 380125820Sgshapiro 381125820Sgshapiro 38238032Speter newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags); 383168515Sgshapiro (void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof(tf)); 38438032Speter tfp = e->e_lockfp; 385120256Sgshapiro if (tfp == NULL && newid) 386120256Sgshapiro { 387120256Sgshapiro /* 388120256Sgshapiro ** open qf file directly: this will give an error if the file 389120256Sgshapiro ** already exists and hence prevent problems if a queue-id 390120256Sgshapiro ** is reused (e.g., because the clock is set back). 391120256Sgshapiro */ 39238032Speter 393168515Sgshapiro (void) sm_strlcpy(tf, queuename(e, ANYQFL_LETTER), sizeof(tf)); 394125820Sgshapiro OPEN_TF; 395120256Sgshapiro if (tfd < 0 || 396125820Sgshapiro#if !SM_OPEN_EXLOCK 397120256Sgshapiro !lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB) || 398125820Sgshapiro#endif /* !SM_OPEN_EXLOCK */ 399120256Sgshapiro (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 400132943Sgshapiro (void *) &tfd, SM_IO_WRONLY, 401120256Sgshapiro NULL)) == NULL) 402120256Sgshapiro { 403120256Sgshapiro int save_errno = errno; 404120256Sgshapiro 405120256Sgshapiro printopenfds(true); 406120256Sgshapiro errno = save_errno; 407132943Sgshapiro syserr("!queueup: cannot create queue file %s, euid=%d, fd=%d, fp=%p", 408132943Sgshapiro tf, (int) geteuid(), tfd, tfp); 409120256Sgshapiro /* NOTREACHED */ 410120256Sgshapiro } 411120256Sgshapiro e->e_lockfp = tfp; 412147078Sgshapiro upd_qs(e, 1, 0, "queueup"); 413120256Sgshapiro } 414120256Sgshapiro 41590792Sgshapiro /* if newid, write the queue file directly (instead of temp file) */ 41638032Speter if (!newid) 41738032Speter { 41838032Speter /* get a locked tf file */ 41938032Speter for (i = 0; i < 128; i++) 42038032Speter { 42164562Sgshapiro if (tfd < 0) 42238032Speter { 423125820Sgshapiro OPEN_TF; 42464562Sgshapiro if (tfd < 0) 42564562Sgshapiro { 42664562Sgshapiro if (errno != EEXIST) 42764562Sgshapiro break; 42864562Sgshapiro if (LogLevel > 0 && (i % 32) == 0) 42964562Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 430173340Sgshapiro "queueup: cannot create %s, euid=%d: %s", 43198121Sgshapiro tf, (int) geteuid(), 43290792Sgshapiro sm_errstring(errno)); 43364562Sgshapiro } 434125820Sgshapiro#if SM_OPEN_EXLOCK 435125820Sgshapiro else 436125820Sgshapiro break; 437125820Sgshapiro#endif /* SM_OPEN_EXLOCK */ 43838032Speter } 43964562Sgshapiro if (tfd >= 0) 44038032Speter { 441125820Sgshapiro#if SM_OPEN_EXLOCK 442125820Sgshapiro /* file is locked by open() */ 443125820Sgshapiro break; 444125820Sgshapiro#else /* SM_OPEN_EXLOCK */ 44564562Sgshapiro if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB)) 44638032Speter break; 447125820Sgshapiro else 448125820Sgshapiro#endif /* SM_OPEN_EXLOCK */ 449125820Sgshapiro if (LogLevel > 0 && (i % 32) == 0) 45038032Speter sm_syslog(LOG_ALERT, e->e_id, 45164562Sgshapiro "queueup: cannot lock %s: %s", 45290792Sgshapiro tf, sm_errstring(errno)); 45364562Sgshapiro if ((i % 32) == 31) 45464562Sgshapiro { 45564562Sgshapiro (void) close(tfd); 45664562Sgshapiro tfd = -1; 45764562Sgshapiro } 45838032Speter } 45938032Speter 46038032Speter if ((i % 32) == 31) 46138032Speter { 46238032Speter /* save the old temp file away */ 46364562Sgshapiro (void) rename(tf, queuename(e, TEMPQF_LETTER)); 46438032Speter } 46538032Speter else 46664562Sgshapiro (void) sleep(i % 32); 46738032Speter } 46890792Sgshapiro if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 469120256Sgshapiro (void *) &tfd, SM_IO_WRONLY_B, 47090792Sgshapiro NULL)) == NULL) 47138032Speter { 47264562Sgshapiro int save_errno = errno; 47364562Sgshapiro 47490792Sgshapiro printopenfds(true); 47564562Sgshapiro errno = save_errno; 47638032Speter syserr("!queueup: cannot create queue temp file %s, uid=%d", 47798121Sgshapiro tf, (int) geteuid()); 47838032Speter } 47938032Speter } 48038032Speter 48138032Speter if (tTd(40, 1)) 48290792Sgshapiro sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n", 48390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir), 48490792Sgshapiro queuename(e, ANYQFL_LETTER), 48590792Sgshapiro newid ? " (new id)" : ""); 48638032Speter if (tTd(40, 3)) 48738032Speter { 48890792Sgshapiro sm_dprintf(" e_flags="); 48938032Speter printenvflags(e); 49038032Speter } 49138032Speter if (tTd(40, 32)) 49238032Speter { 49390792Sgshapiro sm_dprintf(" sendq="); 494132943Sgshapiro printaddr(sm_debug_file(), e->e_sendqueue, true); 49538032Speter } 49638032Speter if (tTd(40, 9)) 49738032Speter { 49890792Sgshapiro sm_dprintf(" tfp="); 49990792Sgshapiro dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false); 50090792Sgshapiro sm_dprintf(" lockfp="); 50138032Speter if (e->e_lockfp == NULL) 50290792Sgshapiro sm_dprintf("NULL\n"); 50338032Speter else 50490792Sgshapiro dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL), 50590792Sgshapiro true, false); 50638032Speter } 50738032Speter 50838032Speter /* 50938032Speter ** If there is no data file yet, create one. 51038032Speter */ 51138032Speter 512168515Sgshapiro (void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof(df)); 51364562Sgshapiro if (bitset(EF_HAS_DF, e->e_flags)) 51438032Speter { 51590792Sgshapiro if (e->e_dfp != NULL && 51690792Sgshapiro SuperSafe != SAFE_REALLY && 517132943Sgshapiro SuperSafe != SAFE_REALLY_POSTMILTER && 51890792Sgshapiro sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 && 51990792Sgshapiro errno != EINVAL) 52090792Sgshapiro { 52164562Sgshapiro syserr("!queueup: cannot commit data file %s, uid=%d", 52298121Sgshapiro queuename(e, DATAFL_LETTER), (int) geteuid()); 52390792Sgshapiro } 52490792Sgshapiro if (e->e_dfp != NULL && 52590792Sgshapiro SuperSafe == SAFE_INTERACTIVE && msync) 52690792Sgshapiro { 52790792Sgshapiro if (tTd(40,32)) 52890792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 52990792Sgshapiro "queueup: fsync(e->e_dfp)"); 53090792Sgshapiro 53190792Sgshapiro if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, 53290792Sgshapiro NULL)) < 0) 53390792Sgshapiro { 53490792Sgshapiro if (newid) 53590792Sgshapiro syserr("!552 Error writing data file %s", 53690792Sgshapiro df); 53790792Sgshapiro else 53890792Sgshapiro syserr("!452 Error writing data file %s", 53990792Sgshapiro df); 54090792Sgshapiro } 54190792Sgshapiro } 54264562Sgshapiro } 54364562Sgshapiro else 54464562Sgshapiro { 54564562Sgshapiro int dfd; 54690792Sgshapiro MODE_T oldumask = 0; 54790792Sgshapiro register SM_FILE_T *dfp = NULL; 54838032Speter struct stat stbuf; 54938032Speter 55090792Sgshapiro if (e->e_dfp != NULL && 55190792Sgshapiro sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE)) 55264562Sgshapiro syserr("committing over bf file"); 55364562Sgshapiro 55490792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 55590792Sgshapiro oldumask = umask(002); 556120256Sgshapiro dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC|QF_O_EXTRA, 557120256Sgshapiro QueueFileMode); 55890792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 55990792Sgshapiro (void) umask(oldumask); 56090792Sgshapiro if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, 561120256Sgshapiro (void *) &dfd, SM_IO_WRONLY_B, 56290792Sgshapiro NULL)) == NULL) 56338032Speter syserr("!queueup: cannot create data temp file %s, uid=%d", 56498121Sgshapiro df, (int) geteuid()); 56564562Sgshapiro if (fstat(dfd, &stbuf) < 0) 56638032Speter e->e_dfino = -1; 56738032Speter else 56838032Speter { 56938032Speter e->e_dfdev = stbuf.st_dev; 57090792Sgshapiro e->e_dfino = ST_INODE(stbuf); 57138032Speter } 57238032Speter e->e_flags |= EF_HAS_DF; 573168515Sgshapiro memset(&mcibuf, '\0', sizeof(mcibuf)); 57438032Speter mcibuf.mci_out = dfp; 57538032Speter mcibuf.mci_mailer = FileMailer; 57638032Speter (*e->e_putbody)(&mcibuf, e, NULL); 57790792Sgshapiro 57890792Sgshapiro if (SuperSafe == SAFE_REALLY || 579132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER || 58090792Sgshapiro (SuperSafe == SAFE_INTERACTIVE && msync)) 58190792Sgshapiro { 58290792Sgshapiro if (tTd(40,32)) 58390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, 58490792Sgshapiro "queueup: fsync(dfp)"); 58590792Sgshapiro 58690792Sgshapiro if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0) 58790792Sgshapiro { 58890792Sgshapiro if (newid) 58990792Sgshapiro syserr("!552 Error writing data file %s", 59090792Sgshapiro df); 59190792Sgshapiro else 59290792Sgshapiro syserr("!452 Error writing data file %s", 59390792Sgshapiro df); 59490792Sgshapiro } 59590792Sgshapiro } 59690792Sgshapiro 59790792Sgshapiro if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0) 59864562Sgshapiro syserr("!queueup: cannot save data temp file %s, uid=%d", 59998121Sgshapiro df, (int) geteuid()); 60038032Speter e->e_putbody = putbody; 60138032Speter } 60238032Speter 60338032Speter /* 60438032Speter ** Output future work requests. 60538032Speter ** Priority and creation time should be first, since 60690792Sgshapiro ** they are required by gatherq. 60738032Speter */ 60838032Speter 60938032Speter /* output queue version number (must be first!) */ 61090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION); 61138032Speter 61238032Speter /* output creation time */ 61390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime); 61438032Speter 61538032Speter /* output last delivery time */ 61690792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime); 61738032Speter 61838032Speter /* output number of delivery attempts */ 61990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries); 62038032Speter 62138032Speter /* output message priority */ 62290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority); 62338032Speter 62490792Sgshapiro /* 62590792Sgshapiro ** If data file is in a different directory than the queue file, 62690792Sgshapiro ** output a "d" record naming the directory of the data file. 62790792Sgshapiro */ 62890792Sgshapiro 62990792Sgshapiro if (e->e_dfqgrp != e->e_qgrp) 63090792Sgshapiro { 63190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n", 63290792Sgshapiro Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name); 63390792Sgshapiro } 63490792Sgshapiro 63538032Speter /* output inode number of data file */ 63638032Speter if (e->e_dfino != -1) 63738032Speter { 63890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n", 63990792Sgshapiro (long) major(e->e_dfdev), 64090792Sgshapiro (long) minor(e->e_dfdev), 64190792Sgshapiro (ULONGLONG_T) e->e_dfino); 64238032Speter } 64338032Speter 64438032Speter /* output body type */ 64538032Speter if (e->e_bodytype != NULL) 64690792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n", 64790792Sgshapiro denlstring(e->e_bodytype, true, false)); 64838032Speter 64990792Sgshapiro /* quarantine reason */ 65090792Sgshapiro if (e->e_quarmsg != NULL) 65190792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n", 65290792Sgshapiro denlstring(e->e_quarmsg, true, false)); 65338032Speter 65438032Speter /* message from envelope, if it exists */ 65538032Speter if (e->e_message != NULL) 65690792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", 65790792Sgshapiro denlstring(e->e_message, true, false)); 65838032Speter 65938032Speter /* send various flag bits through */ 66038032Speter p = buf; 66138032Speter if (bitset(EF_WARNING, e->e_flags)) 66238032Speter *p++ = 'w'; 66338032Speter if (bitset(EF_RESPONSE, e->e_flags)) 66438032Speter *p++ = 'r'; 66538032Speter if (bitset(EF_HAS8BIT, e->e_flags)) 66638032Speter *p++ = '8'; 66738032Speter if (bitset(EF_DELETE_BCC, e->e_flags)) 66838032Speter *p++ = 'b'; 66938032Speter if (bitset(EF_RET_PARAM, e->e_flags)) 67038032Speter *p++ = 'd'; 67138032Speter if (bitset(EF_NO_BODY_RETN, e->e_flags)) 67238032Speter *p++ = 'n'; 67390792Sgshapiro if (bitset(EF_SPLIT, e->e_flags)) 67490792Sgshapiro *p++ = 's'; 67538032Speter *p++ = '\0'; 67638032Speter if (buf[0] != '\0') 67790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf); 67838032Speter 67964562Sgshapiro /* save $={persistentMacros} macro values */ 68090792Sgshapiro queueup_macros(macid("{persistentMacros}"), tfp, e); 68138032Speter 68238032Speter /* output name of sender */ 68338032Speter if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags)) 68438032Speter p = e->e_sender; 68538032Speter else 68638032Speter p = e->e_from.q_paddr; 68790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n", 68890792Sgshapiro denlstring(p, true, false)); 68938032Speter 69038032Speter /* output ESMTP-supplied "original" information */ 69138032Speter if (e->e_envid != NULL) 69290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n", 69390792Sgshapiro denlstring(e->e_envid, true, false)); 69438032Speter 69564562Sgshapiro /* output AUTH= parameter */ 69664562Sgshapiro if (e->e_auth_param != NULL) 69790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n", 69890792Sgshapiro denlstring(e->e_auth_param, true, false)); 69990792Sgshapiro if (e->e_dlvr_flag != 0) 70090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n", 70190792Sgshapiro (char) e->e_dlvr_flag, e->e_deliver_by); 70264562Sgshapiro 70338032Speter /* output list of recipient addresses */ 70438032Speter printctladdr(NULL, NULL); 70538032Speter for (q = e->e_sendqueue; q != NULL; q = q->q_next) 70638032Speter { 70764562Sgshapiro if (!QS_IS_UNDELIVERED(q->q_state)) 70838032Speter continue; 70964562Sgshapiro 71090792Sgshapiro /* message for this recipient, if it exists */ 71190792Sgshapiro if (q->q_message != NULL) 71290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n", 71390792Sgshapiro denlstring(q->q_message, true, 71490792Sgshapiro false)); 71590792Sgshapiro 71638032Speter printctladdr(q, tfp); 71738032Speter if (q->q_orcpt != NULL) 71890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n", 71990792Sgshapiro denlstring(q->q_orcpt, true, 72090792Sgshapiro false)); 72190792Sgshapiro if (q->q_finalrcpt != NULL) 72290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n", 72390792Sgshapiro denlstring(q->q_finalrcpt, true, 72490792Sgshapiro false)); 72590792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R'); 72638032Speter if (bitset(QPRIMARY, q->q_flags)) 72790792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P'); 72838032Speter if (bitset(QHASNOTIFY, q->q_flags)) 72990792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N'); 73038032Speter if (bitset(QPINGONSUCCESS, q->q_flags)) 73190792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S'); 73238032Speter if (bitset(QPINGONFAILURE, q->q_flags)) 73390792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F'); 73438032Speter if (bitset(QPINGONDELAY, q->q_flags)) 73590792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D'); 73671345Sgshapiro if (q->q_alias != NULL && 73771345Sgshapiro bitset(QALIAS, q->q_alias->q_flags)) 73890792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A'); 73990792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':'); 74090792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n", 74190792Sgshapiro denlstring(q->q_paddr, true, false)); 74238032Speter if (announce) 74338032Speter { 74490792Sgshapiro char *tag = "queued"; 74590792Sgshapiro 74690792Sgshapiro if (e->e_quarmsg != NULL) 74790792Sgshapiro tag = "quarantined"; 74890792Sgshapiro 74938032Speter e->e_to = q->q_paddr; 75090792Sgshapiro message(tag); 75138032Speter if (LogLevel > 8) 75264562Sgshapiro logdelivery(q->q_mailer, NULL, q->q_status, 75390792Sgshapiro tag, NULL, (time_t) 0, e); 75438032Speter e->e_to = NULL; 75538032Speter } 75638032Speter if (tTd(40, 1)) 75738032Speter { 75890792Sgshapiro sm_dprintf("queueing "); 759132943Sgshapiro printaddr(sm_debug_file(), q, false); 76038032Speter } 76138032Speter } 76238032Speter 76338032Speter /* 76438032Speter ** Output headers for this message. 76538032Speter ** Expand macros completely here. Queue run will deal with 76638032Speter ** everything as absolute headers. 76738032Speter ** All headers that must be relative to the recipient 76838032Speter ** can be cracked later. 76938032Speter ** We set up a "null mailer" -- i.e., a mailer that will have 77038032Speter ** no effect on the addresses as they are output. 77138032Speter */ 77238032Speter 773168515Sgshapiro memset((char *) &nullmailer, '\0', sizeof(nullmailer)); 77438032Speter nullmailer.m_re_rwset = nullmailer.m_rh_rwset = 77538032Speter nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1; 77638032Speter nullmailer.m_eol = "\n"; 777168515Sgshapiro memset(&mcibuf, '\0', sizeof(mcibuf)); 77838032Speter mcibuf.mci_mailer = &nullmailer; 77938032Speter mcibuf.mci_out = tfp; 78038032Speter 78190792Sgshapiro macdefine(&e->e_macro, A_PERM, 'g', "\201f"); 78238032Speter for (h = e->e_header; h != NULL; h = h->h_link) 78338032Speter { 78443730Speter if (h->h_value == NULL) 78538032Speter continue; 78638032Speter 78738032Speter /* don't output resent headers on non-resent messages */ 78864562Sgshapiro if (bitset(H_RESENT, h->h_flags) && 78964562Sgshapiro !bitset(EF_RESENT, e->e_flags)) 79038032Speter continue; 79138032Speter 79238032Speter /* expand macros; if null, don't output header at all */ 79338032Speter if (bitset(H_DEFAULT, h->h_flags)) 79438032Speter { 795168515Sgshapiro (void) expand(h->h_value, buf, sizeof(buf), e); 79638032Speter if (buf[0] == '\0') 79738032Speter continue; 798168515Sgshapiro if (buf[0] == ' ' && buf[1] == '\0') 799168515Sgshapiro continue; 80038032Speter } 80138032Speter 80238032Speter /* output this header */ 80390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?"); 80438032Speter 80564562Sgshapiro /* output conditional macro if present */ 80664562Sgshapiro if (h->h_macro != '\0') 80738032Speter { 80864562Sgshapiro if (bitset(0200, h->h_macro)) 80990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, 81090792Sgshapiro "${%s}", 81190792Sgshapiro macname(bitidx(h->h_macro))); 81264562Sgshapiro else 81390792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, 81490792Sgshapiro "$%c", h->h_macro); 81564562Sgshapiro } 81664562Sgshapiro else if (!bitzerop(h->h_mflags) && 81764562Sgshapiro bitset(H_CHECK|H_ACHECK, h->h_flags)) 81864562Sgshapiro { 81938032Speter int j; 82038032Speter 82164562Sgshapiro /* if conditional, output the set of conditions */ 82238032Speter for (j = '\0'; j <= '\177'; j++) 82338032Speter if (bitnset(j, h->h_mflags)) 82490792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, 82590792Sgshapiro j); 82638032Speter } 82790792Sgshapiro (void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?'); 82838032Speter 82938032Speter /* output the header: expand macros, convert addresses */ 83064562Sgshapiro if (bitset(H_DEFAULT, h->h_flags) && 83164562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 83238032Speter { 833168515Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n", 83490792Sgshapiro h->h_field, 83590792Sgshapiro denlstring(buf, false, true)); 83638032Speter } 83764562Sgshapiro else if (bitset(H_FROM|H_RCPT, h->h_flags) && 83864562Sgshapiro !bitset(H_BINDLATE, h->h_flags)) 83938032Speter { 84038032Speter bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags); 84190792Sgshapiro SM_FILE_T *savetrace = TrafficLogFile; 84238032Speter 84338032Speter TrafficLogFile = NULL; 84438032Speter 84538032Speter if (bitset(H_FROM, h->h_flags)) 84690792Sgshapiro oldstyle = false; 847173340Sgshapiro commaize(h, h->h_value, oldstyle, &mcibuf, e, 848173340Sgshapiro PXLF_HEADER); 84938032Speter 85038032Speter TrafficLogFile = savetrace; 85138032Speter } 85238032Speter else 85338032Speter { 854168515Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n", 85590792Sgshapiro h->h_field, 85690792Sgshapiro denlstring(h->h_value, false, 85790792Sgshapiro true)); 85838032Speter } 85938032Speter } 86038032Speter 86138032Speter /* 86238032Speter ** Clean up. 86338032Speter ** 86438032Speter ** Write a terminator record -- this is to prevent 86538032Speter ** scurrilous crackers from appending any data. 86638032Speter */ 86738032Speter 86890792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n"); 86938032Speter 87090792Sgshapiro if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 || 87190792Sgshapiro ((SuperSafe == SAFE_REALLY || 872132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER || 87390792Sgshapiro (SuperSafe == SAFE_INTERACTIVE && msync)) && 87490792Sgshapiro fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) || 87590792Sgshapiro sm_io_error(tfp)) 87638032Speter { 87738032Speter if (newid) 87838032Speter syserr("!552 Error writing control file %s", tf); 87938032Speter else 88038032Speter syserr("!452 Error writing control file %s", tf); 88138032Speter } 88238032Speter 88338032Speter if (!newid) 88438032Speter { 88590792Sgshapiro char new = queue_letter(e, ANYQFL_LETTER); 88690792Sgshapiro 88790792Sgshapiro /* rename (locked) tf to be (locked) [qh]f */ 88890792Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), 889168515Sgshapiro sizeof(qf)); 89038032Speter if (rename(tf, qf) < 0) 89138032Speter syserr("cannot rename(%s, %s), uid=%d", 89298121Sgshapiro tf, qf, (int) geteuid()); 89390792Sgshapiro else 89490792Sgshapiro { 89590792Sgshapiro /* 89690792Sgshapiro ** Check if type has changed and only 89790792Sgshapiro ** remove the old item if the rename above 89890792Sgshapiro ** succeeded. 89990792Sgshapiro */ 90090792Sgshapiro 90190792Sgshapiro if (e->e_qfletter != '\0' && 90290792Sgshapiro e->e_qfletter != new) 90390792Sgshapiro { 90490792Sgshapiro if (tTd(40, 5)) 90590792Sgshapiro { 90690792Sgshapiro sm_dprintf("type changed from %c to %c\n", 90790792Sgshapiro e->e_qfletter, new); 90890792Sgshapiro } 90990792Sgshapiro 91090792Sgshapiro if (unlink(queuename(e, e->e_qfletter)) < 0) 91190792Sgshapiro { 91290792Sgshapiro /* XXX: something more drastic? */ 91390792Sgshapiro if (LogLevel > 0) 91490792Sgshapiro sm_syslog(LOG_ERR, e->e_id, 91590792Sgshapiro "queueup: unlink(%s) failed: %s", 91690792Sgshapiro queuename(e, e->e_qfletter), 91790792Sgshapiro sm_errstring(errno)); 91890792Sgshapiro } 91990792Sgshapiro } 92090792Sgshapiro } 92190792Sgshapiro e->e_qfletter = new; 92290792Sgshapiro 92364562Sgshapiro /* 92490792Sgshapiro ** fsync() after renaming to make sure metadata is 92590792Sgshapiro ** written to disk on filesystems in which renames are 92690792Sgshapiro ** not guaranteed. 92764562Sgshapiro */ 92864562Sgshapiro 92990792Sgshapiro if (SuperSafe != SAFE_NO) 93090792Sgshapiro { 93190792Sgshapiro /* for softupdates */ 93290792Sgshapiro if (tfd >= 0 && fsync(tfd) < 0) 93390792Sgshapiro { 93490792Sgshapiro syserr("!queueup: cannot fsync queue temp file %s", 93590792Sgshapiro tf); 93690792Sgshapiro } 93790792Sgshapiro SYNC_DIR(qf, true); 93890792Sgshapiro } 93964562Sgshapiro 94090792Sgshapiro /* close and unlock old (locked) queue file */ 94138032Speter if (e->e_lockfp != NULL) 94290792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 94338032Speter e->e_lockfp = tfp; 94490792Sgshapiro 94590792Sgshapiro /* save log info */ 94690792Sgshapiro if (LogLevel > 79) 94790792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf); 94838032Speter } 94938032Speter else 95090792Sgshapiro { 95190792Sgshapiro /* save log info */ 95290792Sgshapiro if (LogLevel > 79) 95390792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf); 95490792Sgshapiro 95590792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 95690792Sgshapiro } 95790792Sgshapiro 95838032Speter errno = 0; 95938032Speter e->e_flags |= EF_INQUEUE; 96038032Speter 96138032Speter if (tTd(40, 1)) 96290792Sgshapiro sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id); 96338032Speter return; 96438032Speter} 96538032Speter 96690792Sgshapiro/* 96790792Sgshapiro** PRINTCTLADDR -- print control address to file. 96890792Sgshapiro** 96990792Sgshapiro** Parameters: 97090792Sgshapiro** a -- address. 97190792Sgshapiro** tfp -- file pointer. 97290792Sgshapiro** 97390792Sgshapiro** Returns: 97490792Sgshapiro** none. 97590792Sgshapiro** 97690792Sgshapiro** Side Effects: 97790792Sgshapiro** The control address (if changed) is printed to the file. 97890792Sgshapiro** The last control address and uid are saved. 97990792Sgshapiro*/ 98090792Sgshapiro 98164562Sgshapirostatic void 98238032Speterprintctladdr(a, tfp) 98338032Speter register ADDRESS *a; 98490792Sgshapiro SM_FILE_T *tfp; 98538032Speter{ 98664562Sgshapiro char *user; 98738032Speter register ADDRESS *q; 98838032Speter uid_t uid; 98938032Speter gid_t gid; 99038032Speter static ADDRESS *lastctladdr = NULL; 99138032Speter static uid_t lastuid; 99238032Speter 99338032Speter /* initialization */ 99438032Speter if (a == NULL || a->q_alias == NULL || tfp == NULL) 99538032Speter { 99638032Speter if (lastctladdr != NULL && tfp != NULL) 99790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n"); 99838032Speter lastctladdr = NULL; 99938032Speter lastuid = 0; 100038032Speter return; 100138032Speter } 100238032Speter 100338032Speter /* find the active uid */ 100438032Speter q = getctladdr(a); 100538032Speter if (q == NULL) 100638032Speter { 100764562Sgshapiro user = NULL; 100838032Speter uid = 0; 100938032Speter gid = 0; 101038032Speter } 101138032Speter else 101238032Speter { 101364562Sgshapiro user = q->q_ruser != NULL ? q->q_ruser : q->q_user; 101438032Speter uid = q->q_uid; 101538032Speter gid = q->q_gid; 101638032Speter } 101738032Speter a = a->q_alias; 101838032Speter 101938032Speter /* check to see if this is the same as last time */ 102038032Speter if (lastctladdr != NULL && uid == lastuid && 102138032Speter strcmp(lastctladdr->q_paddr, a->q_paddr) == 0) 102238032Speter return; 102338032Speter lastuid = uid; 102438032Speter lastctladdr = a; 102538032Speter 102664562Sgshapiro if (uid == 0 || user == NULL || user[0] == '\0') 102790792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C"); 102838032Speter else 102990792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld", 103090792Sgshapiro denlstring(user, true, false), (long) uid, 103190792Sgshapiro (long) gid); 103290792Sgshapiro (void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n", 103390792Sgshapiro denlstring(a->q_paddr, true, false)); 103438032Speter} 103590792Sgshapiro 103690792Sgshapiro/* 103790792Sgshapiro** RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process 103890792Sgshapiro** 103990792Sgshapiro** This propagates the signal to the child processes that are queue 104090792Sgshapiro** runners. This is for a queue runner "cleanup". After all of the 104190792Sgshapiro** child queue runner processes are signaled (it should be SIGTERM 104290792Sgshapiro** being the sig) then the old signal handler (Oldsh) is called 104390792Sgshapiro** to handle any cleanup set for this process (provided it is not 104490792Sgshapiro** SIG_DFL or SIG_IGN). The signal may not be handled immediately 104590792Sgshapiro** if the BlockOldsh flag is set. If the current process doesn't 104690792Sgshapiro** have a parent then handle the signal immediately, regardless of 104790792Sgshapiro** BlockOldsh. 104890792Sgshapiro** 104990792Sgshapiro** Parameters: 105090792Sgshapiro** sig -- the signal number being sent 105190792Sgshapiro** 105290792Sgshapiro** Returns: 105390792Sgshapiro** none. 105490792Sgshapiro** 105590792Sgshapiro** Side Effects: 105690792Sgshapiro** Sets the NoMoreRunners boolean to true to stop more runners 105790792Sgshapiro** from being started in runqueue(). 105890792Sgshapiro** 105990792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 106090792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 106190792Sgshapiro** DOING. 106290792Sgshapiro*/ 106390792Sgshapiro 106490792Sgshapirostatic bool volatile NoMoreRunners = false; 106590792Sgshapirostatic sigfunc_t Oldsh_term = SIG_DFL; 106690792Sgshapirostatic sigfunc_t Oldsh_hup = SIG_DFL; 106790792Sgshapirostatic sigfunc_t volatile Oldsh = SIG_DFL; 106890792Sgshapirostatic bool BlockOldsh = false; 106990792Sgshapirostatic int volatile Oldsig = 0; 107090792Sgshapirostatic SIGFUNC_DECL runners_sigterm __P((int)); 107190792Sgshapirostatic SIGFUNC_DECL runners_sighup __P((int)); 107290792Sgshapiro 107390792Sgshapirostatic SIGFUNC_DECL 107490792Sgshapirorunners_sigterm(sig) 107590792Sgshapiro int sig; 107690792Sgshapiro{ 107790792Sgshapiro int save_errno = errno; 107890792Sgshapiro 107990792Sgshapiro FIX_SYSV_SIGNAL(sig, runners_sigterm); 108090792Sgshapiro errno = save_errno; 108190792Sgshapiro CHECK_CRITICAL(sig); 108290792Sgshapiro NoMoreRunners = true; 108390792Sgshapiro Oldsh = Oldsh_term; 108490792Sgshapiro Oldsig = sig; 108590792Sgshapiro proc_list_signal(PROC_QUEUE, sig); 108690792Sgshapiro 108790792Sgshapiro if (!BlockOldsh || getppid() <= 1) 108890792Sgshapiro { 108990792Sgshapiro /* Check that a valid 'old signal handler' is callable */ 109090792Sgshapiro if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN && 109190792Sgshapiro Oldsh_term != runners_sigterm) 109290792Sgshapiro (*Oldsh_term)(sig); 109390792Sgshapiro } 109490792Sgshapiro errno = save_errno; 109590792Sgshapiro return SIGFUNC_RETURN; 109690792Sgshapiro} 109790792Sgshapiro/* 109890792Sgshapiro** RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process 109990792Sgshapiro** 110090792Sgshapiro** This propagates the signal to the child processes that are queue 110190792Sgshapiro** runners. This is for a queue runner "cleanup". After all of the 110290792Sgshapiro** child queue runner processes are signaled (it should be SIGHUP 110390792Sgshapiro** being the sig) then the old signal handler (Oldsh) is called to 110490792Sgshapiro** handle any cleanup set for this process (provided it is not SIG_DFL 110590792Sgshapiro** or SIG_IGN). The signal may not be handled immediately if the 110690792Sgshapiro** BlockOldsh flag is set. If the current process doesn't have 110790792Sgshapiro** a parent then handle the signal immediately, regardless of 110890792Sgshapiro** BlockOldsh. 110990792Sgshapiro** 111090792Sgshapiro** Parameters: 111190792Sgshapiro** sig -- the signal number being sent 111290792Sgshapiro** 111390792Sgshapiro** Returns: 111490792Sgshapiro** none. 111590792Sgshapiro** 111690792Sgshapiro** Side Effects: 111790792Sgshapiro** Sets the NoMoreRunners boolean to true to stop more runners 111890792Sgshapiro** from being started in runqueue(). 111990792Sgshapiro** 112090792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 112190792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 112290792Sgshapiro** DOING. 112390792Sgshapiro*/ 112490792Sgshapiro 112590792Sgshapirostatic SIGFUNC_DECL 112690792Sgshapirorunners_sighup(sig) 112790792Sgshapiro int sig; 112890792Sgshapiro{ 112990792Sgshapiro int save_errno = errno; 113090792Sgshapiro 113190792Sgshapiro FIX_SYSV_SIGNAL(sig, runners_sighup); 113290792Sgshapiro errno = save_errno; 113390792Sgshapiro CHECK_CRITICAL(sig); 113490792Sgshapiro NoMoreRunners = true; 113590792Sgshapiro Oldsh = Oldsh_hup; 113690792Sgshapiro Oldsig = sig; 113790792Sgshapiro proc_list_signal(PROC_QUEUE, sig); 113890792Sgshapiro 113990792Sgshapiro if (!BlockOldsh || getppid() <= 1) 114090792Sgshapiro { 114190792Sgshapiro /* Check that a valid 'old signal handler' is callable */ 114290792Sgshapiro if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN && 114390792Sgshapiro Oldsh_hup != runners_sighup) 114490792Sgshapiro (*Oldsh_hup)(sig); 114590792Sgshapiro } 114690792Sgshapiro errno = save_errno; 114790792Sgshapiro return SIGFUNC_RETURN; 114890792Sgshapiro} 114990792Sgshapiro/* 115090792Sgshapiro** MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart 115190792Sgshapiro** 115290792Sgshapiro** Sets a workgroup for restarting. 115390792Sgshapiro** 115490792Sgshapiro** Parameters: 115590792Sgshapiro** wgrp -- the work group id to restart. 115690792Sgshapiro** reason -- why (signal?), -1 to turn off restart 115790792Sgshapiro** 115890792Sgshapiro** Returns: 115990792Sgshapiro** none. 116090792Sgshapiro** 116190792Sgshapiro** Side effects: 116290792Sgshapiro** May set global RestartWorkGroup to true. 116390792Sgshapiro** 116490792Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 116590792Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 116690792Sgshapiro** DOING. 116790792Sgshapiro*/ 116890792Sgshapiro 116990792Sgshapirovoid 117090792Sgshapiromark_work_group_restart(wgrp, reason) 117190792Sgshapiro int wgrp; 117290792Sgshapiro int reason; 117390792Sgshapiro{ 117490792Sgshapiro if (wgrp < 0 || wgrp > NumWorkGroups) 117590792Sgshapiro return; 117690792Sgshapiro 117790792Sgshapiro WorkGrp[wgrp].wg_restart = reason; 117890792Sgshapiro if (reason >= 0) 117990792Sgshapiro RestartWorkGroup = true; 118090792Sgshapiro} 118190792Sgshapiro/* 118290792Sgshapiro** RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart 118390792Sgshapiro** 118490792Sgshapiro** Restart any workgroup marked as needing a restart provided more 118590792Sgshapiro** runners are allowed. 118690792Sgshapiro** 118790792Sgshapiro** Parameters: 118890792Sgshapiro** none. 118990792Sgshapiro** 119090792Sgshapiro** Returns: 119190792Sgshapiro** none. 119290792Sgshapiro** 119390792Sgshapiro** Side effects: 119490792Sgshapiro** Sets global RestartWorkGroup to false. 119590792Sgshapiro*/ 119690792Sgshapiro 119790792Sgshapirovoid 119890792Sgshapirorestart_marked_work_groups() 119990792Sgshapiro{ 120090792Sgshapiro int i; 120190792Sgshapiro int wasblocked; 120290792Sgshapiro 120390792Sgshapiro if (NoMoreRunners) 120490792Sgshapiro return; 120590792Sgshapiro 120690792Sgshapiro /* Block SIGCHLD so reapchild() doesn't mess with us */ 120790792Sgshapiro wasblocked = sm_blocksignal(SIGCHLD); 120890792Sgshapiro 120990792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 121090792Sgshapiro { 121190792Sgshapiro if (WorkGrp[i].wg_restart >= 0) 121290792Sgshapiro { 121390792Sgshapiro if (LogLevel > 8) 121490792Sgshapiro sm_syslog(LOG_ERR, NOQID, 121590792Sgshapiro "restart queue runner=%d due to signal 0x%x", 121690792Sgshapiro i, WorkGrp[i].wg_restart); 121790792Sgshapiro restart_work_group(i); 121890792Sgshapiro } 121990792Sgshapiro } 122090792Sgshapiro RestartWorkGroup = false; 122190792Sgshapiro 122290792Sgshapiro if (wasblocked == 0) 122390792Sgshapiro (void) sm_releasesignal(SIGCHLD); 122490792Sgshapiro} 122590792Sgshapiro/* 122690792Sgshapiro** RESTART_WORK_GROUP -- restart a specific work group 122790792Sgshapiro** 122890792Sgshapiro** Restart a specific workgroup provided more runners are allowed. 122990792Sgshapiro** If the requested work group has been restarted too many times log 123090792Sgshapiro** this and refuse to restart. 123190792Sgshapiro** 123290792Sgshapiro** Parameters: 123390792Sgshapiro** wgrp -- the work group id to restart 123490792Sgshapiro** 123590792Sgshapiro** Returns: 123690792Sgshapiro** none. 123790792Sgshapiro** 123890792Sgshapiro** Side Effects: 123990792Sgshapiro** starts another process doing the work of wgrp 124090792Sgshapiro*/ 124190792Sgshapiro 124290792Sgshapiro#define MAX_PERSIST_RESTART 10 /* max allowed number of restarts */ 124390792Sgshapiro 124490792Sgshapirostatic void 124590792Sgshapirorestart_work_group(wgrp) 124690792Sgshapiro int wgrp; 124790792Sgshapiro{ 124890792Sgshapiro if (NoMoreRunners || 124990792Sgshapiro wgrp < 0 || wgrp > NumWorkGroups) 125090792Sgshapiro return; 125190792Sgshapiro 125290792Sgshapiro WorkGrp[wgrp].wg_restart = -1; 125390792Sgshapiro if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART) 125490792Sgshapiro { 125590792Sgshapiro /* avoid overflow; increment here */ 125690792Sgshapiro WorkGrp[wgrp].wg_restartcnt++; 1257110560Sgshapiro (void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL); 125890792Sgshapiro } 125990792Sgshapiro else 126090792Sgshapiro { 126190792Sgshapiro sm_syslog(LOG_ERR, NOQID, 126290792Sgshapiro "ERROR: persistent queue runner=%d restarted too many times, queue runner lost", 126390792Sgshapiro wgrp); 126490792Sgshapiro } 126590792Sgshapiro} 126690792Sgshapiro/* 126790792Sgshapiro** SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group. 126890792Sgshapiro** 126990792Sgshapiro** Parameters: 127090792Sgshapiro** runall -- schedule even if individual bit is not set. 127190792Sgshapiro** wgrp -- the work group id to schedule. 127294334Sgshapiro** didit -- the queue run was performed for this work group. 127390792Sgshapiro** 127490792Sgshapiro** Returns: 127590792Sgshapiro** nothing 127690792Sgshapiro*/ 127790792Sgshapiro 127890792Sgshapiro#define INCR_MOD(v, m) if (++v >= m) \ 127990792Sgshapiro v = 0; \ 128090792Sgshapiro else 128190792Sgshapiro 128290792Sgshapirostatic void 128394334Sgshapiroschedule_queue_runs(runall, wgrp, didit) 128490792Sgshapiro bool runall; 128590792Sgshapiro int wgrp; 128694334Sgshapiro bool didit; 128790792Sgshapiro{ 128890792Sgshapiro int qgrp, cgrp, endgrp; 128994334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 129094334Sgshapiro time_t lastsched; 129194334Sgshapiro bool sched; 129294334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 129394334Sgshapiro time_t now; 129494334Sgshapiro time_t minqintvl; 129590792Sgshapiro 129690792Sgshapiro /* 129790792Sgshapiro ** This is a bit ugly since we have to duplicate the 129890792Sgshapiro ** code that "walks" through a work queue group. 129990792Sgshapiro */ 130090792Sgshapiro 130194334Sgshapiro now = curtime(); 130294334Sgshapiro minqintvl = 0; 130390792Sgshapiro cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp; 130490792Sgshapiro do 130590792Sgshapiro { 130690792Sgshapiro time_t qintvl; 130790792Sgshapiro 130894334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 130994334Sgshapiro lastsched = 0; 131094334Sgshapiro sched = false; 131194334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 131290792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index; 131390792Sgshapiro if (Queue[qgrp]->qg_queueintvl > 0) 131490792Sgshapiro qintvl = Queue[qgrp]->qg_queueintvl; 131590792Sgshapiro else if (QueueIntvl > 0) 131690792Sgshapiro qintvl = QueueIntvl; 131790792Sgshapiro else 131890792Sgshapiro qintvl = (time_t) 0; 131990792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 132094334Sgshapiro lastsched = Queue[qgrp]->qg_nextrun; 132194334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 132294334Sgshapiro if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0) 132394334Sgshapiro { 132494334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 132594334Sgshapiro sched = true; 132694334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 132794334Sgshapiro if (minqintvl == 0 || qintvl < minqintvl) 132894334Sgshapiro minqintvl = qintvl; 132994334Sgshapiro 133094334Sgshapiro /* 133194334Sgshapiro ** Only set a new time if a queue run was performed 133294334Sgshapiro ** for this queue group. If the queue was not run, 133394334Sgshapiro ** we could starve it by setting a new time on each 133494334Sgshapiro ** call. 133594334Sgshapiro */ 133694334Sgshapiro 133794334Sgshapiro if (didit) 133894334Sgshapiro Queue[qgrp]->qg_nextrun += qintvl; 133994334Sgshapiro } 134094334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 134190792Sgshapiro if (tTd(69, 10)) 134290792Sgshapiro sm_syslog(LOG_INFO, NOQID, 134394334Sgshapiro "sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d", 134490792Sgshapiro wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl, 134594334Sgshapiro QueueIntvl, runall, lastsched, 134694334Sgshapiro Queue[qgrp]->qg_nextrun, sched); 134790792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 134890792Sgshapiro INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp); 134990792Sgshapiro } while (endgrp != cgrp); 135094334Sgshapiro if (minqintvl > 0) 135194334Sgshapiro (void) sm_setevent(minqintvl, runqueueevent, 0); 135290792Sgshapiro} 135394334Sgshapiro 135494334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA 135590792Sgshapiro/* 135694334Sgshapiro** CHECKQUEUERUNNER -- check whether a queue group hasn't been run. 135794334Sgshapiro** 135894334Sgshapiro** Use this if events may get lost and hence queue runners may not 135994334Sgshapiro** be started and mail will pile up in a queue. 136094334Sgshapiro** 136194334Sgshapiro** Parameters: 136294334Sgshapiro** none. 136394334Sgshapiro** 136494334Sgshapiro** Returns: 136594334Sgshapiro** true if a queue run is necessary. 136694334Sgshapiro** 136794334Sgshapiro** Side Effects: 136894334Sgshapiro** may schedule a queue run. 136994334Sgshapiro*/ 137094334Sgshapiro 137194334Sgshapirobool 137294334Sgshapirocheckqueuerunner() 137394334Sgshapiro{ 137494334Sgshapiro int qgrp; 137594334Sgshapiro time_t now, minqintvl; 137694334Sgshapiro 137794334Sgshapiro now = curtime(); 137894334Sgshapiro minqintvl = 0; 137994334Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 138094334Sgshapiro { 138194334Sgshapiro time_t qintvl; 138294334Sgshapiro 138394334Sgshapiro if (Queue[qgrp]->qg_queueintvl > 0) 138494334Sgshapiro qintvl = Queue[qgrp]->qg_queueintvl; 138594334Sgshapiro else if (QueueIntvl > 0) 138694334Sgshapiro qintvl = QueueIntvl; 138794334Sgshapiro else 138894334Sgshapiro qintvl = (time_t) 0; 138994334Sgshapiro if (Queue[qgrp]->qg_nextrun <= now - qintvl) 139094334Sgshapiro { 139194334Sgshapiro if (minqintvl == 0 || qintvl < minqintvl) 139294334Sgshapiro minqintvl = qintvl; 139394334Sgshapiro if (LogLevel > 1) 139494334Sgshapiro sm_syslog(LOG_WARNING, NOQID, 139594334Sgshapiro "checkqueuerunner: queue %d should have been run at %s, queue interval %ld", 139694334Sgshapiro qgrp, 139794334Sgshapiro arpadate(ctime(&Queue[qgrp]->qg_nextrun)), 139894334Sgshapiro qintvl); 139994334Sgshapiro } 140094334Sgshapiro } 140194334Sgshapiro if (minqintvl > 0) 140294334Sgshapiro { 140394334Sgshapiro (void) sm_setevent(minqintvl, runqueueevent, 0); 140494334Sgshapiro return true; 140594334Sgshapiro } 140694334Sgshapiro return false; 140794334Sgshapiro} 140894334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */ 140994334Sgshapiro 141094334Sgshapiro/* 141138032Speter** RUNQUEUE -- run the jobs in the queue. 141238032Speter** 141338032Speter** Gets the stuff out of the queue in some presumably logical 141438032Speter** order and processes them. 141538032Speter** 141638032Speter** Parameters: 141790792Sgshapiro** forkflag -- true if the queue scanning should be done in 141838032Speter** a child process. We double-fork so it is not our 141938032Speter** child and we don't have to clean up after it. 142090792Sgshapiro** false can be ignored if we have multiple queues. 142190792Sgshapiro** verbose -- if true, print out status information. 142290792Sgshapiro** persistent -- persistent queue runner? 142390792Sgshapiro** runall -- run all groups or only a subset (DoQueueRun)? 142438032Speter** 142538032Speter** Returns: 142690792Sgshapiro** true if the queue run successfully began. 142738032Speter** 142838032Speter** Side Effects: 142990792Sgshapiro** runs things in the mail queue using run_work_group(). 143090792Sgshapiro** maybe schedules next queue run. 143138032Speter*/ 143238032Speter 143364562Sgshapirostatic ENVELOPE QueueEnvelope; /* the queue run envelope */ 143464562Sgshapirostatic time_t LastQueueTime = 0; /* last time a queue ID assigned */ 143564562Sgshapirostatic pid_t LastQueuePid = -1; /* last PID which had a queue ID */ 143638032Speter 143764562Sgshapiro/* values for qp_supdirs */ 143864562Sgshapiro#define QP_NOSUB 0x0000 /* No subdirectories */ 143964562Sgshapiro#define QP_SUBDF 0x0001 /* "df" subdirectory */ 144064562Sgshapiro#define QP_SUBQF 0x0002 /* "qf" subdirectory */ 144164562Sgshapiro#define QP_SUBXF 0x0004 /* "xf" subdirectory */ 144264562Sgshapiro 144338032Speterbool 144490792Sgshapirorunqueue(forkflag, verbose, persistent, runall) 144538032Speter bool forkflag; 144638032Speter bool verbose; 144790792Sgshapiro bool persistent; 144890792Sgshapiro bool runall; 144938032Speter{ 145064562Sgshapiro int i; 145190792Sgshapiro bool ret = true; 145264562Sgshapiro static int curnum = 0; 145390792Sgshapiro sigfunc_t cursh; 145490792Sgshapiro#if SM_HEAP_CHECK 145590792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 145664562Sgshapiro 145790792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 145890792Sgshapiro { 145990792Sgshapiro oldgroup = sm_heap_group(); 146090792Sgshapiro sm_heap_newgroup(); 146190792Sgshapiro sm_dprintf("runqueue() heap group #%d\n", sm_heap_group()); 146290792Sgshapiro } 146390792Sgshapiro#endif /* SM_HEAP_CHECK */ 146471345Sgshapiro 146590792Sgshapiro /* queue run has been started, don't do any more this time */ 146694334Sgshapiro DoQueueRun = false; 146771345Sgshapiro 146890792Sgshapiro /* more than one queue or more than one directory per queue */ 146990792Sgshapiro if (!forkflag && !verbose && 147090792Sgshapiro (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 || 147190792Sgshapiro WorkGrp[0].wg_numqgrp > 1)) 147290792Sgshapiro forkflag = true; 147364562Sgshapiro 147490792Sgshapiro /* 147590792Sgshapiro ** For controlling queue runners via signals sent to this process. 147690792Sgshapiro ** Oldsh* will get called too by runners_sig* (if it is not SIG_IGN 147790792Sgshapiro ** or SIG_DFL) to preserve cleanup behavior. Now that this process 147890792Sgshapiro ** will have children (and perhaps grandchildren) this handler will 147990792Sgshapiro ** be left in place. This is because this process, once it has 148090792Sgshapiro ** finished spinning off queue runners, may go back to doing something 148190792Sgshapiro ** else (like being a daemon). And we still want on a SIG{TERM,HUP} to 148290792Sgshapiro ** clean up the child queue runners. Only install 'runners_sig*' once 148390792Sgshapiro ** else we'll get stuck looping forever. 148490792Sgshapiro */ 148590792Sgshapiro 148690792Sgshapiro cursh = sm_signal(SIGTERM, runners_sigterm); 148790792Sgshapiro if (cursh != runners_sigterm) 148890792Sgshapiro Oldsh_term = cursh; 148990792Sgshapiro cursh = sm_signal(SIGHUP, runners_sighup); 149090792Sgshapiro if (cursh != runners_sighup) 149190792Sgshapiro Oldsh_hup = cursh; 149290792Sgshapiro 149390792Sgshapiro for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++) 149464562Sgshapiro { 1495110560Sgshapiro int rwgflags = RWG_NONE; 1496244833Sgshapiro int wasblocked; 1497110560Sgshapiro 149864562Sgshapiro /* 149990792Sgshapiro ** If MaxQueueChildren active then test whether the start 150090792Sgshapiro ** of the next queue group's additional queue runners (maximum) 150190792Sgshapiro ** will result in MaxQueueChildren being exceeded. 150290792Sgshapiro ** 150390792Sgshapiro ** Note: do not use continue; even though another workgroup 150490792Sgshapiro ** may have fewer queue runners, this would be "unfair", 150590792Sgshapiro ** i.e., this work group might "starve" then. 150664562Sgshapiro */ 150764562Sgshapiro 150890792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 150990792Sgshapiro if (tTd(69, 10)) 151090792Sgshapiro sm_syslog(LOG_INFO, NOQID, 151190792Sgshapiro "rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d", 151290792Sgshapiro curnum, MaxQueueChildren, CurRunners, 151390792Sgshapiro WorkGrp[curnum].wg_maxact); 151490792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 151590792Sgshapiro if (MaxQueueChildren > 0 && 151690792Sgshapiro CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren) 151790792Sgshapiro break; 151864562Sgshapiro 151964562Sgshapiro /* 152090792Sgshapiro ** Pick up where we left off (curnum), in case we 152190792Sgshapiro ** used up all the children last time without finishing. 152290792Sgshapiro ** This give a round-robin fairness to queue runs. 1523102528Sgshapiro ** 1524102528Sgshapiro ** Increment CurRunners before calling run_work_group() 1525102528Sgshapiro ** to avoid a "race condition" with proc_list_drop() which 1526102528Sgshapiro ** decrements CurRunners if the queue runners terminate. 1527102528Sgshapiro ** Notice: CurRunners is an upper limit, in some cases 1528102528Sgshapiro ** (too few jobs in the queue) this value is larger than 1529102528Sgshapiro ** the actual number of queue runners. The discrepancy can 1530102528Sgshapiro ** increase if some queue runners "hang" for a long time. 153190792Sgshapiro */ 153290792Sgshapiro 1533244833Sgshapiro /* don't let proc_list_drop() change CurRunners */ 1534244833Sgshapiro wasblocked = sm_blocksignal(SIGCHLD); 1535102528Sgshapiro CurRunners += WorkGrp[curnum].wg_maxact; 1536244833Sgshapiro if (wasblocked == 0) 1537244833Sgshapiro (void) sm_releasesignal(SIGCHLD); 1538110560Sgshapiro if (forkflag) 1539110560Sgshapiro rwgflags |= RWG_FORK; 1540110560Sgshapiro if (verbose) 1541110560Sgshapiro rwgflags |= RWG_VERBOSE; 1542110560Sgshapiro if (persistent) 1543110560Sgshapiro rwgflags |= RWG_PERSISTENT; 1544110560Sgshapiro if (runall) 1545110560Sgshapiro rwgflags |= RWG_RUNALL; 1546110560Sgshapiro ret = run_work_group(curnum, rwgflags); 154790792Sgshapiro 154890792Sgshapiro /* 154964562Sgshapiro ** Failure means a message was printed for ETRN 155064562Sgshapiro ** and subsequent queues are likely to fail as well. 1551102528Sgshapiro ** Decrement CurRunners in that case because 1552102528Sgshapiro ** none have been started. 155364562Sgshapiro */ 155464562Sgshapiro 155564562Sgshapiro if (!ret) 1556102528Sgshapiro { 1557244833Sgshapiro /* don't let proc_list_drop() change CurRunners */ 1558244833Sgshapiro wasblocked = sm_blocksignal(SIGCHLD); 1559102528Sgshapiro CurRunners -= WorkGrp[curnum].wg_maxact; 1560244833Sgshapiro CHK_CUR_RUNNERS("runqueue", curnum, 1561244833Sgshapiro WorkGrp[curnum].wg_maxact); 1562244833Sgshapiro if (wasblocked == 0) 1563244833Sgshapiro (void) sm_releasesignal(SIGCHLD); 156464562Sgshapiro break; 1565102528Sgshapiro } 156664562Sgshapiro 156790792Sgshapiro if (!persistent) 156894334Sgshapiro schedule_queue_runs(runall, curnum, true); 156990792Sgshapiro INCR_MOD(curnum, NumWorkGroups); 157064562Sgshapiro } 157190792Sgshapiro 157290792Sgshapiro /* schedule left over queue runs */ 157390792Sgshapiro if (i < NumWorkGroups && !NoMoreRunners && !persistent) 157490792Sgshapiro { 157590792Sgshapiro int h; 157690792Sgshapiro 157790792Sgshapiro for (h = curnum; i < NumWorkGroups; i++) 157890792Sgshapiro { 157994334Sgshapiro schedule_queue_runs(runall, h, false); 158090792Sgshapiro INCR_MOD(h, NumWorkGroups); 158190792Sgshapiro } 158290792Sgshapiro } 158390792Sgshapiro 158490792Sgshapiro 158590792Sgshapiro#if SM_HEAP_CHECK 158690792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 158790792Sgshapiro sm_heap_setgroup(oldgroup); 158890792Sgshapiro#endif /* SM_HEAP_CHECK */ 158964562Sgshapiro return ret; 159064562Sgshapiro} 1591132943Sgshapiro 1592132943Sgshapiro#if _FFR_SKIP_DOMAINS 159390792Sgshapiro/* 1594132943Sgshapiro** SKIP_DOMAINS -- Skip 'skip' number of domains in the WorkQ. 1595132943Sgshapiro** 1596132943Sgshapiro** Added by Stephen Frost <sfrost@snowman.net> to support 1597132943Sgshapiro** having each runner process every N'th domain instead of 1598132943Sgshapiro** every N'th message. 1599132943Sgshapiro** 1600132943Sgshapiro** Parameters: 1601132943Sgshapiro** skip -- number of domains in WorkQ to skip. 1602132943Sgshapiro** 1603132943Sgshapiro** Returns: 1604132943Sgshapiro** total number of messages skipped. 1605132943Sgshapiro** 1606132943Sgshapiro** Side Effects: 1607132943Sgshapiro** may change WorkQ 1608132943Sgshapiro*/ 1609132943Sgshapiro 1610132943Sgshapirostatic int 1611132943Sgshapiroskip_domains(skip) 1612132943Sgshapiro int skip; 1613132943Sgshapiro{ 1614132943Sgshapiro int n, seqjump; 1615132943Sgshapiro 1616132943Sgshapiro for (n = 0, seqjump = 0; n < skip && WorkQ != NULL; seqjump++) 1617132943Sgshapiro { 1618132943Sgshapiro if (WorkQ->w_next != NULL) 1619132943Sgshapiro { 1620132943Sgshapiro if (WorkQ->w_host != NULL && 1621132943Sgshapiro WorkQ->w_next->w_host != NULL) 1622132943Sgshapiro { 1623132943Sgshapiro if (sm_strcasecmp(WorkQ->w_host, 1624132943Sgshapiro WorkQ->w_next->w_host) != 0) 1625132943Sgshapiro n++; 1626132943Sgshapiro } 1627132943Sgshapiro else 1628132943Sgshapiro { 1629132943Sgshapiro if ((WorkQ->w_host != NULL && 1630132943Sgshapiro WorkQ->w_next->w_host == NULL) || 1631132943Sgshapiro (WorkQ->w_host == NULL && 1632132943Sgshapiro WorkQ->w_next->w_host != NULL)) 1633132943Sgshapiro n++; 1634132943Sgshapiro } 1635132943Sgshapiro } 1636132943Sgshapiro WorkQ = WorkQ->w_next; 1637132943Sgshapiro } 1638132943Sgshapiro return seqjump; 1639132943Sgshapiro} 1640132943Sgshapiro#endif /* _FFR_SKIP_DOMAINS */ 1641132943Sgshapiro 1642132943Sgshapiro/* 164390792Sgshapiro** RUNNER_WORK -- have a queue runner do its work 164464562Sgshapiro** 164590792Sgshapiro** Have a queue runner do its work a list of entries. 164690792Sgshapiro** When work isn't directly being done then this process can take a signal 164790792Sgshapiro** and terminate immediately (in a clean fashion of course). 164890792Sgshapiro** When work is directly being done, it's not to be interrupted 164990792Sgshapiro** immediately: the work should be allowed to finish at a clean point 165090792Sgshapiro** before termination (in a clean fashion of course). 165190792Sgshapiro** 165290792Sgshapiro** Parameters: 165390792Sgshapiro** e -- envelope. 165490792Sgshapiro** sequenceno -- 'th process to run WorkQ. 165590792Sgshapiro** didfork -- did the calling process fork()? 165690792Sgshapiro** skip -- process only each skip'th item. 165790792Sgshapiro** njobs -- number of jobs in WorkQ. 165890792Sgshapiro** 165990792Sgshapiro** Returns: 166090792Sgshapiro** none. 166190792Sgshapiro** 166290792Sgshapiro** Side Effects: 166390792Sgshapiro** runs things in the mail queue. 166490792Sgshapiro*/ 166590792Sgshapiro 166690792Sgshapirostatic void 166790792Sgshapirorunner_work(e, sequenceno, didfork, skip, njobs) 166890792Sgshapiro register ENVELOPE *e; 166990792Sgshapiro int sequenceno; 167090792Sgshapiro bool didfork; 167190792Sgshapiro int skip; 167290792Sgshapiro int njobs; 167390792Sgshapiro{ 1674132943Sgshapiro int n, seqjump; 167590792Sgshapiro WORK *w; 1676120256Sgshapiro time_t now; 167790792Sgshapiro 1678120256Sgshapiro SM_GET_LA(now); 167990792Sgshapiro 168090792Sgshapiro /* 168190792Sgshapiro ** Here we temporarily block the second calling of the handlers. 168290792Sgshapiro ** This allows us to handle the signal without terminating in the 168390792Sgshapiro ** middle of direct work. If a signal does come, the test for 168490792Sgshapiro ** NoMoreRunners will find it. 168590792Sgshapiro */ 168690792Sgshapiro 168790792Sgshapiro BlockOldsh = true; 1688132943Sgshapiro seqjump = skip; 168990792Sgshapiro 169090792Sgshapiro /* process them once at a time */ 169190792Sgshapiro while (WorkQ != NULL) 169290792Sgshapiro { 169390792Sgshapiro#if SM_HEAP_CHECK 169490792Sgshapiro SM_NONVOLATILE int oldgroup = 0; 169590792Sgshapiro 169690792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 169790792Sgshapiro { 169890792Sgshapiro oldgroup = sm_heap_group(); 169990792Sgshapiro sm_heap_newgroup(); 170090792Sgshapiro sm_dprintf("run_queue_group() heap group #%d\n", 170190792Sgshapiro sm_heap_group()); 170290792Sgshapiro } 170390792Sgshapiro#endif /* SM_HEAP_CHECK */ 170490792Sgshapiro 170590792Sgshapiro /* do no more work */ 170690792Sgshapiro if (NoMoreRunners) 170790792Sgshapiro { 170890792Sgshapiro /* Check that a valid signal handler is callable */ 170990792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 171090792Sgshapiro Oldsh != runners_sighup && 171190792Sgshapiro Oldsh != runners_sigterm) 171290792Sgshapiro (*Oldsh)(Oldsig); 171390792Sgshapiro break; 171490792Sgshapiro } 171590792Sgshapiro 171690792Sgshapiro w = WorkQ; /* assign current work item */ 171790792Sgshapiro 171890792Sgshapiro /* 171990792Sgshapiro ** Set the head of the WorkQ to the next work item. 172090792Sgshapiro ** It is set 'skip' ahead (the number of parallel queue 172190792Sgshapiro ** runners working on WorkQ together) since each runner 172290792Sgshapiro ** works on every 'skip'th (N-th) item. 1723132943Sgshapiro#if _FFR_SKIP_DOMAINS 1724132943Sgshapiro ** In the case of the BYHOST Queue Sort Order, the 'item' 1725132943Sgshapiro ** is a domain, so we work on every 'skip'th (N-th) domain. 1726132943Sgshapiro#endif * _FFR_SKIP_DOMAINS * 172790792Sgshapiro */ 172890792Sgshapiro 1729132943Sgshapiro#if _FFR_SKIP_DOMAINS 1730132943Sgshapiro if (QueueSortOrder == QSO_BYHOST) 1731132943Sgshapiro { 1732132943Sgshapiro seqjump = 1; 1733132943Sgshapiro if (WorkQ->w_next != NULL) 1734132943Sgshapiro { 1735132943Sgshapiro if (WorkQ->w_host != NULL && 1736132943Sgshapiro WorkQ->w_next->w_host != NULL) 1737132943Sgshapiro { 1738132943Sgshapiro if (sm_strcasecmp(WorkQ->w_host, 1739132943Sgshapiro WorkQ->w_next->w_host) 1740132943Sgshapiro != 0) 1741132943Sgshapiro seqjump = skip_domains(skip); 1742132943Sgshapiro else 1743132943Sgshapiro WorkQ = WorkQ->w_next; 1744132943Sgshapiro } 1745132943Sgshapiro else 1746132943Sgshapiro { 1747132943Sgshapiro if ((WorkQ->w_host != NULL && 1748132943Sgshapiro WorkQ->w_next->w_host == NULL) || 1749132943Sgshapiro (WorkQ->w_host == NULL && 1750132943Sgshapiro WorkQ->w_next->w_host != NULL)) 1751132943Sgshapiro seqjump = skip_domains(skip); 1752132943Sgshapiro else 1753132943Sgshapiro WorkQ = WorkQ->w_next; 1754132943Sgshapiro } 1755132943Sgshapiro } 1756132943Sgshapiro else 1757132943Sgshapiro WorkQ = WorkQ->w_next; 1758132943Sgshapiro } 1759132943Sgshapiro else 1760132943Sgshapiro#endif /* _FFR_SKIP_DOMAINS */ 1761132943Sgshapiro { 1762132943Sgshapiro for (n = 0; n < skip && WorkQ != NULL; n++) 1763132943Sgshapiro WorkQ = WorkQ->w_next; 1764132943Sgshapiro } 1765132943Sgshapiro 176690792Sgshapiro e->e_to = NULL; 176790792Sgshapiro 176890792Sgshapiro /* 176990792Sgshapiro ** Ignore jobs that are too expensive for the moment. 177090792Sgshapiro ** 177190792Sgshapiro ** Get new load average every GET_NEW_LA_TIME seconds. 177290792Sgshapiro */ 177390792Sgshapiro 1774120256Sgshapiro SM_GET_LA(now); 1775120256Sgshapiro if (shouldqueue(WkRecipFact, Current_LA_time)) 177690792Sgshapiro { 177790792Sgshapiro char *msg = "Aborting queue run: load average too high"; 177890792Sgshapiro 177990792Sgshapiro if (Verbose) 178090792Sgshapiro message("%s", msg); 178190792Sgshapiro if (LogLevel > 8) 178290792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 178390792Sgshapiro break; 178490792Sgshapiro } 178590792Sgshapiro if (shouldqueue(w->w_pri, w->w_ctime)) 178690792Sgshapiro { 178790792Sgshapiro if (Verbose) 178890792Sgshapiro message(EmptyString); 178990792Sgshapiro if (QueueSortOrder == QSO_BYPRIORITY) 179090792Sgshapiro { 179190792Sgshapiro if (Verbose) 179290792Sgshapiro message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue", 179390792Sgshapiro qid_printqueue(w->w_qgrp, 179490792Sgshapiro w->w_qdir), 179590792Sgshapiro w->w_name + 2, sequenceno, 179690792Sgshapiro njobs); 179790792Sgshapiro if (LogLevel > 8) 179890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 179990792Sgshapiro "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)", 180090792Sgshapiro qid_printqueue(w->w_qgrp, 180190792Sgshapiro w->w_qdir), 180290792Sgshapiro w->w_name + 2, w->w_pri, 180390792Sgshapiro CurrentLA, sequenceno, 180490792Sgshapiro njobs); 180590792Sgshapiro break; 180690792Sgshapiro } 180790792Sgshapiro else if (Verbose) 180890792Sgshapiro message("Skipping %s/%s (sequence %d of %d)", 180990792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 181090792Sgshapiro w->w_name + 2, sequenceno, njobs); 181190792Sgshapiro } 181290792Sgshapiro else 181390792Sgshapiro { 181490792Sgshapiro if (Verbose) 181590792Sgshapiro { 181690792Sgshapiro message(EmptyString); 181790792Sgshapiro message("Running %s/%s (sequence %d of %d)", 181890792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 181990792Sgshapiro w->w_name + 2, sequenceno, njobs); 182090792Sgshapiro } 182190792Sgshapiro if (didfork && MaxQueueChildren > 0) 182290792Sgshapiro { 182390792Sgshapiro sm_blocksignal(SIGCHLD); 182490792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 182590792Sgshapiro } 182690792Sgshapiro if (tTd(63, 100)) 182790792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 182890792Sgshapiro "runqueue %s dowork(%s)", 182990792Sgshapiro qid_printqueue(w->w_qgrp, w->w_qdir), 183090792Sgshapiro w->w_name + 2); 183190792Sgshapiro 183290792Sgshapiro (void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2, 1833111823Sgshapiro ForkQueueRuns, false, e); 183490792Sgshapiro errno = 0; 183590792Sgshapiro } 183690792Sgshapiro sm_free(w->w_name); /* XXX */ 183790792Sgshapiro if (w->w_host != NULL) 183890792Sgshapiro sm_free(w->w_host); /* XXX */ 183990792Sgshapiro sm_free((char *) w); /* XXX */ 1840132943Sgshapiro sequenceno += seqjump; /* next sequence number */ 184190792Sgshapiro#if SM_HEAP_CHECK 184290792Sgshapiro if (sm_debug_active(&DebugLeakQ, 1)) 184390792Sgshapiro sm_heap_setgroup(oldgroup); 184490792Sgshapiro#endif /* SM_HEAP_CHECK */ 184590792Sgshapiro } 184690792Sgshapiro 184790792Sgshapiro BlockOldsh = false; 184890792Sgshapiro 184990792Sgshapiro /* check the signals didn't happen during the revert */ 185090792Sgshapiro if (NoMoreRunners) 185190792Sgshapiro { 185290792Sgshapiro /* Check that a valid signal handler is callable */ 185390792Sgshapiro if (Oldsh != SIG_DFL && Oldsh != SIG_IGN && 185490792Sgshapiro Oldsh != runners_sighup && Oldsh != runners_sigterm) 185590792Sgshapiro (*Oldsh)(Oldsig); 185690792Sgshapiro } 185790792Sgshapiro 185890792Sgshapiro Oldsh = SIG_DFL; /* after the NoMoreRunners check */ 185990792Sgshapiro} 186090792Sgshapiro/* 186190792Sgshapiro** RUN_WORK_GROUP -- run the jobs in a queue group from a work group. 186290792Sgshapiro** 186364562Sgshapiro** Gets the stuff out of the queue in some presumably logical 186464562Sgshapiro** order and processes them. 186564562Sgshapiro** 186664562Sgshapiro** Parameters: 186790792Sgshapiro** wgrp -- work group to process. 1868110560Sgshapiro** flags -- RWG_* flags 186964562Sgshapiro** 187064562Sgshapiro** Returns: 187190792Sgshapiro** true if the queue run successfully began. 187264562Sgshapiro** 187364562Sgshapiro** Side Effects: 187464562Sgshapiro** runs things in the mail queue. 187564562Sgshapiro*/ 187664562Sgshapiro 187790792Sgshapiro/* Minimum sleep time for persistent queue runners */ 187890792Sgshapiro#define MIN_SLEEP_TIME 5 187990792Sgshapiro 188090792Sgshapirobool 1881110560Sgshapirorun_work_group(wgrp, flags) 188290792Sgshapiro int wgrp; 1883110560Sgshapiro int flags; 188464562Sgshapiro{ 188538032Speter register ENVELOPE *e; 188690792Sgshapiro int njobs, qdir; 188790792Sgshapiro int sequenceno = 1; 188890792Sgshapiro int qgrp, endgrp, h, i; 1889120256Sgshapiro time_t now; 189090792Sgshapiro bool full, more; 189190792Sgshapiro SM_RPOOL_T *rpool; 189238032Speter extern ENVELOPE BlankEnvelope; 189390792Sgshapiro extern SIGFUNC_DECL reapchild __P((int)); 189438032Speter 189590792Sgshapiro if (wgrp < 0) 189690792Sgshapiro return false; 189790792Sgshapiro 189838032Speter /* 189938032Speter ** If no work will ever be selected, don't even bother reading 190038032Speter ** the queue. 190138032Speter */ 190238032Speter 1903120256Sgshapiro SM_GET_LA(now); 190438032Speter 1905110560Sgshapiro if (!bitset(RWG_PERSISTENT, flags) && 1906120256Sgshapiro shouldqueue(WkRecipFact, Current_LA_time)) 190738032Speter { 190838032Speter char *msg = "Skipping queue run -- load average too high"; 190938032Speter 1910110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 191138032Speter message("458 %s\n", msg); 191238032Speter if (LogLevel > 8) 191390792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg); 191490792Sgshapiro return false; 191538032Speter } 191638032Speter 191738032Speter /* 191838032Speter ** See if we already have too many children. 191938032Speter */ 192038032Speter 1921110560Sgshapiro if (bitset(RWG_FORK, flags) && 1922110560Sgshapiro WorkGrp[wgrp].wg_lowqintvl > 0 && 1923110560Sgshapiro !bitset(RWG_PERSISTENT, flags) && 192438032Speter MaxChildren > 0 && CurChildren >= MaxChildren) 192538032Speter { 192664562Sgshapiro char *msg = "Skipping queue run -- too many children"; 192764562Sgshapiro 1928110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 192964562Sgshapiro message("458 %s (%d)\n", msg, CurChildren); 193064562Sgshapiro if (LogLevel > 8) 193190792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)", 193264562Sgshapiro msg, CurChildren); 193390792Sgshapiro return false; 193438032Speter } 193538032Speter 193638032Speter /* 193738032Speter ** See if we want to go off and do other useful work. 193838032Speter */ 193938032Speter 1940110560Sgshapiro if (bitset(RWG_FORK, flags)) 194138032Speter { 194238032Speter pid_t pid; 194338032Speter 194490792Sgshapiro (void) sm_blocksignal(SIGCHLD); 194590792Sgshapiro (void) sm_signal(SIGCHLD, reapchild); 194638032Speter 194738032Speter pid = dofork(); 194838032Speter if (pid == -1) 194938032Speter { 195038032Speter const char *msg = "Skipping queue run -- fork() failed"; 195190792Sgshapiro const char *err = sm_errstring(errno); 195238032Speter 1953110560Sgshapiro if (bitset(RWG_VERBOSE, flags)) 195438032Speter message("458 %s: %s\n", msg, err); 195538032Speter if (LogLevel > 8) 195690792Sgshapiro sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s", 195764562Sgshapiro msg, err); 195890792Sgshapiro (void) sm_releasesignal(SIGCHLD); 195990792Sgshapiro return false; 196038032Speter } 196138032Speter if (pid != 0) 196238032Speter { 196338032Speter /* parent -- pick up intermediate zombie */ 196490792Sgshapiro (void) sm_blocksignal(SIGALRM); 196590792Sgshapiro 196690792Sgshapiro /* wgrp only used when queue runners are persistent */ 196790792Sgshapiro proc_list_add(pid, "Queue runner", PROC_QUEUE, 196890792Sgshapiro WorkGrp[wgrp].wg_maxact, 1969132943Sgshapiro bitset(RWG_PERSISTENT, flags) ? wgrp : -1, 1970132943Sgshapiro NULL); 197190792Sgshapiro (void) sm_releasesignal(SIGALRM); 197290792Sgshapiro (void) sm_releasesignal(SIGCHLD); 197390792Sgshapiro return true; 197438032Speter } 197590792Sgshapiro 197664562Sgshapiro /* child -- clean up signals */ 197777349Sgshapiro 197877349Sgshapiro /* Reset global flags */ 197977349Sgshapiro RestartRequest = NULL; 198090792Sgshapiro RestartWorkGroup = false; 198177349Sgshapiro ShutdownRequest = NULL; 198277349Sgshapiro PendingSignal = 0; 198390792Sgshapiro CurrentPid = getpid(); 1984132943Sgshapiro close_sendmail_pid(); 198577349Sgshapiro 198690792Sgshapiro /* 198790792Sgshapiro ** Initialize exception stack and default exception 198890792Sgshapiro ** handler for child process. 198990792Sgshapiro */ 199090792Sgshapiro 199190792Sgshapiro sm_exc_newthread(fatal_error); 199242575Speter clrcontrol(); 199338032Speter proc_list_clear(); 199442575Speter 199542575Speter /* Add parent process as first child item */ 199690792Sgshapiro proc_list_add(CurrentPid, "Queue runner child process", 1997132943Sgshapiro PROC_QUEUE_CHILD, 0, -1, NULL); 199890792Sgshapiro (void) sm_releasesignal(SIGCHLD); 199990792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 200090792Sgshapiro (void) sm_signal(SIGHUP, SIG_DFL); 200190792Sgshapiro (void) sm_signal(SIGTERM, intsig); 200238032Speter } 200338032Speter 200438032Speter /* 200538032Speter ** Release any resources used by the daemon code. 200638032Speter */ 200738032Speter 200838032Speter clrdaemon(); 200938032Speter 201038032Speter /* force it to run expensive jobs */ 201190792Sgshapiro NoConnect = false; 201238032Speter 201338032Speter /* drop privileges */ 201438032Speter if (geteuid() == (uid_t) 0) 201590792Sgshapiro (void) drop_privileges(false); 201638032Speter 201738032Speter /* 201838032Speter ** Create ourselves an envelope 201938032Speter */ 202038032Speter 202138032Speter CurEnv = &QueueEnvelope; 202290792Sgshapiro rpool = sm_rpool_new_x(NULL); 202390792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 202438032Speter e->e_flags = BlankEnvelope.e_flags; 202573188Sgshapiro e->e_parent = NULL; 202638032Speter 202738032Speter /* make sure we have disconnected from parent */ 2028110560Sgshapiro if (bitset(RWG_FORK, flags)) 202938032Speter { 203038032Speter disconnect(1, e); 203190792Sgshapiro QuickAbort = false; 203238032Speter } 203338032Speter 203438032Speter /* 203538032Speter ** If we are running part of the queue, always ignore stored 203638032Speter ** host status. 203738032Speter */ 203838032Speter 203938032Speter if (QueueLimitId != NULL || QueueLimitSender != NULL || 204090792Sgshapiro QueueLimitQuarantine != NULL || 204138032Speter QueueLimitRecipient != NULL) 204238032Speter { 204390792Sgshapiro IgnoreHostStatus = true; 204438032Speter MinQueueAge = 0; 2045244833Sgshapiro#if _FFR_EXPDELAY 2046244833Sgshapiro MaxQueueAge = 0; 2047244833Sgshapiro#endif /* _FFR_EXPDELAY */ 204838032Speter } 204938032Speter 205038032Speter /* 205190792Sgshapiro ** Here is where we choose the queue group from the work group. 205290792Sgshapiro ** The caller of the "domorework" label must setup a new envelope. 205390792Sgshapiro */ 205490792Sgshapiro 205590792Sgshapiro endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */ 205690792Sgshapiro 205790792Sgshapiro domorework: 205890792Sgshapiro 205990792Sgshapiro /* 206090792Sgshapiro ** Run a queue group if: 2061110560Sgshapiro ** RWG_RUNALL bit is set or the bit for this group is set. 206290792Sgshapiro */ 206390792Sgshapiro 206494334Sgshapiro now = curtime(); 206590792Sgshapiro for (;;) 206690792Sgshapiro { 206790792Sgshapiro /* 206890792Sgshapiro ** Find the next queue group within the work group that 206990792Sgshapiro ** has been marked as needing a run. 207090792Sgshapiro */ 207190792Sgshapiro 207290792Sgshapiro qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index; 207390792Sgshapiro WorkGrp[wgrp].wg_curqgrp++; /* advance */ 207490792Sgshapiro WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */ 2075110560Sgshapiro if (bitset(RWG_RUNALL, flags) || 207694334Sgshapiro (Queue[qgrp]->qg_nextrun <= now && 207794334Sgshapiro Queue[qgrp]->qg_nextrun != (time_t) -1)) 207890792Sgshapiro break; 207990792Sgshapiro if (endgrp == WorkGrp[wgrp].wg_curqgrp) 208090792Sgshapiro { 208190792Sgshapiro e->e_id = NULL; 2082110560Sgshapiro if (bitset(RWG_FORK, flags)) 208390792Sgshapiro finis(true, true, ExitStat); 208490792Sgshapiro return true; /* we're done */ 208590792Sgshapiro } 208690792Sgshapiro } 208790792Sgshapiro 208890792Sgshapiro qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */ 208990792Sgshapiro#if _FFR_QUEUE_SCHED_DBG 209090792Sgshapiro if (tTd(69, 12)) 209190792Sgshapiro sm_syslog(LOG_INFO, NOQID, 209290792Sgshapiro "rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d", 209390792Sgshapiro wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir), 209490792Sgshapiro WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp); 209590792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 209690792Sgshapiro 209790792Sgshapiro#if HASNICE 209890792Sgshapiro /* tweak niceness of queue runs */ 209990792Sgshapiro if (Queue[qgrp]->qg_nice > 0) 210090792Sgshapiro (void) nice(Queue[qgrp]->qg_nice); 210190792Sgshapiro#endif /* HASNICE */ 210290792Sgshapiro 210390792Sgshapiro /* XXX running queue group... */ 210490792Sgshapiro sm_setproctitle(true, CurEnv, "running queue: %s", 210590792Sgshapiro qid_printqueue(qgrp, qdir)); 210690792Sgshapiro 210790792Sgshapiro if (LogLevel > 69 || tTd(63, 99)) 210890792Sgshapiro sm_syslog(LOG_DEBUG, NOQID, 210990792Sgshapiro "runqueue %s, pid=%d, forkflag=%d", 211090792Sgshapiro qid_printqueue(qgrp, qdir), (int) CurrentPid, 2111110560Sgshapiro bitset(RWG_FORK, flags)); 211290792Sgshapiro 211390792Sgshapiro /* 211438032Speter ** Start making passes through the queue. 211538032Speter ** First, read and sort the entire queue. 211638032Speter ** Then, process the work in that order. 211738032Speter ** But if you take too long, start over. 211838032Speter */ 211938032Speter 212090792Sgshapiro for (i = 0; i < Queue[qgrp]->qg_numqueues; i++) 212190792Sgshapiro { 2122203004Sgshapiro (void) gatherq(qgrp, qdir, false, &full, &more, &h); 212390792Sgshapiro#if SM_CONF_SHM 212490792Sgshapiro if (ShmId != SM_SHM_NO_ID) 212590792Sgshapiro QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h; 212690792Sgshapiro#endif /* SM_CONF_SHM */ 212790792Sgshapiro /* If there are no more items in this queue advance */ 212890792Sgshapiro if (!more) 212990792Sgshapiro { 213090792Sgshapiro /* A round-robin advance */ 213190792Sgshapiro qdir++; 213290792Sgshapiro qdir %= Queue[qgrp]->qg_numqueues; 213390792Sgshapiro } 213490792Sgshapiro 213590792Sgshapiro /* Has the WorkList reached the limit? */ 213690792Sgshapiro if (full) 213790792Sgshapiro break; /* don't try to gather more */ 213890792Sgshapiro } 213990792Sgshapiro 214038032Speter /* order the existing work requests */ 214190792Sgshapiro njobs = sortq(Queue[qgrp]->qg_maxlist); 214290792Sgshapiro Queue[qgrp]->qg_curnum = qdir; /* update */ 214338032Speter 214464562Sgshapiro 214590792Sgshapiro if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags)) 214638032Speter { 214790792Sgshapiro int loop, maxrunners; 214890792Sgshapiro pid_t pid; 214938032Speter 215038032Speter /* 215190792Sgshapiro ** For this WorkQ we want to fork off N children (maxrunners) 215290792Sgshapiro ** at this point. Each child has a copy of WorkQ. Each child 215390792Sgshapiro ** will process every N-th item. The parent will wait for all 215490792Sgshapiro ** of the children to finish before moving on to the next 215590792Sgshapiro ** queue group within the work group. This saves us forking 215690792Sgshapiro ** a new runner-child for each work item. 215790792Sgshapiro ** It's valid for qg_maxqrun == 0 since this may be an 215890792Sgshapiro ** explicit "don't run this queue" setting. 215938032Speter */ 216038032Speter 216190792Sgshapiro maxrunners = Queue[qgrp]->qg_maxqrun; 216290792Sgshapiro 2163173340Sgshapiro /* 2164173340Sgshapiro ** If no runners are configured for this group but 2165173340Sgshapiro ** the queue is "forced" then lets use 1 runner. 2166173340Sgshapiro */ 2167173340Sgshapiro 2168173340Sgshapiro if (maxrunners == 0 && bitset(RWG_FORCE, flags)) 2169173340Sgshapiro maxrunners = 1; 2170173340Sgshapiro 217190792Sgshapiro /* No need to have more runners then there are jobs */ 217290792Sgshapiro if (maxrunners > njobs) 217390792Sgshapiro maxrunners = njobs; 217490792Sgshapiro for (loop = 0; loop < maxrunners; loop++) 217538032Speter { 217690792Sgshapiro /* 217790792Sgshapiro ** Since the delivery may happen in a child and the 217890792Sgshapiro ** parent does not wait, the parent may close the 217990792Sgshapiro ** maps thereby removing any shared memory used by 218090792Sgshapiro ** the map. Therefore, close the maps now so the 218190792Sgshapiro ** child will dynamically open them if necessary. 218290792Sgshapiro */ 218390792Sgshapiro 218490792Sgshapiro closemaps(false); 218590792Sgshapiro 218690792Sgshapiro pid = fork(); 218790792Sgshapiro if (pid < 0) 218890792Sgshapiro { 218990792Sgshapiro syserr("run_work_group: cannot fork"); 2190120256Sgshapiro return false; 219190792Sgshapiro } 219290792Sgshapiro else if (pid > 0) 219390792Sgshapiro { 219490792Sgshapiro /* parent -- clean out connection cache */ 219590792Sgshapiro mci_flush(false, NULL); 2196132943Sgshapiro#if _FFR_SKIP_DOMAINS 2197132943Sgshapiro if (QueueSortOrder == QSO_BYHOST) 2198132943Sgshapiro { 2199132943Sgshapiro sequenceno += skip_domains(1); 2200132943Sgshapiro } 2201132943Sgshapiro else 2202132943Sgshapiro#endif /* _FFR_SKIP_DOMAINS */ 2203132943Sgshapiro { 2204132943Sgshapiro /* for the skip */ 2205132943Sgshapiro WorkQ = WorkQ->w_next; 2206132943Sgshapiro sequenceno++; 2207132943Sgshapiro } 220890792Sgshapiro proc_list_add(pid, "Queue child runner process", 2209132943Sgshapiro PROC_QUEUE_CHILD, 0, -1, NULL); 221090792Sgshapiro 221190792Sgshapiro /* No additional work, no additional runners */ 221290792Sgshapiro if (WorkQ == NULL) 221390792Sgshapiro break; 221490792Sgshapiro } 221590792Sgshapiro else 221690792Sgshapiro { 221790792Sgshapiro /* child -- Reset global flags */ 221890792Sgshapiro RestartRequest = NULL; 221990792Sgshapiro RestartWorkGroup = false; 222090792Sgshapiro ShutdownRequest = NULL; 222190792Sgshapiro PendingSignal = 0; 222290792Sgshapiro CurrentPid = getpid(); 2223132943Sgshapiro close_sendmail_pid(); 222490792Sgshapiro 222590792Sgshapiro /* 222690792Sgshapiro ** Initialize exception stack and default 222790792Sgshapiro ** exception handler for child process. 222890792Sgshapiro ** When fork()'d the child now has a private 222990792Sgshapiro ** copy of WorkQ at its current position. 223090792Sgshapiro */ 223190792Sgshapiro 223290792Sgshapiro sm_exc_newthread(fatal_error); 223390792Sgshapiro 223490792Sgshapiro /* 223590792Sgshapiro ** SMTP processes (whether -bd or -bs) set 223690792Sgshapiro ** SIGCHLD to reapchild to collect 223790792Sgshapiro ** children status. However, at delivery 223890792Sgshapiro ** time, that status must be collected 223990792Sgshapiro ** by sm_wait() to be dealt with properly 224090792Sgshapiro ** (check success of delivery based 224190792Sgshapiro ** on status code, etc). Therefore, if we 224290792Sgshapiro ** are an SMTP process, reset SIGCHLD 224390792Sgshapiro ** back to the default so reapchild 224490792Sgshapiro ** doesn't collect status before 224590792Sgshapiro ** sm_wait(). 224690792Sgshapiro */ 224790792Sgshapiro 224890792Sgshapiro if (OpMode == MD_SMTP || 224990792Sgshapiro OpMode == MD_DAEMON || 225090792Sgshapiro MaxQueueChildren > 0) 225190792Sgshapiro { 225290792Sgshapiro proc_list_clear(); 225390792Sgshapiro sm_releasesignal(SIGCHLD); 225490792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 225590792Sgshapiro } 225690792Sgshapiro 225790792Sgshapiro /* child -- error messages to the transcript */ 225890792Sgshapiro QuickAbort = OnlyOneError = false; 225990792Sgshapiro runner_work(e, sequenceno, true, 226090792Sgshapiro maxrunners, njobs); 226190792Sgshapiro 226290792Sgshapiro /* This child is done */ 226390792Sgshapiro finis(true, true, ExitStat); 226490792Sgshapiro /* NOTREACHED */ 226590792Sgshapiro } 226638032Speter } 226790792Sgshapiro 226890792Sgshapiro sm_releasesignal(SIGCHLD); 226990792Sgshapiro 227090792Sgshapiro /* 227190792Sgshapiro ** Wait until all of the runners have completed before 227290792Sgshapiro ** seeing if there is another queue group in the 227390792Sgshapiro ** work group to process. 227490792Sgshapiro ** XXX Future enhancement: don't wait() for all children 227590792Sgshapiro ** here, just go ahead and make sure that overall the number 227690792Sgshapiro ** of children is not exceeded. 227790792Sgshapiro */ 227890792Sgshapiro 227990792Sgshapiro while (CurChildren > 0) 228038032Speter { 228190792Sgshapiro int status; 228290792Sgshapiro pid_t ret; 228338032Speter 228490792Sgshapiro while ((ret = sm_wait(&status)) <= 0) 228590792Sgshapiro continue; 228690792Sgshapiro proc_list_drop(ret, status, NULL); 228738032Speter } 228890792Sgshapiro } 2289110560Sgshapiro else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags)) 229090792Sgshapiro { 229190792Sgshapiro /* 229290792Sgshapiro ** When current process will not fork children to do the work, 229390792Sgshapiro ** it will do the work itself. The 'skip' will be 1 since 229490792Sgshapiro ** there are no child runners to divide the work across. 229590792Sgshapiro */ 229690792Sgshapiro 229790792Sgshapiro runner_work(e, sequenceno, false, 1, njobs); 229890792Sgshapiro } 229990792Sgshapiro 230090792Sgshapiro /* free memory allocated by newenvelope() above */ 230190792Sgshapiro sm_rpool_free(rpool); 230290792Sgshapiro QueueEnvelope.e_rpool = NULL; 230390792Sgshapiro 230490792Sgshapiro /* Are there still more queues in the work group to process? */ 230590792Sgshapiro if (endgrp != WorkGrp[wgrp].wg_curqgrp) 230690792Sgshapiro { 230790792Sgshapiro rpool = sm_rpool_new_x(NULL); 230890792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 230990792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 231090792Sgshapiro goto domorework; 231190792Sgshapiro } 231290792Sgshapiro 231390792Sgshapiro /* No more queues in work group to process. Now check persistent. */ 2314110560Sgshapiro if (bitset(RWG_PERSISTENT, flags)) 231590792Sgshapiro { 231690792Sgshapiro sequenceno = 1; 2317244833Sgshapiro sm_setproctitle(true, NULL, "running queue: %s", 231890792Sgshapiro qid_printqueue(qgrp, qdir)); 231990792Sgshapiro 232090792Sgshapiro /* 232190792Sgshapiro ** close bogus maps, i.e., maps which caused a tempfail, 232290792Sgshapiro ** so we get fresh map connections on the next lookup. 232390792Sgshapiro ** closemaps() is also called when children are started. 232490792Sgshapiro */ 232590792Sgshapiro 232690792Sgshapiro closemaps(true); 232790792Sgshapiro 232890792Sgshapiro /* Close any cached connections. */ 232990792Sgshapiro mci_flush(true, NULL); 233090792Sgshapiro 233190792Sgshapiro /* Clean out expired related entries. */ 233290792Sgshapiro rmexpstab(); 233390792Sgshapiro 233490792Sgshapiro#if NAMED_BIND 2335132943Sgshapiro /* Update MX records for FallbackMX. */ 2336132943Sgshapiro if (FallbackMX != NULL) 2337132943Sgshapiro (void) getfallbackmxrr(FallbackMX); 233890792Sgshapiro#endif /* NAMED_BIND */ 233990792Sgshapiro 234090792Sgshapiro#if USERDB 234190792Sgshapiro /* close UserDatabase */ 234290792Sgshapiro _udbx_close(); 234390792Sgshapiro#endif /* USERDB */ 234490792Sgshapiro 234590792Sgshapiro#if SM_HEAP_CHECK 234690792Sgshapiro if (sm_debug_active(&SmHeapCheck, 2) 234790792Sgshapiro && access("memdump", F_OK) == 0 234890792Sgshapiro ) 234938032Speter { 235090792Sgshapiro SM_FILE_T *out; 235190792Sgshapiro 235290792Sgshapiro remove("memdump"); 235390792Sgshapiro out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, 235490792Sgshapiro "memdump.out", SM_IO_APPEND, NULL); 235590792Sgshapiro if (out != NULL) 235638032Speter { 235790792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n"); 235890792Sgshapiro sm_heap_report(out, 235990792Sgshapiro sm_debug_level(&SmHeapCheck) - 1); 236090792Sgshapiro (void) sm_io_close(out, SM_TIME_DEFAULT); 236138032Speter } 236238032Speter } 236390792Sgshapiro#endif /* SM_HEAP_CHECK */ 236490792Sgshapiro 236590792Sgshapiro /* let me rest for a second to catch my breath */ 236690792Sgshapiro if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME) 236790792Sgshapiro sleep(MIN_SLEEP_TIME); 236890792Sgshapiro else if (WorkGrp[wgrp].wg_lowqintvl <= 0) 236990792Sgshapiro sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME); 237038032Speter else 237190792Sgshapiro sleep(WorkGrp[wgrp].wg_lowqintvl); 237238032Speter 237390792Sgshapiro /* 237490792Sgshapiro ** Get the LA outside the WorkQ loop if necessary. 237590792Sgshapiro ** In a persistent queue runner the code is repeated over 237690792Sgshapiro ** and over but gatherq() may ignore entries due to 237790792Sgshapiro ** shouldqueue() (do we really have to do this twice?). 237890792Sgshapiro ** Hence the queue runners would just idle around when once 237990792Sgshapiro ** CurrentLA caused all entries in a queue to be ignored. 238090792Sgshapiro */ 238164562Sgshapiro 2382120256Sgshapiro if (njobs == 0) 2383120256Sgshapiro SM_GET_LA(now); 238490792Sgshapiro rpool = sm_rpool_new_x(NULL); 238590792Sgshapiro e = newenvelope(&QueueEnvelope, CurEnv, rpool); 238690792Sgshapiro e->e_flags = BlankEnvelope.e_flags; 238790792Sgshapiro goto domorework; 238838032Speter } 238938032Speter 239038032Speter /* exit without the usual cleanup */ 239138032Speter e->e_id = NULL; 2392110560Sgshapiro if (bitset(RWG_FORK, flags)) 239390792Sgshapiro finis(true, true, ExitStat); 239464562Sgshapiro /* NOTREACHED */ 239590792Sgshapiro return true; 239638032Speter} 239738032Speter 239838032Speter/* 239990792Sgshapiro** DOQUEUERUN -- do a queue run? 240090792Sgshapiro*/ 240190792Sgshapiro 240290792Sgshapirobool 240390792Sgshapirodoqueuerun() 240490792Sgshapiro{ 240594334Sgshapiro return DoQueueRun; 240690792Sgshapiro} 240790792Sgshapiro 240890792Sgshapiro/* 240994334Sgshapiro** RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done. 241077349Sgshapiro** 241177349Sgshapiro** Parameters: 241294334Sgshapiro** none. 241377349Sgshapiro** 241477349Sgshapiro** Returns: 241577349Sgshapiro** none. 241677349Sgshapiro** 241790792Sgshapiro** Side Effects: 241890792Sgshapiro** The invocation of this function via an alarm may interrupt 241990792Sgshapiro** a set of actions. Thus errno may be set in that context. 242090792Sgshapiro** We need to restore errno at the end of this function to ensure 242190792Sgshapiro** that any work done here that sets errno doesn't return a 242290792Sgshapiro** misleading/false errno value. Errno may be EINTR upon entry to 242390792Sgshapiro** this function because of non-restartable/continuable system 242490792Sgshapiro** API was active. Iff this is true we will override errno as 242590792Sgshapiro** a timeout (as a more accurate error message). 242690792Sgshapiro** 242777349Sgshapiro** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 242877349Sgshapiro** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 242977349Sgshapiro** DOING. 243038032Speter*/ 243138032Speter 243290792Sgshapirovoid 2433141858Sgshapirorunqueueevent(ignore) 2434141858Sgshapiro int ignore; 243538032Speter{ 243690792Sgshapiro int save_errno = errno; 243790792Sgshapiro 243890792Sgshapiro /* 243990792Sgshapiro ** Set the general bit that we want a queue run, 244090792Sgshapiro ** tested in doqueuerun() 244190792Sgshapiro */ 244290792Sgshapiro 244394334Sgshapiro DoQueueRun = true; 244494334Sgshapiro#if _FFR_QUEUE_SCHED_DBG 244594334Sgshapiro if (tTd(69, 10)) 244694334Sgshapiro sm_syslog(LOG_INFO, NOQID, "rqe: done"); 244794334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */ 244890792Sgshapiro 244990792Sgshapiro errno = save_errno; 245090792Sgshapiro if (errno == EINTR) 245190792Sgshapiro errno = ETIMEDOUT; 245238032Speter} 245390792Sgshapiro/* 245490792Sgshapiro** GATHERQ -- gather messages from the message queue(s) the work queue. 245538032Speter** 245638032Speter** Parameters: 245790792Sgshapiro** qgrp -- the index of the queue group. 245890792Sgshapiro** qdir -- the index of the queue directory. 245938032Speter** doall -- if set, include everything in the queue (even 246038032Speter** the jobs that cannot be run because the load 246190792Sgshapiro** average is too high, or MaxQueueRun is reached). 246290792Sgshapiro** Otherwise, exclude those jobs. 246390792Sgshapiro** full -- (optional) to be set 'true' if WorkList is full 246490792Sgshapiro** more -- (optional) to be set 'true' if there are still more 246590792Sgshapiro** messages in this queue not added to WorkList 2466203004Sgshapiro** pnentries -- (optional) total nuber of entries in queue 246738032Speter** 246838032Speter** Returns: 246938032Speter** The number of request in the queue (not necessarily 247090792Sgshapiro** the number of requests in WorkList however). 247138032Speter** 247238032Speter** Side Effects: 247390792Sgshapiro** prepares available work into WorkList 247438032Speter*/ 247538032Speter 247690792Sgshapiro#define NEED_P 0001 /* 'P': priority */ 247790792Sgshapiro#define NEED_T 0002 /* 'T': time */ 247890792Sgshapiro#define NEED_R 0004 /* 'R': recipient */ 247990792Sgshapiro#define NEED_S 0010 /* 'S': sender */ 248090792Sgshapiro#define NEED_H 0020 /* host */ 2481132943Sgshapiro#define HAS_QUARANTINE 0040 /* has an unexpected 'q' line */ 2482132943Sgshapiro#define NEED_QUARANTINE 0100 /* 'q': reason */ 248338032Speter 248490792Sgshapirostatic WORK *WorkList = NULL; /* list of unsort work */ 248590792Sgshapirostatic int WorkListSize = 0; /* current max size of WorkList */ 248690792Sgshapirostatic int WorkListCount = 0; /* # of work items in WorkList */ 248738032Speter 248864562Sgshapirostatic int 2489203004Sgshapirogatherq(qgrp, qdir, doall, full, more, pnentries) 249090792Sgshapiro int qgrp; 249190792Sgshapiro int qdir; 249238032Speter bool doall; 249390792Sgshapiro bool *full; 249490792Sgshapiro bool *more; 2495203004Sgshapiro int *pnentries; 249638032Speter{ 249738032Speter register struct dirent *d; 249838032Speter register WORK *w; 249938032Speter register char *p; 250038032Speter DIR *f; 2501203004Sgshapiro int i, num_ent, wn, nentries; 250238032Speter QUEUE_CHAR *check; 250364562Sgshapiro char qd[MAXPATHLEN]; 250464562Sgshapiro char qf[MAXPATHLEN]; 250564562Sgshapiro 250690792Sgshapiro wn = WorkListCount - 1; 250790792Sgshapiro num_ent = 0; 2508203004Sgshapiro nentries = 0; 250990792Sgshapiro if (qdir == NOQDIR) 2510168515Sgshapiro (void) sm_strlcpy(qd, ".", sizeof(qd)); 251164562Sgshapiro else 2512168515Sgshapiro (void) sm_strlcpyn(qd, sizeof(qd), 2, 251390792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 251490792Sgshapiro (bitset(QP_SUBQF, 251590792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 251690792Sgshapiro ? "/qf" : "")); 251764562Sgshapiro 251838032Speter if (tTd(41, 1)) 251938032Speter { 252090792Sgshapiro sm_dprintf("gatherq:\n"); 252138032Speter 252238032Speter check = QueueLimitId; 252338032Speter while (check != NULL) 252438032Speter { 252590792Sgshapiro sm_dprintf("\tQueueLimitId = %s%s\n", 252690792Sgshapiro check->queue_negate ? "!" : "", 252764562Sgshapiro check->queue_match); 252838032Speter check = check->queue_next; 252938032Speter } 253038032Speter 253138032Speter check = QueueLimitSender; 253238032Speter while (check != NULL) 253338032Speter { 253490792Sgshapiro sm_dprintf("\tQueueLimitSender = %s%s\n", 253590792Sgshapiro check->queue_negate ? "!" : "", 253664562Sgshapiro check->queue_match); 253738032Speter check = check->queue_next; 253838032Speter } 253938032Speter 254038032Speter check = QueueLimitRecipient; 254138032Speter while (check != NULL) 254238032Speter { 254390792Sgshapiro sm_dprintf("\tQueueLimitRecipient = %s%s\n", 254490792Sgshapiro check->queue_negate ? "!" : "", 254564562Sgshapiro check->queue_match); 254638032Speter check = check->queue_next; 254738032Speter } 254838032Speter 254990792Sgshapiro if (QueueMode == QM_QUARANTINE) 255090792Sgshapiro { 255190792Sgshapiro check = QueueLimitQuarantine; 255290792Sgshapiro while (check != NULL) 255390792Sgshapiro { 255490792Sgshapiro sm_dprintf("\tQueueLimitQuarantine = %s%s\n", 255590792Sgshapiro check->queue_negate ? "!" : "", 255690792Sgshapiro check->queue_match); 255790792Sgshapiro check = check->queue_next; 255890792Sgshapiro } 255990792Sgshapiro } 256038032Speter } 256138032Speter 256238032Speter /* open the queue directory */ 256364562Sgshapiro f = opendir(qd); 256438032Speter if (f == NULL) 256538032Speter { 256690792Sgshapiro syserr("gatherq: cannot open \"%s\"", 256790792Sgshapiro qid_printqueue(qgrp, qdir)); 256890792Sgshapiro if (full != NULL) 256990792Sgshapiro *full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0; 257090792Sgshapiro if (more != NULL) 257190792Sgshapiro *more = false; 257264562Sgshapiro return 0; 257338032Speter } 257438032Speter 257538032Speter /* 257638032Speter ** Read the work directory. 257738032Speter */ 257838032Speter 257938032Speter while ((d = readdir(f)) != NULL) 258038032Speter { 258190792Sgshapiro SM_FILE_T *cf; 258238032Speter int qfver = 0; 258338032Speter char lbuf[MAXNAME + 1]; 258464562Sgshapiro struct stat sbuf; 258538032Speter 258638032Speter if (tTd(41, 50)) 258790792Sgshapiro sm_dprintf("gatherq: checking %s..", d->d_name); 258838032Speter 258938032Speter /* is this an interesting entry? */ 259090792Sgshapiro if (!(((QueueMode == QM_NORMAL && 259190792Sgshapiro d->d_name[0] == NORMQF_LETTER) || 259290792Sgshapiro (QueueMode == QM_QUARANTINE && 259390792Sgshapiro d->d_name[0] == QUARQF_LETTER) || 259490792Sgshapiro (QueueMode == QM_LOST && 259590792Sgshapiro d->d_name[0] == LOSEQF_LETTER)) && 259690792Sgshapiro d->d_name[1] == 'f')) 259790792Sgshapiro { 259890792Sgshapiro if (tTd(41, 50)) 259990792Sgshapiro sm_dprintf(" skipping\n"); 260038032Speter continue; 260190792Sgshapiro } 260290792Sgshapiro if (tTd(41, 50)) 260390792Sgshapiro sm_dprintf("\n"); 260438032Speter 260564562Sgshapiro if (strlen(d->d_name) >= MAXQFNAME) 260642575Speter { 260742575Speter if (Verbose) 260890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 260990792Sgshapiro "gatherq: %s too long, %d max characters\n", 261090792Sgshapiro d->d_name, MAXQFNAME); 261142575Speter if (LogLevel > 0) 261242575Speter sm_syslog(LOG_ALERT, NOQID, 261390792Sgshapiro "gatherq: %s too long, %d max characters", 261464562Sgshapiro d->d_name, MAXQFNAME); 261538032Speter continue; 261642575Speter } 261738032Speter 2618203004Sgshapiro ++nentries; 261938032Speter check = QueueLimitId; 262038032Speter while (check != NULL) 262138032Speter { 262294334Sgshapiro if (strcontainedin(false, check->queue_match, 262390792Sgshapiro d->d_name) != check->queue_negate) 262438032Speter break; 262538032Speter else 262638032Speter check = check->queue_next; 262738032Speter } 262838032Speter if (QueueLimitId != NULL && check == NULL) 262938032Speter continue; 263038032Speter 263164562Sgshapiro /* grow work list if necessary */ 263238032Speter if (++wn >= MaxQueueRun && MaxQueueRun > 0) 263338032Speter { 263438032Speter if (wn == MaxQueueRun && LogLevel > 0) 263564562Sgshapiro sm_syslog(LOG_WARNING, NOQID, 263664562Sgshapiro "WorkList for %s maxed out at %d", 263790792Sgshapiro qid_printqueue(qgrp, qdir), 263864562Sgshapiro MaxQueueRun); 263990792Sgshapiro if (doall) 264090792Sgshapiro continue; /* just count entries */ 264190792Sgshapiro break; 264238032Speter } 264338032Speter if (wn >= WorkListSize) 264438032Speter { 264590792Sgshapiro grow_wlist(qgrp, qdir); 264638032Speter if (wn >= WorkListSize) 264738032Speter continue; 264838032Speter } 264990792Sgshapiro SM_ASSERT(wn >= 0); 265064562Sgshapiro w = &WorkList[wn]; 265138032Speter 2652168515Sgshapiro (void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", d->d_name); 265364562Sgshapiro if (stat(qf, &sbuf) < 0) 265464562Sgshapiro { 265564562Sgshapiro if (errno != ENOENT) 265664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 265790792Sgshapiro "gatherq: can't stat %s/%s", 265890792Sgshapiro qid_printqueue(qgrp, qdir), 265990792Sgshapiro d->d_name); 266064562Sgshapiro wn--; 266164562Sgshapiro continue; 266264562Sgshapiro } 266364562Sgshapiro if (!bitset(S_IFREG, sbuf.st_mode)) 266464562Sgshapiro { 266564562Sgshapiro /* Yikes! Skip it or we will hang on open! */ 266690792Sgshapiro if (!((d->d_name[0] == DATAFL_LETTER || 266790792Sgshapiro d->d_name[0] == NORMQF_LETTER || 266890792Sgshapiro d->d_name[0] == QUARQF_LETTER || 266990792Sgshapiro d->d_name[0] == LOSEQF_LETTER || 267090792Sgshapiro d->d_name[0] == XSCRPT_LETTER) && 267190792Sgshapiro d->d_name[1] == 'f' && d->d_name[2] == '\0')) 267290792Sgshapiro syserr("gatherq: %s/%s is not a regular file", 267390792Sgshapiro qid_printqueue(qgrp, qdir), d->d_name); 267464562Sgshapiro wn--; 267564562Sgshapiro continue; 267664562Sgshapiro } 267764562Sgshapiro 267864562Sgshapiro /* avoid work if possible */ 267990792Sgshapiro if ((QueueSortOrder == QSO_BYFILENAME || 268090792Sgshapiro QueueSortOrder == QSO_BYMODTIME || 2681161389Sgshapiro QueueSortOrder == QSO_NONE || 268290792Sgshapiro QueueSortOrder == QSO_RANDOM) && 268390792Sgshapiro QueueLimitQuarantine == NULL && 268466494Sgshapiro QueueLimitSender == NULL && 268566494Sgshapiro QueueLimitRecipient == NULL) 268664562Sgshapiro { 268790792Sgshapiro w->w_qgrp = qgrp; 268890792Sgshapiro w->w_qdir = qdir; 268964562Sgshapiro w->w_name = newstr(d->d_name); 269064562Sgshapiro w->w_host = NULL; 269190792Sgshapiro w->w_lock = w->w_tooyoung = false; 269264562Sgshapiro w->w_pri = 0; 269364562Sgshapiro w->w_ctime = 0; 269490792Sgshapiro w->w_mtime = sbuf.st_mtime; 269590792Sgshapiro ++num_ent; 269664562Sgshapiro continue; 269764562Sgshapiro } 269864562Sgshapiro 269964562Sgshapiro /* open control file */ 2700120256Sgshapiro cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B, 270190792Sgshapiro NULL); 270290792Sgshapiro if (cf == NULL && OpMode != MD_PRINT) 270338032Speter { 270438032Speter /* this may be some random person sending hir msgs */ 270538032Speter if (tTd(41, 2)) 270690792Sgshapiro sm_dprintf("gatherq: cannot open %s: %s\n", 270790792Sgshapiro d->d_name, sm_errstring(errno)); 270838032Speter errno = 0; 270938032Speter wn--; 271038032Speter continue; 271138032Speter } 271290792Sgshapiro w->w_qgrp = qgrp; 271390792Sgshapiro w->w_qdir = qdir; 271438032Speter w->w_name = newstr(d->d_name); 271538032Speter w->w_host = NULL; 271690792Sgshapiro if (cf != NULL) 271790792Sgshapiro { 271890792Sgshapiro w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD, 271990792Sgshapiro NULL), 272090792Sgshapiro w->w_name, NULL, 272190792Sgshapiro LOCK_SH|LOCK_NB); 272290792Sgshapiro } 272390792Sgshapiro w->w_tooyoung = false; 272438032Speter 272538032Speter /* make sure jobs in creation don't clog queue */ 272638032Speter w->w_pri = 0x7fffffff; 272738032Speter w->w_ctime = 0; 272890792Sgshapiro w->w_mtime = sbuf.st_mtime; 272938032Speter 273038032Speter /* extract useful information */ 273190792Sgshapiro i = NEED_P|NEED_T; 273290792Sgshapiro if (QueueSortOrder == QSO_BYHOST 273390792Sgshapiro#if _FFR_RHS 273490792Sgshapiro || QueueSortOrder == QSO_BYSHUFFLE 273590792Sgshapiro#endif /* _FFR_RHS */ 273690792Sgshapiro ) 273771345Sgshapiro { 273871345Sgshapiro /* need w_host set for host sort order */ 273971345Sgshapiro i |= NEED_H; 274071345Sgshapiro } 274138032Speter if (QueueLimitSender != NULL) 274238032Speter i |= NEED_S; 274364562Sgshapiro if (QueueLimitRecipient != NULL) 274438032Speter i |= NEED_R; 274590792Sgshapiro if (QueueLimitQuarantine != NULL) 274690792Sgshapiro i |= NEED_QUARANTINE; 274790792Sgshapiro while (cf != NULL && i != 0 && 274890792Sgshapiro sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf, 2749249729Sgshapiro sizeof(lbuf)) >= 0) 275038032Speter { 275138032Speter int c; 275238032Speter time_t age; 275338032Speter 275438032Speter p = strchr(lbuf, '\n'); 275538032Speter if (p != NULL) 275638032Speter *p = '\0'; 275738032Speter else 275838032Speter { 275938032Speter /* flush rest of overly long line */ 276090792Sgshapiro while ((c = sm_io_getc(cf, SM_TIME_DEFAULT)) 276190792Sgshapiro != SM_IO_EOF && c != '\n') 276238032Speter continue; 276338032Speter } 276438032Speter 276538032Speter switch (lbuf[0]) 276638032Speter { 276738032Speter case 'V': 276838032Speter qfver = atoi(&lbuf[1]); 276938032Speter break; 277038032Speter 277138032Speter case 'P': 277238032Speter w->w_pri = atol(&lbuf[1]); 277338032Speter i &= ~NEED_P; 277438032Speter break; 277538032Speter 277638032Speter case 'T': 277738032Speter w->w_ctime = atol(&lbuf[1]); 277838032Speter i &= ~NEED_T; 277938032Speter break; 278038032Speter 278190792Sgshapiro case 'q': 278290792Sgshapiro if (QueueMode != QM_QUARANTINE && 278390792Sgshapiro QueueMode != QM_LOST) 278490792Sgshapiro { 278590792Sgshapiro if (tTd(41, 49)) 278690792Sgshapiro sm_dprintf("%s not marked as quarantined but has a 'q' line\n", 278790792Sgshapiro w->w_name); 278890792Sgshapiro i |= HAS_QUARANTINE; 278990792Sgshapiro } 279090792Sgshapiro else if (QueueMode == QM_QUARANTINE) 279190792Sgshapiro { 279290792Sgshapiro if (QueueLimitQuarantine == NULL) 279390792Sgshapiro { 279490792Sgshapiro i &= ~NEED_QUARANTINE; 279590792Sgshapiro break; 279690792Sgshapiro } 279790792Sgshapiro p = &lbuf[1]; 279890792Sgshapiro check = QueueLimitQuarantine; 279990792Sgshapiro while (check != NULL) 280090792Sgshapiro { 280190792Sgshapiro if (strcontainedin(false, 280290792Sgshapiro check->queue_match, 280390792Sgshapiro p) != 280490792Sgshapiro check->queue_negate) 280590792Sgshapiro break; 280690792Sgshapiro else 280790792Sgshapiro check = check->queue_next; 280890792Sgshapiro } 280990792Sgshapiro if (check != NULL) 281090792Sgshapiro i &= ~NEED_QUARANTINE; 281190792Sgshapiro } 281290792Sgshapiro break; 281390792Sgshapiro 281438032Speter case 'R': 281538032Speter if (w->w_host == NULL && 281638032Speter (p = strrchr(&lbuf[1], '@')) != NULL) 281764562Sgshapiro { 281890792Sgshapiro#if _FFR_RHS 281990792Sgshapiro if (QueueSortOrder == QSO_BYSHUFFLE) 282090792Sgshapiro w->w_host = newstr(&p[1]); 282190792Sgshapiro else 282290792Sgshapiro#endif /* _FFR_RHS */ 282390792Sgshapiro w->w_host = strrev(&p[1]); 282464562Sgshapiro makelower(w->w_host); 282571345Sgshapiro i &= ~NEED_H; 282664562Sgshapiro } 282738032Speter if (QueueLimitRecipient == NULL) 282838032Speter { 282938032Speter i &= ~NEED_R; 283038032Speter break; 283138032Speter } 283238032Speter if (qfver > 0) 283338032Speter { 283438032Speter p = strchr(&lbuf[1], ':'); 283538032Speter if (p == NULL) 283638032Speter p = &lbuf[1]; 2837120256Sgshapiro else 2838120256Sgshapiro ++p; /* skip over ':' */ 283938032Speter } 284038032Speter else 284138032Speter p = &lbuf[1]; 284238032Speter check = QueueLimitRecipient; 284338032Speter while (check != NULL) 284438032Speter { 284590792Sgshapiro if (strcontainedin(true, 284690792Sgshapiro check->queue_match, 284790792Sgshapiro p) != 284890792Sgshapiro check->queue_negate) 284938032Speter break; 285038032Speter else 285138032Speter check = check->queue_next; 285238032Speter } 285338032Speter if (check != NULL) 285438032Speter i &= ~NEED_R; 285538032Speter break; 285638032Speter 285738032Speter case 'S': 285864562Sgshapiro check = QueueLimitSender; 285964562Sgshapiro while (check != NULL) 286064562Sgshapiro { 286190792Sgshapiro if (strcontainedin(true, 286290792Sgshapiro check->queue_match, 286390792Sgshapiro &lbuf[1]) != 286490792Sgshapiro check->queue_negate) 286564562Sgshapiro break; 286664562Sgshapiro else 286764562Sgshapiro check = check->queue_next; 286864562Sgshapiro } 286964562Sgshapiro if (check != NULL) 287064562Sgshapiro i &= ~NEED_S; 287138032Speter break; 287238032Speter 287338032Speter case 'K': 2874203004Sgshapiro#if _FFR_EXPDELAY 2875203004Sgshapiro if (MaxQueueAge > 0) 2876203004Sgshapiro { 2877244833Sgshapiro time_t lasttry, delay; 2878203004Sgshapiro 2879203004Sgshapiro lasttry = (time_t) atol(&lbuf[1]); 2880203004Sgshapiro delay = MIN(lasttry - w->w_ctime, 2881203004Sgshapiro MaxQueueAge); 2882203004Sgshapiro age = curtime() - lasttry; 2883203004Sgshapiro if (age < delay) 2884203004Sgshapiro w->w_tooyoung = true; 2885203004Sgshapiro break; 2886203004Sgshapiro } 2887203004Sgshapiro#endif /* _FFR_EXPDELAY */ 2888203004Sgshapiro 288938032Speter age = curtime() - (time_t) atol(&lbuf[1]); 289038032Speter if (age >= 0 && MinQueueAge > 0 && 289138032Speter age < MinQueueAge) 289290792Sgshapiro w->w_tooyoung = true; 289338032Speter break; 289438032Speter 289538032Speter case 'N': 289638032Speter if (atol(&lbuf[1]) == 0) 289790792Sgshapiro w->w_tooyoung = false; 289838032Speter break; 289938032Speter } 290038032Speter } 290190792Sgshapiro if (cf != NULL) 290290792Sgshapiro (void) sm_io_close(cf, SM_TIME_DEFAULT); 290338032Speter 2904157001Sgshapiro if ((!doall && (shouldqueue(w->w_pri, w->w_ctime) || 2905157001Sgshapiro w->w_tooyoung)) || 290690792Sgshapiro bitset(HAS_QUARANTINE, i) || 290790792Sgshapiro bitset(NEED_QUARANTINE, i) || 290838032Speter bitset(NEED_R|NEED_S, i)) 290938032Speter { 291038032Speter /* don't even bother sorting this job in */ 291138032Speter if (tTd(41, 49)) 291290792Sgshapiro sm_dprintf("skipping %s (%x)\n", w->w_name, i); 291390792Sgshapiro sm_free(w->w_name); /* XXX */ 291490792Sgshapiro if (w->w_host != NULL) 291590792Sgshapiro sm_free(w->w_host); /* XXX */ 291638032Speter wn--; 291738032Speter } 291890792Sgshapiro else 291990792Sgshapiro ++num_ent; 292038032Speter } 292138032Speter (void) closedir(f); 292238032Speter wn++; 292338032Speter 292490792Sgshapiro i = wn - WorkListCount; 292590792Sgshapiro WorkListCount += SM_MIN(num_ent, WorkListSize); 292690792Sgshapiro 292790792Sgshapiro if (more != NULL) 292890792Sgshapiro *more = WorkListCount < wn; 292990792Sgshapiro 293090792Sgshapiro if (full != NULL) 293190792Sgshapiro *full = (wn >= MaxQueueRun && MaxQueueRun > 0) || 293290792Sgshapiro (WorkList == NULL && wn > 0); 293390792Sgshapiro 2934203004Sgshapiro if (pnentries != NULL) 2935203004Sgshapiro *pnentries = nentries; 293690792Sgshapiro return i; 293790792Sgshapiro} 293890792Sgshapiro/* 293990792Sgshapiro** SORTQ -- sort the work list 294090792Sgshapiro** 294190792Sgshapiro** First the old WorkQ is cleared away. Then the WorkList is sorted 294290792Sgshapiro** for all items so that important (higher sorting value) items are not 2943261194Sgshapiro** truncated off. Then the most important items are moved from 294490792Sgshapiro** WorkList to WorkQ. The lower count of 'max' or MaxListCount items 294590792Sgshapiro** are moved. 294690792Sgshapiro** 294790792Sgshapiro** Parameters: 294890792Sgshapiro** max -- maximum number of items to be placed in WorkQ 294990792Sgshapiro** 295090792Sgshapiro** Returns: 295190792Sgshapiro** the number of items in WorkQ 295290792Sgshapiro** 295390792Sgshapiro** Side Effects: 295490792Sgshapiro** WorkQ gets released and filled with new work. WorkList 295590792Sgshapiro** gets released. Work items get sorted in order. 295690792Sgshapiro*/ 295790792Sgshapiro 295890792Sgshapirostatic int 295990792Sgshapirosortq(max) 296090792Sgshapiro int max; 296190792Sgshapiro{ 296290792Sgshapiro register int i; /* local counter */ 296390792Sgshapiro register WORK *w; /* tmp item pointer */ 296490792Sgshapiro int wc = WorkListCount; /* trim size for WorkQ */ 296590792Sgshapiro 296690792Sgshapiro if (WorkQ != NULL) 296790792Sgshapiro { 2968132943Sgshapiro WORK *nw; 2969132943Sgshapiro 297090792Sgshapiro /* Clear out old WorkQ. */ 2971132943Sgshapiro for (w = WorkQ; w != NULL; w = nw) 297290792Sgshapiro { 2973132943Sgshapiro nw = w->w_next; 297490792Sgshapiro sm_free(w->w_name); /* XXX */ 297590792Sgshapiro if (w->w_host != NULL) 297690792Sgshapiro sm_free(w->w_host); /* XXX */ 297790792Sgshapiro sm_free((char *) w); /* XXX */ 297890792Sgshapiro } 297990792Sgshapiro WorkQ = NULL; 298090792Sgshapiro } 298190792Sgshapiro 298290792Sgshapiro if (WorkList == NULL || wc <= 0) 298364562Sgshapiro return 0; 298438032Speter 298590792Sgshapiro /* 298690792Sgshapiro ** The sort now takes place using all of the items in WorkList. 298790792Sgshapiro ** The list gets trimmed to the most important items after the sort. 298890792Sgshapiro ** If the trim were to happen before the sort then one or more 298990792Sgshapiro ** important items might get truncated off -- not what we want. 299090792Sgshapiro */ 299190792Sgshapiro 299264562Sgshapiro if (QueueSortOrder == QSO_BYHOST) 299338032Speter { 299438032Speter /* 299538032Speter ** Sort the work directory for the first time, 299638032Speter ** based on host name, lock status, and priority. 299738032Speter */ 299838032Speter 2999168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf1); 300038032Speter 300138032Speter /* 300238032Speter ** If one message to host is locked, "lock" all messages 300338032Speter ** to that host. 300438032Speter */ 300538032Speter 300638032Speter i = 0; 300738032Speter while (i < wc) 300838032Speter { 300938032Speter if (!WorkList[i].w_lock) 301038032Speter { 301138032Speter i++; 301238032Speter continue; 301338032Speter } 301438032Speter w = &WorkList[i]; 301538032Speter while (++i < wc) 301638032Speter { 301738032Speter if (WorkList[i].w_host == NULL && 301838032Speter w->w_host == NULL) 301990792Sgshapiro WorkList[i].w_lock = true; 302038032Speter else if (WorkList[i].w_host != NULL && 302138032Speter w->w_host != NULL && 302290792Sgshapiro sm_strcasecmp(WorkList[i].w_host, 302390792Sgshapiro w->w_host) == 0) 302490792Sgshapiro WorkList[i].w_lock = true; 302538032Speter else 302638032Speter break; 302738032Speter } 302838032Speter } 302938032Speter 303038032Speter /* 303138032Speter ** Sort the work directory for the second time, 303238032Speter ** based on lock status, host name, and priority. 303338032Speter */ 303438032Speter 3035168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf2); 303638032Speter } 303764562Sgshapiro else if (QueueSortOrder == QSO_BYTIME) 303838032Speter { 303938032Speter /* 304038032Speter ** Simple sort based on submission time only. 304138032Speter */ 304238032Speter 3043168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf3); 304438032Speter } 304564562Sgshapiro else if (QueueSortOrder == QSO_BYFILENAME) 304664562Sgshapiro { 304764562Sgshapiro /* 304890792Sgshapiro ** Sort based on queue filename. 304964562Sgshapiro */ 305064562Sgshapiro 3051168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf4); 305264562Sgshapiro } 305390792Sgshapiro else if (QueueSortOrder == QSO_RANDOM) 305490792Sgshapiro { 305590792Sgshapiro /* 3056110560Sgshapiro ** Sort randomly. To avoid problems with an instable sort, 3057110560Sgshapiro ** use a random index into the queue file name to start 3058110560Sgshapiro ** comparison. 305990792Sgshapiro */ 306090792Sgshapiro 3061110560Sgshapiro randi = get_rand_mod(MAXQFNAME); 3062110560Sgshapiro if (randi < 2) 3063110560Sgshapiro randi = 3; 3064168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf5); 306590792Sgshapiro } 306690792Sgshapiro else if (QueueSortOrder == QSO_BYMODTIME) 306790792Sgshapiro { 306890792Sgshapiro /* 306990792Sgshapiro ** Simple sort based on modification time of queue file. 307090792Sgshapiro ** This puts the oldest items first. 307190792Sgshapiro */ 307290792Sgshapiro 3073168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf6); 307490792Sgshapiro } 307590792Sgshapiro#if _FFR_RHS 307690792Sgshapiro else if (QueueSortOrder == QSO_BYSHUFFLE) 307790792Sgshapiro { 307890792Sgshapiro /* 307990792Sgshapiro ** Simple sort based on shuffled host name. 308090792Sgshapiro */ 308190792Sgshapiro 308290792Sgshapiro init_shuffle_alphabet(); 3083168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf7); 308490792Sgshapiro } 308590792Sgshapiro#endif /* _FFR_RHS */ 3086132943Sgshapiro else if (QueueSortOrder == QSO_BYPRIORITY) 308738032Speter { 308838032Speter /* 308938032Speter ** Simple sort based on queue priority only. 309038032Speter */ 309138032Speter 3092168515Sgshapiro qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf0); 309338032Speter } 3094132943Sgshapiro /* else don't sort at all */ 309538032Speter 3096157001Sgshapiro /* Check if the per queue group item limit will be exceeded */ 3097157001Sgshapiro if (wc > max && max > 0) 3098157001Sgshapiro wc = max; 3099157001Sgshapiro 310038032Speter /* 310138032Speter ** Convert the work list into canonical form. 310238032Speter ** Should be turning it into a list of envelopes here perhaps. 310390792Sgshapiro ** Only take the most important items up to the per queue group 310490792Sgshapiro ** maximum. 310538032Speter */ 310638032Speter 310738032Speter for (i = wc; --i >= 0; ) 310838032Speter { 3109168515Sgshapiro w = (WORK *) xalloc(sizeof(*w)); 311090792Sgshapiro w->w_qgrp = WorkList[i].w_qgrp; 311190792Sgshapiro w->w_qdir = WorkList[i].w_qdir; 311238032Speter w->w_name = WorkList[i].w_name; 311338032Speter w->w_host = WorkList[i].w_host; 311438032Speter w->w_lock = WorkList[i].w_lock; 311538032Speter w->w_tooyoung = WorkList[i].w_tooyoung; 311638032Speter w->w_pri = WorkList[i].w_pri; 311738032Speter w->w_ctime = WorkList[i].w_ctime; 311890792Sgshapiro w->w_mtime = WorkList[i].w_mtime; 311938032Speter w->w_next = WorkQ; 312038032Speter WorkQ = w; 312138032Speter } 3122132943Sgshapiro 3123132943Sgshapiro /* free the rest of the list */ 3124132943Sgshapiro for (i = WorkListCount; --i >= wc; ) 3125132943Sgshapiro { 3126132943Sgshapiro sm_free(WorkList[i].w_name); 3127132943Sgshapiro if (WorkList[i].w_host != NULL) 3128132943Sgshapiro sm_free(WorkList[i].w_host); 3129132943Sgshapiro } 3130132943Sgshapiro 313138032Speter if (WorkList != NULL) 313290792Sgshapiro sm_free(WorkList); /* XXX */ 313338032Speter WorkList = NULL; 313438032Speter WorkListSize = 0; 313590792Sgshapiro WorkListCount = 0; 313638032Speter 313738032Speter if (tTd(40, 1)) 313838032Speter { 313938032Speter for (w = WorkQ; w != NULL; w = w->w_next) 314064562Sgshapiro { 314164562Sgshapiro if (w->w_host != NULL) 314290792Sgshapiro sm_dprintf("%22s: pri=%ld %s\n", 314364562Sgshapiro w->w_name, w->w_pri, w->w_host); 314464562Sgshapiro else 314590792Sgshapiro sm_dprintf("%32s: pri=%ld\n", 314664562Sgshapiro w->w_name, w->w_pri); 314764562Sgshapiro } 314838032Speter } 314938032Speter 315090792Sgshapiro return wc; /* return number of WorkQ items */ 315138032Speter} 315290792Sgshapiro/* 315338032Speter** GROW_WLIST -- make the work list larger 315438032Speter** 315538032Speter** Parameters: 315690792Sgshapiro** qgrp -- the index for the queue group. 315790792Sgshapiro** qdir -- the index for the queue directory. 315838032Speter** 315938032Speter** Returns: 316038032Speter** none. 316138032Speter** 316238032Speter** Side Effects: 316338032Speter** Adds another QUEUESEGSIZE entries to WorkList if possible. 316438032Speter** It can fail if there isn't enough memory, so WorkListSize 316538032Speter** should be checked again upon return. 316638032Speter*/ 316738032Speter 316864562Sgshapirostatic void 316990792Sgshapirogrow_wlist(qgrp, qdir) 317090792Sgshapiro int qgrp; 317190792Sgshapiro int qdir; 317238032Speter{ 317338032Speter if (tTd(41, 1)) 317490792Sgshapiro sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize); 317538032Speter if (WorkList == NULL) 317638032Speter { 3177168515Sgshapiro WorkList = (WORK *) xalloc((sizeof(*WorkList)) * 317864562Sgshapiro (QUEUESEGSIZE + 1)); 317938032Speter WorkListSize = QUEUESEGSIZE; 318038032Speter } 318138032Speter else 318238032Speter { 318338032Speter int newsize = WorkListSize + QUEUESEGSIZE; 318490792Sgshapiro WORK *newlist = (WORK *) sm_realloc((char *) WorkList, 318590792Sgshapiro (unsigned) sizeof(WORK) * (newsize + 1)); 318638032Speter 318738032Speter if (newlist != NULL) 318838032Speter { 318938032Speter WorkListSize = newsize; 319038032Speter WorkList = newlist; 319138032Speter if (LogLevel > 1) 319238032Speter { 319364562Sgshapiro sm_syslog(LOG_INFO, NOQID, 319464562Sgshapiro "grew WorkList for %s to %d", 319590792Sgshapiro qid_printqueue(qgrp, qdir), 319664562Sgshapiro WorkListSize); 319738032Speter } 319838032Speter } 319938032Speter else if (LogLevel > 0) 320038032Speter { 320138032Speter sm_syslog(LOG_ALERT, NOQID, 320264562Sgshapiro "FAILED to grow WorkList for %s to %d", 320390792Sgshapiro qid_printqueue(qgrp, qdir), newsize); 320438032Speter } 320538032Speter } 320638032Speter if (tTd(41, 1)) 320790792Sgshapiro sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize); 320838032Speter} 320990792Sgshapiro/* 321038032Speter** WORKCMPF0 -- simple priority-only compare function. 321138032Speter** 321238032Speter** Parameters: 321338032Speter** a -- the first argument. 321438032Speter** b -- the second argument. 321538032Speter** 321638032Speter** Returns: 321738032Speter** -1 if a < b 321838032Speter** 0 if a == b 321938032Speter** +1 if a > b 322038032Speter** 322138032Speter*/ 322238032Speter 322364562Sgshapirostatic int 322438032Speterworkcmpf0(a, b) 322538032Speter register WORK *a; 322638032Speter register WORK *b; 322738032Speter{ 322838032Speter long pa = a->w_pri; 322938032Speter long pb = b->w_pri; 323038032Speter 323138032Speter if (pa == pb) 323238032Speter return 0; 323338032Speter else if (pa > pb) 323438032Speter return 1; 323538032Speter else 323638032Speter return -1; 323738032Speter} 323890792Sgshapiro/* 323938032Speter** WORKCMPF1 -- first compare function for ordering work based on host name. 324038032Speter** 324138032Speter** Sorts on host name, lock status, and priority in that order. 324238032Speter** 324338032Speter** Parameters: 324438032Speter** a -- the first argument. 324538032Speter** b -- the second argument. 324638032Speter** 324738032Speter** Returns: 324838032Speter** <0 if a < b 324938032Speter** 0 if a == b 325038032Speter** >0 if a > b 325138032Speter** 325238032Speter*/ 325338032Speter 325464562Sgshapirostatic int 325538032Speterworkcmpf1(a, b) 325638032Speter register WORK *a; 325738032Speter register WORK *b; 325838032Speter{ 325938032Speter int i; 326038032Speter 326138032Speter /* host name */ 326238032Speter if (a->w_host != NULL && b->w_host == NULL) 326338032Speter return 1; 326438032Speter else if (a->w_host == NULL && b->w_host != NULL) 326538032Speter return -1; 326638032Speter if (a->w_host != NULL && b->w_host != NULL && 326738032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 326838032Speter return i; 326938032Speter 327038032Speter /* lock status */ 327138032Speter if (a->w_lock != b->w_lock) 327238032Speter return b->w_lock - a->w_lock; 327338032Speter 327438032Speter /* job priority */ 327573188Sgshapiro return workcmpf0(a, b); 327638032Speter} 327790792Sgshapiro/* 327838032Speter** WORKCMPF2 -- second compare function for ordering work based on host name. 327938032Speter** 328038032Speter** Sorts on lock status, host name, and priority in that order. 328138032Speter** 328238032Speter** Parameters: 328338032Speter** a -- the first argument. 328438032Speter** b -- the second argument. 328538032Speter** 328638032Speter** Returns: 328738032Speter** <0 if a < b 328838032Speter** 0 if a == b 328938032Speter** >0 if a > b 329038032Speter** 329138032Speter*/ 329238032Speter 329364562Sgshapirostatic int 329438032Speterworkcmpf2(a, b) 329538032Speter register WORK *a; 329638032Speter register WORK *b; 329738032Speter{ 329838032Speter int i; 329938032Speter 330038032Speter /* lock status */ 330138032Speter if (a->w_lock != b->w_lock) 330238032Speter return a->w_lock - b->w_lock; 330338032Speter 330438032Speter /* host name */ 330538032Speter if (a->w_host != NULL && b->w_host == NULL) 330638032Speter return 1; 330738032Speter else if (a->w_host == NULL && b->w_host != NULL) 330838032Speter return -1; 330938032Speter if (a->w_host != NULL && b->w_host != NULL && 331038032Speter (i = sm_strcasecmp(a->w_host, b->w_host)) != 0) 331138032Speter return i; 331238032Speter 331338032Speter /* job priority */ 331473188Sgshapiro return workcmpf0(a, b); 331538032Speter} 331690792Sgshapiro/* 331738032Speter** WORKCMPF3 -- simple submission-time-only compare function. 331838032Speter** 331938032Speter** Parameters: 332038032Speter** a -- the first argument. 332138032Speter** b -- the second argument. 332238032Speter** 332338032Speter** Returns: 332438032Speter** -1 if a < b 332538032Speter** 0 if a == b 332638032Speter** +1 if a > b 332738032Speter** 332838032Speter*/ 332938032Speter 333064562Sgshapirostatic int 333138032Speterworkcmpf3(a, b) 333238032Speter register WORK *a; 333338032Speter register WORK *b; 333438032Speter{ 333538032Speter if (a->w_ctime > b->w_ctime) 333638032Speter return 1; 333738032Speter else if (a->w_ctime < b->w_ctime) 333838032Speter return -1; 333938032Speter else 334038032Speter return 0; 334138032Speter} 334290792Sgshapiro/* 334364562Sgshapiro** WORKCMPF4 -- compare based on file name 334464562Sgshapiro** 334564562Sgshapiro** Parameters: 334664562Sgshapiro** a -- the first argument. 334764562Sgshapiro** b -- the second argument. 334864562Sgshapiro** 334964562Sgshapiro** Returns: 335064562Sgshapiro** -1 if a < b 335164562Sgshapiro** 0 if a == b 335264562Sgshapiro** +1 if a > b 335364562Sgshapiro** 335464562Sgshapiro*/ 335564562Sgshapiro 335664562Sgshapirostatic int 335764562Sgshapiroworkcmpf4(a, b) 335864562Sgshapiro register WORK *a; 335964562Sgshapiro register WORK *b; 336064562Sgshapiro{ 336164562Sgshapiro return strcmp(a->w_name, b->w_name); 336264562Sgshapiro} 336390792Sgshapiro/* 336490792Sgshapiro** WORKCMPF5 -- compare based on assigned random number 336590792Sgshapiro** 336690792Sgshapiro** Parameters: 3367203004Sgshapiro** a -- the first argument. 3368203004Sgshapiro** b -- the second argument. 336990792Sgshapiro** 337090792Sgshapiro** Returns: 337190792Sgshapiro** randomly 1/-1 337290792Sgshapiro*/ 337390792Sgshapiro 337490792Sgshapiro/* ARGSUSED0 */ 337590792Sgshapirostatic int 337690792Sgshapiroworkcmpf5(a, b) 337790792Sgshapiro register WORK *a; 337890792Sgshapiro register WORK *b; 337990792Sgshapiro{ 3380110560Sgshapiro if (strlen(a->w_name) < randi || strlen(b->w_name) < randi) 3381110560Sgshapiro return -1; 3382110560Sgshapiro return a->w_name[randi] - b->w_name[randi]; 338390792Sgshapiro} 338490792Sgshapiro/* 338590792Sgshapiro** WORKCMPF6 -- simple modification-time-only compare function. 338690792Sgshapiro** 338790792Sgshapiro** Parameters: 338890792Sgshapiro** a -- the first argument. 338990792Sgshapiro** b -- the second argument. 339090792Sgshapiro** 339190792Sgshapiro** Returns: 339290792Sgshapiro** -1 if a < b 339390792Sgshapiro** 0 if a == b 339490792Sgshapiro** +1 if a > b 339590792Sgshapiro** 339690792Sgshapiro*/ 339790792Sgshapiro 339890792Sgshapirostatic int 339990792Sgshapiroworkcmpf6(a, b) 340090792Sgshapiro register WORK *a; 340190792Sgshapiro register WORK *b; 340290792Sgshapiro{ 340390792Sgshapiro if (a->w_mtime > b->w_mtime) 340490792Sgshapiro return 1; 340590792Sgshapiro else if (a->w_mtime < b->w_mtime) 340690792Sgshapiro return -1; 340790792Sgshapiro else 340890792Sgshapiro return 0; 340990792Sgshapiro} 341090792Sgshapiro#if _FFR_RHS 341190792Sgshapiro/* 341290792Sgshapiro** WORKCMPF7 -- compare function for ordering work based on shuffled host name. 341390792Sgshapiro** 341490792Sgshapiro** Sorts on lock status, host name, and priority in that order. 341590792Sgshapiro** 341690792Sgshapiro** Parameters: 341790792Sgshapiro** a -- the first argument. 341890792Sgshapiro** b -- the second argument. 341990792Sgshapiro** 342090792Sgshapiro** Returns: 342190792Sgshapiro** <0 if a < b 342290792Sgshapiro** 0 if a == b 342390792Sgshapiro** >0 if a > b 342490792Sgshapiro** 342590792Sgshapiro*/ 342690792Sgshapiro 342790792Sgshapirostatic int 342890792Sgshapiroworkcmpf7(a, b) 342990792Sgshapiro register WORK *a; 343090792Sgshapiro register WORK *b; 343190792Sgshapiro{ 343290792Sgshapiro int i; 343390792Sgshapiro 343490792Sgshapiro /* lock status */ 343590792Sgshapiro if (a->w_lock != b->w_lock) 343690792Sgshapiro return a->w_lock - b->w_lock; 343790792Sgshapiro 343890792Sgshapiro /* host name */ 343990792Sgshapiro if (a->w_host != NULL && b->w_host == NULL) 344090792Sgshapiro return 1; 344190792Sgshapiro else if (a->w_host == NULL && b->w_host != NULL) 344290792Sgshapiro return -1; 344390792Sgshapiro if (a->w_host != NULL && b->w_host != NULL && 344490792Sgshapiro (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0) 344590792Sgshapiro return i; 344690792Sgshapiro 344790792Sgshapiro /* job priority */ 344890792Sgshapiro return workcmpf0(a, b); 344990792Sgshapiro} 345090792Sgshapiro#endif /* _FFR_RHS */ 345190792Sgshapiro/* 345264562Sgshapiro** STRREV -- reverse string 345364562Sgshapiro** 345464562Sgshapiro** Returns a pointer to a new string that is the reverse of 345564562Sgshapiro** the string pointed to by fwd. The space for the new 345664562Sgshapiro** string is obtained using xalloc(). 345764562Sgshapiro** 345864562Sgshapiro** Parameters: 345964562Sgshapiro** fwd -- the string to reverse. 346064562Sgshapiro** 346164562Sgshapiro** Returns: 346264562Sgshapiro** the reversed string. 346364562Sgshapiro*/ 346464562Sgshapiro 346564562Sgshapirostatic char * 346664562Sgshapirostrrev(fwd) 346764562Sgshapiro char *fwd; 346864562Sgshapiro{ 346964562Sgshapiro char *rev = NULL; 347064562Sgshapiro int len, cnt; 347164562Sgshapiro 347264562Sgshapiro len = strlen(fwd); 347364562Sgshapiro rev = xalloc(len + 1); 347464562Sgshapiro for (cnt = 0; cnt < len; ++cnt) 347564562Sgshapiro rev[cnt] = fwd[len - cnt - 1]; 347664562Sgshapiro rev[len] = '\0'; 347764562Sgshapiro return rev; 347864562Sgshapiro} 347990792Sgshapiro 348090792Sgshapiro#if _FFR_RHS 348190792Sgshapiro 3482112810Sgshapiro# define NASCII 128 3483112810Sgshapiro# define NCHAR 256 348490792Sgshapiro 348590792Sgshapirostatic unsigned char ShuffledAlphabet[NCHAR]; 348690792Sgshapiro 348790792Sgshapirovoid 348890792Sgshapiroinit_shuffle_alphabet() 348990792Sgshapiro{ 349090792Sgshapiro static bool init = false; 349190792Sgshapiro int i; 349290792Sgshapiro 349390792Sgshapiro if (init) 349490792Sgshapiro return; 349590792Sgshapiro 349690792Sgshapiro /* fill the ShuffledAlphabet */ 3497157001Sgshapiro for (i = 0; i < NASCII; i++) 349890792Sgshapiro ShuffledAlphabet[i] = i; 349990792Sgshapiro 350090792Sgshapiro /* mix it */ 3501157001Sgshapiro for (i = 1; i < NASCII; i++) 350290792Sgshapiro { 3503157001Sgshapiro register int j = get_random() % NASCII; 350490792Sgshapiro register int tmp; 350590792Sgshapiro 350690792Sgshapiro tmp = ShuffledAlphabet[j]; 350790792Sgshapiro ShuffledAlphabet[j] = ShuffledAlphabet[i]; 350890792Sgshapiro ShuffledAlphabet[i] = tmp; 350990792Sgshapiro } 351090792Sgshapiro 351190792Sgshapiro /* make it case insensitive */ 351290792Sgshapiro for (i = 'A'; i <= 'Z'; i++) 351390792Sgshapiro ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A']; 351490792Sgshapiro 351590792Sgshapiro /* fill the upper part */ 3516157001Sgshapiro for (i = 0; i < NASCII; i++) 3517157001Sgshapiro ShuffledAlphabet[i + NASCII] = ShuffledAlphabet[i]; 351890792Sgshapiro init = true; 351990792Sgshapiro} 352090792Sgshapiro 352190792Sgshapirostatic int 352290792Sgshapirosm_strshufflecmp(a, b) 352390792Sgshapiro char *a; 352490792Sgshapiro char *b; 352590792Sgshapiro{ 352690792Sgshapiro const unsigned char *us1 = (const unsigned char *) a; 352790792Sgshapiro const unsigned char *us2 = (const unsigned char *) b; 352890792Sgshapiro 352990792Sgshapiro while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++]) 353090792Sgshapiro { 353190792Sgshapiro if (*us1++ == '\0') 353290792Sgshapiro return 0; 353390792Sgshapiro } 353490792Sgshapiro return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]); 353590792Sgshapiro} 353690792Sgshapiro#endif /* _FFR_RHS */ 353790792Sgshapiro 353890792Sgshapiro/* 353938032Speter** DOWORK -- do a work request. 354038032Speter** 354138032Speter** Parameters: 354290792Sgshapiro** qgrp -- the index of the queue group for the job. 354390792Sgshapiro** qdir -- the index of the queue directory for the job. 354438032Speter** id -- the ID of the job to run. 354538032Speter** forkflag -- if set, run this in background. 354638032Speter** requeueflag -- if set, reinstantiate the queue quickly. 354738032Speter** This is used when expanding aliases in the queue. 354838032Speter** If forkflag is also set, it doesn't wait for the 354938032Speter** child. 355038032Speter** e - the envelope in which to run it. 355138032Speter** 355238032Speter** Returns: 355338032Speter** process id of process that is running the queue job. 355438032Speter** 355538032Speter** Side Effects: 355638032Speter** The work request is satisfied if possible. 355738032Speter*/ 355838032Speter 355938032Speterpid_t 356090792Sgshapirodowork(qgrp, qdir, id, forkflag, requeueflag, e) 356190792Sgshapiro int qgrp; 356290792Sgshapiro int qdir; 356338032Speter char *id; 356438032Speter bool forkflag; 356538032Speter bool requeueflag; 356638032Speter register ENVELOPE *e; 356738032Speter{ 356838032Speter register pid_t pid; 356990792Sgshapiro SM_RPOOL_T *rpool; 357038032Speter 357138032Speter if (tTd(40, 1)) 357290792Sgshapiro sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id); 357338032Speter 357438032Speter /* 357538032Speter ** Fork for work. 357638032Speter */ 357738032Speter 357838032Speter if (forkflag) 357938032Speter { 358064562Sgshapiro /* 358164562Sgshapiro ** Since the delivery may happen in a child and the 358264562Sgshapiro ** parent does not wait, the parent may close the 358364562Sgshapiro ** maps thereby removing any shared memory used by 358464562Sgshapiro ** the map. Therefore, close the maps now so the 358564562Sgshapiro ** child will dynamically open them if necessary. 358664562Sgshapiro */ 358764562Sgshapiro 358890792Sgshapiro closemaps(false); 358964562Sgshapiro 359038032Speter pid = fork(); 359138032Speter if (pid < 0) 359238032Speter { 359338032Speter syserr("dowork: cannot fork"); 359438032Speter return 0; 359538032Speter } 359638032Speter else if (pid > 0) 359738032Speter { 359838032Speter /* parent -- clean out connection cache */ 359990792Sgshapiro mci_flush(false, NULL); 360038032Speter } 360138032Speter else 360238032Speter { 360390792Sgshapiro /* 360490792Sgshapiro ** Initialize exception stack and default exception 360590792Sgshapiro ** handler for child process. 360690792Sgshapiro */ 360790792Sgshapiro 360890792Sgshapiro /* Reset global flags */ 360990792Sgshapiro RestartRequest = NULL; 361090792Sgshapiro RestartWorkGroup = false; 361190792Sgshapiro ShutdownRequest = NULL; 361290792Sgshapiro PendingSignal = 0; 361390792Sgshapiro CurrentPid = getpid(); 361490792Sgshapiro sm_exc_newthread(fatal_error); 361590792Sgshapiro 361690792Sgshapiro /* 361790792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 361890792Sgshapiro */ 361990792Sgshapiro 362090792Sgshapiro if (OpMode == MD_SMTP || 362190792Sgshapiro OpMode == MD_DAEMON || 362290792Sgshapiro MaxQueueChildren > 0) 362390792Sgshapiro { 362490792Sgshapiro proc_list_clear(); 362590792Sgshapiro sm_releasesignal(SIGCHLD); 362690792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 362790792Sgshapiro } 362890792Sgshapiro 362938032Speter /* child -- error messages to the transcript */ 363090792Sgshapiro QuickAbort = OnlyOneError = false; 363138032Speter } 363238032Speter } 363338032Speter else 363438032Speter { 363538032Speter pid = 0; 363638032Speter } 363738032Speter 363838032Speter if (pid == 0) 363938032Speter { 364038032Speter /* 364138032Speter ** CHILD 364238032Speter ** Lock the control file to avoid duplicate deliveries. 364338032Speter ** Then run the file as though we had just read it. 364438032Speter ** We save an idea of the temporary name so we 364538032Speter ** can recover on interrupt. 364638032Speter */ 364738032Speter 364890792Sgshapiro if (forkflag) 364990792Sgshapiro { 365090792Sgshapiro /* Reset global flags */ 365190792Sgshapiro RestartRequest = NULL; 365290792Sgshapiro RestartWorkGroup = false; 365390792Sgshapiro ShutdownRequest = NULL; 365490792Sgshapiro PendingSignal = 0; 365590792Sgshapiro } 365677349Sgshapiro 365738032Speter /* set basic modes, etc. */ 365890792Sgshapiro sm_clear_events(); 365964562Sgshapiro clearstats(); 366090792Sgshapiro rpool = sm_rpool_new_x(NULL); 366190792Sgshapiro clearenvelope(e, false, rpool); 366238032Speter e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 366364562Sgshapiro set_delivery_mode(SM_DELIVER, e); 366438032Speter e->e_errormode = EM_MAIL; 366538032Speter e->e_id = id; 366690792Sgshapiro e->e_qgrp = qgrp; 366790792Sgshapiro e->e_qdir = qdir; 366890792Sgshapiro GrabTo = UseErrorsTo = false; 366938032Speter ExitStat = EX_OK; 367038032Speter if (forkflag) 367138032Speter { 367238032Speter disconnect(1, e); 367390792Sgshapiro set_op_mode(MD_QUEUERUN); 367438032Speter } 367590792Sgshapiro sm_setproctitle(true, e, "%s from queue", qid_printname(e)); 367638032Speter if (LogLevel > 76) 367790792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d", 367890792Sgshapiro (int) CurrentPid); 367938032Speter 368038032Speter /* don't use the headers from sendmail.cf... */ 368138032Speter e->e_header = NULL; 368238032Speter 368338032Speter /* read the queue control file -- return if locked */ 368490792Sgshapiro if (!readqf(e, false)) 368538032Speter { 368638032Speter if (tTd(40, 4) && e->e_id != NULL) 368790792Sgshapiro sm_dprintf("readqf(%s) failed\n", 368864562Sgshapiro qid_printname(e)); 368938032Speter e->e_id = NULL; 369038032Speter if (forkflag) 369190792Sgshapiro finis(false, true, EX_OK); 369238032Speter else 369390792Sgshapiro { 369490792Sgshapiro /* adding this frees 8 bytes */ 369590792Sgshapiro clearenvelope(e, false, rpool); 369690792Sgshapiro 369790792Sgshapiro /* adding this frees 12 bytes */ 369890792Sgshapiro sm_rpool_free(rpool); 369990792Sgshapiro e->e_rpool = NULL; 370038032Speter return 0; 370190792Sgshapiro } 370238032Speter } 370338032Speter 370438032Speter e->e_flags |= EF_INQUEUE; 370590792Sgshapiro eatheader(e, requeueflag, true); 370638032Speter 370738032Speter if (requeueflag) 370890792Sgshapiro queueup(e, false, false); 370938032Speter 371038032Speter /* do the delivery */ 371138032Speter sendall(e, SM_DELIVER); 371238032Speter 371338032Speter /* finish up and exit */ 371438032Speter if (forkflag) 371590792Sgshapiro finis(true, true, ExitStat); 371638032Speter else 371790792Sgshapiro { 3718203004Sgshapiro (void) dropenvelope(e, true, false); 371990792Sgshapiro sm_rpool_free(rpool); 372090792Sgshapiro e->e_rpool = NULL; 3721244833Sgshapiro e->e_message = NULL; 372290792Sgshapiro } 372338032Speter } 372438032Speter e->e_id = NULL; 372538032Speter return pid; 372638032Speter} 372790792Sgshapiro 372890792Sgshapiro/* 372990792Sgshapiro** DOWORKLIST -- process a list of envelopes as work requests 373090792Sgshapiro** 373190792Sgshapiro** Similar to dowork(), except that after forking, it processes an 373290792Sgshapiro** envelope and its siblings, treating each envelope as a work request. 373390792Sgshapiro** 373490792Sgshapiro** Parameters: 373590792Sgshapiro** el -- envelope to be processed including its siblings. 373690792Sgshapiro** forkflag -- if set, run this in background. 373790792Sgshapiro** requeueflag -- if set, reinstantiate the queue quickly. 373890792Sgshapiro** This is used when expanding aliases in the queue. 373990792Sgshapiro** If forkflag is also set, it doesn't wait for the 374090792Sgshapiro** child. 374190792Sgshapiro** 374290792Sgshapiro** Returns: 374390792Sgshapiro** process id of process that is running the queue job. 374490792Sgshapiro** 374590792Sgshapiro** Side Effects: 374690792Sgshapiro** The work request is satisfied if possible. 374790792Sgshapiro*/ 374890792Sgshapiro 374990792Sgshapiropid_t 375090792Sgshapirodoworklist(el, forkflag, requeueflag) 375190792Sgshapiro ENVELOPE *el; 375290792Sgshapiro bool forkflag; 375390792Sgshapiro bool requeueflag; 375490792Sgshapiro{ 375590792Sgshapiro register pid_t pid; 375690792Sgshapiro ENVELOPE *ei; 375790792Sgshapiro 375890792Sgshapiro if (tTd(40, 1)) 375990792Sgshapiro sm_dprintf("doworklist()\n"); 376090792Sgshapiro 376190792Sgshapiro /* 376290792Sgshapiro ** Fork for work. 376390792Sgshapiro */ 376490792Sgshapiro 376590792Sgshapiro if (forkflag) 376690792Sgshapiro { 376790792Sgshapiro /* 376890792Sgshapiro ** Since the delivery may happen in a child and the 376990792Sgshapiro ** parent does not wait, the parent may close the 377090792Sgshapiro ** maps thereby removing any shared memory used by 377190792Sgshapiro ** the map. Therefore, close the maps now so the 377290792Sgshapiro ** child will dynamically open them if necessary. 377390792Sgshapiro */ 377490792Sgshapiro 377590792Sgshapiro closemaps(false); 377690792Sgshapiro 377790792Sgshapiro pid = fork(); 377890792Sgshapiro if (pid < 0) 377990792Sgshapiro { 378090792Sgshapiro syserr("doworklist: cannot fork"); 378190792Sgshapiro return 0; 378290792Sgshapiro } 378390792Sgshapiro else if (pid > 0) 378490792Sgshapiro { 378590792Sgshapiro /* parent -- clean out connection cache */ 378690792Sgshapiro mci_flush(false, NULL); 378790792Sgshapiro } 378890792Sgshapiro else 378990792Sgshapiro { 379090792Sgshapiro /* 379190792Sgshapiro ** Initialize exception stack and default exception 379290792Sgshapiro ** handler for child process. 379390792Sgshapiro */ 379490792Sgshapiro 379590792Sgshapiro /* Reset global flags */ 379690792Sgshapiro RestartRequest = NULL; 379790792Sgshapiro RestartWorkGroup = false; 379890792Sgshapiro ShutdownRequest = NULL; 379990792Sgshapiro PendingSignal = 0; 380090792Sgshapiro CurrentPid = getpid(); 380190792Sgshapiro sm_exc_newthread(fatal_error); 380290792Sgshapiro 380390792Sgshapiro /* 380490792Sgshapiro ** See note above about SMTP processes and SIGCHLD. 380590792Sgshapiro */ 380690792Sgshapiro 380790792Sgshapiro if (OpMode == MD_SMTP || 380890792Sgshapiro OpMode == MD_DAEMON || 380990792Sgshapiro MaxQueueChildren > 0) 381090792Sgshapiro { 381190792Sgshapiro proc_list_clear(); 381290792Sgshapiro sm_releasesignal(SIGCHLD); 381390792Sgshapiro (void) sm_signal(SIGCHLD, SIG_DFL); 381490792Sgshapiro } 381590792Sgshapiro 381690792Sgshapiro /* child -- error messages to the transcript */ 381790792Sgshapiro QuickAbort = OnlyOneError = false; 381890792Sgshapiro } 381990792Sgshapiro } 382090792Sgshapiro else 382190792Sgshapiro { 382290792Sgshapiro pid = 0; 382390792Sgshapiro } 382490792Sgshapiro 382590792Sgshapiro if (pid != 0) 382690792Sgshapiro return pid; 382790792Sgshapiro 382890792Sgshapiro /* 382990792Sgshapiro ** IN CHILD 383090792Sgshapiro ** Lock the control file to avoid duplicate deliveries. 383190792Sgshapiro ** Then run the file as though we had just read it. 383290792Sgshapiro ** We save an idea of the temporary name so we 383390792Sgshapiro ** can recover on interrupt. 383490792Sgshapiro */ 383590792Sgshapiro 383690792Sgshapiro if (forkflag) 383790792Sgshapiro { 383890792Sgshapiro /* Reset global flags */ 383990792Sgshapiro RestartRequest = NULL; 384090792Sgshapiro RestartWorkGroup = false; 384190792Sgshapiro ShutdownRequest = NULL; 384290792Sgshapiro PendingSignal = 0; 384390792Sgshapiro } 384490792Sgshapiro 384590792Sgshapiro /* set basic modes, etc. */ 384690792Sgshapiro sm_clear_events(); 384790792Sgshapiro clearstats(); 384890792Sgshapiro GrabTo = UseErrorsTo = false; 384990792Sgshapiro ExitStat = EX_OK; 385090792Sgshapiro if (forkflag) 385190792Sgshapiro { 385290792Sgshapiro disconnect(1, el); 385390792Sgshapiro set_op_mode(MD_QUEUERUN); 385490792Sgshapiro } 385590792Sgshapiro if (LogLevel > 76) 385690792Sgshapiro sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d", 385790792Sgshapiro (int) CurrentPid); 385890792Sgshapiro 385990792Sgshapiro for (ei = el; ei != NULL; ei = ei->e_sibling) 386090792Sgshapiro { 386190792Sgshapiro ENVELOPE e; 386290792Sgshapiro SM_RPOOL_T *rpool; 386390792Sgshapiro 386490792Sgshapiro if (WILL_BE_QUEUED(ei->e_sendmode)) 386590792Sgshapiro continue; 386690792Sgshapiro else if (QueueMode != QM_QUARANTINE && 386790792Sgshapiro ei->e_quarmsg != NULL) 386890792Sgshapiro continue; 386990792Sgshapiro 387090792Sgshapiro rpool = sm_rpool_new_x(NULL); 387190792Sgshapiro clearenvelope(&e, true, rpool); 387290792Sgshapiro e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS; 387390792Sgshapiro set_delivery_mode(SM_DELIVER, &e); 387490792Sgshapiro e.e_errormode = EM_MAIL; 387590792Sgshapiro e.e_id = ei->e_id; 387690792Sgshapiro e.e_qgrp = ei->e_qgrp; 387790792Sgshapiro e.e_qdir = ei->e_qdir; 387890792Sgshapiro openxscript(&e); 387990792Sgshapiro sm_setproctitle(true, &e, "%s from queue", qid_printname(&e)); 388090792Sgshapiro 388190792Sgshapiro /* don't use the headers from sendmail.cf... */ 388290792Sgshapiro e.e_header = NULL; 388390792Sgshapiro CurEnv = &e; 388490792Sgshapiro 388590792Sgshapiro /* read the queue control file -- return if locked */ 388690792Sgshapiro if (readqf(&e, false)) 388790792Sgshapiro { 388890792Sgshapiro e.e_flags |= EF_INQUEUE; 388990792Sgshapiro eatheader(&e, requeueflag, true); 389090792Sgshapiro 389190792Sgshapiro if (requeueflag) 389290792Sgshapiro queueup(&e, false, false); 389390792Sgshapiro 389490792Sgshapiro /* do the delivery */ 389590792Sgshapiro sendall(&e, SM_DELIVER); 3896203004Sgshapiro (void) dropenvelope(&e, true, false); 389790792Sgshapiro } 389890792Sgshapiro else 389990792Sgshapiro { 390090792Sgshapiro if (tTd(40, 4) && e.e_id != NULL) 390190792Sgshapiro sm_dprintf("readqf(%s) failed\n", 390290792Sgshapiro qid_printname(&e)); 390390792Sgshapiro } 390490792Sgshapiro sm_rpool_free(rpool); 390590792Sgshapiro ei->e_id = NULL; 390690792Sgshapiro } 390790792Sgshapiro 390890792Sgshapiro /* restore CurEnv */ 390990792Sgshapiro CurEnv = el; 391090792Sgshapiro 391190792Sgshapiro /* finish up and exit */ 391290792Sgshapiro if (forkflag) 391390792Sgshapiro finis(true, true, ExitStat); 391490792Sgshapiro return 0; 391590792Sgshapiro} 391690792Sgshapiro/* 391738032Speter** READQF -- read queue file and set up environment. 391838032Speter** 391938032Speter** Parameters: 392038032Speter** e -- the envelope of the job to run. 392190792Sgshapiro** openonly -- only open the qf (returned as e_lockfp) 392238032Speter** 392338032Speter** Returns: 392490792Sgshapiro** true if it successfully read the queue file. 392590792Sgshapiro** false otherwise. 392638032Speter** 392738032Speter** Side Effects: 392838032Speter** The queue file is returned locked. 392938032Speter*/ 393038032Speter 393164562Sgshapirostatic bool 393290792Sgshapiroreadqf(e, openonly) 393338032Speter register ENVELOPE *e; 393490792Sgshapiro bool openonly; 393538032Speter{ 393690792Sgshapiro register SM_FILE_T *qfp; 393738032Speter ADDRESS *ctladdr; 393877349Sgshapiro struct stat st, stf; 393938032Speter char *bp; 394038032Speter int qfver = 0; 394138032Speter long hdrsize = 0; 394238032Speter register char *p; 394390792Sgshapiro char *frcpt = NULL; 394438032Speter char *orcpt = NULL; 394590792Sgshapiro bool nomore = false; 394690792Sgshapiro bool bogus = false; 394764562Sgshapiro MODE_T qsafe; 394894334Sgshapiro char *err; 394964562Sgshapiro char qf[MAXPATHLEN]; 395038032Speter char buf[MAXLINE]; 3951168515Sgshapiro int bufsize; 395238032Speter 395338032Speter /* 395438032Speter ** Read and process the file. 395538032Speter */ 395638032Speter 3957168515Sgshapiro SM_REQUIRE(e != NULL); 3958159609Sgshapiro bp = NULL; 3959168515Sgshapiro (void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof(qf)); 3960120256Sgshapiro qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR_B, NULL); 396138032Speter if (qfp == NULL) 396238032Speter { 396364562Sgshapiro int save_errno = errno; 396464562Sgshapiro 396538032Speter if (tTd(40, 8)) 396690792Sgshapiro sm_dprintf("readqf(%s): sm_io_open failure (%s)\n", 396790792Sgshapiro qf, sm_errstring(errno)); 396864562Sgshapiro errno = save_errno; 396964562Sgshapiro if (errno != ENOENT 397064562Sgshapiro ) 397138032Speter syserr("readqf: no control file %s", qf); 397290792Sgshapiro RELEASE_QUEUE; 397390792Sgshapiro return false; 397438032Speter } 397538032Speter 397690792Sgshapiro if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL, 397790792Sgshapiro LOCK_EX|LOCK_NB)) 397838032Speter { 397938032Speter /* being processed by another queuer */ 398064562Sgshapiro if (Verbose) 398190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 398290792Sgshapiro "%s: locked\n", e->e_id); 398364562Sgshapiro if (tTd(40, 8)) 398490792Sgshapiro sm_dprintf("%s: locked\n", e->e_id); 398538032Speter if (LogLevel > 19) 398638032Speter sm_syslog(LOG_DEBUG, e->e_id, "locked"); 398790792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 398890792Sgshapiro RELEASE_QUEUE; 398990792Sgshapiro return false; 399038032Speter } 399138032Speter 3992120256Sgshapiro RELEASE_QUEUE; 3993120256Sgshapiro 399438032Speter /* 399577349Sgshapiro ** Prevent locking race condition. 399677349Sgshapiro ** 399777349Sgshapiro ** Process A: readqf(): qfp = fopen(qffile) 399877349Sgshapiro ** Process B: queueup(): rename(tf, qf) 399977349Sgshapiro ** Process B: unlocks(tf) 400077349Sgshapiro ** Process A: lockfile(qf); 400177349Sgshapiro ** 400277349Sgshapiro ** Process A (us) has the old qf file (before the rename deleted 400377349Sgshapiro ** the directory entry) and will be delivering based on old data. 400477349Sgshapiro ** This can lead to multiple deliveries of the same recipients. 400577349Sgshapiro ** 400677349Sgshapiro ** Catch this by checking if the underlying qf file has changed 400777349Sgshapiro ** *after* acquiring our lock and if so, act as though the file 400877349Sgshapiro ** was still locked (i.e., just return like the lockfile() case 400977349Sgshapiro ** above. 401038032Speter */ 401138032Speter 401277349Sgshapiro if (stat(qf, &stf) < 0 || 401390792Sgshapiro fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0) 401438032Speter { 401538032Speter /* must have been being processed by someone else */ 401638032Speter if (tTd(40, 8)) 401790792Sgshapiro sm_dprintf("readqf(%s): [f]stat failure (%s)\n", 401890792Sgshapiro qf, sm_errstring(errno)); 401990792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 402090792Sgshapiro return false; 402138032Speter } 402238032Speter 402377349Sgshapiro if (st.st_nlink != stf.st_nlink || 402477349Sgshapiro st.st_dev != stf.st_dev || 402590792Sgshapiro ST_INODE(st) != ST_INODE(stf) || 402690792Sgshapiro#if HAS_ST_GEN && 0 /* AFS returns garbage in st_gen */ 402777349Sgshapiro st.st_gen != stf.st_gen || 402890792Sgshapiro#endif /* HAS_ST_GEN && 0 */ 402977349Sgshapiro st.st_uid != stf.st_uid || 403077349Sgshapiro st.st_gid != stf.st_gid || 403177349Sgshapiro st.st_size != stf.st_size) 403277349Sgshapiro { 403377349Sgshapiro /* changed after opened */ 403477349Sgshapiro if (Verbose) 403590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 403690792Sgshapiro "%s: changed\n", e->e_id); 403777349Sgshapiro if (tTd(40, 8)) 403890792Sgshapiro sm_dprintf("%s: changed\n", e->e_id); 403977349Sgshapiro if (LogLevel > 19) 404077349Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "changed"); 404190792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 404290792Sgshapiro return false; 404377349Sgshapiro } 404477349Sgshapiro 404577349Sgshapiro /* 404677349Sgshapiro ** Check the queue file for plausibility to avoid attacks. 404777349Sgshapiro */ 404877349Sgshapiro 404964562Sgshapiro qsafe = S_IWOTH|S_IWGRP; 405064562Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 405164562Sgshapiro qsafe &= ~S_IWGRP; 405264562Sgshapiro 405390792Sgshapiro bogus = st.st_uid != geteuid() && 405490792Sgshapiro st.st_uid != TrustedUid && 405590792Sgshapiro geteuid() != RealUid; 405690792Sgshapiro 405790792Sgshapiro /* 405890792Sgshapiro ** If this qf file results from a set-group-ID binary, then 405990792Sgshapiro ** we check whether the directory is group-writable, 406090792Sgshapiro ** the queue file mode contains the group-writable bit, and 406190792Sgshapiro ** the groups are the same. 406290792Sgshapiro ** Notice: this requires that the set-group-ID binary is used to 406390792Sgshapiro ** run the queue! 406490792Sgshapiro */ 406590792Sgshapiro 406690792Sgshapiro if (bogus && st.st_gid == getegid() && UseMSP) 406738032Speter { 406890792Sgshapiro char delim; 406990792Sgshapiro struct stat dst; 407090792Sgshapiro 407190792Sgshapiro bp = SM_LAST_DIR_DELIM(qf); 407290792Sgshapiro if (bp == NULL) 407390792Sgshapiro delim = '\0'; 407490792Sgshapiro else 407590792Sgshapiro { 407690792Sgshapiro delim = *bp; 407790792Sgshapiro *bp = '\0'; 407890792Sgshapiro } 407990792Sgshapiro if (stat(delim == '\0' ? "." : qf, &dst) < 0) 408090792Sgshapiro syserr("readqf: cannot stat directory %s", 408190792Sgshapiro delim == '\0' ? "." : qf); 408290792Sgshapiro else 408390792Sgshapiro { 408490792Sgshapiro bogus = !(bitset(S_IWGRP, QueueFileMode) && 408590792Sgshapiro bitset(S_IWGRP, dst.st_mode) && 408690792Sgshapiro dst.st_gid == st.st_gid); 408790792Sgshapiro } 408890792Sgshapiro if (delim != '\0') 408990792Sgshapiro *bp = delim; 4090159609Sgshapiro bp = NULL; 409190792Sgshapiro } 409290792Sgshapiro if (!bogus) 409390792Sgshapiro bogus = bitset(qsafe, st.st_mode); 409490792Sgshapiro if (bogus) 409590792Sgshapiro { 409638032Speter if (LogLevel > 0) 409738032Speter { 409838032Speter sm_syslog(LOG_ALERT, e->e_id, 409990792Sgshapiro "bogus queue file, uid=%d, gid=%d, mode=%o", 410090792Sgshapiro st.st_uid, st.st_gid, st.st_mode); 410138032Speter } 410238032Speter if (tTd(40, 8)) 410390792Sgshapiro sm_dprintf("readqf(%s): bogus file\n", qf); 410490792Sgshapiro e->e_flags |= EF_INQUEUE; 410590792Sgshapiro if (!openonly) 410690792Sgshapiro loseqfile(e, "bogus file uid/gid in mqueue"); 410790792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 410890792Sgshapiro return false; 410938032Speter } 411038032Speter 411138032Speter if (st.st_size == 0) 411238032Speter { 411338032Speter /* must be a bogus file -- if also old, just remove it */ 411490792Sgshapiro if (!openonly && st.st_ctime + 10 * 60 < curtime()) 411538032Speter { 411690792Sgshapiro (void) xunlink(queuename(e, DATAFL_LETTER)); 411790792Sgshapiro (void) xunlink(queuename(e, ANYQFL_LETTER)); 411838032Speter } 411990792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 412090792Sgshapiro return false; 412138032Speter } 412238032Speter 412338032Speter if (st.st_nlink == 0) 412438032Speter { 412538032Speter /* 412638032Speter ** Race condition -- we got a file just as it was being 412738032Speter ** unlinked. Just assume it is zero length. 412838032Speter */ 412938032Speter 413090792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 413190792Sgshapiro return false; 413238032Speter } 413338032Speter 413490792Sgshapiro#if _FFR_TRUSTED_QF 413590792Sgshapiro /* 413690792Sgshapiro ** If we don't own the file mark it as unsafe. 413790792Sgshapiro ** However, allow TrustedUser to own it as well 413890792Sgshapiro ** in case TrustedUser manipulates the queue. 413990792Sgshapiro */ 414090792Sgshapiro 414190792Sgshapiro if (st.st_uid != geteuid() && st.st_uid != TrustedUid) 414290792Sgshapiro e->e_flags |= EF_UNSAFE; 414390792Sgshapiro#else /* _FFR_TRUSTED_QF */ 414490792Sgshapiro /* If we don't own the file mark it as unsafe */ 414590792Sgshapiro if (st.st_uid != geteuid()) 414690792Sgshapiro e->e_flags |= EF_UNSAFE; 414790792Sgshapiro#endif /* _FFR_TRUSTED_QF */ 414890792Sgshapiro 414938032Speter /* good file -- save this lock */ 415038032Speter e->e_lockfp = qfp; 415138032Speter 415290792Sgshapiro /* Just wanted the open file */ 415390792Sgshapiro if (openonly) 415490792Sgshapiro return true; 415590792Sgshapiro 415638032Speter /* do basic system initialization */ 415738032Speter initsys(e); 415890792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 415938032Speter 416038032Speter LineNumber = 0; 416138032Speter e->e_flags |= EF_GLOBALERRS; 416290792Sgshapiro set_op_mode(MD_QUEUERUN); 416338032Speter ctladdr = NULL; 416490792Sgshapiro e->e_qfletter = queue_letter(e, ANYQFL_LETTER); 416590792Sgshapiro e->e_dfqgrp = e->e_qgrp; 416690792Sgshapiro e->e_dfqdir = e->e_qdir; 416790792Sgshapiro#if _FFR_QUEUE_MACRO 416890792Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{queue}"), 416990792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 417090792Sgshapiro#endif /* _FFR_QUEUE_MACRO */ 417138032Speter e->e_dfino = -1; 417238032Speter e->e_msgsize = -1; 4173168515Sgshapiro while (bufsize = sizeof(buf), 4174168515Sgshapiro (bp = fgetfolded(buf, &bufsize, qfp)) != NULL) 417538032Speter { 417690792Sgshapiro unsigned long qflags; 417738032Speter ADDRESS *q; 417890792Sgshapiro int r; 417971345Sgshapiro time_t now; 418038032Speter auto char *ep; 418138032Speter 418238032Speter if (tTd(40, 4)) 418390792Sgshapiro sm_dprintf("+++++ %s\n", bp); 418438032Speter if (nomore) 418538032Speter { 418638032Speter /* hack attack */ 418790792Sgshapiro hackattack: 418890792Sgshapiro syserr("SECURITY ALERT: extra or bogus data in queue file: %s", 418990792Sgshapiro bp); 419094334Sgshapiro err = "bogus queue line"; 419194334Sgshapiro goto fail; 419238032Speter } 419338032Speter switch (bp[0]) 419438032Speter { 419590792Sgshapiro case 'A': /* AUTH= parameter */ 419690792Sgshapiro if (!xtextok(&bp[1])) 419790792Sgshapiro goto hackattack; 419890792Sgshapiro e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 419990792Sgshapiro break; 420038032Speter 420190792Sgshapiro case 'B': /* body type */ 420290792Sgshapiro r = check_bodytype(&bp[1]); 420390792Sgshapiro if (!BODYTYPE_VALID(r)) 420490792Sgshapiro goto hackattack; 420590792Sgshapiro e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 420690792Sgshapiro break; 420790792Sgshapiro 420838032Speter case 'C': /* specify controlling user */ 420990792Sgshapiro ctladdr = setctluser(&bp[1], qfver, e); 421038032Speter break; 421138032Speter 421290792Sgshapiro case 'D': /* data file name */ 421390792Sgshapiro /* obsolete -- ignore */ 421438032Speter break; 421538032Speter 421690792Sgshapiro case 'd': /* data file directory name */ 421738032Speter { 421890792Sgshapiro int qgrp, qdir; 421990792Sgshapiro 422090792Sgshapiro#if _FFR_MSP_PARANOIA 422190792Sgshapiro /* forbid queue groups in MSP? */ 422290792Sgshapiro if (UseMSP) 422390792Sgshapiro goto hackattack; 422490792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 422590792Sgshapiro for (qgrp = 0; 422690792Sgshapiro qgrp < NumQueue && Queue[qgrp] != NULL; 422790792Sgshapiro ++qgrp) 422838032Speter { 422990792Sgshapiro for (qdir = 0; 423090792Sgshapiro qdir < Queue[qgrp]->qg_numqueues; 423190792Sgshapiro ++qdir) 423238032Speter { 423390792Sgshapiro if (strcmp(&bp[1], 423490792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name) 423590792Sgshapiro == 0) 423690792Sgshapiro { 423790792Sgshapiro e->e_dfqgrp = qgrp; 423890792Sgshapiro e->e_dfqdir = qdir; 423990792Sgshapiro goto done; 424090792Sgshapiro } 424138032Speter } 424238032Speter } 424394334Sgshapiro err = "bogus queue file directory"; 424494334Sgshapiro goto fail; 424590792Sgshapiro done: 424690792Sgshapiro break; 424738032Speter } 424838032Speter 424938032Speter case 'E': /* specify error recipient */ 425038032Speter /* no longer used */ 425138032Speter break; 425238032Speter 425390792Sgshapiro case 'F': /* flag bits */ 425490792Sgshapiro if (strncmp(bp, "From ", 5) == 0) 425590792Sgshapiro { 425690792Sgshapiro /* we are being spoofed! */ 425790792Sgshapiro syserr("SECURITY ALERT: bogus qf line %s", bp); 425894334Sgshapiro err = "bogus queue line"; 425994334Sgshapiro goto fail; 426090792Sgshapiro } 426190792Sgshapiro for (p = &bp[1]; *p != '\0'; p++) 426290792Sgshapiro { 426390792Sgshapiro switch (*p) 426490792Sgshapiro { 426590792Sgshapiro case '8': /* has 8 bit data */ 426690792Sgshapiro e->e_flags |= EF_HAS8BIT; 426790792Sgshapiro break; 426838032Speter 426990792Sgshapiro case 'b': /* delete Bcc: header */ 427090792Sgshapiro e->e_flags |= EF_DELETE_BCC; 427190792Sgshapiro break; 427238032Speter 427390792Sgshapiro case 'd': /* envelope has DSN RET= */ 427490792Sgshapiro e->e_flags |= EF_RET_PARAM; 427590792Sgshapiro break; 427638032Speter 427790792Sgshapiro case 'n': /* don't return body */ 427890792Sgshapiro e->e_flags |= EF_NO_BODY_RETN; 427990792Sgshapiro break; 428090792Sgshapiro 428190792Sgshapiro case 'r': /* response */ 428290792Sgshapiro e->e_flags |= EF_RESPONSE; 428390792Sgshapiro break; 428490792Sgshapiro 428590792Sgshapiro case 's': /* split */ 428690792Sgshapiro e->e_flags |= EF_SPLIT; 428790792Sgshapiro break; 428890792Sgshapiro 428990792Sgshapiro case 'w': /* warning sent */ 429090792Sgshapiro e->e_flags |= EF_WARNING; 429190792Sgshapiro break; 429290792Sgshapiro } 429390792Sgshapiro } 429438032Speter break; 429538032Speter 429690792Sgshapiro case 'q': /* quarantine reason */ 429790792Sgshapiro e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 429890792Sgshapiro macdefine(&e->e_macro, A_PERM, 429990792Sgshapiro macid("{quarantine}"), e->e_quarmsg); 430038032Speter break; 430138032Speter 430290792Sgshapiro case 'H': /* header */ 430394334Sgshapiro 430494334Sgshapiro /* 430594334Sgshapiro ** count size before chompheader() destroys the line. 430694334Sgshapiro ** this isn't accurate due to macro expansion, but 4307120256Sgshapiro ** better than before. "-3" to skip H?? at least. 430894334Sgshapiro */ 430994334Sgshapiro 4310120256Sgshapiro hdrsize += strlen(bp) - 3; 431190792Sgshapiro (void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e); 431238032Speter break; 431338032Speter 431438032Speter case 'I': /* data file's inode number */ 431538032Speter /* regenerated below */ 431638032Speter break; 431738032Speter 431880785Sgshapiro case 'K': /* time of last delivery attempt */ 431938032Speter e->e_dtime = atol(&buf[1]); 432038032Speter break; 432138032Speter 432290792Sgshapiro case 'L': /* Solaris Content-Length: */ 432390792Sgshapiro case 'M': /* message */ 432490792Sgshapiro /* ignore this; we want a new message next time */ 432564562Sgshapiro break; 432664562Sgshapiro 432738032Speter case 'N': /* number of delivery attempts */ 432838032Speter e->e_ntries = atoi(&buf[1]); 432938032Speter 433038032Speter /* if this has been tried recently, let it be */ 433171345Sgshapiro now = curtime(); 433271345Sgshapiro if (e->e_ntries > 0 && e->e_dtime <= now && 4333132943Sgshapiro now < e->e_dtime + MinQueueAge) 433438032Speter { 433564562Sgshapiro char *howlong; 433638032Speter 433790792Sgshapiro howlong = pintvl(now - e->e_dtime, true); 433864562Sgshapiro if (Verbose) 433990792Sgshapiro (void) sm_io_fprintf(smioout, 434090792Sgshapiro SM_TIME_DEFAULT, 434190792Sgshapiro "%s: too young (%s)\n", 434290792Sgshapiro e->e_id, howlong); 434364562Sgshapiro if (tTd(40, 8)) 434490792Sgshapiro sm_dprintf("%s: too young (%s)\n", 434538032Speter e->e_id, howlong); 434638032Speter if (LogLevel > 19) 434738032Speter sm_syslog(LOG_DEBUG, e->e_id, 434864562Sgshapiro "too young (%s)", 434964562Sgshapiro howlong); 435038032Speter e->e_id = NULL; 435138032Speter unlockqueue(e); 4352168515Sgshapiro if (bp != buf) 4353168515Sgshapiro sm_free(bp); 435490792Sgshapiro return false; 435538032Speter } 435690792Sgshapiro macdefine(&e->e_macro, A_TEMP, 435790792Sgshapiro macid("{ntries}"), &buf[1]); 435864562Sgshapiro 435990792Sgshapiro#if NAMED_BIND 436064562Sgshapiro /* adjust BIND parameters immediately */ 436164562Sgshapiro if (e->e_ntries == 0) 436264562Sgshapiro { 436364562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_FIRST]; 436464562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST]; 436564562Sgshapiro } 436664562Sgshapiro else 436764562Sgshapiro { 436864562Sgshapiro _res.retry = TimeOuts.res_retry[RES_TO_NORMAL]; 436964562Sgshapiro _res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL]; 437064562Sgshapiro } 437190792Sgshapiro#endif /* NAMED_BIND */ 437238032Speter break; 437338032Speter 437438032Speter case 'P': /* message priority */ 437538032Speter e->e_msgpriority = atol(&bp[1]) + WkTimeFact; 437638032Speter break; 437738032Speter 437890792Sgshapiro case 'Q': /* original recipient */ 437990792Sgshapiro orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 438090792Sgshapiro break; 438190792Sgshapiro 438298841Sgshapiro case 'r': /* final recipient */ 438390792Sgshapiro frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 438490792Sgshapiro break; 438590792Sgshapiro 438690792Sgshapiro case 'R': /* specify recipient */ 438790792Sgshapiro p = bp; 438890792Sgshapiro qflags = 0; 438990792Sgshapiro if (qfver >= 1) 439038032Speter { 439190792Sgshapiro /* get flag bits */ 439290792Sgshapiro while (*++p != '\0' && *p != ':') 439338032Speter { 439490792Sgshapiro switch (*p) 439590792Sgshapiro { 439690792Sgshapiro case 'N': 439790792Sgshapiro qflags |= QHASNOTIFY; 439890792Sgshapiro break; 439938032Speter 440090792Sgshapiro case 'S': 440190792Sgshapiro qflags |= QPINGONSUCCESS; 440290792Sgshapiro break; 440338032Speter 440490792Sgshapiro case 'F': 440590792Sgshapiro qflags |= QPINGONFAILURE; 440690792Sgshapiro break; 440738032Speter 440890792Sgshapiro case 'D': 440990792Sgshapiro qflags |= QPINGONDELAY; 441090792Sgshapiro break; 441138032Speter 441290792Sgshapiro case 'P': 441390792Sgshapiro qflags |= QPRIMARY; 441490792Sgshapiro break; 441538032Speter 441690792Sgshapiro case 'A': 441790792Sgshapiro if (ctladdr != NULL) 441890792Sgshapiro ctladdr->q_flags |= QALIAS; 441990792Sgshapiro break; 442090792Sgshapiro 442190792Sgshapiro default: /* ignore or complain? */ 442290792Sgshapiro break; 442390792Sgshapiro } 442438032Speter } 442538032Speter } 442690792Sgshapiro else 442790792Sgshapiro qflags |= QPRIMARY; 4428120256Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), 4429120256Sgshapiro "e r"); 4430120256Sgshapiro if (*p != '\0') 4431120256Sgshapiro q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', 4432120256Sgshapiro NULL, e, true); 4433120256Sgshapiro else 4434120256Sgshapiro q = NULL; 443590792Sgshapiro if (q != NULL) 443690792Sgshapiro { 443794334Sgshapiro /* make sure we keep the current qgrp */ 443894334Sgshapiro if (ISVALIDQGRP(e->e_qgrp)) 443994334Sgshapiro q->q_qgrp = e->e_qgrp; 444090792Sgshapiro q->q_alias = ctladdr; 444190792Sgshapiro if (qfver >= 1) 444290792Sgshapiro q->q_flags &= ~Q_PINGFLAGS; 444390792Sgshapiro q->q_flags |= qflags; 444490792Sgshapiro q->q_finalrcpt = frcpt; 444590792Sgshapiro q->q_orcpt = orcpt; 444690792Sgshapiro (void) recipient(q, &e->e_sendqueue, 0, e); 444790792Sgshapiro } 444890792Sgshapiro frcpt = NULL; 444990792Sgshapiro orcpt = NULL; 4450120256Sgshapiro macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), 4451120256Sgshapiro NULL); 445238032Speter break; 445338032Speter 445490792Sgshapiro case 'S': /* sender */ 445590792Sgshapiro setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]), 445690792Sgshapiro e, NULL, '\0', true); 445738032Speter break; 445838032Speter 445990792Sgshapiro case 'T': /* init time */ 446090792Sgshapiro e->e_ctime = atol(&bp[1]); 446164562Sgshapiro break; 446264562Sgshapiro 446390792Sgshapiro case 'V': /* queue file version number */ 446490792Sgshapiro qfver = atoi(&bp[1]); 446590792Sgshapiro if (qfver <= QF_VERSION) 446690792Sgshapiro break; 446790792Sgshapiro syserr("Version number in queue file (%d) greater than max (%d)", 446890792Sgshapiro qfver, QF_VERSION); 446994334Sgshapiro err = "unsupported queue file version"; 447094334Sgshapiro goto fail; 447190792Sgshapiro /* NOTREACHED */ 447290792Sgshapiro break; 447390792Sgshapiro 447490792Sgshapiro case 'Z': /* original envelope id from ESMTP */ 447590792Sgshapiro e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]); 447690792Sgshapiro macdefine(&e->e_macro, A_PERM, 447790792Sgshapiro macid("{dsn_envid}"), e->e_envid); 447890792Sgshapiro break; 447990792Sgshapiro 448090792Sgshapiro case '!': /* deliver by */ 448190792Sgshapiro 448290792Sgshapiro /* format: flag (1 char) space long-integer */ 448390792Sgshapiro e->e_dlvr_flag = buf[1]; 448490792Sgshapiro e->e_deliver_by = strtol(&buf[3], NULL, 10); 448590792Sgshapiro 448638032Speter case '$': /* define macro */ 448764562Sgshapiro { 448864562Sgshapiro char *p; 448964562Sgshapiro 449090792Sgshapiro /* XXX elimate p? */ 449190792Sgshapiro r = macid_parse(&bp[1], &ep); 449290792Sgshapiro if (r == 0) 449371345Sgshapiro break; 449490792Sgshapiro p = sm_rpool_strdup_x(e->e_rpool, ep); 449590792Sgshapiro macdefine(&e->e_macro, A_PERM, r, p); 449664562Sgshapiro } 449738032Speter break; 449838032Speter 449938032Speter case '.': /* terminate file */ 450090792Sgshapiro nomore = true; 450138032Speter break; 450238032Speter 4503132943Sgshapiro#if _FFR_QUEUEDELAY 4504132943Sgshapiro case 'G': 4505132943Sgshapiro case 'Y': 4506132943Sgshapiro 4507132943Sgshapiro /* 4508132943Sgshapiro ** Maintain backward compatibility for 4509132943Sgshapiro ** users who defined _FFR_QUEUEDELAY in 4510132943Sgshapiro ** previous releases. Remove this 4511132943Sgshapiro ** code in 8.14 or 8.15. 4512132943Sgshapiro */ 4513132943Sgshapiro 4514132943Sgshapiro if (qfver == 5 || qfver == 7) 4515132943Sgshapiro break; 4516132943Sgshapiro 4517132943Sgshapiro /* If not qfver 5 or 7, then 'G' or 'Y' is invalid */ 4518132943Sgshapiro /* FALLTHROUGH */ 4519132943Sgshapiro#endif /* _FFR_QUEUEDELAY */ 4520132943Sgshapiro 452138032Speter default: 452238032Speter syserr("readqf: %s: line %d: bad line \"%s\"", 452338032Speter qf, LineNumber, shortenstring(bp, MAXSHORTSTR)); 452494334Sgshapiro err = "unrecognized line"; 452594334Sgshapiro goto fail; 452638032Speter } 452738032Speter 452838032Speter if (bp != buf) 4529168515Sgshapiro SM_FREE(bp); 453038032Speter } 453138032Speter 453238032Speter /* 453338032Speter ** If we haven't read any lines, this queue file is empty. 453438032Speter ** Arrange to remove it without referencing any null pointers. 453538032Speter */ 453638032Speter 453738032Speter if (LineNumber == 0) 453838032Speter { 453938032Speter errno = 0; 454090792Sgshapiro e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE; 454190792Sgshapiro return true; 454238032Speter } 454338032Speter 454490792Sgshapiro /* Check to make sure we have a complete queue file read */ 454590792Sgshapiro if (!nomore) 454690792Sgshapiro { 454790792Sgshapiro syserr("readqf: %s: incomplete queue file read", qf); 454890792Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 454990792Sgshapiro return false; 455090792Sgshapiro } 4551182352Sgshapiro 4552168515Sgshapiro#if _FFR_QF_PARANOIA 4553168515Sgshapiro /* Check to make sure key fields were read */ 4554168515Sgshapiro if (e->e_from.q_mailer == NULL) 4555168515Sgshapiro { 4556168515Sgshapiro syserr("readqf: %s: sender not specified in queue file", qf); 4557168515Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 4558168515Sgshapiro return false; 4559168515Sgshapiro } 4560168515Sgshapiro /* other checks? */ 4561168515Sgshapiro#endif /* _FFR_QF_PARANOIA */ 456290792Sgshapiro 456364562Sgshapiro /* possibly set ${dsn_ret} macro */ 456464562Sgshapiro if (bitset(EF_RET_PARAM, e->e_flags)) 456564562Sgshapiro { 456664562Sgshapiro if (bitset(EF_NO_BODY_RETN, e->e_flags)) 456790792Sgshapiro macdefine(&e->e_macro, A_PERM, 456890792Sgshapiro macid("{dsn_ret}"), "hdrs"); 456964562Sgshapiro else 457090792Sgshapiro macdefine(&e->e_macro, A_PERM, 457190792Sgshapiro macid("{dsn_ret}"), "full"); 457264562Sgshapiro } 457364562Sgshapiro 457438032Speter /* 457538032Speter ** Arrange to read the data file. 457638032Speter */ 457738032Speter 457890792Sgshapiro p = queuename(e, DATAFL_LETTER); 4579120256Sgshapiro e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY_B, 458090792Sgshapiro NULL); 458138032Speter if (e->e_dfp == NULL) 458238032Speter { 458338032Speter syserr("readqf: cannot open %s", p); 458438032Speter } 458538032Speter else 458638032Speter { 458738032Speter e->e_flags |= EF_HAS_DF; 458890792Sgshapiro if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st) 458990792Sgshapiro >= 0) 459038032Speter { 459138032Speter e->e_msgsize = st.st_size + hdrsize; 459238032Speter e->e_dfdev = st.st_dev; 459390792Sgshapiro e->e_dfino = ST_INODE(st); 4594168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "%ld", 4595244833Sgshapiro PRT_NONNEGL(e->e_msgsize)); 459698121Sgshapiro macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), 459798121Sgshapiro buf); 459838032Speter } 459938032Speter } 460038032Speter 460190792Sgshapiro return true; 460294334Sgshapiro 460394334Sgshapiro fail: 460494334Sgshapiro /* 460594334Sgshapiro ** There was some error reading the qf file (reason is in err var.) 460694334Sgshapiro ** Cleanup: 460794334Sgshapiro ** close file; clear e_lockfp since it is the same as qfp, 460894334Sgshapiro ** hence it is invalid (as file) after qfp is closed; 460994334Sgshapiro ** the qf file is on disk, so set the flag to avoid calling 461094334Sgshapiro ** queueup() with bogus data. 461194334Sgshapiro */ 461294334Sgshapiro 4613168515Sgshapiro if (bp != buf) 4614168515Sgshapiro SM_FREE(bp); 461594334Sgshapiro if (qfp != NULL) 461694334Sgshapiro (void) sm_io_close(qfp, SM_TIME_DEFAULT); 461794334Sgshapiro e->e_lockfp = NULL; 461894334Sgshapiro e->e_flags |= EF_INQUEUE; 461994334Sgshapiro loseqfile(e, err); 462094334Sgshapiro return false; 462138032Speter} 462290792Sgshapiro/* 462364562Sgshapiro** PRTSTR -- print a string, "unprintable" characters are shown as \oct 462464562Sgshapiro** 462564562Sgshapiro** Parameters: 462664562Sgshapiro** s -- string to print 462764562Sgshapiro** ml -- maximum length of output 462864562Sgshapiro** 462964562Sgshapiro** Returns: 463090792Sgshapiro** number of entries 463164562Sgshapiro** 463264562Sgshapiro** Side Effects: 463364562Sgshapiro** Prints a string on stdout. 463464562Sgshapiro*/ 463564562Sgshapiro 4636168515Sgshapirostatic void prtstr __P((char *, int)); 4637168515Sgshapiro 463864562Sgshapirostatic void 463964562Sgshapiroprtstr(s, ml) 464064562Sgshapiro char *s; 464164562Sgshapiro int ml; 464264562Sgshapiro{ 464390792Sgshapiro int c; 464464562Sgshapiro 464564562Sgshapiro if (s == NULL) 464664562Sgshapiro return; 464764562Sgshapiro while (ml-- > 0 && ((c = *s++) != '\0')) 464864562Sgshapiro { 464964562Sgshapiro if (c == '\\') 465064562Sgshapiro { 465164562Sgshapiro if (ml-- > 0) 465264562Sgshapiro { 465390792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 465490792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 465564562Sgshapiro } 465664562Sgshapiro } 465764562Sgshapiro else if (isascii(c) && isprint(c)) 465890792Sgshapiro (void) sm_io_putc(smioout, SM_TIME_DEFAULT, c); 465964562Sgshapiro else 466064562Sgshapiro { 466164562Sgshapiro if ((ml -= 3) > 0) 466290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 466390792Sgshapiro "\\%03o", c & 0xFF); 466464562Sgshapiro } 466564562Sgshapiro } 466664562Sgshapiro} 466790792Sgshapiro/* 466890792Sgshapiro** PRINTNQE -- print out number of entries in the mail queue 466990792Sgshapiro** 467090792Sgshapiro** Parameters: 467190792Sgshapiro** out -- output file pointer. 467290792Sgshapiro** prefix -- string to output in front of each line. 467390792Sgshapiro** 467490792Sgshapiro** Returns: 467590792Sgshapiro** none. 467690792Sgshapiro*/ 467790792Sgshapiro 467890792Sgshapirovoid 467990792Sgshapiroprintnqe(out, prefix) 468090792Sgshapiro SM_FILE_T *out; 468190792Sgshapiro char *prefix; 468290792Sgshapiro{ 468390792Sgshapiro#if SM_CONF_SHM 468490792Sgshapiro int i, k = 0, nrequests = 0; 468590792Sgshapiro bool unknown = false; 468690792Sgshapiro 468790792Sgshapiro if (ShmId == SM_SHM_NO_ID) 468890792Sgshapiro { 468990792Sgshapiro if (prefix == NULL) 469090792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 469190792Sgshapiro "Data unavailable: shared memory not updated\n"); 469290792Sgshapiro else 469390792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 469490792Sgshapiro "%sNOTCONFIGURED:-1\r\n", prefix); 469590792Sgshapiro return; 469690792Sgshapiro } 469790792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 469890792Sgshapiro { 469990792Sgshapiro int j; 470090792Sgshapiro 470190792Sgshapiro k++; 470290792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 470390792Sgshapiro { 470490792Sgshapiro int n; 470590792Sgshapiro 470690792Sgshapiro if (StopRequest) 470790792Sgshapiro stop_sendmail(); 470890792Sgshapiro 470990792Sgshapiro n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx); 471090792Sgshapiro if (prefix != NULL) 471190792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 471290792Sgshapiro "%s%s:%d\r\n", 471390792Sgshapiro prefix, qid_printqueue(i, j), n); 471490792Sgshapiro else if (n < 0) 471590792Sgshapiro { 471690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 471790792Sgshapiro "%s: unknown number of entries\n", 471890792Sgshapiro qid_printqueue(i, j)); 471990792Sgshapiro unknown = true; 472090792Sgshapiro } 472190792Sgshapiro else if (n == 0) 472290792Sgshapiro { 472390792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 472490792Sgshapiro "%s is empty\n", 472590792Sgshapiro qid_printqueue(i, j)); 472690792Sgshapiro } 472790792Sgshapiro else if (n > 0) 472890792Sgshapiro { 472990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 473090792Sgshapiro "%s: entries=%d\n", 473190792Sgshapiro qid_printqueue(i, j), n); 473290792Sgshapiro nrequests += n; 473390792Sgshapiro k++; 473490792Sgshapiro } 473590792Sgshapiro } 473690792Sgshapiro } 473790792Sgshapiro if (prefix == NULL && k > 1) 473890792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 473990792Sgshapiro "\t\tTotal requests: %d%s\n", 474090792Sgshapiro nrequests, unknown ? " (about)" : ""); 474190792Sgshapiro#else /* SM_CONF_SHM */ 474290792Sgshapiro if (prefix == NULL) 474390792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 474490792Sgshapiro "Data unavailable without shared memory support\n"); 474590792Sgshapiro else 474690792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 474790792Sgshapiro "%sNOTAVAILABLE:-1\r\n", prefix); 474890792Sgshapiro#endif /* SM_CONF_SHM */ 474990792Sgshapiro} 475090792Sgshapiro/* 475138032Speter** PRINTQUEUE -- print out a representation of the mail queue 475238032Speter** 475338032Speter** Parameters: 475438032Speter** none. 475538032Speter** 475638032Speter** Returns: 475738032Speter** none. 475838032Speter** 475938032Speter** Side Effects: 476038032Speter** Prints a listing of the mail queue on the standard output. 476138032Speter*/ 476238032Speter 476338032Spetervoid 476438032Speterprintqueue() 476538032Speter{ 476690792Sgshapiro int i, k = 0, nrequests = 0; 476764562Sgshapiro 476890792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 476977349Sgshapiro { 477090792Sgshapiro int j; 477190792Sgshapiro 477290792Sgshapiro k++; 477390792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; j++) 477490792Sgshapiro { 477590792Sgshapiro if (StopRequest) 477690792Sgshapiro stop_sendmail(); 477790792Sgshapiro nrequests += print_single_queue(i, j); 477890792Sgshapiro k++; 477990792Sgshapiro } 478077349Sgshapiro } 478190792Sgshapiro if (k > 1) 478290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 478390792Sgshapiro "\t\tTotal requests: %d\n", 478490792Sgshapiro nrequests); 478564562Sgshapiro} 478690792Sgshapiro/* 478764562Sgshapiro** PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue 478864562Sgshapiro** 478964562Sgshapiro** Parameters: 479090792Sgshapiro** qgrp -- the index of the queue group. 479190792Sgshapiro** qdir -- the queue directory. 479264562Sgshapiro** 479364562Sgshapiro** Returns: 479490792Sgshapiro** number of requests in mail queue. 479564562Sgshapiro** 479664562Sgshapiro** Side Effects: 479764562Sgshapiro** Prints a listing of the mail queue on the standard output. 479864562Sgshapiro*/ 479964562Sgshapiro 480090792Sgshapiroint 480190792Sgshapiroprint_single_queue(qgrp, qdir) 480290792Sgshapiro int qgrp; 480390792Sgshapiro int qdir; 480464562Sgshapiro{ 480538032Speter register WORK *w; 480690792Sgshapiro SM_FILE_T *f; 480738032Speter int nrequests; 480864562Sgshapiro char qd[MAXPATHLEN]; 480964562Sgshapiro char qddf[MAXPATHLEN]; 481038032Speter char buf[MAXLINE]; 481138032Speter 481290792Sgshapiro if (qdir == NOQDIR) 481364562Sgshapiro { 4814168515Sgshapiro (void) sm_strlcpy(qd, ".", sizeof(qd)); 4815168515Sgshapiro (void) sm_strlcpy(qddf, ".", sizeof(qddf)); 481664562Sgshapiro } 481764562Sgshapiro else 481864562Sgshapiro { 4819168515Sgshapiro (void) sm_strlcpyn(qd, sizeof(qd), 2, 482090792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 482190792Sgshapiro (bitset(QP_SUBQF, 482290792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 482390792Sgshapiro ? "/qf" : "")); 4824168515Sgshapiro (void) sm_strlcpyn(qddf, sizeof(qddf), 2, 482590792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_name, 482690792Sgshapiro (bitset(QP_SUBDF, 482790792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 482890792Sgshapiro ? "/df" : "")); 482964562Sgshapiro } 483064562Sgshapiro 483138032Speter /* 483238032Speter ** Check for permission to print the queue 483338032Speter */ 483438032Speter 483538032Speter if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0) 483638032Speter { 483738032Speter struct stat st; 483890792Sgshapiro#ifdef NGROUPS_MAX 483938032Speter int n; 484038032Speter extern GIDSET_T InitialGidSet[NGROUPS_MAX]; 484190792Sgshapiro#endif /* NGROUPS_MAX */ 484238032Speter 484364562Sgshapiro if (stat(qd, &st) < 0) 484438032Speter { 484590792Sgshapiro syserr("Cannot stat %s", 484690792Sgshapiro qid_printqueue(qgrp, qdir)); 484764562Sgshapiro return 0; 484838032Speter } 484990792Sgshapiro#ifdef NGROUPS_MAX 485038032Speter n = NGROUPS_MAX; 485138032Speter while (--n >= 0) 485238032Speter { 485338032Speter if (InitialGidSet[n] == st.st_gid) 485438032Speter break; 485538032Speter } 485638032Speter if (n < 0 && RealGid != st.st_gid) 485790792Sgshapiro#else /* NGROUPS_MAX */ 485838032Speter if (RealGid != st.st_gid) 485990792Sgshapiro#endif /* NGROUPS_MAX */ 486038032Speter { 486138032Speter usrerr("510 You are not permitted to see the queue"); 486238032Speter setstat(EX_NOPERM); 486364562Sgshapiro return 0; 486438032Speter } 486538032Speter } 486638032Speter 486738032Speter /* 486838032Speter ** Read and order the queue. 486938032Speter */ 487038032Speter 4871203004Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL, NULL); 487290792Sgshapiro (void) sortq(Queue[qgrp]->qg_maxlist); 487338032Speter 487438032Speter /* 487538032Speter ** Print the work list that we have read. 487638032Speter */ 487738032Speter 487838032Speter /* first see if there is anything */ 487938032Speter if (nrequests <= 0) 488038032Speter { 488190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n", 488290792Sgshapiro qid_printqueue(qgrp, qdir)); 488364562Sgshapiro return 0; 488438032Speter } 488538032Speter 488690792Sgshapiro sm_getla(); /* get load average */ 488738032Speter 488890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s", 488990792Sgshapiro qid_printqueue(qgrp, qdir), 489090792Sgshapiro nrequests, nrequests == 1 ? "" : "s"); 489138032Speter if (MaxQueueRun > 0 && nrequests > MaxQueueRun) 489290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 489390792Sgshapiro ", only %d printed", MaxQueueRun); 489438032Speter if (Verbose) 489590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 489690792Sgshapiro ")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n"); 489738032Speter else 489890792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 489990792Sgshapiro ")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n"); 490038032Speter for (w = WorkQ; w != NULL; w = w->w_next) 490138032Speter { 490238032Speter struct stat st; 490338032Speter auto time_t submittime = 0; 490438032Speter long dfsize; 490538032Speter int flags = 0; 490638032Speter int qfver; 490790792Sgshapiro char quarmsg[MAXLINE]; 490838032Speter char statmsg[MAXLINE]; 490938032Speter char bodytype[MAXNAME + 1]; 491064562Sgshapiro char qf[MAXPATHLEN]; 491138032Speter 491277349Sgshapiro if (StopRequest) 491377349Sgshapiro stop_sendmail(); 491477349Sgshapiro 491590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s", 491690792Sgshapiro w->w_name + 2); 4917168515Sgshapiro (void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", w->w_name); 4918120256Sgshapiro f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B, 491990792Sgshapiro NULL); 492038032Speter if (f == NULL) 492138032Speter { 492290792Sgshapiro if (errno == EPERM) 492390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 492490792Sgshapiro " (permission denied)\n"); 492590792Sgshapiro else if (errno == ENOENT) 492690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 492790792Sgshapiro " (job completed)\n"); 492890792Sgshapiro else 492990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 493090792Sgshapiro " (%s)\n", 493190792Sgshapiro sm_errstring(errno)); 493238032Speter errno = 0; 493338032Speter continue; 493438032Speter } 493590792Sgshapiro w->w_name[0] = DATAFL_LETTER; 4936168515Sgshapiro (void) sm_strlcpyn(qf, sizeof(qf), 3, qddf, "/", w->w_name); 493764562Sgshapiro if (stat(qf, &st) >= 0) 493838032Speter dfsize = st.st_size; 493938032Speter else 494090792Sgshapiro { 494190792Sgshapiro ENVELOPE e; 494290792Sgshapiro 494390792Sgshapiro /* 494490792Sgshapiro ** Maybe the df file can't be statted because 494590792Sgshapiro ** it is in a different directory than the qf file. 494690792Sgshapiro ** In order to find out, we must read the qf file. 494790792Sgshapiro */ 494890792Sgshapiro 494990792Sgshapiro newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL)); 495090792Sgshapiro e.e_id = w->w_name + 2; 495190792Sgshapiro e.e_qgrp = qgrp; 495290792Sgshapiro e.e_qdir = qdir; 495338032Speter dfsize = -1; 495490792Sgshapiro if (readqf(&e, false)) 495590792Sgshapiro { 495690792Sgshapiro char *df = queuename(&e, DATAFL_LETTER); 495790792Sgshapiro if (stat(df, &st) >= 0) 495890792Sgshapiro dfsize = st.st_size; 495990792Sgshapiro } 496090792Sgshapiro if (e.e_lockfp != NULL) 496190792Sgshapiro { 496290792Sgshapiro (void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT); 496390792Sgshapiro e.e_lockfp = NULL; 496490792Sgshapiro } 496590792Sgshapiro clearenvelope(&e, false, e.e_rpool); 496690792Sgshapiro sm_rpool_free(e.e_rpool); 496790792Sgshapiro } 496838032Speter if (w->w_lock) 496990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*"); 497090792Sgshapiro else if (QueueMode == QM_LOST) 497190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?"); 497238032Speter else if (w->w_tooyoung) 497390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-"); 497438032Speter else if (shouldqueue(w->w_pri, w->w_ctime)) 497590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X"); 497638032Speter else 497790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " "); 497890792Sgshapiro 497938032Speter errno = 0; 498038032Speter 498190792Sgshapiro quarmsg[0] = '\0'; 498238032Speter statmsg[0] = bodytype[0] = '\0'; 498338032Speter qfver = 0; 4984249729Sgshapiro while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0) 498538032Speter { 498638032Speter register int i; 498738032Speter register char *p; 498838032Speter 498977349Sgshapiro if (StopRequest) 499077349Sgshapiro stop_sendmail(); 499177349Sgshapiro 499290792Sgshapiro fixcrlf(buf, true); 499338032Speter switch (buf[0]) 499438032Speter { 499538032Speter case 'V': /* queue file version */ 499638032Speter qfver = atoi(&buf[1]); 499738032Speter break; 499838032Speter 499938032Speter case 'M': /* error message */ 5000168515Sgshapiro if ((i = strlen(&buf[1])) >= sizeof(statmsg)) 5001168515Sgshapiro i = sizeof(statmsg) - 1; 500264562Sgshapiro memmove(statmsg, &buf[1], i); 500338032Speter statmsg[i] = '\0'; 500438032Speter break; 500538032Speter 500690792Sgshapiro case 'q': /* quarantine reason */ 5007168515Sgshapiro if ((i = strlen(&buf[1])) >= sizeof(quarmsg)) 5008168515Sgshapiro i = sizeof(quarmsg) - 1; 500990792Sgshapiro memmove(quarmsg, &buf[1], i); 501090792Sgshapiro quarmsg[i] = '\0'; 501190792Sgshapiro break; 501290792Sgshapiro 501338032Speter case 'B': /* body type */ 5014168515Sgshapiro if ((i = strlen(&buf[1])) >= sizeof(bodytype)) 5015168515Sgshapiro i = sizeof(bodytype) - 1; 501664562Sgshapiro memmove(bodytype, &buf[1], i); 501738032Speter bodytype[i] = '\0'; 501838032Speter break; 501938032Speter 502038032Speter case 'S': /* sender name */ 502138032Speter if (Verbose) 502264562Sgshapiro { 502390792Sgshapiro (void) sm_io_fprintf(smioout, 502490792Sgshapiro SM_TIME_DEFAULT, 502590792Sgshapiro "%8ld %10ld%c%.12s ", 502690792Sgshapiro dfsize, 502790792Sgshapiro w->w_pri, 502890792Sgshapiro bitset(EF_WARNING, flags) 502990792Sgshapiro ? '+' : ' ', 503090792Sgshapiro ctime(&submittime) + 4); 503164562Sgshapiro prtstr(&buf[1], 78); 503264562Sgshapiro } 503338032Speter else 503464562Sgshapiro { 503590792Sgshapiro (void) sm_io_fprintf(smioout, 503690792Sgshapiro SM_TIME_DEFAULT, 503790792Sgshapiro "%8ld %.16s ", 503890792Sgshapiro dfsize, 503990792Sgshapiro ctime(&submittime)); 504090792Sgshapiro prtstr(&buf[1], 39); 504164562Sgshapiro } 5042132943Sgshapiro 504390792Sgshapiro if (quarmsg[0] != '\0') 504490792Sgshapiro { 504590792Sgshapiro (void) sm_io_fprintf(smioout, 504690792Sgshapiro SM_TIME_DEFAULT, 504790792Sgshapiro "\n QUARANTINE: %.*s", 504890792Sgshapiro Verbose ? 100 : 60, 504990792Sgshapiro quarmsg); 505090792Sgshapiro quarmsg[0] = '\0'; 505190792Sgshapiro } 5052132943Sgshapiro 505338032Speter if (statmsg[0] != '\0' || bodytype[0] != '\0') 505438032Speter { 505590792Sgshapiro (void) sm_io_fprintf(smioout, 505690792Sgshapiro SM_TIME_DEFAULT, 505790792Sgshapiro "\n %10.10s", 505890792Sgshapiro bodytype); 505938032Speter if (statmsg[0] != '\0') 506090792Sgshapiro (void) sm_io_fprintf(smioout, 506190792Sgshapiro SM_TIME_DEFAULT, 506290792Sgshapiro " (%.*s)", 506390792Sgshapiro Verbose ? 100 : 60, 506490792Sgshapiro statmsg); 506590792Sgshapiro statmsg[0] = '\0'; 506638032Speter } 506738032Speter break; 506838032Speter 506938032Speter case 'C': /* controlling user */ 507038032Speter if (Verbose) 507190792Sgshapiro (void) sm_io_fprintf(smioout, 507290792Sgshapiro SM_TIME_DEFAULT, 507390792Sgshapiro "\n\t\t\t\t\t\t(---%.64s---)", 507490792Sgshapiro &buf[1]); 507538032Speter break; 507638032Speter 507738032Speter case 'R': /* recipient name */ 507838032Speter p = &buf[1]; 507938032Speter if (qfver >= 1) 508038032Speter { 508138032Speter p = strchr(p, ':'); 508238032Speter if (p == NULL) 508338032Speter break; 508438032Speter p++; 508538032Speter } 508638032Speter if (Verbose) 508764562Sgshapiro { 508890792Sgshapiro (void) sm_io_fprintf(smioout, 508990792Sgshapiro SM_TIME_DEFAULT, 509090792Sgshapiro "\n\t\t\t\t\t\t"); 509190792Sgshapiro prtstr(p, 71); 509264562Sgshapiro } 509338032Speter else 509464562Sgshapiro { 509590792Sgshapiro (void) sm_io_fprintf(smioout, 509690792Sgshapiro SM_TIME_DEFAULT, 509790792Sgshapiro "\n\t\t\t\t\t "); 509890792Sgshapiro prtstr(p, 38); 509964562Sgshapiro } 510090792Sgshapiro if (Verbose && statmsg[0] != '\0') 510190792Sgshapiro { 510290792Sgshapiro (void) sm_io_fprintf(smioout, 510390792Sgshapiro SM_TIME_DEFAULT, 510490792Sgshapiro "\n\t\t (%.100s)", 510590792Sgshapiro statmsg); 510690792Sgshapiro statmsg[0] = '\0'; 510790792Sgshapiro } 510838032Speter break; 510938032Speter 511038032Speter case 'T': /* creation time */ 511138032Speter submittime = atol(&buf[1]); 511238032Speter break; 511338032Speter 511438032Speter case 'F': /* flag bits */ 511538032Speter for (p = &buf[1]; *p != '\0'; p++) 511638032Speter { 511738032Speter switch (*p) 511838032Speter { 511938032Speter case 'w': 512038032Speter flags |= EF_WARNING; 512138032Speter break; 512238032Speter } 512338032Speter } 512438032Speter } 512538032Speter } 512638032Speter if (submittime == (time_t) 0) 512790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 512890792Sgshapiro " (no control file)"); 512990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 513090792Sgshapiro (void) sm_io_close(f, SM_TIME_DEFAULT); 513138032Speter } 513264562Sgshapiro return nrequests; 513338032Speter} 513490792Sgshapiro 513590792Sgshapiro/* 513690792Sgshapiro** QUEUE_LETTER -- get the proper queue letter for the current QueueMode. 513790792Sgshapiro** 513890792Sgshapiro** Parameters: 513990792Sgshapiro** e -- envelope to build it in/from. 514090792Sgshapiro** type -- the file type, used as the first character 514190792Sgshapiro** of the file name. 514290792Sgshapiro** 514390792Sgshapiro** Returns: 514490792Sgshapiro** the letter to use 514590792Sgshapiro*/ 514690792Sgshapiro 514790792Sgshapirostatic char 514890792Sgshapiroqueue_letter(e, type) 514990792Sgshapiro ENVELOPE *e; 515090792Sgshapiro int type; 515190792Sgshapiro{ 515290792Sgshapiro /* Change type according to QueueMode */ 515390792Sgshapiro if (type == ANYQFL_LETTER) 515490792Sgshapiro { 515590792Sgshapiro if (e->e_quarmsg != NULL) 515690792Sgshapiro type = QUARQF_LETTER; 515790792Sgshapiro else 515890792Sgshapiro { 515990792Sgshapiro switch (QueueMode) 516090792Sgshapiro { 516190792Sgshapiro case QM_NORMAL: 516290792Sgshapiro type = NORMQF_LETTER; 516390792Sgshapiro break; 516490792Sgshapiro 516590792Sgshapiro case QM_QUARANTINE: 516690792Sgshapiro type = QUARQF_LETTER; 516790792Sgshapiro break; 516890792Sgshapiro 516990792Sgshapiro case QM_LOST: 517090792Sgshapiro type = LOSEQF_LETTER; 517190792Sgshapiro break; 517290792Sgshapiro 517390792Sgshapiro default: 517490792Sgshapiro /* should never happen */ 517590792Sgshapiro abort(); 517690792Sgshapiro /* NOTREACHED */ 517790792Sgshapiro } 517890792Sgshapiro } 517990792Sgshapiro } 518090792Sgshapiro return type; 518190792Sgshapiro} 518290792Sgshapiro 518390792Sgshapiro/* 518438032Speter** QUEUENAME -- build a file name in the queue directory for this envelope. 518538032Speter** 518638032Speter** Parameters: 518738032Speter** e -- envelope to build it in/from. 518838032Speter** type -- the file type, used as the first character 518938032Speter** of the file name. 519038032Speter** 519138032Speter** Returns: 519264562Sgshapiro** a pointer to the queue name (in a static buffer). 519338032Speter** 519438032Speter** Side Effects: 519564562Sgshapiro** If no id code is already assigned, queuename() will 519664562Sgshapiro** assign an id code with assign_queueid(). If no queue 519764562Sgshapiro** directory is assigned, one will be set with setnewqueue(). 519838032Speter*/ 519938032Speter 520038032Speterchar * 520138032Speterqueuename(e, type) 520238032Speter register ENVELOPE *e; 520338032Speter int type; 520438032Speter{ 520590792Sgshapiro int qd, qg; 520690792Sgshapiro char *sub = "/"; 520790792Sgshapiro char pref[3]; 520864562Sgshapiro static char buf[MAXPATHLEN]; 520938032Speter 521064562Sgshapiro /* Assign an ID if needed */ 521138032Speter if (e->e_id == NULL) 5212223067Sgshapiro { 5213223067Sgshapiro if (IntSig) 5214223067Sgshapiro return NULL; 521564562Sgshapiro assign_queueid(e); 5216223067Sgshapiro } 521790792Sgshapiro type = queue_letter(e, type); 521864562Sgshapiro 521990792Sgshapiro /* begin of filename */ 522090792Sgshapiro pref[0] = (char) type; 522190792Sgshapiro pref[1] = 'f'; 522290792Sgshapiro pref[2] = '\0'; 522390792Sgshapiro 522490792Sgshapiro /* Assign a queue group/directory if needed */ 522590792Sgshapiro if (type == XSCRPT_LETTER) 522690792Sgshapiro { 522790792Sgshapiro /* 522890792Sgshapiro ** We don't want to call setnewqueue() if we are fetching 522990792Sgshapiro ** the pathname of the transcript file, because setnewqueue 523090792Sgshapiro ** chooses a queue, and sometimes we need to write to the 523190792Sgshapiro ** transcript file before we have gathered enough information 523290792Sgshapiro ** to choose a queue. 523390792Sgshapiro */ 523490792Sgshapiro 523590792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 523690792Sgshapiro { 523790792Sgshapiro if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR) 523890792Sgshapiro { 523990792Sgshapiro e->e_xfqgrp = e->e_qgrp; 524090792Sgshapiro e->e_xfqdir = e->e_qdir; 524190792Sgshapiro } 524290792Sgshapiro else 524390792Sgshapiro { 524490792Sgshapiro e->e_xfqgrp = 0; 524590792Sgshapiro if (Queue[e->e_xfqgrp]->qg_numqueues <= 1) 524690792Sgshapiro e->e_xfqdir = 0; 524790792Sgshapiro else 524890792Sgshapiro { 524990792Sgshapiro e->e_xfqdir = get_rand_mod( 525090792Sgshapiro Queue[e->e_xfqgrp]->qg_numqueues); 525190792Sgshapiro } 525290792Sgshapiro } 525390792Sgshapiro } 525490792Sgshapiro qd = e->e_xfqdir; 525590792Sgshapiro qg = e->e_xfqgrp; 525690792Sgshapiro } 525764562Sgshapiro else 525838032Speter { 525990792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 5260223067Sgshapiro { 5261223067Sgshapiro if (IntSig) 5262223067Sgshapiro return NULL; 5263159609Sgshapiro (void) setnewqueue(e); 5264223067Sgshapiro } 526590792Sgshapiro if (type == DATAFL_LETTER) 526690792Sgshapiro { 526790792Sgshapiro qd = e->e_dfqdir; 526890792Sgshapiro qg = e->e_dfqgrp; 526990792Sgshapiro } 527090792Sgshapiro else 527190792Sgshapiro { 527290792Sgshapiro qd = e->e_qdir; 527390792Sgshapiro qg = e->e_qgrp; 527490792Sgshapiro } 527590792Sgshapiro } 527690792Sgshapiro 527794334Sgshapiro /* xf files always have a valid qd and qg picked above */ 5278159609Sgshapiro if ((qd == NOQDIR || qg == NOQGRP) && type != XSCRPT_LETTER) 5279168515Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 2, pref, e->e_id); 528090792Sgshapiro else 528190792Sgshapiro { 528264562Sgshapiro switch (type) 528364562Sgshapiro { 528490792Sgshapiro case DATAFL_LETTER: 528590792Sgshapiro if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 528690792Sgshapiro sub = "/df/"; 528764562Sgshapiro break; 528838032Speter 528990792Sgshapiro case QUARQF_LETTER: 529071345Sgshapiro case TEMPQF_LETTER: 529190792Sgshapiro case NEWQFL_LETTER: 529271345Sgshapiro case LOSEQF_LETTER: 529390792Sgshapiro case NORMQF_LETTER: 529490792Sgshapiro if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 529590792Sgshapiro sub = "/qf/"; 529664562Sgshapiro break; 529764562Sgshapiro 529890792Sgshapiro case XSCRPT_LETTER: 529990792Sgshapiro if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs)) 530090792Sgshapiro sub = "/xf/"; 530164562Sgshapiro break; 530290792Sgshapiro 530390792Sgshapiro default: 5304223067Sgshapiro if (IntSig) 5305223067Sgshapiro return NULL; 530690792Sgshapiro sm_abort("queuename: bad queue file type %d", type); 530738032Speter } 530838032Speter 5309168515Sgshapiro (void) sm_strlcpyn(buf, sizeof(buf), 4, 531090792Sgshapiro Queue[qg]->qg_qpaths[qd].qp_name, 531190792Sgshapiro sub, pref, e->e_id); 531264562Sgshapiro } 531338032Speter 531464562Sgshapiro if (tTd(7, 2)) 531590792Sgshapiro sm_dprintf("queuename: %s\n", buf); 531664562Sgshapiro return buf; 531764562Sgshapiro} 5318125820Sgshapiro 531990792Sgshapiro/* 5320125820Sgshapiro** INIT_QID_ALG -- Initialize the (static) parameters that are used to 5321125820Sgshapiro** generate a queue ID. 5322125820Sgshapiro** 5323125820Sgshapiro** This function is called by the daemon to reset 5324125820Sgshapiro** LastQueueTime and LastQueuePid which are used by assign_queueid(). 5325125820Sgshapiro** Otherwise the algorithm may cause problems because 5326125820Sgshapiro** LastQueueTime and LastQueuePid are set indirectly by main() 5327125820Sgshapiro** before the daemon process is started, hence LastQueuePid is not 5328125820Sgshapiro** the pid of the daemon and therefore a child of the daemon can 5329125820Sgshapiro** actually have the same pid as LastQueuePid which means the section 5330125820Sgshapiro** in assign_queueid(): 5331125820Sgshapiro** * see if we need to get a new base time/pid * 5332125820Sgshapiro** is NOT triggered which will cause the same queue id to be generated. 5333125820Sgshapiro** 5334125820Sgshapiro** Parameters: 5335125820Sgshapiro** none 5336125820Sgshapiro** 5337125820Sgshapiro** Returns: 5338125820Sgshapiro** none. 5339125820Sgshapiro*/ 5340125820Sgshapiro 5341125820Sgshapirovoid 5342125820Sgshapiroinit_qid_alg() 5343125820Sgshapiro{ 5344125820Sgshapiro LastQueueTime = 0; 5345125820Sgshapiro LastQueuePid = -1; 5346125820Sgshapiro} 5347125820Sgshapiro 5348125820Sgshapiro/* 534964562Sgshapiro** ASSIGN_QUEUEID -- assign a queue ID for this envelope. 535064562Sgshapiro** 535164562Sgshapiro** Assigns an id code if one does not already exist. 535264562Sgshapiro** This code assumes that nothing will remain in the queue for 535364562Sgshapiro** longer than 60 years. It is critical that files with the given 535490792Sgshapiro** name do not already exist in the queue. 535590792Sgshapiro** [No longer initializes e_qdir to NOQDIR.] 535664562Sgshapiro** 535764562Sgshapiro** Parameters: 535864562Sgshapiro** e -- envelope to set it in. 535964562Sgshapiro** 536064562Sgshapiro** Returns: 536164562Sgshapiro** none. 536264562Sgshapiro*/ 536338032Speter 5364125820Sgshapirostatic const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 536577349Sgshapiro# define QIC_LEN 60 5366125820Sgshapiro# define QIC_LEN_R 62 5367125820Sgshapiro 5368125820Sgshapiro/* 5369125820Sgshapiro** Note: the length is "officially" 60 because minutes and seconds are 5370125820Sgshapiro** usually only 0-59. However (Linux): 5371125820Sgshapiro** tm_sec The number of seconds after the minute, normally in 5372132943Sgshapiro** the range 0 to 59, but can be up to 61 to allow for 5373132943Sgshapiro** leap seconds. 5374125820Sgshapiro** Hence the real length of the string is 62 to take this into account. 5375125820Sgshapiro** Alternatively % QIC_LEN can (should) be used for access everywhere. 5376125820Sgshapiro*/ 5377125820Sgshapiro 537890792Sgshapiro# define queuenextid() CurrentPid 5379203004Sgshapiro#define QIC_LEN_SQR (QIC_LEN * QIC_LEN) 538038032Speter 538164562Sgshapirovoid 538264562Sgshapiroassign_queueid(e) 538364562Sgshapiro register ENVELOPE *e; 538464562Sgshapiro{ 538590792Sgshapiro pid_t pid = queuenextid(); 5386203004Sgshapiro static unsigned int cX = 0; 5387203004Sgshapiro static unsigned int random_offset; 538864562Sgshapiro struct tm *tm; 538964562Sgshapiro char idbuf[MAXQFNAME - 2]; 5390203004Sgshapiro unsigned int seq; 539138032Speter 539264562Sgshapiro if (e->e_id != NULL) 539364562Sgshapiro return; 539438032Speter 539564562Sgshapiro /* see if we need to get a new base time/pid */ 5396203004Sgshapiro if (cX >= QIC_LEN_SQR || LastQueueTime == 0 || LastQueuePid != pid) 539764562Sgshapiro { 539864562Sgshapiro time_t then = LastQueueTime; 539964562Sgshapiro 540064562Sgshapiro /* if the first time through, pick a random offset */ 540164562Sgshapiro if (LastQueueTime == 0) 5402203004Sgshapiro random_offset = ((unsigned int)get_random()) 5403203004Sgshapiro % QIC_LEN_SQR; 540464562Sgshapiro 540564562Sgshapiro while ((LastQueueTime = curtime()) == then && 540664562Sgshapiro LastQueuePid == pid) 540738032Speter { 540864562Sgshapiro (void) sleep(1); 540938032Speter } 541090792Sgshapiro LastQueuePid = queuenextid(); 541164562Sgshapiro cX = 0; 541238032Speter } 541390792Sgshapiro 541490792Sgshapiro /* 5415203004Sgshapiro ** Generate a new sequence number between 0 and QIC_LEN_SQR-1. 5416203004Sgshapiro ** This lets us generate up to QIC_LEN_SQR unique queue ids 541790792Sgshapiro ** per second, per process. With envelope splitting, 541890792Sgshapiro ** a single message can consume many queue ids. 541990792Sgshapiro */ 542090792Sgshapiro 5421203004Sgshapiro seq = (cX + random_offset) % QIC_LEN_SQR; 542290792Sgshapiro ++cX; 542364562Sgshapiro if (tTd(7, 50)) 5424203004Sgshapiro sm_dprintf("assign_queueid: random_offset=%u (%u)\n", 542590792Sgshapiro random_offset, seq); 542638032Speter 542764562Sgshapiro tm = gmtime(&LastQueueTime); 542877349Sgshapiro idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN]; 542977349Sgshapiro idbuf[1] = QueueIdChars[tm->tm_mon]; 543077349Sgshapiro idbuf[2] = QueueIdChars[tm->tm_mday]; 543177349Sgshapiro idbuf[3] = QueueIdChars[tm->tm_hour]; 5432125820Sgshapiro idbuf[4] = QueueIdChars[tm->tm_min % QIC_LEN_R]; 5433125820Sgshapiro idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R]; 543490792Sgshapiro idbuf[6] = QueueIdChars[seq / QIC_LEN]; 543590792Sgshapiro idbuf[7] = QueueIdChars[seq % QIC_LEN]; 5436168515Sgshapiro (void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d", 543790792Sgshapiro (int) LastQueuePid); 543890792Sgshapiro e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf); 543990792Sgshapiro macdefine(&e->e_macro, A_PERM, 'i', e->e_id); 544090792Sgshapiro#if 0 544190792Sgshapiro /* XXX: inherited from MainEnvelope */ 544290792Sgshapiro e->e_qgrp = NOQGRP; /* too early to do anything else */ 544390792Sgshapiro e->e_qdir = NOQDIR; 544490792Sgshapiro e->e_xfqgrp = NOQGRP; 544590792Sgshapiro#endif /* 0 */ 5446132943Sgshapiro 544790792Sgshapiro /* New ID means it's not on disk yet */ 544890792Sgshapiro e->e_qfletter = '\0'; 5449132943Sgshapiro 545064562Sgshapiro if (tTd(7, 1)) 545190792Sgshapiro sm_dprintf("assign_queueid: assigned id %s, e=%p\n", 545290792Sgshapiro e->e_id, e); 545364562Sgshapiro if (LogLevel > 93) 545464562Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, "assigned id"); 545538032Speter} 545690792Sgshapiro/* 545764562Sgshapiro** SYNC_QUEUE_TIME -- Assure exclusive PID in any given second 545864562Sgshapiro** 545964562Sgshapiro** Make sure one PID can't be used by two processes in any one second. 546064562Sgshapiro** 546164562Sgshapiro** If the system rotates PIDs fast enough, may get the 546264562Sgshapiro** same pid in the same second for two distinct processes. 546364562Sgshapiro** This will interfere with the queue file naming system. 546464562Sgshapiro** 546564562Sgshapiro** Parameters: 546664562Sgshapiro** none 546764562Sgshapiro** 546864562Sgshapiro** Returns: 546964562Sgshapiro** none 547064562Sgshapiro*/ 547190792Sgshapiro 547264562Sgshapirovoid 547364562Sgshapirosync_queue_time() 547464562Sgshapiro{ 547590792Sgshapiro#if FAST_PID_RECYCLE 547664562Sgshapiro if (OpMode != MD_TEST && 5477203004Sgshapiro OpMode != MD_CHECKCONFIG && 547864562Sgshapiro OpMode != MD_VERIFY && 547964562Sgshapiro LastQueueTime > 0 && 548090792Sgshapiro LastQueuePid == CurrentPid && 548164562Sgshapiro curtime() == LastQueueTime) 548264562Sgshapiro (void) sleep(1); 548390792Sgshapiro#endif /* FAST_PID_RECYCLE */ 548464562Sgshapiro} 548590792Sgshapiro/* 548638032Speter** UNLOCKQUEUE -- unlock the queue entry for a specified envelope 548738032Speter** 548838032Speter** Parameters: 548938032Speter** e -- the envelope to unlock. 549038032Speter** 549138032Speter** Returns: 549238032Speter** none 549338032Speter** 549438032Speter** Side Effects: 549538032Speter** unlocks the queue for `e'. 549638032Speter*/ 549738032Speter 549838032Spetervoid 549938032Speterunlockqueue(e) 550038032Speter ENVELOPE *e; 550138032Speter{ 550238032Speter if (tTd(51, 4)) 550390792Sgshapiro sm_dprintf("unlockqueue(%s)\n", 550438032Speter e->e_id == NULL ? "NOQUEUE" : e->e_id); 550538032Speter 550664562Sgshapiro 550738032Speter /* if there is a lock file in the envelope, close it */ 550838032Speter if (e->e_lockfp != NULL) 550990792Sgshapiro (void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT); 551038032Speter e->e_lockfp = NULL; 551138032Speter 551238032Speter /* don't create a queue id if we don't already have one */ 551338032Speter if (e->e_id == NULL) 551438032Speter return; 551538032Speter 551638032Speter /* remove the transcript */ 551738032Speter if (LogLevel > 87) 551838032Speter sm_syslog(LOG_DEBUG, e->e_id, "unlock"); 551938032Speter if (!tTd(51, 104)) 552090792Sgshapiro (void) xunlink(queuename(e, XSCRPT_LETTER)); 552138032Speter} 552290792Sgshapiro/* 552338032Speter** SETCTLUSER -- create a controlling address 552438032Speter** 552538032Speter** Create a fake "address" given only a local login name; this is 552638032Speter** used as a "controlling user" for future recipient addresses. 552738032Speter** 552838032Speter** Parameters: 552938032Speter** user -- the user name of the controlling user. 553090792Sgshapiro** qfver -- the version stamp of this queue file. 553190792Sgshapiro** e -- envelope 553238032Speter** 553338032Speter** Returns: 553490792Sgshapiro** An address descriptor for the controlling user, 553590792Sgshapiro** using storage allocated from e->e_rpool. 553638032Speter** 553738032Speter*/ 553838032Speter 553964562Sgshapirostatic ADDRESS * 554090792Sgshapirosetctluser(user, qfver, e) 554138032Speter char *user; 554238032Speter int qfver; 554390792Sgshapiro ENVELOPE *e; 554438032Speter{ 554538032Speter register ADDRESS *a; 554638032Speter struct passwd *pw; 554738032Speter char *p; 554838032Speter 554938032Speter /* 555038032Speter ** See if this clears our concept of controlling user. 555138032Speter */ 555238032Speter 555338032Speter if (user == NULL || *user == '\0') 555438032Speter return NULL; 555538032Speter 555638032Speter /* 555738032Speter ** Set up addr fields for controlling user. 555838032Speter */ 555938032Speter 5560168515Sgshapiro a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a)); 5561168515Sgshapiro memset((char *) a, '\0', sizeof(*a)); 556238032Speter 556390792Sgshapiro if (*user == ':') 556438032Speter { 556538032Speter p = &user[1]; 556690792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, p); 556738032Speter } 556838032Speter else 556938032Speter { 557038032Speter p = strtok(user, ":"); 557190792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, user); 557238032Speter if (qfver >= 2) 557338032Speter { 557438032Speter if ((p = strtok(NULL, ":")) != NULL) 557538032Speter a->q_uid = atoi(p); 557638032Speter if ((p = strtok(NULL, ":")) != NULL) 557738032Speter a->q_gid = atoi(p); 557838032Speter if ((p = strtok(NULL, ":")) != NULL) 557980785Sgshapiro { 558080785Sgshapiro char *o; 558180785Sgshapiro 558238032Speter a->q_flags |= QGOODUID; 558380785Sgshapiro 558480785Sgshapiro /* if there is another ':': restore it */ 558580785Sgshapiro if ((o = strtok(NULL, ":")) != NULL && o > p) 558680785Sgshapiro o[-1] = ':'; 558780785Sgshapiro } 558838032Speter } 558938032Speter else if ((pw = sm_getpwnam(user)) != NULL) 559038032Speter { 559166494Sgshapiro if (*pw->pw_dir == '\0') 559266494Sgshapiro a->q_home = NULL; 559366494Sgshapiro else if (strcmp(pw->pw_dir, "/") == 0) 559438032Speter a->q_home = ""; 559538032Speter else 559690792Sgshapiro a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir); 559738032Speter a->q_uid = pw->pw_uid; 559838032Speter a->q_gid = pw->pw_gid; 559938032Speter a->q_flags |= QGOODUID; 560038032Speter } 560138032Speter } 560238032Speter 560364562Sgshapiro a->q_flags |= QPRIMARY; /* flag as a "ctladdr" */ 560438032Speter a->q_mailer = LocalMailer; 560538032Speter if (p == NULL) 560690792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user); 560738032Speter else 560890792Sgshapiro a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p); 560938032Speter return a; 561038032Speter} 561190792Sgshapiro/* 561290792Sgshapiro** LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know 561338032Speter** 561438032Speter** Parameters: 561538032Speter** e -- the envelope (e->e_id will be used). 561638032Speter** why -- reported to whomever can hear. 561738032Speter** 561838032Speter** Returns: 561938032Speter** none. 562038032Speter*/ 562138032Speter 562238032Spetervoid 562338032Speterloseqfile(e, why) 562438032Speter register ENVELOPE *e; 562538032Speter char *why; 562638032Speter{ 562790792Sgshapiro bool loseit = true; 562838032Speter char *p; 562964562Sgshapiro char buf[MAXPATHLEN]; 563038032Speter 563138032Speter if (e == NULL || e->e_id == NULL) 563238032Speter return; 563390792Sgshapiro p = queuename(e, ANYQFL_LETTER); 5634168515Sgshapiro if (sm_strlcpy(buf, p, sizeof(buf)) >= sizeof(buf)) 563538032Speter return; 563690792Sgshapiro if (!bitset(EF_INQUEUE, e->e_flags)) 563790792Sgshapiro queueup(e, false, true); 563890792Sgshapiro else if (QueueMode == QM_LOST) 563990792Sgshapiro loseit = false; 564090792Sgshapiro 564190792Sgshapiro /* if already lost, no need to re-lose */ 564290792Sgshapiro if (loseit) 564390792Sgshapiro { 564490792Sgshapiro p = queuename(e, LOSEQF_LETTER); 564590792Sgshapiro if (rename(buf, p) < 0) 564690792Sgshapiro syserr("cannot rename(%s, %s), uid=%d", 564798121Sgshapiro buf, p, (int) geteuid()); 564890792Sgshapiro else if (LogLevel > 0) 564990792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 565090792Sgshapiro "Losing %s: %s", buf, why); 565190792Sgshapiro } 565290792Sgshapiro if (e->e_dfp != NULL) 565390792Sgshapiro { 565490792Sgshapiro (void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT); 565590792Sgshapiro e->e_dfp = NULL; 565690792Sgshapiro } 565790792Sgshapiro e->e_flags &= ~EF_HAS_DF; 565838032Speter} 565990792Sgshapiro/* 566090792Sgshapiro** NAME2QID -- translate a queue group name to a queue group id 566190792Sgshapiro** 566290792Sgshapiro** Parameters: 566390792Sgshapiro** queuename -- name of queue group. 566490792Sgshapiro** 566590792Sgshapiro** Returns: 566690792Sgshapiro** queue group id if found. 566790792Sgshapiro** NOQGRP otherwise. 566890792Sgshapiro*/ 566990792Sgshapiro 567090792Sgshapiroint 567190792Sgshapironame2qid(queuename) 567290792Sgshapiro char *queuename; 567390792Sgshapiro{ 567490792Sgshapiro register STAB *s; 567590792Sgshapiro 567690792Sgshapiro s = stab(queuename, ST_QUEUE, ST_FIND); 567790792Sgshapiro if (s == NULL) 567890792Sgshapiro return NOQGRP; 567990792Sgshapiro return s->s_quegrp->qg_index; 568090792Sgshapiro} 568190792Sgshapiro/* 568264562Sgshapiro** QID_PRINTNAME -- create externally printable version of queue id 568364562Sgshapiro** 568464562Sgshapiro** Parameters: 568564562Sgshapiro** e -- the envelope. 568664562Sgshapiro** 568764562Sgshapiro** Returns: 568864562Sgshapiro** a printable version 568964562Sgshapiro*/ 569064562Sgshapiro 569164562Sgshapirochar * 569264562Sgshapiroqid_printname(e) 569364562Sgshapiro ENVELOPE *e; 569464562Sgshapiro{ 569564562Sgshapiro char *id; 569664562Sgshapiro static char idbuf[MAXQFNAME + 34]; 569764562Sgshapiro 569864562Sgshapiro if (e == NULL) 569964562Sgshapiro return ""; 570064562Sgshapiro 570164562Sgshapiro if (e->e_id == NULL) 570264562Sgshapiro id = ""; 570364562Sgshapiro else 570464562Sgshapiro id = e->e_id; 570564562Sgshapiro 570690792Sgshapiro if (e->e_qdir == NOQDIR) 570764562Sgshapiro return id; 570864562Sgshapiro 5709168515Sgshapiro (void) sm_snprintf(idbuf, sizeof(idbuf), "%.32s/%s", 571090792Sgshapiro Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name, 571190792Sgshapiro id); 571264562Sgshapiro return idbuf; 571364562Sgshapiro} 571490792Sgshapiro/* 571590792Sgshapiro** QID_PRINTQUEUE -- create full version of queue directory for data files 571664562Sgshapiro** 571764562Sgshapiro** Parameters: 571890792Sgshapiro** qgrp -- index in queue group. 571990792Sgshapiro** qdir -- the short version of the queue directory 572064562Sgshapiro** 572164562Sgshapiro** Returns: 572290792Sgshapiro** the full pathname to the queue (might point to a static var) 572364562Sgshapiro*/ 572464562Sgshapiro 572564562Sgshapirochar * 572690792Sgshapiroqid_printqueue(qgrp, qdir) 572790792Sgshapiro int qgrp; 572890792Sgshapiro int qdir; 572964562Sgshapiro{ 573064562Sgshapiro char *subdir; 573164562Sgshapiro static char dir[MAXPATHLEN]; 573264562Sgshapiro 573390792Sgshapiro if (qdir == NOQDIR) 573490792Sgshapiro return Queue[qgrp]->qg_qdir; 573564562Sgshapiro 573690792Sgshapiro if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0) 573764562Sgshapiro subdir = NULL; 573864562Sgshapiro else 573990792Sgshapiro subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name; 574064562Sgshapiro 5741168515Sgshapiro (void) sm_strlcpyn(dir, sizeof(dir), 4, 574290792Sgshapiro Queue[qgrp]->qg_qdir, 574364562Sgshapiro subdir == NULL ? "" : "/", 574464562Sgshapiro subdir == NULL ? "" : subdir, 574590792Sgshapiro (bitset(QP_SUBDF, 574690792Sgshapiro Queue[qgrp]->qg_qpaths[qdir].qp_subdirs) 574790792Sgshapiro ? "/df" : "")); 574864562Sgshapiro return dir; 574964562Sgshapiro} 575090792Sgshapiro 575190792Sgshapiro/* 575290792Sgshapiro** PICKQDIR -- Pick a queue directory from a queue group 575364562Sgshapiro** 575490792Sgshapiro** Parameters: 575590792Sgshapiro** qg -- queue group 575690792Sgshapiro** fsize -- file size in bytes 575790792Sgshapiro** e -- envelope, or NULL 575864562Sgshapiro** 575990792Sgshapiro** Result: 576090792Sgshapiro** NOQDIR if no queue directory in qg has enough free space to 576190792Sgshapiro** hold a file of size 'fsize', otherwise the index of 576290792Sgshapiro** a randomly selected queue directory which resides on a 576390792Sgshapiro** file system with enough disk space. 576490792Sgshapiro** XXX This could be extended to select a queuedir with 576590792Sgshapiro** a few (the fewest?) number of entries. That data 576690792Sgshapiro** is available if shared memory is used. 576764562Sgshapiro** 576890792Sgshapiro** Side Effects: 576990792Sgshapiro** If the request fails and e != NULL then sm_syslog is called. 577090792Sgshapiro*/ 577190792Sgshapiro 577290792Sgshapiroint 577390792Sgshapiropickqdir(qg, fsize, e) 577490792Sgshapiro QUEUEGRP *qg; 577590792Sgshapiro long fsize; 577690792Sgshapiro ENVELOPE *e; 577790792Sgshapiro{ 577890792Sgshapiro int qdir; 577990792Sgshapiro int i; 578090792Sgshapiro long avail = 0; 578190792Sgshapiro 578290792Sgshapiro /* Pick a random directory, as a starting point. */ 578390792Sgshapiro if (qg->qg_numqueues <= 1) 578490792Sgshapiro qdir = 0; 578590792Sgshapiro else 578690792Sgshapiro qdir = get_rand_mod(qg->qg_numqueues); 578790792Sgshapiro 5788203004Sgshapiro#if _FFR_TESTS 5789203004Sgshapiro if (tTd(4, 101)) 5790203004Sgshapiro return NOQDIR; 5791203004Sgshapiro#endif /* _FFR_TESTS */ 579290792Sgshapiro if (MinBlocksFree <= 0 && fsize <= 0) 579390792Sgshapiro return qdir; 579490792Sgshapiro 579590792Sgshapiro /* 579690792Sgshapiro ** Now iterate over the queue directories, 579790792Sgshapiro ** looking for a directory with enough space for this message. 579890792Sgshapiro */ 579990792Sgshapiro 580090792Sgshapiro i = qdir; 580190792Sgshapiro do 580290792Sgshapiro { 580390792Sgshapiro QPATHS *qp = &qg->qg_qpaths[i]; 580490792Sgshapiro long needed = 0; 580590792Sgshapiro long fsavail = 0; 580690792Sgshapiro 580790792Sgshapiro if (fsize > 0) 580890792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx) 580990792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx) 581090792Sgshapiro > 0) ? 1 : 0); 581190792Sgshapiro if (MinBlocksFree > 0) 581290792Sgshapiro needed += MinBlocksFree; 581390792Sgshapiro fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx); 581490792Sgshapiro#if SM_CONF_SHM 581590792Sgshapiro if (fsavail <= 0) 581690792Sgshapiro { 581790792Sgshapiro long blksize; 581890792Sgshapiro 581990792Sgshapiro /* 582090792Sgshapiro ** might be not correctly updated, 582190792Sgshapiro ** let's try to get the info directly. 582290792Sgshapiro */ 582390792Sgshapiro 582490792Sgshapiro fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx), 582590792Sgshapiro &blksize); 582690792Sgshapiro if (fsavail < 0) 582790792Sgshapiro fsavail = 0; 582890792Sgshapiro } 582990792Sgshapiro#endif /* SM_CONF_SHM */ 583090792Sgshapiro if (needed <= fsavail) 583190792Sgshapiro return i; 583290792Sgshapiro if (avail < fsavail) 583390792Sgshapiro avail = fsavail; 583490792Sgshapiro 583590792Sgshapiro if (qg->qg_numqueues > 0) 583690792Sgshapiro i = (i + 1) % qg->qg_numqueues; 583790792Sgshapiro } while (i != qdir); 583890792Sgshapiro 583990792Sgshapiro if (e != NULL && LogLevel > 0) 584090792Sgshapiro sm_syslog(LOG_ALERT, e->e_id, 584190792Sgshapiro "low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld", 584290792Sgshapiro CurHostName == NULL ? "SMTP-DAEMON" : CurHostName, 584390792Sgshapiro fsize, MinBlocksFree, 584490792Sgshapiro qg->qg_qdir, avail); 584590792Sgshapiro return NOQDIR; 584690792Sgshapiro} 584790792Sgshapiro/* 584890792Sgshapiro** SETNEWQUEUE -- Sets a new queue group and directory 584990792Sgshapiro** 585090792Sgshapiro** Assign a queue group and directory to an envelope and store the 585190792Sgshapiro** directory in e->e_qdir. 585290792Sgshapiro** 585364562Sgshapiro** Parameters: 585464562Sgshapiro** e -- envelope to assign a queue for. 585564562Sgshapiro** 585664562Sgshapiro** Returns: 585790792Sgshapiro** true if successful 585890792Sgshapiro** false otherwise 585990792Sgshapiro** 586090792Sgshapiro** Side Effects: 586190792Sgshapiro** On success, e->e_qgrp and e->e_qdir are non-negative. 586290792Sgshapiro** On failure (not enough disk space), 586390792Sgshapiro** e->qgrp = NOQGRP, e->e_qdir = NOQDIR 586490792Sgshapiro** and usrerr() is invoked (which could raise an exception). 586564562Sgshapiro*/ 586664562Sgshapiro 586790792Sgshapirobool 586864562Sgshapirosetnewqueue(e) 586964562Sgshapiro ENVELOPE *e; 587064562Sgshapiro{ 587164562Sgshapiro if (tTd(41, 20)) 587290792Sgshapiro sm_dprintf("setnewqueue: called\n"); 587364562Sgshapiro 587490792Sgshapiro /* not set somewhere else */ 587590792Sgshapiro if (e->e_qgrp == NOQGRP) 587664562Sgshapiro { 5877102528Sgshapiro ADDRESS *q; 5878102528Sgshapiro 587990792Sgshapiro /* 5880102528Sgshapiro ** Use the queue group of the "first" recipient, as set by 588190792Sgshapiro ** the "queuegroup" rule set. If that is not defined, then 588290792Sgshapiro ** use the queue group of the mailer of the first recipient. 588390792Sgshapiro ** If that is not defined either, then use the default 588490792Sgshapiro ** queue group. 5885102528Sgshapiro ** Notice: "first" depends on the sorting of sendqueue 5886102528Sgshapiro ** in recipient(). 5887102528Sgshapiro ** To avoid problems with "bad" recipients look 5888102528Sgshapiro ** for a valid address first. 588990792Sgshapiro */ 589090792Sgshapiro 5891102528Sgshapiro q = e->e_sendqueue; 5892102528Sgshapiro while (q != NULL && 5893102528Sgshapiro (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state))) 5894102528Sgshapiro { 5895102528Sgshapiro q = q->q_next; 5896102528Sgshapiro } 5897102528Sgshapiro if (q == NULL) 589890792Sgshapiro e->e_qgrp = 0; 5899102528Sgshapiro else if (q->q_qgrp >= 0) 5900102528Sgshapiro e->e_qgrp = q->q_qgrp; 5901102528Sgshapiro else if (q->q_mailer != NULL && 5902102528Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 5903102528Sgshapiro e->e_qgrp = q->q_mailer->m_qgrp; 590490792Sgshapiro else 590590792Sgshapiro e->e_qgrp = 0; 590690792Sgshapiro e->e_dfqgrp = e->e_qgrp; 590790792Sgshapiro } 590890792Sgshapiro 590990792Sgshapiro if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir)) 591090792Sgshapiro { 591164562Sgshapiro if (tTd(41, 20)) 591290792Sgshapiro sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n", 591390792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 591490792Sgshapiro return true; 591564562Sgshapiro } 591664562Sgshapiro 591790792Sgshapiro filesys_update(); 591890792Sgshapiro e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e); 591990792Sgshapiro if (e->e_qdir == NOQDIR) 592064562Sgshapiro { 592190792Sgshapiro e->e_qgrp = NOQGRP; 592290792Sgshapiro if (!bitset(EF_FATALERRS, e->e_flags)) 592390792Sgshapiro usrerr("452 4.4.5 Insufficient disk space; try again later"); 592490792Sgshapiro e->e_flags |= EF_FATALERRS; 592590792Sgshapiro return false; 592664562Sgshapiro } 592764562Sgshapiro 592864562Sgshapiro if (tTd(41, 3)) 592990792Sgshapiro sm_dprintf("setnewqueue: Assigned queue directory %s\n", 593090792Sgshapiro qid_printqueue(e->e_qgrp, e->e_qdir)); 593190792Sgshapiro 593290792Sgshapiro if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR) 593390792Sgshapiro { 593490792Sgshapiro e->e_xfqgrp = e->e_qgrp; 593590792Sgshapiro e->e_xfqdir = e->e_qdir; 593690792Sgshapiro } 593790792Sgshapiro e->e_dfqdir = e->e_qdir; 593890792Sgshapiro return true; 593964562Sgshapiro} 594090792Sgshapiro/* 594164562Sgshapiro** CHKQDIR -- check a queue directory 594264562Sgshapiro** 594364562Sgshapiro** Parameters: 594464562Sgshapiro** name -- name of queue directory 594564562Sgshapiro** sff -- flags for safefile() 594664562Sgshapiro** 594764562Sgshapiro** Returns: 594864562Sgshapiro** is it a queue directory? 594964562Sgshapiro*/ 595064562Sgshapiro 5951168515Sgshapirostatic bool chkqdir __P((char *, long)); 5952168515Sgshapiro 595364562Sgshapirostatic bool 595464562Sgshapirochkqdir(name, sff) 595564562Sgshapiro char *name; 595664562Sgshapiro long sff; 595764562Sgshapiro{ 595864562Sgshapiro struct stat statb; 595964562Sgshapiro int i; 596064562Sgshapiro 596166494Sgshapiro /* skip over . and .. directories */ 596266494Sgshapiro if (name[0] == '.' && 596377349Sgshapiro (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) 596490792Sgshapiro return false; 596590792Sgshapiro#if HASLSTAT 596664562Sgshapiro if (lstat(name, &statb) < 0) 596790792Sgshapiro#else /* HASLSTAT */ 596864562Sgshapiro if (stat(name, &statb) < 0) 596990792Sgshapiro#endif /* HASLSTAT */ 597064562Sgshapiro { 597164562Sgshapiro if (tTd(41, 2)) 597290792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 597390792Sgshapiro name, sm_errstring(errno)); 597490792Sgshapiro return false; 597564562Sgshapiro } 597690792Sgshapiro#if HASLSTAT 597764562Sgshapiro if (S_ISLNK(statb.st_mode)) 597864562Sgshapiro { 597964562Sgshapiro /* 598064562Sgshapiro ** For a symlink we need to make sure the 598164562Sgshapiro ** target is a directory 598264562Sgshapiro */ 598390792Sgshapiro 598464562Sgshapiro if (stat(name, &statb) < 0) 598564562Sgshapiro { 598664562Sgshapiro if (tTd(41, 2)) 598790792Sgshapiro sm_dprintf("chkqdir: stat(\"%s\"): %s\n", 598890792Sgshapiro name, sm_errstring(errno)); 598990792Sgshapiro return false; 599064562Sgshapiro } 599164562Sgshapiro } 599290792Sgshapiro#endif /* HASLSTAT */ 599364562Sgshapiro 599464562Sgshapiro if (!S_ISDIR(statb.st_mode)) 599564562Sgshapiro { 599664562Sgshapiro if (tTd(41, 2)) 599790792Sgshapiro sm_dprintf("chkqdir: \"%s\": Not a directory\n", 599864562Sgshapiro name); 599990792Sgshapiro return false; 600064562Sgshapiro } 600164562Sgshapiro 600264562Sgshapiro /* Print a warning if unsafe (but still use it) */ 600390792Sgshapiro /* XXX do this only if we want the warning? */ 600464562Sgshapiro i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0); 600598121Sgshapiro if (i != 0) 600698121Sgshapiro { 600798121Sgshapiro if (tTd(41, 2)) 600898121Sgshapiro sm_dprintf("chkqdir: \"%s\": Not safe: %s\n", 600998121Sgshapiro name, sm_errstring(i)); 601098121Sgshapiro#if _FFR_CHK_QUEUE 601198121Sgshapiro if (LogLevel > 8) 601298121Sgshapiro sm_syslog(LOG_WARNING, NOQID, 601398121Sgshapiro "queue directory \"%s\": Not safe: %s", 601498121Sgshapiro name, sm_errstring(i)); 601598121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 601698121Sgshapiro } 601790792Sgshapiro return true; 601864562Sgshapiro} 601990792Sgshapiro/* 602064562Sgshapiro** MULTIQUEUE_CACHE -- cache a list of paths to queues. 602164562Sgshapiro** 602264562Sgshapiro** Each potential queue is checked as the cache is built. 602364562Sgshapiro** Thereafter, each is blindly trusted. 602464562Sgshapiro** Note that we can be called again after a timeout to rebuild 602564562Sgshapiro** (although code for that is not ready yet). 602664562Sgshapiro** 602764562Sgshapiro** Parameters: 602890792Sgshapiro** basedir -- base of all queue directories. 602990792Sgshapiro** blen -- strlen(basedir). 603090792Sgshapiro** qg -- queue group. 603190792Sgshapiro** qn -- number of queue directories already cached. 603290792Sgshapiro** phash -- pointer to hash value over queue dirs. 603390792Sgshapiro#if SM_CONF_SHM 603490792Sgshapiro** only used if shared memory is active. 603590792Sgshapiro#endif * SM_CONF_SHM * 603664562Sgshapiro** 603764562Sgshapiro** Returns: 603890792Sgshapiro** new number of queue directories. 603964562Sgshapiro*/ 604064562Sgshapiro 604190792Sgshapiro#define INITIAL_SLOTS 20 604290792Sgshapiro#define ADD_SLOTS 10 604390792Sgshapiro 604490792Sgshapirostatic int 604590792Sgshapiromultiqueue_cache(basedir, blen, qg, qn, phash) 604690792Sgshapiro char *basedir; 604790792Sgshapiro int blen; 604890792Sgshapiro QUEUEGRP *qg; 604990792Sgshapiro int qn; 605090792Sgshapiro unsigned int *phash; 605164562Sgshapiro{ 605264562Sgshapiro char *cp; 605364562Sgshapiro int i, len; 605464562Sgshapiro int slotsleft = 0; 605564562Sgshapiro long sff = SFF_ANYFILE; 605664562Sgshapiro char qpath[MAXPATHLEN]; 605764562Sgshapiro char subdir[MAXPATHLEN]; 605890792Sgshapiro char prefix[MAXPATHLEN]; /* dir relative to basedir */ 605964562Sgshapiro 606064562Sgshapiro if (tTd(41, 20)) 606190792Sgshapiro sm_dprintf("multiqueue_cache: called\n"); 606264562Sgshapiro 606390792Sgshapiro /* Initialize to current directory */ 606490792Sgshapiro prefix[0] = '.'; 606590792Sgshapiro prefix[1] = '\0'; 606690792Sgshapiro if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL) 606764562Sgshapiro { 606890792Sgshapiro for (i = 0; i < qg->qg_numqueues; i++) 606964562Sgshapiro { 607090792Sgshapiro if (qg->qg_qpaths[i].qp_name != NULL) 607190792Sgshapiro (void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */ 607264562Sgshapiro } 607390792Sgshapiro (void) sm_free((char *) qg->qg_qpaths); /* XXX */ 607490792Sgshapiro qg->qg_qpaths = NULL; 607590792Sgshapiro qg->qg_numqueues = 0; 607664562Sgshapiro } 607764562Sgshapiro 607864562Sgshapiro /* If running as root, allow safedirpath() checks to use privs */ 607964562Sgshapiro if (RunAsUid == 0) 608064562Sgshapiro sff |= SFF_ROOTOK; 608198121Sgshapiro#if _FFR_CHK_QUEUE 608298121Sgshapiro sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES; 608398121Sgshapiro if (!UseMSP) 608498121Sgshapiro sff |= SFF_NOGWFILES; 608598121Sgshapiro#endif /* _FFR_CHK_QUEUE */ 608664562Sgshapiro 608790792Sgshapiro if (!SM_IS_DIR_START(qg->qg_qdir)) 608890792Sgshapiro { 608990792Sgshapiro /* 609090792Sgshapiro ** XXX we could add basedir, but then we have to realloc() 609190792Sgshapiro ** the string... Maybe another time. 609290792Sgshapiro */ 609390792Sgshapiro 609490792Sgshapiro syserr("QueuePath %s not absolute", qg->qg_qdir); 609590792Sgshapiro ExitStat = EX_CONFIG; 609690792Sgshapiro return qn; 609790792Sgshapiro } 609890792Sgshapiro 609990792Sgshapiro /* qpath: directory of current workgroup */ 6100168515Sgshapiro len = sm_strlcpy(qpath, qg->qg_qdir, sizeof(qpath)); 6101168515Sgshapiro if (len >= sizeof(qpath)) 610290792Sgshapiro { 610390792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 6104168515Sgshapiro qg->qg_qdir, (int) sizeof(qpath)); 610590792Sgshapiro ExitStat = EX_CONFIG; 610690792Sgshapiro return qn; 610790792Sgshapiro } 610890792Sgshapiro 610990792Sgshapiro /* begin of qpath must be same as basedir */ 611090792Sgshapiro if (strncmp(basedir, qpath, blen) != 0 && 611190792Sgshapiro (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1)) 611290792Sgshapiro { 611390792Sgshapiro syserr("QueuePath %s not subpath of QueueDirectory %s", 611490792Sgshapiro qpath, basedir); 611590792Sgshapiro ExitStat = EX_CONFIG; 611690792Sgshapiro return qn; 611790792Sgshapiro } 611890792Sgshapiro 611990792Sgshapiro /* Do we have a nested subdirectory? */ 612090792Sgshapiro if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL) 612190792Sgshapiro { 612290792Sgshapiro 612390792Sgshapiro /* Copy subdirectory into prefix for later use */ 6124168515Sgshapiro if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof(prefix)) >= 6125168515Sgshapiro sizeof(prefix)) 612690792Sgshapiro { 612790792Sgshapiro syserr("QueuePath %.256s too long (%d max)", 6128168515Sgshapiro qg->qg_qdir, (int) sizeof(qpath)); 612990792Sgshapiro ExitStat = EX_CONFIG; 613090792Sgshapiro return qn; 613190792Sgshapiro } 613290792Sgshapiro cp = SM_LAST_DIR_DELIM(prefix); 613390792Sgshapiro SM_ASSERT(cp != NULL); 613490792Sgshapiro *cp = '\0'; /* cut off trailing / */ 613590792Sgshapiro } 613690792Sgshapiro 613790792Sgshapiro /* This is guaranteed by the basedir check above */ 613890792Sgshapiro SM_ASSERT(len >= blen - 1); 613990792Sgshapiro cp = &qpath[len - 1]; 614064562Sgshapiro if (*cp == '*') 614164562Sgshapiro { 614290792Sgshapiro register DIR *dp; 614390792Sgshapiro register struct dirent *d; 614490792Sgshapiro int off; 614590792Sgshapiro char *delim; 614690792Sgshapiro char relpath[MAXPATHLEN]; 614790792Sgshapiro 614890792Sgshapiro *cp = '\0'; /* Overwrite wildcard */ 614990792Sgshapiro if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL) 615064562Sgshapiro { 615164562Sgshapiro syserr("QueueDirectory: can not wildcard relative path"); 615264562Sgshapiro if (tTd(41, 2)) 615390792Sgshapiro sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n", 615471345Sgshapiro qpath); 615564562Sgshapiro ExitStat = EX_CONFIG; 615690792Sgshapiro return qn; 615764562Sgshapiro } 615864562Sgshapiro if (cp == qpath) 615964562Sgshapiro { 616064562Sgshapiro /* 616164562Sgshapiro ** Special case of top level wildcard, like /foo* 616290792Sgshapiro ** Change to //foo* 616364562Sgshapiro */ 616464562Sgshapiro 6165168515Sgshapiro (void) sm_strlcpy(qpath + 1, qpath, sizeof(qpath) - 1); 616664562Sgshapiro ++cp; 616764562Sgshapiro } 616890792Sgshapiro delim = cp; 616990792Sgshapiro *(cp++) = '\0'; /* Replace / with \0 */ 617090792Sgshapiro len = strlen(cp); /* Last component of queue directory */ 617164562Sgshapiro 617290792Sgshapiro /* 617390792Sgshapiro ** Path relative to basedir, with trailing / 617490792Sgshapiro ** It will be modified below to specify the subdirectories 617590792Sgshapiro ** so they can be opened without chdir(). 617690792Sgshapiro */ 617790792Sgshapiro 6178168515Sgshapiro off = sm_strlcpyn(relpath, sizeof(relpath), 2, prefix, "/"); 6179168515Sgshapiro SM_ASSERT(off < sizeof(relpath)); 618090792Sgshapiro 618164562Sgshapiro if (tTd(41, 2)) 618290792Sgshapiro sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n", 618390792Sgshapiro relpath, cp); 618464562Sgshapiro 618590792Sgshapiro /* It is always basedir: we don't need to store it per group */ 618690792Sgshapiro /* XXX: optimize this! -> one more global? */ 618790792Sgshapiro qg->qg_qdir = newstr(basedir); 618890792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; /* cut off trailing / */ 618964562Sgshapiro 619064562Sgshapiro /* 619164562Sgshapiro ** XXX Should probably wrap this whole loop in a timeout 619264562Sgshapiro ** in case some wag decides to NFS mount the queues. 619364562Sgshapiro */ 619464562Sgshapiro 619590792Sgshapiro /* Test path to get warning messages. */ 619690792Sgshapiro if (qn == 0) 619764562Sgshapiro { 619890792Sgshapiro /* XXX qg_runasuid and qg_runasgid for specials? */ 619990792Sgshapiro i = safedirpath(basedir, RunAsUid, RunAsGid, NULL, 620090792Sgshapiro sff, 0, 0); 620190792Sgshapiro if (i != 0 && tTd(41, 2)) 620290792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n", 620390792Sgshapiro basedir, sm_errstring(i)); 620464562Sgshapiro } 620564562Sgshapiro 620690792Sgshapiro if ((dp = opendir(prefix)) == NULL) 620764562Sgshapiro { 620890792Sgshapiro syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix); 620964562Sgshapiro if (tTd(41, 2)) 621090792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n", 621190792Sgshapiro qg->qg_qdir, prefix, 621290792Sgshapiro sm_errstring(errno)); 621364562Sgshapiro ExitStat = EX_CONFIG; 621490792Sgshapiro return qn; 621564562Sgshapiro } 621664562Sgshapiro while ((d = readdir(dp)) != NULL) 621764562Sgshapiro { 6218168515Sgshapiro /* Skip . and .. directories */ 6219168515Sgshapiro if (strcmp(d->d_name, ".") == 0 || 6220168515Sgshapiro strcmp(d->d_name, "..") == 0) 6221168515Sgshapiro continue; 6222168515Sgshapiro 622390792Sgshapiro i = strlen(d->d_name); 622490792Sgshapiro if (i < len || strncmp(d->d_name, cp, len) != 0) 622564562Sgshapiro { 622664562Sgshapiro if (tTd(41, 5)) 622790792Sgshapiro sm_dprintf("multiqueue_cache: \"%s\", skipped\n", 622864562Sgshapiro d->d_name); 622964562Sgshapiro continue; 623064562Sgshapiro } 623190792Sgshapiro 623290792Sgshapiro /* Create relative pathname: prefix + local directory */ 623390792Sgshapiro i = sizeof(relpath) - off; 623490792Sgshapiro if (sm_strlcpy(relpath + off, d->d_name, i) >= i) 623590792Sgshapiro continue; /* way too long */ 623690792Sgshapiro 623790792Sgshapiro if (!chkqdir(relpath, sff)) 623864562Sgshapiro continue; 623964562Sgshapiro 624090792Sgshapiro if (qg->qg_qpaths == NULL) 624164562Sgshapiro { 624290792Sgshapiro slotsleft = INITIAL_SLOTS; 6243168515Sgshapiro qg->qg_qpaths = (QPATHS *)xalloc((sizeof(*qg->qg_qpaths)) * 624490792Sgshapiro slotsleft); 624590792Sgshapiro qg->qg_numqueues = 0; 624664562Sgshapiro } 624764562Sgshapiro else if (slotsleft < 1) 624864562Sgshapiro { 624990792Sgshapiro qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths, 6250168515Sgshapiro (sizeof(*qg->qg_qpaths)) * 625190792Sgshapiro (qg->qg_numqueues + 625290792Sgshapiro ADD_SLOTS)); 625390792Sgshapiro if (qg->qg_qpaths == NULL) 625464562Sgshapiro { 625564562Sgshapiro (void) closedir(dp); 625690792Sgshapiro return qn; 625764562Sgshapiro } 625890792Sgshapiro slotsleft += ADD_SLOTS; 625964562Sgshapiro } 626064562Sgshapiro 626164562Sgshapiro /* check subdirs */ 626290792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB; 626364562Sgshapiro 626490792Sgshapiro#define CHKRSUBDIR(name, flag) \ 6265168515Sgshapiro (void) sm_strlcpyn(subdir, sizeof(subdir), 3, relpath, "/", name); \ 626690792Sgshapiro if (chkqdir(subdir, sff)) \ 626790792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag; \ 626890792Sgshapiro else 626964562Sgshapiro 627064562Sgshapiro 627190792Sgshapiro CHKRSUBDIR("qf", QP_SUBQF); 627290792Sgshapiro CHKRSUBDIR("df", QP_SUBDF); 627390792Sgshapiro CHKRSUBDIR("xf", QP_SUBXF); 627490792Sgshapiro 627564562Sgshapiro /* assert(strlen(d->d_name) < MAXPATHLEN - 14) */ 627664562Sgshapiro /* maybe even - 17 (subdirs) */ 627790792Sgshapiro 627890792Sgshapiro if (prefix[0] != '.') 627990792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 628090792Sgshapiro newstr(relpath); 628190792Sgshapiro else 628290792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_name = 628390792Sgshapiro newstr(d->d_name); 628490792Sgshapiro 628564562Sgshapiro if (tTd(41, 2)) 628690792Sgshapiro sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n", 628790792Sgshapiro qg->qg_numqueues, relpath, 628890792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_subdirs); 628990792Sgshapiro#if SM_CONF_SHM 629090792Sgshapiro qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn; 629190792Sgshapiro *phash = hash_q(relpath, *phash); 629290792Sgshapiro#endif /* SM_CONF_SHM */ 629390792Sgshapiro qg->qg_numqueues++; 629490792Sgshapiro ++qn; 629564562Sgshapiro slotsleft--; 629664562Sgshapiro } 629764562Sgshapiro (void) closedir(dp); 629890792Sgshapiro 629990792Sgshapiro /* undo damage */ 630090792Sgshapiro *delim = '/'; 630164562Sgshapiro } 630290792Sgshapiro if (qg->qg_numqueues == 0) 630364562Sgshapiro { 6304168515Sgshapiro qg->qg_qpaths = (QPATHS *) xalloc(sizeof(*qg->qg_qpaths)); 630564562Sgshapiro 630664562Sgshapiro /* test path to get warning messages */ 630790792Sgshapiro i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0); 630890792Sgshapiro if (i == ENOENT) 630964562Sgshapiro { 631090792Sgshapiro syserr("can not opendir(%s)", qpath); 631164562Sgshapiro if (tTd(41, 2)) 631290792Sgshapiro sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n", 631390792Sgshapiro qpath, sm_errstring(i)); 631464562Sgshapiro ExitStat = EX_CONFIG; 631590792Sgshapiro return qn; 631664562Sgshapiro } 631764562Sgshapiro 631890792Sgshapiro qg->qg_qpaths[0].qp_subdirs = QP_NOSUB; 631990792Sgshapiro qg->qg_numqueues = 1; 632090792Sgshapiro 632164562Sgshapiro /* check subdirs */ 632290792Sgshapiro#define CHKSUBDIR(name, flag) \ 6323168515Sgshapiro (void) sm_strlcpyn(subdir, sizeof(subdir), 3, qg->qg_qdir, "/", name); \ 632490792Sgshapiro if (chkqdir(subdir, sff)) \ 632590792Sgshapiro qg->qg_qpaths[0].qp_subdirs |= flag; \ 632690792Sgshapiro else 632764562Sgshapiro 632890792Sgshapiro CHKSUBDIR("qf", QP_SUBQF); 632990792Sgshapiro CHKSUBDIR("df", QP_SUBDF); 633090792Sgshapiro CHKSUBDIR("xf", QP_SUBXF); 633164562Sgshapiro 633290792Sgshapiro if (qg->qg_qdir[blen - 1] != '\0' && 633390792Sgshapiro qg->qg_qdir[blen] != '\0') 633490792Sgshapiro { 633590792Sgshapiro /* 633690792Sgshapiro ** Copy the last component into qpaths and 633790792Sgshapiro ** cut off qdir 633890792Sgshapiro */ 633990792Sgshapiro 634090792Sgshapiro qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen); 634190792Sgshapiro qg->qg_qdir[blen - 1] = '\0'; 634290792Sgshapiro } 634390792Sgshapiro else 634490792Sgshapiro qg->qg_qpaths[0].qp_name = newstr("."); 634590792Sgshapiro 634690792Sgshapiro#if SM_CONF_SHM 634790792Sgshapiro qg->qg_qpaths[0].qp_idx = qn; 634890792Sgshapiro *phash = hash_q(qg->qg_qpaths[0].qp_name, *phash); 634990792Sgshapiro#endif /* SM_CONF_SHM */ 635090792Sgshapiro ++qn; 635164562Sgshapiro } 635290792Sgshapiro return qn; 635364562Sgshapiro} 635464562Sgshapiro 635590792Sgshapiro/* 635690792Sgshapiro** FILESYS_FIND -- find entry in FileSys table, or add new one 635790792Sgshapiro** 635890792Sgshapiro** Given the pathname of a directory, determine the file system 635990792Sgshapiro** in which that directory resides, and return a pointer to the 636090792Sgshapiro** entry in the FileSys table that describes the file system. 636190792Sgshapiro** A new entry is added if necessary (and requested). 636290792Sgshapiro** If the directory does not exist, -1 is returned. 636390792Sgshapiro** 636490792Sgshapiro** Parameters: 6365157001Sgshapiro** name -- name of directory (must be persistent!) 6366157001Sgshapiro** path -- pathname of directory (name plus maybe "/df") 636790792Sgshapiro** add -- add to structure if not found. 636890792Sgshapiro** 636990792Sgshapiro** Returns: 637090792Sgshapiro** >=0: found: index in file system table 637190792Sgshapiro** <0: some error, i.e., 637290792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 637390792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 637490792Sgshapiro** FSF_NOT_FOUND: not in list 637590792Sgshapiro*/ 637690792Sgshapiro 6377168515Sgshapirostatic short filesys_find __P((const char *, const char *, bool)); 637890792Sgshapiro 637990792Sgshapiro#define FSF_NOT_FOUND (-1) 638090792Sgshapiro#define FSF_STAT_FAIL (-2) 638190792Sgshapiro#define FSF_TOO_MANY (-3) 638290792Sgshapiro 638390792Sgshapirostatic short 6384157001Sgshapirofilesys_find(name, path, add) 6385168515Sgshapiro const char *name; 6386168515Sgshapiro const char *path; 638790792Sgshapiro bool add; 638890792Sgshapiro{ 638990792Sgshapiro struct stat st; 639090792Sgshapiro short i; 639190792Sgshapiro 639290792Sgshapiro if (stat(path, &st) < 0) 639390792Sgshapiro { 639490792Sgshapiro syserr("cannot stat queue directory %s", path); 639590792Sgshapiro return FSF_STAT_FAIL; 639690792Sgshapiro } 639790792Sgshapiro for (i = 0; i < NumFileSys; ++i) 639890792Sgshapiro { 639990792Sgshapiro if (FILE_SYS_DEV(i) == st.st_dev) 6400161389Sgshapiro { 6401161389Sgshapiro /* 6402161389Sgshapiro ** Make sure the file system (FS) name is set: 6403161389Sgshapiro ** even though the source code indicates that 6404161389Sgshapiro ** FILE_SYS_DEV() is only set below, it could be 6405161389Sgshapiro ** set via shared memory, hence we need to perform 6406161389Sgshapiro ** this check/assignment here. 6407161389Sgshapiro */ 6408161389Sgshapiro 6409161389Sgshapiro if (NULL == FILE_SYS_NAME(i)) 6410161389Sgshapiro FILE_SYS_NAME(i) = name; 641190792Sgshapiro return i; 6412161389Sgshapiro } 641390792Sgshapiro } 641490792Sgshapiro if (i >= MAXFILESYS) 641590792Sgshapiro { 641690792Sgshapiro syserr("too many queue file systems (%d max)", MAXFILESYS); 641790792Sgshapiro return FSF_TOO_MANY; 641890792Sgshapiro } 641990792Sgshapiro if (!add) 642090792Sgshapiro return FSF_NOT_FOUND; 642190792Sgshapiro 642290792Sgshapiro ++NumFileSys; 6423157001Sgshapiro FILE_SYS_NAME(i) = name; 642490792Sgshapiro FILE_SYS_DEV(i) = st.st_dev; 642590792Sgshapiro FILE_SYS_AVAIL(i) = 0; 642690792Sgshapiro FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */ 642790792Sgshapiro return i; 642890792Sgshapiro} 642990792Sgshapiro 643090792Sgshapiro/* 643190792Sgshapiro** FILESYS_SETUP -- set up mapping from queue directories to file systems 643290792Sgshapiro** 643390792Sgshapiro** This data structure is used to efficiently check the amount of 643490792Sgshapiro** free space available in a set of queue directories. 643590792Sgshapiro** 643690792Sgshapiro** Parameters: 643790792Sgshapiro** add -- initialize structure if necessary. 643890792Sgshapiro** 643990792Sgshapiro** Returns: 644090792Sgshapiro** 0: success 644190792Sgshapiro** <0: some error, i.e., 644290792Sgshapiro** FSF_NOT_FOUND: not in list 644390792Sgshapiro** FSF_STAT_FAIL: can't stat() filesystem (-> syserr()) 644490792Sgshapiro** FSF_TOO_MANY: too many filesystems (-> syserr()) 644590792Sgshapiro*/ 644690792Sgshapiro 644790792Sgshapirostatic int filesys_setup __P((bool)); 644890792Sgshapiro 644990792Sgshapirostatic int 645090792Sgshapirofilesys_setup(add) 645190792Sgshapiro bool add; 645290792Sgshapiro{ 645390792Sgshapiro int i, j; 645490792Sgshapiro short fs; 645590792Sgshapiro int ret; 645690792Sgshapiro 645790792Sgshapiro ret = 0; 645890792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 645990792Sgshapiro { 646090792Sgshapiro for (j = 0; j < Queue[i]->qg_numqueues; ++j) 646190792Sgshapiro { 646290792Sgshapiro QPATHS *qp = &Queue[i]->qg_qpaths[j]; 6463157001Sgshapiro char qddf[MAXPATHLEN]; 646490792Sgshapiro 6465168515Sgshapiro (void) sm_strlcpyn(qddf, sizeof(qddf), 2, qp->qp_name, 6466157001Sgshapiro (bitset(QP_SUBDF, qp->qp_subdirs) 6467157001Sgshapiro ? "/df" : "")); 6468157001Sgshapiro fs = filesys_find(qp->qp_name, qddf, add); 646990792Sgshapiro if (fs >= 0) 647090792Sgshapiro qp->qp_fsysidx = fs; 647190792Sgshapiro else 647290792Sgshapiro qp->qp_fsysidx = 0; 647390792Sgshapiro if (fs < ret) 647490792Sgshapiro ret = fs; 647590792Sgshapiro } 647690792Sgshapiro } 647790792Sgshapiro return ret; 647890792Sgshapiro} 647990792Sgshapiro 648090792Sgshapiro/* 648190792Sgshapiro** FILESYS_UPDATE -- update amount of free space on all file systems 648290792Sgshapiro** 648390792Sgshapiro** The FileSys table is used to cache the amount of free space 648490792Sgshapiro** available on all queue directory file systems. 648590792Sgshapiro** This function updates the cached information if it has expired. 648690792Sgshapiro** 648790792Sgshapiro** Parameters: 648890792Sgshapiro** none. 648990792Sgshapiro** 649090792Sgshapiro** Returns: 649190792Sgshapiro** none. 649290792Sgshapiro** 649390792Sgshapiro** Side Effects: 649490792Sgshapiro** Updates FileSys table. 649590792Sgshapiro*/ 649690792Sgshapiro 649790792Sgshapirovoid 649890792Sgshapirofilesys_update() 649990792Sgshapiro{ 650090792Sgshapiro int i; 650190792Sgshapiro long avail, blksize; 650290792Sgshapiro time_t now; 650390792Sgshapiro static time_t nextupdate = 0; 650490792Sgshapiro 650590792Sgshapiro#if SM_CONF_SHM 6506168515Sgshapiro /* 6507168515Sgshapiro ** Only the daemon updates the shared memory, i.e., 6508168515Sgshapiro ** if shared memory is available but the pid is not the 6509168515Sgshapiro ** one of the daemon, then don't do anything. 6510168515Sgshapiro */ 6511168515Sgshapiro 6512161389Sgshapiro if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid) 651390792Sgshapiro return; 651490792Sgshapiro#endif /* SM_CONF_SHM */ 651590792Sgshapiro now = curtime(); 651690792Sgshapiro if (now < nextupdate) 651790792Sgshapiro return; 651890792Sgshapiro nextupdate = now + FILESYS_UPDATE_INTERVAL; 651990792Sgshapiro for (i = 0; i < NumFileSys; ++i) 652090792Sgshapiro { 652190792Sgshapiro FILESYS *fs = &FILE_SYS(i); 652290792Sgshapiro 652390792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 652490792Sgshapiro if (avail < 0 || blksize <= 0) 652590792Sgshapiro { 652690792Sgshapiro if (LogLevel > 5) 652790792Sgshapiro sm_syslog(LOG_ERR, NOQID, 652890792Sgshapiro "filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld", 652990792Sgshapiro sm_errstring(errno), 653090792Sgshapiro FILE_SYS_NAME(i), avail, blksize); 653190792Sgshapiro fs->fs_avail = 0; 653290792Sgshapiro fs->fs_blksize = 1024; /* avoid divide by zero */ 653390792Sgshapiro nextupdate = now + 2; /* let's do this soon again */ 653490792Sgshapiro } 653590792Sgshapiro else 653690792Sgshapiro { 653790792Sgshapiro fs->fs_avail = avail; 653890792Sgshapiro fs->fs_blksize = blksize; 653990792Sgshapiro } 654090792Sgshapiro } 654190792Sgshapiro} 654290792Sgshapiro 654390792Sgshapiro#if _FFR_ANY_FREE_FS 654490792Sgshapiro/* 654590792Sgshapiro** FILESYS_FREE -- check whether there is at least one fs with enough space. 654690792Sgshapiro** 654790792Sgshapiro** Parameters: 654890792Sgshapiro** fsize -- file size in bytes 654990792Sgshapiro** 655090792Sgshapiro** Returns: 655190792Sgshapiro** true iff there is one fs with more than fsize bytes free. 655290792Sgshapiro*/ 655390792Sgshapiro 655490792Sgshapirobool 655590792Sgshapirofilesys_free(fsize) 655690792Sgshapiro long fsize; 655790792Sgshapiro{ 655890792Sgshapiro int i; 655990792Sgshapiro 656090792Sgshapiro if (fsize <= 0) 656190792Sgshapiro return true; 656290792Sgshapiro for (i = 0; i < NumFileSys; ++i) 656390792Sgshapiro { 656490792Sgshapiro long needed = 0; 656590792Sgshapiro 656690792Sgshapiro if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0) 656790792Sgshapiro continue; 656890792Sgshapiro needed += fsize / FILE_SYS_BLKSIZE(i) 656990792Sgshapiro + ((fsize % FILE_SYS_BLKSIZE(i) 657090792Sgshapiro > 0) ? 1 : 0) 657190792Sgshapiro + MinBlocksFree; 657290792Sgshapiro if (needed <= FILE_SYS_AVAIL(i)) 657390792Sgshapiro return true; 657490792Sgshapiro } 657590792Sgshapiro return false; 657690792Sgshapiro} 657790792Sgshapiro#endif /* _FFR_ANY_FREE_FS */ 657890792Sgshapiro 657990792Sgshapiro/* 658090792Sgshapiro** DISK_STATUS -- show amount of free space in queue directories 658190792Sgshapiro** 658290792Sgshapiro** Parameters: 658390792Sgshapiro** out -- output file pointer. 658490792Sgshapiro** prefix -- string to output in front of each line. 658590792Sgshapiro** 658690792Sgshapiro** Returns: 658790792Sgshapiro** none. 658890792Sgshapiro*/ 658990792Sgshapiro 659090792Sgshapirovoid 659190792Sgshapirodisk_status(out, prefix) 659290792Sgshapiro SM_FILE_T *out; 659390792Sgshapiro char *prefix; 659490792Sgshapiro{ 659590792Sgshapiro int i; 659690792Sgshapiro long avail, blksize; 659790792Sgshapiro long free; 659890792Sgshapiro 659990792Sgshapiro for (i = 0; i < NumFileSys; ++i) 660090792Sgshapiro { 660190792Sgshapiro avail = freediskspace(FILE_SYS_NAME(i), &blksize); 660290792Sgshapiro if (avail >= 0 && blksize > 0) 660390792Sgshapiro { 660490792Sgshapiro free = (long)((double) avail * 660590792Sgshapiro ((double) blksize / 1024)); 660690792Sgshapiro } 660790792Sgshapiro else 660890792Sgshapiro free = -1; 660990792Sgshapiro (void) sm_io_fprintf(out, SM_TIME_DEFAULT, 661090792Sgshapiro "%s%d/%s/%ld\r\n", 661190792Sgshapiro prefix, i, 661290792Sgshapiro FILE_SYS_NAME(i), 661390792Sgshapiro free); 661490792Sgshapiro } 661590792Sgshapiro} 661690792Sgshapiro 661790792Sgshapiro#if SM_CONF_SHM 6618147078Sgshapiro 661990792Sgshapiro/* 6620147078Sgshapiro** INIT_SEM -- initialize semaphore system 6621147078Sgshapiro** 6622147078Sgshapiro** Parameters: 6623147078Sgshapiro** owner -- is this the owner of semaphores? 6624147078Sgshapiro** 6625147078Sgshapiro** Returns: 6626147078Sgshapiro** none. 6627147078Sgshapiro*/ 6628147078Sgshapiro 6629147078Sgshapiro#if _FFR_USE_SEM_LOCKING 6630147078Sgshapiro#if SM_CONF_SEM 6631147078Sgshapirostatic int SemId = -1; /* Semaphore Id */ 6632147078Sgshapiroint SemKey = SM_SEM_KEY; 6633147078Sgshapiro#endif /* SM_CONF_SEM */ 6634147078Sgshapiro#endif /* _FFR_USE_SEM_LOCKING */ 6635147078Sgshapiro 6636147078Sgshapirostatic void init_sem __P((bool)); 6637147078Sgshapiro 6638147078Sgshapirostatic void 6639147078Sgshapiroinit_sem(owner) 6640147078Sgshapiro bool owner; 6641147078Sgshapiro{ 6642147078Sgshapiro#if _FFR_USE_SEM_LOCKING 6643147078Sgshapiro#if SM_CONF_SEM 6644147078Sgshapiro SemId = sm_sem_start(SemKey, 1, 0, owner); 6645147078Sgshapiro if (SemId < 0) 6646147078Sgshapiro { 6647147078Sgshapiro sm_syslog(LOG_ERR, NOQID, 6648182352Sgshapiro "func=init_sem, sem_key=%ld, sm_sem_start=%d, error=%s", 6649182352Sgshapiro (long) SemKey, SemId, sm_errstring(-SemId)); 6650147078Sgshapiro return; 6651147078Sgshapiro } 6652203004Sgshapiro if (owner && RunAsUid != 0) 6653203004Sgshapiro { 6654203004Sgshapiro int r; 6655203004Sgshapiro 6656203004Sgshapiro r = sm_semsetowner(SemId, RunAsUid, RunAsGid, 0660); 6657203004Sgshapiro if (r != 0) 6658203004Sgshapiro sm_syslog(LOG_ERR, NOQID, 6659203004Sgshapiro "key=%ld, sm_semsetowner=%d, RunAsUid=%d, RunAsGid=%d", 6660203004Sgshapiro (long) SemKey, r, RunAsUid, RunAsGid); 6661203004Sgshapiro } 6662147078Sgshapiro#endif /* SM_CONF_SEM */ 6663147078Sgshapiro#endif /* _FFR_USE_SEM_LOCKING */ 6664147078Sgshapiro return; 6665147078Sgshapiro} 6666147078Sgshapiro 6667147078Sgshapiro/* 6668147078Sgshapiro** STOP_SEM -- stop semaphore system 6669147078Sgshapiro** 6670147078Sgshapiro** Parameters: 6671147078Sgshapiro** owner -- is this the owner of semaphores? 6672147078Sgshapiro** 6673147078Sgshapiro** Returns: 6674147078Sgshapiro** none. 6675147078Sgshapiro*/ 6676147078Sgshapiro 6677147078Sgshapirostatic void stop_sem __P((bool)); 6678147078Sgshapiro 6679147078Sgshapirostatic void 6680147078Sgshapirostop_sem(owner) 6681147078Sgshapiro bool owner; 6682147078Sgshapiro{ 6683147078Sgshapiro#if _FFR_USE_SEM_LOCKING 6684147078Sgshapiro#if SM_CONF_SEM 6685147078Sgshapiro if (owner && SemId >= 0) 6686147078Sgshapiro sm_sem_stop(SemId); 6687147078Sgshapiro#endif /* SM_CONF_SEM */ 6688147078Sgshapiro#endif /* _FFR_USE_SEM_LOCKING */ 6689147078Sgshapiro return; 6690147078Sgshapiro} 6691147078Sgshapiro 6692147078Sgshapiro/* 669390792Sgshapiro** UPD_QS -- update information about queue when adding/deleting an entry 669490792Sgshapiro** 669590792Sgshapiro** Parameters: 669690792Sgshapiro** e -- envelope. 6697147078Sgshapiro** count -- add/remove entry (+1/0/-1: add/no change/remove) 6698147078Sgshapiro** space -- update the space available as well. 6699147078Sgshapiro** (>0/0/<0: add/no change/remove) 6700147078Sgshapiro** where -- caller (for logging) 670190792Sgshapiro** 670290792Sgshapiro** Returns: 670390792Sgshapiro** none. 670490792Sgshapiro** 670590792Sgshapiro** Side Effects: 670690792Sgshapiro** Modifies available space in filesystem. 670790792Sgshapiro** Changes number of entries in queue directory. 670890792Sgshapiro*/ 670990792Sgshapiro 671090792Sgshapirovoid 6711147078Sgshapiroupd_qs(e, count, space, where) 671290792Sgshapiro ENVELOPE *e; 6713147078Sgshapiro int count; 6714147078Sgshapiro int space; 6715147078Sgshapiro char *where; 671690792Sgshapiro{ 671790792Sgshapiro short fidx; 671890792Sgshapiro int idx; 6719147078Sgshapiro# if _FFR_USE_SEM_LOCKING 6720147078Sgshapiro int r; 6721147078Sgshapiro# endif /* _FFR_USE_SEM_LOCKING */ 672290792Sgshapiro long s; 672390792Sgshapiro 672490792Sgshapiro if (ShmId == SM_SHM_NO_ID || e == NULL) 672590792Sgshapiro return; 672690792Sgshapiro if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR) 672790792Sgshapiro return; 672890792Sgshapiro idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx; 6729147078Sgshapiro if (tTd(73,2)) 6730147078Sgshapiro sm_dprintf("func=upd_qs, count=%d, space=%d, where=%s, idx=%d, entries=%d\n", 6731147078Sgshapiro count, space, where, idx, QSHM_ENTRIES(idx)); 673290792Sgshapiro 673390792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 6734147078Sgshapiro if (QSHM_ENTRIES(idx) >= 0 && count != 0) 673590792Sgshapiro { 6736147078Sgshapiro# if _FFR_USE_SEM_LOCKING 6737147078Sgshapiro r = sm_sem_acq(SemId, 0, 1); 6738147078Sgshapiro# endif /* _FFR_USE_SEM_LOCKING */ 6739147078Sgshapiro QSHM_ENTRIES(idx) += count; 6740147078Sgshapiro# if _FFR_USE_SEM_LOCKING 6741147078Sgshapiro if (r >= 0) 6742147078Sgshapiro r = sm_sem_rel(SemId, 0, 1); 6743147078Sgshapiro# endif /* _FFR_USE_SEM_LOCKING */ 674490792Sgshapiro } 674590792Sgshapiro 674690792Sgshapiro fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx; 674790792Sgshapiro if (fidx < 0) 674890792Sgshapiro return; 674990792Sgshapiro 675090792Sgshapiro /* update available space also? (might be loseqfile) */ 6751147078Sgshapiro if (space == 0) 675290792Sgshapiro return; 675390792Sgshapiro 675490792Sgshapiro /* convert size to blocks; this causes rounding errors */ 675590792Sgshapiro s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx); 675690792Sgshapiro if (s == 0) 675790792Sgshapiro return; 675890792Sgshapiro 675990792Sgshapiro /* XXX in theory this needs to be protected with a mutex */ 6760147078Sgshapiro if (space > 0) 676190792Sgshapiro FILE_SYS_AVAIL(fidx) += s; 676290792Sgshapiro else 676390792Sgshapiro FILE_SYS_AVAIL(fidx) -= s; 676490792Sgshapiro 676590792Sgshapiro} 676694334Sgshapiro 676794334Sgshapirostatic bool write_key_file __P((char *, long)); 676894334Sgshapirostatic long read_key_file __P((char *, long)); 676994334Sgshapiro 677090792Sgshapiro/* 677194334Sgshapiro** WRITE_KEY_FILE -- record some key into a file. 677294334Sgshapiro** 677394334Sgshapiro** Parameters: 677494334Sgshapiro** keypath -- file name. 677594334Sgshapiro** key -- key to write. 677694334Sgshapiro** 677794334Sgshapiro** Returns: 677894334Sgshapiro** true iff file could be written. 677994334Sgshapiro** 678094334Sgshapiro** Side Effects: 678194334Sgshapiro** writes file. 678294334Sgshapiro*/ 678394334Sgshapiro 678494334Sgshapirostatic bool 678594334Sgshapirowrite_key_file(keypath, key) 678694334Sgshapiro char *keypath; 678794334Sgshapiro long key; 678894334Sgshapiro{ 678994334Sgshapiro bool ok; 679094334Sgshapiro long sff; 679194334Sgshapiro SM_FILE_T *keyf; 679294334Sgshapiro 679394334Sgshapiro ok = false; 679494334Sgshapiro if (keypath == NULL || *keypath == '\0') 679594334Sgshapiro return ok; 679694334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT; 679794334Sgshapiro if (TrustedUid != 0 && RealUid == TrustedUid) 679894334Sgshapiro sff |= SFF_OPENASROOT; 6799110560Sgshapiro keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff); 680094334Sgshapiro if (keyf == NULL) 680194334Sgshapiro { 680294334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s", 680394334Sgshapiro keypath, sm_errstring(errno)); 680494334Sgshapiro } 680594334Sgshapiro else 680694334Sgshapiro { 6807157001Sgshapiro if (geteuid() == 0 && RunAsUid != 0) 6808157001Sgshapiro { 6809157001Sgshapiro# if HASFCHOWN 6810157001Sgshapiro int fd; 6811157001Sgshapiro 6812157001Sgshapiro fd = keyf->f_file; 6813157001Sgshapiro if (fd >= 0 && fchown(fd, RunAsUid, -1) < 0) 6814157001Sgshapiro { 6815157001Sgshapiro int err = errno; 6816157001Sgshapiro 6817157001Sgshapiro sm_syslog(LOG_ALERT, NOQID, 6818157001Sgshapiro "ownership change on %s to %d failed: %s", 6819157001Sgshapiro keypath, RunAsUid, sm_errstring(err)); 6820157001Sgshapiro } 6821157001Sgshapiro# endif /* HASFCHOWN */ 6822157001Sgshapiro } 682394334Sgshapiro ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) != 682494334Sgshapiro SM_IO_EOF; 6825110560Sgshapiro ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok; 682694334Sgshapiro } 682794334Sgshapiro return ok; 682894334Sgshapiro} 682994334Sgshapiro 683094334Sgshapiro/* 683194334Sgshapiro** READ_KEY_FILE -- read a key from a file. 683294334Sgshapiro** 683394334Sgshapiro** Parameters: 683494334Sgshapiro** keypath -- file name. 683594334Sgshapiro** key -- default key. 683694334Sgshapiro** 683794334Sgshapiro** Returns: 683894334Sgshapiro** key. 683994334Sgshapiro*/ 684094334Sgshapiro 684194334Sgshapirostatic long 684294334Sgshapiroread_key_file(keypath, key) 684394334Sgshapiro char *keypath; 684494334Sgshapiro long key; 684594334Sgshapiro{ 684694334Sgshapiro int r; 684794334Sgshapiro long sff, n; 684894334Sgshapiro SM_FILE_T *keyf; 684994334Sgshapiro 685094334Sgshapiro if (keypath == NULL || *keypath == '\0') 685194334Sgshapiro return key; 685294334Sgshapiro sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY; 6853110560Sgshapiro if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid)) 685494334Sgshapiro sff |= SFF_OPENASROOT; 6855110560Sgshapiro keyf = safefopen(keypath, O_RDONLY, FileMode, sff); 685694334Sgshapiro if (keyf == NULL) 685794334Sgshapiro { 685894334Sgshapiro sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s", 685994334Sgshapiro keypath, sm_errstring(errno)); 686094334Sgshapiro } 686194334Sgshapiro else 686294334Sgshapiro { 686394334Sgshapiro r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n); 686494334Sgshapiro if (r == 1) 686594334Sgshapiro key = n; 686694334Sgshapiro (void) sm_io_close(keyf, SM_TIME_DEFAULT); 686794334Sgshapiro } 686894334Sgshapiro return key; 686994334Sgshapiro} 687094334Sgshapiro 687194334Sgshapiro/* 687290792Sgshapiro** INIT_SHM -- initialize shared memory structure 687390792Sgshapiro** 687490792Sgshapiro** Initialize or attach to shared memory segment. 687590792Sgshapiro** Currently it is not a fatal error if this doesn't work. 687690792Sgshapiro** However, it causes us to have a "fallback" storage location 687790792Sgshapiro** for everything that is supposed to be in the shared memory, 687890792Sgshapiro** which makes the code slightly ugly. 687990792Sgshapiro** 688090792Sgshapiro** Parameters: 688190792Sgshapiro** qn -- number of queue directories. 688290792Sgshapiro** owner -- owner of shared memory. 688390792Sgshapiro** hash -- identifies data that is stored in shared memory. 688490792Sgshapiro** 688590792Sgshapiro** Returns: 688690792Sgshapiro** none. 688790792Sgshapiro*/ 688890792Sgshapiro 688990792Sgshapirostatic void init_shm __P((int, bool, unsigned int)); 689090792Sgshapiro 689190792Sgshapirostatic void 689290792Sgshapiroinit_shm(qn, owner, hash) 689390792Sgshapiro int qn; 689490792Sgshapiro bool owner; 689590792Sgshapiro unsigned int hash; 689690792Sgshapiro{ 689790792Sgshapiro int i; 6898147078Sgshapiro int count; 6899147078Sgshapiro int save_errno; 690094334Sgshapiro bool keyselect; 690190792Sgshapiro 690290792Sgshapiro PtrFileSys = &FileSys[0]; 690390792Sgshapiro PNumFileSys = &Numfilesys; 690494334Sgshapiro/* if this "key" is specified: select one yourself */ 6905168515Sgshapiro#define SEL_SHM_KEY ((key_t) -1) 6906168515Sgshapiro#define FIRST_SHM_KEY 25 690790792Sgshapiro 690890792Sgshapiro /* This allows us to disable shared memory at runtime. */ 6909147078Sgshapiro if (ShmKey == 0) 6910147078Sgshapiro return; 691190792Sgshapiro 6912147078Sgshapiro count = 0; 6913147078Sgshapiro shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T); 6914147078Sgshapiro keyselect = ShmKey == SEL_SHM_KEY; 6915147078Sgshapiro if (keyselect) 6916147078Sgshapiro { 6917147078Sgshapiro if (owner) 6918147078Sgshapiro ShmKey = FIRST_SHM_KEY; 6919147078Sgshapiro else 692094334Sgshapiro { 6921168515Sgshapiro errno = 0; 6922147078Sgshapiro ShmKey = read_key_file(ShmKeyFile, ShmKey); 6923147078Sgshapiro keyselect = false; 6924147078Sgshapiro if (ShmKey == SEL_SHM_KEY) 6925168515Sgshapiro { 6926168515Sgshapiro save_errno = (errno != 0) ? errno : EINVAL; 6927147078Sgshapiro goto error; 6928168515Sgshapiro } 692994334Sgshapiro } 6930147078Sgshapiro } 6931147078Sgshapiro for (;;) 6932147078Sgshapiro { 6933147078Sgshapiro /* allow read/write access for group? */ 6934147078Sgshapiro Pshm = sm_shmstart(ShmKey, shms, 6935147078Sgshapiro SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3), 6936147078Sgshapiro &ShmId, owner); 6937147078Sgshapiro save_errno = errno; 6938147078Sgshapiro if (Pshm != NULL || !sm_file_exists(save_errno)) 6939147078Sgshapiro break; 6940147078Sgshapiro if (++count >= 3) 694190792Sgshapiro { 6942147078Sgshapiro if (keyselect) 694394334Sgshapiro { 6944147078Sgshapiro ++ShmKey; 694594334Sgshapiro 6946147078Sgshapiro /* back where we started? */ 6947147078Sgshapiro if (ShmKey == SEL_SHM_KEY) 6948147078Sgshapiro break; 6949147078Sgshapiro continue; 6950147078Sgshapiro } 6951147078Sgshapiro break; 6952147078Sgshapiro } 6953168515Sgshapiro 6954147078Sgshapiro /* only sleep if we are at the first key */ 6955147078Sgshapiro if (!keyselect || ShmKey == SEL_SHM_KEY) 6956168515Sgshapiro sleep(count); 6957147078Sgshapiro } 6958147078Sgshapiro if (Pshm != NULL) 6959147078Sgshapiro { 6960147078Sgshapiro int *p; 696190792Sgshapiro 6962147078Sgshapiro if (keyselect) 6963147078Sgshapiro (void) write_key_file(ShmKeyFile, (long) ShmKey); 6964147078Sgshapiro if (owner && RunAsUid != 0) 6965147078Sgshapiro { 6966157001Sgshapiro i = sm_shmsetowner(ShmId, RunAsUid, RunAsGid, 0660); 6967147078Sgshapiro if (i != 0) 6968147078Sgshapiro sm_syslog(LOG_ERR, NOQID, 6969157001Sgshapiro "key=%ld, sm_shmsetowner=%d, RunAsUid=%d, RunAsGid=%d", 6970157001Sgshapiro (long) ShmKey, i, RunAsUid, RunAsGid); 6971147078Sgshapiro } 6972147078Sgshapiro p = (int *) Pshm; 6973147078Sgshapiro if (owner) 6974147078Sgshapiro { 6975147078Sgshapiro *p = (int) shms; 6976147078Sgshapiro *((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid; 6977147078Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 6978147078Sgshapiro *p = hash; 6979147078Sgshapiro } 6980147078Sgshapiro else 6981147078Sgshapiro { 6982147078Sgshapiro if (*p != (int) shms) 698390792Sgshapiro { 6984147078Sgshapiro save_errno = EINVAL; 6985147078Sgshapiro cleanup_shm(false); 6986147078Sgshapiro goto error; 698790792Sgshapiro } 6988147078Sgshapiro p = (int *) SHM_OFF_TAG(Pshm); 6989147078Sgshapiro if (*p != (int) hash) 699090792Sgshapiro { 6991147078Sgshapiro save_errno = EINVAL; 6992147078Sgshapiro cleanup_shm(false); 6993147078Sgshapiro goto error; 699490792Sgshapiro } 699590792Sgshapiro 6996147078Sgshapiro /* 6997147078Sgshapiro ** XXX how to check the pid? 6998147078Sgshapiro ** Read it from the pid-file? That does 6999147078Sgshapiro ** not need to exist. 7000147078Sgshapiro ** We could disable shm if we can't confirm 7001147078Sgshapiro ** that it is the right one. 7002147078Sgshapiro */ 700390792Sgshapiro } 7004147078Sgshapiro 7005147078Sgshapiro PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm); 7006147078Sgshapiro PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm); 7007147078Sgshapiro QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm); 7008147078Sgshapiro PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm); 7009147078Sgshapiro *PRSATmpCnt = 0; 7010147078Sgshapiro if (owner) 701190792Sgshapiro { 7012147078Sgshapiro /* initialize values in shared memory */ 7013147078Sgshapiro NumFileSys = 0; 7014147078Sgshapiro for (i = 0; i < qn; i++) 7015147078Sgshapiro QShm[i].qs_entries = -1; 701690792Sgshapiro } 7017147078Sgshapiro init_sem(owner); 7018147078Sgshapiro return; 701990792Sgshapiro } 7020147078Sgshapiro error: 7021147078Sgshapiro if (LogLevel > (owner ? 8 : 11)) 7022147078Sgshapiro { 7023147078Sgshapiro sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID, 7024147078Sgshapiro "can't %s shared memory, key=%ld: %s", 7025147078Sgshapiro owner ? "initialize" : "attach to", 7026147078Sgshapiro (long) ShmKey, sm_errstring(save_errno)); 7027147078Sgshapiro } 702890792Sgshapiro} 702990792Sgshapiro#endif /* SM_CONF_SHM */ 703090792Sgshapiro 7031120256Sgshapiro 703290792Sgshapiro/* 7033168515Sgshapiro** SETUP_QUEUES -- set up all queue groups 703490792Sgshapiro** 703590792Sgshapiro** Parameters: 7036168515Sgshapiro** owner -- owner of shared memory? 703790792Sgshapiro** 703890792Sgshapiro** Returns: 703990792Sgshapiro** none. 704090792Sgshapiro** 704190792Sgshapiro#if SM_CONF_SHM 704290792Sgshapiro** Side Effects: 704390792Sgshapiro** attaches shared memory. 704490792Sgshapiro#endif * SM_CONF_SHM * 704590792Sgshapiro*/ 704690792Sgshapiro 704790792Sgshapirovoid 704890792Sgshapirosetup_queues(owner) 704990792Sgshapiro bool owner; 705090792Sgshapiro{ 705190792Sgshapiro int i, qn, len; 705290792Sgshapiro unsigned int hashval; 705394334Sgshapiro time_t now; 705490792Sgshapiro char basedir[MAXPATHLEN]; 705590792Sgshapiro struct stat st; 705690792Sgshapiro 705790792Sgshapiro /* 705890792Sgshapiro ** Determine basedir for all queue directories. 705990792Sgshapiro ** All queue directories must be (first level) subdirectories 706090792Sgshapiro ** of the basedir. The basedir is the QueueDir 706190792Sgshapiro ** without wildcards, but with trailing / 706290792Sgshapiro */ 706390792Sgshapiro 706490792Sgshapiro hashval = 0; 706590792Sgshapiro errno = 0; 7066168515Sgshapiro len = sm_strlcpy(basedir, QueueDir, sizeof(basedir)); 7067111823Sgshapiro 7068111823Sgshapiro /* Provide space for trailing '/' */ 7069168515Sgshapiro if (len >= sizeof(basedir) - 1) 707090792Sgshapiro { 707190792Sgshapiro syserr("QueueDirectory: path too long: %d, max %d", 7072168515Sgshapiro len, (int) sizeof(basedir) - 1); 707390792Sgshapiro ExitStat = EX_CONFIG; 707490792Sgshapiro return; 707590792Sgshapiro } 707690792Sgshapiro SM_ASSERT(len > 0); 707790792Sgshapiro if (basedir[len - 1] == '*') 707890792Sgshapiro { 707990792Sgshapiro char *cp; 708090792Sgshapiro 708190792Sgshapiro cp = SM_LAST_DIR_DELIM(basedir); 708290792Sgshapiro if (cp == NULL) 708390792Sgshapiro { 708490792Sgshapiro syserr("QueueDirectory: can not wildcard relative path \"%s\"", 708590792Sgshapiro QueueDir); 708690792Sgshapiro if (tTd(41, 2)) 708790792Sgshapiro sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n", 708890792Sgshapiro QueueDir); 708990792Sgshapiro ExitStat = EX_CONFIG; 709090792Sgshapiro return; 709190792Sgshapiro } 709290792Sgshapiro 709390792Sgshapiro /* cut off wildcard pattern */ 709490792Sgshapiro *++cp = '\0'; 709590792Sgshapiro len = cp - basedir; 709690792Sgshapiro } 709790792Sgshapiro else if (!SM_IS_DIR_DELIM(basedir[len - 1])) 709890792Sgshapiro { 709990792Sgshapiro /* append trailing slash since it is a directory */ 710090792Sgshapiro basedir[len] = '/'; 710190792Sgshapiro basedir[++len] = '\0'; 710290792Sgshapiro } 710390792Sgshapiro 710490792Sgshapiro /* len counts up to the last directory delimiter */ 710590792Sgshapiro SM_ASSERT(basedir[len - 1] == '/'); 710690792Sgshapiro 710790792Sgshapiro if (chdir(basedir) < 0) 710890792Sgshapiro { 710990792Sgshapiro int save_errno = errno; 711090792Sgshapiro 711190792Sgshapiro syserr("can not chdir(%s)", basedir); 711290792Sgshapiro if (save_errno == EACCES) 711390792Sgshapiro (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 711490792Sgshapiro "Program mode requires special privileges, e.g., root or TrustedUser.\n"); 711590792Sgshapiro if (tTd(41, 2)) 711690792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 711790792Sgshapiro basedir, sm_errstring(errno)); 711890792Sgshapiro ExitStat = EX_CONFIG; 711990792Sgshapiro return; 712090792Sgshapiro } 712190792Sgshapiro#if SM_CONF_SHM 712290792Sgshapiro hashval = hash_q(basedir, hashval); 712390792Sgshapiro#endif /* SM_CONF_SHM */ 712490792Sgshapiro 712594334Sgshapiro /* initialize for queue runs */ 712694334Sgshapiro DoQueueRun = false; 712794334Sgshapiro now = curtime(); 712894334Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 712994334Sgshapiro Queue[i]->qg_nextrun = now; 713090792Sgshapiro 713190792Sgshapiro 713290792Sgshapiro if (UseMSP && OpMode != MD_TEST) 713390792Sgshapiro { 713490792Sgshapiro long sff = SFF_CREAT; 713590792Sgshapiro 713690792Sgshapiro if (stat(".", &st) < 0) 713790792Sgshapiro { 713890792Sgshapiro syserr("can not stat(%s)", basedir); 713990792Sgshapiro if (tTd(41, 2)) 714090792Sgshapiro sm_dprintf("setup_queues: \"%s\": %s\n", 714190792Sgshapiro basedir, sm_errstring(errno)); 714290792Sgshapiro ExitStat = EX_CONFIG; 714390792Sgshapiro return; 714490792Sgshapiro } 714590792Sgshapiro if (RunAsUid == 0) 714690792Sgshapiro sff |= SFF_ROOTOK; 714790792Sgshapiro 714890792Sgshapiro /* 714990792Sgshapiro ** Check queue directory permissions. 715090792Sgshapiro ** Can we write to a group writable queue directory? 715190792Sgshapiro */ 715290792Sgshapiro 715390792Sgshapiro if (bitset(S_IWGRP, QueueFileMode) && 715490792Sgshapiro bitset(S_IWGRP, st.st_mode) && 715590792Sgshapiro safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff, 715690792Sgshapiro QueueFileMode, NULL) != 0) 715790792Sgshapiro { 715890792Sgshapiro syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)", 715990792Sgshapiro basedir, (int) RunAsGid, (int) st.st_gid); 716090792Sgshapiro } 716190792Sgshapiro if (bitset(S_IWOTH|S_IXOTH, st.st_mode)) 716290792Sgshapiro { 716390792Sgshapiro#if _FFR_MSP_PARANOIA 716490792Sgshapiro syserr("dangerous permissions=%o on queue directory %s", 716590792Sgshapiro (int) st.st_mode, basedir); 716690792Sgshapiro#else /* _FFR_MSP_PARANOIA */ 716790792Sgshapiro if (LogLevel > 0) 716890792Sgshapiro sm_syslog(LOG_ERR, NOQID, 716990792Sgshapiro "dangerous permissions=%o on queue directory %s", 717090792Sgshapiro (int) st.st_mode, basedir); 717190792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 717290792Sgshapiro } 717390792Sgshapiro#if _FFR_MSP_PARANOIA 717490792Sgshapiro if (NumQueue > 1) 717590792Sgshapiro syserr("can not use multiple queues for MSP"); 717690792Sgshapiro#endif /* _FFR_MSP_PARANOIA */ 717790792Sgshapiro } 717890792Sgshapiro 717990792Sgshapiro /* initial number of queue directories */ 718090792Sgshapiro qn = 0; 718190792Sgshapiro for (i = 0; i < NumQueue && Queue[i] != NULL; i++) 718290792Sgshapiro qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval); 718390792Sgshapiro 718490792Sgshapiro#if SM_CONF_SHM 718590792Sgshapiro init_shm(qn, owner, hashval); 718690792Sgshapiro i = filesys_setup(owner || ShmId == SM_SHM_NO_ID); 718790792Sgshapiro if (i == FSF_NOT_FOUND) 718890792Sgshapiro { 718990792Sgshapiro /* 719090792Sgshapiro ** We didn't get the right filesystem data 719190792Sgshapiro ** This may happen if we don't have the right shared memory. 719290792Sgshapiro ** So let's do this without shared memory. 719390792Sgshapiro */ 719490792Sgshapiro 719590792Sgshapiro SM_ASSERT(!owner); 719690792Sgshapiro cleanup_shm(false); /* release shared memory */ 719790792Sgshapiro i = filesys_setup(false); 719890792Sgshapiro if (i < 0) 719990792Sgshapiro syserr("filesys_setup failed twice, result=%d", i); 720090792Sgshapiro else if (LogLevel > 8) 720190792Sgshapiro sm_syslog(LOG_WARNING, NOQID, 720290792Sgshapiro "shared memory does not contain expected data, ignored"); 720390792Sgshapiro } 720490792Sgshapiro#else /* SM_CONF_SHM */ 720590792Sgshapiro i = filesys_setup(true); 720690792Sgshapiro#endif /* SM_CONF_SHM */ 720790792Sgshapiro if (i < 0) 720890792Sgshapiro ExitStat = EX_CONFIG; 720990792Sgshapiro} 721090792Sgshapiro 721190792Sgshapiro#if SM_CONF_SHM 721290792Sgshapiro/* 721390792Sgshapiro** CLEANUP_SHM -- do some cleanup work for shared memory etc 721490792Sgshapiro** 721590792Sgshapiro** Parameters: 721690792Sgshapiro** owner -- owner of shared memory? 721790792Sgshapiro** 721890792Sgshapiro** Returns: 721990792Sgshapiro** none. 722090792Sgshapiro** 722190792Sgshapiro** Side Effects: 722290792Sgshapiro** detaches shared memory. 722390792Sgshapiro*/ 722490792Sgshapiro 722590792Sgshapirovoid 722690792Sgshapirocleanup_shm(owner) 722790792Sgshapiro bool owner; 722890792Sgshapiro{ 722990792Sgshapiro if (ShmId != SM_SHM_NO_ID) 723090792Sgshapiro { 723190792Sgshapiro if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8) 723298121Sgshapiro sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s", 723390792Sgshapiro sm_errstring(errno)); 723490792Sgshapiro Pshm = NULL; 723590792Sgshapiro ShmId = SM_SHM_NO_ID; 723690792Sgshapiro } 7237147078Sgshapiro stop_sem(owner); 723890792Sgshapiro} 723990792Sgshapiro#endif /* SM_CONF_SHM */ 724090792Sgshapiro 724190792Sgshapiro/* 724290792Sgshapiro** CLEANUP_QUEUES -- do some cleanup work for queues 724390792Sgshapiro** 724490792Sgshapiro** Parameters: 724590792Sgshapiro** none. 724690792Sgshapiro** 724790792Sgshapiro** Returns: 724890792Sgshapiro** none. 724990792Sgshapiro** 725090792Sgshapiro*/ 725190792Sgshapiro 725290792Sgshapirovoid 725390792Sgshapirocleanup_queues() 725490792Sgshapiro{ 725590792Sgshapiro sync_queue_time(); 725690792Sgshapiro} 725790792Sgshapiro/* 725890792Sgshapiro** SET_DEF_QUEUEVAL -- set default values for a queue group. 725990792Sgshapiro** 726090792Sgshapiro** Parameters: 726190792Sgshapiro** qg -- queue group 726290792Sgshapiro** all -- set all values (true for default group)? 726390792Sgshapiro** 726490792Sgshapiro** Returns: 726590792Sgshapiro** none. 726690792Sgshapiro** 726790792Sgshapiro** Side Effects: 726890792Sgshapiro** sets default values for the queue group. 726990792Sgshapiro*/ 727090792Sgshapiro 727190792Sgshapirovoid 727290792Sgshapiroset_def_queueval(qg, all) 727390792Sgshapiro QUEUEGRP *qg; 727490792Sgshapiro bool all; 727590792Sgshapiro{ 727690792Sgshapiro if (bitnset(QD_DEFINED, qg->qg_flags)) 727790792Sgshapiro return; 727890792Sgshapiro if (all) 727990792Sgshapiro qg->qg_qdir = QueueDir; 728094334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 728190792Sgshapiro qg->qg_sortorder = QueueSortOrder; 728294334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 728390792Sgshapiro qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1; 728490792Sgshapiro qg->qg_nice = NiceQueueRun; 728590792Sgshapiro} 728690792Sgshapiro/* 728790792Sgshapiro** MAKEQUEUE -- define a new queue. 728890792Sgshapiro** 728990792Sgshapiro** Parameters: 729090792Sgshapiro** line -- description of queue. This is in labeled fields. 729190792Sgshapiro** The fields are: 729290792Sgshapiro** F -- the flags associated with the queue 729390792Sgshapiro** I -- the interval between running the queue 729490792Sgshapiro** J -- the maximum # of jobs in work list 729590792Sgshapiro** [M -- the maximum # of jobs in a queue run] 729690792Sgshapiro** N -- the niceness at which to run 729790792Sgshapiro** P -- the path to the queue 729890792Sgshapiro** S -- the queue sorting order 729990792Sgshapiro** R -- number of parallel queue runners 730090792Sgshapiro** r -- max recipients per envelope 730190792Sgshapiro** The first word is the canonical name of the queue. 730290792Sgshapiro** qdef -- this is a 'Q' definition from .cf 730390792Sgshapiro** 730490792Sgshapiro** Returns: 730590792Sgshapiro** none. 730690792Sgshapiro** 730790792Sgshapiro** Side Effects: 730890792Sgshapiro** enters the queue into the queue table. 730990792Sgshapiro*/ 731090792Sgshapiro 731190792Sgshapirovoid 731290792Sgshapiromakequeue(line, qdef) 731390792Sgshapiro char *line; 731490792Sgshapiro bool qdef; 731590792Sgshapiro{ 731690792Sgshapiro register char *p; 731790792Sgshapiro register QUEUEGRP *qg; 731890792Sgshapiro register STAB *s; 731990792Sgshapiro int i; 732090792Sgshapiro char fcode; 732190792Sgshapiro 732290792Sgshapiro /* allocate a queue and set up defaults */ 7323168515Sgshapiro qg = (QUEUEGRP *) xalloc(sizeof(*qg)); 7324168515Sgshapiro memset((char *) qg, '\0', sizeof(*qg)); 732590792Sgshapiro 732690792Sgshapiro if (line[0] == '\0') 732790792Sgshapiro { 732890792Sgshapiro syserr("name required for queue"); 732990792Sgshapiro return; 733090792Sgshapiro } 733190792Sgshapiro 733290792Sgshapiro /* collect the queue name */ 733390792Sgshapiro for (p = line; 733490792Sgshapiro *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); 733590792Sgshapiro p++) 733690792Sgshapiro continue; 733790792Sgshapiro if (*p != '\0') 733890792Sgshapiro *p++ = '\0'; 733990792Sgshapiro qg->qg_name = newstr(line); 734090792Sgshapiro 734190792Sgshapiro /* set default values, can be overridden below */ 734290792Sgshapiro set_def_queueval(qg, false); 734390792Sgshapiro 734490792Sgshapiro /* now scan through and assign info from the fields */ 734590792Sgshapiro while (*p != '\0') 734690792Sgshapiro { 734790792Sgshapiro auto char *delimptr; 734890792Sgshapiro 734990792Sgshapiro while (*p != '\0' && 735090792Sgshapiro (*p == ',' || (isascii(*p) && isspace(*p)))) 735190792Sgshapiro p++; 735290792Sgshapiro 735390792Sgshapiro /* p now points to field code */ 735490792Sgshapiro fcode = *p; 735590792Sgshapiro while (*p != '\0' && *p != '=' && *p != ',') 735690792Sgshapiro p++; 735790792Sgshapiro if (*p++ != '=') 735890792Sgshapiro { 735990792Sgshapiro syserr("queue %s: `=' expected", qg->qg_name); 736090792Sgshapiro return; 736190792Sgshapiro } 736290792Sgshapiro while (isascii(*p) && isspace(*p)) 736390792Sgshapiro p++; 736490792Sgshapiro 736590792Sgshapiro /* p now points to the field body */ 736690792Sgshapiro p = munchstring(p, &delimptr, ','); 736790792Sgshapiro 736890792Sgshapiro /* install the field into the queue struct */ 736990792Sgshapiro switch (fcode) 737090792Sgshapiro { 737190792Sgshapiro case 'P': /* pathname */ 737290792Sgshapiro if (*p == '\0') 737390792Sgshapiro syserr("queue %s: empty path name", 737490792Sgshapiro qg->qg_name); 737590792Sgshapiro else 737690792Sgshapiro qg->qg_qdir = newstr(p); 737790792Sgshapiro break; 737890792Sgshapiro 737990792Sgshapiro case 'F': /* flags */ 738090792Sgshapiro for (; *p != '\0'; p++) 738190792Sgshapiro if (!(isascii(*p) && isspace(*p))) 738290792Sgshapiro setbitn(*p, qg->qg_flags); 738390792Sgshapiro break; 738490792Sgshapiro 738590792Sgshapiro /* 738690792Sgshapiro ** Do we need two intervals here: 738790792Sgshapiro ** One for persistent queue runners, 738890792Sgshapiro ** one for "normal" queue runs? 738990792Sgshapiro */ 739090792Sgshapiro 739190792Sgshapiro case 'I': /* interval between running the queue */ 739290792Sgshapiro qg->qg_queueintvl = convtime(p, 'm'); 739390792Sgshapiro break; 739490792Sgshapiro 739590792Sgshapiro case 'N': /* run niceness */ 739690792Sgshapiro qg->qg_nice = atoi(p); 739790792Sgshapiro break; 739890792Sgshapiro 739990792Sgshapiro case 'R': /* maximum # of runners for the group */ 740090792Sgshapiro i = atoi(p); 740190792Sgshapiro 740290792Sgshapiro /* can't have more runners than allowed total */ 740390792Sgshapiro if (MaxQueueChildren > 0 && i > MaxQueueChildren) 740490792Sgshapiro { 740590792Sgshapiro qg->qg_maxqrun = MaxQueueChildren; 740690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 740790792Sgshapiro "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n", 740890792Sgshapiro qg->qg_name, i, 740990792Sgshapiro MaxQueueChildren); 741090792Sgshapiro } 741190792Sgshapiro else 741290792Sgshapiro qg->qg_maxqrun = i; 741390792Sgshapiro break; 741490792Sgshapiro 741590792Sgshapiro case 'J': /* maximum # of jobs in work list */ 741690792Sgshapiro qg->qg_maxlist = atoi(p); 741790792Sgshapiro break; 741890792Sgshapiro 741990792Sgshapiro case 'r': /* max recipients per envelope */ 742090792Sgshapiro qg->qg_maxrcpt = atoi(p); 742190792Sgshapiro break; 742290792Sgshapiro 742394334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER 742490792Sgshapiro case 'S': /* queue sorting order */ 742590792Sgshapiro switch (*p) 742690792Sgshapiro { 742790792Sgshapiro case 'h': /* Host first */ 742890792Sgshapiro case 'H': 742990792Sgshapiro qg->qg_sortorder = QSO_BYHOST; 743090792Sgshapiro break; 743190792Sgshapiro 743290792Sgshapiro case 'p': /* Priority order */ 743390792Sgshapiro case 'P': 743490792Sgshapiro qg->qg_sortorder = QSO_BYPRIORITY; 743590792Sgshapiro break; 743690792Sgshapiro 743790792Sgshapiro case 't': /* Submission time */ 743890792Sgshapiro case 'T': 743990792Sgshapiro qg->qg_sortorder = QSO_BYTIME; 744090792Sgshapiro break; 744190792Sgshapiro 744290792Sgshapiro case 'f': /* File name */ 744390792Sgshapiro case 'F': 744490792Sgshapiro qg->qg_sortorder = QSO_BYFILENAME; 744590792Sgshapiro break; 744690792Sgshapiro 744790792Sgshapiro case 'm': /* Modification time */ 744890792Sgshapiro case 'M': 744994334Sgshapiro qg->qg_sortorder = QSO_BYMODTIME; 745090792Sgshapiro break; 745190792Sgshapiro 745294334Sgshapiro case 'r': /* Random */ 745394334Sgshapiro case 'R': 745494334Sgshapiro qg->qg_sortorder = QSO_RANDOM; 745594334Sgshapiro break; 745694334Sgshapiro 745794334Sgshapiro# if _FFR_RHS 745894334Sgshapiro case 's': /* Shuffled host name */ 745994334Sgshapiro case 'S': 746094334Sgshapiro qg->qg_sortorder = QSO_BYSHUFFLE; 746194334Sgshapiro break; 746294334Sgshapiro# endif /* _FFR_RHS */ 746394334Sgshapiro 7464132943Sgshapiro case 'n': /* none */ 7465132943Sgshapiro case 'N': 7466132943Sgshapiro qg->qg_sortorder = QSO_NONE; 7467132943Sgshapiro break; 7468132943Sgshapiro 746990792Sgshapiro default: 747090792Sgshapiro syserr("Invalid queue sort order \"%s\"", p); 747190792Sgshapiro } 747290792Sgshapiro break; 747394334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */ 747490792Sgshapiro 747590792Sgshapiro default: 747690792Sgshapiro syserr("Q%s: unknown queue equate %c=", 747790792Sgshapiro qg->qg_name, fcode); 747890792Sgshapiro break; 747990792Sgshapiro } 748090792Sgshapiro 748190792Sgshapiro p = delimptr; 748290792Sgshapiro } 748390792Sgshapiro 748490792Sgshapiro#if !HASNICE 748590792Sgshapiro if (qg->qg_nice != NiceQueueRun) 748690792Sgshapiro { 748790792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 748890792Sgshapiro "Q%s: Warning: N= set on system that doesn't support nice()\n", 748990792Sgshapiro qg->qg_name); 749090792Sgshapiro } 749190792Sgshapiro#endif /* !HASNICE */ 749290792Sgshapiro 749390792Sgshapiro /* do some rationality checking */ 749490792Sgshapiro if (NumQueue >= MAXQUEUEGROUPS) 749590792Sgshapiro { 749690792Sgshapiro syserr("too many queue groups defined (%d max)", 749790792Sgshapiro MAXQUEUEGROUPS); 749890792Sgshapiro return; 749990792Sgshapiro } 750090792Sgshapiro 750190792Sgshapiro if (qg->qg_qdir == NULL) 750290792Sgshapiro { 750390792Sgshapiro if (QueueDir == NULL || *QueueDir == '\0') 750490792Sgshapiro { 750590792Sgshapiro syserr("QueueDir must be defined before queue groups"); 750690792Sgshapiro return; 750790792Sgshapiro } 750890792Sgshapiro qg->qg_qdir = newstr(QueueDir); 750990792Sgshapiro } 751090792Sgshapiro 751190792Sgshapiro if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags)) 751290792Sgshapiro { 751390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 751490792Sgshapiro "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n", 751590792Sgshapiro qg->qg_name, qg->qg_maxqrun, QD_FORK); 751690792Sgshapiro } 751790792Sgshapiro 751890792Sgshapiro /* enter the queue into the symbol table */ 751990792Sgshapiro if (tTd(37, 8)) 752090792Sgshapiro sm_syslog(LOG_INFO, NOQID, 752190792Sgshapiro "Adding %s to stab, path: %s", qg->qg_name, 752290792Sgshapiro qg->qg_qdir); 752390792Sgshapiro s = stab(qg->qg_name, ST_QUEUE, ST_ENTER); 752490792Sgshapiro if (s->s_quegrp != NULL) 752590792Sgshapiro { 752690792Sgshapiro i = s->s_quegrp->qg_index; 752790792Sgshapiro 752890792Sgshapiro /* XXX what about the pointers inside this struct? */ 752990792Sgshapiro sm_free(s->s_quegrp); /* XXX */ 753090792Sgshapiro } 753190792Sgshapiro else 753290792Sgshapiro i = NumQueue++; 753390792Sgshapiro Queue[i] = s->s_quegrp = qg; 753490792Sgshapiro qg->qg_index = i; 753590792Sgshapiro 753690792Sgshapiro /* set default value for max queue runners */ 753790792Sgshapiro if (qg->qg_maxqrun < 0) 753890792Sgshapiro { 753990792Sgshapiro if (MaxRunnersPerQueue > 0) 754090792Sgshapiro qg->qg_maxqrun = MaxRunnersPerQueue; 754190792Sgshapiro else 754290792Sgshapiro qg->qg_maxqrun = 1; 754390792Sgshapiro } 754490792Sgshapiro if (qdef) 754590792Sgshapiro setbitn(QD_DEFINED, qg->qg_flags); 754690792Sgshapiro} 754790792Sgshapiro#if 0 754890792Sgshapiro/* 754964562Sgshapiro** HASHFQN -- calculate a hash value for a fully qualified host name 755064562Sgshapiro** 755164562Sgshapiro** Arguments: 755264562Sgshapiro** fqn -- an all lower-case host.domain string 755364562Sgshapiro** buckets -- the number of buckets (queue directories) 755464562Sgshapiro** 755564562Sgshapiro** Returns: 755664562Sgshapiro** a bucket number (signed integer) 755764562Sgshapiro** -1 on error 755864562Sgshapiro** 755964562Sgshapiro** Contributed by Exactis.com, Inc. 756064562Sgshapiro*/ 756164562Sgshapiro 756264562Sgshapiroint 756364562Sgshapirohashfqn(fqn, buckets) 756464562Sgshapiro register char *fqn; 756564562Sgshapiro int buckets; 756664562Sgshapiro{ 756764562Sgshapiro register char *p; 756864562Sgshapiro register int h = 0, hash, cnt; 756964562Sgshapiro 757064562Sgshapiro if (fqn == NULL) 757164562Sgshapiro return -1; 757264562Sgshapiro 757364562Sgshapiro /* 757464562Sgshapiro ** A variation on the gdb hash 757564562Sgshapiro ** This is the best as of Feb 19, 1996 --bcx 757664562Sgshapiro */ 757764562Sgshapiro 757864562Sgshapiro p = fqn; 757964562Sgshapiro h = 0x238F13AF * strlen(p); 758064562Sgshapiro for (cnt = 0; *p != 0; ++p, cnt++) 758164562Sgshapiro { 758264562Sgshapiro h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF; 758364562Sgshapiro } 758464562Sgshapiro h = (1103515243 * h + 12345) & 0x7FFFFFFF; 758564562Sgshapiro if (buckets < 2) 758664562Sgshapiro hash = 0; 758764562Sgshapiro else 758864562Sgshapiro hash = (h % buckets); 758964562Sgshapiro 759064562Sgshapiro return hash; 759164562Sgshapiro} 759290792Sgshapiro#endif /* 0 */ 759364562Sgshapiro 759490792Sgshapiro/* 759590792Sgshapiro** A structure for sorting Queue according to maxqrun without 759690792Sgshapiro** screwing up Queue itself. 759790792Sgshapiro*/ 759890792Sgshapiro 759990792Sgshapirostruct sortqgrp 760090792Sgshapiro{ 760190792Sgshapiro int sg_idx; /* original index */ 760290792Sgshapiro int sg_maxqrun; /* max queue runners */ 760390792Sgshapiro}; 760490792Sgshapirotypedef struct sortqgrp SORTQGRP_T; 760590792Sgshapirostatic int cmpidx __P((const void *, const void *)); 760690792Sgshapiro 760790792Sgshapirostatic int 760890792Sgshapirocmpidx(a, b) 760990792Sgshapiro const void *a; 761090792Sgshapiro const void *b; 761190792Sgshapiro{ 761290792Sgshapiro /* The sort is highest to lowest, so the comparison is reversed */ 761390792Sgshapiro if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun) 761490792Sgshapiro return 1; 761590792Sgshapiro else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun) 761690792Sgshapiro return -1; 761790792Sgshapiro else 761890792Sgshapiro return 0; 761990792Sgshapiro} 762090792Sgshapiro 762190792Sgshapiro/* 762290792Sgshapiro** MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren 762390792Sgshapiro** 762490792Sgshapiro** Take the now defined queue groups and assign them to work groups. 762590792Sgshapiro** This is done to balance out the number of concurrently active 762690792Sgshapiro** queue runners such that MaxQueueChildren is not exceeded. This may 762790792Sgshapiro** result in more than one queue group per work group. In such a case 762890792Sgshapiro** the number of running queue groups in that work group will have no 762990792Sgshapiro** more than the work group maximum number of runners (a "fair" portion 763090792Sgshapiro** of MaxQueueRunners). All queue groups within a work group will get a 763190792Sgshapiro** chance at running. 763290792Sgshapiro** 763390792Sgshapiro** Parameters: 763490792Sgshapiro** none. 763590792Sgshapiro** 763690792Sgshapiro** Returns: 763790792Sgshapiro** nothing. 763890792Sgshapiro** 763990792Sgshapiro** Side Effects: 764090792Sgshapiro** Sets up WorkGrp structure. 764190792Sgshapiro*/ 764290792Sgshapiro 764390792Sgshapirovoid 764490792Sgshapiromakeworkgroups() 764590792Sgshapiro{ 7646125820Sgshapiro int i, j, total_runners, dir, h; 764790792Sgshapiro SORTQGRP_T si[MAXQUEUEGROUPS + 1]; 764890792Sgshapiro 7649125820Sgshapiro total_runners = 0; 765090792Sgshapiro if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0) 765190792Sgshapiro { 765290792Sgshapiro /* 765390792Sgshapiro ** There is only the "mqueue" queue group (a default) 765490792Sgshapiro ** containing all of the queues. We want to provide to 765590792Sgshapiro ** this queue group the maximum allowable queue runners. 765690792Sgshapiro ** To match older behavior (8.10/8.11) we'll try for 765790792Sgshapiro ** 1 runner per queue capping it at MaxQueueChildren. 765890792Sgshapiro ** So if there are N queues, then there will be N runners 765990792Sgshapiro ** for the "mqueue" queue group (where N is kept less than 766090792Sgshapiro ** MaxQueueChildren). 766190792Sgshapiro */ 766290792Sgshapiro 766390792Sgshapiro NumWorkGroups = 1; 766490792Sgshapiro WorkGrp[0].wg_numqgrp = 1; 766590792Sgshapiro WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *)); 766690792Sgshapiro WorkGrp[0].wg_qgs[0] = Queue[0]; 766790792Sgshapiro if (MaxQueueChildren > 0 && 766890792Sgshapiro Queue[0]->qg_numqueues > MaxQueueChildren) 766990792Sgshapiro WorkGrp[0].wg_runners = MaxQueueChildren; 767090792Sgshapiro else 767190792Sgshapiro WorkGrp[0].wg_runners = Queue[0]->qg_numqueues; 767290792Sgshapiro 767390792Sgshapiro Queue[0]->qg_wgrp = 0; 767490792Sgshapiro 767590792Sgshapiro /* can't have more runners than allowed total */ 767690792Sgshapiro if (MaxQueueChildren > 0 && 767790792Sgshapiro Queue[0]->qg_maxqrun > MaxQueueChildren) 767890792Sgshapiro Queue[0]->qg_maxqrun = MaxQueueChildren; 767990792Sgshapiro WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun; 768090792Sgshapiro WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl; 768190792Sgshapiro return; 768290792Sgshapiro } 768390792Sgshapiro 768490792Sgshapiro for (i = 0; i < NumQueue; i++) 768590792Sgshapiro { 768690792Sgshapiro si[i].sg_maxqrun = Queue[i]->qg_maxqrun; 768790792Sgshapiro si[i].sg_idx = i; 768890792Sgshapiro } 768990792Sgshapiro qsort(si, NumQueue, sizeof(si[0]), cmpidx); 769090792Sgshapiro 769190792Sgshapiro NumWorkGroups = 0; 769290792Sgshapiro for (i = 0; i < NumQueue; i++) 769390792Sgshapiro { 769490792Sgshapiro total_runners += si[i].sg_maxqrun; 769590792Sgshapiro if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren) 769690792Sgshapiro NumWorkGroups++; 769790792Sgshapiro else 769890792Sgshapiro break; 769990792Sgshapiro } 770090792Sgshapiro 770190792Sgshapiro if (NumWorkGroups < 1) 770290792Sgshapiro NumWorkGroups = 1; /* gotta have one at least */ 770390792Sgshapiro else if (NumWorkGroups > MAXWORKGROUPS) 770490792Sgshapiro NumWorkGroups = MAXWORKGROUPS; /* the limit */ 770590792Sgshapiro 770690792Sgshapiro /* 770790792Sgshapiro ** We now know the number of work groups to pack the queue groups 770890792Sgshapiro ** into. The queue groups in 'Queue' are sorted from highest 770990792Sgshapiro ** to lowest for the number of runners per queue group. 771090792Sgshapiro ** We put the queue groups with the largest number of runners 771190792Sgshapiro ** into work groups first. Then the smaller ones are fitted in 771290792Sgshapiro ** where it looks best. 771390792Sgshapiro */ 771490792Sgshapiro 771590792Sgshapiro j = 0; 771690792Sgshapiro dir = 1; 771790792Sgshapiro for (i = 0; i < NumQueue; i++) 771890792Sgshapiro { 771990792Sgshapiro /* a to-and-fro packing scheme, continue from last position */ 772090792Sgshapiro if (j >= NumWorkGroups) 772190792Sgshapiro { 772290792Sgshapiro dir = -1; 772390792Sgshapiro j = NumWorkGroups - 1; 772490792Sgshapiro } 772590792Sgshapiro else if (j < 0) 772690792Sgshapiro { 772790792Sgshapiro j = 0; 772890792Sgshapiro dir = 1; 772990792Sgshapiro } 773090792Sgshapiro 773190792Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 773294334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) * 773394334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 773494334Sgshapiro else 773594334Sgshapiro WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs, 773694334Sgshapiro sizeof(QUEUEGRP *) * 773794334Sgshapiro (WorkGrp[j].wg_numqgrp + 1)); 773894334Sgshapiro if (WorkGrp[j].wg_qgs == NULL) 773990792Sgshapiro { 774094334Sgshapiro syserr("!cannot allocate memory for work queues, need %d bytes", 774190792Sgshapiro (int) (sizeof(QUEUEGRP *) * 774290792Sgshapiro (WorkGrp[j].wg_numqgrp + 1))); 774390792Sgshapiro } 774490792Sgshapiro 7745125820Sgshapiro h = si[i].sg_idx; 7746125820Sgshapiro WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[h]; 774790792Sgshapiro WorkGrp[j].wg_numqgrp++; 7748125820Sgshapiro WorkGrp[j].wg_runners += Queue[h]->qg_maxqrun; 7749125820Sgshapiro Queue[h]->qg_wgrp = j; 775090792Sgshapiro 775190792Sgshapiro if (WorkGrp[j].wg_maxact == 0) 775290792Sgshapiro { 775390792Sgshapiro /* can't have more runners than allowed total */ 775490792Sgshapiro if (MaxQueueChildren > 0 && 7755125820Sgshapiro Queue[h]->qg_maxqrun > MaxQueueChildren) 7756125820Sgshapiro Queue[h]->qg_maxqrun = MaxQueueChildren; 7757125820Sgshapiro WorkGrp[j].wg_maxact = Queue[h]->qg_maxqrun; 775890792Sgshapiro } 775990792Sgshapiro 776090792Sgshapiro /* 776190792Sgshapiro ** XXX: must wg_lowqintvl be the GCD? 776290792Sgshapiro ** qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for 776390792Sgshapiro ** qg2 occur? 776490792Sgshapiro */ 776590792Sgshapiro 776690792Sgshapiro /* keep track of the lowest interval for a persistent runner */ 7767125820Sgshapiro if (Queue[h]->qg_queueintvl > 0 && 7768125820Sgshapiro WorkGrp[j].wg_lowqintvl < Queue[h]->qg_queueintvl) 7769125820Sgshapiro WorkGrp[j].wg_lowqintvl = Queue[h]->qg_queueintvl; 777090792Sgshapiro j += dir; 777190792Sgshapiro } 777290792Sgshapiro if (tTd(41, 9)) 777390792Sgshapiro { 777490792Sgshapiro for (i = 0; i < NumWorkGroups; i++) 777590792Sgshapiro { 777690792Sgshapiro sm_dprintf("Workgroup[%d]=", i); 777790792Sgshapiro for (j = 0; j < WorkGrp[i].wg_numqgrp; j++) 777890792Sgshapiro { 777990792Sgshapiro sm_dprintf("%s, ", 778090792Sgshapiro WorkGrp[i].wg_qgs[j]->qg_name); 778190792Sgshapiro } 778290792Sgshapiro sm_dprintf("\n"); 778390792Sgshapiro } 778490792Sgshapiro } 778590792Sgshapiro} 778690792Sgshapiro 778790792Sgshapiro/* 778890792Sgshapiro** DUP_DF -- duplicate envelope data file 778990792Sgshapiro** 779090792Sgshapiro** Copy the data file from the 'old' envelope to the 'new' envelope 779190792Sgshapiro** in the most efficient way possible. 779290792Sgshapiro** 779390792Sgshapiro** Create a hard link from the 'old' data file to the 'new' data file. 779490792Sgshapiro** If the old and new queue directories are on different file systems, 779590792Sgshapiro** then the new data file link is created in the old queue directory, 779690792Sgshapiro** and the new queue file will contain a 'd' record pointing to the 779790792Sgshapiro** directory containing the new data file. 779890792Sgshapiro** 779990792Sgshapiro** Parameters: 780090792Sgshapiro** old -- old envelope. 780190792Sgshapiro** new -- new envelope. 780290792Sgshapiro** 780390792Sgshapiro** Results: 780490792Sgshapiro** Returns true on success, false on failure. 780590792Sgshapiro** 780690792Sgshapiro** Side Effects: 780790792Sgshapiro** On success, the new data file is created. 780890792Sgshapiro** On fatal failure, EF_FATALERRS is set in old->e_flags. 780990792Sgshapiro*/ 781090792Sgshapiro 781190792Sgshapirostatic bool dup_df __P((ENVELOPE *, ENVELOPE *)); 781290792Sgshapiro 781390792Sgshapirostatic bool 781490792Sgshapirodup_df(old, new) 781590792Sgshapiro ENVELOPE *old; 781690792Sgshapiro ENVELOPE *new; 781790792Sgshapiro{ 781890792Sgshapiro int ofs, nfs, r; 781990792Sgshapiro char opath[MAXPATHLEN]; 782090792Sgshapiro char npath[MAXPATHLEN]; 782190792Sgshapiro 782294334Sgshapiro if (!bitset(EF_HAS_DF, old->e_flags)) 782394334Sgshapiro { 782494334Sgshapiro /* 782594334Sgshapiro ** this can happen if: SuperSafe != True 782694334Sgshapiro ** and a bounce mail is sent that is split. 782794334Sgshapiro */ 782894334Sgshapiro 782994334Sgshapiro queueup(old, false, true); 783094334Sgshapiro } 783190792Sgshapiro SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir)); 783290792Sgshapiro SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir)); 783390792Sgshapiro 7834168515Sgshapiro (void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof(opath)); 7835168515Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath)); 783690792Sgshapiro 783790792Sgshapiro if (old->e_dfp != NULL) 783890792Sgshapiro { 783990792Sgshapiro r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL); 784090792Sgshapiro if (r < 0 && errno != EINVAL) 784190792Sgshapiro { 784290792Sgshapiro syserr("@can't commit %s", opath); 784390792Sgshapiro old->e_flags |= EF_FATALERRS; 784490792Sgshapiro return false; 784590792Sgshapiro } 784690792Sgshapiro } 784790792Sgshapiro 784890792Sgshapiro /* 784990792Sgshapiro ** Attempt to create a hard link, if we think both old and new 785090792Sgshapiro ** are on the same file system, otherwise copy the file. 785190792Sgshapiro ** 785290792Sgshapiro ** Don't waste time attempting a hard link unless old and new 785390792Sgshapiro ** are on the same file system. 785490792Sgshapiro */ 785590792Sgshapiro 7856157001Sgshapiro SM_REQUIRE(ISVALIDQGRP(old->e_dfqgrp) && ISVALIDQDIR(old->e_dfqdir)); 7857157001Sgshapiro SM_REQUIRE(ISVALIDQGRP(new->e_dfqgrp) && ISVALIDQDIR(new->e_dfqdir)); 7858157001Sgshapiro 7859157001Sgshapiro ofs = Queue[old->e_dfqgrp]->qg_qpaths[old->e_dfqdir].qp_fsysidx; 7860157001Sgshapiro nfs = Queue[new->e_dfqgrp]->qg_qpaths[new->e_dfqdir].qp_fsysidx; 786190792Sgshapiro if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs)) 786290792Sgshapiro { 786390792Sgshapiro if (link(opath, npath) == 0) 786490792Sgshapiro { 786590792Sgshapiro new->e_flags |= EF_HAS_DF; 786690792Sgshapiro SYNC_DIR(npath, true); 786790792Sgshapiro return true; 786890792Sgshapiro } 786990792Sgshapiro goto error; 787090792Sgshapiro } 787190792Sgshapiro 787290792Sgshapiro /* 787390792Sgshapiro ** Can't link across queue directories, so try to create a hard 787490792Sgshapiro ** link in the same queue directory as the old df file. 787590792Sgshapiro ** The qf file will refer to the new df file using a 'd' record. 787690792Sgshapiro */ 787790792Sgshapiro 787890792Sgshapiro new->e_dfqgrp = old->e_dfqgrp; 787990792Sgshapiro new->e_dfqdir = old->e_dfqdir; 7880168515Sgshapiro (void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath)); 788190792Sgshapiro if (link(opath, npath) == 0) 788290792Sgshapiro { 788390792Sgshapiro new->e_flags |= EF_HAS_DF; 788490792Sgshapiro SYNC_DIR(npath, true); 788590792Sgshapiro return true; 788690792Sgshapiro } 788790792Sgshapiro 788890792Sgshapiro error: 788990792Sgshapiro if (LogLevel > 0) 789090792Sgshapiro sm_syslog(LOG_ERR, old->e_id, 789190792Sgshapiro "dup_df: can't link %s to %s, error=%s, envelope splitting failed", 789290792Sgshapiro opath, npath, sm_errstring(errno)); 789390792Sgshapiro return false; 789490792Sgshapiro} 789590792Sgshapiro 789690792Sgshapiro/* 789790792Sgshapiro** SPLIT_ENV -- Allocate a new envelope based on a given envelope. 789890792Sgshapiro** 789990792Sgshapiro** Parameters: 790090792Sgshapiro** e -- envelope. 790190792Sgshapiro** sendqueue -- sendqueue for new envelope. 790290792Sgshapiro** qgrp -- index of queue group. 790390792Sgshapiro** qdir -- queue directory. 790490792Sgshapiro** 790590792Sgshapiro** Results: 790690792Sgshapiro** new envelope. 790790792Sgshapiro** 790890792Sgshapiro*/ 790990792Sgshapiro 791090792Sgshapirostatic ENVELOPE *split_env __P((ENVELOPE *, ADDRESS *, int, int)); 791190792Sgshapiro 791290792Sgshapirostatic ENVELOPE * 791390792Sgshapirosplit_env(e, sendqueue, qgrp, qdir) 791490792Sgshapiro ENVELOPE *e; 791590792Sgshapiro ADDRESS *sendqueue; 791690792Sgshapiro int qgrp; 791790792Sgshapiro int qdir; 791890792Sgshapiro{ 791990792Sgshapiro ENVELOPE *ee; 792090792Sgshapiro 7921168515Sgshapiro ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof(*ee)); 792290792Sgshapiro STRUCTCOPY(*e, *ee); 792390792Sgshapiro ee->e_message = NULL; /* XXX use original message? */ 792490792Sgshapiro ee->e_id = NULL; 792590792Sgshapiro assign_queueid(ee); 792690792Sgshapiro ee->e_sendqueue = sendqueue; 792790792Sgshapiro ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS 792890792Sgshapiro |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF); 792990792Sgshapiro ee->e_flags |= EF_NORECEIPT; /* XXX really? */ 793090792Sgshapiro ee->e_from.q_state = QS_SENDER; 793190792Sgshapiro ee->e_dfp = NULL; 793290792Sgshapiro ee->e_lockfp = NULL; 793390792Sgshapiro if (e->e_xfp != NULL) 793490792Sgshapiro ee->e_xfp = sm_io_dup(e->e_xfp); 793594334Sgshapiro 793694334Sgshapiro /* failed to dup e->e_xfp, start a new transcript */ 793794334Sgshapiro if (ee->e_xfp == NULL) 793894334Sgshapiro openxscript(ee); 793994334Sgshapiro 794090792Sgshapiro ee->e_qgrp = ee->e_dfqgrp = qgrp; 794190792Sgshapiro ee->e_qdir = ee->e_dfqdir = qdir; 794290792Sgshapiro ee->e_errormode = EM_MAIL; 794390792Sgshapiro ee->e_statmsg = NULL; 794490792Sgshapiro if (e->e_quarmsg != NULL) 794590792Sgshapiro ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool, 794690792Sgshapiro e->e_quarmsg); 794790792Sgshapiro 794890792Sgshapiro /* 794990792Sgshapiro ** XXX Not sure if this copying is necessary. 795094334Sgshapiro ** sendall() does this copying, but I (dm) don't know if that is 795190792Sgshapiro ** because of the storage management discipline we were using 795290792Sgshapiro ** before rpools were introduced, or if it is because these lists 795390792Sgshapiro ** can be modified later. 795490792Sgshapiro */ 795590792Sgshapiro 795690792Sgshapiro ee->e_header = copyheader(e->e_header, ee->e_rpool); 795790792Sgshapiro ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool); 795890792Sgshapiro 795990792Sgshapiro return ee; 796090792Sgshapiro} 796190792Sgshapiro 796290792Sgshapiro/* return values from split functions, check also below! */ 796390792Sgshapiro#define SM_SPLIT_FAIL (0) 796490792Sgshapiro#define SM_SPLIT_NONE (1) 796590792Sgshapiro#define SM_SPLIT_NEW(n) (1 + (n)) 796690792Sgshapiro 796790792Sgshapiro/* 796890792Sgshapiro** SPLIT_ACROSS_QUEUE_GROUPS 796990792Sgshapiro** 797090792Sgshapiro** This function splits an envelope across multiple queue groups 797190792Sgshapiro** based on the queue group of each recipient. 797290792Sgshapiro** 797390792Sgshapiro** Parameters: 797490792Sgshapiro** e -- envelope. 797590792Sgshapiro** 797690792Sgshapiro** Results: 797790792Sgshapiro** SM_SPLIT_FAIL on failure 797890792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 797990792Sgshapiro** or 1 + the number of additional envelopes created. 798090792Sgshapiro** 798190792Sgshapiro** Side Effects: 798290792Sgshapiro** On success, e->e_sibling points to a list of zero or more 798390792Sgshapiro** additional envelopes, and the associated data files exist 798490792Sgshapiro** on disk. But the queue files are not created. 798590792Sgshapiro** 798690792Sgshapiro** On failure, e->e_sibling is not changed. 798790792Sgshapiro** The order of recipients in e->e_sendqueue is permuted. 798890792Sgshapiro** Abandoned data files for additional envelopes that failed 798990792Sgshapiro** to be created may exist on disk. 799090792Sgshapiro*/ 799190792Sgshapiro 799290792Sgshapirostatic int q_qgrp_compare __P((const void *, const void *)); 799390792Sgshapirostatic int e_filesys_compare __P((const void *, const void *)); 799490792Sgshapiro 799590792Sgshapirostatic int 799690792Sgshapiroq_qgrp_compare(p1, p2) 799790792Sgshapiro const void *p1; 799890792Sgshapiro const void *p2; 799990792Sgshapiro{ 800090792Sgshapiro ADDRESS **pq1 = (ADDRESS **) p1; 800190792Sgshapiro ADDRESS **pq2 = (ADDRESS **) p2; 800290792Sgshapiro 800390792Sgshapiro return (*pq1)->q_qgrp - (*pq2)->q_qgrp; 800490792Sgshapiro} 800590792Sgshapiro 800690792Sgshapirostatic int 800790792Sgshapiroe_filesys_compare(p1, p2) 800890792Sgshapiro const void *p1; 800990792Sgshapiro const void *p2; 801090792Sgshapiro{ 801190792Sgshapiro ENVELOPE **pe1 = (ENVELOPE **) p1; 801290792Sgshapiro ENVELOPE **pe2 = (ENVELOPE **) p2; 801390792Sgshapiro int fs1, fs2; 801490792Sgshapiro 801590792Sgshapiro fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx; 801690792Sgshapiro fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx; 801790792Sgshapiro if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2)) 801890792Sgshapiro return -1; 801990792Sgshapiro if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2)) 802090792Sgshapiro return 1; 802190792Sgshapiro return 0; 802290792Sgshapiro} 802390792Sgshapiro 8024168515Sgshapirostatic int split_across_queue_groups __P((ENVELOPE *)); 802590792Sgshapirostatic int 802690792Sgshapirosplit_across_queue_groups(e) 802790792Sgshapiro ENVELOPE *e; 802890792Sgshapiro{ 802990792Sgshapiro int naddrs, nsplits, i; 8030102528Sgshapiro bool changed; 803190792Sgshapiro char **pvp; 803290792Sgshapiro ADDRESS *q, **addrs; 803390792Sgshapiro ENVELOPE *ee, *es; 803490792Sgshapiro ENVELOPE *splits[MAXQUEUEGROUPS]; 803590792Sgshapiro char pvpbuf[PSBUFSIZE]; 803690792Sgshapiro 803790792Sgshapiro SM_REQUIRE(ISVALIDQGRP(e->e_qgrp)); 803890792Sgshapiro 803990792Sgshapiro /* Count addresses and assign queue groups. */ 804090792Sgshapiro naddrs = 0; 8041102528Sgshapiro changed = false; 804290792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 804390792Sgshapiro { 804490792Sgshapiro if (QS_IS_DEAD(q->q_state)) 804590792Sgshapiro continue; 804690792Sgshapiro ++naddrs; 804790792Sgshapiro 804890792Sgshapiro /* bad addresses and those already sent stay put */ 804990792Sgshapiro if (QS_IS_BADADDR(q->q_state) || 805090792Sgshapiro QS_IS_SENT(q->q_state)) 805190792Sgshapiro q->q_qgrp = e->e_qgrp; 805290792Sgshapiro else if (!ISVALIDQGRP(q->q_qgrp)) 805390792Sgshapiro { 805490792Sgshapiro /* call ruleset which should return a queue group */ 805590792Sgshapiro i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp, 805690792Sgshapiro pvpbuf, sizeof(pvpbuf)); 805790792Sgshapiro if (i == EX_OK && 805890792Sgshapiro pvp != NULL && pvp[0] != NULL && 805990792Sgshapiro (pvp[0][0] & 0377) == CANONNET && 806090792Sgshapiro pvp[1] != NULL && pvp[1][0] != '\0') 806190792Sgshapiro { 806290792Sgshapiro i = name2qid(pvp[1]); 806390792Sgshapiro if (ISVALIDQGRP(i)) 806490792Sgshapiro { 806590792Sgshapiro q->q_qgrp = i; 8066102528Sgshapiro changed = true; 806790792Sgshapiro if (tTd(20, 4)) 806890792Sgshapiro sm_syslog(LOG_INFO, NOQID, 806990792Sgshapiro "queue group name %s -> %d", 807090792Sgshapiro pvp[1], i); 807190792Sgshapiro continue; 807290792Sgshapiro } 807390792Sgshapiro else if (LogLevel > 10) 807490792Sgshapiro sm_syslog(LOG_INFO, NOQID, 807590792Sgshapiro "can't find queue group name %s, selection ignored", 807690792Sgshapiro pvp[1]); 807790792Sgshapiro } 807890792Sgshapiro if (q->q_mailer != NULL && 807990792Sgshapiro ISVALIDQGRP(q->q_mailer->m_qgrp)) 8080102528Sgshapiro { 8081102528Sgshapiro changed = true; 808290792Sgshapiro q->q_qgrp = q->q_mailer->m_qgrp; 8083102528Sgshapiro } 808494334Sgshapiro else if (ISVALIDQGRP(e->e_qgrp)) 808594334Sgshapiro q->q_qgrp = e->e_qgrp; 808690792Sgshapiro else 808790792Sgshapiro q->q_qgrp = 0; 808890792Sgshapiro } 808990792Sgshapiro } 809090792Sgshapiro 809190792Sgshapiro /* only one address? nothing to split. */ 8092102528Sgshapiro if (naddrs <= 1 && !changed) 809390792Sgshapiro return SM_SPLIT_NONE; 809490792Sgshapiro 809590792Sgshapiro /* sort the addresses by queue group */ 809690792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *)); 809790792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 809890792Sgshapiro { 809990792Sgshapiro if (QS_IS_DEAD(q->q_state)) 810090792Sgshapiro continue; 810190792Sgshapiro addrs[i++] = q; 810290792Sgshapiro } 810390792Sgshapiro qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare); 810490792Sgshapiro 810590792Sgshapiro /* split into multiple envelopes, by queue group */ 810690792Sgshapiro nsplits = 0; 810790792Sgshapiro es = NULL; 810890792Sgshapiro e->e_sendqueue = NULL; 810990792Sgshapiro for (i = 0; i < naddrs; ++i) 811090792Sgshapiro { 811190792Sgshapiro if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp) 811290792Sgshapiro addrs[i]->q_next = NULL; 811390792Sgshapiro else 811490792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 811590792Sgshapiro 811690792Sgshapiro /* same queue group as original envelope? */ 811790792Sgshapiro if (addrs[i]->q_qgrp == e->e_qgrp) 811890792Sgshapiro { 811990792Sgshapiro if (e->e_sendqueue == NULL) 812090792Sgshapiro e->e_sendqueue = addrs[i]; 812190792Sgshapiro continue; 812290792Sgshapiro } 812390792Sgshapiro 812490792Sgshapiro /* different queue group than original envelope */ 812590792Sgshapiro if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp) 812690792Sgshapiro { 812790792Sgshapiro ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR); 812890792Sgshapiro es = ee; 812990792Sgshapiro splits[nsplits++] = ee; 813090792Sgshapiro } 813190792Sgshapiro } 813290792Sgshapiro 813390792Sgshapiro /* no splits? return right now. */ 813490792Sgshapiro if (nsplits <= 0) 813590792Sgshapiro return SM_SPLIT_NONE; 813690792Sgshapiro 813790792Sgshapiro /* assign a queue directory to each additional envelope */ 813890792Sgshapiro for (i = 0; i < nsplits; ++i) 813990792Sgshapiro { 814090792Sgshapiro es = splits[i]; 814190792Sgshapiro#if 0 814290792Sgshapiro es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es); 814390792Sgshapiro#endif /* 0 */ 814490792Sgshapiro if (!setnewqueue(es)) 814590792Sgshapiro goto failure; 814690792Sgshapiro } 814790792Sgshapiro 814890792Sgshapiro /* sort the additional envelopes by queue file system */ 814990792Sgshapiro qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare); 815090792Sgshapiro 815190792Sgshapiro /* create data files for each additional envelope */ 815290792Sgshapiro if (!dup_df(e, splits[0])) 815390792Sgshapiro { 815490792Sgshapiro i = 0; 815590792Sgshapiro goto failure; 815690792Sgshapiro } 815790792Sgshapiro for (i = 1; i < nsplits; ++i) 815890792Sgshapiro { 815990792Sgshapiro /* copy or link to the previous data file */ 816090792Sgshapiro if (!dup_df(splits[i - 1], splits[i])) 816190792Sgshapiro goto failure; 816290792Sgshapiro } 816390792Sgshapiro 816490792Sgshapiro /* success: prepend the new envelopes to the e->e_sibling list */ 816590792Sgshapiro for (i = 0; i < nsplits; ++i) 816690792Sgshapiro { 816790792Sgshapiro es = splits[i]; 816890792Sgshapiro es->e_sibling = e->e_sibling; 816990792Sgshapiro e->e_sibling = es; 817090792Sgshapiro } 817190792Sgshapiro return SM_SPLIT_NEW(nsplits); 817290792Sgshapiro 817390792Sgshapiro /* failure: clean up */ 817490792Sgshapiro failure: 817590792Sgshapiro if (i > 0) 817690792Sgshapiro { 817790792Sgshapiro int j; 817890792Sgshapiro 817990792Sgshapiro for (j = 0; j < i; j++) 818090792Sgshapiro (void) unlink(queuename(splits[j], DATAFL_LETTER)); 818190792Sgshapiro } 818290792Sgshapiro e->e_sendqueue = addrs[0]; 818390792Sgshapiro for (i = 0; i < naddrs - 1; ++i) 818490792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 818590792Sgshapiro addrs[naddrs - 1]->q_next = NULL; 818690792Sgshapiro return SM_SPLIT_FAIL; 818790792Sgshapiro} 818890792Sgshapiro 818990792Sgshapiro/* 819090792Sgshapiro** SPLIT_WITHIN_QUEUE 819190792Sgshapiro** 819290792Sgshapiro** Split an envelope with multiple recipients into several 819390792Sgshapiro** envelopes within the same queue directory, if the number of 819490792Sgshapiro** recipients exceeds the limit for the queue group. 819590792Sgshapiro** 819690792Sgshapiro** Parameters: 819790792Sgshapiro** e -- envelope. 819890792Sgshapiro** 819990792Sgshapiro** Results: 820090792Sgshapiro** SM_SPLIT_FAIL on failure 820190792Sgshapiro** SM_SPLIT_NONE if no splitting occurred, 820290792Sgshapiro** or 1 + the number of additional envelopes created. 820390792Sgshapiro*/ 820490792Sgshapiro 820590792Sgshapiro#define SPLIT_LOG_LEVEL 8 820690792Sgshapiro 820790792Sgshapirostatic int split_within_queue __P((ENVELOPE *)); 820890792Sgshapiro 820990792Sgshapirostatic int 821090792Sgshapirosplit_within_queue(e) 821190792Sgshapiro ENVELOPE *e; 821290792Sgshapiro{ 821390792Sgshapiro int maxrcpt, nrcpt, ndead, nsplit, i; 821490792Sgshapiro int j, l; 821590792Sgshapiro char *lsplits; 821690792Sgshapiro ADDRESS *q, **addrs; 821790792Sgshapiro ENVELOPE *ee, *firstsibling; 821890792Sgshapiro 821990792Sgshapiro if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags)) 822090792Sgshapiro return SM_SPLIT_NONE; 822190792Sgshapiro 822290792Sgshapiro /* don't bother if there is no recipient limit */ 822390792Sgshapiro maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt; 822490792Sgshapiro if (maxrcpt <= 0) 822590792Sgshapiro return SM_SPLIT_NONE; 822690792Sgshapiro 822790792Sgshapiro /* count recipients */ 822890792Sgshapiro nrcpt = 0; 822990792Sgshapiro for (q = e->e_sendqueue; q != NULL; q = q->q_next) 823090792Sgshapiro { 823190792Sgshapiro if (QS_IS_DEAD(q->q_state)) 823290792Sgshapiro continue; 823390792Sgshapiro ++nrcpt; 823490792Sgshapiro } 823590792Sgshapiro if (nrcpt <= maxrcpt) 823690792Sgshapiro return SM_SPLIT_NONE; 823790792Sgshapiro 823890792Sgshapiro /* 823990792Sgshapiro ** Preserve the recipient list 824090792Sgshapiro ** so that we can restore it in case of error. 824190792Sgshapiro ** (But we discard dead addresses.) 824290792Sgshapiro */ 824390792Sgshapiro 824490792Sgshapiro addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *)); 824590792Sgshapiro for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next) 824690792Sgshapiro { 824790792Sgshapiro if (QS_IS_DEAD(q->q_state)) 824890792Sgshapiro continue; 824990792Sgshapiro addrs[i++] = q; 825090792Sgshapiro } 825190792Sgshapiro 825290792Sgshapiro /* 825390792Sgshapiro ** Partition the recipient list so that bad and sent addresses 825490792Sgshapiro ** come first. These will go with the original envelope, and 825590792Sgshapiro ** do not count towards the maxrcpt limit. 825690792Sgshapiro ** addrs[] does not contain QS_IS_DEAD() addresses. 825790792Sgshapiro */ 825890792Sgshapiro 825990792Sgshapiro ndead = 0; 826090792Sgshapiro for (i = 0; i < nrcpt; ++i) 826190792Sgshapiro { 826290792Sgshapiro if (QS_IS_BADADDR(addrs[i]->q_state) || 826390792Sgshapiro QS_IS_SENT(addrs[i]->q_state) || 826490792Sgshapiro QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */ 826590792Sgshapiro { 826690792Sgshapiro if (i > ndead) 826790792Sgshapiro { 826890792Sgshapiro ADDRESS *tmp = addrs[i]; 826990792Sgshapiro 827090792Sgshapiro addrs[i] = addrs[ndead]; 827190792Sgshapiro addrs[ndead] = tmp; 827290792Sgshapiro } 827390792Sgshapiro ++ndead; 827490792Sgshapiro } 827590792Sgshapiro } 827690792Sgshapiro 827790792Sgshapiro /* Check if no splitting required. */ 827890792Sgshapiro if (nrcpt - ndead <= maxrcpt) 827990792Sgshapiro return SM_SPLIT_NONE; 828090792Sgshapiro 828190792Sgshapiro /* fix links */ 828290792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 828390792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 828490792Sgshapiro addrs[nrcpt - 1]->q_next = NULL; 828590792Sgshapiro e->e_sendqueue = addrs[0]; 828690792Sgshapiro 828790792Sgshapiro /* prepare buffer for logging */ 828890792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL) 828990792Sgshapiro { 829090792Sgshapiro l = MAXLINE; 829190792Sgshapiro lsplits = sm_malloc(l); 829290792Sgshapiro if (lsplits != NULL) 829390792Sgshapiro *lsplits = '\0'; 829490792Sgshapiro j = 0; 829590792Sgshapiro } 829690792Sgshapiro else 829790792Sgshapiro { 829890792Sgshapiro /* get rid of stupid compiler warnings */ 829990792Sgshapiro lsplits = NULL; 830090792Sgshapiro j = l = 0; 830190792Sgshapiro } 830290792Sgshapiro 830390792Sgshapiro /* split the envelope */ 830490792Sgshapiro firstsibling = e->e_sibling; 830590792Sgshapiro i = maxrcpt + ndead; 830690792Sgshapiro nsplit = 0; 830790792Sgshapiro for (;;) 830890792Sgshapiro { 830990792Sgshapiro addrs[i - 1]->q_next = NULL; 831090792Sgshapiro ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir); 831190792Sgshapiro if (!dup_df(e, ee)) 831290792Sgshapiro { 831390792Sgshapiro 831490792Sgshapiro ee = firstsibling; 831590792Sgshapiro while (ee != NULL) 831690792Sgshapiro { 831790792Sgshapiro (void) unlink(queuename(ee, DATAFL_LETTER)); 831890792Sgshapiro ee = ee->e_sibling; 831990792Sgshapiro } 832090792Sgshapiro 832190792Sgshapiro /* Error. Restore e's sibling & recipient lists. */ 832290792Sgshapiro e->e_sibling = firstsibling; 832390792Sgshapiro for (i = 0; i < nrcpt - 1; ++i) 832490792Sgshapiro addrs[i]->q_next = addrs[i + 1]; 8325110560Sgshapiro if (lsplits != NULL) 8326110560Sgshapiro sm_free(lsplits); 832790792Sgshapiro return SM_SPLIT_FAIL; 832890792Sgshapiro } 832990792Sgshapiro 833090792Sgshapiro /* prepend the new envelope to e->e_sibling */ 833190792Sgshapiro ee->e_sibling = e->e_sibling; 833290792Sgshapiro e->e_sibling = ee; 833390792Sgshapiro ++nsplit; 833490792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 833590792Sgshapiro { 833690792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 833790792Sgshapiro { 833890792Sgshapiro char *p; 833990792Sgshapiro 834090792Sgshapiro l += MAXLINE; 834190792Sgshapiro p = sm_realloc(lsplits, l); 834290792Sgshapiro if (p == NULL) 834390792Sgshapiro { 834490792Sgshapiro /* let's try to get this done */ 834590792Sgshapiro sm_free(lsplits); 834690792Sgshapiro lsplits = NULL; 834790792Sgshapiro } 834890792Sgshapiro else 834990792Sgshapiro lsplits = p; 835090792Sgshapiro } 835190792Sgshapiro if (lsplits != NULL) 835290792Sgshapiro { 835390792Sgshapiro if (j == 0) 835490792Sgshapiro j += sm_strlcat(lsplits + j, 835590792Sgshapiro ee->e_id, 835690792Sgshapiro l - j); 835790792Sgshapiro else 835890792Sgshapiro j += sm_strlcat2(lsplits + j, 835990792Sgshapiro "; ", 836090792Sgshapiro ee->e_id, 836190792Sgshapiro l - j); 836290792Sgshapiro SM_ASSERT(j < l); 836390792Sgshapiro } 836490792Sgshapiro } 836590792Sgshapiro if (nrcpt - i <= maxrcpt) 836690792Sgshapiro break; 836790792Sgshapiro i += maxrcpt; 836890792Sgshapiro } 8369110560Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 837090792Sgshapiro { 8371110560Sgshapiro if (nsplit > 0) 8372110560Sgshapiro { 8373110560Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, 8374110560Sgshapiro "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s", 8375110560Sgshapiro maxrcpt, nrcpt - ndead, nsplit, 8376110560Sgshapiro nsplit > 1 ? "s" : "", lsplits); 8377110560Sgshapiro } 837890792Sgshapiro sm_free(lsplits); 837990792Sgshapiro } 838090792Sgshapiro return SM_SPLIT_NEW(nsplit); 838190792Sgshapiro} 838290792Sgshapiro/* 838390792Sgshapiro** SPLIT_BY_RECIPIENT 838490792Sgshapiro** 838590792Sgshapiro** Split an envelope with multiple recipients into multiple 838690792Sgshapiro** envelopes as required by the sendmail configuration. 838790792Sgshapiro** 838890792Sgshapiro** Parameters: 838990792Sgshapiro** e -- envelope. 839090792Sgshapiro** 839190792Sgshapiro** Results: 839290792Sgshapiro** Returns true on success, false on failure. 839390792Sgshapiro** 839490792Sgshapiro** Side Effects: 839590792Sgshapiro** see split_across_queue_groups(), split_within_queue(e) 839690792Sgshapiro*/ 839790792Sgshapiro 839890792Sgshapirobool 839990792Sgshapirosplit_by_recipient(e) 840090792Sgshapiro ENVELOPE *e; 840190792Sgshapiro{ 840290792Sgshapiro int split, n, i, j, l; 840390792Sgshapiro char *lsplits; 840490792Sgshapiro ENVELOPE *ee, *next, *firstsibling; 840590792Sgshapiro 840690792Sgshapiro if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) || 840790792Sgshapiro bitset(EF_SPLIT, e->e_flags)) 840890792Sgshapiro return true; 840990792Sgshapiro n = split_across_queue_groups(e); 841090792Sgshapiro if (n == SM_SPLIT_FAIL) 841190792Sgshapiro return false; 841290792Sgshapiro firstsibling = ee = e->e_sibling; 841390792Sgshapiro if (n > 1 && LogLevel > SPLIT_LOG_LEVEL) 841490792Sgshapiro { 841590792Sgshapiro l = MAXLINE; 841690792Sgshapiro lsplits = sm_malloc(l); 841790792Sgshapiro if (lsplits != NULL) 841890792Sgshapiro *lsplits = '\0'; 841990792Sgshapiro j = 0; 842090792Sgshapiro } 842190792Sgshapiro else 842290792Sgshapiro { 842390792Sgshapiro /* get rid of stupid compiler warnings */ 842490792Sgshapiro lsplits = NULL; 842590792Sgshapiro j = l = 0; 842690792Sgshapiro } 842790792Sgshapiro for (i = 1; i < n; ++i) 842890792Sgshapiro { 842990792Sgshapiro next = ee->e_sibling; 843090792Sgshapiro if (split_within_queue(ee) == SM_SPLIT_FAIL) 843190792Sgshapiro { 843290792Sgshapiro e->e_sibling = firstsibling; 843390792Sgshapiro return false; 843490792Sgshapiro } 843590792Sgshapiro ee->e_flags |= EF_SPLIT; 843690792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL) 843790792Sgshapiro { 843890792Sgshapiro if (j >= l - strlen(ee->e_id) - 3) 843990792Sgshapiro { 844090792Sgshapiro char *p; 844190792Sgshapiro 844290792Sgshapiro l += MAXLINE; 844390792Sgshapiro p = sm_realloc(lsplits, l); 844490792Sgshapiro if (p == NULL) 844590792Sgshapiro { 844690792Sgshapiro /* let's try to get this done */ 844790792Sgshapiro sm_free(lsplits); 844890792Sgshapiro lsplits = NULL; 844990792Sgshapiro } 845090792Sgshapiro else 845190792Sgshapiro lsplits = p; 845290792Sgshapiro } 845390792Sgshapiro if (lsplits != NULL) 845490792Sgshapiro { 845590792Sgshapiro if (j == 0) 845690792Sgshapiro j += sm_strlcat(lsplits + j, 845790792Sgshapiro ee->e_id, l - j); 845890792Sgshapiro else 845990792Sgshapiro j += sm_strlcat2(lsplits + j, "; ", 846090792Sgshapiro ee->e_id, l - j); 846190792Sgshapiro SM_ASSERT(j < l); 846290792Sgshapiro } 846390792Sgshapiro } 846490792Sgshapiro ee = next; 846590792Sgshapiro } 846690792Sgshapiro if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1) 846790792Sgshapiro { 846890792Sgshapiro sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s", 846990792Sgshapiro n - 1, n > 2 ? "s" : "", lsplits); 847090792Sgshapiro sm_free(lsplits); 847190792Sgshapiro } 847290792Sgshapiro split = split_within_queue(e) != SM_SPLIT_FAIL; 847390792Sgshapiro if (split) 847490792Sgshapiro e->e_flags |= EF_SPLIT; 847590792Sgshapiro return split; 847690792Sgshapiro} 847790792Sgshapiro 847890792Sgshapiro/* 847990792Sgshapiro** QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope 848090792Sgshapiro** 848190792Sgshapiro** Add/remove quarantine reason and requeue appropriately. 848290792Sgshapiro** 848390792Sgshapiro** Parameters: 848490792Sgshapiro** qgrp -- queue group for the item 848590792Sgshapiro** qdir -- queue directory in the given queue group 848690792Sgshapiro** e -- envelope information for the item 848790792Sgshapiro** reason -- quarantine reason, NULL means unquarantine. 848890792Sgshapiro** 848990792Sgshapiro** Results: 849090792Sgshapiro** true if item changed, false otherwise 849190792Sgshapiro** 849290792Sgshapiro** Side Effects: 849390792Sgshapiro** Changes quarantine tag in queue file and renames it. 849490792Sgshapiro*/ 849590792Sgshapiro 849690792Sgshapirostatic bool 849790792Sgshapiroquarantine_queue_item(qgrp, qdir, e, reason) 849890792Sgshapiro int qgrp; 849990792Sgshapiro int qdir; 850090792Sgshapiro ENVELOPE *e; 850190792Sgshapiro char *reason; 850290792Sgshapiro{ 850390792Sgshapiro bool dirty = false; 850490792Sgshapiro bool failing = false; 850590792Sgshapiro bool foundq = false; 850690792Sgshapiro bool finished = false; 850790792Sgshapiro int fd; 850890792Sgshapiro int flags; 850990792Sgshapiro int oldtype; 851090792Sgshapiro int newtype; 851190792Sgshapiro int save_errno; 851290792Sgshapiro MODE_T oldumask = 0; 851390792Sgshapiro SM_FILE_T *oldqfp, *tempqfp; 851490792Sgshapiro char *bp; 8515168515Sgshapiro int bufsize; 851690792Sgshapiro char oldqf[MAXPATHLEN]; 851790792Sgshapiro char tempqf[MAXPATHLEN]; 851890792Sgshapiro char newqf[MAXPATHLEN]; 851990792Sgshapiro char buf[MAXLINE]; 852090792Sgshapiro 852190792Sgshapiro oldtype = queue_letter(e, ANYQFL_LETTER); 8522168515Sgshapiro (void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof(oldqf)); 8523168515Sgshapiro (void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof(tempqf)); 852490792Sgshapiro 852590792Sgshapiro /* 852690792Sgshapiro ** Instead of duplicating all the open 852790792Sgshapiro ** and lock code here, tell readqf() to 852890792Sgshapiro ** do that work and return the open 852990792Sgshapiro ** file pointer in e_lockfp. Note that 853090792Sgshapiro ** we must release the locks properly when 853190792Sgshapiro ** we are done. 853290792Sgshapiro */ 853390792Sgshapiro 853490792Sgshapiro if (!readqf(e, true)) 853590792Sgshapiro { 853690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 853790792Sgshapiro "Skipping %s\n", qid_printname(e)); 853890792Sgshapiro return false; 853990792Sgshapiro } 854090792Sgshapiro oldqfp = e->e_lockfp; 854190792Sgshapiro 854290792Sgshapiro /* open the new queue file */ 854390792Sgshapiro flags = O_CREAT|O_WRONLY|O_EXCL; 854490792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 854590792Sgshapiro oldumask = umask(002); 854690792Sgshapiro fd = open(tempqf, flags, QueueFileMode); 854790792Sgshapiro if (bitset(S_IWGRP, QueueFileMode)) 854890792Sgshapiro (void) umask(oldumask); 854990792Sgshapiro RELEASE_QUEUE; 855090792Sgshapiro 855190792Sgshapiro if (fd < 0) 855290792Sgshapiro { 855390792Sgshapiro save_errno = errno; 855490792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 855590792Sgshapiro "Skipping %s: Could not open %s: %s\n", 855690792Sgshapiro qid_printname(e), tempqf, 855790792Sgshapiro sm_errstring(save_errno)); 855890792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 855990792Sgshapiro return false; 856090792Sgshapiro } 856190792Sgshapiro if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB)) 856290792Sgshapiro { 856390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 856490792Sgshapiro "Skipping %s: Could not lock %s\n", 856590792Sgshapiro qid_printname(e), tempqf); 856690792Sgshapiro (void) close(fd); 856790792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 856890792Sgshapiro return false; 856990792Sgshapiro } 857090792Sgshapiro 857190792Sgshapiro tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd, 8572120256Sgshapiro SM_IO_WRONLY_B, NULL); 857390792Sgshapiro if (tempqfp == NULL) 857490792Sgshapiro { 857590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 857690792Sgshapiro "Skipping %s: Could not lock %s\n", 857790792Sgshapiro qid_printname(e), tempqf); 857890792Sgshapiro (void) close(fd); 857990792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 858090792Sgshapiro return false; 858190792Sgshapiro } 858290792Sgshapiro 858390792Sgshapiro /* Copy the data over, changing the quarantine reason */ 8584168515Sgshapiro while (bufsize = sizeof(buf), 8585168515Sgshapiro (bp = fgetfolded(buf, &bufsize, oldqfp)) != NULL) 858690792Sgshapiro { 858790792Sgshapiro if (tTd(40, 4)) 858890792Sgshapiro sm_dprintf("+++++ %s\n", bp); 858990792Sgshapiro switch (bp[0]) 859090792Sgshapiro { 859190792Sgshapiro case 'q': /* quarantine reason */ 859290792Sgshapiro foundq = true; 859390792Sgshapiro if (reason == NULL) 859490792Sgshapiro { 859590792Sgshapiro if (Verbose) 859690792Sgshapiro { 859790792Sgshapiro (void) sm_io_fprintf(smioout, 859890792Sgshapiro SM_TIME_DEFAULT, 859990792Sgshapiro "%s: Removed quarantine of \"%s\"\n", 860090792Sgshapiro e->e_id, &bp[1]); 860190792Sgshapiro } 860290792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "unquarantine"); 860390792Sgshapiro dirty = true; 860490792Sgshapiro } 860590792Sgshapiro else if (strcmp(reason, &bp[1]) == 0) 860690792Sgshapiro { 860790792Sgshapiro if (Verbose) 860890792Sgshapiro { 860990792Sgshapiro (void) sm_io_fprintf(smioout, 861090792Sgshapiro SM_TIME_DEFAULT, 861190792Sgshapiro "%s: Already quarantined with \"%s\"\n", 861290792Sgshapiro e->e_id, reason); 861390792Sgshapiro } 861490792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 861590792Sgshapiro "q%s\n", reason); 861690792Sgshapiro } 861790792Sgshapiro else 861890792Sgshapiro { 861990792Sgshapiro if (Verbose) 862090792Sgshapiro { 862190792Sgshapiro (void) sm_io_fprintf(smioout, 862290792Sgshapiro SM_TIME_DEFAULT, 862390792Sgshapiro "%s: Quarantine changed from \"%s\" to \"%s\"\n", 862490792Sgshapiro e->e_id, &bp[1], 862590792Sgshapiro reason); 862690792Sgshapiro } 862790792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 862890792Sgshapiro "q%s\n", reason); 862990792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 863090792Sgshapiro reason); 863190792Sgshapiro dirty = true; 863290792Sgshapiro } 863390792Sgshapiro break; 863490792Sgshapiro 863598121Sgshapiro case 'S': 863690792Sgshapiro /* 863790792Sgshapiro ** If we are quarantining an unquarantined item, 863890792Sgshapiro ** need to put in a new 'q' line before it's 863990792Sgshapiro ** too late. 864090792Sgshapiro */ 864190792Sgshapiro 864290792Sgshapiro if (!foundq && reason != NULL) 864390792Sgshapiro { 864490792Sgshapiro if (Verbose) 864590792Sgshapiro { 864690792Sgshapiro (void) sm_io_fprintf(smioout, 864790792Sgshapiro SM_TIME_DEFAULT, 864890792Sgshapiro "%s: Quarantined with \"%s\"\n", 864990792Sgshapiro e->e_id, reason); 865090792Sgshapiro } 865190792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 865290792Sgshapiro "q%s\n", reason); 865390792Sgshapiro sm_syslog(LOG_INFO, e->e_id, "quarantine=%s", 865490792Sgshapiro reason); 865590792Sgshapiro foundq = true; 865690792Sgshapiro dirty = true; 865790792Sgshapiro } 865890792Sgshapiro 865990792Sgshapiro /* Copy the line to the new file */ 866090792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 866190792Sgshapiro "%s\n", bp); 866290792Sgshapiro break; 866390792Sgshapiro 866490792Sgshapiro case '.': 866590792Sgshapiro finished = true; 866690792Sgshapiro /* FALLTHROUGH */ 866790792Sgshapiro 866890792Sgshapiro default: 866990792Sgshapiro /* Copy the line to the new file */ 867090792Sgshapiro (void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT, 867190792Sgshapiro "%s\n", bp); 867290792Sgshapiro break; 867390792Sgshapiro } 8674168515Sgshapiro if (bp != buf) 8675168515Sgshapiro sm_free(bp); 867690792Sgshapiro } 867790792Sgshapiro 867890792Sgshapiro /* Make sure we read the whole old file */ 867990792Sgshapiro errno = sm_io_error(tempqfp); 868090792Sgshapiro if (errno != 0 && errno != SM_IO_EOF) 868190792Sgshapiro { 868290792Sgshapiro save_errno = errno; 868390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 868490792Sgshapiro "Skipping %s: Error reading %s: %s\n", 868590792Sgshapiro qid_printname(e), oldqf, 868690792Sgshapiro sm_errstring(save_errno)); 868790792Sgshapiro failing = true; 868890792Sgshapiro } 868990792Sgshapiro 869090792Sgshapiro if (!failing && !finished) 869190792Sgshapiro { 869290792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 869390792Sgshapiro "Skipping %s: Incomplete file: %s\n", 869490792Sgshapiro qid_printname(e), oldqf); 869590792Sgshapiro failing = true; 869690792Sgshapiro } 869790792Sgshapiro 869890792Sgshapiro /* Check if we actually changed anything or we can just bail now */ 869990792Sgshapiro if (!dirty) 870090792Sgshapiro { 870190792Sgshapiro /* pretend we failed, even though we technically didn't */ 870290792Sgshapiro failing = true; 870390792Sgshapiro } 870490792Sgshapiro 870590792Sgshapiro /* Make sure we wrote things out safely */ 870690792Sgshapiro if (!failing && 870790792Sgshapiro (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 || 8708132943Sgshapiro ((SuperSafe == SAFE_REALLY || 8709132943Sgshapiro SuperSafe == SAFE_REALLY_POSTMILTER || 8710132943Sgshapiro SuperSafe == SAFE_INTERACTIVE) && 871190792Sgshapiro fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) || 871290792Sgshapiro ((errno = sm_io_error(tempqfp)) != 0))) 871390792Sgshapiro { 871490792Sgshapiro save_errno = errno; 871590792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 871690792Sgshapiro "Skipping %s: Error writing %s: %s\n", 871790792Sgshapiro qid_printname(e), tempqf, 871890792Sgshapiro sm_errstring(save_errno)); 871990792Sgshapiro failing = true; 872090792Sgshapiro } 872190792Sgshapiro 872290792Sgshapiro 872390792Sgshapiro /* Figure out the new filename */ 872490792Sgshapiro newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER); 872590792Sgshapiro if (oldtype == newtype) 872690792Sgshapiro { 872790792Sgshapiro /* going to rename tempqf to oldqf */ 8728168515Sgshapiro (void) sm_strlcpy(newqf, oldqf, sizeof(newqf)); 872990792Sgshapiro } 873090792Sgshapiro else 873190792Sgshapiro { 873290792Sgshapiro /* going to rename tempqf to new name based on newtype */ 8733168515Sgshapiro (void) sm_strlcpy(newqf, queuename(e, newtype), sizeof(newqf)); 873490792Sgshapiro } 873590792Sgshapiro 873690792Sgshapiro save_errno = 0; 873790792Sgshapiro 873890792Sgshapiro /* rename tempqf to newqf */ 873990792Sgshapiro if (!failing && 874090792Sgshapiro rename(tempqf, newqf) < 0) 874190792Sgshapiro save_errno = (errno == 0) ? EINVAL : errno; 874290792Sgshapiro 874390792Sgshapiro /* Check rename() success */ 874490792Sgshapiro if (!failing && save_errno != 0) 874590792Sgshapiro { 874690792Sgshapiro sm_syslog(LOG_DEBUG, e->e_id, 874790792Sgshapiro "quarantine_queue_item: rename(%s, %s): %s", 874890792Sgshapiro tempqf, newqf, sm_errstring(save_errno)); 874990792Sgshapiro 875090792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 875190792Sgshapiro "Error renaming %s to %s: %s\n", 875290792Sgshapiro tempqf, newqf, 875390792Sgshapiro sm_errstring(save_errno)); 875490792Sgshapiro if (oldtype == newtype) 875590792Sgshapiro { 875690792Sgshapiro /* 875790792Sgshapiro ** Bail here since we don't know the state of 875890792Sgshapiro ** the filesystem and may need to keep tempqf 875990792Sgshapiro ** for the user to rescue us. 876090792Sgshapiro */ 876190792Sgshapiro 876290792Sgshapiro RELEASE_QUEUE; 876390792Sgshapiro errno = save_errno; 876490792Sgshapiro syserr("!452 Error renaming control file %s", tempqf); 876590792Sgshapiro /* NOTREACHED */ 876690792Sgshapiro } 876790792Sgshapiro else 876890792Sgshapiro { 876990792Sgshapiro /* remove new file (if rename() half completed) */ 877090792Sgshapiro if (xunlink(newqf) < 0) 877190792Sgshapiro { 877290792Sgshapiro save_errno = errno; 877390792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 877490792Sgshapiro "Error removing %s: %s\n", 877590792Sgshapiro newqf, 877690792Sgshapiro sm_errstring(save_errno)); 877790792Sgshapiro } 877890792Sgshapiro 877990792Sgshapiro /* tempqf removed below */ 878090792Sgshapiro failing = true; 878190792Sgshapiro } 878290792Sgshapiro 878390792Sgshapiro } 878490792Sgshapiro 878590792Sgshapiro /* If changing file types, need to remove old type */ 878690792Sgshapiro if (!failing && oldtype != newtype) 878790792Sgshapiro { 878890792Sgshapiro if (xunlink(oldqf) < 0) 878990792Sgshapiro { 879090792Sgshapiro save_errno = errno; 879190792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 879290792Sgshapiro "Error removing %s: %s\n", 879390792Sgshapiro oldqf, sm_errstring(save_errno)); 879490792Sgshapiro } 879590792Sgshapiro } 879690792Sgshapiro 879790792Sgshapiro /* see if anything above failed */ 879890792Sgshapiro if (failing) 879990792Sgshapiro { 880090792Sgshapiro /* Something failed: remove new file, old file still there */ 880190792Sgshapiro (void) xunlink(tempqf); 880290792Sgshapiro } 880390792Sgshapiro 880490792Sgshapiro /* 880590792Sgshapiro ** fsync() after file operations to make sure metadata is 880690792Sgshapiro ** written to disk on filesystems in which renames are 880790792Sgshapiro ** not guaranteed. It's ok if they fail, mail won't be lost. 880890792Sgshapiro */ 880990792Sgshapiro 881090792Sgshapiro if (SuperSafe != SAFE_NO) 881190792Sgshapiro { 881290792Sgshapiro /* for soft-updates */ 881390792Sgshapiro (void) fsync(sm_io_getinfo(tempqfp, 881490792Sgshapiro SM_IO_WHAT_FD, NULL)); 881590792Sgshapiro 881690792Sgshapiro if (!failing) 881790792Sgshapiro { 881890792Sgshapiro /* for soft-updates */ 881990792Sgshapiro (void) fsync(sm_io_getinfo(oldqfp, 882090792Sgshapiro SM_IO_WHAT_FD, NULL)); 882190792Sgshapiro } 882290792Sgshapiro 882390792Sgshapiro /* for other odd filesystems */ 882490792Sgshapiro SYNC_DIR(tempqf, false); 882590792Sgshapiro } 882690792Sgshapiro 882790792Sgshapiro /* Close up shop */ 882890792Sgshapiro RELEASE_QUEUE; 882990792Sgshapiro if (tempqfp != NULL) 883090792Sgshapiro (void) sm_io_close(tempqfp, SM_TIME_DEFAULT); 883190792Sgshapiro if (oldqfp != NULL) 883290792Sgshapiro (void) sm_io_close(oldqfp, SM_TIME_DEFAULT); 883390792Sgshapiro 883490792Sgshapiro /* All went well */ 883590792Sgshapiro return !failing; 883690792Sgshapiro} 883790792Sgshapiro 883890792Sgshapiro/* 883990792Sgshapiro** QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue 884090792Sgshapiro** 884190792Sgshapiro** Read all matching queue items, add/remove quarantine 884290792Sgshapiro** reason, and requeue appropriately. 884390792Sgshapiro** 884490792Sgshapiro** Parameters: 884590792Sgshapiro** reason -- quarantine reason, "." means unquarantine. 884690792Sgshapiro** qgrplimit -- limit to single queue group unless NOQGRP 884790792Sgshapiro** 884890792Sgshapiro** Results: 884990792Sgshapiro** none. 885090792Sgshapiro** 885190792Sgshapiro** Side Effects: 885290792Sgshapiro** Lots of changes to the queue. 885390792Sgshapiro*/ 885490792Sgshapiro 885590792Sgshapirovoid 885690792Sgshapiroquarantine_queue(reason, qgrplimit) 885790792Sgshapiro char *reason; 885890792Sgshapiro int qgrplimit; 885990792Sgshapiro{ 886090792Sgshapiro int changed = 0; 886190792Sgshapiro int qgrp; 886290792Sgshapiro 886390792Sgshapiro /* Convert internal representation of unquarantine */ 886490792Sgshapiro if (reason != NULL && reason[0] == '.' && reason[1] == '\0') 886590792Sgshapiro reason = NULL; 886690792Sgshapiro 886790792Sgshapiro if (reason != NULL) 886890792Sgshapiro { 886990792Sgshapiro /* clean it */ 887090792Sgshapiro reason = newstr(denlstring(reason, true, true)); 887190792Sgshapiro } 887290792Sgshapiro 887390792Sgshapiro for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++) 887490792Sgshapiro { 887590792Sgshapiro int qdir; 887690792Sgshapiro 887790792Sgshapiro if (qgrplimit != NOQGRP && qgrplimit != qgrp) 887890792Sgshapiro continue; 887990792Sgshapiro 888090792Sgshapiro for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++) 888190792Sgshapiro { 888290792Sgshapiro int i; 888390792Sgshapiro int nrequests; 888490792Sgshapiro 888590792Sgshapiro if (StopRequest) 888690792Sgshapiro stop_sendmail(); 888790792Sgshapiro 8888203004Sgshapiro nrequests = gatherq(qgrp, qdir, true, NULL, NULL, NULL); 888990792Sgshapiro 889090792Sgshapiro /* first see if there is anything */ 889190792Sgshapiro if (nrequests <= 0) 889290792Sgshapiro { 889390792Sgshapiro if (Verbose) 889490792Sgshapiro { 889590792Sgshapiro (void) sm_io_fprintf(smioout, 889690792Sgshapiro SM_TIME_DEFAULT, "%s: no matches\n", 889790792Sgshapiro qid_printqueue(qgrp, qdir)); 889890792Sgshapiro } 889990792Sgshapiro continue; 890090792Sgshapiro } 890190792Sgshapiro 890290792Sgshapiro if (Verbose) 890390792Sgshapiro { 890490792Sgshapiro (void) sm_io_fprintf(smioout, 890590792Sgshapiro SM_TIME_DEFAULT, "Processing %s:\n", 890690792Sgshapiro qid_printqueue(qgrp, qdir)); 890790792Sgshapiro } 890890792Sgshapiro 890990792Sgshapiro for (i = 0; i < WorkListCount; i++) 891090792Sgshapiro { 891190792Sgshapiro ENVELOPE e; 891290792Sgshapiro 891390792Sgshapiro if (StopRequest) 891490792Sgshapiro stop_sendmail(); 891590792Sgshapiro 891690792Sgshapiro /* setup envelope */ 891790792Sgshapiro clearenvelope(&e, true, sm_rpool_new_x(NULL)); 891890792Sgshapiro e.e_id = WorkList[i].w_name + 2; 891990792Sgshapiro e.e_qgrp = qgrp; 892090792Sgshapiro e.e_qdir = qdir; 892190792Sgshapiro 892290792Sgshapiro if (tTd(70, 101)) 892390792Sgshapiro { 892490792Sgshapiro sm_io_fprintf(smioout, SM_TIME_DEFAULT, 892590792Sgshapiro "Would do %s\n", e.e_id); 892690792Sgshapiro changed++; 892790792Sgshapiro } 892890792Sgshapiro else if (quarantine_queue_item(qgrp, qdir, 892990792Sgshapiro &e, reason)) 893090792Sgshapiro changed++; 893190792Sgshapiro 893290792Sgshapiro /* clean up */ 893390792Sgshapiro sm_rpool_free(e.e_rpool); 893490792Sgshapiro e.e_rpool = NULL; 893590792Sgshapiro } 893690792Sgshapiro if (WorkList != NULL) 893790792Sgshapiro sm_free(WorkList); /* XXX */ 893890792Sgshapiro WorkList = NULL; 893990792Sgshapiro WorkListSize = 0; 894090792Sgshapiro WorkListCount = 0; 894190792Sgshapiro } 894290792Sgshapiro } 894390792Sgshapiro if (Verbose) 894490792Sgshapiro { 894590792Sgshapiro if (changed == 0) 894690792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 894790792Sgshapiro "No changes\n"); 894890792Sgshapiro else 894990792Sgshapiro (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 895090792Sgshapiro "%d change%s\n", 895190792Sgshapiro changed, 895290792Sgshapiro changed == 1 ? "" : "s"); 895390792Sgshapiro } 895490792Sgshapiro} 8955