queue.c revision 102528
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
16102528SgshapiroSM_RCSID("@(#)$Id: queue.c,v 8.863.2.6 2002/08/16 16:27:37 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.
1466102528Sgshapiro		**
1467102528Sgshapiro		**  Increment CurRunners before calling run_work_group()
1468102528Sgshapiro		**  to avoid a "race condition" with proc_list_drop() which
1469102528Sgshapiro		**  decrements CurRunners if the queue runners terminate.
1470102528Sgshapiro		**  This actually doesn't cause any harm, but CurRunners
1471102528Sgshapiro		**  might become negative which is at least confusing.
1472102528Sgshapiro		**
1473102528Sgshapiro		**  Notice: CurRunners is an upper limit, in some cases
1474102528Sgshapiro		**  (too few jobs in the queue) this value is larger than
1475102528Sgshapiro		**  the actual number of queue runners. The discrepancy can
1476102528Sgshapiro		**  increase if some queue runners "hang" for a long time.
147790792Sgshapiro		*/
147890792Sgshapiro
1479102528Sgshapiro		CurRunners += WorkGrp[curnum].wg_maxact;
148090792Sgshapiro		ret = run_work_group(curnum, forkflag, verbose, persistent,
148190792Sgshapiro				     runall);
148290792Sgshapiro
148390792Sgshapiro		/*
148464562Sgshapiro		**  Failure means a message was printed for ETRN
148564562Sgshapiro		**  and subsequent queues are likely to fail as well.
1486102528Sgshapiro		**  Decrement CurRunners in that case because
1487102528Sgshapiro		**  none have been started.
148864562Sgshapiro		*/
148964562Sgshapiro
149064562Sgshapiro		if (!ret)
1491102528Sgshapiro		{
1492102528Sgshapiro			CurRunners -= WorkGrp[curnum].wg_maxact;
149364562Sgshapiro			break;
1494102528Sgshapiro		}
149564562Sgshapiro
149690792Sgshapiro		if (!persistent)
149794334Sgshapiro			schedule_queue_runs(runall, curnum, true);
149890792Sgshapiro		INCR_MOD(curnum, NumWorkGroups);
149964562Sgshapiro	}
150090792Sgshapiro
150190792Sgshapiro	/* schedule left over queue runs */
150290792Sgshapiro	if (i < NumWorkGroups && !NoMoreRunners && !persistent)
150390792Sgshapiro	{
150490792Sgshapiro		int h;
150590792Sgshapiro
150690792Sgshapiro		for (h = curnum; i < NumWorkGroups; i++)
150790792Sgshapiro		{
150894334Sgshapiro			schedule_queue_runs(runall, h, false);
150990792Sgshapiro			INCR_MOD(h, NumWorkGroups);
151090792Sgshapiro		}
151190792Sgshapiro	}
151290792Sgshapiro
151390792Sgshapiro
151490792Sgshapiro#if SM_HEAP_CHECK
151590792Sgshapiro	if (sm_debug_active(&DebugLeakQ, 1))
151690792Sgshapiro		sm_heap_setgroup(oldgroup);
151790792Sgshapiro#endif /* SM_HEAP_CHECK */
151864562Sgshapiro	return ret;
151964562Sgshapiro}
152090792Sgshapiro/*
152190792Sgshapiro**  RUNNER_WORK -- have a queue runner do its work
152264562Sgshapiro**
152390792Sgshapiro**  Have a queue runner do its work a list of entries.
152490792Sgshapiro**  When work isn't directly being done then this process can take a signal
152590792Sgshapiro**  and terminate immediately (in a clean fashion of course).
152690792Sgshapiro**  When work is directly being done, it's not to be interrupted
152790792Sgshapiro**  immediately: the work should be allowed to finish at a clean point
152890792Sgshapiro**  before termination (in a clean fashion of course).
152990792Sgshapiro**
153090792Sgshapiro**	Parameters:
153190792Sgshapiro**		e -- envelope.
153290792Sgshapiro**		sequenceno -- 'th process to run WorkQ.
153390792Sgshapiro**		didfork -- did the calling process fork()?
153490792Sgshapiro**		skip -- process only each skip'th item.
153590792Sgshapiro**		njobs -- number of jobs in WorkQ.
153690792Sgshapiro**
153790792Sgshapiro**	Returns:
153890792Sgshapiro**		none.
153990792Sgshapiro**
154090792Sgshapiro**	Side Effects:
154190792Sgshapiro**		runs things in the mail queue.
154290792Sgshapiro*/
154390792Sgshapiro
154490792Sgshapiro/* Get new load average every 30 seconds. */
154590792Sgshapiro#define GET_NEW_LA_TIME	30
154690792Sgshapiro
154790792Sgshapirostatic void
154890792Sgshapirorunner_work(e, sequenceno, didfork, skip, njobs)
154990792Sgshapiro	register ENVELOPE *e;
155090792Sgshapiro	int sequenceno;
155190792Sgshapiro	bool didfork;
155290792Sgshapiro	int skip;
155390792Sgshapiro	int njobs;
155490792Sgshapiro{
155590792Sgshapiro	int n;
155690792Sgshapiro	WORK *w;
155790792Sgshapiro	time_t current_la_time, now;
155890792Sgshapiro
155990792Sgshapiro	current_la_time = curtime();
156090792Sgshapiro
156190792Sgshapiro	/*
156290792Sgshapiro	**  Here we temporarily block the second calling of the handlers.
156390792Sgshapiro	**  This allows us to handle the signal without terminating in the
156490792Sgshapiro	**  middle of direct work. If a signal does come, the test for
156590792Sgshapiro	**  NoMoreRunners will find it.
156690792Sgshapiro	*/
156790792Sgshapiro
156890792Sgshapiro	BlockOldsh = true;
156990792Sgshapiro
157090792Sgshapiro	/* process them once at a time */
157190792Sgshapiro	while (WorkQ != NULL)
157290792Sgshapiro	{
157390792Sgshapiro#if SM_HEAP_CHECK
157490792Sgshapiro		SM_NONVOLATILE int oldgroup = 0;
157590792Sgshapiro
157690792Sgshapiro		if (sm_debug_active(&DebugLeakQ, 1))
157790792Sgshapiro		{
157890792Sgshapiro			oldgroup = sm_heap_group();
157990792Sgshapiro			sm_heap_newgroup();
158090792Sgshapiro			sm_dprintf("run_queue_group() heap group #%d\n",
158190792Sgshapiro				sm_heap_group());
158290792Sgshapiro		}
158390792Sgshapiro#endif /* SM_HEAP_CHECK */
158490792Sgshapiro
158590792Sgshapiro		/* do no more work */
158690792Sgshapiro		if (NoMoreRunners)
158790792Sgshapiro		{
158890792Sgshapiro			/* Check that a valid signal handler is callable */
158990792Sgshapiro			if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
159090792Sgshapiro			    Oldsh != runners_sighup &&
159190792Sgshapiro			    Oldsh != runners_sigterm)
159290792Sgshapiro				(*Oldsh)(Oldsig);
159390792Sgshapiro			break;
159490792Sgshapiro		}
159590792Sgshapiro
159690792Sgshapiro		w = WorkQ; /* assign current work item */
159790792Sgshapiro
159890792Sgshapiro		/*
159990792Sgshapiro		**  Set the head of the WorkQ to the next work item.
160090792Sgshapiro		**  It is set 'skip' ahead (the number of parallel queue
160190792Sgshapiro		**  runners working on WorkQ together) since each runner
160290792Sgshapiro		**  works on every 'skip'th (N-th) item.
160390792Sgshapiro		*/
160490792Sgshapiro
160590792Sgshapiro		for (n = 0; n < skip && WorkQ != NULL; n++)
160690792Sgshapiro			WorkQ = WorkQ->w_next;
160790792Sgshapiro		e->e_to = NULL;
160890792Sgshapiro
160990792Sgshapiro		/*
161090792Sgshapiro		**  Ignore jobs that are too expensive for the moment.
161190792Sgshapiro		**
161290792Sgshapiro		**	Get new load average every GET_NEW_LA_TIME seconds.
161390792Sgshapiro		*/
161490792Sgshapiro
161590792Sgshapiro		now = curtime();
161690792Sgshapiro		if (current_la_time < now - GET_NEW_LA_TIME)
161790792Sgshapiro		{
161890792Sgshapiro			sm_getla();
161990792Sgshapiro			current_la_time = now;
162090792Sgshapiro		}
162190792Sgshapiro		if (shouldqueue(WkRecipFact, current_la_time))
162290792Sgshapiro		{
162390792Sgshapiro			char *msg = "Aborting queue run: load average too high";
162490792Sgshapiro
162590792Sgshapiro			if (Verbose)
162690792Sgshapiro				message("%s", msg);
162790792Sgshapiro			if (LogLevel > 8)
162890792Sgshapiro				sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
162990792Sgshapiro			break;
163090792Sgshapiro		}
163190792Sgshapiro		if (shouldqueue(w->w_pri, w->w_ctime))
163290792Sgshapiro		{
163390792Sgshapiro			if (Verbose)
163490792Sgshapiro				message(EmptyString);
163590792Sgshapiro			if (QueueSortOrder == QSO_BYPRIORITY)
163690792Sgshapiro			{
163790792Sgshapiro				if (Verbose)
163890792Sgshapiro					message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
163990792Sgshapiro						qid_printqueue(w->w_qgrp,
164090792Sgshapiro							       w->w_qdir),
164190792Sgshapiro						w->w_name + 2, sequenceno,
164290792Sgshapiro						njobs);
164390792Sgshapiro				if (LogLevel > 8)
164490792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
164590792Sgshapiro						  "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
164690792Sgshapiro						  qid_printqueue(w->w_qgrp,
164790792Sgshapiro								 w->w_qdir),
164890792Sgshapiro						  w->w_name + 2, w->w_pri,
164990792Sgshapiro						  CurrentLA, sequenceno,
165090792Sgshapiro						  njobs);
165190792Sgshapiro				break;
165290792Sgshapiro			}
165390792Sgshapiro			else if (Verbose)
165490792Sgshapiro				message("Skipping %s/%s (sequence %d of %d)",
165590792Sgshapiro					qid_printqueue(w->w_qgrp, w->w_qdir),
165690792Sgshapiro					w->w_name + 2, sequenceno, njobs);
165790792Sgshapiro		}
165890792Sgshapiro		else
165990792Sgshapiro		{
166090792Sgshapiro			if (Verbose)
166190792Sgshapiro			{
166290792Sgshapiro				message(EmptyString);
166390792Sgshapiro				message("Running %s/%s (sequence %d of %d)",
166490792Sgshapiro					qid_printqueue(w->w_qgrp, w->w_qdir),
166590792Sgshapiro					w->w_name + 2, sequenceno, njobs);
166690792Sgshapiro			}
166790792Sgshapiro			if (didfork && MaxQueueChildren > 0)
166890792Sgshapiro			{
166990792Sgshapiro				sm_blocksignal(SIGCHLD);
167090792Sgshapiro				(void) sm_signal(SIGCHLD, reapchild);
167190792Sgshapiro			}
167290792Sgshapiro			if (tTd(63, 100))
167390792Sgshapiro				sm_syslog(LOG_DEBUG, NOQID,
167490792Sgshapiro					  "runqueue %s dowork(%s)",
167590792Sgshapiro					  qid_printqueue(w->w_qgrp, w->w_qdir),
167690792Sgshapiro					  w->w_name + 2);
167790792Sgshapiro
167890792Sgshapiro			(void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
167990792Sgshapiro				      false, false, e);
168090792Sgshapiro			errno = 0;
168190792Sgshapiro		}
168290792Sgshapiro		sm_free(w->w_name); /* XXX */
168390792Sgshapiro		if (w->w_host != NULL)
168490792Sgshapiro			sm_free(w->w_host); /* XXX */
168590792Sgshapiro		sm_free((char *) w); /* XXX */
168690792Sgshapiro		sequenceno += skip; /* next sequence number */
168790792Sgshapiro#if SM_HEAP_CHECK
168890792Sgshapiro		if (sm_debug_active(&DebugLeakQ, 1))
168990792Sgshapiro			sm_heap_setgroup(oldgroup);
169090792Sgshapiro#endif /* SM_HEAP_CHECK */
169190792Sgshapiro	}
169290792Sgshapiro
169390792Sgshapiro	BlockOldsh = false;
169490792Sgshapiro
169590792Sgshapiro	/* check the signals didn't happen during the revert */
169690792Sgshapiro	if (NoMoreRunners)
169790792Sgshapiro	{
169890792Sgshapiro		/* Check that a valid signal handler is callable */
169990792Sgshapiro		if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
170090792Sgshapiro		    Oldsh != runners_sighup && Oldsh != runners_sigterm)
170190792Sgshapiro			(*Oldsh)(Oldsig);
170290792Sgshapiro	}
170390792Sgshapiro
170490792Sgshapiro	Oldsh = SIG_DFL; /* after the NoMoreRunners check */
170590792Sgshapiro}
170690792Sgshapiro/*
170790792Sgshapiro**  RUN_WORK_GROUP -- run the jobs in a queue group from a work group.
170890792Sgshapiro**
170964562Sgshapiro**	Gets the stuff out of the queue in some presumably logical
171064562Sgshapiro**	order and processes them.
171164562Sgshapiro**
171264562Sgshapiro**	Parameters:
171390792Sgshapiro**		wgrp -- work group to process.
171490792Sgshapiro**		forkflag -- true if the queue scanning should be done in
171564562Sgshapiro**			a child process.  We double-fork so it is not our
171664562Sgshapiro**			child and we don't have to clean up after it.
171790792Sgshapiro**		verbose -- if true, print out status information.
171890792Sgshapiro**		persistent -- persistent queue runner?
171990792Sgshapiro**		runall -- true: run all of the queue groups in this work group
172064562Sgshapiro**
172164562Sgshapiro**	Returns:
172290792Sgshapiro**		true if the queue run successfully began.
172364562Sgshapiro**
172464562Sgshapiro**	Side Effects:
172564562Sgshapiro**		runs things in the mail queue.
172664562Sgshapiro*/
172764562Sgshapiro
172890792Sgshapiro/* Minimum sleep time for persistent queue runners */
172990792Sgshapiro#define MIN_SLEEP_TIME	5
173090792Sgshapiro
173190792Sgshapirobool
173290792Sgshapirorun_work_group(wgrp, forkflag, verbose, persistent, runall)
173390792Sgshapiro	int wgrp;
173464562Sgshapiro	bool forkflag;
173564562Sgshapiro	bool verbose;
173690792Sgshapiro	bool persistent;
173790792Sgshapiro	bool runall;
173864562Sgshapiro{
173938032Speter	register ENVELOPE *e;
174090792Sgshapiro	int njobs, qdir;
174190792Sgshapiro	int sequenceno = 1;
174290792Sgshapiro	int qgrp, endgrp, h, i;
174394334Sgshapiro	time_t current_la_time, now;
174490792Sgshapiro	bool full, more;
174590792Sgshapiro	SM_RPOOL_T *rpool;
174690792Sgshapiro	extern void rmexpstab __P((void));
174738032Speter	extern ENVELOPE BlankEnvelope;
174890792Sgshapiro	extern SIGFUNC_DECL reapchild __P((int));
174938032Speter
175090792Sgshapiro	if (wgrp < 0)
175190792Sgshapiro		return false;
175290792Sgshapiro
175338032Speter	/*
175438032Speter	**  If no work will ever be selected, don't even bother reading
175538032Speter	**  the queue.
175638032Speter	*/
175738032Speter
175890792Sgshapiro	sm_getla();	/* get load average */
175938032Speter	current_la_time = curtime();
176038032Speter
176190792Sgshapiro	if (!persistent && shouldqueue(WkRecipFact, current_la_time))
176238032Speter	{
176338032Speter		char *msg = "Skipping queue run -- load average too high";
176438032Speter
176538032Speter		if (verbose)
176638032Speter			message("458 %s\n", msg);
176738032Speter		if (LogLevel > 8)
176890792Sgshapiro			sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
176990792Sgshapiro		return false;
177038032Speter	}
177138032Speter
177238032Speter	/*
177338032Speter	**  See if we already have too many children.
177438032Speter	*/
177538032Speter
177690792Sgshapiro	if (forkflag && WorkGrp[wgrp].wg_lowqintvl > 0 && !persistent &&
177738032Speter	    MaxChildren > 0 && CurChildren >= MaxChildren)
177838032Speter	{
177964562Sgshapiro		char *msg = "Skipping queue run -- too many children";
178064562Sgshapiro
178164562Sgshapiro		if (verbose)
178264562Sgshapiro			message("458 %s (%d)\n", msg, CurChildren);
178364562Sgshapiro		if (LogLevel > 8)
178490792Sgshapiro			sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)",
178564562Sgshapiro				  msg, CurChildren);
178690792Sgshapiro		return false;
178738032Speter	}
178838032Speter
178938032Speter	/*
179038032Speter	**  See if we want to go off and do other useful work.
179138032Speter	*/
179238032Speter
179338032Speter	if (forkflag)
179438032Speter	{
179538032Speter		pid_t pid;
179638032Speter
179790792Sgshapiro		(void) sm_blocksignal(SIGCHLD);
179890792Sgshapiro		(void) sm_signal(SIGCHLD, reapchild);
179938032Speter
180038032Speter		pid = dofork();
180138032Speter		if (pid == -1)
180238032Speter		{
180338032Speter			const char *msg = "Skipping queue run -- fork() failed";
180490792Sgshapiro			const char *err = sm_errstring(errno);
180538032Speter
180638032Speter			if (verbose)
180738032Speter				message("458 %s: %s\n", msg, err);
180838032Speter			if (LogLevel > 8)
180990792Sgshapiro				sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s",
181064562Sgshapiro					  msg, err);
181190792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
181290792Sgshapiro			return false;
181338032Speter		}
181438032Speter		if (pid != 0)
181538032Speter		{
181638032Speter			/* parent -- pick up intermediate zombie */
181790792Sgshapiro			(void) sm_blocksignal(SIGALRM);
181890792Sgshapiro
181990792Sgshapiro			/* wgrp only used when queue runners are persistent */
182090792Sgshapiro			proc_list_add(pid, "Queue runner", PROC_QUEUE,
182190792Sgshapiro				      WorkGrp[wgrp].wg_maxact,
182290792Sgshapiro				      persistent ? wgrp : -1);
182390792Sgshapiro			(void) sm_releasesignal(SIGALRM);
182490792Sgshapiro			(void) sm_releasesignal(SIGCHLD);
182590792Sgshapiro			return true;
182638032Speter		}
182790792Sgshapiro
182864562Sgshapiro		/* child -- clean up signals */
182977349Sgshapiro
183077349Sgshapiro		/* Reset global flags */
183177349Sgshapiro		RestartRequest = NULL;
183290792Sgshapiro		RestartWorkGroup = false;
183377349Sgshapiro		ShutdownRequest = NULL;
183477349Sgshapiro		PendingSignal = 0;
183590792Sgshapiro		CurrentPid = getpid();
183677349Sgshapiro
183790792Sgshapiro		/*
183890792Sgshapiro		**  Initialize exception stack and default exception
183990792Sgshapiro		**  handler for child process.
184090792Sgshapiro		*/
184190792Sgshapiro
184290792Sgshapiro		sm_exc_newthread(fatal_error);
184342575Speter		clrcontrol();
184438032Speter		proc_list_clear();
184542575Speter
184642575Speter		/* Add parent process as first child item */
184790792Sgshapiro		proc_list_add(CurrentPid, "Queue runner child process",
184890792Sgshapiro			      PROC_QUEUE_CHILD, 0, -1);
184990792Sgshapiro		(void) sm_releasesignal(SIGCHLD);
185090792Sgshapiro		(void) sm_signal(SIGCHLD, SIG_DFL);
185190792Sgshapiro		(void) sm_signal(SIGHUP, SIG_DFL);
185290792Sgshapiro		(void) sm_signal(SIGTERM, intsig);
185338032Speter	}
185438032Speter
185538032Speter	/*
185638032Speter	**  Release any resources used by the daemon code.
185738032Speter	*/
185838032Speter
185938032Speter	clrdaemon();
186038032Speter
186138032Speter	/* force it to run expensive jobs */
186290792Sgshapiro	NoConnect = false;
186338032Speter
186438032Speter	/* drop privileges */
186538032Speter	if (geteuid() == (uid_t) 0)
186690792Sgshapiro		(void) drop_privileges(false);
186738032Speter
186838032Speter	/*
186938032Speter	**  Create ourselves an envelope
187038032Speter	*/
187138032Speter
187238032Speter	CurEnv = &QueueEnvelope;
187390792Sgshapiro	rpool = sm_rpool_new_x(NULL);
187490792Sgshapiro	e = newenvelope(&QueueEnvelope, CurEnv, rpool);
187538032Speter	e->e_flags = BlankEnvelope.e_flags;
187673188Sgshapiro	e->e_parent = NULL;
187738032Speter
187838032Speter	/* make sure we have disconnected from parent */
187938032Speter	if (forkflag)
188038032Speter	{
188138032Speter		disconnect(1, e);
188290792Sgshapiro		QuickAbort = false;
188338032Speter	}
188438032Speter
188538032Speter	/*
188638032Speter	**  If we are running part of the queue, always ignore stored
188738032Speter	**  host status.
188838032Speter	*/
188938032Speter
189038032Speter	if (QueueLimitId != NULL || QueueLimitSender != NULL ||
189190792Sgshapiro#if _FFR_QUARANTINE
189290792Sgshapiro	    QueueLimitQuarantine != NULL ||
189390792Sgshapiro#endif /* _FFR_QUARANTINE */
189438032Speter	    QueueLimitRecipient != NULL)
189538032Speter	{
189690792Sgshapiro		IgnoreHostStatus = true;
189738032Speter		MinQueueAge = 0;
189838032Speter	}
189938032Speter
190038032Speter	/*
190190792Sgshapiro	**  Here is where we choose the queue group from the work group.
190290792Sgshapiro	**  The caller of the "domorework" label must setup a new envelope.
190390792Sgshapiro	*/
190490792Sgshapiro
190590792Sgshapiro	endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
190690792Sgshapiro
190790792Sgshapiro  domorework:
190890792Sgshapiro
190990792Sgshapiro	/*
191090792Sgshapiro	**  Run a queue group if:
191190792Sgshapiro	**  runall is set or the bit for this group is set.
191290792Sgshapiro	*/
191390792Sgshapiro
191494334Sgshapiro	now = curtime();
191590792Sgshapiro	for (;;)
191690792Sgshapiro	{
191790792Sgshapiro		/*
191890792Sgshapiro		**  Find the next queue group within the work group that
191990792Sgshapiro		**  has been marked as needing a run.
192090792Sgshapiro		*/
192190792Sgshapiro
192290792Sgshapiro		qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index;
192390792Sgshapiro		WorkGrp[wgrp].wg_curqgrp++; /* advance */
192490792Sgshapiro		WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */
192594334Sgshapiro		if (runall ||
192694334Sgshapiro		    (Queue[qgrp]->qg_nextrun <= now &&
192794334Sgshapiro		     Queue[qgrp]->qg_nextrun != (time_t) -1))
192890792Sgshapiro			break;
192990792Sgshapiro		if (endgrp == WorkGrp[wgrp].wg_curqgrp)
193090792Sgshapiro		{
193190792Sgshapiro			e->e_id = NULL;
193290792Sgshapiro			if (forkflag)
193390792Sgshapiro				finis(true, true, ExitStat);
193490792Sgshapiro			return true; /* we're done */
193590792Sgshapiro		}
193690792Sgshapiro	}
193790792Sgshapiro
193890792Sgshapiro	qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */
193990792Sgshapiro#if _FFR_QUEUE_SCHED_DBG
194090792Sgshapiro	if (tTd(69, 12))
194190792Sgshapiro		sm_syslog(LOG_INFO, NOQID,
194290792Sgshapiro			"rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d",
194390792Sgshapiro			wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir),
194490792Sgshapiro			WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp);
194590792Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
194690792Sgshapiro
194790792Sgshapiro#if HASNICE
194890792Sgshapiro	/* tweak niceness of queue runs */
194990792Sgshapiro	if (Queue[qgrp]->qg_nice > 0)
195090792Sgshapiro		(void) nice(Queue[qgrp]->qg_nice);
195190792Sgshapiro#endif /* HASNICE */
195290792Sgshapiro
195390792Sgshapiro	/* XXX running queue group... */
195490792Sgshapiro	sm_setproctitle(true, CurEnv, "running queue: %s",
195590792Sgshapiro			qid_printqueue(qgrp, qdir));
195690792Sgshapiro
195790792Sgshapiro	if (LogLevel > 69 || tTd(63, 99))
195890792Sgshapiro		sm_syslog(LOG_DEBUG, NOQID,
195990792Sgshapiro			  "runqueue %s, pid=%d, forkflag=%d",
196090792Sgshapiro			  qid_printqueue(qgrp, qdir), (int) CurrentPid,
196190792Sgshapiro			  forkflag);
196290792Sgshapiro
196390792Sgshapiro	/*
196438032Speter	**  Start making passes through the queue.
196538032Speter	**	First, read and sort the entire queue.
196638032Speter	**	Then, process the work in that order.
196738032Speter	**		But if you take too long, start over.
196838032Speter	*/
196938032Speter
197090792Sgshapiro	for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
197190792Sgshapiro	{
197290792Sgshapiro		h = gatherq(qgrp, qdir, false, &full, &more);
197390792Sgshapiro#if SM_CONF_SHM
197490792Sgshapiro		if (ShmId != SM_SHM_NO_ID)
197590792Sgshapiro			QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h;
197690792Sgshapiro#endif /* SM_CONF_SHM */
197790792Sgshapiro		/* If there are no more items in this queue advance */
197890792Sgshapiro		if (!more)
197990792Sgshapiro		{
198090792Sgshapiro			/* A round-robin advance */
198190792Sgshapiro			qdir++;
198290792Sgshapiro			qdir %= Queue[qgrp]->qg_numqueues;
198390792Sgshapiro		}
198490792Sgshapiro
198590792Sgshapiro		/* Has the WorkList reached the limit? */
198690792Sgshapiro		if (full)
198790792Sgshapiro			break; /* don't try to gather more */
198890792Sgshapiro	}
198990792Sgshapiro
199038032Speter	/* order the existing work requests */
199190792Sgshapiro	njobs = sortq(Queue[qgrp]->qg_maxlist);
199290792Sgshapiro	Queue[qgrp]->qg_curnum = qdir; /* update */
199338032Speter
199464562Sgshapiro
199590792Sgshapiro	if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
199638032Speter	{
199790792Sgshapiro		int loop, maxrunners;
199890792Sgshapiro		pid_t pid;
199938032Speter
200038032Speter		/*
200190792Sgshapiro		**  For this WorkQ we want to fork off N children (maxrunners)
200290792Sgshapiro		**  at this point. Each child has a copy of WorkQ. Each child
200390792Sgshapiro		**  will process every N-th item. The parent will wait for all
200490792Sgshapiro		**  of the children to finish before moving on to the next
200590792Sgshapiro		**  queue group within the work group. This saves us forking
200690792Sgshapiro		**  a new runner-child for each work item.
200790792Sgshapiro		**  It's valid for qg_maxqrun == 0 since this may be an
200890792Sgshapiro		**  explicit "don't run this queue" setting.
200938032Speter		*/
201038032Speter
201190792Sgshapiro		maxrunners = Queue[qgrp]->qg_maxqrun;
201290792Sgshapiro
201390792Sgshapiro		/* No need to have more runners then there are jobs */
201490792Sgshapiro		if (maxrunners > njobs)
201590792Sgshapiro			maxrunners = njobs;
201690792Sgshapiro		for (loop = 0; loop < maxrunners; loop++)
201738032Speter		{
2018102528Sgshapiro#if _FFR_NONSTOP_PERSISTENCE
201990792Sgshapiro			/*
2020102528Sgshapiro			**  Require a free "slot" before processing
2021102528Sgshapiro			**  this queue runner.
2022102528Sgshapiro			*/
2023102528Sgshapiro
2024102528Sgshapiro			while (MaxQueueChildren > 0 &&
2025102528Sgshapiro			       CurChildren > MaxQueueChildren)
2026102528Sgshapiro			{
2027102528Sgshapiro				int status;
2028102528Sgshapiro				pid_t ret;
2029102528Sgshapiro
2030102528Sgshapiro				while ((ret = sm_wait(&status)) <= 0)
2031102528Sgshapiro					continue;
2032102528Sgshapiro				proc_list_drop(ret, status, NULL);
2033102528Sgshapiro			}
2034102528Sgshapiro#endif /* _FFR_NONSTOP_PERSISTENCE */
2035102528Sgshapiro
2036102528Sgshapiro			/*
203790792Sgshapiro			**  Since the delivery may happen in a child and the
203890792Sgshapiro			**  parent does not wait, the parent may close the
203990792Sgshapiro			**  maps thereby removing any shared memory used by
204090792Sgshapiro			**  the map.  Therefore, close the maps now so the
204190792Sgshapiro			**  child will dynamically open them if necessary.
204290792Sgshapiro			*/
204390792Sgshapiro
204490792Sgshapiro			closemaps(false);
204590792Sgshapiro
204690792Sgshapiro			pid = fork();
204790792Sgshapiro			if (pid < 0)
204890792Sgshapiro			{
204990792Sgshapiro				syserr("run_work_group: cannot fork");
205090792Sgshapiro				return 0;
205190792Sgshapiro			}
205290792Sgshapiro			else if (pid > 0)
205390792Sgshapiro			{
205490792Sgshapiro				/* parent -- clean out connection cache */
205590792Sgshapiro				mci_flush(false, NULL);
205690792Sgshapiro				WorkQ = WorkQ->w_next; /* for the skip */
205790792Sgshapiro				sequenceno++;
205890792Sgshapiro				proc_list_add(pid, "Queue child runner process",
205990792Sgshapiro					      PROC_QUEUE_CHILD, 0, -1);
206090792Sgshapiro
206190792Sgshapiro				/* No additional work, no additional runners */
206290792Sgshapiro				if (WorkQ == NULL)
206390792Sgshapiro					break;
206490792Sgshapiro			}
206590792Sgshapiro			else
206690792Sgshapiro			{
206790792Sgshapiro				/* child -- Reset global flags */
206890792Sgshapiro				RestartRequest = NULL;
206990792Sgshapiro				RestartWorkGroup = false;
207090792Sgshapiro				ShutdownRequest = NULL;
207190792Sgshapiro				PendingSignal = 0;
207290792Sgshapiro				CurrentPid = getpid();
207390792Sgshapiro
207490792Sgshapiro				/*
207590792Sgshapiro				**  Initialize exception stack and default
207690792Sgshapiro				**  exception handler for child process.
207790792Sgshapiro				**  When fork()'d the child now has a private
207890792Sgshapiro				**  copy of WorkQ at its current position.
207990792Sgshapiro				*/
208090792Sgshapiro
208190792Sgshapiro				sm_exc_newthread(fatal_error);
208290792Sgshapiro
208390792Sgshapiro				/*
208490792Sgshapiro				**  SMTP processes (whether -bd or -bs) set
208590792Sgshapiro				**  SIGCHLD to reapchild to collect
208690792Sgshapiro				**  children status.  However, at delivery
208790792Sgshapiro				**  time, that status must be collected
208890792Sgshapiro				**  by sm_wait() to be dealt with properly
208990792Sgshapiro				**  (check success of delivery based
209090792Sgshapiro				**  on status code, etc).  Therefore, if we
209190792Sgshapiro				**  are an SMTP process, reset SIGCHLD
209290792Sgshapiro				**  back to the default so reapchild
209390792Sgshapiro				**  doesn't collect status before
209490792Sgshapiro				**  sm_wait().
209590792Sgshapiro				*/
209690792Sgshapiro
209790792Sgshapiro				if (OpMode == MD_SMTP ||
209890792Sgshapiro				    OpMode == MD_DAEMON ||
209990792Sgshapiro				    MaxQueueChildren > 0)
210090792Sgshapiro				{
210190792Sgshapiro					proc_list_clear();
210290792Sgshapiro					sm_releasesignal(SIGCHLD);
210390792Sgshapiro					(void) sm_signal(SIGCHLD, SIG_DFL);
210490792Sgshapiro				}
210590792Sgshapiro
210690792Sgshapiro				/* child -- error messages to the transcript */
210790792Sgshapiro				QuickAbort = OnlyOneError = false;
210890792Sgshapiro				runner_work(e, sequenceno, true,
210990792Sgshapiro					    maxrunners, njobs);
211090792Sgshapiro
211190792Sgshapiro				/* This child is done */
211290792Sgshapiro				finis(true, true, ExitStat);
211390792Sgshapiro				/* NOTREACHED */
211490792Sgshapiro			}
211538032Speter		}
211690792Sgshapiro
211790792Sgshapiro		sm_releasesignal(SIGCHLD);
211890792Sgshapiro
2119102528Sgshapiro#if !_FFR_NONSTOP_PERSISTENCE
212090792Sgshapiro		/*
212190792Sgshapiro		**  Wait until all of the runners have completed before
212290792Sgshapiro		**  seeing if there is another queue group in the
212390792Sgshapiro		**  work group to process.
212490792Sgshapiro		**  XXX Future enhancement: don't wait() for all children
212590792Sgshapiro		**  here, just go ahead and make sure that overall the number
212690792Sgshapiro		**  of children is not exceeded.
212790792Sgshapiro		*/
212890792Sgshapiro
212990792Sgshapiro		while (CurChildren > 0)
213038032Speter		{
213190792Sgshapiro			int status;
213290792Sgshapiro			pid_t ret;
213338032Speter
213490792Sgshapiro			while ((ret = sm_wait(&status)) <= 0)
213590792Sgshapiro				continue;
213690792Sgshapiro			proc_list_drop(ret, status, NULL);
213738032Speter		}
2138102528Sgshapiro#endif /* !_FFR_NONSTOP_PERSISTENCE */
213990792Sgshapiro	}
214090792Sgshapiro	else
214190792Sgshapiro	{
214290792Sgshapiro		/*
214390792Sgshapiro		**  When current process will not fork children to do the work,
214490792Sgshapiro		**  it will do the work itself. The 'skip' will be 1 since
214590792Sgshapiro		**  there are no child runners to divide the work across.
214690792Sgshapiro		*/
214790792Sgshapiro
214890792Sgshapiro		runner_work(e, sequenceno, false, 1, njobs);
214990792Sgshapiro	}
215090792Sgshapiro
215190792Sgshapiro	/* free memory allocated by newenvelope() above */
215290792Sgshapiro	sm_rpool_free(rpool);
215390792Sgshapiro	QueueEnvelope.e_rpool = NULL;
215490792Sgshapiro
215590792Sgshapiro	/* Are there still more queues in the work group to process? */
215690792Sgshapiro	if (endgrp != WorkGrp[wgrp].wg_curqgrp)
215790792Sgshapiro	{
215890792Sgshapiro		rpool = sm_rpool_new_x(NULL);
215990792Sgshapiro		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
216090792Sgshapiro		e->e_flags = BlankEnvelope.e_flags;
216190792Sgshapiro		goto domorework;
216290792Sgshapiro	}
216390792Sgshapiro
216490792Sgshapiro	/* No more queues in work group to process. Now check persistent. */
216590792Sgshapiro	if (persistent)
216690792Sgshapiro	{
216790792Sgshapiro		sequenceno = 1;
216890792Sgshapiro		sm_setproctitle(true, CurEnv, "running queue: %s",
216990792Sgshapiro				qid_printqueue(qgrp, qdir));
217090792Sgshapiro
217190792Sgshapiro		/*
217290792Sgshapiro		**  close bogus maps, i.e., maps which caused a tempfail,
217390792Sgshapiro		**	so we get fresh map connections on the next lookup.
217490792Sgshapiro		**  closemaps() is also called when children are started.
217590792Sgshapiro		*/
217690792Sgshapiro
217790792Sgshapiro		closemaps(true);
217890792Sgshapiro
217990792Sgshapiro		/* Close any cached connections. */
218090792Sgshapiro		mci_flush(true, NULL);
218190792Sgshapiro
218290792Sgshapiro		/* Clean out expired related entries. */
218390792Sgshapiro		rmexpstab();
218490792Sgshapiro
218590792Sgshapiro#if NAMED_BIND
218690792Sgshapiro		/* Update MX records for FallBackMX. */
218790792Sgshapiro		if (FallBackMX != NULL)
218890792Sgshapiro			(void) getfallbackmxrr(FallBackMX);
218990792Sgshapiro#endif /* NAMED_BIND */
219090792Sgshapiro
219190792Sgshapiro#if USERDB
219290792Sgshapiro		/* close UserDatabase */
219390792Sgshapiro		_udbx_close();
219490792Sgshapiro#endif /* USERDB */
219590792Sgshapiro
219690792Sgshapiro#if SM_HEAP_CHECK
219790792Sgshapiro		if (sm_debug_active(&SmHeapCheck, 2)
219890792Sgshapiro		    && access("memdump", F_OK) == 0
219990792Sgshapiro		   )
220038032Speter		{
220190792Sgshapiro			SM_FILE_T *out;
220290792Sgshapiro
220390792Sgshapiro			remove("memdump");
220490792Sgshapiro			out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
220590792Sgshapiro					 "memdump.out", SM_IO_APPEND, NULL);
220690792Sgshapiro			if (out != NULL)
220738032Speter			{
220890792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
220990792Sgshapiro				sm_heap_report(out,
221090792Sgshapiro					sm_debug_level(&SmHeapCheck) - 1);
221190792Sgshapiro				(void) sm_io_close(out, SM_TIME_DEFAULT);
221238032Speter			}
221338032Speter		}
221490792Sgshapiro#endif /* SM_HEAP_CHECK */
221590792Sgshapiro
221690792Sgshapiro		/* let me rest for a second to catch my breath */
221790792Sgshapiro		if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
221890792Sgshapiro			sleep(MIN_SLEEP_TIME);
221990792Sgshapiro		else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
222090792Sgshapiro			sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
222138032Speter		else
222290792Sgshapiro			sleep(WorkGrp[wgrp].wg_lowqintvl);
222338032Speter
222490792Sgshapiro		/*
222590792Sgshapiro		**  Get the LA outside the WorkQ loop if necessary.
222690792Sgshapiro		**  In a persistent queue runner the code is repeated over
222790792Sgshapiro		**  and over but gatherq() may ignore entries due to
222890792Sgshapiro		**  shouldqueue() (do we really have to do this twice?).
222990792Sgshapiro		**  Hence the queue runners would just idle around when once
223090792Sgshapiro		**  CurrentLA caused all entries in a queue to be ignored.
223190792Sgshapiro		*/
223264562Sgshapiro
223390792Sgshapiro		now = curtime();
223490792Sgshapiro		if (njobs == 0 && current_la_time < now - GET_NEW_LA_TIME)
223590792Sgshapiro		{
223690792Sgshapiro			sm_getla();
223790792Sgshapiro			current_la_time = now;
223838032Speter		}
223990792Sgshapiro		rpool = sm_rpool_new_x(NULL);
224090792Sgshapiro		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
224190792Sgshapiro		e->e_flags = BlankEnvelope.e_flags;
224290792Sgshapiro		goto domorework;
224338032Speter	}
224438032Speter
224538032Speter	/* exit without the usual cleanup */
224638032Speter	e->e_id = NULL;
224764562Sgshapiro	if (forkflag)
224890792Sgshapiro		finis(true, true, ExitStat);
224964562Sgshapiro	/* NOTREACHED */
225090792Sgshapiro	return true;
225138032Speter}
225238032Speter
225338032Speter/*
225490792Sgshapiro**  DOQUEUERUN -- do a queue run?
225590792Sgshapiro*/
225690792Sgshapiro
225790792Sgshapirobool
225890792Sgshapirodoqueuerun()
225990792Sgshapiro{
226094334Sgshapiro	return DoQueueRun;
226190792Sgshapiro}
226290792Sgshapiro
226390792Sgshapiro/*
226494334Sgshapiro**  RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done.
226577349Sgshapiro**
226677349Sgshapiro**	Parameters:
226794334Sgshapiro**		none.
226877349Sgshapiro**
226977349Sgshapiro**	Returns:
227077349Sgshapiro**		none.
227177349Sgshapiro**
227290792Sgshapiro**	Side Effects:
227390792Sgshapiro**		The invocation of this function via an alarm may interrupt
227490792Sgshapiro**		a set of actions. Thus errno may be set in that context.
227590792Sgshapiro**		We need to restore errno at the end of this function to ensure
227690792Sgshapiro**		that any work done here that sets errno doesn't return a
227790792Sgshapiro**		misleading/false errno value. Errno may	be EINTR upon entry to
227890792Sgshapiro**		this function because of non-restartable/continuable system
227990792Sgshapiro**		API was active. Iff this is true we will override errno as
228090792Sgshapiro**		a timeout (as a more accurate error message).
228190792Sgshapiro**
228277349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
228377349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
228477349Sgshapiro**		DOING.
228538032Speter*/
228638032Speter
228790792Sgshapirovoid
228894334Sgshapirorunqueueevent()
228938032Speter{
229090792Sgshapiro	int save_errno = errno;
229190792Sgshapiro
229290792Sgshapiro	/*
229390792Sgshapiro	**  Set the general bit that we want a queue run,
229490792Sgshapiro	**  tested in doqueuerun()
229590792Sgshapiro	*/
229690792Sgshapiro
229794334Sgshapiro	DoQueueRun = true;
229894334Sgshapiro#if _FFR_QUEUE_SCHED_DBG
229994334Sgshapiro	if (tTd(69, 10))
230094334Sgshapiro		sm_syslog(LOG_INFO, NOQID, "rqe: done");
230194334Sgshapiro#endif /* _FFR_QUEUE_SCHED_DBG */
230290792Sgshapiro
230390792Sgshapiro	errno = save_errno;
230490792Sgshapiro	if (errno == EINTR)
230590792Sgshapiro		errno = ETIMEDOUT;
230638032Speter}
230790792Sgshapiro/*
230890792Sgshapiro**  GATHERQ -- gather messages from the message queue(s) the work queue.
230938032Speter**
231038032Speter**	Parameters:
231190792Sgshapiro**		qgrp -- the index of the queue group.
231290792Sgshapiro**		qdir -- the index of the queue directory.
231338032Speter**		doall -- if set, include everything in the queue (even
231438032Speter**			the jobs that cannot be run because the load
231590792Sgshapiro**			average is too high, or MaxQueueRun is reached).
231690792Sgshapiro**			Otherwise, exclude those jobs.
231790792Sgshapiro**		full -- (optional) to be set 'true' if WorkList is full
231890792Sgshapiro**		more -- (optional) to be set 'true' if there are still more
231990792Sgshapiro**			messages in this queue not added to WorkList
232038032Speter**
232138032Speter**	Returns:
232238032Speter**		The number of request in the queue (not necessarily
232390792Sgshapiro**		the number of requests in WorkList however).
232438032Speter**
232538032Speter**	Side Effects:
232690792Sgshapiro**		prepares available work into WorkList
232738032Speter*/
232838032Speter
232990792Sgshapiro#define NEED_P		0001	/* 'P': priority */
233090792Sgshapiro#define NEED_T		0002	/* 'T': time */
233190792Sgshapiro#define NEED_R		0004	/* 'R': recipient */
233290792Sgshapiro#define NEED_S		0010	/* 'S': sender */
233390792Sgshapiro#define NEED_H		0020	/* host */
233490792Sgshapiro#if _FFR_QUARANTINE
233590792Sgshapiro# define HAS_QUARANTINE		0040	/* has an unexpected 'q' line */
233690792Sgshapiro# define NEED_QUARANTINE	0100	/* 'q': reason */
233790792Sgshapiro#endif /* _FFR_QUARANTINE */
233838032Speter
233990792Sgshapirostatic WORK	*WorkList = NULL;	/* list of unsort work */
234090792Sgshapirostatic int	WorkListSize = 0;	/* current max size of WorkList */
234190792Sgshapirostatic int	WorkListCount = 0;	/* # of work items in WorkList */
234238032Speter
234364562Sgshapirostatic int
234490792Sgshapirogatherq(qgrp, qdir, doall, full, more)
234590792Sgshapiro	int qgrp;
234690792Sgshapiro	int qdir;
234738032Speter	bool doall;
234890792Sgshapiro	bool *full;
234990792Sgshapiro	bool *more;
235038032Speter{
235138032Speter	register struct dirent *d;
235238032Speter	register WORK *w;
235338032Speter	register char *p;
235438032Speter	DIR *f;
235590792Sgshapiro	int i, num_ent;
235690792Sgshapiro	int wn;
235738032Speter	QUEUE_CHAR *check;
235864562Sgshapiro	char qd[MAXPATHLEN];
235964562Sgshapiro	char qf[MAXPATHLEN];
236064562Sgshapiro
236190792Sgshapiro	wn = WorkListCount - 1;
236290792Sgshapiro	num_ent = 0;
236390792Sgshapiro	if (qdir == NOQDIR)
236490792Sgshapiro		(void) sm_strlcpy(qd, ".", sizeof qd);
236564562Sgshapiro	else
236690792Sgshapiro		(void) sm_strlcpyn(qd, sizeof qd, 2,
236790792Sgshapiro			Queue[qgrp]->qg_qpaths[qdir].qp_name,
236890792Sgshapiro			(bitset(QP_SUBQF,
236990792Sgshapiro				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
237090792Sgshapiro					? "/qf" : ""));
237164562Sgshapiro
237238032Speter	if (tTd(41, 1))
237338032Speter	{
237490792Sgshapiro		sm_dprintf("gatherq:\n");
237538032Speter
237638032Speter		check = QueueLimitId;
237738032Speter		while (check != NULL)
237838032Speter		{
237990792Sgshapiro			sm_dprintf("\tQueueLimitId = %s%s\n",
238090792Sgshapiro				check->queue_negate ? "!" : "",
238164562Sgshapiro				check->queue_match);
238238032Speter			check = check->queue_next;
238338032Speter		}
238438032Speter
238538032Speter		check = QueueLimitSender;
238638032Speter		while (check != NULL)
238738032Speter		{
238890792Sgshapiro			sm_dprintf("\tQueueLimitSender = %s%s\n",
238990792Sgshapiro				check->queue_negate ? "!" : "",
239064562Sgshapiro				check->queue_match);
239138032Speter			check = check->queue_next;
239238032Speter		}
239338032Speter
239438032Speter		check = QueueLimitRecipient;
239538032Speter		while (check != NULL)
239638032Speter		{
239790792Sgshapiro			sm_dprintf("\tQueueLimitRecipient = %s%s\n",
239890792Sgshapiro				check->queue_negate ? "!" : "",
239964562Sgshapiro				check->queue_match);
240038032Speter			check = check->queue_next;
240138032Speter		}
240238032Speter
240390792Sgshapiro#if _FFR_QUARANTINE
240490792Sgshapiro		if (QueueMode == QM_QUARANTINE)
240590792Sgshapiro		{
240690792Sgshapiro			check = QueueLimitQuarantine;
240790792Sgshapiro			while (check != NULL)
240890792Sgshapiro			{
240990792Sgshapiro				sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
241090792Sgshapiro					   check->queue_negate ? "!" : "",
241190792Sgshapiro					   check->queue_match);
241290792Sgshapiro				check = check->queue_next;
241390792Sgshapiro			}
241490792Sgshapiro		}
241590792Sgshapiro#endif /* _FFR_QUARANTINE */
241638032Speter	}
241738032Speter
241838032Speter	/* open the queue directory */
241964562Sgshapiro	f = opendir(qd);
242038032Speter	if (f == NULL)
242138032Speter	{
242290792Sgshapiro		syserr("gatherq: cannot open \"%s\"",
242390792Sgshapiro			qid_printqueue(qgrp, qdir));
242490792Sgshapiro		if (full != NULL)
242590792Sgshapiro			*full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
242690792Sgshapiro		if (more != NULL)
242790792Sgshapiro			*more = false;
242864562Sgshapiro		return 0;
242938032Speter	}
243038032Speter
243138032Speter	/*
243238032Speter	**  Read the work directory.
243338032Speter	*/
243438032Speter
243538032Speter	while ((d = readdir(f)) != NULL)
243638032Speter	{
243790792Sgshapiro		SM_FILE_T *cf;
243838032Speter		int qfver = 0;
243938032Speter		char lbuf[MAXNAME + 1];
244064562Sgshapiro		struct stat sbuf;
244138032Speter
244238032Speter		if (tTd(41, 50))
244390792Sgshapiro			sm_dprintf("gatherq: checking %s..", d->d_name);
244438032Speter
244538032Speter		/* is this an interesting entry? */
244690792Sgshapiro#if _FFR_QUARANTINE
244790792Sgshapiro		if (!(((QueueMode == QM_NORMAL &&
244890792Sgshapiro			d->d_name[0] == NORMQF_LETTER) ||
244990792Sgshapiro		       (QueueMode == QM_QUARANTINE &&
245090792Sgshapiro			d->d_name[0] == QUARQF_LETTER) ||
245190792Sgshapiro		       (QueueMode == QM_LOST &&
245290792Sgshapiro			d->d_name[0] == LOSEQF_LETTER)) &&
245390792Sgshapiro		      d->d_name[1] == 'f'))
245490792Sgshapiro#else /* _FFR_QUARANTINE */
245590792Sgshapiro		if (d->d_name[0] != NORMQF_LETTER || d->d_name[1] != 'f')
245690792Sgshapiro#endif /* _FFR_QUARANTINE */
245790792Sgshapiro		{
245890792Sgshapiro			if (tTd(41, 50))
245990792Sgshapiro				sm_dprintf("  skipping\n");
246038032Speter			continue;
246190792Sgshapiro		}
246290792Sgshapiro		if (tTd(41, 50))
246390792Sgshapiro			sm_dprintf("\n");
246438032Speter
246564562Sgshapiro		if (strlen(d->d_name) >= MAXQFNAME)
246642575Speter		{
246742575Speter			if (Verbose)
246890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
246990792Sgshapiro						     "gatherq: %s too long, %d max characters\n",
247090792Sgshapiro						     d->d_name, MAXQFNAME);
247142575Speter			if (LogLevel > 0)
247242575Speter				sm_syslog(LOG_ALERT, NOQID,
247390792Sgshapiro					  "gatherq: %s too long, %d max characters",
247464562Sgshapiro					  d->d_name, MAXQFNAME);
247538032Speter			continue;
247642575Speter		}
247738032Speter
247838032Speter		check = QueueLimitId;
247938032Speter		while (check != NULL)
248038032Speter		{
248194334Sgshapiro			if (strcontainedin(false, check->queue_match,
248290792Sgshapiro					   d->d_name) != check->queue_negate)
248338032Speter				break;
248438032Speter			else
248538032Speter				check = check->queue_next;
248638032Speter		}
248738032Speter		if (QueueLimitId != NULL && check == NULL)
248838032Speter			continue;
248938032Speter
249064562Sgshapiro		/* grow work list if necessary */
249138032Speter		if (++wn >= MaxQueueRun && MaxQueueRun > 0)
249238032Speter		{
249338032Speter			if (wn == MaxQueueRun && LogLevel > 0)
249464562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
249564562Sgshapiro					  "WorkList for %s maxed out at %d",
249690792Sgshapiro					  qid_printqueue(qgrp, qdir),
249764562Sgshapiro					  MaxQueueRun);
249890792Sgshapiro			if (doall)
249990792Sgshapiro				continue;	/* just count entries */
250090792Sgshapiro			break;
250138032Speter		}
250238032Speter		if (wn >= WorkListSize)
250338032Speter		{
250490792Sgshapiro			grow_wlist(qgrp, qdir);
250538032Speter			if (wn >= WorkListSize)
250638032Speter				continue;
250738032Speter		}
250890792Sgshapiro		SM_ASSERT(wn >= 0);
250964562Sgshapiro		w = &WorkList[wn];
251038032Speter
251190792Sgshapiro		(void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", d->d_name);
251264562Sgshapiro		if (stat(qf, &sbuf) < 0)
251364562Sgshapiro		{
251464562Sgshapiro			if (errno != ENOENT)
251564562Sgshapiro				sm_syslog(LOG_INFO, NOQID,
251690792Sgshapiro					  "gatherq: can't stat %s/%s",
251790792Sgshapiro					  qid_printqueue(qgrp, qdir),
251890792Sgshapiro					  d->d_name);
251964562Sgshapiro			wn--;
252064562Sgshapiro			continue;
252164562Sgshapiro		}
252264562Sgshapiro		if (!bitset(S_IFREG, sbuf.st_mode))
252364562Sgshapiro		{
252464562Sgshapiro			/* Yikes!  Skip it or we will hang on open! */
252590792Sgshapiro			if (!((d->d_name[0] == DATAFL_LETTER ||
252690792Sgshapiro			       d->d_name[0] == NORMQF_LETTER ||
252790792Sgshapiro#if _FFR_QUARANTINE
252890792Sgshapiro			       d->d_name[0] == QUARQF_LETTER ||
252990792Sgshapiro			       d->d_name[0] == LOSEQF_LETTER ||
253090792Sgshapiro#endif /* _FFR_QUARANTINE */
253190792Sgshapiro			       d->d_name[0] == XSCRPT_LETTER) &&
253290792Sgshapiro			      d->d_name[1] == 'f' && d->d_name[2] == '\0'))
253390792Sgshapiro				syserr("gatherq: %s/%s is not a regular file",
253490792Sgshapiro				       qid_printqueue(qgrp, qdir), d->d_name);
253564562Sgshapiro			wn--;
253664562Sgshapiro			continue;
253764562Sgshapiro		}
253864562Sgshapiro
253964562Sgshapiro		/* avoid work if possible */
254090792Sgshapiro		if ((QueueSortOrder == QSO_BYFILENAME ||
254190792Sgshapiro		     QueueSortOrder == QSO_BYMODTIME ||
254290792Sgshapiro		     QueueSortOrder == QSO_RANDOM) &&
254390792Sgshapiro#if _FFR_QUARANTINE
254490792Sgshapiro		    QueueLimitQuarantine == NULL &&
254590792Sgshapiro#endif /* _FFR_QUARANTINE */
254666494Sgshapiro		    QueueLimitSender == NULL &&
254766494Sgshapiro		    QueueLimitRecipient == NULL)
254864562Sgshapiro		{
254990792Sgshapiro			w->w_qgrp = qgrp;
255090792Sgshapiro			w->w_qdir = qdir;
255164562Sgshapiro			w->w_name = newstr(d->d_name);
255264562Sgshapiro			w->w_host = NULL;
255390792Sgshapiro			w->w_lock = w->w_tooyoung = false;
255464562Sgshapiro			w->w_pri = 0;
255564562Sgshapiro			w->w_ctime = 0;
255690792Sgshapiro			w->w_mtime = sbuf.st_mtime;
255790792Sgshapiro			++num_ent;
255864562Sgshapiro			continue;
255964562Sgshapiro		}
256064562Sgshapiro
256164562Sgshapiro		/* open control file */
256290792Sgshapiro		cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY,
256390792Sgshapiro				NULL);
256490792Sgshapiro		if (cf == NULL && OpMode != MD_PRINT)
256538032Speter		{
256638032Speter			/* this may be some random person sending hir msgs */
256738032Speter			if (tTd(41, 2))
256890792Sgshapiro				sm_dprintf("gatherq: cannot open %s: %s\n",
256990792Sgshapiro					d->d_name, sm_errstring(errno));
257038032Speter			errno = 0;
257138032Speter			wn--;
257238032Speter			continue;
257338032Speter		}
257490792Sgshapiro		w->w_qgrp = qgrp;
257590792Sgshapiro		w->w_qdir = qdir;
257638032Speter		w->w_name = newstr(d->d_name);
257738032Speter		w->w_host = NULL;
257890792Sgshapiro		if (cf != NULL)
257990792Sgshapiro		{
258090792Sgshapiro			w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
258190792Sgshapiro							    NULL),
258290792Sgshapiro					      w->w_name, NULL,
258390792Sgshapiro					      LOCK_SH|LOCK_NB);
258490792Sgshapiro		}
258590792Sgshapiro		w->w_tooyoung = false;
258638032Speter
258738032Speter		/* make sure jobs in creation don't clog queue */
258838032Speter		w->w_pri = 0x7fffffff;
258938032Speter		w->w_ctime = 0;
259090792Sgshapiro		w->w_mtime = sbuf.st_mtime;
259138032Speter
259238032Speter		/* extract useful information */
259390792Sgshapiro		i = NEED_P|NEED_T;
259490792Sgshapiro		if (QueueSortOrder == QSO_BYHOST
259590792Sgshapiro#if _FFR_RHS
259690792Sgshapiro		    || QueueSortOrder == QSO_BYSHUFFLE
259790792Sgshapiro#endif /* _FFR_RHS */
259890792Sgshapiro		   )
259971345Sgshapiro		{
260071345Sgshapiro			/* need w_host set for host sort order */
260171345Sgshapiro			i |= NEED_H;
260271345Sgshapiro		}
260338032Speter		if (QueueLimitSender != NULL)
260438032Speter			i |= NEED_S;
260564562Sgshapiro		if (QueueLimitRecipient != NULL)
260638032Speter			i |= NEED_R;
260790792Sgshapiro#if _FFR_QUARANTINE
260890792Sgshapiro		if (QueueLimitQuarantine != NULL)
260990792Sgshapiro			i |= NEED_QUARANTINE;
261090792Sgshapiro#endif /* _FFR_QUARANTINE */
261190792Sgshapiro		while (cf != NULL && i != 0 &&
261290792Sgshapiro		       sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
261390792Sgshapiro				   sizeof lbuf) != NULL)
261438032Speter		{
261538032Speter			int c;
261638032Speter			time_t age;
261738032Speter
261838032Speter			p = strchr(lbuf, '\n');
261938032Speter			if (p != NULL)
262038032Speter				*p = '\0';
262138032Speter			else
262238032Speter			{
262338032Speter				/* flush rest of overly long line */
262490792Sgshapiro				while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
262590792Sgshapiro				       != SM_IO_EOF && c != '\n')
262638032Speter					continue;
262738032Speter			}
262838032Speter
262938032Speter			switch (lbuf[0])
263038032Speter			{
263138032Speter			  case 'V':
263238032Speter				qfver = atoi(&lbuf[1]);
263338032Speter				break;
263438032Speter
263538032Speter			  case 'P':
263638032Speter				w->w_pri = atol(&lbuf[1]);
263738032Speter				i &= ~NEED_P;
263838032Speter				break;
263938032Speter
264038032Speter			  case 'T':
264138032Speter				w->w_ctime = atol(&lbuf[1]);
264238032Speter				i &= ~NEED_T;
264338032Speter				break;
264438032Speter
264590792Sgshapiro#if _FFR_QUARANTINE
264690792Sgshapiro			  case 'q':
264790792Sgshapiro				if (QueueMode != QM_QUARANTINE &&
264890792Sgshapiro				    QueueMode != QM_LOST)
264990792Sgshapiro				{
265090792Sgshapiro					if (tTd(41, 49))
265190792Sgshapiro						sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
265290792Sgshapiro							   w->w_name);
265390792Sgshapiro					i |= HAS_QUARANTINE;
265490792Sgshapiro				}
265590792Sgshapiro				else if (QueueMode == QM_QUARANTINE)
265690792Sgshapiro				{
265790792Sgshapiro					if (QueueLimitQuarantine == NULL)
265890792Sgshapiro					{
265990792Sgshapiro						i &= ~NEED_QUARANTINE;
266090792Sgshapiro						break;
266190792Sgshapiro					}
266290792Sgshapiro					p = &lbuf[1];
266390792Sgshapiro					check = QueueLimitQuarantine;
266490792Sgshapiro					while (check != NULL)
266590792Sgshapiro					{
266690792Sgshapiro						if (strcontainedin(false,
266790792Sgshapiro								   check->queue_match,
266890792Sgshapiro								   p) !=
266990792Sgshapiro						    check->queue_negate)
267090792Sgshapiro							break;
267190792Sgshapiro						else
267290792Sgshapiro							check = check->queue_next;
267390792Sgshapiro					}
267490792Sgshapiro					if (check != NULL)
267590792Sgshapiro						i &= ~NEED_QUARANTINE;
267690792Sgshapiro				}
267790792Sgshapiro				break;
267890792Sgshapiro#endif /* _FFR_QUARANTINE */
267990792Sgshapiro
268038032Speter			  case 'R':
268138032Speter				if (w->w_host == NULL &&
268238032Speter				    (p = strrchr(&lbuf[1], '@')) != NULL)
268364562Sgshapiro				{
268490792Sgshapiro#if _FFR_RHS
268590792Sgshapiro					if (QueueSortOrder == QSO_BYSHUFFLE)
268690792Sgshapiro						w->w_host = newstr(&p[1]);
268790792Sgshapiro					else
268890792Sgshapiro#endif /* _FFR_RHS */
268990792Sgshapiro						w->w_host = strrev(&p[1]);
269064562Sgshapiro					makelower(w->w_host);
269171345Sgshapiro					i &= ~NEED_H;
269264562Sgshapiro				}
269338032Speter				if (QueueLimitRecipient == NULL)
269438032Speter				{
269538032Speter					i &= ~NEED_R;
269638032Speter					break;
269738032Speter				}
269838032Speter				if (qfver > 0)
269938032Speter				{
270038032Speter					p = strchr(&lbuf[1], ':');
270138032Speter					if (p == NULL)
270238032Speter						p = &lbuf[1];
270338032Speter				}
270438032Speter				else
270538032Speter					p = &lbuf[1];
270638032Speter				check = QueueLimitRecipient;
270738032Speter				while (check != NULL)
270838032Speter				{
270990792Sgshapiro					if (strcontainedin(true,
271090792Sgshapiro							   check->queue_match,
271190792Sgshapiro							   p) !=
271290792Sgshapiro					    check->queue_negate)
271338032Speter						break;
271438032Speter					else
271538032Speter						check = check->queue_next;
271638032Speter				}
271738032Speter				if (check != NULL)
271838032Speter					i &= ~NEED_R;
271938032Speter				break;
272038032Speter
272138032Speter			  case 'S':
272264562Sgshapiro				check = QueueLimitSender;
272364562Sgshapiro				while (check != NULL)
272464562Sgshapiro				{
272590792Sgshapiro					if (strcontainedin(true,
272690792Sgshapiro							   check->queue_match,
272790792Sgshapiro							   &lbuf[1]) !=
272890792Sgshapiro					    check->queue_negate)
272964562Sgshapiro						break;
273064562Sgshapiro					else
273164562Sgshapiro						check = check->queue_next;
273264562Sgshapiro				}
273364562Sgshapiro				if (check != NULL)
273464562Sgshapiro					i &= ~NEED_S;
273538032Speter				break;
273638032Speter
273738032Speter			  case 'K':
273838032Speter				age = curtime() - (time_t) atol(&lbuf[1]);
273938032Speter				if (age >= 0 && MinQueueAge > 0 &&
274038032Speter				    age < MinQueueAge)
274190792Sgshapiro					w->w_tooyoung = true;
274238032Speter				break;
274338032Speter
274438032Speter			  case 'N':
274538032Speter				if (atol(&lbuf[1]) == 0)
274690792Sgshapiro					w->w_tooyoung = false;
274738032Speter				break;
274864562Sgshapiro
274990792Sgshapiro#if _FFR_QUEUEDELAY
275064562Sgshapiro/*
275164562Sgshapiro			  case 'G':
275264562Sgshapiro				queuealg = atoi(lbuf[1]);
275364562Sgshapiro				break;
275464562Sgshapiro			  case 'Y':
275564562Sgshapiro				queuedelay = (time_t) atol(&lbuf[1]);
275664562Sgshapiro				break;
275764562Sgshapiro*/
275890792Sgshapiro#endif /* _FFR_QUEUEDELAY */
275938032Speter			}
276038032Speter		}
276190792Sgshapiro		if (cf != NULL)
276290792Sgshapiro			(void) sm_io_close(cf, SM_TIME_DEFAULT);
276338032Speter
276438032Speter		if ((!doall && shouldqueue(w->w_pri, w->w_ctime)) ||
276590792Sgshapiro#if _FFR_QUARANTINE
276690792Sgshapiro		    bitset(HAS_QUARANTINE, i) ||
276790792Sgshapiro		    bitset(NEED_QUARANTINE, i) ||
276890792Sgshapiro#endif /* _FFR_QUARANTINE */
276938032Speter		    bitset(NEED_R|NEED_S, i))
277038032Speter		{
277138032Speter			/* don't even bother sorting this job in */
277238032Speter			if (tTd(41, 49))
277390792Sgshapiro				sm_dprintf("skipping %s (%x)\n", w->w_name, i);
277490792Sgshapiro			sm_free(w->w_name); /* XXX */
277590792Sgshapiro			if (w->w_host != NULL)
277690792Sgshapiro				sm_free(w->w_host); /* XXX */
277738032Speter			wn--;
277838032Speter		}
277990792Sgshapiro		else
278090792Sgshapiro			++num_ent;
278138032Speter	}
278238032Speter	(void) closedir(f);
278338032Speter	wn++;
278438032Speter
278590792Sgshapiro	i = wn - WorkListCount;
278690792Sgshapiro	WorkListCount += SM_MIN(num_ent, WorkListSize);
278790792Sgshapiro
278890792Sgshapiro	if (more != NULL)
278990792Sgshapiro		*more = WorkListCount < wn;
279090792Sgshapiro
279190792Sgshapiro	if (full != NULL)
279290792Sgshapiro		*full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
279390792Sgshapiro			(WorkList == NULL && wn > 0);
279490792Sgshapiro
279590792Sgshapiro	return i;
279690792Sgshapiro}
279790792Sgshapiro/*
279890792Sgshapiro**  SORTQ -- sort the work list
279990792Sgshapiro**
280090792Sgshapiro**	First the old WorkQ is cleared away. Then the WorkList is sorted
280190792Sgshapiro**	for all items so that important (higher sorting value) items are not
280290792Sgshapiro**	trunctated off. Then the most important items are moved from
280390792Sgshapiro**	WorkList to WorkQ. The lower count of 'max' or MaxListCount items
280490792Sgshapiro**	are moved.
280590792Sgshapiro**
280690792Sgshapiro**	Parameters:
280790792Sgshapiro**		max -- maximum number of items to be placed in WorkQ
280890792Sgshapiro**
280990792Sgshapiro**	Returns:
281090792Sgshapiro**		the number of items in WorkQ
281190792Sgshapiro**
281290792Sgshapiro**	Side Effects:
281390792Sgshapiro**		WorkQ gets released and filled with new work. WorkList
281490792Sgshapiro**		gets released. Work items get sorted in order.
281590792Sgshapiro*/
281690792Sgshapiro
281790792Sgshapirostatic int
281890792Sgshapirosortq(max)
281990792Sgshapiro	int max;
282090792Sgshapiro{
282190792Sgshapiro	register int i;			/* local counter */
282290792Sgshapiro	register WORK *w;		/* tmp item pointer */
282390792Sgshapiro	int wc = WorkListCount;		/* trim size for WorkQ */
282490792Sgshapiro
282590792Sgshapiro	if (WorkQ != NULL)
282690792Sgshapiro	{
282790792Sgshapiro		/* Clear out old WorkQ. */
282890792Sgshapiro		for (w = WorkQ; w != NULL; )
282990792Sgshapiro		{
283090792Sgshapiro			register WORK *nw = w->w_next;
283190792Sgshapiro
283290792Sgshapiro			WorkQ = nw;
283390792Sgshapiro			sm_free(w->w_name); /* XXX */
283490792Sgshapiro			if (w->w_host != NULL)
283590792Sgshapiro				sm_free(w->w_host); /* XXX */
283690792Sgshapiro			sm_free((char *) w); /* XXX */
283790792Sgshapiro			w = nw;
283890792Sgshapiro		}
283990792Sgshapiro		sm_free((char *) WorkQ);
284090792Sgshapiro		WorkQ = NULL;
284190792Sgshapiro	}
284290792Sgshapiro
284390792Sgshapiro	if (WorkList == NULL || wc <= 0)
284464562Sgshapiro		return 0;
284538032Speter
284690792Sgshapiro	/* Check if the per queue group item limit will be exceeded */
284790792Sgshapiro	if (wc > max && max > 0)
284890792Sgshapiro		wc = max;
284990792Sgshapiro
285090792Sgshapiro	/*
285190792Sgshapiro	**  The sort now takes place using all of the items in WorkList.
285290792Sgshapiro	**  The list gets trimmed to the most important items after the sort.
285390792Sgshapiro	**  If the trim were to happen before the sort then one or more
285490792Sgshapiro	**  important items might get truncated off -- not what we want.
285590792Sgshapiro	*/
285690792Sgshapiro
285764562Sgshapiro	if (QueueSortOrder == QSO_BYHOST)
285838032Speter	{
285938032Speter		/*
286038032Speter		**  Sort the work directory for the first time,
286138032Speter		**  based on host name, lock status, and priority.
286238032Speter		*/
286338032Speter
286438032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1);
286538032Speter
286638032Speter		/*
286738032Speter		**  If one message to host is locked, "lock" all messages
286838032Speter		**  to that host.
286938032Speter		*/
287038032Speter
287138032Speter		i = 0;
287238032Speter		while (i < wc)
287338032Speter		{
287438032Speter			if (!WorkList[i].w_lock)
287538032Speter			{
287638032Speter				i++;
287738032Speter				continue;
287838032Speter			}
287938032Speter			w = &WorkList[i];
288038032Speter			while (++i < wc)
288138032Speter			{
288238032Speter				if (WorkList[i].w_host == NULL &&
288338032Speter				    w->w_host == NULL)
288490792Sgshapiro					WorkList[i].w_lock = true;
288538032Speter				else if (WorkList[i].w_host != NULL &&
288638032Speter					 w->w_host != NULL &&
288790792Sgshapiro					 sm_strcasecmp(WorkList[i].w_host,
288890792Sgshapiro						       w->w_host) == 0)
288990792Sgshapiro					WorkList[i].w_lock = true;
289038032Speter				else
289138032Speter					break;
289238032Speter			}
289338032Speter		}
289438032Speter
289538032Speter		/*
289638032Speter		**  Sort the work directory for the second time,
289738032Speter		**  based on lock status, host name, and priority.
289838032Speter		*/
289938032Speter
290038032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2);
290138032Speter	}
290264562Sgshapiro	else if (QueueSortOrder == QSO_BYTIME)
290338032Speter	{
290438032Speter		/*
290538032Speter		**  Simple sort based on submission time only.
290638032Speter		*/
290738032Speter
290838032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3);
290938032Speter	}
291064562Sgshapiro	else if (QueueSortOrder == QSO_BYFILENAME)
291164562Sgshapiro	{
291264562Sgshapiro		/*
291390792Sgshapiro		**  Sort based on queue filename.
291464562Sgshapiro		*/
291564562Sgshapiro
291664562Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4);
291764562Sgshapiro	}
291890792Sgshapiro	else if (QueueSortOrder == QSO_RANDOM)
291990792Sgshapiro	{
292090792Sgshapiro		/*
292190792Sgshapiro		**  Sort randomly.
292290792Sgshapiro		**	workcmpf5() returns a random 1 or -1.
292390792Sgshapiro		**	As long as nobody does a verification pass over the
292490792Sgshapiro		**	sorted list, we should be golden.
292590792Sgshapiro		*/
292690792Sgshapiro
292790792Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf5);
292890792Sgshapiro	}
292990792Sgshapiro	else if (QueueSortOrder == QSO_BYMODTIME)
293090792Sgshapiro	{
293190792Sgshapiro		/*
293290792Sgshapiro		**  Simple sort based on modification time of queue file.
293390792Sgshapiro		**  This puts the oldest items first.
293490792Sgshapiro		*/
293590792Sgshapiro
293690792Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf6);
293790792Sgshapiro	}
293890792Sgshapiro#if _FFR_RHS
293990792Sgshapiro	else if (QueueSortOrder == QSO_BYSHUFFLE)
294090792Sgshapiro	{
294190792Sgshapiro		/*
294290792Sgshapiro		**  Simple sort based on shuffled host name.
294390792Sgshapiro		*/
294490792Sgshapiro
294590792Sgshapiro		init_shuffle_alphabet();
294690792Sgshapiro		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf7);
294790792Sgshapiro	}
294890792Sgshapiro#endif /* _FFR_RHS */
294938032Speter	else
295038032Speter	{
295138032Speter		/*
295238032Speter		**  Simple sort based on queue priority only.
295338032Speter		*/
295438032Speter
295538032Speter		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0);
295638032Speter	}
295738032Speter
295838032Speter	/*
295938032Speter	**  Convert the work list into canonical form.
296038032Speter	**	Should be turning it into a list of envelopes here perhaps.
296190792Sgshapiro	**  Only take the most important items up to the per queue group
296290792Sgshapiro	**  maximum.
296338032Speter	*/
296438032Speter
296538032Speter	for (i = wc; --i >= 0; )
296638032Speter	{
296738032Speter		w = (WORK *) xalloc(sizeof *w);
296890792Sgshapiro		w->w_qgrp = WorkList[i].w_qgrp;
296990792Sgshapiro		w->w_qdir = WorkList[i].w_qdir;
297038032Speter		w->w_name = WorkList[i].w_name;
297138032Speter		w->w_host = WorkList[i].w_host;
297238032Speter		w->w_lock = WorkList[i].w_lock;
297338032Speter		w->w_tooyoung = WorkList[i].w_tooyoung;
297438032Speter		w->w_pri = WorkList[i].w_pri;
297538032Speter		w->w_ctime = WorkList[i].w_ctime;
297690792Sgshapiro		w->w_mtime = WorkList[i].w_mtime;
297738032Speter		w->w_next = WorkQ;
297838032Speter		WorkQ = w;
297938032Speter	}
298038032Speter	if (WorkList != NULL)
298190792Sgshapiro		sm_free(WorkList); /* XXX */
298238032Speter	WorkList = NULL;
298338032Speter	WorkListSize = 0;
298490792Sgshapiro	WorkListCount = 0;
298538032Speter
298638032Speter	if (tTd(40, 1))
298738032Speter	{
298838032Speter		for (w = WorkQ; w != NULL; w = w->w_next)
298964562Sgshapiro		{
299064562Sgshapiro			if (w->w_host != NULL)
299190792Sgshapiro				sm_dprintf("%22s: pri=%ld %s\n",
299264562Sgshapiro					w->w_name, w->w_pri, w->w_host);
299364562Sgshapiro			else
299490792Sgshapiro				sm_dprintf("%32s: pri=%ld\n",
299564562Sgshapiro					w->w_name, w->w_pri);
299664562Sgshapiro		}
299738032Speter	}
299838032Speter
299990792Sgshapiro	return wc; /* return number of WorkQ items */
300038032Speter}
300190792Sgshapiro/*
300238032Speter**  GROW_WLIST -- make the work list larger
300338032Speter**
300438032Speter**	Parameters:
300590792Sgshapiro**		qgrp -- the index for the queue group.
300690792Sgshapiro**		qdir -- the index for the queue directory.
300738032Speter**
300838032Speter**	Returns:
300938032Speter**		none.
301038032Speter**
301138032Speter**	Side Effects:
301238032Speter**		Adds another QUEUESEGSIZE entries to WorkList if possible.
301338032Speter**		It can fail if there isn't enough memory, so WorkListSize
301438032Speter**		should be checked again upon return.
301538032Speter*/
301638032Speter
301764562Sgshapirostatic void
301890792Sgshapirogrow_wlist(qgrp, qdir)
301990792Sgshapiro	int qgrp;
302090792Sgshapiro	int qdir;
302138032Speter{
302238032Speter	if (tTd(41, 1))
302390792Sgshapiro		sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
302438032Speter	if (WorkList == NULL)
302538032Speter	{
302664562Sgshapiro		WorkList = (WORK *) xalloc((sizeof *WorkList) *
302764562Sgshapiro					   (QUEUESEGSIZE + 1));
302838032Speter		WorkListSize = QUEUESEGSIZE;
302938032Speter	}
303038032Speter	else
303138032Speter	{
303238032Speter		int newsize = WorkListSize + QUEUESEGSIZE;
303390792Sgshapiro		WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
303490792Sgshapiro					  (unsigned) sizeof(WORK) * (newsize + 1));
303538032Speter
303638032Speter		if (newlist != NULL)
303738032Speter		{
303838032Speter			WorkListSize = newsize;
303938032Speter			WorkList = newlist;
304038032Speter			if (LogLevel > 1)
304138032Speter			{
304264562Sgshapiro				sm_syslog(LOG_INFO, NOQID,
304364562Sgshapiro					  "grew WorkList for %s to %d",
304490792Sgshapiro					  qid_printqueue(qgrp, qdir),
304564562Sgshapiro					  WorkListSize);
304638032Speter			}
304738032Speter		}
304838032Speter		else if (LogLevel > 0)
304938032Speter		{
305038032Speter			sm_syslog(LOG_ALERT, NOQID,
305164562Sgshapiro				  "FAILED to grow WorkList for %s to %d",
305290792Sgshapiro				  qid_printqueue(qgrp, qdir), newsize);
305338032Speter		}
305438032Speter	}
305538032Speter	if (tTd(41, 1))
305690792Sgshapiro		sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
305738032Speter}
305890792Sgshapiro/*
305938032Speter**  WORKCMPF0 -- simple priority-only compare function.
306038032Speter**
306138032Speter**	Parameters:
306238032Speter**		a -- the first argument.
306338032Speter**		b -- the second argument.
306438032Speter**
306538032Speter**	Returns:
306638032Speter**		-1 if a < b
306738032Speter**		 0 if a == b
306838032Speter**		+1 if a > b
306938032Speter**
307038032Speter*/
307138032Speter
307264562Sgshapirostatic int
307338032Speterworkcmpf0(a, b)
307438032Speter	register WORK *a;
307538032Speter	register WORK *b;
307638032Speter{
307738032Speter	long pa = a->w_pri;
307838032Speter	long pb = b->w_pri;
307938032Speter
308038032Speter	if (pa == pb)
308138032Speter		return 0;
308238032Speter	else if (pa > pb)
308338032Speter		return 1;
308438032Speter	else
308538032Speter		return -1;
308638032Speter}
308790792Sgshapiro/*
308838032Speter**  WORKCMPF1 -- first compare function for ordering work based on host name.
308938032Speter**
309038032Speter**	Sorts on host name, lock status, and priority in that order.
309138032Speter**
309238032Speter**	Parameters:
309338032Speter**		a -- the first argument.
309438032Speter**		b -- the second argument.
309538032Speter**
309638032Speter**	Returns:
309738032Speter**		<0 if a < b
309838032Speter**		 0 if a == b
309938032Speter**		>0 if a > b
310038032Speter**
310138032Speter*/
310238032Speter
310364562Sgshapirostatic int
310438032Speterworkcmpf1(a, b)
310538032Speter	register WORK *a;
310638032Speter	register WORK *b;
310738032Speter{
310838032Speter	int i;
310938032Speter
311038032Speter	/* host name */
311138032Speter	if (a->w_host != NULL && b->w_host == NULL)
311238032Speter		return 1;
311338032Speter	else if (a->w_host == NULL && b->w_host != NULL)
311438032Speter		return -1;
311538032Speter	if (a->w_host != NULL && b->w_host != NULL &&
311638032Speter	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
311738032Speter		return i;
311838032Speter
311938032Speter	/* lock status */
312038032Speter	if (a->w_lock != b->w_lock)
312138032Speter		return b->w_lock - a->w_lock;
312238032Speter
312338032Speter	/* job priority */
312473188Sgshapiro	return workcmpf0(a, b);
312538032Speter}
312690792Sgshapiro/*
312738032Speter**  WORKCMPF2 -- second compare function for ordering work based on host name.
312838032Speter**
312938032Speter**	Sorts on lock status, host name, and priority in that order.
313038032Speter**
313138032Speter**	Parameters:
313238032Speter**		a -- the first argument.
313338032Speter**		b -- the second argument.
313438032Speter**
313538032Speter**	Returns:
313638032Speter**		<0 if a < b
313738032Speter**		 0 if a == b
313838032Speter**		>0 if a > b
313938032Speter**
314038032Speter*/
314138032Speter
314264562Sgshapirostatic int
314338032Speterworkcmpf2(a, b)
314438032Speter	register WORK *a;
314538032Speter	register WORK *b;
314638032Speter{
314738032Speter	int i;
314838032Speter
314938032Speter	/* lock status */
315038032Speter	if (a->w_lock != b->w_lock)
315138032Speter		return a->w_lock - b->w_lock;
315238032Speter
315338032Speter	/* host name */
315438032Speter	if (a->w_host != NULL && b->w_host == NULL)
315538032Speter		return 1;
315638032Speter	else if (a->w_host == NULL && b->w_host != NULL)
315738032Speter		return -1;
315838032Speter	if (a->w_host != NULL && b->w_host != NULL &&
315938032Speter	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
316038032Speter		return i;
316138032Speter
316238032Speter	/* job priority */
316373188Sgshapiro	return workcmpf0(a, b);
316438032Speter}
316590792Sgshapiro/*
316638032Speter**  WORKCMPF3 -- simple submission-time-only compare function.
316738032Speter**
316838032Speter**	Parameters:
316938032Speter**		a -- the first argument.
317038032Speter**		b -- the second argument.
317138032Speter**
317238032Speter**	Returns:
317338032Speter**		-1 if a < b
317438032Speter**		 0 if a == b
317538032Speter**		+1 if a > b
317638032Speter**
317738032Speter*/
317838032Speter
317964562Sgshapirostatic int
318038032Speterworkcmpf3(a, b)
318138032Speter	register WORK *a;
318238032Speter	register WORK *b;
318338032Speter{
318438032Speter	if (a->w_ctime > b->w_ctime)
318538032Speter		return 1;
318638032Speter	else if (a->w_ctime < b->w_ctime)
318738032Speter		return -1;
318838032Speter	else
318938032Speter		return 0;
319038032Speter}
319190792Sgshapiro/*
319264562Sgshapiro**  WORKCMPF4 -- compare based on file name
319364562Sgshapiro**
319464562Sgshapiro**	Parameters:
319564562Sgshapiro**		a -- the first argument.
319664562Sgshapiro**		b -- the second argument.
319764562Sgshapiro**
319864562Sgshapiro**	Returns:
319964562Sgshapiro**		-1 if a < b
320064562Sgshapiro**		 0 if a == b
320164562Sgshapiro**		+1 if a > b
320264562Sgshapiro**
320364562Sgshapiro*/
320464562Sgshapiro
320564562Sgshapirostatic int
320664562Sgshapiroworkcmpf4(a, b)
320764562Sgshapiro	register WORK *a;
320864562Sgshapiro	register WORK *b;
320964562Sgshapiro{
321064562Sgshapiro	return strcmp(a->w_name, b->w_name);
321164562Sgshapiro}
321290792Sgshapiro/*
321390792Sgshapiro**  WORKCMPF5 -- compare based on assigned random number
321490792Sgshapiro**
321590792Sgshapiro**	Parameters:
321690792Sgshapiro**		a -- the first argument (ignored).
321790792Sgshapiro**		b -- the second argument (ignored).
321890792Sgshapiro**
321990792Sgshapiro**	Returns:
322090792Sgshapiro**		randomly 1/-1
322190792Sgshapiro*/
322290792Sgshapiro
322390792Sgshapiro/* ARGSUSED0 */
322490792Sgshapirostatic int
322590792Sgshapiroworkcmpf5(a, b)
322690792Sgshapiro	register WORK *a;
322790792Sgshapiro	register WORK *b;
322890792Sgshapiro{
322990792Sgshapiro	return (get_rand_mod(2)) ? 1 : -1;
323090792Sgshapiro}
323190792Sgshapiro/*
323290792Sgshapiro**  WORKCMPF6 -- simple modification-time-only compare function.
323390792Sgshapiro**
323490792Sgshapiro**	Parameters:
323590792Sgshapiro**		a -- the first argument.
323690792Sgshapiro**		b -- the second argument.
323790792Sgshapiro**
323890792Sgshapiro**	Returns:
323990792Sgshapiro**		-1 if a < b
324090792Sgshapiro**		 0 if a == b
324190792Sgshapiro**		+1 if a > b
324290792Sgshapiro**
324390792Sgshapiro*/
324490792Sgshapiro
324590792Sgshapirostatic int
324690792Sgshapiroworkcmpf6(a, b)
324790792Sgshapiro	register WORK *a;
324890792Sgshapiro	register WORK *b;
324990792Sgshapiro{
325090792Sgshapiro	if (a->w_mtime > b->w_mtime)
325190792Sgshapiro		return 1;
325290792Sgshapiro	else if (a->w_mtime < b->w_mtime)
325390792Sgshapiro		return -1;
325490792Sgshapiro	else
325590792Sgshapiro		return 0;
325690792Sgshapiro}
325790792Sgshapiro#if _FFR_RHS
325890792Sgshapiro/*
325990792Sgshapiro**  WORKCMPF7 -- compare function for ordering work based on shuffled host name.
326090792Sgshapiro**
326190792Sgshapiro**	Sorts on lock status, host name, and priority in that order.
326290792Sgshapiro**
326390792Sgshapiro**	Parameters:
326490792Sgshapiro**		a -- the first argument.
326590792Sgshapiro**		b -- the second argument.
326690792Sgshapiro**
326790792Sgshapiro**	Returns:
326890792Sgshapiro**		<0 if a < b
326990792Sgshapiro**		 0 if a == b
327090792Sgshapiro**		>0 if a > b
327190792Sgshapiro**
327290792Sgshapiro*/
327390792Sgshapiro
327490792Sgshapirostatic int
327590792Sgshapiroworkcmpf7(a, b)
327690792Sgshapiro	register WORK *a;
327790792Sgshapiro	register WORK *b;
327890792Sgshapiro{
327990792Sgshapiro	int i;
328090792Sgshapiro
328190792Sgshapiro	/* lock status */
328290792Sgshapiro	if (a->w_lock != b->w_lock)
328390792Sgshapiro		return a->w_lock - b->w_lock;
328490792Sgshapiro
328590792Sgshapiro	/* host name */
328690792Sgshapiro	if (a->w_host != NULL && b->w_host == NULL)
328790792Sgshapiro		return 1;
328890792Sgshapiro	else if (a->w_host == NULL && b->w_host != NULL)
328990792Sgshapiro		return -1;
329090792Sgshapiro	if (a->w_host != NULL && b->w_host != NULL &&
329190792Sgshapiro	    (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
329290792Sgshapiro		return i;
329390792Sgshapiro
329490792Sgshapiro	/* job priority */
329590792Sgshapiro	return workcmpf0(a, b);
329690792Sgshapiro}
329790792Sgshapiro#endif /* _FFR_RHS */
329890792Sgshapiro/*
329964562Sgshapiro**  STRREV -- reverse string
330064562Sgshapiro**
330164562Sgshapiro**	Returns a pointer to a new string that is the reverse of
330264562Sgshapiro**	the string pointed to by fwd.  The space for the new
330364562Sgshapiro**	string is obtained using xalloc().
330464562Sgshapiro**
330564562Sgshapiro**	Parameters:
330664562Sgshapiro**		fwd -- the string to reverse.
330764562Sgshapiro**
330864562Sgshapiro**	Returns:
330964562Sgshapiro**		the reversed string.
331064562Sgshapiro*/
331164562Sgshapiro
331264562Sgshapirostatic char *
331364562Sgshapirostrrev(fwd)
331464562Sgshapiro	char *fwd;
331564562Sgshapiro{
331664562Sgshapiro	char *rev = NULL;
331764562Sgshapiro	int len, cnt;
331864562Sgshapiro
331964562Sgshapiro	len = strlen(fwd);
332064562Sgshapiro	rev = xalloc(len + 1);
332164562Sgshapiro	for (cnt = 0; cnt < len; ++cnt)
332264562Sgshapiro		rev[cnt] = fwd[len - cnt - 1];
332364562Sgshapiro	rev[len] = '\0';
332464562Sgshapiro	return rev;
332564562Sgshapiro}
332690792Sgshapiro
332790792Sgshapiro#if _FFR_RHS
332890792Sgshapiro
332990792Sgshapiro#define NASCII	128
333090792Sgshapiro#define NCHAR	256
333190792Sgshapiro
333290792Sgshapirostatic unsigned char ShuffledAlphabet[NCHAR];
333390792Sgshapiro
333490792Sgshapirovoid
333590792Sgshapiroinit_shuffle_alphabet()
333690792Sgshapiro{
333790792Sgshapiro	static bool init = false;
333890792Sgshapiro	int i;
333990792Sgshapiro
334090792Sgshapiro	if (init)
334190792Sgshapiro		return;
334290792Sgshapiro
334390792Sgshapiro	/* fill the ShuffledAlphabet */
334490792Sgshapiro	for (i = 0; i < NCHAR; i++)
334590792Sgshapiro		ShuffledAlphabet[i] = i;
334690792Sgshapiro
334790792Sgshapiro	/* mix it */
334890792Sgshapiro	for (i = 1; i < NCHAR; i++)
334990792Sgshapiro	{
335090792Sgshapiro		register int j = get_random() % NCHAR;
335190792Sgshapiro		register int tmp;
335290792Sgshapiro
335390792Sgshapiro		tmp = ShuffledAlphabet[j];
335490792Sgshapiro		ShuffledAlphabet[j] = ShuffledAlphabet[i];
335590792Sgshapiro		ShuffledAlphabet[i] = tmp;
335690792Sgshapiro	}
335790792Sgshapiro
335890792Sgshapiro	/* make it case insensitive */
335990792Sgshapiro	for (i = 'A'; i <= 'Z'; i++)
336090792Sgshapiro		ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
336190792Sgshapiro
336290792Sgshapiro	/* fill the upper part */
336390792Sgshapiro	for (i = 0; i < NCHAR; i++)
336490792Sgshapiro		ShuffledAlphabet[i + NCHAR] = ShuffledAlphabet[i];
336590792Sgshapiro	init = true;
336690792Sgshapiro}
336790792Sgshapiro
336890792Sgshapirostatic int
336990792Sgshapirosm_strshufflecmp(a, b)
337090792Sgshapiro	char *a;
337190792Sgshapiro	char *b;
337290792Sgshapiro{
337390792Sgshapiro	const unsigned char *us1 = (const unsigned char *) a;
337490792Sgshapiro	const unsigned char *us2 = (const unsigned char *) b;
337590792Sgshapiro
337690792Sgshapiro	while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
337790792Sgshapiro	{
337890792Sgshapiro		if (*us1++ == '\0')
337990792Sgshapiro			return 0;
338090792Sgshapiro	}
338190792Sgshapiro	return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
338290792Sgshapiro}
338390792Sgshapiro#endif /* _FFR_RHS */
338490792Sgshapiro
338590792Sgshapiro/*
338638032Speter**  DOWORK -- do a work request.
338738032Speter**
338838032Speter**	Parameters:
338990792Sgshapiro**		qgrp -- the index of the queue group for the job.
339090792Sgshapiro**		qdir -- the index of the queue directory for the job.
339138032Speter**		id -- the ID of the job to run.
339238032Speter**		forkflag -- if set, run this in background.
339338032Speter**		requeueflag -- if set, reinstantiate the queue quickly.
339438032Speter**			This is used when expanding aliases in the queue.
339538032Speter**			If forkflag is also set, it doesn't wait for the
339638032Speter**			child.
339738032Speter**		e - the envelope in which to run it.
339838032Speter**
339938032Speter**	Returns:
340038032Speter**		process id of process that is running the queue job.
340138032Speter**
340238032Speter**	Side Effects:
340338032Speter**		The work request is satisfied if possible.
340438032Speter*/
340538032Speter
340638032Speterpid_t
340790792Sgshapirodowork(qgrp, qdir, id, forkflag, requeueflag, e)
340890792Sgshapiro	int qgrp;
340990792Sgshapiro	int qdir;
341038032Speter	char *id;
341138032Speter	bool forkflag;
341238032Speter	bool requeueflag;
341338032Speter	register ENVELOPE *e;
341438032Speter{
341538032Speter	register pid_t pid;
341690792Sgshapiro	SM_RPOOL_T *rpool;
341738032Speter
341838032Speter	if (tTd(40, 1))
341990792Sgshapiro		sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
342038032Speter
342138032Speter	/*
342238032Speter	**  Fork for work.
342338032Speter	*/
342438032Speter
342538032Speter	if (forkflag)
342638032Speter	{
342764562Sgshapiro		/*
342864562Sgshapiro		**  Since the delivery may happen in a child and the
342964562Sgshapiro		**  parent does not wait, the parent may close the
343064562Sgshapiro		**  maps thereby removing any shared memory used by
343164562Sgshapiro		**  the map.  Therefore, close the maps now so the
343264562Sgshapiro		**  child will dynamically open them if necessary.
343364562Sgshapiro		*/
343464562Sgshapiro
343590792Sgshapiro		closemaps(false);
343664562Sgshapiro
343738032Speter		pid = fork();
343838032Speter		if (pid < 0)
343938032Speter		{
344038032Speter			syserr("dowork: cannot fork");
344138032Speter			return 0;
344238032Speter		}
344338032Speter		else if (pid > 0)
344438032Speter		{
344538032Speter			/* parent -- clean out connection cache */
344690792Sgshapiro			mci_flush(false, NULL);
344738032Speter		}
344838032Speter		else
344938032Speter		{
345090792Sgshapiro			/*
345190792Sgshapiro			**  Initialize exception stack and default exception
345290792Sgshapiro			**  handler for child process.
345390792Sgshapiro			*/
345490792Sgshapiro
345590792Sgshapiro			/* Reset global flags */
345690792Sgshapiro			RestartRequest = NULL;
345790792Sgshapiro			RestartWorkGroup = false;
345890792Sgshapiro			ShutdownRequest = NULL;
345990792Sgshapiro			PendingSignal = 0;
346090792Sgshapiro			CurrentPid = getpid();
346190792Sgshapiro			sm_exc_newthread(fatal_error);
346290792Sgshapiro
346390792Sgshapiro			/*
346490792Sgshapiro			**  See note above about SMTP processes and SIGCHLD.
346590792Sgshapiro			*/
346690792Sgshapiro
346790792Sgshapiro			if (OpMode == MD_SMTP ||
346890792Sgshapiro			    OpMode == MD_DAEMON ||
346990792Sgshapiro			    MaxQueueChildren > 0)
347090792Sgshapiro			{
347190792Sgshapiro				proc_list_clear();
347290792Sgshapiro				sm_releasesignal(SIGCHLD);
347390792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
347490792Sgshapiro			}
347590792Sgshapiro
347638032Speter			/* child -- error messages to the transcript */
347790792Sgshapiro			QuickAbort = OnlyOneError = false;
347838032Speter		}
347938032Speter	}
348038032Speter	else
348138032Speter	{
348238032Speter		pid = 0;
348338032Speter	}
348438032Speter
348538032Speter	if (pid == 0)
348638032Speter	{
348738032Speter		/*
348838032Speter		**  CHILD
348938032Speter		**	Lock the control file to avoid duplicate deliveries.
349038032Speter		**		Then run the file as though we had just read it.
349138032Speter		**	We save an idea of the temporary name so we
349238032Speter		**		can recover on interrupt.
349338032Speter		*/
349438032Speter
349590792Sgshapiro		if (forkflag)
349690792Sgshapiro		{
349790792Sgshapiro			/* Reset global flags */
349890792Sgshapiro			RestartRequest = NULL;
349990792Sgshapiro			RestartWorkGroup = false;
350090792Sgshapiro			ShutdownRequest = NULL;
350190792Sgshapiro			PendingSignal = 0;
350290792Sgshapiro		}
350377349Sgshapiro
350438032Speter		/* set basic modes, etc. */
350590792Sgshapiro		sm_clear_events();
350664562Sgshapiro		clearstats();
350790792Sgshapiro		rpool = sm_rpool_new_x(NULL);
350890792Sgshapiro		clearenvelope(e, false, rpool);
350938032Speter		e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
351064562Sgshapiro		set_delivery_mode(SM_DELIVER, e);
351138032Speter		e->e_errormode = EM_MAIL;
351238032Speter		e->e_id = id;
351390792Sgshapiro		e->e_qgrp = qgrp;
351490792Sgshapiro		e->e_qdir = qdir;
351590792Sgshapiro		GrabTo = UseErrorsTo = false;
351638032Speter		ExitStat = EX_OK;
351738032Speter		if (forkflag)
351838032Speter		{
351938032Speter			disconnect(1, e);
352090792Sgshapiro			set_op_mode(MD_QUEUERUN);
352138032Speter		}
352290792Sgshapiro		sm_setproctitle(true, e, "%s from queue", qid_printname(e));
352338032Speter		if (LogLevel > 76)
352490792Sgshapiro			sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
352590792Sgshapiro				  (int) CurrentPid);
352638032Speter
352738032Speter		/* don't use the headers from sendmail.cf... */
352838032Speter		e->e_header = NULL;
352938032Speter
353038032Speter		/* read the queue control file -- return if locked */
353190792Sgshapiro		if (!readqf(e, false))
353238032Speter		{
353338032Speter			if (tTd(40, 4) && e->e_id != NULL)
353490792Sgshapiro				sm_dprintf("readqf(%s) failed\n",
353564562Sgshapiro					qid_printname(e));
353638032Speter			e->e_id = NULL;
353738032Speter			if (forkflag)
353890792Sgshapiro				finis(false, true, EX_OK);
353938032Speter			else
354090792Sgshapiro			{
354190792Sgshapiro				/* adding this frees 8 bytes */
354290792Sgshapiro				clearenvelope(e, false, rpool);
354390792Sgshapiro
354490792Sgshapiro				/* adding this frees 12 bytes */
354590792Sgshapiro				sm_rpool_free(rpool);
354690792Sgshapiro				e->e_rpool = NULL;
354738032Speter				return 0;
354890792Sgshapiro			}
354938032Speter		}
355038032Speter
355138032Speter		e->e_flags |= EF_INQUEUE;
355290792Sgshapiro		eatheader(e, requeueflag, true);
355338032Speter
355438032Speter		if (requeueflag)
355590792Sgshapiro			queueup(e, false, false);
355638032Speter
355738032Speter		/* do the delivery */
355838032Speter		sendall(e, SM_DELIVER);
355938032Speter
356038032Speter		/* finish up and exit */
356138032Speter		if (forkflag)
356290792Sgshapiro			finis(true, true, ExitStat);
356338032Speter		else
356490792Sgshapiro		{
356590792Sgshapiro			dropenvelope(e, true, false);
356690792Sgshapiro			sm_rpool_free(rpool);
356790792Sgshapiro			e->e_rpool = NULL;
356890792Sgshapiro		}
356938032Speter	}
357038032Speter	e->e_id = NULL;
357138032Speter	return pid;
357238032Speter}
357390792Sgshapiro
357490792Sgshapiro/*
357590792Sgshapiro**  DOWORKLIST -- process a list of envelopes as work requests
357690792Sgshapiro**
357790792Sgshapiro**	Similar to dowork(), except that after forking, it processes an
357890792Sgshapiro**	envelope and its siblings, treating each envelope as a work request.
357990792Sgshapiro**
358090792Sgshapiro**	Parameters:
358190792Sgshapiro**		el -- envelope to be processed including its siblings.
358290792Sgshapiro**		forkflag -- if set, run this in background.
358390792Sgshapiro**		requeueflag -- if set, reinstantiate the queue quickly.
358490792Sgshapiro**			This is used when expanding aliases in the queue.
358590792Sgshapiro**			If forkflag is also set, it doesn't wait for the
358690792Sgshapiro**			child.
358790792Sgshapiro**
358890792Sgshapiro**	Returns:
358990792Sgshapiro**		process id of process that is running the queue job.
359090792Sgshapiro**
359190792Sgshapiro**	Side Effects:
359290792Sgshapiro**		The work request is satisfied if possible.
359390792Sgshapiro*/
359490792Sgshapiro
359590792Sgshapiropid_t
359690792Sgshapirodoworklist(el, forkflag, requeueflag)
359790792Sgshapiro	ENVELOPE *el;
359890792Sgshapiro	bool forkflag;
359990792Sgshapiro	bool requeueflag;
360090792Sgshapiro{
360190792Sgshapiro	register pid_t pid;
360290792Sgshapiro	ENVELOPE *ei;
360390792Sgshapiro
360490792Sgshapiro	if (tTd(40, 1))
360590792Sgshapiro		sm_dprintf("doworklist()\n");
360690792Sgshapiro
360790792Sgshapiro	/*
360890792Sgshapiro	**  Fork for work.
360990792Sgshapiro	*/
361090792Sgshapiro
361190792Sgshapiro	if (forkflag)
361290792Sgshapiro	{
361390792Sgshapiro		/*
361490792Sgshapiro		**  Since the delivery may happen in a child and the
361590792Sgshapiro		**  parent does not wait, the parent may close the
361690792Sgshapiro		**  maps thereby removing any shared memory used by
361790792Sgshapiro		**  the map.  Therefore, close the maps now so the
361890792Sgshapiro		**  child will dynamically open them if necessary.
361990792Sgshapiro		*/
362090792Sgshapiro
362190792Sgshapiro		closemaps(false);
362290792Sgshapiro
362390792Sgshapiro		pid = fork();
362490792Sgshapiro		if (pid < 0)
362590792Sgshapiro		{
362690792Sgshapiro			syserr("doworklist: cannot fork");
362790792Sgshapiro			return 0;
362890792Sgshapiro		}
362990792Sgshapiro		else if (pid > 0)
363090792Sgshapiro		{
363190792Sgshapiro			/* parent -- clean out connection cache */
363290792Sgshapiro			mci_flush(false, NULL);
363390792Sgshapiro		}
363490792Sgshapiro		else
363590792Sgshapiro		{
363690792Sgshapiro			/*
363790792Sgshapiro			**  Initialize exception stack and default exception
363890792Sgshapiro			**  handler for child process.
363990792Sgshapiro			*/
364090792Sgshapiro
364190792Sgshapiro			/* Reset global flags */
364290792Sgshapiro			RestartRequest = NULL;
364390792Sgshapiro			RestartWorkGroup = false;
364490792Sgshapiro			ShutdownRequest = NULL;
364590792Sgshapiro			PendingSignal = 0;
364690792Sgshapiro			CurrentPid = getpid();
364790792Sgshapiro			sm_exc_newthread(fatal_error);
364890792Sgshapiro
364990792Sgshapiro			/*
365090792Sgshapiro			**  See note above about SMTP processes and SIGCHLD.
365190792Sgshapiro			*/
365290792Sgshapiro
365390792Sgshapiro			if (OpMode == MD_SMTP ||
365490792Sgshapiro			    OpMode == MD_DAEMON ||
365590792Sgshapiro			    MaxQueueChildren > 0)
365690792Sgshapiro			{
365790792Sgshapiro				proc_list_clear();
365890792Sgshapiro				sm_releasesignal(SIGCHLD);
365990792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
366090792Sgshapiro			}
366190792Sgshapiro
366290792Sgshapiro			/* child -- error messages to the transcript */
366390792Sgshapiro			QuickAbort = OnlyOneError = false;
366490792Sgshapiro		}
366590792Sgshapiro	}
366690792Sgshapiro	else
366790792Sgshapiro	{
366890792Sgshapiro		pid = 0;
366990792Sgshapiro	}
367090792Sgshapiro
367190792Sgshapiro	if (pid != 0)
367290792Sgshapiro		return pid;
367390792Sgshapiro
367490792Sgshapiro	/*
367590792Sgshapiro	**  IN CHILD
367690792Sgshapiro	**	Lock the control file to avoid duplicate deliveries.
367790792Sgshapiro	**		Then run the file as though we had just read it.
367890792Sgshapiro	**	We save an idea of the temporary name so we
367990792Sgshapiro	**		can recover on interrupt.
368090792Sgshapiro	*/
368190792Sgshapiro
368290792Sgshapiro	if (forkflag)
368390792Sgshapiro	{
368490792Sgshapiro		/* Reset global flags */
368590792Sgshapiro		RestartRequest = NULL;
368690792Sgshapiro		RestartWorkGroup = false;
368790792Sgshapiro		ShutdownRequest = NULL;
368890792Sgshapiro		PendingSignal = 0;
368990792Sgshapiro	}
369090792Sgshapiro
369190792Sgshapiro	/* set basic modes, etc. */
369290792Sgshapiro	sm_clear_events();
369390792Sgshapiro	clearstats();
369490792Sgshapiro	GrabTo = UseErrorsTo = false;
369590792Sgshapiro	ExitStat = EX_OK;
369690792Sgshapiro	if (forkflag)
369790792Sgshapiro	{
369890792Sgshapiro		disconnect(1, el);
369990792Sgshapiro		set_op_mode(MD_QUEUERUN);
370090792Sgshapiro	}
370190792Sgshapiro	if (LogLevel > 76)
370290792Sgshapiro		sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
370390792Sgshapiro			  (int) CurrentPid);
370490792Sgshapiro
370590792Sgshapiro	for (ei = el; ei != NULL; ei = ei->e_sibling)
370690792Sgshapiro	{
370790792Sgshapiro		ENVELOPE e;
370890792Sgshapiro		SM_RPOOL_T *rpool;
370990792Sgshapiro
371090792Sgshapiro		if (WILL_BE_QUEUED(ei->e_sendmode))
371190792Sgshapiro			continue;
371290792Sgshapiro#if _FFR_QUARANTINE
371390792Sgshapiro		else if (QueueMode != QM_QUARANTINE &&
371490792Sgshapiro			 ei->e_quarmsg != NULL)
371590792Sgshapiro			continue;
371690792Sgshapiro#endif /* _FFR_QUARANTINE */
371790792Sgshapiro
371890792Sgshapiro		rpool = sm_rpool_new_x(NULL);
371990792Sgshapiro		clearenvelope(&e, true, rpool);
372090792Sgshapiro		e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
372190792Sgshapiro		set_delivery_mode(SM_DELIVER, &e);
372290792Sgshapiro		e.e_errormode = EM_MAIL;
372390792Sgshapiro		e.e_id = ei->e_id;
372490792Sgshapiro		e.e_qgrp = ei->e_qgrp;
372590792Sgshapiro		e.e_qdir = ei->e_qdir;
372690792Sgshapiro		openxscript(&e);
372790792Sgshapiro		sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
372890792Sgshapiro
372990792Sgshapiro		/* don't use the headers from sendmail.cf... */
373090792Sgshapiro		e.e_header = NULL;
373190792Sgshapiro		CurEnv = &e;
373290792Sgshapiro
373390792Sgshapiro		/* read the queue control file -- return if locked */
373490792Sgshapiro		if (readqf(&e, false))
373590792Sgshapiro		{
373690792Sgshapiro			e.e_flags |= EF_INQUEUE;
373790792Sgshapiro			eatheader(&e, requeueflag, true);
373890792Sgshapiro
373990792Sgshapiro			if (requeueflag)
374090792Sgshapiro				queueup(&e, false, false);
374190792Sgshapiro
374290792Sgshapiro			/* do the delivery */
374390792Sgshapiro			sendall(&e, SM_DELIVER);
374490792Sgshapiro			dropenvelope(&e, true, false);
374590792Sgshapiro		}
374690792Sgshapiro		else
374790792Sgshapiro		{
374890792Sgshapiro			if (tTd(40, 4) && e.e_id != NULL)
374990792Sgshapiro				sm_dprintf("readqf(%s) failed\n",
375090792Sgshapiro					qid_printname(&e));
375190792Sgshapiro		}
375290792Sgshapiro		sm_rpool_free(rpool);
375390792Sgshapiro		ei->e_id = NULL;
375490792Sgshapiro	}
375590792Sgshapiro
375690792Sgshapiro	/* restore CurEnv */
375790792Sgshapiro	CurEnv = el;
375890792Sgshapiro
375990792Sgshapiro	/* finish up and exit */
376090792Sgshapiro	if (forkflag)
376190792Sgshapiro		finis(true, true, ExitStat);
376290792Sgshapiro	return 0;
376390792Sgshapiro}
376490792Sgshapiro/*
376538032Speter**  READQF -- read queue file and set up environment.
376638032Speter**
376738032Speter**	Parameters:
376838032Speter**		e -- the envelope of the job to run.
376990792Sgshapiro**		openonly -- only open the qf (returned as e_lockfp)
377038032Speter**
377138032Speter**	Returns:
377290792Sgshapiro**		true if it successfully read the queue file.
377390792Sgshapiro**		false otherwise.
377438032Speter**
377538032Speter**	Side Effects:
377638032Speter**		The queue file is returned locked.
377738032Speter*/
377838032Speter
377964562Sgshapirostatic bool
378090792Sgshapiroreadqf(e, openonly)
378138032Speter	register ENVELOPE *e;
378290792Sgshapiro	bool openonly;
378338032Speter{
378490792Sgshapiro	register SM_FILE_T *qfp;
378538032Speter	ADDRESS *ctladdr;
378677349Sgshapiro	struct stat st, stf;
378738032Speter	char *bp;
378838032Speter	int qfver = 0;
378938032Speter	long hdrsize = 0;
379038032Speter	register char *p;
379190792Sgshapiro	char *frcpt = NULL;
379238032Speter	char *orcpt = NULL;
379390792Sgshapiro	bool nomore = false;
379490792Sgshapiro	bool bogus = false;
379564562Sgshapiro	MODE_T qsafe;
379694334Sgshapiro	char *err;
379764562Sgshapiro	char qf[MAXPATHLEN];
379838032Speter	char buf[MAXLINE];
379938032Speter
380038032Speter	/*
380138032Speter	**  Read and process the file.
380238032Speter	*/
380338032Speter
380490792Sgshapiro	(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof qf);
380590792Sgshapiro	qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR, NULL);
380638032Speter	if (qfp == NULL)
380738032Speter	{
380864562Sgshapiro		int save_errno = errno;
380964562Sgshapiro
381038032Speter		if (tTd(40, 8))
381190792Sgshapiro			sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
381290792Sgshapiro				qf, sm_errstring(errno));
381364562Sgshapiro		errno = save_errno;
381464562Sgshapiro		if (errno != ENOENT
381564562Sgshapiro		    )
381638032Speter			syserr("readqf: no control file %s", qf);
381790792Sgshapiro		RELEASE_QUEUE;
381890792Sgshapiro		return false;
381938032Speter	}
382038032Speter
382190792Sgshapiro	if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
382290792Sgshapiro		      LOCK_EX|LOCK_NB))
382338032Speter	{
382438032Speter		/* being processed by another queuer */
382564562Sgshapiro		if (Verbose)
382690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
382790792Sgshapiro					     "%s: locked\n", e->e_id);
382864562Sgshapiro		if (tTd(40, 8))
382990792Sgshapiro			sm_dprintf("%s: locked\n", e->e_id);
383038032Speter		if (LogLevel > 19)
383138032Speter			sm_syslog(LOG_DEBUG, e->e_id, "locked");
383290792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
383390792Sgshapiro		RELEASE_QUEUE;
383490792Sgshapiro		return false;
383538032Speter	}
383638032Speter
383738032Speter	/*
383877349Sgshapiro	**  Prevent locking race condition.
383977349Sgshapiro	**
384077349Sgshapiro	**  Process A: readqf(): qfp = fopen(qffile)
384177349Sgshapiro	**  Process B: queueup(): rename(tf, qf)
384277349Sgshapiro	**  Process B: unlocks(tf)
384377349Sgshapiro	**  Process A: lockfile(qf);
384477349Sgshapiro	**
384577349Sgshapiro	**  Process A (us) has the old qf file (before the rename deleted
384677349Sgshapiro	**  the directory entry) and will be delivering based on old data.
384777349Sgshapiro	**  This can lead to multiple deliveries of the same recipients.
384877349Sgshapiro	**
384977349Sgshapiro	**  Catch this by checking if the underlying qf file has changed
385077349Sgshapiro	**  *after* acquiring our lock and if so, act as though the file
385177349Sgshapiro	**  was still locked (i.e., just return like the lockfile() case
385277349Sgshapiro	**  above.
385338032Speter	*/
385438032Speter
385577349Sgshapiro	if (stat(qf, &stf) < 0 ||
385690792Sgshapiro	    fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
385738032Speter	{
385838032Speter		/* must have been being processed by someone else */
385938032Speter		if (tTd(40, 8))
386090792Sgshapiro			sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
386190792Sgshapiro				qf, sm_errstring(errno));
386290792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
386390792Sgshapiro		RELEASE_QUEUE;
386490792Sgshapiro		return false;
386538032Speter	}
386638032Speter
386777349Sgshapiro	if (st.st_nlink != stf.st_nlink ||
386877349Sgshapiro	    st.st_dev != stf.st_dev ||
386990792Sgshapiro	    ST_INODE(st) != ST_INODE(stf) ||
387090792Sgshapiro#if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
387177349Sgshapiro	    st.st_gen != stf.st_gen ||
387290792Sgshapiro#endif /* HAS_ST_GEN && 0 */
387377349Sgshapiro	    st.st_uid != stf.st_uid ||
387477349Sgshapiro	    st.st_gid != stf.st_gid ||
387577349Sgshapiro	    st.st_size != stf.st_size)
387677349Sgshapiro	{
387777349Sgshapiro		/* changed after opened */
387877349Sgshapiro		if (Verbose)
387990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
388090792Sgshapiro					     "%s: changed\n", e->e_id);
388177349Sgshapiro		if (tTd(40, 8))
388290792Sgshapiro			sm_dprintf("%s: changed\n", e->e_id);
388377349Sgshapiro		if (LogLevel > 19)
388477349Sgshapiro			sm_syslog(LOG_DEBUG, e->e_id, "changed");
388590792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
388690792Sgshapiro		RELEASE_QUEUE;
388790792Sgshapiro		return false;
388877349Sgshapiro	}
388977349Sgshapiro
389077349Sgshapiro	/*
389177349Sgshapiro	**  Check the queue file for plausibility to avoid attacks.
389277349Sgshapiro	*/
389377349Sgshapiro
389464562Sgshapiro	qsafe = S_IWOTH|S_IWGRP;
389564562Sgshapiro	if (bitset(S_IWGRP, QueueFileMode))
389664562Sgshapiro		qsafe &= ~S_IWGRP;
389764562Sgshapiro
389890792Sgshapiro	bogus = st.st_uid != geteuid() &&
389990792Sgshapiro		st.st_uid != TrustedUid &&
390090792Sgshapiro		geteuid() != RealUid;
390190792Sgshapiro
390290792Sgshapiro	/*
390390792Sgshapiro	**  If this qf file results from a set-group-ID binary, then
390490792Sgshapiro	**  we check whether the directory is group-writable,
390590792Sgshapiro	**  the queue file mode contains the group-writable bit, and
390690792Sgshapiro	**  the groups are the same.
390790792Sgshapiro	**  Notice: this requires that the set-group-ID binary is used to
390890792Sgshapiro	**  run the queue!
390990792Sgshapiro	*/
391090792Sgshapiro
391190792Sgshapiro	if (bogus && st.st_gid == getegid() && UseMSP)
391238032Speter	{
391390792Sgshapiro		char delim;
391490792Sgshapiro		struct stat dst;
391590792Sgshapiro
391690792Sgshapiro		bp = SM_LAST_DIR_DELIM(qf);
391790792Sgshapiro		if (bp == NULL)
391890792Sgshapiro			delim = '\0';
391990792Sgshapiro		else
392090792Sgshapiro		{
392190792Sgshapiro			delim = *bp;
392290792Sgshapiro			*bp = '\0';
392390792Sgshapiro		}
392490792Sgshapiro		if (stat(delim == '\0' ? "." : qf, &dst) < 0)
392590792Sgshapiro			syserr("readqf: cannot stat directory %s",
392690792Sgshapiro				delim == '\0' ? "." : qf);
392790792Sgshapiro		else
392890792Sgshapiro		{
392990792Sgshapiro			bogus = !(bitset(S_IWGRP, QueueFileMode) &&
393090792Sgshapiro				  bitset(S_IWGRP, dst.st_mode) &&
393190792Sgshapiro				  dst.st_gid == st.st_gid);
393290792Sgshapiro		}
393390792Sgshapiro		if (delim != '\0')
393490792Sgshapiro			*bp = delim;
393590792Sgshapiro	}
393690792Sgshapiro	if (!bogus)
393790792Sgshapiro		bogus = bitset(qsafe, st.st_mode);
393890792Sgshapiro	if (bogus)
393990792Sgshapiro	{
394038032Speter		if (LogLevel > 0)
394138032Speter		{
394238032Speter			sm_syslog(LOG_ALERT, e->e_id,
394390792Sgshapiro				  "bogus queue file, uid=%d, gid=%d, mode=%o",
394490792Sgshapiro				  st.st_uid, st.st_gid, st.st_mode);
394538032Speter		}
394638032Speter		if (tTd(40, 8))
394790792Sgshapiro			sm_dprintf("readqf(%s): bogus file\n", qf);
394890792Sgshapiro		e->e_flags |= EF_INQUEUE;
394990792Sgshapiro		if (!openonly)
395090792Sgshapiro			loseqfile(e, "bogus file uid/gid in mqueue");
395190792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
395290792Sgshapiro		RELEASE_QUEUE;
395390792Sgshapiro		return false;
395438032Speter	}
395538032Speter
395638032Speter	if (st.st_size == 0)
395738032Speter	{
395838032Speter		/* must be a bogus file -- if also old, just remove it */
395990792Sgshapiro		if (!openonly && st.st_ctime + 10 * 60 < curtime())
396038032Speter		{
396190792Sgshapiro			(void) xunlink(queuename(e, DATAFL_LETTER));
396290792Sgshapiro			(void) xunlink(queuename(e, ANYQFL_LETTER));
396338032Speter		}
396490792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
396590792Sgshapiro		RELEASE_QUEUE;
396690792Sgshapiro		return false;
396738032Speter	}
396838032Speter
396938032Speter	if (st.st_nlink == 0)
397038032Speter	{
397138032Speter		/*
397238032Speter		**  Race condition -- we got a file just as it was being
397338032Speter		**  unlinked.  Just assume it is zero length.
397438032Speter		*/
397538032Speter
397690792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
397790792Sgshapiro		RELEASE_QUEUE;
397890792Sgshapiro		return false;
397938032Speter	}
398038032Speter
398190792Sgshapiro#if _FFR_TRUSTED_QF
398290792Sgshapiro	/*
398390792Sgshapiro	**  If we don't own the file mark it as unsafe.
398490792Sgshapiro	**  However, allow TrustedUser to own it as well
398590792Sgshapiro	**  in case TrustedUser manipulates the queue.
398690792Sgshapiro	*/
398790792Sgshapiro
398890792Sgshapiro	if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
398990792Sgshapiro		e->e_flags |= EF_UNSAFE;
399090792Sgshapiro#else /* _FFR_TRUSTED_QF */
399190792Sgshapiro	/* If we don't own the file mark it as unsafe */
399290792Sgshapiro	if (st.st_uid != geteuid())
399390792Sgshapiro		e->e_flags |= EF_UNSAFE;
399490792Sgshapiro#endif /* _FFR_TRUSTED_QF */
399590792Sgshapiro
399638032Speter	/* good file -- save this lock */
399738032Speter	e->e_lockfp = qfp;
399838032Speter
399990792Sgshapiro	/* Just wanted the open file */
400090792Sgshapiro	if (openonly)
400190792Sgshapiro		return true;
400290792Sgshapiro
400338032Speter	/* do basic system initialization */
400438032Speter	initsys(e);
400590792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
400638032Speter
400738032Speter	LineNumber = 0;
400838032Speter	e->e_flags |= EF_GLOBALERRS;
400990792Sgshapiro	set_op_mode(MD_QUEUERUN);
401038032Speter	ctladdr = NULL;
401190792Sgshapiro#if _FFR_QUARANTINE
401290792Sgshapiro	e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
401390792Sgshapiro#endif /* _FFR_QUARANTINE */
401490792Sgshapiro	e->e_dfqgrp = e->e_qgrp;
401590792Sgshapiro	e->e_dfqdir = e->e_qdir;
401690792Sgshapiro#if _FFR_QUEUE_MACRO
401790792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
401890792Sgshapiro		  qid_printqueue(e->e_qgrp, e->e_qdir));
401990792Sgshapiro#endif /* _FFR_QUEUE_MACRO */
402038032Speter	e->e_dfino = -1;
402138032Speter	e->e_msgsize = -1;
402290792Sgshapiro#if _FFR_QUEUEDELAY
402364562Sgshapiro	e->e_queuealg = QD_LINEAR;
402464562Sgshapiro	e->e_queuedelay = (time_t) 0;
402590792Sgshapiro#endif /* _FFR_QUEUEDELAY */
402638032Speter	while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
402738032Speter	{
402890792Sgshapiro		unsigned long qflags;
402938032Speter		ADDRESS *q;
403090792Sgshapiro		int r;
403171345Sgshapiro		time_t now;
403238032Speter		auto char *ep;
403338032Speter
403438032Speter		if (tTd(40, 4))
403590792Sgshapiro			sm_dprintf("+++++ %s\n", bp);
403638032Speter		if (nomore)
403738032Speter		{
403838032Speter			/* hack attack */
403990792Sgshapiro  hackattack:
404090792Sgshapiro			syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
404190792Sgshapiro			       bp);
404294334Sgshapiro			err = "bogus queue line";
404394334Sgshapiro			goto fail;
404438032Speter		}
404538032Speter		switch (bp[0])
404638032Speter		{
404790792Sgshapiro		  case 'A':		/* AUTH= parameter */
404890792Sgshapiro			if (!xtextok(&bp[1]))
404990792Sgshapiro				goto hackattack;
405090792Sgshapiro			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
405190792Sgshapiro			break;
405238032Speter
405390792Sgshapiro		  case 'B':		/* body type */
405490792Sgshapiro			r = check_bodytype(&bp[1]);
405590792Sgshapiro			if (!BODYTYPE_VALID(r))
405690792Sgshapiro				goto hackattack;
405790792Sgshapiro			e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
405890792Sgshapiro			break;
405990792Sgshapiro
406038032Speter		  case 'C':		/* specify controlling user */
406190792Sgshapiro			ctladdr = setctluser(&bp[1], qfver, e);
406238032Speter			break;
406338032Speter
406490792Sgshapiro		  case 'D':		/* data file name */
406590792Sgshapiro			/* obsolete -- ignore */
406638032Speter			break;
406738032Speter
406890792Sgshapiro		  case 'd':		/* data file directory name */
406938032Speter			{
407090792Sgshapiro				int qgrp, qdir;
407190792Sgshapiro
407290792Sgshapiro#if _FFR_MSP_PARANOIA
407390792Sgshapiro				/* forbid queue groups in MSP? */
407490792Sgshapiro				if (UseMSP)
407590792Sgshapiro					goto hackattack;
407690792Sgshapiro#endif /* _FFR_MSP_PARANOIA */
407790792Sgshapiro				for (qgrp = 0;
407890792Sgshapiro				     qgrp < NumQueue && Queue[qgrp] != NULL;
407990792Sgshapiro				     ++qgrp)
408038032Speter				{
408190792Sgshapiro					for (qdir = 0;
408290792Sgshapiro					     qdir < Queue[qgrp]->qg_numqueues;
408390792Sgshapiro					     ++qdir)
408438032Speter					{
408590792Sgshapiro						if (strcmp(&bp[1],
408690792Sgshapiro							   Queue[qgrp]->qg_qpaths[qdir].qp_name)
408790792Sgshapiro						    == 0)
408890792Sgshapiro						{
408990792Sgshapiro							e->e_dfqgrp = qgrp;
409090792Sgshapiro							e->e_dfqdir = qdir;
409190792Sgshapiro							goto done;
409290792Sgshapiro						}
409338032Speter					}
409438032Speter				}
409594334Sgshapiro				err = "bogus queue file directory";
409694334Sgshapiro				goto fail;
409790792Sgshapiro			  done:
409890792Sgshapiro				break;
409938032Speter			}
410038032Speter
410138032Speter		  case 'E':		/* specify error recipient */
410238032Speter			/* no longer used */
410338032Speter			break;
410438032Speter
410590792Sgshapiro		  case 'F':		/* flag bits */
410690792Sgshapiro			if (strncmp(bp, "From ", 5) == 0)
410790792Sgshapiro			{
410890792Sgshapiro				/* we are being spoofed! */
410990792Sgshapiro				syserr("SECURITY ALERT: bogus qf line %s", bp);
411094334Sgshapiro				err = "bogus queue line";
411194334Sgshapiro				goto fail;
411290792Sgshapiro			}
411390792Sgshapiro			for (p = &bp[1]; *p != '\0'; p++)
411490792Sgshapiro			{
411590792Sgshapiro				switch (*p)
411690792Sgshapiro				{
411790792Sgshapiro				  case '8':	/* has 8 bit data */
411890792Sgshapiro					e->e_flags |= EF_HAS8BIT;
411990792Sgshapiro					break;
412038032Speter
412190792Sgshapiro				  case 'b':	/* delete Bcc: header */
412290792Sgshapiro					e->e_flags |= EF_DELETE_BCC;
412390792Sgshapiro					break;
412438032Speter
412590792Sgshapiro				  case 'd':	/* envelope has DSN RET= */
412690792Sgshapiro					e->e_flags |= EF_RET_PARAM;
412790792Sgshapiro					break;
412838032Speter
412990792Sgshapiro				  case 'n':	/* don't return body */
413090792Sgshapiro					e->e_flags |= EF_NO_BODY_RETN;
413190792Sgshapiro					break;
413290792Sgshapiro
413390792Sgshapiro				  case 'r':	/* response */
413490792Sgshapiro					e->e_flags |= EF_RESPONSE;
413590792Sgshapiro					break;
413690792Sgshapiro
413790792Sgshapiro				  case 's':	/* split */
413890792Sgshapiro					e->e_flags |= EF_SPLIT;
413990792Sgshapiro					break;
414090792Sgshapiro
414190792Sgshapiro				  case 'w':	/* warning sent */
414290792Sgshapiro					e->e_flags |= EF_WARNING;
414390792Sgshapiro					break;
414490792Sgshapiro				}
414590792Sgshapiro			}
414638032Speter			break;
414738032Speter
414890792Sgshapiro#if _FFR_QUEUEDELAY
414990792Sgshapiro		  case 'G':		/* queue delay algorithm */
415090792Sgshapiro			e->e_queuealg = atoi(&buf[1]);
415138032Speter			break;
415290792Sgshapiro#endif /* _FFR_QUEUEDELAY */
415338032Speter
415490792Sgshapiro#if _FFR_QUARANTINE
415590792Sgshapiro		  case 'q':		/* quarantine reason */
415690792Sgshapiro			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
415790792Sgshapiro			macdefine(&e->e_macro, A_PERM,
415890792Sgshapiro				  macid("{quarantine}"), e->e_quarmsg);
415938032Speter			break;
416090792Sgshapiro#endif /* _FFR_QUARANTINE */
416138032Speter
416290792Sgshapiro		  case 'H':		/* header */
416394334Sgshapiro
416494334Sgshapiro			/*
416594334Sgshapiro			**  count size before chompheader() destroys the line.
416694334Sgshapiro			**  this isn't accurate due to macro expansion, but
416794334Sgshapiro			**  better than before. "+3" to skip H?? at least.
416894334Sgshapiro			*/
416994334Sgshapiro
417094334Sgshapiro			hdrsize += strlen(bp + 3);
417190792Sgshapiro			(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
417238032Speter			break;
417338032Speter
417438032Speter		  case 'I':		/* data file's inode number */
417538032Speter			/* regenerated below */
417638032Speter			break;
417738032Speter
417880785Sgshapiro		  case 'K':		/* time of last delivery attempt */
417938032Speter			e->e_dtime = atol(&buf[1]);
418038032Speter			break;
418138032Speter
418290792Sgshapiro		  case 'L':		/* Solaris Content-Length: */
418390792Sgshapiro		  case 'M':		/* message */
418490792Sgshapiro			/* ignore this; we want a new message next time */
418564562Sgshapiro			break;
418664562Sgshapiro
418738032Speter		  case 'N':		/* number of delivery attempts */
418838032Speter			e->e_ntries = atoi(&buf[1]);
418938032Speter
419038032Speter			/* if this has been tried recently, let it be */
419171345Sgshapiro			now = curtime();
419271345Sgshapiro			if (e->e_ntries > 0 && e->e_dtime <= now &&
419371345Sgshapiro			    now < e->e_dtime + queuedelay(e))
419438032Speter			{
419564562Sgshapiro				char *howlong;
419638032Speter
419790792Sgshapiro				howlong = pintvl(now - e->e_dtime, true);
419864562Sgshapiro				if (Verbose)
419990792Sgshapiro					(void) sm_io_fprintf(smioout,
420090792Sgshapiro							     SM_TIME_DEFAULT,
420190792Sgshapiro							     "%s: too young (%s)\n",
420290792Sgshapiro							     e->e_id, howlong);
420364562Sgshapiro				if (tTd(40, 8))
420490792Sgshapiro					sm_dprintf("%s: too young (%s)\n",
420538032Speter						e->e_id, howlong);
420638032Speter				if (LogLevel > 19)
420738032Speter					sm_syslog(LOG_DEBUG, e->e_id,
420864562Sgshapiro						  "too young (%s)",
420964562Sgshapiro						  howlong);
421038032Speter				e->e_id = NULL;
421138032Speter				unlockqueue(e);
421290792Sgshapiro				RELEASE_QUEUE;
421390792Sgshapiro				return false;
421438032Speter			}
421590792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
421690792Sgshapiro				macid("{ntries}"), &buf[1]);
421764562Sgshapiro
421890792Sgshapiro#if NAMED_BIND
421964562Sgshapiro			/* adjust BIND parameters immediately */
422064562Sgshapiro			if (e->e_ntries == 0)
422164562Sgshapiro			{
422264562Sgshapiro				_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
422364562Sgshapiro				_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
422464562Sgshapiro			}
422564562Sgshapiro			else
422664562Sgshapiro			{
422764562Sgshapiro				_res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
422864562Sgshapiro				_res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
422964562Sgshapiro			}
423090792Sgshapiro#endif /* NAMED_BIND */
423138032Speter			break;
423238032Speter
423338032Speter		  case 'P':		/* message priority */
423438032Speter			e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
423538032Speter			break;
423638032Speter
423790792Sgshapiro		  case 'Q':		/* original recipient */
423890792Sgshapiro			orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
423990792Sgshapiro			break;
424090792Sgshapiro
424198841Sgshapiro		  case 'r':		/* final recipient */
424290792Sgshapiro			frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
424390792Sgshapiro			break;
424490792Sgshapiro
424590792Sgshapiro		  case 'R':		/* specify recipient */
424690792Sgshapiro			p = bp;
424790792Sgshapiro			qflags = 0;
424890792Sgshapiro			if (qfver >= 1)
424938032Speter			{
425090792Sgshapiro				/* get flag bits */
425190792Sgshapiro				while (*++p != '\0' && *p != ':')
425238032Speter				{
425390792Sgshapiro					switch (*p)
425490792Sgshapiro					{
425590792Sgshapiro					  case 'N':
425690792Sgshapiro						qflags |= QHASNOTIFY;
425790792Sgshapiro						break;
425838032Speter
425990792Sgshapiro					  case 'S':
426090792Sgshapiro						qflags |= QPINGONSUCCESS;
426190792Sgshapiro						break;
426238032Speter
426390792Sgshapiro					  case 'F':
426490792Sgshapiro						qflags |= QPINGONFAILURE;
426590792Sgshapiro						break;
426638032Speter
426790792Sgshapiro					  case 'D':
426890792Sgshapiro						qflags |= QPINGONDELAY;
426990792Sgshapiro						break;
427038032Speter
427190792Sgshapiro					  case 'P':
427290792Sgshapiro						qflags |= QPRIMARY;
427390792Sgshapiro						break;
427438032Speter
427590792Sgshapiro					  case 'A':
427690792Sgshapiro						if (ctladdr != NULL)
427790792Sgshapiro							ctladdr->q_flags |= QALIAS;
427890792Sgshapiro						break;
427990792Sgshapiro
428090792Sgshapiro					  default: /* ignore or complain? */
428190792Sgshapiro						break;
428290792Sgshapiro					}
428338032Speter				}
428438032Speter			}
428590792Sgshapiro			else
428690792Sgshapiro				qflags |= QPRIMARY;
428790792Sgshapiro			q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0', NULL, e,
428890792Sgshapiro				      true);
428990792Sgshapiro			if (q != NULL)
429090792Sgshapiro			{
429194334Sgshapiro				/* make sure we keep the current qgrp */
429294334Sgshapiro				if (ISVALIDQGRP(e->e_qgrp))
429394334Sgshapiro					q->q_qgrp = e->e_qgrp;
429490792Sgshapiro				q->q_alias = ctladdr;
429590792Sgshapiro				if (qfver >= 1)
429690792Sgshapiro					q->q_flags &= ~Q_PINGFLAGS;
429790792Sgshapiro				q->q_flags |= qflags;
429890792Sgshapiro				q->q_finalrcpt = frcpt;
429990792Sgshapiro				q->q_orcpt = orcpt;
430090792Sgshapiro				(void) recipient(q, &e->e_sendqueue, 0, e);
430190792Sgshapiro			}
430290792Sgshapiro			frcpt = NULL;
430390792Sgshapiro			orcpt = NULL;
430438032Speter			break;
430538032Speter
430690792Sgshapiro		  case 'S':		/* sender */
430790792Sgshapiro			setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
430890792Sgshapiro				  e, NULL, '\0', true);
430938032Speter			break;
431038032Speter
431190792Sgshapiro		  case 'T':		/* init time */
431290792Sgshapiro			e->e_ctime = atol(&bp[1]);
431364562Sgshapiro			break;
431464562Sgshapiro
431590792Sgshapiro		  case 'V':		/* queue file version number */
431690792Sgshapiro			qfver = atoi(&bp[1]);
431790792Sgshapiro			if (queuedelay_qfver_unsupported(qfver))
431890792Sgshapiro				syserr("queue file version %d not supported: %s",
431990792Sgshapiro				       qfver,
432090792Sgshapiro				       "sendmail not compiled with _FFR_QUEUEDELAY");
432190792Sgshapiro			if (qfver <= QF_VERSION)
432290792Sgshapiro				break;
432390792Sgshapiro			syserr("Version number in queue file (%d) greater than max (%d)",
432490792Sgshapiro				qfver, QF_VERSION);
432594334Sgshapiro			err = "unsupported queue file version";
432694334Sgshapiro			goto fail;
432790792Sgshapiro			/* NOTREACHED */
432890792Sgshapiro			break;
432990792Sgshapiro
433090792Sgshapiro#if _FFR_QUEUEDELAY
433190792Sgshapiro		  case 'Y':		/* current delay */
433290792Sgshapiro			e->e_queuedelay = (time_t) atol(&buf[1]);
433390792Sgshapiro			break;
433490792Sgshapiro#endif /* _FFR_QUEUEDELAY */
433590792Sgshapiro
433690792Sgshapiro		  case 'Z':		/* original envelope id from ESMTP */
433790792Sgshapiro			e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
433890792Sgshapiro			macdefine(&e->e_macro, A_PERM,
433990792Sgshapiro				macid("{dsn_envid}"), e->e_envid);
434090792Sgshapiro			break;
434190792Sgshapiro
434290792Sgshapiro		  case '!':		/* deliver by */
434390792Sgshapiro
434490792Sgshapiro			/* format: flag (1 char) space long-integer */
434590792Sgshapiro			e->e_dlvr_flag = buf[1];
434690792Sgshapiro			e->e_deliver_by = strtol(&buf[3], NULL, 10);
434790792Sgshapiro
434838032Speter		  case '$':		/* define macro */
434964562Sgshapiro			{
435064562Sgshapiro				char *p;
435164562Sgshapiro
435290792Sgshapiro				/* XXX elimate p? */
435390792Sgshapiro				r = macid_parse(&bp[1], &ep);
435490792Sgshapiro				if (r == 0)
435571345Sgshapiro					break;
435690792Sgshapiro				p = sm_rpool_strdup_x(e->e_rpool, ep);
435790792Sgshapiro				macdefine(&e->e_macro, A_PERM, r, p);
435864562Sgshapiro			}
435938032Speter			break;
436038032Speter
436138032Speter		  case '.':		/* terminate file */
436290792Sgshapiro			nomore = true;
436338032Speter			break;
436438032Speter
436538032Speter		  default:
436638032Speter			syserr("readqf: %s: line %d: bad line \"%s\"",
436738032Speter				qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
436894334Sgshapiro			err = "unrecognized line";
436994334Sgshapiro			goto fail;
437038032Speter		}
437138032Speter
437238032Speter		if (bp != buf)
437390792Sgshapiro			sm_free(bp); /* XXX */
437438032Speter	}
437538032Speter
437638032Speter	/*
437738032Speter	**  If we haven't read any lines, this queue file is empty.
437838032Speter	**  Arrange to remove it without referencing any null pointers.
437938032Speter	*/
438038032Speter
438138032Speter	if (LineNumber == 0)
438238032Speter	{
438338032Speter		errno = 0;
438490792Sgshapiro		e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
438590792Sgshapiro		RELEASE_QUEUE;
438690792Sgshapiro		return true;
438738032Speter	}
438838032Speter
438990792Sgshapiro	/* Check to make sure we have a complete queue file read */
439090792Sgshapiro	if (!nomore)
439190792Sgshapiro	{
439290792Sgshapiro		syserr("readqf: %s: incomplete queue file read", qf);
439390792Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
439490792Sgshapiro		RELEASE_QUEUE;
439590792Sgshapiro		return false;
439690792Sgshapiro	}
439790792Sgshapiro
439864562Sgshapiro	/* possibly set ${dsn_ret} macro */
439964562Sgshapiro	if (bitset(EF_RET_PARAM, e->e_flags))
440064562Sgshapiro	{
440164562Sgshapiro		if (bitset(EF_NO_BODY_RETN, e->e_flags))
440290792Sgshapiro			macdefine(&e->e_macro, A_PERM,
440390792Sgshapiro				macid("{dsn_ret}"), "hdrs");
440464562Sgshapiro		else
440590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
440690792Sgshapiro				macid("{dsn_ret}"), "full");
440764562Sgshapiro	}
440864562Sgshapiro
440938032Speter	/*
441038032Speter	**  Arrange to read the data file.
441138032Speter	*/
441238032Speter
441390792Sgshapiro	p = queuename(e, DATAFL_LETTER);
441490792Sgshapiro	e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY,
441590792Sgshapiro			      NULL);
441638032Speter	if (e->e_dfp == NULL)
441738032Speter	{
441838032Speter		syserr("readqf: cannot open %s", p);
441938032Speter	}
442038032Speter	else
442138032Speter	{
442238032Speter		e->e_flags |= EF_HAS_DF;
442390792Sgshapiro		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
442490792Sgshapiro		    >= 0)
442538032Speter		{
442638032Speter			e->e_msgsize = st.st_size + hdrsize;
442738032Speter			e->e_dfdev = st.st_dev;
442890792Sgshapiro			e->e_dfino = ST_INODE(st);
442998121Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%ld",
443098121Sgshapiro					   e->e_msgsize);
443198121Sgshapiro			macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"),
443298121Sgshapiro				  buf);
443338032Speter		}
443438032Speter	}
443538032Speter
443690792Sgshapiro	RELEASE_QUEUE;
443790792Sgshapiro	return true;
443894334Sgshapiro
443994334Sgshapiro  fail:
444094334Sgshapiro	/*
444194334Sgshapiro	**  There was some error reading the qf file (reason is in err var.)
444294334Sgshapiro	**  Cleanup:
444394334Sgshapiro	**	close file; clear e_lockfp since it is the same as qfp,
444494334Sgshapiro	**	hence it is invalid (as file) after qfp is closed;
444594334Sgshapiro	**	the qf file is on disk, so set the flag to avoid calling
444694334Sgshapiro	**	queueup() with bogus data.
444794334Sgshapiro	*/
444894334Sgshapiro
444994334Sgshapiro	if (qfp != NULL)
445094334Sgshapiro		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
445194334Sgshapiro	e->e_lockfp = NULL;
445294334Sgshapiro	e->e_flags |= EF_INQUEUE;
445394334Sgshapiro	loseqfile(e, err);
445494334Sgshapiro	RELEASE_QUEUE;
445594334Sgshapiro	return false;
445638032Speter}
445790792Sgshapiro/*
445864562Sgshapiro**  PRTSTR -- print a string, "unprintable" characters are shown as \oct
445964562Sgshapiro**
446064562Sgshapiro**	Parameters:
446164562Sgshapiro**		s -- string to print
446264562Sgshapiro**		ml -- maximum length of output
446364562Sgshapiro**
446464562Sgshapiro**	Returns:
446590792Sgshapiro**		number of entries
446664562Sgshapiro**
446764562Sgshapiro**	Side Effects:
446864562Sgshapiro**		Prints a string on stdout.
446964562Sgshapiro*/
447064562Sgshapiro
447164562Sgshapirostatic void
447264562Sgshapiroprtstr(s, ml)
447364562Sgshapiro	char *s;
447464562Sgshapiro	int ml;
447564562Sgshapiro{
447690792Sgshapiro	int c;
447764562Sgshapiro
447864562Sgshapiro	if (s == NULL)
447964562Sgshapiro		return;
448064562Sgshapiro	while (ml-- > 0 && ((c = *s++) != '\0'))
448164562Sgshapiro	{
448264562Sgshapiro		if (c == '\\')
448364562Sgshapiro		{
448464562Sgshapiro			if (ml-- > 0)
448564562Sgshapiro			{
448690792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
448790792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
448864562Sgshapiro			}
448964562Sgshapiro		}
449064562Sgshapiro		else if (isascii(c) && isprint(c))
449190792Sgshapiro			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
449264562Sgshapiro		else
449364562Sgshapiro		{
449464562Sgshapiro			if ((ml -= 3) > 0)
449590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
449690792Sgshapiro						     "\\%03o", c & 0xFF);
449764562Sgshapiro		}
449864562Sgshapiro	}
449964562Sgshapiro}
450090792Sgshapiro/*
450190792Sgshapiro**  PRINTNQE -- print out number of entries in the mail queue
450290792Sgshapiro**
450390792Sgshapiro**	Parameters:
450490792Sgshapiro**		out -- output file pointer.
450590792Sgshapiro**		prefix -- string to output in front of each line.
450690792Sgshapiro**
450790792Sgshapiro**	Returns:
450890792Sgshapiro**		none.
450990792Sgshapiro*/
451090792Sgshapiro
451190792Sgshapirovoid
451290792Sgshapiroprintnqe(out, prefix)
451390792Sgshapiro	SM_FILE_T *out;
451490792Sgshapiro	char *prefix;
451590792Sgshapiro{
451690792Sgshapiro#if SM_CONF_SHM
451790792Sgshapiro	int i, k = 0, nrequests = 0;
451890792Sgshapiro	bool unknown = false;
451990792Sgshapiro
452090792Sgshapiro	if (ShmId == SM_SHM_NO_ID)
452190792Sgshapiro	{
452290792Sgshapiro		if (prefix == NULL)
452390792Sgshapiro			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
452490792Sgshapiro					"Data unavailable: shared memory not updated\n");
452590792Sgshapiro		else
452690792Sgshapiro			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
452790792Sgshapiro					"%sNOTCONFIGURED:-1\r\n", prefix);
452890792Sgshapiro		return;
452990792Sgshapiro	}
453090792Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
453190792Sgshapiro	{
453290792Sgshapiro		int j;
453390792Sgshapiro
453490792Sgshapiro		k++;
453590792Sgshapiro		for (j = 0; j < Queue[i]->qg_numqueues; j++)
453690792Sgshapiro		{
453790792Sgshapiro			int n;
453890792Sgshapiro
453990792Sgshapiro			if (StopRequest)
454090792Sgshapiro				stop_sendmail();
454190792Sgshapiro
454290792Sgshapiro			n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
454390792Sgshapiro			if (prefix != NULL)
454490792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
454590792Sgshapiro					"%s%s:%d\r\n",
454690792Sgshapiro					prefix, qid_printqueue(i, j), n);
454790792Sgshapiro			else if (n < 0)
454890792Sgshapiro			{
454990792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
455090792Sgshapiro					"%s: unknown number of entries\n",
455190792Sgshapiro					qid_printqueue(i, j));
455290792Sgshapiro				unknown = true;
455390792Sgshapiro			}
455490792Sgshapiro			else if (n == 0)
455590792Sgshapiro			{
455690792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
455790792Sgshapiro					"%s is empty\n",
455890792Sgshapiro					qid_printqueue(i, j));
455990792Sgshapiro			}
456090792Sgshapiro			else if (n > 0)
456190792Sgshapiro			{
456290792Sgshapiro				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
456390792Sgshapiro					"%s: entries=%d\n",
456490792Sgshapiro					qid_printqueue(i, j), n);
456590792Sgshapiro				nrequests += n;
456690792Sgshapiro				k++;
456790792Sgshapiro			}
456890792Sgshapiro		}
456990792Sgshapiro	}
457090792Sgshapiro	if (prefix == NULL && k > 1)
457190792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
457290792Sgshapiro				     "\t\tTotal requests: %d%s\n",
457390792Sgshapiro				     nrequests, unknown ? " (about)" : "");
457490792Sgshapiro#else /* SM_CONF_SHM */
457590792Sgshapiro	if (prefix == NULL)
457690792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
457790792Sgshapiro			     "Data unavailable without shared memory support\n");
457890792Sgshapiro	else
457990792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
458090792Sgshapiro			     "%sNOTAVAILABLE:-1\r\n", prefix);
458190792Sgshapiro#endif /* SM_CONF_SHM */
458290792Sgshapiro}
458390792Sgshapiro/*
458438032Speter**  PRINTQUEUE -- print out a representation of the mail queue
458538032Speter**
458638032Speter**	Parameters:
458738032Speter**		none.
458838032Speter**
458938032Speter**	Returns:
459038032Speter**		none.
459138032Speter**
459238032Speter**	Side Effects:
459338032Speter**		Prints a listing of the mail queue on the standard output.
459438032Speter*/
459538032Speter
459638032Spetervoid
459738032Speterprintqueue()
459838032Speter{
459990792Sgshapiro	int i, k = 0, nrequests = 0;
460064562Sgshapiro
460190792Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
460277349Sgshapiro	{
460390792Sgshapiro		int j;
460490792Sgshapiro
460590792Sgshapiro		k++;
460690792Sgshapiro		for (j = 0; j < Queue[i]->qg_numqueues; j++)
460790792Sgshapiro		{
460890792Sgshapiro			if (StopRequest)
460990792Sgshapiro				stop_sendmail();
461090792Sgshapiro			nrequests += print_single_queue(i, j);
461190792Sgshapiro			k++;
461290792Sgshapiro		}
461377349Sgshapiro	}
461490792Sgshapiro	if (k > 1)
461590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
461690792Sgshapiro				     "\t\tTotal requests: %d\n",
461790792Sgshapiro				     nrequests);
461864562Sgshapiro}
461990792Sgshapiro/*
462064562Sgshapiro**  PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
462164562Sgshapiro**
462264562Sgshapiro**	Parameters:
462390792Sgshapiro**		qgrp -- the index of the queue group.
462490792Sgshapiro**		qdir -- the queue directory.
462564562Sgshapiro**
462664562Sgshapiro**	Returns:
462790792Sgshapiro**		number of requests in mail queue.
462864562Sgshapiro**
462964562Sgshapiro**	Side Effects:
463064562Sgshapiro**		Prints a listing of the mail queue on the standard output.
463164562Sgshapiro*/
463264562Sgshapiro
463390792Sgshapiroint
463490792Sgshapiroprint_single_queue(qgrp, qdir)
463590792Sgshapiro	int qgrp;
463690792Sgshapiro	int qdir;
463764562Sgshapiro{
463838032Speter	register WORK *w;
463990792Sgshapiro	SM_FILE_T *f;
464038032Speter	int nrequests;
464164562Sgshapiro	char qd[MAXPATHLEN];
464264562Sgshapiro	char qddf[MAXPATHLEN];
464338032Speter	char buf[MAXLINE];
464438032Speter
464590792Sgshapiro	if (qdir == NOQDIR)
464664562Sgshapiro	{
464790792Sgshapiro		(void) sm_strlcpy(qd, ".", sizeof qd);
464890792Sgshapiro		(void) sm_strlcpy(qddf, ".", sizeof qddf);
464964562Sgshapiro	}
465064562Sgshapiro	else
465164562Sgshapiro	{
465290792Sgshapiro		(void) sm_strlcpyn(qd, sizeof qd, 2,
465390792Sgshapiro			Queue[qgrp]->qg_qpaths[qdir].qp_name,
465490792Sgshapiro			(bitset(QP_SUBQF,
465590792Sgshapiro				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
465690792Sgshapiro					? "/qf" : ""));
465790792Sgshapiro		(void) sm_strlcpyn(qddf, sizeof qddf, 2,
465890792Sgshapiro			Queue[qgrp]->qg_qpaths[qdir].qp_name,
465990792Sgshapiro			(bitset(QP_SUBDF,
466090792Sgshapiro				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
466190792Sgshapiro					? "/df" : ""));
466264562Sgshapiro	}
466364562Sgshapiro
466438032Speter	/*
466538032Speter	**  Check for permission to print the queue
466638032Speter	*/
466738032Speter
466838032Speter	if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
466938032Speter	{
467038032Speter		struct stat st;
467190792Sgshapiro#ifdef NGROUPS_MAX
467238032Speter		int n;
467338032Speter		extern GIDSET_T InitialGidSet[NGROUPS_MAX];
467490792Sgshapiro#endif /* NGROUPS_MAX */
467538032Speter
467664562Sgshapiro		if (stat(qd, &st) < 0)
467738032Speter		{
467890792Sgshapiro			syserr("Cannot stat %s",
467990792Sgshapiro				qid_printqueue(qgrp, qdir));
468064562Sgshapiro			return 0;
468138032Speter		}
468290792Sgshapiro#ifdef NGROUPS_MAX
468338032Speter		n = NGROUPS_MAX;
468438032Speter		while (--n >= 0)
468538032Speter		{
468638032Speter			if (InitialGidSet[n] == st.st_gid)
468738032Speter				break;
468838032Speter		}
468938032Speter		if (n < 0 && RealGid != st.st_gid)
469090792Sgshapiro#else /* NGROUPS_MAX */
469138032Speter		if (RealGid != st.st_gid)
469290792Sgshapiro#endif /* NGROUPS_MAX */
469338032Speter		{
469438032Speter			usrerr("510 You are not permitted to see the queue");
469538032Speter			setstat(EX_NOPERM);
469664562Sgshapiro			return 0;
469738032Speter		}
469838032Speter	}
469938032Speter
470038032Speter	/*
470138032Speter	**  Read and order the queue.
470238032Speter	*/
470338032Speter
470490792Sgshapiro	nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
470590792Sgshapiro	(void) sortq(Queue[qgrp]->qg_maxlist);
470638032Speter
470738032Speter	/*
470838032Speter	**  Print the work list that we have read.
470938032Speter	*/
471038032Speter
471138032Speter	/* first see if there is anything */
471238032Speter	if (nrequests <= 0)
471338032Speter	{
471490792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
471590792Sgshapiro				     qid_printqueue(qgrp, qdir));
471664562Sgshapiro		return 0;
471738032Speter	}
471838032Speter
471990792Sgshapiro	sm_getla();	/* get load average */
472038032Speter
472190792Sgshapiro	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
472290792Sgshapiro			     qid_printqueue(qgrp, qdir),
472390792Sgshapiro			     nrequests, nrequests == 1 ? "" : "s");
472438032Speter	if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
472590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
472690792Sgshapiro				     ", only %d printed", MaxQueueRun);
472738032Speter	if (Verbose)
472890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
472990792Sgshapiro			")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
473038032Speter	else
473190792Sgshapiro		(void) sm_io_fprintf(smioout,  SM_TIME_DEFAULT,
473290792Sgshapiro			")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
473338032Speter	for (w = WorkQ; w != NULL; w = w->w_next)
473438032Speter	{
473538032Speter		struct stat st;
473638032Speter		auto time_t submittime = 0;
473738032Speter		long dfsize;
473838032Speter		int flags = 0;
473938032Speter		int qfver;
474090792Sgshapiro#if _FFR_QUARANTINE
474190792Sgshapiro		char quarmsg[MAXLINE];
474290792Sgshapiro#endif /* _FFR_QUARANTINE */
474338032Speter		char statmsg[MAXLINE];
474438032Speter		char bodytype[MAXNAME + 1];
474564562Sgshapiro		char qf[MAXPATHLEN];
474638032Speter
474777349Sgshapiro		if (StopRequest)
474877349Sgshapiro			stop_sendmail();
474977349Sgshapiro
475090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
475190792Sgshapiro				     w->w_name + 2);
475290792Sgshapiro		(void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", w->w_name);
475390792Sgshapiro		f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY,
475490792Sgshapiro			       NULL);
475538032Speter		if (f == NULL)
475638032Speter		{
475790792Sgshapiro			if (errno == EPERM)
475890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
475990792Sgshapiro						     " (permission denied)\n");
476090792Sgshapiro			else if (errno == ENOENT)
476190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
476290792Sgshapiro						     " (job completed)\n");
476390792Sgshapiro			else
476490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
476590792Sgshapiro						     " (%s)\n",
476690792Sgshapiro						     sm_errstring(errno));
476738032Speter			errno = 0;
476838032Speter			continue;
476938032Speter		}
477090792Sgshapiro		w->w_name[0] = DATAFL_LETTER;
477190792Sgshapiro		(void) sm_strlcpyn(qf, sizeof qf, 3, qddf, "/", w->w_name);
477264562Sgshapiro		if (stat(qf, &st) >= 0)
477338032Speter			dfsize = st.st_size;
477438032Speter		else
477590792Sgshapiro		{
477690792Sgshapiro			ENVELOPE e;
477790792Sgshapiro
477890792Sgshapiro			/*
477990792Sgshapiro			**  Maybe the df file can't be statted because
478090792Sgshapiro			**  it is in a different directory than the qf file.
478190792Sgshapiro			**  In order to find out, we must read the qf file.
478290792Sgshapiro			*/
478390792Sgshapiro
478490792Sgshapiro			newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
478590792Sgshapiro			e.e_id = w->w_name + 2;
478690792Sgshapiro			e.e_qgrp = qgrp;
478790792Sgshapiro			e.e_qdir = qdir;
478838032Speter			dfsize = -1;
478990792Sgshapiro			if (readqf(&e, false))
479090792Sgshapiro			{
479190792Sgshapiro				char *df = queuename(&e, DATAFL_LETTER);
479290792Sgshapiro				if (stat(df, &st) >= 0)
479390792Sgshapiro					dfsize = st.st_size;
479490792Sgshapiro			}
479590792Sgshapiro			if (e.e_lockfp != NULL)
479690792Sgshapiro			{
479790792Sgshapiro				(void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
479890792Sgshapiro				e.e_lockfp = NULL;
479990792Sgshapiro			}
480090792Sgshapiro			clearenvelope(&e, false, e.e_rpool);
480190792Sgshapiro			sm_rpool_free(e.e_rpool);
480290792Sgshapiro		}
480338032Speter		if (w->w_lock)
480490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
480590792Sgshapiro#if _FFR_QUARANTINE
480690792Sgshapiro		else if (QueueMode == QM_LOST)
480790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
480890792Sgshapiro#endif /* _FFR_QUARANTINE */
480938032Speter		else if (w->w_tooyoung)
481090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
481138032Speter		else if (shouldqueue(w->w_pri, w->w_ctime))
481290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
481338032Speter		else
481490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
481590792Sgshapiro
481638032Speter		errno = 0;
481738032Speter
481890792Sgshapiro#if _FFR_QUARANTINE
481990792Sgshapiro		quarmsg[0] = '\0';
482090792Sgshapiro#endif /* _FFR_QUARANTINE */
482138032Speter		statmsg[0] = bodytype[0] = '\0';
482238032Speter		qfver = 0;
482390792Sgshapiro		while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
482438032Speter		{
482538032Speter			register int i;
482638032Speter			register char *p;
482738032Speter
482877349Sgshapiro			if (StopRequest)
482977349Sgshapiro				stop_sendmail();
483077349Sgshapiro
483190792Sgshapiro			fixcrlf(buf, true);
483238032Speter			switch (buf[0])
483338032Speter			{
483438032Speter			  case 'V':	/* queue file version */
483538032Speter				qfver = atoi(&buf[1]);
483638032Speter				break;
483738032Speter
483838032Speter			  case 'M':	/* error message */
483938032Speter				if ((i = strlen(&buf[1])) >= sizeof statmsg)
484038032Speter					i = sizeof statmsg - 1;
484164562Sgshapiro				memmove(statmsg, &buf[1], i);
484238032Speter				statmsg[i] = '\0';
484338032Speter				break;
484438032Speter
484590792Sgshapiro#if _FFR_QUARANTINE
484690792Sgshapiro			  case 'q':	/* quarantine reason */
484790792Sgshapiro				if ((i = strlen(&buf[1])) >= sizeof quarmsg)
484890792Sgshapiro					i = sizeof quarmsg - 1;
484990792Sgshapiro				memmove(quarmsg, &buf[1], i);
485090792Sgshapiro				quarmsg[i] = '\0';
485190792Sgshapiro				break;
485290792Sgshapiro#endif /* _FFR_QUARANTINE */
485390792Sgshapiro
485438032Speter			  case 'B':	/* body type */
485538032Speter				if ((i = strlen(&buf[1])) >= sizeof bodytype)
485638032Speter					i = sizeof bodytype - 1;
485764562Sgshapiro				memmove(bodytype, &buf[1], i);
485838032Speter				bodytype[i] = '\0';
485938032Speter				break;
486038032Speter
486138032Speter			  case 'S':	/* sender name */
486238032Speter				if (Verbose)
486364562Sgshapiro				{
486490792Sgshapiro					(void) sm_io_fprintf(smioout,
486590792Sgshapiro						SM_TIME_DEFAULT,
486690792Sgshapiro						"%8ld %10ld%c%.12s ",
486790792Sgshapiro						dfsize,
486890792Sgshapiro						w->w_pri,
486990792Sgshapiro						bitset(EF_WARNING, flags)
487090792Sgshapiro							? '+' : ' ',
487190792Sgshapiro						ctime(&submittime) + 4);
487264562Sgshapiro					prtstr(&buf[1], 78);
487364562Sgshapiro				}
487438032Speter				else
487564562Sgshapiro				{
487690792Sgshapiro					(void) sm_io_fprintf(smioout,
487790792Sgshapiro						SM_TIME_DEFAULT,
487890792Sgshapiro						"%8ld %.16s ",
487990792Sgshapiro						dfsize,
488090792Sgshapiro						ctime(&submittime));
488190792Sgshapiro					prtstr(&buf[1], 39);
488264562Sgshapiro				}
488390792Sgshapiro#if _FFR_QUARANTINE
488490792Sgshapiro				if (quarmsg[0] != '\0')
488590792Sgshapiro				{
488690792Sgshapiro					(void) sm_io_fprintf(smioout,
488790792Sgshapiro							     SM_TIME_DEFAULT,
488890792Sgshapiro							     "\n     QUARANTINE: %.*s",
488990792Sgshapiro							     Verbose ? 100 : 60,
489090792Sgshapiro							     quarmsg);
489190792Sgshapiro					quarmsg[0] = '\0';
489290792Sgshapiro				}
489390792Sgshapiro#endif /* _FFR_QUARANTINE */
489438032Speter				if (statmsg[0] != '\0' || bodytype[0] != '\0')
489538032Speter				{
489690792Sgshapiro					(void) sm_io_fprintf(smioout,
489790792Sgshapiro						SM_TIME_DEFAULT,
489890792Sgshapiro						"\n    %10.10s",
489990792Sgshapiro						bodytype);
490038032Speter					if (statmsg[0] != '\0')
490190792Sgshapiro						(void) sm_io_fprintf(smioout,
490290792Sgshapiro							SM_TIME_DEFAULT,
490390792Sgshapiro							"   (%.*s)",
490490792Sgshapiro							Verbose ? 100 : 60,
490590792Sgshapiro							statmsg);
490690792Sgshapiro					statmsg[0] = '\0';
490738032Speter				}
490838032Speter				break;
490938032Speter
491038032Speter			  case 'C':	/* controlling user */
491138032Speter				if (Verbose)
491290792Sgshapiro					(void) sm_io_fprintf(smioout,
491390792Sgshapiro						SM_TIME_DEFAULT,
491490792Sgshapiro						"\n\t\t\t\t\t\t(---%.64s---)",
491590792Sgshapiro						&buf[1]);
491638032Speter				break;
491738032Speter
491838032Speter			  case 'R':	/* recipient name */
491938032Speter				p = &buf[1];
492038032Speter				if (qfver >= 1)
492138032Speter				{
492238032Speter					p = strchr(p, ':');
492338032Speter					if (p == NULL)
492438032Speter						break;
492538032Speter					p++;
492638032Speter				}
492738032Speter				if (Verbose)
492864562Sgshapiro				{
492990792Sgshapiro					(void) sm_io_fprintf(smioout,
493090792Sgshapiro							SM_TIME_DEFAULT,
493190792Sgshapiro							"\n\t\t\t\t\t\t");
493290792Sgshapiro					prtstr(p, 71);
493364562Sgshapiro				}
493438032Speter				else
493564562Sgshapiro				{
493690792Sgshapiro					(void) sm_io_fprintf(smioout,
493790792Sgshapiro							SM_TIME_DEFAULT,
493890792Sgshapiro							"\n\t\t\t\t\t ");
493990792Sgshapiro					prtstr(p, 38);
494064562Sgshapiro				}
494190792Sgshapiro				if (Verbose && statmsg[0] != '\0')
494290792Sgshapiro				{
494390792Sgshapiro					(void) sm_io_fprintf(smioout,
494490792Sgshapiro							SM_TIME_DEFAULT,
494590792Sgshapiro							"\n\t\t (%.100s)",
494690792Sgshapiro							statmsg);
494790792Sgshapiro					statmsg[0] = '\0';
494890792Sgshapiro				}
494938032Speter				break;
495038032Speter
495138032Speter			  case 'T':	/* creation time */
495238032Speter				submittime = atol(&buf[1]);
495338032Speter				break;
495438032Speter
495538032Speter			  case 'F':	/* flag bits */
495638032Speter				for (p = &buf[1]; *p != '\0'; p++)
495738032Speter				{
495838032Speter					switch (*p)
495938032Speter					{
496038032Speter					  case 'w':
496138032Speter						flags |= EF_WARNING;
496238032Speter						break;
496338032Speter					}
496438032Speter				}
496538032Speter			}
496638032Speter		}
496738032Speter		if (submittime == (time_t) 0)
496890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
496990792Sgshapiro					     " (no control file)");
497090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
497190792Sgshapiro		(void) sm_io_close(f, SM_TIME_DEFAULT);
497238032Speter	}
497364562Sgshapiro	return nrequests;
497438032Speter}
497590792Sgshapiro
497690792Sgshapiro#if _FFR_QUARANTINE
497790792Sgshapiro/*
497890792Sgshapiro**  QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
497990792Sgshapiro**
498090792Sgshapiro**	Parameters:
498190792Sgshapiro**		e -- envelope to build it in/from.
498290792Sgshapiro**		type -- the file type, used as the first character
498390792Sgshapiro**			of the file name.
498490792Sgshapiro**
498590792Sgshapiro**	Returns:
498690792Sgshapiro**		the letter to use
498790792Sgshapiro*/
498890792Sgshapiro
498990792Sgshapirostatic char
499090792Sgshapiroqueue_letter(e, type)
499190792Sgshapiro	ENVELOPE *e;
499290792Sgshapiro	int type;
499390792Sgshapiro{
499490792Sgshapiro	/* Change type according to QueueMode */
499590792Sgshapiro	if (type == ANYQFL_LETTER)
499690792Sgshapiro	{
499790792Sgshapiro		if (e->e_quarmsg != NULL)
499890792Sgshapiro			type = QUARQF_LETTER;
499990792Sgshapiro		else
500090792Sgshapiro		{
500190792Sgshapiro			switch (QueueMode)
500290792Sgshapiro			{
500390792Sgshapiro			  case QM_NORMAL:
500490792Sgshapiro				type = NORMQF_LETTER;
500590792Sgshapiro				break;
500690792Sgshapiro
500790792Sgshapiro			  case QM_QUARANTINE:
500890792Sgshapiro				type = QUARQF_LETTER;
500990792Sgshapiro				break;
501090792Sgshapiro
501190792Sgshapiro			  case QM_LOST:
501290792Sgshapiro				type = LOSEQF_LETTER;
501390792Sgshapiro				break;
501490792Sgshapiro
501590792Sgshapiro			  default:
501690792Sgshapiro				/* should never happen */
501790792Sgshapiro				abort();
501890792Sgshapiro				/* NOTREACHED */
501990792Sgshapiro			}
502090792Sgshapiro		}
502190792Sgshapiro	}
502290792Sgshapiro	return type;
502390792Sgshapiro}
502490792Sgshapiro#endif /* _FFR_QUARANTINE */
502590792Sgshapiro
502690792Sgshapiro/*
502738032Speter**  QUEUENAME -- build a file name in the queue directory for this envelope.
502838032Speter**
502938032Speter**	Parameters:
503038032Speter**		e -- envelope to build it in/from.
503138032Speter**		type -- the file type, used as the first character
503238032Speter**			of the file name.
503338032Speter**
503438032Speter**	Returns:
503564562Sgshapiro**		a pointer to the queue name (in a static buffer).
503638032Speter**
503738032Speter**	Side Effects:
503864562Sgshapiro**		If no id code is already assigned, queuename() will
503964562Sgshapiro**		assign an id code with assign_queueid().  If no queue
504064562Sgshapiro**		directory is assigned, one will be set with setnewqueue().
504138032Speter*/
504238032Speter
504338032Speterchar *
504438032Speterqueuename(e, type)
504538032Speter	register ENVELOPE *e;
504638032Speter	int type;
504738032Speter{
504890792Sgshapiro	int qd, qg;
504990792Sgshapiro	char *sub = "/";
505090792Sgshapiro	char pref[3];
505164562Sgshapiro	static char buf[MAXPATHLEN];
505238032Speter
505364562Sgshapiro	/* Assign an ID if needed */
505438032Speter	if (e->e_id == NULL)
505564562Sgshapiro		assign_queueid(e);
505664562Sgshapiro
505790792Sgshapiro#if _FFR_QUARANTINE
505890792Sgshapiro	type = queue_letter(e, type);
505990792Sgshapiro#endif /* _FFR_QUARANTINE */
506064562Sgshapiro
506190792Sgshapiro	/* begin of filename */
506290792Sgshapiro	pref[0] = (char) type;
506390792Sgshapiro	pref[1] = 'f';
506490792Sgshapiro	pref[2] = '\0';
506590792Sgshapiro
506690792Sgshapiro	/* Assign a queue group/directory if needed */
506790792Sgshapiro	if (type == XSCRPT_LETTER)
506890792Sgshapiro	{
506990792Sgshapiro		/*
507090792Sgshapiro		**  We don't want to call setnewqueue() if we are fetching
507190792Sgshapiro		**  the pathname of the transcript file, because setnewqueue
507290792Sgshapiro		**  chooses a queue, and sometimes we need to write to the
507390792Sgshapiro		**  transcript file before we have gathered enough information
507490792Sgshapiro		**  to choose a queue.
507590792Sgshapiro		*/
507690792Sgshapiro
507790792Sgshapiro		if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
507890792Sgshapiro		{
507990792Sgshapiro			if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
508090792Sgshapiro			{
508190792Sgshapiro				e->e_xfqgrp = e->e_qgrp;
508290792Sgshapiro				e->e_xfqdir = e->e_qdir;
508390792Sgshapiro			}
508490792Sgshapiro			else
508590792Sgshapiro			{
508690792Sgshapiro				e->e_xfqgrp = 0;
508790792Sgshapiro				if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
508890792Sgshapiro					e->e_xfqdir = 0;
508990792Sgshapiro				else
509090792Sgshapiro				{
509190792Sgshapiro					e->e_xfqdir = get_rand_mod(
509290792Sgshapiro					      Queue[e->e_xfqgrp]->qg_numqueues);
509390792Sgshapiro				}
509490792Sgshapiro			}
509590792Sgshapiro		}
509690792Sgshapiro		qd = e->e_xfqdir;
509790792Sgshapiro		qg = e->e_xfqgrp;
509890792Sgshapiro	}
509964562Sgshapiro	else
510038032Speter	{
510190792Sgshapiro		if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
510290792Sgshapiro			setnewqueue(e);
510390792Sgshapiro		if (type ==  DATAFL_LETTER)
510490792Sgshapiro		{
510590792Sgshapiro			qd = e->e_dfqdir;
510690792Sgshapiro			qg = e->e_dfqgrp;
510790792Sgshapiro		}
510890792Sgshapiro		else
510990792Sgshapiro		{
511090792Sgshapiro			qd = e->e_qdir;
511190792Sgshapiro			qg = e->e_qgrp;
511290792Sgshapiro		}
511390792Sgshapiro	}
511490792Sgshapiro
511594334Sgshapiro	/* xf files always have a valid qd and qg picked above */
511694334Sgshapiro	if (e->e_qdir == NOQDIR && type != XSCRPT_LETTER)
511790792Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id);
511890792Sgshapiro	else
511990792Sgshapiro	{
512064562Sgshapiro		switch (type)
512164562Sgshapiro		{
512290792Sgshapiro		  case DATAFL_LETTER:
512390792Sgshapiro			if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
512490792Sgshapiro				sub = "/df/";
512564562Sgshapiro			break;
512638032Speter
512790792Sgshapiro#if _FFR_QUARANTINE
512890792Sgshapiro		  case QUARQF_LETTER:
512990792Sgshapiro#endif /* _FFR_QUARANTINE */
513071345Sgshapiro		  case TEMPQF_LETTER:
513190792Sgshapiro		  case NEWQFL_LETTER:
513271345Sgshapiro		  case LOSEQF_LETTER:
513390792Sgshapiro		  case NORMQF_LETTER:
513490792Sgshapiro			if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
513590792Sgshapiro				sub = "/qf/";
513664562Sgshapiro			break;
513764562Sgshapiro
513890792Sgshapiro		  case XSCRPT_LETTER:
513990792Sgshapiro			if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
514090792Sgshapiro				sub = "/xf/";
514164562Sgshapiro			break;
514290792Sgshapiro
514390792Sgshapiro		  default:
514490792Sgshapiro			sm_abort("queuename: bad queue file type %d", type);
514538032Speter		}
514638032Speter
514790792Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 4,
514890792Sgshapiro				Queue[qg]->qg_qpaths[qd].qp_name,
514990792Sgshapiro				sub, pref, e->e_id);
515064562Sgshapiro	}
515138032Speter
515264562Sgshapiro	if (tTd(7, 2))
515390792Sgshapiro		sm_dprintf("queuename: %s\n", buf);
515464562Sgshapiro	return buf;
515564562Sgshapiro}
515690792Sgshapiro/*
515764562Sgshapiro**  ASSIGN_QUEUEID -- assign a queue ID for this envelope.
515864562Sgshapiro**
515964562Sgshapiro**	Assigns an id code if one does not already exist.
516064562Sgshapiro**	This code assumes that nothing will remain in the queue for
516164562Sgshapiro**	longer than 60 years.  It is critical that files with the given
516290792Sgshapiro**	name do not already exist in the queue.
516390792Sgshapiro**	[No longer initializes e_qdir to NOQDIR.]
516464562Sgshapiro**
516564562Sgshapiro**	Parameters:
516664562Sgshapiro**		e -- envelope to set it in.
516764562Sgshapiro**
516864562Sgshapiro**	Returns:
516964562Sgshapiro**		none.
517064562Sgshapiro*/
517138032Speter
517277349Sgshapirostatic const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx";
517377349Sgshapiro# define QIC_LEN	60
517490792Sgshapiro# define queuenextid() CurrentPid
517538032Speter
517690792Sgshapiro
517764562Sgshapirovoid
517864562Sgshapiroassign_queueid(e)
517964562Sgshapiro	register ENVELOPE *e;
518064562Sgshapiro{
518190792Sgshapiro	pid_t pid = queuenextid();
518290792Sgshapiro	static int cX = 0;
518364562Sgshapiro	static long random_offset;
518464562Sgshapiro	struct tm *tm;
518564562Sgshapiro	char idbuf[MAXQFNAME - 2];
518690792Sgshapiro	int seq;
518738032Speter
518864562Sgshapiro	if (e->e_id != NULL)
518964562Sgshapiro		return;
519038032Speter
519164562Sgshapiro	/* see if we need to get a new base time/pid */
519290792Sgshapiro	if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 ||
519390792Sgshapiro	    LastQueuePid != pid)
519464562Sgshapiro	{
519564562Sgshapiro		time_t then = LastQueueTime;
519664562Sgshapiro
519764562Sgshapiro		/* if the first time through, pick a random offset */
519864562Sgshapiro		if (LastQueueTime == 0)
519964562Sgshapiro			random_offset = get_random();
520064562Sgshapiro
520164562Sgshapiro		while ((LastQueueTime = curtime()) == then &&
520264562Sgshapiro		       LastQueuePid == pid)
520338032Speter		{
520464562Sgshapiro			(void) sleep(1);
520538032Speter		}
520690792Sgshapiro		LastQueuePid = queuenextid();
520764562Sgshapiro		cX = 0;
520838032Speter	}
520990792Sgshapiro
521090792Sgshapiro	/*
521190792Sgshapiro	**  Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1.
521290792Sgshapiro	**  This lets us generate up to QIC_LEN*QIC_LEN unique queue ids
521390792Sgshapiro	**  per second, per process.  With envelope splitting,
521490792Sgshapiro	**  a single message can consume many queue ids.
521590792Sgshapiro	*/
521690792Sgshapiro
521790792Sgshapiro	seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN));
521890792Sgshapiro	++cX;
521964562Sgshapiro	if (tTd(7, 50))
522090792Sgshapiro		sm_dprintf("assign_queueid: random_offset = %ld (%d)\n",
522190792Sgshapiro			random_offset, seq);
522238032Speter
522364562Sgshapiro	tm = gmtime(&LastQueueTime);
522477349Sgshapiro	idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
522577349Sgshapiro	idbuf[1] = QueueIdChars[tm->tm_mon];
522677349Sgshapiro	idbuf[2] = QueueIdChars[tm->tm_mday];
522777349Sgshapiro	idbuf[3] = QueueIdChars[tm->tm_hour];
522877349Sgshapiro	idbuf[4] = QueueIdChars[tm->tm_min];
522977349Sgshapiro	idbuf[5] = QueueIdChars[tm->tm_sec];
523090792Sgshapiro	idbuf[6] = QueueIdChars[seq / QIC_LEN];
523190792Sgshapiro	idbuf[7] = QueueIdChars[seq % QIC_LEN];
523290792Sgshapiro	(void) sm_snprintf(&idbuf[8], sizeof idbuf - 8, "%06d",
523390792Sgshapiro			   (int) LastQueuePid);
523490792Sgshapiro	e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
523590792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
523690792Sgshapiro#if 0
523790792Sgshapiro	/* XXX: inherited from MainEnvelope */
523890792Sgshapiro	e->e_qgrp = NOQGRP;  /* too early to do anything else */
523990792Sgshapiro	e->e_qdir = NOQDIR;
524090792Sgshapiro	e->e_xfqgrp = NOQGRP;
524190792Sgshapiro#endif /* 0 */
524290792Sgshapiro#if _FFR_QUARANTINE
524390792Sgshapiro	/* New ID means it's not on disk yet */
524490792Sgshapiro	e->e_qfletter = '\0';
524590792Sgshapiro#endif /* _FFR_QUARANTINE */
524664562Sgshapiro	if (tTd(7, 1))
524790792Sgshapiro		sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
524890792Sgshapiro			e->e_id, e);
524964562Sgshapiro	if (LogLevel > 93)
525064562Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
525138032Speter}
525290792Sgshapiro/*
525364562Sgshapiro**  SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
525464562Sgshapiro**
525564562Sgshapiro**	Make sure one PID can't be used by two processes in any one second.
525664562Sgshapiro**
525764562Sgshapiro**		If the system rotates PIDs fast enough, may get the
525864562Sgshapiro**		same pid in the same second for two distinct processes.
525964562Sgshapiro**		This will interfere with the queue file naming system.
526064562Sgshapiro**
526164562Sgshapiro**	Parameters:
526264562Sgshapiro**		none
526364562Sgshapiro**
526464562Sgshapiro**	Returns:
526564562Sgshapiro**		none
526664562Sgshapiro*/
526790792Sgshapiro
526864562Sgshapirovoid
526964562Sgshapirosync_queue_time()
527064562Sgshapiro{
527190792Sgshapiro#if FAST_PID_RECYCLE
527264562Sgshapiro	if (OpMode != MD_TEST &&
527364562Sgshapiro	    OpMode != MD_VERIFY &&
527464562Sgshapiro	    LastQueueTime > 0 &&
527590792Sgshapiro	    LastQueuePid == CurrentPid &&
527664562Sgshapiro	    curtime() == LastQueueTime)
527764562Sgshapiro		(void) sleep(1);
527890792Sgshapiro#endif /* FAST_PID_RECYCLE */
527964562Sgshapiro}
528090792Sgshapiro/*
528138032Speter**  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
528238032Speter**
528338032Speter**	Parameters:
528438032Speter**		e -- the envelope to unlock.
528538032Speter**
528638032Speter**	Returns:
528738032Speter**		none
528838032Speter**
528938032Speter**	Side Effects:
529038032Speter**		unlocks the queue for `e'.
529138032Speter*/
529238032Speter
529338032Spetervoid
529438032Speterunlockqueue(e)
529538032Speter	ENVELOPE *e;
529638032Speter{
529738032Speter	if (tTd(51, 4))
529890792Sgshapiro		sm_dprintf("unlockqueue(%s)\n",
529938032Speter			e->e_id == NULL ? "NOQUEUE" : e->e_id);
530038032Speter
530164562Sgshapiro
530238032Speter	/* if there is a lock file in the envelope, close it */
530338032Speter	if (e->e_lockfp != NULL)
530490792Sgshapiro		(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
530538032Speter	e->e_lockfp = NULL;
530638032Speter
530738032Speter	/* don't create a queue id if we don't already have one */
530838032Speter	if (e->e_id == NULL)
530938032Speter		return;
531038032Speter
531138032Speter	/* remove the transcript */
531238032Speter	if (LogLevel > 87)
531338032Speter		sm_syslog(LOG_DEBUG, e->e_id, "unlock");
531438032Speter	if (!tTd(51, 104))
531590792Sgshapiro		(void) xunlink(queuename(e, XSCRPT_LETTER));
531638032Speter}
531790792Sgshapiro/*
531838032Speter**  SETCTLUSER -- create a controlling address
531938032Speter**
532038032Speter**	Create a fake "address" given only a local login name; this is
532138032Speter**	used as a "controlling user" for future recipient addresses.
532238032Speter**
532338032Speter**	Parameters:
532438032Speter**		user -- the user name of the controlling user.
532590792Sgshapiro**		qfver -- the version stamp of this queue file.
532690792Sgshapiro**		e -- envelope
532738032Speter**
532838032Speter**	Returns:
532990792Sgshapiro**		An address descriptor for the controlling user,
533090792Sgshapiro**		using storage allocated from e->e_rpool.
533138032Speter**
533238032Speter*/
533338032Speter
533464562Sgshapirostatic ADDRESS *
533590792Sgshapirosetctluser(user, qfver, e)
533638032Speter	char *user;
533738032Speter	int qfver;
533890792Sgshapiro	ENVELOPE *e;
533938032Speter{
534038032Speter	register ADDRESS *a;
534138032Speter	struct passwd *pw;
534238032Speter	char *p;
534338032Speter
534438032Speter	/*
534538032Speter	**  See if this clears our concept of controlling user.
534638032Speter	*/
534738032Speter
534838032Speter	if (user == NULL || *user == '\0')
534938032Speter		return NULL;
535038032Speter
535138032Speter	/*
535238032Speter	**  Set up addr fields for controlling user.
535338032Speter	*/
535438032Speter
535590792Sgshapiro	a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);
535664562Sgshapiro	memset((char *) a, '\0', sizeof *a);
535738032Speter
535890792Sgshapiro	if (*user == ':')
535938032Speter	{
536038032Speter		p = &user[1];
536190792Sgshapiro		a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
536238032Speter	}
536338032Speter	else
536438032Speter	{
536538032Speter		p = strtok(user, ":");
536690792Sgshapiro		a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
536738032Speter		if (qfver >= 2)
536838032Speter		{
536938032Speter			if ((p = strtok(NULL, ":")) != NULL)
537038032Speter				a->q_uid = atoi(p);
537138032Speter			if ((p = strtok(NULL, ":")) != NULL)
537238032Speter				a->q_gid = atoi(p);
537338032Speter			if ((p = strtok(NULL, ":")) != NULL)
537480785Sgshapiro			{
537580785Sgshapiro				char *o;
537680785Sgshapiro
537738032Speter				a->q_flags |= QGOODUID;
537880785Sgshapiro
537980785Sgshapiro				/* if there is another ':': restore it */
538080785Sgshapiro				if ((o = strtok(NULL, ":")) != NULL && o > p)
538180785Sgshapiro					o[-1] = ':';
538280785Sgshapiro			}
538338032Speter		}
538438032Speter		else if ((pw = sm_getpwnam(user)) != NULL)
538538032Speter		{
538666494Sgshapiro			if (*pw->pw_dir == '\0')
538766494Sgshapiro				a->q_home = NULL;
538866494Sgshapiro			else if (strcmp(pw->pw_dir, "/") == 0)
538938032Speter				a->q_home = "";
539038032Speter			else
539190792Sgshapiro				a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
539238032Speter			a->q_uid = pw->pw_uid;
539338032Speter			a->q_gid = pw->pw_gid;
539438032Speter			a->q_flags |= QGOODUID;
539538032Speter		}
539638032Speter	}
539738032Speter
539864562Sgshapiro	a->q_flags |= QPRIMARY;		/* flag as a "ctladdr" */
539938032Speter	a->q_mailer = LocalMailer;
540038032Speter	if (p == NULL)
540190792Sgshapiro		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
540238032Speter	else
540390792Sgshapiro		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
540438032Speter	return a;
540538032Speter}
540690792Sgshapiro/*
540790792Sgshapiro**  LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
540838032Speter**
540938032Speter**	Parameters:
541038032Speter**		e -- the envelope (e->e_id will be used).
541138032Speter**		why -- reported to whomever can hear.
541238032Speter**
541338032Speter**	Returns:
541438032Speter**		none.
541538032Speter*/
541638032Speter
541738032Spetervoid
541838032Speterloseqfile(e, why)
541938032Speter	register ENVELOPE *e;
542038032Speter	char *why;
542138032Speter{
542290792Sgshapiro	bool loseit = true;
542338032Speter	char *p;
542464562Sgshapiro	char buf[MAXPATHLEN];
542538032Speter
542638032Speter	if (e == NULL || e->e_id == NULL)
542738032Speter		return;
542890792Sgshapiro	p = queuename(e, ANYQFL_LETTER);
542990792Sgshapiro	if (sm_strlcpy(buf, p, sizeof buf) >= sizeof buf)
543038032Speter		return;
543190792Sgshapiro	if (!bitset(EF_INQUEUE, e->e_flags))
543290792Sgshapiro		queueup(e, false, true);
543390792Sgshapiro#if _FFR_QUARANTINE
543490792Sgshapiro	else if (QueueMode == QM_LOST)
543590792Sgshapiro		loseit = false;
543690792Sgshapiro#endif /* _FFR_QUARANTINE */
543790792Sgshapiro
543890792Sgshapiro	/* if already lost, no need to re-lose */
543990792Sgshapiro	if (loseit)
544090792Sgshapiro	{
544190792Sgshapiro		p = queuename(e, LOSEQF_LETTER);
544290792Sgshapiro		if (rename(buf, p) < 0)
544390792Sgshapiro			syserr("cannot rename(%s, %s), uid=%d",
544498121Sgshapiro			       buf, p, (int) geteuid());
544590792Sgshapiro		else if (LogLevel > 0)
544690792Sgshapiro			sm_syslog(LOG_ALERT, e->e_id,
544790792Sgshapiro				  "Losing %s: %s", buf, why);
544890792Sgshapiro	}
544990792Sgshapiro	if (e->e_dfp != NULL)
545090792Sgshapiro	{
545190792Sgshapiro		(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
545290792Sgshapiro		e->e_dfp = NULL;
545390792Sgshapiro	}
545490792Sgshapiro	e->e_flags &= ~EF_HAS_DF;
545538032Speter}
545690792Sgshapiro/*
545790792Sgshapiro**  NAME2QID -- translate a queue group name to a queue group id
545890792Sgshapiro**
545990792Sgshapiro**	Parameters:
546090792Sgshapiro**		queuename -- name of queue group.
546190792Sgshapiro**
546290792Sgshapiro**	Returns:
546390792Sgshapiro**		queue group id if found.
546490792Sgshapiro**		NOQGRP otherwise.
546590792Sgshapiro*/
546690792Sgshapiro
546790792Sgshapiroint
546890792Sgshapironame2qid(queuename)
546990792Sgshapiro	char *queuename;
547090792Sgshapiro{
547190792Sgshapiro	register STAB *s;
547290792Sgshapiro
547390792Sgshapiro	s = stab(queuename, ST_QUEUE, ST_FIND);
547490792Sgshapiro	if (s == NULL)
547590792Sgshapiro		return NOQGRP;
547690792Sgshapiro	return s->s_quegrp->qg_index;
547790792Sgshapiro}
547890792Sgshapiro/*
547964562Sgshapiro**  QID_PRINTNAME -- create externally printable version of queue id
548064562Sgshapiro**
548164562Sgshapiro**	Parameters:
548264562Sgshapiro**		e -- the envelope.
548364562Sgshapiro**
548464562Sgshapiro**	Returns:
548564562Sgshapiro**		a printable version
548664562Sgshapiro*/
548764562Sgshapiro
548864562Sgshapirochar *
548964562Sgshapiroqid_printname(e)
549064562Sgshapiro	ENVELOPE *e;
549164562Sgshapiro{
549264562Sgshapiro	char *id;
549364562Sgshapiro	static char idbuf[MAXQFNAME + 34];
549464562Sgshapiro
549564562Sgshapiro	if (e == NULL)
549664562Sgshapiro		return "";
549764562Sgshapiro
549864562Sgshapiro	if (e->e_id == NULL)
549964562Sgshapiro		id = "";
550064562Sgshapiro	else
550164562Sgshapiro		id = e->e_id;
550264562Sgshapiro
550390792Sgshapiro	if (e->e_qdir == NOQDIR)
550464562Sgshapiro		return id;
550564562Sgshapiro
550690792Sgshapiro	(void) sm_snprintf(idbuf, sizeof idbuf, "%.32s/%s",
550790792Sgshapiro			   Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
550890792Sgshapiro			   id);
550964562Sgshapiro	return idbuf;
551064562Sgshapiro}
551190792Sgshapiro/*
551290792Sgshapiro**  QID_PRINTQUEUE -- create full version of queue directory for data files
551364562Sgshapiro**
551464562Sgshapiro**	Parameters:
551590792Sgshapiro**		qgrp -- index in queue group.
551690792Sgshapiro**		qdir -- the short version of the queue directory
551764562Sgshapiro**
551864562Sgshapiro**	Returns:
551990792Sgshapiro**		the full pathname to the queue (might point to a static var)
552064562Sgshapiro*/
552164562Sgshapiro
552264562Sgshapirochar *
552390792Sgshapiroqid_printqueue(qgrp, qdir)
552490792Sgshapiro	int qgrp;
552590792Sgshapiro	int qdir;
552664562Sgshapiro{
552764562Sgshapiro	char *subdir;
552864562Sgshapiro	static char dir[MAXPATHLEN];
552964562Sgshapiro
553090792Sgshapiro	if (qdir == NOQDIR)
553190792Sgshapiro		return Queue[qgrp]->qg_qdir;
553264562Sgshapiro
553390792Sgshapiro	if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
553464562Sgshapiro		subdir = NULL;
553564562Sgshapiro	else
553690792Sgshapiro		subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
553764562Sgshapiro
553890792Sgshapiro	(void) sm_strlcpyn(dir, sizeof dir, 4,
553990792Sgshapiro			Queue[qgrp]->qg_qdir,
554064562Sgshapiro			subdir == NULL ? "" : "/",
554164562Sgshapiro			subdir == NULL ? "" : subdir,
554290792Sgshapiro			(bitset(QP_SUBDF,
554390792Sgshapiro				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
554490792Sgshapiro					? "/df" : ""));
554564562Sgshapiro	return dir;
554664562Sgshapiro}
554790792Sgshapiro
554890792Sgshapiro/*
554990792Sgshapiro**  PICKQDIR -- Pick a queue directory from a queue group
555064562Sgshapiro**
555190792Sgshapiro**	Parameters:
555290792Sgshapiro**		qg -- queue group
555390792Sgshapiro**		fsize -- file size in bytes
555490792Sgshapiro**		e -- envelope, or NULL
555564562Sgshapiro**
555690792Sgshapiro**	Result:
555790792Sgshapiro**		NOQDIR if no queue directory in qg has enough free space to
555890792Sgshapiro**		hold a file of size 'fsize', otherwise the index of
555990792Sgshapiro**		a randomly selected queue directory which resides on a
556090792Sgshapiro**		file system with enough disk space.
556190792Sgshapiro**		XXX This could be extended to select a queuedir with
556290792Sgshapiro**			a few (the fewest?) number of entries. That data
556390792Sgshapiro**			is available if shared memory is used.
556464562Sgshapiro**
556590792Sgshapiro**	Side Effects:
556690792Sgshapiro**		If the request fails and e != NULL then sm_syslog is called.
556790792Sgshapiro*/
556890792Sgshapiro
556990792Sgshapiroint
557090792Sgshapiropickqdir(qg, fsize, e)
557190792Sgshapiro	QUEUEGRP *qg;
557290792Sgshapiro	long fsize;
557390792Sgshapiro	ENVELOPE *e;
557490792Sgshapiro{
557590792Sgshapiro	int qdir;
557690792Sgshapiro	int i;
557790792Sgshapiro	long avail = 0;
557890792Sgshapiro
557990792Sgshapiro	/* Pick a random directory, as a starting point. */
558090792Sgshapiro	if (qg->qg_numqueues <= 1)
558190792Sgshapiro		qdir = 0;
558290792Sgshapiro	else
558390792Sgshapiro		qdir = get_rand_mod(qg->qg_numqueues);
558490792Sgshapiro
558590792Sgshapiro	if (MinBlocksFree <= 0 && fsize <= 0)
558690792Sgshapiro		return qdir;
558790792Sgshapiro
558890792Sgshapiro	/*
558990792Sgshapiro	**  Now iterate over the queue directories,
559090792Sgshapiro	**  looking for a directory with enough space for this message.
559190792Sgshapiro	*/
559290792Sgshapiro
559390792Sgshapiro	i = qdir;
559490792Sgshapiro	do
559590792Sgshapiro	{
559690792Sgshapiro		QPATHS *qp = &qg->qg_qpaths[i];
559790792Sgshapiro		long needed = 0;
559890792Sgshapiro		long fsavail = 0;
559990792Sgshapiro
560090792Sgshapiro		if (fsize > 0)
560190792Sgshapiro			needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
560290792Sgshapiro				  + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
560390792Sgshapiro				      > 0) ? 1 : 0);
560490792Sgshapiro		if (MinBlocksFree > 0)
560590792Sgshapiro			needed += MinBlocksFree;
560690792Sgshapiro		fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
560790792Sgshapiro#if SM_CONF_SHM
560890792Sgshapiro		if (fsavail <= 0)
560990792Sgshapiro		{
561090792Sgshapiro			long blksize;
561190792Sgshapiro
561290792Sgshapiro			/*
561390792Sgshapiro			**  might be not correctly updated,
561490792Sgshapiro			**  let's try to get the info directly.
561590792Sgshapiro			*/
561690792Sgshapiro
561790792Sgshapiro			fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
561890792Sgshapiro						&blksize);
561990792Sgshapiro			if (fsavail < 0)
562090792Sgshapiro				fsavail = 0;
562190792Sgshapiro		}
562290792Sgshapiro#endif /* SM_CONF_SHM */
562390792Sgshapiro		if (needed <= fsavail)
562490792Sgshapiro			return i;
562590792Sgshapiro		if (avail < fsavail)
562690792Sgshapiro			avail = fsavail;
562790792Sgshapiro
562890792Sgshapiro		if (qg->qg_numqueues > 0)
562990792Sgshapiro			i = (i + 1) % qg->qg_numqueues;
563090792Sgshapiro	} while (i != qdir);
563190792Sgshapiro
563290792Sgshapiro	if (e != NULL && LogLevel > 0)
563390792Sgshapiro		sm_syslog(LOG_ALERT, e->e_id,
563490792Sgshapiro			"low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
563590792Sgshapiro			CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
563690792Sgshapiro			fsize, MinBlocksFree,
563790792Sgshapiro			qg->qg_qdir, avail);
563890792Sgshapiro	return NOQDIR;
563990792Sgshapiro}
564090792Sgshapiro/*
564190792Sgshapiro**  SETNEWQUEUE -- Sets a new queue group and directory
564290792Sgshapiro**
564390792Sgshapiro**	Assign a queue group and directory to an envelope and store the
564490792Sgshapiro**	directory in e->e_qdir.
564590792Sgshapiro**
564664562Sgshapiro**	Parameters:
564764562Sgshapiro**		e -- envelope to assign a queue for.
564864562Sgshapiro**
564964562Sgshapiro**	Returns:
565090792Sgshapiro**		true if successful
565190792Sgshapiro**		false otherwise
565290792Sgshapiro**
565390792Sgshapiro**	Side Effects:
565490792Sgshapiro**		On success, e->e_qgrp and e->e_qdir are non-negative.
565590792Sgshapiro**		On failure (not enough disk space),
565690792Sgshapiro**		e->qgrp = NOQGRP, e->e_qdir = NOQDIR
565790792Sgshapiro**		and usrerr() is invoked (which could raise an exception).
565864562Sgshapiro*/
565964562Sgshapiro
566090792Sgshapirobool
566164562Sgshapirosetnewqueue(e)
566264562Sgshapiro	ENVELOPE *e;
566364562Sgshapiro{
566464562Sgshapiro	if (tTd(41, 20))
566590792Sgshapiro		sm_dprintf("setnewqueue: called\n");
566664562Sgshapiro
566790792Sgshapiro	/* not set somewhere else */
566890792Sgshapiro	if (e->e_qgrp == NOQGRP)
566964562Sgshapiro	{
5670102528Sgshapiro		ADDRESS *q;
5671102528Sgshapiro
567290792Sgshapiro		/*
5673102528Sgshapiro		**  Use the queue group of the "first" recipient, as set by
567490792Sgshapiro		**  the "queuegroup" rule set.  If that is not defined, then
567590792Sgshapiro		**  use the queue group of the mailer of the first recipient.
567690792Sgshapiro		**  If that is not defined either, then use the default
567790792Sgshapiro		**  queue group.
5678102528Sgshapiro		**  Notice: "first" depends on the sorting of sendqueue
5679102528Sgshapiro		**  in recipient().
5680102528Sgshapiro		**  To avoid problems with "bad" recipients look
5681102528Sgshapiro		**  for a valid address first.
568290792Sgshapiro		*/
568390792Sgshapiro
5684102528Sgshapiro		q = e->e_sendqueue;
5685102528Sgshapiro		while (q != NULL &&
5686102528Sgshapiro		       (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state)))
5687102528Sgshapiro		{
5688102528Sgshapiro			q = q->q_next;
5689102528Sgshapiro		}
5690102528Sgshapiro		if (q == NULL)
569190792Sgshapiro			e->e_qgrp = 0;
5692102528Sgshapiro		else if (q->q_qgrp >= 0)
5693102528Sgshapiro			e->e_qgrp = q->q_qgrp;
5694102528Sgshapiro		else if (q->q_mailer != NULL &&
5695102528Sgshapiro			 ISVALIDQGRP(q->q_mailer->m_qgrp))
5696102528Sgshapiro			e->e_qgrp = q->q_mailer->m_qgrp;
569790792Sgshapiro		else
569890792Sgshapiro			e->e_qgrp = 0;
569990792Sgshapiro		e->e_dfqgrp = e->e_qgrp;
570090792Sgshapiro	}
570190792Sgshapiro
570290792Sgshapiro	if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
570390792Sgshapiro	{
570464562Sgshapiro		if (tTd(41, 20))
570590792Sgshapiro			sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
570690792Sgshapiro				qid_printqueue(e->e_qgrp, e->e_qdir));
570790792Sgshapiro		return true;
570864562Sgshapiro	}
570964562Sgshapiro
571090792Sgshapiro	filesys_update();
571190792Sgshapiro	e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
571290792Sgshapiro	if (e->e_qdir == NOQDIR)
571364562Sgshapiro	{
571490792Sgshapiro		e->e_qgrp = NOQGRP;
571590792Sgshapiro		if (!bitset(EF_FATALERRS, e->e_flags))
571690792Sgshapiro			usrerr("452 4.4.5 Insufficient disk space; try again later");
571790792Sgshapiro		e->e_flags |= EF_FATALERRS;
571890792Sgshapiro		return false;
571964562Sgshapiro	}
572064562Sgshapiro
572164562Sgshapiro	if (tTd(41, 3))
572290792Sgshapiro		sm_dprintf("setnewqueue: Assigned queue directory %s\n",
572390792Sgshapiro			qid_printqueue(e->e_qgrp, e->e_qdir));
572490792Sgshapiro
572590792Sgshapiro	if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
572690792Sgshapiro	{
572790792Sgshapiro		e->e_xfqgrp = e->e_qgrp;
572890792Sgshapiro		e->e_xfqdir = e->e_qdir;
572990792Sgshapiro	}
573090792Sgshapiro	e->e_dfqdir = e->e_qdir;
573190792Sgshapiro	return true;
573264562Sgshapiro}
573390792Sgshapiro/*
573464562Sgshapiro**  CHKQDIR -- check a queue directory
573564562Sgshapiro**
573664562Sgshapiro**	Parameters:
573764562Sgshapiro**		name -- name of queue directory
573864562Sgshapiro**		sff -- flags for safefile()
573964562Sgshapiro**
574064562Sgshapiro**	Returns:
574164562Sgshapiro**		is it a queue directory?
574264562Sgshapiro*/
574364562Sgshapiro
574464562Sgshapirostatic bool
574564562Sgshapirochkqdir(name, sff)
574664562Sgshapiro	char *name;
574764562Sgshapiro	long sff;
574864562Sgshapiro{
574964562Sgshapiro	struct stat statb;
575064562Sgshapiro	int i;
575164562Sgshapiro
575266494Sgshapiro	/* skip over . and .. directories */
575366494Sgshapiro	if (name[0] == '.' &&
575477349Sgshapiro	    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
575590792Sgshapiro		return false;
575690792Sgshapiro#if HASLSTAT
575764562Sgshapiro	if (lstat(name, &statb) < 0)
575890792Sgshapiro#else /* HASLSTAT */
575964562Sgshapiro	if (stat(name, &statb) < 0)
576090792Sgshapiro#endif /* HASLSTAT */
576164562Sgshapiro	{
576264562Sgshapiro		if (tTd(41, 2))
576390792Sgshapiro			sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
576490792Sgshapiro				   name, sm_errstring(errno));
576590792Sgshapiro		return false;
576664562Sgshapiro	}
576790792Sgshapiro#if HASLSTAT
576864562Sgshapiro	if (S_ISLNK(statb.st_mode))
576964562Sgshapiro	{
577064562Sgshapiro		/*
577164562Sgshapiro		**  For a symlink we need to make sure the
577264562Sgshapiro		**  target is a directory
577364562Sgshapiro		*/
577490792Sgshapiro
577564562Sgshapiro		if (stat(name, &statb) < 0)
577664562Sgshapiro		{
577764562Sgshapiro			if (tTd(41, 2))
577890792Sgshapiro				sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
577990792Sgshapiro					   name, sm_errstring(errno));
578090792Sgshapiro			return false;
578164562Sgshapiro		}
578264562Sgshapiro	}
578390792Sgshapiro#endif /* HASLSTAT */
578464562Sgshapiro
578564562Sgshapiro	if (!S_ISDIR(statb.st_mode))
578664562Sgshapiro	{
578764562Sgshapiro		if (tTd(41, 2))
578890792Sgshapiro			sm_dprintf("chkqdir: \"%s\": Not a directory\n",
578964562Sgshapiro				name);
579090792Sgshapiro		return false;
579164562Sgshapiro	}
579264562Sgshapiro
579364562Sgshapiro	/* Print a warning if unsafe (but still use it) */
579490792Sgshapiro	/* XXX do this only if we want the warning? */
579564562Sgshapiro	i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
579698121Sgshapiro	if (i != 0)
579798121Sgshapiro	{
579898121Sgshapiro		if (tTd(41, 2))
579998121Sgshapiro			sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
580098121Sgshapiro				   name, sm_errstring(i));
580198121Sgshapiro#if _FFR_CHK_QUEUE
580298121Sgshapiro		if (LogLevel > 8)
580398121Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
580498121Sgshapiro				  "queue directory \"%s\": Not safe: %s",
580598121Sgshapiro				  name, sm_errstring(i));
580698121Sgshapiro#endif /* _FFR_CHK_QUEUE */
580798121Sgshapiro	}
580890792Sgshapiro	return true;
580964562Sgshapiro}
581090792Sgshapiro/*
581164562Sgshapiro**  MULTIQUEUE_CACHE -- cache a list of paths to queues.
581264562Sgshapiro**
581364562Sgshapiro**	Each potential queue is checked as the cache is built.
581464562Sgshapiro**	Thereafter, each is blindly trusted.
581564562Sgshapiro**	Note that we can be called again after a timeout to rebuild
581664562Sgshapiro**	(although code for that is not ready yet).
581764562Sgshapiro**
581864562Sgshapiro**	Parameters:
581990792Sgshapiro**		basedir -- base of all queue directories.
582090792Sgshapiro**		blen -- strlen(basedir).
582190792Sgshapiro**		qg -- queue group.
582290792Sgshapiro**		qn -- number of queue directories already cached.
582390792Sgshapiro**		phash -- pointer to hash value over queue dirs.
582490792Sgshapiro#if SM_CONF_SHM
582590792Sgshapiro**			only used if shared memory is active.
582690792Sgshapiro#endif * SM_CONF_SHM *
582764562Sgshapiro**
582864562Sgshapiro**	Returns:
582990792Sgshapiro**		new number of queue directories.
583064562Sgshapiro*/
583164562Sgshapiro
583290792Sgshapiro#define INITIAL_SLOTS	20
583390792Sgshapiro#define ADD_SLOTS	10
583490792Sgshapiro
583590792Sgshapirostatic int
583690792Sgshapiromultiqueue_cache(basedir, blen, qg, qn, phash)
583790792Sgshapiro	char *basedir;
583890792Sgshapiro	int blen;
583990792Sgshapiro	QUEUEGRP *qg;
584090792Sgshapiro	int qn;
584190792Sgshapiro	unsigned int *phash;
584264562Sgshapiro{
584364562Sgshapiro	char *cp;
584464562Sgshapiro	int i, len;
584564562Sgshapiro	int slotsleft = 0;
584664562Sgshapiro	long sff = SFF_ANYFILE;
584764562Sgshapiro	char qpath[MAXPATHLEN];
584864562Sgshapiro	char subdir[MAXPATHLEN];
584990792Sgshapiro	char prefix[MAXPATHLEN];	/* dir relative to basedir */
585064562Sgshapiro
585164562Sgshapiro	if (tTd(41, 20))
585290792Sgshapiro		sm_dprintf("multiqueue_cache: called\n");
585364562Sgshapiro
585490792Sgshapiro	/* Initialize to current directory */
585590792Sgshapiro	prefix[0] = '.';
585690792Sgshapiro	prefix[1] = '\0';
585790792Sgshapiro	if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
585864562Sgshapiro	{
585990792Sgshapiro		for (i = 0; i < qg->qg_numqueues; i++)
586064562Sgshapiro		{
586190792Sgshapiro			if (qg->qg_qpaths[i].qp_name != NULL)
586290792Sgshapiro				(void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
586364562Sgshapiro		}
586490792Sgshapiro		(void) sm_free((char *) qg->qg_qpaths); /* XXX */
586590792Sgshapiro		qg->qg_qpaths = NULL;
586690792Sgshapiro		qg->qg_numqueues = 0;
586764562Sgshapiro	}
586864562Sgshapiro
586964562Sgshapiro	/* If running as root, allow safedirpath() checks to use privs */
587064562Sgshapiro	if (RunAsUid == 0)
587164562Sgshapiro		sff |= SFF_ROOTOK;
587298121Sgshapiro#if _FFR_CHK_QUEUE
587398121Sgshapiro	sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES;
587498121Sgshapiro	if (!UseMSP)
587598121Sgshapiro		sff |= SFF_NOGWFILES;
587698121Sgshapiro#endif /* _FFR_CHK_QUEUE */
587764562Sgshapiro
587890792Sgshapiro	if (!SM_IS_DIR_START(qg->qg_qdir))
587990792Sgshapiro	{
588090792Sgshapiro		/*
588190792Sgshapiro		**  XXX we could add basedir, but then we have to realloc()
588290792Sgshapiro		**  the string... Maybe another time.
588390792Sgshapiro		*/
588490792Sgshapiro
588590792Sgshapiro		syserr("QueuePath %s not absolute", qg->qg_qdir);
588690792Sgshapiro		ExitStat = EX_CONFIG;
588790792Sgshapiro		return qn;
588890792Sgshapiro	}
588990792Sgshapiro
589090792Sgshapiro	/* qpath: directory of current workgroup */
589190792Sgshapiro	len = sm_strlcpy(qpath, qg->qg_qdir, sizeof qpath);
589290792Sgshapiro	if (len >= sizeof qpath)
589390792Sgshapiro	{
589490792Sgshapiro		syserr("QueuePath %.256s too long (%d max)",
589590792Sgshapiro		       qg->qg_qdir, (int) sizeof qpath);
589690792Sgshapiro		ExitStat = EX_CONFIG;
589790792Sgshapiro		return qn;
589890792Sgshapiro	}
589990792Sgshapiro
590090792Sgshapiro	/* begin of qpath must be same as basedir */
590190792Sgshapiro	if (strncmp(basedir, qpath, blen) != 0 &&
590290792Sgshapiro	    (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
590390792Sgshapiro	{
590490792Sgshapiro		syserr("QueuePath %s not subpath of QueueDirectory %s",
590590792Sgshapiro			qpath, basedir);
590690792Sgshapiro		ExitStat = EX_CONFIG;
590790792Sgshapiro		return qn;
590890792Sgshapiro	}
590990792Sgshapiro
591090792Sgshapiro	/* Do we have a nested subdirectory? */
591190792Sgshapiro	if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
591290792Sgshapiro	{
591390792Sgshapiro
591490792Sgshapiro		/* Copy subdirectory into prefix for later use */
591590792Sgshapiro		if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof prefix) >=
591690792Sgshapiro		    sizeof prefix)
591790792Sgshapiro		{
591890792Sgshapiro			syserr("QueuePath %.256s too long (%d max)",
591990792Sgshapiro				qg->qg_qdir, (int) sizeof qpath);
592090792Sgshapiro			ExitStat = EX_CONFIG;
592190792Sgshapiro			return qn;
592290792Sgshapiro		}
592390792Sgshapiro		cp = SM_LAST_DIR_DELIM(prefix);
592490792Sgshapiro		SM_ASSERT(cp != NULL);
592590792Sgshapiro		*cp = '\0';	/* cut off trailing / */
592690792Sgshapiro	}
592790792Sgshapiro
592890792Sgshapiro	/* This is guaranteed by the basedir check above */
592990792Sgshapiro	SM_ASSERT(len >= blen - 1);
593090792Sgshapiro	cp = &qpath[len - 1];
593164562Sgshapiro	if (*cp == '*')
593264562Sgshapiro	{
593390792Sgshapiro		register DIR *dp;
593490792Sgshapiro		register struct dirent *d;
593590792Sgshapiro		int off;
593690792Sgshapiro		char *delim;
593790792Sgshapiro		char relpath[MAXPATHLEN];
593890792Sgshapiro
593990792Sgshapiro		*cp = '\0';	/* Overwrite wildcard */
594090792Sgshapiro		if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
594164562Sgshapiro		{
594264562Sgshapiro			syserr("QueueDirectory: can not wildcard relative path");
594364562Sgshapiro			if (tTd(41, 2))
594490792Sgshapiro				sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
594571345Sgshapiro					qpath);
594664562Sgshapiro			ExitStat = EX_CONFIG;
594790792Sgshapiro			return qn;
594864562Sgshapiro		}
594964562Sgshapiro		if (cp == qpath)
595064562Sgshapiro		{
595164562Sgshapiro			/*
595264562Sgshapiro			**  Special case of top level wildcard, like /foo*
595390792Sgshapiro			**	Change to //foo*
595464562Sgshapiro			*/
595564562Sgshapiro
595690792Sgshapiro			(void) sm_strlcpy(qpath + 1, qpath, sizeof qpath - 1);
595764562Sgshapiro			++cp;
595864562Sgshapiro		}
595990792Sgshapiro		delim = cp;
596090792Sgshapiro		*(cp++) = '\0';		/* Replace / with \0 */
596190792Sgshapiro		len = strlen(cp);	/* Last component of queue directory */
596264562Sgshapiro
596390792Sgshapiro		/*
596490792Sgshapiro		**  Path relative to basedir, with trailing /
596590792Sgshapiro		**  It will be modified below to specify the subdirectories
596690792Sgshapiro		**  so they can be opened without chdir().
596790792Sgshapiro		*/
596890792Sgshapiro
596990792Sgshapiro		off = sm_strlcpyn(relpath, sizeof relpath, 2, prefix, "/");
597090792Sgshapiro		SM_ASSERT(off < sizeof relpath);
597190792Sgshapiro
597264562Sgshapiro		if (tTd(41, 2))
597390792Sgshapiro			sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
597490792Sgshapiro				   relpath, cp);
597564562Sgshapiro
597690792Sgshapiro		/* It is always basedir: we don't need to store it per group */
597790792Sgshapiro		/* XXX: optimize this! -> one more global? */
597890792Sgshapiro		qg->qg_qdir = newstr(basedir);
597990792Sgshapiro		qg->qg_qdir[blen - 1] = '\0';	/* cut off trailing / */
598064562Sgshapiro
598164562Sgshapiro		/*
598264562Sgshapiro		**  XXX Should probably wrap this whole loop in a timeout
598364562Sgshapiro		**  in case some wag decides to NFS mount the queues.
598464562Sgshapiro		*/
598564562Sgshapiro
598690792Sgshapiro		/* Test path to get warning messages. */
598790792Sgshapiro		if (qn == 0)
598864562Sgshapiro		{
598990792Sgshapiro			/*  XXX qg_runasuid and qg_runasgid for specials? */
599090792Sgshapiro			i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
599190792Sgshapiro					sff, 0, 0);
599290792Sgshapiro			if (i != 0 && tTd(41, 2))
599390792Sgshapiro				sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
599490792Sgshapiro					   basedir, sm_errstring(i));
599564562Sgshapiro		}
599664562Sgshapiro
599790792Sgshapiro		if ((dp = opendir(prefix)) == NULL)
599864562Sgshapiro		{
599990792Sgshapiro			syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
600064562Sgshapiro			if (tTd(41, 2))
600190792Sgshapiro				sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
600290792Sgshapiro					   qg->qg_qdir, prefix,
600390792Sgshapiro					   sm_errstring(errno));
600464562Sgshapiro			ExitStat = EX_CONFIG;
600590792Sgshapiro			return qn;
600664562Sgshapiro		}
600764562Sgshapiro		while ((d = readdir(dp)) != NULL)
600864562Sgshapiro		{
600990792Sgshapiro			i = strlen(d->d_name);
601090792Sgshapiro			if (i < len || strncmp(d->d_name, cp, len) != 0)
601164562Sgshapiro			{
601264562Sgshapiro				if (tTd(41, 5))
601390792Sgshapiro					sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
601464562Sgshapiro						d->d_name);
601564562Sgshapiro				continue;
601664562Sgshapiro			}
601790792Sgshapiro
601890792Sgshapiro			/* Create relative pathname: prefix + local directory */
601990792Sgshapiro			i = sizeof(relpath) - off;
602090792Sgshapiro			if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
602190792Sgshapiro				continue;	/* way too long */
602290792Sgshapiro
602390792Sgshapiro			if (!chkqdir(relpath, sff))
602464562Sgshapiro				continue;
602564562Sgshapiro
602690792Sgshapiro			if (qg->qg_qpaths == NULL)
602764562Sgshapiro			{
602890792Sgshapiro				slotsleft = INITIAL_SLOTS;
602990792Sgshapiro				qg->qg_qpaths = (QPATHS *)xalloc((sizeof *qg->qg_qpaths) *
603090792Sgshapiro								slotsleft);
603190792Sgshapiro				qg->qg_numqueues = 0;
603264562Sgshapiro			}
603364562Sgshapiro			else if (slotsleft < 1)
603464562Sgshapiro			{
603590792Sgshapiro				qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
603690792Sgshapiro							  (sizeof *qg->qg_qpaths) *
603790792Sgshapiro							  (qg->qg_numqueues +
603890792Sgshapiro							   ADD_SLOTS));
603990792Sgshapiro				if (qg->qg_qpaths == NULL)
604064562Sgshapiro				{
604164562Sgshapiro					(void) closedir(dp);
604290792Sgshapiro					return qn;
604364562Sgshapiro				}
604490792Sgshapiro				slotsleft += ADD_SLOTS;
604564562Sgshapiro			}
604664562Sgshapiro
604764562Sgshapiro			/* check subdirs */
604890792Sgshapiro			qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
604964562Sgshapiro
605090792Sgshapiro#define CHKRSUBDIR(name, flag)	\
605190792Sgshapiro	(void) sm_strlcpyn(subdir, sizeof subdir, 3, relpath, "/", name); \
605290792Sgshapiro	if (chkqdir(subdir, sff))	\
605390792Sgshapiro		qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag;	\
605490792Sgshapiro	else
605564562Sgshapiro
605664562Sgshapiro
605790792Sgshapiro			CHKRSUBDIR("qf", QP_SUBQF);
605890792Sgshapiro			CHKRSUBDIR("df", QP_SUBDF);
605990792Sgshapiro			CHKRSUBDIR("xf", QP_SUBXF);
606090792Sgshapiro
606164562Sgshapiro			/* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
606264562Sgshapiro			/* maybe even - 17 (subdirs) */
606390792Sgshapiro
606490792Sgshapiro			if (prefix[0] != '.')
606590792Sgshapiro				qg->qg_qpaths[qg->qg_numqueues].qp_name =
606690792Sgshapiro					newstr(relpath);
606790792Sgshapiro			else
606890792Sgshapiro				qg->qg_qpaths[qg->qg_numqueues].qp_name =
606990792Sgshapiro					newstr(d->d_name);
607090792Sgshapiro
607164562Sgshapiro			if (tTd(41, 2))
607290792Sgshapiro				sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
607390792Sgshapiro					qg->qg_numqueues, relpath,
607490792Sgshapiro					qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
607590792Sgshapiro#if SM_CONF_SHM
607690792Sgshapiro			qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
607790792Sgshapiro			*phash = hash_q(relpath, *phash);
607890792Sgshapiro#endif /* SM_CONF_SHM */
607990792Sgshapiro			qg->qg_numqueues++;
608090792Sgshapiro			++qn;
608164562Sgshapiro			slotsleft--;
608264562Sgshapiro		}
608364562Sgshapiro		(void) closedir(dp);
608490792Sgshapiro
608590792Sgshapiro		/* undo damage */
608690792Sgshapiro		*delim = '/';
608764562Sgshapiro	}
608890792Sgshapiro	if (qg->qg_numqueues == 0)
608964562Sgshapiro	{
609090792Sgshapiro		qg->qg_qpaths = (QPATHS *) xalloc(sizeof *qg->qg_qpaths);
609164562Sgshapiro
609264562Sgshapiro		/* test path to get warning messages */
609390792Sgshapiro		i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
609490792Sgshapiro		if (i == ENOENT)
609564562Sgshapiro		{
609690792Sgshapiro			syserr("can not opendir(%s)", qpath);
609764562Sgshapiro			if (tTd(41, 2))
609890792Sgshapiro				sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
609990792Sgshapiro					   qpath, sm_errstring(i));
610064562Sgshapiro			ExitStat = EX_CONFIG;
610190792Sgshapiro			return qn;
610264562Sgshapiro		}
610364562Sgshapiro
610490792Sgshapiro		qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
610590792Sgshapiro		qg->qg_numqueues = 1;
610690792Sgshapiro
610764562Sgshapiro		/* check subdirs */
610890792Sgshapiro#define CHKSUBDIR(name, flag)	\
610990792Sgshapiro	(void) sm_strlcpyn(subdir, sizeof subdir, 3, qg->qg_qdir, "/", name); \
611090792Sgshapiro	if (chkqdir(subdir, sff))	\
611190792Sgshapiro		qg->qg_qpaths[0].qp_subdirs |= flag;	\
611290792Sgshapiro	else
611364562Sgshapiro
611490792Sgshapiro		CHKSUBDIR("qf", QP_SUBQF);
611590792Sgshapiro		CHKSUBDIR("df", QP_SUBDF);
611690792Sgshapiro		CHKSUBDIR("xf", QP_SUBXF);
611764562Sgshapiro
611890792Sgshapiro		if (qg->qg_qdir[blen - 1] != '\0' &&
611990792Sgshapiro		    qg->qg_qdir[blen] != '\0')
612090792Sgshapiro		{
612190792Sgshapiro			/*
612290792Sgshapiro			**  Copy the last component into qpaths and
612390792Sgshapiro			**  cut off qdir
612490792Sgshapiro			*/
612590792Sgshapiro
612690792Sgshapiro			qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
612790792Sgshapiro			qg->qg_qdir[blen - 1] = '\0';
612890792Sgshapiro		}
612990792Sgshapiro		else
613090792Sgshapiro			qg->qg_qpaths[0].qp_name = newstr(".");
613190792Sgshapiro
613290792Sgshapiro#if SM_CONF_SHM
613390792Sgshapiro		qg->qg_qpaths[0].qp_idx = qn;
613490792Sgshapiro		*phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
613590792Sgshapiro#endif /* SM_CONF_SHM */
613690792Sgshapiro		++qn;
613764562Sgshapiro	}
613890792Sgshapiro	return qn;
613964562Sgshapiro}
614064562Sgshapiro
614190792Sgshapiro/*
614290792Sgshapiro**  FILESYS_FIND -- find entry in FileSys table, or add new one
614390792Sgshapiro**
614490792Sgshapiro**	Given the pathname of a directory, determine the file system
614590792Sgshapiro**	in which that directory resides, and return a pointer to the
614690792Sgshapiro**	entry in the FileSys table that describes the file system.
614790792Sgshapiro**	A new entry is added if necessary (and requested).
614890792Sgshapiro**	If the directory does not exist, -1 is returned.
614990792Sgshapiro**
615090792Sgshapiro**	Parameters:
615190792Sgshapiro**		path -- pathname of directory
615290792Sgshapiro**		add -- add to structure if not found.
615390792Sgshapiro**
615490792Sgshapiro**	Returns:
615590792Sgshapiro**		>=0: found: index in file system table
615690792Sgshapiro**		<0: some error, i.e.,
615790792Sgshapiro**		FSF_TOO_MANY: too many filesystems (-> syserr())
615890792Sgshapiro**		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
615990792Sgshapiro**		FSF_NOT_FOUND: not in list
616090792Sgshapiro*/
616190792Sgshapiro
616290792Sgshapirostatic short filesys_find __P((char *, bool));
616390792Sgshapiro
616490792Sgshapiro#define FSF_NOT_FOUND	(-1)
616590792Sgshapiro#define FSF_STAT_FAIL	(-2)
616690792Sgshapiro#define FSF_TOO_MANY	(-3)
616790792Sgshapiro
616890792Sgshapirostatic short
616990792Sgshapirofilesys_find(path, add)
617090792Sgshapiro	char *path;
617190792Sgshapiro	bool add;
617290792Sgshapiro{
617390792Sgshapiro	struct stat st;
617490792Sgshapiro	short i;
617590792Sgshapiro
617690792Sgshapiro	if (stat(path, &st) < 0)
617790792Sgshapiro	{
617890792Sgshapiro		syserr("cannot stat queue directory %s", path);
617990792Sgshapiro		return FSF_STAT_FAIL;
618090792Sgshapiro	}
618190792Sgshapiro	for (i = 0; i < NumFileSys; ++i)
618290792Sgshapiro	{
618390792Sgshapiro		if (FILE_SYS_DEV(i) == st.st_dev)
618490792Sgshapiro			return i;
618590792Sgshapiro	}
618690792Sgshapiro	if (i >= MAXFILESYS)
618790792Sgshapiro	{
618890792Sgshapiro		syserr("too many queue file systems (%d max)", MAXFILESYS);
618990792Sgshapiro		return FSF_TOO_MANY;
619090792Sgshapiro	}
619190792Sgshapiro	if (!add)
619290792Sgshapiro		return FSF_NOT_FOUND;
619390792Sgshapiro
619490792Sgshapiro	++NumFileSys;
619590792Sgshapiro	FILE_SYS_NAME(i) = path;
619690792Sgshapiro	FILE_SYS_DEV(i) = st.st_dev;
619790792Sgshapiro	FILE_SYS_AVAIL(i) = 0;
619890792Sgshapiro	FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
619990792Sgshapiro	return i;
620090792Sgshapiro}
620190792Sgshapiro
620290792Sgshapiro/*
620390792Sgshapiro**  FILESYS_SETUP -- set up mapping from queue directories to file systems
620490792Sgshapiro**
620590792Sgshapiro**	This data structure is used to efficiently check the amount of
620690792Sgshapiro**	free space available in a set of queue directories.
620790792Sgshapiro**
620890792Sgshapiro**	Parameters:
620990792Sgshapiro**		add -- initialize structure if necessary.
621090792Sgshapiro**
621190792Sgshapiro**	Returns:
621290792Sgshapiro**		0: success
621390792Sgshapiro**		<0: some error, i.e.,
621490792Sgshapiro**		FSF_NOT_FOUND: not in list
621590792Sgshapiro**		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
621690792Sgshapiro**		FSF_TOO_MANY: too many filesystems (-> syserr())
621790792Sgshapiro*/
621890792Sgshapiro
621990792Sgshapirostatic int filesys_setup __P((bool));
622090792Sgshapiro
622190792Sgshapirostatic int
622290792Sgshapirofilesys_setup(add)
622390792Sgshapiro	bool add;
622490792Sgshapiro{
622590792Sgshapiro	int i, j;
622690792Sgshapiro	short fs;
622790792Sgshapiro	int ret;
622890792Sgshapiro
622990792Sgshapiro	ret = 0;
623090792Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
623190792Sgshapiro	{
623290792Sgshapiro		for (j = 0; j < Queue[i]->qg_numqueues; ++j)
623390792Sgshapiro		{
623490792Sgshapiro			QPATHS *qp = &Queue[i]->qg_qpaths[j];
623590792Sgshapiro
623690792Sgshapiro			fs = filesys_find(qp->qp_name, add);
623790792Sgshapiro			if (fs >= 0)
623890792Sgshapiro				qp->qp_fsysidx = fs;
623990792Sgshapiro			else
624090792Sgshapiro				qp->qp_fsysidx = 0;
624190792Sgshapiro			if (fs < ret)
624290792Sgshapiro				ret = fs;
624390792Sgshapiro		}
624490792Sgshapiro	}
624590792Sgshapiro	return ret;
624690792Sgshapiro}
624790792Sgshapiro
624890792Sgshapiro/*
624990792Sgshapiro**  FILESYS_UPDATE -- update amount of free space on all file systems
625090792Sgshapiro**
625190792Sgshapiro**	The FileSys table is used to cache the amount of free space
625290792Sgshapiro**	available on all queue directory file systems.
625390792Sgshapiro**	This function updates the cached information if it has expired.
625490792Sgshapiro**
625590792Sgshapiro**	Parameters:
625690792Sgshapiro**		none.
625790792Sgshapiro**
625890792Sgshapiro**	Returns:
625990792Sgshapiro**		none.
626090792Sgshapiro**
626190792Sgshapiro**	Side Effects:
626290792Sgshapiro**		Updates FileSys table.
626390792Sgshapiro*/
626490792Sgshapiro
626590792Sgshapirovoid
626690792Sgshapirofilesys_update()
626790792Sgshapiro{
626890792Sgshapiro	int i;
626990792Sgshapiro	long avail, blksize;
627090792Sgshapiro	time_t now;
627190792Sgshapiro	static time_t nextupdate = 0;
627290792Sgshapiro
627390792Sgshapiro#if SM_CONF_SHM
627490792Sgshapiro	/* only the daemon updates this structure */
627590792Sgshapiro	if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
627690792Sgshapiro		return;
627790792Sgshapiro#endif /* SM_CONF_SHM */
627890792Sgshapiro	now = curtime();
627990792Sgshapiro	if (now < nextupdate)
628090792Sgshapiro		return;
628190792Sgshapiro	nextupdate = now + FILESYS_UPDATE_INTERVAL;
628290792Sgshapiro	for (i = 0; i < NumFileSys; ++i)
628390792Sgshapiro	{
628490792Sgshapiro		FILESYS *fs = &FILE_SYS(i);
628590792Sgshapiro
628690792Sgshapiro		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
628790792Sgshapiro		if (avail < 0 || blksize <= 0)
628890792Sgshapiro		{
628990792Sgshapiro			if (LogLevel > 5)
629090792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
629190792Sgshapiro					"filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
629290792Sgshapiro					sm_errstring(errno),
629390792Sgshapiro					FILE_SYS_NAME(i), avail, blksize);
629490792Sgshapiro			fs->fs_avail = 0;
629590792Sgshapiro			fs->fs_blksize = 1024; /* avoid divide by zero */
629690792Sgshapiro			nextupdate = now + 2; /* let's do this soon again */
629790792Sgshapiro		}
629890792Sgshapiro		else
629990792Sgshapiro		{
630090792Sgshapiro			fs->fs_avail = avail;
630190792Sgshapiro			fs->fs_blksize = blksize;
630290792Sgshapiro		}
630390792Sgshapiro	}
630490792Sgshapiro}
630590792Sgshapiro
630690792Sgshapiro#if _FFR_ANY_FREE_FS
630790792Sgshapiro/*
630890792Sgshapiro**  FILESYS_FREE -- check whether there is at least one fs with enough space.
630990792Sgshapiro**
631090792Sgshapiro**	Parameters:
631190792Sgshapiro**		fsize -- file size in bytes
631290792Sgshapiro**
631390792Sgshapiro**	Returns:
631490792Sgshapiro**		true iff there is one fs with more than fsize bytes free.
631590792Sgshapiro*/
631690792Sgshapiro
631790792Sgshapirobool
631890792Sgshapirofilesys_free(fsize)
631990792Sgshapiro	long fsize;
632090792Sgshapiro{
632190792Sgshapiro	int i;
632290792Sgshapiro
632390792Sgshapiro	if (fsize <= 0)
632490792Sgshapiro		return true;
632590792Sgshapiro	for (i = 0; i < NumFileSys; ++i)
632690792Sgshapiro	{
632790792Sgshapiro		long needed = 0;
632890792Sgshapiro
632990792Sgshapiro		if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
633090792Sgshapiro			continue;
633190792Sgshapiro		needed += fsize / FILE_SYS_BLKSIZE(i)
633290792Sgshapiro			  + ((fsize % FILE_SYS_BLKSIZE(i)
633390792Sgshapiro			      > 0) ? 1 : 0)
633490792Sgshapiro			  + MinBlocksFree;
633590792Sgshapiro		if (needed <= FILE_SYS_AVAIL(i))
633690792Sgshapiro			return true;
633790792Sgshapiro	}
633890792Sgshapiro	return false;
633990792Sgshapiro}
634090792Sgshapiro#endif /* _FFR_ANY_FREE_FS */
634190792Sgshapiro
634290792Sgshapiro#if _FFR_CONTROL_MSTAT
634390792Sgshapiro/*
634490792Sgshapiro**  DISK_STATUS -- show amount of free space in queue directories
634590792Sgshapiro**
634690792Sgshapiro**	Parameters:
634790792Sgshapiro**		out -- output file pointer.
634890792Sgshapiro**		prefix -- string to output in front of each line.
634990792Sgshapiro**
635090792Sgshapiro**	Returns:
635190792Sgshapiro**		none.
635290792Sgshapiro*/
635390792Sgshapiro
635490792Sgshapirovoid
635590792Sgshapirodisk_status(out, prefix)
635690792Sgshapiro	SM_FILE_T *out;
635790792Sgshapiro	char *prefix;
635890792Sgshapiro{
635990792Sgshapiro	int i;
636090792Sgshapiro	long avail, blksize;
636190792Sgshapiro	long free;
636290792Sgshapiro
636390792Sgshapiro	for (i = 0; i < NumFileSys; ++i)
636490792Sgshapiro	{
636590792Sgshapiro		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
636690792Sgshapiro		if (avail >= 0 && blksize > 0)
636790792Sgshapiro		{
636890792Sgshapiro			free = (long)((double) avail *
636990792Sgshapiro				((double) blksize / 1024));
637090792Sgshapiro		}
637190792Sgshapiro		else
637290792Sgshapiro			free = -1;
637390792Sgshapiro		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
637490792Sgshapiro				"%s%d/%s/%ld\r\n",
637590792Sgshapiro				prefix, i,
637690792Sgshapiro				FILE_SYS_NAME(i),
637790792Sgshapiro					free);
637890792Sgshapiro	}
637990792Sgshapiro}
638090792Sgshapiro#endif /* _FFR_CONTROL_MSTAT */
638190792Sgshapiro
638290792Sgshapiro#if SM_CONF_SHM
638390792Sgshapiro/*
638490792Sgshapiro**  UPD_QS -- update information about queue when adding/deleting an entry
638590792Sgshapiro**
638690792Sgshapiro**	Parameters:
638790792Sgshapiro**		e -- envelope.
638890792Sgshapiro**		delete -- delete/add entry.
638990792Sgshapiro**		avail -- update the space available as well.
639090792Sgshapiro**
639190792Sgshapiro**	Returns:
639290792Sgshapiro**		none.
639390792Sgshapiro**
639490792Sgshapiro**	Side Effects:
639590792Sgshapiro**		Modifies available space in filesystem.
639690792Sgshapiro**		Changes number of entries in queue directory.
639790792Sgshapiro*/
639890792Sgshapiro
639990792Sgshapirovoid
640090792Sgshapiroupd_qs(e, delete, avail)
640190792Sgshapiro	ENVELOPE *e;
640290792Sgshapiro	bool delete;
640390792Sgshapiro	bool avail;
640490792Sgshapiro{
640590792Sgshapiro	short fidx;
640690792Sgshapiro	int idx;
640790792Sgshapiro	long s;
640890792Sgshapiro
640990792Sgshapiro	if (ShmId == SM_SHM_NO_ID || e == NULL)
641090792Sgshapiro		return;
641190792Sgshapiro	if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
641290792Sgshapiro		return;
641390792Sgshapiro	idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
641490792Sgshapiro
641590792Sgshapiro	/* XXX in theory this needs to be protected with a mutex */
641690792Sgshapiro	if (QSHM_ENTRIES(idx) >= 0)
641790792Sgshapiro	{
641890792Sgshapiro		if (delete)
641990792Sgshapiro			--QSHM_ENTRIES(idx);
642090792Sgshapiro		else
642190792Sgshapiro			++QSHM_ENTRIES(idx);
642290792Sgshapiro	}
642390792Sgshapiro
642490792Sgshapiro	fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
642590792Sgshapiro	if (fidx < 0)
642690792Sgshapiro		return;
642790792Sgshapiro
642890792Sgshapiro	/* update available space also?  (might be loseqfile) */
642990792Sgshapiro	if (!avail)
643090792Sgshapiro		return;
643190792Sgshapiro
643290792Sgshapiro	/* convert size to blocks; this causes rounding errors */
643390792Sgshapiro	s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
643490792Sgshapiro	if (s == 0)
643590792Sgshapiro		return;
643690792Sgshapiro
643790792Sgshapiro	/* XXX in theory this needs to be protected with a mutex */
643890792Sgshapiro	if (delete)
643990792Sgshapiro		FILE_SYS_AVAIL(fidx) += s;
644090792Sgshapiro	else
644190792Sgshapiro		FILE_SYS_AVAIL(fidx) -= s;
644290792Sgshapiro
644390792Sgshapiro}
644494334Sgshapiro
644594334Sgshapiro#if _FFR_SELECT_SHM
644694334Sgshapiro
644794334Sgshapirostatic bool write_key_file __P((char *, long));
644894334Sgshapirostatic long read_key_file __P((char *, long));
644994334Sgshapiro
645090792Sgshapiro/*
645194334Sgshapiro**  WRITE_KEY_FILE -- record some key into a file.
645294334Sgshapiro**
645394334Sgshapiro**	Parameters:
645494334Sgshapiro**		keypath -- file name.
645594334Sgshapiro**		key -- key to write.
645694334Sgshapiro**
645794334Sgshapiro**	Returns:
645894334Sgshapiro**		true iff file could be written.
645994334Sgshapiro**
646094334Sgshapiro**	Side Effects:
646194334Sgshapiro**		writes file.
646294334Sgshapiro*/
646394334Sgshapiro
646494334Sgshapirostatic bool
646594334Sgshapirowrite_key_file(keypath, key)
646694334Sgshapiro	char *keypath;
646794334Sgshapiro	long key;
646894334Sgshapiro{
646994334Sgshapiro	bool ok;
647094334Sgshapiro	long sff;
647194334Sgshapiro	SM_FILE_T *keyf;
647294334Sgshapiro
647394334Sgshapiro	ok = false;
647494334Sgshapiro	if (keypath == NULL || *keypath == '\0')
647594334Sgshapiro		return ok;
647694334Sgshapiro	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
647794334Sgshapiro	if (TrustedUid != 0 && RealUid == TrustedUid)
647894334Sgshapiro		sff |= SFF_OPENASROOT;
647994334Sgshapiro	keyf = safefopen(keypath, O_WRONLY|O_TRUNC, 0644, sff);
648094334Sgshapiro	if (keyf == NULL)
648194334Sgshapiro	{
648294334Sgshapiro		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
648394334Sgshapiro			  keypath, sm_errstring(errno));
648494334Sgshapiro	}
648594334Sgshapiro	else
648694334Sgshapiro	{
648794334Sgshapiro		ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
648894334Sgshapiro		     SM_IO_EOF;
648994334Sgshapiro		ok = ok && (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF);
649094334Sgshapiro	}
649194334Sgshapiro	return ok;
649294334Sgshapiro}
649394334Sgshapiro
649494334Sgshapiro/*
649594334Sgshapiro**  READ_KEY_FILE -- read a key from a file.
649694334Sgshapiro**
649794334Sgshapiro**	Parameters:
649894334Sgshapiro**		keypath -- file name.
649994334Sgshapiro**		key -- default key.
650094334Sgshapiro**
650194334Sgshapiro**	Returns:
650294334Sgshapiro**		key.
650394334Sgshapiro*/
650494334Sgshapiro
650594334Sgshapirostatic long
650694334Sgshapiroread_key_file(keypath, key)
650794334Sgshapiro	char *keypath;
650894334Sgshapiro	long key;
650994334Sgshapiro{
651094334Sgshapiro	int r;
651194334Sgshapiro	long sff, n;
651294334Sgshapiro	SM_FILE_T *keyf;
651394334Sgshapiro
651494334Sgshapiro	if (keypath == NULL || *keypath == '\0')
651594334Sgshapiro		return key;
651694334Sgshapiro	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
651794334Sgshapiro	if (TrustedUid != 0 && RealUid == TrustedUid)
651894334Sgshapiro		sff |= SFF_OPENASROOT;
651994334Sgshapiro	keyf = safefopen(keypath, O_RDONLY, 0644, sff);
652094334Sgshapiro	if (keyf == NULL)
652194334Sgshapiro	{
652294334Sgshapiro		sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s",
652394334Sgshapiro			  keypath, sm_errstring(errno));
652494334Sgshapiro	}
652594334Sgshapiro	else
652694334Sgshapiro	{
652794334Sgshapiro		r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n);
652894334Sgshapiro		if (r == 1)
652994334Sgshapiro			key = n;
653094334Sgshapiro		(void) sm_io_close(keyf, SM_TIME_DEFAULT);
653194334Sgshapiro	}
653294334Sgshapiro	return key;
653394334Sgshapiro}
653494334Sgshapiro#endif /* _FFR_SELECT_SHM */
653594334Sgshapiro
653694334Sgshapiro/*
653790792Sgshapiro**  INIT_SHM -- initialize shared memory structure
653890792Sgshapiro**
653990792Sgshapiro**	Initialize or attach to shared memory segment.
654090792Sgshapiro**	Currently it is not a fatal error if this doesn't work.
654190792Sgshapiro**	However, it causes us to have a "fallback" storage location
654290792Sgshapiro**	for everything that is supposed to be in the shared memory,
654390792Sgshapiro**	which makes the code slightly ugly.
654490792Sgshapiro**
654590792Sgshapiro**	Parameters:
654690792Sgshapiro**		qn -- number of queue directories.
654790792Sgshapiro**		owner -- owner of shared memory.
654890792Sgshapiro**		hash -- identifies data that is stored in shared memory.
654990792Sgshapiro**
655090792Sgshapiro**	Returns:
655190792Sgshapiro**		none.
655290792Sgshapiro*/
655390792Sgshapiro
655490792Sgshapirostatic void init_shm __P((int, bool, unsigned int));
655590792Sgshapiro
655690792Sgshapirostatic void
655790792Sgshapiroinit_shm(qn, owner, hash)
655890792Sgshapiro	int qn;
655990792Sgshapiro	bool owner;
656090792Sgshapiro	unsigned int hash;
656190792Sgshapiro{
656290792Sgshapiro	int i;
656394334Sgshapiro#if _FFR_SELECT_SHM
656494334Sgshapiro	bool keyselect;
656594334Sgshapiro#endif /* _FFR_SELECT_SHM */
656690792Sgshapiro
656790792Sgshapiro	PtrFileSys = &FileSys[0];
656890792Sgshapiro	PNumFileSys = &Numfilesys;
656994334Sgshapiro#if _FFR_SELECT_SHM
657094334Sgshapiro/* if this "key" is specified: select one yourself */
657194334Sgshapiro# define SEL_SHM_KEY	((key_t) -1)
657294334Sgshapiro# define FIRST_SHM_KEY	25
657394334Sgshapiro#endif /* _FFR_SELECT_SHM */
657490792Sgshapiro
657590792Sgshapiro	/* This allows us to disable shared memory at runtime. */
657690792Sgshapiro	if (ShmKey != 0)
657790792Sgshapiro	{
657890792Sgshapiro		int count;
657990792Sgshapiro		int save_errno;
658090792Sgshapiro		size_t shms;
658190792Sgshapiro
658290792Sgshapiro		count = 0;
658390792Sgshapiro		shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
658494334Sgshapiro#if _FFR_SELECT_SHM
658594334Sgshapiro		keyselect = ShmKey == SEL_SHM_KEY;
658694334Sgshapiro		if (keyselect)
658794334Sgshapiro		{
658894334Sgshapiro			if (owner)
658994334Sgshapiro				ShmKey = FIRST_SHM_KEY;
659094334Sgshapiro			else
659194334Sgshapiro			{
659294334Sgshapiro				ShmKey = read_key_file(ShmKeyFile, ShmKey);
659394334Sgshapiro				keyselect = false;
659494334Sgshapiro				if (ShmKey == SEL_SHM_KEY)
659594334Sgshapiro					goto error;
659694334Sgshapiro			}
659794334Sgshapiro		}
659894334Sgshapiro#endif /* _FFR_SELECT_SHM */
659990792Sgshapiro		for (;;)
660090792Sgshapiro		{
660190792Sgshapiro			/* XXX: maybe allow read access for group? */
660290792Sgshapiro			Pshm = sm_shmstart(ShmKey, shms, SHM_R|SHM_W, &ShmId,
660390792Sgshapiro					   owner);
660490792Sgshapiro			save_errno = errno;
660590792Sgshapiro			if (Pshm != NULL || save_errno != EEXIST)
660690792Sgshapiro				break;
660790792Sgshapiro			if (++count >= 3)
660894334Sgshapiro			{
660994334Sgshapiro#if _FFR_SELECT_SHM
661094334Sgshapiro				if (keyselect)
661194334Sgshapiro				{
661294334Sgshapiro					++ShmKey;
661394334Sgshapiro
661494334Sgshapiro					/* back where we started? */
661594334Sgshapiro					if (ShmKey == SEL_SHM_KEY)
661694334Sgshapiro						break;
661794334Sgshapiro					continue;
661894334Sgshapiro				}
661994334Sgshapiro#endif /* _FFR_SELECT_SHM */
662090792Sgshapiro				break;
662194334Sgshapiro			}
662294334Sgshapiro#if _FFR_SELECT_SHM
662394334Sgshapiro			/* only sleep if we are at the first key */
662494334Sgshapiro			if (!keyselect || ShmKey == SEL_SHM_KEY)
662594334Sgshapiro#endif /* _FFR_SELECT_SHM */
662690792Sgshapiro			sleep(count);
662790792Sgshapiro		}
662890792Sgshapiro		if (Pshm != NULL)
662990792Sgshapiro		{
663090792Sgshapiro			int *p;
663190792Sgshapiro
663294334Sgshapiro#if _FFR_SELECT_SHM
663394334Sgshapiro			if (keyselect)
663494334Sgshapiro				(void) write_key_file(ShmKeyFile, (long) ShmKey);
663594334Sgshapiro#endif /* _FFR_SELECT_SHM */
663690792Sgshapiro			p = (int *) Pshm;
663790792Sgshapiro			if (owner)
663890792Sgshapiro			{
663990792Sgshapiro				*p = (int) shms;
664090792Sgshapiro				*((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
664190792Sgshapiro				p = (int *) SHM_OFF_TAG(Pshm);
664290792Sgshapiro				*p = hash;
664390792Sgshapiro			}
664490792Sgshapiro			else
664590792Sgshapiro			{
664690792Sgshapiro				if (*p != (int) shms)
664790792Sgshapiro				{
664890792Sgshapiro					save_errno = EINVAL;
664990792Sgshapiro					cleanup_shm(false);
665090792Sgshapiro					goto error;
665190792Sgshapiro				}
665290792Sgshapiro				p = (int *) SHM_OFF_TAG(Pshm);
665390792Sgshapiro				if (*p != (int) hash)
665490792Sgshapiro				{
665590792Sgshapiro					save_errno = EINVAL;
665690792Sgshapiro					cleanup_shm(false);
665790792Sgshapiro					goto error;
665890792Sgshapiro				}
665990792Sgshapiro
666090792Sgshapiro				/*
666190792Sgshapiro				**  XXX how to check the pid?
666290792Sgshapiro				**  Read it from the pid-file? That does
666390792Sgshapiro				**  not need to exist.
666490792Sgshapiro				**  We could disable shm if we can't confirm
666590792Sgshapiro				**  that it is the right one.
666690792Sgshapiro				*/
666790792Sgshapiro			}
666890792Sgshapiro
666990792Sgshapiro			PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
667090792Sgshapiro			PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
667190792Sgshapiro			QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
667290792Sgshapiro			PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
667390792Sgshapiro			*PRSATmpCnt = 0;
667490792Sgshapiro			if (owner)
667590792Sgshapiro			{
667690792Sgshapiro				/* initialize values in shared memory */
667790792Sgshapiro				NumFileSys = 0;
667890792Sgshapiro				for (i = 0; i < qn; i++)
667990792Sgshapiro					QShm[i].qs_entries = -1;
668090792Sgshapiro			}
668190792Sgshapiro			return;
668290792Sgshapiro		}
668390792Sgshapiro  error:
668490792Sgshapiro		if (LogLevel > (owner ? 8 : 11))
668590792Sgshapiro		{
668690792Sgshapiro			sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
668790792Sgshapiro				  "can't %s shared memory, key=%ld: %s",
668890792Sgshapiro				  owner ? "initialize" : "attach to",
668990792Sgshapiro				  (long) ShmKey, sm_errstring(save_errno));
669090792Sgshapiro		}
669190792Sgshapiro	}
669290792Sgshapiro}
669390792Sgshapiro#endif /* SM_CONF_SHM */
669490792Sgshapiro
669590792Sgshapiro/*
669690792Sgshapiro**  SETUP_QUEUES -- setup all queue groups
669790792Sgshapiro**
669890792Sgshapiro**	Parameters:
669990792Sgshapiro**		owner -- owner of shared memory.
670090792Sgshapiro**
670190792Sgshapiro**	Returns:
670290792Sgshapiro**		none.
670390792Sgshapiro**
670490792Sgshapiro#if SM_CONF_SHM
670590792Sgshapiro**	Side Effects:
670690792Sgshapiro**		attaches shared memory.
670790792Sgshapiro#endif * SM_CONF_SHM *
670890792Sgshapiro*/
670990792Sgshapiro
671090792Sgshapirovoid
671190792Sgshapirosetup_queues(owner)
671290792Sgshapiro	bool owner;
671390792Sgshapiro{
671490792Sgshapiro	int i, qn, len;
671590792Sgshapiro	unsigned int hashval;
671694334Sgshapiro	time_t now;
671790792Sgshapiro	char basedir[MAXPATHLEN];
671890792Sgshapiro	struct stat st;
671990792Sgshapiro
672090792Sgshapiro	/*
672190792Sgshapiro	**  Determine basedir for all queue directories.
672290792Sgshapiro	**  All queue directories must be (first level) subdirectories
672390792Sgshapiro	**  of the basedir.  The basedir is the QueueDir
672490792Sgshapiro	**  without wildcards, but with trailing /
672590792Sgshapiro	*/
672690792Sgshapiro
672790792Sgshapiro	hashval = 0;
672890792Sgshapiro	errno = 0;
672990792Sgshapiro	len = sm_strlcpy(basedir, QueueDir, sizeof basedir);
673090792Sgshapiro	if (len >= sizeof basedir)
673190792Sgshapiro	{
673290792Sgshapiro		syserr("QueueDirectory: path too long: %d,  max %d",
673390792Sgshapiro			len, (int) sizeof basedir);
673490792Sgshapiro		ExitStat = EX_CONFIG;
673590792Sgshapiro		return;
673690792Sgshapiro	}
673790792Sgshapiro	SM_ASSERT(len > 0);
673890792Sgshapiro	if (basedir[len - 1] == '*')
673990792Sgshapiro	{
674090792Sgshapiro		char *cp;
674190792Sgshapiro
674290792Sgshapiro		cp = SM_LAST_DIR_DELIM(basedir);
674390792Sgshapiro		if (cp == NULL)
674490792Sgshapiro		{
674590792Sgshapiro			syserr("QueueDirectory: can not wildcard relative path \"%s\"",
674690792Sgshapiro				QueueDir);
674790792Sgshapiro			if (tTd(41, 2))
674890792Sgshapiro				sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
674990792Sgshapiro					QueueDir);
675090792Sgshapiro			ExitStat = EX_CONFIG;
675190792Sgshapiro			return;
675290792Sgshapiro		}
675390792Sgshapiro
675490792Sgshapiro		/* cut off wildcard pattern */
675590792Sgshapiro		*++cp = '\0';
675690792Sgshapiro		len = cp - basedir;
675790792Sgshapiro	}
675890792Sgshapiro	else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
675990792Sgshapiro	{
676090792Sgshapiro		/* append trailing slash since it is a directory */
676190792Sgshapiro		basedir[len] = '/';
676290792Sgshapiro		basedir[++len] = '\0';
676390792Sgshapiro	}
676490792Sgshapiro
676590792Sgshapiro	/* len counts up to the last directory delimiter */
676690792Sgshapiro	SM_ASSERT(basedir[len - 1] == '/');
676790792Sgshapiro
676890792Sgshapiro	if (chdir(basedir) < 0)
676990792Sgshapiro	{
677090792Sgshapiro		int save_errno = errno;
677190792Sgshapiro
677290792Sgshapiro		syserr("can not chdir(%s)", basedir);
677390792Sgshapiro		if (save_errno == EACCES)
677490792Sgshapiro			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
677590792Sgshapiro				"Program mode requires special privileges, e.g., root or TrustedUser.\n");
677690792Sgshapiro		if (tTd(41, 2))
677790792Sgshapiro			sm_dprintf("setup_queues: \"%s\": %s\n",
677890792Sgshapiro				   basedir, sm_errstring(errno));
677990792Sgshapiro		ExitStat = EX_CONFIG;
678090792Sgshapiro		return;
678190792Sgshapiro	}
678290792Sgshapiro#if SM_CONF_SHM
678390792Sgshapiro	hashval = hash_q(basedir, hashval);
678490792Sgshapiro#endif /* SM_CONF_SHM */
678590792Sgshapiro
678694334Sgshapiro	/* initialize for queue runs */
678794334Sgshapiro	DoQueueRun = false;
678894334Sgshapiro	now = curtime();
678994334Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
679094334Sgshapiro		Queue[i]->qg_nextrun = now;
679190792Sgshapiro
679290792Sgshapiro
679390792Sgshapiro	if (UseMSP && OpMode != MD_TEST)
679490792Sgshapiro	{
679590792Sgshapiro		long sff = SFF_CREAT;
679690792Sgshapiro
679790792Sgshapiro		if (stat(".", &st) < 0)
679890792Sgshapiro		{
679990792Sgshapiro			syserr("can not stat(%s)", basedir);
680090792Sgshapiro			if (tTd(41, 2))
680190792Sgshapiro				sm_dprintf("setup_queues: \"%s\": %s\n",
680290792Sgshapiro					   basedir, sm_errstring(errno));
680390792Sgshapiro			ExitStat = EX_CONFIG;
680490792Sgshapiro			return;
680590792Sgshapiro		}
680690792Sgshapiro		if (RunAsUid == 0)
680790792Sgshapiro			sff |= SFF_ROOTOK;
680890792Sgshapiro
680990792Sgshapiro		/*
681090792Sgshapiro		**  Check queue directory permissions.
681190792Sgshapiro		**	Can we write to a group writable queue directory?
681290792Sgshapiro		*/
681390792Sgshapiro
681490792Sgshapiro		if (bitset(S_IWGRP, QueueFileMode) &&
681590792Sgshapiro		    bitset(S_IWGRP, st.st_mode) &&
681690792Sgshapiro		    safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
681790792Sgshapiro			     QueueFileMode, NULL) != 0)
681890792Sgshapiro		{
681990792Sgshapiro			syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
682090792Sgshapiro				basedir, (int) RunAsGid, (int) st.st_gid);
682190792Sgshapiro		}
682290792Sgshapiro		if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
682390792Sgshapiro		{
682490792Sgshapiro#if _FFR_MSP_PARANOIA
682590792Sgshapiro			syserr("dangerous permissions=%o on queue directory %s",
682690792Sgshapiro				(int) st.st_mode, basedir);
682790792Sgshapiro#else /* _FFR_MSP_PARANOIA */
682890792Sgshapiro			if (LogLevel > 0)
682990792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
683090792Sgshapiro					  "dangerous permissions=%o on queue directory %s",
683190792Sgshapiro					  (int) st.st_mode, basedir);
683290792Sgshapiro#endif /* _FFR_MSP_PARANOIA */
683390792Sgshapiro		}
683490792Sgshapiro#if _FFR_MSP_PARANOIA
683590792Sgshapiro		if (NumQueue > 1)
683690792Sgshapiro			syserr("can not use multiple queues for MSP");
683790792Sgshapiro#endif /* _FFR_MSP_PARANOIA */
683890792Sgshapiro	}
683990792Sgshapiro
684090792Sgshapiro	/* initial number of queue directories */
684190792Sgshapiro	qn = 0;
684290792Sgshapiro	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
684390792Sgshapiro		qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
684490792Sgshapiro
684590792Sgshapiro#if SM_CONF_SHM
684690792Sgshapiro	init_shm(qn, owner, hashval);
684790792Sgshapiro	i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
684890792Sgshapiro	if (i == FSF_NOT_FOUND)
684990792Sgshapiro	{
685090792Sgshapiro		/*
685190792Sgshapiro		**  We didn't get the right filesystem data
685290792Sgshapiro		**  This may happen if we don't have the right shared memory.
685390792Sgshapiro		**  So let's do this without shared memory.
685490792Sgshapiro		*/
685590792Sgshapiro
685690792Sgshapiro		SM_ASSERT(!owner);
685790792Sgshapiro		cleanup_shm(false);	/* release shared memory */
685890792Sgshapiro		i = filesys_setup(false);
685990792Sgshapiro		if (i < 0)
686090792Sgshapiro			syserr("filesys_setup failed twice, result=%d", i);
686190792Sgshapiro		else if (LogLevel > 8)
686290792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
686390792Sgshapiro				  "shared memory does not contain expected data, ignored");
686490792Sgshapiro	}
686590792Sgshapiro#else /* SM_CONF_SHM */
686690792Sgshapiro	i = filesys_setup(true);
686790792Sgshapiro#endif /* SM_CONF_SHM */
686890792Sgshapiro	if (i < 0)
686990792Sgshapiro		ExitStat = EX_CONFIG;
687090792Sgshapiro}
687190792Sgshapiro
687290792Sgshapiro#if SM_CONF_SHM
687390792Sgshapiro/*
687490792Sgshapiro**  CLEANUP_SHM -- do some cleanup work for shared memory etc
687590792Sgshapiro**
687690792Sgshapiro**	Parameters:
687790792Sgshapiro**		owner -- owner of shared memory?
687890792Sgshapiro**
687990792Sgshapiro**	Returns:
688090792Sgshapiro**		none.
688190792Sgshapiro**
688290792Sgshapiro**	Side Effects:
688390792Sgshapiro**		detaches shared memory.
688490792Sgshapiro*/
688590792Sgshapiro
688690792Sgshapirovoid
688790792Sgshapirocleanup_shm(owner)
688890792Sgshapiro	bool owner;
688990792Sgshapiro{
689090792Sgshapiro	if (ShmId != SM_SHM_NO_ID)
689190792Sgshapiro	{
689290792Sgshapiro		if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
689398121Sgshapiro			sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s",
689490792Sgshapiro				  sm_errstring(errno));
689590792Sgshapiro		Pshm = NULL;
689690792Sgshapiro		ShmId = SM_SHM_NO_ID;
689790792Sgshapiro	}
689890792Sgshapiro}
689990792Sgshapiro#endif /* SM_CONF_SHM */
690090792Sgshapiro
690190792Sgshapiro/*
690290792Sgshapiro**  CLEANUP_QUEUES -- do some cleanup work for queues
690390792Sgshapiro**
690490792Sgshapiro**	Parameters:
690590792Sgshapiro**		none.
690690792Sgshapiro**
690790792Sgshapiro**	Returns:
690890792Sgshapiro**		none.
690990792Sgshapiro**
691090792Sgshapiro*/
691190792Sgshapiro
691290792Sgshapirovoid
691390792Sgshapirocleanup_queues()
691490792Sgshapiro{
691590792Sgshapiro	sync_queue_time();
691690792Sgshapiro}
691790792Sgshapiro/*
691890792Sgshapiro**  SET_DEF_QUEUEVAL -- set default values for a queue group.
691990792Sgshapiro**
692090792Sgshapiro**	Parameters:
692190792Sgshapiro**		qg -- queue group
692290792Sgshapiro**		all -- set all values (true for default group)?
692390792Sgshapiro**
692490792Sgshapiro**	Returns:
692590792Sgshapiro**		none.
692690792Sgshapiro**
692790792Sgshapiro**	Side Effects:
692890792Sgshapiro**		sets default values for the queue group.
692990792Sgshapiro*/
693090792Sgshapiro
693190792Sgshapirovoid
693290792Sgshapiroset_def_queueval(qg, all)
693390792Sgshapiro	QUEUEGRP *qg;
693490792Sgshapiro	bool all;
693590792Sgshapiro{
693690792Sgshapiro	if (bitnset(QD_DEFINED, qg->qg_flags))
693790792Sgshapiro		return;
693890792Sgshapiro	if (all)
693990792Sgshapiro		qg->qg_qdir = QueueDir;
694094334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER
694190792Sgshapiro	qg->qg_sortorder = QueueSortOrder;
694294334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */
694390792Sgshapiro	qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
694490792Sgshapiro	qg->qg_nice = NiceQueueRun;
694590792Sgshapiro}
694690792Sgshapiro/*
694790792Sgshapiro**  MAKEQUEUE -- define a new queue.
694890792Sgshapiro**
694990792Sgshapiro**	Parameters:
695090792Sgshapiro**		line -- description of queue.  This is in labeled fields.
695190792Sgshapiro**			The fields are:
695290792Sgshapiro**			   F -- the flags associated with the queue
695390792Sgshapiro**			   I -- the interval between running the queue
695490792Sgshapiro**			   J -- the maximum # of jobs in work list
695590792Sgshapiro**			   [M -- the maximum # of jobs in a queue run]
695690792Sgshapiro**			   N -- the niceness at which to run
695790792Sgshapiro**			   P -- the path to the queue
695890792Sgshapiro**			   S -- the queue sorting order
695990792Sgshapiro**			   R -- number of parallel queue runners
696090792Sgshapiro**			   r -- max recipients per envelope
696190792Sgshapiro**			The first word is the canonical name of the queue.
696290792Sgshapiro**		qdef -- this is a 'Q' definition from .cf
696390792Sgshapiro**
696490792Sgshapiro**	Returns:
696590792Sgshapiro**		none.
696690792Sgshapiro**
696790792Sgshapiro**	Side Effects:
696890792Sgshapiro**		enters the queue into the queue table.
696990792Sgshapiro*/
697090792Sgshapiro
697190792Sgshapirovoid
697290792Sgshapiromakequeue(line, qdef)
697390792Sgshapiro	char *line;
697490792Sgshapiro	bool qdef;
697590792Sgshapiro{
697690792Sgshapiro	register char *p;
697790792Sgshapiro	register QUEUEGRP *qg;
697890792Sgshapiro	register STAB *s;
697990792Sgshapiro	int i;
698090792Sgshapiro	char fcode;
698190792Sgshapiro
698290792Sgshapiro	/* allocate a queue and set up defaults */
698390792Sgshapiro	qg = (QUEUEGRP *) xalloc(sizeof *qg);
698490792Sgshapiro	memset((char *) qg, '\0', sizeof *qg);
698590792Sgshapiro
698690792Sgshapiro	if (line[0] == '\0')
698790792Sgshapiro	{
698890792Sgshapiro		syserr("name required for queue");
698990792Sgshapiro		return;
699090792Sgshapiro	}
699190792Sgshapiro
699290792Sgshapiro	/* collect the queue name */
699390792Sgshapiro	for (p = line;
699490792Sgshapiro	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
699590792Sgshapiro	     p++)
699690792Sgshapiro		continue;
699790792Sgshapiro	if (*p != '\0')
699890792Sgshapiro		*p++ = '\0';
699990792Sgshapiro	qg->qg_name = newstr(line);
700090792Sgshapiro
700190792Sgshapiro	/* set default values, can be overridden below */
700290792Sgshapiro	set_def_queueval(qg, false);
700390792Sgshapiro
700490792Sgshapiro	/* now scan through and assign info from the fields */
700590792Sgshapiro	while (*p != '\0')
700690792Sgshapiro	{
700790792Sgshapiro		auto char *delimptr;
700890792Sgshapiro
700990792Sgshapiro		while (*p != '\0' &&
701090792Sgshapiro		       (*p == ',' || (isascii(*p) && isspace(*p))))
701190792Sgshapiro			p++;
701290792Sgshapiro
701390792Sgshapiro		/* p now points to field code */
701490792Sgshapiro		fcode = *p;
701590792Sgshapiro		while (*p != '\0' && *p != '=' && *p != ',')
701690792Sgshapiro			p++;
701790792Sgshapiro		if (*p++ != '=')
701890792Sgshapiro		{
701990792Sgshapiro			syserr("queue %s: `=' expected", qg->qg_name);
702090792Sgshapiro			return;
702190792Sgshapiro		}
702290792Sgshapiro		while (isascii(*p) && isspace(*p))
702390792Sgshapiro			p++;
702490792Sgshapiro
702590792Sgshapiro		/* p now points to the field body */
702690792Sgshapiro		p = munchstring(p, &delimptr, ',');
702790792Sgshapiro
702890792Sgshapiro		/* install the field into the queue struct */
702990792Sgshapiro		switch (fcode)
703090792Sgshapiro		{
703190792Sgshapiro		  case 'P':		/* pathname */
703290792Sgshapiro			if (*p == '\0')
703390792Sgshapiro				syserr("queue %s: empty path name",
703490792Sgshapiro					qg->qg_name);
703590792Sgshapiro			else
703690792Sgshapiro				qg->qg_qdir = newstr(p);
703790792Sgshapiro			break;
703890792Sgshapiro
703990792Sgshapiro		  case 'F':		/* flags */
704090792Sgshapiro			for (; *p != '\0'; p++)
704190792Sgshapiro				if (!(isascii(*p) && isspace(*p)))
704290792Sgshapiro					setbitn(*p, qg->qg_flags);
704390792Sgshapiro			break;
704490792Sgshapiro
704590792Sgshapiro			/*
704690792Sgshapiro			**  Do we need two intervals here:
704790792Sgshapiro			**  One for persistent queue runners,
704890792Sgshapiro			**  one for "normal" queue runs?
704990792Sgshapiro			*/
705090792Sgshapiro
705190792Sgshapiro		  case 'I':	/* interval between running the queue */
705290792Sgshapiro			qg->qg_queueintvl = convtime(p, 'm');
705390792Sgshapiro			break;
705490792Sgshapiro
705590792Sgshapiro		  case 'N':		/* run niceness */
705690792Sgshapiro			qg->qg_nice = atoi(p);
705790792Sgshapiro			break;
705890792Sgshapiro
705990792Sgshapiro		  case 'R':		/* maximum # of runners for the group */
706090792Sgshapiro			i = atoi(p);
706190792Sgshapiro
706290792Sgshapiro			/* can't have more runners than allowed total */
706390792Sgshapiro			if (MaxQueueChildren > 0 && i > MaxQueueChildren)
706490792Sgshapiro			{
706590792Sgshapiro				qg->qg_maxqrun = MaxQueueChildren;
706690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
706790792Sgshapiro						     "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
706890792Sgshapiro						     qg->qg_name, i,
706990792Sgshapiro						     MaxQueueChildren);
707090792Sgshapiro			}
707190792Sgshapiro			else
707290792Sgshapiro				qg->qg_maxqrun = i;
707390792Sgshapiro			break;
707490792Sgshapiro
707590792Sgshapiro		  case 'J':		/* maximum # of jobs in work list */
707690792Sgshapiro			qg->qg_maxlist = atoi(p);
707790792Sgshapiro			break;
707890792Sgshapiro
707990792Sgshapiro		  case 'r':		/* max recipients per envelope */
708090792Sgshapiro			qg->qg_maxrcpt = atoi(p);
708190792Sgshapiro			break;
708290792Sgshapiro
708394334Sgshapiro#if _FFR_QUEUE_GROUP_SORTORDER
708490792Sgshapiro		  case 'S':		/* queue sorting order */
708590792Sgshapiro			switch (*p)
708690792Sgshapiro			{
708790792Sgshapiro			  case 'h':	/* Host first */
708890792Sgshapiro			  case 'H':
708990792Sgshapiro				qg->qg_sortorder = QSO_BYHOST;
709090792Sgshapiro				break;
709190792Sgshapiro
709290792Sgshapiro			  case 'p':	/* Priority order */
709390792Sgshapiro			  case 'P':
709490792Sgshapiro				qg->qg_sortorder = QSO_BYPRIORITY;
709590792Sgshapiro				break;
709690792Sgshapiro
709790792Sgshapiro			  case 't':	/* Submission time */
709890792Sgshapiro			  case 'T':
709990792Sgshapiro				qg->qg_sortorder = QSO_BYTIME;
710090792Sgshapiro				break;
710190792Sgshapiro
710290792Sgshapiro			  case 'f':	/* File name */
710390792Sgshapiro			  case 'F':
710490792Sgshapiro				qg->qg_sortorder = QSO_BYFILENAME;
710590792Sgshapiro				break;
710690792Sgshapiro
710790792Sgshapiro			  case 'm':	/* Modification time */
710890792Sgshapiro			  case 'M':
710994334Sgshapiro				qg->qg_sortorder = QSO_BYMODTIME;
711090792Sgshapiro				break;
711190792Sgshapiro
711294334Sgshapiro			  case 'r':	/* Random */
711394334Sgshapiro			  case 'R':
711494334Sgshapiro				qg->qg_sortorder = QSO_RANDOM;
711594334Sgshapiro				break;
711694334Sgshapiro
711794334Sgshapiro# if _FFR_RHS
711894334Sgshapiro			  case 's':	/* Shuffled host name */
711994334Sgshapiro			  case 'S':
712094334Sgshapiro				qg->qg_sortorder = QSO_BYSHUFFLE;
712194334Sgshapiro				break;
712294334Sgshapiro# endif /* _FFR_RHS */
712394334Sgshapiro
712490792Sgshapiro			  default:
712590792Sgshapiro				syserr("Invalid queue sort order \"%s\"", p);
712690792Sgshapiro			}
712790792Sgshapiro			break;
712894334Sgshapiro#endif /* _FFR_QUEUE_GROUP_SORTORDER */
712990792Sgshapiro
713090792Sgshapiro		  default:
713190792Sgshapiro			syserr("Q%s: unknown queue equate %c=",
713290792Sgshapiro			       qg->qg_name, fcode);
713390792Sgshapiro			break;
713490792Sgshapiro		}
713590792Sgshapiro
713690792Sgshapiro		p = delimptr;
713790792Sgshapiro	}
713890792Sgshapiro
713990792Sgshapiro#if !HASNICE
714090792Sgshapiro	if (qg->qg_nice != NiceQueueRun)
714190792Sgshapiro	{
714290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
714390792Sgshapiro				     "Q%s: Warning: N= set on system that doesn't support nice()\n",
714490792Sgshapiro				     qg->qg_name);
714590792Sgshapiro	}
714690792Sgshapiro#endif /* !HASNICE */
714790792Sgshapiro
714890792Sgshapiro	/* do some rationality checking */
714990792Sgshapiro	if (NumQueue >= MAXQUEUEGROUPS)
715090792Sgshapiro	{
715190792Sgshapiro		syserr("too many queue groups defined (%d max)",
715290792Sgshapiro			MAXQUEUEGROUPS);
715390792Sgshapiro		return;
715490792Sgshapiro	}
715590792Sgshapiro
715690792Sgshapiro	if (qg->qg_qdir == NULL)
715790792Sgshapiro	{
715890792Sgshapiro		if (QueueDir == NULL || *QueueDir == '\0')
715990792Sgshapiro		{
716090792Sgshapiro			syserr("QueueDir must be defined before queue groups");
716190792Sgshapiro			return;
716290792Sgshapiro		}
716390792Sgshapiro		qg->qg_qdir = newstr(QueueDir);
716490792Sgshapiro	}
716590792Sgshapiro
716690792Sgshapiro	if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
716790792Sgshapiro	{
716890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
716990792Sgshapiro				     "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
717090792Sgshapiro				     qg->qg_name, qg->qg_maxqrun, QD_FORK);
717190792Sgshapiro	}
717290792Sgshapiro
717390792Sgshapiro	/* enter the queue into the symbol table */
717490792Sgshapiro	if (tTd(37, 8))
717590792Sgshapiro		sm_syslog(LOG_INFO, NOQID,
717690792Sgshapiro			  "Adding %s to stab, path: %s", qg->qg_name,
717790792Sgshapiro			  qg->qg_qdir);
717890792Sgshapiro	s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
717990792Sgshapiro	if (s->s_quegrp != NULL)
718090792Sgshapiro	{
718190792Sgshapiro		i = s->s_quegrp->qg_index;
718290792Sgshapiro
718390792Sgshapiro		/* XXX what about the pointers inside this struct? */
718490792Sgshapiro		sm_free(s->s_quegrp); /* XXX */
718590792Sgshapiro	}
718690792Sgshapiro	else
718790792Sgshapiro		i = NumQueue++;
718890792Sgshapiro	Queue[i] = s->s_quegrp = qg;
718990792Sgshapiro	qg->qg_index = i;
719090792Sgshapiro
719190792Sgshapiro	/* set default value for max queue runners */
719290792Sgshapiro	if (qg->qg_maxqrun < 0)
719390792Sgshapiro	{
719490792Sgshapiro		if (MaxRunnersPerQueue > 0)
719590792Sgshapiro			qg->qg_maxqrun = MaxRunnersPerQueue;
719690792Sgshapiro		else
719790792Sgshapiro			qg->qg_maxqrun = 1;
719890792Sgshapiro	}
719990792Sgshapiro	if (qdef)
720090792Sgshapiro		setbitn(QD_DEFINED, qg->qg_flags);
720190792Sgshapiro}
720290792Sgshapiro#if 0
720390792Sgshapiro/*
720464562Sgshapiro**  HASHFQN -- calculate a hash value for a fully qualified host name
720564562Sgshapiro**
720664562Sgshapiro**	Arguments:
720764562Sgshapiro**		fqn -- an all lower-case host.domain string
720864562Sgshapiro**		buckets -- the number of buckets (queue directories)
720964562Sgshapiro**
721064562Sgshapiro**	Returns:
721164562Sgshapiro**		a bucket number (signed integer)
721264562Sgshapiro**		-1 on error
721364562Sgshapiro**
721464562Sgshapiro**	Contributed by Exactis.com, Inc.
721564562Sgshapiro*/
721664562Sgshapiro
721764562Sgshapiroint
721864562Sgshapirohashfqn(fqn, buckets)
721964562Sgshapiro	register char *fqn;
722064562Sgshapiro	int buckets;
722164562Sgshapiro{
722264562Sgshapiro	register char *p;
722364562Sgshapiro	register int h = 0, hash, cnt;
722464562Sgshapiro
722564562Sgshapiro	if (fqn == NULL)
722664562Sgshapiro		return -1;
722764562Sgshapiro
722864562Sgshapiro	/*
722964562Sgshapiro	**  A variation on the gdb hash
723064562Sgshapiro	**  This is the best as of Feb 19, 1996 --bcx
723164562Sgshapiro	*/
723264562Sgshapiro
723364562Sgshapiro	p = fqn;
723464562Sgshapiro	h = 0x238F13AF * strlen(p);
723564562Sgshapiro	for (cnt = 0; *p != 0; ++p, cnt++)
723664562Sgshapiro	{
723764562Sgshapiro		h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
723864562Sgshapiro	}
723964562Sgshapiro	h = (1103515243 * h + 12345) & 0x7FFFFFFF;
724064562Sgshapiro	if (buckets < 2)
724164562Sgshapiro		hash = 0;
724264562Sgshapiro	else
724364562Sgshapiro		hash = (h % buckets);
724464562Sgshapiro
724564562Sgshapiro	return hash;
724664562Sgshapiro}
724790792Sgshapiro#endif /* 0 */
724864562Sgshapiro
724990792Sgshapiro#if _FFR_QUEUEDELAY
725090792Sgshapiro/*
725164562Sgshapiro**  QUEUEDELAY -- compute queue delay time
725264562Sgshapiro**
725364562Sgshapiro**	Parameters:
725464562Sgshapiro**		e -- the envelope to queue up.
725564562Sgshapiro**
725664562Sgshapiro**	Returns:
725764562Sgshapiro**		queue delay time
725864562Sgshapiro**
725964562Sgshapiro**	Side Effects:
726064562Sgshapiro**		may change e_queuedelay
726164562Sgshapiro*/
726264562Sgshapiro
726364562Sgshapirostatic time_t
726464562Sgshapiroqueuedelay(e)
726564562Sgshapiro	ENVELOPE *e;
726664562Sgshapiro{
726764562Sgshapiro	time_t qd;
726864562Sgshapiro
726964562Sgshapiro	if (e->e_queuealg == QD_EXP)
727064562Sgshapiro	{
727164562Sgshapiro		if (e->e_queuedelay == 0)
727264562Sgshapiro			e->e_queuedelay = QueueInitDelay;
727364562Sgshapiro		else
727464562Sgshapiro		{
727564562Sgshapiro			e->e_queuedelay *= 2;
727664562Sgshapiro			if (e->e_queuedelay > QueueMaxDelay)
727764562Sgshapiro				e->e_queuedelay = QueueMaxDelay;
727864562Sgshapiro		}
727964562Sgshapiro		qd = e->e_queuedelay;
728064562Sgshapiro	}
728164562Sgshapiro	else
728264562Sgshapiro		qd = MinQueueAge;
728364562Sgshapiro	return qd;
728464562Sgshapiro}
728590792Sgshapiro#endif /* _FFR_QUEUEDELAY */
728690792Sgshapiro
728790792Sgshapiro/*
728890792Sgshapiro**  A structure for sorting Queue according to maxqrun without
728990792Sgshapiro**	screwing up Queue itself.
729090792Sgshapiro*/
729190792Sgshapiro
729290792Sgshapirostruct sortqgrp
729390792Sgshapiro{
729490792Sgshapiro	int sg_idx;		/* original index */
729590792Sgshapiro	int sg_maxqrun;		/* max queue runners */
729690792Sgshapiro};
729790792Sgshapirotypedef struct sortqgrp	SORTQGRP_T;
729890792Sgshapirostatic int cmpidx __P((const void *, const void *));
729990792Sgshapiro
730090792Sgshapirostatic int
730190792Sgshapirocmpidx(a, b)
730290792Sgshapiro	const void *a;
730390792Sgshapiro	const void *b;
730490792Sgshapiro{
730590792Sgshapiro	/* The sort is highest to lowest, so the comparison is reversed */
730690792Sgshapiro	if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
730790792Sgshapiro		return 1;
730890792Sgshapiro	else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
730990792Sgshapiro		return -1;
731090792Sgshapiro	else
731190792Sgshapiro		return 0;
731290792Sgshapiro}
731390792Sgshapiro
731490792Sgshapiro/*
731590792Sgshapiro**  MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
731690792Sgshapiro**
731790792Sgshapiro**  Take the now defined queue groups and assign them to work groups.
731890792Sgshapiro**  This is done to balance out the number of concurrently active
731990792Sgshapiro**  queue runners such that MaxQueueChildren is not exceeded. This may
732090792Sgshapiro**  result in more than one queue group per work group. In such a case
732190792Sgshapiro**  the number of running queue groups in that work group will have no
732290792Sgshapiro**  more than the work group maximum number of runners (a "fair" portion
732390792Sgshapiro**  of MaxQueueRunners). All queue groups within a work group will get a
732490792Sgshapiro**  chance at running.
732590792Sgshapiro**
732690792Sgshapiro**	Parameters:
732790792Sgshapiro**		none.
732890792Sgshapiro**
732990792Sgshapiro**	Returns:
733090792Sgshapiro**		nothing.
733190792Sgshapiro**
733290792Sgshapiro**	Side Effects:
733390792Sgshapiro**		Sets up WorkGrp structure.
733490792Sgshapiro*/
733590792Sgshapiro
733690792Sgshapirovoid
733790792Sgshapiromakeworkgroups()
733890792Sgshapiro{
733990792Sgshapiro	int i, j, total_runners = 0;
734090792Sgshapiro	int dir;
734190792Sgshapiro	SORTQGRP_T si[MAXQUEUEGROUPS + 1];
734290792Sgshapiro
734390792Sgshapiro	if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
734490792Sgshapiro	{
734590792Sgshapiro		/*
734690792Sgshapiro		**  There is only the "mqueue" queue group (a default)
734790792Sgshapiro		**  containing all of the queues. We want to provide to
734890792Sgshapiro		**  this queue group the maximum allowable queue runners.
734990792Sgshapiro		**  To match older behavior (8.10/8.11) we'll try for
735090792Sgshapiro		**  1 runner per queue capping it at MaxQueueChildren.
735190792Sgshapiro		**  So if there are N queues, then there will be N runners
735290792Sgshapiro		**  for the "mqueue" queue group (where N is kept less than
735390792Sgshapiro		**  MaxQueueChildren).
735490792Sgshapiro		*/
735590792Sgshapiro
735690792Sgshapiro		NumWorkGroups = 1;
735790792Sgshapiro		WorkGrp[0].wg_numqgrp = 1;
735890792Sgshapiro		WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
735990792Sgshapiro		WorkGrp[0].wg_qgs[0] = Queue[0];
736090792Sgshapiro		if (MaxQueueChildren > 0 &&
736190792Sgshapiro		    Queue[0]->qg_numqueues > MaxQueueChildren)
736290792Sgshapiro			WorkGrp[0].wg_runners = MaxQueueChildren;
736390792Sgshapiro		else
736490792Sgshapiro			WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
736590792Sgshapiro
736690792Sgshapiro		Queue[0]->qg_wgrp = 0;
736790792Sgshapiro
736890792Sgshapiro		/* can't have more runners than allowed total */
736990792Sgshapiro		if (MaxQueueChildren > 0 &&
737090792Sgshapiro		    Queue[0]->qg_maxqrun > MaxQueueChildren)
737190792Sgshapiro			Queue[0]->qg_maxqrun = MaxQueueChildren;
737290792Sgshapiro		WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
737390792Sgshapiro		WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
737490792Sgshapiro		return;
737590792Sgshapiro	}
737690792Sgshapiro
737790792Sgshapiro	for (i = 0; i < NumQueue; i++)
737890792Sgshapiro	{
737990792Sgshapiro		si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
738090792Sgshapiro		si[i].sg_idx = i;
738190792Sgshapiro	}
738290792Sgshapiro	qsort(si, NumQueue, sizeof(si[0]), cmpidx);
738390792Sgshapiro
738490792Sgshapiro	NumWorkGroups = 0;
738590792Sgshapiro	for (i = 0; i < NumQueue; i++)
738690792Sgshapiro	{
738790792Sgshapiro		total_runners += si[i].sg_maxqrun;
738890792Sgshapiro		if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
738990792Sgshapiro			NumWorkGroups++;
739090792Sgshapiro		else
739190792Sgshapiro			break;
739290792Sgshapiro	}
739390792Sgshapiro
739490792Sgshapiro	if (NumWorkGroups < 1)
739590792Sgshapiro		NumWorkGroups = 1; /* gotta have one at least */
739690792Sgshapiro	else if (NumWorkGroups > MAXWORKGROUPS)
739790792Sgshapiro		NumWorkGroups = MAXWORKGROUPS; /* the limit */
739890792Sgshapiro
739990792Sgshapiro	/*
740090792Sgshapiro	**  We now know the number of work groups to pack the queue groups
740190792Sgshapiro	**  into. The queue groups in 'Queue' are sorted from highest
740290792Sgshapiro	**  to lowest for the number of runners per queue group.
740390792Sgshapiro	**  We put the queue groups with the largest number of runners
740490792Sgshapiro	**  into work groups first. Then the smaller ones are fitted in
740590792Sgshapiro	**  where it looks best.
740690792Sgshapiro	*/
740790792Sgshapiro
740890792Sgshapiro	j = 0;
740990792Sgshapiro	dir = 1;
741090792Sgshapiro	for (i = 0; i < NumQueue; i++)
741190792Sgshapiro	{
741290792Sgshapiro		/* a to-and-fro packing scheme, continue from last position */
741390792Sgshapiro		if (j >= NumWorkGroups)
741490792Sgshapiro		{
741590792Sgshapiro			dir = -1;
741690792Sgshapiro			j = NumWorkGroups - 1;
741790792Sgshapiro		}
741890792Sgshapiro		else if (j < 0)
741990792Sgshapiro		{
742090792Sgshapiro			j = 0;
742190792Sgshapiro			dir = 1;
742290792Sgshapiro		}
742390792Sgshapiro
742490792Sgshapiro		if (WorkGrp[j].wg_qgs == NULL)
742594334Sgshapiro			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) *
742694334Sgshapiro							(WorkGrp[j].wg_numqgrp + 1));
742794334Sgshapiro		else
742894334Sgshapiro			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
742994334Sgshapiro							sizeof(QUEUEGRP *) *
743094334Sgshapiro							(WorkGrp[j].wg_numqgrp + 1));
743194334Sgshapiro		if (WorkGrp[j].wg_qgs == NULL)
743290792Sgshapiro		{
743394334Sgshapiro			syserr("!cannot allocate memory for work queues, need %d bytes",
743490792Sgshapiro			       (int) (sizeof(QUEUEGRP *) *
743590792Sgshapiro				      (WorkGrp[j].wg_numqgrp + 1)));
743690792Sgshapiro		}
743790792Sgshapiro
743890792Sgshapiro		WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[si[i].sg_idx];
743990792Sgshapiro		WorkGrp[j].wg_numqgrp++;
744090792Sgshapiro		WorkGrp[j].wg_runners += Queue[i]->qg_maxqrun;
744190792Sgshapiro		Queue[si[i].sg_idx]->qg_wgrp = j;
744290792Sgshapiro
744390792Sgshapiro		if (WorkGrp[j].wg_maxact == 0)
744490792Sgshapiro		{
744590792Sgshapiro			/* can't have more runners than allowed total */
744690792Sgshapiro			if (MaxQueueChildren > 0 &&
744790792Sgshapiro			    Queue[i]->qg_maxqrun > MaxQueueChildren)
744890792Sgshapiro				Queue[i]->qg_maxqrun = MaxQueueChildren;
744990792Sgshapiro			WorkGrp[j].wg_maxact = Queue[i]->qg_maxqrun;
745090792Sgshapiro		}
745190792Sgshapiro
745290792Sgshapiro		/*
745390792Sgshapiro		**  XXX: must wg_lowqintvl be the GCD?
745490792Sgshapiro		**  qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
745590792Sgshapiro		**  qg2 occur?
745690792Sgshapiro		*/
745790792Sgshapiro
745890792Sgshapiro		/* keep track of the lowest interval for a persistent runner */
745990792Sgshapiro		if (Queue[si[i].sg_idx]->qg_queueintvl > 0 &&
746090792Sgshapiro		    WorkGrp[j].wg_lowqintvl < Queue[si[i].sg_idx]->qg_queueintvl)
746190792Sgshapiro			WorkGrp[j].wg_lowqintvl = Queue[si[i].sg_idx]->qg_queueintvl;
746290792Sgshapiro		j += dir;
746390792Sgshapiro	}
746490792Sgshapiro	if (tTd(41, 9))
746590792Sgshapiro	{
746690792Sgshapiro		for (i = 0; i < NumWorkGroups; i++)
746790792Sgshapiro		{
746890792Sgshapiro			sm_dprintf("Workgroup[%d]=", i);
746990792Sgshapiro			for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
747090792Sgshapiro			{
747190792Sgshapiro				sm_dprintf("%s, ",
747290792Sgshapiro					WorkGrp[i].wg_qgs[j]->qg_name);
747390792Sgshapiro			}
747490792Sgshapiro			sm_dprintf("\n");
747590792Sgshapiro		}
747690792Sgshapiro	}
747790792Sgshapiro}
747890792Sgshapiro
747990792Sgshapiro/*
748090792Sgshapiro**  DUP_DF -- duplicate envelope data file
748190792Sgshapiro**
748290792Sgshapiro**	Copy the data file from the 'old' envelope to the 'new' envelope
748390792Sgshapiro**	in the most efficient way possible.
748490792Sgshapiro**
748590792Sgshapiro**	Create a hard link from the 'old' data file to the 'new' data file.
748690792Sgshapiro**	If the old and new queue directories are on different file systems,
748790792Sgshapiro**	then the new data file link is created in the old queue directory,
748890792Sgshapiro**	and the new queue file will contain a 'd' record pointing to the
748990792Sgshapiro**	directory containing the new data file.
749090792Sgshapiro**
749190792Sgshapiro**	Parameters:
749290792Sgshapiro**		old -- old envelope.
749390792Sgshapiro**		new -- new envelope.
749490792Sgshapiro**
749590792Sgshapiro**	Results:
749690792Sgshapiro**		Returns true on success, false on failure.
749790792Sgshapiro**
749890792Sgshapiro**	Side Effects:
749990792Sgshapiro**		On success, the new data file is created.
750090792Sgshapiro**		On fatal failure, EF_FATALERRS is set in old->e_flags.
750190792Sgshapiro*/
750290792Sgshapiro
750390792Sgshapirostatic bool	dup_df __P((ENVELOPE *, ENVELOPE *));
750490792Sgshapiro
750590792Sgshapirostatic bool
750690792Sgshapirodup_df(old, new)
750790792Sgshapiro	ENVELOPE *old;
750890792Sgshapiro	ENVELOPE *new;
750990792Sgshapiro{
751090792Sgshapiro	int ofs, nfs, r;
751190792Sgshapiro	char opath[MAXPATHLEN];
751290792Sgshapiro	char npath[MAXPATHLEN];
751390792Sgshapiro
751494334Sgshapiro	if (!bitset(EF_HAS_DF, old->e_flags))
751594334Sgshapiro	{
751694334Sgshapiro		/*
751794334Sgshapiro		**  this can happen if: SuperSafe != True
751894334Sgshapiro		**  and a bounce mail is sent that is split.
751994334Sgshapiro		*/
752094334Sgshapiro
752194334Sgshapiro		queueup(old, false, true);
752294334Sgshapiro	}
752390792Sgshapiro	SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
752490792Sgshapiro	SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
752590792Sgshapiro
752690792Sgshapiro	(void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof opath);
752790792Sgshapiro	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath);
752890792Sgshapiro
752990792Sgshapiro	if (old->e_dfp != NULL)
753090792Sgshapiro	{
753190792Sgshapiro		r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
753290792Sgshapiro		if (r < 0 && errno != EINVAL)
753390792Sgshapiro		{
753490792Sgshapiro			syserr("@can't commit %s", opath);
753590792Sgshapiro			old->e_flags |= EF_FATALERRS;
753690792Sgshapiro			return false;
753790792Sgshapiro		}
753890792Sgshapiro	}
753990792Sgshapiro
754090792Sgshapiro	/*
754190792Sgshapiro	**  Attempt to create a hard link, if we think both old and new
754290792Sgshapiro	**  are on the same file system, otherwise copy the file.
754390792Sgshapiro	**
754490792Sgshapiro	**  Don't waste time attempting a hard link unless old and new
754590792Sgshapiro	**  are on the same file system.
754690792Sgshapiro	*/
754790792Sgshapiro
754890792Sgshapiro	ofs = Queue[old->e_qgrp]->qg_qpaths[old->e_qdir].qp_fsysidx;
754990792Sgshapiro	nfs = Queue[new->e_qgrp]->qg_qpaths[new->e_qdir].qp_fsysidx;
755090792Sgshapiro	if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
755190792Sgshapiro	{
755290792Sgshapiro		if (link(opath, npath) == 0)
755390792Sgshapiro		{
755490792Sgshapiro			new->e_flags |= EF_HAS_DF;
755590792Sgshapiro			SYNC_DIR(npath, true);
755690792Sgshapiro			return true;
755790792Sgshapiro		}
755890792Sgshapiro		goto error;
755990792Sgshapiro	}
756090792Sgshapiro
756190792Sgshapiro	/*
756290792Sgshapiro	**  Can't link across queue directories, so try to create a hard
756390792Sgshapiro	**  link in the same queue directory as the old df file.
756490792Sgshapiro	**  The qf file will refer to the new df file using a 'd' record.
756590792Sgshapiro	*/
756690792Sgshapiro
756790792Sgshapiro	new->e_dfqgrp = old->e_dfqgrp;
756890792Sgshapiro	new->e_dfqdir = old->e_dfqdir;
756990792Sgshapiro	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath);
757090792Sgshapiro	if (link(opath, npath) == 0)
757190792Sgshapiro	{
757290792Sgshapiro		new->e_flags |= EF_HAS_DF;
757390792Sgshapiro		SYNC_DIR(npath, true);
757490792Sgshapiro		return true;
757590792Sgshapiro	}
757690792Sgshapiro
757790792Sgshapiro  error:
757890792Sgshapiro	if (LogLevel > 0)
757990792Sgshapiro		sm_syslog(LOG_ERR, old->e_id,
758090792Sgshapiro			  "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
758190792Sgshapiro			  opath, npath, sm_errstring(errno));
758290792Sgshapiro	return false;
758390792Sgshapiro}
758490792Sgshapiro
758590792Sgshapiro/*
758690792Sgshapiro**  SPLIT_ENV -- Allocate a new envelope based on a given envelope.
758790792Sgshapiro**
758890792Sgshapiro**	Parameters:
758990792Sgshapiro**		e -- envelope.
759090792Sgshapiro**		sendqueue -- sendqueue for new envelope.
759190792Sgshapiro**		qgrp -- index of queue group.
759290792Sgshapiro**		qdir -- queue directory.
759390792Sgshapiro**
759490792Sgshapiro**	Results:
759590792Sgshapiro**		new envelope.
759690792Sgshapiro**
759790792Sgshapiro*/
759890792Sgshapiro
759990792Sgshapirostatic ENVELOPE	*split_env __P((ENVELOPE *, ADDRESS *, int, int));
760090792Sgshapiro
760190792Sgshapirostatic ENVELOPE *
760290792Sgshapirosplit_env(e, sendqueue, qgrp, qdir)
760390792Sgshapiro	ENVELOPE *e;
760490792Sgshapiro	ADDRESS *sendqueue;
760590792Sgshapiro	int qgrp;
760690792Sgshapiro	int qdir;
760790792Sgshapiro{
760890792Sgshapiro	ENVELOPE *ee;
760990792Sgshapiro
761090792Sgshapiro	ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof *ee);
761190792Sgshapiro	STRUCTCOPY(*e, *ee);
761290792Sgshapiro	ee->e_message = NULL;	/* XXX use original message? */
761390792Sgshapiro	ee->e_id = NULL;
761490792Sgshapiro	assign_queueid(ee);
761590792Sgshapiro	ee->e_sendqueue = sendqueue;
761690792Sgshapiro	ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
761790792Sgshapiro			 |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
761890792Sgshapiro	ee->e_flags |= EF_NORECEIPT;	/* XXX really? */
761990792Sgshapiro	ee->e_from.q_state = QS_SENDER;
762090792Sgshapiro	ee->e_dfp = NULL;
762190792Sgshapiro	ee->e_lockfp = NULL;
762290792Sgshapiro	if (e->e_xfp != NULL)
762390792Sgshapiro		ee->e_xfp = sm_io_dup(e->e_xfp);
762494334Sgshapiro
762594334Sgshapiro	/* failed to dup e->e_xfp, start a new transcript */
762694334Sgshapiro	if (ee->e_xfp == NULL)
762794334Sgshapiro		openxscript(ee);
762894334Sgshapiro
762990792Sgshapiro	ee->e_qgrp = ee->e_dfqgrp = qgrp;
763090792Sgshapiro	ee->e_qdir = ee->e_dfqdir = qdir;
763190792Sgshapiro	ee->e_errormode = EM_MAIL;
763290792Sgshapiro	ee->e_statmsg = NULL;
763390792Sgshapiro#if _FFR_QUARANTINE
763490792Sgshapiro	if (e->e_quarmsg != NULL)
763590792Sgshapiro		ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
763690792Sgshapiro						  e->e_quarmsg);
763790792Sgshapiro#endif /* _FFR_QUARANTINE */
763890792Sgshapiro
763990792Sgshapiro	/*
764090792Sgshapiro	**  XXX Not sure if this copying is necessary.
764194334Sgshapiro	**  sendall() does this copying, but I (dm) don't know if that is
764290792Sgshapiro	**  because of the storage management discipline we were using
764390792Sgshapiro	**  before rpools were introduced, or if it is because these lists
764490792Sgshapiro	**  can be modified later.
764590792Sgshapiro	*/
764690792Sgshapiro
764790792Sgshapiro	ee->e_header = copyheader(e->e_header, ee->e_rpool);
764890792Sgshapiro	ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
764990792Sgshapiro
765090792Sgshapiro	return ee;
765190792Sgshapiro}
765290792Sgshapiro
765390792Sgshapiro/* return values from split functions, check also below! */
765490792Sgshapiro#define SM_SPLIT_FAIL	(0)
765590792Sgshapiro#define SM_SPLIT_NONE	(1)
765690792Sgshapiro#define SM_SPLIT_NEW(n)	(1 + (n))
765790792Sgshapiro
765890792Sgshapiro/*
765990792Sgshapiro**  SPLIT_ACROSS_QUEUE_GROUPS
766090792Sgshapiro**
766190792Sgshapiro**	This function splits an envelope across multiple queue groups
766290792Sgshapiro**	based on the queue group of each recipient.
766390792Sgshapiro**
766490792Sgshapiro**	Parameters:
766590792Sgshapiro**		e -- envelope.
766690792Sgshapiro**
766790792Sgshapiro**	Results:
766890792Sgshapiro**		SM_SPLIT_FAIL on failure
766990792Sgshapiro**		SM_SPLIT_NONE if no splitting occurred,
767090792Sgshapiro**		or 1 + the number of additional envelopes created.
767190792Sgshapiro**
767290792Sgshapiro**	Side Effects:
767390792Sgshapiro**		On success, e->e_sibling points to a list of zero or more
767490792Sgshapiro**		additional envelopes, and the associated data files exist
767590792Sgshapiro**		on disk.  But the queue files are not created.
767690792Sgshapiro**
767790792Sgshapiro**		On failure, e->e_sibling is not changed.
767890792Sgshapiro**		The order of recipients in e->e_sendqueue is permuted.
767990792Sgshapiro**		Abandoned data files for additional envelopes that failed
768090792Sgshapiro**		to be created may exist on disk.
768190792Sgshapiro*/
768290792Sgshapiro
768390792Sgshapirostatic int	q_qgrp_compare __P((const void *, const void *));
768490792Sgshapirostatic int	e_filesys_compare __P((const void *, const void *));
768590792Sgshapiro
768690792Sgshapirostatic int
768790792Sgshapiroq_qgrp_compare(p1, p2)
768890792Sgshapiro	const void *p1;
768990792Sgshapiro	const void *p2;
769090792Sgshapiro{
769190792Sgshapiro	ADDRESS **pq1 = (ADDRESS **) p1;
769290792Sgshapiro	ADDRESS **pq2 = (ADDRESS **) p2;
769390792Sgshapiro
769490792Sgshapiro	return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
769590792Sgshapiro}
769690792Sgshapiro
769790792Sgshapirostatic int
769890792Sgshapiroe_filesys_compare(p1, p2)
769990792Sgshapiro	const void *p1;
770090792Sgshapiro	const void *p2;
770190792Sgshapiro{
770290792Sgshapiro	ENVELOPE **pe1 = (ENVELOPE **) p1;
770390792Sgshapiro	ENVELOPE **pe2 = (ENVELOPE **) p2;
770490792Sgshapiro	int fs1, fs2;
770590792Sgshapiro
770690792Sgshapiro	fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
770790792Sgshapiro	fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
770890792Sgshapiro	if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
770990792Sgshapiro		return -1;
771090792Sgshapiro	if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
771190792Sgshapiro		return 1;
771290792Sgshapiro	return 0;
771390792Sgshapiro}
771490792Sgshapiro
771590792Sgshapirostatic int
771690792Sgshapirosplit_across_queue_groups(e)
771790792Sgshapiro	ENVELOPE *e;
771890792Sgshapiro{
771990792Sgshapiro	int naddrs, nsplits, i;
7720102528Sgshapiro	bool changed;
772190792Sgshapiro	char **pvp;
772290792Sgshapiro	ADDRESS *q, **addrs;
772390792Sgshapiro	ENVELOPE *ee, *es;
772490792Sgshapiro	ENVELOPE *splits[MAXQUEUEGROUPS];
772590792Sgshapiro	char pvpbuf[PSBUFSIZE];
772690792Sgshapiro
772790792Sgshapiro	SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
772890792Sgshapiro
772990792Sgshapiro	/* Count addresses and assign queue groups. */
773090792Sgshapiro	naddrs = 0;
7731102528Sgshapiro	changed = false;
773290792Sgshapiro	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
773390792Sgshapiro	{
773490792Sgshapiro		if (QS_IS_DEAD(q->q_state))
773590792Sgshapiro			continue;
773690792Sgshapiro		++naddrs;
773790792Sgshapiro
773890792Sgshapiro		/* bad addresses and those already sent stay put */
773990792Sgshapiro		if (QS_IS_BADADDR(q->q_state) ||
774090792Sgshapiro		    QS_IS_SENT(q->q_state))
774190792Sgshapiro			q->q_qgrp = e->e_qgrp;
774290792Sgshapiro		else if (!ISVALIDQGRP(q->q_qgrp))
774390792Sgshapiro		{
774490792Sgshapiro			/* call ruleset which should return a queue group */
774590792Sgshapiro			i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
774690792Sgshapiro				  pvpbuf, sizeof(pvpbuf));
774790792Sgshapiro			if (i == EX_OK &&
774890792Sgshapiro			    pvp != NULL && pvp[0] != NULL &&
774990792Sgshapiro			    (pvp[0][0] & 0377) == CANONNET &&
775090792Sgshapiro			    pvp[1] != NULL && pvp[1][0] != '\0')
775190792Sgshapiro			{
775290792Sgshapiro				i = name2qid(pvp[1]);
775390792Sgshapiro				if (ISVALIDQGRP(i))
775490792Sgshapiro				{
775590792Sgshapiro					q->q_qgrp = i;
7756102528Sgshapiro					changed = true;
775790792Sgshapiro					if (tTd(20, 4))
775890792Sgshapiro						sm_syslog(LOG_INFO, NOQID,
775990792Sgshapiro							"queue group name %s -> %d",
776090792Sgshapiro							pvp[1], i);
776190792Sgshapiro					continue;
776290792Sgshapiro				}
776390792Sgshapiro				else if (LogLevel > 10)
776490792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
776590792Sgshapiro						"can't find queue group name %s, selection ignored",
776690792Sgshapiro						pvp[1]);
776790792Sgshapiro			}
776890792Sgshapiro			if (q->q_mailer != NULL &&
776990792Sgshapiro			    ISVALIDQGRP(q->q_mailer->m_qgrp))
7770102528Sgshapiro			{
7771102528Sgshapiro				changed = true;
777290792Sgshapiro				q->q_qgrp = q->q_mailer->m_qgrp;
7773102528Sgshapiro			}
777494334Sgshapiro			else if (ISVALIDQGRP(e->e_qgrp))
777594334Sgshapiro				q->q_qgrp = e->e_qgrp;
777690792Sgshapiro			else
777790792Sgshapiro				q->q_qgrp = 0;
777890792Sgshapiro		}
777990792Sgshapiro	}
778090792Sgshapiro
778190792Sgshapiro	/* only one address? nothing to split. */
7782102528Sgshapiro	if (naddrs <= 1 && !changed)
778390792Sgshapiro		return SM_SPLIT_NONE;
778490792Sgshapiro
778590792Sgshapiro	/* sort the addresses by queue group */
778690792Sgshapiro	addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
778790792Sgshapiro	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
778890792Sgshapiro	{
778990792Sgshapiro		if (QS_IS_DEAD(q->q_state))
779090792Sgshapiro			continue;
779190792Sgshapiro		addrs[i++] = q;
779290792Sgshapiro	}
779390792Sgshapiro	qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
779490792Sgshapiro
779590792Sgshapiro	/* split into multiple envelopes, by queue group */
779690792Sgshapiro	nsplits = 0;
779790792Sgshapiro	es = NULL;
779890792Sgshapiro	e->e_sendqueue = NULL;
779990792Sgshapiro	for (i = 0; i < naddrs; ++i)
780090792Sgshapiro	{
780190792Sgshapiro		if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
780290792Sgshapiro			addrs[i]->q_next = NULL;
780390792Sgshapiro		else
780490792Sgshapiro			addrs[i]->q_next = addrs[i + 1];
780590792Sgshapiro
780690792Sgshapiro		/* same queue group as original envelope? */
780790792Sgshapiro		if (addrs[i]->q_qgrp == e->e_qgrp)
780890792Sgshapiro		{
780990792Sgshapiro			if (e->e_sendqueue == NULL)
781090792Sgshapiro				e->e_sendqueue = addrs[i];
781190792Sgshapiro			continue;
781290792Sgshapiro		}
781390792Sgshapiro
781490792Sgshapiro		/* different queue group than original envelope */
781590792Sgshapiro		if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
781690792Sgshapiro		{
781790792Sgshapiro			ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
781890792Sgshapiro			es = ee;
781990792Sgshapiro			splits[nsplits++] = ee;
782090792Sgshapiro		}
782190792Sgshapiro	}
782290792Sgshapiro
782390792Sgshapiro	/* no splits? return right now. */
782490792Sgshapiro	if (nsplits <= 0)
782590792Sgshapiro		return SM_SPLIT_NONE;
782690792Sgshapiro
782790792Sgshapiro	/* assign a queue directory to each additional envelope */
782890792Sgshapiro	for (i = 0; i < nsplits; ++i)
782990792Sgshapiro	{
783090792Sgshapiro		es = splits[i];
783190792Sgshapiro#if 0
783290792Sgshapiro		es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
783390792Sgshapiro#endif /* 0 */
783490792Sgshapiro		if (!setnewqueue(es))
783590792Sgshapiro			goto failure;
783690792Sgshapiro	}
783790792Sgshapiro
783890792Sgshapiro	/* sort the additional envelopes by queue file system */
783990792Sgshapiro	qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
784090792Sgshapiro
784190792Sgshapiro	/* create data files for each additional envelope */
784290792Sgshapiro	if (!dup_df(e, splits[0]))
784390792Sgshapiro	{
784490792Sgshapiro		i = 0;
784590792Sgshapiro		goto failure;
784690792Sgshapiro	}
784790792Sgshapiro	for (i = 1; i < nsplits; ++i)
784890792Sgshapiro	{
784990792Sgshapiro		/* copy or link to the previous data file */
785090792Sgshapiro		if (!dup_df(splits[i - 1], splits[i]))
785190792Sgshapiro			goto failure;
785290792Sgshapiro	}
785390792Sgshapiro
785490792Sgshapiro	/* success: prepend the new envelopes to the e->e_sibling list */
785590792Sgshapiro	for (i = 0; i < nsplits; ++i)
785690792Sgshapiro	{
785790792Sgshapiro		es = splits[i];
785890792Sgshapiro		es->e_sibling = e->e_sibling;
785990792Sgshapiro		e->e_sibling = es;
786090792Sgshapiro	}
786190792Sgshapiro	return SM_SPLIT_NEW(nsplits);
786290792Sgshapiro
786390792Sgshapiro	/* failure: clean up */
786490792Sgshapiro  failure:
786590792Sgshapiro	if (i > 0)
786690792Sgshapiro	{
786790792Sgshapiro		int j;
786890792Sgshapiro
786990792Sgshapiro		for (j = 0; j < i; j++)
787090792Sgshapiro			(void) unlink(queuename(splits[j], DATAFL_LETTER));
787190792Sgshapiro	}
787290792Sgshapiro	e->e_sendqueue = addrs[0];
787390792Sgshapiro	for (i = 0; i < naddrs - 1; ++i)
787490792Sgshapiro		addrs[i]->q_next = addrs[i + 1];
787590792Sgshapiro	addrs[naddrs - 1]->q_next = NULL;
787690792Sgshapiro	return SM_SPLIT_FAIL;
787790792Sgshapiro}
787890792Sgshapiro
787990792Sgshapiro/*
788090792Sgshapiro**  SPLIT_WITHIN_QUEUE
788190792Sgshapiro**
788290792Sgshapiro**	Split an envelope with multiple recipients into several
788390792Sgshapiro**	envelopes within the same queue directory, if the number of
788490792Sgshapiro**	recipients exceeds the limit for the queue group.
788590792Sgshapiro**
788690792Sgshapiro**	Parameters:
788790792Sgshapiro**		e -- envelope.
788890792Sgshapiro**
788990792Sgshapiro**	Results:
789090792Sgshapiro**		SM_SPLIT_FAIL on failure
789190792Sgshapiro**		SM_SPLIT_NONE if no splitting occurred,
789290792Sgshapiro**		or 1 + the number of additional envelopes created.
789390792Sgshapiro*/
789490792Sgshapiro
789590792Sgshapiro#define SPLIT_LOG_LEVEL	8
789690792Sgshapiro
789790792Sgshapirostatic int	split_within_queue __P((ENVELOPE *));
789890792Sgshapiro
789990792Sgshapirostatic int
790090792Sgshapirosplit_within_queue(e)
790190792Sgshapiro	ENVELOPE *e;
790290792Sgshapiro{
790390792Sgshapiro	int maxrcpt, nrcpt, ndead, nsplit, i;
790490792Sgshapiro	int j, l;
790590792Sgshapiro	char *lsplits;
790690792Sgshapiro	ADDRESS *q, **addrs;
790790792Sgshapiro	ENVELOPE *ee, *firstsibling;
790890792Sgshapiro
790990792Sgshapiro	if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
791090792Sgshapiro		return SM_SPLIT_NONE;
791190792Sgshapiro
791290792Sgshapiro	/* don't bother if there is no recipient limit */
791390792Sgshapiro	maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
791490792Sgshapiro	if (maxrcpt <= 0)
791590792Sgshapiro		return SM_SPLIT_NONE;
791690792Sgshapiro
791790792Sgshapiro	/* count recipients */
791890792Sgshapiro	nrcpt = 0;
791990792Sgshapiro	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
792090792Sgshapiro	{
792190792Sgshapiro		if (QS_IS_DEAD(q->q_state))
792290792Sgshapiro			continue;
792390792Sgshapiro		++nrcpt;
792490792Sgshapiro	}
792590792Sgshapiro	if (nrcpt <= maxrcpt)
792690792Sgshapiro		return SM_SPLIT_NONE;
792790792Sgshapiro
792890792Sgshapiro	/*
792990792Sgshapiro	**  Preserve the recipient list
793090792Sgshapiro	**  so that we can restore it in case of error.
793190792Sgshapiro	**  (But we discard dead addresses.)
793290792Sgshapiro	*/
793390792Sgshapiro
793490792Sgshapiro	addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
793590792Sgshapiro	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
793690792Sgshapiro	{
793790792Sgshapiro		if (QS_IS_DEAD(q->q_state))
793890792Sgshapiro			continue;
793990792Sgshapiro		addrs[i++] = q;
794090792Sgshapiro	}
794190792Sgshapiro
794290792Sgshapiro	/*
794390792Sgshapiro	**  Partition the recipient list so that bad and sent addresses
794490792Sgshapiro	**  come first. These will go with the original envelope, and
794590792Sgshapiro	**  do not count towards the maxrcpt limit.
794690792Sgshapiro	**  addrs[] does not contain QS_IS_DEAD() addresses.
794790792Sgshapiro	*/
794890792Sgshapiro
794990792Sgshapiro	ndead = 0;
795090792Sgshapiro	for (i = 0; i < nrcpt; ++i)
795190792Sgshapiro	{
795290792Sgshapiro		if (QS_IS_BADADDR(addrs[i]->q_state) ||
795390792Sgshapiro		    QS_IS_SENT(addrs[i]->q_state) ||
795490792Sgshapiro		    QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
795590792Sgshapiro		{
795690792Sgshapiro			if (i > ndead)
795790792Sgshapiro			{
795890792Sgshapiro				ADDRESS *tmp = addrs[i];
795990792Sgshapiro
796090792Sgshapiro				addrs[i] = addrs[ndead];
796190792Sgshapiro				addrs[ndead] = tmp;
796290792Sgshapiro			}
796390792Sgshapiro			++ndead;
796490792Sgshapiro		}
796590792Sgshapiro	}
796690792Sgshapiro
796790792Sgshapiro	/* Check if no splitting required. */
796890792Sgshapiro	if (nrcpt - ndead <= maxrcpt)
796990792Sgshapiro		return SM_SPLIT_NONE;
797090792Sgshapiro
797190792Sgshapiro	/* fix links */
797290792Sgshapiro	for (i = 0; i < nrcpt - 1; ++i)
797390792Sgshapiro		addrs[i]->q_next = addrs[i + 1];
797490792Sgshapiro	addrs[nrcpt - 1]->q_next = NULL;
797590792Sgshapiro	e->e_sendqueue = addrs[0];
797690792Sgshapiro
797790792Sgshapiro	/* prepare buffer for logging */
797890792Sgshapiro	if (LogLevel > SPLIT_LOG_LEVEL)
797990792Sgshapiro	{
798090792Sgshapiro		l = MAXLINE;
798190792Sgshapiro		lsplits = sm_malloc(l);
798290792Sgshapiro		if (lsplits != NULL)
798390792Sgshapiro			*lsplits = '\0';
798490792Sgshapiro		j = 0;
798590792Sgshapiro	}
798690792Sgshapiro	else
798790792Sgshapiro	{
798890792Sgshapiro		/* get rid of stupid compiler warnings */
798990792Sgshapiro		lsplits = NULL;
799090792Sgshapiro		j = l = 0;
799190792Sgshapiro	}
799290792Sgshapiro
799390792Sgshapiro	/* split the envelope */
799490792Sgshapiro	firstsibling = e->e_sibling;
799590792Sgshapiro	i = maxrcpt + ndead;
799690792Sgshapiro	nsplit = 0;
799790792Sgshapiro	for (;;)
799890792Sgshapiro	{
799990792Sgshapiro		addrs[i - 1]->q_next = NULL;
800090792Sgshapiro		ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
800190792Sgshapiro		if (!dup_df(e, ee))
800290792Sgshapiro		{
800390792Sgshapiro
800490792Sgshapiro			ee = firstsibling;
800590792Sgshapiro			while (ee != NULL)
800690792Sgshapiro			{
800790792Sgshapiro				(void) unlink(queuename(ee, DATAFL_LETTER));
800890792Sgshapiro				ee = ee->e_sibling;
800990792Sgshapiro			}
801090792Sgshapiro
801190792Sgshapiro			/* Error.  Restore e's sibling & recipient lists. */
801290792Sgshapiro			e->e_sibling = firstsibling;
801390792Sgshapiro			for (i = 0; i < nrcpt - 1; ++i)
801490792Sgshapiro				addrs[i]->q_next = addrs[i + 1];
801590792Sgshapiro			return SM_SPLIT_FAIL;
801690792Sgshapiro		}
801790792Sgshapiro
801890792Sgshapiro		/* prepend the new envelope to e->e_sibling */
801990792Sgshapiro		ee->e_sibling = e->e_sibling;
802090792Sgshapiro		e->e_sibling = ee;
802190792Sgshapiro		++nsplit;
802290792Sgshapiro		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
802390792Sgshapiro		{
802490792Sgshapiro			if (j >= l - strlen(ee->e_id) - 3)
802590792Sgshapiro			{
802690792Sgshapiro				char *p;
802790792Sgshapiro
802890792Sgshapiro				l += MAXLINE;
802990792Sgshapiro				p = sm_realloc(lsplits, l);
803090792Sgshapiro				if (p == NULL)
803190792Sgshapiro				{
803290792Sgshapiro					/* let's try to get this done */
803390792Sgshapiro					sm_free(lsplits);
803490792Sgshapiro					lsplits = NULL;
803590792Sgshapiro				}
803690792Sgshapiro				else
803790792Sgshapiro					lsplits = p;
803890792Sgshapiro			}
803990792Sgshapiro			if (lsplits != NULL)
804090792Sgshapiro			{
804190792Sgshapiro				if (j == 0)
804290792Sgshapiro					j += sm_strlcat(lsplits + j,
804390792Sgshapiro							ee->e_id,
804490792Sgshapiro							l - j);
804590792Sgshapiro				else
804690792Sgshapiro					j += sm_strlcat2(lsplits + j,
804790792Sgshapiro							 "; ",
804890792Sgshapiro							 ee->e_id,
804990792Sgshapiro							 l - j);
805090792Sgshapiro				SM_ASSERT(j < l);
805190792Sgshapiro			}
805290792Sgshapiro		}
805390792Sgshapiro		if (nrcpt - i <= maxrcpt)
805490792Sgshapiro			break;
805590792Sgshapiro		i += maxrcpt;
805690792Sgshapiro	}
805790792Sgshapiro	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && nsplit > 0)
805890792Sgshapiro	{
805990792Sgshapiro		sm_syslog(LOG_NOTICE, e->e_id,
806090792Sgshapiro			  "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
806190792Sgshapiro			  maxrcpt, nrcpt - ndead, nsplit,
806290792Sgshapiro			  nsplit > 1 ? "s" : "", lsplits);
806390792Sgshapiro		sm_free(lsplits);
806490792Sgshapiro	}
806590792Sgshapiro	return SM_SPLIT_NEW(nsplit);
806690792Sgshapiro}
806790792Sgshapiro/*
806890792Sgshapiro**  SPLIT_BY_RECIPIENT
806990792Sgshapiro**
807090792Sgshapiro**	Split an envelope with multiple recipients into multiple
807190792Sgshapiro**	envelopes as required by the sendmail configuration.
807290792Sgshapiro**
807390792Sgshapiro**	Parameters:
807490792Sgshapiro**		e -- envelope.
807590792Sgshapiro**
807690792Sgshapiro**	Results:
807790792Sgshapiro**		Returns true on success, false on failure.
807890792Sgshapiro**
807990792Sgshapiro**	Side Effects:
808090792Sgshapiro**		see split_across_queue_groups(), split_within_queue(e)
808190792Sgshapiro*/
808290792Sgshapiro
808390792Sgshapirobool
808490792Sgshapirosplit_by_recipient(e)
808590792Sgshapiro	ENVELOPE *e;
808690792Sgshapiro{
808790792Sgshapiro	int split, n, i, j, l;
808890792Sgshapiro	char *lsplits;
808990792Sgshapiro	ENVELOPE *ee, *next, *firstsibling;
809090792Sgshapiro
809190792Sgshapiro	if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
809290792Sgshapiro	    bitset(EF_SPLIT, e->e_flags))
809390792Sgshapiro		return true;
809490792Sgshapiro	n = split_across_queue_groups(e);
809590792Sgshapiro	if (n == SM_SPLIT_FAIL)
809690792Sgshapiro		return false;
809790792Sgshapiro	firstsibling = ee = e->e_sibling;
809890792Sgshapiro	if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
809990792Sgshapiro	{
810090792Sgshapiro		l = MAXLINE;
810190792Sgshapiro		lsplits = sm_malloc(l);
810290792Sgshapiro		if (lsplits != NULL)
810390792Sgshapiro			*lsplits = '\0';
810490792Sgshapiro		j = 0;
810590792Sgshapiro	}
810690792Sgshapiro	else
810790792Sgshapiro	{
810890792Sgshapiro		/* get rid of stupid compiler warnings */
810990792Sgshapiro		lsplits = NULL;
811090792Sgshapiro		j = l = 0;
811190792Sgshapiro	}
811290792Sgshapiro	for (i = 1; i < n; ++i)
811390792Sgshapiro	{
811490792Sgshapiro		next = ee->e_sibling;
811590792Sgshapiro		if (split_within_queue(ee) == SM_SPLIT_FAIL)
811690792Sgshapiro		{
811790792Sgshapiro			e->e_sibling = firstsibling;
811890792Sgshapiro			return false;
811990792Sgshapiro		}
812090792Sgshapiro		ee->e_flags |= EF_SPLIT;
812190792Sgshapiro		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
812290792Sgshapiro		{
812390792Sgshapiro			if (j >= l - strlen(ee->e_id) - 3)
812490792Sgshapiro			{
812590792Sgshapiro				char *p;
812690792Sgshapiro
812790792Sgshapiro				l += MAXLINE;
812890792Sgshapiro				p = sm_realloc(lsplits, l);
812990792Sgshapiro				if (p == NULL)
813090792Sgshapiro				{
813190792Sgshapiro					/* let's try to get this done */
813290792Sgshapiro					sm_free(lsplits);
813390792Sgshapiro					lsplits = NULL;
813490792Sgshapiro				}
813590792Sgshapiro				else
813690792Sgshapiro					lsplits = p;
813790792Sgshapiro			}
813890792Sgshapiro			if (lsplits != NULL)
813990792Sgshapiro			{
814090792Sgshapiro				if (j == 0)
814190792Sgshapiro					j += sm_strlcat(lsplits + j,
814290792Sgshapiro							ee->e_id, l - j);
814390792Sgshapiro				else
814490792Sgshapiro					j += sm_strlcat2(lsplits + j, "; ",
814590792Sgshapiro							 ee->e_id, l - j);
814690792Sgshapiro				SM_ASSERT(j < l);
814790792Sgshapiro			}
814890792Sgshapiro		}
814990792Sgshapiro		ee = next;
815090792Sgshapiro	}
815190792Sgshapiro	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
815290792Sgshapiro	{
815390792Sgshapiro		sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
815490792Sgshapiro			  n - 1, n > 2 ? "s" : "", lsplits);
815590792Sgshapiro		sm_free(lsplits);
815690792Sgshapiro	}
815790792Sgshapiro	split = split_within_queue(e) != SM_SPLIT_FAIL;
815890792Sgshapiro	if (split)
815990792Sgshapiro		e->e_flags |= EF_SPLIT;
816090792Sgshapiro	return split;
816190792Sgshapiro}
816290792Sgshapiro
816390792Sgshapiro#if _FFR_QUARANTINE
816490792Sgshapiro/*
816590792Sgshapiro**  QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
816690792Sgshapiro**
816790792Sgshapiro**	Add/remove quarantine reason and requeue appropriately.
816890792Sgshapiro**
816990792Sgshapiro**	Parameters:
817090792Sgshapiro**		qgrp -- queue group for the item
817190792Sgshapiro**		qdir -- queue directory in the given queue group
817290792Sgshapiro**		e -- envelope information for the item
817390792Sgshapiro**		reason -- quarantine reason, NULL means unquarantine.
817490792Sgshapiro**
817590792Sgshapiro**	Results:
817690792Sgshapiro**		true if item changed, false otherwise
817790792Sgshapiro**
817890792Sgshapiro**	Side Effects:
817990792Sgshapiro**		Changes quarantine tag in queue file and renames it.
818090792Sgshapiro*/
818190792Sgshapiro
818290792Sgshapirostatic bool
818390792Sgshapiroquarantine_queue_item(qgrp, qdir, e, reason)
818490792Sgshapiro	int qgrp;
818590792Sgshapiro	int qdir;
818690792Sgshapiro	ENVELOPE *e;
818790792Sgshapiro	char *reason;
818890792Sgshapiro{
818990792Sgshapiro	bool dirty = false;
819090792Sgshapiro	bool failing = false;
819190792Sgshapiro	bool foundq = false;
819290792Sgshapiro	bool finished = false;
819390792Sgshapiro	int fd;
819490792Sgshapiro	int flags;
819590792Sgshapiro	int oldtype;
819690792Sgshapiro	int newtype;
819790792Sgshapiro	int save_errno;
819890792Sgshapiro	MODE_T oldumask = 0;
819990792Sgshapiro	SM_FILE_T *oldqfp, *tempqfp;
820090792Sgshapiro	char *bp;
820190792Sgshapiro	char oldqf[MAXPATHLEN];
820290792Sgshapiro	char tempqf[MAXPATHLEN];
820390792Sgshapiro	char newqf[MAXPATHLEN];
820490792Sgshapiro	char buf[MAXLINE];
820590792Sgshapiro
820690792Sgshapiro	oldtype = queue_letter(e, ANYQFL_LETTER);
820790792Sgshapiro	(void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof oldqf);
820890792Sgshapiro	(void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof tempqf);
820990792Sgshapiro
821090792Sgshapiro	/*
821190792Sgshapiro	**  Instead of duplicating all the open
821290792Sgshapiro	**  and lock code here, tell readqf() to
821390792Sgshapiro	**  do that work and return the open
821490792Sgshapiro	**  file pointer in e_lockfp.  Note that
821590792Sgshapiro	**  we must release the locks properly when
821690792Sgshapiro	**  we are done.
821790792Sgshapiro	*/
821890792Sgshapiro
821990792Sgshapiro	if (!readqf(e, true))
822090792Sgshapiro	{
822190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
822290792Sgshapiro				     "Skipping %s\n", qid_printname(e));
822390792Sgshapiro		return false;
822490792Sgshapiro	}
822590792Sgshapiro	oldqfp = e->e_lockfp;
822690792Sgshapiro
822790792Sgshapiro	/* open the new queue file */
822890792Sgshapiro	flags = O_CREAT|O_WRONLY|O_EXCL;
822990792Sgshapiro	if (bitset(S_IWGRP, QueueFileMode))
823090792Sgshapiro		oldumask = umask(002);
823190792Sgshapiro	fd = open(tempqf, flags, QueueFileMode);
823290792Sgshapiro	if (bitset(S_IWGRP, QueueFileMode))
823390792Sgshapiro		(void) umask(oldumask);
823490792Sgshapiro	RELEASE_QUEUE;
823590792Sgshapiro
823690792Sgshapiro	if (fd < 0)
823790792Sgshapiro	{
823890792Sgshapiro		save_errno = errno;
823990792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
824090792Sgshapiro				     "Skipping %s: Could not open %s: %s\n",
824190792Sgshapiro				     qid_printname(e), tempqf,
824290792Sgshapiro				     sm_errstring(save_errno));
824390792Sgshapiro		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
824490792Sgshapiro		return false;
824590792Sgshapiro	}
824690792Sgshapiro	if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
824790792Sgshapiro	{
824890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
824990792Sgshapiro				     "Skipping %s: Could not lock %s\n",
825090792Sgshapiro				     qid_printname(e), tempqf);
825190792Sgshapiro		(void) close(fd);
825290792Sgshapiro		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
825390792Sgshapiro		return false;
825490792Sgshapiro	}
825590792Sgshapiro
825690792Sgshapiro	tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
825790792Sgshapiro			     SM_IO_WRONLY, NULL);
825890792Sgshapiro	if (tempqfp == NULL)
825990792Sgshapiro	{
826090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
826190792Sgshapiro				     "Skipping %s: Could not lock %s\n",
826290792Sgshapiro				     qid_printname(e), tempqf);
826390792Sgshapiro		(void) close(fd);
826490792Sgshapiro		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
826590792Sgshapiro		return false;
826690792Sgshapiro	}
826790792Sgshapiro
826890792Sgshapiro	/* Copy the data over, changing the quarantine reason */
826990792Sgshapiro	while ((bp = fgetfolded(buf, sizeof buf, oldqfp)) != NULL)
827090792Sgshapiro	{
827190792Sgshapiro		if (tTd(40, 4))
827290792Sgshapiro			sm_dprintf("+++++ %s\n", bp);
827390792Sgshapiro		switch (bp[0])
827490792Sgshapiro		{
827590792Sgshapiro		  case 'q':		/* quarantine reason */
827690792Sgshapiro			foundq = true;
827790792Sgshapiro			if (reason == NULL)
827890792Sgshapiro			{
827990792Sgshapiro				if (Verbose)
828090792Sgshapiro				{
828190792Sgshapiro					(void) sm_io_fprintf(smioout,
828290792Sgshapiro							     SM_TIME_DEFAULT,
828390792Sgshapiro							     "%s: Removed quarantine of \"%s\"\n",
828490792Sgshapiro							     e->e_id, &bp[1]);
828590792Sgshapiro				}
828690792Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "unquarantine");
828790792Sgshapiro				dirty = true;
828890792Sgshapiro				continue;
828990792Sgshapiro			}
829090792Sgshapiro			else if (strcmp(reason, &bp[1]) == 0)
829190792Sgshapiro			{
829290792Sgshapiro				if (Verbose)
829390792Sgshapiro				{
829490792Sgshapiro					(void) sm_io_fprintf(smioout,
829590792Sgshapiro							     SM_TIME_DEFAULT,
829690792Sgshapiro							     "%s: Already quarantined with \"%s\"\n",
829790792Sgshapiro							     e->e_id, reason);
829890792Sgshapiro				}
829990792Sgshapiro				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
830090792Sgshapiro						     "q%s\n", reason);
830190792Sgshapiro			}
830290792Sgshapiro			else
830390792Sgshapiro			{
830490792Sgshapiro				if (Verbose)
830590792Sgshapiro				{
830690792Sgshapiro					(void) sm_io_fprintf(smioout,
830790792Sgshapiro							     SM_TIME_DEFAULT,
830890792Sgshapiro							     "%s: Quarantine changed from \"%s\" to \"%s\"\n",
830990792Sgshapiro							     e->e_id, &bp[1],
831090792Sgshapiro							     reason);
831190792Sgshapiro				}
831290792Sgshapiro				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
831390792Sgshapiro						     "q%s\n", reason);
831490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
831590792Sgshapiro					  reason);
831690792Sgshapiro				dirty = true;
831790792Sgshapiro			}
831890792Sgshapiro			break;
831990792Sgshapiro
832098121Sgshapiro		  case 'S':
832190792Sgshapiro			/*
832290792Sgshapiro			**  If we are quarantining an unquarantined item,
832390792Sgshapiro			**  need to put in a new 'q' line before it's
832490792Sgshapiro			**  too late.
832590792Sgshapiro			*/
832690792Sgshapiro
832790792Sgshapiro			if (!foundq && reason != NULL)
832890792Sgshapiro			{
832990792Sgshapiro				if (Verbose)
833090792Sgshapiro				{
833190792Sgshapiro					(void) sm_io_fprintf(smioout,
833290792Sgshapiro							     SM_TIME_DEFAULT,
833390792Sgshapiro							     "%s: Quarantined with \"%s\"\n",
833490792Sgshapiro							     e->e_id, reason);
833590792Sgshapiro				}
833690792Sgshapiro				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
833790792Sgshapiro						     "q%s\n", reason);
833890792Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
833990792Sgshapiro					  reason);
834090792Sgshapiro				foundq = true;
834190792Sgshapiro				dirty = true;
834290792Sgshapiro			}
834390792Sgshapiro
834490792Sgshapiro			/* Copy the line to the new file */
834590792Sgshapiro			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
834690792Sgshapiro					     "%s\n", bp);
834790792Sgshapiro			break;
834890792Sgshapiro
834990792Sgshapiro		  case '.':
835090792Sgshapiro			finished = true;
835190792Sgshapiro			/* FALLTHROUGH */
835290792Sgshapiro
835390792Sgshapiro		  default:
835490792Sgshapiro			/* Copy the line to the new file */
835590792Sgshapiro			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
835690792Sgshapiro					     "%s\n", bp);
835790792Sgshapiro			break;
835890792Sgshapiro		}
835990792Sgshapiro	}
836090792Sgshapiro
836190792Sgshapiro	/* Make sure we read the whole old file */
836290792Sgshapiro	errno = sm_io_error(tempqfp);
836390792Sgshapiro	if (errno != 0 && errno != SM_IO_EOF)
836490792Sgshapiro	{
836590792Sgshapiro		save_errno = errno;
836690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
836790792Sgshapiro				     "Skipping %s: Error reading %s: %s\n",
836890792Sgshapiro				     qid_printname(e), oldqf,
836990792Sgshapiro				     sm_errstring(save_errno));
837090792Sgshapiro		failing = true;
837190792Sgshapiro	}
837290792Sgshapiro
837390792Sgshapiro	if (!failing && !finished)
837490792Sgshapiro	{
837590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
837690792Sgshapiro				     "Skipping %s: Incomplete file: %s\n",
837790792Sgshapiro				     qid_printname(e), oldqf);
837890792Sgshapiro		failing = true;
837990792Sgshapiro	}
838090792Sgshapiro
838190792Sgshapiro	/* Check if we actually changed anything or we can just bail now */
838290792Sgshapiro	if (!dirty)
838390792Sgshapiro	{
838490792Sgshapiro		/* pretend we failed, even though we technically didn't */
838590792Sgshapiro		failing = true;
838690792Sgshapiro	}
838790792Sgshapiro
838890792Sgshapiro	/* Make sure we wrote things out safely */
838990792Sgshapiro	if (!failing &&
839090792Sgshapiro	    (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
839190792Sgshapiro	     ((SuperSafe == SAFE_REALLY || SuperSafe == SAFE_INTERACTIVE) &&
839290792Sgshapiro	      fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
839390792Sgshapiro	     ((errno = sm_io_error(tempqfp)) != 0)))
839490792Sgshapiro	{
839590792Sgshapiro		save_errno = errno;
839690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
839790792Sgshapiro				     "Skipping %s: Error writing %s: %s\n",
839890792Sgshapiro				     qid_printname(e), tempqf,
839990792Sgshapiro				     sm_errstring(save_errno));
840090792Sgshapiro		failing = true;
840190792Sgshapiro	}
840290792Sgshapiro
840390792Sgshapiro
840490792Sgshapiro	/* Figure out the new filename */
840590792Sgshapiro	newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
840690792Sgshapiro	if (oldtype == newtype)
840790792Sgshapiro	{
840890792Sgshapiro		/* going to rename tempqf to oldqf */
840990792Sgshapiro		(void) sm_strlcpy(newqf, oldqf, sizeof newqf);
841090792Sgshapiro	}
841190792Sgshapiro	else
841290792Sgshapiro	{
841390792Sgshapiro		/* going to rename tempqf to new name based on newtype */
841490792Sgshapiro		(void) sm_strlcpy(newqf, queuename(e, newtype), sizeof newqf);
841590792Sgshapiro	}
841690792Sgshapiro
841790792Sgshapiro	save_errno = 0;
841890792Sgshapiro
841990792Sgshapiro	/* rename tempqf to newqf */
842090792Sgshapiro	if (!failing &&
842190792Sgshapiro	    rename(tempqf, newqf) < 0)
842290792Sgshapiro		save_errno = (errno == 0) ? EINVAL : errno;
842390792Sgshapiro
842490792Sgshapiro	/* Check rename() success */
842590792Sgshapiro	if (!failing && save_errno != 0)
842690792Sgshapiro	{
842790792Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id,
842890792Sgshapiro			  "quarantine_queue_item: rename(%s, %s): %s",
842990792Sgshapiro			  tempqf, newqf, sm_errstring(save_errno));
843090792Sgshapiro
843190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
843290792Sgshapiro				     "Error renaming %s to %s: %s\n",
843390792Sgshapiro				     tempqf, newqf,
843490792Sgshapiro				     sm_errstring(save_errno));
843590792Sgshapiro		if (oldtype == newtype)
843690792Sgshapiro		{
843790792Sgshapiro			/*
843890792Sgshapiro			**  Bail here since we don't know the state of
843990792Sgshapiro			**  the filesystem and may need to keep tempqf
844090792Sgshapiro			**  for the user to rescue us.
844190792Sgshapiro			*/
844290792Sgshapiro
844390792Sgshapiro			RELEASE_QUEUE;
844490792Sgshapiro			errno = save_errno;
844590792Sgshapiro			syserr("!452 Error renaming control file %s", tempqf);
844690792Sgshapiro			/* NOTREACHED */
844790792Sgshapiro		}
844890792Sgshapiro		else
844990792Sgshapiro		{
845090792Sgshapiro			/* remove new file (if rename() half completed) */
845190792Sgshapiro			if (xunlink(newqf) < 0)
845290792Sgshapiro			{
845390792Sgshapiro				save_errno = errno;
845490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
845590792Sgshapiro						     "Error removing %s: %s\n",
845690792Sgshapiro						     newqf,
845790792Sgshapiro						     sm_errstring(save_errno));
845890792Sgshapiro			}
845990792Sgshapiro
846090792Sgshapiro			/* tempqf removed below */
846190792Sgshapiro			failing = true;
846290792Sgshapiro		}
846390792Sgshapiro
846490792Sgshapiro	}
846590792Sgshapiro
846690792Sgshapiro	/* If changing file types, need to remove old type */
846790792Sgshapiro	if (!failing && oldtype != newtype)
846890792Sgshapiro	{
846990792Sgshapiro		if (xunlink(oldqf) < 0)
847090792Sgshapiro		{
847190792Sgshapiro			save_errno = errno;
847290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
847390792Sgshapiro					     "Error removing %s: %s\n",
847490792Sgshapiro					     oldqf, sm_errstring(save_errno));
847590792Sgshapiro		}
847690792Sgshapiro	}
847790792Sgshapiro
847890792Sgshapiro	/* see if anything above failed */
847990792Sgshapiro	if (failing)
848090792Sgshapiro	{
848190792Sgshapiro		/* Something failed: remove new file, old file still there */
848290792Sgshapiro		(void) xunlink(tempqf);
848390792Sgshapiro	}
848490792Sgshapiro
848590792Sgshapiro	/*
848690792Sgshapiro	**  fsync() after file operations to make sure metadata is
848790792Sgshapiro	**  written to disk on filesystems in which renames are
848890792Sgshapiro	**  not guaranteed.  It's ok if they fail, mail won't be lost.
848990792Sgshapiro	*/
849090792Sgshapiro
849190792Sgshapiro	if (SuperSafe != SAFE_NO)
849290792Sgshapiro	{
849390792Sgshapiro		/* for soft-updates */
849490792Sgshapiro		(void) fsync(sm_io_getinfo(tempqfp,
849590792Sgshapiro					   SM_IO_WHAT_FD, NULL));
849690792Sgshapiro
849790792Sgshapiro		if (!failing)
849890792Sgshapiro		{
849990792Sgshapiro			/* for soft-updates */
850090792Sgshapiro			(void) fsync(sm_io_getinfo(oldqfp,
850190792Sgshapiro						   SM_IO_WHAT_FD, NULL));
850290792Sgshapiro		}
850390792Sgshapiro
850490792Sgshapiro		/* for other odd filesystems */
850590792Sgshapiro		SYNC_DIR(tempqf, false);
850690792Sgshapiro	}
850790792Sgshapiro
850890792Sgshapiro	/* Close up shop */
850990792Sgshapiro	RELEASE_QUEUE;
851090792Sgshapiro	if (tempqfp != NULL)
851190792Sgshapiro		(void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
851290792Sgshapiro	if (oldqfp != NULL)
851390792Sgshapiro		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
851490792Sgshapiro
851590792Sgshapiro	/* All went well */
851690792Sgshapiro	return !failing;
851790792Sgshapiro}
851890792Sgshapiro
851990792Sgshapiro/*
852090792Sgshapiro**  QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
852190792Sgshapiro**
852290792Sgshapiro**	Read all matching queue items, add/remove quarantine
852390792Sgshapiro**	reason, and requeue appropriately.
852490792Sgshapiro**
852590792Sgshapiro**	Parameters:
852690792Sgshapiro**		reason -- quarantine reason, "." means unquarantine.
852790792Sgshapiro**		qgrplimit -- limit to single queue group unless NOQGRP
852890792Sgshapiro**
852990792Sgshapiro**	Results:
853090792Sgshapiro**		none.
853190792Sgshapiro**
853290792Sgshapiro**	Side Effects:
853390792Sgshapiro**		Lots of changes to the queue.
853490792Sgshapiro*/
853590792Sgshapiro
853690792Sgshapirovoid
853790792Sgshapiroquarantine_queue(reason, qgrplimit)
853890792Sgshapiro	char *reason;
853990792Sgshapiro	int qgrplimit;
854090792Sgshapiro{
854190792Sgshapiro	int changed = 0;
854290792Sgshapiro	int qgrp;
854390792Sgshapiro
854490792Sgshapiro	/* Convert internal representation of unquarantine */
854590792Sgshapiro	if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
854690792Sgshapiro		reason = NULL;
854790792Sgshapiro
854890792Sgshapiro	if (reason != NULL)
854990792Sgshapiro	{
855090792Sgshapiro		/* clean it */
855190792Sgshapiro		reason = newstr(denlstring(reason, true, true));
855290792Sgshapiro	}
855390792Sgshapiro
855490792Sgshapiro	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
855590792Sgshapiro	{
855690792Sgshapiro		int qdir;
855790792Sgshapiro
855890792Sgshapiro		if (qgrplimit != NOQGRP && qgrplimit != qgrp)
855990792Sgshapiro			continue;
856090792Sgshapiro
856190792Sgshapiro		for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
856290792Sgshapiro		{
856390792Sgshapiro			int i;
856490792Sgshapiro			int nrequests;
856590792Sgshapiro
856690792Sgshapiro			if (StopRequest)
856790792Sgshapiro				stop_sendmail();
856890792Sgshapiro
856990792Sgshapiro			nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
857090792Sgshapiro
857190792Sgshapiro			/* first see if there is anything */
857290792Sgshapiro			if (nrequests <= 0)
857390792Sgshapiro			{
857490792Sgshapiro				if (Verbose)
857590792Sgshapiro				{
857690792Sgshapiro					(void) sm_io_fprintf(smioout,
857790792Sgshapiro							     SM_TIME_DEFAULT, "%s: no matches\n",
857890792Sgshapiro							     qid_printqueue(qgrp, qdir));
857990792Sgshapiro				}
858090792Sgshapiro				continue;
858190792Sgshapiro			}
858290792Sgshapiro
858390792Sgshapiro			if (Verbose)
858490792Sgshapiro			{
858590792Sgshapiro				(void) sm_io_fprintf(smioout,
858690792Sgshapiro						     SM_TIME_DEFAULT, "Processing %s:\n",
858790792Sgshapiro						     qid_printqueue(qgrp, qdir));
858890792Sgshapiro			}
858990792Sgshapiro
859090792Sgshapiro			for (i = 0; i < WorkListCount; i++)
859190792Sgshapiro			{
859290792Sgshapiro				ENVELOPE e;
859390792Sgshapiro
859490792Sgshapiro				if (StopRequest)
859590792Sgshapiro					stop_sendmail();
859690792Sgshapiro
859790792Sgshapiro				/* setup envelope */
859890792Sgshapiro				clearenvelope(&e, true, sm_rpool_new_x(NULL));
859990792Sgshapiro				e.e_id = WorkList[i].w_name + 2;
860090792Sgshapiro				e.e_qgrp = qgrp;
860190792Sgshapiro				e.e_qdir = qdir;
860290792Sgshapiro
860390792Sgshapiro				if (tTd(70, 101))
860490792Sgshapiro				{
860590792Sgshapiro					sm_io_fprintf(smioout, SM_TIME_DEFAULT,
860690792Sgshapiro						      "Would do %s\n", e.e_id);
860790792Sgshapiro					changed++;
860890792Sgshapiro				}
860990792Sgshapiro				else if (quarantine_queue_item(qgrp, qdir,
861090792Sgshapiro							       &e, reason))
861190792Sgshapiro					changed++;
861290792Sgshapiro
861390792Sgshapiro				/* clean up */
861490792Sgshapiro				sm_rpool_free(e.e_rpool);
861590792Sgshapiro				e.e_rpool = NULL;
861690792Sgshapiro			}
861790792Sgshapiro			if (WorkList != NULL)
861890792Sgshapiro				sm_free(WorkList); /* XXX */
861990792Sgshapiro			WorkList = NULL;
862090792Sgshapiro			WorkListSize = 0;
862190792Sgshapiro			WorkListCount = 0;
862290792Sgshapiro		}
862390792Sgshapiro	}
862490792Sgshapiro	if (Verbose)
862590792Sgshapiro	{
862690792Sgshapiro		if (changed == 0)
862790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
862890792Sgshapiro					     "No changes\n");
862990792Sgshapiro		else
863090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
863190792Sgshapiro					     "%d change%s\n",
863290792Sgshapiro					     changed,
863390792Sgshapiro					     changed == 1 ? "" : "s");
863490792Sgshapiro	}
863590792Sgshapiro}
863690792Sgshapiro#endif /* _FFR_QUARANTINE */
8637