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