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