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