queue.c revision 111823
138032Speter/*
2111823Sgshapiro * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1564562Sgshapiro
16111823SgshapiroSM_RCSID("@(#)$Id: queue.c,v 8.863.2.28 2003/02/11 17:17:22 ca Exp $")
1738032Speter
1890792Sgshapiro#include <dirent.h>
1938032Speter
2090792Sgshapiro# define RELEASE_QUEUE	(void) 0
2190792Sgshapiro# define ST_INODE(st)	(st).st_ino
2290792Sgshapiro
2390792Sgshapiro
2490792Sgshapiro/*
2590792Sgshapiro**  Historical notes:
26110560Sgshapiro**     QF_VERSION == 4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY
27110560Sgshapiro**     QF_VERSION == 5 was sendmail 8.10/8.11 with    _FFR_QUEUEDELAY
28110560Sgshapiro**     QF_VERSION == 6 is  sendmail 8.12      without _FFR_QUEUEDELAY
29110560Sgshapiro**     QF_VERSION == 7 is  sendmail 8.12      with    _FFR_QUEUEDELAY
3090792Sgshapiro*/
3190792Sgshapiro
3290792Sgshapiro#if _FFR_QUEUEDELAY
3390792Sgshapiro# define QF_VERSION	7	/* version number of this queue format */
3464562Sgshapirostatic time_t	queuedelay __P((ENVELOPE *));
3590792Sgshapiro#define queuedelay_qfver_unsupported(qfver) false
3690792Sgshapiro#else /* _FFR_QUEUEDELAY */
3790792Sgshapiro# define QF_VERSION	6	/* version number of this queue format */
3890792Sgshapiro# define queuedelay(e)	MinQueueAge
3990792Sgshapiro#define queuedelay_qfver_unsupported(qfver) ((qfver) == 5 || (qfver) == 7)
4090792Sgshapiro#endif /* _FFR_QUEUEDELAY */
4190792Sgshapiro#if _FFR_QUARANTINE
4290792Sgshapirostatic char	queue_letter __P((ENVELOPE *, int));
4390792Sgshapirostatic bool	quarantine_queue_item __P((int, int, ENVELOPE *, char *));
4490792Sgshapiro#endif /* _FFR_QUARANTINE */
4564562Sgshapiro
4690792Sgshapiro/* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */
4790792Sgshapiro
4838032Speter/*
4938032Speter**  Work queue.
5038032Speter*/
5138032Speter
5238032Speterstruct work
5338032Speter{
5438032Speter	char		*w_name;	/* name of control file */
5538032Speter	char		*w_host;	/* name of recipient host */
5638032Speter	bool		w_lock;		/* is message locked? */
5738032Speter	bool		w_tooyoung;	/* is it too young to run? */
5838032Speter	long		w_pri;		/* priority of message, see below */
5990792Sgshapiro	time_t		w_ctime;	/* creation time */
6090792Sgshapiro	time_t		w_mtime;	/* modification time */
6190792Sgshapiro	int		w_qgrp;		/* queue group located in */
6290792Sgshapiro	int		w_qdir;		/* queue directory located in */
6338032Speter	struct work	*w_next;	/* next in queue */
6438032Speter};
6538032Speter
6638032Spetertypedef struct work	WORK;
6738032Speter
6890792Sgshapirostatic WORK	*WorkQ;		/* queue of things to be done */
6990792Sgshapirostatic int	NumWorkGroups;	/* number of work groups */
7038032Speter
7190792Sgshapiro/*
7294334Sgshapiro**  DoQueueRun indicates that a queue run is needed.
7394334Sgshapiro**	Notice: DoQueueRun is modified in a signal handler!
7490792Sgshapiro*/
7590792Sgshapiro
76111823Sgshapirostatic bool	volatile DoQueueRun; /* non-interrupt time queue run needed */
7790792Sgshapiro
7890792Sgshapiro/*
7990792Sgshapiro**  Work group definition structure.
8090792Sgshapiro**	Each work group contains one or more queue groups. This is done
8190792Sgshapiro**	to manage the number of queue group runners active at the same time
8290792Sgshapiro**	to be within the constraints of MaxQueueChildren (if it is set).
8390792Sgshapiro**	The number of queue groups that can be run on the next work run
8490792Sgshapiro**	is kept track of. The queue groups are run in a round robin.
8590792Sgshapiro*/
8690792Sgshapiro
8790792Sgshapirostruct workgrp
8890792Sgshapiro{
8990792Sgshapiro	int		wg_numqgrp;	/* number of queue groups in work grp */
9090792Sgshapiro	int		wg_runners;	/* total runners */
9190792Sgshapiro	int		wg_curqgrp;	/* current queue group */
9290792Sgshapiro	QUEUEGRP	**wg_qgs;	/* array of queue groups */
9390792Sgshapiro	int		wg_maxact;	/* max # of active runners */
9490792Sgshapiro	time_t		wg_lowqintvl;	/* lowest queue interval */
9590792Sgshapiro	int		wg_restart;	/* needs restarting? */
9690792Sgshapiro	int		wg_restartcnt;	/* count of times restarted */
9790792Sgshapiro};
9890792Sgshapiro
9990792Sgshapirotypedef struct workgrp WORKGRP;
10090792Sgshapiro
10190792Sgshapirostatic WORKGRP	volatile WorkGrp[MAXWORKGROUPS + 1];	/* work groups */
10290792Sgshapiro
10390792Sgshapiro#if SM_HEAP_CHECK
10490792Sgshapirostatic SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q",
10590792Sgshapiro	"@(#)$Debug: leak_q - trace memory leaks during queue processing $");
10690792Sgshapiro#endif /* SM_HEAP_CHECK */
10790792Sgshapiro
10890792Sgshapiro/*
10990792Sgshapiro**  We use EmptyString instead of "" to avoid
11090792Sgshapiro**  'zero-length format string' warnings from gcc
11190792Sgshapiro*/
11290792Sgshapiro
11390792Sgshapirostatic const char EmptyString[] = "";
11490792Sgshapiro
11590792Sgshapirostatic void	grow_wlist __P((int, int));
11690792Sgshapirostatic int	multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *));
11790792Sgshapirostatic int	gatherq __P((int, int, bool, bool *, bool *));
11890792Sgshapirostatic int	sortq __P((int));
11990792Sgshapirostatic void	printctladdr __P((ADDRESS *, SM_FILE_T *));
12090792Sgshapirostatic bool	readqf __P((ENVELOPE *, bool));
12190792Sgshapirostatic void	restart_work_group __P((int));
12290792Sgshapirostatic void	runner_work __P((ENVELOPE *, int, bool, int, int));
12394334Sgshapirostatic void	schedule_queue_runs __P((bool, int, bool));
12464562Sgshapirostatic char	*strrev __P((char *));
12590792Sgshapirostatic ADDRESS	*setctluser __P((char *, int, ENVELOPE *));
12690792Sgshapiro#if _FFR_RHS
12790792Sgshapirostatic int	sm_strshufflecmp __P((char *, char *));
12890792Sgshapirostatic void	init_shuffle_alphabet __P(());
12990792Sgshapiro#endif /* _FFR_RHS */
13064562Sgshapirostatic int	workcmpf0();
13164562Sgshapirostatic int	workcmpf1();
13264562Sgshapirostatic int	workcmpf2();
13364562Sgshapirostatic int	workcmpf3();
13464562Sgshapirostatic int	workcmpf4();
135110560Sgshapirostatic int	randi = 3;	/* index for workcmpf5() */
13690792Sgshapirostatic int	workcmpf5();
13790792Sgshapirostatic int	workcmpf6();
13890792Sgshapiro#if _FFR_RHS
13990792Sgshapirostatic int	workcmpf7();
14090792Sgshapiro#endif /* _FFR_RHS */
14138032Speter
14290792Sgshapiro#if RANDOMSHIFT
14390792Sgshapiro# define get_rand_mod(m)	((get_random() >> RANDOMSHIFT) % (m))
14490792Sgshapiro#else /* RANDOMSHIFT */
14590792Sgshapiro# define get_rand_mod(m)	(get_random() % (m))
14690792Sgshapiro#endif /* RANDOMSHIFT */
14790792Sgshapiro
14880785Sgshapiro/*
14990792Sgshapiro**  File system definition.
15090792Sgshapiro**	Used to keep track of how much free space is available
15190792Sgshapiro**	on a file system in which one or more queue directories reside.
15290792Sgshapiro*/
15390792Sgshapiro
15490792Sgshapirotypedef struct filesys_shared	FILESYS;
15590792Sgshapiro
15690792Sgshapirostruct filesys_shared
15790792Sgshapiro{
15890792Sgshapiro	dev_t	fs_dev;		/* unique device id */
15990792Sgshapiro	long	fs_avail;	/* number of free blocks available */
16090792Sgshapiro	long	fs_blksize;	/* block size, in bytes */
16190792Sgshapiro};
16290792Sgshapiro
16390792Sgshapiro/* probably kept in shared memory */
16490792Sgshapirostatic FILESYS	FileSys[MAXFILESYS];	/* queue file systems */
16590792Sgshapirostatic char	*FSPath[MAXFILESYS];	/* pathnames for file systems */
16690792Sgshapiro
16790792Sgshapiro#if SM_CONF_SHM
16890792Sgshapiro
16990792Sgshapiro/*
17090792Sgshapiro**  Shared memory data
17190792Sgshapiro**
17290792Sgshapiro**  Current layout:
17390792Sgshapiro**	size -- size of shared memory segment
17490792Sgshapiro**	pid -- pid of owner, should be a unique id to avoid misinterpretations
17590792Sgshapiro**		by other processes.
17690792Sgshapiro**	tag -- should be a unique id to avoid misinterpretations by others.
17790792Sgshapiro**		idea: hash over configuration data that will be stored here.
17890792Sgshapiro**	NumFileSys -- number of file systems.
17990792Sgshapiro**	FileSys -- (arrary of) structure for used file systems.
18090792Sgshapiro**	RSATmpCnt -- counter for number of uses of ephemeral RSA key.
18190792Sgshapiro**	QShm -- (array of) structure for information about queue directories.
18290792Sgshapiro*/
18390792Sgshapiro
18490792Sgshapiro/*
18590792Sgshapiro**  Queue data in shared memory
18690792Sgshapiro*/
18790792Sgshapiro
18890792Sgshapirotypedef struct queue_shared	QUEUE_SHM_T;
18990792Sgshapiro
19090792Sgshapirostruct queue_shared
19190792Sgshapiro{
19290792Sgshapiro	int	qs_entries;	/* number of entries */
19390792Sgshapiro	/* XXX more to follow? */
19490792Sgshapiro};
19590792Sgshapiro
19690792Sgshapirostatic void	*Pshm;		/* pointer to shared memory */
19790792Sgshapirostatic FILESYS	*PtrFileSys;	/* pointer to queue file system array */
19890792Sgshapiroint		ShmId = SM_SHM_NO_ID;	/* shared memory id */
19990792Sgshapirostatic QUEUE_SHM_T	*QShm;		/* pointer to shared queue data */
200110560Sgshapirostatic size_t shms;
20190792Sgshapiro
20290792Sgshapiro# define SHM_OFF_PID(p)	(((char *) (p)) + sizeof(int))
20390792Sgshapiro# define SHM_OFF_TAG(p)	(((char *) (p)) + sizeof(pid_t) + sizeof(int))
20490792Sgshapiro# define SHM_OFF_HEAD	(sizeof(pid_t) + sizeof(int) * 2)
20590792Sgshapiro
20690792Sgshapiro/* how to access FileSys */
20790792Sgshapiro# define FILE_SYS(i)	(PtrFileSys[i])
20890792Sgshapiro
20990792Sgshapiro/* first entry is a tag, for now just the size */
21090792Sgshapiro# define OFF_FILE_SYS(p)	(((char *) (p)) + SHM_OFF_HEAD)
21190792Sgshapiro
21290792Sgshapiro/* offset for PNumFileSys */
21390792Sgshapiro# define OFF_NUM_FILE_SYS(p)	(((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys))
21490792Sgshapiro
21590792Sgshapiro/* offset for PRSATmpCnt */
21690792Sgshapiro# define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int))
21790792Sgshapiroint	*PRSATmpCnt;
21890792Sgshapiro
21990792Sgshapiro/* offset for queue_shm */
22090792Sgshapiro# define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
22190792Sgshapiro
22290792Sgshapiro#define QSHM_ENTRIES(i)	QShm[i].qs_entries
22390792Sgshapiro
22490792Sgshapiro/* basic size of shared memory segment */
22590792Sgshapiro# define SM_T_SIZE	(SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
22690792Sgshapiro
22790792Sgshapirostatic unsigned int	hash_q __P((char *, unsigned int));
22890792Sgshapiro
22990792Sgshapiro/*
23090792Sgshapiro**  HASH_Q -- simple hash function
23190792Sgshapiro**
23290792Sgshapiro**	Parameters:
23390792Sgshapiro**		p -- string to hash.
23490792Sgshapiro**		h -- hash start value (from previous run).
23590792Sgshapiro**
23690792Sgshapiro**	Returns:
23790792Sgshapiro**		hash value.
23890792Sgshapiro*/
23990792Sgshapiro
24090792Sgshapirostatic unsigned int
24190792Sgshapirohash_q(p, h)
24290792Sgshapiro	char *p;
24390792Sgshapiro	unsigned int h;
24490792Sgshapiro{
24590792Sgshapiro	int c, d;
24690792Sgshapiro
24790792Sgshapiro	while (*p != '\0')
24890792Sgshapiro	{
24990792Sgshapiro		d = *p++;
25090792Sgshapiro		c = d;
25190792Sgshapiro		c ^= c<<6;
25290792Sgshapiro		h += (c<<11) ^ (c>>1);
25390792Sgshapiro		h ^= (d<<14) + (d<<7) + (d<<4) + d;
25490792Sgshapiro	}
25590792Sgshapiro	return h;
25690792Sgshapiro}
25790792Sgshapiro
258110560Sgshapiro
25990792Sgshapiro#else /* SM_CONF_SHM */
26090792Sgshapiro# define FILE_SYS(i)	FileSys[i]
26190792Sgshapiro#endif /* SM_CONF_SHM */
26290792Sgshapiro
26390792Sgshapiro/* access to the various components of file system data */
26490792Sgshapiro#define FILE_SYS_NAME(i)	FSPath[i]
26590792Sgshapiro#define FILE_SYS_AVAIL(i)	FILE_SYS(i).fs_avail
26690792Sgshapiro#define FILE_SYS_BLKSIZE(i)	FILE_SYS(i).fs_blksize
26790792Sgshapiro#define FILE_SYS_DEV(i)	FILE_SYS(i).fs_dev
26890792Sgshapiro
269110560Sgshapiro
27090792Sgshapiro/*
27180785Sgshapiro**  Current qf file field assignments:
27280785Sgshapiro**
27380785Sgshapiro**	A	AUTH= parameter
27480785Sgshapiro**	B	body type
27580785Sgshapiro**	C	controlling user
27680785Sgshapiro**	D	data file name
27790792Sgshapiro**	d	data file directory name (added in 8.12)
27880785Sgshapiro**	E	error recipient
27980785Sgshapiro**	F	flag bits
28090792Sgshapiro**	G	queue delay algorithm (_FFR_QUEUEDELAY)
28180785Sgshapiro**	H	header
28280785Sgshapiro**	I	data file's inode number
28380785Sgshapiro**	K	time of last delivery attempt
28480785Sgshapiro**	L	Solaris Content-Length: header (obsolete)
28598841Sgshapiro**	M	message
28680785Sgshapiro**	N	number of delivery attempts
28780785Sgshapiro**	P	message priority
28890792Sgshapiro**	q	quarantine reason (_FFR_QUARANTINE)
28980785Sgshapiro**	Q	original recipient (ORCPT=)
29090792Sgshapiro**	r	final recipient (Final-Recipient: DSN field)
29180785Sgshapiro**	R	recipient
29280785Sgshapiro**	S	sender
29380785Sgshapiro**	T	init time
29480785Sgshapiro**	V	queue file version
29590792Sgshapiro**	X	free (was: character set if _FFR_SAVE_CHARSET)
29690792Sgshapiro**	Y	current delay (_FFR_QUEUEDELAY)
29780785Sgshapiro**	Z	original envelope id from ESMTP
29890792Sgshapiro**	!	deliver by (added in 8.12)
29980785Sgshapiro**	$	define macro
30080785Sgshapiro**	.	terminate file
30180785Sgshapiro*/
30280785Sgshapiro
30390792Sgshapiro/*
30438032Speter**  QUEUEUP -- queue a message up for future transmission.
30538032Speter**
30638032Speter**	Parameters:
30738032Speter**		e -- the envelope to queue up.
30890792Sgshapiro**		announce -- if true, tell when you are queueing up.
30990792Sgshapiro**		msync -- if true, then fsync() if SuperSafe interactive mode.
31038032Speter**
31138032Speter**	Returns:
31238032Speter**		none.
31338032Speter**
31438032Speter**	Side Effects:
31590792Sgshapiro**		The current request is saved in a control file.
31638032Speter**		The queue file is left locked.
31738032Speter*/
31838032Speter
31938032Spetervoid
32090792Sgshapiroqueueup(e, announce, msync)
32138032Speter	register ENVELOPE *e;
32238032Speter	bool announce;
32390792Sgshapiro	bool msync;
32438032Speter{
32590792Sgshapiro	register SM_FILE_T *tfp;
32638032Speter	register HDR *h;
32738032Speter	register ADDRESS *q;
32864562Sgshapiro	int tfd = -1;
32938032Speter	int i;
33038032Speter	bool newid;
33138032Speter	register char *p;
33238032Speter	MAILER nullmailer;
33338032Speter	MCI mcibuf;
33490792Sgshapiro	char qf[MAXPATHLEN];
33564562Sgshapiro	char tf[MAXPATHLEN];
33690792Sgshapiro	char df[MAXPATHLEN];
33738032Speter	char buf[MAXLINE];
33838032Speter
33938032Speter	/*
34038032Speter	**  Create control file.
34138032Speter	*/
34238032Speter
34338032Speter	newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
34490792Sgshapiro	(void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof tf);
34538032Speter	tfp = e->e_lockfp;
34638032Speter	if (tfp == NULL)
34790792Sgshapiro		newid = false;
34838032Speter
34990792Sgshapiro	/* if newid, write the queue file directly (instead of temp file) */
35038032Speter	if (!newid)
35138032Speter	{
35290792Sgshapiro		const int flags = O_CREAT|O_WRONLY|O_EXCL;
35364562Sgshapiro
35438032Speter		/* get a locked tf file */
35538032Speter		for (i = 0; i < 128; i++)
35638032Speter		{
35764562Sgshapiro			if (tfd < 0)
35838032Speter			{
35990792Sgshapiro				MODE_T oldumask = 0;
36064562Sgshapiro
36164562Sgshapiro				if (bitset(S_IWGRP, QueueFileMode))
36264562Sgshapiro					oldumask = umask(002);
36364562Sgshapiro				tfd = open(tf, flags, QueueFileMode);
36464562Sgshapiro				if (bitset(S_IWGRP, QueueFileMode))
36564562Sgshapiro					(void) umask(oldumask);
36664562Sgshapiro
36764562Sgshapiro				if (tfd < 0)
36864562Sgshapiro				{
36964562Sgshapiro					if (errno != EEXIST)
37064562Sgshapiro						break;
37164562Sgshapiro					if (LogLevel > 0 && (i % 32) == 0)
37264562Sgshapiro						sm_syslog(LOG_ALERT, e->e_id,
37364562Sgshapiro							  "queueup: cannot create %s, uid=%d: %s",
37498121Sgshapiro							  tf, (int) geteuid(),
37590792Sgshapiro							  sm_errstring(errno));
37664562Sgshapiro				}
37738032Speter			}
37864562Sgshapiro			if (tfd >= 0)
37938032Speter			{
38064562Sgshapiro				if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
38138032Speter					break;
38238032Speter				else if (LogLevel > 0 && (i % 32) == 0)
38338032Speter					sm_syslog(LOG_ALERT, e->e_id,
38464562Sgshapiro						  "queueup: cannot lock %s: %s",
38590792Sgshapiro						  tf, sm_errstring(errno));
38664562Sgshapiro				if ((i % 32) == 31)
38764562Sgshapiro				{
38864562Sgshapiro					(void) close(tfd);
38964562Sgshapiro					tfd = -1;
39064562Sgshapiro				}
39138032Speter			}
39238032Speter
39338032Speter			if ((i % 32) == 31)
39438032Speter			{
39538032Speter				/* save the old temp file away */
39664562Sgshapiro				(void) rename(tf, queuename(e, TEMPQF_LETTER));
39738032Speter			}
39838032Speter			else
39964562Sgshapiro				(void) sleep(i % 32);
40038032Speter		}
40190792Sgshapiro		if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
40290792Sgshapiro						 (void *) &tfd, SM_IO_WRONLY,
40390792Sgshapiro						 NULL)) == NULL)
40438032Speter		{
40564562Sgshapiro			int save_errno = errno;
40664562Sgshapiro
40790792Sgshapiro			printopenfds(true);
40864562Sgshapiro			errno = save_errno;
40938032Speter			syserr("!queueup: cannot create queue temp file %s, uid=%d",
41098121Sgshapiro				tf, (int) geteuid());
41138032Speter		}
41238032Speter	}
41338032Speter
41438032Speter	if (tTd(40, 1))
41590792Sgshapiro		sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n",
41690792Sgshapiro			   qid_printqueue(e->e_qgrp, e->e_qdir),
41790792Sgshapiro			   queuename(e, ANYQFL_LETTER),
41890792Sgshapiro			   newid ? " (new id)" : "");
41938032Speter	if (tTd(40, 3))
42038032Speter	{
42190792Sgshapiro		sm_dprintf("  e_flags=");
42238032Speter		printenvflags(e);
42338032Speter	}
42438032Speter	if (tTd(40, 32))
42538032Speter	{
42690792Sgshapiro		sm_dprintf("  sendq=");
42790792Sgshapiro		printaddr(e->e_sendqueue, true);
42838032Speter	}
42938032Speter	if (tTd(40, 9))
43038032Speter	{
43190792Sgshapiro		sm_dprintf("  tfp=");
43290792Sgshapiro		dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false);
43390792Sgshapiro		sm_dprintf("  lockfp=");
43438032Speter		if (e->e_lockfp == NULL)
43590792Sgshapiro			sm_dprintf("NULL\n");
43638032Speter		else
43790792Sgshapiro			dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL),
43890792Sgshapiro			       true, false);
43938032Speter	}
44038032Speter
44138032Speter	/*
44238032Speter	**  If there is no data file yet, create one.
44338032Speter	*/
44438032Speter
44590792Sgshapiro	(void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof df);
44664562Sgshapiro	if (bitset(EF_HAS_DF, e->e_flags))
44738032Speter	{
44890792Sgshapiro		if (e->e_dfp != NULL &&
44990792Sgshapiro		    SuperSafe != SAFE_REALLY &&
45090792Sgshapiro		    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
45190792Sgshapiro		    errno != EINVAL)
45290792Sgshapiro		{
45364562Sgshapiro			syserr("!queueup: cannot commit data file %s, uid=%d",
45498121Sgshapiro			       queuename(e, DATAFL_LETTER), (int) geteuid());
45590792Sgshapiro		}
45690792Sgshapiro		if (e->e_dfp != NULL &&
45790792Sgshapiro		    SuperSafe == SAFE_INTERACTIVE && msync)
45890792Sgshapiro		{
45990792Sgshapiro			if (tTd(40,32))
46090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
46190792Sgshapiro					  "queueup: fsync(e->e_dfp)");
46290792Sgshapiro
46390792Sgshapiro			if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD,
46490792Sgshapiro						NULL)) < 0)
46590792Sgshapiro			{
46690792Sgshapiro				if (newid)
46790792Sgshapiro					syserr("!552 Error writing data file %s",
46890792Sgshapiro					       df);
46990792Sgshapiro				else
47090792Sgshapiro					syserr("!452 Error writing data file %s",
47190792Sgshapiro					       df);
47290792Sgshapiro			}
47390792Sgshapiro		}
47464562Sgshapiro	}
47564562Sgshapiro	else
47664562Sgshapiro	{
47764562Sgshapiro		int dfd;
47890792Sgshapiro		MODE_T oldumask = 0;
47990792Sgshapiro		register SM_FILE_T *dfp = NULL;
48038032Speter		struct stat stbuf;
48138032Speter
48290792Sgshapiro		if (e->e_dfp != NULL &&
48390792Sgshapiro		    sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
48464562Sgshapiro			syserr("committing over bf file");
48564562Sgshapiro
48690792Sgshapiro		if (bitset(S_IWGRP, QueueFileMode))
48790792Sgshapiro			oldumask = umask(002);
48890792Sgshapiro		dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC, QueueFileMode);
48990792Sgshapiro		if (bitset(S_IWGRP, QueueFileMode))
49090792Sgshapiro			(void) umask(oldumask);
49190792Sgshapiro		if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
49290792Sgshapiro						 (void *) &dfd, SM_IO_WRONLY,
49390792Sgshapiro						 NULL)) == NULL)
49438032Speter			syserr("!queueup: cannot create data temp file %s, uid=%d",
49598121Sgshapiro				df, (int) geteuid());
49664562Sgshapiro		if (fstat(dfd, &stbuf) < 0)
49738032Speter			e->e_dfino = -1;
49838032Speter		else
49938032Speter		{
50038032Speter			e->e_dfdev = stbuf.st_dev;
50190792Sgshapiro			e->e_dfino = ST_INODE(stbuf);
50238032Speter		}
50338032Speter		e->e_flags |= EF_HAS_DF;
50464562Sgshapiro		memset(&mcibuf, '\0', sizeof mcibuf);
50538032Speter		mcibuf.mci_out = dfp;
50638032Speter		mcibuf.mci_mailer = FileMailer;
50738032Speter		(*e->e_putbody)(&mcibuf, e, NULL);
50890792Sgshapiro
50990792Sgshapiro		if (SuperSafe == SAFE_REALLY ||
51090792Sgshapiro		    (SuperSafe == SAFE_INTERACTIVE && msync))
51190792Sgshapiro		{
51290792Sgshapiro			if (tTd(40,32))
51390792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
51490792Sgshapiro					  "queueup: fsync(dfp)");
51590792Sgshapiro
51690792Sgshapiro			if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0)
51790792Sgshapiro			{
51890792Sgshapiro				if (newid)
51990792Sgshapiro					syserr("!552 Error writing data file %s",
52090792Sgshapiro					       df);
52190792Sgshapiro				else
52290792Sgshapiro					syserr("!452 Error writing data file %s",
52390792Sgshapiro					       df);
52490792Sgshapiro			}
52590792Sgshapiro		}
52690792Sgshapiro
52790792Sgshapiro		if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0)
52864562Sgshapiro			syserr("!queueup: cannot save data temp file %s, uid=%d",
52998121Sgshapiro				df, (int) geteuid());
53038032Speter		e->e_putbody = putbody;
53138032Speter	}
53238032Speter
53338032Speter	/*
53438032Speter	**  Output future work requests.
53538032Speter	**	Priority and creation time should be first, since
53690792Sgshapiro	**	they are required by gatherq.
53738032Speter	*/
53838032Speter
53938032Speter	/* output queue version number (must be first!) */
54090792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION);
54138032Speter
54238032Speter	/* output creation time */
54390792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime);
54438032Speter
54538032Speter	/* output last delivery time */
54690792Sgshapiro#if _FFR_QUEUEDELAY
54790792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
54890792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "G%d\n", e->e_queuealg);
54990792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Y%ld\n", (long) e->e_queuedelay);
55064562Sgshapiro	if (tTd(40, 64))
55164562Sgshapiro		sm_syslog(LOG_INFO, e->e_id,
55264562Sgshapiro			"queue alg: %d delay %ld next: %ld (now: %ld)\n",
55364562Sgshapiro			e->e_queuealg, e->e_queuedelay, e->e_dtime, curtime());
55490792Sgshapiro#else /* _FFR_QUEUEDELAY */
55590792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
55690792Sgshapiro#endif /* _FFR_QUEUEDELAY */
55738032Speter
55838032Speter	/* output number of delivery attempts */
55990792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries);
56038032Speter
56138032Speter	/* output message priority */
56290792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority);
56338032Speter
56490792Sgshapiro	/*
56590792Sgshapiro	**  If data file is in a different directory than the queue file,
56690792Sgshapiro	**  output a "d" record naming the directory of the data file.
56790792Sgshapiro	*/
56890792Sgshapiro
56990792Sgshapiro	if (e->e_dfqgrp != e->e_qgrp)
57090792Sgshapiro	{
57190792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n",
57290792Sgshapiro			Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name);
57390792Sgshapiro	}
57490792Sgshapiro
57538032Speter	/* output inode number of data file */
57638032Speter	/* XXX should probably include device major/minor too */
57738032Speter	if (e->e_dfino != -1)
57838032Speter	{
57990792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n",
58090792Sgshapiro				     (long) major(e->e_dfdev),
58190792Sgshapiro				     (long) minor(e->e_dfdev),
58290792Sgshapiro				     (ULONGLONG_T) e->e_dfino);
58338032Speter	}
58438032Speter
58538032Speter	/* output body type */
58638032Speter	if (e->e_bodytype != NULL)
58790792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n",
58890792Sgshapiro				     denlstring(e->e_bodytype, true, false));
58938032Speter
59090792Sgshapiro#if _FFR_QUARANTINE
59190792Sgshapiro	/* quarantine reason */
59290792Sgshapiro	if (e->e_quarmsg != NULL)
59390792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n",
59490792Sgshapiro				     denlstring(e->e_quarmsg, true, false));
59590792Sgshapiro#endif /* _FFR_QUARANTINE */
59638032Speter
59738032Speter	/* message from envelope, if it exists */
59838032Speter	if (e->e_message != NULL)
59990792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
60090792Sgshapiro				     denlstring(e->e_message, true, false));
60138032Speter
60238032Speter	/* send various flag bits through */
60338032Speter	p = buf;
60438032Speter	if (bitset(EF_WARNING, e->e_flags))
60538032Speter		*p++ = 'w';
60638032Speter	if (bitset(EF_RESPONSE, e->e_flags))
60738032Speter		*p++ = 'r';
60838032Speter	if (bitset(EF_HAS8BIT, e->e_flags))
60938032Speter		*p++ = '8';
61038032Speter	if (bitset(EF_DELETE_BCC, e->e_flags))
61138032Speter		*p++ = 'b';
61238032Speter	if (bitset(EF_RET_PARAM, e->e_flags))
61338032Speter		*p++ = 'd';
61438032Speter	if (bitset(EF_NO_BODY_RETN, e->e_flags))
61538032Speter		*p++ = 'n';
61690792Sgshapiro	if (bitset(EF_SPLIT, e->e_flags))
61790792Sgshapiro		*p++ = 's';
61838032Speter	*p++ = '\0';
61938032Speter	if (buf[0] != '\0')
62090792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf);
62138032Speter
62264562Sgshapiro	/* save $={persistentMacros} macro values */
62390792Sgshapiro	queueup_macros(macid("{persistentMacros}"), tfp, e);
62438032Speter
62538032Speter	/* output name of sender */
62638032Speter	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
62738032Speter		p = e->e_sender;
62838032Speter	else
62938032Speter		p = e->e_from.q_paddr;
63090792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n",
63190792Sgshapiro			     denlstring(p, true, false));
63238032Speter
63338032Speter	/* output ESMTP-supplied "original" information */
63438032Speter	if (e->e_envid != NULL)
63590792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n",
63690792Sgshapiro				     denlstring(e->e_envid, true, false));
63738032Speter
63864562Sgshapiro	/* output AUTH= parameter */
63964562Sgshapiro	if (e->e_auth_param != NULL)
64090792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n",
64190792Sgshapiro				     denlstring(e->e_auth_param, true, false));
64290792Sgshapiro	if (e->e_dlvr_flag != 0)
64390792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n",
64490792Sgshapiro				     (char) e->e_dlvr_flag, e->e_deliver_by);
64564562Sgshapiro
64638032Speter	/* output list of recipient addresses */
64738032Speter	printctladdr(NULL, NULL);
64838032Speter	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
64938032Speter	{
65064562Sgshapiro		if (!QS_IS_UNDELIVERED(q->q_state))
65138032Speter			continue;
65264562Sgshapiro
65390792Sgshapiro		/* message for this recipient, if it exists */
65490792Sgshapiro		if (q->q_message != NULL)
65590792Sgshapiro			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
65690792Sgshapiro					     denlstring(q->q_message, true,
65790792Sgshapiro							false));
65890792Sgshapiro
65938032Speter		printctladdr(q, tfp);
66038032Speter		if (q->q_orcpt != NULL)
66190792Sgshapiro			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n",
66290792Sgshapiro					     denlstring(q->q_orcpt, true,
66390792Sgshapiro							false));
66490792Sgshapiro		if (q->q_finalrcpt != NULL)
66590792Sgshapiro			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n",
66690792Sgshapiro					     denlstring(q->q_finalrcpt, true,
66790792Sgshapiro							false));
66890792Sgshapiro		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R');
66938032Speter		if (bitset(QPRIMARY, q->q_flags))
67090792Sgshapiro			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P');
67138032Speter		if (bitset(QHASNOTIFY, q->q_flags))
67290792Sgshapiro			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N');
67338032Speter		if (bitset(QPINGONSUCCESS, q->q_flags))
67490792Sgshapiro			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S');
67538032Speter		if (bitset(QPINGONFAILURE, q->q_flags))
67690792Sgshapiro			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F');
67738032Speter		if (bitset(QPINGONDELAY, q->q_flags))
67890792Sgshapiro			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D');
67971345Sgshapiro		if (q->q_alias != NULL &&
68071345Sgshapiro		    bitset(QALIAS, q->q_alias->q_flags))
68190792Sgshapiro			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A');
68290792Sgshapiro		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':');
68390792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n",
68490792Sgshapiro				     denlstring(q->q_paddr, true, false));
68538032Speter		if (announce)
68638032Speter		{
68790792Sgshapiro			char *tag = "queued";
68890792Sgshapiro
68990792Sgshapiro#if _FFR_QUARANTINE
69090792Sgshapiro			if (e->e_quarmsg != NULL)
69190792Sgshapiro				tag = "quarantined";
69290792Sgshapiro#endif /* _FFR_QUARANTINE */
69390792Sgshapiro
69438032Speter			e->e_to = q->q_paddr;
69590792Sgshapiro			message(tag);
69638032Speter			if (LogLevel > 8)
69764562Sgshapiro				logdelivery(q->q_mailer, NULL, q->q_status,
69890792Sgshapiro					    tag, NULL, (time_t) 0, e);
69938032Speter			e->e_to = NULL;
70038032Speter		}
70138032Speter		if (tTd(40, 1))
70238032Speter		{
70390792Sgshapiro			sm_dprintf("queueing ");
70490792Sgshapiro			printaddr(q, false);
70538032Speter		}
70638032Speter	}
70738032Speter
70838032Speter	/*
70938032Speter	**  Output headers for this message.
71038032Speter	**	Expand macros completely here.  Queue run will deal with
71138032Speter	**	everything as absolute headers.
71238032Speter	**		All headers that must be relative to the recipient
71338032Speter	**		can be cracked later.
71438032Speter	**	We set up a "null mailer" -- i.e., a mailer that will have
71538032Speter	**	no effect on the addresses as they are output.
71638032Speter	*/
71738032Speter
71864562Sgshapiro	memset((char *) &nullmailer, '\0', sizeof nullmailer);
71938032Speter	nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
72038032Speter			nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
72138032Speter	nullmailer.m_eol = "\n";
72264562Sgshapiro	memset(&mcibuf, '\0', sizeof mcibuf);
72338032Speter	mcibuf.mci_mailer = &nullmailer;
72438032Speter	mcibuf.mci_out = tfp;
72538032Speter
72690792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'g', "\201f");
72738032Speter	for (h = e->e_header; h != NULL; h = h->h_link)
72838032Speter	{
72943730Speter		if (h->h_value == NULL)
73038032Speter			continue;
73138032Speter
73238032Speter		/* don't output resent headers on non-resent messages */
73364562Sgshapiro		if (bitset(H_RESENT, h->h_flags) &&
73464562Sgshapiro		    !bitset(EF_RESENT, e->e_flags))
73538032Speter			continue;
73638032Speter
73738032Speter		/* expand macros; if null, don't output header at all */
73838032Speter		if (bitset(H_DEFAULT, h->h_flags))
73938032Speter		{
74038032Speter			(void) expand(h->h_value, buf, sizeof buf, e);
74138032Speter			if (buf[0] == '\0')
74238032Speter				continue;
74338032Speter		}
74438032Speter
74538032Speter		/* output this header */
74690792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?");
74738032Speter
74864562Sgshapiro		/* output conditional macro if present */
74964562Sgshapiro		if (h->h_macro != '\0')
75038032Speter		{
75164562Sgshapiro			if (bitset(0200, h->h_macro))
75290792Sgshapiro				(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
75390792Sgshapiro						     "${%s}",
75490792Sgshapiro						      macname(bitidx(h->h_macro)));
75564562Sgshapiro			else
75690792Sgshapiro				(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
75790792Sgshapiro						     "$%c", h->h_macro);
75864562Sgshapiro		}
75964562Sgshapiro		else if (!bitzerop(h->h_mflags) &&
76064562Sgshapiro			 bitset(H_CHECK|H_ACHECK, h->h_flags))
76164562Sgshapiro		{
76238032Speter			int j;
76338032Speter
76464562Sgshapiro			/* if conditional, output the set of conditions */
76538032Speter			for (j = '\0'; j <= '\177'; j++)
76638032Speter				if (bitnset(j, h->h_mflags))
76790792Sgshapiro					(void) sm_io_putc(tfp, SM_TIME_DEFAULT,
76890792Sgshapiro							  j);
76938032Speter		}
77090792Sgshapiro		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?');
77138032Speter
77238032Speter		/* output the header: expand macros, convert addresses */
77364562Sgshapiro		if (bitset(H_DEFAULT, h->h_flags) &&
77464562Sgshapiro		    !bitset(H_BINDLATE, h->h_flags))
77538032Speter		{
77690792Sgshapiro			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n",
77790792Sgshapiro					     h->h_field,
77890792Sgshapiro					     denlstring(buf, false, true));
77938032Speter		}
78064562Sgshapiro		else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
78164562Sgshapiro			 !bitset(H_BINDLATE, h->h_flags))
78238032Speter		{
78338032Speter			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
78490792Sgshapiro			SM_FILE_T *savetrace = TrafficLogFile;
78538032Speter
78638032Speter			TrafficLogFile = NULL;
78738032Speter
78838032Speter			if (bitset(H_FROM, h->h_flags))
78990792Sgshapiro				oldstyle = false;
79038032Speter
79138032Speter			commaize(h, h->h_value, oldstyle, &mcibuf, e);
79238032Speter
79338032Speter			TrafficLogFile = savetrace;
79438032Speter		}
79538032Speter		else
79638032Speter		{
79790792Sgshapiro			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n",
79890792Sgshapiro					     h->h_field,
79990792Sgshapiro					     denlstring(h->h_value, false,
80090792Sgshapiro							true));
80138032Speter		}
80238032Speter	}
80338032Speter
80438032Speter	/*
80538032Speter	**  Clean up.
80638032Speter	**
80738032Speter	**	Write a terminator record -- this is to prevent
80838032Speter	**	scurrilous crackers from appending any data.
80938032Speter	*/
81038032Speter
81190792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n");
81238032Speter
81390792Sgshapiro	if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 ||
81490792Sgshapiro	    ((SuperSafe == SAFE_REALLY ||
81590792Sgshapiro	      (SuperSafe == SAFE_INTERACTIVE && msync)) &&
81690792Sgshapiro	     fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) ||
81790792Sgshapiro	    sm_io_error(tfp))
81838032Speter	{
81938032Speter		if (newid)
82038032Speter			syserr("!552 Error writing control file %s", tf);
82138032Speter		else
82238032Speter			syserr("!452 Error writing control file %s", tf);
82338032Speter	}
82438032Speter
82538032Speter	if (!newid)
82638032Speter	{
82790792Sgshapiro#if _FFR_QUARANTINE
82890792Sgshapiro		char new = queue_letter(e, ANYQFL_LETTER);
82990792Sgshapiro#endif /* _FFR_QUARANTINE */
83090792Sgshapiro
83190792Sgshapiro		/* rename (locked) tf to be (locked) [qh]f */
83290792Sgshapiro		(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER),
83390792Sgshapiro				  sizeof qf);
83438032Speter		if (rename(tf, qf) < 0)
83538032Speter			syserr("cannot rename(%s, %s), uid=%d",
83698121Sgshapiro				tf, qf, (int) geteuid());
83790792Sgshapiro# if _FFR_QUARANTINE
83890792Sgshapiro		else
83990792Sgshapiro		{
84090792Sgshapiro			/*
84190792Sgshapiro			**  Check if type has changed and only
84290792Sgshapiro			**  remove the old item if the rename above
84390792Sgshapiro			**  succeeded.
84490792Sgshapiro			*/
84590792Sgshapiro
84690792Sgshapiro			if (e->e_qfletter != '\0' &&
84790792Sgshapiro			    e->e_qfletter != new)
84890792Sgshapiro			{
84990792Sgshapiro				if (tTd(40, 5))
85090792Sgshapiro				{
85190792Sgshapiro					sm_dprintf("type changed from %c to %c\n",
85290792Sgshapiro						   e->e_qfletter, new);
85390792Sgshapiro				}
85490792Sgshapiro
85590792Sgshapiro				if (unlink(queuename(e, e->e_qfletter)) < 0)
85690792Sgshapiro				{
85790792Sgshapiro					/* XXX: something more drastic? */
85890792Sgshapiro					if (LogLevel > 0)
85990792Sgshapiro						sm_syslog(LOG_ERR, e->e_id,
86090792Sgshapiro							  "queueup: unlink(%s) failed: %s",
86190792Sgshapiro							  queuename(e, e->e_qfletter),
86290792Sgshapiro							  sm_errstring(errno));
86390792Sgshapiro				}
86490792Sgshapiro			}
86590792Sgshapiro		}
86690792Sgshapiro		e->e_qfletter = new;
86790792Sgshapiro# endif /* _FFR_QUARANTINE */
86890792Sgshapiro
86964562Sgshapiro		/*
87090792Sgshapiro		**  fsync() after renaming to make sure metadata is
87190792Sgshapiro		**  written to disk on filesystems in which renames are
87290792Sgshapiro		**  not guaranteed.
87364562Sgshapiro		*/
87464562Sgshapiro
87590792Sgshapiro		if (SuperSafe != SAFE_NO)
87690792Sgshapiro		{
87790792Sgshapiro			/* for softupdates */
87890792Sgshapiro			if (tfd >= 0 && fsync(tfd) < 0)
87990792Sgshapiro			{
88090792Sgshapiro				syserr("!queueup: cannot fsync queue temp file %s",
88190792Sgshapiro				       tf);
88290792Sgshapiro			}
88390792Sgshapiro			SYNC_DIR(qf, true);
88490792Sgshapiro		}
88564562Sgshapiro
88690792Sgshapiro		/* close and unlock old (locked) queue file */
88738032Speter		if (e->e_lockfp != NULL)
88890792Sgshapiro			(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
88938032Speter		e->e_lockfp = tfp;
89090792Sgshapiro
89190792Sgshapiro		/* save log info */
89290792Sgshapiro		if (LogLevel > 79)
89390792Sgshapiro			sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf);
89438032Speter	}
89538032Speter	else
89690792Sgshapiro	{
89790792Sgshapiro		/* save log info */
89890792Sgshapiro		if (LogLevel > 79)
89990792Sgshapiro			sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf);
90090792Sgshapiro
90190792Sgshapiro#if _FFR_QUARANTINE
90290792Sgshapiro		e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
90390792Sgshapiro#endif /* _FFR_QUARANTINE */
90490792Sgshapiro	}
90590792Sgshapiro
90638032Speter	errno = 0;
90738032Speter	e->e_flags |= EF_INQUEUE;
90838032Speter
90938032Speter	if (tTd(40, 1))
91090792Sgshapiro		sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
91138032Speter	return;
91238032Speter}
91338032Speter
91490792Sgshapiro/*
91590792Sgshapiro**  PRINTCTLADDR -- print control address to file.
91690792Sgshapiro**
91790792Sgshapiro**	Parameters:
91890792Sgshapiro**		a -- address.
91990792Sgshapiro**		tfp -- file pointer.
92090792Sgshapiro**
92190792Sgshapiro**	Returns:
92290792Sgshapiro**		none.
92390792Sgshapiro**
92490792Sgshapiro**	Side Effects:
92590792Sgshapiro**		The control address (if changed) is printed to the file.
92690792Sgshapiro**		The last control address and uid are saved.
92790792Sgshapiro*/
92890792Sgshapiro
92964562Sgshapirostatic void
93038032Speterprintctladdr(a, tfp)
93138032Speter	register ADDRESS *a;
93290792Sgshapiro	SM_FILE_T *tfp;
93338032Speter{
93464562Sgshapiro	char *user;
93538032Speter	register ADDRESS *q;
93638032Speter	uid_t uid;
93738032Speter	gid_t gid;
93838032Speter	static ADDRESS *lastctladdr = NULL;
93938032Speter	static uid_t lastuid;
94038032Speter
94138032Speter	/* initialization */
94238032Speter	if (a == NULL || a->q_alias == NULL || tfp == NULL)
94338032Speter	{
94438032Speter		if (lastctladdr != NULL && tfp != NULL)
94590792Sgshapiro			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n");
94638032Speter		lastctladdr = NULL;
94738032Speter		lastuid = 0;
94838032Speter		return;
94938032Speter	}
95038032Speter
95138032Speter	/* find the active uid */
95238032Speter	q = getctladdr(a);
95338032Speter	if (q == NULL)
95438032Speter	{
95564562Sgshapiro		user = NULL;
95638032Speter		uid = 0;
95738032Speter		gid = 0;
95838032Speter	}
95938032Speter	else
96038032Speter	{
96164562Sgshapiro		user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
96238032Speter		uid = q->q_uid;
96338032Speter		gid = q->q_gid;
96438032Speter	}
96538032Speter	a = a->q_alias;
96638032Speter
96738032Speter	/* check to see if this is the same as last time */
96838032Speter	if (lastctladdr != NULL && uid == lastuid &&
96938032Speter	    strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
97038032Speter		return;
97138032Speter	lastuid = uid;
97238032Speter	lastctladdr = a;
97338032Speter
97464562Sgshapiro	if (uid == 0 || user == NULL || user[0] == '\0')
97590792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C");
97638032Speter	else
97790792Sgshapiro		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld",
97890792Sgshapiro				     denlstring(user, true, false), (long) uid,
97990792Sgshapiro				     (long) gid);
98090792Sgshapiro	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n",
98190792Sgshapiro			     denlstring(a->q_paddr, true, false));
98238032Speter}
98390792Sgshapiro
98490792Sgshapiro/*
98590792Sgshapiro**  RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process
98690792Sgshapiro**
98790792Sgshapiro**	This propagates the signal to the child processes that are queue
98890792Sgshapiro**	runners. This is for a queue runner "cleanup". After all of the
98990792Sgshapiro**	child queue runner processes are signaled (it should be SIGTERM
99090792Sgshapiro**	being the sig) then the old signal handler (Oldsh) is called
99190792Sgshapiro**	to handle any cleanup set for this process (provided it is not
99290792Sgshapiro**	SIG_DFL or SIG_IGN). The signal may not be handled immediately
99390792Sgshapiro**	if the BlockOldsh flag is set. If the current process doesn't
99490792Sgshapiro**	have a parent then handle the signal immediately, regardless of
99590792Sgshapiro**	BlockOldsh.
99690792Sgshapiro**
99790792Sgshapiro**	Parameters:
99890792Sgshapiro**		sig -- the signal number being sent
99990792Sgshapiro**
100090792Sgshapiro**	Returns:
100190792Sgshapiro**		none.
100290792Sgshapiro**
100390792Sgshapiro**	Side Effects:
100490792Sgshapiro**		Sets the NoMoreRunners boolean to true to stop more runners
100590792Sgshapiro**		from being started in runqueue().
100690792Sgshapiro**
100790792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
100890792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
100990792Sgshapiro**		DOING.
101090792Sgshapiro*/
101190792Sgshapiro
101290792Sgshapirostatic bool		volatile NoMoreRunners = false;
101390792Sgshapirostatic sigfunc_t	Oldsh_term = SIG_DFL;
101490792Sgshapirostatic sigfunc_t	Oldsh_hup = SIG_DFL;
101590792Sgshapirostatic sigfunc_t	volatile Oldsh = SIG_DFL;
101690792Sgshapirostatic bool		BlockOldsh = false;
101790792Sgshapirostatic int		volatile Oldsig = 0;
101890792Sgshapirostatic SIGFUNC_DECL	runners_sigterm __P((int));
101990792Sgshapirostatic SIGFUNC_DECL	runners_sighup __P((int));
102090792Sgshapiro
102190792Sgshapirostatic SIGFUNC_DECL
102290792Sgshapirorunners_sigterm(sig)
102390792Sgshapiro	int sig;
102490792Sgshapiro{
102590792Sgshapiro	int save_errno = errno;
102690792Sgshapiro
102790792Sgshapiro	FIX_SYSV_SIGNAL(sig, runners_sigterm);
102890792Sgshapiro	errno = save_errno;
102990792Sgshapiro	CHECK_CRITICAL(sig);
103090792Sgshapiro	NoMoreRunners = true;
103190792Sgshapiro	Oldsh = Oldsh_term;
103290792Sgshapiro	Oldsig = sig;
103390792Sgshapiro	proc_list_signal(PROC_QUEUE, sig);
103490792Sgshapiro
103590792Sgshapiro	if (!BlockOldsh || getppid() <= 1)
103690792Sgshapiro	{
103790792Sgshapiro		/* Check that a valid 'old signal handler' is callable */
103890792Sgshapiro		if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN &&
103990792Sgshapiro		    Oldsh_term != runners_sigterm)
104090792Sgshapiro			(*Oldsh_term)(sig);
104190792Sgshapiro	}
104290792Sgshapiro	errno = save_errno;
104390792Sgshapiro	return SIGFUNC_RETURN;
104490792Sgshapiro}
104590792Sgshapiro/*
104690792Sgshapiro**  RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process
104790792Sgshapiro**
104890792Sgshapiro**	This propagates the signal to the child processes that are queue
104990792Sgshapiro**	runners. This is for a queue runner "cleanup". After all of the
105090792Sgshapiro**	child queue runner processes are signaled (it should be SIGHUP
105190792Sgshapiro**	being the sig) then the old signal handler (Oldsh) is called to
105290792Sgshapiro**	handle any cleanup set for this process (provided it is not SIG_DFL
105390792Sgshapiro**	or SIG_IGN). The signal may not be handled immediately if the
105490792Sgshapiro**	BlockOldsh flag is set. If the current process doesn't have
105590792Sgshapiro**	a parent then handle the signal immediately, regardless of
105690792Sgshapiro**	BlockOldsh.
105790792Sgshapiro**
105890792Sgshapiro**	Parameters:
105990792Sgshapiro**		sig -- the signal number being sent
106090792Sgshapiro**
106190792Sgshapiro**	Returns:
106290792Sgshapiro**		none.
106390792Sgshapiro**
106490792Sgshapiro**	Side Effects:
106590792Sgshapiro**		Sets the NoMoreRunners boolean to true to stop more runners
106690792Sgshapiro**		from being started in runqueue().
106790792Sgshapiro**
106890792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
106990792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
107090792Sgshapiro**		DOING.
107190792Sgshapiro*/
107290792Sgshapiro
107390792Sgshapirostatic SIGFUNC_DECL
107490792Sgshapirorunners_sighup(sig)
107590792Sgshapiro	int sig;
107690792Sgshapiro{
107790792Sgshapiro	int save_errno = errno;
107890792Sgshapiro
107990792Sgshapiro	FIX_SYSV_SIGNAL(sig, runners_sighup);
108090792Sgshapiro	errno = save_errno;
108190792Sgshapiro	CHECK_CRITICAL(sig);
108290792Sgshapiro	NoMoreRunners = true;
108390792Sgshapiro	Oldsh = Oldsh_hup;
108490792Sgshapiro	Oldsig = sig;
108590792Sgshapiro	proc_list_signal(PROC_QUEUE, sig);
108690792Sgshapiro
108790792Sgshapiro	if (!BlockOldsh || getppid() <= 1)
108890792Sgshapiro	{
108990792Sgshapiro		/* Check that a valid 'old signal handler' is callable */
109090792Sgshapiro		if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN &&
109190792Sgshapiro		    Oldsh_hup != runners_sighup)
109290792Sgshapiro			(*Oldsh_hup)(sig);
109390792Sgshapiro	}
109490792Sgshapiro	errno = save_errno;
109590792Sgshapiro	return SIGFUNC_RETURN;
109690792Sgshapiro}
109790792Sgshapiro/*
109890792Sgshapiro**  MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart
109990792Sgshapiro**
110090792Sgshapiro**  Sets a workgroup for restarting.
110190792Sgshapiro**
110290792Sgshapiro**	Parameters:
110390792Sgshapiro**		wgrp -- the work group id to restart.
110490792Sgshapiro**		reason -- why (signal?), -1 to turn off restart
110590792Sgshapiro**
110690792Sgshapiro**	Returns:
110790792Sgshapiro**		none.
110890792Sgshapiro**
110990792Sgshapiro**	Side effects:
111090792Sgshapiro**		May set global RestartWorkGroup to true.
111190792Sgshapiro**
111290792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
111390792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
111490792Sgshapiro**		DOING.
111590792Sgshapiro*/
111690792Sgshapiro
111790792Sgshapirovoid
111890792Sgshapiromark_work_group_restart(wgrp, reason)
111990792Sgshapiro	int wgrp;
112090792Sgshapiro	int reason;
112190792Sgshapiro{
112290792Sgshapiro	if (wgrp < 0 || wgrp > NumWorkGroups)
112390792Sgshapiro		return;
112490792Sgshapiro
112590792Sgshapiro	WorkGrp[wgrp].wg_restart = reason;
112690792Sgshapiro	if (reason >= 0)
112790792Sgshapiro		RestartWorkGroup = true;
112890792Sgshapiro}
112990792Sgshapiro/*
113090792Sgshapiro**  RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart
113190792Sgshapiro**
113290792Sgshapiro**  Restart any workgroup marked as needing a restart provided more
113390792Sgshapiro**  runners are allowed.
113490792Sgshapiro**
113590792Sgshapiro**	Parameters:
113690792Sgshapiro**		none.
113790792Sgshapiro**
113890792Sgshapiro**	Returns:
113990792Sgshapiro**		none.
114090792Sgshapiro**
114190792Sgshapiro**	Side effects:
114290792Sgshapiro**		Sets global RestartWorkGroup to false.
114390792Sgshapiro*/
114490792Sgshapiro
114590792Sgshapirovoid
114690792Sgshapirorestart_marked_work_groups()
114790792Sgshapiro{
114890792Sgshapiro	int i;
114990792Sgshapiro	int wasblocked;
115090792Sgshapiro
115190792Sgshapiro	if (NoMoreRunners)
115290792Sgshapiro		return;
115390792Sgshapiro
115490792Sgshapiro	/* Block SIGCHLD so reapchild() doesn't mess with us */
115590792Sgshapiro	wasblocked = sm_blocksignal(SIGCHLD);
115690792Sgshapiro
115790792Sgshapiro	for (i = 0; i < NumWorkGroups; i++)
115890792Sgshapiro	{
115990792Sgshapiro		if (WorkGrp[i].wg_restart >= 0)
116090792Sgshapiro		{
116190792Sgshapiro			if (LogLevel > 8)
116290792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
116390792Sgshapiro					  "restart queue runner=%d due to signal 0x%x",
116490792Sgshapiro					  i, WorkGrp[i].wg_restart);
116590792Sgshapiro			restart_work_group(i);
116690792Sgshapiro		}
116790792Sgshapiro	}
116890792Sgshapiro	RestartWorkGroup = false;
116990792Sgshapiro
117090792Sgshapiro	if (wasblocked == 0)
117190792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
117290792Sgshapiro}
117390792Sgshapiro/*
117490792Sgshapiro**  RESTART_WORK_GROUP -- restart a specific work group
117590792Sgshapiro**
117690792Sgshapiro**  Restart a specific workgroup provided more runners are allowed.
117790792Sgshapiro**  If the requested work group has been restarted too many times log
117890792Sgshapiro**  this and refuse to restart.
117990792Sgshapiro**
118090792Sgshapiro**	Parameters:
118190792Sgshapiro**		wgrp -- the work group id to restart
118290792Sgshapiro**
118390792Sgshapiro**	Returns:
118490792Sgshapiro**		none.
118590792Sgshapiro**
118690792Sgshapiro**	Side Effects:
118790792Sgshapiro**		starts another process doing the work of wgrp
118890792Sgshapiro*/
118990792Sgshapiro
119090792Sgshapiro#define MAX_PERSIST_RESTART	10	/* max allowed number of restarts */
119190792Sgshapiro
119290792Sgshapirostatic void
119390792Sgshapirorestart_work_group(wgrp)
119490792Sgshapiro	int wgrp;
119590792Sgshapiro{
119690792Sgshapiro	if (NoMoreRunners ||
119790792Sgshapiro	    wgrp < 0 || wgrp > NumWorkGroups)
119890792Sgshapiro		return;
119990792Sgshapiro
120090792Sgshapiro	WorkGrp[wgrp].wg_restart = -1;
120190792Sgshapiro	if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART)
120290792Sgshapiro	{
120390792Sgshapiro		/* avoid overflow; increment here */
120490792Sgshapiro		WorkGrp[wgrp].wg_restartcnt++;
1205110560Sgshapiro		(void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL);
120690792Sgshapiro	}
120790792Sgshapiro	else
120890792Sgshapiro	{
120990792Sgshapiro		sm_syslog(LOG_ERR, NOQID,
121090792Sgshapiro			  "ERROR: persistent queue runner=%d restarted too many times, queue runner lost",
121190792Sgshapiro			  wgrp);
121290792Sgshapiro	}
121390792Sgshapiro}
121490792Sgshapiro/*
121590792Sgshapiro**  SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group.
121690792Sgshapiro**
121790792Sgshapiro**	Parameters:
121890792Sgshapiro**		runall -- schedule even if individual bit is not set.
121990792Sgshapiro**		wgrp -- the work group id to schedule.
122094334Sgshapiro**		didit -- the queue run was performed for this work group.
122190792Sgshapiro**
122290792Sgshapiro**	Returns:
122390792Sgshapiro**		nothing
122490792Sgshapiro*/
122590792Sgshapiro
122690792Sgshapiro#define INCR_MOD(v, m)	if (++v >= m)	\
122790792Sgshapiro				v = 0;	\
122890792Sgshapiro			else
122990792Sgshapiro
123090792Sgshapirostatic void
123194334Sgshapiroschedule_queue_runs(runall, wgrp, didit)
123290792Sgshapiro	bool runall;
123390792Sgshapiro	int wgrp;
123494334Sgshapiro	bool didit;
123590792Sgshapiro{
123690792Sgshapiro	int qgrp, cgrp, endgrp;
123794334Sgshapiro#if _FFR_QUEUE_SCHED_DBG
123894334Sgshapiro	time_t lastsched;
123994334Sgshapiro	bool sched;
124094334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
124194334Sgshapiro	time_t now;
124294334Sgshapiro	time_t minqintvl;
124390792Sgshapiro
124490792Sgshapiro	/*
124590792Sgshapiro	**  This is a bit ugly since we have to duplicate the
124690792Sgshapiro	**  code that "walks" through a work queue group.
124790792Sgshapiro	*/
124890792Sgshapiro
124994334Sgshapiro	now = curtime();
125094334Sgshapiro	minqintvl = 0;
125190792Sgshapiro	cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp;
125290792Sgshapiro	do
125390792Sgshapiro	{
125490792Sgshapiro		time_t qintvl;
125590792Sgshapiro
125694334Sgshapiro#if _FFR_QUEUE_SCHED_DBG
125794334Sgshapiro		lastsched = 0;
125894334Sgshapiro		sched = false;
125994334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
126090792Sgshapiro		qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index;
126190792Sgshapiro		if (Queue[qgrp]->qg_queueintvl > 0)
126290792Sgshapiro			qintvl = Queue[qgrp]->qg_queueintvl;
126390792Sgshapiro		else if (QueueIntvl > 0)
126490792Sgshapiro			qintvl = QueueIntvl;
126590792Sgshapiro		else
126690792Sgshapiro			qintvl = (time_t) 0;
126790792Sgshapiro#if _FFR_QUEUE_SCHED_DBG
126894334Sgshapiro		lastsched = Queue[qgrp]->qg_nextrun;
126994334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
127094334Sgshapiro		if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0)
127194334Sgshapiro		{
127294334Sgshapiro#if _FFR_QUEUE_SCHED_DBG
127394334Sgshapiro			sched = true;
127494334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
127594334Sgshapiro			if (minqintvl == 0 || qintvl < minqintvl)
127694334Sgshapiro				minqintvl = qintvl;
127794334Sgshapiro
127894334Sgshapiro			/*
127994334Sgshapiro			**  Only set a new time if a queue run was performed
128094334Sgshapiro			**  for this queue group.  If the queue was not run,
128194334Sgshapiro			**  we could starve it by setting a new time on each
128294334Sgshapiro			**  call.
128394334Sgshapiro			*/
128494334Sgshapiro
128594334Sgshapiro			if (didit)
128694334Sgshapiro				Queue[qgrp]->qg_nextrun += qintvl;
128794334Sgshapiro		}
128894334Sgshapiro#if _FFR_QUEUE_SCHED_DBG
128990792Sgshapiro		if (tTd(69, 10))
129090792Sgshapiro			sm_syslog(LOG_INFO, NOQID,
129194334Sgshapiro				"sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d",
129290792Sgshapiro				wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl,
129394334Sgshapiro				QueueIntvl, runall, lastsched,
129494334Sgshapiro				Queue[qgrp]->qg_nextrun, sched);
129590792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
129690792Sgshapiro		INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp);
129790792Sgshapiro	} while (endgrp != cgrp);
129894334Sgshapiro	if (minqintvl > 0)
129994334Sgshapiro		(void) sm_setevent(minqintvl, runqueueevent, 0);
130090792Sgshapiro}
130194334Sgshapiro
130294334Sgshapiro#if _FFR_QUEUE_RUN_PARANOIA
130390792Sgshapiro/*
130494334Sgshapiro**  CHECKQUEUERUNNER -- check whether a queue group hasn't been run.
130594334Sgshapiro**
130694334Sgshapiro**	Use this if events may get lost and hence queue runners may not
130794334Sgshapiro**	be started and mail will pile up in a queue.
130894334Sgshapiro**
130994334Sgshapiro**	Parameters:
131094334Sgshapiro**		none.
131194334Sgshapiro**
131294334Sgshapiro**	Returns:
131394334Sgshapiro**		true if a queue run is necessary.
131494334Sgshapiro**
131594334Sgshapiro**	Side Effects:
131694334Sgshapiro**		may schedule a queue run.
131794334Sgshapiro*/
131894334Sgshapiro
131994334Sgshapirobool
132094334Sgshapirocheckqueuerunner()
132194334Sgshapiro{
132294334Sgshapiro	int qgrp;
132394334Sgshapiro	time_t now, minqintvl;
132494334Sgshapiro
132594334Sgshapiro	now = curtime();
132694334Sgshapiro	minqintvl = 0;
132794334Sgshapiro	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
132894334Sgshapiro	{
132994334Sgshapiro		time_t qintvl;
133094334Sgshapiro
133194334Sgshapiro		if (Queue[qgrp]->qg_queueintvl > 0)
133294334Sgshapiro			qintvl = Queue[qgrp]->qg_queueintvl;
133394334Sgshapiro		else if (QueueIntvl > 0)
133494334Sgshapiro			qintvl = QueueIntvl;
133594334Sgshapiro		else
133694334Sgshapiro			qintvl = (time_t) 0;
133794334Sgshapiro		if (Queue[qgrp]->qg_nextrun <= now - qintvl)
133894334Sgshapiro		{
133994334Sgshapiro			if (minqintvl == 0 || qintvl < minqintvl)
134094334Sgshapiro				minqintvl = qintvl;
134194334Sgshapiro			if (LogLevel > 1)
134294334Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
134394334Sgshapiro					"checkqueuerunner: queue %d should have been run at %s, queue interval %ld",
134494334Sgshapiro					qgrp,
134594334Sgshapiro					arpadate(ctime(&Queue[qgrp]->qg_nextrun)),
134694334Sgshapiro					qintvl);
134794334Sgshapiro		}
134894334Sgshapiro	}
134994334Sgshapiro	if (minqintvl > 0)
135094334Sgshapiro	{
135194334Sgshapiro		(void) sm_setevent(minqintvl, runqueueevent, 0);
135294334Sgshapiro		return true;
135394334Sgshapiro	}
135494334Sgshapiro	return false;
135594334Sgshapiro}
135694334Sgshapiro#endif /* _FFR_QUEUE_RUN_PARANOIA */
135794334Sgshapiro
135894334Sgshapiro/*
135938032Speter**  RUNQUEUE -- run the jobs in the queue.
136038032Speter**
136138032Speter**	Gets the stuff out of the queue in some presumably logical
136238032Speter**	order and processes them.
136338032Speter**
136438032Speter**	Parameters:
136590792Sgshapiro**		forkflag -- true if the queue scanning should be done in
136638032Speter**			a child process.  We double-fork so it is not our
136738032Speter**			child and we don't have to clean up after it.
136890792Sgshapiro**			false can be ignored if we have multiple queues.
136990792Sgshapiro**		verbose -- if true, print out status information.
137090792Sgshapiro**		persistent -- persistent queue runner?
137190792Sgshapiro**		runall -- run all groups or only a subset (DoQueueRun)?
137238032Speter**
137338032Speter**	Returns:
137490792Sgshapiro**		true if the queue run successfully began.
137538032Speter**
137638032Speter**	Side Effects:
137790792Sgshapiro**		runs things in the mail queue using run_work_group().
137890792Sgshapiro**		maybe schedules next queue run.
137938032Speter*/
138038032Speter
138164562Sgshapirostatic ENVELOPE	QueueEnvelope;		/* the queue run envelope */
138264562Sgshapirostatic time_t	LastQueueTime = 0;	/* last time a queue ID assigned */
138364562Sgshapirostatic pid_t	LastQueuePid = -1;	/* last PID which had a queue ID */
138438032Speter
138564562Sgshapiro/* values for qp_supdirs */
138664562Sgshapiro#define QP_NOSUB	0x0000	/* No subdirectories */
138764562Sgshapiro#define QP_SUBDF	0x0001	/* "df" subdirectory */
138864562Sgshapiro#define QP_SUBQF	0x0002	/* "qf" subdirectory */
138964562Sgshapiro#define QP_SUBXF	0x0004	/* "xf" subdirectory */
139064562Sgshapiro
139138032Speterbool
139290792Sgshapirorunqueue(forkflag, verbose, persistent, runall)
139338032Speter	bool forkflag;
139438032Speter	bool verbose;
139590792Sgshapiro	bool persistent;
139690792Sgshapiro	bool runall;
139738032Speter{
139864562Sgshapiro	int i;
139990792Sgshapiro	bool ret = true;
140064562Sgshapiro	static int curnum = 0;
140190792Sgshapiro	sigfunc_t cursh;
140290792Sgshapiro#if SM_HEAP_CHECK
140390792Sgshapiro	SM_NONVOLATILE int oldgroup = 0;
140464562Sgshapiro
140590792Sgshapiro	if (sm_debug_active(&DebugLeakQ, 1))
140690792Sgshapiro	{
140790792Sgshapiro		oldgroup = sm_heap_group();
140890792Sgshapiro		sm_heap_newgroup();
140990792Sgshapiro		sm_dprintf("runqueue() heap group #%d\n", sm_heap_group());
141090792Sgshapiro	}
141190792Sgshapiro#endif /* SM_HEAP_CHECK */
141271345Sgshapiro
141390792Sgshapiro	/* queue run has been started, don't do any more this time */
141494334Sgshapiro	DoQueueRun = false;
141571345Sgshapiro
141690792Sgshapiro	/* more than one queue or more than one directory per queue */
141790792Sgshapiro	if (!forkflag && !verbose &&
141890792Sgshapiro	    (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 ||
141990792Sgshapiro	     WorkGrp[0].wg_numqgrp > 1))
142090792Sgshapiro		forkflag = true;
142164562Sgshapiro
142290792Sgshapiro	/*
142390792Sgshapiro	**  For controlling queue runners via signals sent to this process.
142490792Sgshapiro	**  Oldsh* will get called too by runners_sig* (if it is not SIG_IGN
142590792Sgshapiro	**  or SIG_DFL) to preserve cleanup behavior. Now that this process
142690792Sgshapiro	**  will have children (and perhaps grandchildren) this handler will
142790792Sgshapiro	**  be left in place. This is because this process, once it has
142890792Sgshapiro	**  finished spinning off queue runners, may go back to doing something
142990792Sgshapiro	**  else (like being a daemon). And we still want on a SIG{TERM,HUP} to
143090792Sgshapiro	**  clean up the child queue runners. Only install 'runners_sig*' once
143190792Sgshapiro	**  else we'll get stuck looping forever.
143290792Sgshapiro	*/
143390792Sgshapiro
143490792Sgshapiro	cursh = sm_signal(SIGTERM, runners_sigterm);
143590792Sgshapiro	if (cursh != runners_sigterm)
143690792Sgshapiro		Oldsh_term = cursh;
143790792Sgshapiro	cursh = sm_signal(SIGHUP, runners_sighup);
143890792Sgshapiro	if (cursh != runners_sighup)
143990792Sgshapiro		Oldsh_hup = cursh;
144090792Sgshapiro
144190792Sgshapiro	for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++)
144264562Sgshapiro	{
1443110560Sgshapiro		int rwgflags = RWG_NONE;
1444110560Sgshapiro
144564562Sgshapiro		/*
144690792Sgshapiro		**  If MaxQueueChildren active then test whether the start
144790792Sgshapiro		**  of the next queue group's additional queue runners (maximum)
144890792Sgshapiro		**  will result in MaxQueueChildren being exceeded.
144990792Sgshapiro		**
145090792Sgshapiro		**  Note: do not use continue; even though another workgroup
145190792Sgshapiro		**	may have fewer queue runners, this would be "unfair",
145290792Sgshapiro		**	i.e., this work group might "starve" then.
145364562Sgshapiro		*/
145464562Sgshapiro
145590792Sgshapiro#if _FFR_QUEUE_SCHED_DBG
145690792Sgshapiro		if (tTd(69, 10))
145790792Sgshapiro			sm_syslog(LOG_INFO, NOQID,
145890792Sgshapiro				"rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d",
145990792Sgshapiro				curnum, MaxQueueChildren, CurRunners,
146090792Sgshapiro				WorkGrp[curnum].wg_maxact);
146190792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
146290792Sgshapiro		if (MaxQueueChildren > 0 &&
146390792Sgshapiro		    CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren)
146490792Sgshapiro			break;
146564562Sgshapiro
146664562Sgshapiro		/*
146790792Sgshapiro		**  Pick up where we left off (curnum), in case we
146890792Sgshapiro		**  used up all the children last time without finishing.
146990792Sgshapiro		**  This give a round-robin fairness to queue runs.
1470102528Sgshapiro		**
1471102528Sgshapiro		**  Increment CurRunners before calling run_work_group()
1472102528Sgshapiro		**  to avoid a "race condition" with proc_list_drop() which
1473102528Sgshapiro		**  decrements CurRunners if the queue runners terminate.
1474102528Sgshapiro		**  This actually doesn't cause any harm, but CurRunners
1475102528Sgshapiro		**  might become negative which is at least confusing.
1476102528Sgshapiro		**
1477102528Sgshapiro		**  Notice: CurRunners is an upper limit, in some cases
1478102528Sgshapiro		**  (too few jobs in the queue) this value is larger than
1479102528Sgshapiro		**  the actual number of queue runners. The discrepancy can
1480102528Sgshapiro		**  increase if some queue runners "hang" for a long time.
148190792Sgshapiro		*/
148290792Sgshapiro
1483102528Sgshapiro		CurRunners += WorkGrp[curnum].wg_maxact;
1484110560Sgshapiro		if (forkflag)
1485110560Sgshapiro			rwgflags |= RWG_FORK;
1486110560Sgshapiro		if (verbose)
1487110560Sgshapiro			rwgflags |= RWG_VERBOSE;
1488110560Sgshapiro		if (persistent)
1489110560Sgshapiro			rwgflags |= RWG_PERSISTENT;
1490110560Sgshapiro		if (runall)
1491110560Sgshapiro			rwgflags |= RWG_RUNALL;
1492110560Sgshapiro		ret = run_work_group(curnum, rwgflags);
149390792Sgshapiro
149490792Sgshapiro		/*
149564562Sgshapiro		**  Failure means a message was printed for ETRN
149664562Sgshapiro		**  and subsequent queues are likely to fail as well.
1497102528Sgshapiro		**  Decrement CurRunners in that case because
1498102528Sgshapiro		**  none have been started.
149964562Sgshapiro		*/
150064562Sgshapiro
150164562Sgshapiro		if (!ret)
1502102528Sgshapiro		{
1503102528Sgshapiro			CurRunners -= WorkGrp[curnum].wg_maxact;
150464562Sgshapiro			break;
1505102528Sgshapiro		}
150664562Sgshapiro
150790792Sgshapiro		if (!persistent)
150894334Sgshapiro			schedule_queue_runs(runall, curnum, true);
150990792Sgshapiro		INCR_MOD(curnum, NumWorkGroups);
151064562Sgshapiro	}
151190792Sgshapiro
151290792Sgshapiro	/* schedule left over queue runs */
151390792Sgshapiro	if (i < NumWorkGroups && !NoMoreRunners && !persistent)
151490792Sgshapiro	{
151590792Sgshapiro		int h;
151690792Sgshapiro
151790792Sgshapiro		for (h = curnum; i < NumWorkGroups; i++)
151890792Sgshapiro		{
151994334Sgshapiro			schedule_queue_runs(runall, h, false);
152090792Sgshapiro			INCR_MOD(h, NumWorkGroups);
152190792Sgshapiro		}
152290792Sgshapiro	}
152390792Sgshapiro
152490792Sgshapiro
152590792Sgshapiro#if SM_HEAP_CHECK
152690792Sgshapiro	if (sm_debug_active(&DebugLeakQ, 1))
152790792Sgshapiro		sm_heap_setgroup(oldgroup);
152890792Sgshapiro#endif /* SM_HEAP_CHECK */
152964562Sgshapiro	return ret;
153064562Sgshapiro}
153190792Sgshapiro/*
153290792Sgshapiro**  RUNNER_WORK -- have a queue runner do its work
153364562Sgshapiro**
153490792Sgshapiro**  Have a queue runner do its work a list of entries.
153590792Sgshapiro**  When work isn't directly being done then this process can take a signal
153690792Sgshapiro**  and terminate immediately (in a clean fashion of course).
153790792Sgshapiro**  When work is directly being done, it's not to be interrupted
153890792Sgshapiro**  immediately: the work should be allowed to finish at a clean point
153990792Sgshapiro**  before termination (in a clean fashion of course).
154090792Sgshapiro**
154190792Sgshapiro**	Parameters:
154290792Sgshapiro**		e -- envelope.
154390792Sgshapiro**		sequenceno -- 'th process to run WorkQ.
154490792Sgshapiro**		didfork -- did the calling process fork()?
154590792Sgshapiro**		skip -- process only each skip'th item.
154690792Sgshapiro**		njobs -- number of jobs in WorkQ.
154790792Sgshapiro**
154890792Sgshapiro**	Returns:
154990792Sgshapiro**		none.
155090792Sgshapiro**
155190792Sgshapiro**	Side Effects:
155290792Sgshapiro**		runs things in the mail queue.
155390792Sgshapiro*/
155490792Sgshapiro
155590792Sgshapiro/* Get new load average every 30 seconds. */
155690792Sgshapiro#define GET_NEW_LA_TIME	30
155790792Sgshapiro
155890792Sgshapirostatic void
155990792Sgshapirorunner_work(e, sequenceno, didfork, skip, njobs)
156090792Sgshapiro	register ENVELOPE *e;
156190792Sgshapiro	int sequenceno;
156290792Sgshapiro	bool didfork;
156390792Sgshapiro	int skip;
156490792Sgshapiro	int njobs;
156590792Sgshapiro{
156690792Sgshapiro	int n;
156790792Sgshapiro	WORK *w;
156890792Sgshapiro	time_t current_la_time, now;
156990792Sgshapiro
157090792Sgshapiro	current_la_time = curtime();
157190792Sgshapiro
157290792Sgshapiro	/*
157390792Sgshapiro	**  Here we temporarily block the second calling of the handlers.
157490792Sgshapiro	**  This allows us to handle the signal without terminating in the
157590792Sgshapiro	**  middle of direct work. If a signal does come, the test for
157690792Sgshapiro	**  NoMoreRunners will find it.
157790792Sgshapiro	*/
157890792Sgshapiro
157990792Sgshapiro	BlockOldsh = true;
158090792Sgshapiro
158190792Sgshapiro	/* process them once at a time */
158290792Sgshapiro	while (WorkQ != NULL)
158390792Sgshapiro	{
158490792Sgshapiro#if SM_HEAP_CHECK
158590792Sgshapiro		SM_NONVOLATILE int oldgroup = 0;
158690792Sgshapiro
158790792Sgshapiro		if (sm_debug_active(&DebugLeakQ, 1))
158890792Sgshapiro		{
158990792Sgshapiro			oldgroup = sm_heap_group();
159090792Sgshapiro			sm_heap_newgroup();
159190792Sgshapiro			sm_dprintf("run_queue_group() heap group #%d\n",
159290792Sgshapiro				sm_heap_group());
159390792Sgshapiro		}
159490792Sgshapiro#endif /* SM_HEAP_CHECK */
159590792Sgshapiro
159690792Sgshapiro		/* do no more work */
159790792Sgshapiro		if (NoMoreRunners)
159890792Sgshapiro		{
159990792Sgshapiro			/* Check that a valid signal handler is callable */
160090792Sgshapiro			if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
160190792Sgshapiro			    Oldsh != runners_sighup &&
160290792Sgshapiro			    Oldsh != runners_sigterm)
160390792Sgshapiro				(*Oldsh)(Oldsig);
160490792Sgshapiro			break;
160590792Sgshapiro		}
160690792Sgshapiro
160790792Sgshapiro		w = WorkQ; /* assign current work item */
160890792Sgshapiro
160990792Sgshapiro		/*
161090792Sgshapiro		**  Set the head of the WorkQ to the next work item.
161190792Sgshapiro		**  It is set 'skip' ahead (the number of parallel queue
161290792Sgshapiro		**  runners working on WorkQ together) since each runner
161390792Sgshapiro		**  works on every 'skip'th (N-th) item.
161490792Sgshapiro		*/
161590792Sgshapiro
161690792Sgshapiro		for (n = 0; n < skip && WorkQ != NULL; n++)
161790792Sgshapiro			WorkQ = WorkQ->w_next;
161890792Sgshapiro		e->e_to = NULL;
161990792Sgshapiro
162090792Sgshapiro		/*
162190792Sgshapiro		**  Ignore jobs that are too expensive for the moment.
162290792Sgshapiro		**
162390792Sgshapiro		**	Get new load average every GET_NEW_LA_TIME seconds.
162490792Sgshapiro		*/
162590792Sgshapiro
162690792Sgshapiro		now = curtime();
162790792Sgshapiro		if (current_la_time < now - GET_NEW_LA_TIME)
162890792Sgshapiro		{
162990792Sgshapiro			sm_getla();
163090792Sgshapiro			current_la_time = now;
163190792Sgshapiro		}
163290792Sgshapiro		if (shouldqueue(WkRecipFact, current_la_time))
163390792Sgshapiro		{
163490792Sgshapiro			char *msg = "Aborting queue run: load average too high";
163590792Sgshapiro
163690792Sgshapiro			if (Verbose)
163790792Sgshapiro				message("%s", msg);
163890792Sgshapiro			if (LogLevel > 8)
163990792Sgshapiro				sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
164090792Sgshapiro			break;
164190792Sgshapiro		}
164290792Sgshapiro		if (shouldqueue(w->w_pri, w->w_ctime))
164390792Sgshapiro		{
164490792Sgshapiro			if (Verbose)
164590792Sgshapiro				message(EmptyString);
164690792Sgshapiro			if (QueueSortOrder == QSO_BYPRIORITY)
164790792Sgshapiro			{
164890792Sgshapiro				if (Verbose)
164990792Sgshapiro					message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
165090792Sgshapiro						qid_printqueue(w->w_qgrp,
165190792Sgshapiro							       w->w_qdir),
165290792Sgshapiro						w->w_name + 2, sequenceno,
165390792Sgshapiro						njobs);
165490792Sgshapiro				if (LogLevel > 8)
165590792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
165690792Sgshapiro						  "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
165790792Sgshapiro						  qid_printqueue(w->w_qgrp,
165890792Sgshapiro								 w->w_qdir),
165990792Sgshapiro						  w->w_name + 2, w->w_pri,
166090792Sgshapiro						  CurrentLA, sequenceno,
166190792Sgshapiro						  njobs);
166290792Sgshapiro				break;
166390792Sgshapiro			}
166490792Sgshapiro			else if (Verbose)
166590792Sgshapiro				message("Skipping %s/%s (sequence %d of %d)",
166690792Sgshapiro					qid_printqueue(w->w_qgrp, w->w_qdir),
166790792Sgshapiro					w->w_name + 2, sequenceno, njobs);
166890792Sgshapiro		}
166990792Sgshapiro		else
167090792Sgshapiro		{
167190792Sgshapiro			if (Verbose)
167290792Sgshapiro			{
167390792Sgshapiro				message(EmptyString);
167490792Sgshapiro				message("Running %s/%s (sequence %d of %d)",
167590792Sgshapiro					qid_printqueue(w->w_qgrp, w->w_qdir),
167690792Sgshapiro					w->w_name + 2, sequenceno, njobs);
167790792Sgshapiro			}
167890792Sgshapiro			if (didfork && MaxQueueChildren > 0)
167990792Sgshapiro			{
168090792Sgshapiro				sm_blocksignal(SIGCHLD);
168190792Sgshapiro				(void) sm_signal(SIGCHLD, reapchild);
168290792Sgshapiro			}
168390792Sgshapiro			if (tTd(63, 100))
168490792Sgshapiro				sm_syslog(LOG_DEBUG, NOQID,
168590792Sgshapiro					  "runqueue %s dowork(%s)",
168690792Sgshapiro					  qid_printqueue(w->w_qgrp, w->w_qdir),
168790792Sgshapiro					  w->w_name + 2);
168890792Sgshapiro
168990792Sgshapiro			(void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
1690111823Sgshapiro				      ForkQueueRuns, false, e);
169190792Sgshapiro			errno = 0;
169290792Sgshapiro		}
169390792Sgshapiro		sm_free(w->w_name); /* XXX */
169490792Sgshapiro		if (w->w_host != NULL)
169590792Sgshapiro			sm_free(w->w_host); /* XXX */
169690792Sgshapiro		sm_free((char *) w); /* XXX */
169790792Sgshapiro		sequenceno += skip; /* next sequence number */
169890792Sgshapiro#if SM_HEAP_CHECK
169990792Sgshapiro		if (sm_debug_active(&DebugLeakQ, 1))
170090792Sgshapiro			sm_heap_setgroup(oldgroup);
170190792Sgshapiro#endif /* SM_HEAP_CHECK */
170290792Sgshapiro	}
170390792Sgshapiro
170490792Sgshapiro	BlockOldsh = false;
170590792Sgshapiro
170690792Sgshapiro	/* check the signals didn't happen during the revert */
170790792Sgshapiro	if (NoMoreRunners)
170890792Sgshapiro	{
170990792Sgshapiro		/* Check that a valid signal handler is callable */
171090792Sgshapiro		if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
171190792Sgshapiro		    Oldsh != runners_sighup && Oldsh != runners_sigterm)
171290792Sgshapiro			(*Oldsh)(Oldsig);
171390792Sgshapiro	}
171490792Sgshapiro
171590792Sgshapiro	Oldsh = SIG_DFL; /* after the NoMoreRunners check */
171690792Sgshapiro}
171790792Sgshapiro/*
171890792Sgshapiro**  RUN_WORK_GROUP -- run the jobs in a queue group from a work group.
171990792Sgshapiro**
172064562Sgshapiro**	Gets the stuff out of the queue in some presumably logical
172164562Sgshapiro**	order and processes them.
172264562Sgshapiro**
172364562Sgshapiro**	Parameters:
172490792Sgshapiro**		wgrp -- work group to process.
1725110560Sgshapiro**		flags -- RWG_* flags
172664562Sgshapiro**
172764562Sgshapiro**	Returns:
172890792Sgshapiro**		true if the queue run successfully began.
172964562Sgshapiro**
173064562Sgshapiro**	Side Effects:
173164562Sgshapiro**		runs things in the mail queue.
173264562Sgshapiro*/
173364562Sgshapiro
173490792Sgshapiro/* Minimum sleep time for persistent queue runners */
173590792Sgshapiro#define MIN_SLEEP_TIME	5
173690792Sgshapiro
173790792Sgshapirobool
1738110560Sgshapirorun_work_group(wgrp, flags)
173990792Sgshapiro	int wgrp;
1740110560Sgshapiro	int flags;
174164562Sgshapiro{
174238032Speter	register ENVELOPE *e;
174390792Sgshapiro	int njobs, qdir;
174490792Sgshapiro	int sequenceno = 1;
174590792Sgshapiro	int qgrp, endgrp, h, i;
174694334Sgshapiro	time_t current_la_time, now;
174790792Sgshapiro	bool full, more;
174890792Sgshapiro	SM_RPOOL_T *rpool;
174990792Sgshapiro	extern void rmexpstab __P((void));
175038032Speter	extern ENVELOPE BlankEnvelope;
175190792Sgshapiro	extern SIGFUNC_DECL reapchild __P((int));
175238032Speter
175390792Sgshapiro	if (wgrp < 0)
175490792Sgshapiro		return false;
175590792Sgshapiro
175638032Speter	/*
175738032Speter	**  If no work will ever be selected, don't even bother reading
175838032Speter	**  the queue.
175938032Speter	*/
176038032Speter
176190792Sgshapiro	sm_getla();	/* get load average */
176238032Speter	current_la_time = curtime();
176338032Speter
1764110560Sgshapiro	if (!bitset(RWG_PERSISTENT, flags) &&
1765110560Sgshapiro	    shouldqueue(WkRecipFact, current_la_time))
176638032Speter	{
176738032Speter		char *msg = "Skipping queue run -- load average too high";
176838032Speter
1769110560Sgshapiro		if (bitset(RWG_VERBOSE, flags))
177038032Speter			message("458 %s\n", msg);
177138032Speter		if (LogLevel > 8)
177290792Sgshapiro			sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
177390792Sgshapiro		return false;
177438032Speter	}
177538032Speter
177638032Speter	/*
177738032Speter	**  See if we already have too many children.
177838032Speter	*/
177938032Speter
1780110560Sgshapiro	if (bitset(RWG_FORK, flags) &&
1781110560Sgshapiro	    WorkGrp[wgrp].wg_lowqintvl > 0 &&
1782110560Sgshapiro	    !bitset(RWG_PERSISTENT, flags) &&
178338032Speter	    MaxChildren > 0 && CurChildren >= MaxChildren)
178438032Speter	{
178564562Sgshapiro		char *msg = "Skipping queue run -- too many children";
178664562Sgshapiro
1787110560Sgshapiro		if (bitset(RWG_VERBOSE, flags))
178864562Sgshapiro			message("458 %s (%d)\n", msg, CurChildren);
178964562Sgshapiro		if (LogLevel > 8)
179090792Sgshapiro			sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)",
179164562Sgshapiro				  msg, CurChildren);
179290792Sgshapiro		return false;
179338032Speter	}
179438032Speter
179538032Speter	/*
179638032Speter	**  See if we want to go off and do other useful work.
179738032Speter	*/
179838032Speter
1799110560Sgshapiro	if (bitset(RWG_FORK, flags))
180038032Speter	{
180138032Speter		pid_t pid;
180238032Speter
180390792Sgshapiro		(void) sm_blocksignal(SIGCHLD);
180490792Sgshapiro		(void) sm_signal(SIGCHLD, reapchild);
180538032Speter
180638032Speter		pid = dofork();
180738032Speter		if (pid == -1)
180838032Speter		{
180938032Speter			const char *msg = "Skipping queue run -- fork() failed";
181090792Sgshapiro			const char *err = sm_errstring(errno);
181138032Speter
1812110560Sgshapiro			if (bitset(RWG_VERBOSE, flags))
181338032Speter				message("458 %s: %s\n", msg, err);
181438032Speter			if (LogLevel > 8)
181590792Sgshapiro				sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s",
181664562Sgshapiro					  msg, err);
181790792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
181890792Sgshapiro			return false;
181938032Speter		}
182038032Speter		if (pid != 0)
182138032Speter		{
182238032Speter			/* parent -- pick up intermediate zombie */
182390792Sgshapiro			(void) sm_blocksignal(SIGALRM);
182490792Sgshapiro
182590792Sgshapiro			/* wgrp only used when queue runners are persistent */
182690792Sgshapiro			proc_list_add(pid, "Queue runner", PROC_QUEUE,
182790792Sgshapiro				      WorkGrp[wgrp].wg_maxact,
1828110560Sgshapiro				      bitset(RWG_PERSISTENT, flags) ? wgrp : -1);
182990792Sgshapiro			(void) sm_releasesignal(SIGALRM);
183090792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
183190792Sgshapiro			return true;
183238032Speter		}
183390792Sgshapiro
183464562Sgshapiro		/* child -- clean up signals */
183577349Sgshapiro
183677349Sgshapiro		/* Reset global flags */
183777349Sgshapiro		RestartRequest = NULL;
183890792Sgshapiro		RestartWorkGroup = false;
183977349Sgshapiro		ShutdownRequest = NULL;
184077349Sgshapiro		PendingSignal = 0;
184190792Sgshapiro		CurrentPid = getpid();
184277349Sgshapiro
184390792Sgshapiro		/*
184490792Sgshapiro		**  Initialize exception stack and default exception
184590792Sgshapiro		**  handler for child process.
184690792Sgshapiro		*/
184790792Sgshapiro
184890792Sgshapiro		sm_exc_newthread(fatal_error);
184942575Speter		clrcontrol();
185038032Speter		proc_list_clear();
185142575Speter
185242575Speter		/* Add parent process as first child item */
185390792Sgshapiro		proc_list_add(CurrentPid, "Queue runner child process",
185490792Sgshapiro			      PROC_QUEUE_CHILD, 0, -1);
185590792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
185690792Sgshapiro		(void) sm_signal(SIGCHLD, SIG_DFL);
185790792Sgshapiro		(void) sm_signal(SIGHUP, SIG_DFL);
185890792Sgshapiro		(void) sm_signal(SIGTERM, intsig);
185938032Speter	}
186038032Speter
186138032Speter	/*
186238032Speter	**  Release any resources used by the daemon code.
186338032Speter	*/
186438032Speter
186538032Speter	clrdaemon();
186638032Speter
186738032Speter	/* force it to run expensive jobs */
186890792Sgshapiro	NoConnect = false;
186938032Speter
187038032Speter	/* drop privileges */
187138032Speter	if (geteuid() == (uid_t) 0)
187290792Sgshapiro		(void) drop_privileges(false);
187338032Speter
187438032Speter	/*
187538032Speter	**  Create ourselves an envelope
187638032Speter	*/
187738032Speter
187838032Speter	CurEnv = &QueueEnvelope;
187990792Sgshapiro	rpool = sm_rpool_new_x(NULL);
188090792Sgshapiro	e = newenvelope(&QueueEnvelope, CurEnv, rpool);
188138032Speter	e->e_flags = BlankEnvelope.e_flags;
188273188Sgshapiro	e->e_parent = NULL;
188338032Speter
188438032Speter	/* make sure we have disconnected from parent */
1885110560Sgshapiro	if (bitset(RWG_FORK, flags))
188638032Speter	{
188738032Speter		disconnect(1, e);
188890792Sgshapiro		QuickAbort = false;
188938032Speter	}
189038032Speter
189138032Speter	/*
189238032Speter	**  If we are running part of the queue, always ignore stored
189338032Speter	**  host status.
189438032Speter	*/
189538032Speter
189638032Speter	if (QueueLimitId != NULL || QueueLimitSender != NULL ||
189790792Sgshapiro#if _FFR_QUARANTINE
189890792Sgshapiro	    QueueLimitQuarantine != NULL ||
189990792Sgshapiro#endif /* _FFR_QUARANTINE */
190038032Speter	    QueueLimitRecipient != NULL)
190138032Speter	{
190290792Sgshapiro		IgnoreHostStatus = true;
190338032Speter		MinQueueAge = 0;
190438032Speter	}
190538032Speter
190638032Speter	/*
190790792Sgshapiro	**  Here is where we choose the queue group from the work group.
190890792Sgshapiro	**  The caller of the "domorework" label must setup a new envelope.
190990792Sgshapiro	*/
191090792Sgshapiro
191190792Sgshapiro	endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
191290792Sgshapiro
191390792Sgshapiro  domorework:
191490792Sgshapiro
191590792Sgshapiro	/*
191690792Sgshapiro	**  Run a queue group if:
1917110560Sgshapiro	**  RWG_RUNALL bit is set or the bit for this group is set.
191890792Sgshapiro	*/
191990792Sgshapiro
192094334Sgshapiro	now = curtime();
192190792Sgshapiro	for (;;)
192290792Sgshapiro	{
192390792Sgshapiro		/*
192490792Sgshapiro		**  Find the next queue group within the work group that
192590792Sgshapiro		**  has been marked as needing a run.
192690792Sgshapiro		*/
192790792Sgshapiro
192890792Sgshapiro		qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index;
192990792Sgshapiro		WorkGrp[wgrp].wg_curqgrp++; /* advance */
193090792Sgshapiro		WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */
1931110560Sgshapiro		if (bitset(RWG_RUNALL, flags) ||
193294334Sgshapiro		    (Queue[qgrp]->qg_nextrun <= now &&
193394334Sgshapiro		     Queue[qgrp]->qg_nextrun != (time_t) -1))
193490792Sgshapiro			break;
193590792Sgshapiro		if (endgrp == WorkGrp[wgrp].wg_curqgrp)
193690792Sgshapiro		{
193790792Sgshapiro			e->e_id = NULL;
1938110560Sgshapiro			if (bitset(RWG_FORK, flags))
193990792Sgshapiro				finis(true, true, ExitStat);
194090792Sgshapiro			return true; /* we're done */
194190792Sgshapiro		}
194290792Sgshapiro	}
194390792Sgshapiro
194490792Sgshapiro	qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */
194590792Sgshapiro#if _FFR_QUEUE_SCHED_DBG
194690792Sgshapiro	if (tTd(69, 12))
194790792Sgshapiro		sm_syslog(LOG_INFO, NOQID,
194890792Sgshapiro			"rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d",
194990792Sgshapiro			wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir),
195090792Sgshapiro			WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp);
195190792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
195290792Sgshapiro
195390792Sgshapiro#if HASNICE
195490792Sgshapiro	/* tweak niceness of queue runs */
195590792Sgshapiro	if (Queue[qgrp]->qg_nice > 0)
195690792Sgshapiro		(void) nice(Queue[qgrp]->qg_nice);
195790792Sgshapiro#endif /* HASNICE */
195890792Sgshapiro
195990792Sgshapiro	/* XXX running queue group... */
196090792Sgshapiro	sm_setproctitle(true, CurEnv, "running queue: %s",
196190792Sgshapiro			qid_printqueue(qgrp, qdir));
196290792Sgshapiro
196390792Sgshapiro	if (LogLevel > 69 || tTd(63, 99))
196490792Sgshapiro		sm_syslog(LOG_DEBUG, NOQID,
196590792Sgshapiro			  "runqueue %s, pid=%d, forkflag=%d",
196690792Sgshapiro			  qid_printqueue(qgrp, qdir), (int) CurrentPid,
1967110560Sgshapiro			  bitset(RWG_FORK, flags));
196890792Sgshapiro
196990792Sgshapiro	/*
197038032Speter	**  Start making passes through the queue.
197138032Speter	**	First, read and sort the entire queue.
197238032Speter	**	Then, process the work in that order.
197338032Speter	**		But if you take too long, start over.
197438032Speter	*/
197538032Speter
197690792Sgshapiro	for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
197790792Sgshapiro	{
197890792Sgshapiro		h = gatherq(qgrp, qdir, false, &full, &more);
197990792Sgshapiro#if SM_CONF_SHM
198090792Sgshapiro		if (ShmId != SM_SHM_NO_ID)
198190792Sgshapiro			QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h;
198290792Sgshapiro#endif /* SM_CONF_SHM */
198390792Sgshapiro		/* If there are no more items in this queue advance */
198490792Sgshapiro		if (!more)
198590792Sgshapiro		{
198690792Sgshapiro			/* A round-robin advance */
198790792Sgshapiro			qdir++;
198890792Sgshapiro			qdir %= Queue[qgrp]->qg_numqueues;
198990792Sgshapiro		}
199090792Sgshapiro
199190792Sgshapiro		/* Has the WorkList reached the limit? */
199290792Sgshapiro		if (full)
199390792Sgshapiro			break; /* don't try to gather more */
199490792Sgshapiro	}
199590792Sgshapiro
199638032Speter	/* order the existing work requests */
199790792Sgshapiro	njobs = sortq(Queue[qgrp]->qg_maxlist);
199890792Sgshapiro	Queue[qgrp]->qg_curnum = qdir; /* update */
199938032Speter
200064562Sgshapiro
200190792Sgshapiro	if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
200238032Speter	{
200390792Sgshapiro		int loop, maxrunners;
200490792Sgshapiro		pid_t pid;
200538032Speter
200638032Speter		/*
200790792Sgshapiro		**  For this WorkQ we want to fork off N children (maxrunners)
200890792Sgshapiro		**  at this point. Each child has a copy of WorkQ. Each child
200990792Sgshapiro		**  will process every N-th item. The parent will wait for all
201090792Sgshapiro		**  of the children to finish before moving on to the next
201190792Sgshapiro		**  queue group within the work group. This saves us forking
201290792Sgshapiro		**  a new runner-child for each work item.
201390792Sgshapiro		**  It's valid for qg_maxqrun == 0 since this may be an
201490792Sgshapiro		**  explicit "don't run this queue" setting.
201538032Speter		*/
201638032Speter
201790792Sgshapiro		maxrunners = Queue[qgrp]->qg_maxqrun;
201890792Sgshapiro
201990792Sgshapiro		/* No need to have more runners then there are jobs */
202090792Sgshapiro		if (maxrunners > njobs)
202190792Sgshapiro			maxrunners = njobs;
202290792Sgshapiro		for (loop = 0; loop < maxrunners; loop++)
202338032Speter		{
202490792Sgshapiro			/*
202590792Sgshapiro			**  Since the delivery may happen in a child and the
202690792Sgshapiro			**  parent does not wait, the parent may close the
202790792Sgshapiro			**  maps thereby removing any shared memory used by
202890792Sgshapiro			**  the map.  Therefore, close the maps now so the
202990792Sgshapiro			**  child will dynamically open them if necessary.
203090792Sgshapiro			*/
203190792Sgshapiro
203290792Sgshapiro			closemaps(false);
203390792Sgshapiro
203490792Sgshapiro			pid = fork();
203590792Sgshapiro			if (pid < 0)
203690792Sgshapiro			{
203790792Sgshapiro				syserr("run_work_group: cannot fork");
203890792Sgshapiro				return 0;
203990792Sgshapiro			}
204090792Sgshapiro			else if (pid > 0)
204190792Sgshapiro			{
204290792Sgshapiro				/* parent -- clean out connection cache */
204390792Sgshapiro				mci_flush(false, NULL);
204490792Sgshapiro				WorkQ = WorkQ->w_next; /* for the skip */
204590792Sgshapiro				sequenceno++;
204690792Sgshapiro				proc_list_add(pid, "Queue child runner process",
204790792Sgshapiro					      PROC_QUEUE_CHILD, 0, -1);
204890792Sgshapiro
204990792Sgshapiro				/* No additional work, no additional runners */
205090792Sgshapiro				if (WorkQ == NULL)
205190792Sgshapiro					break;
205290792Sgshapiro			}
205390792Sgshapiro			else
205490792Sgshapiro			{
205590792Sgshapiro				/* child -- Reset global flags */
205690792Sgshapiro				RestartRequest = NULL;
205790792Sgshapiro				RestartWorkGroup = false;
205890792Sgshapiro				ShutdownRequest = NULL;
205990792Sgshapiro				PendingSignal = 0;
206090792Sgshapiro				CurrentPid = getpid();
206190792Sgshapiro
206290792Sgshapiro				/*
206390792Sgshapiro				**  Initialize exception stack and default
206490792Sgshapiro				**  exception handler for child process.
206590792Sgshapiro				**  When fork()'d the child now has a private
206690792Sgshapiro				**  copy of WorkQ at its current position.
206790792Sgshapiro				*/
206890792Sgshapiro
206990792Sgshapiro				sm_exc_newthread(fatal_error);
207090792Sgshapiro
207190792Sgshapiro				/*
207290792Sgshapiro				**  SMTP processes (whether -bd or -bs) set
207390792Sgshapiro				**  SIGCHLD to reapchild to collect
207490792Sgshapiro				**  children status.  However, at delivery
207590792Sgshapiro				**  time, that status must be collected
207690792Sgshapiro				**  by sm_wait() to be dealt with properly
207790792Sgshapiro				**  (check success of delivery based
207890792Sgshapiro				**  on status code, etc).  Therefore, if we
207990792Sgshapiro				**  are an SMTP process, reset SIGCHLD
208090792Sgshapiro				**  back to the default so reapchild
208190792Sgshapiro				**  doesn't collect status before
208290792Sgshapiro				**  sm_wait().
208390792Sgshapiro				*/
208490792Sgshapiro
208590792Sgshapiro				if (OpMode == MD_SMTP ||
208690792Sgshapiro				    OpMode == MD_DAEMON ||
208790792Sgshapiro				    MaxQueueChildren > 0)
208890792Sgshapiro				{
208990792Sgshapiro					proc_list_clear();
209090792Sgshapiro					sm_releasesignal(SIGCHLD);
209190792Sgshapiro					(void) sm_signal(SIGCHLD, SIG_DFL);
209290792Sgshapiro				}
209390792Sgshapiro
209490792Sgshapiro				/* child -- error messages to the transcript */
209590792Sgshapiro				QuickAbort = OnlyOneError = false;
209690792Sgshapiro				runner_work(e, sequenceno, true,
209790792Sgshapiro					    maxrunners, njobs);
209890792Sgshapiro
209990792Sgshapiro				/* This child is done */
210090792Sgshapiro				finis(true, true, ExitStat);
210190792Sgshapiro				/* NOTREACHED */
210290792Sgshapiro			}
210338032Speter		}
210490792Sgshapiro
210590792Sgshapiro		sm_releasesignal(SIGCHLD);
210690792Sgshapiro
210790792Sgshapiro		/*
210890792Sgshapiro		**  Wait until all of the runners have completed before
210990792Sgshapiro		**  seeing if there is another queue group in the
211090792Sgshapiro		**  work group to process.
211190792Sgshapiro		**  XXX Future enhancement: don't wait() for all children
211290792Sgshapiro		**  here, just go ahead and make sure that overall the number
211390792Sgshapiro		**  of children is not exceeded.
211490792Sgshapiro		*/
211590792Sgshapiro
211690792Sgshapiro		while (CurChildren > 0)
211738032Speter		{
211890792Sgshapiro			int status;
211990792Sgshapiro			pid_t ret;
212038032Speter
212190792Sgshapiro			while ((ret = sm_wait(&status)) <= 0)
212290792Sgshapiro				continue;
212390792Sgshapiro			proc_list_drop(ret, status, NULL);
212438032Speter		}
212590792Sgshapiro	}
2126110560Sgshapiro	else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags))
212790792Sgshapiro	{
212890792Sgshapiro		/*
212990792Sgshapiro		**  When current process will not fork children to do the work,
213090792Sgshapiro		**  it will do the work itself. The 'skip' will be 1 since
213190792Sgshapiro		**  there are no child runners to divide the work across.
213290792Sgshapiro		*/
213390792Sgshapiro
213490792Sgshapiro		runner_work(e, sequenceno, false, 1, njobs);
213590792Sgshapiro	}
213690792Sgshapiro
213790792Sgshapiro	/* free memory allocated by newenvelope() above */
213890792Sgshapiro	sm_rpool_free(rpool);
213990792Sgshapiro	QueueEnvelope.e_rpool = NULL;
214090792Sgshapiro
214190792Sgshapiro	/* Are there still more queues in the work group to process? */
214290792Sgshapiro	if (endgrp != WorkGrp[wgrp].wg_curqgrp)
214390792Sgshapiro	{
214490792Sgshapiro		rpool = sm_rpool_new_x(NULL);
214590792Sgshapiro		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
214690792Sgshapiro		e->e_flags = BlankEnvelope.e_flags;
214790792Sgshapiro		goto domorework;
214890792Sgshapiro	}
214990792Sgshapiro
215090792Sgshapiro	/* No more queues in work group to process. Now check persistent. */
2151110560Sgshapiro	if (bitset(RWG_PERSISTENT, flags))
215290792Sgshapiro	{
215390792Sgshapiro		sequenceno = 1;
215490792Sgshapiro		sm_setproctitle(true, CurEnv, "running queue: %s",
215590792Sgshapiro				qid_printqueue(qgrp, qdir));
215690792Sgshapiro
215790792Sgshapiro		/*
215890792Sgshapiro		**  close bogus maps, i.e., maps which caused a tempfail,
215990792Sgshapiro		**	so we get fresh map connections on the next lookup.
216090792Sgshapiro		**  closemaps() is also called when children are started.
216190792Sgshapiro		*/
216290792Sgshapiro
216390792Sgshapiro		closemaps(true);
216490792Sgshapiro
216590792Sgshapiro		/* Close any cached connections. */
216690792Sgshapiro		mci_flush(true, NULL);
216790792Sgshapiro
216890792Sgshapiro		/* Clean out expired related entries. */
216990792Sgshapiro		rmexpstab();
217090792Sgshapiro
217190792Sgshapiro#if NAMED_BIND
217290792Sgshapiro		/* Update MX records for FallBackMX. */
217390792Sgshapiro		if (FallBackMX != NULL)
217490792Sgshapiro			(void) getfallbackmxrr(FallBackMX);
217590792Sgshapiro#endif /* NAMED_BIND */
217690792Sgshapiro
217790792Sgshapiro#if USERDB
217890792Sgshapiro		/* close UserDatabase */
217990792Sgshapiro		_udbx_close();
218090792Sgshapiro#endif /* USERDB */
218190792Sgshapiro
218290792Sgshapiro#if SM_HEAP_CHECK
218390792Sgshapiro		if (sm_debug_active(&SmHeapCheck, 2)
218490792Sgshapiro		    && access("memdump", F_OK) == 0
218590792Sgshapiro		   )
218638032Speter		{
218790792Sgshapiro			SM_FILE_T *out;
218890792Sgshapiro
218990792Sgshapiro			remove("memdump");
219090792Sgshapiro			out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
219190792Sgshapiro					 "memdump.out", SM_IO_APPEND, NULL);
219290792Sgshapiro			if (out != NULL)
219338032Speter			{
219490792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
219590792Sgshapiro				sm_heap_report(out,
219690792Sgshapiro					sm_debug_level(&SmHeapCheck) - 1);
219790792Sgshapiro				(void) sm_io_close(out, SM_TIME_DEFAULT);
219838032Speter			}
219938032Speter		}
220090792Sgshapiro#endif /* SM_HEAP_CHECK */
220190792Sgshapiro
220290792Sgshapiro		/* let me rest for a second to catch my breath */
220390792Sgshapiro		if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
220490792Sgshapiro			sleep(MIN_SLEEP_TIME);
220590792Sgshapiro		else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
220690792Sgshapiro			sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
220738032Speter		else
220890792Sgshapiro			sleep(WorkGrp[wgrp].wg_lowqintvl);
220938032Speter
221090792Sgshapiro		/*
221190792Sgshapiro		**  Get the LA outside the WorkQ loop if necessary.
221290792Sgshapiro		**  In a persistent queue runner the code is repeated over
221390792Sgshapiro		**  and over but gatherq() may ignore entries due to
221490792Sgshapiro		**  shouldqueue() (do we really have to do this twice?).
221590792Sgshapiro		**  Hence the queue runners would just idle around when once
221690792Sgshapiro		**  CurrentLA caused all entries in a queue to be ignored.
221790792Sgshapiro		*/
221864562Sgshapiro
221990792Sgshapiro		now = curtime();
222090792Sgshapiro		if (njobs == 0 && current_la_time < now - GET_NEW_LA_TIME)
222190792Sgshapiro		{
222290792Sgshapiro			sm_getla();
222390792Sgshapiro			current_la_time = now;
222438032Speter		}
222590792Sgshapiro		rpool = sm_rpool_new_x(NULL);
222690792Sgshapiro		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
222790792Sgshapiro		e->e_flags = BlankEnvelope.e_flags;
222890792Sgshapiro		goto domorework;
222938032Speter	}
223038032Speter
223138032Speter	/* exit without the usual cleanup */
223238032Speter	e->e_id = NULL;
2233110560Sgshapiro	if (bitset(RWG_FORK, flags))
223490792Sgshapiro		finis(true, true, ExitStat);
223564562Sgshapiro	/* NOTREACHED */
223690792Sgshapiro	return true;
223738032Speter}
223838032Speter
223938032Speter/*
224090792Sgshapiro**  DOQUEUERUN -- do a queue run?
224190792Sgshapiro*/
224290792Sgshapiro
224390792Sgshapirobool
224490792Sgshapirodoqueuerun()
224590792Sgshapiro{
224694334Sgshapiro	return DoQueueRun;
224790792Sgshapiro}
224890792Sgshapiro
224990792Sgshapiro/*
225094334Sgshapiro**  RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done.
225177349Sgshapiro**
225277349Sgshapiro**	Parameters:
225394334Sgshapiro**		none.
225477349Sgshapiro**
225577349Sgshapiro**	Returns:
225677349Sgshapiro**		none.
225777349Sgshapiro**
225890792Sgshapiro**	Side Effects:
225990792Sgshapiro**		The invocation of this function via an alarm may interrupt
226090792Sgshapiro**		a set of actions. Thus errno may be set in that context.
226190792Sgshapiro**		We need to restore errno at the end of this function to ensure
226290792Sgshapiro**		that any work done here that sets errno doesn't return a
226390792Sgshapiro**		misleading/false errno value. Errno may	be EINTR upon entry to
226490792Sgshapiro**		this function because of non-restartable/continuable system
226590792Sgshapiro**		API was active. Iff this is true we will override errno as
226690792Sgshapiro**		a timeout (as a more accurate error message).
226790792Sgshapiro**
226877349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
226977349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
227077349Sgshapiro**		DOING.
227138032Speter*/
227238032Speter
227390792Sgshapirovoid
227494334Sgshapirorunqueueevent()
227538032Speter{
227690792Sgshapiro	int save_errno = errno;
227790792Sgshapiro
227890792Sgshapiro	/*
227990792Sgshapiro	**  Set the general bit that we want a queue run,
228090792Sgshapiro	**  tested in doqueuerun()
228190792Sgshapiro	*/
228290792Sgshapiro
228394334Sgshapiro	DoQueueRun = true;
228494334Sgshapiro#if _FFR_QUEUE_SCHED_DBG
228594334Sgshapiro	if (tTd(69, 10))
228694334Sgshapiro		sm_syslog(LOG_INFO, NOQID, "rqe: done");
228794334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
228890792Sgshapiro
228990792Sgshapiro	errno = save_errno;
229090792Sgshapiro	if (errno == EINTR)
229190792Sgshapiro		errno = ETIMEDOUT;
229238032Speter}
229390792Sgshapiro/*
229490792Sgshapiro**  GATHERQ -- gather messages from the message queue(s) the work queue.
229538032Speter**
229638032Speter**	Parameters:
229790792Sgshapiro**		qgrp -- the index of the queue group.
229890792Sgshapiro**		qdir -- the index of the queue directory.
229938032Speter**		doall -- if set, include everything in the queue (even
230038032Speter**			the jobs that cannot be run because the load
230190792Sgshapiro**			average is too high, or MaxQueueRun is reached).
230290792Sgshapiro**			Otherwise, exclude those jobs.
230390792Sgshapiro**		full -- (optional) to be set 'true' if WorkList is full
230490792Sgshapiro**		more -- (optional) to be set 'true' if there are still more
230590792Sgshapiro**			messages in this queue not added to WorkList
230638032Speter**
230738032Speter**	Returns:
230838032Speter**		The number of request in the queue (not necessarily
230990792Sgshapiro**		the number of requests in WorkList however).
231038032Speter**
231138032Speter**	Side Effects:
231290792Sgshapiro**		prepares available work into WorkList
231338032Speter*/
231438032Speter
231590792Sgshapiro#define NEED_P		0001	/* 'P': priority */
231690792Sgshapiro#define NEED_T		0002	/* 'T': time */
231790792Sgshapiro#define NEED_R		0004	/* 'R': recipient */
231890792Sgshapiro#define NEED_S		0010	/* 'S': sender */
231990792Sgshapiro#define NEED_H		0020	/* host */
232090792Sgshapiro#if _FFR_QUARANTINE
232190792Sgshapiro# define HAS_QUARANTINE		0040	/* has an unexpected 'q' line */
232290792Sgshapiro# define NEED_QUARANTINE	0100	/* 'q': reason */
232390792Sgshapiro#endif /* _FFR_QUARANTINE */
232438032Speter
232590792Sgshapirostatic WORK	*WorkList = NULL;	/* list of unsort work */
232690792Sgshapirostatic int	WorkListSize = 0;	/* current max size of WorkList */
232790792Sgshapirostatic int	WorkListCount = 0;	/* # of work items in WorkList */
232838032Speter
232964562Sgshapirostatic int
233090792Sgshapirogatherq(qgrp, qdir, doall, full, more)
233190792Sgshapiro	int qgrp;
233290792Sgshapiro	int qdir;
233338032Speter	bool doall;
233490792Sgshapiro	bool *full;
233590792Sgshapiro	bool *more;
233638032Speter{
233738032Speter	register struct dirent *d;
233838032Speter	register WORK *w;
233938032Speter	register char *p;
234038032Speter	DIR *f;
234190792Sgshapiro	int i, num_ent;
234290792Sgshapiro	int wn;
234338032Speter	QUEUE_CHAR *check;
234464562Sgshapiro	char qd[MAXPATHLEN];
234564562Sgshapiro	char qf[MAXPATHLEN];
234664562Sgshapiro
234790792Sgshapiro	wn = WorkListCount - 1;
234890792Sgshapiro	num_ent = 0;
234990792Sgshapiro	if (qdir == NOQDIR)
235090792Sgshapiro		(void) sm_strlcpy(qd, ".", sizeof qd);
235164562Sgshapiro	else
235290792Sgshapiro		(void) sm_strlcpyn(qd, sizeof qd, 2,
235390792Sgshapiro			Queue[qgrp]->qg_qpaths[qdir].qp_name,
235490792Sgshapiro			(bitset(QP_SUBQF,
235590792Sgshapiro				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
235690792Sgshapiro					? "/qf" : ""));
235764562Sgshapiro
235838032Speter	if (tTd(41, 1))
235938032Speter	{
236090792Sgshapiro		sm_dprintf("gatherq:\n");
236138032Speter
236238032Speter		check = QueueLimitId;
236338032Speter		while (check != NULL)
236438032Speter		{
236590792Sgshapiro			sm_dprintf("\tQueueLimitId = %s%s\n",
236690792Sgshapiro				check->queue_negate ? "!" : "",
236764562Sgshapiro				check->queue_match);
236838032Speter			check = check->queue_next;
236938032Speter		}
237038032Speter
237138032Speter		check = QueueLimitSender;
237238032Speter		while (check != NULL)
237338032Speter		{
237490792Sgshapiro			sm_dprintf("\tQueueLimitSender = %s%s\n",
237590792Sgshapiro				check->queue_negate ? "!" : "",
237664562Sgshapiro				check->queue_match);
237738032Speter			check = check->queue_next;
237838032Speter		}
237938032Speter
238038032Speter		check = QueueLimitRecipient;
238138032Speter		while (check != NULL)
238238032Speter		{
238390792Sgshapiro			sm_dprintf("\tQueueLimitRecipient = %s%s\n",
238490792Sgshapiro				check->queue_negate ? "!" : "",
238564562Sgshapiro				check->queue_match);
238638032Speter			check = check->queue_next;
238738032Speter		}
238838032Speter
238990792Sgshapiro#if _FFR_QUARANTINE
239090792Sgshapiro		if (QueueMode == QM_QUARANTINE)
239190792Sgshapiro		{
239290792Sgshapiro			check = QueueLimitQuarantine;
239390792Sgshapiro			while (check != NULL)
239490792Sgshapiro			{
239590792Sgshapiro				sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
239690792Sgshapiro					   check->queue_negate ? "!" : "",
239790792Sgshapiro					   check->queue_match);
239890792Sgshapiro				check = check->queue_next;
239990792Sgshapiro			}
240090792Sgshapiro		}
240190792Sgshapiro#endif /* _FFR_QUARANTINE */
240238032Speter	}
240338032Speter
240438032Speter	/* open the queue directory */
240564562Sgshapiro	f = opendir(qd);
240638032Speter	if (f == NULL)
240738032Speter	{
240890792Sgshapiro		syserr("gatherq: cannot open \"%s\"",
240990792Sgshapiro			qid_printqueue(qgrp, qdir));
241090792Sgshapiro		if (full != NULL)
241190792Sgshapiro			*full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
241290792Sgshapiro		if (more != NULL)
241390792Sgshapiro			*more = false;
241464562Sgshapiro		return 0;
241538032Speter	}
241638032Speter
241738032Speter	/*
241838032Speter	**  Read the work directory.
241938032Speter	*/
242038032Speter
242138032Speter	while ((d = readdir(f)) != NULL)
242238032Speter	{
242390792Sgshapiro		SM_FILE_T *cf;
242438032Speter		int qfver = 0;
242538032Speter		char lbuf[MAXNAME + 1];
242664562Sgshapiro		struct stat sbuf;
242738032Speter
242838032Speter		if (tTd(41, 50))
242990792Sgshapiro			sm_dprintf("gatherq: checking %s..", d->d_name);
243038032Speter
243138032Speter		/* is this an interesting entry? */
243290792Sgshapiro#if _FFR_QUARANTINE
243390792Sgshapiro		if (!(((QueueMode == QM_NORMAL &&
243490792Sgshapiro			d->d_name[0] == NORMQF_LETTER) ||
243590792Sgshapiro		       (QueueMode == QM_QUARANTINE &&
243690792Sgshapiro			d->d_name[0] == QUARQF_LETTER) ||
243790792Sgshapiro		       (QueueMode == QM_LOST &&
243890792Sgshapiro			d->d_name[0] == LOSEQF_LETTER)) &&
243990792Sgshapiro		      d->d_name[1] == 'f'))
244090792Sgshapiro#else /* _FFR_QUARANTINE */
244190792Sgshapiro		if (d->d_name[0] != NORMQF_LETTER || d->d_name[1] != 'f')
244290792Sgshapiro#endif /* _FFR_QUARANTINE */
244390792Sgshapiro		{
244490792Sgshapiro			if (tTd(41, 50))
244590792Sgshapiro				sm_dprintf("  skipping\n");
244638032Speter			continue;
244790792Sgshapiro		}
244890792Sgshapiro		if (tTd(41, 50))
244990792Sgshapiro			sm_dprintf("\n");
245038032Speter
245164562Sgshapiro		if (strlen(d->d_name) >= MAXQFNAME)
245242575Speter		{
245342575Speter			if (Verbose)
245490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
245590792Sgshapiro						     "gatherq: %s too long, %d max characters\n",
245690792Sgshapiro						     d->d_name, MAXQFNAME);
245742575Speter			if (LogLevel > 0)
245842575Speter				sm_syslog(LOG_ALERT, NOQID,
245990792Sgshapiro					  "gatherq: %s too long, %d max characters",
246064562Sgshapiro					  d->d_name, MAXQFNAME);
246138032Speter			continue;
246242575Speter		}
246338032Speter
246438032Speter		check = QueueLimitId;
246538032Speter		while (check != NULL)
246638032Speter		{
246794334Sgshapiro			if (strcontainedin(false, check->queue_match,
246890792Sgshapiro					   d->d_name) != check->queue_negate)
246938032Speter				break;
247038032Speter			else
247138032Speter				check = check->queue_next;
247238032Speter		}
247338032Speter		if (QueueLimitId != NULL && check == NULL)
247438032Speter			continue;
247538032Speter
247664562Sgshapiro		/* grow work list if necessary */
247738032Speter		if (++wn >= MaxQueueRun && MaxQueueRun > 0)
247838032Speter		{
247938032Speter			if (wn == MaxQueueRun && LogLevel > 0)
248064562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
248164562Sgshapiro					  "WorkList for %s maxed out at %d",
248290792Sgshapiro					  qid_printqueue(qgrp, qdir),
248364562Sgshapiro					  MaxQueueRun);
248490792Sgshapiro			if (doall)
248590792Sgshapiro				continue;	/* just count entries */
248690792Sgshapiro			break;
248738032Speter		}
248838032Speter		if (wn >= WorkListSize)
248938032Speter		{
249090792Sgshapiro			grow_wlist(qgrp, qdir);
249138032Speter			if (wn >= WorkListSize)
249238032Speter				continue;
249338032Speter		}
249490792Sgshapiro		SM_ASSERT(wn >= 0);
249564562Sgshapiro		w = &WorkList[wn];
249638032Speter
249790792Sgshapiro		(void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", d->d_name);
249864562Sgshapiro		if (stat(qf, &sbuf) < 0)
249964562Sgshapiro		{
250064562Sgshapiro			if (errno != ENOENT)
250164562Sgshapiro				sm_syslog(LOG_INFO, NOQID,
250290792Sgshapiro					  "gatherq: can't stat %s/%s",
250390792Sgshapiro					  qid_printqueue(qgrp, qdir),
250490792Sgshapiro					  d->d_name);
250564562Sgshapiro			wn--;
250664562Sgshapiro			continue;
250764562Sgshapiro		}
250864562Sgshapiro		if (!bitset(S_IFREG, sbuf.st_mode))
250964562Sgshapiro		{
251064562Sgshapiro			/* Yikes!  Skip it or we will hang on open! */
251190792Sgshapiro			if (!((d->d_name[0] == DATAFL_LETTER ||
251290792Sgshapiro			       d->d_name[0] == NORMQF_LETTER ||
251390792Sgshapiro#if _FFR_QUARANTINE
251490792Sgshapiro			       d->d_name[0] == QUARQF_LETTER ||
251590792Sgshapiro			       d->d_name[0] == LOSEQF_LETTER ||
251690792Sgshapiro#endif /* _FFR_QUARANTINE */
251790792Sgshapiro			       d->d_name[0] == XSCRPT_LETTER) &&
251890792Sgshapiro			      d->d_name[1] == 'f' && d->d_name[2] == '\0'))
251990792Sgshapiro				syserr("gatherq: %s/%s is not a regular file",
252090792Sgshapiro				       qid_printqueue(qgrp, qdir), d->d_name);
252164562Sgshapiro			wn--;
252264562Sgshapiro			continue;
252364562Sgshapiro		}
252464562Sgshapiro
252564562Sgshapiro		/* avoid work if possible */
252690792Sgshapiro		if ((QueueSortOrder == QSO_BYFILENAME ||
252790792Sgshapiro		     QueueSortOrder == QSO_BYMODTIME ||
252890792Sgshapiro		     QueueSortOrder == QSO_RANDOM) &&
252990792Sgshapiro#if _FFR_QUARANTINE
253090792Sgshapiro		    QueueLimitQuarantine == NULL &&
253190792Sgshapiro#endif /* _FFR_QUARANTINE */
253266494Sgshapiro		    QueueLimitSender == NULL &&
253366494Sgshapiro		    QueueLimitRecipient == NULL)
253464562Sgshapiro		{
253590792Sgshapiro			w->w_qgrp = qgrp;
253690792Sgshapiro			w->w_qdir = qdir;
253764562Sgshapiro			w->w_name = newstr(d->d_name);
253864562Sgshapiro			w->w_host = NULL;
253990792Sgshapiro			w->w_lock = w->w_tooyoung = false;
254064562Sgshapiro			w->w_pri = 0;
254164562Sgshapiro			w->w_ctime = 0;
254290792Sgshapiro			w->w_mtime = sbuf.st_mtime;
254390792Sgshapiro			++num_ent;
254464562Sgshapiro			continue;
254564562Sgshapiro		}
254664562Sgshapiro
254764562Sgshapiro		/* open control file */
254890792Sgshapiro		cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY,
254990792Sgshapiro				NULL);
255090792Sgshapiro		if (cf == NULL && OpMode != MD_PRINT)
255138032Speter		{
255238032Speter			/* this may be some random person sending hir msgs */
255338032Speter			if (tTd(41, 2))
255490792Sgshapiro				sm_dprintf("gatherq: cannot open %s: %s\n",
255590792Sgshapiro					d->d_name, sm_errstring(errno));
255638032Speter			errno = 0;
255738032Speter			wn--;
255838032Speter			continue;
255938032Speter		}
256090792Sgshapiro		w->w_qgrp = qgrp;
256190792Sgshapiro		w->w_qdir = qdir;
256238032Speter		w->w_name = newstr(d->d_name);
256338032Speter		w->w_host = NULL;
256490792Sgshapiro		if (cf != NULL)
256590792Sgshapiro		{
256690792Sgshapiro			w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
256790792Sgshapiro							    NULL),
256890792Sgshapiro					      w->w_name, NULL,
256990792Sgshapiro					      LOCK_SH|LOCK_NB);
257090792Sgshapiro		}
257190792Sgshapiro		w->w_tooyoung = false;
257238032Speter
257338032Speter		/* make sure jobs in creation don't clog queue */
257438032Speter		w->w_pri = 0x7fffffff;
257538032Speter		w->w_ctime = 0;
257690792Sgshapiro		w->w_mtime = sbuf.st_mtime;
257738032Speter
257838032Speter		/* extract useful information */
257990792Sgshapiro		i = NEED_P|NEED_T;
258090792Sgshapiro		if (QueueSortOrder == QSO_BYHOST
258190792Sgshapiro#if _FFR_RHS
258290792Sgshapiro		    || QueueSortOrder == QSO_BYSHUFFLE
258390792Sgshapiro#endif /* _FFR_RHS */
258490792Sgshapiro		   )
258571345Sgshapiro		{
258671345Sgshapiro			/* need w_host set for host sort order */
258771345Sgshapiro			i |= NEED_H;
258871345Sgshapiro		}
258938032Speter		if (QueueLimitSender != NULL)
259038032Speter			i |= NEED_S;
259164562Sgshapiro		if (QueueLimitRecipient != NULL)
259238032Speter			i |= NEED_R;
259390792Sgshapiro#if _FFR_QUARANTINE
259490792Sgshapiro		if (QueueLimitQuarantine != NULL)
259590792Sgshapiro			i |= NEED_QUARANTINE;
259690792Sgshapiro#endif /* _FFR_QUARANTINE */
259790792Sgshapiro		while (cf != NULL && i != 0 &&
259890792Sgshapiro		       sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
259990792Sgshapiro				   sizeof lbuf) != NULL)
260038032Speter		{
260138032Speter			int c;
260238032Speter			time_t age;
260338032Speter
260438032Speter			p = strchr(lbuf, '\n');
260538032Speter			if (p != NULL)
260638032Speter				*p = '\0';
260738032Speter			else
260838032Speter			{
260938032Speter				/* flush rest of overly long line */
261090792Sgshapiro				while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
261190792Sgshapiro				       != SM_IO_EOF && c != '\n')
261238032Speter					continue;
261338032Speter			}
261438032Speter
261538032Speter			switch (lbuf[0])
261638032Speter			{
261738032Speter			  case 'V':
261838032Speter				qfver = atoi(&lbuf[1]);
261938032Speter				break;
262038032Speter
262138032Speter			  case 'P':
262238032Speter				w->w_pri = atol(&lbuf[1]);
262338032Speter				i &= ~NEED_P;
262438032Speter				break;
262538032Speter
262638032Speter			  case 'T':
262738032Speter				w->w_ctime = atol(&lbuf[1]);
262838032Speter				i &= ~NEED_T;
262938032Speter				break;
263038032Speter
263190792Sgshapiro#if _FFR_QUARANTINE
263290792Sgshapiro			  case 'q':
263390792Sgshapiro				if (QueueMode != QM_QUARANTINE &&
263490792Sgshapiro				    QueueMode != QM_LOST)
263590792Sgshapiro				{
263690792Sgshapiro					if (tTd(41, 49))
263790792Sgshapiro						sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
263890792Sgshapiro							   w->w_name);
263990792Sgshapiro					i |= HAS_QUARANTINE;
264090792Sgshapiro				}
264190792Sgshapiro				else if (QueueMode == QM_QUARANTINE)
264290792Sgshapiro				{
264390792Sgshapiro					if (QueueLimitQuarantine == NULL)
264490792Sgshapiro					{
264590792Sgshapiro						i &= ~NEED_QUARANTINE;
264690792Sgshapiro						break;
264790792Sgshapiro					}
264890792Sgshapiro					p = &lbuf[1];
264990792Sgshapiro					check = QueueLimitQuarantine;
265090792Sgshapiro					while (check != NULL)
265190792Sgshapiro					{
265290792Sgshapiro						if (strcontainedin(false,
265390792Sgshapiro								   check->queue_match,
265490792Sgshapiro								   p) !=
265590792Sgshapiro						    check->queue_negate)
265690792Sgshapiro							break;
265790792Sgshapiro						else
265890792Sgshapiro							check = check->queue_next;
265990792Sgshapiro					}
266090792Sgshapiro					if (check != NULL)
266190792Sgshapiro						i &= ~NEED_QUARANTINE;
266290792Sgshapiro				}
266390792Sgshapiro				break;
266490792Sgshapiro#endif /* _FFR_QUARANTINE */
266590792Sgshapiro
266638032Speter			  case 'R':
266738032Speter				if (w->w_host == NULL &&
266838032Speter				    (p = strrchr(&lbuf[1], '@')) != NULL)
266964562Sgshapiro				{
267090792Sgshapiro#if _FFR_RHS
267190792Sgshapiro					if (QueueSortOrder == QSO_BYSHUFFLE)
267290792Sgshapiro						w->w_host = newstr(&p[1]);
267390792Sgshapiro					else
267490792Sgshapiro#endif /* _FFR_RHS */
267590792Sgshapiro						w->w_host = strrev(&p[1]);
267664562Sgshapiro					makelower(w->w_host);
267771345Sgshapiro					i &= ~NEED_H;
267864562Sgshapiro				}
267938032Speter				if (QueueLimitRecipient == NULL)
268038032Speter				{
268138032Speter					i &= ~NEED_R;
268238032Speter					break;
268338032Speter				}
268438032Speter				if (qfver > 0)
268538032Speter				{
268638032Speter					p = strchr(&lbuf[1], ':');
268738032Speter					if (p == NULL)
268838032Speter						p = &lbuf[1];
268938032Speter				}
269038032Speter				else
269138032Speter					p = &lbuf[1];
269238032Speter				check = QueueLimitRecipient;
269338032Speter				while (check != NULL)
269438032Speter				{
269590792Sgshapiro					if (strcontainedin(true,
269690792Sgshapiro							   check->queue_match,
269790792Sgshapiro							   p) !=
269890792Sgshapiro					    check->queue_negate)
269938032Speter						break;
270038032Speter					else
270138032Speter						check = check->queue_next;
270238032Speter				}
270338032Speter				if (check != NULL)
270438032Speter					i &= ~NEED_R;
270538032Speter				break;
270638032Speter
270738032Speter			  case 'S':
270864562Sgshapiro				check = QueueLimitSender;
270964562Sgshapiro				while (check != NULL)
271064562Sgshapiro				{
271190792Sgshapiro					if (strcontainedin(true,
271290792Sgshapiro							   check->queue_match,
271390792Sgshapiro							   &lbuf[1]) !=
271490792Sgshapiro					    check->queue_negate)
271564562Sgshapiro						break;
271664562Sgshapiro					else
271764562Sgshapiro						check = check->queue_next;
271864562Sgshapiro				}
271964562Sgshapiro				if (check != NULL)
272064562Sgshapiro					i &= ~NEED_S;
272138032Speter				break;
272238032Speter
272338032Speter			  case 'K':
272438032Speter				age = curtime() - (time_t) atol(&lbuf[1]);
272538032Speter				if (age >= 0 && MinQueueAge > 0 &&
272638032Speter				    age < MinQueueAge)
272790792Sgshapiro					w->w_tooyoung = true;
272838032Speter				break;
272938032Speter
273038032Speter			  case 'N':
273138032Speter				if (atol(&lbuf[1]) == 0)
273290792Sgshapiro					w->w_tooyoung = false;
273338032Speter				break;
273464562Sgshapiro
273590792Sgshapiro#if _FFR_QUEUEDELAY
273664562Sgshapiro/*
273764562Sgshapiro			  case 'G':
273864562Sgshapiro				queuealg = atoi(lbuf[1]);
273964562Sgshapiro				break;
274064562Sgshapiro			  case 'Y':
274164562Sgshapiro				queuedelay = (time_t) atol(&lbuf[1]);
274264562Sgshapiro				break;
274364562Sgshapiro*/
274490792Sgshapiro#endif /* _FFR_QUEUEDELAY */
274538032Speter			}
274638032Speter		}
274790792Sgshapiro		if (cf != NULL)
274890792Sgshapiro			(void) sm_io_close(cf, SM_TIME_DEFAULT);
274938032Speter
275038032Speter		if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) ||
275190792Sgshapiro#if _FFR_QUARANTINE
275290792Sgshapiro		    bitset(HAS_QUARANTINE, i) ||
275390792Sgshapiro		    bitset(NEED_QUARANTINE, i) ||
275490792Sgshapiro#endif /* _FFR_QUARANTINE */
275538032Speter		    bitset(NEED_R|NEED_S, i))
275638032Speter		{
275738032Speter			/* don't even bother sorting this job in */
275838032Speter			if (tTd(41, 49))
275990792Sgshapiro				sm_dprintf("skipping %s (%x)\n", w->w_name, i);
276090792Sgshapiro			sm_free(w->w_name); /* XXX */
276190792Sgshapiro			if (w->w_host != NULL)
276290792Sgshapiro				sm_free(w->w_host); /* XXX */
276338032Speter			wn--;
276438032Speter		}
276590792Sgshapiro		else
276690792Sgshapiro			++num_ent;
276738032Speter	}
276838032Speter	(void) closedir(f);
276938032Speter	wn++;
277038032Speter
277190792Sgshapiro	i = wn - WorkListCount;
277290792Sgshapiro	WorkListCount += SM_MIN(num_ent, WorkListSize);
277390792Sgshapiro
277490792Sgshapiro	if (more != NULL)
277590792Sgshapiro		*more = WorkListCount < wn;
277690792Sgshapiro
277790792Sgshapiro	if (full != NULL)
277890792Sgshapiro		*full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
277990792Sgshapiro			(WorkList == NULL && wn > 0);
278090792Sgshapiro
278190792Sgshapiro	return i;
278290792Sgshapiro}
278390792Sgshapiro/*
278490792Sgshapiro**  SORTQ -- sort the work list
278590792Sgshapiro**
278690792Sgshapiro**	First the old WorkQ is cleared away. Then the WorkList is sorted
278790792Sgshapiro**	for all items so that important (higher sorting value) items are not
278890792Sgshapiro**	trunctated off. Then the most important items are moved from
278990792Sgshapiro**	WorkList to WorkQ. The lower count of 'max' or MaxListCount items
279090792Sgshapiro**	are moved.
279190792Sgshapiro**
279290792Sgshapiro**	Parameters:
279390792Sgshapiro**		max -- maximum number of items to be placed in WorkQ
279490792Sgshapiro**
279590792Sgshapiro**	Returns:
279690792Sgshapiro**		the number of items in WorkQ
279790792Sgshapiro**
279890792Sgshapiro**	Side Effects:
279990792Sgshapiro**		WorkQ gets released and filled with new work. WorkList
280090792Sgshapiro**		gets released. Work items get sorted in order.
280190792Sgshapiro*/
280290792Sgshapiro
280390792Sgshapirostatic int
280490792Sgshapirosortq(max)
280590792Sgshapiro	int max;
280690792Sgshapiro{
280790792Sgshapiro	register int i;			/* local counter */
280890792Sgshapiro	register WORK *w;		/* tmp item pointer */
280990792Sgshapiro	int wc = WorkListCount;		/* trim size for WorkQ */
281090792Sgshapiro
281190792Sgshapiro	if (WorkQ != NULL)
281290792Sgshapiro	{
281390792Sgshapiro		/* Clear out old WorkQ. */
281490792Sgshapiro		for (w = WorkQ; w != NULL; )
281590792Sgshapiro		{
281690792Sgshapiro			register WORK *nw = w->w_next;
281790792Sgshapiro
281890792Sgshapiro			WorkQ = nw;
281990792Sgshapiro			sm_free(w->w_name); /* XXX */
282090792Sgshapiro			if (w->w_host != NULL)
282190792Sgshapiro				sm_free(w->w_host); /* XXX */
282290792Sgshapiro			sm_free((char *) w); /* XXX */
282390792Sgshapiro			w = nw;
282490792Sgshapiro		}
282590792Sgshapiro		sm_free((char *) WorkQ);
282690792Sgshapiro		WorkQ = NULL;
282790792Sgshapiro	}
282890792Sgshapiro
282990792Sgshapiro	if (WorkList == NULL || wc <= 0)
283064562Sgshapiro		return 0;
283138032Speter
283290792Sgshapiro	/* Check if the per queue group item limit will be exceeded */
283390792Sgshapiro	if (wc > max && max > 0)
283490792Sgshapiro		wc = max;
283590792Sgshapiro
283690792Sgshapiro	/*
283790792Sgshapiro	**  The sort now takes place using all of the items in WorkList.
283890792Sgshapiro	**  The list gets trimmed to the most important items after the sort.
283990792Sgshapiro	**  If the trim were to happen before the sort then one or more
284090792Sgshapiro	**  important items might get truncated off -- not what we want.
284190792Sgshapiro	*/
284290792Sgshapiro
284364562Sgshapiro	if (QueueSortOrder == QSO_BYHOST)
284438032Speter	{
284538032Speter		/*
284638032Speter		**  Sort the work directory for the first time,
284738032Speter		**  based on host name, lock status, and priority.
284838032Speter		*/
284938032Speter
285038032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1);
285138032Speter
285238032Speter		/*
285338032Speter		**  If one message to host is locked, "lock" all messages
285438032Speter		**  to that host.
285538032Speter		*/
285638032Speter
285738032Speter		i = 0;
285838032Speter		while (i < wc)
285938032Speter		{
286038032Speter			if (!WorkList[i].w_lock)
286138032Speter			{
286238032Speter				i++;
286338032Speter				continue;
286438032Speter			}
286538032Speter			w = &WorkList[i];
286638032Speter			while (++i < wc)
286738032Speter			{
286838032Speter				if (WorkList[i].w_host == NULL &&
286938032Speter				    w->w_host == NULL)
287090792Sgshapiro					WorkList[i].w_lock = true;
287138032Speter				else if (WorkList[i].w_host != NULL &&
287238032Speter					 w->w_host != NULL &&
287390792Sgshapiro					 sm_strcasecmp(WorkList[i].w_host,
287490792Sgshapiro						       w->w_host) == 0)
287590792Sgshapiro					WorkList[i].w_lock = true;
287638032Speter				else
287738032Speter					break;
287838032Speter			}
287938032Speter		}
288038032Speter
288138032Speter		/*
288238032Speter		**  Sort the work directory for the second time,
288338032Speter		**  based on lock status, host name, and priority.
288438032Speter		*/
288538032Speter
288638032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2);
288738032Speter	}
288864562Sgshapiro	else if (QueueSortOrder == QSO_BYTIME)
288938032Speter	{
289038032Speter		/*
289138032Speter		**  Simple sort based on submission time only.
289238032Speter		*/
289338032Speter
289438032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3);
289538032Speter	}
289664562Sgshapiro	else if (QueueSortOrder == QSO_BYFILENAME)
289764562Sgshapiro	{
289864562Sgshapiro		/*
289990792Sgshapiro		**  Sort based on queue filename.
290064562Sgshapiro		*/
290164562Sgshapiro
290264562Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4);
290364562Sgshapiro	}
290490792Sgshapiro	else if (QueueSortOrder == QSO_RANDOM)
290590792Sgshapiro	{
290690792Sgshapiro		/*
2907110560Sgshapiro		**  Sort randomly.  To avoid problems with an instable sort,
2908110560Sgshapiro		**  use a random index into the queue file name to start
2909110560Sgshapiro		**  comparison.
291090792Sgshapiro		*/
291190792Sgshapiro
2912110560Sgshapiro		randi = get_rand_mod(MAXQFNAME);
2913110560Sgshapiro		if (randi < 2)
2914110560Sgshapiro			randi = 3;
291590792Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf5);
291690792Sgshapiro	}
291790792Sgshapiro	else if (QueueSortOrder == QSO_BYMODTIME)
291890792Sgshapiro	{
291990792Sgshapiro		/*
292090792Sgshapiro		**  Simple sort based on modification time of queue file.
292190792Sgshapiro		**  This puts the oldest items first.
292290792Sgshapiro		*/
292390792Sgshapiro
292490792Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf6);
292590792Sgshapiro	}
292690792Sgshapiro#if _FFR_RHS
292790792Sgshapiro	else if (QueueSortOrder == QSO_BYSHUFFLE)
292890792Sgshapiro	{
292990792Sgshapiro		/*
293090792Sgshapiro		**  Simple sort based on shuffled host name.
293190792Sgshapiro		*/
293290792Sgshapiro
293390792Sgshapiro		init_shuffle_alphabet();
293490792Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf7);
293590792Sgshapiro	}
293690792Sgshapiro#endif /* _FFR_RHS */
293738032Speter	else
293838032Speter	{
293938032Speter		/*
294038032Speter		**  Simple sort based on queue priority only.
294138032Speter		*/
294238032Speter
294338032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0);
294438032Speter	}
294538032Speter
294638032Speter	/*
294738032Speter	**  Convert the work list into canonical form.
294838032Speter	**	Should be turning it into a list of envelopes here perhaps.
294990792Sgshapiro	**  Only take the most important items up to the per queue group
295090792Sgshapiro	**  maximum.
295138032Speter	*/
295238032Speter
295338032Speter	for (i = wc; --i >= 0; )
295438032Speter	{
295538032Speter		w = (WORK *) xalloc(sizeof *w);
295690792Sgshapiro		w->w_qgrp = WorkList[i].w_qgrp;
295790792Sgshapiro		w->w_qdir = WorkList[i].w_qdir;
295838032Speter		w->w_name = WorkList[i].w_name;
295938032Speter		w->w_host = WorkList[i].w_host;
296038032Speter		w->w_lock = WorkList[i].w_lock;
296138032Speter		w->w_tooyoung = WorkList[i].w_tooyoung;
296238032Speter		w->w_pri = WorkList[i].w_pri;
296338032Speter		w->w_ctime = WorkList[i].w_ctime;
296490792Sgshapiro		w->w_mtime = WorkList[i].w_mtime;
296538032Speter		w->w_next = WorkQ;
296638032Speter		WorkQ = w;
296738032Speter	}
296838032Speter	if (WorkList != NULL)
296990792Sgshapiro		sm_free(WorkList); /* XXX */
297038032Speter	WorkList = NULL;
297138032Speter	WorkListSize = 0;
297290792Sgshapiro	WorkListCount = 0;
297338032Speter
297438032Speter	if (tTd(40, 1))
297538032Speter	{
297638032Speter		for (w = WorkQ; w != NULL; w = w->w_next)
297764562Sgshapiro		{
297864562Sgshapiro			if (w->w_host != NULL)
297990792Sgshapiro				sm_dprintf("%22s: pri=%ld %s\n",
298064562Sgshapiro					w->w_name, w->w_pri, w->w_host);
298164562Sgshapiro			else
298290792Sgshapiro				sm_dprintf("%32s: pri=%ld\n",
298364562Sgshapiro					w->w_name, w->w_pri);
298464562Sgshapiro		}
298538032Speter	}
298638032Speter
298790792Sgshapiro	return wc; /* return number of WorkQ items */
298838032Speter}
298990792Sgshapiro/*
299038032Speter**  GROW_WLIST -- make the work list larger
299138032Speter**
299238032Speter**	Parameters:
299390792Sgshapiro**		qgrp -- the index for the queue group.
299490792Sgshapiro**		qdir -- the index for the queue directory.
299538032Speter**
299638032Speter**	Returns:
299738032Speter**		none.
299838032Speter**
299938032Speter**	Side Effects:
300038032Speter**		Adds another QUEUESEGSIZE entries to WorkList if possible.
300138032Speter**		It can fail if there isn't enough memory, so WorkListSize
300238032Speter**		should be checked again upon return.
300338032Speter*/
300438032Speter
300564562Sgshapirostatic void
300690792Sgshapirogrow_wlist(qgrp, qdir)
300790792Sgshapiro	int qgrp;
300890792Sgshapiro	int qdir;
300938032Speter{
301038032Speter	if (tTd(41, 1))
301190792Sgshapiro		sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
301238032Speter	if (WorkList == NULL)
301338032Speter	{
301464562Sgshapiro		WorkList = (WORK *) xalloc((sizeof *WorkList) *
301564562Sgshapiro					   (QUEUESEGSIZE + 1));
301638032Speter		WorkListSize = QUEUESEGSIZE;
301738032Speter	}
301838032Speter	else
301938032Speter	{
302038032Speter		int newsize = WorkListSize + QUEUESEGSIZE;
302190792Sgshapiro		WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
302290792Sgshapiro					  (unsigned) sizeof(WORK) * (newsize + 1));
302338032Speter
302438032Speter		if (newlist != NULL)
302538032Speter		{
302638032Speter			WorkListSize = newsize;
302738032Speter			WorkList = newlist;
302838032Speter			if (LogLevel > 1)
302938032Speter			{
303064562Sgshapiro				sm_syslog(LOG_INFO, NOQID,
303164562Sgshapiro					  "grew WorkList for %s to %d",
303290792Sgshapiro					  qid_printqueue(qgrp, qdir),
303364562Sgshapiro					  WorkListSize);
303438032Speter			}
303538032Speter		}
303638032Speter		else if (LogLevel > 0)
303738032Speter		{
303838032Speter			sm_syslog(LOG_ALERT, NOQID,
303964562Sgshapiro				  "FAILED to grow WorkList for %s to %d",
304090792Sgshapiro				  qid_printqueue(qgrp, qdir), newsize);
304138032Speter		}
304238032Speter	}
304338032Speter	if (tTd(41, 1))
304490792Sgshapiro		sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
304538032Speter}
304690792Sgshapiro/*
304738032Speter**  WORKCMPF0 -- simple priority-only compare function.
304838032Speter**
304938032Speter**	Parameters:
305038032Speter**		a -- the first argument.
305138032Speter**		b -- the second argument.
305238032Speter**
305338032Speter**	Returns:
305438032Speter**		-1 if a < b
305538032Speter**		 0 if a == b
305638032Speter**		+1 if a > b
305738032Speter**
305838032Speter*/
305938032Speter
306064562Sgshapirostatic int
306138032Speterworkcmpf0(a, b)
306238032Speter	register WORK *a;
306338032Speter	register WORK *b;
306438032Speter{
306538032Speter	long pa = a->w_pri;
306638032Speter	long pb = b->w_pri;
306738032Speter
306838032Speter	if (pa == pb)
306938032Speter		return 0;
307038032Speter	else if (pa > pb)
307138032Speter		return 1;
307238032Speter	else
307338032Speter		return -1;
307438032Speter}
307590792Sgshapiro/*
307638032Speter**  WORKCMPF1 -- first compare function for ordering work based on host name.
307738032Speter**
307838032Speter**	Sorts on host name, lock status, and priority in that order.
307938032Speter**
308038032Speter**	Parameters:
308138032Speter**		a -- the first argument.
308238032Speter**		b -- the second argument.
308338032Speter**
308438032Speter**	Returns:
308538032Speter**		<0 if a < b
308638032Speter**		 0 if a == b
308738032Speter**		>0 if a > b
308838032Speter**
308938032Speter*/
309038032Speter
309164562Sgshapirostatic int
309238032Speterworkcmpf1(a, b)
309338032Speter	register WORK *a;
309438032Speter	register WORK *b;
309538032Speter{
309638032Speter	int i;
309738032Speter
309838032Speter	/* host name */
309938032Speter	if (a->w_host != NULL && b->w_host == NULL)
310038032Speter		return 1;
310138032Speter	else if (a->w_host == NULL && b->w_host != NULL)
310238032Speter		return -1;
310338032Speter	if (a->w_host != NULL && b->w_host != NULL &&
310438032Speter	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
310538032Speter		return i;
310638032Speter
310738032Speter	/* lock status */
310838032Speter	if (a->w_lock != b->w_lock)
310938032Speter		return b->w_lock - a->w_lock;
311038032Speter
311138032Speter	/* job priority */
311273188Sgshapiro	return workcmpf0(a, b);
311338032Speter}
311490792Sgshapiro/*
311538032Speter**  WORKCMPF2 -- second compare function for ordering work based on host name.
311638032Speter**
311738032Speter**	Sorts on lock status, host name, and priority in that order.
311838032Speter**
311938032Speter**	Parameters:
312038032Speter**		a -- the first argument.
312138032Speter**		b -- the second argument.
312238032Speter**
312338032Speter**	Returns:
312438032Speter**		<0 if a < b
312538032Speter**		 0 if a == b
312638032Speter**		>0 if a > b
312738032Speter**
312838032Speter*/
312938032Speter
313064562Sgshapirostatic int
313138032Speterworkcmpf2(a, b)
313238032Speter	register WORK *a;
313338032Speter	register WORK *b;
313438032Speter{
313538032Speter	int i;
313638032Speter
313738032Speter	/* lock status */
313838032Speter	if (a->w_lock != b->w_lock)
313938032Speter		return a->w_lock - b->w_lock;
314038032Speter
314138032Speter	/* host name */
314238032Speter	if (a->w_host != NULL && b->w_host == NULL)
314338032Speter		return 1;
314438032Speter	else if (a->w_host == NULL && b->w_host != NULL)
314538032Speter		return -1;
314638032Speter	if (a->w_host != NULL && b->w_host != NULL &&
314738032Speter	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
314838032Speter		return i;
314938032Speter
315038032Speter	/* job priority */
315173188Sgshapiro	return workcmpf0(a, b);
315238032Speter}
315390792Sgshapiro/*
315438032Speter**  WORKCMPF3 -- simple submission-time-only compare function.
315538032Speter**
315638032Speter**	Parameters:
315738032Speter**		a -- the first argument.
315838032Speter**		b -- the second argument.
315938032Speter**
316038032Speter**	Returns:
316138032Speter**		-1 if a < b
316238032Speter**		 0 if a == b
316338032Speter**		+1 if a > b
316438032Speter**
316538032Speter*/
316638032Speter
316764562Sgshapirostatic int
316838032Speterworkcmpf3(a, b)
316938032Speter	register WORK *a;
317038032Speter	register WORK *b;
317138032Speter{
317238032Speter	if (a->w_ctime > b->w_ctime)
317338032Speter		return 1;
317438032Speter	else if (a->w_ctime < b->w_ctime)
317538032Speter		return -1;
317638032Speter	else
317738032Speter		return 0;
317838032Speter}
317990792Sgshapiro/*
318064562Sgshapiro**  WORKCMPF4 -- compare based on file name
318164562Sgshapiro**
318264562Sgshapiro**	Parameters:
318364562Sgshapiro**		a -- the first argument.
318464562Sgshapiro**		b -- the second argument.
318564562Sgshapiro**
318664562Sgshapiro**	Returns:
318764562Sgshapiro**		-1 if a < b
318864562Sgshapiro**		 0 if a == b
318964562Sgshapiro**		+1 if a > b
319064562Sgshapiro**
319164562Sgshapiro*/
319264562Sgshapiro
319364562Sgshapirostatic int
319464562Sgshapiroworkcmpf4(a, b)
319564562Sgshapiro	register WORK *a;
319664562Sgshapiro	register WORK *b;
319764562Sgshapiro{
319864562Sgshapiro	return strcmp(a->w_name, b->w_name);
319964562Sgshapiro}
320090792Sgshapiro/*
320190792Sgshapiro**  WORKCMPF5 -- compare based on assigned random number
320290792Sgshapiro**
320390792Sgshapiro**	Parameters:
320490792Sgshapiro**		a -- the first argument (ignored).
320590792Sgshapiro**		b -- the second argument (ignored).
320690792Sgshapiro**
320790792Sgshapiro**	Returns:
320890792Sgshapiro**		randomly 1/-1
320990792Sgshapiro*/
321090792Sgshapiro
321190792Sgshapiro/* ARGSUSED0 */
321290792Sgshapirostatic int
321390792Sgshapiroworkcmpf5(a, b)
321490792Sgshapiro	register WORK *a;
321590792Sgshapiro	register WORK *b;
321690792Sgshapiro{
3217110560Sgshapiro	if (strlen(a->w_name) < randi || strlen(b->w_name) < randi)
3218110560Sgshapiro		return -1;
3219110560Sgshapiro	return a->w_name[randi] - b->w_name[randi];
322090792Sgshapiro}
322190792Sgshapiro/*
322290792Sgshapiro**  WORKCMPF6 -- simple modification-time-only compare function.
322390792Sgshapiro**
322490792Sgshapiro**	Parameters:
322590792Sgshapiro**		a -- the first argument.
322690792Sgshapiro**		b -- the second argument.
322790792Sgshapiro**
322890792Sgshapiro**	Returns:
322990792Sgshapiro**		-1 if a < b
323090792Sgshapiro**		 0 if a == b
323190792Sgshapiro**		+1 if a > b
323290792Sgshapiro**
323390792Sgshapiro*/
323490792Sgshapiro
323590792Sgshapirostatic int
323690792Sgshapiroworkcmpf6(a, b)
323790792Sgshapiro	register WORK *a;
323890792Sgshapiro	register WORK *b;
323990792Sgshapiro{
324090792Sgshapiro	if (a->w_mtime > b->w_mtime)
324190792Sgshapiro		return 1;
324290792Sgshapiro	else if (a->w_mtime < b->w_mtime)
324390792Sgshapiro		return -1;
324490792Sgshapiro	else
324590792Sgshapiro		return 0;
324690792Sgshapiro}
324790792Sgshapiro#if _FFR_RHS
324890792Sgshapiro/*
324990792Sgshapiro**  WORKCMPF7 -- compare function for ordering work based on shuffled host name.
325090792Sgshapiro**
325190792Sgshapiro**	Sorts on lock status, host name, and priority in that order.
325290792Sgshapiro**
325390792Sgshapiro**	Parameters:
325490792Sgshapiro**		a -- the first argument.
325590792Sgshapiro**		b -- the second argument.
325690792Sgshapiro**
325790792Sgshapiro**	Returns:
325890792Sgshapiro**		<0 if a < b
325990792Sgshapiro**		 0 if a == b
326090792Sgshapiro**		>0 if a > b
326190792Sgshapiro**
326290792Sgshapiro*/
326390792Sgshapiro
326490792Sgshapirostatic int
326590792Sgshapiroworkcmpf7(a, b)
326690792Sgshapiro	register WORK *a;
326790792Sgshapiro	register WORK *b;
326890792Sgshapiro{
326990792Sgshapiro	int i;
327090792Sgshapiro
327190792Sgshapiro	/* lock status */
327290792Sgshapiro	if (a->w_lock != b->w_lock)
327390792Sgshapiro		return a->w_lock - b->w_lock;
327490792Sgshapiro
327590792Sgshapiro	/* host name */
327690792Sgshapiro	if (a->w_host != NULL && b->w_host == NULL)
327790792Sgshapiro		return 1;
327890792Sgshapiro	else if (a->w_host == NULL && b->w_host != NULL)
327990792Sgshapiro		return -1;
328090792Sgshapiro	if (a->w_host != NULL && b->w_host != NULL &&
328190792Sgshapiro	    (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
328290792Sgshapiro		return i;
328390792Sgshapiro
328490792Sgshapiro	/* job priority */
328590792Sgshapiro	return workcmpf0(a, b);
328690792Sgshapiro}
328790792Sgshapiro#endif /* _FFR_RHS */
328890792Sgshapiro/*
328964562Sgshapiro**  STRREV -- reverse string
329064562Sgshapiro**
329164562Sgshapiro**	Returns a pointer to a new string that is the reverse of
329264562Sgshapiro**	the string pointed to by fwd.  The space for the new
329364562Sgshapiro**	string is obtained using xalloc().
329464562Sgshapiro**
329564562Sgshapiro**	Parameters:
329664562Sgshapiro**		fwd -- the string to reverse.
329764562Sgshapiro**
329864562Sgshapiro**	Returns:
329964562Sgshapiro**		the reversed string.
330064562Sgshapiro*/
330164562Sgshapiro
330264562Sgshapirostatic char *
330364562Sgshapirostrrev(fwd)
330464562Sgshapiro	char *fwd;
330564562Sgshapiro{
330664562Sgshapiro	char *rev = NULL;
330764562Sgshapiro	int len, cnt;
330864562Sgshapiro
330964562Sgshapiro	len = strlen(fwd);
331064562Sgshapiro	rev = xalloc(len + 1);
331164562Sgshapiro	for (cnt = 0; cnt < len; ++cnt)
331264562Sgshapiro		rev[cnt] = fwd[len - cnt - 1];
331364562Sgshapiro	rev[len] = '\0';
331464562Sgshapiro	return rev;
331564562Sgshapiro}
331690792Sgshapiro
331790792Sgshapiro#if _FFR_RHS
331890792Sgshapiro
331990792Sgshapiro#define NASCII	128
332090792Sgshapiro#define NCHAR	256
332190792Sgshapiro
332290792Sgshapirostatic unsigned char ShuffledAlphabet[NCHAR];
332390792Sgshapiro
332490792Sgshapirovoid
332590792Sgshapiroinit_shuffle_alphabet()
332690792Sgshapiro{
332790792Sgshapiro	static bool init = false;
332890792Sgshapiro	int i;
332990792Sgshapiro
333090792Sgshapiro	if (init)
333190792Sgshapiro		return;
333290792Sgshapiro
333390792Sgshapiro	/* fill the ShuffledAlphabet */
333490792Sgshapiro	for (i = 0; i < NCHAR; i++)
333590792Sgshapiro		ShuffledAlphabet[i] = i;
333690792Sgshapiro
333790792Sgshapiro	/* mix it */
333890792Sgshapiro	for (i = 1; i < NCHAR; i++)
333990792Sgshapiro	{
334090792Sgshapiro		register int j = get_random() % NCHAR;
334190792Sgshapiro		register int tmp;
334290792Sgshapiro
334390792Sgshapiro		tmp = ShuffledAlphabet[j];
334490792Sgshapiro		ShuffledAlphabet[j] = ShuffledAlphabet[i];
334590792Sgshapiro		ShuffledAlphabet[i] = tmp;
334690792Sgshapiro	}
334790792Sgshapiro
334890792Sgshapiro	/* make it case insensitive */
334990792Sgshapiro	for (i = 'A'; i <= 'Z'; i++)
335090792Sgshapiro		ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
335190792Sgshapiro
335290792Sgshapiro	/* fill the upper part */
335390792Sgshapiro	for (i = 0; i < NCHAR; i++)
335490792Sgshapiro		ShuffledAlphabet[i + NCHAR] = ShuffledAlphabet[i];
335590792Sgshapiro	init = true;
335690792Sgshapiro}
335790792Sgshapiro
335890792Sgshapirostatic int
335990792Sgshapirosm_strshufflecmp(a, b)
336090792Sgshapiro	char *a;
336190792Sgshapiro	char *b;
336290792Sgshapiro{
336390792Sgshapiro	const unsigned char *us1 = (const unsigned char *) a;
336490792Sgshapiro	const unsigned char *us2 = (const unsigned char *) b;
336590792Sgshapiro
336690792Sgshapiro	while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
336790792Sgshapiro	{
336890792Sgshapiro		if (*us1++ == '\0')
336990792Sgshapiro			return 0;
337090792Sgshapiro	}
337190792Sgshapiro	return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
337290792Sgshapiro}
337390792Sgshapiro#endif /* _FFR_RHS */
337490792Sgshapiro
337590792Sgshapiro/*
337638032Speter**  DOWORK -- do a work request.
337738032Speter**
337838032Speter**	Parameters:
337990792Sgshapiro**		qgrp -- the index of the queue group for the job.
338090792Sgshapiro**		qdir -- the index of the queue directory for the job.
338138032Speter**		id -- the ID of the job to run.
338238032Speter**		forkflag -- if set, run this in background.
338338032Speter**		requeueflag -- if set, reinstantiate the queue quickly.
338438032Speter**			This is used when expanding aliases in the queue.
338538032Speter**			If forkflag is also set, it doesn't wait for the
338638032Speter**			child.
338738032Speter**		e - the envelope in which to run it.
338838032Speter**
338938032Speter**	Returns:
339038032Speter**		process id of process that is running the queue job.
339138032Speter**
339238032Speter**	Side Effects:
339338032Speter**		The work request is satisfied if possible.
339438032Speter*/
339538032Speter
339638032Speterpid_t
339790792Sgshapirodowork(qgrp, qdir, id, forkflag, requeueflag, e)
339890792Sgshapiro	int qgrp;
339990792Sgshapiro	int qdir;
340038032Speter	char *id;
340138032Speter	bool forkflag;
340238032Speter	bool requeueflag;
340338032Speter	register ENVELOPE *e;
340438032Speter{
340538032Speter	register pid_t pid;
340690792Sgshapiro	SM_RPOOL_T *rpool;
340738032Speter
340838032Speter	if (tTd(40, 1))
340990792Sgshapiro		sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
341038032Speter
341138032Speter	/*
341238032Speter	**  Fork for work.
341338032Speter	*/
341438032Speter
341538032Speter	if (forkflag)
341638032Speter	{
341764562Sgshapiro		/*
341864562Sgshapiro		**  Since the delivery may happen in a child and the
341964562Sgshapiro		**  parent does not wait, the parent may close the
342064562Sgshapiro		**  maps thereby removing any shared memory used by
342164562Sgshapiro		**  the map.  Therefore, close the maps now so the
342264562Sgshapiro		**  child will dynamically open them if necessary.
342364562Sgshapiro		*/
342464562Sgshapiro
342590792Sgshapiro		closemaps(false);
342664562Sgshapiro
342738032Speter		pid = fork();
342838032Speter		if (pid < 0)
342938032Speter		{
343038032Speter			syserr("dowork: cannot fork");
343138032Speter			return 0;
343238032Speter		}
343338032Speter		else if (pid > 0)
343438032Speter		{
343538032Speter			/* parent -- clean out connection cache */
343690792Sgshapiro			mci_flush(false, NULL);
343738032Speter		}
343838032Speter		else
343938032Speter		{
344090792Sgshapiro			/*
344190792Sgshapiro			**  Initialize exception stack and default exception
344290792Sgshapiro			**  handler for child process.
344390792Sgshapiro			*/
344490792Sgshapiro
344590792Sgshapiro			/* Reset global flags */
344690792Sgshapiro			RestartRequest = NULL;
344790792Sgshapiro			RestartWorkGroup = false;
344890792Sgshapiro			ShutdownRequest = NULL;
344990792Sgshapiro			PendingSignal = 0;
345090792Sgshapiro			CurrentPid = getpid();
345190792Sgshapiro			sm_exc_newthread(fatal_error);
345290792Sgshapiro
345390792Sgshapiro			/*
345490792Sgshapiro			**  See note above about SMTP processes and SIGCHLD.
345590792Sgshapiro			*/
345690792Sgshapiro
345790792Sgshapiro			if (OpMode == MD_SMTP ||
345890792Sgshapiro			    OpMode == MD_DAEMON ||
345990792Sgshapiro			    MaxQueueChildren > 0)
346090792Sgshapiro			{
346190792Sgshapiro				proc_list_clear();
346290792Sgshapiro				sm_releasesignal(SIGCHLD);
346390792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
346490792Sgshapiro			}
346590792Sgshapiro
346638032Speter			/* child -- error messages to the transcript */
346790792Sgshapiro			QuickAbort = OnlyOneError = false;
346838032Speter		}
346938032Speter	}
347038032Speter	else
347138032Speter	{
347238032Speter		pid = 0;
347338032Speter	}
347438032Speter
347538032Speter	if (pid == 0)
347638032Speter	{
347738032Speter		/*
347838032Speter		**  CHILD
347938032Speter		**	Lock the control file to avoid duplicate deliveries.
348038032Speter		**		Then run the file as though we had just read it.
348138032Speter		**	We save an idea of the temporary name so we
348238032Speter		**		can recover on interrupt.
348338032Speter		*/
348438032Speter
348590792Sgshapiro		if (forkflag)
348690792Sgshapiro		{
348790792Sgshapiro			/* Reset global flags */
348890792Sgshapiro			RestartRequest = NULL;
348990792Sgshapiro			RestartWorkGroup = false;
349090792Sgshapiro			ShutdownRequest = NULL;
349190792Sgshapiro			PendingSignal = 0;
349290792Sgshapiro		}
349377349Sgshapiro
349438032Speter		/* set basic modes, etc. */
349590792Sgshapiro		sm_clear_events();
349664562Sgshapiro		clearstats();
349790792Sgshapiro		rpool = sm_rpool_new_x(NULL);
349890792Sgshapiro		clearenvelope(e, false, rpool);
349938032Speter		e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
350064562Sgshapiro		set_delivery_mode(SM_DELIVER, e);
350138032Speter		e->e_errormode = EM_MAIL;
350238032Speter		e->e_id = id;
350390792Sgshapiro		e->e_qgrp = qgrp;
350490792Sgshapiro		e->e_qdir = qdir;
350590792Sgshapiro		GrabTo = UseErrorsTo = false;
350638032Speter		ExitStat = EX_OK;
350738032Speter		if (forkflag)
350838032Speter		{
350938032Speter			disconnect(1, e);
351090792Sgshapiro			set_op_mode(MD_QUEUERUN);
351138032Speter		}
351290792Sgshapiro		sm_setproctitle(true, e, "%s from queue", qid_printname(e));
351338032Speter		if (LogLevel > 76)
351490792Sgshapiro			sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
351590792Sgshapiro				  (int) CurrentPid);
351638032Speter
351738032Speter		/* don't use the headers from sendmail.cf... */
351838032Speter		e->e_header = NULL;
351938032Speter
352038032Speter		/* read the queue control file -- return if locked */
352190792Sgshapiro		if (!readqf(e, false))
352238032Speter		{
352338032Speter			if (tTd(40, 4) && e->e_id != NULL)
352490792Sgshapiro				sm_dprintf("readqf(%s) failed\n",
352564562Sgshapiro					qid_printname(e));
352638032Speter			e->e_id = NULL;
352738032Speter			if (forkflag)
352890792Sgshapiro				finis(false, true, EX_OK);
352938032Speter			else
353090792Sgshapiro			{
353190792Sgshapiro				/* adding this frees 8 bytes */
353290792Sgshapiro				clearenvelope(e, false, rpool);
353390792Sgshapiro
353490792Sgshapiro				/* adding this frees 12 bytes */
353590792Sgshapiro				sm_rpool_free(rpool);
353690792Sgshapiro				e->e_rpool = NULL;
353738032Speter				return 0;
353890792Sgshapiro			}
353938032Speter		}
354038032Speter
354138032Speter		e->e_flags |= EF_INQUEUE;
354290792Sgshapiro		eatheader(e, requeueflag, true);
354338032Speter
354438032Speter		if (requeueflag)
354590792Sgshapiro			queueup(e, false, false);
354638032Speter
354738032Speter		/* do the delivery */
354838032Speter		sendall(e, SM_DELIVER);
354938032Speter
355038032Speter		/* finish up and exit */
355138032Speter		if (forkflag)
355290792Sgshapiro			finis(true, true, ExitStat);
355338032Speter		else
355490792Sgshapiro		{
355590792Sgshapiro			dropenvelope(e, true, false);
355690792Sgshapiro			sm_rpool_free(rpool);
355790792Sgshapiro			e->e_rpool = NULL;
355890792Sgshapiro		}
355938032Speter	}
356038032Speter	e->e_id = NULL;
356138032Speter	return pid;
356238032Speter}
356390792Sgshapiro
356490792Sgshapiro/*
356590792Sgshapiro**  DOWORKLIST -- process a list of envelopes as work requests
356690792Sgshapiro**
356790792Sgshapiro**	Similar to dowork(), except that after forking, it processes an
356890792Sgshapiro**	envelope and its siblings, treating each envelope as a work request.
356990792Sgshapiro**
357090792Sgshapiro**	Parameters:
357190792Sgshapiro**		el -- envelope to be processed including its siblings.
357290792Sgshapiro**		forkflag -- if set, run this in background.
357390792Sgshapiro**		requeueflag -- if set, reinstantiate the queue quickly.
357490792Sgshapiro**			This is used when expanding aliases in the queue.
357590792Sgshapiro**			If forkflag is also set, it doesn't wait for the
357690792Sgshapiro**			child.
357790792Sgshapiro**
357890792Sgshapiro**	Returns:
357990792Sgshapiro**		process id of process that is running the queue job.
358090792Sgshapiro**
358190792Sgshapiro**	Side Effects:
358290792Sgshapiro**		The work request is satisfied if possible.
358390792Sgshapiro*/
358490792Sgshapiro
358590792Sgshapiropid_t
358690792Sgshapirodoworklist(el, forkflag, requeueflag)
358790792Sgshapiro	ENVELOPE *el;
358890792Sgshapiro	bool forkflag;
358990792Sgshapiro	bool requeueflag;
359090792Sgshapiro{
359190792Sgshapiro	register pid_t pid;
359290792Sgshapiro	ENVELOPE *ei;
359390792Sgshapiro
359490792Sgshapiro	if (tTd(40, 1))
359590792Sgshapiro		sm_dprintf("doworklist()\n");
359690792Sgshapiro
359790792Sgshapiro	/*
359890792Sgshapiro	**  Fork for work.
359990792Sgshapiro	*/
360090792Sgshapiro
360190792Sgshapiro	if (forkflag)
360290792Sgshapiro	{
360390792Sgshapiro		/*
360490792Sgshapiro		**  Since the delivery may happen in a child and the
360590792Sgshapiro		**  parent does not wait, the parent may close the
360690792Sgshapiro		**  maps thereby removing any shared memory used by
360790792Sgshapiro		**  the map.  Therefore, close the maps now so the
360890792Sgshapiro		**  child will dynamically open them if necessary.
360990792Sgshapiro		*/
361090792Sgshapiro
361190792Sgshapiro		closemaps(false);
361290792Sgshapiro
361390792Sgshapiro		pid = fork();
361490792Sgshapiro		if (pid < 0)
361590792Sgshapiro		{
361690792Sgshapiro			syserr("doworklist: cannot fork");
361790792Sgshapiro			return 0;
361890792Sgshapiro		}
361990792Sgshapiro		else if (pid > 0)
362090792Sgshapiro		{
362190792Sgshapiro			/* parent -- clean out connection cache */
362290792Sgshapiro			mci_flush(false, NULL);
362390792Sgshapiro		}
362490792Sgshapiro		else
362590792Sgshapiro		{
362690792Sgshapiro			/*
362790792Sgshapiro			**  Initialize exception stack and default exception
362890792Sgshapiro			**  handler for child process.
362990792Sgshapiro			*/
363090792Sgshapiro
363190792Sgshapiro			/* Reset global flags */
363290792Sgshapiro			RestartRequest = NULL;
363390792Sgshapiro			RestartWorkGroup = false;
363490792Sgshapiro			ShutdownRequest = NULL;
363590792Sgshapiro			PendingSignal = 0;
363690792Sgshapiro			CurrentPid = getpid();
363790792Sgshapiro			sm_exc_newthread(fatal_error);
363890792Sgshapiro
363990792Sgshapiro			/*
364090792Sgshapiro			**  See note above about SMTP processes and SIGCHLD.
364190792Sgshapiro			*/
364290792Sgshapiro
364390792Sgshapiro			if (OpMode == MD_SMTP ||
364490792Sgshapiro			    OpMode == MD_DAEMON ||
364590792Sgshapiro			    MaxQueueChildren > 0)
364690792Sgshapiro			{
364790792Sgshapiro				proc_list_clear();
364890792Sgshapiro				sm_releasesignal(SIGCHLD);
364990792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
365090792Sgshapiro			}
365190792Sgshapiro
365290792Sgshapiro			/* child -- error messages to the transcript */
365390792Sgshapiro			QuickAbort = OnlyOneError = false;
365490792Sgshapiro		}
365590792Sgshapiro	}
365690792Sgshapiro	else
365790792Sgshapiro	{
365890792Sgshapiro		pid = 0;
365990792Sgshapiro	}
366090792Sgshapiro
366190792Sgshapiro	if (pid != 0)
366290792Sgshapiro		return pid;
366390792Sgshapiro
366490792Sgshapiro	/*
366590792Sgshapiro	**  IN CHILD
366690792Sgshapiro	**	Lock the control file to avoid duplicate deliveries.
366790792Sgshapiro	**		Then run the file as though we had just read it.
366890792Sgshapiro	**	We save an idea of the temporary name so we
366990792Sgshapiro	**		can recover on interrupt.
367090792Sgshapiro	*/
367190792Sgshapiro
367290792Sgshapiro	if (forkflag)
367390792Sgshapiro	{
367490792Sgshapiro		/* Reset global flags */
367590792Sgshapiro		RestartRequest = NULL;
367690792Sgshapiro		RestartWorkGroup = false;
367790792Sgshapiro		ShutdownRequest = NULL;
367890792Sgshapiro		PendingSignal = 0;
367990792Sgshapiro	}
368090792Sgshapiro
368190792Sgshapiro	/* set basic modes, etc. */
368290792Sgshapiro	sm_clear_events();
368390792Sgshapiro	clearstats();
368490792Sgshapiro	GrabTo = UseErrorsTo = false;
368590792Sgshapiro	ExitStat = EX_OK;
368690792Sgshapiro	if (forkflag)
368790792Sgshapiro	{
368890792Sgshapiro		disconnect(1, el);
368990792Sgshapiro		set_op_mode(MD_QUEUERUN);
369090792Sgshapiro	}
369190792Sgshapiro	if (LogLevel > 76)
369290792Sgshapiro		sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
369390792Sgshapiro			  (int) CurrentPid);
369490792Sgshapiro
369590792Sgshapiro	for (ei = el; ei != NULL; ei = ei->e_sibling)
369690792Sgshapiro	{
369790792Sgshapiro		ENVELOPE e;
369890792Sgshapiro		SM_RPOOL_T *rpool;
369990792Sgshapiro
370090792Sgshapiro		if (WILL_BE_QUEUED(ei->e_sendmode))
370190792Sgshapiro			continue;
370290792Sgshapiro#if _FFR_QUARANTINE
370390792Sgshapiro		else if (QueueMode != QM_QUARANTINE &&
370490792Sgshapiro			 ei->e_quarmsg != NULL)
370590792Sgshapiro			continue;
370690792Sgshapiro#endif /* _FFR_QUARANTINE */
370790792Sgshapiro
370890792Sgshapiro		rpool = sm_rpool_new_x(NULL);
370990792Sgshapiro		clearenvelope(&e, true, rpool);
371090792Sgshapiro		e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
371190792Sgshapiro		set_delivery_mode(SM_DELIVER, &e);
371290792Sgshapiro		e.e_errormode = EM_MAIL;
371390792Sgshapiro		e.e_id = ei->e_id;
371490792Sgshapiro		e.e_qgrp = ei->e_qgrp;
371590792Sgshapiro		e.e_qdir = ei->e_qdir;
371690792Sgshapiro		openxscript(&e);
371790792Sgshapiro		sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
371890792Sgshapiro
371990792Sgshapiro		/* don't use the headers from sendmail.cf... */
372090792Sgshapiro		e.e_header = NULL;
372190792Sgshapiro		CurEnv = &e;
372290792Sgshapiro
372390792Sgshapiro		/* read the queue control file -- return if locked */
372490792Sgshapiro		if (readqf(&e, false))
372590792Sgshapiro		{
372690792Sgshapiro			e.e_flags |= EF_INQUEUE;
372790792Sgshapiro			eatheader(&e, requeueflag, true);
372890792Sgshapiro
372990792Sgshapiro			if (requeueflag)
373090792Sgshapiro				queueup(&e, false, false);
373190792Sgshapiro
373290792Sgshapiro			/* do the delivery */
373390792Sgshapiro			sendall(&e, SM_DELIVER);
373490792Sgshapiro			dropenvelope(&e, true, false);
373590792Sgshapiro		}
373690792Sgshapiro		else
373790792Sgshapiro		{
373890792Sgshapiro			if (tTd(40, 4) && e.e_id != NULL)
373990792Sgshapiro				sm_dprintf("readqf(%s) failed\n",
374090792Sgshapiro					qid_printname(&e));
374190792Sgshapiro		}
374290792Sgshapiro		sm_rpool_free(rpool);
374390792Sgshapiro		ei->e_id = NULL;
374490792Sgshapiro	}
374590792Sgshapiro
374690792Sgshapiro	/* restore CurEnv */
374790792Sgshapiro	CurEnv = el;
374890792Sgshapiro
374990792Sgshapiro	/* finish up and exit */
375090792Sgshapiro	if (forkflag)
375190792Sgshapiro		finis(true, true, ExitStat);
375290792Sgshapiro	return 0;
375390792Sgshapiro}
375490792Sgshapiro/*
375538032Speter**  READQF -- read queue file and set up environment.
375638032Speter**
375738032Speter**	Parameters:
375838032Speter**		e -- the envelope of the job to run.
375990792Sgshapiro**		openonly -- only open the qf (returned as e_lockfp)
376038032Speter**
376138032Speter**	Returns:
376290792Sgshapiro**		true if it successfully read the queue file.
376390792Sgshapiro**		false otherwise.
376438032Speter**
376538032Speter**	Side Effects:
376638032Speter**		The queue file is returned locked.
376738032Speter*/
376838032Speter
376964562Sgshapirostatic bool
377090792Sgshapiroreadqf(e, openonly)
377138032Speter	register ENVELOPE *e;
377290792Sgshapiro	bool openonly;
377338032Speter{
377490792Sgshapiro	register SM_FILE_T *qfp;
377538032Speter	ADDRESS *ctladdr;
377677349Sgshapiro	struct stat st, stf;
377738032Speter	char *bp;
377838032Speter	int qfver = 0;
377938032Speter	long hdrsize = 0;
378038032Speter	register char *p;
378190792Sgshapiro	char *frcpt = NULL;
378238032Speter	char *orcpt = NULL;
378390792Sgshapiro	bool nomore = false;
378490792Sgshapiro	bool bogus = false;
378564562Sgshapiro	MODE_T qsafe;
378694334Sgshapiro	char *err;
378764562Sgshapiro	char qf[MAXPATHLEN];
378838032Speter	char buf[MAXLINE];
378938032Speter
379038032Speter	/*
379138032Speter	**  Read and process the file.
379238032Speter	*/
379338032Speter
379490792Sgshapiro	(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof qf);
379590792Sgshapiro	qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR, NULL);
379638032Speter	if (qfp == NULL)
379738032Speter	{
379864562Sgshapiro		int save_errno = errno;
379964562Sgshapiro
380038032Speter		if (tTd(40, 8))
380190792Sgshapiro			sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
380290792Sgshapiro				qf, sm_errstring(errno));
380364562Sgshapiro		errno = save_errno;
380464562Sgshapiro		if (errno != ENOENT
380564562Sgshapiro		    )
380638032Speter			syserr("readqf: no control file %s", qf);
380790792Sgshapiro		RELEASE_QUEUE;
380890792Sgshapiro		return false;
380938032Speter	}
381038032Speter
381190792Sgshapiro	if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
381290792Sgshapiro		      LOCK_EX|LOCK_NB))
381338032Speter	{
381438032Speter		/* being processed by another queuer */
381564562Sgshapiro		if (Verbose)
381690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
381790792Sgshapiro					     "%s: locked\n", e->e_id);
381864562Sgshapiro		if (tTd(40, 8))
381990792Sgshapiro			sm_dprintf("%s: locked\n", e->e_id);
382038032Speter		if (LogLevel > 19)
382138032Speter			sm_syslog(LOG_DEBUG, e->e_id, "locked");
382290792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
382390792Sgshapiro		RELEASE_QUEUE;
382490792Sgshapiro		return false;
382538032Speter	}
382638032Speter
382738032Speter	/*
382877349Sgshapiro	**  Prevent locking race condition.
382977349Sgshapiro	**
383077349Sgshapiro	**  Process A: readqf(): qfp = fopen(qffile)
383177349Sgshapiro	**  Process B: queueup(): rename(tf, qf)
383277349Sgshapiro	**  Process B: unlocks(tf)
383377349Sgshapiro	**  Process A: lockfile(qf);
383477349Sgshapiro	**
383577349Sgshapiro	**  Process A (us) has the old qf file (before the rename deleted
383677349Sgshapiro	**  the directory entry) and will be delivering based on old data.
383777349Sgshapiro	**  This can lead to multiple deliveries of the same recipients.
383877349Sgshapiro	**
383977349Sgshapiro	**  Catch this by checking if the underlying qf file has changed
384077349Sgshapiro	**  *after* acquiring our lock and if so, act as though the file
384177349Sgshapiro	**  was still locked (i.e., just return like the lockfile() case
384277349Sgshapiro	**  above.
384338032Speter	*/
384438032Speter
384577349Sgshapiro	if (stat(qf, &stf) < 0 ||
384690792Sgshapiro	    fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
384738032Speter	{
384838032Speter		/* must have been being processed by someone else */
384938032Speter		if (tTd(40, 8))
385090792Sgshapiro			sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
385190792Sgshapiro				qf, sm_errstring(errno));
385290792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
385390792Sgshapiro		RELEASE_QUEUE;
385490792Sgshapiro		return false;
385538032Speter	}
385638032Speter
385777349Sgshapiro	if (st.st_nlink != stf.st_nlink ||
385877349Sgshapiro	    st.st_dev != stf.st_dev ||
385990792Sgshapiro	    ST_INODE(st) != ST_INODE(stf) ||
386090792Sgshapiro#if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
386177349Sgshapiro	    st.st_gen != stf.st_gen ||
386290792Sgshapiro#endif /* HAS_ST_GEN && 0 */
386377349Sgshapiro	    st.st_uid != stf.st_uid ||
386477349Sgshapiro	    st.st_gid != stf.st_gid ||
386577349Sgshapiro	    st.st_size != stf.st_size)
386677349Sgshapiro	{
386777349Sgshapiro		/* changed after opened */
386877349Sgshapiro		if (Verbose)
386990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
387090792Sgshapiro					     "%s: changed\n", e->e_id);
387177349Sgshapiro		if (tTd(40, 8))
387290792Sgshapiro			sm_dprintf("%s: changed\n", e->e_id);
387377349Sgshapiro		if (LogLevel > 19)
387477349Sgshapiro			sm_syslog(LOG_DEBUG, e->e_id, "changed");
387590792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
387690792Sgshapiro		RELEASE_QUEUE;
387790792Sgshapiro		return false;
387877349Sgshapiro	}
387977349Sgshapiro
388077349Sgshapiro	/*
388177349Sgshapiro	**  Check the queue file for plausibility to avoid attacks.
388277349Sgshapiro	*/
388377349Sgshapiro
388464562Sgshapiro	qsafe = S_IWOTH|S_IWGRP;
388564562Sgshapiro	if (bitset(S_IWGRP, QueueFileMode))
388664562Sgshapiro		qsafe &= ~S_IWGRP;
388764562Sgshapiro
388890792Sgshapiro	bogus = st.st_uid != geteuid() &&
388990792Sgshapiro		st.st_uid != TrustedUid &&
389090792Sgshapiro		geteuid() != RealUid;
389190792Sgshapiro
389290792Sgshapiro	/*
389390792Sgshapiro	**  If this qf file results from a set-group-ID binary, then
389490792Sgshapiro	**  we check whether the directory is group-writable,
389590792Sgshapiro	**  the queue file mode contains the group-writable bit, and
389690792Sgshapiro	**  the groups are the same.
389790792Sgshapiro	**  Notice: this requires that the set-group-ID binary is used to
389890792Sgshapiro	**  run the queue!
389990792Sgshapiro	*/
390090792Sgshapiro
390190792Sgshapiro	if (bogus && st.st_gid == getegid() && UseMSP)
390238032Speter	{
390390792Sgshapiro		char delim;
390490792Sgshapiro		struct stat dst;
390590792Sgshapiro
390690792Sgshapiro		bp = SM_LAST_DIR_DELIM(qf);
390790792Sgshapiro		if (bp == NULL)
390890792Sgshapiro			delim = '\0';
390990792Sgshapiro		else
391090792Sgshapiro		{
391190792Sgshapiro			delim = *bp;
391290792Sgshapiro			*bp = '\0';
391390792Sgshapiro		}
391490792Sgshapiro		if (stat(delim == '\0' ? "." : qf, &dst) < 0)
391590792Sgshapiro			syserr("readqf: cannot stat directory %s",
391690792Sgshapiro				delim == '\0' ? "." : qf);
391790792Sgshapiro		else
391890792Sgshapiro		{
391990792Sgshapiro			bogus = !(bitset(S_IWGRP, QueueFileMode) &&
392090792Sgshapiro				  bitset(S_IWGRP, dst.st_mode) &&
392190792Sgshapiro				  dst.st_gid == st.st_gid);
392290792Sgshapiro		}
392390792Sgshapiro		if (delim != '\0')
392490792Sgshapiro			*bp = delim;
392590792Sgshapiro	}
392690792Sgshapiro	if (!bogus)
392790792Sgshapiro		bogus = bitset(qsafe, st.st_mode);
392890792Sgshapiro	if (bogus)
392990792Sgshapiro	{
393038032Speter		if (LogLevel > 0)
393138032Speter		{
393238032Speter			sm_syslog(LOG_ALERT, e->e_id,
393390792Sgshapiro				  "bogus queue file, uid=%d, gid=%d, mode=%o",
393490792Sgshapiro				  st.st_uid, st.st_gid, st.st_mode);
393538032Speter		}
393638032Speter		if (tTd(40, 8))
393790792Sgshapiro			sm_dprintf("readqf(%s): bogus file\n", qf);
393890792Sgshapiro		e->e_flags |= EF_INQUEUE;
393990792Sgshapiro		if (!openonly)
394090792Sgshapiro			loseqfile(e, "bogus file uid/gid in mqueue");
394190792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
394290792Sgshapiro		RELEASE_QUEUE;
394390792Sgshapiro		return false;
394438032Speter	}
394538032Speter
394638032Speter	if (st.st_size == 0)
394738032Speter	{
394838032Speter		/* must be a bogus file -- if also old, just remove it */
394990792Sgshapiro		if (!openonly && st.st_ctime + 10 * 60 < curtime())
395038032Speter		{
395190792Sgshapiro			(void) xunlink(queuename(e, DATAFL_LETTER));
395290792Sgshapiro			(void) xunlink(queuename(e, ANYQFL_LETTER));
395338032Speter		}
395490792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
395590792Sgshapiro		RELEASE_QUEUE;
395690792Sgshapiro		return false;
395738032Speter	}
395838032Speter
395938032Speter	if (st.st_nlink == 0)
396038032Speter	{
396138032Speter		/*
396238032Speter		**  Race condition -- we got a file just as it was being
396338032Speter		**  unlinked.  Just assume it is zero length.
396438032Speter		*/
396538032Speter
396690792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
396790792Sgshapiro		RELEASE_QUEUE;
396890792Sgshapiro		return false;
396938032Speter	}
397038032Speter
397190792Sgshapiro#if _FFR_TRUSTED_QF
397290792Sgshapiro	/*
397390792Sgshapiro	**  If we don't own the file mark it as unsafe.
397490792Sgshapiro	**  However, allow TrustedUser to own it as well
397590792Sgshapiro	**  in case TrustedUser manipulates the queue.
397690792Sgshapiro	*/
397790792Sgshapiro
397890792Sgshapiro	if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
397990792Sgshapiro		e->e_flags |= EF_UNSAFE;
398090792Sgshapiro#else /* _FFR_TRUSTED_QF */
398190792Sgshapiro	/* If we don't own the file mark it as unsafe */
398290792Sgshapiro	if (st.st_uid != geteuid())
398390792Sgshapiro		e->e_flags |= EF_UNSAFE;
398490792Sgshapiro#endif /* _FFR_TRUSTED_QF */
398590792Sgshapiro
398638032Speter	/* good file -- save this lock */
398738032Speter	e->e_lockfp = qfp;
398838032Speter
398990792Sgshapiro	/* Just wanted the open file */
399090792Sgshapiro	if (openonly)
399190792Sgshapiro		return true;
399290792Sgshapiro
399338032Speter	/* do basic system initialization */
399438032Speter	initsys(e);
399590792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
399638032Speter
399738032Speter	LineNumber = 0;
399838032Speter	e->e_flags |= EF_GLOBALERRS;
399990792Sgshapiro	set_op_mode(MD_QUEUERUN);
400038032Speter	ctladdr = NULL;
400190792Sgshapiro#if _FFR_QUARANTINE
400290792Sgshapiro	e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
400390792Sgshapiro#endif /* _FFR_QUARANTINE */
400490792Sgshapiro	e->e_dfqgrp = e->e_qgrp;
400590792Sgshapiro	e->e_dfqdir = e->e_qdir;
400690792Sgshapiro#if _FFR_QUEUE_MACRO
400790792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
400890792Sgshapiro		  qid_printqueue(e->e_qgrp, e->e_qdir));
400990792Sgshapiro#endif /* _FFR_QUEUE_MACRO */
401038032Speter	e->e_dfino = -1;
401138032Speter	e->e_msgsize = -1;
401290792Sgshapiro#if _FFR_QUEUEDELAY
401364562Sgshapiro	e->e_queuealg = QD_LINEAR;
401464562Sgshapiro	e->e_queuedelay = (time_t) 0;
401590792Sgshapiro#endif /* _FFR_QUEUEDELAY */
401638032Speter	while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
401738032Speter	{
401890792Sgshapiro		unsigned long qflags;
401938032Speter		ADDRESS *q;
402090792Sgshapiro		int r;
402171345Sgshapiro		time_t now;
402238032Speter		auto char *ep;
402338032Speter
402438032Speter		if (tTd(40, 4))
402590792Sgshapiro			sm_dprintf("+++++ %s\n", bp);
402638032Speter		if (nomore)
402738032Speter		{
402838032Speter			/* hack attack */
402990792Sgshapiro  hackattack:
403090792Sgshapiro			syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
403190792Sgshapiro			       bp);
403294334Sgshapiro			err = "bogus queue line";
403394334Sgshapiro			goto fail;
403438032Speter		}
403538032Speter		switch (bp[0])
403638032Speter		{
403790792Sgshapiro		  case 'A':		/* AUTH= parameter */
403890792Sgshapiro			if (!xtextok(&bp[1]))
403990792Sgshapiro				goto hackattack;
404090792Sgshapiro			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
404190792Sgshapiro			break;
404238032Speter
404390792Sgshapiro		  case 'B':		/* body type */
404490792Sgshapiro			r = check_bodytype(&bp[1]);
404590792Sgshapiro			if (!BODYTYPE_VALID(r))
404690792Sgshapiro				goto hackattack;
404790792Sgshapiro			e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
404890792Sgshapiro			break;
404990792Sgshapiro
405038032Speter		  case 'C':		/* specify controlling user */
405190792Sgshapiro			ctladdr = setctluser(&bp[1], qfver, e);
405238032Speter			break;
405338032Speter
405490792Sgshapiro		  case 'D':		/* data file name */
405590792Sgshapiro			/* obsolete -- ignore */
405638032Speter			break;
405738032Speter
405890792Sgshapiro		  case 'd':		/* data file directory name */
405938032Speter			{
406090792Sgshapiro				int qgrp, qdir;
406190792Sgshapiro
406290792Sgshapiro#if _FFR_MSP_PARANOIA
406390792Sgshapiro				/* forbid queue groups in MSP? */
406490792Sgshapiro				if (UseMSP)
406590792Sgshapiro					goto hackattack;
406690792Sgshapiro#endif /* _FFR_MSP_PARANOIA */
406790792Sgshapiro				for (qgrp = 0;
406890792Sgshapiro				     qgrp < NumQueue && Queue[qgrp] != NULL;
406990792Sgshapiro				     ++qgrp)
407038032Speter				{
407190792Sgshapiro					for (qdir = 0;
407290792Sgshapiro					     qdir < Queue[qgrp]->qg_numqueues;
407390792Sgshapiro					     ++qdir)
407438032Speter					{
407590792Sgshapiro						if (strcmp(&bp[1],
407690792Sgshapiro							   Queue[qgrp]->qg_qpaths[qdir].qp_name)
407790792Sgshapiro						    == 0)
407890792Sgshapiro						{
407990792Sgshapiro							e->e_dfqgrp = qgrp;
408090792Sgshapiro							e->e_dfqdir = qdir;
408190792Sgshapiro							goto done;
408290792Sgshapiro						}
408338032Speter					}
408438032Speter				}
408594334Sgshapiro				err = "bogus queue file directory";
408694334Sgshapiro				goto fail;
408790792Sgshapiro			  done:
408890792Sgshapiro				break;
408938032Speter			}
409038032Speter
409138032Speter		  case 'E':		/* specify error recipient */
409238032Speter			/* no longer used */
409338032Speter			break;
409438032Speter
409590792Sgshapiro		  case 'F':		/* flag bits */
409690792Sgshapiro			if (strncmp(bp, "From ", 5) == 0)
409790792Sgshapiro			{
409890792Sgshapiro				/* we are being spoofed! */
409990792Sgshapiro				syserr("SECURITY ALERT: bogus qf line %s", bp);
410094334Sgshapiro				err = "bogus queue line";
410194334Sgshapiro				goto fail;
410290792Sgshapiro			}
410390792Sgshapiro			for (p = &bp[1]; *p != '\0'; p++)
410490792Sgshapiro			{
410590792Sgshapiro				switch (*p)
410690792Sgshapiro				{
410790792Sgshapiro				  case '8':	/* has 8 bit data */
410890792Sgshapiro					e->e_flags |= EF_HAS8BIT;
410990792Sgshapiro					break;
411038032Speter
411190792Sgshapiro				  case 'b':	/* delete Bcc: header */
411290792Sgshapiro					e->e_flags |= EF_DELETE_BCC;
411390792Sgshapiro					break;
411438032Speter
411590792Sgshapiro				  case 'd':	/* envelope has DSN RET= */
411690792Sgshapiro					e->e_flags |= EF_RET_PARAM;
411790792Sgshapiro					break;
411838032Speter
411990792Sgshapiro				  case 'n':	/* don't return body */
412090792Sgshapiro					e->e_flags |= EF_NO_BODY_RETN;
412190792Sgshapiro					break;
412290792Sgshapiro
412390792Sgshapiro				  case 'r':	/* response */
412490792Sgshapiro					e->e_flags |= EF_RESPONSE;
412590792Sgshapiro					break;
412690792Sgshapiro
412790792Sgshapiro				  case 's':	/* split */
412890792Sgshapiro					e->e_flags |= EF_SPLIT;
412990792Sgshapiro					break;
413090792Sgshapiro
413190792Sgshapiro				  case 'w':	/* warning sent */
413290792Sgshapiro					e->e_flags |= EF_WARNING;
413390792Sgshapiro					break;
413490792Sgshapiro				}
413590792Sgshapiro			}
413638032Speter			break;
413738032Speter
413890792Sgshapiro#if _FFR_QUEUEDELAY
413990792Sgshapiro		  case 'G':		/* queue delay algorithm */
414090792Sgshapiro			e->e_queuealg = atoi(&buf[1]);
414138032Speter			break;
414290792Sgshapiro#endif /* _FFR_QUEUEDELAY */
414338032Speter
414490792Sgshapiro#if _FFR_QUARANTINE
414590792Sgshapiro		  case 'q':		/* quarantine reason */
414690792Sgshapiro			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
414790792Sgshapiro			macdefine(&e->e_macro, A_PERM,
414890792Sgshapiro				  macid("{quarantine}"), e->e_quarmsg);
414938032Speter			break;
415090792Sgshapiro#endif /* _FFR_QUARANTINE */
415138032Speter
415290792Sgshapiro		  case 'H':		/* header */
415394334Sgshapiro
415494334Sgshapiro			/*
415594334Sgshapiro			**  count size before chompheader() destroys the line.
415694334Sgshapiro			**  this isn't accurate due to macro expansion, but
415794334Sgshapiro			**  better than before. "+3" to skip H?? at least.
415894334Sgshapiro			*/
415994334Sgshapiro
416094334Sgshapiro			hdrsize += strlen(bp + 3);
416190792Sgshapiro			(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
416238032Speter			break;
416338032Speter
416438032Speter		  case 'I':		/* data file's inode number */
416538032Speter			/* regenerated below */
416638032Speter			break;
416738032Speter
416880785Sgshapiro		  case 'K':		/* time of last delivery attempt */
416938032Speter			e->e_dtime = atol(&buf[1]);
417038032Speter			break;
417138032Speter
417290792Sgshapiro		  case 'L':		/* Solaris Content-Length: */
417390792Sgshapiro		  case 'M':		/* message */
417490792Sgshapiro			/* ignore this; we want a new message next time */
417564562Sgshapiro			break;
417664562Sgshapiro
417738032Speter		  case 'N':		/* number of delivery attempts */
417838032Speter			e->e_ntries = atoi(&buf[1]);
417938032Speter
418038032Speter			/* if this has been tried recently, let it be */
418171345Sgshapiro			now = curtime();
418271345Sgshapiro			if (e->e_ntries > 0 && e->e_dtime <= now &&
418371345Sgshapiro			    now < e->e_dtime + queuedelay(e))
418438032Speter			{
418564562Sgshapiro				char *howlong;
418638032Speter
418790792Sgshapiro				howlong = pintvl(now - e->e_dtime, true);
418864562Sgshapiro				if (Verbose)
418990792Sgshapiro					(void) sm_io_fprintf(smioout,
419090792Sgshapiro							     SM_TIME_DEFAULT,
419190792Sgshapiro							     "%s: too young (%s)\n",
419290792Sgshapiro							     e->e_id, howlong);
419364562Sgshapiro				if (tTd(40, 8))
419490792Sgshapiro					sm_dprintf("%s: too young (%s)\n",
419538032Speter						e->e_id, howlong);
419638032Speter				if (LogLevel > 19)
419738032Speter					sm_syslog(LOG_DEBUG, e->e_id,
419864562Sgshapiro						  "too young (%s)",
419964562Sgshapiro						  howlong);
420038032Speter				e->e_id = NULL;
420138032Speter				unlockqueue(e);
420290792Sgshapiro				RELEASE_QUEUE;
420390792Sgshapiro				return false;
420438032Speter			}
420590792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
420690792Sgshapiro				macid("{ntries}"), &buf[1]);
420764562Sgshapiro
420890792Sgshapiro#if NAMED_BIND
420964562Sgshapiro			/* adjust BIND parameters immediately */
421064562Sgshapiro			if (e->e_ntries == 0)
421164562Sgshapiro			{
421264562Sgshapiro				_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
421364562Sgshapiro				_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
421464562Sgshapiro			}
421564562Sgshapiro			else
421664562Sgshapiro			{
421764562Sgshapiro				_res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
421864562Sgshapiro				_res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
421964562Sgshapiro			}
422090792Sgshapiro#endif /* NAMED_BIND */
422138032Speter			break;
422238032Speter
422338032Speter		  case 'P':		/* message priority */
422438032Speter			e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
422538032Speter			break;
422638032Speter
422790792Sgshapiro		  case 'Q':		/* original recipient */
422890792Sgshapiro			orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
422990792Sgshapiro			break;
423090792Sgshapiro
423198841Sgshapiro		  case 'r':		/* final recipient */
423290792Sgshapiro			frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
423390792Sgshapiro			break;
423490792Sgshapiro
423590792Sgshapiro		  case 'R':		/* specify recipient */
423690792Sgshapiro			p = bp;
423790792Sgshapiro			qflags = 0;
423890792Sgshapiro			if (qfver >= 1)
423938032Speter			{
424090792Sgshapiro				/* get flag bits */
424190792Sgshapiro				while (*++p != '\0' && *p != ':')
424238032Speter				{
424390792Sgshapiro					switch (*p)
424490792Sgshapiro					{
424590792Sgshapiro					  case 'N':
424690792Sgshapiro						qflags |= QHASNOTIFY;
424790792Sgshapiro						break;
424838032Speter
424990792Sgshapiro					  case 'S':
425090792Sgshapiro						qflags |= QPINGONSUCCESS;
425190792Sgshapiro						break;
425238032Speter
425390792Sgshapiro					  case 'F':
425490792Sgshapiro						qflags |= QPINGONFAILURE;
425590792Sgshapiro						break;
425638032Speter
425790792Sgshapiro					  case 'D':
425890792Sgshapiro						qflags |= QPINGONDELAY;
425990792Sgshapiro						break;
426038032Speter
426190792Sgshapiro					  case 'P':
426290792Sgshapiro						qflags |= QPRIMARY;
426390792Sgshapiro						break;
426438032Speter
426590792Sgshapiro					  case 'A':
426690792Sgshapiro						if (ctladdr != NULL)
426790792Sgshapiro							ctladdr->q_flags |= QALIAS;
426890792Sgshapiro						break;
426990792Sgshapiro
427090792Sgshapiro					  default: /* ignore or complain? */
427190792Sgshapiro						break;
427290792Sgshapiro					}
427338032Speter				}
427438032Speter			}
427590792Sgshapiro			else
427690792Sgshapiro				qflags |= QPRIMARY;
427790792Sgshapiro			q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e,
427890792Sgshapiro				      true);
427990792Sgshapiro			if (q != NULL)
428090792Sgshapiro			{
428194334Sgshapiro				/* make sure we keep the current qgrp */
428294334Sgshapiro				if (ISVALIDQGRP(e->e_qgrp))
428394334Sgshapiro					q->q_qgrp = e->e_qgrp;
428490792Sgshapiro				q->q_alias = ctladdr;
428590792Sgshapiro				if (qfver >= 1)
428690792Sgshapiro					q->q_flags &= ~Q_PINGFLAGS;
428790792Sgshapiro				q->q_flags |= qflags;
428890792Sgshapiro				q->q_finalrcpt = frcpt;
428990792Sgshapiro				q->q_orcpt = orcpt;
429090792Sgshapiro				(void) recipient(q, &e->e_sendqueue, 0, e);
429190792Sgshapiro			}
429290792Sgshapiro			frcpt = NULL;
429390792Sgshapiro			orcpt = NULL;
429438032Speter			break;
429538032Speter
429690792Sgshapiro		  case 'S':		/* sender */
429790792Sgshapiro			setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
429890792Sgshapiro				  e, NULL, '\0', true);
429938032Speter			break;
430038032Speter
430190792Sgshapiro		  case 'T':		/* init time */
430290792Sgshapiro			e->e_ctime = atol(&bp[1]);
430364562Sgshapiro			break;
430464562Sgshapiro
430590792Sgshapiro		  case 'V':		/* queue file version number */
430690792Sgshapiro			qfver = atoi(&bp[1]);
430790792Sgshapiro			if (queuedelay_qfver_unsupported(qfver))
430890792Sgshapiro				syserr("queue file version %d not supported: %s",
430990792Sgshapiro				       qfver,
431090792Sgshapiro				       "sendmail not compiled with _FFR_QUEUEDELAY");
431190792Sgshapiro			if (qfver <= QF_VERSION)
431290792Sgshapiro				break;
431390792Sgshapiro			syserr("Version number in queue file (%d) greater than max (%d)",
431490792Sgshapiro				qfver, QF_VERSION);
431594334Sgshapiro			err = "unsupported queue file version";
431694334Sgshapiro			goto fail;
431790792Sgshapiro			/* NOTREACHED */
431890792Sgshapiro			break;
431990792Sgshapiro
432090792Sgshapiro#if _FFR_QUEUEDELAY
432190792Sgshapiro		  case 'Y':		/* current delay */
432290792Sgshapiro			e->e_queuedelay = (time_t) atol(&buf[1]);
432390792Sgshapiro			break;
432490792Sgshapiro#endif /* _FFR_QUEUEDELAY */
432590792Sgshapiro
432690792Sgshapiro		  case 'Z':		/* original envelope id from ESMTP */
432790792Sgshapiro			e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
432890792Sgshapiro			macdefine(&e->e_macro, A_PERM,
432990792Sgshapiro				macid("{dsn_envid}"), e->e_envid);
433090792Sgshapiro			break;
433190792Sgshapiro
433290792Sgshapiro		  case '!':		/* deliver by */
433390792Sgshapiro
433490792Sgshapiro			/* format: flag (1 char) space long-integer */
433590792Sgshapiro			e->e_dlvr_flag = buf[1];
433690792Sgshapiro			e->e_deliver_by = strtol(&buf[3], NULL, 10);
433790792Sgshapiro
433838032Speter		  case '$':		/* define macro */
433964562Sgshapiro			{
434064562Sgshapiro				char *p;
434164562Sgshapiro
434290792Sgshapiro				/* XXX elimate p? */
434390792Sgshapiro				r = macid_parse(&bp[1], &ep);
434490792Sgshapiro				if (r == 0)
434571345Sgshapiro					break;
434690792Sgshapiro				p = sm_rpool_strdup_x(e->e_rpool, ep);
434790792Sgshapiro				macdefine(&e->e_macro, A_PERM, r, p);
434864562Sgshapiro			}
434938032Speter			break;
435038032Speter
435138032Speter		  case '.':		/* terminate file */
435290792Sgshapiro			nomore = true;
435338032Speter			break;
435438032Speter
435538032Speter		  default:
435638032Speter			syserr("readqf: %s: line %d: bad line \"%s\"",
435738032Speter				qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
435894334Sgshapiro			err = "unrecognized line";
435994334Sgshapiro			goto fail;
436038032Speter		}
436138032Speter
436238032Speter		if (bp != buf)
436390792Sgshapiro			sm_free(bp); /* XXX */
436438032Speter	}
436538032Speter
436638032Speter	/*
436738032Speter	**  If we haven't read any lines, this queue file is empty.
436838032Speter	**  Arrange to remove it without referencing any null pointers.
436938032Speter	*/
437038032Speter
437138032Speter	if (LineNumber == 0)
437238032Speter	{
437338032Speter		errno = 0;
437490792Sgshapiro		e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
437590792Sgshapiro		RELEASE_QUEUE;
437690792Sgshapiro		return true;
437738032Speter	}
437838032Speter
437990792Sgshapiro	/* Check to make sure we have a complete queue file read */
438090792Sgshapiro	if (!nomore)
438190792Sgshapiro	{
438290792Sgshapiro		syserr("readqf: %s: incomplete queue file read", qf);
438390792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
438490792Sgshapiro		RELEASE_QUEUE;
438590792Sgshapiro		return false;
438690792Sgshapiro	}
438790792Sgshapiro
438864562Sgshapiro	/* possibly set ${dsn_ret} macro */
438964562Sgshapiro	if (bitset(EF_RET_PARAM, e->e_flags))
439064562Sgshapiro	{
439164562Sgshapiro		if (bitset(EF_NO_BODY_RETN, e->e_flags))
439290792Sgshapiro			macdefine(&e->e_macro, A_PERM,
439390792Sgshapiro				macid("{dsn_ret}"), "hdrs");
439464562Sgshapiro		else
439590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
439690792Sgshapiro				macid("{dsn_ret}"), "full");
439764562Sgshapiro	}
439864562Sgshapiro
439938032Speter	/*
440038032Speter	**  Arrange to read the data file.
440138032Speter	*/
440238032Speter
440390792Sgshapiro	p = queuename(e, DATAFL_LETTER);
440490792Sgshapiro	e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY,
440590792Sgshapiro			      NULL);
440638032Speter	if (e->e_dfp == NULL)
440738032Speter	{
440838032Speter		syserr("readqf: cannot open %s", p);
440938032Speter	}
441038032Speter	else
441138032Speter	{
441238032Speter		e->e_flags |= EF_HAS_DF;
441390792Sgshapiro		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
441490792Sgshapiro		    >= 0)
441538032Speter		{
441638032Speter			e->e_msgsize = st.st_size + hdrsize;
441738032Speter			e->e_dfdev = st.st_dev;
441890792Sgshapiro			e->e_dfino = ST_INODE(st);
441998121Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%ld",
442098121Sgshapiro					   e->e_msgsize);
442198121Sgshapiro			macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"),
442298121Sgshapiro				  buf);
442338032Speter		}
442438032Speter	}
442538032Speter
442690792Sgshapiro	RELEASE_QUEUE;
442790792Sgshapiro	return true;
442894334Sgshapiro
442994334Sgshapiro  fail:
443094334Sgshapiro	/*
443194334Sgshapiro	**  There was some error reading the qf file (reason is in err var.)
443294334Sgshapiro	**  Cleanup:
443394334Sgshapiro	**	close file; clear e_lockfp since it is the same as qfp,
443494334Sgshapiro	**	hence it is invalid (as file) after qfp is closed;
443594334Sgshapiro	**	the qf file is on disk, so set the flag to avoid calling
443694334Sgshapiro	**	queueup() with bogus data.
443794334Sgshapiro	*/
443894334Sgshapiro
443994334Sgshapiro	if (qfp != NULL)
444094334Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
444194334Sgshapiro	e->e_lockfp = NULL;
444294334Sgshapiro	e->e_flags |= EF_INQUEUE;
444394334Sgshapiro	loseqfile(e, err);
444494334Sgshapiro	RELEASE_QUEUE;
444594334Sgshapiro	return false;
444638032Speter}
444790792Sgshapiro/*
444864562Sgshapiro**  PRTSTR -- print a string, "unprintable" characters are shown as \oct
444964562Sgshapiro**
445064562Sgshapiro**	Parameters:
445164562Sgshapiro**		s -- string to print
445264562Sgshapiro**		ml -- maximum length of output
445364562Sgshapiro**
445464562Sgshapiro**	Returns:
445590792Sgshapiro**		number of entries
445664562Sgshapiro**
445764562Sgshapiro**	Side Effects:
445864562Sgshapiro**		Prints a string on stdout.
445964562Sgshapiro*/
446064562Sgshapiro
446164562Sgshapirostatic void
446264562Sgshapiroprtstr(s, ml)
446364562Sgshapiro	char *s;
446464562Sgshapiro	int ml;
446564562Sgshapiro{
446690792Sgshapiro	int c;
446764562Sgshapiro
446864562Sgshapiro	if (s == NULL)
446964562Sgshapiro		return;
447064562Sgshapiro	while (ml-- > 0 && ((c = *s++) != '\0'))
447164562Sgshapiro	{
447264562Sgshapiro		if (c == '\\')
447364562Sgshapiro		{
447464562Sgshapiro			if (ml-- > 0)
447564562Sgshapiro			{
447690792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
447790792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
447864562Sgshapiro			}
447964562Sgshapiro		}
448064562Sgshapiro		else if (isascii(c) && isprint(c))
448190792Sgshapiro			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
448264562Sgshapiro		else
448364562Sgshapiro		{
448464562Sgshapiro			if ((ml -= 3) > 0)
448590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
448690792Sgshapiro						     "\\%03o", c & 0xFF);
448764562Sgshapiro		}
448864562Sgshapiro	}
448964562Sgshapiro}
449090792Sgshapiro/*
449190792Sgshapiro**  PRINTNQE -- print out number of entries in the mail queue
449290792Sgshapiro**
449390792Sgshapiro**	Parameters:
449490792Sgshapiro**		out -- output file pointer.
449590792Sgshapiro**		prefix -- string to output in front of each line.
449690792Sgshapiro**
449790792Sgshapiro**	Returns:
449890792Sgshapiro**		none.
449990792Sgshapiro*/
450090792Sgshapiro
450190792Sgshapirovoid
450290792Sgshapiroprintnqe(out, prefix)
450390792Sgshapiro	SM_FILE_T *out;
450490792Sgshapiro	char *prefix;
450590792Sgshapiro{
450690792Sgshapiro#if SM_CONF_SHM
450790792Sgshapiro	int i, k = 0, nrequests = 0;
450890792Sgshapiro	bool unknown = false;
450990792Sgshapiro
451090792Sgshapiro	if (ShmId == SM_SHM_NO_ID)
451190792Sgshapiro	{
451290792Sgshapiro		if (prefix == NULL)
451390792Sgshapiro			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
451490792Sgshapiro					"Data unavailable: shared memory not updated\n");
451590792Sgshapiro		else
451690792Sgshapiro			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
451790792Sgshapiro					"%sNOTCONFIGURED:-1\r\n", prefix);
451890792Sgshapiro		return;
451990792Sgshapiro	}
452090792Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
452190792Sgshapiro	{
452290792Sgshapiro		int j;
452390792Sgshapiro
452490792Sgshapiro		k++;
452590792Sgshapiro		for (j = 0; j < Queue[i]->qg_numqueues; j++)
452690792Sgshapiro		{
452790792Sgshapiro			int n;
452890792Sgshapiro
452990792Sgshapiro			if (StopRequest)
453090792Sgshapiro				stop_sendmail();
453190792Sgshapiro
453290792Sgshapiro			n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
453390792Sgshapiro			if (prefix != NULL)
453490792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
453590792Sgshapiro					"%s%s:%d\r\n",
453690792Sgshapiro					prefix, qid_printqueue(i, j), n);
453790792Sgshapiro			else if (n < 0)
453890792Sgshapiro			{
453990792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
454090792Sgshapiro					"%s: unknown number of entries\n",
454190792Sgshapiro					qid_printqueue(i, j));
454290792Sgshapiro				unknown = true;
454390792Sgshapiro			}
454490792Sgshapiro			else if (n == 0)
454590792Sgshapiro			{
454690792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
454790792Sgshapiro					"%s is empty\n",
454890792Sgshapiro					qid_printqueue(i, j));
454990792Sgshapiro			}
455090792Sgshapiro			else if (n > 0)
455190792Sgshapiro			{
455290792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
455390792Sgshapiro					"%s: entries=%d\n",
455490792Sgshapiro					qid_printqueue(i, j), n);
455590792Sgshapiro				nrequests += n;
455690792Sgshapiro				k++;
455790792Sgshapiro			}
455890792Sgshapiro		}
455990792Sgshapiro	}
456090792Sgshapiro	if (prefix == NULL && k > 1)
456190792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
456290792Sgshapiro				     "\t\tTotal requests: %d%s\n",
456390792Sgshapiro				     nrequests, unknown ? " (about)" : "");
456490792Sgshapiro#else /* SM_CONF_SHM */
456590792Sgshapiro	if (prefix == NULL)
456690792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
456790792Sgshapiro			     "Data unavailable without shared memory support\n");
456890792Sgshapiro	else
456990792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
457090792Sgshapiro			     "%sNOTAVAILABLE:-1\r\n", prefix);
457190792Sgshapiro#endif /* SM_CONF_SHM */
457290792Sgshapiro}
457390792Sgshapiro/*
457438032Speter**  PRINTQUEUE -- print out a representation of the mail queue
457538032Speter**
457638032Speter**	Parameters:
457738032Speter**		none.
457838032Speter**
457938032Speter**	Returns:
458038032Speter**		none.
458138032Speter**
458238032Speter**	Side Effects:
458338032Speter**		Prints a listing of the mail queue on the standard output.
458438032Speter*/
458538032Speter
458638032Spetervoid
458738032Speterprintqueue()
458838032Speter{
458990792Sgshapiro	int i, k = 0, nrequests = 0;
459064562Sgshapiro
459190792Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
459277349Sgshapiro	{
459390792Sgshapiro		int j;
459490792Sgshapiro
459590792Sgshapiro		k++;
459690792Sgshapiro		for (j = 0; j < Queue[i]->qg_numqueues; j++)
459790792Sgshapiro		{
459890792Sgshapiro			if (StopRequest)
459990792Sgshapiro				stop_sendmail();
460090792Sgshapiro			nrequests += print_single_queue(i, j);
460190792Sgshapiro			k++;
460290792Sgshapiro		}
460377349Sgshapiro	}
460490792Sgshapiro	if (k > 1)
460590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
460690792Sgshapiro				     "\t\tTotal requests: %d\n",
460790792Sgshapiro				     nrequests);
460864562Sgshapiro}
460990792Sgshapiro/*
461064562Sgshapiro**  PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
461164562Sgshapiro**
461264562Sgshapiro**	Parameters:
461390792Sgshapiro**		qgrp -- the index of the queue group.
461490792Sgshapiro**		qdir -- the queue directory.
461564562Sgshapiro**
461664562Sgshapiro**	Returns:
461790792Sgshapiro**		number of requests in mail queue.
461864562Sgshapiro**
461964562Sgshapiro**	Side Effects:
462064562Sgshapiro**		Prints a listing of the mail queue on the standard output.
462164562Sgshapiro*/
462264562Sgshapiro
462390792Sgshapiroint
462490792Sgshapiroprint_single_queue(qgrp, qdir)
462590792Sgshapiro	int qgrp;
462690792Sgshapiro	int qdir;
462764562Sgshapiro{
462838032Speter	register WORK *w;
462990792Sgshapiro	SM_FILE_T *f;
463038032Speter	int nrequests;
463164562Sgshapiro	char qd[MAXPATHLEN];
463264562Sgshapiro	char qddf[MAXPATHLEN];
463338032Speter	char buf[MAXLINE];
463438032Speter
463590792Sgshapiro	if (qdir == NOQDIR)
463664562Sgshapiro	{
463790792Sgshapiro		(void) sm_strlcpy(qd, ".", sizeof qd);
463890792Sgshapiro		(void) sm_strlcpy(qddf, ".", sizeof qddf);
463964562Sgshapiro	}
464064562Sgshapiro	else
464164562Sgshapiro	{
464290792Sgshapiro		(void) sm_strlcpyn(qd, sizeof qd, 2,
464390792Sgshapiro			Queue[qgrp]->qg_qpaths[qdir].qp_name,
464490792Sgshapiro			(bitset(QP_SUBQF,
464590792Sgshapiro				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
464690792Sgshapiro					? "/qf" : ""));
464790792Sgshapiro		(void) sm_strlcpyn(qddf, sizeof qddf, 2,
464890792Sgshapiro			Queue[qgrp]->qg_qpaths[qdir].qp_name,
464990792Sgshapiro			(bitset(QP_SUBDF,
465090792Sgshapiro				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
465190792Sgshapiro					? "/df" : ""));
465264562Sgshapiro	}
465364562Sgshapiro
465438032Speter	/*
465538032Speter	**  Check for permission to print the queue
465638032Speter	*/
465738032Speter
465838032Speter	if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
465938032Speter	{
466038032Speter		struct stat st;
466190792Sgshapiro#ifdef NGROUPS_MAX
466238032Speter		int n;
466338032Speter		extern GIDSET_T InitialGidSet[NGROUPS_MAX];
466490792Sgshapiro#endif /* NGROUPS_MAX */
466538032Speter
466664562Sgshapiro		if (stat(qd, &st) < 0)
466738032Speter		{
466890792Sgshapiro			syserr("Cannot stat %s",
466990792Sgshapiro				qid_printqueue(qgrp, qdir));
467064562Sgshapiro			return 0;
467138032Speter		}
467290792Sgshapiro#ifdef NGROUPS_MAX
467338032Speter		n = NGROUPS_MAX;
467438032Speter		while (--n >= 0)
467538032Speter		{
467638032Speter			if (InitialGidSet[n] == st.st_gid)
467738032Speter				break;
467838032Speter		}
467938032Speter		if (n < 0 && RealGid != st.st_gid)
468090792Sgshapiro#else /* NGROUPS_MAX */
468138032Speter		if (RealGid != st.st_gid)
468290792Sgshapiro#endif /* NGROUPS_MAX */
468338032Speter		{
468438032Speter			usrerr("510 You are not permitted to see the queue");
468538032Speter			setstat(EX_NOPERM);
468664562Sgshapiro			return 0;
468738032Speter		}
468838032Speter	}
468938032Speter
469038032Speter	/*
469138032Speter	**  Read and order the queue.
469238032Speter	*/
469338032Speter
469490792Sgshapiro	nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
469590792Sgshapiro	(void) sortq(Queue[qgrp]->qg_maxlist);
469638032Speter
469738032Speter	/*
469838032Speter	**  Print the work list that we have read.
469938032Speter	*/
470038032Speter
470138032Speter	/* first see if there is anything */
470238032Speter	if (nrequests <= 0)
470338032Speter	{
470490792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
470590792Sgshapiro				     qid_printqueue(qgrp, qdir));
470664562Sgshapiro		return 0;
470738032Speter	}
470838032Speter
470990792Sgshapiro	sm_getla();	/* get load average */
471038032Speter
471190792Sgshapiro	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
471290792Sgshapiro			     qid_printqueue(qgrp, qdir),
471390792Sgshapiro			     nrequests, nrequests == 1 ? "" : "s");
471438032Speter	if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
471590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
471690792Sgshapiro				     ", only %d printed", MaxQueueRun);
471738032Speter	if (Verbose)
471890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
471990792Sgshapiro			")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
472038032Speter	else
472190792Sgshapiro		(void) sm_io_fprintf(smioout,  SM_TIME_DEFAULT,
472290792Sgshapiro			")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
472338032Speter	for (w = WorkQ; w != NULL; w = w->w_next)
472438032Speter	{
472538032Speter		struct stat st;
472638032Speter		auto time_t submittime = 0;
472738032Speter		long dfsize;
472838032Speter		int flags = 0;
472938032Speter		int qfver;
473090792Sgshapiro#if _FFR_QUARANTINE
473190792Sgshapiro		char quarmsg[MAXLINE];
473290792Sgshapiro#endif /* _FFR_QUARANTINE */
473338032Speter		char statmsg[MAXLINE];
473438032Speter		char bodytype[MAXNAME + 1];
473564562Sgshapiro		char qf[MAXPATHLEN];
473638032Speter
473777349Sgshapiro		if (StopRequest)
473877349Sgshapiro			stop_sendmail();
473977349Sgshapiro
474090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
474190792Sgshapiro				     w->w_name + 2);
474290792Sgshapiro		(void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", w->w_name);
474390792Sgshapiro		f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY,
474490792Sgshapiro			       NULL);
474538032Speter		if (f == NULL)
474638032Speter		{
474790792Sgshapiro			if (errno == EPERM)
474890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
474990792Sgshapiro						     " (permission denied)\n");
475090792Sgshapiro			else if (errno == ENOENT)
475190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
475290792Sgshapiro						     " (job completed)\n");
475390792Sgshapiro			else
475490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
475590792Sgshapiro						     " (%s)\n",
475690792Sgshapiro						     sm_errstring(errno));
475738032Speter			errno = 0;
475838032Speter			continue;
475938032Speter		}
476090792Sgshapiro		w->w_name[0] = DATAFL_LETTER;
476190792Sgshapiro		(void) sm_strlcpyn(qf, sizeof qf, 3, qddf, "/", w->w_name);
476264562Sgshapiro		if (stat(qf, &st) >= 0)
476338032Speter			dfsize = st.st_size;
476438032Speter		else
476590792Sgshapiro		{
476690792Sgshapiro			ENVELOPE e;
476790792Sgshapiro
476890792Sgshapiro			/*
476990792Sgshapiro			**  Maybe the df file can't be statted because
477090792Sgshapiro			**  it is in a different directory than the qf file.
477190792Sgshapiro			**  In order to find out, we must read the qf file.
477290792Sgshapiro			*/
477390792Sgshapiro
477490792Sgshapiro			newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
477590792Sgshapiro			e.e_id = w->w_name + 2;
477690792Sgshapiro			e.e_qgrp = qgrp;
477790792Sgshapiro			e.e_qdir = qdir;
477838032Speter			dfsize = -1;
477990792Sgshapiro			if (readqf(&e, false))
478090792Sgshapiro			{
478190792Sgshapiro				char *df = queuename(&e, DATAFL_LETTER);
478290792Sgshapiro				if (stat(df, &st) >= 0)
478390792Sgshapiro					dfsize = st.st_size;
478490792Sgshapiro			}
478590792Sgshapiro			if (e.e_lockfp != NULL)
478690792Sgshapiro			{
478790792Sgshapiro				(void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
478890792Sgshapiro				e.e_lockfp = NULL;
478990792Sgshapiro			}
479090792Sgshapiro			clearenvelope(&e, false, e.e_rpool);
479190792Sgshapiro			sm_rpool_free(e.e_rpool);
479290792Sgshapiro		}
479338032Speter		if (w->w_lock)
479490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
479590792Sgshapiro#if _FFR_QUARANTINE
479690792Sgshapiro		else if (QueueMode == QM_LOST)
479790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
479890792Sgshapiro#endif /* _FFR_QUARANTINE */
479938032Speter		else if (w->w_tooyoung)
480090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
480138032Speter		else if (shouldqueue(w->w_pri, w->w_ctime))
480290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
480338032Speter		else
480490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
480590792Sgshapiro
480638032Speter		errno = 0;
480738032Speter
480890792Sgshapiro#if _FFR_QUARANTINE
480990792Sgshapiro		quarmsg[0] = '\0';
481090792Sgshapiro#endif /* _FFR_QUARANTINE */
481138032Speter		statmsg[0] = bodytype[0] = '\0';
481238032Speter		qfver = 0;
481390792Sgshapiro		while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
481438032Speter		{
481538032Speter			register int i;
481638032Speter			register char *p;
481738032Speter
481877349Sgshapiro			if (StopRequest)
481977349Sgshapiro				stop_sendmail();
482077349Sgshapiro
482190792Sgshapiro			fixcrlf(buf, true);
482238032Speter			switch (buf[0])
482338032Speter			{
482438032Speter			  case 'V':	/* queue file version */
482538032Speter				qfver = atoi(&buf[1]);
482638032Speter				break;
482738032Speter
482838032Speter			  case 'M':	/* error message */
482938032Speter				if ((i = strlen(&buf[1])) >= sizeof statmsg)
483038032Speter					i = sizeof statmsg - 1;
483164562Sgshapiro				memmove(statmsg, &buf[1], i);
483238032Speter				statmsg[i] = '\0';
483338032Speter				break;
483438032Speter
483590792Sgshapiro#if _FFR_QUARANTINE
483690792Sgshapiro			  case 'q':	/* quarantine reason */
483790792Sgshapiro				if ((i = strlen(&buf[1])) >= sizeof quarmsg)
483890792Sgshapiro					i = sizeof quarmsg - 1;
483990792Sgshapiro				memmove(quarmsg, &buf[1], i);
484090792Sgshapiro				quarmsg[i] = '\0';
484190792Sgshapiro				break;
484290792Sgshapiro#endif /* _FFR_QUARANTINE */
484390792Sgshapiro
484438032Speter			  case 'B':	/* body type */
484538032Speter				if ((i = strlen(&buf[1])) >= sizeof bodytype)
484638032Speter					i = sizeof bodytype - 1;
484764562Sgshapiro				memmove(bodytype, &buf[1], i);
484838032Speter				bodytype[i] = '\0';
484938032Speter				break;
485038032Speter
485138032Speter			  case 'S':	/* sender name */
485238032Speter				if (Verbose)
485364562Sgshapiro				{
485490792Sgshapiro					(void) sm_io_fprintf(smioout,
485590792Sgshapiro						SM_TIME_DEFAULT,
485690792Sgshapiro						"%8ld %10ld%c%.12s ",
485790792Sgshapiro						dfsize,
485890792Sgshapiro						w->w_pri,
485990792Sgshapiro						bitset(EF_WARNING, flags)
486090792Sgshapiro							? '+' : ' ',
486190792Sgshapiro						ctime(&submittime) + 4);
486264562Sgshapiro					prtstr(&buf[1], 78);
486364562Sgshapiro				}
486438032Speter				else
486564562Sgshapiro				{
486690792Sgshapiro					(void) sm_io_fprintf(smioout,
486790792Sgshapiro						SM_TIME_DEFAULT,
486890792Sgshapiro						"%8ld %.16s ",
486990792Sgshapiro						dfsize,
487090792Sgshapiro						ctime(&submittime));
487190792Sgshapiro					prtstr(&buf[1], 39);
487264562Sgshapiro				}
487390792Sgshapiro#if _FFR_QUARANTINE
487490792Sgshapiro				if (quarmsg[0] != '\0')
487590792Sgshapiro				{
487690792Sgshapiro					(void) sm_io_fprintf(smioout,
487790792Sgshapiro							     SM_TIME_DEFAULT,
487890792Sgshapiro							     "\n     QUARANTINE: %.*s",
487990792Sgshapiro							     Verbose ? 100 : 60,
488090792Sgshapiro							     quarmsg);
488190792Sgshapiro					quarmsg[0] = '\0';
488290792Sgshapiro				}
488390792Sgshapiro#endif /* _FFR_QUARANTINE */
488438032Speter				if (statmsg[0] != '\0' || bodytype[0] != '\0')
488538032Speter				{
488690792Sgshapiro					(void) sm_io_fprintf(smioout,
488790792Sgshapiro						SM_TIME_DEFAULT,
488890792Sgshapiro						"\n    %10.10s",
488990792Sgshapiro						bodytype);
489038032Speter					if (statmsg[0] != '\0')
489190792Sgshapiro						(void) sm_io_fprintf(smioout,
489290792Sgshapiro							SM_TIME_DEFAULT,
489390792Sgshapiro							"   (%.*s)",
489490792Sgshapiro							Verbose ? 100 : 60,
489590792Sgshapiro							statmsg);
489690792Sgshapiro					statmsg[0] = '\0';
489738032Speter				}
489838032Speter				break;
489938032Speter
490038032Speter			  case 'C':	/* controlling user */
490138032Speter				if (Verbose)
490290792Sgshapiro					(void) sm_io_fprintf(smioout,
490390792Sgshapiro						SM_TIME_DEFAULT,
490490792Sgshapiro						"\n\t\t\t\t\t\t(---%.64s---)",
490590792Sgshapiro						&buf[1]);
490638032Speter				break;
490738032Speter
490838032Speter			  case 'R':	/* recipient name */
490938032Speter				p = &buf[1];
491038032Speter				if (qfver >= 1)
491138032Speter				{
491238032Speter					p = strchr(p, ':');
491338032Speter					if (p == NULL)
491438032Speter						break;
491538032Speter					p++;
491638032Speter				}
491738032Speter				if (Verbose)
491864562Sgshapiro				{
491990792Sgshapiro					(void) sm_io_fprintf(smioout,
492090792Sgshapiro							SM_TIME_DEFAULT,
492190792Sgshapiro							"\n\t\t\t\t\t\t");
492290792Sgshapiro					prtstr(p, 71);
492364562Sgshapiro				}
492438032Speter				else
492564562Sgshapiro				{
492690792Sgshapiro					(void) sm_io_fprintf(smioout,
492790792Sgshapiro							SM_TIME_DEFAULT,
492890792Sgshapiro							"\n\t\t\t\t\t ");
492990792Sgshapiro					prtstr(p, 38);
493064562Sgshapiro				}
493190792Sgshapiro				if (Verbose && statmsg[0] != '\0')
493290792Sgshapiro				{
493390792Sgshapiro					(void) sm_io_fprintf(smioout,
493490792Sgshapiro							SM_TIME_DEFAULT,
493590792Sgshapiro							"\n\t\t (%.100s)",
493690792Sgshapiro							statmsg);
493790792Sgshapiro					statmsg[0] = '\0';
493890792Sgshapiro				}
493938032Speter				break;
494038032Speter
494138032Speter			  case 'T':	/* creation time */
494238032Speter				submittime = atol(&buf[1]);
494338032Speter				break;
494438032Speter
494538032Speter			  case 'F':	/* flag bits */
494638032Speter				for (p = &buf[1]; *p != '\0'; p++)
494738032Speter				{
494838032Speter					switch (*p)
494938032Speter					{
495038032Speter					  case 'w':
495138032Speter						flags |= EF_WARNING;
495238032Speter						break;
495338032Speter					}
495438032Speter				}
495538032Speter			}
495638032Speter		}
495738032Speter		if (submittime == (time_t) 0)
495890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
495990792Sgshapiro					     " (no control file)");
496090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
496190792Sgshapiro		(void) sm_io_close(f, SM_TIME_DEFAULT);
496238032Speter	}
496364562Sgshapiro	return nrequests;
496438032Speter}
496590792Sgshapiro
496690792Sgshapiro#if _FFR_QUARANTINE
496790792Sgshapiro/*
496890792Sgshapiro**  QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
496990792Sgshapiro**
497090792Sgshapiro**	Parameters:
497190792Sgshapiro**		e -- envelope to build it in/from.
497290792Sgshapiro**		type -- the file type, used as the first character
497390792Sgshapiro**			of the file name.
497490792Sgshapiro**
497590792Sgshapiro**	Returns:
497690792Sgshapiro**		the letter to use
497790792Sgshapiro*/
497890792Sgshapiro
497990792Sgshapirostatic char
498090792Sgshapiroqueue_letter(e, type)
498190792Sgshapiro	ENVELOPE *e;
498290792Sgshapiro	int type;
498390792Sgshapiro{
498490792Sgshapiro	/* Change type according to QueueMode */
498590792Sgshapiro	if (type == ANYQFL_LETTER)
498690792Sgshapiro	{
498790792Sgshapiro		if (e->e_quarmsg != NULL)
498890792Sgshapiro			type = QUARQF_LETTER;
498990792Sgshapiro		else
499090792Sgshapiro		{
499190792Sgshapiro			switch (QueueMode)
499290792Sgshapiro			{
499390792Sgshapiro			  case QM_NORMAL:
499490792Sgshapiro				type = NORMQF_LETTER;
499590792Sgshapiro				break;
499690792Sgshapiro
499790792Sgshapiro			  case QM_QUARANTINE:
499890792Sgshapiro				type = QUARQF_LETTER;
499990792Sgshapiro				break;
500090792Sgshapiro
500190792Sgshapiro			  case QM_LOST:
500290792Sgshapiro				type = LOSEQF_LETTER;
500390792Sgshapiro				break;
500490792Sgshapiro
500590792Sgshapiro			  default:
500690792Sgshapiro				/* should never happen */
500790792Sgshapiro				abort();
500890792Sgshapiro				/* NOTREACHED */
500990792Sgshapiro			}
501090792Sgshapiro		}
501190792Sgshapiro	}
501290792Sgshapiro	return type;
501390792Sgshapiro}
501490792Sgshapiro#endif /* _FFR_QUARANTINE */
501590792Sgshapiro
501690792Sgshapiro/*
501738032Speter**  QUEUENAME -- build a file name in the queue directory for this envelope.
501838032Speter**
501938032Speter**	Parameters:
502038032Speter**		e -- envelope to build it in/from.
502138032Speter**		type -- the file type, used as the first character
502238032Speter**			of the file name.
502338032Speter**
502438032Speter**	Returns:
502564562Sgshapiro**		a pointer to the queue name (in a static buffer).
502638032Speter**
502738032Speter**	Side Effects:
502864562Sgshapiro**		If no id code is already assigned, queuename() will
502964562Sgshapiro**		assign an id code with assign_queueid().  If no queue
503064562Sgshapiro**		directory is assigned, one will be set with setnewqueue().
503138032Speter*/
503238032Speter
503338032Speterchar *
503438032Speterqueuename(e, type)
503538032Speter	register ENVELOPE *e;
503638032Speter	int type;
503738032Speter{
503890792Sgshapiro	int qd, qg;
503990792Sgshapiro	char *sub = "/";
504090792Sgshapiro	char pref[3];
504164562Sgshapiro	static char buf[MAXPATHLEN];
504238032Speter
504364562Sgshapiro	/* Assign an ID if needed */
504438032Speter	if (e->e_id == NULL)
504564562Sgshapiro		assign_queueid(e);
504664562Sgshapiro
504790792Sgshapiro#if _FFR_QUARANTINE
504890792Sgshapiro	type = queue_letter(e, type);
504990792Sgshapiro#endif /* _FFR_QUARANTINE */
505064562Sgshapiro
505190792Sgshapiro	/* begin of filename */
505290792Sgshapiro	pref[0] = (char) type;
505390792Sgshapiro	pref[1] = 'f';
505490792Sgshapiro	pref[2] = '\0';
505590792Sgshapiro
505690792Sgshapiro	/* Assign a queue group/directory if needed */
505790792Sgshapiro	if (type == XSCRPT_LETTER)
505890792Sgshapiro	{
505990792Sgshapiro		/*
506090792Sgshapiro		**  We don't want to call setnewqueue() if we are fetching
506190792Sgshapiro		**  the pathname of the transcript file, because setnewqueue
506290792Sgshapiro		**  chooses a queue, and sometimes we need to write to the
506390792Sgshapiro		**  transcript file before we have gathered enough information
506490792Sgshapiro		**  to choose a queue.
506590792Sgshapiro		*/
506690792Sgshapiro
506790792Sgshapiro		if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
506890792Sgshapiro		{
506990792Sgshapiro			if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
507090792Sgshapiro			{
507190792Sgshapiro				e->e_xfqgrp = e->e_qgrp;
507290792Sgshapiro				e->e_xfqdir = e->e_qdir;
507390792Sgshapiro			}
507490792Sgshapiro			else
507590792Sgshapiro			{
507690792Sgshapiro				e->e_xfqgrp = 0;
507790792Sgshapiro				if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
507890792Sgshapiro					e->e_xfqdir = 0;
507990792Sgshapiro				else
508090792Sgshapiro				{
508190792Sgshapiro					e->e_xfqdir = get_rand_mod(
508290792Sgshapiro					      Queue[e->e_xfqgrp]->qg_numqueues);
508390792Sgshapiro				}
508490792Sgshapiro			}
508590792Sgshapiro		}
508690792Sgshapiro		qd = e->e_xfqdir;
508790792Sgshapiro		qg = e->e_xfqgrp;
508890792Sgshapiro	}
508964562Sgshapiro	else
509038032Speter	{
509190792Sgshapiro		if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
509290792Sgshapiro			setnewqueue(e);
509390792Sgshapiro		if (type ==  DATAFL_LETTER)
509490792Sgshapiro		{
509590792Sgshapiro			qd = e->e_dfqdir;
509690792Sgshapiro			qg = e->e_dfqgrp;
509790792Sgshapiro		}
509890792Sgshapiro		else
509990792Sgshapiro		{
510090792Sgshapiro			qd = e->e_qdir;
510190792Sgshapiro			qg = e->e_qgrp;
510290792Sgshapiro		}
510390792Sgshapiro	}
510490792Sgshapiro
510594334Sgshapiro	/* xf files always have a valid qd and qg picked above */
510694334Sgshapiro	if (e->e_qdir == NOQDIR && type != XSCRPT_LETTER)
510790792Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id);
510890792Sgshapiro	else
510990792Sgshapiro	{
511064562Sgshapiro		switch (type)
511164562Sgshapiro		{
511290792Sgshapiro		  case DATAFL_LETTER:
511390792Sgshapiro			if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
511490792Sgshapiro				sub = "/df/";
511564562Sgshapiro			break;
511638032Speter
511790792Sgshapiro#if _FFR_QUARANTINE
511890792Sgshapiro		  case QUARQF_LETTER:
511990792Sgshapiro#endif /* _FFR_QUARANTINE */
512071345Sgshapiro		  case TEMPQF_LETTER:
512190792Sgshapiro		  case NEWQFL_LETTER:
512271345Sgshapiro		  case LOSEQF_LETTER:
512390792Sgshapiro		  case NORMQF_LETTER:
512490792Sgshapiro			if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
512590792Sgshapiro				sub = "/qf/";
512664562Sgshapiro			break;
512764562Sgshapiro
512890792Sgshapiro		  case XSCRPT_LETTER:
512990792Sgshapiro			if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
513090792Sgshapiro				sub = "/xf/";
513164562Sgshapiro			break;
513290792Sgshapiro
513390792Sgshapiro		  default:
513490792Sgshapiro			sm_abort("queuename: bad queue file type %d", type);
513538032Speter		}
513638032Speter
513790792Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 4,
513890792Sgshapiro				Queue[qg]->qg_qpaths[qd].qp_name,
513990792Sgshapiro				sub, pref, e->e_id);
514064562Sgshapiro	}
514138032Speter
514264562Sgshapiro	if (tTd(7, 2))
514390792Sgshapiro		sm_dprintf("queuename: %s\n", buf);
514464562Sgshapiro	return buf;
514564562Sgshapiro}
514690792Sgshapiro/*
514764562Sgshapiro**  ASSIGN_QUEUEID -- assign a queue ID for this envelope.
514864562Sgshapiro**
514964562Sgshapiro**	Assigns an id code if one does not already exist.
515064562Sgshapiro**	This code assumes that nothing will remain in the queue for
515164562Sgshapiro**	longer than 60 years.  It is critical that files with the given
515290792Sgshapiro**	name do not already exist in the queue.
515390792Sgshapiro**	[No longer initializes e_qdir to NOQDIR.]
515464562Sgshapiro**
515564562Sgshapiro**	Parameters:
515664562Sgshapiro**		e -- envelope to set it in.
515764562Sgshapiro**
515864562Sgshapiro**	Returns:
515964562Sgshapiro**		none.
516064562Sgshapiro*/
516138032Speter
516277349Sgshapirostatic const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx";
516377349Sgshapiro# define QIC_LEN	60
516490792Sgshapiro# define queuenextid() CurrentPid
516538032Speter
516690792Sgshapiro
516764562Sgshapirovoid
516864562Sgshapiroassign_queueid(e)
516964562Sgshapiro	register ENVELOPE *e;
517064562Sgshapiro{
517190792Sgshapiro	pid_t pid = queuenextid();
517290792Sgshapiro	static int cX = 0;
517364562Sgshapiro	static long random_offset;
517464562Sgshapiro	struct tm *tm;
517564562Sgshapiro	char idbuf[MAXQFNAME - 2];
517690792Sgshapiro	int seq;
517738032Speter
517864562Sgshapiro	if (e->e_id != NULL)
517964562Sgshapiro		return;
518038032Speter
518164562Sgshapiro	/* see if we need to get a new base time/pid */
518290792Sgshapiro	if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 ||
518390792Sgshapiro	    LastQueuePid != pid)
518464562Sgshapiro	{
518564562Sgshapiro		time_t then = LastQueueTime;
518664562Sgshapiro
518764562Sgshapiro		/* if the first time through, pick a random offset */
518864562Sgshapiro		if (LastQueueTime == 0)
518964562Sgshapiro			random_offset = get_random();
519064562Sgshapiro
519164562Sgshapiro		while ((LastQueueTime = curtime()) == then &&
519264562Sgshapiro		       LastQueuePid == pid)
519338032Speter		{
519464562Sgshapiro			(void) sleep(1);
519538032Speter		}
519690792Sgshapiro		LastQueuePid = queuenextid();
519764562Sgshapiro		cX = 0;
519838032Speter	}
519990792Sgshapiro
520090792Sgshapiro	/*
520190792Sgshapiro	**  Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1.
520290792Sgshapiro	**  This lets us generate up to QIC_LEN*QIC_LEN unique queue ids
520390792Sgshapiro	**  per second, per process.  With envelope splitting,
520490792Sgshapiro	**  a single message can consume many queue ids.
520590792Sgshapiro	*/
520690792Sgshapiro
520790792Sgshapiro	seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN));
520890792Sgshapiro	++cX;
520964562Sgshapiro	if (tTd(7, 50))
521090792Sgshapiro		sm_dprintf("assign_queueid: random_offset = %ld (%d)\n",
521190792Sgshapiro			random_offset, seq);
521238032Speter
521364562Sgshapiro	tm = gmtime(&LastQueueTime);
521477349Sgshapiro	idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
521577349Sgshapiro	idbuf[1] = QueueIdChars[tm->tm_mon];
521677349Sgshapiro	idbuf[2] = QueueIdChars[tm->tm_mday];
521777349Sgshapiro	idbuf[3] = QueueIdChars[tm->tm_hour];
521877349Sgshapiro	idbuf[4] = QueueIdChars[tm->tm_min];
521977349Sgshapiro	idbuf[5] = QueueIdChars[tm->tm_sec];
522090792Sgshapiro	idbuf[6] = QueueIdChars[seq / QIC_LEN];
522190792Sgshapiro	idbuf[7] = QueueIdChars[seq % QIC_LEN];
522290792Sgshapiro	(void) sm_snprintf(&idbuf[8], sizeof idbuf - 8, "%06d",
522390792Sgshapiro			   (int) LastQueuePid);
522490792Sgshapiro	e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
522590792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
522690792Sgshapiro#if 0
522790792Sgshapiro	/* XXX: inherited from MainEnvelope */
522890792Sgshapiro	e->e_qgrp = NOQGRP;  /* too early to do anything else */
522990792Sgshapiro	e->e_qdir = NOQDIR;
523090792Sgshapiro	e->e_xfqgrp = NOQGRP;
523190792Sgshapiro#endif /* 0 */
523290792Sgshapiro#if _FFR_QUARANTINE
523390792Sgshapiro	/* New ID means it's not on disk yet */
523490792Sgshapiro	e->e_qfletter = '\0';
523590792Sgshapiro#endif /* _FFR_QUARANTINE */
523664562Sgshapiro	if (tTd(7, 1))
523790792Sgshapiro		sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
523890792Sgshapiro			e->e_id, e);
523964562Sgshapiro	if (LogLevel > 93)
524064562Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
524138032Speter}
524290792Sgshapiro/*
524364562Sgshapiro**  SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
524464562Sgshapiro**
524564562Sgshapiro**	Make sure one PID can't be used by two processes in any one second.
524664562Sgshapiro**
524764562Sgshapiro**		If the system rotates PIDs fast enough, may get the
524864562Sgshapiro**		same pid in the same second for two distinct processes.
524964562Sgshapiro**		This will interfere with the queue file naming system.
525064562Sgshapiro**
525164562Sgshapiro**	Parameters:
525264562Sgshapiro**		none
525364562Sgshapiro**
525464562Sgshapiro**	Returns:
525564562Sgshapiro**		none
525664562Sgshapiro*/
525790792Sgshapiro
525864562Sgshapirovoid
525964562Sgshapirosync_queue_time()
526064562Sgshapiro{
526190792Sgshapiro#if FAST_PID_RECYCLE
526264562Sgshapiro	if (OpMode != MD_TEST &&
526364562Sgshapiro	    OpMode != MD_VERIFY &&
526464562Sgshapiro	    LastQueueTime > 0 &&
526590792Sgshapiro	    LastQueuePid == CurrentPid &&
526664562Sgshapiro	    curtime() == LastQueueTime)
526764562Sgshapiro		(void) sleep(1);
526890792Sgshapiro#endif /* FAST_PID_RECYCLE */
526964562Sgshapiro}
527090792Sgshapiro/*
527138032Speter**  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
527238032Speter**
527338032Speter**	Parameters:
527438032Speter**		e -- the envelope to unlock.
527538032Speter**
527638032Speter**	Returns:
527738032Speter**		none
527838032Speter**
527938032Speter**	Side Effects:
528038032Speter**		unlocks the queue for `e'.
528138032Speter*/
528238032Speter
528338032Spetervoid
528438032Speterunlockqueue(e)
528538032Speter	ENVELOPE *e;
528638032Speter{
528738032Speter	if (tTd(51, 4))
528890792Sgshapiro		sm_dprintf("unlockqueue(%s)\n",
528938032Speter			e->e_id == NULL ? "NOQUEUE" : e->e_id);
529038032Speter
529164562Sgshapiro
529238032Speter	/* if there is a lock file in the envelope, close it */
529338032Speter	if (e->e_lockfp != NULL)
529490792Sgshapiro		(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
529538032Speter	e->e_lockfp = NULL;
529638032Speter
529738032Speter	/* don't create a queue id if we don't already have one */
529838032Speter	if (e->e_id == NULL)
529938032Speter		return;
530038032Speter
530138032Speter	/* remove the transcript */
530238032Speter	if (LogLevel > 87)
530338032Speter		sm_syslog(LOG_DEBUG, e->e_id, "unlock");
530438032Speter	if (!tTd(51, 104))
530590792Sgshapiro		(void) xunlink(queuename(e, XSCRPT_LETTER));
530638032Speter}
530790792Sgshapiro/*
530838032Speter**  SETCTLUSER -- create a controlling address
530938032Speter**
531038032Speter**	Create a fake "address" given only a local login name; this is
531138032Speter**	used as a "controlling user" for future recipient addresses.
531238032Speter**
531338032Speter**	Parameters:
531438032Speter**		user -- the user name of the controlling user.
531590792Sgshapiro**		qfver -- the version stamp of this queue file.
531690792Sgshapiro**		e -- envelope
531738032Speter**
531838032Speter**	Returns:
531990792Sgshapiro**		An address descriptor for the controlling user,
532090792Sgshapiro**		using storage allocated from e->e_rpool.
532138032Speter**
532238032Speter*/
532338032Speter
532464562Sgshapirostatic ADDRESS *
532590792Sgshapirosetctluser(user, qfver, e)
532638032Speter	char *user;
532738032Speter	int qfver;
532890792Sgshapiro	ENVELOPE *e;
532938032Speter{
533038032Speter	register ADDRESS *a;
533138032Speter	struct passwd *pw;
533238032Speter	char *p;
533338032Speter
533438032Speter	/*
533538032Speter	**  See if this clears our concept of controlling user.
533638032Speter	*/
533738032Speter
533838032Speter	if (user == NULL || *user == '\0')
533938032Speter		return NULL;
534038032Speter
534138032Speter	/*
534238032Speter	**  Set up addr fields for controlling user.
534338032Speter	*/
534438032Speter
534590792Sgshapiro	a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);
534664562Sgshapiro	memset((char *) a, '\0', sizeof *a);
534738032Speter
534890792Sgshapiro	if (*user == ':')
534938032Speter	{
535038032Speter		p = &user[1];
535190792Sgshapiro		a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
535238032Speter	}
535338032Speter	else
535438032Speter	{
535538032Speter		p = strtok(user, ":");
535690792Sgshapiro		a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
535738032Speter		if (qfver >= 2)
535838032Speter		{
535938032Speter			if ((p = strtok(NULL, ":")) != NULL)
536038032Speter				a->q_uid = atoi(p);
536138032Speter			if ((p = strtok(NULL, ":")) != NULL)
536238032Speter				a->q_gid = atoi(p);
536338032Speter			if ((p = strtok(NULL, ":")) != NULL)
536480785Sgshapiro			{
536580785Sgshapiro				char *o;
536680785Sgshapiro
536738032Speter				a->q_flags |= QGOODUID;
536880785Sgshapiro
536980785Sgshapiro				/* if there is another ':': restore it */
537080785Sgshapiro				if ((o = strtok(NULL, ":")) != NULL && o > p)
537180785Sgshapiro					o[-1] = ':';
537280785Sgshapiro			}
537338032Speter		}
537438032Speter		else if ((pw = sm_getpwnam(user)) != NULL)
537538032Speter		{
537666494Sgshapiro			if (*pw->pw_dir == '\0')
537766494Sgshapiro				a->q_home = NULL;
537866494Sgshapiro			else if (strcmp(pw->pw_dir, "/") == 0)
537938032Speter				a->q_home = "";
538038032Speter			else
538190792Sgshapiro				a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
538238032Speter			a->q_uid = pw->pw_uid;
538338032Speter			a->q_gid = pw->pw_gid;
538438032Speter			a->q_flags |= QGOODUID;
538538032Speter		}
538638032Speter	}
538738032Speter
538864562Sgshapiro	a->q_flags |= QPRIMARY;		/* flag as a "ctladdr" */
538938032Speter	a->q_mailer = LocalMailer;
539038032Speter	if (p == NULL)
539190792Sgshapiro		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
539238032Speter	else
539390792Sgshapiro		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
539438032Speter	return a;
539538032Speter}
539690792Sgshapiro/*
539790792Sgshapiro**  LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
539838032Speter**
539938032Speter**	Parameters:
540038032Speter**		e -- the envelope (e->e_id will be used).
540138032Speter**		why -- reported to whomever can hear.
540238032Speter**
540338032Speter**	Returns:
540438032Speter**		none.
540538032Speter*/
540638032Speter
540738032Spetervoid
540838032Speterloseqfile(e, why)
540938032Speter	register ENVELOPE *e;
541038032Speter	char *why;
541138032Speter{
541290792Sgshapiro	bool loseit = true;
541338032Speter	char *p;
541464562Sgshapiro	char buf[MAXPATHLEN];
541538032Speter
541638032Speter	if (e == NULL || e->e_id == NULL)
541738032Speter		return;
541890792Sgshapiro	p = queuename(e, ANYQFL_LETTER);
541990792Sgshapiro	if (sm_strlcpy(buf, p, sizeof buf) >= sizeof buf)
542038032Speter		return;
542190792Sgshapiro	if (!bitset(EF_INQUEUE, e->e_flags))
542290792Sgshapiro		queueup(e, false, true);
542390792Sgshapiro#if _FFR_QUARANTINE
542490792Sgshapiro	else if (QueueMode == QM_LOST)
542590792Sgshapiro		loseit = false;
542690792Sgshapiro#endif /* _FFR_QUARANTINE */
542790792Sgshapiro
542890792Sgshapiro	/* if already lost, no need to re-lose */
542990792Sgshapiro	if (loseit)
543090792Sgshapiro	{
543190792Sgshapiro		p = queuename(e, LOSEQF_LETTER);
543290792Sgshapiro		if (rename(buf, p) < 0)
543390792Sgshapiro			syserr("cannot rename(%s, %s), uid=%d",
543498121Sgshapiro			       buf, p, (int) geteuid());
543590792Sgshapiro		else if (LogLevel > 0)
543690792Sgshapiro			sm_syslog(LOG_ALERT, e->e_id,
543790792Sgshapiro				  "Losing %s: %s", buf, why);
543890792Sgshapiro	}
543990792Sgshapiro	if (e->e_dfp != NULL)
544090792Sgshapiro	{
544190792Sgshapiro		(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
544290792Sgshapiro		e->e_dfp = NULL;
544390792Sgshapiro	}
544490792Sgshapiro	e->e_flags &= ~EF_HAS_DF;
544538032Speter}
544690792Sgshapiro/*
544790792Sgshapiro**  NAME2QID -- translate a queue group name to a queue group id
544890792Sgshapiro**
544990792Sgshapiro**	Parameters:
545090792Sgshapiro**		queuename -- name of queue group.
545190792Sgshapiro**
545290792Sgshapiro**	Returns:
545390792Sgshapiro**		queue group id if found.
545490792Sgshapiro**		NOQGRP otherwise.
545590792Sgshapiro*/
545690792Sgshapiro
545790792Sgshapiroint
545890792Sgshapironame2qid(queuename)
545990792Sgshapiro	char *queuename;
546090792Sgshapiro{
546190792Sgshapiro	register STAB *s;
546290792Sgshapiro
546390792Sgshapiro	s = stab(queuename, ST_QUEUE, ST_FIND);
546490792Sgshapiro	if (s == NULL)
546590792Sgshapiro		return NOQGRP;
546690792Sgshapiro	return s->s_quegrp->qg_index;
546790792Sgshapiro}
546890792Sgshapiro/*
546964562Sgshapiro**  QID_PRINTNAME -- create externally printable version of queue id
547064562Sgshapiro**
547164562Sgshapiro**	Parameters:
547264562Sgshapiro**		e -- the envelope.
547364562Sgshapiro**
547464562Sgshapiro**	Returns:
547564562Sgshapiro**		a printable version
547664562Sgshapiro*/
547764562Sgshapiro
547864562Sgshapirochar *
547964562Sgshapiroqid_printname(e)
548064562Sgshapiro	ENVELOPE *e;
548164562Sgshapiro{
548264562Sgshapiro	char *id;
548364562Sgshapiro	static char idbuf[MAXQFNAME + 34];
548464562Sgshapiro
548564562Sgshapiro	if (e == NULL)
548664562Sgshapiro		return "";
548764562Sgshapiro
548864562Sgshapiro	if (e->e_id == NULL)
548964562Sgshapiro		id = "";
549064562Sgshapiro	else
549164562Sgshapiro		id = e->e_id;
549264562Sgshapiro
549390792Sgshapiro	if (e->e_qdir == NOQDIR)
549464562Sgshapiro		return id;
549564562Sgshapiro
549690792Sgshapiro	(void) sm_snprintf(idbuf, sizeof idbuf, "%.32s/%s",
549790792Sgshapiro			   Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
549890792Sgshapiro			   id);
549964562Sgshapiro	return idbuf;
550064562Sgshapiro}
550190792Sgshapiro/*
550290792Sgshapiro**  QID_PRINTQUEUE -- create full version of queue directory for data files
550364562Sgshapiro**
550464562Sgshapiro**	Parameters:
550590792Sgshapiro**		qgrp -- index in queue group.
550690792Sgshapiro**		qdir -- the short version of the queue directory
550764562Sgshapiro**
550864562Sgshapiro**	Returns:
550990792Sgshapiro**		the full pathname to the queue (might point to a static var)
551064562Sgshapiro*/
551164562Sgshapiro
551264562Sgshapirochar *
551390792Sgshapiroqid_printqueue(qgrp, qdir)
551490792Sgshapiro	int qgrp;
551590792Sgshapiro	int qdir;
551664562Sgshapiro{
551764562Sgshapiro	char *subdir;
551864562Sgshapiro	static char dir[MAXPATHLEN];
551964562Sgshapiro
552090792Sgshapiro	if (qdir == NOQDIR)
552190792Sgshapiro		return Queue[qgrp]->qg_qdir;
552264562Sgshapiro
552390792Sgshapiro	if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
552464562Sgshapiro		subdir = NULL;
552564562Sgshapiro	else
552690792Sgshapiro		subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
552764562Sgshapiro
552890792Sgshapiro	(void) sm_strlcpyn(dir, sizeof dir, 4,
552990792Sgshapiro			Queue[qgrp]->qg_qdir,
553064562Sgshapiro			subdir == NULL ? "" : "/",
553164562Sgshapiro			subdir == NULL ? "" : subdir,
553290792Sgshapiro			(bitset(QP_SUBDF,
553390792Sgshapiro				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
553490792Sgshapiro					? "/df" : ""));
553564562Sgshapiro	return dir;
553664562Sgshapiro}
553790792Sgshapiro
553890792Sgshapiro/*
553990792Sgshapiro**  PICKQDIR -- Pick a queue directory from a queue group
554064562Sgshapiro**
554190792Sgshapiro**	Parameters:
554290792Sgshapiro**		qg -- queue group
554390792Sgshapiro**		fsize -- file size in bytes
554490792Sgshapiro**		e -- envelope, or NULL
554564562Sgshapiro**
554690792Sgshapiro**	Result:
554790792Sgshapiro**		NOQDIR if no queue directory in qg has enough free space to
554890792Sgshapiro**		hold a file of size 'fsize', otherwise the index of
554990792Sgshapiro**		a randomly selected queue directory which resides on a
555090792Sgshapiro**		file system with enough disk space.
555190792Sgshapiro**		XXX This could be extended to select a queuedir with
555290792Sgshapiro**			a few (the fewest?) number of entries. That data
555390792Sgshapiro**			is available if shared memory is used.
555464562Sgshapiro**
555590792Sgshapiro**	Side Effects:
555690792Sgshapiro**		If the request fails and e != NULL then sm_syslog is called.
555790792Sgshapiro*/
555890792Sgshapiro
555990792Sgshapiroint
556090792Sgshapiropickqdir(qg, fsize, e)
556190792Sgshapiro	QUEUEGRP *qg;
556290792Sgshapiro	long fsize;
556390792Sgshapiro	ENVELOPE *e;
556490792Sgshapiro{
556590792Sgshapiro	int qdir;
556690792Sgshapiro	int i;
556790792Sgshapiro	long avail = 0;
556890792Sgshapiro
556990792Sgshapiro	/* Pick a random directory, as a starting point. */
557090792Sgshapiro	if (qg->qg_numqueues <= 1)
557190792Sgshapiro		qdir = 0;
557290792Sgshapiro	else
557390792Sgshapiro		qdir = get_rand_mod(qg->qg_numqueues);
557490792Sgshapiro
557590792Sgshapiro	if (MinBlocksFree <= 0 && fsize <= 0)
557690792Sgshapiro		return qdir;
557790792Sgshapiro
557890792Sgshapiro	/*
557990792Sgshapiro	**  Now iterate over the queue directories,
558090792Sgshapiro	**  looking for a directory with enough space for this message.
558190792Sgshapiro	*/
558290792Sgshapiro
558390792Sgshapiro	i = qdir;
558490792Sgshapiro	do
558590792Sgshapiro	{
558690792Sgshapiro		QPATHS *qp = &qg->qg_qpaths[i];
558790792Sgshapiro		long needed = 0;
558890792Sgshapiro		long fsavail = 0;
558990792Sgshapiro
559090792Sgshapiro		if (fsize > 0)
559190792Sgshapiro			needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
559290792Sgshapiro				  + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
559390792Sgshapiro				      > 0) ? 1 : 0);
559490792Sgshapiro		if (MinBlocksFree > 0)
559590792Sgshapiro			needed += MinBlocksFree;
559690792Sgshapiro		fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
559790792Sgshapiro#if SM_CONF_SHM
559890792Sgshapiro		if (fsavail <= 0)
559990792Sgshapiro		{
560090792Sgshapiro			long blksize;
560190792Sgshapiro
560290792Sgshapiro			/*
560390792Sgshapiro			**  might be not correctly updated,
560490792Sgshapiro			**  let's try to get the info directly.
560590792Sgshapiro			*/
560690792Sgshapiro
560790792Sgshapiro			fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
560890792Sgshapiro						&blksize);
560990792Sgshapiro			if (fsavail < 0)
561090792Sgshapiro				fsavail = 0;
561190792Sgshapiro		}
561290792Sgshapiro#endif /* SM_CONF_SHM */
561390792Sgshapiro		if (needed <= fsavail)
561490792Sgshapiro			return i;
561590792Sgshapiro		if (avail < fsavail)
561690792Sgshapiro			avail = fsavail;
561790792Sgshapiro
561890792Sgshapiro		if (qg->qg_numqueues > 0)
561990792Sgshapiro			i = (i + 1) % qg->qg_numqueues;
562090792Sgshapiro	} while (i != qdir);
562190792Sgshapiro
562290792Sgshapiro	if (e != NULL && LogLevel > 0)
562390792Sgshapiro		sm_syslog(LOG_ALERT, e->e_id,
562490792Sgshapiro			"low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
562590792Sgshapiro			CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
562690792Sgshapiro			fsize, MinBlocksFree,
562790792Sgshapiro			qg->qg_qdir, avail);
562890792Sgshapiro	return NOQDIR;
562990792Sgshapiro}
563090792Sgshapiro/*
563190792Sgshapiro**  SETNEWQUEUE -- Sets a new queue group and directory
563290792Sgshapiro**
563390792Sgshapiro**	Assign a queue group and directory to an envelope and store the
563490792Sgshapiro**	directory in e->e_qdir.
563590792Sgshapiro**
563664562Sgshapiro**	Parameters:
563764562Sgshapiro**		e -- envelope to assign a queue for.
563864562Sgshapiro**
563964562Sgshapiro**	Returns:
564090792Sgshapiro**		true if successful
564190792Sgshapiro**		false otherwise
564290792Sgshapiro**
564390792Sgshapiro**	Side Effects:
564490792Sgshapiro**		On success, e->e_qgrp and e->e_qdir are non-negative.
564590792Sgshapiro**		On failure (not enough disk space),
564690792Sgshapiro**		e->qgrp = NOQGRP, e->e_qdir = NOQDIR
564790792Sgshapiro**		and usrerr() is invoked (which could raise an exception).
564864562Sgshapiro*/
564964562Sgshapiro
565090792Sgshapirobool
565164562Sgshapirosetnewqueue(e)
565264562Sgshapiro	ENVELOPE *e;
565364562Sgshapiro{
565464562Sgshapiro	if (tTd(41, 20))
565590792Sgshapiro		sm_dprintf("setnewqueue: called\n");
565664562Sgshapiro
565790792Sgshapiro	/* not set somewhere else */
565890792Sgshapiro	if (e->e_qgrp == NOQGRP)
565964562Sgshapiro	{
5660102528Sgshapiro		ADDRESS *q;
5661102528Sgshapiro
566290792Sgshapiro		/*
5663102528Sgshapiro		**  Use the queue group of the "first" recipient, as set by
566490792Sgshapiro		**  the "queuegroup" rule set.  If that is not defined, then
566590792Sgshapiro		**  use the queue group of the mailer of the first recipient.
566690792Sgshapiro		**  If that is not defined either, then use the default
566790792Sgshapiro		**  queue group.
5668102528Sgshapiro		**  Notice: "first" depends on the sorting of sendqueue
5669102528Sgshapiro		**  in recipient().
5670102528Sgshapiro		**  To avoid problems with "bad" recipients look
5671102528Sgshapiro		**  for a valid address first.
567290792Sgshapiro		*/
567390792Sgshapiro
5674102528Sgshapiro		q = e->e_sendqueue;
5675102528Sgshapiro		while (q != NULL &&
5676102528Sgshapiro		       (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state)))
5677102528Sgshapiro		{
5678102528Sgshapiro			q = q->q_next;
5679102528Sgshapiro		}
5680102528Sgshapiro		if (q == NULL)
568190792Sgshapiro			e->e_qgrp = 0;
5682102528Sgshapiro		else if (q->q_qgrp >= 0)
5683102528Sgshapiro			e->e_qgrp = q->q_qgrp;
5684102528Sgshapiro		else if (q->q_mailer != NULL &&
5685102528Sgshapiro			 ISVALIDQGRP(q->q_mailer->m_qgrp))
5686102528Sgshapiro			e->e_qgrp = q->q_mailer->m_qgrp;
568790792Sgshapiro		else
568890792Sgshapiro			e->e_qgrp = 0;
568990792Sgshapiro		e->e_dfqgrp = e->e_qgrp;
569090792Sgshapiro	}
569190792Sgshapiro
569290792Sgshapiro	if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
569390792Sgshapiro	{
569464562Sgshapiro		if (tTd(41, 20))
569590792Sgshapiro			sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
569690792Sgshapiro				qid_printqueue(e->e_qgrp, e->e_qdir));
569790792Sgshapiro		return true;
569864562Sgshapiro	}
569964562Sgshapiro
570090792Sgshapiro	filesys_update();
570190792Sgshapiro	e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
570290792Sgshapiro	if (e->e_qdir == NOQDIR)
570364562Sgshapiro	{
570490792Sgshapiro		e->e_qgrp = NOQGRP;
570590792Sgshapiro		if (!bitset(EF_FATALERRS, e->e_flags))
570690792Sgshapiro			usrerr("452 4.4.5 Insufficient disk space; try again later");
570790792Sgshapiro		e->e_flags |= EF_FATALERRS;
570890792Sgshapiro		return false;
570964562Sgshapiro	}
571064562Sgshapiro
571164562Sgshapiro	if (tTd(41, 3))
571290792Sgshapiro		sm_dprintf("setnewqueue: Assigned queue directory %s\n",
571390792Sgshapiro			qid_printqueue(e->e_qgrp, e->e_qdir));
571490792Sgshapiro
571590792Sgshapiro	if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
571690792Sgshapiro	{
571790792Sgshapiro		e->e_xfqgrp = e->e_qgrp;
571890792Sgshapiro		e->e_xfqdir = e->e_qdir;
571990792Sgshapiro	}
572090792Sgshapiro	e->e_dfqdir = e->e_qdir;
572190792Sgshapiro	return true;
572264562Sgshapiro}
572390792Sgshapiro/*
572464562Sgshapiro**  CHKQDIR -- check a queue directory
572564562Sgshapiro**
572664562Sgshapiro**	Parameters:
572764562Sgshapiro**		name -- name of queue directory
572864562Sgshapiro**		sff -- flags for safefile()
572964562Sgshapiro**
573064562Sgshapiro**	Returns:
573164562Sgshapiro**		is it a queue directory?
573264562Sgshapiro*/
573364562Sgshapiro
573464562Sgshapirostatic bool
573564562Sgshapirochkqdir(name, sff)
573664562Sgshapiro	char *name;
573764562Sgshapiro	long sff;
573864562Sgshapiro{
573964562Sgshapiro	struct stat statb;
574064562Sgshapiro	int i;
574164562Sgshapiro
574266494Sgshapiro	/* skip over . and .. directories */
574366494Sgshapiro	if (name[0] == '.' &&
574477349Sgshapiro	    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
574590792Sgshapiro		return false;
574690792Sgshapiro#if HASLSTAT
574764562Sgshapiro	if (lstat(name, &statb) < 0)
574890792Sgshapiro#else /* HASLSTAT */
574964562Sgshapiro	if (stat(name, &statb) < 0)
575090792Sgshapiro#endif /* HASLSTAT */
575164562Sgshapiro	{
575264562Sgshapiro		if (tTd(41, 2))
575390792Sgshapiro			sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
575490792Sgshapiro				   name, sm_errstring(errno));
575590792Sgshapiro		return false;
575664562Sgshapiro	}
575790792Sgshapiro#if HASLSTAT
575864562Sgshapiro	if (S_ISLNK(statb.st_mode))
575964562Sgshapiro	{
576064562Sgshapiro		/*
576164562Sgshapiro		**  For a symlink we need to make sure the
576264562Sgshapiro		**  target is a directory
576364562Sgshapiro		*/
576490792Sgshapiro
576564562Sgshapiro		if (stat(name, &statb) < 0)
576664562Sgshapiro		{
576764562Sgshapiro			if (tTd(41, 2))
576890792Sgshapiro				sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
576990792Sgshapiro					   name, sm_errstring(errno));
577090792Sgshapiro			return false;
577164562Sgshapiro		}
577264562Sgshapiro	}
577390792Sgshapiro#endif /* HASLSTAT */
577464562Sgshapiro
577564562Sgshapiro	if (!S_ISDIR(statb.st_mode))
577664562Sgshapiro	{
577764562Sgshapiro		if (tTd(41, 2))
577890792Sgshapiro			sm_dprintf("chkqdir: \"%s\": Not a directory\n",
577964562Sgshapiro				name);
578090792Sgshapiro		return false;
578164562Sgshapiro	}
578264562Sgshapiro
578364562Sgshapiro	/* Print a warning if unsafe (but still use it) */
578490792Sgshapiro	/* XXX do this only if we want the warning? */
578564562Sgshapiro	i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
578698121Sgshapiro	if (i != 0)
578798121Sgshapiro	{
578898121Sgshapiro		if (tTd(41, 2))
578998121Sgshapiro			sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
579098121Sgshapiro				   name, sm_errstring(i));
579198121Sgshapiro#if _FFR_CHK_QUEUE
579298121Sgshapiro		if (LogLevel > 8)
579398121Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
579498121Sgshapiro				  "queue directory \"%s\": Not safe: %s",
579598121Sgshapiro				  name, sm_errstring(i));
579698121Sgshapiro#endif /* _FFR_CHK_QUEUE */
579798121Sgshapiro	}
579890792Sgshapiro	return true;
579964562Sgshapiro}
580090792Sgshapiro/*
580164562Sgshapiro**  MULTIQUEUE_CACHE -- cache a list of paths to queues.
580264562Sgshapiro**
580364562Sgshapiro**	Each potential queue is checked as the cache is built.
580464562Sgshapiro**	Thereafter, each is blindly trusted.
580564562Sgshapiro**	Note that we can be called again after a timeout to rebuild
580664562Sgshapiro**	(although code for that is not ready yet).
580764562Sgshapiro**
580864562Sgshapiro**	Parameters:
580990792Sgshapiro**		basedir -- base of all queue directories.
581090792Sgshapiro**		blen -- strlen(basedir).
581190792Sgshapiro**		qg -- queue group.
581290792Sgshapiro**		qn -- number of queue directories already cached.
581390792Sgshapiro**		phash -- pointer to hash value over queue dirs.
581490792Sgshapiro#if SM_CONF_SHM
581590792Sgshapiro**			only used if shared memory is active.
581690792Sgshapiro#endif * SM_CONF_SHM *
581764562Sgshapiro**
581864562Sgshapiro**	Returns:
581990792Sgshapiro**		new number of queue directories.
582064562Sgshapiro*/
582164562Sgshapiro
582290792Sgshapiro#define INITIAL_SLOTS	20
582390792Sgshapiro#define ADD_SLOTS	10
582490792Sgshapiro
582590792Sgshapirostatic int
582690792Sgshapiromultiqueue_cache(basedir, blen, qg, qn, phash)
582790792Sgshapiro	char *basedir;
582890792Sgshapiro	int blen;
582990792Sgshapiro	QUEUEGRP *qg;
583090792Sgshapiro	int qn;
583190792Sgshapiro	unsigned int *phash;
583264562Sgshapiro{
583364562Sgshapiro	char *cp;
583464562Sgshapiro	int i, len;
583564562Sgshapiro	int slotsleft = 0;
583664562Sgshapiro	long sff = SFF_ANYFILE;
583764562Sgshapiro	char qpath[MAXPATHLEN];
583864562Sgshapiro	char subdir[MAXPATHLEN];
583990792Sgshapiro	char prefix[MAXPATHLEN];	/* dir relative to basedir */
584064562Sgshapiro
584164562Sgshapiro	if (tTd(41, 20))
584290792Sgshapiro		sm_dprintf("multiqueue_cache: called\n");
584364562Sgshapiro
584490792Sgshapiro	/* Initialize to current directory */
584590792Sgshapiro	prefix[0] = '.';
584690792Sgshapiro	prefix[1] = '\0';
584790792Sgshapiro	if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
584864562Sgshapiro	{
584990792Sgshapiro		for (i = 0; i < qg->qg_numqueues; i++)
585064562Sgshapiro		{
585190792Sgshapiro			if (qg->qg_qpaths[i].qp_name != NULL)
585290792Sgshapiro				(void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
585364562Sgshapiro		}
585490792Sgshapiro		(void) sm_free((char *) qg->qg_qpaths); /* XXX */
585590792Sgshapiro		qg->qg_qpaths = NULL;
585690792Sgshapiro		qg->qg_numqueues = 0;
585764562Sgshapiro	}
585864562Sgshapiro
585964562Sgshapiro	/* If running as root, allow safedirpath() checks to use privs */
586064562Sgshapiro	if (RunAsUid == 0)
586164562Sgshapiro		sff |= SFF_ROOTOK;
586298121Sgshapiro#if _FFR_CHK_QUEUE
586398121Sgshapiro	sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES;
586498121Sgshapiro	if (!UseMSP)
586598121Sgshapiro		sff |= SFF_NOGWFILES;
586698121Sgshapiro#endif /* _FFR_CHK_QUEUE */
586764562Sgshapiro
586890792Sgshapiro	if (!SM_IS_DIR_START(qg->qg_qdir))
586990792Sgshapiro	{
587090792Sgshapiro		/*
587190792Sgshapiro		**  XXX we could add basedir, but then we have to realloc()
587290792Sgshapiro		**  the string... Maybe another time.
587390792Sgshapiro		*/
587490792Sgshapiro
587590792Sgshapiro		syserr("QueuePath %s not absolute", qg->qg_qdir);
587690792Sgshapiro		ExitStat = EX_CONFIG;
587790792Sgshapiro		return qn;
587890792Sgshapiro	}
587990792Sgshapiro
588090792Sgshapiro	/* qpath: directory of current workgroup */
588190792Sgshapiro	len = sm_strlcpy(qpath, qg->qg_qdir, sizeof qpath);
588290792Sgshapiro	if (len >= sizeof qpath)
588390792Sgshapiro	{
588490792Sgshapiro		syserr("QueuePath %.256s too long (%d max)",
588590792Sgshapiro		       qg->qg_qdir, (int) sizeof qpath);
588690792Sgshapiro		ExitStat = EX_CONFIG;
588790792Sgshapiro		return qn;
588890792Sgshapiro	}
588990792Sgshapiro
589090792Sgshapiro	/* begin of qpath must be same as basedir */
589190792Sgshapiro	if (strncmp(basedir, qpath, blen) != 0 &&
589290792Sgshapiro	    (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
589390792Sgshapiro	{
589490792Sgshapiro		syserr("QueuePath %s not subpath of QueueDirectory %s",
589590792Sgshapiro			qpath, basedir);
589690792Sgshapiro		ExitStat = EX_CONFIG;
589790792Sgshapiro		return qn;
589890792Sgshapiro	}
589990792Sgshapiro
590090792Sgshapiro	/* Do we have a nested subdirectory? */
590190792Sgshapiro	if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
590290792Sgshapiro	{
590390792Sgshapiro
590490792Sgshapiro		/* Copy subdirectory into prefix for later use */
590590792Sgshapiro		if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof prefix) >=
590690792Sgshapiro		    sizeof prefix)
590790792Sgshapiro		{
590890792Sgshapiro			syserr("QueuePath %.256s too long (%d max)",
590990792Sgshapiro				qg->qg_qdir, (int) sizeof qpath);
591090792Sgshapiro			ExitStat = EX_CONFIG;
591190792Sgshapiro			return qn;
591290792Sgshapiro		}
591390792Sgshapiro		cp = SM_LAST_DIR_DELIM(prefix);
591490792Sgshapiro		SM_ASSERT(cp != NULL);
591590792Sgshapiro		*cp = '\0';	/* cut off trailing / */
591690792Sgshapiro	}
591790792Sgshapiro
591890792Sgshapiro	/* This is guaranteed by the basedir check above */
591990792Sgshapiro	SM_ASSERT(len >= blen - 1);
592090792Sgshapiro	cp = &qpath[len - 1];
592164562Sgshapiro	if (*cp == '*')
592264562Sgshapiro	{
592390792Sgshapiro		register DIR *dp;
592490792Sgshapiro		register struct dirent *d;
592590792Sgshapiro		int off;
592690792Sgshapiro		char *delim;
592790792Sgshapiro		char relpath[MAXPATHLEN];
592890792Sgshapiro
592990792Sgshapiro		*cp = '\0';	/* Overwrite wildcard */
593090792Sgshapiro		if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
593164562Sgshapiro		{
593264562Sgshapiro			syserr("QueueDirectory: can not wildcard relative path");
593364562Sgshapiro			if (tTd(41, 2))
593490792Sgshapiro				sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
593571345Sgshapiro					qpath);
593664562Sgshapiro			ExitStat = EX_CONFIG;
593790792Sgshapiro			return qn;
593864562Sgshapiro		}
593964562Sgshapiro		if (cp == qpath)
594064562Sgshapiro		{
594164562Sgshapiro			/*
594264562Sgshapiro			**  Special case of top level wildcard, like /foo*
594390792Sgshapiro			**	Change to //foo*
594464562Sgshapiro			*/
594564562Sgshapiro
594690792Sgshapiro			(void) sm_strlcpy(qpath + 1, qpath, sizeof qpath - 1);
594764562Sgshapiro			++cp;
594864562Sgshapiro		}
594990792Sgshapiro		delim = cp;
595090792Sgshapiro		*(cp++) = '\0';		/* Replace / with \0 */
595190792Sgshapiro		len = strlen(cp);	/* Last component of queue directory */
595264562Sgshapiro
595390792Sgshapiro		/*
595490792Sgshapiro		**  Path relative to basedir, with trailing /
595590792Sgshapiro		**  It will be modified below to specify the subdirectories
595690792Sgshapiro		**  so they can be opened without chdir().
595790792Sgshapiro		*/
595890792Sgshapiro
595990792Sgshapiro		off = sm_strlcpyn(relpath, sizeof relpath, 2, prefix, "/");
596090792Sgshapiro		SM_ASSERT(off < sizeof relpath);
596190792Sgshapiro
596264562Sgshapiro		if (tTd(41, 2))
596390792Sgshapiro			sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
596490792Sgshapiro				   relpath, cp);
596564562Sgshapiro
596690792Sgshapiro		/* It is always basedir: we don't need to store it per group */
596790792Sgshapiro		/* XXX: optimize this! -> one more global? */
596890792Sgshapiro		qg->qg_qdir = newstr(basedir);
596990792Sgshapiro		qg->qg_qdir[blen - 1] = '\0';	/* cut off trailing / */
597064562Sgshapiro
597164562Sgshapiro		/*
597264562Sgshapiro		**  XXX Should probably wrap this whole loop in a timeout
597364562Sgshapiro		**  in case some wag decides to NFS mount the queues.
597464562Sgshapiro		*/
597564562Sgshapiro
597690792Sgshapiro		/* Test path to get warning messages. */
597790792Sgshapiro		if (qn == 0)
597864562Sgshapiro		{
597990792Sgshapiro			/*  XXX qg_runasuid and qg_runasgid for specials? */
598090792Sgshapiro			i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
598190792Sgshapiro					sff, 0, 0);
598290792Sgshapiro			if (i != 0 && tTd(41, 2))
598390792Sgshapiro				sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
598490792Sgshapiro					   basedir, sm_errstring(i));
598564562Sgshapiro		}
598664562Sgshapiro
598790792Sgshapiro		if ((dp = opendir(prefix)) == NULL)
598864562Sgshapiro		{
598990792Sgshapiro			syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
599064562Sgshapiro			if (tTd(41, 2))
599190792Sgshapiro				sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
599290792Sgshapiro					   qg->qg_qdir, prefix,
599390792Sgshapiro					   sm_errstring(errno));
599464562Sgshapiro			ExitStat = EX_CONFIG;
599590792Sgshapiro			return qn;
599664562Sgshapiro		}
599764562Sgshapiro		while ((d = readdir(dp)) != NULL)
599864562Sgshapiro		{
599990792Sgshapiro			i = strlen(d->d_name);
600090792Sgshapiro			if (i < len || strncmp(d->d_name, cp, len) != 0)
600164562Sgshapiro			{
600264562Sgshapiro				if (tTd(41, 5))
600390792Sgshapiro					sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
600464562Sgshapiro						d->d_name);
600564562Sgshapiro				continue;
600664562Sgshapiro			}
600790792Sgshapiro
600890792Sgshapiro			/* Create relative pathname: prefix + local directory */
600990792Sgshapiro			i = sizeof(relpath) - off;
601090792Sgshapiro			if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
601190792Sgshapiro				continue;	/* way too long */
601290792Sgshapiro
601390792Sgshapiro			if (!chkqdir(relpath, sff))
601464562Sgshapiro				continue;
601564562Sgshapiro
601690792Sgshapiro			if (qg->qg_qpaths == NULL)
601764562Sgshapiro			{
601890792Sgshapiro				slotsleft = INITIAL_SLOTS;
601990792Sgshapiro				qg->qg_qpaths = (QPATHS *)xalloc((sizeof *qg->qg_qpaths) *
602090792Sgshapiro								slotsleft);
602190792Sgshapiro				qg->qg_numqueues = 0;
602264562Sgshapiro			}
602364562Sgshapiro			else if (slotsleft < 1)
602464562Sgshapiro			{
602590792Sgshapiro				qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
602690792Sgshapiro							  (sizeof *qg->qg_qpaths) *
602790792Sgshapiro							  (qg->qg_numqueues +
602890792Sgshapiro							   ADD_SLOTS));
602990792Sgshapiro				if (qg->qg_qpaths == NULL)
603064562Sgshapiro				{
603164562Sgshapiro					(void) closedir(dp);
603290792Sgshapiro					return qn;
603364562Sgshapiro				}
603490792Sgshapiro				slotsleft += ADD_SLOTS;
603564562Sgshapiro			}
603664562Sgshapiro
603764562Sgshapiro			/* check subdirs */
603890792Sgshapiro			qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
603964562Sgshapiro
604090792Sgshapiro#define CHKRSUBDIR(name, flag)	\
604190792Sgshapiro	(void) sm_strlcpyn(subdir, sizeof subdir, 3, relpath, "/", name); \
604290792Sgshapiro	if (chkqdir(subdir, sff))	\
604390792Sgshapiro		qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag;	\
604490792Sgshapiro	else
604564562Sgshapiro
604664562Sgshapiro
604790792Sgshapiro			CHKRSUBDIR("qf", QP_SUBQF);
604890792Sgshapiro			CHKRSUBDIR("df", QP_SUBDF);
604990792Sgshapiro			CHKRSUBDIR("xf", QP_SUBXF);
605090792Sgshapiro
605164562Sgshapiro			/* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
605264562Sgshapiro			/* maybe even - 17 (subdirs) */
605390792Sgshapiro
605490792Sgshapiro			if (prefix[0] != '.')
605590792Sgshapiro				qg->qg_qpaths[qg->qg_numqueues].qp_name =
605690792Sgshapiro					newstr(relpath);
605790792Sgshapiro			else
605890792Sgshapiro				qg->qg_qpaths[qg->qg_numqueues].qp_name =
605990792Sgshapiro					newstr(d->d_name);
606090792Sgshapiro
606164562Sgshapiro			if (tTd(41, 2))
606290792Sgshapiro				sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
606390792Sgshapiro					qg->qg_numqueues, relpath,
606490792Sgshapiro					qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
606590792Sgshapiro#if SM_CONF_SHM
606690792Sgshapiro			qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
606790792Sgshapiro			*phash = hash_q(relpath, *phash);
606890792Sgshapiro#endif /* SM_CONF_SHM */
606990792Sgshapiro			qg->qg_numqueues++;
607090792Sgshapiro			++qn;
607164562Sgshapiro			slotsleft--;
607264562Sgshapiro		}
607364562Sgshapiro		(void) closedir(dp);
607490792Sgshapiro
607590792Sgshapiro		/* undo damage */
607690792Sgshapiro		*delim = '/';
607764562Sgshapiro	}
607890792Sgshapiro	if (qg->qg_numqueues == 0)
607964562Sgshapiro	{
608090792Sgshapiro		qg->qg_qpaths = (QPATHS *) xalloc(sizeof *qg->qg_qpaths);
608164562Sgshapiro
608264562Sgshapiro		/* test path to get warning messages */
608390792Sgshapiro		i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
608490792Sgshapiro		if (i == ENOENT)
608564562Sgshapiro		{
608690792Sgshapiro			syserr("can not opendir(%s)", qpath);
608764562Sgshapiro			if (tTd(41, 2))
608890792Sgshapiro				sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
608990792Sgshapiro					   qpath, sm_errstring(i));
609064562Sgshapiro			ExitStat = EX_CONFIG;
609190792Sgshapiro			return qn;
609264562Sgshapiro		}
609364562Sgshapiro
609490792Sgshapiro		qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
609590792Sgshapiro		qg->qg_numqueues = 1;
609690792Sgshapiro
609764562Sgshapiro		/* check subdirs */
609890792Sgshapiro#define CHKSUBDIR(name, flag)	\
609990792Sgshapiro	(void) sm_strlcpyn(subdir, sizeof subdir, 3, qg->qg_qdir, "/", name); \
610090792Sgshapiro	if (chkqdir(subdir, sff))	\
610190792Sgshapiro		qg->qg_qpaths[0].qp_subdirs |= flag;	\
610290792Sgshapiro	else
610364562Sgshapiro
610490792Sgshapiro		CHKSUBDIR("qf", QP_SUBQF);
610590792Sgshapiro		CHKSUBDIR("df", QP_SUBDF);
610690792Sgshapiro		CHKSUBDIR("xf", QP_SUBXF);
610764562Sgshapiro
610890792Sgshapiro		if (qg->qg_qdir[blen - 1] != '\0' &&
610990792Sgshapiro		    qg->qg_qdir[blen] != '\0')
611090792Sgshapiro		{
611190792Sgshapiro			/*
611290792Sgshapiro			**  Copy the last component into qpaths and
611390792Sgshapiro			**  cut off qdir
611490792Sgshapiro			*/
611590792Sgshapiro
611690792Sgshapiro			qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
611790792Sgshapiro			qg->qg_qdir[blen - 1] = '\0';
611890792Sgshapiro		}
611990792Sgshapiro		else
612090792Sgshapiro			qg->qg_qpaths[0].qp_name = newstr(".");
612190792Sgshapiro
612290792Sgshapiro#if SM_CONF_SHM
612390792Sgshapiro		qg->qg_qpaths[0].qp_idx = qn;
612490792Sgshapiro		*phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
612590792Sgshapiro#endif /* SM_CONF_SHM */
612690792Sgshapiro		++qn;
612764562Sgshapiro	}
612890792Sgshapiro	return qn;
612964562Sgshapiro}
613064562Sgshapiro
613190792Sgshapiro/*
613290792Sgshapiro**  FILESYS_FIND -- find entry in FileSys table, or add new one
613390792Sgshapiro**
613490792Sgshapiro**	Given the pathname of a directory, determine the file system
613590792Sgshapiro**	in which that directory resides, and return a pointer to the
613690792Sgshapiro**	entry in the FileSys table that describes the file system.
613790792Sgshapiro**	A new entry is added if necessary (and requested).
613890792Sgshapiro**	If the directory does not exist, -1 is returned.
613990792Sgshapiro**
614090792Sgshapiro**	Parameters:
614190792Sgshapiro**		path -- pathname of directory
614290792Sgshapiro**		add -- add to structure if not found.
614390792Sgshapiro**
614490792Sgshapiro**	Returns:
614590792Sgshapiro**		>=0: found: index in file system table
614690792Sgshapiro**		<0: some error, i.e.,
614790792Sgshapiro**		FSF_TOO_MANY: too many filesystems (-> syserr())
614890792Sgshapiro**		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
614990792Sgshapiro**		FSF_NOT_FOUND: not in list
615090792Sgshapiro*/
615190792Sgshapiro
615290792Sgshapirostatic short filesys_find __P((char *, bool));
615390792Sgshapiro
615490792Sgshapiro#define FSF_NOT_FOUND	(-1)
615590792Sgshapiro#define FSF_STAT_FAIL	(-2)
615690792Sgshapiro#define FSF_TOO_MANY	(-3)
615790792Sgshapiro
615890792Sgshapirostatic short
615990792Sgshapirofilesys_find(path, add)
616090792Sgshapiro	char *path;
616190792Sgshapiro	bool add;
616290792Sgshapiro{
616390792Sgshapiro	struct stat st;
616490792Sgshapiro	short i;
616590792Sgshapiro
616690792Sgshapiro	if (stat(path, &st) < 0)
616790792Sgshapiro	{
616890792Sgshapiro		syserr("cannot stat queue directory %s", path);
616990792Sgshapiro		return FSF_STAT_FAIL;
617090792Sgshapiro	}
617190792Sgshapiro	for (i = 0; i < NumFileSys; ++i)
617290792Sgshapiro	{
617390792Sgshapiro		if (FILE_SYS_DEV(i) == st.st_dev)
617490792Sgshapiro			return i;
617590792Sgshapiro	}
617690792Sgshapiro	if (i >= MAXFILESYS)
617790792Sgshapiro	{
617890792Sgshapiro		syserr("too many queue file systems (%d max)", MAXFILESYS);
617990792Sgshapiro		return FSF_TOO_MANY;
618090792Sgshapiro	}
618190792Sgshapiro	if (!add)
618290792Sgshapiro		return FSF_NOT_FOUND;
618390792Sgshapiro
618490792Sgshapiro	++NumFileSys;
618590792Sgshapiro	FILE_SYS_NAME(i) = path;
618690792Sgshapiro	FILE_SYS_DEV(i) = st.st_dev;
618790792Sgshapiro	FILE_SYS_AVAIL(i) = 0;
618890792Sgshapiro	FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
618990792Sgshapiro	return i;
619090792Sgshapiro}
619190792Sgshapiro
619290792Sgshapiro/*
619390792Sgshapiro**  FILESYS_SETUP -- set up mapping from queue directories to file systems
619490792Sgshapiro**
619590792Sgshapiro**	This data structure is used to efficiently check the amount of
619690792Sgshapiro**	free space available in a set of queue directories.
619790792Sgshapiro**
619890792Sgshapiro**	Parameters:
619990792Sgshapiro**		add -- initialize structure if necessary.
620090792Sgshapiro**
620190792Sgshapiro**	Returns:
620290792Sgshapiro**		0: success
620390792Sgshapiro**		<0: some error, i.e.,
620490792Sgshapiro**		FSF_NOT_FOUND: not in list
620590792Sgshapiro**		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
620690792Sgshapiro**		FSF_TOO_MANY: too many filesystems (-> syserr())
620790792Sgshapiro*/
620890792Sgshapiro
620990792Sgshapirostatic int filesys_setup __P((bool));
621090792Sgshapiro
621190792Sgshapirostatic int
621290792Sgshapirofilesys_setup(add)
621390792Sgshapiro	bool add;
621490792Sgshapiro{
621590792Sgshapiro	int i, j;
621690792Sgshapiro	short fs;
621790792Sgshapiro	int ret;
621890792Sgshapiro
621990792Sgshapiro	ret = 0;
622090792Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
622190792Sgshapiro	{
622290792Sgshapiro		for (j = 0; j < Queue[i]->qg_numqueues; ++j)
622390792Sgshapiro		{
622490792Sgshapiro			QPATHS *qp = &Queue[i]->qg_qpaths[j];
622590792Sgshapiro
622690792Sgshapiro			fs = filesys_find(qp->qp_name, add);
622790792Sgshapiro			if (fs >= 0)
622890792Sgshapiro				qp->qp_fsysidx = fs;
622990792Sgshapiro			else
623090792Sgshapiro				qp->qp_fsysidx = 0;
623190792Sgshapiro			if (fs < ret)
623290792Sgshapiro				ret = fs;
623390792Sgshapiro		}
623490792Sgshapiro	}
623590792Sgshapiro	return ret;
623690792Sgshapiro}
623790792Sgshapiro
623890792Sgshapiro/*
623990792Sgshapiro**  FILESYS_UPDATE -- update amount of free space on all file systems
624090792Sgshapiro**
624190792Sgshapiro**	The FileSys table is used to cache the amount of free space
624290792Sgshapiro**	available on all queue directory file systems.
624390792Sgshapiro**	This function updates the cached information if it has expired.
624490792Sgshapiro**
624590792Sgshapiro**	Parameters:
624690792Sgshapiro**		none.
624790792Sgshapiro**
624890792Sgshapiro**	Returns:
624990792Sgshapiro**		none.
625090792Sgshapiro**
625190792Sgshapiro**	Side Effects:
625290792Sgshapiro**		Updates FileSys table.
625390792Sgshapiro*/
625490792Sgshapiro
625590792Sgshapirovoid
625690792Sgshapirofilesys_update()
625790792Sgshapiro{
625890792Sgshapiro	int i;
625990792Sgshapiro	long avail, blksize;
626090792Sgshapiro	time_t now;
626190792Sgshapiro	static time_t nextupdate = 0;
626290792Sgshapiro
626390792Sgshapiro#if SM_CONF_SHM
626490792Sgshapiro	/* only the daemon updates this structure */
626590792Sgshapiro	if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
626690792Sgshapiro		return;
626790792Sgshapiro#endif /* SM_CONF_SHM */
626890792Sgshapiro	now = curtime();
626990792Sgshapiro	if (now < nextupdate)
627090792Sgshapiro		return;
627190792Sgshapiro	nextupdate = now + FILESYS_UPDATE_INTERVAL;
627290792Sgshapiro	for (i = 0; i < NumFileSys; ++i)
627390792Sgshapiro	{
627490792Sgshapiro		FILESYS *fs = &FILE_SYS(i);
627590792Sgshapiro
627690792Sgshapiro		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
627790792Sgshapiro		if (avail < 0 || blksize <= 0)
627890792Sgshapiro		{
627990792Sgshapiro			if (LogLevel > 5)
628090792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
628190792Sgshapiro					"filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
628290792Sgshapiro					sm_errstring(errno),
628390792Sgshapiro					FILE_SYS_NAME(i), avail, blksize);
628490792Sgshapiro			fs->fs_avail = 0;
628590792Sgshapiro			fs->fs_blksize = 1024; /* avoid divide by zero */
628690792Sgshapiro			nextupdate = now + 2; /* let's do this soon again */
628790792Sgshapiro		}
628890792Sgshapiro		else
628990792Sgshapiro		{
629090792Sgshapiro			fs->fs_avail = avail;
629190792Sgshapiro			fs->fs_blksize = blksize;
629290792Sgshapiro		}
629390792Sgshapiro	}
629490792Sgshapiro}
629590792Sgshapiro
629690792Sgshapiro#if _FFR_ANY_FREE_FS
629790792Sgshapiro/*
629890792Sgshapiro**  FILESYS_FREE -- check whether there is at least one fs with enough space.
629990792Sgshapiro**
630090792Sgshapiro**	Parameters:
630190792Sgshapiro**		fsize -- file size in bytes
630290792Sgshapiro**
630390792Sgshapiro**	Returns:
630490792Sgshapiro**		true iff there is one fs with more than fsize bytes free.
630590792Sgshapiro*/
630690792Sgshapiro
630790792Sgshapirobool
630890792Sgshapirofilesys_free(fsize)
630990792Sgshapiro	long fsize;
631090792Sgshapiro{
631190792Sgshapiro	int i;
631290792Sgshapiro
631390792Sgshapiro	if (fsize <= 0)
631490792Sgshapiro		return true;
631590792Sgshapiro	for (i = 0; i < NumFileSys; ++i)
631690792Sgshapiro	{
631790792Sgshapiro		long needed = 0;
631890792Sgshapiro
631990792Sgshapiro		if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
632090792Sgshapiro			continue;
632190792Sgshapiro		needed += fsize / FILE_SYS_BLKSIZE(i)
632290792Sgshapiro			  + ((fsize % FILE_SYS_BLKSIZE(i)
632390792Sgshapiro			      > 0) ? 1 : 0)
632490792Sgshapiro			  + MinBlocksFree;
632590792Sgshapiro		if (needed <= FILE_SYS_AVAIL(i))
632690792Sgshapiro			return true;
632790792Sgshapiro	}
632890792Sgshapiro	return false;
632990792Sgshapiro}
633090792Sgshapiro#endif /* _FFR_ANY_FREE_FS */
633190792Sgshapiro
633290792Sgshapiro#if _FFR_CONTROL_MSTAT
633390792Sgshapiro/*
633490792Sgshapiro**  DISK_STATUS -- show amount of free space in queue directories
633590792Sgshapiro**
633690792Sgshapiro**	Parameters:
633790792Sgshapiro**		out -- output file pointer.
633890792Sgshapiro**		prefix -- string to output in front of each line.
633990792Sgshapiro**
634090792Sgshapiro**	Returns:
634190792Sgshapiro**		none.
634290792Sgshapiro*/
634390792Sgshapiro
634490792Sgshapirovoid
634590792Sgshapirodisk_status(out, prefix)
634690792Sgshapiro	SM_FILE_T *out;
634790792Sgshapiro	char *prefix;
634890792Sgshapiro{
634990792Sgshapiro	int i;
635090792Sgshapiro	long avail, blksize;
635190792Sgshapiro	long free;
635290792Sgshapiro
635390792Sgshapiro	for (i = 0; i < NumFileSys; ++i)
635490792Sgshapiro	{
635590792Sgshapiro		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
635690792Sgshapiro		if (avail >= 0 && blksize > 0)
635790792Sgshapiro		{
635890792Sgshapiro			free = (long)((double) avail *
635990792Sgshapiro				((double) blksize / 1024));
636090792Sgshapiro		}
636190792Sgshapiro		else
636290792Sgshapiro			free = -1;
636390792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
636490792Sgshapiro				"%s%d/%s/%ld\r\n",
636590792Sgshapiro				prefix, i,
636690792Sgshapiro				FILE_SYS_NAME(i),
636790792Sgshapiro					free);
636890792Sgshapiro	}
636990792Sgshapiro}
637090792Sgshapiro#endif /* _FFR_CONTROL_MSTAT */
637190792Sgshapiro
637290792Sgshapiro#if SM_CONF_SHM
637390792Sgshapiro/*
637490792Sgshapiro**  UPD_QS -- update information about queue when adding/deleting an entry
637590792Sgshapiro**
637690792Sgshapiro**	Parameters:
637790792Sgshapiro**		e -- envelope.
637890792Sgshapiro**		delete -- delete/add entry.
637990792Sgshapiro**		avail -- update the space available as well.
638090792Sgshapiro**
638190792Sgshapiro**	Returns:
638290792Sgshapiro**		none.
638390792Sgshapiro**
638490792Sgshapiro**	Side Effects:
638590792Sgshapiro**		Modifies available space in filesystem.
638690792Sgshapiro**		Changes number of entries in queue directory.
638790792Sgshapiro*/
638890792Sgshapiro
638990792Sgshapirovoid
639090792Sgshapiroupd_qs(e, delete, avail)
639190792Sgshapiro	ENVELOPE *e;
639290792Sgshapiro	bool delete;
639390792Sgshapiro	bool avail;
639490792Sgshapiro{
639590792Sgshapiro	short fidx;
639690792Sgshapiro	int idx;
639790792Sgshapiro	long s;
639890792Sgshapiro
639990792Sgshapiro	if (ShmId == SM_SHM_NO_ID || e == NULL)
640090792Sgshapiro		return;
640190792Sgshapiro	if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
640290792Sgshapiro		return;
640390792Sgshapiro	idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
640490792Sgshapiro
640590792Sgshapiro	/* XXX in theory this needs to be protected with a mutex */
640690792Sgshapiro	if (QSHM_ENTRIES(idx) >= 0)
640790792Sgshapiro	{
640890792Sgshapiro		if (delete)
640990792Sgshapiro			--QSHM_ENTRIES(idx);
641090792Sgshapiro		else
641190792Sgshapiro			++QSHM_ENTRIES(idx);
641290792Sgshapiro	}
641390792Sgshapiro
641490792Sgshapiro	fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
641590792Sgshapiro	if (fidx < 0)
641690792Sgshapiro		return;
641790792Sgshapiro
641890792Sgshapiro	/* update available space also?  (might be loseqfile) */
641990792Sgshapiro	if (!avail)
642090792Sgshapiro		return;
642190792Sgshapiro
642290792Sgshapiro	/* convert size to blocks; this causes rounding errors */
642390792Sgshapiro	s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
642490792Sgshapiro	if (s == 0)
642590792Sgshapiro		return;
642690792Sgshapiro
642790792Sgshapiro	/* XXX in theory this needs to be protected with a mutex */
642890792Sgshapiro	if (delete)
642990792Sgshapiro		FILE_SYS_AVAIL(fidx) += s;
643090792Sgshapiro	else
643190792Sgshapiro		FILE_SYS_AVAIL(fidx) -= s;
643290792Sgshapiro
643390792Sgshapiro}
643494334Sgshapiro
643594334Sgshapiro#if _FFR_SELECT_SHM
643694334Sgshapiro
643794334Sgshapirostatic bool write_key_file __P((char *, long));
643894334Sgshapirostatic long read_key_file __P((char *, long));
643994334Sgshapiro
644090792Sgshapiro/*
644194334Sgshapiro**  WRITE_KEY_FILE -- record some key into a file.
644294334Sgshapiro**
644394334Sgshapiro**	Parameters:
644494334Sgshapiro**		keypath -- file name.
644594334Sgshapiro**		key -- key to write.
644694334Sgshapiro**
644794334Sgshapiro**	Returns:
644894334Sgshapiro**		true iff file could be written.
644994334Sgshapiro**
645094334Sgshapiro**	Side Effects:
645194334Sgshapiro**		writes file.
645294334Sgshapiro*/
645394334Sgshapiro
645494334Sgshapirostatic bool
645594334Sgshapirowrite_key_file(keypath, key)
645694334Sgshapiro	char *keypath;
645794334Sgshapiro	long key;
645894334Sgshapiro{
645994334Sgshapiro	bool ok;
646094334Sgshapiro	long sff;
646194334Sgshapiro	SM_FILE_T *keyf;
646294334Sgshapiro
646394334Sgshapiro	ok = false;
646494334Sgshapiro	if (keypath == NULL || *keypath == '\0')
646594334Sgshapiro		return ok;
646694334Sgshapiro	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
646794334Sgshapiro	if (TrustedUid != 0 && RealUid == TrustedUid)
646894334Sgshapiro		sff |= SFF_OPENASROOT;
6469110560Sgshapiro	keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff);
647094334Sgshapiro	if (keyf == NULL)
647194334Sgshapiro	{
647294334Sgshapiro		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
647394334Sgshapiro			  keypath, sm_errstring(errno));
647494334Sgshapiro	}
647594334Sgshapiro	else
647694334Sgshapiro	{
647794334Sgshapiro		ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
647894334Sgshapiro		     SM_IO_EOF;
6479110560Sgshapiro		ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok;
648094334Sgshapiro	}
648194334Sgshapiro	return ok;
648294334Sgshapiro}
648394334Sgshapiro
648494334Sgshapiro/*
648594334Sgshapiro**  READ_KEY_FILE -- read a key from a file.
648694334Sgshapiro**
648794334Sgshapiro**	Parameters:
648894334Sgshapiro**		keypath -- file name.
648994334Sgshapiro**		key -- default key.
649094334Sgshapiro**
649194334Sgshapiro**	Returns:
649294334Sgshapiro**		key.
649394334Sgshapiro*/
649494334Sgshapiro
649594334Sgshapirostatic long
649694334Sgshapiroread_key_file(keypath, key)
649794334Sgshapiro	char *keypath;
649894334Sgshapiro	long key;
649994334Sgshapiro{
650094334Sgshapiro	int r;
650194334Sgshapiro	long sff, n;
650294334Sgshapiro	SM_FILE_T *keyf;
650394334Sgshapiro
650494334Sgshapiro	if (keypath == NULL || *keypath == '\0')
650594334Sgshapiro		return key;
650694334Sgshapiro	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
6507110560Sgshapiro	if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid))
650894334Sgshapiro		sff |= SFF_OPENASROOT;
6509110560Sgshapiro	keyf = safefopen(keypath, O_RDONLY, FileMode, sff);
651094334Sgshapiro	if (keyf == NULL)
651194334Sgshapiro	{
651294334Sgshapiro		sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s",
651394334Sgshapiro			  keypath, sm_errstring(errno));
651494334Sgshapiro	}
651594334Sgshapiro	else
651694334Sgshapiro	{
651794334Sgshapiro		r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n);
651894334Sgshapiro		if (r == 1)
651994334Sgshapiro			key = n;
652094334Sgshapiro		(void) sm_io_close(keyf, SM_TIME_DEFAULT);
652194334Sgshapiro	}
652294334Sgshapiro	return key;
652394334Sgshapiro}
652494334Sgshapiro#endif /* _FFR_SELECT_SHM */
652594334Sgshapiro
652694334Sgshapiro/*
652790792Sgshapiro**  INIT_SHM -- initialize shared memory structure
652890792Sgshapiro**
652990792Sgshapiro**	Initialize or attach to shared memory segment.
653090792Sgshapiro**	Currently it is not a fatal error if this doesn't work.
653190792Sgshapiro**	However, it causes us to have a "fallback" storage location
653290792Sgshapiro**	for everything that is supposed to be in the shared memory,
653390792Sgshapiro**	which makes the code slightly ugly.
653490792Sgshapiro**
653590792Sgshapiro**	Parameters:
653690792Sgshapiro**		qn -- number of queue directories.
653790792Sgshapiro**		owner -- owner of shared memory.
653890792Sgshapiro**		hash -- identifies data that is stored in shared memory.
653990792Sgshapiro**
654090792Sgshapiro**	Returns:
654190792Sgshapiro**		none.
654290792Sgshapiro*/
654390792Sgshapiro
654490792Sgshapirostatic void init_shm __P((int, bool, unsigned int));
654590792Sgshapiro
654690792Sgshapirostatic void
654790792Sgshapiroinit_shm(qn, owner, hash)
654890792Sgshapiro	int qn;
654990792Sgshapiro	bool owner;
655090792Sgshapiro	unsigned int hash;
655190792Sgshapiro{
655290792Sgshapiro	int i;
655394334Sgshapiro#if _FFR_SELECT_SHM
655494334Sgshapiro	bool keyselect;
655594334Sgshapiro#endif /* _FFR_SELECT_SHM */
655690792Sgshapiro
655790792Sgshapiro	PtrFileSys = &FileSys[0];
655890792Sgshapiro	PNumFileSys = &Numfilesys;
655994334Sgshapiro#if _FFR_SELECT_SHM
656094334Sgshapiro/* if this "key" is specified: select one yourself */
656194334Sgshapiro# define SEL_SHM_KEY	((key_t) -1)
656294334Sgshapiro# define FIRST_SHM_KEY	25
656394334Sgshapiro#endif /* _FFR_SELECT_SHM */
656490792Sgshapiro
656590792Sgshapiro	/* This allows us to disable shared memory at runtime. */
656690792Sgshapiro	if (ShmKey != 0)
656790792Sgshapiro	{
656890792Sgshapiro		int count;
656990792Sgshapiro		int save_errno;
657090792Sgshapiro
657190792Sgshapiro		count = 0;
657290792Sgshapiro		shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
657394334Sgshapiro#if _FFR_SELECT_SHM
657494334Sgshapiro		keyselect = ShmKey == SEL_SHM_KEY;
657594334Sgshapiro		if (keyselect)
657694334Sgshapiro		{
657794334Sgshapiro			if (owner)
657894334Sgshapiro				ShmKey = FIRST_SHM_KEY;
657994334Sgshapiro			else
658094334Sgshapiro			{
658194334Sgshapiro				ShmKey = read_key_file(ShmKeyFile, ShmKey);
658294334Sgshapiro				keyselect = false;
658394334Sgshapiro				if (ShmKey == SEL_SHM_KEY)
658494334Sgshapiro					goto error;
658594334Sgshapiro			}
658694334Sgshapiro		}
658794334Sgshapiro#endif /* _FFR_SELECT_SHM */
658890792Sgshapiro		for (;;)
658990792Sgshapiro		{
659090792Sgshapiro			/* XXX: maybe allow read access for group? */
659190792Sgshapiro			Pshm = sm_shmstart(ShmKey, shms, SHM_R|SHM_W, &ShmId,
659290792Sgshapiro					   owner);
659390792Sgshapiro			save_errno = errno;
659490792Sgshapiro			if (Pshm != NULL || save_errno != EEXIST)
659590792Sgshapiro				break;
659690792Sgshapiro			if (++count >= 3)
659794334Sgshapiro			{
659894334Sgshapiro#if _FFR_SELECT_SHM
659994334Sgshapiro				if (keyselect)
660094334Sgshapiro				{
660194334Sgshapiro					++ShmKey;
660294334Sgshapiro
660394334Sgshapiro					/* back where we started? */
660494334Sgshapiro					if (ShmKey == SEL_SHM_KEY)
660594334Sgshapiro						break;
660694334Sgshapiro					continue;
660794334Sgshapiro				}
660894334Sgshapiro#endif /* _FFR_SELECT_SHM */
660990792Sgshapiro				break;
661094334Sgshapiro			}
661194334Sgshapiro#if _FFR_SELECT_SHM
661294334Sgshapiro			/* only sleep if we are at the first key */
661394334Sgshapiro			if (!keyselect || ShmKey == SEL_SHM_KEY)
661494334Sgshapiro#endif /* _FFR_SELECT_SHM */
661590792Sgshapiro			sleep(count);
661690792Sgshapiro		}
661790792Sgshapiro		if (Pshm != NULL)
661890792Sgshapiro		{
661990792Sgshapiro			int *p;
662090792Sgshapiro
662194334Sgshapiro#if _FFR_SELECT_SHM
662294334Sgshapiro			if (keyselect)
662394334Sgshapiro				(void) write_key_file(ShmKeyFile, (long) ShmKey);
662494334Sgshapiro#endif /* _FFR_SELECT_SHM */
662590792Sgshapiro			p = (int *) Pshm;
662690792Sgshapiro			if (owner)
662790792Sgshapiro			{
662890792Sgshapiro				*p = (int) shms;
662990792Sgshapiro				*((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
663090792Sgshapiro				p = (int *) SHM_OFF_TAG(Pshm);
663190792Sgshapiro				*p = hash;
663290792Sgshapiro			}
663390792Sgshapiro			else
663490792Sgshapiro			{
663590792Sgshapiro				if (*p != (int) shms)
663690792Sgshapiro				{
663790792Sgshapiro					save_errno = EINVAL;
663890792Sgshapiro					cleanup_shm(false);
663990792Sgshapiro					goto error;
664090792Sgshapiro				}
664190792Sgshapiro				p = (int *) SHM_OFF_TAG(Pshm);
664290792Sgshapiro				if (*p != (int) hash)
664390792Sgshapiro				{
664490792Sgshapiro					save_errno = EINVAL;
664590792Sgshapiro					cleanup_shm(false);
664690792Sgshapiro					goto error;
664790792Sgshapiro				}
664890792Sgshapiro
664990792Sgshapiro				/*
665090792Sgshapiro				**  XXX how to check the pid?
665190792Sgshapiro				**  Read it from the pid-file? That does
665290792Sgshapiro				**  not need to exist.
665390792Sgshapiro				**  We could disable shm if we can't confirm
665490792Sgshapiro				**  that it is the right one.
665590792Sgshapiro				*/
665690792Sgshapiro			}
665790792Sgshapiro
665890792Sgshapiro			PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
665990792Sgshapiro			PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
666090792Sgshapiro			QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
666190792Sgshapiro			PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
666290792Sgshapiro			*PRSATmpCnt = 0;
666390792Sgshapiro			if (owner)
666490792Sgshapiro			{
666590792Sgshapiro				/* initialize values in shared memory */
666690792Sgshapiro				NumFileSys = 0;
666790792Sgshapiro				for (i = 0; i < qn; i++)
666890792Sgshapiro					QShm[i].qs_entries = -1;
666990792Sgshapiro			}
667090792Sgshapiro			return;
667190792Sgshapiro		}
667290792Sgshapiro  error:
667390792Sgshapiro		if (LogLevel > (owner ? 8 : 11))
667490792Sgshapiro		{
667590792Sgshapiro			sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
667690792Sgshapiro				  "can't %s shared memory, key=%ld: %s",
667790792Sgshapiro				  owner ? "initialize" : "attach to",
667890792Sgshapiro				  (long) ShmKey, sm_errstring(save_errno));
667990792Sgshapiro		}
668090792Sgshapiro	}
668190792Sgshapiro}
668290792Sgshapiro#endif /* SM_CONF_SHM */
668390792Sgshapiro
668490792Sgshapiro/*
668590792Sgshapiro**  SETUP_QUEUES -- setup all queue groups
668690792Sgshapiro**
668790792Sgshapiro**	Parameters:
668890792Sgshapiro**		owner -- owner of shared memory.
668990792Sgshapiro**
669090792Sgshapiro**	Returns:
669190792Sgshapiro**		none.
669290792Sgshapiro**
669390792Sgshapiro#if SM_CONF_SHM
669490792Sgshapiro**	Side Effects:
669590792Sgshapiro**		attaches shared memory.
669690792Sgshapiro#endif * SM_CONF_SHM *
669790792Sgshapiro*/
669890792Sgshapiro
669990792Sgshapirovoid
670090792Sgshapirosetup_queues(owner)
670190792Sgshapiro	bool owner;
670290792Sgshapiro{
670390792Sgshapiro	int i, qn, len;
670490792Sgshapiro	unsigned int hashval;
670594334Sgshapiro	time_t now;
670690792Sgshapiro	char basedir[MAXPATHLEN];
670790792Sgshapiro	struct stat st;
670890792Sgshapiro
670990792Sgshapiro	/*
671090792Sgshapiro	**  Determine basedir for all queue directories.
671190792Sgshapiro	**  All queue directories must be (first level) subdirectories
671290792Sgshapiro	**  of the basedir.  The basedir is the QueueDir
671390792Sgshapiro	**  without wildcards, but with trailing /
671490792Sgshapiro	*/
671590792Sgshapiro
671690792Sgshapiro	hashval = 0;
671790792Sgshapiro	errno = 0;
671890792Sgshapiro	len = sm_strlcpy(basedir, QueueDir, sizeof basedir);
6719111823Sgshapiro
6720111823Sgshapiro	/* Provide space for trailing '/' */
6721111823Sgshapiro	if (len >= sizeof basedir - 1)
672290792Sgshapiro	{
672390792Sgshapiro		syserr("QueueDirectory: path too long: %d,  max %d",
6724111823Sgshapiro			len, (int) sizeof basedir - 1);
672590792Sgshapiro		ExitStat = EX_CONFIG;
672690792Sgshapiro		return;
672790792Sgshapiro	}
672890792Sgshapiro	SM_ASSERT(len > 0);
672990792Sgshapiro	if (basedir[len - 1] == '*')
673090792Sgshapiro	{
673190792Sgshapiro		char *cp;
673290792Sgshapiro
673390792Sgshapiro		cp = SM_LAST_DIR_DELIM(basedir);
673490792Sgshapiro		if (cp == NULL)
673590792Sgshapiro		{
673690792Sgshapiro			syserr("QueueDirectory: can not wildcard relative path \"%s\"",
673790792Sgshapiro				QueueDir);
673890792Sgshapiro			if (tTd(41, 2))
673990792Sgshapiro				sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
674090792Sgshapiro					QueueDir);
674190792Sgshapiro			ExitStat = EX_CONFIG;
674290792Sgshapiro			return;
674390792Sgshapiro		}
674490792Sgshapiro
674590792Sgshapiro		/* cut off wildcard pattern */
674690792Sgshapiro		*++cp = '\0';
674790792Sgshapiro		len = cp - basedir;
674890792Sgshapiro	}
674990792Sgshapiro	else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
675090792Sgshapiro	{
675190792Sgshapiro		/* append trailing slash since it is a directory */
675290792Sgshapiro		basedir[len] = '/';
675390792Sgshapiro		basedir[++len] = '\0';
675490792Sgshapiro	}
675590792Sgshapiro
675690792Sgshapiro	/* len counts up to the last directory delimiter */
675790792Sgshapiro	SM_ASSERT(basedir[len - 1] == '/');
675890792Sgshapiro
675990792Sgshapiro	if (chdir(basedir) < 0)
676090792Sgshapiro	{
676190792Sgshapiro		int save_errno = errno;
676290792Sgshapiro
676390792Sgshapiro		syserr("can not chdir(%s)", basedir);
676490792Sgshapiro		if (save_errno == EACCES)
676590792Sgshapiro			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
676690792Sgshapiro				"Program mode requires special privileges, e.g., root or TrustedUser.\n");
676790792Sgshapiro		if (tTd(41, 2))
676890792Sgshapiro			sm_dprintf("setup_queues: \"%s\": %s\n",
676990792Sgshapiro				   basedir, sm_errstring(errno));
677090792Sgshapiro		ExitStat = EX_CONFIG;
677190792Sgshapiro		return;
677290792Sgshapiro	}
677390792Sgshapiro#if SM_CONF_SHM
677490792Sgshapiro	hashval = hash_q(basedir, hashval);
677590792Sgshapiro#endif /* SM_CONF_SHM */
677690792Sgshapiro
677794334Sgshapiro	/* initialize for queue runs */
677894334Sgshapiro	DoQueueRun = false;
677994334Sgshapiro	now = curtime();
678094334Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
678194334Sgshapiro		Queue[i]->qg_nextrun = now;
678290792Sgshapiro
678390792Sgshapiro
678490792Sgshapiro	if (UseMSP && OpMode != MD_TEST)
678590792Sgshapiro	{
678690792Sgshapiro		long sff = SFF_CREAT;
678790792Sgshapiro
678890792Sgshapiro		if (stat(".", &st) < 0)
678990792Sgshapiro		{
679090792Sgshapiro			syserr("can not stat(%s)", basedir);
679190792Sgshapiro			if (tTd(41, 2))
679290792Sgshapiro				sm_dprintf("setup_queues: \"%s\": %s\n",
679390792Sgshapiro					   basedir, sm_errstring(errno));
679490792Sgshapiro			ExitStat = EX_CONFIG;
679590792Sgshapiro			return;
679690792Sgshapiro		}
679790792Sgshapiro		if (RunAsUid == 0)
679890792Sgshapiro			sff |= SFF_ROOTOK;
679990792Sgshapiro
680090792Sgshapiro		/*
680190792Sgshapiro		**  Check queue directory permissions.
680290792Sgshapiro		**	Can we write to a group writable queue directory?
680390792Sgshapiro		*/
680490792Sgshapiro
680590792Sgshapiro		if (bitset(S_IWGRP, QueueFileMode) &&
680690792Sgshapiro		    bitset(S_IWGRP, st.st_mode) &&
680790792Sgshapiro		    safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
680890792Sgshapiro			     QueueFileMode, NULL) != 0)
680990792Sgshapiro		{
681090792Sgshapiro			syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
681190792Sgshapiro				basedir, (int) RunAsGid, (int) st.st_gid);
681290792Sgshapiro		}
681390792Sgshapiro		if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
681490792Sgshapiro		{
681590792Sgshapiro#if _FFR_MSP_PARANOIA
681690792Sgshapiro			syserr("dangerous permissions=%o on queue directory %s",
681790792Sgshapiro				(int) st.st_mode, basedir);
681890792Sgshapiro#else /* _FFR_MSP_PARANOIA */
681990792Sgshapiro			if (LogLevel > 0)
682090792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
682190792Sgshapiro					  "dangerous permissions=%o on queue directory %s",
682290792Sgshapiro					  (int) st.st_mode, basedir);
682390792Sgshapiro#endif /* _FFR_MSP_PARANOIA */
682490792Sgshapiro		}
682590792Sgshapiro#if _FFR_MSP_PARANOIA
682690792Sgshapiro		if (NumQueue > 1)
682790792Sgshapiro			syserr("can not use multiple queues for MSP");
682890792Sgshapiro#endif /* _FFR_MSP_PARANOIA */
682990792Sgshapiro	}
683090792Sgshapiro
683190792Sgshapiro	/* initial number of queue directories */
683290792Sgshapiro	qn = 0;
683390792Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
683490792Sgshapiro		qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
683590792Sgshapiro
683690792Sgshapiro#if SM_CONF_SHM
683790792Sgshapiro	init_shm(qn, owner, hashval);
683890792Sgshapiro	i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
683990792Sgshapiro	if (i == FSF_NOT_FOUND)
684090792Sgshapiro	{
684190792Sgshapiro		/*
684290792Sgshapiro		**  We didn't get the right filesystem data
684390792Sgshapiro		**  This may happen if we don't have the right shared memory.
684490792Sgshapiro		**  So let's do this without shared memory.
684590792Sgshapiro		*/
684690792Sgshapiro
684790792Sgshapiro		SM_ASSERT(!owner);
684890792Sgshapiro		cleanup_shm(false);	/* release shared memory */
684990792Sgshapiro		i = filesys_setup(false);
685090792Sgshapiro		if (i < 0)
685190792Sgshapiro			syserr("filesys_setup failed twice, result=%d", i);
685290792Sgshapiro		else if (LogLevel > 8)
685390792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
685490792Sgshapiro				  "shared memory does not contain expected data, ignored");
685590792Sgshapiro	}
685690792Sgshapiro#else /* SM_CONF_SHM */
685790792Sgshapiro	i = filesys_setup(true);
685890792Sgshapiro#endif /* SM_CONF_SHM */
685990792Sgshapiro	if (i < 0)
686090792Sgshapiro		ExitStat = EX_CONFIG;
686190792Sgshapiro}
686290792Sgshapiro
686390792Sgshapiro#if SM_CONF_SHM
686490792Sgshapiro/*
686590792Sgshapiro**  CLEANUP_SHM -- do some cleanup work for shared memory etc
686690792Sgshapiro**
686790792Sgshapiro**	Parameters:
686890792Sgshapiro**		owner -- owner of shared memory?
686990792Sgshapiro**
687090792Sgshapiro**	Returns:
687190792Sgshapiro**		none.
687290792Sgshapiro**
687390792Sgshapiro**	Side Effects:
687490792Sgshapiro**		detaches shared memory.
687590792Sgshapiro*/
687690792Sgshapiro
687790792Sgshapirovoid
687890792Sgshapirocleanup_shm(owner)
687990792Sgshapiro	bool owner;
688090792Sgshapiro{
688190792Sgshapiro	if (ShmId != SM_SHM_NO_ID)
688290792Sgshapiro	{
688390792Sgshapiro		if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
688498121Sgshapiro			sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s",
688590792Sgshapiro				  sm_errstring(errno));
688690792Sgshapiro		Pshm = NULL;
688790792Sgshapiro		ShmId = SM_SHM_NO_ID;
688890792Sgshapiro	}
688990792Sgshapiro}
689090792Sgshapiro#endif /* SM_CONF_SHM */
689190792Sgshapiro
689290792Sgshapiro/*
689390792Sgshapiro**  CLEANUP_QUEUES -- do some cleanup work for queues
689490792Sgshapiro**
689590792Sgshapiro**	Parameters:
689690792Sgshapiro**		none.
689790792Sgshapiro**
689890792Sgshapiro**	Returns:
689990792Sgshapiro**		none.
690090792Sgshapiro**
690190792Sgshapiro*/
690290792Sgshapiro
690390792Sgshapirovoid
690490792Sgshapirocleanup_queues()
690590792Sgshapiro{
690690792Sgshapiro	sync_queue_time();
690790792Sgshapiro}
690890792Sgshapiro/*
690990792Sgshapiro**  SET_DEF_QUEUEVAL -- set default values for a queue group.
691090792Sgshapiro**
691190792Sgshapiro**	Parameters:
691290792Sgshapiro**		qg -- queue group
691390792Sgshapiro**		all -- set all values (true for default group)?
691490792Sgshapiro**
691590792Sgshapiro**	Returns:
691690792Sgshapiro**		none.
691790792Sgshapiro**
691890792Sgshapiro**	Side Effects:
691990792Sgshapiro**		sets default values for the queue group.
692090792Sgshapiro*/
692190792Sgshapiro
692290792Sgshapirovoid
692390792Sgshapiroset_def_queueval(qg, all)
692490792Sgshapiro	QUEUEGRP *qg;
692590792Sgshapiro	bool all;
692690792Sgshapiro{
692790792Sgshapiro	if (bitnset(QD_DEFINED, qg->qg_flags))
692890792Sgshapiro		return;
692990792Sgshapiro	if (all)
693090792Sgshapiro		qg->qg_qdir = QueueDir;
693194334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER
693290792Sgshapiro	qg->qg_sortorder = QueueSortOrder;
693394334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */
693490792Sgshapiro	qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
693590792Sgshapiro	qg->qg_nice = NiceQueueRun;
693690792Sgshapiro}
693790792Sgshapiro/*
693890792Sgshapiro**  MAKEQUEUE -- define a new queue.
693990792Sgshapiro**
694090792Sgshapiro**	Parameters:
694190792Sgshapiro**		line -- description of queue.  This is in labeled fields.
694290792Sgshapiro**			The fields are:
694390792Sgshapiro**			   F -- the flags associated with the queue
694490792Sgshapiro**			   I -- the interval between running the queue
694590792Sgshapiro**			   J -- the maximum # of jobs in work list
694690792Sgshapiro**			   [M -- the maximum # of jobs in a queue run]
694790792Sgshapiro**			   N -- the niceness at which to run
694890792Sgshapiro**			   P -- the path to the queue
694990792Sgshapiro**			   S -- the queue sorting order
695090792Sgshapiro**			   R -- number of parallel queue runners
695190792Sgshapiro**			   r -- max recipients per envelope
695290792Sgshapiro**			The first word is the canonical name of the queue.
695390792Sgshapiro**		qdef -- this is a 'Q' definition from .cf
695490792Sgshapiro**
695590792Sgshapiro**	Returns:
695690792Sgshapiro**		none.
695790792Sgshapiro**
695890792Sgshapiro**	Side Effects:
695990792Sgshapiro**		enters the queue into the queue table.
696090792Sgshapiro*/
696190792Sgshapiro
696290792Sgshapirovoid
696390792Sgshapiromakequeue(line, qdef)
696490792Sgshapiro	char *line;
696590792Sgshapiro	bool qdef;
696690792Sgshapiro{
696790792Sgshapiro	register char *p;
696890792Sgshapiro	register QUEUEGRP *qg;
696990792Sgshapiro	register STAB *s;
697090792Sgshapiro	int i;
697190792Sgshapiro	char fcode;
697290792Sgshapiro
697390792Sgshapiro	/* allocate a queue and set up defaults */
697490792Sgshapiro	qg = (QUEUEGRP *) xalloc(sizeof *qg);
697590792Sgshapiro	memset((char *) qg, '\0', sizeof *qg);
697690792Sgshapiro
697790792Sgshapiro	if (line[0] == '\0')
697890792Sgshapiro	{
697990792Sgshapiro		syserr("name required for queue");
698090792Sgshapiro		return;
698190792Sgshapiro	}
698290792Sgshapiro
698390792Sgshapiro	/* collect the queue name */
698490792Sgshapiro	for (p = line;
698590792Sgshapiro	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
698690792Sgshapiro	     p++)
698790792Sgshapiro		continue;
698890792Sgshapiro	if (*p != '\0')
698990792Sgshapiro		*p++ = '\0';
699090792Sgshapiro	qg->qg_name = newstr(line);
699190792Sgshapiro
699290792Sgshapiro	/* set default values, can be overridden below */
699390792Sgshapiro	set_def_queueval(qg, false);
699490792Sgshapiro
699590792Sgshapiro	/* now scan through and assign info from the fields */
699690792Sgshapiro	while (*p != '\0')
699790792Sgshapiro	{
699890792Sgshapiro		auto char *delimptr;
699990792Sgshapiro
700090792Sgshapiro		while (*p != '\0' &&
700190792Sgshapiro		       (*p == ',' || (isascii(*p) && isspace(*p))))
700290792Sgshapiro			p++;
700390792Sgshapiro
700490792Sgshapiro		/* p now points to field code */
700590792Sgshapiro		fcode = *p;
700690792Sgshapiro		while (*p != '\0' && *p != '=' && *p != ',')
700790792Sgshapiro			p++;
700890792Sgshapiro		if (*p++ != '=')
700990792Sgshapiro		{
701090792Sgshapiro			syserr("queue %s: `=' expected", qg->qg_name);
701190792Sgshapiro			return;
701290792Sgshapiro		}
701390792Sgshapiro		while (isascii(*p) && isspace(*p))
701490792Sgshapiro			p++;
701590792Sgshapiro
701690792Sgshapiro		/* p now points to the field body */
701790792Sgshapiro		p = munchstring(p, &delimptr, ',');
701890792Sgshapiro
701990792Sgshapiro		/* install the field into the queue struct */
702090792Sgshapiro		switch (fcode)
702190792Sgshapiro		{
702290792Sgshapiro		  case 'P':		/* pathname */
702390792Sgshapiro			if (*p == '\0')
702490792Sgshapiro				syserr("queue %s: empty path name",
702590792Sgshapiro					qg->qg_name);
702690792Sgshapiro			else
702790792Sgshapiro				qg->qg_qdir = newstr(p);
702890792Sgshapiro			break;
702990792Sgshapiro
703090792Sgshapiro		  case 'F':		/* flags */
703190792Sgshapiro			for (; *p != '\0'; p++)
703290792Sgshapiro				if (!(isascii(*p) && isspace(*p)))
703390792Sgshapiro					setbitn(*p, qg->qg_flags);
703490792Sgshapiro			break;
703590792Sgshapiro
703690792Sgshapiro			/*
703790792Sgshapiro			**  Do we need two intervals here:
703890792Sgshapiro			**  One for persistent queue runners,
703990792Sgshapiro			**  one for "normal" queue runs?
704090792Sgshapiro			*/
704190792Sgshapiro
704290792Sgshapiro		  case 'I':	/* interval between running the queue */
704390792Sgshapiro			qg->qg_queueintvl = convtime(p, 'm');
704490792Sgshapiro			break;
704590792Sgshapiro
704690792Sgshapiro		  case 'N':		/* run niceness */
704790792Sgshapiro			qg->qg_nice = atoi(p);
704890792Sgshapiro			break;
704990792Sgshapiro
705090792Sgshapiro		  case 'R':		/* maximum # of runners for the group */
705190792Sgshapiro			i = atoi(p);
705290792Sgshapiro
705390792Sgshapiro			/* can't have more runners than allowed total */
705490792Sgshapiro			if (MaxQueueChildren > 0 && i > MaxQueueChildren)
705590792Sgshapiro			{
705690792Sgshapiro				qg->qg_maxqrun = MaxQueueChildren;
705790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
705890792Sgshapiro						     "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
705990792Sgshapiro						     qg->qg_name, i,
706090792Sgshapiro						     MaxQueueChildren);
706190792Sgshapiro			}
706290792Sgshapiro			else
706390792Sgshapiro				qg->qg_maxqrun = i;
706490792Sgshapiro			break;
706590792Sgshapiro
706690792Sgshapiro		  case 'J':		/* maximum # of jobs in work list */
706790792Sgshapiro			qg->qg_maxlist = atoi(p);
706890792Sgshapiro			break;
706990792Sgshapiro
707090792Sgshapiro		  case 'r':		/* max recipients per envelope */
707190792Sgshapiro			qg->qg_maxrcpt = atoi(p);
707290792Sgshapiro			break;
707390792Sgshapiro
707494334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER
707590792Sgshapiro		  case 'S':		/* queue sorting order */
707690792Sgshapiro			switch (*p)
707790792Sgshapiro			{
707890792Sgshapiro			  case 'h':	/* Host first */
707990792Sgshapiro			  case 'H':
708090792Sgshapiro				qg->qg_sortorder = QSO_BYHOST;
708190792Sgshapiro				break;
708290792Sgshapiro
708390792Sgshapiro			  case 'p':	/* Priority order */
708490792Sgshapiro			  case 'P':
708590792Sgshapiro				qg->qg_sortorder = QSO_BYPRIORITY;
708690792Sgshapiro				break;
708790792Sgshapiro
708890792Sgshapiro			  case 't':	/* Submission time */
708990792Sgshapiro			  case 'T':
709090792Sgshapiro				qg->qg_sortorder = QSO_BYTIME;
709190792Sgshapiro				break;
709290792Sgshapiro
709390792Sgshapiro			  case 'f':	/* File name */
709490792Sgshapiro			  case 'F':
709590792Sgshapiro				qg->qg_sortorder = QSO_BYFILENAME;
709690792Sgshapiro				break;
709790792Sgshapiro
709890792Sgshapiro			  case 'm':	/* Modification time */
709990792Sgshapiro			  case 'M':
710094334Sgshapiro				qg->qg_sortorder = QSO_BYMODTIME;
710190792Sgshapiro				break;
710290792Sgshapiro
710394334Sgshapiro			  case 'r':	/* Random */
710494334Sgshapiro			  case 'R':
710594334Sgshapiro				qg->qg_sortorder = QSO_RANDOM;
710694334Sgshapiro				break;
710794334Sgshapiro
710894334Sgshapiro# if _FFR_RHS
710994334Sgshapiro			  case 's':	/* Shuffled host name */
711094334Sgshapiro			  case 'S':
711194334Sgshapiro				qg->qg_sortorder = QSO_BYSHUFFLE;
711294334Sgshapiro				break;
711394334Sgshapiro# endif /* _FFR_RHS */
711494334Sgshapiro
711590792Sgshapiro			  default:
711690792Sgshapiro				syserr("Invalid queue sort order \"%s\"", p);
711790792Sgshapiro			}
711890792Sgshapiro			break;
711994334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */
712090792Sgshapiro
712190792Sgshapiro		  default:
712290792Sgshapiro			syserr("Q%s: unknown queue equate %c=",
712390792Sgshapiro			       qg->qg_name, fcode);
712490792Sgshapiro			break;
712590792Sgshapiro		}
712690792Sgshapiro
712790792Sgshapiro		p = delimptr;
712890792Sgshapiro	}
712990792Sgshapiro
713090792Sgshapiro#if !HASNICE
713190792Sgshapiro	if (qg->qg_nice != NiceQueueRun)
713290792Sgshapiro	{
713390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
713490792Sgshapiro				     "Q%s: Warning: N= set on system that doesn't support nice()\n",
713590792Sgshapiro				     qg->qg_name);
713690792Sgshapiro	}
713790792Sgshapiro#endif /* !HASNICE */
713890792Sgshapiro
713990792Sgshapiro	/* do some rationality checking */
714090792Sgshapiro	if (NumQueue >= MAXQUEUEGROUPS)
714190792Sgshapiro	{
714290792Sgshapiro		syserr("too many queue groups defined (%d max)",
714390792Sgshapiro			MAXQUEUEGROUPS);
714490792Sgshapiro		return;
714590792Sgshapiro	}
714690792Sgshapiro
714790792Sgshapiro	if (qg->qg_qdir == NULL)
714890792Sgshapiro	{
714990792Sgshapiro		if (QueueDir == NULL || *QueueDir == '\0')
715090792Sgshapiro		{
715190792Sgshapiro			syserr("QueueDir must be defined before queue groups");
715290792Sgshapiro			return;
715390792Sgshapiro		}
715490792Sgshapiro		qg->qg_qdir = newstr(QueueDir);
715590792Sgshapiro	}
715690792Sgshapiro
715790792Sgshapiro	if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
715890792Sgshapiro	{
715990792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
716090792Sgshapiro				     "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
716190792Sgshapiro				     qg->qg_name, qg->qg_maxqrun, QD_FORK);
716290792Sgshapiro	}
716390792Sgshapiro
716490792Sgshapiro	/* enter the queue into the symbol table */
716590792Sgshapiro	if (tTd(37, 8))
716690792Sgshapiro		sm_syslog(LOG_INFO, NOQID,
716790792Sgshapiro			  "Adding %s to stab, path: %s", qg->qg_name,
716890792Sgshapiro			  qg->qg_qdir);
716990792Sgshapiro	s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
717090792Sgshapiro	if (s->s_quegrp != NULL)
717190792Sgshapiro	{
717290792Sgshapiro		i = s->s_quegrp->qg_index;
717390792Sgshapiro
717490792Sgshapiro		/* XXX what about the pointers inside this struct? */
717590792Sgshapiro		sm_free(s->s_quegrp); /* XXX */
717690792Sgshapiro	}
717790792Sgshapiro	else
717890792Sgshapiro		i = NumQueue++;
717990792Sgshapiro	Queue[i] = s->s_quegrp = qg;
718090792Sgshapiro	qg->qg_index = i;
718190792Sgshapiro
718290792Sgshapiro	/* set default value for max queue runners */
718390792Sgshapiro	if (qg->qg_maxqrun < 0)
718490792Sgshapiro	{
718590792Sgshapiro		if (MaxRunnersPerQueue > 0)
718690792Sgshapiro			qg->qg_maxqrun = MaxRunnersPerQueue;
718790792Sgshapiro		else
718890792Sgshapiro			qg->qg_maxqrun = 1;
718990792Sgshapiro	}
719090792Sgshapiro	if (qdef)
719190792Sgshapiro		setbitn(QD_DEFINED, qg->qg_flags);
719290792Sgshapiro}
719390792Sgshapiro#if 0
719490792Sgshapiro/*
719564562Sgshapiro**  HASHFQN -- calculate a hash value for a fully qualified host name
719664562Sgshapiro**
719764562Sgshapiro**	Arguments:
719864562Sgshapiro**		fqn -- an all lower-case host.domain string
719964562Sgshapiro**		buckets -- the number of buckets (queue directories)
720064562Sgshapiro**
720164562Sgshapiro**	Returns:
720264562Sgshapiro**		a bucket number (signed integer)
720364562Sgshapiro**		-1 on error
720464562Sgshapiro**
720564562Sgshapiro**	Contributed by Exactis.com, Inc.
720664562Sgshapiro*/
720764562Sgshapiro
720864562Sgshapiroint
720964562Sgshapirohashfqn(fqn, buckets)
721064562Sgshapiro	register char *fqn;
721164562Sgshapiro	int buckets;
721264562Sgshapiro{
721364562Sgshapiro	register char *p;
721464562Sgshapiro	register int h = 0, hash, cnt;
721564562Sgshapiro
721664562Sgshapiro	if (fqn == NULL)
721764562Sgshapiro		return -1;
721864562Sgshapiro
721964562Sgshapiro	/*
722064562Sgshapiro	**  A variation on the gdb hash
722164562Sgshapiro	**  This is the best as of Feb 19, 1996 --bcx
722264562Sgshapiro	*/
722364562Sgshapiro
722464562Sgshapiro	p = fqn;
722564562Sgshapiro	h = 0x238F13AF * strlen(p);
722664562Sgshapiro	for (cnt = 0; *p != 0; ++p, cnt++)
722764562Sgshapiro	{
722864562Sgshapiro		h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
722964562Sgshapiro	}
723064562Sgshapiro	h = (1103515243 * h + 12345) & 0x7FFFFFFF;
723164562Sgshapiro	if (buckets < 2)
723264562Sgshapiro		hash = 0;
723364562Sgshapiro	else
723464562Sgshapiro		hash = (h % buckets);
723564562Sgshapiro
723664562Sgshapiro	return hash;
723764562Sgshapiro}
723890792Sgshapiro#endif /* 0 */
723964562Sgshapiro
724090792Sgshapiro#if _FFR_QUEUEDELAY
724190792Sgshapiro/*
724264562Sgshapiro**  QUEUEDELAY -- compute queue delay time
724364562Sgshapiro**
724464562Sgshapiro**	Parameters:
724564562Sgshapiro**		e -- the envelope to queue up.
724664562Sgshapiro**
724764562Sgshapiro**	Returns:
724864562Sgshapiro**		queue delay time
724964562Sgshapiro**
725064562Sgshapiro**	Side Effects:
725164562Sgshapiro**		may change e_queuedelay
725264562Sgshapiro*/
725364562Sgshapiro
725464562Sgshapirostatic time_t
725564562Sgshapiroqueuedelay(e)
725664562Sgshapiro	ENVELOPE *e;
725764562Sgshapiro{
725864562Sgshapiro	time_t qd;
725964562Sgshapiro
726064562Sgshapiro	if (e->e_queuealg == QD_EXP)
726164562Sgshapiro	{
726264562Sgshapiro		if (e->e_queuedelay == 0)
726364562Sgshapiro			e->e_queuedelay = QueueInitDelay;
726464562Sgshapiro		else
726564562Sgshapiro		{
726664562Sgshapiro			e->e_queuedelay *= 2;
726764562Sgshapiro			if (e->e_queuedelay > QueueMaxDelay)
726864562Sgshapiro				e->e_queuedelay = QueueMaxDelay;
726964562Sgshapiro		}
727064562Sgshapiro		qd = e->e_queuedelay;
727164562Sgshapiro	}
727264562Sgshapiro	else
727364562Sgshapiro		qd = MinQueueAge;
727464562Sgshapiro	return qd;
727564562Sgshapiro}
727690792Sgshapiro#endif /* _FFR_QUEUEDELAY */
727790792Sgshapiro
727890792Sgshapiro/*
727990792Sgshapiro**  A structure for sorting Queue according to maxqrun without
728090792Sgshapiro**	screwing up Queue itself.
728190792Sgshapiro*/
728290792Sgshapiro
728390792Sgshapirostruct sortqgrp
728490792Sgshapiro{
728590792Sgshapiro	int sg_idx;		/* original index */
728690792Sgshapiro	int sg_maxqrun;		/* max queue runners */
728790792Sgshapiro};
728890792Sgshapirotypedef struct sortqgrp	SORTQGRP_T;
728990792Sgshapirostatic int cmpidx __P((const void *, const void *));
729090792Sgshapiro
729190792Sgshapirostatic int
729290792Sgshapirocmpidx(a, b)
729390792Sgshapiro	const void *a;
729490792Sgshapiro	const void *b;
729590792Sgshapiro{
729690792Sgshapiro	/* The sort is highest to lowest, so the comparison is reversed */
729790792Sgshapiro	if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
729890792Sgshapiro		return 1;
729990792Sgshapiro	else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
730090792Sgshapiro		return -1;
730190792Sgshapiro	else
730290792Sgshapiro		return 0;
730390792Sgshapiro}
730490792Sgshapiro
730590792Sgshapiro/*
730690792Sgshapiro**  MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
730790792Sgshapiro**
730890792Sgshapiro**  Take the now defined queue groups and assign them to work groups.
730990792Sgshapiro**  This is done to balance out the number of concurrently active
731090792Sgshapiro**  queue runners such that MaxQueueChildren is not exceeded. This may
731190792Sgshapiro**  result in more than one queue group per work group. In such a case
731290792Sgshapiro**  the number of running queue groups in that work group will have no
731390792Sgshapiro**  more than the work group maximum number of runners (a "fair" portion
731490792Sgshapiro**  of MaxQueueRunners). All queue groups within a work group will get a
731590792Sgshapiro**  chance at running.
731690792Sgshapiro**
731790792Sgshapiro**	Parameters:
731890792Sgshapiro**		none.
731990792Sgshapiro**
732090792Sgshapiro**	Returns:
732190792Sgshapiro**		nothing.
732290792Sgshapiro**
732390792Sgshapiro**	Side Effects:
732490792Sgshapiro**		Sets up WorkGrp structure.
732590792Sgshapiro*/
732690792Sgshapiro
732790792Sgshapirovoid
732890792Sgshapiromakeworkgroups()
732990792Sgshapiro{
733090792Sgshapiro	int i, j, total_runners = 0;
733190792Sgshapiro	int dir;
733290792Sgshapiro	SORTQGRP_T si[MAXQUEUEGROUPS + 1];
733390792Sgshapiro
733490792Sgshapiro	if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
733590792Sgshapiro	{
733690792Sgshapiro		/*
733790792Sgshapiro		**  There is only the "mqueue" queue group (a default)
733890792Sgshapiro		**  containing all of the queues. We want to provide to
733990792Sgshapiro		**  this queue group the maximum allowable queue runners.
734090792Sgshapiro		**  To match older behavior (8.10/8.11) we'll try for
734190792Sgshapiro		**  1 runner per queue capping it at MaxQueueChildren.
734290792Sgshapiro		**  So if there are N queues, then there will be N runners
734390792Sgshapiro		**  for the "mqueue" queue group (where N is kept less than
734490792Sgshapiro		**  MaxQueueChildren).
734590792Sgshapiro		*/
734690792Sgshapiro
734790792Sgshapiro		NumWorkGroups = 1;
734890792Sgshapiro		WorkGrp[0].wg_numqgrp = 1;
734990792Sgshapiro		WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
735090792Sgshapiro		WorkGrp[0].wg_qgs[0] = Queue[0];
735190792Sgshapiro		if (MaxQueueChildren > 0 &&
735290792Sgshapiro		    Queue[0]->qg_numqueues > MaxQueueChildren)
735390792Sgshapiro			WorkGrp[0].wg_runners = MaxQueueChildren;
735490792Sgshapiro		else
735590792Sgshapiro			WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
735690792Sgshapiro
735790792Sgshapiro		Queue[0]->qg_wgrp = 0;
735890792Sgshapiro
735990792Sgshapiro		/* can't have more runners than allowed total */
736090792Sgshapiro		if (MaxQueueChildren > 0 &&
736190792Sgshapiro		    Queue[0]->qg_maxqrun > MaxQueueChildren)
736290792Sgshapiro			Queue[0]->qg_maxqrun = MaxQueueChildren;
736390792Sgshapiro		WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
736490792Sgshapiro		WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
736590792Sgshapiro		return;
736690792Sgshapiro	}
736790792Sgshapiro
736890792Sgshapiro	for (i = 0; i < NumQueue; i++)
736990792Sgshapiro	{
737090792Sgshapiro		si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
737190792Sgshapiro		si[i].sg_idx = i;
737290792Sgshapiro	}
737390792Sgshapiro	qsort(si, NumQueue, sizeof(si[0]), cmpidx);
737490792Sgshapiro
737590792Sgshapiro	NumWorkGroups = 0;
737690792Sgshapiro	for (i = 0; i < NumQueue; i++)
737790792Sgshapiro	{
737890792Sgshapiro		total_runners += si[i].sg_maxqrun;
737990792Sgshapiro		if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
738090792Sgshapiro			NumWorkGroups++;
738190792Sgshapiro		else
738290792Sgshapiro			break;
738390792Sgshapiro	}
738490792Sgshapiro
738590792Sgshapiro	if (NumWorkGroups < 1)
738690792Sgshapiro		NumWorkGroups = 1; /* gotta have one at least */
738790792Sgshapiro	else if (NumWorkGroups > MAXWORKGROUPS)
738890792Sgshapiro		NumWorkGroups = MAXWORKGROUPS; /* the limit */
738990792Sgshapiro
739090792Sgshapiro	/*
739190792Sgshapiro	**  We now know the number of work groups to pack the queue groups
739290792Sgshapiro	**  into. The queue groups in 'Queue' are sorted from highest
739390792Sgshapiro	**  to lowest for the number of runners per queue group.
739490792Sgshapiro	**  We put the queue groups with the largest number of runners
739590792Sgshapiro	**  into work groups first. Then the smaller ones are fitted in
739690792Sgshapiro	**  where it looks best.
739790792Sgshapiro	*/
739890792Sgshapiro
739990792Sgshapiro	j = 0;
740090792Sgshapiro	dir = 1;
740190792Sgshapiro	for (i = 0; i < NumQueue; i++)
740290792Sgshapiro	{
740390792Sgshapiro		/* a to-and-fro packing scheme, continue from last position */
740490792Sgshapiro		if (j >= NumWorkGroups)
740590792Sgshapiro		{
740690792Sgshapiro			dir = -1;
740790792Sgshapiro			j = NumWorkGroups - 1;
740890792Sgshapiro		}
740990792Sgshapiro		else if (j < 0)
741090792Sgshapiro		{
741190792Sgshapiro			j = 0;
741290792Sgshapiro			dir = 1;
741390792Sgshapiro		}
741490792Sgshapiro
741590792Sgshapiro		if (WorkGrp[j].wg_qgs == NULL)
741694334Sgshapiro			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) *
741794334Sgshapiro							(WorkGrp[j].wg_numqgrp + 1));
741894334Sgshapiro		else
741994334Sgshapiro			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
742094334Sgshapiro							sizeof(QUEUEGRP *) *
742194334Sgshapiro							(WorkGrp[j].wg_numqgrp + 1));
742294334Sgshapiro		if (WorkGrp[j].wg_qgs == NULL)
742390792Sgshapiro		{
742494334Sgshapiro			syserr("!cannot allocate memory for work queues, need %d bytes",
742590792Sgshapiro			       (int) (sizeof(QUEUEGRP *) *
742690792Sgshapiro				      (WorkGrp[j].wg_numqgrp + 1)));
742790792Sgshapiro		}
742890792Sgshapiro
742990792Sgshapiro		WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[si[i].sg_idx];
743090792Sgshapiro		WorkGrp[j].wg_numqgrp++;
743190792Sgshapiro		WorkGrp[j].wg_runners += Queue[i]->qg_maxqrun;
743290792Sgshapiro		Queue[si[i].sg_idx]->qg_wgrp = j;
743390792Sgshapiro
743490792Sgshapiro		if (WorkGrp[j].wg_maxact == 0)
743590792Sgshapiro		{
743690792Sgshapiro			/* can't have more runners than allowed total */
743790792Sgshapiro			if (MaxQueueChildren > 0 &&
743890792Sgshapiro			    Queue[i]->qg_maxqrun > MaxQueueChildren)
743990792Sgshapiro				Queue[i]->qg_maxqrun = MaxQueueChildren;
744090792Sgshapiro			WorkGrp[j].wg_maxact = Queue[i]->qg_maxqrun;
744190792Sgshapiro		}
744290792Sgshapiro
744390792Sgshapiro		/*
744490792Sgshapiro		**  XXX: must wg_lowqintvl be the GCD?
744590792Sgshapiro		**  qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
744690792Sgshapiro		**  qg2 occur?
744790792Sgshapiro		*/
744890792Sgshapiro
744990792Sgshapiro		/* keep track of the lowest interval for a persistent runner */
745090792Sgshapiro		if (Queue[si[i].sg_idx]->qg_queueintvl > 0 &&
745190792Sgshapiro		    WorkGrp[j].wg_lowqintvl < Queue[si[i].sg_idx]->qg_queueintvl)
745290792Sgshapiro			WorkGrp[j].wg_lowqintvl = Queue[si[i].sg_idx]->qg_queueintvl;
745390792Sgshapiro		j += dir;
745490792Sgshapiro	}
745590792Sgshapiro	if (tTd(41, 9))
745690792Sgshapiro	{
745790792Sgshapiro		for (i = 0; i < NumWorkGroups; i++)
745890792Sgshapiro		{
745990792Sgshapiro			sm_dprintf("Workgroup[%d]=", i);
746090792Sgshapiro			for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
746190792Sgshapiro			{
746290792Sgshapiro				sm_dprintf("%s, ",
746390792Sgshapiro					WorkGrp[i].wg_qgs[j]->qg_name);
746490792Sgshapiro			}
746590792Sgshapiro			sm_dprintf("\n");
746690792Sgshapiro		}
746790792Sgshapiro	}
746890792Sgshapiro}
746990792Sgshapiro
747090792Sgshapiro/*
747190792Sgshapiro**  DUP_DF -- duplicate envelope data file
747290792Sgshapiro**
747390792Sgshapiro**	Copy the data file from the 'old' envelope to the 'new' envelope
747490792Sgshapiro**	in the most efficient way possible.
747590792Sgshapiro**
747690792Sgshapiro**	Create a hard link from the 'old' data file to the 'new' data file.
747790792Sgshapiro**	If the old and new queue directories are on different file systems,
747890792Sgshapiro**	then the new data file link is created in the old queue directory,
747990792Sgshapiro**	and the new queue file will contain a 'd' record pointing to the
748090792Sgshapiro**	directory containing the new data file.
748190792Sgshapiro**
748290792Sgshapiro**	Parameters:
748390792Sgshapiro**		old -- old envelope.
748490792Sgshapiro**		new -- new envelope.
748590792Sgshapiro**
748690792Sgshapiro**	Results:
748790792Sgshapiro**		Returns true on success, false on failure.
748890792Sgshapiro**
748990792Sgshapiro**	Side Effects:
749090792Sgshapiro**		On success, the new data file is created.
749190792Sgshapiro**		On fatal failure, EF_FATALERRS is set in old->e_flags.
749290792Sgshapiro*/
749390792Sgshapiro
749490792Sgshapirostatic bool	dup_df __P((ENVELOPE *, ENVELOPE *));
749590792Sgshapiro
749690792Sgshapirostatic bool
749790792Sgshapirodup_df(old, new)
749890792Sgshapiro	ENVELOPE *old;
749990792Sgshapiro	ENVELOPE *new;
750090792Sgshapiro{
750190792Sgshapiro	int ofs, nfs, r;
750290792Sgshapiro	char opath[MAXPATHLEN];
750390792Sgshapiro	char npath[MAXPATHLEN];
750490792Sgshapiro
750594334Sgshapiro	if (!bitset(EF_HAS_DF, old->e_flags))
750694334Sgshapiro	{
750794334Sgshapiro		/*
750894334Sgshapiro		**  this can happen if: SuperSafe != True
750994334Sgshapiro		**  and a bounce mail is sent that is split.
751094334Sgshapiro		*/
751194334Sgshapiro
751294334Sgshapiro		queueup(old, false, true);
751394334Sgshapiro	}
751490792Sgshapiro	SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
751590792Sgshapiro	SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
751690792Sgshapiro
751790792Sgshapiro	(void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof opath);
751890792Sgshapiro	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath);
751990792Sgshapiro
752090792Sgshapiro	if (old->e_dfp != NULL)
752190792Sgshapiro	{
752290792Sgshapiro		r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
752390792Sgshapiro		if (r < 0 && errno != EINVAL)
752490792Sgshapiro		{
752590792Sgshapiro			syserr("@can't commit %s", opath);
752690792Sgshapiro			old->e_flags |= EF_FATALERRS;
752790792Sgshapiro			return false;
752890792Sgshapiro		}
752990792Sgshapiro	}
753090792Sgshapiro
753190792Sgshapiro	/*
753290792Sgshapiro	**  Attempt to create a hard link, if we think both old and new
753390792Sgshapiro	**  are on the same file system, otherwise copy the file.
753490792Sgshapiro	**
753590792Sgshapiro	**  Don't waste time attempting a hard link unless old and new
753690792Sgshapiro	**  are on the same file system.
753790792Sgshapiro	*/
753890792Sgshapiro
753990792Sgshapiro	ofs = Queue[old->e_qgrp]->qg_qpaths[old->e_qdir].qp_fsysidx;
754090792Sgshapiro	nfs = Queue[new->e_qgrp]->qg_qpaths[new->e_qdir].qp_fsysidx;
754190792Sgshapiro	if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
754290792Sgshapiro	{
754390792Sgshapiro		if (link(opath, npath) == 0)
754490792Sgshapiro		{
754590792Sgshapiro			new->e_flags |= EF_HAS_DF;
754690792Sgshapiro			SYNC_DIR(npath, true);
754790792Sgshapiro			return true;
754890792Sgshapiro		}
754990792Sgshapiro		goto error;
755090792Sgshapiro	}
755190792Sgshapiro
755290792Sgshapiro	/*
755390792Sgshapiro	**  Can't link across queue directories, so try to create a hard
755490792Sgshapiro	**  link in the same queue directory as the old df file.
755590792Sgshapiro	**  The qf file will refer to the new df file using a 'd' record.
755690792Sgshapiro	*/
755790792Sgshapiro
755890792Sgshapiro	new->e_dfqgrp = old->e_dfqgrp;
755990792Sgshapiro	new->e_dfqdir = old->e_dfqdir;
756090792Sgshapiro	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath);
756190792Sgshapiro	if (link(opath, npath) == 0)
756290792Sgshapiro	{
756390792Sgshapiro		new->e_flags |= EF_HAS_DF;
756490792Sgshapiro		SYNC_DIR(npath, true);
756590792Sgshapiro		return true;
756690792Sgshapiro	}
756790792Sgshapiro
756890792Sgshapiro  error:
756990792Sgshapiro	if (LogLevel > 0)
757090792Sgshapiro		sm_syslog(LOG_ERR, old->e_id,
757190792Sgshapiro			  "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
757290792Sgshapiro			  opath, npath, sm_errstring(errno));
757390792Sgshapiro	return false;
757490792Sgshapiro}
757590792Sgshapiro
757690792Sgshapiro/*
757790792Sgshapiro**  SPLIT_ENV -- Allocate a new envelope based on a given envelope.
757890792Sgshapiro**
757990792Sgshapiro**	Parameters:
758090792Sgshapiro**		e -- envelope.
758190792Sgshapiro**		sendqueue -- sendqueue for new envelope.
758290792Sgshapiro**		qgrp -- index of queue group.
758390792Sgshapiro**		qdir -- queue directory.
758490792Sgshapiro**
758590792Sgshapiro**	Results:
758690792Sgshapiro**		new envelope.
758790792Sgshapiro**
758890792Sgshapiro*/
758990792Sgshapiro
759090792Sgshapirostatic ENVELOPE	*split_env __P((ENVELOPE *, ADDRESS *, int, int));
759190792Sgshapiro
759290792Sgshapirostatic ENVELOPE *
759390792Sgshapirosplit_env(e, sendqueue, qgrp, qdir)
759490792Sgshapiro	ENVELOPE *e;
759590792Sgshapiro	ADDRESS *sendqueue;
759690792Sgshapiro	int qgrp;
759790792Sgshapiro	int qdir;
759890792Sgshapiro{
759990792Sgshapiro	ENVELOPE *ee;
760090792Sgshapiro
760190792Sgshapiro	ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof *ee);
760290792Sgshapiro	STRUCTCOPY(*e, *ee);
760390792Sgshapiro	ee->e_message = NULL;	/* XXX use original message? */
760490792Sgshapiro	ee->e_id = NULL;
760590792Sgshapiro	assign_queueid(ee);
760690792Sgshapiro	ee->e_sendqueue = sendqueue;
760790792Sgshapiro	ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
760890792Sgshapiro			 |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
760990792Sgshapiro	ee->e_flags |= EF_NORECEIPT;	/* XXX really? */
761090792Sgshapiro	ee->e_from.q_state = QS_SENDER;
761190792Sgshapiro	ee->e_dfp = NULL;
761290792Sgshapiro	ee->e_lockfp = NULL;
761390792Sgshapiro	if (e->e_xfp != NULL)
761490792Sgshapiro		ee->e_xfp = sm_io_dup(e->e_xfp);
761594334Sgshapiro
761694334Sgshapiro	/* failed to dup e->e_xfp, start a new transcript */
761794334Sgshapiro	if (ee->e_xfp == NULL)
761894334Sgshapiro		openxscript(ee);
761994334Sgshapiro
762090792Sgshapiro	ee->e_qgrp = ee->e_dfqgrp = qgrp;
762190792Sgshapiro	ee->e_qdir = ee->e_dfqdir = qdir;
762290792Sgshapiro	ee->e_errormode = EM_MAIL;
762390792Sgshapiro	ee->e_statmsg = NULL;
762490792Sgshapiro#if _FFR_QUARANTINE
762590792Sgshapiro	if (e->e_quarmsg != NULL)
762690792Sgshapiro		ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
762790792Sgshapiro						  e->e_quarmsg);
762890792Sgshapiro#endif /* _FFR_QUARANTINE */
762990792Sgshapiro
763090792Sgshapiro	/*
763190792Sgshapiro	**  XXX Not sure if this copying is necessary.
763294334Sgshapiro	**  sendall() does this copying, but I (dm) don't know if that is
763390792Sgshapiro	**  because of the storage management discipline we were using
763490792Sgshapiro	**  before rpools were introduced, or if it is because these lists
763590792Sgshapiro	**  can be modified later.
763690792Sgshapiro	*/
763790792Sgshapiro
763890792Sgshapiro	ee->e_header = copyheader(e->e_header, ee->e_rpool);
763990792Sgshapiro	ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
764090792Sgshapiro
764190792Sgshapiro	return ee;
764290792Sgshapiro}
764390792Sgshapiro
764490792Sgshapiro/* return values from split functions, check also below! */
764590792Sgshapiro#define SM_SPLIT_FAIL	(0)
764690792Sgshapiro#define SM_SPLIT_NONE	(1)
764790792Sgshapiro#define SM_SPLIT_NEW(n)	(1 + (n))
764890792Sgshapiro
764990792Sgshapiro/*
765090792Sgshapiro**  SPLIT_ACROSS_QUEUE_GROUPS
765190792Sgshapiro**
765290792Sgshapiro**	This function splits an envelope across multiple queue groups
765390792Sgshapiro**	based on the queue group of each recipient.
765490792Sgshapiro**
765590792Sgshapiro**	Parameters:
765690792Sgshapiro**		e -- envelope.
765790792Sgshapiro**
765890792Sgshapiro**	Results:
765990792Sgshapiro**		SM_SPLIT_FAIL on failure
766090792Sgshapiro**		SM_SPLIT_NONE if no splitting occurred,
766190792Sgshapiro**		or 1 + the number of additional envelopes created.
766290792Sgshapiro**
766390792Sgshapiro**	Side Effects:
766490792Sgshapiro**		On success, e->e_sibling points to a list of zero or more
766590792Sgshapiro**		additional envelopes, and the associated data files exist
766690792Sgshapiro**		on disk.  But the queue files are not created.
766790792Sgshapiro**
766890792Sgshapiro**		On failure, e->e_sibling is not changed.
766990792Sgshapiro**		The order of recipients in e->e_sendqueue is permuted.
767090792Sgshapiro**		Abandoned data files for additional envelopes that failed
767190792Sgshapiro**		to be created may exist on disk.
767290792Sgshapiro*/
767390792Sgshapiro
767490792Sgshapirostatic int	q_qgrp_compare __P((const void *, const void *));
767590792Sgshapirostatic int	e_filesys_compare __P((const void *, const void *));
767690792Sgshapiro
767790792Sgshapirostatic int
767890792Sgshapiroq_qgrp_compare(p1, p2)
767990792Sgshapiro	const void *p1;
768090792Sgshapiro	const void *p2;
768190792Sgshapiro{
768290792Sgshapiro	ADDRESS **pq1 = (ADDRESS **) p1;
768390792Sgshapiro	ADDRESS **pq2 = (ADDRESS **) p2;
768490792Sgshapiro
768590792Sgshapiro	return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
768690792Sgshapiro}
768790792Sgshapiro
768890792Sgshapirostatic int
768990792Sgshapiroe_filesys_compare(p1, p2)
769090792Sgshapiro	const void *p1;
769190792Sgshapiro	const void *p2;
769290792Sgshapiro{
769390792Sgshapiro	ENVELOPE **pe1 = (ENVELOPE **) p1;
769490792Sgshapiro	ENVELOPE **pe2 = (ENVELOPE **) p2;
769590792Sgshapiro	int fs1, fs2;
769690792Sgshapiro
769790792Sgshapiro	fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
769890792Sgshapiro	fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
769990792Sgshapiro	if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
770090792Sgshapiro		return -1;
770190792Sgshapiro	if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
770290792Sgshapiro		return 1;
770390792Sgshapiro	return 0;
770490792Sgshapiro}
770590792Sgshapiro
770690792Sgshapirostatic int
770790792Sgshapirosplit_across_queue_groups(e)
770890792Sgshapiro	ENVELOPE *e;
770990792Sgshapiro{
771090792Sgshapiro	int naddrs, nsplits, i;
7711102528Sgshapiro	bool changed;
771290792Sgshapiro	char **pvp;
771390792Sgshapiro	ADDRESS *q, **addrs;
771490792Sgshapiro	ENVELOPE *ee, *es;
771590792Sgshapiro	ENVELOPE *splits[MAXQUEUEGROUPS];
771690792Sgshapiro	char pvpbuf[PSBUFSIZE];
771790792Sgshapiro
771890792Sgshapiro	SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
771990792Sgshapiro
772090792Sgshapiro	/* Count addresses and assign queue groups. */
772190792Sgshapiro	naddrs = 0;
7722102528Sgshapiro	changed = false;
772390792Sgshapiro	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
772490792Sgshapiro	{
772590792Sgshapiro		if (QS_IS_DEAD(q->q_state))
772690792Sgshapiro			continue;
772790792Sgshapiro		++naddrs;
772890792Sgshapiro
772990792Sgshapiro		/* bad addresses and those already sent stay put */
773090792Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
773190792Sgshapiro		    QS_IS_SENT(q->q_state))
773290792Sgshapiro			q->q_qgrp = e->e_qgrp;
773390792Sgshapiro		else if (!ISVALIDQGRP(q->q_qgrp))
773490792Sgshapiro		{
773590792Sgshapiro			/* call ruleset which should return a queue group */
773690792Sgshapiro			i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
773790792Sgshapiro				  pvpbuf, sizeof(pvpbuf));
773890792Sgshapiro			if (i == EX_OK &&
773990792Sgshapiro			    pvp != NULL && pvp[0] != NULL &&
774090792Sgshapiro			    (pvp[0][0] & 0377) == CANONNET &&
774190792Sgshapiro			    pvp[1] != NULL && pvp[1][0] != '\0')
774290792Sgshapiro			{
774390792Sgshapiro				i = name2qid(pvp[1]);
774490792Sgshapiro				if (ISVALIDQGRP(i))
774590792Sgshapiro				{
774690792Sgshapiro					q->q_qgrp = i;
7747102528Sgshapiro					changed = true;
774890792Sgshapiro					if (tTd(20, 4))
774990792Sgshapiro						sm_syslog(LOG_INFO, NOQID,
775090792Sgshapiro							"queue group name %s -> %d",
775190792Sgshapiro							pvp[1], i);
775290792Sgshapiro					continue;
775390792Sgshapiro				}
775490792Sgshapiro				else if (LogLevel > 10)
775590792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
775690792Sgshapiro						"can't find queue group name %s, selection ignored",
775790792Sgshapiro						pvp[1]);
775890792Sgshapiro			}
775990792Sgshapiro			if (q->q_mailer != NULL &&
776090792Sgshapiro			    ISVALIDQGRP(q->q_mailer->m_qgrp))
7761102528Sgshapiro			{
7762102528Sgshapiro				changed = true;
776390792Sgshapiro				q->q_qgrp = q->q_mailer->m_qgrp;
7764102528Sgshapiro			}
776594334Sgshapiro			else if (ISVALIDQGRP(e->e_qgrp))
776694334Sgshapiro				q->q_qgrp = e->e_qgrp;
776790792Sgshapiro			else
776890792Sgshapiro				q->q_qgrp = 0;
776990792Sgshapiro		}
777090792Sgshapiro	}
777190792Sgshapiro
777290792Sgshapiro	/* only one address? nothing to split. */
7773102528Sgshapiro	if (naddrs <= 1 && !changed)
777490792Sgshapiro		return SM_SPLIT_NONE;
777590792Sgshapiro
777690792Sgshapiro	/* sort the addresses by queue group */
777790792Sgshapiro	addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
777890792Sgshapiro	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
777990792Sgshapiro	{
778090792Sgshapiro		if (QS_IS_DEAD(q->q_state))
778190792Sgshapiro			continue;
778290792Sgshapiro		addrs[i++] = q;
778390792Sgshapiro	}
778490792Sgshapiro	qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
778590792Sgshapiro
778690792Sgshapiro	/* split into multiple envelopes, by queue group */
778790792Sgshapiro	nsplits = 0;
778890792Sgshapiro	es = NULL;
778990792Sgshapiro	e->e_sendqueue = NULL;
779090792Sgshapiro	for (i = 0; i < naddrs; ++i)
779190792Sgshapiro	{
779290792Sgshapiro		if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
779390792Sgshapiro			addrs[i]->q_next = NULL;
779490792Sgshapiro		else
779590792Sgshapiro			addrs[i]->q_next = addrs[i + 1];
779690792Sgshapiro
779790792Sgshapiro		/* same queue group as original envelope? */
779890792Sgshapiro		if (addrs[i]->q_qgrp == e->e_qgrp)
779990792Sgshapiro		{
780090792Sgshapiro			if (e->e_sendqueue == NULL)
780190792Sgshapiro				e->e_sendqueue = addrs[i];
780290792Sgshapiro			continue;
780390792Sgshapiro		}
780490792Sgshapiro
780590792Sgshapiro		/* different queue group than original envelope */
780690792Sgshapiro		if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
780790792Sgshapiro		{
780890792Sgshapiro			ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
780990792Sgshapiro			es = ee;
781090792Sgshapiro			splits[nsplits++] = ee;
781190792Sgshapiro		}
781290792Sgshapiro	}
781390792Sgshapiro
781490792Sgshapiro	/* no splits? return right now. */
781590792Sgshapiro	if (nsplits <= 0)
781690792Sgshapiro		return SM_SPLIT_NONE;
781790792Sgshapiro
781890792Sgshapiro	/* assign a queue directory to each additional envelope */
781990792Sgshapiro	for (i = 0; i < nsplits; ++i)
782090792Sgshapiro	{
782190792Sgshapiro		es = splits[i];
782290792Sgshapiro#if 0
782390792Sgshapiro		es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
782490792Sgshapiro#endif /* 0 */
782590792Sgshapiro		if (!setnewqueue(es))
782690792Sgshapiro			goto failure;
782790792Sgshapiro	}
782890792Sgshapiro
782990792Sgshapiro	/* sort the additional envelopes by queue file system */
783090792Sgshapiro	qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
783190792Sgshapiro
783290792Sgshapiro	/* create data files for each additional envelope */
783390792Sgshapiro	if (!dup_df(e, splits[0]))
783490792Sgshapiro	{
783590792Sgshapiro		i = 0;
783690792Sgshapiro		goto failure;
783790792Sgshapiro	}
783890792Sgshapiro	for (i = 1; i < nsplits; ++i)
783990792Sgshapiro	{
784090792Sgshapiro		/* copy or link to the previous data file */
784190792Sgshapiro		if (!dup_df(splits[i - 1], splits[i]))
784290792Sgshapiro			goto failure;
784390792Sgshapiro	}
784490792Sgshapiro
784590792Sgshapiro	/* success: prepend the new envelopes to the e->e_sibling list */
784690792Sgshapiro	for (i = 0; i < nsplits; ++i)
784790792Sgshapiro	{
784890792Sgshapiro		es = splits[i];
784990792Sgshapiro		es->e_sibling = e->e_sibling;
785090792Sgshapiro		e->e_sibling = es;
785190792Sgshapiro	}
785290792Sgshapiro	return SM_SPLIT_NEW(nsplits);
785390792Sgshapiro
785490792Sgshapiro	/* failure: clean up */
785590792Sgshapiro  failure:
785690792Sgshapiro	if (i > 0)
785790792Sgshapiro	{
785890792Sgshapiro		int j;
785990792Sgshapiro
786090792Sgshapiro		for (j = 0; j < i; j++)
786190792Sgshapiro			(void) unlink(queuename(splits[j], DATAFL_LETTER));
786290792Sgshapiro	}
786390792Sgshapiro	e->e_sendqueue = addrs[0];
786490792Sgshapiro	for (i = 0; i < naddrs - 1; ++i)
786590792Sgshapiro		addrs[i]->q_next = addrs[i + 1];
786690792Sgshapiro	addrs[naddrs - 1]->q_next = NULL;
786790792Sgshapiro	return SM_SPLIT_FAIL;
786890792Sgshapiro}
786990792Sgshapiro
787090792Sgshapiro/*
787190792Sgshapiro**  SPLIT_WITHIN_QUEUE
787290792Sgshapiro**
787390792Sgshapiro**	Split an envelope with multiple recipients into several
787490792Sgshapiro**	envelopes within the same queue directory, if the number of
787590792Sgshapiro**	recipients exceeds the limit for the queue group.
787690792Sgshapiro**
787790792Sgshapiro**	Parameters:
787890792Sgshapiro**		e -- envelope.
787990792Sgshapiro**
788090792Sgshapiro**	Results:
788190792Sgshapiro**		SM_SPLIT_FAIL on failure
788290792Sgshapiro**		SM_SPLIT_NONE if no splitting occurred,
788390792Sgshapiro**		or 1 + the number of additional envelopes created.
788490792Sgshapiro*/
788590792Sgshapiro
788690792Sgshapiro#define SPLIT_LOG_LEVEL	8
788790792Sgshapiro
788890792Sgshapirostatic int	split_within_queue __P((ENVELOPE *));
788990792Sgshapiro
789090792Sgshapirostatic int
789190792Sgshapirosplit_within_queue(e)
789290792Sgshapiro	ENVELOPE *e;
789390792Sgshapiro{
789490792Sgshapiro	int maxrcpt, nrcpt, ndead, nsplit, i;
789590792Sgshapiro	int j, l;
789690792Sgshapiro	char *lsplits;
789790792Sgshapiro	ADDRESS *q, **addrs;
789890792Sgshapiro	ENVELOPE *ee, *firstsibling;
789990792Sgshapiro
790090792Sgshapiro	if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
790190792Sgshapiro		return SM_SPLIT_NONE;
790290792Sgshapiro
790390792Sgshapiro	/* don't bother if there is no recipient limit */
790490792Sgshapiro	maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
790590792Sgshapiro	if (maxrcpt <= 0)
790690792Sgshapiro		return SM_SPLIT_NONE;
790790792Sgshapiro
790890792Sgshapiro	/* count recipients */
790990792Sgshapiro	nrcpt = 0;
791090792Sgshapiro	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
791190792Sgshapiro	{
791290792Sgshapiro		if (QS_IS_DEAD(q->q_state))
791390792Sgshapiro			continue;
791490792Sgshapiro		++nrcpt;
791590792Sgshapiro	}
791690792Sgshapiro	if (nrcpt <= maxrcpt)
791790792Sgshapiro		return SM_SPLIT_NONE;
791890792Sgshapiro
791990792Sgshapiro	/*
792090792Sgshapiro	**  Preserve the recipient list
792190792Sgshapiro	**  so that we can restore it in case of error.
792290792Sgshapiro	**  (But we discard dead addresses.)
792390792Sgshapiro	*/
792490792Sgshapiro
792590792Sgshapiro	addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
792690792Sgshapiro	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
792790792Sgshapiro	{
792890792Sgshapiro		if (QS_IS_DEAD(q->q_state))
792990792Sgshapiro			continue;
793090792Sgshapiro		addrs[i++] = q;
793190792Sgshapiro	}
793290792Sgshapiro
793390792Sgshapiro	/*
793490792Sgshapiro	**  Partition the recipient list so that bad and sent addresses
793590792Sgshapiro	**  come first. These will go with the original envelope, and
793690792Sgshapiro	**  do not count towards the maxrcpt limit.
793790792Sgshapiro	**  addrs[] does not contain QS_IS_DEAD() addresses.
793890792Sgshapiro	*/
793990792Sgshapiro
794090792Sgshapiro	ndead = 0;
794190792Sgshapiro	for (i = 0; i < nrcpt; ++i)
794290792Sgshapiro	{
794390792Sgshapiro		if (QS_IS_BADADDR(addrs[i]->q_state) ||
794490792Sgshapiro		    QS_IS_SENT(addrs[i]->q_state) ||
794590792Sgshapiro		    QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
794690792Sgshapiro		{
794790792Sgshapiro			if (i > ndead)
794890792Sgshapiro			{
794990792Sgshapiro				ADDRESS *tmp = addrs[i];
795090792Sgshapiro
795190792Sgshapiro				addrs[i] = addrs[ndead];
795290792Sgshapiro				addrs[ndead] = tmp;
795390792Sgshapiro			}
795490792Sgshapiro			++ndead;
795590792Sgshapiro		}
795690792Sgshapiro	}
795790792Sgshapiro
795890792Sgshapiro	/* Check if no splitting required. */
795990792Sgshapiro	if (nrcpt - ndead <= maxrcpt)
796090792Sgshapiro		return SM_SPLIT_NONE;
796190792Sgshapiro
796290792Sgshapiro	/* fix links */
796390792Sgshapiro	for (i = 0; i < nrcpt - 1; ++i)
796490792Sgshapiro		addrs[i]->q_next = addrs[i + 1];
796590792Sgshapiro	addrs[nrcpt - 1]->q_next = NULL;
796690792Sgshapiro	e->e_sendqueue = addrs[0];
796790792Sgshapiro
796890792Sgshapiro	/* prepare buffer for logging */
796990792Sgshapiro	if (LogLevel > SPLIT_LOG_LEVEL)
797090792Sgshapiro	{
797190792Sgshapiro		l = MAXLINE;
797290792Sgshapiro		lsplits = sm_malloc(l);
797390792Sgshapiro		if (lsplits != NULL)
797490792Sgshapiro			*lsplits = '\0';
797590792Sgshapiro		j = 0;
797690792Sgshapiro	}
797790792Sgshapiro	else
797890792Sgshapiro	{
797990792Sgshapiro		/* get rid of stupid compiler warnings */
798090792Sgshapiro		lsplits = NULL;
798190792Sgshapiro		j = l = 0;
798290792Sgshapiro	}
798390792Sgshapiro
798490792Sgshapiro	/* split the envelope */
798590792Sgshapiro	firstsibling = e->e_sibling;
798690792Sgshapiro	i = maxrcpt + ndead;
798790792Sgshapiro	nsplit = 0;
798890792Sgshapiro	for (;;)
798990792Sgshapiro	{
799090792Sgshapiro		addrs[i - 1]->q_next = NULL;
799190792Sgshapiro		ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
799290792Sgshapiro		if (!dup_df(e, ee))
799390792Sgshapiro		{
799490792Sgshapiro
799590792Sgshapiro			ee = firstsibling;
799690792Sgshapiro			while (ee != NULL)
799790792Sgshapiro			{
799890792Sgshapiro				(void) unlink(queuename(ee, DATAFL_LETTER));
799990792Sgshapiro				ee = ee->e_sibling;
800090792Sgshapiro			}
800190792Sgshapiro
800290792Sgshapiro			/* Error.  Restore e's sibling & recipient lists. */
800390792Sgshapiro			e->e_sibling = firstsibling;
800490792Sgshapiro			for (i = 0; i < nrcpt - 1; ++i)
800590792Sgshapiro				addrs[i]->q_next = addrs[i + 1];
8006110560Sgshapiro			if (lsplits != NULL)
8007110560Sgshapiro				sm_free(lsplits);
800890792Sgshapiro			return SM_SPLIT_FAIL;
800990792Sgshapiro		}
801090792Sgshapiro
801190792Sgshapiro		/* prepend the new envelope to e->e_sibling */
801290792Sgshapiro		ee->e_sibling = e->e_sibling;
801390792Sgshapiro		e->e_sibling = ee;
801490792Sgshapiro		++nsplit;
801590792Sgshapiro		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
801690792Sgshapiro		{
801790792Sgshapiro			if (j >= l - strlen(ee->e_id) - 3)
801890792Sgshapiro			{
801990792Sgshapiro				char *p;
802090792Sgshapiro
802190792Sgshapiro				l += MAXLINE;
802290792Sgshapiro				p = sm_realloc(lsplits, l);
802390792Sgshapiro				if (p == NULL)
802490792Sgshapiro				{
802590792Sgshapiro					/* let's try to get this done */
802690792Sgshapiro					sm_free(lsplits);
802790792Sgshapiro					lsplits = NULL;
802890792Sgshapiro				}
802990792Sgshapiro				else
803090792Sgshapiro					lsplits = p;
803190792Sgshapiro			}
803290792Sgshapiro			if (lsplits != NULL)
803390792Sgshapiro			{
803490792Sgshapiro				if (j == 0)
803590792Sgshapiro					j += sm_strlcat(lsplits + j,
803690792Sgshapiro							ee->e_id,
803790792Sgshapiro							l - j);
803890792Sgshapiro				else
803990792Sgshapiro					j += sm_strlcat2(lsplits + j,
804090792Sgshapiro							 "; ",
804190792Sgshapiro							 ee->e_id,
804290792Sgshapiro							 l - j);
804390792Sgshapiro				SM_ASSERT(j < l);
804490792Sgshapiro			}
804590792Sgshapiro		}
804690792Sgshapiro		if (nrcpt - i <= maxrcpt)
804790792Sgshapiro			break;
804890792Sgshapiro		i += maxrcpt;
804990792Sgshapiro	}
8050110560Sgshapiro	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
805190792Sgshapiro	{
8052110560Sgshapiro		if (nsplit > 0)
8053110560Sgshapiro		{
8054110560Sgshapiro			sm_syslog(LOG_NOTICE, e->e_id,
8055110560Sgshapiro				  "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
8056110560Sgshapiro				  maxrcpt, nrcpt - ndead, nsplit,
8057110560Sgshapiro				  nsplit > 1 ? "s" : "", lsplits);
8058110560Sgshapiro		}
805990792Sgshapiro		sm_free(lsplits);
806090792Sgshapiro	}
806190792Sgshapiro	return SM_SPLIT_NEW(nsplit);
806290792Sgshapiro}
806390792Sgshapiro/*
806490792Sgshapiro**  SPLIT_BY_RECIPIENT
806590792Sgshapiro**
806690792Sgshapiro**	Split an envelope with multiple recipients into multiple
806790792Sgshapiro**	envelopes as required by the sendmail configuration.
806890792Sgshapiro**
806990792Sgshapiro**	Parameters:
807090792Sgshapiro**		e -- envelope.
807190792Sgshapiro**
807290792Sgshapiro**	Results:
807390792Sgshapiro**		Returns true on success, false on failure.
807490792Sgshapiro**
807590792Sgshapiro**	Side Effects:
807690792Sgshapiro**		see split_across_queue_groups(), split_within_queue(e)
807790792Sgshapiro*/
807890792Sgshapiro
807990792Sgshapirobool
808090792Sgshapirosplit_by_recipient(e)
808190792Sgshapiro	ENVELOPE *e;
808290792Sgshapiro{
808390792Sgshapiro	int split, n, i, j, l;
808490792Sgshapiro	char *lsplits;
808590792Sgshapiro	ENVELOPE *ee, *next, *firstsibling;
808690792Sgshapiro
808790792Sgshapiro	if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
808890792Sgshapiro	    bitset(EF_SPLIT, e->e_flags))
808990792Sgshapiro		return true;
809090792Sgshapiro	n = split_across_queue_groups(e);
809190792Sgshapiro	if (n == SM_SPLIT_FAIL)
809290792Sgshapiro		return false;
809390792Sgshapiro	firstsibling = ee = e->e_sibling;
809490792Sgshapiro	if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
809590792Sgshapiro	{
809690792Sgshapiro		l = MAXLINE;
809790792Sgshapiro		lsplits = sm_malloc(l);
809890792Sgshapiro		if (lsplits != NULL)
809990792Sgshapiro			*lsplits = '\0';
810090792Sgshapiro		j = 0;
810190792Sgshapiro	}
810290792Sgshapiro	else
810390792Sgshapiro	{
810490792Sgshapiro		/* get rid of stupid compiler warnings */
810590792Sgshapiro		lsplits = NULL;
810690792Sgshapiro		j = l = 0;
810790792Sgshapiro	}
810890792Sgshapiro	for (i = 1; i < n; ++i)
810990792Sgshapiro	{
811090792Sgshapiro		next = ee->e_sibling;
811190792Sgshapiro		if (split_within_queue(ee) == SM_SPLIT_FAIL)
811290792Sgshapiro		{
811390792Sgshapiro			e->e_sibling = firstsibling;
811490792Sgshapiro			return false;
811590792Sgshapiro		}
811690792Sgshapiro		ee->e_flags |= EF_SPLIT;
811790792Sgshapiro		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
811890792Sgshapiro		{
811990792Sgshapiro			if (j >= l - strlen(ee->e_id) - 3)
812090792Sgshapiro			{
812190792Sgshapiro				char *p;
812290792Sgshapiro
812390792Sgshapiro				l += MAXLINE;
812490792Sgshapiro				p = sm_realloc(lsplits, l);
812590792Sgshapiro				if (p == NULL)
812690792Sgshapiro				{
812790792Sgshapiro					/* let's try to get this done */
812890792Sgshapiro					sm_free(lsplits);
812990792Sgshapiro					lsplits = NULL;
813090792Sgshapiro				}
813190792Sgshapiro				else
813290792Sgshapiro					lsplits = p;
813390792Sgshapiro			}
813490792Sgshapiro			if (lsplits != NULL)
813590792Sgshapiro			{
813690792Sgshapiro				if (j == 0)
813790792Sgshapiro					j += sm_strlcat(lsplits + j,
813890792Sgshapiro							ee->e_id, l - j);
813990792Sgshapiro				else
814090792Sgshapiro					j += sm_strlcat2(lsplits + j, "; ",
814190792Sgshapiro							 ee->e_id, l - j);
814290792Sgshapiro				SM_ASSERT(j < l);
814390792Sgshapiro			}
814490792Sgshapiro		}
814590792Sgshapiro		ee = next;
814690792Sgshapiro	}
814790792Sgshapiro	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
814890792Sgshapiro	{
814990792Sgshapiro		sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
815090792Sgshapiro			  n - 1, n > 2 ? "s" : "", lsplits);
815190792Sgshapiro		sm_free(lsplits);
815290792Sgshapiro	}
815390792Sgshapiro	split = split_within_queue(e) != SM_SPLIT_FAIL;
815490792Sgshapiro	if (split)
815590792Sgshapiro		e->e_flags |= EF_SPLIT;
815690792Sgshapiro	return split;
815790792Sgshapiro}
815890792Sgshapiro
815990792Sgshapiro#if _FFR_QUARANTINE
816090792Sgshapiro/*
816190792Sgshapiro**  QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
816290792Sgshapiro**
816390792Sgshapiro**	Add/remove quarantine reason and requeue appropriately.
816490792Sgshapiro**
816590792Sgshapiro**	Parameters:
816690792Sgshapiro**		qgrp -- queue group for the item
816790792Sgshapiro**		qdir -- queue directory in the given queue group
816890792Sgshapiro**		e -- envelope information for the item
816990792Sgshapiro**		reason -- quarantine reason, NULL means unquarantine.
817090792Sgshapiro**
817190792Sgshapiro**	Results:
817290792Sgshapiro**		true if item changed, false otherwise
817390792Sgshapiro**
817490792Sgshapiro**	Side Effects:
817590792Sgshapiro**		Changes quarantine tag in queue file and renames it.
817690792Sgshapiro*/
817790792Sgshapiro
817890792Sgshapirostatic bool
817990792Sgshapiroquarantine_queue_item(qgrp, qdir, e, reason)
818090792Sgshapiro	int qgrp;
818190792Sgshapiro	int qdir;
818290792Sgshapiro	ENVELOPE *e;
818390792Sgshapiro	char *reason;
818490792Sgshapiro{
818590792Sgshapiro	bool dirty = false;
818690792Sgshapiro	bool failing = false;
818790792Sgshapiro	bool foundq = false;
818890792Sgshapiro	bool finished = false;
818990792Sgshapiro	int fd;
819090792Sgshapiro	int flags;
819190792Sgshapiro	int oldtype;
819290792Sgshapiro	int newtype;
819390792Sgshapiro	int save_errno;
819490792Sgshapiro	MODE_T oldumask = 0;
819590792Sgshapiro	SM_FILE_T *oldqfp, *tempqfp;
819690792Sgshapiro	char *bp;
819790792Sgshapiro	char oldqf[MAXPATHLEN];
819890792Sgshapiro	char tempqf[MAXPATHLEN];
819990792Sgshapiro	char newqf[MAXPATHLEN];
820090792Sgshapiro	char buf[MAXLINE];
820190792Sgshapiro
820290792Sgshapiro	oldtype = queue_letter(e, ANYQFL_LETTER);
820390792Sgshapiro	(void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof oldqf);
820490792Sgshapiro	(void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof tempqf);
820590792Sgshapiro
820690792Sgshapiro	/*
820790792Sgshapiro	**  Instead of duplicating all the open
820890792Sgshapiro	**  and lock code here, tell readqf() to
820990792Sgshapiro	**  do that work and return the open
821090792Sgshapiro	**  file pointer in e_lockfp.  Note that
821190792Sgshapiro	**  we must release the locks properly when
821290792Sgshapiro	**  we are done.
821390792Sgshapiro	*/
821490792Sgshapiro
821590792Sgshapiro	if (!readqf(e, true))
821690792Sgshapiro	{
821790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
821890792Sgshapiro				     "Skipping %s\n", qid_printname(e));
821990792Sgshapiro		return false;
822090792Sgshapiro	}
822190792Sgshapiro	oldqfp = e->e_lockfp;
822290792Sgshapiro
822390792Sgshapiro	/* open the new queue file */
822490792Sgshapiro	flags = O_CREAT|O_WRONLY|O_EXCL;
822590792Sgshapiro	if (bitset(S_IWGRP, QueueFileMode))
822690792Sgshapiro		oldumask = umask(002);
822790792Sgshapiro	fd = open(tempqf, flags, QueueFileMode);
822890792Sgshapiro	if (bitset(S_IWGRP, QueueFileMode))
822990792Sgshapiro		(void) umask(oldumask);
823090792Sgshapiro	RELEASE_QUEUE;
823190792Sgshapiro
823290792Sgshapiro	if (fd < 0)
823390792Sgshapiro	{
823490792Sgshapiro		save_errno = errno;
823590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
823690792Sgshapiro				     "Skipping %s: Could not open %s: %s\n",
823790792Sgshapiro				     qid_printname(e), tempqf,
823890792Sgshapiro				     sm_errstring(save_errno));
823990792Sgshapiro		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
824090792Sgshapiro		return false;
824190792Sgshapiro	}
824290792Sgshapiro	if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
824390792Sgshapiro	{
824490792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
824590792Sgshapiro				     "Skipping %s: Could not lock %s\n",
824690792Sgshapiro				     qid_printname(e), tempqf);
824790792Sgshapiro		(void) close(fd);
824890792Sgshapiro		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
824990792Sgshapiro		return false;
825090792Sgshapiro	}
825190792Sgshapiro
825290792Sgshapiro	tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
825390792Sgshapiro			     SM_IO_WRONLY, NULL);
825490792Sgshapiro	if (tempqfp == NULL)
825590792Sgshapiro	{
825690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
825790792Sgshapiro				     "Skipping %s: Could not lock %s\n",
825890792Sgshapiro				     qid_printname(e), tempqf);
825990792Sgshapiro		(void) close(fd);
826090792Sgshapiro		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
826190792Sgshapiro		return false;
826290792Sgshapiro	}
826390792Sgshapiro
826490792Sgshapiro	/* Copy the data over, changing the quarantine reason */
826590792Sgshapiro	while ((bp = fgetfolded(buf, sizeof buf, oldqfp)) != NULL)
826690792Sgshapiro	{
826790792Sgshapiro		if (tTd(40, 4))
826890792Sgshapiro			sm_dprintf("+++++ %s\n", bp);
826990792Sgshapiro		switch (bp[0])
827090792Sgshapiro		{
827190792Sgshapiro		  case 'q':		/* quarantine reason */
827290792Sgshapiro			foundq = true;
827390792Sgshapiro			if (reason == NULL)
827490792Sgshapiro			{
827590792Sgshapiro				if (Verbose)
827690792Sgshapiro				{
827790792Sgshapiro					(void) sm_io_fprintf(smioout,
827890792Sgshapiro							     SM_TIME_DEFAULT,
827990792Sgshapiro							     "%s: Removed quarantine of \"%s\"\n",
828090792Sgshapiro							     e->e_id, &bp[1]);
828190792Sgshapiro				}
828290792Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "unquarantine");
828390792Sgshapiro				dirty = true;
828490792Sgshapiro				continue;
828590792Sgshapiro			}
828690792Sgshapiro			else if (strcmp(reason, &bp[1]) == 0)
828790792Sgshapiro			{
828890792Sgshapiro				if (Verbose)
828990792Sgshapiro				{
829090792Sgshapiro					(void) sm_io_fprintf(smioout,
829190792Sgshapiro							     SM_TIME_DEFAULT,
829290792Sgshapiro							     "%s: Already quarantined with \"%s\"\n",
829390792Sgshapiro							     e->e_id, reason);
829490792Sgshapiro				}
829590792Sgshapiro				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
829690792Sgshapiro						     "q%s\n", reason);
829790792Sgshapiro			}
829890792Sgshapiro			else
829990792Sgshapiro			{
830090792Sgshapiro				if (Verbose)
830190792Sgshapiro				{
830290792Sgshapiro					(void) sm_io_fprintf(smioout,
830390792Sgshapiro							     SM_TIME_DEFAULT,
830490792Sgshapiro							     "%s: Quarantine changed from \"%s\" to \"%s\"\n",
830590792Sgshapiro							     e->e_id, &bp[1],
830690792Sgshapiro							     reason);
830790792Sgshapiro				}
830890792Sgshapiro				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
830990792Sgshapiro						     "q%s\n", reason);
831090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
831190792Sgshapiro					  reason);
831290792Sgshapiro				dirty = true;
831390792Sgshapiro			}
831490792Sgshapiro			break;
831590792Sgshapiro
831698121Sgshapiro		  case 'S':
831790792Sgshapiro			/*
831890792Sgshapiro			**  If we are quarantining an unquarantined item,
831990792Sgshapiro			**  need to put in a new 'q' line before it's
832090792Sgshapiro			**  too late.
832190792Sgshapiro			*/
832290792Sgshapiro
832390792Sgshapiro			if (!foundq && reason != NULL)
832490792Sgshapiro			{
832590792Sgshapiro				if (Verbose)
832690792Sgshapiro				{
832790792Sgshapiro					(void) sm_io_fprintf(smioout,
832890792Sgshapiro							     SM_TIME_DEFAULT,
832990792Sgshapiro							     "%s: Quarantined with \"%s\"\n",
833090792Sgshapiro							     e->e_id, reason);
833190792Sgshapiro				}
833290792Sgshapiro				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
833390792Sgshapiro						     "q%s\n", reason);
833490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
833590792Sgshapiro					  reason);
833690792Sgshapiro				foundq = true;
833790792Sgshapiro				dirty = true;
833890792Sgshapiro			}
833990792Sgshapiro
834090792Sgshapiro			/* Copy the line to the new file */
834190792Sgshapiro			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
834290792Sgshapiro					     "%s\n", bp);
834390792Sgshapiro			break;
834490792Sgshapiro
834590792Sgshapiro		  case '.':
834690792Sgshapiro			finished = true;
834790792Sgshapiro			/* FALLTHROUGH */
834890792Sgshapiro
834990792Sgshapiro		  default:
835090792Sgshapiro			/* Copy the line to the new file */
835190792Sgshapiro			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
835290792Sgshapiro					     "%s\n", bp);
835390792Sgshapiro			break;
835490792Sgshapiro		}
835590792Sgshapiro	}
835690792Sgshapiro
835790792Sgshapiro	/* Make sure we read the whole old file */
835890792Sgshapiro	errno = sm_io_error(tempqfp);
835990792Sgshapiro	if (errno != 0 && errno != SM_IO_EOF)
836090792Sgshapiro	{
836190792Sgshapiro		save_errno = errno;
836290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
836390792Sgshapiro				     "Skipping %s: Error reading %s: %s\n",
836490792Sgshapiro				     qid_printname(e), oldqf,
836590792Sgshapiro				     sm_errstring(save_errno));
836690792Sgshapiro		failing = true;
836790792Sgshapiro	}
836890792Sgshapiro
836990792Sgshapiro	if (!failing && !finished)
837090792Sgshapiro	{
837190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
837290792Sgshapiro				     "Skipping %s: Incomplete file: %s\n",
837390792Sgshapiro				     qid_printname(e), oldqf);
837490792Sgshapiro		failing = true;
837590792Sgshapiro	}
837690792Sgshapiro
837790792Sgshapiro	/* Check if we actually changed anything or we can just bail now */
837890792Sgshapiro	if (!dirty)
837990792Sgshapiro	{
838090792Sgshapiro		/* pretend we failed, even though we technically didn't */
838190792Sgshapiro		failing = true;
838290792Sgshapiro	}
838390792Sgshapiro
838490792Sgshapiro	/* Make sure we wrote things out safely */
838590792Sgshapiro	if (!failing &&
838690792Sgshapiro	    (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
838790792Sgshapiro	     ((SuperSafe == SAFE_REALLY || SuperSafe == SAFE_INTERACTIVE) &&
838890792Sgshapiro	      fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
838990792Sgshapiro	     ((errno = sm_io_error(tempqfp)) != 0)))
839090792Sgshapiro	{
839190792Sgshapiro		save_errno = errno;
839290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
839390792Sgshapiro				     "Skipping %s: Error writing %s: %s\n",
839490792Sgshapiro				     qid_printname(e), tempqf,
839590792Sgshapiro				     sm_errstring(save_errno));
839690792Sgshapiro		failing = true;
839790792Sgshapiro	}
839890792Sgshapiro
839990792Sgshapiro
840090792Sgshapiro	/* Figure out the new filename */
840190792Sgshapiro	newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
840290792Sgshapiro	if (oldtype == newtype)
840390792Sgshapiro	{
840490792Sgshapiro		/* going to rename tempqf to oldqf */
840590792Sgshapiro		(void) sm_strlcpy(newqf, oldqf, sizeof newqf);
840690792Sgshapiro	}
840790792Sgshapiro	else
840890792Sgshapiro	{
840990792Sgshapiro		/* going to rename tempqf to new name based on newtype */
841090792Sgshapiro		(void) sm_strlcpy(newqf, queuename(e, newtype), sizeof newqf);
841190792Sgshapiro	}
841290792Sgshapiro
841390792Sgshapiro	save_errno = 0;
841490792Sgshapiro
841590792Sgshapiro	/* rename tempqf to newqf */
841690792Sgshapiro	if (!failing &&
841790792Sgshapiro	    rename(tempqf, newqf) < 0)
841890792Sgshapiro		save_errno = (errno == 0) ? EINVAL : errno;
841990792Sgshapiro
842090792Sgshapiro	/* Check rename() success */
842190792Sgshapiro	if (!failing && save_errno != 0)
842290792Sgshapiro	{
842390792Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id,
842490792Sgshapiro			  "quarantine_queue_item: rename(%s, %s): %s",
842590792Sgshapiro			  tempqf, newqf, sm_errstring(save_errno));
842690792Sgshapiro
842790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
842890792Sgshapiro				     "Error renaming %s to %s: %s\n",
842990792Sgshapiro				     tempqf, newqf,
843090792Sgshapiro				     sm_errstring(save_errno));
843190792Sgshapiro		if (oldtype == newtype)
843290792Sgshapiro		{
843390792Sgshapiro			/*
843490792Sgshapiro			**  Bail here since we don't know the state of
843590792Sgshapiro			**  the filesystem and may need to keep tempqf
843690792Sgshapiro			**  for the user to rescue us.
843790792Sgshapiro			*/
843890792Sgshapiro
843990792Sgshapiro			RELEASE_QUEUE;
844090792Sgshapiro			errno = save_errno;
844190792Sgshapiro			syserr("!452 Error renaming control file %s", tempqf);
844290792Sgshapiro			/* NOTREACHED */
844390792Sgshapiro		}
844490792Sgshapiro		else
844590792Sgshapiro		{
844690792Sgshapiro			/* remove new file (if rename() half completed) */
844790792Sgshapiro			if (xunlink(newqf) < 0)
844890792Sgshapiro			{
844990792Sgshapiro				save_errno = errno;
845090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
845190792Sgshapiro						     "Error removing %s: %s\n",
845290792Sgshapiro						     newqf,
845390792Sgshapiro						     sm_errstring(save_errno));
845490792Sgshapiro			}
845590792Sgshapiro
845690792Sgshapiro			/* tempqf removed below */
845790792Sgshapiro			failing = true;
845890792Sgshapiro		}
845990792Sgshapiro
846090792Sgshapiro	}
846190792Sgshapiro
846290792Sgshapiro	/* If changing file types, need to remove old type */
846390792Sgshapiro	if (!failing && oldtype != newtype)
846490792Sgshapiro	{
846590792Sgshapiro		if (xunlink(oldqf) < 0)
846690792Sgshapiro		{
846790792Sgshapiro			save_errno = errno;
846890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
846990792Sgshapiro					     "Error removing %s: %s\n",
847090792Sgshapiro					     oldqf, sm_errstring(save_errno));
847190792Sgshapiro		}
847290792Sgshapiro	}
847390792Sgshapiro
847490792Sgshapiro	/* see if anything above failed */
847590792Sgshapiro	if (failing)
847690792Sgshapiro	{
847790792Sgshapiro		/* Something failed: remove new file, old file still there */
847890792Sgshapiro		(void) xunlink(tempqf);
847990792Sgshapiro	}
848090792Sgshapiro
848190792Sgshapiro	/*
848290792Sgshapiro	**  fsync() after file operations to make sure metadata is
848390792Sgshapiro	**  written to disk on filesystems in which renames are
848490792Sgshapiro	**  not guaranteed.  It's ok if they fail, mail won't be lost.
848590792Sgshapiro	*/
848690792Sgshapiro
848790792Sgshapiro	if (SuperSafe != SAFE_NO)
848890792Sgshapiro	{
848990792Sgshapiro		/* for soft-updates */
849090792Sgshapiro		(void) fsync(sm_io_getinfo(tempqfp,
849190792Sgshapiro					   SM_IO_WHAT_FD, NULL));
849290792Sgshapiro
849390792Sgshapiro		if (!failing)
849490792Sgshapiro		{
849590792Sgshapiro			/* for soft-updates */
849690792Sgshapiro			(void) fsync(sm_io_getinfo(oldqfp,
849790792Sgshapiro						   SM_IO_WHAT_FD, NULL));
849890792Sgshapiro		}
849990792Sgshapiro
850090792Sgshapiro		/* for other odd filesystems */
850190792Sgshapiro		SYNC_DIR(tempqf, false);
850290792Sgshapiro	}
850390792Sgshapiro
850490792Sgshapiro	/* Close up shop */
850590792Sgshapiro	RELEASE_QUEUE;
850690792Sgshapiro	if (tempqfp != NULL)
850790792Sgshapiro		(void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
850890792Sgshapiro	if (oldqfp != NULL)
850990792Sgshapiro		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
851090792Sgshapiro
851190792Sgshapiro	/* All went well */
851290792Sgshapiro	return !failing;
851390792Sgshapiro}
851490792Sgshapiro
851590792Sgshapiro/*
851690792Sgshapiro**  QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
851790792Sgshapiro**
851890792Sgshapiro**	Read all matching queue items, add/remove quarantine
851990792Sgshapiro**	reason, and requeue appropriately.
852090792Sgshapiro**
852190792Sgshapiro**	Parameters:
852290792Sgshapiro**		reason -- quarantine reason, "." means unquarantine.
852390792Sgshapiro**		qgrplimit -- limit to single queue group unless NOQGRP
852490792Sgshapiro**
852590792Sgshapiro**	Results:
852690792Sgshapiro**		none.
852790792Sgshapiro**
852890792Sgshapiro**	Side Effects:
852990792Sgshapiro**		Lots of changes to the queue.
853090792Sgshapiro*/
853190792Sgshapiro
853290792Sgshapirovoid
853390792Sgshapiroquarantine_queue(reason, qgrplimit)
853490792Sgshapiro	char *reason;
853590792Sgshapiro	int qgrplimit;
853690792Sgshapiro{
853790792Sgshapiro	int changed = 0;
853890792Sgshapiro	int qgrp;
853990792Sgshapiro
854090792Sgshapiro	/* Convert internal representation of unquarantine */
854190792Sgshapiro	if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
854290792Sgshapiro		reason = NULL;
854390792Sgshapiro
854490792Sgshapiro	if (reason != NULL)
854590792Sgshapiro	{
854690792Sgshapiro		/* clean it */
854790792Sgshapiro		reason = newstr(denlstring(reason, true, true));
854890792Sgshapiro	}
854990792Sgshapiro
855090792Sgshapiro	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
855190792Sgshapiro	{
855290792Sgshapiro		int qdir;
855390792Sgshapiro
855490792Sgshapiro		if (qgrplimit != NOQGRP && qgrplimit != qgrp)
855590792Sgshapiro			continue;
855690792Sgshapiro
855790792Sgshapiro		for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
855890792Sgshapiro		{
855990792Sgshapiro			int i;
856090792Sgshapiro			int nrequests;
856190792Sgshapiro
856290792Sgshapiro			if (StopRequest)
856390792Sgshapiro				stop_sendmail();
856490792Sgshapiro
856590792Sgshapiro			nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
856690792Sgshapiro
856790792Sgshapiro			/* first see if there is anything */
856890792Sgshapiro			if (nrequests <= 0)
856990792Sgshapiro			{
857090792Sgshapiro				if (Verbose)
857190792Sgshapiro				{
857290792Sgshapiro					(void) sm_io_fprintf(smioout,
857390792Sgshapiro							     SM_TIME_DEFAULT, "%s: no matches\n",
857490792Sgshapiro							     qid_printqueue(qgrp, qdir));
857590792Sgshapiro				}
857690792Sgshapiro				continue;
857790792Sgshapiro			}
857890792Sgshapiro
857990792Sgshapiro			if (Verbose)
858090792Sgshapiro			{
858190792Sgshapiro				(void) sm_io_fprintf(smioout,
858290792Sgshapiro						     SM_TIME_DEFAULT, "Processing %s:\n",
858390792Sgshapiro						     qid_printqueue(qgrp, qdir));
858490792Sgshapiro			}
858590792Sgshapiro
858690792Sgshapiro			for (i = 0; i < WorkListCount; i++)
858790792Sgshapiro			{
858890792Sgshapiro				ENVELOPE e;
858990792Sgshapiro
859090792Sgshapiro				if (StopRequest)
859190792Sgshapiro					stop_sendmail();
859290792Sgshapiro
859390792Sgshapiro				/* setup envelope */
859490792Sgshapiro				clearenvelope(&e, true, sm_rpool_new_x(NULL));
859590792Sgshapiro				e.e_id = WorkList[i].w_name + 2;
859690792Sgshapiro				e.e_qgrp = qgrp;
859790792Sgshapiro				e.e_qdir = qdir;
859890792Sgshapiro
859990792Sgshapiro				if (tTd(70, 101))
860090792Sgshapiro				{
860190792Sgshapiro					sm_io_fprintf(smioout, SM_TIME_DEFAULT,
860290792Sgshapiro						      "Would do %s\n", e.e_id);
860390792Sgshapiro					changed++;
860490792Sgshapiro				}
860590792Sgshapiro				else if (quarantine_queue_item(qgrp, qdir,
860690792Sgshapiro							       &e, reason))
860790792Sgshapiro					changed++;
860890792Sgshapiro
860990792Sgshapiro				/* clean up */
861090792Sgshapiro				sm_rpool_free(e.e_rpool);
861190792Sgshapiro				e.e_rpool = NULL;
861290792Sgshapiro			}
861390792Sgshapiro			if (WorkList != NULL)
861490792Sgshapiro				sm_free(WorkList); /* XXX */
861590792Sgshapiro			WorkList = NULL;
861690792Sgshapiro			WorkListSize = 0;
861790792Sgshapiro			WorkListCount = 0;
861890792Sgshapiro		}
861990792Sgshapiro	}
862090792Sgshapiro	if (Verbose)
862190792Sgshapiro	{
862290792Sgshapiro		if (changed == 0)
862390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
862490792Sgshapiro					     "No changes\n");
862590792Sgshapiro		else
862690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
862790792Sgshapiro					     "%d change%s\n",
862890792Sgshapiro					     changed,
862990792Sgshapiro					     changed == 1 ? "" : "s");
863090792Sgshapiro	}
863190792Sgshapiro}
863290792Sgshapiro#endif /* _FFR_QUARANTINE */
8633