xref: /illumos-gate/usr/src/cmd/sendmail/src/queue.c (revision 445f2479fe3d7435daab18bf2cdc310b86cd6738)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *	All rights reserved.
47c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
57c478bd9Sstevel@tonic-gate  * Copyright (c) 1988, 1993
67c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
97c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
107c478bd9Sstevel@tonic-gate  * the sendmail distribution.
117c478bd9Sstevel@tonic-gate  *
127c478bd9Sstevel@tonic-gate  */
137c478bd9Sstevel@tonic-gate 
147c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
157c478bd9Sstevel@tonic-gate 
167c478bd9Sstevel@tonic-gate #include <sendmail.h>
177c478bd9Sstevel@tonic-gate #include <sm/sem.h>
187c478bd9Sstevel@tonic-gate 
19*445f2479Sjbeck SM_RCSID("@(#)$Id: queue.c,v 8.951 2006/03/02 19:13:38 ca Exp $")
207c478bd9Sstevel@tonic-gate 
217c478bd9Sstevel@tonic-gate #include <dirent.h>
227c478bd9Sstevel@tonic-gate 
237c478bd9Sstevel@tonic-gate # define RELEASE_QUEUE	(void) 0
247c478bd9Sstevel@tonic-gate # define ST_INODE(st)	(st).st_ino
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #  define sm_file_exists(errno) ((errno) == EEXIST)
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate # if HASFLOCK && defined(O_EXLOCK)
297c478bd9Sstevel@tonic-gate #   define SM_OPEN_EXLOCK 1
307c478bd9Sstevel@tonic-gate #   define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK)
317c478bd9Sstevel@tonic-gate # else /* HASFLOCK && defined(O_EXLOCK) */
327c478bd9Sstevel@tonic-gate #  define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL)
337c478bd9Sstevel@tonic-gate # endif /* HASFLOCK && defined(O_EXLOCK) */
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate #ifndef SM_OPEN_EXLOCK
367c478bd9Sstevel@tonic-gate # define SM_OPEN_EXLOCK 0
377c478bd9Sstevel@tonic-gate #endif /* ! SM_OPEN_EXLOCK */
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate /*
407c478bd9Sstevel@tonic-gate **  Historical notes:
417c478bd9Sstevel@tonic-gate **	QF_VERSION == 4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY
427c478bd9Sstevel@tonic-gate **	QF_VERSION == 5 was sendmail 8.10/8.11 with    _FFR_QUEUEDELAY
437c478bd9Sstevel@tonic-gate **	QF_VERSION == 6 was sendmail 8.12      without _FFR_QUEUEDELAY
447c478bd9Sstevel@tonic-gate **	QF_VERSION == 7 was sendmail 8.12      with    _FFR_QUEUEDELAY
457c478bd9Sstevel@tonic-gate **	QF_VERSION == 8 is  sendmail 8.13
467c478bd9Sstevel@tonic-gate */
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #define QF_VERSION	8	/* version number of this queue format */
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate static char	queue_letter __P((ENVELOPE *, int));
517c478bd9Sstevel@tonic-gate static bool	quarantine_queue_item __P((int, int, ENVELOPE *, char *));
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate /* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate /*
567c478bd9Sstevel@tonic-gate **  Work queue.
577c478bd9Sstevel@tonic-gate */
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate struct work
607c478bd9Sstevel@tonic-gate {
617c478bd9Sstevel@tonic-gate 	char		*w_name;	/* name of control file */
627c478bd9Sstevel@tonic-gate 	char		*w_host;	/* name of recipient host */
637c478bd9Sstevel@tonic-gate 	bool		w_lock;		/* is message locked? */
647c478bd9Sstevel@tonic-gate 	bool		w_tooyoung;	/* is it too young to run? */
657c478bd9Sstevel@tonic-gate 	long		w_pri;		/* priority of message, see below */
667c478bd9Sstevel@tonic-gate 	time_t		w_ctime;	/* creation time */
677c478bd9Sstevel@tonic-gate 	time_t		w_mtime;	/* modification time */
687c478bd9Sstevel@tonic-gate 	int		w_qgrp;		/* queue group located in */
697c478bd9Sstevel@tonic-gate 	int		w_qdir;		/* queue directory located in */
707c478bd9Sstevel@tonic-gate 	struct work	*w_next;	/* next in queue */
717c478bd9Sstevel@tonic-gate };
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate typedef struct work	WORK;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate static WORK	*WorkQ;		/* queue of things to be done */
767c478bd9Sstevel@tonic-gate static int	NumWorkGroups;	/* number of work groups */
777c478bd9Sstevel@tonic-gate static time_t	Current_LA_time = 0;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /* Get new load average every 30 seconds. */
807c478bd9Sstevel@tonic-gate #define GET_NEW_LA_TIME	30
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate #define SM_GET_LA(now)	\
837c478bd9Sstevel@tonic-gate 	do							\
847c478bd9Sstevel@tonic-gate 	{							\
857c478bd9Sstevel@tonic-gate 		now = curtime();				\
867c478bd9Sstevel@tonic-gate 		if (Current_LA_time < now - GET_NEW_LA_TIME)	\
877c478bd9Sstevel@tonic-gate 		{						\
887c478bd9Sstevel@tonic-gate 			sm_getla();				\
897c478bd9Sstevel@tonic-gate 			Current_LA_time = now;			\
907c478bd9Sstevel@tonic-gate 		}						\
917c478bd9Sstevel@tonic-gate 	} while (0)
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate **  DoQueueRun indicates that a queue run is needed.
957c478bd9Sstevel@tonic-gate **	Notice: DoQueueRun is modified in a signal handler!
967c478bd9Sstevel@tonic-gate */
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate static bool	volatile DoQueueRun; /* non-interrupt time queue run needed */
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate /*
1017c478bd9Sstevel@tonic-gate **  Work group definition structure.
1027c478bd9Sstevel@tonic-gate **	Each work group contains one or more queue groups. This is done
1037c478bd9Sstevel@tonic-gate **	to manage the number of queue group runners active at the same time
1047c478bd9Sstevel@tonic-gate **	to be within the constraints of MaxQueueChildren (if it is set).
1057c478bd9Sstevel@tonic-gate **	The number of queue groups that can be run on the next work run
1067c478bd9Sstevel@tonic-gate **	is kept track of. The queue groups are run in a round robin.
1077c478bd9Sstevel@tonic-gate */
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate struct workgrp
1107c478bd9Sstevel@tonic-gate {
1117c478bd9Sstevel@tonic-gate 	int		wg_numqgrp;	/* number of queue groups in work grp */
1127c478bd9Sstevel@tonic-gate 	int		wg_runners;	/* total runners */
1137c478bd9Sstevel@tonic-gate 	int		wg_curqgrp;	/* current queue group */
1147c478bd9Sstevel@tonic-gate 	QUEUEGRP	**wg_qgs;	/* array of queue groups */
1157c478bd9Sstevel@tonic-gate 	int		wg_maxact;	/* max # of active runners */
1167c478bd9Sstevel@tonic-gate 	time_t		wg_lowqintvl;	/* lowest queue interval */
1177c478bd9Sstevel@tonic-gate 	int		wg_restart;	/* needs restarting? */
1187c478bd9Sstevel@tonic-gate 	int		wg_restartcnt;	/* count of times restarted */
1197c478bd9Sstevel@tonic-gate };
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate typedef struct workgrp WORKGRP;
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate static WORKGRP	volatile WorkGrp[MAXWORKGROUPS + 1];	/* work groups */
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
1267c478bd9Sstevel@tonic-gate static SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q",
1277c478bd9Sstevel@tonic-gate 	"@(#)$Debug: leak_q - trace memory leaks during queue processing $");
1287c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate /*
1317c478bd9Sstevel@tonic-gate **  We use EmptyString instead of "" to avoid
1327c478bd9Sstevel@tonic-gate **  'zero-length format string' warnings from gcc
1337c478bd9Sstevel@tonic-gate */
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate static const char EmptyString[] = "";
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate static void	grow_wlist __P((int, int));
1387c478bd9Sstevel@tonic-gate static int	multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *));
1397c478bd9Sstevel@tonic-gate static int	gatherq __P((int, int, bool, bool *, bool *));
1407c478bd9Sstevel@tonic-gate static int	sortq __P((int));
1417c478bd9Sstevel@tonic-gate static void	printctladdr __P((ADDRESS *, SM_FILE_T *));
1427c478bd9Sstevel@tonic-gate static bool	readqf __P((ENVELOPE *, bool));
1437c478bd9Sstevel@tonic-gate static void	restart_work_group __P((int));
1447c478bd9Sstevel@tonic-gate static void	runner_work __P((ENVELOPE *, int, bool, int, int));
1457c478bd9Sstevel@tonic-gate static void	schedule_queue_runs __P((bool, int, bool));
1467c478bd9Sstevel@tonic-gate static char	*strrev __P((char *));
1477c478bd9Sstevel@tonic-gate static ADDRESS	*setctluser __P((char *, int, ENVELOPE *));
1487c478bd9Sstevel@tonic-gate #if _FFR_RHS
1497c478bd9Sstevel@tonic-gate static int	sm_strshufflecmp __P((char *, char *));
1507c478bd9Sstevel@tonic-gate static void	init_shuffle_alphabet __P(());
1517c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
1527c478bd9Sstevel@tonic-gate static int	workcmpf0();
1537c478bd9Sstevel@tonic-gate static int	workcmpf1();
1547c478bd9Sstevel@tonic-gate static int	workcmpf2();
1557c478bd9Sstevel@tonic-gate static int	workcmpf3();
1567c478bd9Sstevel@tonic-gate static int	workcmpf4();
1577c478bd9Sstevel@tonic-gate static int	randi = 3;	/* index for workcmpf5() */
1587c478bd9Sstevel@tonic-gate static int	workcmpf5();
1597c478bd9Sstevel@tonic-gate static int	workcmpf6();
1607c478bd9Sstevel@tonic-gate #if _FFR_RHS
1617c478bd9Sstevel@tonic-gate static int	workcmpf7();
1627c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate #if RANDOMSHIFT
1657c478bd9Sstevel@tonic-gate # define get_rand_mod(m)	((get_random() >> RANDOMSHIFT) % (m))
1667c478bd9Sstevel@tonic-gate #else /* RANDOMSHIFT */
1677c478bd9Sstevel@tonic-gate # define get_rand_mod(m)	(get_random() % (m))
1687c478bd9Sstevel@tonic-gate #endif /* RANDOMSHIFT */
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate /*
1717c478bd9Sstevel@tonic-gate **  File system definition.
1727c478bd9Sstevel@tonic-gate **	Used to keep track of how much free space is available
1737c478bd9Sstevel@tonic-gate **	on a file system in which one or more queue directories reside.
1747c478bd9Sstevel@tonic-gate */
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate typedef struct filesys_shared	FILESYS;
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate struct filesys_shared
1797c478bd9Sstevel@tonic-gate {
1807c478bd9Sstevel@tonic-gate 	dev_t	fs_dev;		/* unique device id */
1817c478bd9Sstevel@tonic-gate 	long	fs_avail;	/* number of free blocks available */
1827c478bd9Sstevel@tonic-gate 	long	fs_blksize;	/* block size, in bytes */
1837c478bd9Sstevel@tonic-gate };
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate /* probably kept in shared memory */
1867c478bd9Sstevel@tonic-gate static FILESYS	FileSys[MAXFILESYS];	/* queue file systems */
1877c478bd9Sstevel@tonic-gate static char	*FSPath[MAXFILESYS];	/* pathnames for file systems */
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate /*
1927c478bd9Sstevel@tonic-gate **  Shared memory data
1937c478bd9Sstevel@tonic-gate **
1947c478bd9Sstevel@tonic-gate **  Current layout:
1957c478bd9Sstevel@tonic-gate **	size -- size of shared memory segment
1967c478bd9Sstevel@tonic-gate **	pid -- pid of owner, should be a unique id to avoid misinterpretations
1977c478bd9Sstevel@tonic-gate **		by other processes.
1987c478bd9Sstevel@tonic-gate **	tag -- should be a unique id to avoid misinterpretations by others.
1997c478bd9Sstevel@tonic-gate **		idea: hash over configuration data that will be stored here.
2007c478bd9Sstevel@tonic-gate **	NumFileSys -- number of file systems.
2017c478bd9Sstevel@tonic-gate **	FileSys -- (arrary of) structure for used file systems.
2027c478bd9Sstevel@tonic-gate **	RSATmpCnt -- counter for number of uses of ephemeral RSA key.
2037c478bd9Sstevel@tonic-gate **	QShm -- (array of) structure for information about queue directories.
2047c478bd9Sstevel@tonic-gate */
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate /*
2077c478bd9Sstevel@tonic-gate **  Queue data in shared memory
2087c478bd9Sstevel@tonic-gate */
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate typedef struct queue_shared	QUEUE_SHM_T;
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate struct queue_shared
2137c478bd9Sstevel@tonic-gate {
2147c478bd9Sstevel@tonic-gate 	int	qs_entries;	/* number of entries */
2157c478bd9Sstevel@tonic-gate 	/* XXX more to follow? */
2167c478bd9Sstevel@tonic-gate };
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate static void	*Pshm;		/* pointer to shared memory */
2197c478bd9Sstevel@tonic-gate static FILESYS	*PtrFileSys;	/* pointer to queue file system array */
2207c478bd9Sstevel@tonic-gate int		ShmId = SM_SHM_NO_ID;	/* shared memory id */
2217c478bd9Sstevel@tonic-gate static QUEUE_SHM_T	*QShm;		/* pointer to shared queue data */
2227c478bd9Sstevel@tonic-gate static size_t shms;
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate # define SHM_OFF_PID(p)	(((char *) (p)) + sizeof(int))
2257c478bd9Sstevel@tonic-gate # define SHM_OFF_TAG(p)	(((char *) (p)) + sizeof(pid_t) + sizeof(int))
2267c478bd9Sstevel@tonic-gate # define SHM_OFF_HEAD	(sizeof(pid_t) + sizeof(int) * 2)
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate /* how to access FileSys */
2297c478bd9Sstevel@tonic-gate # define FILE_SYS(i)	(PtrFileSys[i])
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate /* first entry is a tag, for now just the size */
2327c478bd9Sstevel@tonic-gate # define OFF_FILE_SYS(p)	(((char *) (p)) + SHM_OFF_HEAD)
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate /* offset for PNumFileSys */
2357c478bd9Sstevel@tonic-gate # define OFF_NUM_FILE_SYS(p)	(((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys))
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate /* offset for PRSATmpCnt */
2387c478bd9Sstevel@tonic-gate # define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int))
2397c478bd9Sstevel@tonic-gate int	*PRSATmpCnt;
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate /* offset for queue_shm */
2427c478bd9Sstevel@tonic-gate # define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate # define QSHM_ENTRIES(i)	QShm[i].qs_entries
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate /* basic size of shared memory segment */
2477c478bd9Sstevel@tonic-gate # define SM_T_SIZE	(SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate static unsigned int	hash_q __P((char *, unsigned int));
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate /*
2527c478bd9Sstevel@tonic-gate **  HASH_Q -- simple hash function
2537c478bd9Sstevel@tonic-gate **
2547c478bd9Sstevel@tonic-gate **	Parameters:
2557c478bd9Sstevel@tonic-gate **		p -- string to hash.
2567c478bd9Sstevel@tonic-gate **		h -- hash start value (from previous run).
2577c478bd9Sstevel@tonic-gate **
2587c478bd9Sstevel@tonic-gate **	Returns:
2597c478bd9Sstevel@tonic-gate **		hash value.
2607c478bd9Sstevel@tonic-gate */
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate static unsigned int
2637c478bd9Sstevel@tonic-gate hash_q(p, h)
2647c478bd9Sstevel@tonic-gate 	char *p;
2657c478bd9Sstevel@tonic-gate 	unsigned int h;
2667c478bd9Sstevel@tonic-gate {
2677c478bd9Sstevel@tonic-gate 	int c, d;
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	while (*p != '\0')
2707c478bd9Sstevel@tonic-gate 	{
2717c478bd9Sstevel@tonic-gate 		d = *p++;
2727c478bd9Sstevel@tonic-gate 		c = d;
2737c478bd9Sstevel@tonic-gate 		c ^= c<<6;
2747c478bd9Sstevel@tonic-gate 		h += (c<<11) ^ (c>>1);
2757c478bd9Sstevel@tonic-gate 		h ^= (d<<14) + (d<<7) + (d<<4) + d;
2767c478bd9Sstevel@tonic-gate 	}
2777c478bd9Sstevel@tonic-gate 	return h;
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
2827c478bd9Sstevel@tonic-gate # define FILE_SYS(i)	FileSys[i]
2837c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate /* access to the various components of file system data */
2867c478bd9Sstevel@tonic-gate #define FILE_SYS_NAME(i)	FSPath[i]
2877c478bd9Sstevel@tonic-gate #define FILE_SYS_AVAIL(i)	FILE_SYS(i).fs_avail
2887c478bd9Sstevel@tonic-gate #define FILE_SYS_BLKSIZE(i)	FILE_SYS(i).fs_blksize
2897c478bd9Sstevel@tonic-gate #define FILE_SYS_DEV(i)	FILE_SYS(i).fs_dev
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate /*
2937c478bd9Sstevel@tonic-gate **  Current qf file field assignments:
2947c478bd9Sstevel@tonic-gate **
2957c478bd9Sstevel@tonic-gate **	A	AUTH= parameter
2967c478bd9Sstevel@tonic-gate **	B	body type
2977c478bd9Sstevel@tonic-gate **	C	controlling user
2987c478bd9Sstevel@tonic-gate **	D	data file name
2997c478bd9Sstevel@tonic-gate **	d	data file directory name (added in 8.12)
3007c478bd9Sstevel@tonic-gate **	E	error recipient
3017c478bd9Sstevel@tonic-gate **	F	flag bits
3027c478bd9Sstevel@tonic-gate **	G	free (was: queue delay algorithm if _FFR_QUEUEDELAY)
3037c478bd9Sstevel@tonic-gate **	H	header
3047c478bd9Sstevel@tonic-gate **	I	data file's inode number
3057c478bd9Sstevel@tonic-gate **	K	time of last delivery attempt
3067c478bd9Sstevel@tonic-gate **	L	Solaris Content-Length: header (obsolete)
3077c478bd9Sstevel@tonic-gate **	M	message
3087c478bd9Sstevel@tonic-gate **	N	number of delivery attempts
3097c478bd9Sstevel@tonic-gate **	P	message priority
3107c478bd9Sstevel@tonic-gate **	q	quarantine reason
3117c478bd9Sstevel@tonic-gate **	Q	original recipient (ORCPT=)
3127c478bd9Sstevel@tonic-gate **	r	final recipient (Final-Recipient: DSN field)
3137c478bd9Sstevel@tonic-gate **	R	recipient
3147c478bd9Sstevel@tonic-gate **	S	sender
3157c478bd9Sstevel@tonic-gate **	T	init time
3167c478bd9Sstevel@tonic-gate **	V	queue file version
3177c478bd9Sstevel@tonic-gate **	X	free (was: character set if _FFR_SAVE_CHARSET)
3187c478bd9Sstevel@tonic-gate **	Y	free (was: current delay if _FFR_QUEUEDELAY)
3197c478bd9Sstevel@tonic-gate **	Z	original envelope id from ESMTP
3207c478bd9Sstevel@tonic-gate **	!	deliver by (added in 8.12)
3217c478bd9Sstevel@tonic-gate **	$	define macro
3227c478bd9Sstevel@tonic-gate **	.	terminate file
3237c478bd9Sstevel@tonic-gate */
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate /*
3267c478bd9Sstevel@tonic-gate **  QUEUEUP -- queue a message up for future transmission.
3277c478bd9Sstevel@tonic-gate **
3287c478bd9Sstevel@tonic-gate **	Parameters:
3297c478bd9Sstevel@tonic-gate **		e -- the envelope to queue up.
3307c478bd9Sstevel@tonic-gate **		announce -- if true, tell when you are queueing up.
3317c478bd9Sstevel@tonic-gate **		msync -- if true, then fsync() if SuperSafe interactive mode.
3327c478bd9Sstevel@tonic-gate **
3337c478bd9Sstevel@tonic-gate **	Returns:
3347c478bd9Sstevel@tonic-gate **		none.
3357c478bd9Sstevel@tonic-gate **
3367c478bd9Sstevel@tonic-gate **	Side Effects:
3377c478bd9Sstevel@tonic-gate **		The current request is saved in a control file.
3387c478bd9Sstevel@tonic-gate **		The queue file is left locked.
3397c478bd9Sstevel@tonic-gate */
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate void
3427c478bd9Sstevel@tonic-gate queueup(e, announce, msync)
3437c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
3447c478bd9Sstevel@tonic-gate 	bool announce;
3457c478bd9Sstevel@tonic-gate 	bool msync;
3467c478bd9Sstevel@tonic-gate {
3477c478bd9Sstevel@tonic-gate 	register SM_FILE_T *tfp;
3487c478bd9Sstevel@tonic-gate 	register HDR *h;
3497c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
3507c478bd9Sstevel@tonic-gate 	int tfd = -1;
3517c478bd9Sstevel@tonic-gate 	int i;
3527c478bd9Sstevel@tonic-gate 	bool newid;
3537c478bd9Sstevel@tonic-gate 	register char *p;
3547c478bd9Sstevel@tonic-gate 	MAILER nullmailer;
3557c478bd9Sstevel@tonic-gate 	MCI mcibuf;
3567c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
3577c478bd9Sstevel@tonic-gate 	char tf[MAXPATHLEN];
3587c478bd9Sstevel@tonic-gate 	char df[MAXPATHLEN];
3597c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 	/*
3627c478bd9Sstevel@tonic-gate 	**  Create control file.
3637c478bd9Sstevel@tonic-gate 	*/
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate #define OPEN_TF	do							\
3667c478bd9Sstevel@tonic-gate 		{							\
3677c478bd9Sstevel@tonic-gate 			MODE_T oldumask = 0;				\
3687c478bd9Sstevel@tonic-gate 									\
3697c478bd9Sstevel@tonic-gate 			if (bitset(S_IWGRP, QueueFileMode))		\
3707c478bd9Sstevel@tonic-gate 				oldumask = umask(002);			\
3717c478bd9Sstevel@tonic-gate 			tfd = open(tf, TF_OPEN_FLAGS, QueueFileMode);	\
3727c478bd9Sstevel@tonic-gate 			if (bitset(S_IWGRP, QueueFileMode))		\
3737c478bd9Sstevel@tonic-gate 				(void) umask(oldumask);			\
3747c478bd9Sstevel@tonic-gate 		} while (0)
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
3787c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof tf);
3797c478bd9Sstevel@tonic-gate 	tfp = e->e_lockfp;
3807c478bd9Sstevel@tonic-gate 	if (tfp == NULL && newid)
3817c478bd9Sstevel@tonic-gate 	{
3827c478bd9Sstevel@tonic-gate 		/*
3837c478bd9Sstevel@tonic-gate 		**  open qf file directly: this will give an error if the file
3847c478bd9Sstevel@tonic-gate 		**  already exists and hence prevent problems if a queue-id
3857c478bd9Sstevel@tonic-gate 		**  is reused (e.g., because the clock is set back).
3867c478bd9Sstevel@tonic-gate 		*/
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(tf, queuename(e, ANYQFL_LETTER), sizeof tf);
3897c478bd9Sstevel@tonic-gate 		OPEN_TF;
3907c478bd9Sstevel@tonic-gate 		if (tfd < 0 ||
3917c478bd9Sstevel@tonic-gate #if !SM_OPEN_EXLOCK
3927c478bd9Sstevel@tonic-gate 		    !lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB) ||
3937c478bd9Sstevel@tonic-gate #endif /* !SM_OPEN_EXLOCK */
3947c478bd9Sstevel@tonic-gate 		    (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
3957c478bd9Sstevel@tonic-gate 					 (void *) &tfd, SM_IO_WRONLY,
3967c478bd9Sstevel@tonic-gate 					 NULL)) == NULL)
3977c478bd9Sstevel@tonic-gate 		{
3987c478bd9Sstevel@tonic-gate 			int save_errno = errno;
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 			printopenfds(true);
4017c478bd9Sstevel@tonic-gate 			errno = save_errno;
4027c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create queue file %s, euid=%d, fd=%d, fp=%p",
4037c478bd9Sstevel@tonic-gate 				tf, (int) geteuid(), tfd, tfp);
4047c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4057c478bd9Sstevel@tonic-gate 		}
4067c478bd9Sstevel@tonic-gate 		e->e_lockfp = tfp;
4077c478bd9Sstevel@tonic-gate 		upd_qs(e, 1, 0, "queueup");
4087c478bd9Sstevel@tonic-gate 	}
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	/* if newid, write the queue file directly (instead of temp file) */
4117c478bd9Sstevel@tonic-gate 	if (!newid)
4127c478bd9Sstevel@tonic-gate 	{
4137c478bd9Sstevel@tonic-gate 		/* get a locked tf file */
4147c478bd9Sstevel@tonic-gate 		for (i = 0; i < 128; i++)
4157c478bd9Sstevel@tonic-gate 		{
4167c478bd9Sstevel@tonic-gate 			if (tfd < 0)
4177c478bd9Sstevel@tonic-gate 			{
4187c478bd9Sstevel@tonic-gate 				OPEN_TF;
4197c478bd9Sstevel@tonic-gate 				if (tfd < 0)
4207c478bd9Sstevel@tonic-gate 				{
4217c478bd9Sstevel@tonic-gate 					if (errno != EEXIST)
4227c478bd9Sstevel@tonic-gate 						break;
4237c478bd9Sstevel@tonic-gate 					if (LogLevel > 0 && (i % 32) == 0)
4247c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ALERT, e->e_id,
4257c478bd9Sstevel@tonic-gate 							  "queueup: cannot create %s, uid=%d: %s",
4267c478bd9Sstevel@tonic-gate 							  tf, (int) geteuid(),
4277c478bd9Sstevel@tonic-gate 							  sm_errstring(errno));
4287c478bd9Sstevel@tonic-gate 				}
4297c478bd9Sstevel@tonic-gate #if SM_OPEN_EXLOCK
4307c478bd9Sstevel@tonic-gate 				else
4317c478bd9Sstevel@tonic-gate 					break;
4327c478bd9Sstevel@tonic-gate #endif /* SM_OPEN_EXLOCK */
4337c478bd9Sstevel@tonic-gate 			}
4347c478bd9Sstevel@tonic-gate 			if (tfd >= 0)
4357c478bd9Sstevel@tonic-gate 			{
4367c478bd9Sstevel@tonic-gate #if SM_OPEN_EXLOCK
4377c478bd9Sstevel@tonic-gate 				/* file is locked by open() */
4387c478bd9Sstevel@tonic-gate 				break;
4397c478bd9Sstevel@tonic-gate #else /* SM_OPEN_EXLOCK */
4407c478bd9Sstevel@tonic-gate 				if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
4417c478bd9Sstevel@tonic-gate 					break;
4427c478bd9Sstevel@tonic-gate 				else
4437c478bd9Sstevel@tonic-gate #endif /* SM_OPEN_EXLOCK */
4447c478bd9Sstevel@tonic-gate 				if (LogLevel > 0 && (i % 32) == 0)
4457c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ALERT, e->e_id,
4467c478bd9Sstevel@tonic-gate 						  "queueup: cannot lock %s: %s",
4477c478bd9Sstevel@tonic-gate 						  tf, sm_errstring(errno));
4487c478bd9Sstevel@tonic-gate 				if ((i % 32) == 31)
4497c478bd9Sstevel@tonic-gate 				{
4507c478bd9Sstevel@tonic-gate 					(void) close(tfd);
4517c478bd9Sstevel@tonic-gate 					tfd = -1;
4527c478bd9Sstevel@tonic-gate 				}
4537c478bd9Sstevel@tonic-gate 			}
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 			if ((i % 32) == 31)
4567c478bd9Sstevel@tonic-gate 			{
4577c478bd9Sstevel@tonic-gate 				/* save the old temp file away */
4587c478bd9Sstevel@tonic-gate 				(void) rename(tf, queuename(e, TEMPQF_LETTER));
4597c478bd9Sstevel@tonic-gate 			}
4607c478bd9Sstevel@tonic-gate 			else
4617c478bd9Sstevel@tonic-gate 				(void) sleep(i % 32);
4627c478bd9Sstevel@tonic-gate 		}
4637c478bd9Sstevel@tonic-gate 		if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
4647c478bd9Sstevel@tonic-gate 						 (void *) &tfd, SM_IO_WRONLY_B,
4657c478bd9Sstevel@tonic-gate 						 NULL)) == NULL)
4667c478bd9Sstevel@tonic-gate 		{
4677c478bd9Sstevel@tonic-gate 			int save_errno = errno;
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 			printopenfds(true);
4707c478bd9Sstevel@tonic-gate 			errno = save_errno;
4717c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create queue temp file %s, uid=%d",
4727c478bd9Sstevel@tonic-gate 				tf, (int) geteuid());
4737c478bd9Sstevel@tonic-gate 		}
4747c478bd9Sstevel@tonic-gate 	}
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
4777c478bd9Sstevel@tonic-gate 		sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n",
4787c478bd9Sstevel@tonic-gate 			   qid_printqueue(e->e_qgrp, e->e_qdir),
4797c478bd9Sstevel@tonic-gate 			   queuename(e, ANYQFL_LETTER),
4807c478bd9Sstevel@tonic-gate 			   newid ? " (new id)" : "");
4817c478bd9Sstevel@tonic-gate 	if (tTd(40, 3))
4827c478bd9Sstevel@tonic-gate 	{
4837c478bd9Sstevel@tonic-gate 		sm_dprintf("  e_flags=");
4847c478bd9Sstevel@tonic-gate 		printenvflags(e);
4857c478bd9Sstevel@tonic-gate 	}
4867c478bd9Sstevel@tonic-gate 	if (tTd(40, 32))
4877c478bd9Sstevel@tonic-gate 	{
4887c478bd9Sstevel@tonic-gate 		sm_dprintf("  sendq=");
4897c478bd9Sstevel@tonic-gate 		printaddr(sm_debug_file(), e->e_sendqueue, true);
4907c478bd9Sstevel@tonic-gate 	}
4917c478bd9Sstevel@tonic-gate 	if (tTd(40, 9))
4927c478bd9Sstevel@tonic-gate 	{
4937c478bd9Sstevel@tonic-gate 		sm_dprintf("  tfp=");
4947c478bd9Sstevel@tonic-gate 		dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false);
4957c478bd9Sstevel@tonic-gate 		sm_dprintf("  lockfp=");
4967c478bd9Sstevel@tonic-gate 		if (e->e_lockfp == NULL)
4977c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL\n");
4987c478bd9Sstevel@tonic-gate 		else
4997c478bd9Sstevel@tonic-gate 			dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL),
5007c478bd9Sstevel@tonic-gate 			       true, false);
5017c478bd9Sstevel@tonic-gate 	}
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	/*
5047c478bd9Sstevel@tonic-gate 	**  If there is no data file yet, create one.
5057c478bd9Sstevel@tonic-gate 	*/
5067c478bd9Sstevel@tonic-gate 
5077c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof df);
5087c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS_DF, e->e_flags))
5097c478bd9Sstevel@tonic-gate 	{
5107c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5117c478bd9Sstevel@tonic-gate 		    SuperSafe != SAFE_REALLY &&
5127c478bd9Sstevel@tonic-gate 		    SuperSafe != SAFE_REALLY_POSTMILTER &&
5137c478bd9Sstevel@tonic-gate 		    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
5147c478bd9Sstevel@tonic-gate 		    errno != EINVAL)
5157c478bd9Sstevel@tonic-gate 		{
5167c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot commit data file %s, uid=%d",
5177c478bd9Sstevel@tonic-gate 			       queuename(e, DATAFL_LETTER), (int) geteuid());
5187c478bd9Sstevel@tonic-gate 		}
5197c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5207c478bd9Sstevel@tonic-gate 		    SuperSafe == SAFE_INTERACTIVE && msync)
5217c478bd9Sstevel@tonic-gate 		{
5227c478bd9Sstevel@tonic-gate 			if (tTd(40,32))
5237c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
5247c478bd9Sstevel@tonic-gate 					  "queueup: fsync(e->e_dfp)");
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 			if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD,
5277c478bd9Sstevel@tonic-gate 						NULL)) < 0)
5287c478bd9Sstevel@tonic-gate 			{
5297c478bd9Sstevel@tonic-gate 				if (newid)
5307c478bd9Sstevel@tonic-gate 					syserr("!552 Error writing data file %s",
5317c478bd9Sstevel@tonic-gate 					       df);
5327c478bd9Sstevel@tonic-gate 				else
5337c478bd9Sstevel@tonic-gate 					syserr("!452 Error writing data file %s",
5347c478bd9Sstevel@tonic-gate 					       df);
5357c478bd9Sstevel@tonic-gate 			}
5367c478bd9Sstevel@tonic-gate 		}
5377c478bd9Sstevel@tonic-gate 	}
5387c478bd9Sstevel@tonic-gate 	else
5397c478bd9Sstevel@tonic-gate 	{
5407c478bd9Sstevel@tonic-gate 		int dfd;
5417c478bd9Sstevel@tonic-gate 		MODE_T oldumask = 0;
5427c478bd9Sstevel@tonic-gate 		register SM_FILE_T *dfp = NULL;
5437c478bd9Sstevel@tonic-gate 		struct stat stbuf;
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5467c478bd9Sstevel@tonic-gate 		    sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
5477c478bd9Sstevel@tonic-gate 			syserr("committing over bf file");
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode))
5507c478bd9Sstevel@tonic-gate 			oldumask = umask(002);
5517c478bd9Sstevel@tonic-gate 		dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC|QF_O_EXTRA,
5527c478bd9Sstevel@tonic-gate 			   QueueFileMode);
5537c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode))
5547c478bd9Sstevel@tonic-gate 			(void) umask(oldumask);
5557c478bd9Sstevel@tonic-gate 		if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
5567c478bd9Sstevel@tonic-gate 						 (void *) &dfd, SM_IO_WRONLY_B,
5577c478bd9Sstevel@tonic-gate 						 NULL)) == NULL)
5587c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create data temp file %s, uid=%d",
5597c478bd9Sstevel@tonic-gate 				df, (int) geteuid());
5607c478bd9Sstevel@tonic-gate 		if (fstat(dfd, &stbuf) < 0)
5617c478bd9Sstevel@tonic-gate 			e->e_dfino = -1;
5627c478bd9Sstevel@tonic-gate 		else
5637c478bd9Sstevel@tonic-gate 		{
5647c478bd9Sstevel@tonic-gate 			e->e_dfdev = stbuf.st_dev;
5657c478bd9Sstevel@tonic-gate 			e->e_dfino = ST_INODE(stbuf);
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
5687c478bd9Sstevel@tonic-gate 		memset(&mcibuf, '\0', sizeof mcibuf);
5697c478bd9Sstevel@tonic-gate 		mcibuf.mci_out = dfp;
5707c478bd9Sstevel@tonic-gate 		mcibuf.mci_mailer = FileMailer;
5717c478bd9Sstevel@tonic-gate 		(*e->e_putbody)(&mcibuf, e, NULL);
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 		if (SuperSafe == SAFE_REALLY ||
5747c478bd9Sstevel@tonic-gate 		    SuperSafe == SAFE_REALLY_POSTMILTER ||
5757c478bd9Sstevel@tonic-gate 		    (SuperSafe == SAFE_INTERACTIVE && msync))
5767c478bd9Sstevel@tonic-gate 		{
5777c478bd9Sstevel@tonic-gate 			if (tTd(40,32))
5787c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
5797c478bd9Sstevel@tonic-gate 					  "queueup: fsync(dfp)");
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate 			if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0)
5827c478bd9Sstevel@tonic-gate 			{
5837c478bd9Sstevel@tonic-gate 				if (newid)
5847c478bd9Sstevel@tonic-gate 					syserr("!552 Error writing data file %s",
5857c478bd9Sstevel@tonic-gate 					       df);
5867c478bd9Sstevel@tonic-gate 				else
5877c478bd9Sstevel@tonic-gate 					syserr("!452 Error writing data file %s",
5887c478bd9Sstevel@tonic-gate 					       df);
5897c478bd9Sstevel@tonic-gate 			}
5907c478bd9Sstevel@tonic-gate 		}
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 		if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0)
5937c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot save data temp file %s, uid=%d",
5947c478bd9Sstevel@tonic-gate 				df, (int) geteuid());
5957c478bd9Sstevel@tonic-gate 		e->e_putbody = putbody;
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	/*
5997c478bd9Sstevel@tonic-gate 	**  Output future work requests.
6007c478bd9Sstevel@tonic-gate 	**	Priority and creation time should be first, since
6017c478bd9Sstevel@tonic-gate 	**	they are required by gatherq.
6027c478bd9Sstevel@tonic-gate 	*/
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	/* output queue version number (must be first!) */
6057c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION);
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	/* output creation time */
6087c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime);
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	/* output last delivery time */
6117c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
6127c478bd9Sstevel@tonic-gate 
6137c478bd9Sstevel@tonic-gate 	/* output number of delivery attempts */
6147c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries);
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	/* output message priority */
6177c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority);
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	/*
6207c478bd9Sstevel@tonic-gate 	**  If data file is in a different directory than the queue file,
6217c478bd9Sstevel@tonic-gate 	**  output a "d" record naming the directory of the data file.
6227c478bd9Sstevel@tonic-gate 	*/
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	if (e->e_dfqgrp != e->e_qgrp)
6257c478bd9Sstevel@tonic-gate 	{
6267c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n",
6277c478bd9Sstevel@tonic-gate 			Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name);
6287c478bd9Sstevel@tonic-gate 	}
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	/* output inode number of data file */
6317c478bd9Sstevel@tonic-gate 	/* XXX should probably include device major/minor too */
6327c478bd9Sstevel@tonic-gate 	if (e->e_dfino != -1)
6337c478bd9Sstevel@tonic-gate 	{
6347c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n",
6357c478bd9Sstevel@tonic-gate 				     (long) major(e->e_dfdev),
6367c478bd9Sstevel@tonic-gate 				     (long) minor(e->e_dfdev),
6377c478bd9Sstevel@tonic-gate 				     (ULONGLONG_T) e->e_dfino);
6387c478bd9Sstevel@tonic-gate 	}
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 	/* output body type */
6417c478bd9Sstevel@tonic-gate 	if (e->e_bodytype != NULL)
6427c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n",
6437c478bd9Sstevel@tonic-gate 				     denlstring(e->e_bodytype, true, false));
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	/* quarantine reason */
6467c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg != NULL)
6477c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n",
6487c478bd9Sstevel@tonic-gate 				     denlstring(e->e_quarmsg, true, false));
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	/* message from envelope, if it exists */
6517c478bd9Sstevel@tonic-gate 	if (e->e_message != NULL)
6527c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
6537c478bd9Sstevel@tonic-gate 				     denlstring(e->e_message, true, false));
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	/* send various flag bits through */
6567c478bd9Sstevel@tonic-gate 	p = buf;
6577c478bd9Sstevel@tonic-gate 	if (bitset(EF_WARNING, e->e_flags))
6587c478bd9Sstevel@tonic-gate 		*p++ = 'w';
6597c478bd9Sstevel@tonic-gate 	if (bitset(EF_RESPONSE, e->e_flags))
6607c478bd9Sstevel@tonic-gate 		*p++ = 'r';
6617c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS8BIT, e->e_flags))
6627c478bd9Sstevel@tonic-gate 		*p++ = '8';
6637c478bd9Sstevel@tonic-gate 	if (bitset(EF_DELETE_BCC, e->e_flags))
6647c478bd9Sstevel@tonic-gate 		*p++ = 'b';
6657c478bd9Sstevel@tonic-gate 	if (bitset(EF_RET_PARAM, e->e_flags))
6667c478bd9Sstevel@tonic-gate 		*p++ = 'd';
6677c478bd9Sstevel@tonic-gate 	if (bitset(EF_NO_BODY_RETN, e->e_flags))
6687c478bd9Sstevel@tonic-gate 		*p++ = 'n';
6697c478bd9Sstevel@tonic-gate 	if (bitset(EF_SPLIT, e->e_flags))
6707c478bd9Sstevel@tonic-gate 		*p++ = 's';
6717c478bd9Sstevel@tonic-gate 	*p++ = '\0';
6727c478bd9Sstevel@tonic-gate 	if (buf[0] != '\0')
6737c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf);
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	/* save $={persistentMacros} macro values */
6767c478bd9Sstevel@tonic-gate 	queueup_macros(macid("{persistentMacros}"), tfp, e);
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	/* output name of sender */
6797c478bd9Sstevel@tonic-gate 	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
6807c478bd9Sstevel@tonic-gate 		p = e->e_sender;
6817c478bd9Sstevel@tonic-gate 	else
6827c478bd9Sstevel@tonic-gate 		p = e->e_from.q_paddr;
6837c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n",
6847c478bd9Sstevel@tonic-gate 			     denlstring(p, true, false));
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	/* output ESMTP-supplied "original" information */
6877c478bd9Sstevel@tonic-gate 	if (e->e_envid != NULL)
6887c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n",
6897c478bd9Sstevel@tonic-gate 				     denlstring(e->e_envid, true, false));
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	/* output AUTH= parameter */
6927c478bd9Sstevel@tonic-gate 	if (e->e_auth_param != NULL)
6937c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n",
6947c478bd9Sstevel@tonic-gate 				     denlstring(e->e_auth_param, true, false));
6957c478bd9Sstevel@tonic-gate 	if (e->e_dlvr_flag != 0)
6967c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n",
6977c478bd9Sstevel@tonic-gate 				     (char) e->e_dlvr_flag, e->e_deliver_by);
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	/* output list of recipient addresses */
7007c478bd9Sstevel@tonic-gate 	printctladdr(NULL, NULL);
7017c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
7027c478bd9Sstevel@tonic-gate 	{
7037c478bd9Sstevel@tonic-gate 		if (!QS_IS_UNDELIVERED(q->q_state))
7047c478bd9Sstevel@tonic-gate 			continue;
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 		/* message for this recipient, if it exists */
7077c478bd9Sstevel@tonic-gate 		if (q->q_message != NULL)
7087c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
7097c478bd9Sstevel@tonic-gate 					     denlstring(q->q_message, true,
7107c478bd9Sstevel@tonic-gate 							false));
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate 		printctladdr(q, tfp);
7137c478bd9Sstevel@tonic-gate 		if (q->q_orcpt != NULL)
7147c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n",
7157c478bd9Sstevel@tonic-gate 					     denlstring(q->q_orcpt, true,
7167c478bd9Sstevel@tonic-gate 							false));
7177c478bd9Sstevel@tonic-gate 		if (q->q_finalrcpt != NULL)
7187c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n",
7197c478bd9Sstevel@tonic-gate 					     denlstring(q->q_finalrcpt, true,
7207c478bd9Sstevel@tonic-gate 							false));
7217c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R');
7227c478bd9Sstevel@tonic-gate 		if (bitset(QPRIMARY, q->q_flags))
7237c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P');
7247c478bd9Sstevel@tonic-gate 		if (bitset(QHASNOTIFY, q->q_flags))
7257c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N');
7267c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONSUCCESS, q->q_flags))
7277c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S');
7287c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONFAILURE, q->q_flags))
7297c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F');
7307c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONDELAY, q->q_flags))
7317c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D');
7327c478bd9Sstevel@tonic-gate 		if (q->q_alias != NULL &&
7337c478bd9Sstevel@tonic-gate 		    bitset(QALIAS, q->q_alias->q_flags))
7347c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A');
7357c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':');
7367c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n",
7377c478bd9Sstevel@tonic-gate 				     denlstring(q->q_paddr, true, false));
7387c478bd9Sstevel@tonic-gate 		if (announce)
7397c478bd9Sstevel@tonic-gate 		{
7407c478bd9Sstevel@tonic-gate 			char *tag = "queued";
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 			if (e->e_quarmsg != NULL)
7437c478bd9Sstevel@tonic-gate 				tag = "quarantined";
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 			e->e_to = q->q_paddr;
7467c478bd9Sstevel@tonic-gate 			message(tag);
7477c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
7487c478bd9Sstevel@tonic-gate 				logdelivery(q->q_mailer, NULL, q->q_status,
7497c478bd9Sstevel@tonic-gate 					    tag, NULL, (time_t) 0, e);
7507c478bd9Sstevel@tonic-gate 			e->e_to = NULL;
7517c478bd9Sstevel@tonic-gate 		}
7527c478bd9Sstevel@tonic-gate 		if (tTd(40, 1))
7537c478bd9Sstevel@tonic-gate 		{
7547c478bd9Sstevel@tonic-gate 			sm_dprintf("queueing ");
7557c478bd9Sstevel@tonic-gate 			printaddr(sm_debug_file(), q, false);
7567c478bd9Sstevel@tonic-gate 		}
7577c478bd9Sstevel@tonic-gate 	}
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate 	/*
7607c478bd9Sstevel@tonic-gate 	**  Output headers for this message.
7617c478bd9Sstevel@tonic-gate 	**	Expand macros completely here.  Queue run will deal with
7627c478bd9Sstevel@tonic-gate 	**	everything as absolute headers.
7637c478bd9Sstevel@tonic-gate 	**		All headers that must be relative to the recipient
7647c478bd9Sstevel@tonic-gate 	**		can be cracked later.
7657c478bd9Sstevel@tonic-gate 	**	We set up a "null mailer" -- i.e., a mailer that will have
7667c478bd9Sstevel@tonic-gate 	**	no effect on the addresses as they are output.
7677c478bd9Sstevel@tonic-gate 	*/
7687c478bd9Sstevel@tonic-gate 
7697c478bd9Sstevel@tonic-gate 	memset((char *) &nullmailer, '\0', sizeof nullmailer);
7707c478bd9Sstevel@tonic-gate 	nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
7717c478bd9Sstevel@tonic-gate 			nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
7727c478bd9Sstevel@tonic-gate 	nullmailer.m_eol = "\n";
7737c478bd9Sstevel@tonic-gate 	memset(&mcibuf, '\0', sizeof mcibuf);
7747c478bd9Sstevel@tonic-gate 	mcibuf.mci_mailer = &nullmailer;
7757c478bd9Sstevel@tonic-gate 	mcibuf.mci_out = tfp;
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'g', "\201f");
7787c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
7797c478bd9Sstevel@tonic-gate 	{
7807c478bd9Sstevel@tonic-gate 		if (h->h_value == NULL)
7817c478bd9Sstevel@tonic-gate 			continue;
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate 		/* don't output resent headers on non-resent messages */
7847c478bd9Sstevel@tonic-gate 		if (bitset(H_RESENT, h->h_flags) &&
7857c478bd9Sstevel@tonic-gate 		    !bitset(EF_RESENT, e->e_flags))
7867c478bd9Sstevel@tonic-gate 			continue;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 		/* expand macros; if null, don't output header at all */
7897c478bd9Sstevel@tonic-gate 		if (bitset(H_DEFAULT, h->h_flags))
7907c478bd9Sstevel@tonic-gate 		{
7917c478bd9Sstevel@tonic-gate 			(void) expand(h->h_value, buf, sizeof buf, e);
7927c478bd9Sstevel@tonic-gate 			if (buf[0] == '\0')
7937c478bd9Sstevel@tonic-gate 				continue;
7947c478bd9Sstevel@tonic-gate 		}
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 		/* output this header */
7977c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?");
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 		/* output conditional macro if present */
8007c478bd9Sstevel@tonic-gate 		if (h->h_macro != '\0')
8017c478bd9Sstevel@tonic-gate 		{
8027c478bd9Sstevel@tonic-gate 			if (bitset(0200, h->h_macro))
8037c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
8047c478bd9Sstevel@tonic-gate 						     "${%s}",
8057c478bd9Sstevel@tonic-gate 						      macname(bitidx(h->h_macro)));
8067c478bd9Sstevel@tonic-gate 			else
8077c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
8087c478bd9Sstevel@tonic-gate 						     "$%c", h->h_macro);
8097c478bd9Sstevel@tonic-gate 		}
8107c478bd9Sstevel@tonic-gate 		else if (!bitzerop(h->h_mflags) &&
8117c478bd9Sstevel@tonic-gate 			 bitset(H_CHECK|H_ACHECK, h->h_flags))
8127c478bd9Sstevel@tonic-gate 		{
8137c478bd9Sstevel@tonic-gate 			int j;
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 			/* if conditional, output the set of conditions */
8167c478bd9Sstevel@tonic-gate 			for (j = '\0'; j <= '\177'; j++)
8177c478bd9Sstevel@tonic-gate 				if (bitnset(j, h->h_mflags))
8187c478bd9Sstevel@tonic-gate 					(void) sm_io_putc(tfp, SM_TIME_DEFAULT,
8197c478bd9Sstevel@tonic-gate 							  j);
8207c478bd9Sstevel@tonic-gate 		}
8217c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?');
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 		/* output the header: expand macros, convert addresses */
8247c478bd9Sstevel@tonic-gate 		if (bitset(H_DEFAULT, h->h_flags) &&
8257c478bd9Sstevel@tonic-gate 		    !bitset(H_BINDLATE, h->h_flags))
8267c478bd9Sstevel@tonic-gate 		{
8277c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n",
8287c478bd9Sstevel@tonic-gate 					     h->h_field,
8297c478bd9Sstevel@tonic-gate 					     denlstring(buf, false, true));
8307c478bd9Sstevel@tonic-gate 		}
8317c478bd9Sstevel@tonic-gate 		else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
8327c478bd9Sstevel@tonic-gate 			 !bitset(H_BINDLATE, h->h_flags))
8337c478bd9Sstevel@tonic-gate 		{
8347c478bd9Sstevel@tonic-gate 			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
8357c478bd9Sstevel@tonic-gate 			SM_FILE_T *savetrace = TrafficLogFile;
8367c478bd9Sstevel@tonic-gate 
8377c478bd9Sstevel@tonic-gate 			TrafficLogFile = NULL;
8387c478bd9Sstevel@tonic-gate 
8397c478bd9Sstevel@tonic-gate 			if (bitset(H_FROM, h->h_flags))
8407c478bd9Sstevel@tonic-gate 				oldstyle = false;
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 			commaize(h, h->h_value, oldstyle, &mcibuf, e);
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 			TrafficLogFile = savetrace;
8457c478bd9Sstevel@tonic-gate 		}
8467c478bd9Sstevel@tonic-gate 		else
8477c478bd9Sstevel@tonic-gate 		{
8487c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s: %s\n",
8497c478bd9Sstevel@tonic-gate 					     h->h_field,
8507c478bd9Sstevel@tonic-gate 					     denlstring(h->h_value, false,
8517c478bd9Sstevel@tonic-gate 							true));
8527c478bd9Sstevel@tonic-gate 		}
8537c478bd9Sstevel@tonic-gate 	}
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	/*
8567c478bd9Sstevel@tonic-gate 	**  Clean up.
8577c478bd9Sstevel@tonic-gate 	**
8587c478bd9Sstevel@tonic-gate 	**	Write a terminator record -- this is to prevent
8597c478bd9Sstevel@tonic-gate 	**	scurrilous crackers from appending any data.
8607c478bd9Sstevel@tonic-gate 	*/
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n");
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 ||
8657c478bd9Sstevel@tonic-gate 	    ((SuperSafe == SAFE_REALLY ||
8667c478bd9Sstevel@tonic-gate 	      SuperSafe == SAFE_REALLY_POSTMILTER ||
8677c478bd9Sstevel@tonic-gate 	      (SuperSafe == SAFE_INTERACTIVE && msync)) &&
8687c478bd9Sstevel@tonic-gate 	     fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) ||
8697c478bd9Sstevel@tonic-gate 	    sm_io_error(tfp))
8707c478bd9Sstevel@tonic-gate 	{
8717c478bd9Sstevel@tonic-gate 		if (newid)
8727c478bd9Sstevel@tonic-gate 			syserr("!552 Error writing control file %s", tf);
8737c478bd9Sstevel@tonic-gate 		else
8747c478bd9Sstevel@tonic-gate 			syserr("!452 Error writing control file %s", tf);
8757c478bd9Sstevel@tonic-gate 	}
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate 	if (!newid)
8787c478bd9Sstevel@tonic-gate 	{
8797c478bd9Sstevel@tonic-gate 		char new = queue_letter(e, ANYQFL_LETTER);
8807c478bd9Sstevel@tonic-gate 
8817c478bd9Sstevel@tonic-gate 		/* rename (locked) tf to be (locked) [qh]f */
8827c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER),
8837c478bd9Sstevel@tonic-gate 				  sizeof qf);
8847c478bd9Sstevel@tonic-gate 		if (rename(tf, qf) < 0)
8857c478bd9Sstevel@tonic-gate 			syserr("cannot rename(%s, %s), uid=%d",
8867c478bd9Sstevel@tonic-gate 				tf, qf, (int) geteuid());
8877c478bd9Sstevel@tonic-gate 		else
8887c478bd9Sstevel@tonic-gate 		{
8897c478bd9Sstevel@tonic-gate 			/*
8907c478bd9Sstevel@tonic-gate 			**  Check if type has changed and only
8917c478bd9Sstevel@tonic-gate 			**  remove the old item if the rename above
8927c478bd9Sstevel@tonic-gate 			**  succeeded.
8937c478bd9Sstevel@tonic-gate 			*/
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 			if (e->e_qfletter != '\0' &&
8967c478bd9Sstevel@tonic-gate 			    e->e_qfletter != new)
8977c478bd9Sstevel@tonic-gate 			{
8987c478bd9Sstevel@tonic-gate 				if (tTd(40, 5))
8997c478bd9Sstevel@tonic-gate 				{
9007c478bd9Sstevel@tonic-gate 					sm_dprintf("type changed from %c to %c\n",
9017c478bd9Sstevel@tonic-gate 						   e->e_qfletter, new);
9027c478bd9Sstevel@tonic-gate 				}
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 				if (unlink(queuename(e, e->e_qfletter)) < 0)
9057c478bd9Sstevel@tonic-gate 				{
9067c478bd9Sstevel@tonic-gate 					/* XXX: something more drastic? */
9077c478bd9Sstevel@tonic-gate 					if (LogLevel > 0)
9087c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
9097c478bd9Sstevel@tonic-gate 							  "queueup: unlink(%s) failed: %s",
9107c478bd9Sstevel@tonic-gate 							  queuename(e, e->e_qfletter),
9117c478bd9Sstevel@tonic-gate 							  sm_errstring(errno));
9127c478bd9Sstevel@tonic-gate 				}
9137c478bd9Sstevel@tonic-gate 			}
9147c478bd9Sstevel@tonic-gate 		}
9157c478bd9Sstevel@tonic-gate 		e->e_qfletter = new;
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 		/*
9187c478bd9Sstevel@tonic-gate 		**  fsync() after renaming to make sure metadata is
9197c478bd9Sstevel@tonic-gate 		**  written to disk on filesystems in which renames are
9207c478bd9Sstevel@tonic-gate 		**  not guaranteed.
9217c478bd9Sstevel@tonic-gate 		*/
9227c478bd9Sstevel@tonic-gate 
9237c478bd9Sstevel@tonic-gate 		if (SuperSafe != SAFE_NO)
9247c478bd9Sstevel@tonic-gate 		{
9257c478bd9Sstevel@tonic-gate 			/* for softupdates */
9267c478bd9Sstevel@tonic-gate 			if (tfd >= 0 && fsync(tfd) < 0)
9277c478bd9Sstevel@tonic-gate 			{
9287c478bd9Sstevel@tonic-gate 				syserr("!queueup: cannot fsync queue temp file %s",
9297c478bd9Sstevel@tonic-gate 				       tf);
9307c478bd9Sstevel@tonic-gate 			}
9317c478bd9Sstevel@tonic-gate 			SYNC_DIR(qf, true);
9327c478bd9Sstevel@tonic-gate 		}
9337c478bd9Sstevel@tonic-gate 
9347c478bd9Sstevel@tonic-gate 		/* close and unlock old (locked) queue file */
9357c478bd9Sstevel@tonic-gate 		if (e->e_lockfp != NULL)
9367c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
9377c478bd9Sstevel@tonic-gate 		e->e_lockfp = tfp;
9387c478bd9Sstevel@tonic-gate 
9397c478bd9Sstevel@tonic-gate 		/* save log info */
9407c478bd9Sstevel@tonic-gate 		if (LogLevel > 79)
9417c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf);
9427c478bd9Sstevel@tonic-gate 	}
9437c478bd9Sstevel@tonic-gate 	else
9447c478bd9Sstevel@tonic-gate 	{
9457c478bd9Sstevel@tonic-gate 		/* save log info */
9467c478bd9Sstevel@tonic-gate 		if (LogLevel > 79)
9477c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf);
9487c478bd9Sstevel@tonic-gate 
9497c478bd9Sstevel@tonic-gate 		e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
9507c478bd9Sstevel@tonic-gate 	}
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	errno = 0;
9537c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_INQUEUE;
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
9567c478bd9Sstevel@tonic-gate 		sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
9577c478bd9Sstevel@tonic-gate 	return;
9587c478bd9Sstevel@tonic-gate }
9597c478bd9Sstevel@tonic-gate 
9607c478bd9Sstevel@tonic-gate /*
9617c478bd9Sstevel@tonic-gate **  PRINTCTLADDR -- print control address to file.
9627c478bd9Sstevel@tonic-gate **
9637c478bd9Sstevel@tonic-gate **	Parameters:
9647c478bd9Sstevel@tonic-gate **		a -- address.
9657c478bd9Sstevel@tonic-gate **		tfp -- file pointer.
9667c478bd9Sstevel@tonic-gate **
9677c478bd9Sstevel@tonic-gate **	Returns:
9687c478bd9Sstevel@tonic-gate **		none.
9697c478bd9Sstevel@tonic-gate **
9707c478bd9Sstevel@tonic-gate **	Side Effects:
9717c478bd9Sstevel@tonic-gate **		The control address (if changed) is printed to the file.
9727c478bd9Sstevel@tonic-gate **		The last control address and uid are saved.
9737c478bd9Sstevel@tonic-gate */
9747c478bd9Sstevel@tonic-gate 
9757c478bd9Sstevel@tonic-gate static void
9767c478bd9Sstevel@tonic-gate printctladdr(a, tfp)
9777c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
9787c478bd9Sstevel@tonic-gate 	SM_FILE_T *tfp;
9797c478bd9Sstevel@tonic-gate {
9807c478bd9Sstevel@tonic-gate 	char *user;
9817c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
9827c478bd9Sstevel@tonic-gate 	uid_t uid;
9837c478bd9Sstevel@tonic-gate 	gid_t gid;
9847c478bd9Sstevel@tonic-gate 	static ADDRESS *lastctladdr = NULL;
9857c478bd9Sstevel@tonic-gate 	static uid_t lastuid;
9867c478bd9Sstevel@tonic-gate 
9877c478bd9Sstevel@tonic-gate 	/* initialization */
9887c478bd9Sstevel@tonic-gate 	if (a == NULL || a->q_alias == NULL || tfp == NULL)
9897c478bd9Sstevel@tonic-gate 	{
9907c478bd9Sstevel@tonic-gate 		if (lastctladdr != NULL && tfp != NULL)
9917c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n");
9927c478bd9Sstevel@tonic-gate 		lastctladdr = NULL;
9937c478bd9Sstevel@tonic-gate 		lastuid = 0;
9947c478bd9Sstevel@tonic-gate 		return;
9957c478bd9Sstevel@tonic-gate 	}
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	/* find the active uid */
9987c478bd9Sstevel@tonic-gate 	q = getctladdr(a);
9997c478bd9Sstevel@tonic-gate 	if (q == NULL)
10007c478bd9Sstevel@tonic-gate 	{
10017c478bd9Sstevel@tonic-gate 		user = NULL;
10027c478bd9Sstevel@tonic-gate 		uid = 0;
10037c478bd9Sstevel@tonic-gate 		gid = 0;
10047c478bd9Sstevel@tonic-gate 	}
10057c478bd9Sstevel@tonic-gate 	else
10067c478bd9Sstevel@tonic-gate 	{
10077c478bd9Sstevel@tonic-gate 		user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
10087c478bd9Sstevel@tonic-gate 		uid = q->q_uid;
10097c478bd9Sstevel@tonic-gate 		gid = q->q_gid;
10107c478bd9Sstevel@tonic-gate 	}
10117c478bd9Sstevel@tonic-gate 	a = a->q_alias;
10127c478bd9Sstevel@tonic-gate 
10137c478bd9Sstevel@tonic-gate 	/* check to see if this is the same as last time */
10147c478bd9Sstevel@tonic-gate 	if (lastctladdr != NULL && uid == lastuid &&
10157c478bd9Sstevel@tonic-gate 	    strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
10167c478bd9Sstevel@tonic-gate 		return;
10177c478bd9Sstevel@tonic-gate 	lastuid = uid;
10187c478bd9Sstevel@tonic-gate 	lastctladdr = a;
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 	if (uid == 0 || user == NULL || user[0] == '\0')
10217c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C");
10227c478bd9Sstevel@tonic-gate 	else
10237c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld",
10247c478bd9Sstevel@tonic-gate 				     denlstring(user, true, false), (long) uid,
10257c478bd9Sstevel@tonic-gate 				     (long) gid);
10267c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n",
10277c478bd9Sstevel@tonic-gate 			     denlstring(a->q_paddr, true, false));
10287c478bd9Sstevel@tonic-gate }
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate /*
10317c478bd9Sstevel@tonic-gate **  RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process
10327c478bd9Sstevel@tonic-gate **
10337c478bd9Sstevel@tonic-gate **	This propagates the signal to the child processes that are queue
10347c478bd9Sstevel@tonic-gate **	runners. This is for a queue runner "cleanup". After all of the
10357c478bd9Sstevel@tonic-gate **	child queue runner processes are signaled (it should be SIGTERM
10367c478bd9Sstevel@tonic-gate **	being the sig) then the old signal handler (Oldsh) is called
10377c478bd9Sstevel@tonic-gate **	to handle any cleanup set for this process (provided it is not
10387c478bd9Sstevel@tonic-gate **	SIG_DFL or SIG_IGN). The signal may not be handled immediately
10397c478bd9Sstevel@tonic-gate **	if the BlockOldsh flag is set. If the current process doesn't
10407c478bd9Sstevel@tonic-gate **	have a parent then handle the signal immediately, regardless of
10417c478bd9Sstevel@tonic-gate **	BlockOldsh.
10427c478bd9Sstevel@tonic-gate **
10437c478bd9Sstevel@tonic-gate **	Parameters:
10447c478bd9Sstevel@tonic-gate **		sig -- the signal number being sent
10457c478bd9Sstevel@tonic-gate **
10467c478bd9Sstevel@tonic-gate **	Returns:
10477c478bd9Sstevel@tonic-gate **		none.
10487c478bd9Sstevel@tonic-gate **
10497c478bd9Sstevel@tonic-gate **	Side Effects:
10507c478bd9Sstevel@tonic-gate **		Sets the NoMoreRunners boolean to true to stop more runners
10517c478bd9Sstevel@tonic-gate **		from being started in runqueue().
10527c478bd9Sstevel@tonic-gate **
10537c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
10547c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
10557c478bd9Sstevel@tonic-gate **		DOING.
10567c478bd9Sstevel@tonic-gate */
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate static bool		volatile NoMoreRunners = false;
10597c478bd9Sstevel@tonic-gate static sigfunc_t	Oldsh_term = SIG_DFL;
10607c478bd9Sstevel@tonic-gate static sigfunc_t	Oldsh_hup = SIG_DFL;
10617c478bd9Sstevel@tonic-gate static sigfunc_t	volatile Oldsh = SIG_DFL;
10627c478bd9Sstevel@tonic-gate static bool		BlockOldsh = false;
10637c478bd9Sstevel@tonic-gate static int		volatile Oldsig = 0;
10647c478bd9Sstevel@tonic-gate static SIGFUNC_DECL	runners_sigterm __P((int));
10657c478bd9Sstevel@tonic-gate static SIGFUNC_DECL	runners_sighup __P((int));
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate static SIGFUNC_DECL
10687c478bd9Sstevel@tonic-gate runners_sigterm(sig)
10697c478bd9Sstevel@tonic-gate 	int sig;
10707c478bd9Sstevel@tonic-gate {
10717c478bd9Sstevel@tonic-gate 	int save_errno = errno;
10727c478bd9Sstevel@tonic-gate 
10737c478bd9Sstevel@tonic-gate 	FIX_SYSV_SIGNAL(sig, runners_sigterm);
10747c478bd9Sstevel@tonic-gate 	errno = save_errno;
10757c478bd9Sstevel@tonic-gate 	CHECK_CRITICAL(sig);
10767c478bd9Sstevel@tonic-gate 	NoMoreRunners = true;
10777c478bd9Sstevel@tonic-gate 	Oldsh = Oldsh_term;
10787c478bd9Sstevel@tonic-gate 	Oldsig = sig;
10797c478bd9Sstevel@tonic-gate 	proc_list_signal(PROC_QUEUE, sig);
10807c478bd9Sstevel@tonic-gate 
10817c478bd9Sstevel@tonic-gate 	if (!BlockOldsh || getppid() <= 1)
10827c478bd9Sstevel@tonic-gate 	{
10837c478bd9Sstevel@tonic-gate 		/* Check that a valid 'old signal handler' is callable */
10847c478bd9Sstevel@tonic-gate 		if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN &&
10857c478bd9Sstevel@tonic-gate 		    Oldsh_term != runners_sigterm)
10867c478bd9Sstevel@tonic-gate 			(*Oldsh_term)(sig);
10877c478bd9Sstevel@tonic-gate 	}
10887c478bd9Sstevel@tonic-gate 	errno = save_errno;
10897c478bd9Sstevel@tonic-gate 	return SIGFUNC_RETURN;
10907c478bd9Sstevel@tonic-gate }
10917c478bd9Sstevel@tonic-gate /*
10927c478bd9Sstevel@tonic-gate **  RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process
10937c478bd9Sstevel@tonic-gate **
10947c478bd9Sstevel@tonic-gate **	This propagates the signal to the child processes that are queue
10957c478bd9Sstevel@tonic-gate **	runners. This is for a queue runner "cleanup". After all of the
10967c478bd9Sstevel@tonic-gate **	child queue runner processes are signaled (it should be SIGHUP
10977c478bd9Sstevel@tonic-gate **	being the sig) then the old signal handler (Oldsh) is called to
10987c478bd9Sstevel@tonic-gate **	handle any cleanup set for this process (provided it is not SIG_DFL
10997c478bd9Sstevel@tonic-gate **	or SIG_IGN). The signal may not be handled immediately if the
11007c478bd9Sstevel@tonic-gate **	BlockOldsh flag is set. If the current process doesn't have
11017c478bd9Sstevel@tonic-gate **	a parent then handle the signal immediately, regardless of
11027c478bd9Sstevel@tonic-gate **	BlockOldsh.
11037c478bd9Sstevel@tonic-gate **
11047c478bd9Sstevel@tonic-gate **	Parameters:
11057c478bd9Sstevel@tonic-gate **		sig -- the signal number being sent
11067c478bd9Sstevel@tonic-gate **
11077c478bd9Sstevel@tonic-gate **	Returns:
11087c478bd9Sstevel@tonic-gate **		none.
11097c478bd9Sstevel@tonic-gate **
11107c478bd9Sstevel@tonic-gate **	Side Effects:
11117c478bd9Sstevel@tonic-gate **		Sets the NoMoreRunners boolean to true to stop more runners
11127c478bd9Sstevel@tonic-gate **		from being started in runqueue().
11137c478bd9Sstevel@tonic-gate **
11147c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11157c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
11167c478bd9Sstevel@tonic-gate **		DOING.
11177c478bd9Sstevel@tonic-gate */
11187c478bd9Sstevel@tonic-gate 
11197c478bd9Sstevel@tonic-gate static SIGFUNC_DECL
11207c478bd9Sstevel@tonic-gate runners_sighup(sig)
11217c478bd9Sstevel@tonic-gate 	int sig;
11227c478bd9Sstevel@tonic-gate {
11237c478bd9Sstevel@tonic-gate 	int save_errno = errno;
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	FIX_SYSV_SIGNAL(sig, runners_sighup);
11267c478bd9Sstevel@tonic-gate 	errno = save_errno;
11277c478bd9Sstevel@tonic-gate 	CHECK_CRITICAL(sig);
11287c478bd9Sstevel@tonic-gate 	NoMoreRunners = true;
11297c478bd9Sstevel@tonic-gate 	Oldsh = Oldsh_hup;
11307c478bd9Sstevel@tonic-gate 	Oldsig = sig;
11317c478bd9Sstevel@tonic-gate 	proc_list_signal(PROC_QUEUE, sig);
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate 	if (!BlockOldsh || getppid() <= 1)
11347c478bd9Sstevel@tonic-gate 	{
11357c478bd9Sstevel@tonic-gate 		/* Check that a valid 'old signal handler' is callable */
11367c478bd9Sstevel@tonic-gate 		if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN &&
11377c478bd9Sstevel@tonic-gate 		    Oldsh_hup != runners_sighup)
11387c478bd9Sstevel@tonic-gate 			(*Oldsh_hup)(sig);
11397c478bd9Sstevel@tonic-gate 	}
11407c478bd9Sstevel@tonic-gate 	errno = save_errno;
11417c478bd9Sstevel@tonic-gate 	return SIGFUNC_RETURN;
11427c478bd9Sstevel@tonic-gate }
11437c478bd9Sstevel@tonic-gate /*
11447c478bd9Sstevel@tonic-gate **  MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart
11457c478bd9Sstevel@tonic-gate **
11467c478bd9Sstevel@tonic-gate **  Sets a workgroup for restarting.
11477c478bd9Sstevel@tonic-gate **
11487c478bd9Sstevel@tonic-gate **	Parameters:
11497c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to restart.
11507c478bd9Sstevel@tonic-gate **		reason -- why (signal?), -1 to turn off restart
11517c478bd9Sstevel@tonic-gate **
11527c478bd9Sstevel@tonic-gate **	Returns:
11537c478bd9Sstevel@tonic-gate **		none.
11547c478bd9Sstevel@tonic-gate **
11557c478bd9Sstevel@tonic-gate **	Side effects:
11567c478bd9Sstevel@tonic-gate **		May set global RestartWorkGroup to true.
11577c478bd9Sstevel@tonic-gate **
11587c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11597c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
11607c478bd9Sstevel@tonic-gate **		DOING.
11617c478bd9Sstevel@tonic-gate */
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate void
11647c478bd9Sstevel@tonic-gate mark_work_group_restart(wgrp, reason)
11657c478bd9Sstevel@tonic-gate 	int wgrp;
11667c478bd9Sstevel@tonic-gate 	int reason;
11677c478bd9Sstevel@tonic-gate {
11687c478bd9Sstevel@tonic-gate 	if (wgrp < 0 || wgrp > NumWorkGroups)
11697c478bd9Sstevel@tonic-gate 		return;
11707c478bd9Sstevel@tonic-gate 
11717c478bd9Sstevel@tonic-gate 	WorkGrp[wgrp].wg_restart = reason;
11727c478bd9Sstevel@tonic-gate 	if (reason >= 0)
11737c478bd9Sstevel@tonic-gate 		RestartWorkGroup = true;
11747c478bd9Sstevel@tonic-gate }
11757c478bd9Sstevel@tonic-gate /*
11767c478bd9Sstevel@tonic-gate **  RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart
11777c478bd9Sstevel@tonic-gate **
11787c478bd9Sstevel@tonic-gate **  Restart any workgroup marked as needing a restart provided more
11797c478bd9Sstevel@tonic-gate **  runners are allowed.
11807c478bd9Sstevel@tonic-gate **
11817c478bd9Sstevel@tonic-gate **	Parameters:
11827c478bd9Sstevel@tonic-gate **		none.
11837c478bd9Sstevel@tonic-gate **
11847c478bd9Sstevel@tonic-gate **	Returns:
11857c478bd9Sstevel@tonic-gate **		none.
11867c478bd9Sstevel@tonic-gate **
11877c478bd9Sstevel@tonic-gate **	Side effects:
11887c478bd9Sstevel@tonic-gate **		Sets global RestartWorkGroup to false.
11897c478bd9Sstevel@tonic-gate */
11907c478bd9Sstevel@tonic-gate 
11917c478bd9Sstevel@tonic-gate void
11927c478bd9Sstevel@tonic-gate restart_marked_work_groups()
11937c478bd9Sstevel@tonic-gate {
11947c478bd9Sstevel@tonic-gate 	int i;
11957c478bd9Sstevel@tonic-gate 	int wasblocked;
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate 	if (NoMoreRunners)
11987c478bd9Sstevel@tonic-gate 		return;
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate 	/* Block SIGCHLD so reapchild() doesn't mess with us */
12017c478bd9Sstevel@tonic-gate 	wasblocked = sm_blocksignal(SIGCHLD);
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumWorkGroups; i++)
12047c478bd9Sstevel@tonic-gate 	{
12057c478bd9Sstevel@tonic-gate 		if (WorkGrp[i].wg_restart >= 0)
12067c478bd9Sstevel@tonic-gate 		{
12077c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
12087c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
12097c478bd9Sstevel@tonic-gate 					  "restart queue runner=%d due to signal 0x%x",
12107c478bd9Sstevel@tonic-gate 					  i, WorkGrp[i].wg_restart);
12117c478bd9Sstevel@tonic-gate 			restart_work_group(i);
12127c478bd9Sstevel@tonic-gate 		}
12137c478bd9Sstevel@tonic-gate 	}
12147c478bd9Sstevel@tonic-gate 	RestartWorkGroup = false;
12157c478bd9Sstevel@tonic-gate 
12167c478bd9Sstevel@tonic-gate 	if (wasblocked == 0)
12177c478bd9Sstevel@tonic-gate 		(void) sm_releasesignal(SIGCHLD);
12187c478bd9Sstevel@tonic-gate }
12197c478bd9Sstevel@tonic-gate /*
12207c478bd9Sstevel@tonic-gate **  RESTART_WORK_GROUP -- restart a specific work group
12217c478bd9Sstevel@tonic-gate **
12227c478bd9Sstevel@tonic-gate **  Restart a specific workgroup provided more runners are allowed.
12237c478bd9Sstevel@tonic-gate **  If the requested work group has been restarted too many times log
12247c478bd9Sstevel@tonic-gate **  this and refuse to restart.
12257c478bd9Sstevel@tonic-gate **
12267c478bd9Sstevel@tonic-gate **	Parameters:
12277c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to restart
12287c478bd9Sstevel@tonic-gate **
12297c478bd9Sstevel@tonic-gate **	Returns:
12307c478bd9Sstevel@tonic-gate **		none.
12317c478bd9Sstevel@tonic-gate **
12327c478bd9Sstevel@tonic-gate **	Side Effects:
12337c478bd9Sstevel@tonic-gate **		starts another process doing the work of wgrp
12347c478bd9Sstevel@tonic-gate */
12357c478bd9Sstevel@tonic-gate 
12367c478bd9Sstevel@tonic-gate #define MAX_PERSIST_RESTART	10	/* max allowed number of restarts */
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate static void
12397c478bd9Sstevel@tonic-gate restart_work_group(wgrp)
12407c478bd9Sstevel@tonic-gate 	int wgrp;
12417c478bd9Sstevel@tonic-gate {
12427c478bd9Sstevel@tonic-gate 	if (NoMoreRunners ||
12437c478bd9Sstevel@tonic-gate 	    wgrp < 0 || wgrp > NumWorkGroups)
12447c478bd9Sstevel@tonic-gate 		return;
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 	WorkGrp[wgrp].wg_restart = -1;
12477c478bd9Sstevel@tonic-gate 	if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART)
12487c478bd9Sstevel@tonic-gate 	{
12497c478bd9Sstevel@tonic-gate 		/* avoid overflow; increment here */
12507c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_restartcnt++;
12517c478bd9Sstevel@tonic-gate 		(void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL);
12527c478bd9Sstevel@tonic-gate 	}
12537c478bd9Sstevel@tonic-gate 	else
12547c478bd9Sstevel@tonic-gate 	{
12557c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID,
12567c478bd9Sstevel@tonic-gate 			  "ERROR: persistent queue runner=%d restarted too many times, queue runner lost",
12577c478bd9Sstevel@tonic-gate 			  wgrp);
12587c478bd9Sstevel@tonic-gate 	}
12597c478bd9Sstevel@tonic-gate }
12607c478bd9Sstevel@tonic-gate /*
12617c478bd9Sstevel@tonic-gate **  SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group.
12627c478bd9Sstevel@tonic-gate **
12637c478bd9Sstevel@tonic-gate **	Parameters:
12647c478bd9Sstevel@tonic-gate **		runall -- schedule even if individual bit is not set.
12657c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to schedule.
12667c478bd9Sstevel@tonic-gate **		didit -- the queue run was performed for this work group.
12677c478bd9Sstevel@tonic-gate **
12687c478bd9Sstevel@tonic-gate **	Returns:
12697c478bd9Sstevel@tonic-gate **		nothing
12707c478bd9Sstevel@tonic-gate */
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate #define INCR_MOD(v, m)	if (++v >= m)	\
12737c478bd9Sstevel@tonic-gate 				v = 0;	\
12747c478bd9Sstevel@tonic-gate 			else
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate static void
12777c478bd9Sstevel@tonic-gate schedule_queue_runs(runall, wgrp, didit)
12787c478bd9Sstevel@tonic-gate 	bool runall;
12797c478bd9Sstevel@tonic-gate 	int wgrp;
12807c478bd9Sstevel@tonic-gate 	bool didit;
12817c478bd9Sstevel@tonic-gate {
12827c478bd9Sstevel@tonic-gate 	int qgrp, cgrp, endgrp;
12837c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
12847c478bd9Sstevel@tonic-gate 	time_t lastsched;
12857c478bd9Sstevel@tonic-gate 	bool sched;
12867c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
12877c478bd9Sstevel@tonic-gate 	time_t now;
12887c478bd9Sstevel@tonic-gate 	time_t minqintvl;
12897c478bd9Sstevel@tonic-gate 
12907c478bd9Sstevel@tonic-gate 	/*
12917c478bd9Sstevel@tonic-gate 	**  This is a bit ugly since we have to duplicate the
12927c478bd9Sstevel@tonic-gate 	**  code that "walks" through a work queue group.
12937c478bd9Sstevel@tonic-gate 	*/
12947c478bd9Sstevel@tonic-gate 
12957c478bd9Sstevel@tonic-gate 	now = curtime();
12967c478bd9Sstevel@tonic-gate 	minqintvl = 0;
12977c478bd9Sstevel@tonic-gate 	cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp;
12987c478bd9Sstevel@tonic-gate 	do
12997c478bd9Sstevel@tonic-gate 	{
13007c478bd9Sstevel@tonic-gate 		time_t qintvl;
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13037c478bd9Sstevel@tonic-gate 		lastsched = 0;
13047c478bd9Sstevel@tonic-gate 		sched = false;
13057c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13067c478bd9Sstevel@tonic-gate 		qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index;
13077c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_queueintvl > 0)
13087c478bd9Sstevel@tonic-gate 			qintvl = Queue[qgrp]->qg_queueintvl;
13097c478bd9Sstevel@tonic-gate 		else if (QueueIntvl > 0)
13107c478bd9Sstevel@tonic-gate 			qintvl = QueueIntvl;
13117c478bd9Sstevel@tonic-gate 		else
13127c478bd9Sstevel@tonic-gate 			qintvl = (time_t) 0;
13137c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13147c478bd9Sstevel@tonic-gate 		lastsched = Queue[qgrp]->qg_nextrun;
13157c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13167c478bd9Sstevel@tonic-gate 		if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0)
13177c478bd9Sstevel@tonic-gate 		{
13187c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13197c478bd9Sstevel@tonic-gate 			sched = true;
13207c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13217c478bd9Sstevel@tonic-gate 			if (minqintvl == 0 || qintvl < minqintvl)
13227c478bd9Sstevel@tonic-gate 				minqintvl = qintvl;
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 			/*
13257c478bd9Sstevel@tonic-gate 			**  Only set a new time if a queue run was performed
13267c478bd9Sstevel@tonic-gate 			**  for this queue group.  If the queue was not run,
13277c478bd9Sstevel@tonic-gate 			**  we could starve it by setting a new time on each
13287c478bd9Sstevel@tonic-gate 			**  call.
13297c478bd9Sstevel@tonic-gate 			*/
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 			if (didit)
13327c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_nextrun += qintvl;
13337c478bd9Sstevel@tonic-gate 		}
13347c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13357c478bd9Sstevel@tonic-gate 		if (tTd(69, 10))
13367c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
13377c478bd9Sstevel@tonic-gate 				"sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d",
13387c478bd9Sstevel@tonic-gate 				wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl,
13397c478bd9Sstevel@tonic-gate 				QueueIntvl, runall, lastsched,
13407c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_nextrun, sched);
13417c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13427c478bd9Sstevel@tonic-gate 		INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp);
13437c478bd9Sstevel@tonic-gate 	} while (endgrp != cgrp);
13447c478bd9Sstevel@tonic-gate 	if (minqintvl > 0)
13457c478bd9Sstevel@tonic-gate 		(void) sm_setevent(minqintvl, runqueueevent, 0);
13467c478bd9Sstevel@tonic-gate }
13477c478bd9Sstevel@tonic-gate 
13487c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_RUN_PARANOIA
13497c478bd9Sstevel@tonic-gate /*
13507c478bd9Sstevel@tonic-gate **  CHECKQUEUERUNNER -- check whether a queue group hasn't been run.
13517c478bd9Sstevel@tonic-gate **
13527c478bd9Sstevel@tonic-gate **	Use this if events may get lost and hence queue runners may not
13537c478bd9Sstevel@tonic-gate **	be started and mail will pile up in a queue.
13547c478bd9Sstevel@tonic-gate **
13557c478bd9Sstevel@tonic-gate **	Parameters:
13567c478bd9Sstevel@tonic-gate **		none.
13577c478bd9Sstevel@tonic-gate **
13587c478bd9Sstevel@tonic-gate **	Returns:
13597c478bd9Sstevel@tonic-gate **		true if a queue run is necessary.
13607c478bd9Sstevel@tonic-gate **
13617c478bd9Sstevel@tonic-gate **	Side Effects:
13627c478bd9Sstevel@tonic-gate **		may schedule a queue run.
13637c478bd9Sstevel@tonic-gate */
13647c478bd9Sstevel@tonic-gate 
13657c478bd9Sstevel@tonic-gate bool
13667c478bd9Sstevel@tonic-gate checkqueuerunner()
13677c478bd9Sstevel@tonic-gate {
13687c478bd9Sstevel@tonic-gate 	int qgrp;
13697c478bd9Sstevel@tonic-gate 	time_t now, minqintvl;
13707c478bd9Sstevel@tonic-gate 
13717c478bd9Sstevel@tonic-gate 	now = curtime();
13727c478bd9Sstevel@tonic-gate 	minqintvl = 0;
13737c478bd9Sstevel@tonic-gate 	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
13747c478bd9Sstevel@tonic-gate 	{
13757c478bd9Sstevel@tonic-gate 		time_t qintvl;
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_queueintvl > 0)
13787c478bd9Sstevel@tonic-gate 			qintvl = Queue[qgrp]->qg_queueintvl;
13797c478bd9Sstevel@tonic-gate 		else if (QueueIntvl > 0)
13807c478bd9Sstevel@tonic-gate 			qintvl = QueueIntvl;
13817c478bd9Sstevel@tonic-gate 		else
13827c478bd9Sstevel@tonic-gate 			qintvl = (time_t) 0;
13837c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_nextrun <= now - qintvl)
13847c478bd9Sstevel@tonic-gate 		{
13857c478bd9Sstevel@tonic-gate 			if (minqintvl == 0 || qintvl < minqintvl)
13867c478bd9Sstevel@tonic-gate 				minqintvl = qintvl;
13877c478bd9Sstevel@tonic-gate 			if (LogLevel > 1)
13887c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, NOQID,
13897c478bd9Sstevel@tonic-gate 					"checkqueuerunner: queue %d should have been run at %s, queue interval %ld",
13907c478bd9Sstevel@tonic-gate 					qgrp,
13917c478bd9Sstevel@tonic-gate 					arpadate(ctime(&Queue[qgrp]->qg_nextrun)),
13927c478bd9Sstevel@tonic-gate 					qintvl);
13937c478bd9Sstevel@tonic-gate 		}
13947c478bd9Sstevel@tonic-gate 	}
13957c478bd9Sstevel@tonic-gate 	if (minqintvl > 0)
13967c478bd9Sstevel@tonic-gate 	{
13977c478bd9Sstevel@tonic-gate 		(void) sm_setevent(minqintvl, runqueueevent, 0);
13987c478bd9Sstevel@tonic-gate 		return true;
13997c478bd9Sstevel@tonic-gate 	}
14007c478bd9Sstevel@tonic-gate 	return false;
14017c478bd9Sstevel@tonic-gate }
14027c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_RUN_PARANOIA */
14037c478bd9Sstevel@tonic-gate 
14047c478bd9Sstevel@tonic-gate /*
14057c478bd9Sstevel@tonic-gate **  RUNQUEUE -- run the jobs in the queue.
14067c478bd9Sstevel@tonic-gate **
14077c478bd9Sstevel@tonic-gate **	Gets the stuff out of the queue in some presumably logical
14087c478bd9Sstevel@tonic-gate **	order and processes them.
14097c478bd9Sstevel@tonic-gate **
14107c478bd9Sstevel@tonic-gate **	Parameters:
14117c478bd9Sstevel@tonic-gate **		forkflag -- true if the queue scanning should be done in
14127c478bd9Sstevel@tonic-gate **			a child process.  We double-fork so it is not our
14137c478bd9Sstevel@tonic-gate **			child and we don't have to clean up after it.
14147c478bd9Sstevel@tonic-gate **			false can be ignored if we have multiple queues.
14157c478bd9Sstevel@tonic-gate **		verbose -- if true, print out status information.
14167c478bd9Sstevel@tonic-gate **		persistent -- persistent queue runner?
14177c478bd9Sstevel@tonic-gate **		runall -- run all groups or only a subset (DoQueueRun)?
14187c478bd9Sstevel@tonic-gate **
14197c478bd9Sstevel@tonic-gate **	Returns:
14207c478bd9Sstevel@tonic-gate **		true if the queue run successfully began.
14217c478bd9Sstevel@tonic-gate **
14227c478bd9Sstevel@tonic-gate **	Side Effects:
14237c478bd9Sstevel@tonic-gate **		runs things in the mail queue using run_work_group().
14247c478bd9Sstevel@tonic-gate **		maybe schedules next queue run.
14257c478bd9Sstevel@tonic-gate */
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate static ENVELOPE	QueueEnvelope;		/* the queue run envelope */
14287c478bd9Sstevel@tonic-gate static time_t	LastQueueTime = 0;	/* last time a queue ID assigned */
14297c478bd9Sstevel@tonic-gate static pid_t	LastQueuePid = -1;	/* last PID which had a queue ID */
14307c478bd9Sstevel@tonic-gate 
14317c478bd9Sstevel@tonic-gate /* values for qp_supdirs */
14327c478bd9Sstevel@tonic-gate #define QP_NOSUB	0x0000	/* No subdirectories */
14337c478bd9Sstevel@tonic-gate #define QP_SUBDF	0x0001	/* "df" subdirectory */
14347c478bd9Sstevel@tonic-gate #define QP_SUBQF	0x0002	/* "qf" subdirectory */
14357c478bd9Sstevel@tonic-gate #define QP_SUBXF	0x0004	/* "xf" subdirectory */
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate bool
14387c478bd9Sstevel@tonic-gate runqueue(forkflag, verbose, persistent, runall)
14397c478bd9Sstevel@tonic-gate 	bool forkflag;
14407c478bd9Sstevel@tonic-gate 	bool verbose;
14417c478bd9Sstevel@tonic-gate 	bool persistent;
14427c478bd9Sstevel@tonic-gate 	bool runall;
14437c478bd9Sstevel@tonic-gate {
14447c478bd9Sstevel@tonic-gate 	int i;
14457c478bd9Sstevel@tonic-gate 	bool ret = true;
14467c478bd9Sstevel@tonic-gate 	static int curnum = 0;
14477c478bd9Sstevel@tonic-gate 	sigfunc_t cursh;
14487c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
14497c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE int oldgroup = 0;
14507c478bd9Sstevel@tonic-gate 
14517c478bd9Sstevel@tonic-gate 	if (sm_debug_active(&DebugLeakQ, 1))
14527c478bd9Sstevel@tonic-gate 	{
14537c478bd9Sstevel@tonic-gate 		oldgroup = sm_heap_group();
14547c478bd9Sstevel@tonic-gate 		sm_heap_newgroup();
14557c478bd9Sstevel@tonic-gate 		sm_dprintf("runqueue() heap group #%d\n", sm_heap_group());
14567c478bd9Sstevel@tonic-gate 	}
14577c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
14587c478bd9Sstevel@tonic-gate 
14597c478bd9Sstevel@tonic-gate 	/* queue run has been started, don't do any more this time */
14607c478bd9Sstevel@tonic-gate 	DoQueueRun = false;
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 	/* more than one queue or more than one directory per queue */
14637c478bd9Sstevel@tonic-gate 	if (!forkflag && !verbose &&
14647c478bd9Sstevel@tonic-gate 	    (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 ||
14657c478bd9Sstevel@tonic-gate 	     WorkGrp[0].wg_numqgrp > 1))
14667c478bd9Sstevel@tonic-gate 		forkflag = true;
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 	/*
14697c478bd9Sstevel@tonic-gate 	**  For controlling queue runners via signals sent to this process.
14707c478bd9Sstevel@tonic-gate 	**  Oldsh* will get called too by runners_sig* (if it is not SIG_IGN
14717c478bd9Sstevel@tonic-gate 	**  or SIG_DFL) to preserve cleanup behavior. Now that this process
14727c478bd9Sstevel@tonic-gate 	**  will have children (and perhaps grandchildren) this handler will
14737c478bd9Sstevel@tonic-gate 	**  be left in place. This is because this process, once it has
14747c478bd9Sstevel@tonic-gate 	**  finished spinning off queue runners, may go back to doing something
14757c478bd9Sstevel@tonic-gate 	**  else (like being a daemon). And we still want on a SIG{TERM,HUP} to
14767c478bd9Sstevel@tonic-gate 	**  clean up the child queue runners. Only install 'runners_sig*' once
14777c478bd9Sstevel@tonic-gate 	**  else we'll get stuck looping forever.
14787c478bd9Sstevel@tonic-gate 	*/
14797c478bd9Sstevel@tonic-gate 
14807c478bd9Sstevel@tonic-gate 	cursh = sm_signal(SIGTERM, runners_sigterm);
14817c478bd9Sstevel@tonic-gate 	if (cursh != runners_sigterm)
14827c478bd9Sstevel@tonic-gate 		Oldsh_term = cursh;
14837c478bd9Sstevel@tonic-gate 	cursh = sm_signal(SIGHUP, runners_sighup);
14847c478bd9Sstevel@tonic-gate 	if (cursh != runners_sighup)
14857c478bd9Sstevel@tonic-gate 		Oldsh_hup = cursh;
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++)
14887c478bd9Sstevel@tonic-gate 	{
14897c478bd9Sstevel@tonic-gate 		int rwgflags = RWG_NONE;
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 		/*
14927c478bd9Sstevel@tonic-gate 		**  If MaxQueueChildren active then test whether the start
14937c478bd9Sstevel@tonic-gate 		**  of the next queue group's additional queue runners (maximum)
14947c478bd9Sstevel@tonic-gate 		**  will result in MaxQueueChildren being exceeded.
14957c478bd9Sstevel@tonic-gate 		**
14967c478bd9Sstevel@tonic-gate 		**  Note: do not use continue; even though another workgroup
14977c478bd9Sstevel@tonic-gate 		**	may have fewer queue runners, this would be "unfair",
14987c478bd9Sstevel@tonic-gate 		**	i.e., this work group might "starve" then.
14997c478bd9Sstevel@tonic-gate 		*/
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
15027c478bd9Sstevel@tonic-gate 		if (tTd(69, 10))
15037c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
15047c478bd9Sstevel@tonic-gate 				"rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d",
15057c478bd9Sstevel@tonic-gate 				curnum, MaxQueueChildren, CurRunners,
15067c478bd9Sstevel@tonic-gate 				WorkGrp[curnum].wg_maxact);
15077c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
15087c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
15097c478bd9Sstevel@tonic-gate 		    CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren)
15107c478bd9Sstevel@tonic-gate 			break;
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate 		/*
15137c478bd9Sstevel@tonic-gate 		**  Pick up where we left off (curnum), in case we
15147c478bd9Sstevel@tonic-gate 		**  used up all the children last time without finishing.
15157c478bd9Sstevel@tonic-gate 		**  This give a round-robin fairness to queue runs.
15167c478bd9Sstevel@tonic-gate 		**
15177c478bd9Sstevel@tonic-gate 		**  Increment CurRunners before calling run_work_group()
15187c478bd9Sstevel@tonic-gate 		**  to avoid a "race condition" with proc_list_drop() which
15197c478bd9Sstevel@tonic-gate 		**  decrements CurRunners if the queue runners terminate.
15207c478bd9Sstevel@tonic-gate 		**  Notice: CurRunners is an upper limit, in some cases
15217c478bd9Sstevel@tonic-gate 		**  (too few jobs in the queue) this value is larger than
15227c478bd9Sstevel@tonic-gate 		**  the actual number of queue runners. The discrepancy can
15237c478bd9Sstevel@tonic-gate 		**  increase if some queue runners "hang" for a long time.
15247c478bd9Sstevel@tonic-gate 		*/
15257c478bd9Sstevel@tonic-gate 
15267c478bd9Sstevel@tonic-gate 		CurRunners += WorkGrp[curnum].wg_maxact;
15277c478bd9Sstevel@tonic-gate 		if (forkflag)
15287c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_FORK;
15297c478bd9Sstevel@tonic-gate 		if (verbose)
15307c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_VERBOSE;
15317c478bd9Sstevel@tonic-gate 		if (persistent)
15327c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_PERSISTENT;
15337c478bd9Sstevel@tonic-gate 		if (runall)
15347c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_RUNALL;
15357c478bd9Sstevel@tonic-gate 		ret = run_work_group(curnum, rwgflags);
15367c478bd9Sstevel@tonic-gate 
15377c478bd9Sstevel@tonic-gate 		/*
15387c478bd9Sstevel@tonic-gate 		**  Failure means a message was printed for ETRN
15397c478bd9Sstevel@tonic-gate 		**  and subsequent queues are likely to fail as well.
15407c478bd9Sstevel@tonic-gate 		**  Decrement CurRunners in that case because
15417c478bd9Sstevel@tonic-gate 		**  none have been started.
15427c478bd9Sstevel@tonic-gate 		*/
15437c478bd9Sstevel@tonic-gate 
15447c478bd9Sstevel@tonic-gate 		if (!ret)
15457c478bd9Sstevel@tonic-gate 		{
15467c478bd9Sstevel@tonic-gate 			CurRunners -= WorkGrp[curnum].wg_maxact;
15477c478bd9Sstevel@tonic-gate 			break;
15487c478bd9Sstevel@tonic-gate 		}
15497c478bd9Sstevel@tonic-gate 
15507c478bd9Sstevel@tonic-gate 		if (!persistent)
15517c478bd9Sstevel@tonic-gate 			schedule_queue_runs(runall, curnum, true);
15527c478bd9Sstevel@tonic-gate 		INCR_MOD(curnum, NumWorkGroups);
15537c478bd9Sstevel@tonic-gate 	}
15547c478bd9Sstevel@tonic-gate 
15557c478bd9Sstevel@tonic-gate 	/* schedule left over queue runs */
15567c478bd9Sstevel@tonic-gate 	if (i < NumWorkGroups && !NoMoreRunners && !persistent)
15577c478bd9Sstevel@tonic-gate 	{
15587c478bd9Sstevel@tonic-gate 		int h;
15597c478bd9Sstevel@tonic-gate 
15607c478bd9Sstevel@tonic-gate 		for (h = curnum; i < NumWorkGroups; i++)
15617c478bd9Sstevel@tonic-gate 		{
15627c478bd9Sstevel@tonic-gate 			schedule_queue_runs(runall, h, false);
15637c478bd9Sstevel@tonic-gate 			INCR_MOD(h, NumWorkGroups);
15647c478bd9Sstevel@tonic-gate 		}
15657c478bd9Sstevel@tonic-gate 	}
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate 
15687c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
15697c478bd9Sstevel@tonic-gate 	if (sm_debug_active(&DebugLeakQ, 1))
15707c478bd9Sstevel@tonic-gate 		sm_heap_setgroup(oldgroup);
15717c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
15727c478bd9Sstevel@tonic-gate 	return ret;
15737c478bd9Sstevel@tonic-gate }
15747c478bd9Sstevel@tonic-gate 
15757c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
15767c478bd9Sstevel@tonic-gate /*
15777c478bd9Sstevel@tonic-gate **  SKIP_DOMAINS -- Skip 'skip' number of domains in the WorkQ.
15787c478bd9Sstevel@tonic-gate **
15797c478bd9Sstevel@tonic-gate **  Added by Stephen Frost <sfrost@snowman.net> to support
15807c478bd9Sstevel@tonic-gate **  having each runner process every N'th domain instead of
15817c478bd9Sstevel@tonic-gate **  every N'th message.
15827c478bd9Sstevel@tonic-gate **
15837c478bd9Sstevel@tonic-gate **	Parameters:
15847c478bd9Sstevel@tonic-gate **		skip -- number of domains in WorkQ to skip.
15857c478bd9Sstevel@tonic-gate **
15867c478bd9Sstevel@tonic-gate **	Returns:
15877c478bd9Sstevel@tonic-gate **		total number of messages skipped.
15887c478bd9Sstevel@tonic-gate **
15897c478bd9Sstevel@tonic-gate **	Side Effects:
15907c478bd9Sstevel@tonic-gate **		may change WorkQ
15917c478bd9Sstevel@tonic-gate */
15927c478bd9Sstevel@tonic-gate 
15937c478bd9Sstevel@tonic-gate static int
15947c478bd9Sstevel@tonic-gate skip_domains(skip)
15957c478bd9Sstevel@tonic-gate 	int skip;
15967c478bd9Sstevel@tonic-gate {
15977c478bd9Sstevel@tonic-gate 	int n, seqjump;
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate 	for (n = 0, seqjump = 0; n < skip && WorkQ != NULL; seqjump++)
16007c478bd9Sstevel@tonic-gate 	{
16017c478bd9Sstevel@tonic-gate 		if (WorkQ->w_next != NULL)
16027c478bd9Sstevel@tonic-gate 		{
16037c478bd9Sstevel@tonic-gate 			if (WorkQ->w_host != NULL &&
16047c478bd9Sstevel@tonic-gate 			    WorkQ->w_next->w_host != NULL)
16057c478bd9Sstevel@tonic-gate 			{
16067c478bd9Sstevel@tonic-gate 				if (sm_strcasecmp(WorkQ->w_host,
16077c478bd9Sstevel@tonic-gate 						WorkQ->w_next->w_host) != 0)
16087c478bd9Sstevel@tonic-gate 					n++;
16097c478bd9Sstevel@tonic-gate 			}
16107c478bd9Sstevel@tonic-gate 			else
16117c478bd9Sstevel@tonic-gate 			{
16127c478bd9Sstevel@tonic-gate 				if ((WorkQ->w_host != NULL &&
16137c478bd9Sstevel@tonic-gate 				     WorkQ->w_next->w_host == NULL) ||
16147c478bd9Sstevel@tonic-gate 				    (WorkQ->w_host == NULL &&
16157c478bd9Sstevel@tonic-gate 				     WorkQ->w_next->w_host != NULL))
16167c478bd9Sstevel@tonic-gate 					     n++;
16177c478bd9Sstevel@tonic-gate 			}
16187c478bd9Sstevel@tonic-gate 		}
16197c478bd9Sstevel@tonic-gate 		WorkQ = WorkQ->w_next;
16207c478bd9Sstevel@tonic-gate 	}
16217c478bd9Sstevel@tonic-gate 	return seqjump;
16227c478bd9Sstevel@tonic-gate }
16237c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
16247c478bd9Sstevel@tonic-gate 
16257c478bd9Sstevel@tonic-gate /*
16267c478bd9Sstevel@tonic-gate **  RUNNER_WORK -- have a queue runner do its work
16277c478bd9Sstevel@tonic-gate **
16287c478bd9Sstevel@tonic-gate **  Have a queue runner do its work a list of entries.
16297c478bd9Sstevel@tonic-gate **  When work isn't directly being done then this process can take a signal
16307c478bd9Sstevel@tonic-gate **  and terminate immediately (in a clean fashion of course).
16317c478bd9Sstevel@tonic-gate **  When work is directly being done, it's not to be interrupted
16327c478bd9Sstevel@tonic-gate **  immediately: the work should be allowed to finish at a clean point
16337c478bd9Sstevel@tonic-gate **  before termination (in a clean fashion of course).
16347c478bd9Sstevel@tonic-gate **
16357c478bd9Sstevel@tonic-gate **	Parameters:
16367c478bd9Sstevel@tonic-gate **		e -- envelope.
16377c478bd9Sstevel@tonic-gate **		sequenceno -- 'th process to run WorkQ.
16387c478bd9Sstevel@tonic-gate **		didfork -- did the calling process fork()?
16397c478bd9Sstevel@tonic-gate **		skip -- process only each skip'th item.
16407c478bd9Sstevel@tonic-gate **		njobs -- number of jobs in WorkQ.
16417c478bd9Sstevel@tonic-gate **
16427c478bd9Sstevel@tonic-gate **	Returns:
16437c478bd9Sstevel@tonic-gate **		none.
16447c478bd9Sstevel@tonic-gate **
16457c478bd9Sstevel@tonic-gate **	Side Effects:
16467c478bd9Sstevel@tonic-gate **		runs things in the mail queue.
16477c478bd9Sstevel@tonic-gate */
16487c478bd9Sstevel@tonic-gate 
16497c478bd9Sstevel@tonic-gate static void
16507c478bd9Sstevel@tonic-gate runner_work(e, sequenceno, didfork, skip, njobs)
16517c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
16527c478bd9Sstevel@tonic-gate 	int sequenceno;
16537c478bd9Sstevel@tonic-gate 	bool didfork;
16547c478bd9Sstevel@tonic-gate 	int skip;
16557c478bd9Sstevel@tonic-gate 	int njobs;
16567c478bd9Sstevel@tonic-gate {
16577c478bd9Sstevel@tonic-gate 	int n, seqjump;
16587c478bd9Sstevel@tonic-gate 	WORK *w;
16597c478bd9Sstevel@tonic-gate 	time_t now;
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 	SM_GET_LA(now);
16627c478bd9Sstevel@tonic-gate 
16637c478bd9Sstevel@tonic-gate 	/*
16647c478bd9Sstevel@tonic-gate 	**  Here we temporarily block the second calling of the handlers.
16657c478bd9Sstevel@tonic-gate 	**  This allows us to handle the signal without terminating in the
16667c478bd9Sstevel@tonic-gate 	**  middle of direct work. If a signal does come, the test for
16677c478bd9Sstevel@tonic-gate 	**  NoMoreRunners will find it.
16687c478bd9Sstevel@tonic-gate 	*/
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate 	BlockOldsh = true;
16717c478bd9Sstevel@tonic-gate 	seqjump = skip;
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 	/* process them once at a time */
16747c478bd9Sstevel@tonic-gate 	while (WorkQ != NULL)
16757c478bd9Sstevel@tonic-gate 	{
16767c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
16777c478bd9Sstevel@tonic-gate 		SM_NONVOLATILE int oldgroup = 0;
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&DebugLeakQ, 1))
16807c478bd9Sstevel@tonic-gate 		{
16817c478bd9Sstevel@tonic-gate 			oldgroup = sm_heap_group();
16827c478bd9Sstevel@tonic-gate 			sm_heap_newgroup();
16837c478bd9Sstevel@tonic-gate 			sm_dprintf("run_queue_group() heap group #%d\n",
16847c478bd9Sstevel@tonic-gate 				sm_heap_group());
16857c478bd9Sstevel@tonic-gate 		}
16867c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 		/* do no more work */
16897c478bd9Sstevel@tonic-gate 		if (NoMoreRunners)
16907c478bd9Sstevel@tonic-gate 		{
16917c478bd9Sstevel@tonic-gate 			/* Check that a valid signal handler is callable */
16927c478bd9Sstevel@tonic-gate 			if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
16937c478bd9Sstevel@tonic-gate 			    Oldsh != runners_sighup &&
16947c478bd9Sstevel@tonic-gate 			    Oldsh != runners_sigterm)
16957c478bd9Sstevel@tonic-gate 				(*Oldsh)(Oldsig);
16967c478bd9Sstevel@tonic-gate 			break;
16977c478bd9Sstevel@tonic-gate 		}
16987c478bd9Sstevel@tonic-gate 
16997c478bd9Sstevel@tonic-gate 		w = WorkQ; /* assign current work item */
17007c478bd9Sstevel@tonic-gate 
17017c478bd9Sstevel@tonic-gate 		/*
17027c478bd9Sstevel@tonic-gate 		**  Set the head of the WorkQ to the next work item.
17037c478bd9Sstevel@tonic-gate 		**  It is set 'skip' ahead (the number of parallel queue
17047c478bd9Sstevel@tonic-gate 		**  runners working on WorkQ together) since each runner
17057c478bd9Sstevel@tonic-gate 		**  works on every 'skip'th (N-th) item.
17067c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
17077c478bd9Sstevel@tonic-gate 		**  In the case of the BYHOST Queue Sort Order, the 'item'
17087c478bd9Sstevel@tonic-gate 		**  is a domain, so we work on every 'skip'th (N-th) domain.
17097c478bd9Sstevel@tonic-gate #endif * _FFR_SKIP_DOMAINS *
17107c478bd9Sstevel@tonic-gate 		*/
17117c478bd9Sstevel@tonic-gate 
17127c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
17137c478bd9Sstevel@tonic-gate 		if (QueueSortOrder == QSO_BYHOST)
17147c478bd9Sstevel@tonic-gate 		{
17157c478bd9Sstevel@tonic-gate 			seqjump = 1;
17167c478bd9Sstevel@tonic-gate 			if (WorkQ->w_next != NULL)
17177c478bd9Sstevel@tonic-gate 			{
17187c478bd9Sstevel@tonic-gate 				if (WorkQ->w_host != NULL &&
17197c478bd9Sstevel@tonic-gate 				    WorkQ->w_next->w_host != NULL)
17207c478bd9Sstevel@tonic-gate 				{
17217c478bd9Sstevel@tonic-gate 					if (sm_strcasecmp(WorkQ->w_host,
17227c478bd9Sstevel@tonic-gate 							WorkQ->w_next->w_host)
17237c478bd9Sstevel@tonic-gate 								!= 0)
17247c478bd9Sstevel@tonic-gate 						seqjump = skip_domains(skip);
17257c478bd9Sstevel@tonic-gate 					else
17267c478bd9Sstevel@tonic-gate 						WorkQ = WorkQ->w_next;
17277c478bd9Sstevel@tonic-gate 				}
17287c478bd9Sstevel@tonic-gate 				else
17297c478bd9Sstevel@tonic-gate 				{
17307c478bd9Sstevel@tonic-gate 					if ((WorkQ->w_host != NULL &&
17317c478bd9Sstevel@tonic-gate 					     WorkQ->w_next->w_host == NULL) ||
17327c478bd9Sstevel@tonic-gate 					    (WorkQ->w_host == NULL &&
17337c478bd9Sstevel@tonic-gate 					     WorkQ->w_next->w_host != NULL))
17347c478bd9Sstevel@tonic-gate 						seqjump = skip_domains(skip);
17357c478bd9Sstevel@tonic-gate 					else
17367c478bd9Sstevel@tonic-gate 						WorkQ = WorkQ->w_next;
17377c478bd9Sstevel@tonic-gate 				}
17387c478bd9Sstevel@tonic-gate 			}
17397c478bd9Sstevel@tonic-gate 			else
17407c478bd9Sstevel@tonic-gate 				WorkQ = WorkQ->w_next;
17417c478bd9Sstevel@tonic-gate 		}
17427c478bd9Sstevel@tonic-gate 		else
17437c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
17447c478bd9Sstevel@tonic-gate 		{
17457c478bd9Sstevel@tonic-gate 			for (n = 0; n < skip && WorkQ != NULL; n++)
17467c478bd9Sstevel@tonic-gate 				WorkQ = WorkQ->w_next;
17477c478bd9Sstevel@tonic-gate 		}
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate 		e->e_to = NULL;
17507c478bd9Sstevel@tonic-gate 
17517c478bd9Sstevel@tonic-gate 		/*
17527c478bd9Sstevel@tonic-gate 		**  Ignore jobs that are too expensive for the moment.
17537c478bd9Sstevel@tonic-gate 		**
17547c478bd9Sstevel@tonic-gate 		**	Get new load average every GET_NEW_LA_TIME seconds.
17557c478bd9Sstevel@tonic-gate 		*/
17567c478bd9Sstevel@tonic-gate 
17577c478bd9Sstevel@tonic-gate 		SM_GET_LA(now);
17587c478bd9Sstevel@tonic-gate 		if (shouldqueue(WkRecipFact, Current_LA_time))
17597c478bd9Sstevel@tonic-gate 		{
17607c478bd9Sstevel@tonic-gate 			char *msg = "Aborting queue run: load average too high";
17617c478bd9Sstevel@tonic-gate 
17627c478bd9Sstevel@tonic-gate 			if (Verbose)
17637c478bd9Sstevel@tonic-gate 				message("%s", msg);
17647c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
17657c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
17667c478bd9Sstevel@tonic-gate 			break;
17677c478bd9Sstevel@tonic-gate 		}
17687c478bd9Sstevel@tonic-gate 		if (shouldqueue(w->w_pri, w->w_ctime))
17697c478bd9Sstevel@tonic-gate 		{
17707c478bd9Sstevel@tonic-gate 			if (Verbose)
17717c478bd9Sstevel@tonic-gate 				message(EmptyString);
17727c478bd9Sstevel@tonic-gate 			if (QueueSortOrder == QSO_BYPRIORITY)
17737c478bd9Sstevel@tonic-gate 			{
17747c478bd9Sstevel@tonic-gate 				if (Verbose)
17757c478bd9Sstevel@tonic-gate 					message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
17767c478bd9Sstevel@tonic-gate 						qid_printqueue(w->w_qgrp,
17777c478bd9Sstevel@tonic-gate 							       w->w_qdir),
17787c478bd9Sstevel@tonic-gate 						w->w_name + 2, sequenceno,
17797c478bd9Sstevel@tonic-gate 						njobs);
17807c478bd9Sstevel@tonic-gate 				if (LogLevel > 8)
17817c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
17827c478bd9Sstevel@tonic-gate 						  "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
17837c478bd9Sstevel@tonic-gate 						  qid_printqueue(w->w_qgrp,
17847c478bd9Sstevel@tonic-gate 								 w->w_qdir),
17857c478bd9Sstevel@tonic-gate 						  w->w_name + 2, w->w_pri,
17867c478bd9Sstevel@tonic-gate 						  CurrentLA, sequenceno,
17877c478bd9Sstevel@tonic-gate 						  njobs);
17887c478bd9Sstevel@tonic-gate 				break;
17897c478bd9Sstevel@tonic-gate 			}
17907c478bd9Sstevel@tonic-gate 			else if (Verbose)
17917c478bd9Sstevel@tonic-gate 				message("Skipping %s/%s (sequence %d of %d)",
17927c478bd9Sstevel@tonic-gate 					qid_printqueue(w->w_qgrp, w->w_qdir),
17937c478bd9Sstevel@tonic-gate 					w->w_name + 2, sequenceno, njobs);
17947c478bd9Sstevel@tonic-gate 		}
17957c478bd9Sstevel@tonic-gate 		else
17967c478bd9Sstevel@tonic-gate 		{
17977c478bd9Sstevel@tonic-gate 			if (Verbose)
17987c478bd9Sstevel@tonic-gate 			{
17997c478bd9Sstevel@tonic-gate 				message(EmptyString);
18007c478bd9Sstevel@tonic-gate 				message("Running %s/%s (sequence %d of %d)",
18017c478bd9Sstevel@tonic-gate 					qid_printqueue(w->w_qgrp, w->w_qdir),
18027c478bd9Sstevel@tonic-gate 					w->w_name + 2, sequenceno, njobs);
18037c478bd9Sstevel@tonic-gate 			}
18047c478bd9Sstevel@tonic-gate 			if (didfork && MaxQueueChildren > 0)
18057c478bd9Sstevel@tonic-gate 			{
18067c478bd9Sstevel@tonic-gate 				sm_blocksignal(SIGCHLD);
18077c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, reapchild);
18087c478bd9Sstevel@tonic-gate 			}
18097c478bd9Sstevel@tonic-gate 			if (tTd(63, 100))
18107c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_DEBUG, NOQID,
18117c478bd9Sstevel@tonic-gate 					  "runqueue %s dowork(%s)",
18127c478bd9Sstevel@tonic-gate 					  qid_printqueue(w->w_qgrp, w->w_qdir),
18137c478bd9Sstevel@tonic-gate 					  w->w_name + 2);
18147c478bd9Sstevel@tonic-gate 
18157c478bd9Sstevel@tonic-gate 			(void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
18167c478bd9Sstevel@tonic-gate 				      ForkQueueRuns, false, e);
18177c478bd9Sstevel@tonic-gate 			errno = 0;
18187c478bd9Sstevel@tonic-gate 		}
18197c478bd9Sstevel@tonic-gate 		sm_free(w->w_name); /* XXX */
18207c478bd9Sstevel@tonic-gate 		if (w->w_host != NULL)
18217c478bd9Sstevel@tonic-gate 			sm_free(w->w_host); /* XXX */
18227c478bd9Sstevel@tonic-gate 		sm_free((char *) w); /* XXX */
18237c478bd9Sstevel@tonic-gate 		sequenceno += seqjump; /* next sequence number */
18247c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
18257c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&DebugLeakQ, 1))
18267c478bd9Sstevel@tonic-gate 			sm_heap_setgroup(oldgroup);
18277c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
18287c478bd9Sstevel@tonic-gate 	}
18297c478bd9Sstevel@tonic-gate 
18307c478bd9Sstevel@tonic-gate 	BlockOldsh = false;
18317c478bd9Sstevel@tonic-gate 
18327c478bd9Sstevel@tonic-gate 	/* check the signals didn't happen during the revert */
18337c478bd9Sstevel@tonic-gate 	if (NoMoreRunners)
18347c478bd9Sstevel@tonic-gate 	{
18357c478bd9Sstevel@tonic-gate 		/* Check that a valid signal handler is callable */
18367c478bd9Sstevel@tonic-gate 		if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
18377c478bd9Sstevel@tonic-gate 		    Oldsh != runners_sighup && Oldsh != runners_sigterm)
18387c478bd9Sstevel@tonic-gate 			(*Oldsh)(Oldsig);
18397c478bd9Sstevel@tonic-gate 	}
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 	Oldsh = SIG_DFL; /* after the NoMoreRunners check */
18427c478bd9Sstevel@tonic-gate }
18437c478bd9Sstevel@tonic-gate /*
18447c478bd9Sstevel@tonic-gate **  RUN_WORK_GROUP -- run the jobs in a queue group from a work group.
18457c478bd9Sstevel@tonic-gate **
18467c478bd9Sstevel@tonic-gate **	Gets the stuff out of the queue in some presumably logical
18477c478bd9Sstevel@tonic-gate **	order and processes them.
18487c478bd9Sstevel@tonic-gate **
18497c478bd9Sstevel@tonic-gate **	Parameters:
18507c478bd9Sstevel@tonic-gate **		wgrp -- work group to process.
18517c478bd9Sstevel@tonic-gate **		flags -- RWG_* flags
18527c478bd9Sstevel@tonic-gate **
18537c478bd9Sstevel@tonic-gate **	Returns:
18547c478bd9Sstevel@tonic-gate **		true if the queue run successfully began.
18557c478bd9Sstevel@tonic-gate **
18567c478bd9Sstevel@tonic-gate **	Side Effects:
18577c478bd9Sstevel@tonic-gate **		runs things in the mail queue.
18587c478bd9Sstevel@tonic-gate */
18597c478bd9Sstevel@tonic-gate 
18607c478bd9Sstevel@tonic-gate /* Minimum sleep time for persistent queue runners */
18617c478bd9Sstevel@tonic-gate #define MIN_SLEEP_TIME	5
18627c478bd9Sstevel@tonic-gate 
18637c478bd9Sstevel@tonic-gate bool
18647c478bd9Sstevel@tonic-gate run_work_group(wgrp, flags)
18657c478bd9Sstevel@tonic-gate 	int wgrp;
18667c478bd9Sstevel@tonic-gate 	int flags;
18677c478bd9Sstevel@tonic-gate {
18687c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
18697c478bd9Sstevel@tonic-gate 	int njobs, qdir;
18707c478bd9Sstevel@tonic-gate 	int sequenceno = 1;
18717c478bd9Sstevel@tonic-gate 	int qgrp, endgrp, h, i;
18727c478bd9Sstevel@tonic-gate 	time_t now;
18737c478bd9Sstevel@tonic-gate 	bool full, more;
18747c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
18757c478bd9Sstevel@tonic-gate 	extern void rmexpstab __P((void));
18767c478bd9Sstevel@tonic-gate 	extern ENVELOPE BlankEnvelope;
18777c478bd9Sstevel@tonic-gate 	extern SIGFUNC_DECL reapchild __P((int));
18787c478bd9Sstevel@tonic-gate 
18797c478bd9Sstevel@tonic-gate 	if (wgrp < 0)
18807c478bd9Sstevel@tonic-gate 		return false;
18817c478bd9Sstevel@tonic-gate 
18827c478bd9Sstevel@tonic-gate 	/*
18837c478bd9Sstevel@tonic-gate 	**  If no work will ever be selected, don't even bother reading
18847c478bd9Sstevel@tonic-gate 	**  the queue.
18857c478bd9Sstevel@tonic-gate 	*/
18867c478bd9Sstevel@tonic-gate 
18877c478bd9Sstevel@tonic-gate 	SM_GET_LA(now);
18887c478bd9Sstevel@tonic-gate 
18897c478bd9Sstevel@tonic-gate 	if (!bitset(RWG_PERSISTENT, flags) &&
18907c478bd9Sstevel@tonic-gate 	    shouldqueue(WkRecipFact, Current_LA_time))
18917c478bd9Sstevel@tonic-gate 	{
18927c478bd9Sstevel@tonic-gate 		char *msg = "Skipping queue run -- load average too high";
18937c478bd9Sstevel@tonic-gate 
18947c478bd9Sstevel@tonic-gate 		if (bitset(RWG_VERBOSE, flags))
18957c478bd9Sstevel@tonic-gate 			message("458 %s\n", msg);
18967c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
18977c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
18987c478bd9Sstevel@tonic-gate 		return false;
18997c478bd9Sstevel@tonic-gate 	}
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate 	/*
19027c478bd9Sstevel@tonic-gate 	**  See if we already have too many children.
19037c478bd9Sstevel@tonic-gate 	*/
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags) &&
19067c478bd9Sstevel@tonic-gate 	    WorkGrp[wgrp].wg_lowqintvl > 0 &&
19077c478bd9Sstevel@tonic-gate 	    !bitset(RWG_PERSISTENT, flags) &&
19087c478bd9Sstevel@tonic-gate 	    MaxChildren > 0 && CurChildren >= MaxChildren)
19097c478bd9Sstevel@tonic-gate 	{
19107c478bd9Sstevel@tonic-gate 		char *msg = "Skipping queue run -- too many children";
19117c478bd9Sstevel@tonic-gate 
19127c478bd9Sstevel@tonic-gate 		if (bitset(RWG_VERBOSE, flags))
19137c478bd9Sstevel@tonic-gate 			message("458 %s (%d)\n", msg, CurChildren);
19147c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
19157c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)",
19167c478bd9Sstevel@tonic-gate 				  msg, CurChildren);
19177c478bd9Sstevel@tonic-gate 		return false;
19187c478bd9Sstevel@tonic-gate 	}
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 	/*
19217c478bd9Sstevel@tonic-gate 	**  See if we want to go off and do other useful work.
19227c478bd9Sstevel@tonic-gate 	*/
19237c478bd9Sstevel@tonic-gate 
19247c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
19257c478bd9Sstevel@tonic-gate 	{
19267c478bd9Sstevel@tonic-gate 		pid_t pid;
19277c478bd9Sstevel@tonic-gate 
19287c478bd9Sstevel@tonic-gate 		(void) sm_blocksignal(SIGCHLD);
19297c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGCHLD, reapchild);
19307c478bd9Sstevel@tonic-gate 
19317c478bd9Sstevel@tonic-gate 		pid = dofork();
19327c478bd9Sstevel@tonic-gate 		if (pid == -1)
19337c478bd9Sstevel@tonic-gate 		{
19347c478bd9Sstevel@tonic-gate 			const char *msg = "Skipping queue run -- fork() failed";
19357c478bd9Sstevel@tonic-gate 			const char *err = sm_errstring(errno);
19367c478bd9Sstevel@tonic-gate 
19377c478bd9Sstevel@tonic-gate 			if (bitset(RWG_VERBOSE, flags))
19387c478bd9Sstevel@tonic-gate 				message("458 %s: %s\n", msg, err);
19397c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
19407c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s",
19417c478bd9Sstevel@tonic-gate 					  msg, err);
19427c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGCHLD);
19437c478bd9Sstevel@tonic-gate 			return false;
19447c478bd9Sstevel@tonic-gate 		}
19457c478bd9Sstevel@tonic-gate 		if (pid != 0)
19467c478bd9Sstevel@tonic-gate 		{
19477c478bd9Sstevel@tonic-gate 			/* parent -- pick up intermediate zombie */
19487c478bd9Sstevel@tonic-gate 			(void) sm_blocksignal(SIGALRM);
19497c478bd9Sstevel@tonic-gate 
19507c478bd9Sstevel@tonic-gate 			/* wgrp only used when queue runners are persistent */
19517c478bd9Sstevel@tonic-gate 			proc_list_add(pid, "Queue runner", PROC_QUEUE,
19527c478bd9Sstevel@tonic-gate 				      WorkGrp[wgrp].wg_maxact,
19537c478bd9Sstevel@tonic-gate 				      bitset(RWG_PERSISTENT, flags) ? wgrp : -1,
19547c478bd9Sstevel@tonic-gate 				      NULL);
19557c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGALRM);
19567c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGCHLD);
19577c478bd9Sstevel@tonic-gate 			return true;
19587c478bd9Sstevel@tonic-gate 		}
19597c478bd9Sstevel@tonic-gate 
19607c478bd9Sstevel@tonic-gate 		/* child -- clean up signals */
19617c478bd9Sstevel@tonic-gate 
19627c478bd9Sstevel@tonic-gate 		/* Reset global flags */
19637c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
19647c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
19657c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
19667c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
19677c478bd9Sstevel@tonic-gate 		CurrentPid = getpid();
19687c478bd9Sstevel@tonic-gate 		close_sendmail_pid();
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate 		/*
19717c478bd9Sstevel@tonic-gate 		**  Initialize exception stack and default exception
19727c478bd9Sstevel@tonic-gate 		**  handler for child process.
19737c478bd9Sstevel@tonic-gate 		*/
19747c478bd9Sstevel@tonic-gate 
19757c478bd9Sstevel@tonic-gate 		sm_exc_newthread(fatal_error);
19767c478bd9Sstevel@tonic-gate 		clrcontrol();
19777c478bd9Sstevel@tonic-gate 		proc_list_clear();
19787c478bd9Sstevel@tonic-gate 
19797c478bd9Sstevel@tonic-gate 		/* Add parent process as first child item */
19807c478bd9Sstevel@tonic-gate 		proc_list_add(CurrentPid, "Queue runner child process",
19817c478bd9Sstevel@tonic-gate 			      PROC_QUEUE_CHILD, 0, -1, NULL);
19827c478bd9Sstevel@tonic-gate 		(void) sm_releasesignal(SIGCHLD);
19837c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGCHLD, SIG_DFL);
19847c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGHUP, SIG_DFL);
19857c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGTERM, intsig);
19867c478bd9Sstevel@tonic-gate 	}
19877c478bd9Sstevel@tonic-gate 
19887c478bd9Sstevel@tonic-gate 	/*
19897c478bd9Sstevel@tonic-gate 	**  Release any resources used by the daemon code.
19907c478bd9Sstevel@tonic-gate 	*/
19917c478bd9Sstevel@tonic-gate 
19927c478bd9Sstevel@tonic-gate 	clrdaemon();
19937c478bd9Sstevel@tonic-gate 
19947c478bd9Sstevel@tonic-gate 	/* force it to run expensive jobs */
19957c478bd9Sstevel@tonic-gate 	NoConnect = false;
19967c478bd9Sstevel@tonic-gate 
19977c478bd9Sstevel@tonic-gate 	/* drop privileges */
19987c478bd9Sstevel@tonic-gate 	if (geteuid() == (uid_t) 0)
19997c478bd9Sstevel@tonic-gate 		(void) drop_privileges(false);
20007c478bd9Sstevel@tonic-gate 
20017c478bd9Sstevel@tonic-gate 	/*
20027c478bd9Sstevel@tonic-gate 	**  Create ourselves an envelope
20037c478bd9Sstevel@tonic-gate 	*/
20047c478bd9Sstevel@tonic-gate 
20057c478bd9Sstevel@tonic-gate 	CurEnv = &QueueEnvelope;
20067c478bd9Sstevel@tonic-gate 	rpool = sm_rpool_new_x(NULL);
20077c478bd9Sstevel@tonic-gate 	e = newenvelope(&QueueEnvelope, CurEnv, rpool);
20087c478bd9Sstevel@tonic-gate 	e->e_flags = BlankEnvelope.e_flags;
20097c478bd9Sstevel@tonic-gate 	e->e_parent = NULL;
20107c478bd9Sstevel@tonic-gate 
20117c478bd9Sstevel@tonic-gate 	/* make sure we have disconnected from parent */
20127c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
20137c478bd9Sstevel@tonic-gate 	{
20147c478bd9Sstevel@tonic-gate 		disconnect(1, e);
20157c478bd9Sstevel@tonic-gate 		QuickAbort = false;
20167c478bd9Sstevel@tonic-gate 	}
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	/*
20197c478bd9Sstevel@tonic-gate 	**  If we are running part of the queue, always ignore stored
20207c478bd9Sstevel@tonic-gate 	**  host status.
20217c478bd9Sstevel@tonic-gate 	*/
20227c478bd9Sstevel@tonic-gate 
20237c478bd9Sstevel@tonic-gate 	if (QueueLimitId != NULL || QueueLimitSender != NULL ||
20247c478bd9Sstevel@tonic-gate 	    QueueLimitQuarantine != NULL ||
20257c478bd9Sstevel@tonic-gate 	    QueueLimitRecipient != NULL)
20267c478bd9Sstevel@tonic-gate 	{
20277c478bd9Sstevel@tonic-gate 		IgnoreHostStatus = true;
20287c478bd9Sstevel@tonic-gate 		MinQueueAge = 0;
20297c478bd9Sstevel@tonic-gate 	}
20307c478bd9Sstevel@tonic-gate 
20317c478bd9Sstevel@tonic-gate 	/*
20327c478bd9Sstevel@tonic-gate 	**  Here is where we choose the queue group from the work group.
20337c478bd9Sstevel@tonic-gate 	**  The caller of the "domorework" label must setup a new envelope.
20347c478bd9Sstevel@tonic-gate 	*/
20357c478bd9Sstevel@tonic-gate 
20367c478bd9Sstevel@tonic-gate 	endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
20377c478bd9Sstevel@tonic-gate 
20387c478bd9Sstevel@tonic-gate   domorework:
20397c478bd9Sstevel@tonic-gate 
20407c478bd9Sstevel@tonic-gate 	/*
20417c478bd9Sstevel@tonic-gate 	**  Run a queue group if:
20427c478bd9Sstevel@tonic-gate 	**  RWG_RUNALL bit is set or the bit for this group is set.
20437c478bd9Sstevel@tonic-gate 	*/
20447c478bd9Sstevel@tonic-gate 
20457c478bd9Sstevel@tonic-gate 	now = curtime();
20467c478bd9Sstevel@tonic-gate 	for (;;)
20477c478bd9Sstevel@tonic-gate 	{
20487c478bd9Sstevel@tonic-gate 		/*
20497c478bd9Sstevel@tonic-gate 		**  Find the next queue group within the work group that
20507c478bd9Sstevel@tonic-gate 		**  has been marked as needing a run.
20517c478bd9Sstevel@tonic-gate 		*/
20527c478bd9Sstevel@tonic-gate 
20537c478bd9Sstevel@tonic-gate 		qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index;
20547c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_curqgrp++; /* advance */
20557c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */
20567c478bd9Sstevel@tonic-gate 		if (bitset(RWG_RUNALL, flags) ||
20577c478bd9Sstevel@tonic-gate 		    (Queue[qgrp]->qg_nextrun <= now &&
20587c478bd9Sstevel@tonic-gate 		     Queue[qgrp]->qg_nextrun != (time_t) -1))
20597c478bd9Sstevel@tonic-gate 			break;
20607c478bd9Sstevel@tonic-gate 		if (endgrp == WorkGrp[wgrp].wg_curqgrp)
20617c478bd9Sstevel@tonic-gate 		{
20627c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
20637c478bd9Sstevel@tonic-gate 			if (bitset(RWG_FORK, flags))
20647c478bd9Sstevel@tonic-gate 				finis(true, true, ExitStat);
20657c478bd9Sstevel@tonic-gate 			return true; /* we're done */
20667c478bd9Sstevel@tonic-gate 		}
20677c478bd9Sstevel@tonic-gate 	}
20687c478bd9Sstevel@tonic-gate 
20697c478bd9Sstevel@tonic-gate 	qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */
20707c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
20717c478bd9Sstevel@tonic-gate 	if (tTd(69, 12))
20727c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
20737c478bd9Sstevel@tonic-gate 			"rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d",
20747c478bd9Sstevel@tonic-gate 			wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir),
20757c478bd9Sstevel@tonic-gate 			WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp);
20767c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
20777c478bd9Sstevel@tonic-gate 
20787c478bd9Sstevel@tonic-gate #if HASNICE
20797c478bd9Sstevel@tonic-gate 	/* tweak niceness of queue runs */
20807c478bd9Sstevel@tonic-gate 	if (Queue[qgrp]->qg_nice > 0)
20817c478bd9Sstevel@tonic-gate 		(void) nice(Queue[qgrp]->qg_nice);
20827c478bd9Sstevel@tonic-gate #endif /* HASNICE */
20837c478bd9Sstevel@tonic-gate 
20847c478bd9Sstevel@tonic-gate 	/* XXX running queue group... */
20857c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, CurEnv, "running queue: %s",
20867c478bd9Sstevel@tonic-gate 			qid_printqueue(qgrp, qdir));
20877c478bd9Sstevel@tonic-gate 
20887c478bd9Sstevel@tonic-gate 	if (LogLevel > 69 || tTd(63, 99))
20897c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, NOQID,
20907c478bd9Sstevel@tonic-gate 			  "runqueue %s, pid=%d, forkflag=%d",
20917c478bd9Sstevel@tonic-gate 			  qid_printqueue(qgrp, qdir), (int) CurrentPid,
20927c478bd9Sstevel@tonic-gate 			  bitset(RWG_FORK, flags));
20937c478bd9Sstevel@tonic-gate 
20947c478bd9Sstevel@tonic-gate 	/*
20957c478bd9Sstevel@tonic-gate 	**  Start making passes through the queue.
20967c478bd9Sstevel@tonic-gate 	**	First, read and sort the entire queue.
20977c478bd9Sstevel@tonic-gate 	**	Then, process the work in that order.
20987c478bd9Sstevel@tonic-gate 	**		But if you take too long, start over.
20997c478bd9Sstevel@tonic-gate 	*/
21007c478bd9Sstevel@tonic-gate 
21017c478bd9Sstevel@tonic-gate 	for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
21027c478bd9Sstevel@tonic-gate 	{
21037c478bd9Sstevel@tonic-gate 		h = gatherq(qgrp, qdir, false, &full, &more);
21047c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
21057c478bd9Sstevel@tonic-gate 		if (ShmId != SM_SHM_NO_ID)
21067c478bd9Sstevel@tonic-gate 			QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h;
21077c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
21087c478bd9Sstevel@tonic-gate 		/* If there are no more items in this queue advance */
21097c478bd9Sstevel@tonic-gate 		if (!more)
21107c478bd9Sstevel@tonic-gate 		{
21117c478bd9Sstevel@tonic-gate 			/* A round-robin advance */
21127c478bd9Sstevel@tonic-gate 			qdir++;
21137c478bd9Sstevel@tonic-gate 			qdir %= Queue[qgrp]->qg_numqueues;
21147c478bd9Sstevel@tonic-gate 		}
21157c478bd9Sstevel@tonic-gate 
21167c478bd9Sstevel@tonic-gate 		/* Has the WorkList reached the limit? */
21177c478bd9Sstevel@tonic-gate 		if (full)
21187c478bd9Sstevel@tonic-gate 			break; /* don't try to gather more */
21197c478bd9Sstevel@tonic-gate 	}
21207c478bd9Sstevel@tonic-gate 
21217c478bd9Sstevel@tonic-gate 	/* order the existing work requests */
21227c478bd9Sstevel@tonic-gate 	njobs = sortq(Queue[qgrp]->qg_maxlist);
21237c478bd9Sstevel@tonic-gate 	Queue[qgrp]->qg_curnum = qdir; /* update */
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate 
21267c478bd9Sstevel@tonic-gate 	if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
21277c478bd9Sstevel@tonic-gate 	{
21287c478bd9Sstevel@tonic-gate 		int loop, maxrunners;
21297c478bd9Sstevel@tonic-gate 		pid_t pid;
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate 		/*
21327c478bd9Sstevel@tonic-gate 		**  For this WorkQ we want to fork off N children (maxrunners)
21337c478bd9Sstevel@tonic-gate 		**  at this point. Each child has a copy of WorkQ. Each child
21347c478bd9Sstevel@tonic-gate 		**  will process every N-th item. The parent will wait for all
21357c478bd9Sstevel@tonic-gate 		**  of the children to finish before moving on to the next
21367c478bd9Sstevel@tonic-gate 		**  queue group within the work group. This saves us forking
21377c478bd9Sstevel@tonic-gate 		**  a new runner-child for each work item.
21387c478bd9Sstevel@tonic-gate 		**  It's valid for qg_maxqrun == 0 since this may be an
21397c478bd9Sstevel@tonic-gate 		**  explicit "don't run this queue" setting.
21407c478bd9Sstevel@tonic-gate 		*/
21417c478bd9Sstevel@tonic-gate 
21427c478bd9Sstevel@tonic-gate 		maxrunners = Queue[qgrp]->qg_maxqrun;
21437c478bd9Sstevel@tonic-gate 
21447c478bd9Sstevel@tonic-gate 		/* No need to have more runners then there are jobs */
21457c478bd9Sstevel@tonic-gate 		if (maxrunners > njobs)
21467c478bd9Sstevel@tonic-gate 			maxrunners = njobs;
21477c478bd9Sstevel@tonic-gate 		for (loop = 0; loop < maxrunners; loop++)
21487c478bd9Sstevel@tonic-gate 		{
21497c478bd9Sstevel@tonic-gate 			/*
21507c478bd9Sstevel@tonic-gate 			**  Since the delivery may happen in a child and the
21517c478bd9Sstevel@tonic-gate 			**  parent does not wait, the parent may close the
21527c478bd9Sstevel@tonic-gate 			**  maps thereby removing any shared memory used by
21537c478bd9Sstevel@tonic-gate 			**  the map.  Therefore, close the maps now so the
21547c478bd9Sstevel@tonic-gate 			**  child will dynamically open them if necessary.
21557c478bd9Sstevel@tonic-gate 			*/
21567c478bd9Sstevel@tonic-gate 
21577c478bd9Sstevel@tonic-gate 			closemaps(false);
21587c478bd9Sstevel@tonic-gate 
21597c478bd9Sstevel@tonic-gate 			pid = fork();
21607c478bd9Sstevel@tonic-gate 			if (pid < 0)
21617c478bd9Sstevel@tonic-gate 			{
21627c478bd9Sstevel@tonic-gate 				syserr("run_work_group: cannot fork");
21637c478bd9Sstevel@tonic-gate 				return false;
21647c478bd9Sstevel@tonic-gate 			}
21657c478bd9Sstevel@tonic-gate 			else if (pid > 0)
21667c478bd9Sstevel@tonic-gate 			{
21677c478bd9Sstevel@tonic-gate 				/* parent -- clean out connection cache */
21687c478bd9Sstevel@tonic-gate 				mci_flush(false, NULL);
21697c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
21707c478bd9Sstevel@tonic-gate 				if (QueueSortOrder == QSO_BYHOST)
21717c478bd9Sstevel@tonic-gate 				{
21727c478bd9Sstevel@tonic-gate 					sequenceno += skip_domains(1);
21737c478bd9Sstevel@tonic-gate 				}
21747c478bd9Sstevel@tonic-gate 				else
21757c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
21767c478bd9Sstevel@tonic-gate 				{
21777c478bd9Sstevel@tonic-gate 					/* for the skip */
21787c478bd9Sstevel@tonic-gate 					WorkQ = WorkQ->w_next;
21797c478bd9Sstevel@tonic-gate 					sequenceno++;
21807c478bd9Sstevel@tonic-gate 				}
21817c478bd9Sstevel@tonic-gate 				proc_list_add(pid, "Queue child runner process",
21827c478bd9Sstevel@tonic-gate 					      PROC_QUEUE_CHILD, 0, -1, NULL);
21837c478bd9Sstevel@tonic-gate 
21847c478bd9Sstevel@tonic-gate 				/* No additional work, no additional runners */
21857c478bd9Sstevel@tonic-gate 				if (WorkQ == NULL)
21867c478bd9Sstevel@tonic-gate 					break;
21877c478bd9Sstevel@tonic-gate 			}
21887c478bd9Sstevel@tonic-gate 			else
21897c478bd9Sstevel@tonic-gate 			{
21907c478bd9Sstevel@tonic-gate 				/* child -- Reset global flags */
21917c478bd9Sstevel@tonic-gate 				RestartRequest = NULL;
21927c478bd9Sstevel@tonic-gate 				RestartWorkGroup = false;
21937c478bd9Sstevel@tonic-gate 				ShutdownRequest = NULL;
21947c478bd9Sstevel@tonic-gate 				PendingSignal = 0;
21957c478bd9Sstevel@tonic-gate 				CurrentPid = getpid();
21967c478bd9Sstevel@tonic-gate 				close_sendmail_pid();
21977c478bd9Sstevel@tonic-gate 
21987c478bd9Sstevel@tonic-gate 				/*
21997c478bd9Sstevel@tonic-gate 				**  Initialize exception stack and default
22007c478bd9Sstevel@tonic-gate 				**  exception handler for child process.
22017c478bd9Sstevel@tonic-gate 				**  When fork()'d the child now has a private
22027c478bd9Sstevel@tonic-gate 				**  copy of WorkQ at its current position.
22037c478bd9Sstevel@tonic-gate 				*/
22047c478bd9Sstevel@tonic-gate 
22057c478bd9Sstevel@tonic-gate 				sm_exc_newthread(fatal_error);
22067c478bd9Sstevel@tonic-gate 
22077c478bd9Sstevel@tonic-gate 				/*
22087c478bd9Sstevel@tonic-gate 				**  SMTP processes (whether -bd or -bs) set
22097c478bd9Sstevel@tonic-gate 				**  SIGCHLD to reapchild to collect
22107c478bd9Sstevel@tonic-gate 				**  children status.  However, at delivery
22117c478bd9Sstevel@tonic-gate 				**  time, that status must be collected
22127c478bd9Sstevel@tonic-gate 				**  by sm_wait() to be dealt with properly
22137c478bd9Sstevel@tonic-gate 				**  (check success of delivery based
22147c478bd9Sstevel@tonic-gate 				**  on status code, etc).  Therefore, if we
22157c478bd9Sstevel@tonic-gate 				**  are an SMTP process, reset SIGCHLD
22167c478bd9Sstevel@tonic-gate 				**  back to the default so reapchild
22177c478bd9Sstevel@tonic-gate 				**  doesn't collect status before
22187c478bd9Sstevel@tonic-gate 				**  sm_wait().
22197c478bd9Sstevel@tonic-gate 				*/
22207c478bd9Sstevel@tonic-gate 
22217c478bd9Sstevel@tonic-gate 				if (OpMode == MD_SMTP ||
22227c478bd9Sstevel@tonic-gate 				    OpMode == MD_DAEMON ||
22237c478bd9Sstevel@tonic-gate 				    MaxQueueChildren > 0)
22247c478bd9Sstevel@tonic-gate 				{
22257c478bd9Sstevel@tonic-gate 					proc_list_clear();
22267c478bd9Sstevel@tonic-gate 					sm_releasesignal(SIGCHLD);
22277c478bd9Sstevel@tonic-gate 					(void) sm_signal(SIGCHLD, SIG_DFL);
22287c478bd9Sstevel@tonic-gate 				}
22297c478bd9Sstevel@tonic-gate 
22307c478bd9Sstevel@tonic-gate 				/* child -- error messages to the transcript */
22317c478bd9Sstevel@tonic-gate 				QuickAbort = OnlyOneError = false;
22327c478bd9Sstevel@tonic-gate 				runner_work(e, sequenceno, true,
22337c478bd9Sstevel@tonic-gate 					    maxrunners, njobs);
22347c478bd9Sstevel@tonic-gate 
22357c478bd9Sstevel@tonic-gate 				/* This child is done */
22367c478bd9Sstevel@tonic-gate 				finis(true, true, ExitStat);
22377c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
22387c478bd9Sstevel@tonic-gate 			}
22397c478bd9Sstevel@tonic-gate 		}
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 		sm_releasesignal(SIGCHLD);
22427c478bd9Sstevel@tonic-gate 
22437c478bd9Sstevel@tonic-gate 		/*
22447c478bd9Sstevel@tonic-gate 		**  Wait until all of the runners have completed before
22457c478bd9Sstevel@tonic-gate 		**  seeing if there is another queue group in the
22467c478bd9Sstevel@tonic-gate 		**  work group to process.
22477c478bd9Sstevel@tonic-gate 		**  XXX Future enhancement: don't wait() for all children
22487c478bd9Sstevel@tonic-gate 		**  here, just go ahead and make sure that overall the number
22497c478bd9Sstevel@tonic-gate 		**  of children is not exceeded.
22507c478bd9Sstevel@tonic-gate 		*/
22517c478bd9Sstevel@tonic-gate 
22527c478bd9Sstevel@tonic-gate 		while (CurChildren > 0)
22537c478bd9Sstevel@tonic-gate 		{
22547c478bd9Sstevel@tonic-gate 			int status;
22557c478bd9Sstevel@tonic-gate 			pid_t ret;
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate 			while ((ret = sm_wait(&status)) <= 0)
22587c478bd9Sstevel@tonic-gate 				continue;
22597c478bd9Sstevel@tonic-gate 			proc_list_drop(ret, status, NULL);
22607c478bd9Sstevel@tonic-gate 		}
22617c478bd9Sstevel@tonic-gate 	}
22627c478bd9Sstevel@tonic-gate 	else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags))
22637c478bd9Sstevel@tonic-gate 	{
22647c478bd9Sstevel@tonic-gate 		/*
22657c478bd9Sstevel@tonic-gate 		**  When current process will not fork children to do the work,
22667c478bd9Sstevel@tonic-gate 		**  it will do the work itself. The 'skip' will be 1 since
22677c478bd9Sstevel@tonic-gate 		**  there are no child runners to divide the work across.
22687c478bd9Sstevel@tonic-gate 		*/
22697c478bd9Sstevel@tonic-gate 
22707c478bd9Sstevel@tonic-gate 		runner_work(e, sequenceno, false, 1, njobs);
22717c478bd9Sstevel@tonic-gate 	}
22727c478bd9Sstevel@tonic-gate 
22737c478bd9Sstevel@tonic-gate 	/* free memory allocated by newenvelope() above */
22747c478bd9Sstevel@tonic-gate 	sm_rpool_free(rpool);
22757c478bd9Sstevel@tonic-gate 	QueueEnvelope.e_rpool = NULL;
22767c478bd9Sstevel@tonic-gate 
22777c478bd9Sstevel@tonic-gate 	/* Are there still more queues in the work group to process? */
22787c478bd9Sstevel@tonic-gate 	if (endgrp != WorkGrp[wgrp].wg_curqgrp)
22797c478bd9Sstevel@tonic-gate 	{
22807c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
22817c478bd9Sstevel@tonic-gate 		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
22827c478bd9Sstevel@tonic-gate 		e->e_flags = BlankEnvelope.e_flags;
22837c478bd9Sstevel@tonic-gate 		goto domorework;
22847c478bd9Sstevel@tonic-gate 	}
22857c478bd9Sstevel@tonic-gate 
22867c478bd9Sstevel@tonic-gate 	/* No more queues in work group to process. Now check persistent. */
22877c478bd9Sstevel@tonic-gate 	if (bitset(RWG_PERSISTENT, flags))
22887c478bd9Sstevel@tonic-gate 	{
22897c478bd9Sstevel@tonic-gate 		sequenceno = 1;
22907c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, CurEnv, "running queue: %s",
22917c478bd9Sstevel@tonic-gate 				qid_printqueue(qgrp, qdir));
22927c478bd9Sstevel@tonic-gate 
22937c478bd9Sstevel@tonic-gate 		/*
22947c478bd9Sstevel@tonic-gate 		**  close bogus maps, i.e., maps which caused a tempfail,
22957c478bd9Sstevel@tonic-gate 		**	so we get fresh map connections on the next lookup.
22967c478bd9Sstevel@tonic-gate 		**  closemaps() is also called when children are started.
22977c478bd9Sstevel@tonic-gate 		*/
22987c478bd9Sstevel@tonic-gate 
22997c478bd9Sstevel@tonic-gate 		closemaps(true);
23007c478bd9Sstevel@tonic-gate 
23017c478bd9Sstevel@tonic-gate 		/* Close any cached connections. */
23027c478bd9Sstevel@tonic-gate 		mci_flush(true, NULL);
23037c478bd9Sstevel@tonic-gate 
23047c478bd9Sstevel@tonic-gate 		/* Clean out expired related entries. */
23057c478bd9Sstevel@tonic-gate 		rmexpstab();
23067c478bd9Sstevel@tonic-gate 
23077c478bd9Sstevel@tonic-gate #if NAMED_BIND
23087c478bd9Sstevel@tonic-gate 		/* Update MX records for FallbackMX. */
23097c478bd9Sstevel@tonic-gate 		if (FallbackMX != NULL)
23107c478bd9Sstevel@tonic-gate 			(void) getfallbackmxrr(FallbackMX);
23117c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate #if USERDB
23147c478bd9Sstevel@tonic-gate 		/* close UserDatabase */
23157c478bd9Sstevel@tonic-gate 		_udbx_close();
23167c478bd9Sstevel@tonic-gate #endif /* USERDB */
23177c478bd9Sstevel@tonic-gate 
23187c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
23197c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&SmHeapCheck, 2)
23207c478bd9Sstevel@tonic-gate 		    && access("memdump", F_OK) == 0
23217c478bd9Sstevel@tonic-gate 		   )
23227c478bd9Sstevel@tonic-gate 		{
23237c478bd9Sstevel@tonic-gate 			SM_FILE_T *out;
23247c478bd9Sstevel@tonic-gate 
23257c478bd9Sstevel@tonic-gate 			remove("memdump");
23267c478bd9Sstevel@tonic-gate 			out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
23277c478bd9Sstevel@tonic-gate 					 "memdump.out", SM_IO_APPEND, NULL);
23287c478bd9Sstevel@tonic-gate 			if (out != NULL)
23297c478bd9Sstevel@tonic-gate 			{
23307c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
23317c478bd9Sstevel@tonic-gate 				sm_heap_report(out,
23327c478bd9Sstevel@tonic-gate 					sm_debug_level(&SmHeapCheck) - 1);
23337c478bd9Sstevel@tonic-gate 				(void) sm_io_close(out, SM_TIME_DEFAULT);
23347c478bd9Sstevel@tonic-gate 			}
23357c478bd9Sstevel@tonic-gate 		}
23367c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
23377c478bd9Sstevel@tonic-gate 
23387c478bd9Sstevel@tonic-gate 		/* let me rest for a second to catch my breath */
23397c478bd9Sstevel@tonic-gate 		if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
23407c478bd9Sstevel@tonic-gate 			sleep(MIN_SLEEP_TIME);
23417c478bd9Sstevel@tonic-gate 		else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
23427c478bd9Sstevel@tonic-gate 			sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
23437c478bd9Sstevel@tonic-gate 		else
23447c478bd9Sstevel@tonic-gate 			sleep(WorkGrp[wgrp].wg_lowqintvl);
23457c478bd9Sstevel@tonic-gate 
23467c478bd9Sstevel@tonic-gate 		/*
23477c478bd9Sstevel@tonic-gate 		**  Get the LA outside the WorkQ loop if necessary.
23487c478bd9Sstevel@tonic-gate 		**  In a persistent queue runner the code is repeated over
23497c478bd9Sstevel@tonic-gate 		**  and over but gatherq() may ignore entries due to
23507c478bd9Sstevel@tonic-gate 		**  shouldqueue() (do we really have to do this twice?).
23517c478bd9Sstevel@tonic-gate 		**  Hence the queue runners would just idle around when once
23527c478bd9Sstevel@tonic-gate 		**  CurrentLA caused all entries in a queue to be ignored.
23537c478bd9Sstevel@tonic-gate 		*/
23547c478bd9Sstevel@tonic-gate 
23557c478bd9Sstevel@tonic-gate 		if (njobs == 0)
23567c478bd9Sstevel@tonic-gate 			SM_GET_LA(now);
23577c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
23587c478bd9Sstevel@tonic-gate 		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
23597c478bd9Sstevel@tonic-gate 		e->e_flags = BlankEnvelope.e_flags;
23607c478bd9Sstevel@tonic-gate 		goto domorework;
23617c478bd9Sstevel@tonic-gate 	}
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 	/* exit without the usual cleanup */
23647c478bd9Sstevel@tonic-gate 	e->e_id = NULL;
23657c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
23667c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
23677c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
23687c478bd9Sstevel@tonic-gate 	return true;
23697c478bd9Sstevel@tonic-gate }
23707c478bd9Sstevel@tonic-gate 
23717c478bd9Sstevel@tonic-gate /*
23727c478bd9Sstevel@tonic-gate **  DOQUEUERUN -- do a queue run?
23737c478bd9Sstevel@tonic-gate */
23747c478bd9Sstevel@tonic-gate 
23757c478bd9Sstevel@tonic-gate bool
23767c478bd9Sstevel@tonic-gate doqueuerun()
23777c478bd9Sstevel@tonic-gate {
23787c478bd9Sstevel@tonic-gate 	return DoQueueRun;
23797c478bd9Sstevel@tonic-gate }
23807c478bd9Sstevel@tonic-gate 
23817c478bd9Sstevel@tonic-gate /*
23827c478bd9Sstevel@tonic-gate **  RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done.
23837c478bd9Sstevel@tonic-gate **
23847c478bd9Sstevel@tonic-gate **	Parameters:
23857c478bd9Sstevel@tonic-gate **		none.
23867c478bd9Sstevel@tonic-gate **
23877c478bd9Sstevel@tonic-gate **	Returns:
23887c478bd9Sstevel@tonic-gate **		none.
23897c478bd9Sstevel@tonic-gate **
23907c478bd9Sstevel@tonic-gate **	Side Effects:
23917c478bd9Sstevel@tonic-gate **		The invocation of this function via an alarm may interrupt
23927c478bd9Sstevel@tonic-gate **		a set of actions. Thus errno may be set in that context.
23937c478bd9Sstevel@tonic-gate **		We need to restore errno at the end of this function to ensure
23947c478bd9Sstevel@tonic-gate **		that any work done here that sets errno doesn't return a
23957c478bd9Sstevel@tonic-gate **		misleading/false errno value. Errno may	be EINTR upon entry to
23967c478bd9Sstevel@tonic-gate **		this function because of non-restartable/continuable system
23977c478bd9Sstevel@tonic-gate **		API was active. Iff this is true we will override errno as
23987c478bd9Sstevel@tonic-gate **		a timeout (as a more accurate error message).
23997c478bd9Sstevel@tonic-gate **
24007c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
24017c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
24027c478bd9Sstevel@tonic-gate **		DOING.
24037c478bd9Sstevel@tonic-gate */
24047c478bd9Sstevel@tonic-gate 
24057c478bd9Sstevel@tonic-gate void
24067c478bd9Sstevel@tonic-gate runqueueevent(ignore)
24077c478bd9Sstevel@tonic-gate 	int ignore;
24087c478bd9Sstevel@tonic-gate {
24097c478bd9Sstevel@tonic-gate 	int save_errno = errno;
24107c478bd9Sstevel@tonic-gate 
24117c478bd9Sstevel@tonic-gate 	/*
24127c478bd9Sstevel@tonic-gate 	**  Set the general bit that we want a queue run,
24137c478bd9Sstevel@tonic-gate 	**  tested in doqueuerun()
24147c478bd9Sstevel@tonic-gate 	*/
24157c478bd9Sstevel@tonic-gate 
24167c478bd9Sstevel@tonic-gate 	DoQueueRun = true;
24177c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
24187c478bd9Sstevel@tonic-gate 	if (tTd(69, 10))
24197c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID, "rqe: done");
24207c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
24217c478bd9Sstevel@tonic-gate 
24227c478bd9Sstevel@tonic-gate 	errno = save_errno;
24237c478bd9Sstevel@tonic-gate 	if (errno == EINTR)
24247c478bd9Sstevel@tonic-gate 		errno = ETIMEDOUT;
24257c478bd9Sstevel@tonic-gate }
24267c478bd9Sstevel@tonic-gate /*
24277c478bd9Sstevel@tonic-gate **  GATHERQ -- gather messages from the message queue(s) the work queue.
24287c478bd9Sstevel@tonic-gate **
24297c478bd9Sstevel@tonic-gate **	Parameters:
24307c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group.
24317c478bd9Sstevel@tonic-gate **		qdir -- the index of the queue directory.
24327c478bd9Sstevel@tonic-gate **		doall -- if set, include everything in the queue (even
24337c478bd9Sstevel@tonic-gate **			the jobs that cannot be run because the load
24347c478bd9Sstevel@tonic-gate **			average is too high, or MaxQueueRun is reached).
24357c478bd9Sstevel@tonic-gate **			Otherwise, exclude those jobs.
24367c478bd9Sstevel@tonic-gate **		full -- (optional) to be set 'true' if WorkList is full
24377c478bd9Sstevel@tonic-gate **		more -- (optional) to be set 'true' if there are still more
24387c478bd9Sstevel@tonic-gate **			messages in this queue not added to WorkList
24397c478bd9Sstevel@tonic-gate **
24407c478bd9Sstevel@tonic-gate **	Returns:
24417c478bd9Sstevel@tonic-gate **		The number of request in the queue (not necessarily
24427c478bd9Sstevel@tonic-gate **		the number of requests in WorkList however).
24437c478bd9Sstevel@tonic-gate **
24447c478bd9Sstevel@tonic-gate **	Side Effects:
24457c478bd9Sstevel@tonic-gate **		prepares available work into WorkList
24467c478bd9Sstevel@tonic-gate */
24477c478bd9Sstevel@tonic-gate 
24487c478bd9Sstevel@tonic-gate #define NEED_P		0001	/* 'P': priority */
24497c478bd9Sstevel@tonic-gate #define NEED_T		0002	/* 'T': time */
24507c478bd9Sstevel@tonic-gate #define NEED_R		0004	/* 'R': recipient */
24517c478bd9Sstevel@tonic-gate #define NEED_S		0010	/* 'S': sender */
24527c478bd9Sstevel@tonic-gate #define NEED_H		0020	/* host */
24537c478bd9Sstevel@tonic-gate #define HAS_QUARANTINE	0040	/* has an unexpected 'q' line */
24547c478bd9Sstevel@tonic-gate #define NEED_QUARANTINE	0100	/* 'q': reason */
24557c478bd9Sstevel@tonic-gate 
24567c478bd9Sstevel@tonic-gate static WORK	*WorkList = NULL;	/* list of unsort work */
24577c478bd9Sstevel@tonic-gate static int	WorkListSize = 0;	/* current max size of WorkList */
24587c478bd9Sstevel@tonic-gate static int	WorkListCount = 0;	/* # of work items in WorkList */
24597c478bd9Sstevel@tonic-gate 
24607c478bd9Sstevel@tonic-gate static int
24617c478bd9Sstevel@tonic-gate gatherq(qgrp, qdir, doall, full, more)
24627c478bd9Sstevel@tonic-gate 	int qgrp;
24637c478bd9Sstevel@tonic-gate 	int qdir;
24647c478bd9Sstevel@tonic-gate 	bool doall;
24657c478bd9Sstevel@tonic-gate 	bool *full;
24667c478bd9Sstevel@tonic-gate 	bool *more;
24677c478bd9Sstevel@tonic-gate {
24687c478bd9Sstevel@tonic-gate 	register struct dirent *d;
24697c478bd9Sstevel@tonic-gate 	register WORK *w;
24707c478bd9Sstevel@tonic-gate 	register char *p;
24717c478bd9Sstevel@tonic-gate 	DIR *f;
24727c478bd9Sstevel@tonic-gate 	int i, num_ent;
24737c478bd9Sstevel@tonic-gate 	int wn;
24747c478bd9Sstevel@tonic-gate 	QUEUE_CHAR *check;
24757c478bd9Sstevel@tonic-gate 	char qd[MAXPATHLEN];
24767c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
24777c478bd9Sstevel@tonic-gate 
24787c478bd9Sstevel@tonic-gate 	wn = WorkListCount - 1;
24797c478bd9Sstevel@tonic-gate 	num_ent = 0;
24807c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
24817c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(qd, ".", sizeof qd);
24827c478bd9Sstevel@tonic-gate 	else
24837c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(qd, sizeof qd, 2,
24847c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
24857c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBQF,
24867c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
24877c478bd9Sstevel@tonic-gate 					? "/qf" : ""));
24887c478bd9Sstevel@tonic-gate 
24897c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
24907c478bd9Sstevel@tonic-gate 	{
24917c478bd9Sstevel@tonic-gate 		sm_dprintf("gatherq:\n");
24927c478bd9Sstevel@tonic-gate 
24937c478bd9Sstevel@tonic-gate 		check = QueueLimitId;
24947c478bd9Sstevel@tonic-gate 		while (check != NULL)
24957c478bd9Sstevel@tonic-gate 		{
24967c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitId = %s%s\n",
24977c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
24987c478bd9Sstevel@tonic-gate 				check->queue_match);
24997c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25007c478bd9Sstevel@tonic-gate 		}
25017c478bd9Sstevel@tonic-gate 
25027c478bd9Sstevel@tonic-gate 		check = QueueLimitSender;
25037c478bd9Sstevel@tonic-gate 		while (check != NULL)
25047c478bd9Sstevel@tonic-gate 		{
25057c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitSender = %s%s\n",
25067c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25077c478bd9Sstevel@tonic-gate 				check->queue_match);
25087c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25097c478bd9Sstevel@tonic-gate 		}
25107c478bd9Sstevel@tonic-gate 
25117c478bd9Sstevel@tonic-gate 		check = QueueLimitRecipient;
25127c478bd9Sstevel@tonic-gate 		while (check != NULL)
25137c478bd9Sstevel@tonic-gate 		{
25147c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitRecipient = %s%s\n",
25157c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25167c478bd9Sstevel@tonic-gate 				check->queue_match);
25177c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25187c478bd9Sstevel@tonic-gate 		}
25197c478bd9Sstevel@tonic-gate 
25207c478bd9Sstevel@tonic-gate 		if (QueueMode == QM_QUARANTINE)
25217c478bd9Sstevel@tonic-gate 		{
25227c478bd9Sstevel@tonic-gate 			check = QueueLimitQuarantine;
25237c478bd9Sstevel@tonic-gate 			while (check != NULL)
25247c478bd9Sstevel@tonic-gate 			{
25257c478bd9Sstevel@tonic-gate 				sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
25267c478bd9Sstevel@tonic-gate 					   check->queue_negate ? "!" : "",
25277c478bd9Sstevel@tonic-gate 					   check->queue_match);
25287c478bd9Sstevel@tonic-gate 				check = check->queue_next;
25297c478bd9Sstevel@tonic-gate 			}
25307c478bd9Sstevel@tonic-gate 		}
25317c478bd9Sstevel@tonic-gate 	}
25327c478bd9Sstevel@tonic-gate 
25337c478bd9Sstevel@tonic-gate 	/* open the queue directory */
25347c478bd9Sstevel@tonic-gate 	f = opendir(qd);
25357c478bd9Sstevel@tonic-gate 	if (f == NULL)
25367c478bd9Sstevel@tonic-gate 	{
25377c478bd9Sstevel@tonic-gate 		syserr("gatherq: cannot open \"%s\"",
25387c478bd9Sstevel@tonic-gate 			qid_printqueue(qgrp, qdir));
25397c478bd9Sstevel@tonic-gate 		if (full != NULL)
25407c478bd9Sstevel@tonic-gate 			*full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
25417c478bd9Sstevel@tonic-gate 		if (more != NULL)
25427c478bd9Sstevel@tonic-gate 			*more = false;
25437c478bd9Sstevel@tonic-gate 		return 0;
25447c478bd9Sstevel@tonic-gate 	}
25457c478bd9Sstevel@tonic-gate 
25467c478bd9Sstevel@tonic-gate 	/*
25477c478bd9Sstevel@tonic-gate 	**  Read the work directory.
25487c478bd9Sstevel@tonic-gate 	*/
25497c478bd9Sstevel@tonic-gate 
25507c478bd9Sstevel@tonic-gate 	while ((d = readdir(f)) != NULL)
25517c478bd9Sstevel@tonic-gate 	{
25527c478bd9Sstevel@tonic-gate 		SM_FILE_T *cf;
25537c478bd9Sstevel@tonic-gate 		int qfver = 0;
25547c478bd9Sstevel@tonic-gate 		char lbuf[MAXNAME + 1];
25557c478bd9Sstevel@tonic-gate 		struct stat sbuf;
25567c478bd9Sstevel@tonic-gate 
25577c478bd9Sstevel@tonic-gate 		if (tTd(41, 50))
25587c478bd9Sstevel@tonic-gate 			sm_dprintf("gatherq: checking %s..", d->d_name);
25597c478bd9Sstevel@tonic-gate 
25607c478bd9Sstevel@tonic-gate 		/* is this an interesting entry? */
25617c478bd9Sstevel@tonic-gate 		if (!(((QueueMode == QM_NORMAL &&
25627c478bd9Sstevel@tonic-gate 			d->d_name[0] == NORMQF_LETTER) ||
25637c478bd9Sstevel@tonic-gate 		       (QueueMode == QM_QUARANTINE &&
25647c478bd9Sstevel@tonic-gate 			d->d_name[0] == QUARQF_LETTER) ||
25657c478bd9Sstevel@tonic-gate 		       (QueueMode == QM_LOST &&
25667c478bd9Sstevel@tonic-gate 			d->d_name[0] == LOSEQF_LETTER)) &&
25677c478bd9Sstevel@tonic-gate 		      d->d_name[1] == 'f'))
25687c478bd9Sstevel@tonic-gate 		{
25697c478bd9Sstevel@tonic-gate 			if (tTd(41, 50))
25707c478bd9Sstevel@tonic-gate 				sm_dprintf("  skipping\n");
25717c478bd9Sstevel@tonic-gate 			continue;
25727c478bd9Sstevel@tonic-gate 		}
25737c478bd9Sstevel@tonic-gate 		if (tTd(41, 50))
25747c478bd9Sstevel@tonic-gate 			sm_dprintf("\n");
25757c478bd9Sstevel@tonic-gate 
25767c478bd9Sstevel@tonic-gate 		if (strlen(d->d_name) >= MAXQFNAME)
25777c478bd9Sstevel@tonic-gate 		{
25787c478bd9Sstevel@tonic-gate 			if (Verbose)
25797c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
25807c478bd9Sstevel@tonic-gate 						     "gatherq: %s too long, %d max characters\n",
25817c478bd9Sstevel@tonic-gate 						     d->d_name, MAXQFNAME);
25827c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
25837c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ALERT, NOQID,
25847c478bd9Sstevel@tonic-gate 					  "gatherq: %s too long, %d max characters",
25857c478bd9Sstevel@tonic-gate 					  d->d_name, MAXQFNAME);
25867c478bd9Sstevel@tonic-gate 			continue;
25877c478bd9Sstevel@tonic-gate 		}
25887c478bd9Sstevel@tonic-gate 
25897c478bd9Sstevel@tonic-gate 		check = QueueLimitId;
25907c478bd9Sstevel@tonic-gate 		while (check != NULL)
25917c478bd9Sstevel@tonic-gate 		{
25927c478bd9Sstevel@tonic-gate 			if (strcontainedin(false, check->queue_match,
25937c478bd9Sstevel@tonic-gate 					   d->d_name) != check->queue_negate)
25947c478bd9Sstevel@tonic-gate 				break;
25957c478bd9Sstevel@tonic-gate 			else
25967c478bd9Sstevel@tonic-gate 				check = check->queue_next;
25977c478bd9Sstevel@tonic-gate 		}
25987c478bd9Sstevel@tonic-gate 		if (QueueLimitId != NULL && check == NULL)
25997c478bd9Sstevel@tonic-gate 			continue;
26007c478bd9Sstevel@tonic-gate 
26017c478bd9Sstevel@tonic-gate 		/* grow work list if necessary */
26027c478bd9Sstevel@tonic-gate 		if (++wn >= MaxQueueRun && MaxQueueRun > 0)
26037c478bd9Sstevel@tonic-gate 		{
26047c478bd9Sstevel@tonic-gate 			if (wn == MaxQueueRun && LogLevel > 0)
26057c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, NOQID,
26067c478bd9Sstevel@tonic-gate 					  "WorkList for %s maxed out at %d",
26077c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
26087c478bd9Sstevel@tonic-gate 					  MaxQueueRun);
26097c478bd9Sstevel@tonic-gate 			if (doall)
26107c478bd9Sstevel@tonic-gate 				continue;	/* just count entries */
26117c478bd9Sstevel@tonic-gate 			break;
26127c478bd9Sstevel@tonic-gate 		}
26137c478bd9Sstevel@tonic-gate 		if (wn >= WorkListSize)
26147c478bd9Sstevel@tonic-gate 		{
26157c478bd9Sstevel@tonic-gate 			grow_wlist(qgrp, qdir);
26167c478bd9Sstevel@tonic-gate 			if (wn >= WorkListSize)
26177c478bd9Sstevel@tonic-gate 				continue;
26187c478bd9Sstevel@tonic-gate 		}
26197c478bd9Sstevel@tonic-gate 		SM_ASSERT(wn >= 0);
26207c478bd9Sstevel@tonic-gate 		w = &WorkList[wn];
26217c478bd9Sstevel@tonic-gate 
26227c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", d->d_name);
26237c478bd9Sstevel@tonic-gate 		if (stat(qf, &sbuf) < 0)
26247c478bd9Sstevel@tonic-gate 		{
26257c478bd9Sstevel@tonic-gate 			if (errno != ENOENT)
26267c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
26277c478bd9Sstevel@tonic-gate 					  "gatherq: can't stat %s/%s",
26287c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
26297c478bd9Sstevel@tonic-gate 					  d->d_name);
26307c478bd9Sstevel@tonic-gate 			wn--;
26317c478bd9Sstevel@tonic-gate 			continue;
26327c478bd9Sstevel@tonic-gate 		}
26337c478bd9Sstevel@tonic-gate 		if (!bitset(S_IFREG, sbuf.st_mode))
26347c478bd9Sstevel@tonic-gate 		{
26357c478bd9Sstevel@tonic-gate 			/* Yikes!  Skip it or we will hang on open! */
26367c478bd9Sstevel@tonic-gate 			if (!((d->d_name[0] == DATAFL_LETTER ||
26377c478bd9Sstevel@tonic-gate 			       d->d_name[0] == NORMQF_LETTER ||
26387c478bd9Sstevel@tonic-gate 			       d->d_name[0] == QUARQF_LETTER ||
26397c478bd9Sstevel@tonic-gate 			       d->d_name[0] == LOSEQF_LETTER ||
26407c478bd9Sstevel@tonic-gate 			       d->d_name[0] == XSCRPT_LETTER) &&
26417c478bd9Sstevel@tonic-gate 			      d->d_name[1] == 'f' && d->d_name[2] == '\0'))
26427c478bd9Sstevel@tonic-gate 				syserr("gatherq: %s/%s is not a regular file",
26437c478bd9Sstevel@tonic-gate 				       qid_printqueue(qgrp, qdir), d->d_name);
26447c478bd9Sstevel@tonic-gate 			wn--;
26457c478bd9Sstevel@tonic-gate 			continue;
26467c478bd9Sstevel@tonic-gate 		}
26477c478bd9Sstevel@tonic-gate 
26487c478bd9Sstevel@tonic-gate 		/* avoid work if possible */
26497c478bd9Sstevel@tonic-gate 		if ((QueueSortOrder == QSO_BYFILENAME ||
26507c478bd9Sstevel@tonic-gate 		     QueueSortOrder == QSO_BYMODTIME ||
26517c478bd9Sstevel@tonic-gate 		     QueueSortOrder == QSO_RANDOM) &&
26527c478bd9Sstevel@tonic-gate 		    QueueLimitQuarantine == NULL &&
26537c478bd9Sstevel@tonic-gate 		    QueueLimitSender == NULL &&
26547c478bd9Sstevel@tonic-gate 		    QueueLimitRecipient == NULL)
26557c478bd9Sstevel@tonic-gate 		{
26567c478bd9Sstevel@tonic-gate 			w->w_qgrp = qgrp;
26577c478bd9Sstevel@tonic-gate 			w->w_qdir = qdir;
26587c478bd9Sstevel@tonic-gate 			w->w_name = newstr(d->d_name);
26597c478bd9Sstevel@tonic-gate 			w->w_host = NULL;
26607c478bd9Sstevel@tonic-gate 			w->w_lock = w->w_tooyoung = false;
26617c478bd9Sstevel@tonic-gate 			w->w_pri = 0;
26627c478bd9Sstevel@tonic-gate 			w->w_ctime = 0;
26637c478bd9Sstevel@tonic-gate 			w->w_mtime = sbuf.st_mtime;
26647c478bd9Sstevel@tonic-gate 			++num_ent;
26657c478bd9Sstevel@tonic-gate 			continue;
26667c478bd9Sstevel@tonic-gate 		}
26677c478bd9Sstevel@tonic-gate 
26687c478bd9Sstevel@tonic-gate 		/* open control file */
26697c478bd9Sstevel@tonic-gate 		cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
26707c478bd9Sstevel@tonic-gate 				NULL);
26717c478bd9Sstevel@tonic-gate 		if (cf == NULL && OpMode != MD_PRINT)
26727c478bd9Sstevel@tonic-gate 		{
26737c478bd9Sstevel@tonic-gate 			/* this may be some random person sending hir msgs */
26747c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
26757c478bd9Sstevel@tonic-gate 				sm_dprintf("gatherq: cannot open %s: %s\n",
26767c478bd9Sstevel@tonic-gate 					d->d_name, sm_errstring(errno));
26777c478bd9Sstevel@tonic-gate 			errno = 0;
26787c478bd9Sstevel@tonic-gate 			wn--;
26797c478bd9Sstevel@tonic-gate 			continue;
26807c478bd9Sstevel@tonic-gate 		}
26817c478bd9Sstevel@tonic-gate 		w->w_qgrp = qgrp;
26827c478bd9Sstevel@tonic-gate 		w->w_qdir = qdir;
26837c478bd9Sstevel@tonic-gate 		w->w_name = newstr(d->d_name);
26847c478bd9Sstevel@tonic-gate 		w->w_host = NULL;
26857c478bd9Sstevel@tonic-gate 		if (cf != NULL)
26867c478bd9Sstevel@tonic-gate 		{
26877c478bd9Sstevel@tonic-gate 			w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
26887c478bd9Sstevel@tonic-gate 							    NULL),
26897c478bd9Sstevel@tonic-gate 					      w->w_name, NULL,
26907c478bd9Sstevel@tonic-gate 					      LOCK_SH|LOCK_NB);
26917c478bd9Sstevel@tonic-gate 		}
26927c478bd9Sstevel@tonic-gate 		w->w_tooyoung = false;
26937c478bd9Sstevel@tonic-gate 
26947c478bd9Sstevel@tonic-gate 		/* make sure jobs in creation don't clog queue */
26957c478bd9Sstevel@tonic-gate 		w->w_pri = 0x7fffffff;
26967c478bd9Sstevel@tonic-gate 		w->w_ctime = 0;
26977c478bd9Sstevel@tonic-gate 		w->w_mtime = sbuf.st_mtime;
26987c478bd9Sstevel@tonic-gate 
26997c478bd9Sstevel@tonic-gate 		/* extract useful information */
27007c478bd9Sstevel@tonic-gate 		i = NEED_P|NEED_T;
27017c478bd9Sstevel@tonic-gate 		if (QueueSortOrder == QSO_BYHOST
27027c478bd9Sstevel@tonic-gate #if _FFR_RHS
27037c478bd9Sstevel@tonic-gate 		    || QueueSortOrder == QSO_BYSHUFFLE
27047c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
27057c478bd9Sstevel@tonic-gate 		   )
27067c478bd9Sstevel@tonic-gate 		{
27077c478bd9Sstevel@tonic-gate 			/* need w_host set for host sort order */
27087c478bd9Sstevel@tonic-gate 			i |= NEED_H;
27097c478bd9Sstevel@tonic-gate 		}
27107c478bd9Sstevel@tonic-gate 		if (QueueLimitSender != NULL)
27117c478bd9Sstevel@tonic-gate 			i |= NEED_S;
27127c478bd9Sstevel@tonic-gate 		if (QueueLimitRecipient != NULL)
27137c478bd9Sstevel@tonic-gate 			i |= NEED_R;
27147c478bd9Sstevel@tonic-gate 		if (QueueLimitQuarantine != NULL)
27157c478bd9Sstevel@tonic-gate 			i |= NEED_QUARANTINE;
27167c478bd9Sstevel@tonic-gate 		while (cf != NULL && i != 0 &&
27177c478bd9Sstevel@tonic-gate 		       sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
27187c478bd9Sstevel@tonic-gate 				   sizeof lbuf) != NULL)
27197c478bd9Sstevel@tonic-gate 		{
27207c478bd9Sstevel@tonic-gate 			int c;
27217c478bd9Sstevel@tonic-gate 			time_t age;
27227c478bd9Sstevel@tonic-gate 
27237c478bd9Sstevel@tonic-gate 			p = strchr(lbuf, '\n');
27247c478bd9Sstevel@tonic-gate 			if (p != NULL)
27257c478bd9Sstevel@tonic-gate 				*p = '\0';
27267c478bd9Sstevel@tonic-gate 			else
27277c478bd9Sstevel@tonic-gate 			{
27287c478bd9Sstevel@tonic-gate 				/* flush rest of overly long line */
27297c478bd9Sstevel@tonic-gate 				while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
27307c478bd9Sstevel@tonic-gate 				       != SM_IO_EOF && c != '\n')
27317c478bd9Sstevel@tonic-gate 					continue;
27327c478bd9Sstevel@tonic-gate 			}
27337c478bd9Sstevel@tonic-gate 
27347c478bd9Sstevel@tonic-gate 			switch (lbuf[0])
27357c478bd9Sstevel@tonic-gate 			{
27367c478bd9Sstevel@tonic-gate 			  case 'V':
27377c478bd9Sstevel@tonic-gate 				qfver = atoi(&lbuf[1]);
27387c478bd9Sstevel@tonic-gate 				break;
27397c478bd9Sstevel@tonic-gate 
27407c478bd9Sstevel@tonic-gate 			  case 'P':
27417c478bd9Sstevel@tonic-gate 				w->w_pri = atol(&lbuf[1]);
27427c478bd9Sstevel@tonic-gate 				i &= ~NEED_P;
27437c478bd9Sstevel@tonic-gate 				break;
27447c478bd9Sstevel@tonic-gate 
27457c478bd9Sstevel@tonic-gate 			  case 'T':
27467c478bd9Sstevel@tonic-gate 				w->w_ctime = atol(&lbuf[1]);
27477c478bd9Sstevel@tonic-gate 				i &= ~NEED_T;
27487c478bd9Sstevel@tonic-gate 				break;
27497c478bd9Sstevel@tonic-gate 
27507c478bd9Sstevel@tonic-gate 			  case 'q':
27517c478bd9Sstevel@tonic-gate 				if (QueueMode != QM_QUARANTINE &&
27527c478bd9Sstevel@tonic-gate 				    QueueMode != QM_LOST)
27537c478bd9Sstevel@tonic-gate 				{
27547c478bd9Sstevel@tonic-gate 					if (tTd(41, 49))
27557c478bd9Sstevel@tonic-gate 						sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
27567c478bd9Sstevel@tonic-gate 							   w->w_name);
27577c478bd9Sstevel@tonic-gate 					i |= HAS_QUARANTINE;
27587c478bd9Sstevel@tonic-gate 				}
27597c478bd9Sstevel@tonic-gate 				else if (QueueMode == QM_QUARANTINE)
27607c478bd9Sstevel@tonic-gate 				{
27617c478bd9Sstevel@tonic-gate 					if (QueueLimitQuarantine == NULL)
27627c478bd9Sstevel@tonic-gate 					{
27637c478bd9Sstevel@tonic-gate 						i &= ~NEED_QUARANTINE;
27647c478bd9Sstevel@tonic-gate 						break;
27657c478bd9Sstevel@tonic-gate 					}
27667c478bd9Sstevel@tonic-gate 					p = &lbuf[1];
27677c478bd9Sstevel@tonic-gate 					check = QueueLimitQuarantine;
27687c478bd9Sstevel@tonic-gate 					while (check != NULL)
27697c478bd9Sstevel@tonic-gate 					{
27707c478bd9Sstevel@tonic-gate 						if (strcontainedin(false,
27717c478bd9Sstevel@tonic-gate 								   check->queue_match,
27727c478bd9Sstevel@tonic-gate 								   p) !=
27737c478bd9Sstevel@tonic-gate 						    check->queue_negate)
27747c478bd9Sstevel@tonic-gate 							break;
27757c478bd9Sstevel@tonic-gate 						else
27767c478bd9Sstevel@tonic-gate 							check = check->queue_next;
27777c478bd9Sstevel@tonic-gate 					}
27787c478bd9Sstevel@tonic-gate 					if (check != NULL)
27797c478bd9Sstevel@tonic-gate 						i &= ~NEED_QUARANTINE;
27807c478bd9Sstevel@tonic-gate 				}
27817c478bd9Sstevel@tonic-gate 				break;
27827c478bd9Sstevel@tonic-gate 
27837c478bd9Sstevel@tonic-gate 			  case 'R':
27847c478bd9Sstevel@tonic-gate 				if (w->w_host == NULL &&
27857c478bd9Sstevel@tonic-gate 				    (p = strrchr(&lbuf[1], '@')) != NULL)
27867c478bd9Sstevel@tonic-gate 				{
27877c478bd9Sstevel@tonic-gate #if _FFR_RHS
27887c478bd9Sstevel@tonic-gate 					if (QueueSortOrder == QSO_BYSHUFFLE)
27897c478bd9Sstevel@tonic-gate 						w->w_host = newstr(&p[1]);
27907c478bd9Sstevel@tonic-gate 					else
27917c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
27927c478bd9Sstevel@tonic-gate 						w->w_host = strrev(&p[1]);
27937c478bd9Sstevel@tonic-gate 					makelower(w->w_host);
27947c478bd9Sstevel@tonic-gate 					i &= ~NEED_H;
27957c478bd9Sstevel@tonic-gate 				}
27967c478bd9Sstevel@tonic-gate 				if (QueueLimitRecipient == NULL)
27977c478bd9Sstevel@tonic-gate 				{
27987c478bd9Sstevel@tonic-gate 					i &= ~NEED_R;
27997c478bd9Sstevel@tonic-gate 					break;
28007c478bd9Sstevel@tonic-gate 				}
28017c478bd9Sstevel@tonic-gate 				if (qfver > 0)
28027c478bd9Sstevel@tonic-gate 				{
28037c478bd9Sstevel@tonic-gate 					p = strchr(&lbuf[1], ':');
28047c478bd9Sstevel@tonic-gate 					if (p == NULL)
28057c478bd9Sstevel@tonic-gate 						p = &lbuf[1];
28067c478bd9Sstevel@tonic-gate 					else
28077c478bd9Sstevel@tonic-gate 						++p; /* skip over ':' */
28087c478bd9Sstevel@tonic-gate 				}
28097c478bd9Sstevel@tonic-gate 				else
28107c478bd9Sstevel@tonic-gate 					p = &lbuf[1];
28117c478bd9Sstevel@tonic-gate 				check = QueueLimitRecipient;
28127c478bd9Sstevel@tonic-gate 				while (check != NULL)
28137c478bd9Sstevel@tonic-gate 				{
28147c478bd9Sstevel@tonic-gate 					if (strcontainedin(true,
28157c478bd9Sstevel@tonic-gate 							   check->queue_match,
28167c478bd9Sstevel@tonic-gate 							   p) !=
28177c478bd9Sstevel@tonic-gate 					    check->queue_negate)
28187c478bd9Sstevel@tonic-gate 						break;
28197c478bd9Sstevel@tonic-gate 					else
28207c478bd9Sstevel@tonic-gate 						check = check->queue_next;
28217c478bd9Sstevel@tonic-gate 				}
28227c478bd9Sstevel@tonic-gate 				if (check != NULL)
28237c478bd9Sstevel@tonic-gate 					i &= ~NEED_R;
28247c478bd9Sstevel@tonic-gate 				break;
28257c478bd9Sstevel@tonic-gate 
28267c478bd9Sstevel@tonic-gate 			  case 'S':
28277c478bd9Sstevel@tonic-gate 				check = QueueLimitSender;
28287c478bd9Sstevel@tonic-gate 				while (check != NULL)
28297c478bd9Sstevel@tonic-gate 				{
28307c478bd9Sstevel@tonic-gate 					if (strcontainedin(true,
28317c478bd9Sstevel@tonic-gate 							   check->queue_match,
28327c478bd9Sstevel@tonic-gate 							   &lbuf[1]) !=
28337c478bd9Sstevel@tonic-gate 					    check->queue_negate)
28347c478bd9Sstevel@tonic-gate 						break;
28357c478bd9Sstevel@tonic-gate 					else
28367c478bd9Sstevel@tonic-gate 						check = check->queue_next;
28377c478bd9Sstevel@tonic-gate 				}
28387c478bd9Sstevel@tonic-gate 				if (check != NULL)
28397c478bd9Sstevel@tonic-gate 					i &= ~NEED_S;
28407c478bd9Sstevel@tonic-gate 				break;
28417c478bd9Sstevel@tonic-gate 
28427c478bd9Sstevel@tonic-gate 			  case 'K':
28437c478bd9Sstevel@tonic-gate 				age = curtime() - (time_t) atol(&lbuf[1]);
28447c478bd9Sstevel@tonic-gate 				if (age >= 0 && MinQueueAge > 0 &&
28457c478bd9Sstevel@tonic-gate 				    age < MinQueueAge)
28467c478bd9Sstevel@tonic-gate 					w->w_tooyoung = true;
28477c478bd9Sstevel@tonic-gate 				break;
28487c478bd9Sstevel@tonic-gate 
28497c478bd9Sstevel@tonic-gate 			  case 'N':
28507c478bd9Sstevel@tonic-gate 				if (atol(&lbuf[1]) == 0)
28517c478bd9Sstevel@tonic-gate 					w->w_tooyoung = false;
28527c478bd9Sstevel@tonic-gate 				break;
28537c478bd9Sstevel@tonic-gate 			}
28547c478bd9Sstevel@tonic-gate 		}
28557c478bd9Sstevel@tonic-gate 		if (cf != NULL)
28567c478bd9Sstevel@tonic-gate 			(void) sm_io_close(cf, SM_TIME_DEFAULT);
28577c478bd9Sstevel@tonic-gate 
2858*445f2479Sjbeck 		if ((!doall && (shouldqueue(w->w_pri, w->w_ctime) ||
2859*445f2479Sjbeck 		    w->w_tooyoung)) ||
28607c478bd9Sstevel@tonic-gate 		    bitset(HAS_QUARANTINE, i) ||
28617c478bd9Sstevel@tonic-gate 		    bitset(NEED_QUARANTINE, i) ||
28627c478bd9Sstevel@tonic-gate 		    bitset(NEED_R|NEED_S, i))
28637c478bd9Sstevel@tonic-gate 		{
28647c478bd9Sstevel@tonic-gate 			/* don't even bother sorting this job in */
28657c478bd9Sstevel@tonic-gate 			if (tTd(41, 49))
28667c478bd9Sstevel@tonic-gate 				sm_dprintf("skipping %s (%x)\n", w->w_name, i);
28677c478bd9Sstevel@tonic-gate 			sm_free(w->w_name); /* XXX */
28687c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
28697c478bd9Sstevel@tonic-gate 				sm_free(w->w_host); /* XXX */
28707c478bd9Sstevel@tonic-gate 			wn--;
28717c478bd9Sstevel@tonic-gate 		}
28727c478bd9Sstevel@tonic-gate 		else
28737c478bd9Sstevel@tonic-gate 			++num_ent;
28747c478bd9Sstevel@tonic-gate 	}
28757c478bd9Sstevel@tonic-gate 	(void) closedir(f);
28767c478bd9Sstevel@tonic-gate 	wn++;
28777c478bd9Sstevel@tonic-gate 
28787c478bd9Sstevel@tonic-gate 	i = wn - WorkListCount;
28797c478bd9Sstevel@tonic-gate 	WorkListCount += SM_MIN(num_ent, WorkListSize);
28807c478bd9Sstevel@tonic-gate 
28817c478bd9Sstevel@tonic-gate 	if (more != NULL)
28827c478bd9Sstevel@tonic-gate 		*more = WorkListCount < wn;
28837c478bd9Sstevel@tonic-gate 
28847c478bd9Sstevel@tonic-gate 	if (full != NULL)
28857c478bd9Sstevel@tonic-gate 		*full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
28867c478bd9Sstevel@tonic-gate 			(WorkList == NULL && wn > 0);
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate 	return i;
28897c478bd9Sstevel@tonic-gate }
28907c478bd9Sstevel@tonic-gate /*
28917c478bd9Sstevel@tonic-gate **  SORTQ -- sort the work list
28927c478bd9Sstevel@tonic-gate **
28937c478bd9Sstevel@tonic-gate **	First the old WorkQ is cleared away. Then the WorkList is sorted
28947c478bd9Sstevel@tonic-gate **	for all items so that important (higher sorting value) items are not
28957c478bd9Sstevel@tonic-gate **	trunctated off. Then the most important items are moved from
28967c478bd9Sstevel@tonic-gate **	WorkList to WorkQ. The lower count of 'max' or MaxListCount items
28977c478bd9Sstevel@tonic-gate **	are moved.
28987c478bd9Sstevel@tonic-gate **
28997c478bd9Sstevel@tonic-gate **	Parameters:
29007c478bd9Sstevel@tonic-gate **		max -- maximum number of items to be placed in WorkQ
29017c478bd9Sstevel@tonic-gate **
29027c478bd9Sstevel@tonic-gate **	Returns:
29037c478bd9Sstevel@tonic-gate **		the number of items in WorkQ
29047c478bd9Sstevel@tonic-gate **
29057c478bd9Sstevel@tonic-gate **	Side Effects:
29067c478bd9Sstevel@tonic-gate **		WorkQ gets released and filled with new work. WorkList
29077c478bd9Sstevel@tonic-gate **		gets released. Work items get sorted in order.
29087c478bd9Sstevel@tonic-gate */
29097c478bd9Sstevel@tonic-gate 
29107c478bd9Sstevel@tonic-gate static int
29117c478bd9Sstevel@tonic-gate sortq(max)
29127c478bd9Sstevel@tonic-gate 	int max;
29137c478bd9Sstevel@tonic-gate {
29147c478bd9Sstevel@tonic-gate 	register int i;			/* local counter */
29157c478bd9Sstevel@tonic-gate 	register WORK *w;		/* tmp item pointer */
29167c478bd9Sstevel@tonic-gate 	int wc = WorkListCount;		/* trim size for WorkQ */
29177c478bd9Sstevel@tonic-gate 
29187c478bd9Sstevel@tonic-gate 	if (WorkQ != NULL)
29197c478bd9Sstevel@tonic-gate 	{
29207c478bd9Sstevel@tonic-gate 		WORK *nw;
29217c478bd9Sstevel@tonic-gate 
29227c478bd9Sstevel@tonic-gate 		/* Clear out old WorkQ. */
29237c478bd9Sstevel@tonic-gate 		for (w = WorkQ; w != NULL; w = nw)
29247c478bd9Sstevel@tonic-gate 		{
29257c478bd9Sstevel@tonic-gate 			nw = w->w_next;
29267c478bd9Sstevel@tonic-gate 			sm_free(w->w_name); /* XXX */
29277c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
29287c478bd9Sstevel@tonic-gate 				sm_free(w->w_host); /* XXX */
29297c478bd9Sstevel@tonic-gate 			sm_free((char *) w); /* XXX */
29307c478bd9Sstevel@tonic-gate 		}
29317c478bd9Sstevel@tonic-gate 		WorkQ = NULL;
29327c478bd9Sstevel@tonic-gate 	}
29337c478bd9Sstevel@tonic-gate 
29347c478bd9Sstevel@tonic-gate 	if (WorkList == NULL || wc <= 0)
29357c478bd9Sstevel@tonic-gate 		return 0;
29367c478bd9Sstevel@tonic-gate 
29377c478bd9Sstevel@tonic-gate 	/*
29387c478bd9Sstevel@tonic-gate 	**  The sort now takes place using all of the items in WorkList.
29397c478bd9Sstevel@tonic-gate 	**  The list gets trimmed to the most important items after the sort.
29407c478bd9Sstevel@tonic-gate 	**  If the trim were to happen before the sort then one or more
29417c478bd9Sstevel@tonic-gate 	**  important items might get truncated off -- not what we want.
29427c478bd9Sstevel@tonic-gate 	*/
29437c478bd9Sstevel@tonic-gate 
29447c478bd9Sstevel@tonic-gate 	if (QueueSortOrder == QSO_BYHOST)
29457c478bd9Sstevel@tonic-gate 	{
29467c478bd9Sstevel@tonic-gate 		/*
29477c478bd9Sstevel@tonic-gate 		**  Sort the work directory for the first time,
29487c478bd9Sstevel@tonic-gate 		**  based on host name, lock status, and priority.
29497c478bd9Sstevel@tonic-gate 		*/
29507c478bd9Sstevel@tonic-gate 
29517c478bd9Sstevel@tonic-gate 		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf1);
29527c478bd9Sstevel@tonic-gate 
29537c478bd9Sstevel@tonic-gate 		/*
29547c478bd9Sstevel@tonic-gate 		**  If one message to host is locked, "lock" all messages
29557c478bd9Sstevel@tonic-gate 		**  to that host.
29567c478bd9Sstevel@tonic-gate 		*/
29577c478bd9Sstevel@tonic-gate 
29587c478bd9Sstevel@tonic-gate 		i = 0;
29597c478bd9Sstevel@tonic-gate 		while (i < wc)
29607c478bd9Sstevel@tonic-gate 		{
29617c478bd9Sstevel@tonic-gate 			if (!WorkList[i].w_lock)
29627c478bd9Sstevel@tonic-gate 			{
29637c478bd9Sstevel@tonic-gate 				i++;
29647c478bd9Sstevel@tonic-gate 				continue;
29657c478bd9Sstevel@tonic-gate 			}
29667c478bd9Sstevel@tonic-gate 			w = &WorkList[i];
29677c478bd9Sstevel@tonic-gate 			while (++i < wc)
29687c478bd9Sstevel@tonic-gate 			{
29697c478bd9Sstevel@tonic-gate 				if (WorkList[i].w_host == NULL &&
29707c478bd9Sstevel@tonic-gate 				    w->w_host == NULL)
29717c478bd9Sstevel@tonic-gate 					WorkList[i].w_lock = true;
29727c478bd9Sstevel@tonic-gate 				else if (WorkList[i].w_host != NULL &&
29737c478bd9Sstevel@tonic-gate 					 w->w_host != NULL &&
29747c478bd9Sstevel@tonic-gate 					 sm_strcasecmp(WorkList[i].w_host,
29757c478bd9Sstevel@tonic-gate 						       w->w_host) == 0)
29767c478bd9Sstevel@tonic-gate 					WorkList[i].w_lock = true;
29777c478bd9Sstevel@tonic-gate 				else
29787c478bd9Sstevel@tonic-gate 					break;
29797c478bd9Sstevel@tonic-gate 			}
29807c478bd9Sstevel@tonic-gate 		}
29817c478bd9Sstevel@tonic-gate 
29827c478bd9Sstevel@tonic-gate 		/*
29837c478bd9Sstevel@tonic-gate 		**  Sort the work directory for the second time,
29847c478bd9Sstevel@tonic-gate 		**  based on lock status, host name, and priority.
29857c478bd9Sstevel@tonic-gate 		*/
29867c478bd9Sstevel@tonic-gate 
29877c478bd9Sstevel@tonic-gate 		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf2);
29887c478bd9Sstevel@tonic-gate 	}
29897c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYTIME)
29907c478bd9Sstevel@tonic-gate 	{
29917c478bd9Sstevel@tonic-gate 		/*
29927c478bd9Sstevel@tonic-gate 		**  Simple sort based on submission time only.
29937c478bd9Sstevel@tonic-gate 		*/
29947c478bd9Sstevel@tonic-gate 
29957c478bd9Sstevel@tonic-gate 		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf3);
29967c478bd9Sstevel@tonic-gate 	}
29977c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYFILENAME)
29987c478bd9Sstevel@tonic-gate 	{
29997c478bd9Sstevel@tonic-gate 		/*
30007c478bd9Sstevel@tonic-gate 		**  Sort based on queue filename.
30017c478bd9Sstevel@tonic-gate 		*/
30027c478bd9Sstevel@tonic-gate 
30037c478bd9Sstevel@tonic-gate 		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf4);
30047c478bd9Sstevel@tonic-gate 	}
30057c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_RANDOM)
30067c478bd9Sstevel@tonic-gate 	{
30077c478bd9Sstevel@tonic-gate 		/*
30087c478bd9Sstevel@tonic-gate 		**  Sort randomly.  To avoid problems with an instable sort,
30097c478bd9Sstevel@tonic-gate 		**  use a random index into the queue file name to start
30107c478bd9Sstevel@tonic-gate 		**  comparison.
30117c478bd9Sstevel@tonic-gate 		*/
30127c478bd9Sstevel@tonic-gate 
30137c478bd9Sstevel@tonic-gate 		randi = get_rand_mod(MAXQFNAME);
30147c478bd9Sstevel@tonic-gate 		if (randi < 2)
30157c478bd9Sstevel@tonic-gate 			randi = 3;
30167c478bd9Sstevel@tonic-gate 		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf5);
30177c478bd9Sstevel@tonic-gate 	}
30187c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYMODTIME)
30197c478bd9Sstevel@tonic-gate 	{
30207c478bd9Sstevel@tonic-gate 		/*
30217c478bd9Sstevel@tonic-gate 		**  Simple sort based on modification time of queue file.
30227c478bd9Sstevel@tonic-gate 		**  This puts the oldest items first.
30237c478bd9Sstevel@tonic-gate 		*/
30247c478bd9Sstevel@tonic-gate 
30257c478bd9Sstevel@tonic-gate 		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf6);
30267c478bd9Sstevel@tonic-gate 	}
30277c478bd9Sstevel@tonic-gate #if _FFR_RHS
30287c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYSHUFFLE)
30297c478bd9Sstevel@tonic-gate 	{
30307c478bd9Sstevel@tonic-gate 		/*
30317c478bd9Sstevel@tonic-gate 		**  Simple sort based on shuffled host name.
30327c478bd9Sstevel@tonic-gate 		*/
30337c478bd9Sstevel@tonic-gate 
30347c478bd9Sstevel@tonic-gate 		init_shuffle_alphabet();
30357c478bd9Sstevel@tonic-gate 		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf7);
30367c478bd9Sstevel@tonic-gate 	}
30377c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
30387c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYPRIORITY)
30397c478bd9Sstevel@tonic-gate 	{
30407c478bd9Sstevel@tonic-gate 		/*
30417c478bd9Sstevel@tonic-gate 		**  Simple sort based on queue priority only.
30427c478bd9Sstevel@tonic-gate 		*/
30437c478bd9Sstevel@tonic-gate 
30447c478bd9Sstevel@tonic-gate 		qsort((char *) WorkList, wc, sizeof *WorkList, workcmpf0);
30457c478bd9Sstevel@tonic-gate 	}
30467c478bd9Sstevel@tonic-gate 	/* else don't sort at all */
30477c478bd9Sstevel@tonic-gate 
304849218d4fSjbeck 	/* Check if the per queue group item limit will be exceeded */
304949218d4fSjbeck 	if (wc > max && max > 0)
305049218d4fSjbeck 		wc = max;
305149218d4fSjbeck 
30527c478bd9Sstevel@tonic-gate 	/*
30537c478bd9Sstevel@tonic-gate 	**  Convert the work list into canonical form.
30547c478bd9Sstevel@tonic-gate 	**	Should be turning it into a list of envelopes here perhaps.
30557c478bd9Sstevel@tonic-gate 	**  Only take the most important items up to the per queue group
30567c478bd9Sstevel@tonic-gate 	**  maximum.
30577c478bd9Sstevel@tonic-gate 	*/
30587c478bd9Sstevel@tonic-gate 
30597c478bd9Sstevel@tonic-gate 	for (i = wc; --i >= 0; )
30607c478bd9Sstevel@tonic-gate 	{
30617c478bd9Sstevel@tonic-gate 		w = (WORK *) xalloc(sizeof *w);
30627c478bd9Sstevel@tonic-gate 		w->w_qgrp = WorkList[i].w_qgrp;
30637c478bd9Sstevel@tonic-gate 		w->w_qdir = WorkList[i].w_qdir;
30647c478bd9Sstevel@tonic-gate 		w->w_name = WorkList[i].w_name;
30657c478bd9Sstevel@tonic-gate 		w->w_host = WorkList[i].w_host;
30667c478bd9Sstevel@tonic-gate 		w->w_lock = WorkList[i].w_lock;
30677c478bd9Sstevel@tonic-gate 		w->w_tooyoung = WorkList[i].w_tooyoung;
30687c478bd9Sstevel@tonic-gate 		w->w_pri = WorkList[i].w_pri;
30697c478bd9Sstevel@tonic-gate 		w->w_ctime = WorkList[i].w_ctime;
30707c478bd9Sstevel@tonic-gate 		w->w_mtime = WorkList[i].w_mtime;
30717c478bd9Sstevel@tonic-gate 		w->w_next = WorkQ;
30727c478bd9Sstevel@tonic-gate 		WorkQ = w;
30737c478bd9Sstevel@tonic-gate 	}
30747c478bd9Sstevel@tonic-gate 
30757c478bd9Sstevel@tonic-gate 	/* free the rest of the list */
30767c478bd9Sstevel@tonic-gate 	for (i = WorkListCount; --i >= wc; )
30777c478bd9Sstevel@tonic-gate 	{
30787c478bd9Sstevel@tonic-gate 		sm_free(WorkList[i].w_name);
30797c478bd9Sstevel@tonic-gate 		if (WorkList[i].w_host != NULL)
30807c478bd9Sstevel@tonic-gate 			sm_free(WorkList[i].w_host);
30817c478bd9Sstevel@tonic-gate 	}
30827c478bd9Sstevel@tonic-gate 
30837c478bd9Sstevel@tonic-gate 	if (WorkList != NULL)
30847c478bd9Sstevel@tonic-gate 		sm_free(WorkList); /* XXX */
30857c478bd9Sstevel@tonic-gate 	WorkList = NULL;
30867c478bd9Sstevel@tonic-gate 	WorkListSize = 0;
30877c478bd9Sstevel@tonic-gate 	WorkListCount = 0;
30887c478bd9Sstevel@tonic-gate 
30897c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
30907c478bd9Sstevel@tonic-gate 	{
30917c478bd9Sstevel@tonic-gate 		for (w = WorkQ; w != NULL; w = w->w_next)
30927c478bd9Sstevel@tonic-gate 		{
30937c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
30947c478bd9Sstevel@tonic-gate 				sm_dprintf("%22s: pri=%ld %s\n",
30957c478bd9Sstevel@tonic-gate 					w->w_name, w->w_pri, w->w_host);
30967c478bd9Sstevel@tonic-gate 			else
30977c478bd9Sstevel@tonic-gate 				sm_dprintf("%32s: pri=%ld\n",
30987c478bd9Sstevel@tonic-gate 					w->w_name, w->w_pri);
30997c478bd9Sstevel@tonic-gate 		}
31007c478bd9Sstevel@tonic-gate 	}
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 	return wc; /* return number of WorkQ items */
31037c478bd9Sstevel@tonic-gate }
31047c478bd9Sstevel@tonic-gate /*
31057c478bd9Sstevel@tonic-gate **  GROW_WLIST -- make the work list larger
31067c478bd9Sstevel@tonic-gate **
31077c478bd9Sstevel@tonic-gate **	Parameters:
31087c478bd9Sstevel@tonic-gate **		qgrp -- the index for the queue group.
31097c478bd9Sstevel@tonic-gate **		qdir -- the index for the queue directory.
31107c478bd9Sstevel@tonic-gate **
31117c478bd9Sstevel@tonic-gate **	Returns:
31127c478bd9Sstevel@tonic-gate **		none.
31137c478bd9Sstevel@tonic-gate **
31147c478bd9Sstevel@tonic-gate **	Side Effects:
31157c478bd9Sstevel@tonic-gate **		Adds another QUEUESEGSIZE entries to WorkList if possible.
31167c478bd9Sstevel@tonic-gate **		It can fail if there isn't enough memory, so WorkListSize
31177c478bd9Sstevel@tonic-gate **		should be checked again upon return.
31187c478bd9Sstevel@tonic-gate */
31197c478bd9Sstevel@tonic-gate 
31207c478bd9Sstevel@tonic-gate static void
31217c478bd9Sstevel@tonic-gate grow_wlist(qgrp, qdir)
31227c478bd9Sstevel@tonic-gate 	int qgrp;
31237c478bd9Sstevel@tonic-gate 	int qdir;
31247c478bd9Sstevel@tonic-gate {
31257c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
31267c478bd9Sstevel@tonic-gate 		sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
31277c478bd9Sstevel@tonic-gate 	if (WorkList == NULL)
31287c478bd9Sstevel@tonic-gate 	{
31297c478bd9Sstevel@tonic-gate 		WorkList = (WORK *) xalloc((sizeof *WorkList) *
31307c478bd9Sstevel@tonic-gate 					   (QUEUESEGSIZE + 1));
31317c478bd9Sstevel@tonic-gate 		WorkListSize = QUEUESEGSIZE;
31327c478bd9Sstevel@tonic-gate 	}
31337c478bd9Sstevel@tonic-gate 	else
31347c478bd9Sstevel@tonic-gate 	{
31357c478bd9Sstevel@tonic-gate 		int newsize = WorkListSize + QUEUESEGSIZE;
31367c478bd9Sstevel@tonic-gate 		WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
31377c478bd9Sstevel@tonic-gate 					  (unsigned) sizeof(WORK) * (newsize + 1));
31387c478bd9Sstevel@tonic-gate 
31397c478bd9Sstevel@tonic-gate 		if (newlist != NULL)
31407c478bd9Sstevel@tonic-gate 		{
31417c478bd9Sstevel@tonic-gate 			WorkListSize = newsize;
31427c478bd9Sstevel@tonic-gate 			WorkList = newlist;
31437c478bd9Sstevel@tonic-gate 			if (LogLevel > 1)
31447c478bd9Sstevel@tonic-gate 			{
31457c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
31467c478bd9Sstevel@tonic-gate 					  "grew WorkList for %s to %d",
31477c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
31487c478bd9Sstevel@tonic-gate 					  WorkListSize);
31497c478bd9Sstevel@tonic-gate 			}
31507c478bd9Sstevel@tonic-gate 		}
31517c478bd9Sstevel@tonic-gate 		else if (LogLevel > 0)
31527c478bd9Sstevel@tonic-gate 		{
31537c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, NOQID,
31547c478bd9Sstevel@tonic-gate 				  "FAILED to grow WorkList for %s to %d",
31557c478bd9Sstevel@tonic-gate 				  qid_printqueue(qgrp, qdir), newsize);
31567c478bd9Sstevel@tonic-gate 		}
31577c478bd9Sstevel@tonic-gate 	}
31587c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
31597c478bd9Sstevel@tonic-gate 		sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
31607c478bd9Sstevel@tonic-gate }
31617c478bd9Sstevel@tonic-gate /*
31627c478bd9Sstevel@tonic-gate **  WORKCMPF0 -- simple priority-only compare function.
31637c478bd9Sstevel@tonic-gate **
31647c478bd9Sstevel@tonic-gate **	Parameters:
31657c478bd9Sstevel@tonic-gate **		a -- the first argument.
31667c478bd9Sstevel@tonic-gate **		b -- the second argument.
31677c478bd9Sstevel@tonic-gate **
31687c478bd9Sstevel@tonic-gate **	Returns:
31697c478bd9Sstevel@tonic-gate **		-1 if a < b
31707c478bd9Sstevel@tonic-gate **		 0 if a == b
31717c478bd9Sstevel@tonic-gate **		+1 if a > b
31727c478bd9Sstevel@tonic-gate **
31737c478bd9Sstevel@tonic-gate */
31747c478bd9Sstevel@tonic-gate 
31757c478bd9Sstevel@tonic-gate static int
31767c478bd9Sstevel@tonic-gate workcmpf0(a, b)
31777c478bd9Sstevel@tonic-gate 	register WORK *a;
31787c478bd9Sstevel@tonic-gate 	register WORK *b;
31797c478bd9Sstevel@tonic-gate {
31807c478bd9Sstevel@tonic-gate 	long pa = a->w_pri;
31817c478bd9Sstevel@tonic-gate 	long pb = b->w_pri;
31827c478bd9Sstevel@tonic-gate 
31837c478bd9Sstevel@tonic-gate 	if (pa == pb)
31847c478bd9Sstevel@tonic-gate 		return 0;
31857c478bd9Sstevel@tonic-gate 	else if (pa > pb)
31867c478bd9Sstevel@tonic-gate 		return 1;
31877c478bd9Sstevel@tonic-gate 	else
31887c478bd9Sstevel@tonic-gate 		return -1;
31897c478bd9Sstevel@tonic-gate }
31907c478bd9Sstevel@tonic-gate /*
31917c478bd9Sstevel@tonic-gate **  WORKCMPF1 -- first compare function for ordering work based on host name.
31927c478bd9Sstevel@tonic-gate **
31937c478bd9Sstevel@tonic-gate **	Sorts on host name, lock status, and priority in that order.
31947c478bd9Sstevel@tonic-gate **
31957c478bd9Sstevel@tonic-gate **	Parameters:
31967c478bd9Sstevel@tonic-gate **		a -- the first argument.
31977c478bd9Sstevel@tonic-gate **		b -- the second argument.
31987c478bd9Sstevel@tonic-gate **
31997c478bd9Sstevel@tonic-gate **	Returns:
32007c478bd9Sstevel@tonic-gate **		<0 if a < b
32017c478bd9Sstevel@tonic-gate **		 0 if a == b
32027c478bd9Sstevel@tonic-gate **		>0 if a > b
32037c478bd9Sstevel@tonic-gate **
32047c478bd9Sstevel@tonic-gate */
32057c478bd9Sstevel@tonic-gate 
32067c478bd9Sstevel@tonic-gate static int
32077c478bd9Sstevel@tonic-gate workcmpf1(a, b)
32087c478bd9Sstevel@tonic-gate 	register WORK *a;
32097c478bd9Sstevel@tonic-gate 	register WORK *b;
32107c478bd9Sstevel@tonic-gate {
32117c478bd9Sstevel@tonic-gate 	int i;
32127c478bd9Sstevel@tonic-gate 
32137c478bd9Sstevel@tonic-gate 	/* host name */
32147c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
32157c478bd9Sstevel@tonic-gate 		return 1;
32167c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
32177c478bd9Sstevel@tonic-gate 		return -1;
32187c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
32197c478bd9Sstevel@tonic-gate 	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
32207c478bd9Sstevel@tonic-gate 		return i;
32217c478bd9Sstevel@tonic-gate 
32227c478bd9Sstevel@tonic-gate 	/* lock status */
32237c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
32247c478bd9Sstevel@tonic-gate 		return b->w_lock - a->w_lock;
32257c478bd9Sstevel@tonic-gate 
32267c478bd9Sstevel@tonic-gate 	/* job priority */
32277c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
32287c478bd9Sstevel@tonic-gate }
32297c478bd9Sstevel@tonic-gate /*
32307c478bd9Sstevel@tonic-gate **  WORKCMPF2 -- second compare function for ordering work based on host name.
32317c478bd9Sstevel@tonic-gate **
32327c478bd9Sstevel@tonic-gate **	Sorts on lock status, host name, and priority in that order.
32337c478bd9Sstevel@tonic-gate **
32347c478bd9Sstevel@tonic-gate **	Parameters:
32357c478bd9Sstevel@tonic-gate **		a -- the first argument.
32367c478bd9Sstevel@tonic-gate **		b -- the second argument.
32377c478bd9Sstevel@tonic-gate **
32387c478bd9Sstevel@tonic-gate **	Returns:
32397c478bd9Sstevel@tonic-gate **		<0 if a < b
32407c478bd9Sstevel@tonic-gate **		 0 if a == b
32417c478bd9Sstevel@tonic-gate **		>0 if a > b
32427c478bd9Sstevel@tonic-gate **
32437c478bd9Sstevel@tonic-gate */
32447c478bd9Sstevel@tonic-gate 
32457c478bd9Sstevel@tonic-gate static int
32467c478bd9Sstevel@tonic-gate workcmpf2(a, b)
32477c478bd9Sstevel@tonic-gate 	register WORK *a;
32487c478bd9Sstevel@tonic-gate 	register WORK *b;
32497c478bd9Sstevel@tonic-gate {
32507c478bd9Sstevel@tonic-gate 	int i;
32517c478bd9Sstevel@tonic-gate 
32527c478bd9Sstevel@tonic-gate 	/* lock status */
32537c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
32547c478bd9Sstevel@tonic-gate 		return a->w_lock - b->w_lock;
32557c478bd9Sstevel@tonic-gate 
32567c478bd9Sstevel@tonic-gate 	/* host name */
32577c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
32587c478bd9Sstevel@tonic-gate 		return 1;
32597c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
32607c478bd9Sstevel@tonic-gate 		return -1;
32617c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
32627c478bd9Sstevel@tonic-gate 	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
32637c478bd9Sstevel@tonic-gate 		return i;
32647c478bd9Sstevel@tonic-gate 
32657c478bd9Sstevel@tonic-gate 	/* job priority */
32667c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
32677c478bd9Sstevel@tonic-gate }
32687c478bd9Sstevel@tonic-gate /*
32697c478bd9Sstevel@tonic-gate **  WORKCMPF3 -- simple submission-time-only compare function.
32707c478bd9Sstevel@tonic-gate **
32717c478bd9Sstevel@tonic-gate **	Parameters:
32727c478bd9Sstevel@tonic-gate **		a -- the first argument.
32737c478bd9Sstevel@tonic-gate **		b -- the second argument.
32747c478bd9Sstevel@tonic-gate **
32757c478bd9Sstevel@tonic-gate **	Returns:
32767c478bd9Sstevel@tonic-gate **		-1 if a < b
32777c478bd9Sstevel@tonic-gate **		 0 if a == b
32787c478bd9Sstevel@tonic-gate **		+1 if a > b
32797c478bd9Sstevel@tonic-gate **
32807c478bd9Sstevel@tonic-gate */
32817c478bd9Sstevel@tonic-gate 
32827c478bd9Sstevel@tonic-gate static int
32837c478bd9Sstevel@tonic-gate workcmpf3(a, b)
32847c478bd9Sstevel@tonic-gate 	register WORK *a;
32857c478bd9Sstevel@tonic-gate 	register WORK *b;
32867c478bd9Sstevel@tonic-gate {
32877c478bd9Sstevel@tonic-gate 	if (a->w_ctime > b->w_ctime)
32887c478bd9Sstevel@tonic-gate 		return 1;
32897c478bd9Sstevel@tonic-gate 	else if (a->w_ctime < b->w_ctime)
32907c478bd9Sstevel@tonic-gate 		return -1;
32917c478bd9Sstevel@tonic-gate 	else
32927c478bd9Sstevel@tonic-gate 		return 0;
32937c478bd9Sstevel@tonic-gate }
32947c478bd9Sstevel@tonic-gate /*
32957c478bd9Sstevel@tonic-gate **  WORKCMPF4 -- compare based on file name
32967c478bd9Sstevel@tonic-gate **
32977c478bd9Sstevel@tonic-gate **	Parameters:
32987c478bd9Sstevel@tonic-gate **		a -- the first argument.
32997c478bd9Sstevel@tonic-gate **		b -- the second argument.
33007c478bd9Sstevel@tonic-gate **
33017c478bd9Sstevel@tonic-gate **	Returns:
33027c478bd9Sstevel@tonic-gate **		-1 if a < b
33037c478bd9Sstevel@tonic-gate **		 0 if a == b
33047c478bd9Sstevel@tonic-gate **		+1 if a > b
33057c478bd9Sstevel@tonic-gate **
33067c478bd9Sstevel@tonic-gate */
33077c478bd9Sstevel@tonic-gate 
33087c478bd9Sstevel@tonic-gate static int
33097c478bd9Sstevel@tonic-gate workcmpf4(a, b)
33107c478bd9Sstevel@tonic-gate 	register WORK *a;
33117c478bd9Sstevel@tonic-gate 	register WORK *b;
33127c478bd9Sstevel@tonic-gate {
33137c478bd9Sstevel@tonic-gate 	return strcmp(a->w_name, b->w_name);
33147c478bd9Sstevel@tonic-gate }
33157c478bd9Sstevel@tonic-gate /*
33167c478bd9Sstevel@tonic-gate **  WORKCMPF5 -- compare based on assigned random number
33177c478bd9Sstevel@tonic-gate **
33187c478bd9Sstevel@tonic-gate **	Parameters:
33197c478bd9Sstevel@tonic-gate **		a -- the first argument (ignored).
33207c478bd9Sstevel@tonic-gate **		b -- the second argument (ignored).
33217c478bd9Sstevel@tonic-gate **
33227c478bd9Sstevel@tonic-gate **	Returns:
33237c478bd9Sstevel@tonic-gate **		randomly 1/-1
33247c478bd9Sstevel@tonic-gate */
33257c478bd9Sstevel@tonic-gate 
33267c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
33277c478bd9Sstevel@tonic-gate static int
33287c478bd9Sstevel@tonic-gate workcmpf5(a, b)
33297c478bd9Sstevel@tonic-gate 	register WORK *a;
33307c478bd9Sstevel@tonic-gate 	register WORK *b;
33317c478bd9Sstevel@tonic-gate {
33327c478bd9Sstevel@tonic-gate 	if (strlen(a->w_name) < randi || strlen(b->w_name) < randi)
33337c478bd9Sstevel@tonic-gate 		return -1;
33347c478bd9Sstevel@tonic-gate 	return a->w_name[randi] - b->w_name[randi];
33357c478bd9Sstevel@tonic-gate }
33367c478bd9Sstevel@tonic-gate /*
33377c478bd9Sstevel@tonic-gate **  WORKCMPF6 -- simple modification-time-only compare function.
33387c478bd9Sstevel@tonic-gate **
33397c478bd9Sstevel@tonic-gate **	Parameters:
33407c478bd9Sstevel@tonic-gate **		a -- the first argument.
33417c478bd9Sstevel@tonic-gate **		b -- the second argument.
33427c478bd9Sstevel@tonic-gate **
33437c478bd9Sstevel@tonic-gate **	Returns:
33447c478bd9Sstevel@tonic-gate **		-1 if a < b
33457c478bd9Sstevel@tonic-gate **		 0 if a == b
33467c478bd9Sstevel@tonic-gate **		+1 if a > b
33477c478bd9Sstevel@tonic-gate **
33487c478bd9Sstevel@tonic-gate */
33497c478bd9Sstevel@tonic-gate 
33507c478bd9Sstevel@tonic-gate static int
33517c478bd9Sstevel@tonic-gate workcmpf6(a, b)
33527c478bd9Sstevel@tonic-gate 	register WORK *a;
33537c478bd9Sstevel@tonic-gate 	register WORK *b;
33547c478bd9Sstevel@tonic-gate {
33557c478bd9Sstevel@tonic-gate 	if (a->w_mtime > b->w_mtime)
33567c478bd9Sstevel@tonic-gate 		return 1;
33577c478bd9Sstevel@tonic-gate 	else if (a->w_mtime < b->w_mtime)
33587c478bd9Sstevel@tonic-gate 		return -1;
33597c478bd9Sstevel@tonic-gate 	else
33607c478bd9Sstevel@tonic-gate 		return 0;
33617c478bd9Sstevel@tonic-gate }
33627c478bd9Sstevel@tonic-gate #if _FFR_RHS
33637c478bd9Sstevel@tonic-gate /*
33647c478bd9Sstevel@tonic-gate **  WORKCMPF7 -- compare function for ordering work based on shuffled host name.
33657c478bd9Sstevel@tonic-gate **
33667c478bd9Sstevel@tonic-gate **	Sorts on lock status, host name, and priority in that order.
33677c478bd9Sstevel@tonic-gate **
33687c478bd9Sstevel@tonic-gate **	Parameters:
33697c478bd9Sstevel@tonic-gate **		a -- the first argument.
33707c478bd9Sstevel@tonic-gate **		b -- the second argument.
33717c478bd9Sstevel@tonic-gate **
33727c478bd9Sstevel@tonic-gate **	Returns:
33737c478bd9Sstevel@tonic-gate **		<0 if a < b
33747c478bd9Sstevel@tonic-gate **		 0 if a == b
33757c478bd9Sstevel@tonic-gate **		>0 if a > b
33767c478bd9Sstevel@tonic-gate **
33777c478bd9Sstevel@tonic-gate */
33787c478bd9Sstevel@tonic-gate 
33797c478bd9Sstevel@tonic-gate static int
33807c478bd9Sstevel@tonic-gate workcmpf7(a, b)
33817c478bd9Sstevel@tonic-gate 	register WORK *a;
33827c478bd9Sstevel@tonic-gate 	register WORK *b;
33837c478bd9Sstevel@tonic-gate {
33847c478bd9Sstevel@tonic-gate 	int i;
33857c478bd9Sstevel@tonic-gate 
33867c478bd9Sstevel@tonic-gate 	/* lock status */
33877c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
33887c478bd9Sstevel@tonic-gate 		return a->w_lock - b->w_lock;
33897c478bd9Sstevel@tonic-gate 
33907c478bd9Sstevel@tonic-gate 	/* host name */
33917c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
33927c478bd9Sstevel@tonic-gate 		return 1;
33937c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
33947c478bd9Sstevel@tonic-gate 		return -1;
33957c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
33967c478bd9Sstevel@tonic-gate 	    (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
33977c478bd9Sstevel@tonic-gate 		return i;
33987c478bd9Sstevel@tonic-gate 
33997c478bd9Sstevel@tonic-gate 	/* job priority */
34007c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
34017c478bd9Sstevel@tonic-gate }
34027c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
34037c478bd9Sstevel@tonic-gate /*
34047c478bd9Sstevel@tonic-gate **  STRREV -- reverse string
34057c478bd9Sstevel@tonic-gate **
34067c478bd9Sstevel@tonic-gate **	Returns a pointer to a new string that is the reverse of
34077c478bd9Sstevel@tonic-gate **	the string pointed to by fwd.  The space for the new
34087c478bd9Sstevel@tonic-gate **	string is obtained using xalloc().
34097c478bd9Sstevel@tonic-gate **
34107c478bd9Sstevel@tonic-gate **	Parameters:
34117c478bd9Sstevel@tonic-gate **		fwd -- the string to reverse.
34127c478bd9Sstevel@tonic-gate **
34137c478bd9Sstevel@tonic-gate **	Returns:
34147c478bd9Sstevel@tonic-gate **		the reversed string.
34157c478bd9Sstevel@tonic-gate */
34167c478bd9Sstevel@tonic-gate 
34177c478bd9Sstevel@tonic-gate static char *
34187c478bd9Sstevel@tonic-gate strrev(fwd)
34197c478bd9Sstevel@tonic-gate 	char *fwd;
34207c478bd9Sstevel@tonic-gate {
34217c478bd9Sstevel@tonic-gate 	char *rev = NULL;
34227c478bd9Sstevel@tonic-gate 	int len, cnt;
34237c478bd9Sstevel@tonic-gate 
34247c478bd9Sstevel@tonic-gate 	len = strlen(fwd);
34257c478bd9Sstevel@tonic-gate 	rev = xalloc(len + 1);
34267c478bd9Sstevel@tonic-gate 	for (cnt = 0; cnt < len; ++cnt)
34277c478bd9Sstevel@tonic-gate 		rev[cnt] = fwd[len - cnt - 1];
34287c478bd9Sstevel@tonic-gate 	rev[len] = '\0';
34297c478bd9Sstevel@tonic-gate 	return rev;
34307c478bd9Sstevel@tonic-gate }
34317c478bd9Sstevel@tonic-gate 
34327c478bd9Sstevel@tonic-gate #if _FFR_RHS
34337c478bd9Sstevel@tonic-gate 
34347c478bd9Sstevel@tonic-gate # define NASCII	128
34357c478bd9Sstevel@tonic-gate # define NCHAR	256
34367c478bd9Sstevel@tonic-gate 
34377c478bd9Sstevel@tonic-gate static unsigned char ShuffledAlphabet[NCHAR];
34387c478bd9Sstevel@tonic-gate 
34397c478bd9Sstevel@tonic-gate void
34407c478bd9Sstevel@tonic-gate init_shuffle_alphabet()
34417c478bd9Sstevel@tonic-gate {
34427c478bd9Sstevel@tonic-gate 	static bool init = false;
34437c478bd9Sstevel@tonic-gate 	int i;
34447c478bd9Sstevel@tonic-gate 
34457c478bd9Sstevel@tonic-gate 	if (init)
34467c478bd9Sstevel@tonic-gate 		return;
34477c478bd9Sstevel@tonic-gate 
34487c478bd9Sstevel@tonic-gate 	/* fill the ShuffledAlphabet */
344949218d4fSjbeck 	for (i = 0; i < NASCII; i++)
34507c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = i;
34517c478bd9Sstevel@tonic-gate 
34527c478bd9Sstevel@tonic-gate 	/* mix it */
345349218d4fSjbeck 	for (i = 1; i < NASCII; i++)
34547c478bd9Sstevel@tonic-gate 	{
345549218d4fSjbeck 		register int j = get_random() % NASCII;
34567c478bd9Sstevel@tonic-gate 		register int tmp;
34577c478bd9Sstevel@tonic-gate 
34587c478bd9Sstevel@tonic-gate 		tmp = ShuffledAlphabet[j];
34597c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[j] = ShuffledAlphabet[i];
34607c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = tmp;
34617c478bd9Sstevel@tonic-gate 	}
34627c478bd9Sstevel@tonic-gate 
34637c478bd9Sstevel@tonic-gate 	/* make it case insensitive */
34647c478bd9Sstevel@tonic-gate 	for (i = 'A'; i <= 'Z'; i++)
34657c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
34667c478bd9Sstevel@tonic-gate 
34677c478bd9Sstevel@tonic-gate 	/* fill the upper part */
346849218d4fSjbeck 	for (i = 0; i < NASCII; i++)
346949218d4fSjbeck 		ShuffledAlphabet[i + NASCII] = ShuffledAlphabet[i];
34707c478bd9Sstevel@tonic-gate 	init = true;
34717c478bd9Sstevel@tonic-gate }
34727c478bd9Sstevel@tonic-gate 
34737c478bd9Sstevel@tonic-gate static int
34747c478bd9Sstevel@tonic-gate sm_strshufflecmp(a, b)
34757c478bd9Sstevel@tonic-gate 	char *a;
34767c478bd9Sstevel@tonic-gate 	char *b;
34777c478bd9Sstevel@tonic-gate {
34787c478bd9Sstevel@tonic-gate 	const unsigned char *us1 = (const unsigned char *) a;
34797c478bd9Sstevel@tonic-gate 	const unsigned char *us2 = (const unsigned char *) b;
34807c478bd9Sstevel@tonic-gate 
34817c478bd9Sstevel@tonic-gate 	while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
34827c478bd9Sstevel@tonic-gate 	{
34837c478bd9Sstevel@tonic-gate 		if (*us1++ == '\0')
34847c478bd9Sstevel@tonic-gate 			return 0;
34857c478bd9Sstevel@tonic-gate 	}
34867c478bd9Sstevel@tonic-gate 	return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
34877c478bd9Sstevel@tonic-gate }
34887c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
34897c478bd9Sstevel@tonic-gate 
34907c478bd9Sstevel@tonic-gate /*
34917c478bd9Sstevel@tonic-gate **  DOWORK -- do a work request.
34927c478bd9Sstevel@tonic-gate **
34937c478bd9Sstevel@tonic-gate **	Parameters:
34947c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group for the job.
34957c478bd9Sstevel@tonic-gate **		qdir -- the index of the queue directory for the job.
34967c478bd9Sstevel@tonic-gate **		id -- the ID of the job to run.
34977c478bd9Sstevel@tonic-gate **		forkflag -- if set, run this in background.
34987c478bd9Sstevel@tonic-gate **		requeueflag -- if set, reinstantiate the queue quickly.
34997c478bd9Sstevel@tonic-gate **			This is used when expanding aliases in the queue.
35007c478bd9Sstevel@tonic-gate **			If forkflag is also set, it doesn't wait for the
35017c478bd9Sstevel@tonic-gate **			child.
35027c478bd9Sstevel@tonic-gate **		e - the envelope in which to run it.
35037c478bd9Sstevel@tonic-gate **
35047c478bd9Sstevel@tonic-gate **	Returns:
35057c478bd9Sstevel@tonic-gate **		process id of process that is running the queue job.
35067c478bd9Sstevel@tonic-gate **
35077c478bd9Sstevel@tonic-gate **	Side Effects:
35087c478bd9Sstevel@tonic-gate **		The work request is satisfied if possible.
35097c478bd9Sstevel@tonic-gate */
35107c478bd9Sstevel@tonic-gate 
35117c478bd9Sstevel@tonic-gate pid_t
35127c478bd9Sstevel@tonic-gate dowork(qgrp, qdir, id, forkflag, requeueflag, e)
35137c478bd9Sstevel@tonic-gate 	int qgrp;
35147c478bd9Sstevel@tonic-gate 	int qdir;
35157c478bd9Sstevel@tonic-gate 	char *id;
35167c478bd9Sstevel@tonic-gate 	bool forkflag;
35177c478bd9Sstevel@tonic-gate 	bool requeueflag;
35187c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
35197c478bd9Sstevel@tonic-gate {
35207c478bd9Sstevel@tonic-gate 	register pid_t pid;
35217c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
35227c478bd9Sstevel@tonic-gate 
35237c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
35247c478bd9Sstevel@tonic-gate 		sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
35257c478bd9Sstevel@tonic-gate 
35267c478bd9Sstevel@tonic-gate 	/*
35277c478bd9Sstevel@tonic-gate 	**  Fork for work.
35287c478bd9Sstevel@tonic-gate 	*/
35297c478bd9Sstevel@tonic-gate 
35307c478bd9Sstevel@tonic-gate 	if (forkflag)
35317c478bd9Sstevel@tonic-gate 	{
35327c478bd9Sstevel@tonic-gate 		/*
35337c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the
35347c478bd9Sstevel@tonic-gate 		**  parent does not wait, the parent may close the
35357c478bd9Sstevel@tonic-gate 		**  maps thereby removing any shared memory used by
35367c478bd9Sstevel@tonic-gate 		**  the map.  Therefore, close the maps now so the
35377c478bd9Sstevel@tonic-gate 		**  child will dynamically open them if necessary.
35387c478bd9Sstevel@tonic-gate 		*/
35397c478bd9Sstevel@tonic-gate 
35407c478bd9Sstevel@tonic-gate 		closemaps(false);
35417c478bd9Sstevel@tonic-gate 
35427c478bd9Sstevel@tonic-gate 		pid = fork();
35437c478bd9Sstevel@tonic-gate 		if (pid < 0)
35447c478bd9Sstevel@tonic-gate 		{
35457c478bd9Sstevel@tonic-gate 			syserr("dowork: cannot fork");
35467c478bd9Sstevel@tonic-gate 			return 0;
35477c478bd9Sstevel@tonic-gate 		}
35487c478bd9Sstevel@tonic-gate 		else if (pid > 0)
35497c478bd9Sstevel@tonic-gate 		{
35507c478bd9Sstevel@tonic-gate 			/* parent -- clean out connection cache */
35517c478bd9Sstevel@tonic-gate 			mci_flush(false, NULL);
35527c478bd9Sstevel@tonic-gate 		}
35537c478bd9Sstevel@tonic-gate 		else
35547c478bd9Sstevel@tonic-gate 		{
35557c478bd9Sstevel@tonic-gate 			/*
35567c478bd9Sstevel@tonic-gate 			**  Initialize exception stack and default exception
35577c478bd9Sstevel@tonic-gate 			**  handler for child process.
35587c478bd9Sstevel@tonic-gate 			*/
35597c478bd9Sstevel@tonic-gate 
35607c478bd9Sstevel@tonic-gate 			/* Reset global flags */
35617c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
35627c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
35637c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
35647c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
35657c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
35667c478bd9Sstevel@tonic-gate 			sm_exc_newthread(fatal_error);
35677c478bd9Sstevel@tonic-gate 
35687c478bd9Sstevel@tonic-gate 			/*
35697c478bd9Sstevel@tonic-gate 			**  See note above about SMTP processes and SIGCHLD.
35707c478bd9Sstevel@tonic-gate 			*/
35717c478bd9Sstevel@tonic-gate 
35727c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP ||
35737c478bd9Sstevel@tonic-gate 			    OpMode == MD_DAEMON ||
35747c478bd9Sstevel@tonic-gate 			    MaxQueueChildren > 0)
35757c478bd9Sstevel@tonic-gate 			{
35767c478bd9Sstevel@tonic-gate 				proc_list_clear();
35777c478bd9Sstevel@tonic-gate 				sm_releasesignal(SIGCHLD);
35787c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, SIG_DFL);
35797c478bd9Sstevel@tonic-gate 			}
35807c478bd9Sstevel@tonic-gate 
35817c478bd9Sstevel@tonic-gate 			/* child -- error messages to the transcript */
35827c478bd9Sstevel@tonic-gate 			QuickAbort = OnlyOneError = false;
35837c478bd9Sstevel@tonic-gate 		}
35847c478bd9Sstevel@tonic-gate 	}
35857c478bd9Sstevel@tonic-gate 	else
35867c478bd9Sstevel@tonic-gate 	{
35877c478bd9Sstevel@tonic-gate 		pid = 0;
35887c478bd9Sstevel@tonic-gate 	}
35897c478bd9Sstevel@tonic-gate 
35907c478bd9Sstevel@tonic-gate 	if (pid == 0)
35917c478bd9Sstevel@tonic-gate 	{
35927c478bd9Sstevel@tonic-gate 		/*
35937c478bd9Sstevel@tonic-gate 		**  CHILD
35947c478bd9Sstevel@tonic-gate 		**	Lock the control file to avoid duplicate deliveries.
35957c478bd9Sstevel@tonic-gate 		**		Then run the file as though we had just read it.
35967c478bd9Sstevel@tonic-gate 		**	We save an idea of the temporary name so we
35977c478bd9Sstevel@tonic-gate 		**		can recover on interrupt.
35987c478bd9Sstevel@tonic-gate 		*/
35997c478bd9Sstevel@tonic-gate 
36007c478bd9Sstevel@tonic-gate 		if (forkflag)
36017c478bd9Sstevel@tonic-gate 		{
36027c478bd9Sstevel@tonic-gate 			/* Reset global flags */
36037c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
36047c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
36057c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
36067c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
36077c478bd9Sstevel@tonic-gate 		}
36087c478bd9Sstevel@tonic-gate 
36097c478bd9Sstevel@tonic-gate 		/* set basic modes, etc. */
36107c478bd9Sstevel@tonic-gate 		sm_clear_events();
36117c478bd9Sstevel@tonic-gate 		clearstats();
36127c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
36137c478bd9Sstevel@tonic-gate 		clearenvelope(e, false, rpool);
36147c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
36157c478bd9Sstevel@tonic-gate 		set_delivery_mode(SM_DELIVER, e);
36167c478bd9Sstevel@tonic-gate 		e->e_errormode = EM_MAIL;
36177c478bd9Sstevel@tonic-gate 		e->e_id = id;
36187c478bd9Sstevel@tonic-gate 		e->e_qgrp = qgrp;
36197c478bd9Sstevel@tonic-gate 		e->e_qdir = qdir;
36207c478bd9Sstevel@tonic-gate 		GrabTo = UseErrorsTo = false;
36217c478bd9Sstevel@tonic-gate 		ExitStat = EX_OK;
36227c478bd9Sstevel@tonic-gate 		if (forkflag)
36237c478bd9Sstevel@tonic-gate 		{
36247c478bd9Sstevel@tonic-gate 			disconnect(1, e);
36257c478bd9Sstevel@tonic-gate 			set_op_mode(MD_QUEUERUN);
36267c478bd9Sstevel@tonic-gate 		}
36277c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, e, "%s from queue", qid_printname(e));
36287c478bd9Sstevel@tonic-gate 		if (LogLevel > 76)
36297c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
36307c478bd9Sstevel@tonic-gate 				  (int) CurrentPid);
36317c478bd9Sstevel@tonic-gate 
36327c478bd9Sstevel@tonic-gate 		/* don't use the headers from sendmail.cf... */
36337c478bd9Sstevel@tonic-gate 		e->e_header = NULL;
36347c478bd9Sstevel@tonic-gate 
36357c478bd9Sstevel@tonic-gate 		/* read the queue control file -- return if locked */
36367c478bd9Sstevel@tonic-gate 		if (!readqf(e, false))
36377c478bd9Sstevel@tonic-gate 		{
36387c478bd9Sstevel@tonic-gate 			if (tTd(40, 4) && e->e_id != NULL)
36397c478bd9Sstevel@tonic-gate 				sm_dprintf("readqf(%s) failed\n",
36407c478bd9Sstevel@tonic-gate 					qid_printname(e));
36417c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
36427c478bd9Sstevel@tonic-gate 			if (forkflag)
36437c478bd9Sstevel@tonic-gate 				finis(false, true, EX_OK);
36447c478bd9Sstevel@tonic-gate 			else
36457c478bd9Sstevel@tonic-gate 			{
36467c478bd9Sstevel@tonic-gate 				/* adding this frees 8 bytes */
36477c478bd9Sstevel@tonic-gate 				clearenvelope(e, false, rpool);
36487c478bd9Sstevel@tonic-gate 
36497c478bd9Sstevel@tonic-gate 				/* adding this frees 12 bytes */
36507c478bd9Sstevel@tonic-gate 				sm_rpool_free(rpool);
36517c478bd9Sstevel@tonic-gate 				e->e_rpool = NULL;
36527c478bd9Sstevel@tonic-gate 				return 0;
36537c478bd9Sstevel@tonic-gate 			}
36547c478bd9Sstevel@tonic-gate 		}
36557c478bd9Sstevel@tonic-gate 
36567c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_INQUEUE;
36577c478bd9Sstevel@tonic-gate 		eatheader(e, requeueflag, true);
36587c478bd9Sstevel@tonic-gate 
36597c478bd9Sstevel@tonic-gate 		if (requeueflag)
36607c478bd9Sstevel@tonic-gate 			queueup(e, false, false);
36617c478bd9Sstevel@tonic-gate 
36627c478bd9Sstevel@tonic-gate 		/* do the delivery */
36637c478bd9Sstevel@tonic-gate 		sendall(e, SM_DELIVER);
36647c478bd9Sstevel@tonic-gate 
36657c478bd9Sstevel@tonic-gate 		/* finish up and exit */
36667c478bd9Sstevel@tonic-gate 		if (forkflag)
36677c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
36687c478bd9Sstevel@tonic-gate 		else
36697c478bd9Sstevel@tonic-gate 		{
36707c478bd9Sstevel@tonic-gate 			dropenvelope(e, true, false);
36717c478bd9Sstevel@tonic-gate 			sm_rpool_free(rpool);
36727c478bd9Sstevel@tonic-gate 			e->e_rpool = NULL;
36737c478bd9Sstevel@tonic-gate 		}
36747c478bd9Sstevel@tonic-gate 	}
36757c478bd9Sstevel@tonic-gate 	e->e_id = NULL;
36767c478bd9Sstevel@tonic-gate 	return pid;
36777c478bd9Sstevel@tonic-gate }
36787c478bd9Sstevel@tonic-gate 
36797c478bd9Sstevel@tonic-gate /*
36807c478bd9Sstevel@tonic-gate **  DOWORKLIST -- process a list of envelopes as work requests
36817c478bd9Sstevel@tonic-gate **
36827c478bd9Sstevel@tonic-gate **	Similar to dowork(), except that after forking, it processes an
36837c478bd9Sstevel@tonic-gate **	envelope and its siblings, treating each envelope as a work request.
36847c478bd9Sstevel@tonic-gate **
36857c478bd9Sstevel@tonic-gate **	Parameters:
36867c478bd9Sstevel@tonic-gate **		el -- envelope to be processed including its siblings.
36877c478bd9Sstevel@tonic-gate **		forkflag -- if set, run this in background.
36887c478bd9Sstevel@tonic-gate **		requeueflag -- if set, reinstantiate the queue quickly.
36897c478bd9Sstevel@tonic-gate **			This is used when expanding aliases in the queue.
36907c478bd9Sstevel@tonic-gate **			If forkflag is also set, it doesn't wait for the
36917c478bd9Sstevel@tonic-gate **			child.
36927c478bd9Sstevel@tonic-gate **
36937c478bd9Sstevel@tonic-gate **	Returns:
36947c478bd9Sstevel@tonic-gate **		process id of process that is running the queue job.
36957c478bd9Sstevel@tonic-gate **
36967c478bd9Sstevel@tonic-gate **	Side Effects:
36977c478bd9Sstevel@tonic-gate **		The work request is satisfied if possible.
36987c478bd9Sstevel@tonic-gate */
36997c478bd9Sstevel@tonic-gate 
37007c478bd9Sstevel@tonic-gate pid_t
37017c478bd9Sstevel@tonic-gate doworklist(el, forkflag, requeueflag)
37027c478bd9Sstevel@tonic-gate 	ENVELOPE *el;
37037c478bd9Sstevel@tonic-gate 	bool forkflag;
37047c478bd9Sstevel@tonic-gate 	bool requeueflag;
37057c478bd9Sstevel@tonic-gate {
37067c478bd9Sstevel@tonic-gate 	register pid_t pid;
37077c478bd9Sstevel@tonic-gate 	ENVELOPE *ei;
37087c478bd9Sstevel@tonic-gate 
37097c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
37107c478bd9Sstevel@tonic-gate 		sm_dprintf("doworklist()\n");
37117c478bd9Sstevel@tonic-gate 
37127c478bd9Sstevel@tonic-gate 	/*
37137c478bd9Sstevel@tonic-gate 	**  Fork for work.
37147c478bd9Sstevel@tonic-gate 	*/
37157c478bd9Sstevel@tonic-gate 
37167c478bd9Sstevel@tonic-gate 	if (forkflag)
37177c478bd9Sstevel@tonic-gate 	{
37187c478bd9Sstevel@tonic-gate 		/*
37197c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the
37207c478bd9Sstevel@tonic-gate 		**  parent does not wait, the parent may close the
37217c478bd9Sstevel@tonic-gate 		**  maps thereby removing any shared memory used by
37227c478bd9Sstevel@tonic-gate 		**  the map.  Therefore, close the maps now so the
37237c478bd9Sstevel@tonic-gate 		**  child will dynamically open them if necessary.
37247c478bd9Sstevel@tonic-gate 		*/
37257c478bd9Sstevel@tonic-gate 
37267c478bd9Sstevel@tonic-gate 		closemaps(false);
37277c478bd9Sstevel@tonic-gate 
37287c478bd9Sstevel@tonic-gate 		pid = fork();
37297c478bd9Sstevel@tonic-gate 		if (pid < 0)
37307c478bd9Sstevel@tonic-gate 		{
37317c478bd9Sstevel@tonic-gate 			syserr("doworklist: cannot fork");
37327c478bd9Sstevel@tonic-gate 			return 0;
37337c478bd9Sstevel@tonic-gate 		}
37347c478bd9Sstevel@tonic-gate 		else if (pid > 0)
37357c478bd9Sstevel@tonic-gate 		{
37367c478bd9Sstevel@tonic-gate 			/* parent -- clean out connection cache */
37377c478bd9Sstevel@tonic-gate 			mci_flush(false, NULL);
37387c478bd9Sstevel@tonic-gate 		}
37397c478bd9Sstevel@tonic-gate 		else
37407c478bd9Sstevel@tonic-gate 		{
37417c478bd9Sstevel@tonic-gate 			/*
37427c478bd9Sstevel@tonic-gate 			**  Initialize exception stack and default exception
37437c478bd9Sstevel@tonic-gate 			**  handler for child process.
37447c478bd9Sstevel@tonic-gate 			*/
37457c478bd9Sstevel@tonic-gate 
37467c478bd9Sstevel@tonic-gate 			/* Reset global flags */
37477c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
37487c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
37497c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
37507c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
37517c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
37527c478bd9Sstevel@tonic-gate 			sm_exc_newthread(fatal_error);
37537c478bd9Sstevel@tonic-gate 
37547c478bd9Sstevel@tonic-gate 			/*
37557c478bd9Sstevel@tonic-gate 			**  See note above about SMTP processes and SIGCHLD.
37567c478bd9Sstevel@tonic-gate 			*/
37577c478bd9Sstevel@tonic-gate 
37587c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP ||
37597c478bd9Sstevel@tonic-gate 			    OpMode == MD_DAEMON ||
37607c478bd9Sstevel@tonic-gate 			    MaxQueueChildren > 0)
37617c478bd9Sstevel@tonic-gate 			{
37627c478bd9Sstevel@tonic-gate 				proc_list_clear();
37637c478bd9Sstevel@tonic-gate 				sm_releasesignal(SIGCHLD);
37647c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, SIG_DFL);
37657c478bd9Sstevel@tonic-gate 			}
37667c478bd9Sstevel@tonic-gate 
37677c478bd9Sstevel@tonic-gate 			/* child -- error messages to the transcript */
37687c478bd9Sstevel@tonic-gate 			QuickAbort = OnlyOneError = false;
37697c478bd9Sstevel@tonic-gate 		}
37707c478bd9Sstevel@tonic-gate 	}
37717c478bd9Sstevel@tonic-gate 	else
37727c478bd9Sstevel@tonic-gate 	{
37737c478bd9Sstevel@tonic-gate 		pid = 0;
37747c478bd9Sstevel@tonic-gate 	}
37757c478bd9Sstevel@tonic-gate 
37767c478bd9Sstevel@tonic-gate 	if (pid != 0)
37777c478bd9Sstevel@tonic-gate 		return pid;
37787c478bd9Sstevel@tonic-gate 
37797c478bd9Sstevel@tonic-gate 	/*
37807c478bd9Sstevel@tonic-gate 	**  IN CHILD
37817c478bd9Sstevel@tonic-gate 	**	Lock the control file to avoid duplicate deliveries.
37827c478bd9Sstevel@tonic-gate 	**		Then run the file as though we had just read it.
37837c478bd9Sstevel@tonic-gate 	**	We save an idea of the temporary name so we
37847c478bd9Sstevel@tonic-gate 	**		can recover on interrupt.
37857c478bd9Sstevel@tonic-gate 	*/
37867c478bd9Sstevel@tonic-gate 
37877c478bd9Sstevel@tonic-gate 	if (forkflag)
37887c478bd9Sstevel@tonic-gate 	{
37897c478bd9Sstevel@tonic-gate 		/* Reset global flags */
37907c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
37917c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
37927c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
37937c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
37947c478bd9Sstevel@tonic-gate 	}
37957c478bd9Sstevel@tonic-gate 
37967c478bd9Sstevel@tonic-gate 	/* set basic modes, etc. */
37977c478bd9Sstevel@tonic-gate 	sm_clear_events();
37987c478bd9Sstevel@tonic-gate 	clearstats();
37997c478bd9Sstevel@tonic-gate 	GrabTo = UseErrorsTo = false;
38007c478bd9Sstevel@tonic-gate 	ExitStat = EX_OK;
38017c478bd9Sstevel@tonic-gate 	if (forkflag)
38027c478bd9Sstevel@tonic-gate 	{
38037c478bd9Sstevel@tonic-gate 		disconnect(1, el);
38047c478bd9Sstevel@tonic-gate 		set_op_mode(MD_QUEUERUN);
38057c478bd9Sstevel@tonic-gate 	}
38067c478bd9Sstevel@tonic-gate 	if (LogLevel > 76)
38077c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
38087c478bd9Sstevel@tonic-gate 			  (int) CurrentPid);
38097c478bd9Sstevel@tonic-gate 
38107c478bd9Sstevel@tonic-gate 	for (ei = el; ei != NULL; ei = ei->e_sibling)
38117c478bd9Sstevel@tonic-gate 	{
38127c478bd9Sstevel@tonic-gate 		ENVELOPE e;
38137c478bd9Sstevel@tonic-gate 		SM_RPOOL_T *rpool;
38147c478bd9Sstevel@tonic-gate 
38157c478bd9Sstevel@tonic-gate 		if (WILL_BE_QUEUED(ei->e_sendmode))
38167c478bd9Sstevel@tonic-gate 			continue;
38177c478bd9Sstevel@tonic-gate 		else if (QueueMode != QM_QUARANTINE &&
38187c478bd9Sstevel@tonic-gate 			 ei->e_quarmsg != NULL)
38197c478bd9Sstevel@tonic-gate 			continue;
38207c478bd9Sstevel@tonic-gate 
38217c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
38227c478bd9Sstevel@tonic-gate 		clearenvelope(&e, true, rpool);
38237c478bd9Sstevel@tonic-gate 		e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
38247c478bd9Sstevel@tonic-gate 		set_delivery_mode(SM_DELIVER, &e);
38257c478bd9Sstevel@tonic-gate 		e.e_errormode = EM_MAIL;
38267c478bd9Sstevel@tonic-gate 		e.e_id = ei->e_id;
38277c478bd9Sstevel@tonic-gate 		e.e_qgrp = ei->e_qgrp;
38287c478bd9Sstevel@tonic-gate 		e.e_qdir = ei->e_qdir;
38297c478bd9Sstevel@tonic-gate 		openxscript(&e);
38307c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
38317c478bd9Sstevel@tonic-gate 
38327c478bd9Sstevel@tonic-gate 		/* don't use the headers from sendmail.cf... */
38337c478bd9Sstevel@tonic-gate 		e.e_header = NULL;
38347c478bd9Sstevel@tonic-gate 		CurEnv = &e;
38357c478bd9Sstevel@tonic-gate 
38367c478bd9Sstevel@tonic-gate 		/* read the queue control file -- return if locked */
38377c478bd9Sstevel@tonic-gate 		if (readqf(&e, false))
38387c478bd9Sstevel@tonic-gate 		{
38397c478bd9Sstevel@tonic-gate 			e.e_flags |= EF_INQUEUE;
38407c478bd9Sstevel@tonic-gate 			eatheader(&e, requeueflag, true);
38417c478bd9Sstevel@tonic-gate 
38427c478bd9Sstevel@tonic-gate 			if (requeueflag)
38437c478bd9Sstevel@tonic-gate 				queueup(&e, false, false);
38447c478bd9Sstevel@tonic-gate 
38457c478bd9Sstevel@tonic-gate 			/* do the delivery */
38467c478bd9Sstevel@tonic-gate 			sendall(&e, SM_DELIVER);
38477c478bd9Sstevel@tonic-gate 			dropenvelope(&e, true, false);
38487c478bd9Sstevel@tonic-gate 		}
38497c478bd9Sstevel@tonic-gate 		else
38507c478bd9Sstevel@tonic-gate 		{
38517c478bd9Sstevel@tonic-gate 			if (tTd(40, 4) && e.e_id != NULL)
38527c478bd9Sstevel@tonic-gate 				sm_dprintf("readqf(%s) failed\n",
38537c478bd9Sstevel@tonic-gate 					qid_printname(&e));
38547c478bd9Sstevel@tonic-gate 		}
38557c478bd9Sstevel@tonic-gate 		sm_rpool_free(rpool);
38567c478bd9Sstevel@tonic-gate 		ei->e_id = NULL;
38577c478bd9Sstevel@tonic-gate 	}
38587c478bd9Sstevel@tonic-gate 
38597c478bd9Sstevel@tonic-gate 	/* restore CurEnv */
38607c478bd9Sstevel@tonic-gate 	CurEnv = el;
38617c478bd9Sstevel@tonic-gate 
38627c478bd9Sstevel@tonic-gate 	/* finish up and exit */
38637c478bd9Sstevel@tonic-gate 	if (forkflag)
38647c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
38657c478bd9Sstevel@tonic-gate 	return 0;
38667c478bd9Sstevel@tonic-gate }
38677c478bd9Sstevel@tonic-gate /*
38687c478bd9Sstevel@tonic-gate **  READQF -- read queue file and set up environment.
38697c478bd9Sstevel@tonic-gate **
38707c478bd9Sstevel@tonic-gate **	Parameters:
38717c478bd9Sstevel@tonic-gate **		e -- the envelope of the job to run.
38727c478bd9Sstevel@tonic-gate **		openonly -- only open the qf (returned as e_lockfp)
38737c478bd9Sstevel@tonic-gate **
38747c478bd9Sstevel@tonic-gate **	Returns:
38757c478bd9Sstevel@tonic-gate **		true if it successfully read the queue file.
38767c478bd9Sstevel@tonic-gate **		false otherwise.
38777c478bd9Sstevel@tonic-gate **
38787c478bd9Sstevel@tonic-gate **	Side Effects:
38797c478bd9Sstevel@tonic-gate **		The queue file is returned locked.
38807c478bd9Sstevel@tonic-gate */
38817c478bd9Sstevel@tonic-gate 
38827c478bd9Sstevel@tonic-gate static bool
38837c478bd9Sstevel@tonic-gate readqf(e, openonly)
38847c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
38857c478bd9Sstevel@tonic-gate 	bool openonly;
38867c478bd9Sstevel@tonic-gate {
38877c478bd9Sstevel@tonic-gate 	register SM_FILE_T *qfp;
38887c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
38897c478bd9Sstevel@tonic-gate 	struct stat st, stf;
38907c478bd9Sstevel@tonic-gate 	char *bp;
38917c478bd9Sstevel@tonic-gate 	int qfver = 0;
38927c478bd9Sstevel@tonic-gate 	long hdrsize = 0;
38937c478bd9Sstevel@tonic-gate 	register char *p;
38947c478bd9Sstevel@tonic-gate 	char *frcpt = NULL;
38957c478bd9Sstevel@tonic-gate 	char *orcpt = NULL;
38967c478bd9Sstevel@tonic-gate 	bool nomore = false;
38977c478bd9Sstevel@tonic-gate 	bool bogus = false;
38987c478bd9Sstevel@tonic-gate 	MODE_T qsafe;
38997c478bd9Sstevel@tonic-gate 	char *err;
39007c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
39017c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
39027c478bd9Sstevel@tonic-gate 
39037c478bd9Sstevel@tonic-gate 	/*
39047c478bd9Sstevel@tonic-gate 	**  Read and process the file.
39057c478bd9Sstevel@tonic-gate 	*/
39067c478bd9Sstevel@tonic-gate 
39077c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof qf);
39087c478bd9Sstevel@tonic-gate 	qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR_B, NULL);
39097c478bd9Sstevel@tonic-gate 	if (qfp == NULL)
39107c478bd9Sstevel@tonic-gate 	{
39117c478bd9Sstevel@tonic-gate 		int save_errno = errno;
39127c478bd9Sstevel@tonic-gate 
39137c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39147c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
39157c478bd9Sstevel@tonic-gate 				qf, sm_errstring(errno));
39167c478bd9Sstevel@tonic-gate 		errno = save_errno;
39177c478bd9Sstevel@tonic-gate 		if (errno != ENOENT
39187c478bd9Sstevel@tonic-gate 		    )
39197c478bd9Sstevel@tonic-gate 			syserr("readqf: no control file %s", qf);
39207c478bd9Sstevel@tonic-gate 		RELEASE_QUEUE;
39217c478bd9Sstevel@tonic-gate 		return false;
39227c478bd9Sstevel@tonic-gate 	}
39237c478bd9Sstevel@tonic-gate 
39247c478bd9Sstevel@tonic-gate 	if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
39257c478bd9Sstevel@tonic-gate 		      LOCK_EX|LOCK_NB))
39267c478bd9Sstevel@tonic-gate 	{
39277c478bd9Sstevel@tonic-gate 		/* being processed by another queuer */
39287c478bd9Sstevel@tonic-gate 		if (Verbose)
39297c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
39307c478bd9Sstevel@tonic-gate 					     "%s: locked\n", e->e_id);
39317c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39327c478bd9Sstevel@tonic-gate 			sm_dprintf("%s: locked\n", e->e_id);
39337c478bd9Sstevel@tonic-gate 		if (LogLevel > 19)
39347c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "locked");
39357c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39367c478bd9Sstevel@tonic-gate 		RELEASE_QUEUE;
39377c478bd9Sstevel@tonic-gate 		return false;
39387c478bd9Sstevel@tonic-gate 	}
39397c478bd9Sstevel@tonic-gate 
39407c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
39417c478bd9Sstevel@tonic-gate 
39427c478bd9Sstevel@tonic-gate 	/*
39437c478bd9Sstevel@tonic-gate 	**  Prevent locking race condition.
39447c478bd9Sstevel@tonic-gate 	**
39457c478bd9Sstevel@tonic-gate 	**  Process A: readqf(): qfp = fopen(qffile)
39467c478bd9Sstevel@tonic-gate 	**  Process B: queueup(): rename(tf, qf)
39477c478bd9Sstevel@tonic-gate 	**  Process B: unlocks(tf)
39487c478bd9Sstevel@tonic-gate 	**  Process A: lockfile(qf);
39497c478bd9Sstevel@tonic-gate 	**
39507c478bd9Sstevel@tonic-gate 	**  Process A (us) has the old qf file (before the rename deleted
39517c478bd9Sstevel@tonic-gate 	**  the directory entry) and will be delivering based on old data.
39527c478bd9Sstevel@tonic-gate 	**  This can lead to multiple deliveries of the same recipients.
39537c478bd9Sstevel@tonic-gate 	**
39547c478bd9Sstevel@tonic-gate 	**  Catch this by checking if the underlying qf file has changed
39557c478bd9Sstevel@tonic-gate 	**  *after* acquiring our lock and if so, act as though the file
39567c478bd9Sstevel@tonic-gate 	**  was still locked (i.e., just return like the lockfile() case
39577c478bd9Sstevel@tonic-gate 	**  above.
39587c478bd9Sstevel@tonic-gate 	*/
39597c478bd9Sstevel@tonic-gate 
39607c478bd9Sstevel@tonic-gate 	if (stat(qf, &stf) < 0 ||
39617c478bd9Sstevel@tonic-gate 	    fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
39627c478bd9Sstevel@tonic-gate 	{
39637c478bd9Sstevel@tonic-gate 		/* must have been being processed by someone else */
39647c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39657c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
39667c478bd9Sstevel@tonic-gate 				qf, sm_errstring(errno));
39677c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39687c478bd9Sstevel@tonic-gate 		return false;
39697c478bd9Sstevel@tonic-gate 	}
39707c478bd9Sstevel@tonic-gate 
39717c478bd9Sstevel@tonic-gate 	if (st.st_nlink != stf.st_nlink ||
39727c478bd9Sstevel@tonic-gate 	    st.st_dev != stf.st_dev ||
39737c478bd9Sstevel@tonic-gate 	    ST_INODE(st) != ST_INODE(stf) ||
39747c478bd9Sstevel@tonic-gate #if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
39757c478bd9Sstevel@tonic-gate 	    st.st_gen != stf.st_gen ||
39767c478bd9Sstevel@tonic-gate #endif /* HAS_ST_GEN && 0 */
39777c478bd9Sstevel@tonic-gate 	    st.st_uid != stf.st_uid ||
39787c478bd9Sstevel@tonic-gate 	    st.st_gid != stf.st_gid ||
39797c478bd9Sstevel@tonic-gate 	    st.st_size != stf.st_size)
39807c478bd9Sstevel@tonic-gate 	{
39817c478bd9Sstevel@tonic-gate 		/* changed after opened */
39827c478bd9Sstevel@tonic-gate 		if (Verbose)
39837c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
39847c478bd9Sstevel@tonic-gate 					     "%s: changed\n", e->e_id);
39857c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39867c478bd9Sstevel@tonic-gate 			sm_dprintf("%s: changed\n", e->e_id);
39877c478bd9Sstevel@tonic-gate 		if (LogLevel > 19)
39887c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "changed");
39897c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39907c478bd9Sstevel@tonic-gate 		return false;
39917c478bd9Sstevel@tonic-gate 	}
39927c478bd9Sstevel@tonic-gate 
39937c478bd9Sstevel@tonic-gate 	/*
39947c478bd9Sstevel@tonic-gate 	**  Check the queue file for plausibility to avoid attacks.
39957c478bd9Sstevel@tonic-gate 	*/
39967c478bd9Sstevel@tonic-gate 
39977c478bd9Sstevel@tonic-gate 	qsafe = S_IWOTH|S_IWGRP;
39987c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
39997c478bd9Sstevel@tonic-gate 		qsafe &= ~S_IWGRP;
40007c478bd9Sstevel@tonic-gate 
40017c478bd9Sstevel@tonic-gate 	bogus = st.st_uid != geteuid() &&
40027c478bd9Sstevel@tonic-gate 		st.st_uid != TrustedUid &&
40037c478bd9Sstevel@tonic-gate 		geteuid() != RealUid;
40047c478bd9Sstevel@tonic-gate 
40057c478bd9Sstevel@tonic-gate 	/*
40067c478bd9Sstevel@tonic-gate 	**  If this qf file results from a set-group-ID binary, then
40077c478bd9Sstevel@tonic-gate 	**  we check whether the directory is group-writable,
40087c478bd9Sstevel@tonic-gate 	**  the queue file mode contains the group-writable bit, and
40097c478bd9Sstevel@tonic-gate 	**  the groups are the same.
40107c478bd9Sstevel@tonic-gate 	**  Notice: this requires that the set-group-ID binary is used to
40117c478bd9Sstevel@tonic-gate 	**  run the queue!
40127c478bd9Sstevel@tonic-gate 	*/
40137c478bd9Sstevel@tonic-gate 
40147c478bd9Sstevel@tonic-gate 	if (bogus && st.st_gid == getegid() && UseMSP)
40157c478bd9Sstevel@tonic-gate 	{
40167c478bd9Sstevel@tonic-gate 		char delim;
40177c478bd9Sstevel@tonic-gate 		struct stat dst;
40187c478bd9Sstevel@tonic-gate 
40197c478bd9Sstevel@tonic-gate 		bp = SM_LAST_DIR_DELIM(qf);
40207c478bd9Sstevel@tonic-gate 		if (bp == NULL)
40217c478bd9Sstevel@tonic-gate 			delim = '\0';
40227c478bd9Sstevel@tonic-gate 		else
40237c478bd9Sstevel@tonic-gate 		{
40247c478bd9Sstevel@tonic-gate 			delim = *bp;
40257c478bd9Sstevel@tonic-gate 			*bp = '\0';
40267c478bd9Sstevel@tonic-gate 		}
40277c478bd9Sstevel@tonic-gate 		if (stat(delim == '\0' ? "." : qf, &dst) < 0)
40287c478bd9Sstevel@tonic-gate 			syserr("readqf: cannot stat directory %s",
40297c478bd9Sstevel@tonic-gate 				delim == '\0' ? "." : qf);
40307c478bd9Sstevel@tonic-gate 		else
40317c478bd9Sstevel@tonic-gate 		{
40327c478bd9Sstevel@tonic-gate 			bogus = !(bitset(S_IWGRP, QueueFileMode) &&
40337c478bd9Sstevel@tonic-gate 				  bitset(S_IWGRP, dst.st_mode) &&
40347c478bd9Sstevel@tonic-gate 				  dst.st_gid == st.st_gid);
40357c478bd9Sstevel@tonic-gate 		}
40367c478bd9Sstevel@tonic-gate 		if (delim != '\0')
40377c478bd9Sstevel@tonic-gate 			*bp = delim;
40387c478bd9Sstevel@tonic-gate 	}
40397c478bd9Sstevel@tonic-gate 	if (!bogus)
40407c478bd9Sstevel@tonic-gate 		bogus = bitset(qsafe, st.st_mode);
40417c478bd9Sstevel@tonic-gate 	if (bogus)
40427c478bd9Sstevel@tonic-gate 	{
40437c478bd9Sstevel@tonic-gate 		if (LogLevel > 0)
40447c478bd9Sstevel@tonic-gate 		{
40457c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, e->e_id,
40467c478bd9Sstevel@tonic-gate 				  "bogus queue file, uid=%d, gid=%d, mode=%o",
40477c478bd9Sstevel@tonic-gate 				  st.st_uid, st.st_gid, st.st_mode);
40487c478bd9Sstevel@tonic-gate 		}
40497c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
40507c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): bogus file\n", qf);
40517c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_INQUEUE;
40527c478bd9Sstevel@tonic-gate 		if (!openonly)
40537c478bd9Sstevel@tonic-gate 			loseqfile(e, "bogus file uid/gid in mqueue");
40547c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40557c478bd9Sstevel@tonic-gate 		return false;
40567c478bd9Sstevel@tonic-gate 	}
40577c478bd9Sstevel@tonic-gate 
40587c478bd9Sstevel@tonic-gate 	if (st.st_size == 0)
40597c478bd9Sstevel@tonic-gate 	{
40607c478bd9Sstevel@tonic-gate 		/* must be a bogus file -- if also old, just remove it */
40617c478bd9Sstevel@tonic-gate 		if (!openonly && st.st_ctime + 10 * 60 < curtime())
40627c478bd9Sstevel@tonic-gate 		{
40637c478bd9Sstevel@tonic-gate 			(void) xunlink(queuename(e, DATAFL_LETTER));
40647c478bd9Sstevel@tonic-gate 			(void) xunlink(queuename(e, ANYQFL_LETTER));
40657c478bd9Sstevel@tonic-gate 		}
40667c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40677c478bd9Sstevel@tonic-gate 		return false;
40687c478bd9Sstevel@tonic-gate 	}
40697c478bd9Sstevel@tonic-gate 
40707c478bd9Sstevel@tonic-gate 	if (st.st_nlink == 0)
40717c478bd9Sstevel@tonic-gate 	{
40727c478bd9Sstevel@tonic-gate 		/*
40737c478bd9Sstevel@tonic-gate 		**  Race condition -- we got a file just as it was being
40747c478bd9Sstevel@tonic-gate 		**  unlinked.  Just assume it is zero length.
40757c478bd9Sstevel@tonic-gate 		*/
40767c478bd9Sstevel@tonic-gate 
40777c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40787c478bd9Sstevel@tonic-gate 		return false;
40797c478bd9Sstevel@tonic-gate 	}
40807c478bd9Sstevel@tonic-gate 
40817c478bd9Sstevel@tonic-gate #if _FFR_TRUSTED_QF
40827c478bd9Sstevel@tonic-gate 	/*
40837c478bd9Sstevel@tonic-gate 	**  If we don't own the file mark it as unsafe.
40847c478bd9Sstevel@tonic-gate 	**  However, allow TrustedUser to own it as well
40857c478bd9Sstevel@tonic-gate 	**  in case TrustedUser manipulates the queue.
40867c478bd9Sstevel@tonic-gate 	*/
40877c478bd9Sstevel@tonic-gate 
40887c478bd9Sstevel@tonic-gate 	if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
40897c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_UNSAFE;
40907c478bd9Sstevel@tonic-gate #else /* _FFR_TRUSTED_QF */
40917c478bd9Sstevel@tonic-gate 	/* If we don't own the file mark it as unsafe */
40927c478bd9Sstevel@tonic-gate 	if (st.st_uid != geteuid())
40937c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_UNSAFE;
40947c478bd9Sstevel@tonic-gate #endif /* _FFR_TRUSTED_QF */
40957c478bd9Sstevel@tonic-gate 
40967c478bd9Sstevel@tonic-gate 	/* good file -- save this lock */
40977c478bd9Sstevel@tonic-gate 	e->e_lockfp = qfp;
40987c478bd9Sstevel@tonic-gate 
40997c478bd9Sstevel@tonic-gate 	/* Just wanted the open file */
41007c478bd9Sstevel@tonic-gate 	if (openonly)
41017c478bd9Sstevel@tonic-gate 		return true;
41027c478bd9Sstevel@tonic-gate 
41037c478bd9Sstevel@tonic-gate 	/* do basic system initialization */
41047c478bd9Sstevel@tonic-gate 	initsys(e);
41057c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
41067c478bd9Sstevel@tonic-gate 
41077c478bd9Sstevel@tonic-gate 	LineNumber = 0;
41087c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_GLOBALERRS;
41097c478bd9Sstevel@tonic-gate 	set_op_mode(MD_QUEUERUN);
41107c478bd9Sstevel@tonic-gate 	ctladdr = NULL;
41117c478bd9Sstevel@tonic-gate 	e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
41127c478bd9Sstevel@tonic-gate 	e->e_dfqgrp = e->e_qgrp;
41137c478bd9Sstevel@tonic-gate 	e->e_dfqdir = e->e_qdir;
41147c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_MACRO
41157c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
41167c478bd9Sstevel@tonic-gate 		  qid_printqueue(e->e_qgrp, e->e_qdir));
41177c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_MACRO */
41187c478bd9Sstevel@tonic-gate 	e->e_dfino = -1;
41197c478bd9Sstevel@tonic-gate 	e->e_msgsize = -1;
41207c478bd9Sstevel@tonic-gate 	while ((bp = fgetfolded(buf, sizeof buf, qfp)) != NULL)
41217c478bd9Sstevel@tonic-gate 	{
41227c478bd9Sstevel@tonic-gate 		unsigned long qflags;
41237c478bd9Sstevel@tonic-gate 		ADDRESS *q;
41247c478bd9Sstevel@tonic-gate 		int r;
41257c478bd9Sstevel@tonic-gate 		time_t now;
41267c478bd9Sstevel@tonic-gate 		auto char *ep;
41277c478bd9Sstevel@tonic-gate 
41287c478bd9Sstevel@tonic-gate 		if (tTd(40, 4))
41297c478bd9Sstevel@tonic-gate 			sm_dprintf("+++++ %s\n", bp);
41307c478bd9Sstevel@tonic-gate 		if (nomore)
41317c478bd9Sstevel@tonic-gate 		{
41327c478bd9Sstevel@tonic-gate 			/* hack attack */
41337c478bd9Sstevel@tonic-gate   hackattack:
41347c478bd9Sstevel@tonic-gate 			syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
41357c478bd9Sstevel@tonic-gate 			       bp);
41367c478bd9Sstevel@tonic-gate 			err = "bogus queue line";
41377c478bd9Sstevel@tonic-gate 			goto fail;
41387c478bd9Sstevel@tonic-gate 		}
41397c478bd9Sstevel@tonic-gate 		switch (bp[0])
41407c478bd9Sstevel@tonic-gate 		{
41417c478bd9Sstevel@tonic-gate 		  case 'A':		/* AUTH= parameter */
41427c478bd9Sstevel@tonic-gate 			if (!xtextok(&bp[1]))
41437c478bd9Sstevel@tonic-gate 				goto hackattack;
41447c478bd9Sstevel@tonic-gate 			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
41457c478bd9Sstevel@tonic-gate 			break;
41467c478bd9Sstevel@tonic-gate 
41477c478bd9Sstevel@tonic-gate 		  case 'B':		/* body type */
41487c478bd9Sstevel@tonic-gate 			r = check_bodytype(&bp[1]);
41497c478bd9Sstevel@tonic-gate 			if (!BODYTYPE_VALID(r))
41507c478bd9Sstevel@tonic-gate 				goto hackattack;
41517c478bd9Sstevel@tonic-gate 			e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
41527c478bd9Sstevel@tonic-gate 			break;
41537c478bd9Sstevel@tonic-gate 
41547c478bd9Sstevel@tonic-gate 		  case 'C':		/* specify controlling user */
41557c478bd9Sstevel@tonic-gate 			ctladdr = setctluser(&bp[1], qfver, e);
41567c478bd9Sstevel@tonic-gate 			break;
41577c478bd9Sstevel@tonic-gate 
41587c478bd9Sstevel@tonic-gate 		  case 'D':		/* data file name */
41597c478bd9Sstevel@tonic-gate 			/* obsolete -- ignore */
41607c478bd9Sstevel@tonic-gate 			break;
41617c478bd9Sstevel@tonic-gate 
41627c478bd9Sstevel@tonic-gate 		  case 'd':		/* data file directory name */
41637c478bd9Sstevel@tonic-gate 			{
41647c478bd9Sstevel@tonic-gate 				int qgrp, qdir;
41657c478bd9Sstevel@tonic-gate 
41667c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
41677c478bd9Sstevel@tonic-gate 				/* forbid queue groups in MSP? */
41687c478bd9Sstevel@tonic-gate 				if (UseMSP)
41697c478bd9Sstevel@tonic-gate 					goto hackattack;
41707c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
41717c478bd9Sstevel@tonic-gate 				for (qgrp = 0;
41727c478bd9Sstevel@tonic-gate 				     qgrp < NumQueue && Queue[qgrp] != NULL;
41737c478bd9Sstevel@tonic-gate 				     ++qgrp)
41747c478bd9Sstevel@tonic-gate 				{
41757c478bd9Sstevel@tonic-gate 					for (qdir = 0;
41767c478bd9Sstevel@tonic-gate 					     qdir < Queue[qgrp]->qg_numqueues;
41777c478bd9Sstevel@tonic-gate 					     ++qdir)
41787c478bd9Sstevel@tonic-gate 					{
41797c478bd9Sstevel@tonic-gate 						if (strcmp(&bp[1],
41807c478bd9Sstevel@tonic-gate 							   Queue[qgrp]->qg_qpaths[qdir].qp_name)
41817c478bd9Sstevel@tonic-gate 						    == 0)
41827c478bd9Sstevel@tonic-gate 						{
41837c478bd9Sstevel@tonic-gate 							e->e_dfqgrp = qgrp;
41847c478bd9Sstevel@tonic-gate 							e->e_dfqdir = qdir;
41857c478bd9Sstevel@tonic-gate 							goto done;
41867c478bd9Sstevel@tonic-gate 						}
41877c478bd9Sstevel@tonic-gate 					}
41887c478bd9Sstevel@tonic-gate 				}
41897c478bd9Sstevel@tonic-gate 				err = "bogus queue file directory";
41907c478bd9Sstevel@tonic-gate 				goto fail;
41917c478bd9Sstevel@tonic-gate 			  done:
41927c478bd9Sstevel@tonic-gate 				break;
41937c478bd9Sstevel@tonic-gate 			}
41947c478bd9Sstevel@tonic-gate 
41957c478bd9Sstevel@tonic-gate 		  case 'E':		/* specify error recipient */
41967c478bd9Sstevel@tonic-gate 			/* no longer used */
41977c478bd9Sstevel@tonic-gate 			break;
41987c478bd9Sstevel@tonic-gate 
41997c478bd9Sstevel@tonic-gate 		  case 'F':		/* flag bits */
42007c478bd9Sstevel@tonic-gate 			if (strncmp(bp, "From ", 5) == 0)
42017c478bd9Sstevel@tonic-gate 			{
42027c478bd9Sstevel@tonic-gate 				/* we are being spoofed! */
42037c478bd9Sstevel@tonic-gate 				syserr("SECURITY ALERT: bogus qf line %s", bp);
42047c478bd9Sstevel@tonic-gate 				err = "bogus queue line";
42057c478bd9Sstevel@tonic-gate 				goto fail;
42067c478bd9Sstevel@tonic-gate 			}
42077c478bd9Sstevel@tonic-gate 			for (p = &bp[1]; *p != '\0'; p++)
42087c478bd9Sstevel@tonic-gate 			{
42097c478bd9Sstevel@tonic-gate 				switch (*p)
42107c478bd9Sstevel@tonic-gate 				{
42117c478bd9Sstevel@tonic-gate 				  case '8':	/* has 8 bit data */
42127c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_HAS8BIT;
42137c478bd9Sstevel@tonic-gate 					break;
42147c478bd9Sstevel@tonic-gate 
42157c478bd9Sstevel@tonic-gate 				  case 'b':	/* delete Bcc: header */
42167c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_DELETE_BCC;
42177c478bd9Sstevel@tonic-gate 					break;
42187c478bd9Sstevel@tonic-gate 
42197c478bd9Sstevel@tonic-gate 				  case 'd':	/* envelope has DSN RET= */
42207c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_RET_PARAM;
42217c478bd9Sstevel@tonic-gate 					break;
42227c478bd9Sstevel@tonic-gate 
42237c478bd9Sstevel@tonic-gate 				  case 'n':	/* don't return body */
42247c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_NO_BODY_RETN;
42257c478bd9Sstevel@tonic-gate 					break;
42267c478bd9Sstevel@tonic-gate 
42277c478bd9Sstevel@tonic-gate 				  case 'r':	/* response */
42287c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_RESPONSE;
42297c478bd9Sstevel@tonic-gate 					break;
42307c478bd9Sstevel@tonic-gate 
42317c478bd9Sstevel@tonic-gate 				  case 's':	/* split */
42327c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_SPLIT;
42337c478bd9Sstevel@tonic-gate 					break;
42347c478bd9Sstevel@tonic-gate 
42357c478bd9Sstevel@tonic-gate 				  case 'w':	/* warning sent */
42367c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_WARNING;
42377c478bd9Sstevel@tonic-gate 					break;
42387c478bd9Sstevel@tonic-gate 				}
42397c478bd9Sstevel@tonic-gate 			}
42407c478bd9Sstevel@tonic-gate 			break;
42417c478bd9Sstevel@tonic-gate 
42427c478bd9Sstevel@tonic-gate 		  case 'q':		/* quarantine reason */
42437c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
42447c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
42457c478bd9Sstevel@tonic-gate 				  macid("{quarantine}"), e->e_quarmsg);
42467c478bd9Sstevel@tonic-gate 			break;
42477c478bd9Sstevel@tonic-gate 
42487c478bd9Sstevel@tonic-gate 		  case 'H':		/* header */
42497c478bd9Sstevel@tonic-gate 
42507c478bd9Sstevel@tonic-gate 			/*
42517c478bd9Sstevel@tonic-gate 			**  count size before chompheader() destroys the line.
42527c478bd9Sstevel@tonic-gate 			**  this isn't accurate due to macro expansion, but
42537c478bd9Sstevel@tonic-gate 			**  better than before. "-3" to skip H?? at least.
42547c478bd9Sstevel@tonic-gate 			*/
42557c478bd9Sstevel@tonic-gate 
42567c478bd9Sstevel@tonic-gate 			hdrsize += strlen(bp) - 3;
42577c478bd9Sstevel@tonic-gate 			(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
42587c478bd9Sstevel@tonic-gate 			break;
42597c478bd9Sstevel@tonic-gate 
42607c478bd9Sstevel@tonic-gate 		  case 'I':		/* data file's inode number */
42617c478bd9Sstevel@tonic-gate 			/* regenerated below */
42627c478bd9Sstevel@tonic-gate 			break;
42637c478bd9Sstevel@tonic-gate 
42647c478bd9Sstevel@tonic-gate 		  case 'K':		/* time of last delivery attempt */
42657c478bd9Sstevel@tonic-gate 			e->e_dtime = atol(&buf[1]);
42667c478bd9Sstevel@tonic-gate 			break;
42677c478bd9Sstevel@tonic-gate 
42687c478bd9Sstevel@tonic-gate 		  case 'L':		/* Solaris Content-Length: */
42697c478bd9Sstevel@tonic-gate 		  case 'M':		/* message */
42707c478bd9Sstevel@tonic-gate 			/* ignore this; we want a new message next time */
42717c478bd9Sstevel@tonic-gate 			break;
42727c478bd9Sstevel@tonic-gate 
42737c478bd9Sstevel@tonic-gate 		  case 'N':		/* number of delivery attempts */
42747c478bd9Sstevel@tonic-gate 			e->e_ntries = atoi(&buf[1]);
42757c478bd9Sstevel@tonic-gate 
42767c478bd9Sstevel@tonic-gate 			/* if this has been tried recently, let it be */
42777c478bd9Sstevel@tonic-gate 			now = curtime();
42787c478bd9Sstevel@tonic-gate 			if (e->e_ntries > 0 && e->e_dtime <= now &&
42797c478bd9Sstevel@tonic-gate 			    now < e->e_dtime + MinQueueAge)
42807c478bd9Sstevel@tonic-gate 			{
42817c478bd9Sstevel@tonic-gate 				char *howlong;
42827c478bd9Sstevel@tonic-gate 
42837c478bd9Sstevel@tonic-gate 				howlong = pintvl(now - e->e_dtime, true);
42847c478bd9Sstevel@tonic-gate 				if (Verbose)
42857c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
42867c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
42877c478bd9Sstevel@tonic-gate 							     "%s: too young (%s)\n",
42887c478bd9Sstevel@tonic-gate 							     e->e_id, howlong);
42897c478bd9Sstevel@tonic-gate 				if (tTd(40, 8))
42907c478bd9Sstevel@tonic-gate 					sm_dprintf("%s: too young (%s)\n",
42917c478bd9Sstevel@tonic-gate 						e->e_id, howlong);
42927c478bd9Sstevel@tonic-gate 				if (LogLevel > 19)
42937c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_DEBUG, e->e_id,
42947c478bd9Sstevel@tonic-gate 						  "too young (%s)",
42957c478bd9Sstevel@tonic-gate 						  howlong);
42967c478bd9Sstevel@tonic-gate 				e->e_id = NULL;
42977c478bd9Sstevel@tonic-gate 				unlockqueue(e);
42987c478bd9Sstevel@tonic-gate 				return false;
42997c478bd9Sstevel@tonic-gate 			}
43007c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP,
43017c478bd9Sstevel@tonic-gate 				macid("{ntries}"), &buf[1]);
43027c478bd9Sstevel@tonic-gate 
43037c478bd9Sstevel@tonic-gate #if NAMED_BIND
43047c478bd9Sstevel@tonic-gate 			/* adjust BIND parameters immediately */
43057c478bd9Sstevel@tonic-gate 			if (e->e_ntries == 0)
43067c478bd9Sstevel@tonic-gate 			{
43077c478bd9Sstevel@tonic-gate 				_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
43087c478bd9Sstevel@tonic-gate 				_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
43097c478bd9Sstevel@tonic-gate 			}
43107c478bd9Sstevel@tonic-gate 			else
43117c478bd9Sstevel@tonic-gate 			{
43127c478bd9Sstevel@tonic-gate 				_res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
43137c478bd9Sstevel@tonic-gate 				_res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
43147c478bd9Sstevel@tonic-gate 			}
43157c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
43167c478bd9Sstevel@tonic-gate 			break;
43177c478bd9Sstevel@tonic-gate 
43187c478bd9Sstevel@tonic-gate 		  case 'P':		/* message priority */
43197c478bd9Sstevel@tonic-gate 			e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
43207c478bd9Sstevel@tonic-gate 			break;
43217c478bd9Sstevel@tonic-gate 
43227c478bd9Sstevel@tonic-gate 		  case 'Q':		/* original recipient */
43237c478bd9Sstevel@tonic-gate 			orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
43247c478bd9Sstevel@tonic-gate 			break;
43257c478bd9Sstevel@tonic-gate 
43267c478bd9Sstevel@tonic-gate 		  case 'r':		/* final recipient */
43277c478bd9Sstevel@tonic-gate 			frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
43287c478bd9Sstevel@tonic-gate 			break;
43297c478bd9Sstevel@tonic-gate 
43307c478bd9Sstevel@tonic-gate 		  case 'R':		/* specify recipient */
43317c478bd9Sstevel@tonic-gate 			p = bp;
43327c478bd9Sstevel@tonic-gate 			qflags = 0;
43337c478bd9Sstevel@tonic-gate 			if (qfver >= 1)
43347c478bd9Sstevel@tonic-gate 			{
43357c478bd9Sstevel@tonic-gate 				/* get flag bits */
43367c478bd9Sstevel@tonic-gate 				while (*++p != '\0' && *p != ':')
43377c478bd9Sstevel@tonic-gate 				{
43387c478bd9Sstevel@tonic-gate 					switch (*p)
43397c478bd9Sstevel@tonic-gate 					{
43407c478bd9Sstevel@tonic-gate 					  case 'N':
43417c478bd9Sstevel@tonic-gate 						qflags |= QHASNOTIFY;
43427c478bd9Sstevel@tonic-gate 						break;
43437c478bd9Sstevel@tonic-gate 
43447c478bd9Sstevel@tonic-gate 					  case 'S':
43457c478bd9Sstevel@tonic-gate 						qflags |= QPINGONSUCCESS;
43467c478bd9Sstevel@tonic-gate 						break;
43477c478bd9Sstevel@tonic-gate 
43487c478bd9Sstevel@tonic-gate 					  case 'F':
43497c478bd9Sstevel@tonic-gate 						qflags |= QPINGONFAILURE;
43507c478bd9Sstevel@tonic-gate 						break;
43517c478bd9Sstevel@tonic-gate 
43527c478bd9Sstevel@tonic-gate 					  case 'D':
43537c478bd9Sstevel@tonic-gate 						qflags |= QPINGONDELAY;
43547c478bd9Sstevel@tonic-gate 						break;
43557c478bd9Sstevel@tonic-gate 
43567c478bd9Sstevel@tonic-gate 					  case 'P':
43577c478bd9Sstevel@tonic-gate 						qflags |= QPRIMARY;
43587c478bd9Sstevel@tonic-gate 						break;
43597c478bd9Sstevel@tonic-gate 
43607c478bd9Sstevel@tonic-gate 					  case 'A':
43617c478bd9Sstevel@tonic-gate 						if (ctladdr != NULL)
43627c478bd9Sstevel@tonic-gate 							ctladdr->q_flags |= QALIAS;
43637c478bd9Sstevel@tonic-gate 						break;
43647c478bd9Sstevel@tonic-gate 
43657c478bd9Sstevel@tonic-gate 					  default: /* ignore or complain? */
43667c478bd9Sstevel@tonic-gate 						break;
43677c478bd9Sstevel@tonic-gate 					}
43687c478bd9Sstevel@tonic-gate 				}
43697c478bd9Sstevel@tonic-gate 			}
43707c478bd9Sstevel@tonic-gate 			else
43717c478bd9Sstevel@tonic-gate 				qflags |= QPRIMARY;
43727c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
43737c478bd9Sstevel@tonic-gate 				"e r");
43747c478bd9Sstevel@tonic-gate 			if (*p != '\0')
43757c478bd9Sstevel@tonic-gate 				q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0',
43767c478bd9Sstevel@tonic-gate 						NULL, e, true);
43777c478bd9Sstevel@tonic-gate 			else
43787c478bd9Sstevel@tonic-gate 				q = NULL;
43797c478bd9Sstevel@tonic-gate 			if (q != NULL)
43807c478bd9Sstevel@tonic-gate 			{
43817c478bd9Sstevel@tonic-gate 				/* make sure we keep the current qgrp */
43827c478bd9Sstevel@tonic-gate 				if (ISVALIDQGRP(e->e_qgrp))
43837c478bd9Sstevel@tonic-gate 					q->q_qgrp = e->e_qgrp;
43847c478bd9Sstevel@tonic-gate 				q->q_alias = ctladdr;
43857c478bd9Sstevel@tonic-gate 				if (qfver >= 1)
43867c478bd9Sstevel@tonic-gate 					q->q_flags &= ~Q_PINGFLAGS;
43877c478bd9Sstevel@tonic-gate 				q->q_flags |= qflags;
43887c478bd9Sstevel@tonic-gate 				q->q_finalrcpt = frcpt;
43897c478bd9Sstevel@tonic-gate 				q->q_orcpt = orcpt;
43907c478bd9Sstevel@tonic-gate 				(void) recipient(q, &e->e_sendqueue, 0, e);
43917c478bd9Sstevel@tonic-gate 			}
43927c478bd9Sstevel@tonic-gate 			frcpt = NULL;
43937c478bd9Sstevel@tonic-gate 			orcpt = NULL;
43947c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
43957c478bd9Sstevel@tonic-gate 				NULL);
43967c478bd9Sstevel@tonic-gate 			break;
43977c478bd9Sstevel@tonic-gate 
43987c478bd9Sstevel@tonic-gate 		  case 'S':		/* sender */
43997c478bd9Sstevel@tonic-gate 			setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
44007c478bd9Sstevel@tonic-gate 				  e, NULL, '\0', true);
44017c478bd9Sstevel@tonic-gate 			break;
44027c478bd9Sstevel@tonic-gate 
44037c478bd9Sstevel@tonic-gate 		  case 'T':		/* init time */
44047c478bd9Sstevel@tonic-gate 			e->e_ctime = atol(&bp[1]);
44057c478bd9Sstevel@tonic-gate 			break;
44067c478bd9Sstevel@tonic-gate 
44077c478bd9Sstevel@tonic-gate 		  case 'V':		/* queue file version number */
44087c478bd9Sstevel@tonic-gate 			qfver = atoi(&bp[1]);
44097c478bd9Sstevel@tonic-gate 			if (qfver <= QF_VERSION)
44107c478bd9Sstevel@tonic-gate 				break;
44117c478bd9Sstevel@tonic-gate 			syserr("Version number in queue file (%d) greater than max (%d)",
44127c478bd9Sstevel@tonic-gate 				qfver, QF_VERSION);
44137c478bd9Sstevel@tonic-gate 			err = "unsupported queue file version";
44147c478bd9Sstevel@tonic-gate 			goto fail;
44157c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
44167c478bd9Sstevel@tonic-gate 			break;
44177c478bd9Sstevel@tonic-gate 
44187c478bd9Sstevel@tonic-gate 		  case 'Z':		/* original envelope id from ESMTP */
44197c478bd9Sstevel@tonic-gate 			e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
44207c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
44217c478bd9Sstevel@tonic-gate 				macid("{dsn_envid}"), e->e_envid);
44227c478bd9Sstevel@tonic-gate 			break;
44237c478bd9Sstevel@tonic-gate 
44247c478bd9Sstevel@tonic-gate 		  case '!':		/* deliver by */
44257c478bd9Sstevel@tonic-gate 
44267c478bd9Sstevel@tonic-gate 			/* format: flag (1 char) space long-integer */
44277c478bd9Sstevel@tonic-gate 			e->e_dlvr_flag = buf[1];
44287c478bd9Sstevel@tonic-gate 			e->e_deliver_by = strtol(&buf[3], NULL, 10);
44297c478bd9Sstevel@tonic-gate 
44307c478bd9Sstevel@tonic-gate 		  case '$':		/* define macro */
44317c478bd9Sstevel@tonic-gate 			{
44327c478bd9Sstevel@tonic-gate 				char *p;
44337c478bd9Sstevel@tonic-gate 
44347c478bd9Sstevel@tonic-gate 				/* XXX elimate p? */
44357c478bd9Sstevel@tonic-gate 				r = macid_parse(&bp[1], &ep);
44367c478bd9Sstevel@tonic-gate 				if (r == 0)
44377c478bd9Sstevel@tonic-gate 					break;
44387c478bd9Sstevel@tonic-gate 				p = sm_rpool_strdup_x(e->e_rpool, ep);
44397c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM, r, p);
44407c478bd9Sstevel@tonic-gate 			}
44417c478bd9Sstevel@tonic-gate 			break;
44427c478bd9Sstevel@tonic-gate 
44437c478bd9Sstevel@tonic-gate 		  case '.':		/* terminate file */
44447c478bd9Sstevel@tonic-gate 			nomore = true;
44457c478bd9Sstevel@tonic-gate 			break;
44467c478bd9Sstevel@tonic-gate 
44477c478bd9Sstevel@tonic-gate #if _FFR_QUEUEDELAY
44487c478bd9Sstevel@tonic-gate 		  case 'G':
44497c478bd9Sstevel@tonic-gate 		  case 'Y':
44507c478bd9Sstevel@tonic-gate 
44517c478bd9Sstevel@tonic-gate 			/*
44527c478bd9Sstevel@tonic-gate 			**  Maintain backward compatibility for
44537c478bd9Sstevel@tonic-gate 			**  users who defined _FFR_QUEUEDELAY in
44547c478bd9Sstevel@tonic-gate 			**  previous releases.  Remove this
44557c478bd9Sstevel@tonic-gate 			**  code in 8.14 or 8.15.
44567c478bd9Sstevel@tonic-gate 			*/
44577c478bd9Sstevel@tonic-gate 
44587c478bd9Sstevel@tonic-gate 			if (qfver == 5 || qfver == 7)
44597c478bd9Sstevel@tonic-gate 				break;
44607c478bd9Sstevel@tonic-gate 
44617c478bd9Sstevel@tonic-gate 			/* If not qfver 5 or 7, then 'G' or 'Y' is invalid */
44627c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
44637c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUEDELAY */
44647c478bd9Sstevel@tonic-gate 
44657c478bd9Sstevel@tonic-gate 		  default:
44667c478bd9Sstevel@tonic-gate 			syserr("readqf: %s: line %d: bad line \"%s\"",
44677c478bd9Sstevel@tonic-gate 				qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
44687c478bd9Sstevel@tonic-gate 			err = "unrecognized line";
44697c478bd9Sstevel@tonic-gate 			goto fail;
44707c478bd9Sstevel@tonic-gate 		}
44717c478bd9Sstevel@tonic-gate 
44727c478bd9Sstevel@tonic-gate 		if (bp != buf)
44737c478bd9Sstevel@tonic-gate 			sm_free(bp); /* XXX */
44747c478bd9Sstevel@tonic-gate 	}
44757c478bd9Sstevel@tonic-gate 
44767c478bd9Sstevel@tonic-gate 	/*
44777c478bd9Sstevel@tonic-gate 	**  If we haven't read any lines, this queue file is empty.
44787c478bd9Sstevel@tonic-gate 	**  Arrange to remove it without referencing any null pointers.
44797c478bd9Sstevel@tonic-gate 	*/
44807c478bd9Sstevel@tonic-gate 
44817c478bd9Sstevel@tonic-gate 	if (LineNumber == 0)
44827c478bd9Sstevel@tonic-gate 	{
44837c478bd9Sstevel@tonic-gate 		errno = 0;
44847c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
44857c478bd9Sstevel@tonic-gate 		return true;
44867c478bd9Sstevel@tonic-gate 	}
44877c478bd9Sstevel@tonic-gate 
44887c478bd9Sstevel@tonic-gate 	/* Check to make sure we have a complete queue file read */
44897c478bd9Sstevel@tonic-gate 	if (!nomore)
44907c478bd9Sstevel@tonic-gate 	{
44917c478bd9Sstevel@tonic-gate 		syserr("readqf: %s: incomplete queue file read", qf);
44927c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
44937c478bd9Sstevel@tonic-gate 		return false;
44947c478bd9Sstevel@tonic-gate 	}
44957c478bd9Sstevel@tonic-gate 
44967c478bd9Sstevel@tonic-gate 	/* possibly set ${dsn_ret} macro */
44977c478bd9Sstevel@tonic-gate 	if (bitset(EF_RET_PARAM, e->e_flags))
44987c478bd9Sstevel@tonic-gate 	{
44997c478bd9Sstevel@tonic-gate 		if (bitset(EF_NO_BODY_RETN, e->e_flags))
45007c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
45017c478bd9Sstevel@tonic-gate 				macid("{dsn_ret}"), "hdrs");
45027c478bd9Sstevel@tonic-gate 		else
45037c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
45047c478bd9Sstevel@tonic-gate 				macid("{dsn_ret}"), "full");
45057c478bd9Sstevel@tonic-gate 	}
45067c478bd9Sstevel@tonic-gate 
45077c478bd9Sstevel@tonic-gate 	/*
45087c478bd9Sstevel@tonic-gate 	**  Arrange to read the data file.
45097c478bd9Sstevel@tonic-gate 	*/
45107c478bd9Sstevel@tonic-gate 
45117c478bd9Sstevel@tonic-gate 	p = queuename(e, DATAFL_LETTER);
45127c478bd9Sstevel@tonic-gate 	e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY_B,
45137c478bd9Sstevel@tonic-gate 			      NULL);
45147c478bd9Sstevel@tonic-gate 	if (e->e_dfp == NULL)
45157c478bd9Sstevel@tonic-gate 	{
45167c478bd9Sstevel@tonic-gate 		syserr("readqf: cannot open %s", p);
45177c478bd9Sstevel@tonic-gate 	}
45187c478bd9Sstevel@tonic-gate 	else
45197c478bd9Sstevel@tonic-gate 	{
45207c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
45217c478bd9Sstevel@tonic-gate 		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
45227c478bd9Sstevel@tonic-gate 		    >= 0)
45237c478bd9Sstevel@tonic-gate 		{
45247c478bd9Sstevel@tonic-gate 			e->e_msgsize = st.st_size + hdrsize;
45257c478bd9Sstevel@tonic-gate 			e->e_dfdev = st.st_dev;
45267c478bd9Sstevel@tonic-gate 			e->e_dfino = ST_INODE(st);
45277c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(buf, sizeof buf, "%ld",
45287c478bd9Sstevel@tonic-gate 					   e->e_msgsize);
45297c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"),
45307c478bd9Sstevel@tonic-gate 				  buf);
45317c478bd9Sstevel@tonic-gate 		}
45327c478bd9Sstevel@tonic-gate 	}
45337c478bd9Sstevel@tonic-gate 
45347c478bd9Sstevel@tonic-gate 	return true;
45357c478bd9Sstevel@tonic-gate 
45367c478bd9Sstevel@tonic-gate   fail:
45377c478bd9Sstevel@tonic-gate 	/*
45387c478bd9Sstevel@tonic-gate 	**  There was some error reading the qf file (reason is in err var.)
45397c478bd9Sstevel@tonic-gate 	**  Cleanup:
45407c478bd9Sstevel@tonic-gate 	**	close file; clear e_lockfp since it is the same as qfp,
45417c478bd9Sstevel@tonic-gate 	**	hence it is invalid (as file) after qfp is closed;
45427c478bd9Sstevel@tonic-gate 	**	the qf file is on disk, so set the flag to avoid calling
45437c478bd9Sstevel@tonic-gate 	**	queueup() with bogus data.
45447c478bd9Sstevel@tonic-gate 	*/
45457c478bd9Sstevel@tonic-gate 
45467c478bd9Sstevel@tonic-gate 	if (qfp != NULL)
45477c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
45487c478bd9Sstevel@tonic-gate 	e->e_lockfp = NULL;
45497c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_INQUEUE;
45507c478bd9Sstevel@tonic-gate 	loseqfile(e, err);
45517c478bd9Sstevel@tonic-gate 	return false;
45527c478bd9Sstevel@tonic-gate }
45537c478bd9Sstevel@tonic-gate /*
45547c478bd9Sstevel@tonic-gate **  PRTSTR -- print a string, "unprintable" characters are shown as \oct
45557c478bd9Sstevel@tonic-gate **
45567c478bd9Sstevel@tonic-gate **	Parameters:
45577c478bd9Sstevel@tonic-gate **		s -- string to print
45587c478bd9Sstevel@tonic-gate **		ml -- maximum length of output
45597c478bd9Sstevel@tonic-gate **
45607c478bd9Sstevel@tonic-gate **	Returns:
45617c478bd9Sstevel@tonic-gate **		number of entries
45627c478bd9Sstevel@tonic-gate **
45637c478bd9Sstevel@tonic-gate **	Side Effects:
45647c478bd9Sstevel@tonic-gate **		Prints a string on stdout.
45657c478bd9Sstevel@tonic-gate */
45667c478bd9Sstevel@tonic-gate 
45677c478bd9Sstevel@tonic-gate static void
45687c478bd9Sstevel@tonic-gate prtstr(s, ml)
45697c478bd9Sstevel@tonic-gate 	char *s;
45707c478bd9Sstevel@tonic-gate 	int ml;
45717c478bd9Sstevel@tonic-gate {
45727c478bd9Sstevel@tonic-gate 	int c;
45737c478bd9Sstevel@tonic-gate 
45747c478bd9Sstevel@tonic-gate 	if (s == NULL)
45757c478bd9Sstevel@tonic-gate 		return;
45767c478bd9Sstevel@tonic-gate 	while (ml-- > 0 && ((c = *s++) != '\0'))
45777c478bd9Sstevel@tonic-gate 	{
45787c478bd9Sstevel@tonic-gate 		if (c == '\\')
45797c478bd9Sstevel@tonic-gate 		{
45807c478bd9Sstevel@tonic-gate 			if (ml-- > 0)
45817c478bd9Sstevel@tonic-gate 			{
45827c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
45837c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
45847c478bd9Sstevel@tonic-gate 			}
45857c478bd9Sstevel@tonic-gate 		}
45867c478bd9Sstevel@tonic-gate 		else if (isascii(c) && isprint(c))
45877c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
45887c478bd9Sstevel@tonic-gate 		else
45897c478bd9Sstevel@tonic-gate 		{
45907c478bd9Sstevel@tonic-gate 			if ((ml -= 3) > 0)
45917c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
45927c478bd9Sstevel@tonic-gate 						     "\\%03o", c & 0xFF);
45937c478bd9Sstevel@tonic-gate 		}
45947c478bd9Sstevel@tonic-gate 	}
45957c478bd9Sstevel@tonic-gate }
45967c478bd9Sstevel@tonic-gate /*
45977c478bd9Sstevel@tonic-gate **  PRINTNQE -- print out number of entries in the mail queue
45987c478bd9Sstevel@tonic-gate **
45997c478bd9Sstevel@tonic-gate **	Parameters:
46007c478bd9Sstevel@tonic-gate **		out -- output file pointer.
46017c478bd9Sstevel@tonic-gate **		prefix -- string to output in front of each line.
46027c478bd9Sstevel@tonic-gate **
46037c478bd9Sstevel@tonic-gate **	Returns:
46047c478bd9Sstevel@tonic-gate **		none.
46057c478bd9Sstevel@tonic-gate */
46067c478bd9Sstevel@tonic-gate 
46077c478bd9Sstevel@tonic-gate void
46087c478bd9Sstevel@tonic-gate printnqe(out, prefix)
46097c478bd9Sstevel@tonic-gate 	SM_FILE_T *out;
46107c478bd9Sstevel@tonic-gate 	char *prefix;
46117c478bd9Sstevel@tonic-gate {
46127c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
46137c478bd9Sstevel@tonic-gate 	int i, k = 0, nrequests = 0;
46147c478bd9Sstevel@tonic-gate 	bool unknown = false;
46157c478bd9Sstevel@tonic-gate 
46167c478bd9Sstevel@tonic-gate 	if (ShmId == SM_SHM_NO_ID)
46177c478bd9Sstevel@tonic-gate 	{
46187c478bd9Sstevel@tonic-gate 		if (prefix == NULL)
46197c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46207c478bd9Sstevel@tonic-gate 					"Data unavailable: shared memory not updated\n");
46217c478bd9Sstevel@tonic-gate 		else
46227c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46237c478bd9Sstevel@tonic-gate 					"%sNOTCONFIGURED:-1\r\n", prefix);
46247c478bd9Sstevel@tonic-gate 		return;
46257c478bd9Sstevel@tonic-gate 	}
46267c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
46277c478bd9Sstevel@tonic-gate 	{
46287c478bd9Sstevel@tonic-gate 		int j;
46297c478bd9Sstevel@tonic-gate 
46307c478bd9Sstevel@tonic-gate 		k++;
46317c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; j++)
46327c478bd9Sstevel@tonic-gate 		{
46337c478bd9Sstevel@tonic-gate 			int n;
46347c478bd9Sstevel@tonic-gate 
46357c478bd9Sstevel@tonic-gate 			if (StopRequest)
46367c478bd9Sstevel@tonic-gate 				stop_sendmail();
46377c478bd9Sstevel@tonic-gate 
46387c478bd9Sstevel@tonic-gate 			n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
46397c478bd9Sstevel@tonic-gate 			if (prefix != NULL)
46407c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46417c478bd9Sstevel@tonic-gate 					"%s%s:%d\r\n",
46427c478bd9Sstevel@tonic-gate 					prefix, qid_printqueue(i, j), n);
46437c478bd9Sstevel@tonic-gate 			else if (n < 0)
46447c478bd9Sstevel@tonic-gate 			{
46457c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46467c478bd9Sstevel@tonic-gate 					"%s: unknown number of entries\n",
46477c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j));
46487c478bd9Sstevel@tonic-gate 				unknown = true;
46497c478bd9Sstevel@tonic-gate 			}
46507c478bd9Sstevel@tonic-gate 			else if (n == 0)
46517c478bd9Sstevel@tonic-gate 			{
46527c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46537c478bd9Sstevel@tonic-gate 					"%s is empty\n",
46547c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j));
46557c478bd9Sstevel@tonic-gate 			}
46567c478bd9Sstevel@tonic-gate 			else if (n > 0)
46577c478bd9Sstevel@tonic-gate 			{
46587c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46597c478bd9Sstevel@tonic-gate 					"%s: entries=%d\n",
46607c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j), n);
46617c478bd9Sstevel@tonic-gate 				nrequests += n;
46627c478bd9Sstevel@tonic-gate 				k++;
46637c478bd9Sstevel@tonic-gate 			}
46647c478bd9Sstevel@tonic-gate 		}
46657c478bd9Sstevel@tonic-gate 	}
46667c478bd9Sstevel@tonic-gate 	if (prefix == NULL && k > 1)
46677c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46687c478bd9Sstevel@tonic-gate 				     "\t\tTotal requests: %d%s\n",
46697c478bd9Sstevel@tonic-gate 				     nrequests, unknown ? " (about)" : "");
46707c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
46717c478bd9Sstevel@tonic-gate 	if (prefix == NULL)
46727c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46737c478bd9Sstevel@tonic-gate 			     "Data unavailable without shared memory support\n");
46747c478bd9Sstevel@tonic-gate 	else
46757c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46767c478bd9Sstevel@tonic-gate 			     "%sNOTAVAILABLE:-1\r\n", prefix);
46777c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
46787c478bd9Sstevel@tonic-gate }
46797c478bd9Sstevel@tonic-gate /*
46807c478bd9Sstevel@tonic-gate **  PRINTQUEUE -- print out a representation of the mail queue
46817c478bd9Sstevel@tonic-gate **
46827c478bd9Sstevel@tonic-gate **	Parameters:
46837c478bd9Sstevel@tonic-gate **		none.
46847c478bd9Sstevel@tonic-gate **
46857c478bd9Sstevel@tonic-gate **	Returns:
46867c478bd9Sstevel@tonic-gate **		none.
46877c478bd9Sstevel@tonic-gate **
46887c478bd9Sstevel@tonic-gate **	Side Effects:
46897c478bd9Sstevel@tonic-gate **		Prints a listing of the mail queue on the standard output.
46907c478bd9Sstevel@tonic-gate */
46917c478bd9Sstevel@tonic-gate 
46927c478bd9Sstevel@tonic-gate void
46937c478bd9Sstevel@tonic-gate printqueue()
46947c478bd9Sstevel@tonic-gate {
46957c478bd9Sstevel@tonic-gate 	int i, k = 0, nrequests = 0;
46967c478bd9Sstevel@tonic-gate 
46977c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
46987c478bd9Sstevel@tonic-gate 	{
46997c478bd9Sstevel@tonic-gate 		int j;
47007c478bd9Sstevel@tonic-gate 
47017c478bd9Sstevel@tonic-gate 		k++;
47027c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; j++)
47037c478bd9Sstevel@tonic-gate 		{
47047c478bd9Sstevel@tonic-gate 			if (StopRequest)
47057c478bd9Sstevel@tonic-gate 				stop_sendmail();
47067c478bd9Sstevel@tonic-gate 			nrequests += print_single_queue(i, j);
47077c478bd9Sstevel@tonic-gate 			k++;
47087c478bd9Sstevel@tonic-gate 		}
47097c478bd9Sstevel@tonic-gate 	}
47107c478bd9Sstevel@tonic-gate 	if (k > 1)
47117c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
47127c478bd9Sstevel@tonic-gate 				     "\t\tTotal requests: %d\n",
47137c478bd9Sstevel@tonic-gate 				     nrequests);
47147c478bd9Sstevel@tonic-gate }
47157c478bd9Sstevel@tonic-gate /*
47167c478bd9Sstevel@tonic-gate **  PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
47177c478bd9Sstevel@tonic-gate **
47187c478bd9Sstevel@tonic-gate **	Parameters:
47197c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group.
47207c478bd9Sstevel@tonic-gate **		qdir -- the queue directory.
47217c478bd9Sstevel@tonic-gate **
47227c478bd9Sstevel@tonic-gate **	Returns:
47237c478bd9Sstevel@tonic-gate **		number of requests in mail queue.
47247c478bd9Sstevel@tonic-gate **
47257c478bd9Sstevel@tonic-gate **	Side Effects:
47267c478bd9Sstevel@tonic-gate **		Prints a listing of the mail queue on the standard output.
47277c478bd9Sstevel@tonic-gate */
47287c478bd9Sstevel@tonic-gate 
47297c478bd9Sstevel@tonic-gate int
47307c478bd9Sstevel@tonic-gate print_single_queue(qgrp, qdir)
47317c478bd9Sstevel@tonic-gate 	int qgrp;
47327c478bd9Sstevel@tonic-gate 	int qdir;
47337c478bd9Sstevel@tonic-gate {
47347c478bd9Sstevel@tonic-gate 	register WORK *w;
47357c478bd9Sstevel@tonic-gate 	SM_FILE_T *f;
47367c478bd9Sstevel@tonic-gate 	int nrequests;
47377c478bd9Sstevel@tonic-gate 	char qd[MAXPATHLEN];
47387c478bd9Sstevel@tonic-gate 	char qddf[MAXPATHLEN];
47397c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
47407c478bd9Sstevel@tonic-gate 
47417c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
47427c478bd9Sstevel@tonic-gate 	{
47437c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(qd, ".", sizeof qd);
47447c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(qddf, ".", sizeof qddf);
47457c478bd9Sstevel@tonic-gate 	}
47467c478bd9Sstevel@tonic-gate 	else
47477c478bd9Sstevel@tonic-gate 	{
47487c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(qd, sizeof qd, 2,
47497c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
47507c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBQF,
47517c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
47527c478bd9Sstevel@tonic-gate 					? "/qf" : ""));
47537c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(qddf, sizeof qddf, 2,
47547c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
47557c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBDF,
47567c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
47577c478bd9Sstevel@tonic-gate 					? "/df" : ""));
47587c478bd9Sstevel@tonic-gate 	}
47597c478bd9Sstevel@tonic-gate 
47607c478bd9Sstevel@tonic-gate 	/*
47617c478bd9Sstevel@tonic-gate 	**  Check for permission to print the queue
47627c478bd9Sstevel@tonic-gate 	*/
47637c478bd9Sstevel@tonic-gate 
47647c478bd9Sstevel@tonic-gate 	if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
47657c478bd9Sstevel@tonic-gate 	{
47667c478bd9Sstevel@tonic-gate 		struct stat st;
47677c478bd9Sstevel@tonic-gate #ifdef NGROUPS_MAX
47687c478bd9Sstevel@tonic-gate 		int n;
47697c478bd9Sstevel@tonic-gate 		extern GIDSET_T InitialGidSet[NGROUPS_MAX];
47707c478bd9Sstevel@tonic-gate #endif /* NGROUPS_MAX */
47717c478bd9Sstevel@tonic-gate 
47727c478bd9Sstevel@tonic-gate 		if (stat(qd, &st) < 0)
47737c478bd9Sstevel@tonic-gate 		{
47747c478bd9Sstevel@tonic-gate 			syserr("Cannot stat %s",
47757c478bd9Sstevel@tonic-gate 				qid_printqueue(qgrp, qdir));
47767c478bd9Sstevel@tonic-gate 			return 0;
47777c478bd9Sstevel@tonic-gate 		}
47787c478bd9Sstevel@tonic-gate #ifdef NGROUPS_MAX
47797c478bd9Sstevel@tonic-gate 		n = NGROUPS_MAX;
47807c478bd9Sstevel@tonic-gate 		while (--n >= 0)
47817c478bd9Sstevel@tonic-gate 		{
47827c478bd9Sstevel@tonic-gate 			if (InitialGidSet[n] == st.st_gid)
47837c478bd9Sstevel@tonic-gate 				break;
47847c478bd9Sstevel@tonic-gate 		}
47857c478bd9Sstevel@tonic-gate 		if (n < 0 && RealGid != st.st_gid)
47867c478bd9Sstevel@tonic-gate #else /* NGROUPS_MAX */
47877c478bd9Sstevel@tonic-gate 		if (RealGid != st.st_gid)
47887c478bd9Sstevel@tonic-gate #endif /* NGROUPS_MAX */
47897c478bd9Sstevel@tonic-gate 		{
47907c478bd9Sstevel@tonic-gate 			usrerr("510 You are not permitted to see the queue");
47917c478bd9Sstevel@tonic-gate 			setstat(EX_NOPERM);
47927c478bd9Sstevel@tonic-gate 			return 0;
47937c478bd9Sstevel@tonic-gate 		}
47947c478bd9Sstevel@tonic-gate 	}
47957c478bd9Sstevel@tonic-gate 
47967c478bd9Sstevel@tonic-gate 	/*
47977c478bd9Sstevel@tonic-gate 	**  Read and order the queue.
47987c478bd9Sstevel@tonic-gate 	*/
47997c478bd9Sstevel@tonic-gate 
48007c478bd9Sstevel@tonic-gate 	nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
48017c478bd9Sstevel@tonic-gate 	(void) sortq(Queue[qgrp]->qg_maxlist);
48027c478bd9Sstevel@tonic-gate 
48037c478bd9Sstevel@tonic-gate 	/*
48047c478bd9Sstevel@tonic-gate 	**  Print the work list that we have read.
48057c478bd9Sstevel@tonic-gate 	*/
48067c478bd9Sstevel@tonic-gate 
48077c478bd9Sstevel@tonic-gate 	/* first see if there is anything */
48087c478bd9Sstevel@tonic-gate 	if (nrequests <= 0)
48097c478bd9Sstevel@tonic-gate 	{
48107c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
48117c478bd9Sstevel@tonic-gate 				     qid_printqueue(qgrp, qdir));
48127c478bd9Sstevel@tonic-gate 		return 0;
48137c478bd9Sstevel@tonic-gate 	}
48147c478bd9Sstevel@tonic-gate 
48157c478bd9Sstevel@tonic-gate 	sm_getla();	/* get load average */
48167c478bd9Sstevel@tonic-gate 
48177c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
48187c478bd9Sstevel@tonic-gate 			     qid_printqueue(qgrp, qdir),
48197c478bd9Sstevel@tonic-gate 			     nrequests, nrequests == 1 ? "" : "s");
48207c478bd9Sstevel@tonic-gate 	if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
48217c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48227c478bd9Sstevel@tonic-gate 				     ", only %d printed", MaxQueueRun);
48237c478bd9Sstevel@tonic-gate 	if (Verbose)
48247c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48257c478bd9Sstevel@tonic-gate 			")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
48267c478bd9Sstevel@tonic-gate 	else
48277c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout,  SM_TIME_DEFAULT,
48287c478bd9Sstevel@tonic-gate 			")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
48297c478bd9Sstevel@tonic-gate 	for (w = WorkQ; w != NULL; w = w->w_next)
48307c478bd9Sstevel@tonic-gate 	{
48317c478bd9Sstevel@tonic-gate 		struct stat st;
48327c478bd9Sstevel@tonic-gate 		auto time_t submittime = 0;
48337c478bd9Sstevel@tonic-gate 		long dfsize;
48347c478bd9Sstevel@tonic-gate 		int flags = 0;
48357c478bd9Sstevel@tonic-gate 		int qfver;
48367c478bd9Sstevel@tonic-gate 		char quarmsg[MAXLINE];
48377c478bd9Sstevel@tonic-gate 		char statmsg[MAXLINE];
48387c478bd9Sstevel@tonic-gate 		char bodytype[MAXNAME + 1];
48397c478bd9Sstevel@tonic-gate 		char qf[MAXPATHLEN];
48407c478bd9Sstevel@tonic-gate 
48417c478bd9Sstevel@tonic-gate 		if (StopRequest)
48427c478bd9Sstevel@tonic-gate 			stop_sendmail();
48437c478bd9Sstevel@tonic-gate 
48447c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
48457c478bd9Sstevel@tonic-gate 				     w->w_name + 2);
48467c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(qf, sizeof qf, 3, qd, "/", w->w_name);
48477c478bd9Sstevel@tonic-gate 		f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
48487c478bd9Sstevel@tonic-gate 			       NULL);
48497c478bd9Sstevel@tonic-gate 		if (f == NULL)
48507c478bd9Sstevel@tonic-gate 		{
48517c478bd9Sstevel@tonic-gate 			if (errno == EPERM)
48527c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48537c478bd9Sstevel@tonic-gate 						     " (permission denied)\n");
48547c478bd9Sstevel@tonic-gate 			else if (errno == ENOENT)
48557c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48567c478bd9Sstevel@tonic-gate 						     " (job completed)\n");
48577c478bd9Sstevel@tonic-gate 			else
48587c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48597c478bd9Sstevel@tonic-gate 						     " (%s)\n",
48607c478bd9Sstevel@tonic-gate 						     sm_errstring(errno));
48617c478bd9Sstevel@tonic-gate 			errno = 0;
48627c478bd9Sstevel@tonic-gate 			continue;
48637c478bd9Sstevel@tonic-gate 		}
48647c478bd9Sstevel@tonic-gate 		w->w_name[0] = DATAFL_LETTER;
48657c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(qf, sizeof qf, 3, qddf, "/", w->w_name);
48667c478bd9Sstevel@tonic-gate 		if (stat(qf, &st) >= 0)
48677c478bd9Sstevel@tonic-gate 			dfsize = st.st_size;
48687c478bd9Sstevel@tonic-gate 		else
48697c478bd9Sstevel@tonic-gate 		{
48707c478bd9Sstevel@tonic-gate 			ENVELOPE e;
48717c478bd9Sstevel@tonic-gate 
48727c478bd9Sstevel@tonic-gate 			/*
48737c478bd9Sstevel@tonic-gate 			**  Maybe the df file can't be statted because
48747c478bd9Sstevel@tonic-gate 			**  it is in a different directory than the qf file.
48757c478bd9Sstevel@tonic-gate 			**  In order to find out, we must read the qf file.
48767c478bd9Sstevel@tonic-gate 			*/
48777c478bd9Sstevel@tonic-gate 
48787c478bd9Sstevel@tonic-gate 			newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
48797c478bd9Sstevel@tonic-gate 			e.e_id = w->w_name + 2;
48807c478bd9Sstevel@tonic-gate 			e.e_qgrp = qgrp;
48817c478bd9Sstevel@tonic-gate 			e.e_qdir = qdir;
48827c478bd9Sstevel@tonic-gate 			dfsize = -1;
48837c478bd9Sstevel@tonic-gate 			if (readqf(&e, false))
48847c478bd9Sstevel@tonic-gate 			{
48857c478bd9Sstevel@tonic-gate 				char *df = queuename(&e, DATAFL_LETTER);
48867c478bd9Sstevel@tonic-gate 				if (stat(df, &st) >= 0)
48877c478bd9Sstevel@tonic-gate 					dfsize = st.st_size;
48887c478bd9Sstevel@tonic-gate 			}
48897c478bd9Sstevel@tonic-gate 			if (e.e_lockfp != NULL)
48907c478bd9Sstevel@tonic-gate 			{
48917c478bd9Sstevel@tonic-gate 				(void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
48927c478bd9Sstevel@tonic-gate 				e.e_lockfp = NULL;
48937c478bd9Sstevel@tonic-gate 			}
48947c478bd9Sstevel@tonic-gate 			clearenvelope(&e, false, e.e_rpool);
48957c478bd9Sstevel@tonic-gate 			sm_rpool_free(e.e_rpool);
48967c478bd9Sstevel@tonic-gate 		}
48977c478bd9Sstevel@tonic-gate 		if (w->w_lock)
48987c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
48997c478bd9Sstevel@tonic-gate 		else if (QueueMode == QM_LOST)
49007c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
49017c478bd9Sstevel@tonic-gate 		else if (w->w_tooyoung)
49027c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
49037c478bd9Sstevel@tonic-gate 		else if (shouldqueue(w->w_pri, w->w_ctime))
49047c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
49057c478bd9Sstevel@tonic-gate 		else
49067c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
49077c478bd9Sstevel@tonic-gate 
49087c478bd9Sstevel@tonic-gate 		errno = 0;
49097c478bd9Sstevel@tonic-gate 
49107c478bd9Sstevel@tonic-gate 		quarmsg[0] = '\0';
49117c478bd9Sstevel@tonic-gate 		statmsg[0] = bodytype[0] = '\0';
49127c478bd9Sstevel@tonic-gate 		qfver = 0;
49137c478bd9Sstevel@tonic-gate 		while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
49147c478bd9Sstevel@tonic-gate 		{
49157c478bd9Sstevel@tonic-gate 			register int i;
49167c478bd9Sstevel@tonic-gate 			register char *p;
49177c478bd9Sstevel@tonic-gate 
49187c478bd9Sstevel@tonic-gate 			if (StopRequest)
49197c478bd9Sstevel@tonic-gate 				stop_sendmail();
49207c478bd9Sstevel@tonic-gate 
49217c478bd9Sstevel@tonic-gate 			fixcrlf(buf, true);
49227c478bd9Sstevel@tonic-gate 			switch (buf[0])
49237c478bd9Sstevel@tonic-gate 			{
49247c478bd9Sstevel@tonic-gate 			  case 'V':	/* queue file version */
49257c478bd9Sstevel@tonic-gate 				qfver = atoi(&buf[1]);
49267c478bd9Sstevel@tonic-gate 				break;
49277c478bd9Sstevel@tonic-gate 
49287c478bd9Sstevel@tonic-gate 			  case 'M':	/* error message */
49297c478bd9Sstevel@tonic-gate 				if ((i = strlen(&buf[1])) >= sizeof statmsg)
49307c478bd9Sstevel@tonic-gate 					i = sizeof statmsg - 1;
49317c478bd9Sstevel@tonic-gate 				memmove(statmsg, &buf[1], i);
49327c478bd9Sstevel@tonic-gate 				statmsg[i] = '\0';
49337c478bd9Sstevel@tonic-gate 				break;
49347c478bd9Sstevel@tonic-gate 
49357c478bd9Sstevel@tonic-gate 			  case 'q':	/* quarantine reason */
49367c478bd9Sstevel@tonic-gate 				if ((i = strlen(&buf[1])) >= sizeof quarmsg)
49377c478bd9Sstevel@tonic-gate 					i = sizeof quarmsg - 1;
49387c478bd9Sstevel@tonic-gate 				memmove(quarmsg, &buf[1], i);
49397c478bd9Sstevel@tonic-gate 				quarmsg[i] = '\0';
49407c478bd9Sstevel@tonic-gate 				break;
49417c478bd9Sstevel@tonic-gate 
49427c478bd9Sstevel@tonic-gate 			  case 'B':	/* body type */
49437c478bd9Sstevel@tonic-gate 				if ((i = strlen(&buf[1])) >= sizeof bodytype)
49447c478bd9Sstevel@tonic-gate 					i = sizeof bodytype - 1;
49457c478bd9Sstevel@tonic-gate 				memmove(bodytype, &buf[1], i);
49467c478bd9Sstevel@tonic-gate 				bodytype[i] = '\0';
49477c478bd9Sstevel@tonic-gate 				break;
49487c478bd9Sstevel@tonic-gate 
49497c478bd9Sstevel@tonic-gate 			  case 'S':	/* sender name */
49507c478bd9Sstevel@tonic-gate 				if (Verbose)
49517c478bd9Sstevel@tonic-gate 				{
49527c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
49537c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
49547c478bd9Sstevel@tonic-gate 						"%8ld %10ld%c%.12s ",
49557c478bd9Sstevel@tonic-gate 						dfsize,
49567c478bd9Sstevel@tonic-gate 						w->w_pri,
49577c478bd9Sstevel@tonic-gate 						bitset(EF_WARNING, flags)
49587c478bd9Sstevel@tonic-gate 							? '+' : ' ',
49597c478bd9Sstevel@tonic-gate 						ctime(&submittime) + 4);
49607c478bd9Sstevel@tonic-gate 					prtstr(&buf[1], 78);
49617c478bd9Sstevel@tonic-gate 				}
49627c478bd9Sstevel@tonic-gate 				else
49637c478bd9Sstevel@tonic-gate 				{
49647c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
49657c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
49667c478bd9Sstevel@tonic-gate 						"%8ld %.16s ",
49677c478bd9Sstevel@tonic-gate 						dfsize,
49687c478bd9Sstevel@tonic-gate 						ctime(&submittime));
49697c478bd9Sstevel@tonic-gate 					prtstr(&buf[1], 39);
49707c478bd9Sstevel@tonic-gate 				}
49717c478bd9Sstevel@tonic-gate 
49727c478bd9Sstevel@tonic-gate 				if (quarmsg[0] != '\0')
49737c478bd9Sstevel@tonic-gate 				{
49747c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
49757c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
49767c478bd9Sstevel@tonic-gate 							     "\n     QUARANTINE: %.*s",
49777c478bd9Sstevel@tonic-gate 							     Verbose ? 100 : 60,
49787c478bd9Sstevel@tonic-gate 							     quarmsg);
49797c478bd9Sstevel@tonic-gate 					quarmsg[0] = '\0';
49807c478bd9Sstevel@tonic-gate 				}
49817c478bd9Sstevel@tonic-gate 
49827c478bd9Sstevel@tonic-gate 				if (statmsg[0] != '\0' || bodytype[0] != '\0')
49837c478bd9Sstevel@tonic-gate 				{
49847c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
49857c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
49867c478bd9Sstevel@tonic-gate 						"\n    %10.10s",
49877c478bd9Sstevel@tonic-gate 						bodytype);
49887c478bd9Sstevel@tonic-gate 					if (statmsg[0] != '\0')
49897c478bd9Sstevel@tonic-gate 						(void) sm_io_fprintf(smioout,
49907c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
49917c478bd9Sstevel@tonic-gate 							"   (%.*s)",
49927c478bd9Sstevel@tonic-gate 							Verbose ? 100 : 60,
49937c478bd9Sstevel@tonic-gate 							statmsg);
49947c478bd9Sstevel@tonic-gate 					statmsg[0] = '\0';
49957c478bd9Sstevel@tonic-gate 				}
49967c478bd9Sstevel@tonic-gate 				break;
49977c478bd9Sstevel@tonic-gate 
49987c478bd9Sstevel@tonic-gate 			  case 'C':	/* controlling user */
49997c478bd9Sstevel@tonic-gate 				if (Verbose)
50007c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50017c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50027c478bd9Sstevel@tonic-gate 						"\n\t\t\t\t\t\t(---%.64s---)",
50037c478bd9Sstevel@tonic-gate 						&buf[1]);
50047c478bd9Sstevel@tonic-gate 				break;
50057c478bd9Sstevel@tonic-gate 
50067c478bd9Sstevel@tonic-gate 			  case 'R':	/* recipient name */
50077c478bd9Sstevel@tonic-gate 				p = &buf[1];
50087c478bd9Sstevel@tonic-gate 				if (qfver >= 1)
50097c478bd9Sstevel@tonic-gate 				{
50107c478bd9Sstevel@tonic-gate 					p = strchr(p, ':');
50117c478bd9Sstevel@tonic-gate 					if (p == NULL)
50127c478bd9Sstevel@tonic-gate 						break;
50137c478bd9Sstevel@tonic-gate 					p++;
50147c478bd9Sstevel@tonic-gate 				}
50157c478bd9Sstevel@tonic-gate 				if (Verbose)
50167c478bd9Sstevel@tonic-gate 				{
50177c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50187c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50197c478bd9Sstevel@tonic-gate 							"\n\t\t\t\t\t\t");
50207c478bd9Sstevel@tonic-gate 					prtstr(p, 71);
50217c478bd9Sstevel@tonic-gate 				}
50227c478bd9Sstevel@tonic-gate 				else
50237c478bd9Sstevel@tonic-gate 				{
50247c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50257c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50267c478bd9Sstevel@tonic-gate 							"\n\t\t\t\t\t ");
50277c478bd9Sstevel@tonic-gate 					prtstr(p, 38);
50287c478bd9Sstevel@tonic-gate 				}
50297c478bd9Sstevel@tonic-gate 				if (Verbose && statmsg[0] != '\0')
50307c478bd9Sstevel@tonic-gate 				{
50317c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50327c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50337c478bd9Sstevel@tonic-gate 							"\n\t\t (%.100s)",
50347c478bd9Sstevel@tonic-gate 							statmsg);
50357c478bd9Sstevel@tonic-gate 					statmsg[0] = '\0';
50367c478bd9Sstevel@tonic-gate 				}
50377c478bd9Sstevel@tonic-gate 				break;
50387c478bd9Sstevel@tonic-gate 
50397c478bd9Sstevel@tonic-gate 			  case 'T':	/* creation time */
50407c478bd9Sstevel@tonic-gate 				submittime = atol(&buf[1]);
50417c478bd9Sstevel@tonic-gate 				break;
50427c478bd9Sstevel@tonic-gate 
50437c478bd9Sstevel@tonic-gate 			  case 'F':	/* flag bits */
50447c478bd9Sstevel@tonic-gate 				for (p = &buf[1]; *p != '\0'; p++)
50457c478bd9Sstevel@tonic-gate 				{
50467c478bd9Sstevel@tonic-gate 					switch (*p)
50477c478bd9Sstevel@tonic-gate 					{
50487c478bd9Sstevel@tonic-gate 					  case 'w':
50497c478bd9Sstevel@tonic-gate 						flags |= EF_WARNING;
50507c478bd9Sstevel@tonic-gate 						break;
50517c478bd9Sstevel@tonic-gate 					}
50527c478bd9Sstevel@tonic-gate 				}
50537c478bd9Sstevel@tonic-gate 			}
50547c478bd9Sstevel@tonic-gate 		}
50557c478bd9Sstevel@tonic-gate 		if (submittime == (time_t) 0)
50567c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
50577c478bd9Sstevel@tonic-gate 					     " (no control file)");
50587c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
50597c478bd9Sstevel@tonic-gate 		(void) sm_io_close(f, SM_TIME_DEFAULT);
50607c478bd9Sstevel@tonic-gate 	}
50617c478bd9Sstevel@tonic-gate 	return nrequests;
50627c478bd9Sstevel@tonic-gate }
50637c478bd9Sstevel@tonic-gate 
50647c478bd9Sstevel@tonic-gate /*
50657c478bd9Sstevel@tonic-gate **  QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
50667c478bd9Sstevel@tonic-gate **
50677c478bd9Sstevel@tonic-gate **	Parameters:
50687c478bd9Sstevel@tonic-gate **		e -- envelope to build it in/from.
50697c478bd9Sstevel@tonic-gate **		type -- the file type, used as the first character
50707c478bd9Sstevel@tonic-gate **			of the file name.
50717c478bd9Sstevel@tonic-gate **
50727c478bd9Sstevel@tonic-gate **	Returns:
50737c478bd9Sstevel@tonic-gate **		the letter to use
50747c478bd9Sstevel@tonic-gate */
50757c478bd9Sstevel@tonic-gate 
50767c478bd9Sstevel@tonic-gate static char
50777c478bd9Sstevel@tonic-gate queue_letter(e, type)
50787c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
50797c478bd9Sstevel@tonic-gate 	int type;
50807c478bd9Sstevel@tonic-gate {
50817c478bd9Sstevel@tonic-gate 	/* Change type according to QueueMode */
50827c478bd9Sstevel@tonic-gate 	if (type == ANYQFL_LETTER)
50837c478bd9Sstevel@tonic-gate 	{
50847c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg != NULL)
50857c478bd9Sstevel@tonic-gate 			type = QUARQF_LETTER;
50867c478bd9Sstevel@tonic-gate 		else
50877c478bd9Sstevel@tonic-gate 		{
50887c478bd9Sstevel@tonic-gate 			switch (QueueMode)
50897c478bd9Sstevel@tonic-gate 			{
50907c478bd9Sstevel@tonic-gate 			  case QM_NORMAL:
50917c478bd9Sstevel@tonic-gate 				type = NORMQF_LETTER;
50927c478bd9Sstevel@tonic-gate 				break;
50937c478bd9Sstevel@tonic-gate 
50947c478bd9Sstevel@tonic-gate 			  case QM_QUARANTINE:
50957c478bd9Sstevel@tonic-gate 				type = QUARQF_LETTER;
50967c478bd9Sstevel@tonic-gate 				break;
50977c478bd9Sstevel@tonic-gate 
50987c478bd9Sstevel@tonic-gate 			  case QM_LOST:
50997c478bd9Sstevel@tonic-gate 				type = LOSEQF_LETTER;
51007c478bd9Sstevel@tonic-gate 				break;
51017c478bd9Sstevel@tonic-gate 
51027c478bd9Sstevel@tonic-gate 			  default:
51037c478bd9Sstevel@tonic-gate 				/* should never happen */
51047c478bd9Sstevel@tonic-gate 				abort();
51057c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
51067c478bd9Sstevel@tonic-gate 			}
51077c478bd9Sstevel@tonic-gate 		}
51087c478bd9Sstevel@tonic-gate 	}
51097c478bd9Sstevel@tonic-gate 	return type;
51107c478bd9Sstevel@tonic-gate }
51117c478bd9Sstevel@tonic-gate 
51127c478bd9Sstevel@tonic-gate /*
51137c478bd9Sstevel@tonic-gate **  QUEUENAME -- build a file name in the queue directory for this envelope.
51147c478bd9Sstevel@tonic-gate **
51157c478bd9Sstevel@tonic-gate **	Parameters:
51167c478bd9Sstevel@tonic-gate **		e -- envelope to build it in/from.
51177c478bd9Sstevel@tonic-gate **		type -- the file type, used as the first character
51187c478bd9Sstevel@tonic-gate **			of the file name.
51197c478bd9Sstevel@tonic-gate **
51207c478bd9Sstevel@tonic-gate **	Returns:
51217c478bd9Sstevel@tonic-gate **		a pointer to the queue name (in a static buffer).
51227c478bd9Sstevel@tonic-gate **
51237c478bd9Sstevel@tonic-gate **	Side Effects:
51247c478bd9Sstevel@tonic-gate **		If no id code is already assigned, queuename() will
51257c478bd9Sstevel@tonic-gate **		assign an id code with assign_queueid().  If no queue
51267c478bd9Sstevel@tonic-gate **		directory is assigned, one will be set with setnewqueue().
51277c478bd9Sstevel@tonic-gate */
51287c478bd9Sstevel@tonic-gate 
51297c478bd9Sstevel@tonic-gate char *
51307c478bd9Sstevel@tonic-gate queuename(e, type)
51317c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
51327c478bd9Sstevel@tonic-gate 	int type;
51337c478bd9Sstevel@tonic-gate {
51347c478bd9Sstevel@tonic-gate 	int qd, qg;
51357c478bd9Sstevel@tonic-gate 	char *sub = "/";
51367c478bd9Sstevel@tonic-gate 	char pref[3];
51377c478bd9Sstevel@tonic-gate 	static char buf[MAXPATHLEN];
51387c478bd9Sstevel@tonic-gate 
51397c478bd9Sstevel@tonic-gate 	/* Assign an ID if needed */
51407c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
51417c478bd9Sstevel@tonic-gate 		assign_queueid(e);
51427c478bd9Sstevel@tonic-gate 	type = queue_letter(e, type);
51437c478bd9Sstevel@tonic-gate 
51447c478bd9Sstevel@tonic-gate 	/* begin of filename */
51457c478bd9Sstevel@tonic-gate 	pref[0] = (char) type;
51467c478bd9Sstevel@tonic-gate 	pref[1] = 'f';
51477c478bd9Sstevel@tonic-gate 	pref[2] = '\0';
51487c478bd9Sstevel@tonic-gate 
51497c478bd9Sstevel@tonic-gate 	/* Assign a queue group/directory if needed */
51507c478bd9Sstevel@tonic-gate 	if (type == XSCRPT_LETTER)
51517c478bd9Sstevel@tonic-gate 	{
51527c478bd9Sstevel@tonic-gate 		/*
51537c478bd9Sstevel@tonic-gate 		**  We don't want to call setnewqueue() if we are fetching
51547c478bd9Sstevel@tonic-gate 		**  the pathname of the transcript file, because setnewqueue
51557c478bd9Sstevel@tonic-gate 		**  chooses a queue, and sometimes we need to write to the
51567c478bd9Sstevel@tonic-gate 		**  transcript file before we have gathered enough information
51577c478bd9Sstevel@tonic-gate 		**  to choose a queue.
51587c478bd9Sstevel@tonic-gate 		*/
51597c478bd9Sstevel@tonic-gate 
51607c478bd9Sstevel@tonic-gate 		if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
51617c478bd9Sstevel@tonic-gate 		{
51627c478bd9Sstevel@tonic-gate 			if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
51637c478bd9Sstevel@tonic-gate 			{
51647c478bd9Sstevel@tonic-gate 				e->e_xfqgrp = e->e_qgrp;
51657c478bd9Sstevel@tonic-gate 				e->e_xfqdir = e->e_qdir;
51667c478bd9Sstevel@tonic-gate 			}
51677c478bd9Sstevel@tonic-gate 			else
51687c478bd9Sstevel@tonic-gate 			{
51697c478bd9Sstevel@tonic-gate 				e->e_xfqgrp = 0;
51707c478bd9Sstevel@tonic-gate 				if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
51717c478bd9Sstevel@tonic-gate 					e->e_xfqdir = 0;
51727c478bd9Sstevel@tonic-gate 				else
51737c478bd9Sstevel@tonic-gate 				{
51747c478bd9Sstevel@tonic-gate 					e->e_xfqdir = get_rand_mod(
51757c478bd9Sstevel@tonic-gate 					      Queue[e->e_xfqgrp]->qg_numqueues);
51767c478bd9Sstevel@tonic-gate 				}
51777c478bd9Sstevel@tonic-gate 			}
51787c478bd9Sstevel@tonic-gate 		}
51797c478bd9Sstevel@tonic-gate 		qd = e->e_xfqdir;
51807c478bd9Sstevel@tonic-gate 		qg = e->e_xfqgrp;
51817c478bd9Sstevel@tonic-gate 	}
51827c478bd9Sstevel@tonic-gate 	else
51837c478bd9Sstevel@tonic-gate 	{
51847c478bd9Sstevel@tonic-gate 		if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
51857c478bd9Sstevel@tonic-gate 			setnewqueue(e);
51867c478bd9Sstevel@tonic-gate 		if (type ==  DATAFL_LETTER)
51877c478bd9Sstevel@tonic-gate 		{
51887c478bd9Sstevel@tonic-gate 			qd = e->e_dfqdir;
51897c478bd9Sstevel@tonic-gate 			qg = e->e_dfqgrp;
51907c478bd9Sstevel@tonic-gate 		}
51917c478bd9Sstevel@tonic-gate 		else
51927c478bd9Sstevel@tonic-gate 		{
51937c478bd9Sstevel@tonic-gate 			qd = e->e_qdir;
51947c478bd9Sstevel@tonic-gate 			qg = e->e_qgrp;
51957c478bd9Sstevel@tonic-gate 		}
51967c478bd9Sstevel@tonic-gate 	}
51977c478bd9Sstevel@tonic-gate 
51987c478bd9Sstevel@tonic-gate 	/* xf files always have a valid qd and qg picked above */
51997c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR && type != XSCRPT_LETTER)
52007c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(buf, sizeof buf, 2, pref, e->e_id);
52017c478bd9Sstevel@tonic-gate 	else
52027c478bd9Sstevel@tonic-gate 	{
52037c478bd9Sstevel@tonic-gate 		switch (type)
52047c478bd9Sstevel@tonic-gate 		{
52057c478bd9Sstevel@tonic-gate 		  case DATAFL_LETTER:
52067c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52077c478bd9Sstevel@tonic-gate 				sub = "/df/";
52087c478bd9Sstevel@tonic-gate 			break;
52097c478bd9Sstevel@tonic-gate 
52107c478bd9Sstevel@tonic-gate 		  case QUARQF_LETTER:
52117c478bd9Sstevel@tonic-gate 		  case TEMPQF_LETTER:
52127c478bd9Sstevel@tonic-gate 		  case NEWQFL_LETTER:
52137c478bd9Sstevel@tonic-gate 		  case LOSEQF_LETTER:
52147c478bd9Sstevel@tonic-gate 		  case NORMQF_LETTER:
52157c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52167c478bd9Sstevel@tonic-gate 				sub = "/qf/";
52177c478bd9Sstevel@tonic-gate 			break;
52187c478bd9Sstevel@tonic-gate 
52197c478bd9Sstevel@tonic-gate 		  case XSCRPT_LETTER:
52207c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52217c478bd9Sstevel@tonic-gate 				sub = "/xf/";
52227c478bd9Sstevel@tonic-gate 			break;
52237c478bd9Sstevel@tonic-gate 
52247c478bd9Sstevel@tonic-gate 		  default:
52257c478bd9Sstevel@tonic-gate 			sm_abort("queuename: bad queue file type %d", type);
52267c478bd9Sstevel@tonic-gate 		}
52277c478bd9Sstevel@tonic-gate 
52287c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(buf, sizeof buf, 4,
52297c478bd9Sstevel@tonic-gate 				Queue[qg]->qg_qpaths[qd].qp_name,
52307c478bd9Sstevel@tonic-gate 				sub, pref, e->e_id);
52317c478bd9Sstevel@tonic-gate 	}
52327c478bd9Sstevel@tonic-gate 
52337c478bd9Sstevel@tonic-gate 	if (tTd(7, 2))
52347c478bd9Sstevel@tonic-gate 		sm_dprintf("queuename: %s\n", buf);
52357c478bd9Sstevel@tonic-gate 	return buf;
52367c478bd9Sstevel@tonic-gate }
52377c478bd9Sstevel@tonic-gate 
52387c478bd9Sstevel@tonic-gate /*
52397c478bd9Sstevel@tonic-gate **  INIT_QID_ALG -- Initialize the (static) parameters that are used to
52407c478bd9Sstevel@tonic-gate **	generate a queue ID.
52417c478bd9Sstevel@tonic-gate **
52427c478bd9Sstevel@tonic-gate **	This function is called by the daemon to reset
52437c478bd9Sstevel@tonic-gate **	LastQueueTime and LastQueuePid which are used by assign_queueid().
52447c478bd9Sstevel@tonic-gate **	Otherwise the algorithm may cause problems because
52457c478bd9Sstevel@tonic-gate **	LastQueueTime and LastQueuePid are set indirectly by main()
52467c478bd9Sstevel@tonic-gate **	before the daemon process is started, hence LastQueuePid is not
52477c478bd9Sstevel@tonic-gate **	the pid of the daemon and therefore a child of the daemon can
52487c478bd9Sstevel@tonic-gate **	actually have the same pid as LastQueuePid which means the section
52497c478bd9Sstevel@tonic-gate **	in  assign_queueid():
52507c478bd9Sstevel@tonic-gate **	* see if we need to get a new base time/pid *
52517c478bd9Sstevel@tonic-gate **	is NOT triggered which will cause the same queue id to be generated.
52527c478bd9Sstevel@tonic-gate **
52537c478bd9Sstevel@tonic-gate **	Parameters:
52547c478bd9Sstevel@tonic-gate **		none
52557c478bd9Sstevel@tonic-gate **
52567c478bd9Sstevel@tonic-gate **	Returns:
52577c478bd9Sstevel@tonic-gate **		none.
52587c478bd9Sstevel@tonic-gate */
52597c478bd9Sstevel@tonic-gate 
52607c478bd9Sstevel@tonic-gate void
52617c478bd9Sstevel@tonic-gate init_qid_alg()
52627c478bd9Sstevel@tonic-gate {
52637c478bd9Sstevel@tonic-gate 	LastQueueTime = 0;
52647c478bd9Sstevel@tonic-gate 	LastQueuePid = -1;
52657c478bd9Sstevel@tonic-gate }
52667c478bd9Sstevel@tonic-gate 
52677c478bd9Sstevel@tonic-gate /*
52687c478bd9Sstevel@tonic-gate **  ASSIGN_QUEUEID -- assign a queue ID for this envelope.
52697c478bd9Sstevel@tonic-gate **
52707c478bd9Sstevel@tonic-gate **	Assigns an id code if one does not already exist.
52717c478bd9Sstevel@tonic-gate **	This code assumes that nothing will remain in the queue for
52727c478bd9Sstevel@tonic-gate **	longer than 60 years.  It is critical that files with the given
52737c478bd9Sstevel@tonic-gate **	name do not already exist in the queue.
52747c478bd9Sstevel@tonic-gate **	[No longer initializes e_qdir to NOQDIR.]
52757c478bd9Sstevel@tonic-gate **
52767c478bd9Sstevel@tonic-gate **	Parameters:
52777c478bd9Sstevel@tonic-gate **		e -- envelope to set it in.
52787c478bd9Sstevel@tonic-gate **
52797c478bd9Sstevel@tonic-gate **	Returns:
52807c478bd9Sstevel@tonic-gate **		none.
52817c478bd9Sstevel@tonic-gate */
52827c478bd9Sstevel@tonic-gate 
52837c478bd9Sstevel@tonic-gate static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
52847c478bd9Sstevel@tonic-gate # define QIC_LEN	60
52857c478bd9Sstevel@tonic-gate # define QIC_LEN_R	62
52867c478bd9Sstevel@tonic-gate 
52877c478bd9Sstevel@tonic-gate /*
52887c478bd9Sstevel@tonic-gate **  Note: the length is "officially" 60 because minutes and seconds are
52897c478bd9Sstevel@tonic-gate **	usually only 0-59.  However (Linux):
52907c478bd9Sstevel@tonic-gate **       tm_sec The number of seconds after the minute, normally in
52917c478bd9Sstevel@tonic-gate **		the range 0 to 59, but can be up to 61 to allow for
52927c478bd9Sstevel@tonic-gate **		leap seconds.
52937c478bd9Sstevel@tonic-gate **	Hence the real length of the string is 62 to take this into account.
52947c478bd9Sstevel@tonic-gate **	Alternatively % QIC_LEN can (should) be used for access everywhere.
52957c478bd9Sstevel@tonic-gate */
52967c478bd9Sstevel@tonic-gate 
52977c478bd9Sstevel@tonic-gate # define queuenextid() CurrentPid
52987c478bd9Sstevel@tonic-gate 
52997c478bd9Sstevel@tonic-gate 
53007c478bd9Sstevel@tonic-gate void
53017c478bd9Sstevel@tonic-gate assign_queueid(e)
53027c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
53037c478bd9Sstevel@tonic-gate {
53047c478bd9Sstevel@tonic-gate 	pid_t pid = queuenextid();
53057c478bd9Sstevel@tonic-gate 	static int cX = 0;
53067c478bd9Sstevel@tonic-gate 	static long random_offset;
53077c478bd9Sstevel@tonic-gate 	struct tm *tm;
53087c478bd9Sstevel@tonic-gate 	char idbuf[MAXQFNAME - 2];
53097c478bd9Sstevel@tonic-gate 	int seq;
53107c478bd9Sstevel@tonic-gate 
53117c478bd9Sstevel@tonic-gate 	if (e->e_id != NULL)
53127c478bd9Sstevel@tonic-gate 		return;
53137c478bd9Sstevel@tonic-gate 
53147c478bd9Sstevel@tonic-gate 	/* see if we need to get a new base time/pid */
53157c478bd9Sstevel@tonic-gate 	if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 ||
53167c478bd9Sstevel@tonic-gate 	    LastQueuePid != pid)
53177c478bd9Sstevel@tonic-gate 	{
53187c478bd9Sstevel@tonic-gate 		time_t then = LastQueueTime;
53197c478bd9Sstevel@tonic-gate 
53207c478bd9Sstevel@tonic-gate 		/* if the first time through, pick a random offset */
53217c478bd9Sstevel@tonic-gate 		if (LastQueueTime == 0)
53227c478bd9Sstevel@tonic-gate 			random_offset = get_random();
53237c478bd9Sstevel@tonic-gate 
53247c478bd9Sstevel@tonic-gate 		while ((LastQueueTime = curtime()) == then &&
53257c478bd9Sstevel@tonic-gate 		       LastQueuePid == pid)
53267c478bd9Sstevel@tonic-gate 		{
53277c478bd9Sstevel@tonic-gate 			(void) sleep(1);
53287c478bd9Sstevel@tonic-gate 		}
53297c478bd9Sstevel@tonic-gate 		LastQueuePid = queuenextid();
53307c478bd9Sstevel@tonic-gate 		cX = 0;
53317c478bd9Sstevel@tonic-gate 	}
53327c478bd9Sstevel@tonic-gate 
53337c478bd9Sstevel@tonic-gate 	/*
53347c478bd9Sstevel@tonic-gate 	**  Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1.
53357c478bd9Sstevel@tonic-gate 	**  This lets us generate up to QIC_LEN*QIC_LEN unique queue ids
53367c478bd9Sstevel@tonic-gate 	**  per second, per process.  With envelope splitting,
53377c478bd9Sstevel@tonic-gate 	**  a single message can consume many queue ids.
53387c478bd9Sstevel@tonic-gate 	*/
53397c478bd9Sstevel@tonic-gate 
53407c478bd9Sstevel@tonic-gate 	seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN));
53417c478bd9Sstevel@tonic-gate 	++cX;
53427c478bd9Sstevel@tonic-gate 	if (tTd(7, 50))
53437c478bd9Sstevel@tonic-gate 		sm_dprintf("assign_queueid: random_offset = %ld (%d)\n",
53447c478bd9Sstevel@tonic-gate 			random_offset, seq);
53457c478bd9Sstevel@tonic-gate 
53467c478bd9Sstevel@tonic-gate 	tm = gmtime(&LastQueueTime);
53477c478bd9Sstevel@tonic-gate 	idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
53487c478bd9Sstevel@tonic-gate 	idbuf[1] = QueueIdChars[tm->tm_mon];
53497c478bd9Sstevel@tonic-gate 	idbuf[2] = QueueIdChars[tm->tm_mday];
53507c478bd9Sstevel@tonic-gate 	idbuf[3] = QueueIdChars[tm->tm_hour];
53517c478bd9Sstevel@tonic-gate 	idbuf[4] = QueueIdChars[tm->tm_min % QIC_LEN_R];
53527c478bd9Sstevel@tonic-gate 	idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R];
53537c478bd9Sstevel@tonic-gate 	idbuf[6] = QueueIdChars[seq / QIC_LEN];
53547c478bd9Sstevel@tonic-gate 	idbuf[7] = QueueIdChars[seq % QIC_LEN];
53557c478bd9Sstevel@tonic-gate 	(void) sm_snprintf(&idbuf[8], sizeof idbuf - 8, "%06d",
53567c478bd9Sstevel@tonic-gate 			   (int) LastQueuePid);
53577c478bd9Sstevel@tonic-gate 	e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
53587c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
53597c478bd9Sstevel@tonic-gate #if 0
53607c478bd9Sstevel@tonic-gate 	/* XXX: inherited from MainEnvelope */
53617c478bd9Sstevel@tonic-gate 	e->e_qgrp = NOQGRP;  /* too early to do anything else */
53627c478bd9Sstevel@tonic-gate 	e->e_qdir = NOQDIR;
53637c478bd9Sstevel@tonic-gate 	e->e_xfqgrp = NOQGRP;
53647c478bd9Sstevel@tonic-gate #endif /* 0 */
53657c478bd9Sstevel@tonic-gate 
53667c478bd9Sstevel@tonic-gate 	/* New ID means it's not on disk yet */
53677c478bd9Sstevel@tonic-gate 	e->e_qfletter = '\0';
53687c478bd9Sstevel@tonic-gate 
53697c478bd9Sstevel@tonic-gate 	if (tTd(7, 1))
53707c478bd9Sstevel@tonic-gate 		sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
53717c478bd9Sstevel@tonic-gate 			e->e_id, e);
53727c478bd9Sstevel@tonic-gate 	if (LogLevel > 93)
53737c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
53747c478bd9Sstevel@tonic-gate }
53757c478bd9Sstevel@tonic-gate /*
53767c478bd9Sstevel@tonic-gate **  SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
53777c478bd9Sstevel@tonic-gate **
53787c478bd9Sstevel@tonic-gate **	Make sure one PID can't be used by two processes in any one second.
53797c478bd9Sstevel@tonic-gate **
53807c478bd9Sstevel@tonic-gate **		If the system rotates PIDs fast enough, may get the
53817c478bd9Sstevel@tonic-gate **		same pid in the same second for two distinct processes.
53827c478bd9Sstevel@tonic-gate **		This will interfere with the queue file naming system.
53837c478bd9Sstevel@tonic-gate **
53847c478bd9Sstevel@tonic-gate **	Parameters:
53857c478bd9Sstevel@tonic-gate **		none
53867c478bd9Sstevel@tonic-gate **
53877c478bd9Sstevel@tonic-gate **	Returns:
53887c478bd9Sstevel@tonic-gate **		none
53897c478bd9Sstevel@tonic-gate */
53907c478bd9Sstevel@tonic-gate 
53917c478bd9Sstevel@tonic-gate void
53927c478bd9Sstevel@tonic-gate sync_queue_time()
53937c478bd9Sstevel@tonic-gate {
53947c478bd9Sstevel@tonic-gate #if FAST_PID_RECYCLE
53957c478bd9Sstevel@tonic-gate 	if (OpMode != MD_TEST &&
53967c478bd9Sstevel@tonic-gate 	    OpMode != MD_VERIFY &&
53977c478bd9Sstevel@tonic-gate 	    LastQueueTime > 0 &&
53987c478bd9Sstevel@tonic-gate 	    LastQueuePid == CurrentPid &&
53997c478bd9Sstevel@tonic-gate 	    curtime() == LastQueueTime)
54007c478bd9Sstevel@tonic-gate 		(void) sleep(1);
54017c478bd9Sstevel@tonic-gate #endif /* FAST_PID_RECYCLE */
54027c478bd9Sstevel@tonic-gate }
54037c478bd9Sstevel@tonic-gate /*
54047c478bd9Sstevel@tonic-gate **  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
54057c478bd9Sstevel@tonic-gate **
54067c478bd9Sstevel@tonic-gate **	Parameters:
54077c478bd9Sstevel@tonic-gate **		e -- the envelope to unlock.
54087c478bd9Sstevel@tonic-gate **
54097c478bd9Sstevel@tonic-gate **	Returns:
54107c478bd9Sstevel@tonic-gate **		none
54117c478bd9Sstevel@tonic-gate **
54127c478bd9Sstevel@tonic-gate **	Side Effects:
54137c478bd9Sstevel@tonic-gate **		unlocks the queue for `e'.
54147c478bd9Sstevel@tonic-gate */
54157c478bd9Sstevel@tonic-gate 
54167c478bd9Sstevel@tonic-gate void
54177c478bd9Sstevel@tonic-gate unlockqueue(e)
54187c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
54197c478bd9Sstevel@tonic-gate {
54207c478bd9Sstevel@tonic-gate 	if (tTd(51, 4))
54217c478bd9Sstevel@tonic-gate 		sm_dprintf("unlockqueue(%s)\n",
54227c478bd9Sstevel@tonic-gate 			e->e_id == NULL ? "NOQUEUE" : e->e_id);
54237c478bd9Sstevel@tonic-gate 
54247c478bd9Sstevel@tonic-gate 
54257c478bd9Sstevel@tonic-gate 	/* if there is a lock file in the envelope, close it */
54267c478bd9Sstevel@tonic-gate 	if (e->e_lockfp != NULL)
54277c478bd9Sstevel@tonic-gate 		(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
54287c478bd9Sstevel@tonic-gate 	e->e_lockfp = NULL;
54297c478bd9Sstevel@tonic-gate 
54307c478bd9Sstevel@tonic-gate 	/* don't create a queue id if we don't already have one */
54317c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
54327c478bd9Sstevel@tonic-gate 		return;
54337c478bd9Sstevel@tonic-gate 
54347c478bd9Sstevel@tonic-gate 	/* remove the transcript */
54357c478bd9Sstevel@tonic-gate 	if (LogLevel > 87)
54367c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id, "unlock");
54377c478bd9Sstevel@tonic-gate 	if (!tTd(51, 104))
54387c478bd9Sstevel@tonic-gate 		(void) xunlink(queuename(e, XSCRPT_LETTER));
54397c478bd9Sstevel@tonic-gate }
54407c478bd9Sstevel@tonic-gate /*
54417c478bd9Sstevel@tonic-gate **  SETCTLUSER -- create a controlling address
54427c478bd9Sstevel@tonic-gate **
54437c478bd9Sstevel@tonic-gate **	Create a fake "address" given only a local login name; this is
54447c478bd9Sstevel@tonic-gate **	used as a "controlling user" for future recipient addresses.
54457c478bd9Sstevel@tonic-gate **
54467c478bd9Sstevel@tonic-gate **	Parameters:
54477c478bd9Sstevel@tonic-gate **		user -- the user name of the controlling user.
54487c478bd9Sstevel@tonic-gate **		qfver -- the version stamp of this queue file.
54497c478bd9Sstevel@tonic-gate **		e -- envelope
54507c478bd9Sstevel@tonic-gate **
54517c478bd9Sstevel@tonic-gate **	Returns:
54527c478bd9Sstevel@tonic-gate **		An address descriptor for the controlling user,
54537c478bd9Sstevel@tonic-gate **		using storage allocated from e->e_rpool.
54547c478bd9Sstevel@tonic-gate **
54557c478bd9Sstevel@tonic-gate */
54567c478bd9Sstevel@tonic-gate 
54577c478bd9Sstevel@tonic-gate static ADDRESS *
54587c478bd9Sstevel@tonic-gate setctluser(user, qfver, e)
54597c478bd9Sstevel@tonic-gate 	char *user;
54607c478bd9Sstevel@tonic-gate 	int qfver;
54617c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
54627c478bd9Sstevel@tonic-gate {
54637c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
54647c478bd9Sstevel@tonic-gate 	struct passwd *pw;
54657c478bd9Sstevel@tonic-gate 	char *p;
54667c478bd9Sstevel@tonic-gate 
54677c478bd9Sstevel@tonic-gate 	/*
54687c478bd9Sstevel@tonic-gate 	**  See if this clears our concept of controlling user.
54697c478bd9Sstevel@tonic-gate 	*/
54707c478bd9Sstevel@tonic-gate 
54717c478bd9Sstevel@tonic-gate 	if (user == NULL || *user == '\0')
54727c478bd9Sstevel@tonic-gate 		return NULL;
54737c478bd9Sstevel@tonic-gate 
54747c478bd9Sstevel@tonic-gate 	/*
54757c478bd9Sstevel@tonic-gate 	**  Set up addr fields for controlling user.
54767c478bd9Sstevel@tonic-gate 	*/
54777c478bd9Sstevel@tonic-gate 
54787c478bd9Sstevel@tonic-gate 	a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof *a);
54797c478bd9Sstevel@tonic-gate 	memset((char *) a, '\0', sizeof *a);
54807c478bd9Sstevel@tonic-gate 
54817c478bd9Sstevel@tonic-gate 	if (*user == ':')
54827c478bd9Sstevel@tonic-gate 	{
54837c478bd9Sstevel@tonic-gate 		p = &user[1];
54847c478bd9Sstevel@tonic-gate 		a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
54857c478bd9Sstevel@tonic-gate 	}
54867c478bd9Sstevel@tonic-gate 	else
54877c478bd9Sstevel@tonic-gate 	{
54887c478bd9Sstevel@tonic-gate 		p = strtok(user, ":");
54897c478bd9Sstevel@tonic-gate 		a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
54907c478bd9Sstevel@tonic-gate 		if (qfver >= 2)
54917c478bd9Sstevel@tonic-gate 		{
54927c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
54937c478bd9Sstevel@tonic-gate 				a->q_uid = atoi(p);
54947c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
54957c478bd9Sstevel@tonic-gate 				a->q_gid = atoi(p);
54967c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
54977c478bd9Sstevel@tonic-gate 			{
54987c478bd9Sstevel@tonic-gate 				char *o;
54997c478bd9Sstevel@tonic-gate 
55007c478bd9Sstevel@tonic-gate 				a->q_flags |= QGOODUID;
55017c478bd9Sstevel@tonic-gate 
55027c478bd9Sstevel@tonic-gate 				/* if there is another ':': restore it */
55037c478bd9Sstevel@tonic-gate 				if ((o = strtok(NULL, ":")) != NULL && o > p)
55047c478bd9Sstevel@tonic-gate 					o[-1] = ':';
55057c478bd9Sstevel@tonic-gate 			}
55067c478bd9Sstevel@tonic-gate 		}
55077c478bd9Sstevel@tonic-gate 		else if ((pw = sm_getpwnam(user)) != NULL)
55087c478bd9Sstevel@tonic-gate 		{
55097c478bd9Sstevel@tonic-gate 			if (*pw->pw_dir == '\0')
55107c478bd9Sstevel@tonic-gate 				a->q_home = NULL;
55117c478bd9Sstevel@tonic-gate 			else if (strcmp(pw->pw_dir, "/") == 0)
55127c478bd9Sstevel@tonic-gate 				a->q_home = "";
55137c478bd9Sstevel@tonic-gate 			else
55147c478bd9Sstevel@tonic-gate 				a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
55157c478bd9Sstevel@tonic-gate 			a->q_uid = pw->pw_uid;
55167c478bd9Sstevel@tonic-gate 			a->q_gid = pw->pw_gid;
55177c478bd9Sstevel@tonic-gate 			a->q_flags |= QGOODUID;
55187c478bd9Sstevel@tonic-gate 		}
55197c478bd9Sstevel@tonic-gate 	}
55207c478bd9Sstevel@tonic-gate 
55217c478bd9Sstevel@tonic-gate 	a->q_flags |= QPRIMARY;		/* flag as a "ctladdr" */
55227c478bd9Sstevel@tonic-gate 	a->q_mailer = LocalMailer;
55237c478bd9Sstevel@tonic-gate 	if (p == NULL)
55247c478bd9Sstevel@tonic-gate 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
55257c478bd9Sstevel@tonic-gate 	else
55267c478bd9Sstevel@tonic-gate 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
55277c478bd9Sstevel@tonic-gate 	return a;
55287c478bd9Sstevel@tonic-gate }
55297c478bd9Sstevel@tonic-gate /*
55307c478bd9Sstevel@tonic-gate **  LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
55317c478bd9Sstevel@tonic-gate **
55327c478bd9Sstevel@tonic-gate **	Parameters:
55337c478bd9Sstevel@tonic-gate **		e -- the envelope (e->e_id will be used).
55347c478bd9Sstevel@tonic-gate **		why -- reported to whomever can hear.
55357c478bd9Sstevel@tonic-gate **
55367c478bd9Sstevel@tonic-gate **	Returns:
55377c478bd9Sstevel@tonic-gate **		none.
55387c478bd9Sstevel@tonic-gate */
55397c478bd9Sstevel@tonic-gate 
55407c478bd9Sstevel@tonic-gate void
55417c478bd9Sstevel@tonic-gate loseqfile(e, why)
55427c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
55437c478bd9Sstevel@tonic-gate 	char *why;
55447c478bd9Sstevel@tonic-gate {
55457c478bd9Sstevel@tonic-gate 	bool loseit = true;
55467c478bd9Sstevel@tonic-gate 	char *p;
55477c478bd9Sstevel@tonic-gate 	char buf[MAXPATHLEN];
55487c478bd9Sstevel@tonic-gate 
55497c478bd9Sstevel@tonic-gate 	if (e == NULL || e->e_id == NULL)
55507c478bd9Sstevel@tonic-gate 		return;
55517c478bd9Sstevel@tonic-gate 	p = queuename(e, ANYQFL_LETTER);
55527c478bd9Sstevel@tonic-gate 	if (sm_strlcpy(buf, p, sizeof buf) >= sizeof buf)
55537c478bd9Sstevel@tonic-gate 		return;
55547c478bd9Sstevel@tonic-gate 	if (!bitset(EF_INQUEUE, e->e_flags))
55557c478bd9Sstevel@tonic-gate 		queueup(e, false, true);
55567c478bd9Sstevel@tonic-gate 	else if (QueueMode == QM_LOST)
55577c478bd9Sstevel@tonic-gate 		loseit = false;
55587c478bd9Sstevel@tonic-gate 
55597c478bd9Sstevel@tonic-gate 	/* if already lost, no need to re-lose */
55607c478bd9Sstevel@tonic-gate 	if (loseit)
55617c478bd9Sstevel@tonic-gate 	{
55627c478bd9Sstevel@tonic-gate 		p = queuename(e, LOSEQF_LETTER);
55637c478bd9Sstevel@tonic-gate 		if (rename(buf, p) < 0)
55647c478bd9Sstevel@tonic-gate 			syserr("cannot rename(%s, %s), uid=%d",
55657c478bd9Sstevel@tonic-gate 			       buf, p, (int) geteuid());
55667c478bd9Sstevel@tonic-gate 		else if (LogLevel > 0)
55677c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, e->e_id,
55687c478bd9Sstevel@tonic-gate 				  "Losing %s: %s", buf, why);
55697c478bd9Sstevel@tonic-gate 	}
55707c478bd9Sstevel@tonic-gate 	if (e->e_dfp != NULL)
55717c478bd9Sstevel@tonic-gate 	{
55727c478bd9Sstevel@tonic-gate 		(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
55737c478bd9Sstevel@tonic-gate 		e->e_dfp = NULL;
55747c478bd9Sstevel@tonic-gate 	}
55757c478bd9Sstevel@tonic-gate 	e->e_flags &= ~EF_HAS_DF;
55767c478bd9Sstevel@tonic-gate }
55777c478bd9Sstevel@tonic-gate /*
55787c478bd9Sstevel@tonic-gate **  NAME2QID -- translate a queue group name to a queue group id
55797c478bd9Sstevel@tonic-gate **
55807c478bd9Sstevel@tonic-gate **	Parameters:
55817c478bd9Sstevel@tonic-gate **		queuename -- name of queue group.
55827c478bd9Sstevel@tonic-gate **
55837c478bd9Sstevel@tonic-gate **	Returns:
55847c478bd9Sstevel@tonic-gate **		queue group id if found.
55857c478bd9Sstevel@tonic-gate **		NOQGRP otherwise.
55867c478bd9Sstevel@tonic-gate */
55877c478bd9Sstevel@tonic-gate 
55887c478bd9Sstevel@tonic-gate int
55897c478bd9Sstevel@tonic-gate name2qid(queuename)
55907c478bd9Sstevel@tonic-gate 	char *queuename;
55917c478bd9Sstevel@tonic-gate {
55927c478bd9Sstevel@tonic-gate 	register STAB *s;
55937c478bd9Sstevel@tonic-gate 
55947c478bd9Sstevel@tonic-gate 	s = stab(queuename, ST_QUEUE, ST_FIND);
55957c478bd9Sstevel@tonic-gate 	if (s == NULL)
55967c478bd9Sstevel@tonic-gate 		return NOQGRP;
55977c478bd9Sstevel@tonic-gate 	return s->s_quegrp->qg_index;
55987c478bd9Sstevel@tonic-gate }
55997c478bd9Sstevel@tonic-gate /*
56007c478bd9Sstevel@tonic-gate **  QID_PRINTNAME -- create externally printable version of queue id
56017c478bd9Sstevel@tonic-gate **
56027c478bd9Sstevel@tonic-gate **	Parameters:
56037c478bd9Sstevel@tonic-gate **		e -- the envelope.
56047c478bd9Sstevel@tonic-gate **
56057c478bd9Sstevel@tonic-gate **	Returns:
56067c478bd9Sstevel@tonic-gate **		a printable version
56077c478bd9Sstevel@tonic-gate */
56087c478bd9Sstevel@tonic-gate 
56097c478bd9Sstevel@tonic-gate char *
56107c478bd9Sstevel@tonic-gate qid_printname(e)
56117c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
56127c478bd9Sstevel@tonic-gate {
56137c478bd9Sstevel@tonic-gate 	char *id;
56147c478bd9Sstevel@tonic-gate 	static char idbuf[MAXQFNAME + 34];
56157c478bd9Sstevel@tonic-gate 
56167c478bd9Sstevel@tonic-gate 	if (e == NULL)
56177c478bd9Sstevel@tonic-gate 		return "";
56187c478bd9Sstevel@tonic-gate 
56197c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
56207c478bd9Sstevel@tonic-gate 		id = "";
56217c478bd9Sstevel@tonic-gate 	else
56227c478bd9Sstevel@tonic-gate 		id = e->e_id;
56237c478bd9Sstevel@tonic-gate 
56247c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR)
56257c478bd9Sstevel@tonic-gate 		return id;
56267c478bd9Sstevel@tonic-gate 
56277c478bd9Sstevel@tonic-gate 	(void) sm_snprintf(idbuf, sizeof idbuf, "%.32s/%s",
56287c478bd9Sstevel@tonic-gate 			   Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
56297c478bd9Sstevel@tonic-gate 			   id);
56307c478bd9Sstevel@tonic-gate 	return idbuf;
56317c478bd9Sstevel@tonic-gate }
56327c478bd9Sstevel@tonic-gate /*
56337c478bd9Sstevel@tonic-gate **  QID_PRINTQUEUE -- create full version of queue directory for data files
56347c478bd9Sstevel@tonic-gate **
56357c478bd9Sstevel@tonic-gate **	Parameters:
56367c478bd9Sstevel@tonic-gate **		qgrp -- index in queue group.
56377c478bd9Sstevel@tonic-gate **		qdir -- the short version of the queue directory
56387c478bd9Sstevel@tonic-gate **
56397c478bd9Sstevel@tonic-gate **	Returns:
56407c478bd9Sstevel@tonic-gate **		the full pathname to the queue (might point to a static var)
56417c478bd9Sstevel@tonic-gate */
56427c478bd9Sstevel@tonic-gate 
56437c478bd9Sstevel@tonic-gate char *
56447c478bd9Sstevel@tonic-gate qid_printqueue(qgrp, qdir)
56457c478bd9Sstevel@tonic-gate 	int qgrp;
56467c478bd9Sstevel@tonic-gate 	int qdir;
56477c478bd9Sstevel@tonic-gate {
56487c478bd9Sstevel@tonic-gate 	char *subdir;
56497c478bd9Sstevel@tonic-gate 	static char dir[MAXPATHLEN];
56507c478bd9Sstevel@tonic-gate 
56517c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
56527c478bd9Sstevel@tonic-gate 		return Queue[qgrp]->qg_qdir;
56537c478bd9Sstevel@tonic-gate 
56547c478bd9Sstevel@tonic-gate 	if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
56557c478bd9Sstevel@tonic-gate 		subdir = NULL;
56567c478bd9Sstevel@tonic-gate 	else
56577c478bd9Sstevel@tonic-gate 		subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
56587c478bd9Sstevel@tonic-gate 
56597c478bd9Sstevel@tonic-gate 	(void) sm_strlcpyn(dir, sizeof dir, 4,
56607c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qdir,
56617c478bd9Sstevel@tonic-gate 			subdir == NULL ? "" : "/",
56627c478bd9Sstevel@tonic-gate 			subdir == NULL ? "" : subdir,
56637c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBDF,
56647c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
56657c478bd9Sstevel@tonic-gate 					? "/df" : ""));
56667c478bd9Sstevel@tonic-gate 	return dir;
56677c478bd9Sstevel@tonic-gate }
56687c478bd9Sstevel@tonic-gate 
56697c478bd9Sstevel@tonic-gate /*
56707c478bd9Sstevel@tonic-gate **  PICKQDIR -- Pick a queue directory from a queue group
56717c478bd9Sstevel@tonic-gate **
56727c478bd9Sstevel@tonic-gate **	Parameters:
56737c478bd9Sstevel@tonic-gate **		qg -- queue group
56747c478bd9Sstevel@tonic-gate **		fsize -- file size in bytes
56757c478bd9Sstevel@tonic-gate **		e -- envelope, or NULL
56767c478bd9Sstevel@tonic-gate **
56777c478bd9Sstevel@tonic-gate **	Result:
56787c478bd9Sstevel@tonic-gate **		NOQDIR if no queue directory in qg has enough free space to
56797c478bd9Sstevel@tonic-gate **		hold a file of size 'fsize', otherwise the index of
56807c478bd9Sstevel@tonic-gate **		a randomly selected queue directory which resides on a
56817c478bd9Sstevel@tonic-gate **		file system with enough disk space.
56827c478bd9Sstevel@tonic-gate **		XXX This could be extended to select a queuedir with
56837c478bd9Sstevel@tonic-gate **			a few (the fewest?) number of entries. That data
56847c478bd9Sstevel@tonic-gate **			is available if shared memory is used.
56857c478bd9Sstevel@tonic-gate **
56867c478bd9Sstevel@tonic-gate **	Side Effects:
56877c478bd9Sstevel@tonic-gate **		If the request fails and e != NULL then sm_syslog is called.
56887c478bd9Sstevel@tonic-gate */
56897c478bd9Sstevel@tonic-gate 
56907c478bd9Sstevel@tonic-gate int
56917c478bd9Sstevel@tonic-gate pickqdir(qg, fsize, e)
56927c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
56937c478bd9Sstevel@tonic-gate 	long fsize;
56947c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
56957c478bd9Sstevel@tonic-gate {
56967c478bd9Sstevel@tonic-gate 	int qdir;
56977c478bd9Sstevel@tonic-gate 	int i;
56987c478bd9Sstevel@tonic-gate 	long avail = 0;
56997c478bd9Sstevel@tonic-gate 
57007c478bd9Sstevel@tonic-gate 	/* Pick a random directory, as a starting point. */
57017c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues <= 1)
57027c478bd9Sstevel@tonic-gate 		qdir = 0;
57037c478bd9Sstevel@tonic-gate 	else
57047c478bd9Sstevel@tonic-gate 		qdir = get_rand_mod(qg->qg_numqueues);
57057c478bd9Sstevel@tonic-gate 
57067c478bd9Sstevel@tonic-gate 	if (MinBlocksFree <= 0 && fsize <= 0)
57077c478bd9Sstevel@tonic-gate 		return qdir;
57087c478bd9Sstevel@tonic-gate 
57097c478bd9Sstevel@tonic-gate 	/*
57107c478bd9Sstevel@tonic-gate 	**  Now iterate over the queue directories,
57117c478bd9Sstevel@tonic-gate 	**  looking for a directory with enough space for this message.
57127c478bd9Sstevel@tonic-gate 	*/
57137c478bd9Sstevel@tonic-gate 
57147c478bd9Sstevel@tonic-gate 	i = qdir;
57157c478bd9Sstevel@tonic-gate 	do
57167c478bd9Sstevel@tonic-gate 	{
57177c478bd9Sstevel@tonic-gate 		QPATHS *qp = &qg->qg_qpaths[i];
57187c478bd9Sstevel@tonic-gate 		long needed = 0;
57197c478bd9Sstevel@tonic-gate 		long fsavail = 0;
57207c478bd9Sstevel@tonic-gate 
57217c478bd9Sstevel@tonic-gate 		if (fsize > 0)
57227c478bd9Sstevel@tonic-gate 			needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
57237c478bd9Sstevel@tonic-gate 				  + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
57247c478bd9Sstevel@tonic-gate 				      > 0) ? 1 : 0);
57257c478bd9Sstevel@tonic-gate 		if (MinBlocksFree > 0)
57267c478bd9Sstevel@tonic-gate 			needed += MinBlocksFree;
57277c478bd9Sstevel@tonic-gate 		fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
57287c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
57297c478bd9Sstevel@tonic-gate 		if (fsavail <= 0)
57307c478bd9Sstevel@tonic-gate 		{
57317c478bd9Sstevel@tonic-gate 			long blksize;
57327c478bd9Sstevel@tonic-gate 
57337c478bd9Sstevel@tonic-gate 			/*
57347c478bd9Sstevel@tonic-gate 			**  might be not correctly updated,
57357c478bd9Sstevel@tonic-gate 			**  let's try to get the info directly.
57367c478bd9Sstevel@tonic-gate 			*/
57377c478bd9Sstevel@tonic-gate 
57387c478bd9Sstevel@tonic-gate 			fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
57397c478bd9Sstevel@tonic-gate 						&blksize);
57407c478bd9Sstevel@tonic-gate 			if (fsavail < 0)
57417c478bd9Sstevel@tonic-gate 				fsavail = 0;
57427c478bd9Sstevel@tonic-gate 		}
57437c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
57447c478bd9Sstevel@tonic-gate 		if (needed <= fsavail)
57457c478bd9Sstevel@tonic-gate 			return i;
57467c478bd9Sstevel@tonic-gate 		if (avail < fsavail)
57477c478bd9Sstevel@tonic-gate 			avail = fsavail;
57487c478bd9Sstevel@tonic-gate 
57497c478bd9Sstevel@tonic-gate 		if (qg->qg_numqueues > 0)
57507c478bd9Sstevel@tonic-gate 			i = (i + 1) % qg->qg_numqueues;
57517c478bd9Sstevel@tonic-gate 	} while (i != qdir);
57527c478bd9Sstevel@tonic-gate 
57537c478bd9Sstevel@tonic-gate 	if (e != NULL && LogLevel > 0)
57547c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ALERT, e->e_id,
57557c478bd9Sstevel@tonic-gate 			"low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
57567c478bd9Sstevel@tonic-gate 			CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
57577c478bd9Sstevel@tonic-gate 			fsize, MinBlocksFree,
57587c478bd9Sstevel@tonic-gate 			qg->qg_qdir, avail);
57597c478bd9Sstevel@tonic-gate 	return NOQDIR;
57607c478bd9Sstevel@tonic-gate }
57617c478bd9Sstevel@tonic-gate /*
57627c478bd9Sstevel@tonic-gate **  SETNEWQUEUE -- Sets a new queue group and directory
57637c478bd9Sstevel@tonic-gate **
57647c478bd9Sstevel@tonic-gate **	Assign a queue group and directory to an envelope and store the
57657c478bd9Sstevel@tonic-gate **	directory in e->e_qdir.
57667c478bd9Sstevel@tonic-gate **
57677c478bd9Sstevel@tonic-gate **	Parameters:
57687c478bd9Sstevel@tonic-gate **		e -- envelope to assign a queue for.
57697c478bd9Sstevel@tonic-gate **
57707c478bd9Sstevel@tonic-gate **	Returns:
57717c478bd9Sstevel@tonic-gate **		true if successful
57727c478bd9Sstevel@tonic-gate **		false otherwise
57737c478bd9Sstevel@tonic-gate **
57747c478bd9Sstevel@tonic-gate **	Side Effects:
57757c478bd9Sstevel@tonic-gate **		On success, e->e_qgrp and e->e_qdir are non-negative.
57767c478bd9Sstevel@tonic-gate **		On failure (not enough disk space),
57777c478bd9Sstevel@tonic-gate **		e->qgrp = NOQGRP, e->e_qdir = NOQDIR
57787c478bd9Sstevel@tonic-gate **		and usrerr() is invoked (which could raise an exception).
57797c478bd9Sstevel@tonic-gate */
57807c478bd9Sstevel@tonic-gate 
57817c478bd9Sstevel@tonic-gate bool
57827c478bd9Sstevel@tonic-gate setnewqueue(e)
57837c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
57847c478bd9Sstevel@tonic-gate {
57857c478bd9Sstevel@tonic-gate 	if (tTd(41, 20))
57867c478bd9Sstevel@tonic-gate 		sm_dprintf("setnewqueue: called\n");
57877c478bd9Sstevel@tonic-gate 
57887c478bd9Sstevel@tonic-gate 	/* not set somewhere else */
57897c478bd9Sstevel@tonic-gate 	if (e->e_qgrp == NOQGRP)
57907c478bd9Sstevel@tonic-gate 	{
57917c478bd9Sstevel@tonic-gate 		ADDRESS *q;
57927c478bd9Sstevel@tonic-gate 
57937c478bd9Sstevel@tonic-gate 		/*
57947c478bd9Sstevel@tonic-gate 		**  Use the queue group of the "first" recipient, as set by
57957c478bd9Sstevel@tonic-gate 		**  the "queuegroup" rule set.  If that is not defined, then
57967c478bd9Sstevel@tonic-gate 		**  use the queue group of the mailer of the first recipient.
57977c478bd9Sstevel@tonic-gate 		**  If that is not defined either, then use the default
57987c478bd9Sstevel@tonic-gate 		**  queue group.
57997c478bd9Sstevel@tonic-gate 		**  Notice: "first" depends on the sorting of sendqueue
58007c478bd9Sstevel@tonic-gate 		**  in recipient().
58017c478bd9Sstevel@tonic-gate 		**  To avoid problems with "bad" recipients look
58027c478bd9Sstevel@tonic-gate 		**  for a valid address first.
58037c478bd9Sstevel@tonic-gate 		*/
58047c478bd9Sstevel@tonic-gate 
58057c478bd9Sstevel@tonic-gate 		q = e->e_sendqueue;
58067c478bd9Sstevel@tonic-gate 		while (q != NULL &&
58077c478bd9Sstevel@tonic-gate 		       (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state)))
58087c478bd9Sstevel@tonic-gate 		{
58097c478bd9Sstevel@tonic-gate 			q = q->q_next;
58107c478bd9Sstevel@tonic-gate 		}
58117c478bd9Sstevel@tonic-gate 		if (q == NULL)
58127c478bd9Sstevel@tonic-gate 			e->e_qgrp = 0;
58137c478bd9Sstevel@tonic-gate 		else if (q->q_qgrp >= 0)
58147c478bd9Sstevel@tonic-gate 			e->e_qgrp = q->q_qgrp;
58157c478bd9Sstevel@tonic-gate 		else if (q->q_mailer != NULL &&
58167c478bd9Sstevel@tonic-gate 			 ISVALIDQGRP(q->q_mailer->m_qgrp))
58177c478bd9Sstevel@tonic-gate 			e->e_qgrp = q->q_mailer->m_qgrp;
58187c478bd9Sstevel@tonic-gate 		else
58197c478bd9Sstevel@tonic-gate 			e->e_qgrp = 0;
58207c478bd9Sstevel@tonic-gate 		e->e_dfqgrp = e->e_qgrp;
58217c478bd9Sstevel@tonic-gate 	}
58227c478bd9Sstevel@tonic-gate 
58237c478bd9Sstevel@tonic-gate 	if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
58247c478bd9Sstevel@tonic-gate 	{
58257c478bd9Sstevel@tonic-gate 		if (tTd(41, 20))
58267c478bd9Sstevel@tonic-gate 			sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
58277c478bd9Sstevel@tonic-gate 				qid_printqueue(e->e_qgrp, e->e_qdir));
58287c478bd9Sstevel@tonic-gate 		return true;
58297c478bd9Sstevel@tonic-gate 	}
58307c478bd9Sstevel@tonic-gate 
58317c478bd9Sstevel@tonic-gate 	filesys_update();
58327c478bd9Sstevel@tonic-gate 	e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
58337c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR)
58347c478bd9Sstevel@tonic-gate 	{
58357c478bd9Sstevel@tonic-gate 		e->e_qgrp = NOQGRP;
58367c478bd9Sstevel@tonic-gate 		if (!bitset(EF_FATALERRS, e->e_flags))
58377c478bd9Sstevel@tonic-gate 			usrerr("452 4.4.5 Insufficient disk space; try again later");
58387c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_FATALERRS;
58397c478bd9Sstevel@tonic-gate 		return false;
58407c478bd9Sstevel@tonic-gate 	}
58417c478bd9Sstevel@tonic-gate 
58427c478bd9Sstevel@tonic-gate 	if (tTd(41, 3))
58437c478bd9Sstevel@tonic-gate 		sm_dprintf("setnewqueue: Assigned queue directory %s\n",
58447c478bd9Sstevel@tonic-gate 			qid_printqueue(e->e_qgrp, e->e_qdir));
58457c478bd9Sstevel@tonic-gate 
58467c478bd9Sstevel@tonic-gate 	if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
58477c478bd9Sstevel@tonic-gate 	{
58487c478bd9Sstevel@tonic-gate 		e->e_xfqgrp = e->e_qgrp;
58497c478bd9Sstevel@tonic-gate 		e->e_xfqdir = e->e_qdir;
58507c478bd9Sstevel@tonic-gate 	}
58517c478bd9Sstevel@tonic-gate 	e->e_dfqdir = e->e_qdir;
58527c478bd9Sstevel@tonic-gate 	return true;
58537c478bd9Sstevel@tonic-gate }
58547c478bd9Sstevel@tonic-gate /*
58557c478bd9Sstevel@tonic-gate **  CHKQDIR -- check a queue directory
58567c478bd9Sstevel@tonic-gate **
58577c478bd9Sstevel@tonic-gate **	Parameters:
58587c478bd9Sstevel@tonic-gate **		name -- name of queue directory
58597c478bd9Sstevel@tonic-gate **		sff -- flags for safefile()
58607c478bd9Sstevel@tonic-gate **
58617c478bd9Sstevel@tonic-gate **	Returns:
58627c478bd9Sstevel@tonic-gate **		is it a queue directory?
58637c478bd9Sstevel@tonic-gate */
58647c478bd9Sstevel@tonic-gate 
58657c478bd9Sstevel@tonic-gate static bool
58667c478bd9Sstevel@tonic-gate chkqdir(name, sff)
58677c478bd9Sstevel@tonic-gate 	char *name;
58687c478bd9Sstevel@tonic-gate 	long sff;
58697c478bd9Sstevel@tonic-gate {
58707c478bd9Sstevel@tonic-gate 	struct stat statb;
58717c478bd9Sstevel@tonic-gate 	int i;
58727c478bd9Sstevel@tonic-gate 
58737c478bd9Sstevel@tonic-gate 	/* skip over . and .. directories */
58747c478bd9Sstevel@tonic-gate 	if (name[0] == '.' &&
58757c478bd9Sstevel@tonic-gate 	    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
58767c478bd9Sstevel@tonic-gate 		return false;
58777c478bd9Sstevel@tonic-gate #if HASLSTAT
58787c478bd9Sstevel@tonic-gate 	if (lstat(name, &statb) < 0)
58797c478bd9Sstevel@tonic-gate #else /* HASLSTAT */
58807c478bd9Sstevel@tonic-gate 	if (stat(name, &statb) < 0)
58817c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
58827c478bd9Sstevel@tonic-gate 	{
58837c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
58847c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
58857c478bd9Sstevel@tonic-gate 				   name, sm_errstring(errno));
58867c478bd9Sstevel@tonic-gate 		return false;
58877c478bd9Sstevel@tonic-gate 	}
58887c478bd9Sstevel@tonic-gate #if HASLSTAT
58897c478bd9Sstevel@tonic-gate 	if (S_ISLNK(statb.st_mode))
58907c478bd9Sstevel@tonic-gate 	{
58917c478bd9Sstevel@tonic-gate 		/*
58927c478bd9Sstevel@tonic-gate 		**  For a symlink we need to make sure the
58937c478bd9Sstevel@tonic-gate 		**  target is a directory
58947c478bd9Sstevel@tonic-gate 		*/
58957c478bd9Sstevel@tonic-gate 
58967c478bd9Sstevel@tonic-gate 		if (stat(name, &statb) < 0)
58977c478bd9Sstevel@tonic-gate 		{
58987c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
58997c478bd9Sstevel@tonic-gate 				sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
59007c478bd9Sstevel@tonic-gate 					   name, sm_errstring(errno));
59017c478bd9Sstevel@tonic-gate 			return false;
59027c478bd9Sstevel@tonic-gate 		}
59037c478bd9Sstevel@tonic-gate 	}
59047c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
59057c478bd9Sstevel@tonic-gate 
59067c478bd9Sstevel@tonic-gate 	if (!S_ISDIR(statb.st_mode))
59077c478bd9Sstevel@tonic-gate 	{
59087c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59097c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: \"%s\": Not a directory\n",
59107c478bd9Sstevel@tonic-gate 				name);
59117c478bd9Sstevel@tonic-gate 		return false;
59127c478bd9Sstevel@tonic-gate 	}
59137c478bd9Sstevel@tonic-gate 
59147c478bd9Sstevel@tonic-gate 	/* Print a warning if unsafe (but still use it) */
59157c478bd9Sstevel@tonic-gate 	/* XXX do this only if we want the warning? */
59167c478bd9Sstevel@tonic-gate 	i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
59177c478bd9Sstevel@tonic-gate 	if (i != 0)
59187c478bd9Sstevel@tonic-gate 	{
59197c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59207c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
59217c478bd9Sstevel@tonic-gate 				   name, sm_errstring(i));
59227c478bd9Sstevel@tonic-gate #if _FFR_CHK_QUEUE
59237c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
59247c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
59257c478bd9Sstevel@tonic-gate 				  "queue directory \"%s\": Not safe: %s",
59267c478bd9Sstevel@tonic-gate 				  name, sm_errstring(i));
59277c478bd9Sstevel@tonic-gate #endif /* _FFR_CHK_QUEUE */
59287c478bd9Sstevel@tonic-gate 	}
59297c478bd9Sstevel@tonic-gate 	return true;
59307c478bd9Sstevel@tonic-gate }
59317c478bd9Sstevel@tonic-gate /*
59327c478bd9Sstevel@tonic-gate **  MULTIQUEUE_CACHE -- cache a list of paths to queues.
59337c478bd9Sstevel@tonic-gate **
59347c478bd9Sstevel@tonic-gate **	Each potential queue is checked as the cache is built.
59357c478bd9Sstevel@tonic-gate **	Thereafter, each is blindly trusted.
59367c478bd9Sstevel@tonic-gate **	Note that we can be called again after a timeout to rebuild
59377c478bd9Sstevel@tonic-gate **	(although code for that is not ready yet).
59387c478bd9Sstevel@tonic-gate **
59397c478bd9Sstevel@tonic-gate **	Parameters:
59407c478bd9Sstevel@tonic-gate **		basedir -- base of all queue directories.
59417c478bd9Sstevel@tonic-gate **		blen -- strlen(basedir).
59427c478bd9Sstevel@tonic-gate **		qg -- queue group.
59437c478bd9Sstevel@tonic-gate **		qn -- number of queue directories already cached.
59447c478bd9Sstevel@tonic-gate **		phash -- pointer to hash value over queue dirs.
59457c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
59467c478bd9Sstevel@tonic-gate **			only used if shared memory is active.
59477c478bd9Sstevel@tonic-gate #endif * SM_CONF_SHM *
59487c478bd9Sstevel@tonic-gate **
59497c478bd9Sstevel@tonic-gate **	Returns:
59507c478bd9Sstevel@tonic-gate **		new number of queue directories.
59517c478bd9Sstevel@tonic-gate */
59527c478bd9Sstevel@tonic-gate 
59537c478bd9Sstevel@tonic-gate #define INITIAL_SLOTS	20
59547c478bd9Sstevel@tonic-gate #define ADD_SLOTS	10
59557c478bd9Sstevel@tonic-gate 
59567c478bd9Sstevel@tonic-gate static int
59577c478bd9Sstevel@tonic-gate multiqueue_cache(basedir, blen, qg, qn, phash)
59587c478bd9Sstevel@tonic-gate 	char *basedir;
59597c478bd9Sstevel@tonic-gate 	int blen;
59607c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
59617c478bd9Sstevel@tonic-gate 	int qn;
59627c478bd9Sstevel@tonic-gate 	unsigned int *phash;
59637c478bd9Sstevel@tonic-gate {
59647c478bd9Sstevel@tonic-gate 	char *cp;
59657c478bd9Sstevel@tonic-gate 	int i, len;
59667c478bd9Sstevel@tonic-gate 	int slotsleft = 0;
59677c478bd9Sstevel@tonic-gate 	long sff = SFF_ANYFILE;
59687c478bd9Sstevel@tonic-gate 	char qpath[MAXPATHLEN];
59697c478bd9Sstevel@tonic-gate 	char subdir[MAXPATHLEN];
59707c478bd9Sstevel@tonic-gate 	char prefix[MAXPATHLEN];	/* dir relative to basedir */
59717c478bd9Sstevel@tonic-gate 
59727c478bd9Sstevel@tonic-gate 	if (tTd(41, 20))
59737c478bd9Sstevel@tonic-gate 		sm_dprintf("multiqueue_cache: called\n");
59747c478bd9Sstevel@tonic-gate 
59757c478bd9Sstevel@tonic-gate 	/* Initialize to current directory */
59767c478bd9Sstevel@tonic-gate 	prefix[0] = '.';
59777c478bd9Sstevel@tonic-gate 	prefix[1] = '\0';
59787c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
59797c478bd9Sstevel@tonic-gate 	{
59807c478bd9Sstevel@tonic-gate 		for (i = 0; i < qg->qg_numqueues; i++)
59817c478bd9Sstevel@tonic-gate 		{
59827c478bd9Sstevel@tonic-gate 			if (qg->qg_qpaths[i].qp_name != NULL)
59837c478bd9Sstevel@tonic-gate 				(void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
59847c478bd9Sstevel@tonic-gate 		}
59857c478bd9Sstevel@tonic-gate 		(void) sm_free((char *) qg->qg_qpaths); /* XXX */
59867c478bd9Sstevel@tonic-gate 		qg->qg_qpaths = NULL;
59877c478bd9Sstevel@tonic-gate 		qg->qg_numqueues = 0;
59887c478bd9Sstevel@tonic-gate 	}
59897c478bd9Sstevel@tonic-gate 
59907c478bd9Sstevel@tonic-gate 	/* If running as root, allow safedirpath() checks to use privs */
59917c478bd9Sstevel@tonic-gate 	if (RunAsUid == 0)
59927c478bd9Sstevel@tonic-gate 		sff |= SFF_ROOTOK;
59937c478bd9Sstevel@tonic-gate #if _FFR_CHK_QUEUE
59947c478bd9Sstevel@tonic-gate 	sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES;
59957c478bd9Sstevel@tonic-gate 	if (!UseMSP)
59967c478bd9Sstevel@tonic-gate 		sff |= SFF_NOGWFILES;
59977c478bd9Sstevel@tonic-gate #endif /* _FFR_CHK_QUEUE */
59987c478bd9Sstevel@tonic-gate 
59997c478bd9Sstevel@tonic-gate 	if (!SM_IS_DIR_START(qg->qg_qdir))
60007c478bd9Sstevel@tonic-gate 	{
60017c478bd9Sstevel@tonic-gate 		/*
60027c478bd9Sstevel@tonic-gate 		**  XXX we could add basedir, but then we have to realloc()
60037c478bd9Sstevel@tonic-gate 		**  the string... Maybe another time.
60047c478bd9Sstevel@tonic-gate 		*/
60057c478bd9Sstevel@tonic-gate 
60067c478bd9Sstevel@tonic-gate 		syserr("QueuePath %s not absolute", qg->qg_qdir);
60077c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60087c478bd9Sstevel@tonic-gate 		return qn;
60097c478bd9Sstevel@tonic-gate 	}
60107c478bd9Sstevel@tonic-gate 
60117c478bd9Sstevel@tonic-gate 	/* qpath: directory of current workgroup */
60127c478bd9Sstevel@tonic-gate 	len = sm_strlcpy(qpath, qg->qg_qdir, sizeof qpath);
60137c478bd9Sstevel@tonic-gate 	if (len >= sizeof qpath)
60147c478bd9Sstevel@tonic-gate 	{
60157c478bd9Sstevel@tonic-gate 		syserr("QueuePath %.256s too long (%d max)",
60167c478bd9Sstevel@tonic-gate 		       qg->qg_qdir, (int) sizeof qpath);
60177c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60187c478bd9Sstevel@tonic-gate 		return qn;
60197c478bd9Sstevel@tonic-gate 	}
60207c478bd9Sstevel@tonic-gate 
60217c478bd9Sstevel@tonic-gate 	/* begin of qpath must be same as basedir */
60227c478bd9Sstevel@tonic-gate 	if (strncmp(basedir, qpath, blen) != 0 &&
60237c478bd9Sstevel@tonic-gate 	    (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
60247c478bd9Sstevel@tonic-gate 	{
60257c478bd9Sstevel@tonic-gate 		syserr("QueuePath %s not subpath of QueueDirectory %s",
60267c478bd9Sstevel@tonic-gate 			qpath, basedir);
60277c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60287c478bd9Sstevel@tonic-gate 		return qn;
60297c478bd9Sstevel@tonic-gate 	}
60307c478bd9Sstevel@tonic-gate 
60317c478bd9Sstevel@tonic-gate 	/* Do we have a nested subdirectory? */
60327c478bd9Sstevel@tonic-gate 	if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
60337c478bd9Sstevel@tonic-gate 	{
60347c478bd9Sstevel@tonic-gate 
60357c478bd9Sstevel@tonic-gate 		/* Copy subdirectory into prefix for later use */
60367c478bd9Sstevel@tonic-gate 		if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof prefix) >=
60377c478bd9Sstevel@tonic-gate 		    sizeof prefix)
60387c478bd9Sstevel@tonic-gate 		{
60397c478bd9Sstevel@tonic-gate 			syserr("QueuePath %.256s too long (%d max)",
60407c478bd9Sstevel@tonic-gate 				qg->qg_qdir, (int) sizeof qpath);
60417c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
60427c478bd9Sstevel@tonic-gate 			return qn;
60437c478bd9Sstevel@tonic-gate 		}
60447c478bd9Sstevel@tonic-gate 		cp = SM_LAST_DIR_DELIM(prefix);
60457c478bd9Sstevel@tonic-gate 		SM_ASSERT(cp != NULL);
60467c478bd9Sstevel@tonic-gate 		*cp = '\0';	/* cut off trailing / */
60477c478bd9Sstevel@tonic-gate 	}
60487c478bd9Sstevel@tonic-gate 
60497c478bd9Sstevel@tonic-gate 	/* This is guaranteed by the basedir check above */
60507c478bd9Sstevel@tonic-gate 	SM_ASSERT(len >= blen - 1);
60517c478bd9Sstevel@tonic-gate 	cp = &qpath[len - 1];
60527c478bd9Sstevel@tonic-gate 	if (*cp == '*')
60537c478bd9Sstevel@tonic-gate 	{
60547c478bd9Sstevel@tonic-gate 		register DIR *dp;
60557c478bd9Sstevel@tonic-gate 		register struct dirent *d;
60567c478bd9Sstevel@tonic-gate 		int off;
60577c478bd9Sstevel@tonic-gate 		char *delim;
60587c478bd9Sstevel@tonic-gate 		char relpath[MAXPATHLEN];
60597c478bd9Sstevel@tonic-gate 
60607c478bd9Sstevel@tonic-gate 		*cp = '\0';	/* Overwrite wildcard */
60617c478bd9Sstevel@tonic-gate 		if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
60627c478bd9Sstevel@tonic-gate 		{
60637c478bd9Sstevel@tonic-gate 			syserr("QueueDirectory: can not wildcard relative path");
60647c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
60657c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
60667c478bd9Sstevel@tonic-gate 					qpath);
60677c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
60687c478bd9Sstevel@tonic-gate 			return qn;
60697c478bd9Sstevel@tonic-gate 		}
60707c478bd9Sstevel@tonic-gate 		if (cp == qpath)
60717c478bd9Sstevel@tonic-gate 		{
60727c478bd9Sstevel@tonic-gate 			/*
60737c478bd9Sstevel@tonic-gate 			**  Special case of top level wildcard, like /foo*
60747c478bd9Sstevel@tonic-gate 			**	Change to //foo*
60757c478bd9Sstevel@tonic-gate 			*/
60767c478bd9Sstevel@tonic-gate 
60777c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(qpath + 1, qpath, sizeof qpath - 1);
60787c478bd9Sstevel@tonic-gate 			++cp;
60797c478bd9Sstevel@tonic-gate 		}
60807c478bd9Sstevel@tonic-gate 		delim = cp;
60817c478bd9Sstevel@tonic-gate 		*(cp++) = '\0';		/* Replace / with \0 */
60827c478bd9Sstevel@tonic-gate 		len = strlen(cp);	/* Last component of queue directory */
60837c478bd9Sstevel@tonic-gate 
60847c478bd9Sstevel@tonic-gate 		/*
60857c478bd9Sstevel@tonic-gate 		**  Path relative to basedir, with trailing /
60867c478bd9Sstevel@tonic-gate 		**  It will be modified below to specify the subdirectories
60877c478bd9Sstevel@tonic-gate 		**  so they can be opened without chdir().
60887c478bd9Sstevel@tonic-gate 		*/
60897c478bd9Sstevel@tonic-gate 
60907c478bd9Sstevel@tonic-gate 		off = sm_strlcpyn(relpath, sizeof relpath, 2, prefix, "/");
60917c478bd9Sstevel@tonic-gate 		SM_ASSERT(off < sizeof relpath);
60927c478bd9Sstevel@tonic-gate 
60937c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
60947c478bd9Sstevel@tonic-gate 			sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
60957c478bd9Sstevel@tonic-gate 				   relpath, cp);
60967c478bd9Sstevel@tonic-gate 
60977c478bd9Sstevel@tonic-gate 		/* It is always basedir: we don't need to store it per group */
60987c478bd9Sstevel@tonic-gate 		/* XXX: optimize this! -> one more global? */
60997c478bd9Sstevel@tonic-gate 		qg->qg_qdir = newstr(basedir);
61007c478bd9Sstevel@tonic-gate 		qg->qg_qdir[blen - 1] = '\0';	/* cut off trailing / */
61017c478bd9Sstevel@tonic-gate 
61027c478bd9Sstevel@tonic-gate 		/*
61037c478bd9Sstevel@tonic-gate 		**  XXX Should probably wrap this whole loop in a timeout
61047c478bd9Sstevel@tonic-gate 		**  in case some wag decides to NFS mount the queues.
61057c478bd9Sstevel@tonic-gate 		*/
61067c478bd9Sstevel@tonic-gate 
61077c478bd9Sstevel@tonic-gate 		/* Test path to get warning messages. */
61087c478bd9Sstevel@tonic-gate 		if (qn == 0)
61097c478bd9Sstevel@tonic-gate 		{
61107c478bd9Sstevel@tonic-gate 			/*  XXX qg_runasuid and qg_runasgid for specials? */
61117c478bd9Sstevel@tonic-gate 			i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
61127c478bd9Sstevel@tonic-gate 					sff, 0, 0);
61137c478bd9Sstevel@tonic-gate 			if (i != 0 && tTd(41, 2))
61147c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
61157c478bd9Sstevel@tonic-gate 					   basedir, sm_errstring(i));
61167c478bd9Sstevel@tonic-gate 		}
61177c478bd9Sstevel@tonic-gate 
61187c478bd9Sstevel@tonic-gate 		if ((dp = opendir(prefix)) == NULL)
61197c478bd9Sstevel@tonic-gate 		{
61207c478bd9Sstevel@tonic-gate 			syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
61217c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
61227c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
61237c478bd9Sstevel@tonic-gate 					   qg->qg_qdir, prefix,
61247c478bd9Sstevel@tonic-gate 					   sm_errstring(errno));
61257c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
61267c478bd9Sstevel@tonic-gate 			return qn;
61277c478bd9Sstevel@tonic-gate 		}
61287c478bd9Sstevel@tonic-gate 		while ((d = readdir(dp)) != NULL)
61297c478bd9Sstevel@tonic-gate 		{
61307c478bd9Sstevel@tonic-gate 			i = strlen(d->d_name);
61317c478bd9Sstevel@tonic-gate 			if (i < len || strncmp(d->d_name, cp, len) != 0)
61327c478bd9Sstevel@tonic-gate 			{
61337c478bd9Sstevel@tonic-gate 				if (tTd(41, 5))
61347c478bd9Sstevel@tonic-gate 					sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
61357c478bd9Sstevel@tonic-gate 						d->d_name);
61367c478bd9Sstevel@tonic-gate 				continue;
61377c478bd9Sstevel@tonic-gate 			}
61387c478bd9Sstevel@tonic-gate 
61397c478bd9Sstevel@tonic-gate 			/* Create relative pathname: prefix + local directory */
61407c478bd9Sstevel@tonic-gate 			i = sizeof(relpath) - off;
61417c478bd9Sstevel@tonic-gate 			if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
61427c478bd9Sstevel@tonic-gate 				continue;	/* way too long */
61437c478bd9Sstevel@tonic-gate 
61447c478bd9Sstevel@tonic-gate 			if (!chkqdir(relpath, sff))
61457c478bd9Sstevel@tonic-gate 				continue;
61467c478bd9Sstevel@tonic-gate 
61477c478bd9Sstevel@tonic-gate 			if (qg->qg_qpaths == NULL)
61487c478bd9Sstevel@tonic-gate 			{
61497c478bd9Sstevel@tonic-gate 				slotsleft = INITIAL_SLOTS;
61507c478bd9Sstevel@tonic-gate 				qg->qg_qpaths = (QPATHS *)xalloc((sizeof *qg->qg_qpaths) *
61517c478bd9Sstevel@tonic-gate 								slotsleft);
61527c478bd9Sstevel@tonic-gate 				qg->qg_numqueues = 0;
61537c478bd9Sstevel@tonic-gate 			}
61547c478bd9Sstevel@tonic-gate 			else if (slotsleft < 1)
61557c478bd9Sstevel@tonic-gate 			{
61567c478bd9Sstevel@tonic-gate 				qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
61577c478bd9Sstevel@tonic-gate 							  (sizeof *qg->qg_qpaths) *
61587c478bd9Sstevel@tonic-gate 							  (qg->qg_numqueues +
61597c478bd9Sstevel@tonic-gate 							   ADD_SLOTS));
61607c478bd9Sstevel@tonic-gate 				if (qg->qg_qpaths == NULL)
61617c478bd9Sstevel@tonic-gate 				{
61627c478bd9Sstevel@tonic-gate 					(void) closedir(dp);
61637c478bd9Sstevel@tonic-gate 					return qn;
61647c478bd9Sstevel@tonic-gate 				}
61657c478bd9Sstevel@tonic-gate 				slotsleft += ADD_SLOTS;
61667c478bd9Sstevel@tonic-gate 			}
61677c478bd9Sstevel@tonic-gate 
61687c478bd9Sstevel@tonic-gate 			/* check subdirs */
61697c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
61707c478bd9Sstevel@tonic-gate 
61717c478bd9Sstevel@tonic-gate #define CHKRSUBDIR(name, flag)	\
61727c478bd9Sstevel@tonic-gate 	(void) sm_strlcpyn(subdir, sizeof subdir, 3, relpath, "/", name); \
61737c478bd9Sstevel@tonic-gate 	if (chkqdir(subdir, sff))	\
61747c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag;	\
61757c478bd9Sstevel@tonic-gate 	else
61767c478bd9Sstevel@tonic-gate 
61777c478bd9Sstevel@tonic-gate 
61787c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("qf", QP_SUBQF);
61797c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("df", QP_SUBDF);
61807c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("xf", QP_SUBXF);
61817c478bd9Sstevel@tonic-gate 
61827c478bd9Sstevel@tonic-gate 			/* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
61837c478bd9Sstevel@tonic-gate 			/* maybe even - 17 (subdirs) */
61847c478bd9Sstevel@tonic-gate 
61857c478bd9Sstevel@tonic-gate 			if (prefix[0] != '.')
61867c478bd9Sstevel@tonic-gate 				qg->qg_qpaths[qg->qg_numqueues].qp_name =
61877c478bd9Sstevel@tonic-gate 					newstr(relpath);
61887c478bd9Sstevel@tonic-gate 			else
61897c478bd9Sstevel@tonic-gate 				qg->qg_qpaths[qg->qg_numqueues].qp_name =
61907c478bd9Sstevel@tonic-gate 					newstr(d->d_name);
61917c478bd9Sstevel@tonic-gate 
61927c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
61937c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
61947c478bd9Sstevel@tonic-gate 					qg->qg_numqueues, relpath,
61957c478bd9Sstevel@tonic-gate 					qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
61967c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
61977c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
61987c478bd9Sstevel@tonic-gate 			*phash = hash_q(relpath, *phash);
61997c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
62007c478bd9Sstevel@tonic-gate 			qg->qg_numqueues++;
62017c478bd9Sstevel@tonic-gate 			++qn;
62027c478bd9Sstevel@tonic-gate 			slotsleft--;
62037c478bd9Sstevel@tonic-gate 		}
62047c478bd9Sstevel@tonic-gate 		(void) closedir(dp);
62057c478bd9Sstevel@tonic-gate 
62067c478bd9Sstevel@tonic-gate 		/* undo damage */
62077c478bd9Sstevel@tonic-gate 		*delim = '/';
62087c478bd9Sstevel@tonic-gate 	}
62097c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues == 0)
62107c478bd9Sstevel@tonic-gate 	{
62117c478bd9Sstevel@tonic-gate 		qg->qg_qpaths = (QPATHS *) xalloc(sizeof *qg->qg_qpaths);
62127c478bd9Sstevel@tonic-gate 
62137c478bd9Sstevel@tonic-gate 		/* test path to get warning messages */
62147c478bd9Sstevel@tonic-gate 		i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
62157c478bd9Sstevel@tonic-gate 		if (i == ENOENT)
62167c478bd9Sstevel@tonic-gate 		{
62177c478bd9Sstevel@tonic-gate 			syserr("can not opendir(%s)", qpath);
62187c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
62197c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
62207c478bd9Sstevel@tonic-gate 					   qpath, sm_errstring(i));
62217c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
62227c478bd9Sstevel@tonic-gate 			return qn;
62237c478bd9Sstevel@tonic-gate 		}
62247c478bd9Sstevel@tonic-gate 
62257c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
62267c478bd9Sstevel@tonic-gate 		qg->qg_numqueues = 1;
62277c478bd9Sstevel@tonic-gate 
62287c478bd9Sstevel@tonic-gate 		/* check subdirs */
62297c478bd9Sstevel@tonic-gate #define CHKSUBDIR(name, flag)	\
62307c478bd9Sstevel@tonic-gate 	(void) sm_strlcpyn(subdir, sizeof subdir, 3, qg->qg_qdir, "/", name); \
62317c478bd9Sstevel@tonic-gate 	if (chkqdir(subdir, sff))	\
62327c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_subdirs |= flag;	\
62337c478bd9Sstevel@tonic-gate 	else
62347c478bd9Sstevel@tonic-gate 
62357c478bd9Sstevel@tonic-gate 		CHKSUBDIR("qf", QP_SUBQF);
62367c478bd9Sstevel@tonic-gate 		CHKSUBDIR("df", QP_SUBDF);
62377c478bd9Sstevel@tonic-gate 		CHKSUBDIR("xf", QP_SUBXF);
62387c478bd9Sstevel@tonic-gate 
62397c478bd9Sstevel@tonic-gate 		if (qg->qg_qdir[blen - 1] != '\0' &&
62407c478bd9Sstevel@tonic-gate 		    qg->qg_qdir[blen] != '\0')
62417c478bd9Sstevel@tonic-gate 		{
62427c478bd9Sstevel@tonic-gate 			/*
62437c478bd9Sstevel@tonic-gate 			**  Copy the last component into qpaths and
62447c478bd9Sstevel@tonic-gate 			**  cut off qdir
62457c478bd9Sstevel@tonic-gate 			*/
62467c478bd9Sstevel@tonic-gate 
62477c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
62487c478bd9Sstevel@tonic-gate 			qg->qg_qdir[blen - 1] = '\0';
62497c478bd9Sstevel@tonic-gate 		}
62507c478bd9Sstevel@tonic-gate 		else
62517c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[0].qp_name = newstr(".");
62527c478bd9Sstevel@tonic-gate 
62537c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
62547c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_idx = qn;
62557c478bd9Sstevel@tonic-gate 		*phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
62567c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
62577c478bd9Sstevel@tonic-gate 		++qn;
62587c478bd9Sstevel@tonic-gate 	}
62597c478bd9Sstevel@tonic-gate 	return qn;
62607c478bd9Sstevel@tonic-gate }
62617c478bd9Sstevel@tonic-gate 
62627c478bd9Sstevel@tonic-gate /*
62637c478bd9Sstevel@tonic-gate **  FILESYS_FIND -- find entry in FileSys table, or add new one
62647c478bd9Sstevel@tonic-gate **
62657c478bd9Sstevel@tonic-gate **	Given the pathname of a directory, determine the file system
62667c478bd9Sstevel@tonic-gate **	in which that directory resides, and return a pointer to the
62677c478bd9Sstevel@tonic-gate **	entry in the FileSys table that describes the file system.
62687c478bd9Sstevel@tonic-gate **	A new entry is added if necessary (and requested).
62697c478bd9Sstevel@tonic-gate **	If the directory does not exist, -1 is returned.
62707c478bd9Sstevel@tonic-gate **
62717c478bd9Sstevel@tonic-gate **	Parameters:
627249218d4fSjbeck **		name -- name of directory (must be persistent!)
627349218d4fSjbeck **		path -- pathname of directory (name plus maybe "/df")
62747c478bd9Sstevel@tonic-gate **		add -- add to structure if not found.
62757c478bd9Sstevel@tonic-gate **
62767c478bd9Sstevel@tonic-gate **	Returns:
62777c478bd9Sstevel@tonic-gate **		>=0: found: index in file system table
62787c478bd9Sstevel@tonic-gate **		<0: some error, i.e.,
62797c478bd9Sstevel@tonic-gate **		FSF_TOO_MANY: too many filesystems (-> syserr())
62807c478bd9Sstevel@tonic-gate **		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
62817c478bd9Sstevel@tonic-gate **		FSF_NOT_FOUND: not in list
62827c478bd9Sstevel@tonic-gate */
62837c478bd9Sstevel@tonic-gate 
628449218d4fSjbeck static short filesys_find __P((char *, char *, bool));
62857c478bd9Sstevel@tonic-gate 
62867c478bd9Sstevel@tonic-gate #define FSF_NOT_FOUND	(-1)
62877c478bd9Sstevel@tonic-gate #define FSF_STAT_FAIL	(-2)
62887c478bd9Sstevel@tonic-gate #define FSF_TOO_MANY	(-3)
62897c478bd9Sstevel@tonic-gate 
62907c478bd9Sstevel@tonic-gate static short
629149218d4fSjbeck filesys_find(name, path, add)
629249218d4fSjbeck 	char *name;
62937c478bd9Sstevel@tonic-gate 	char *path;
62947c478bd9Sstevel@tonic-gate 	bool add;
62957c478bd9Sstevel@tonic-gate {
62967c478bd9Sstevel@tonic-gate 	struct stat st;
62977c478bd9Sstevel@tonic-gate 	short i;
62987c478bd9Sstevel@tonic-gate 
62997c478bd9Sstevel@tonic-gate 	if (stat(path, &st) < 0)
63007c478bd9Sstevel@tonic-gate 	{
63017c478bd9Sstevel@tonic-gate 		syserr("cannot stat queue directory %s", path);
63027c478bd9Sstevel@tonic-gate 		return FSF_STAT_FAIL;
63037c478bd9Sstevel@tonic-gate 	}
63047c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
63057c478bd9Sstevel@tonic-gate 	{
63067c478bd9Sstevel@tonic-gate 		if (FILE_SYS_DEV(i) == st.st_dev)
63077c478bd9Sstevel@tonic-gate 			return i;
63087c478bd9Sstevel@tonic-gate 	}
63097c478bd9Sstevel@tonic-gate 	if (i >= MAXFILESYS)
63107c478bd9Sstevel@tonic-gate 	{
63117c478bd9Sstevel@tonic-gate 		syserr("too many queue file systems (%d max)", MAXFILESYS);
63127c478bd9Sstevel@tonic-gate 		return FSF_TOO_MANY;
63137c478bd9Sstevel@tonic-gate 	}
63147c478bd9Sstevel@tonic-gate 	if (!add)
63157c478bd9Sstevel@tonic-gate 		return FSF_NOT_FOUND;
63167c478bd9Sstevel@tonic-gate 
63177c478bd9Sstevel@tonic-gate 	++NumFileSys;
631849218d4fSjbeck 	FILE_SYS_NAME(i) = name;
63197c478bd9Sstevel@tonic-gate 	FILE_SYS_DEV(i) = st.st_dev;
63207c478bd9Sstevel@tonic-gate 	FILE_SYS_AVAIL(i) = 0;
63217c478bd9Sstevel@tonic-gate 	FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
63227c478bd9Sstevel@tonic-gate 	return i;
63237c478bd9Sstevel@tonic-gate }
63247c478bd9Sstevel@tonic-gate 
63257c478bd9Sstevel@tonic-gate /*
63267c478bd9Sstevel@tonic-gate **  FILESYS_SETUP -- set up mapping from queue directories to file systems
63277c478bd9Sstevel@tonic-gate **
63287c478bd9Sstevel@tonic-gate **	This data structure is used to efficiently check the amount of
63297c478bd9Sstevel@tonic-gate **	free space available in a set of queue directories.
63307c478bd9Sstevel@tonic-gate **
63317c478bd9Sstevel@tonic-gate **	Parameters:
63327c478bd9Sstevel@tonic-gate **		add -- initialize structure if necessary.
63337c478bd9Sstevel@tonic-gate **
63347c478bd9Sstevel@tonic-gate **	Returns:
63357c478bd9Sstevel@tonic-gate **		0: success
63367c478bd9Sstevel@tonic-gate **		<0: some error, i.e.,
63377c478bd9Sstevel@tonic-gate **		FSF_NOT_FOUND: not in list
63387c478bd9Sstevel@tonic-gate **		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
63397c478bd9Sstevel@tonic-gate **		FSF_TOO_MANY: too many filesystems (-> syserr())
63407c478bd9Sstevel@tonic-gate */
63417c478bd9Sstevel@tonic-gate 
63427c478bd9Sstevel@tonic-gate static int filesys_setup __P((bool));
63437c478bd9Sstevel@tonic-gate 
63447c478bd9Sstevel@tonic-gate static int
63457c478bd9Sstevel@tonic-gate filesys_setup(add)
63467c478bd9Sstevel@tonic-gate 	bool add;
63477c478bd9Sstevel@tonic-gate {
63487c478bd9Sstevel@tonic-gate 	int i, j;
63497c478bd9Sstevel@tonic-gate 	short fs;
63507c478bd9Sstevel@tonic-gate 	int ret;
63517c478bd9Sstevel@tonic-gate 
63527c478bd9Sstevel@tonic-gate 	ret = 0;
63537c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
63547c478bd9Sstevel@tonic-gate 	{
63557c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; ++j)
63567c478bd9Sstevel@tonic-gate 		{
63577c478bd9Sstevel@tonic-gate 			QPATHS *qp = &Queue[i]->qg_qpaths[j];
635849218d4fSjbeck 			char qddf[MAXPATHLEN];
63597c478bd9Sstevel@tonic-gate 
636049218d4fSjbeck 			(void) sm_strlcpyn(qddf, sizeof qddf, 2, qp->qp_name,
636149218d4fSjbeck 					(bitset(QP_SUBDF, qp->qp_subdirs)
636249218d4fSjbeck 						? "/df" : ""));
636349218d4fSjbeck 			fs = filesys_find(qp->qp_name, qddf, add);
63647c478bd9Sstevel@tonic-gate 			if (fs >= 0)
63657c478bd9Sstevel@tonic-gate 				qp->qp_fsysidx = fs;
63667c478bd9Sstevel@tonic-gate 			else
63677c478bd9Sstevel@tonic-gate 				qp->qp_fsysidx = 0;
63687c478bd9Sstevel@tonic-gate 			if (fs < ret)
63697c478bd9Sstevel@tonic-gate 				ret = fs;
63707c478bd9Sstevel@tonic-gate 		}
63717c478bd9Sstevel@tonic-gate 	}
63727c478bd9Sstevel@tonic-gate 	return ret;
63737c478bd9Sstevel@tonic-gate }
63747c478bd9Sstevel@tonic-gate 
63757c478bd9Sstevel@tonic-gate /*
63767c478bd9Sstevel@tonic-gate **  FILESYS_UPDATE -- update amount of free space on all file systems
63777c478bd9Sstevel@tonic-gate **
63787c478bd9Sstevel@tonic-gate **	The FileSys table is used to cache the amount of free space
63797c478bd9Sstevel@tonic-gate **	available on all queue directory file systems.
63807c478bd9Sstevel@tonic-gate **	This function updates the cached information if it has expired.
63817c478bd9Sstevel@tonic-gate **
63827c478bd9Sstevel@tonic-gate **	Parameters:
63837c478bd9Sstevel@tonic-gate **		none.
63847c478bd9Sstevel@tonic-gate **
63857c478bd9Sstevel@tonic-gate **	Returns:
63867c478bd9Sstevel@tonic-gate **		none.
63877c478bd9Sstevel@tonic-gate **
63887c478bd9Sstevel@tonic-gate **	Side Effects:
63897c478bd9Sstevel@tonic-gate **		Updates FileSys table.
63907c478bd9Sstevel@tonic-gate */
63917c478bd9Sstevel@tonic-gate 
63927c478bd9Sstevel@tonic-gate void
63937c478bd9Sstevel@tonic-gate filesys_update()
63947c478bd9Sstevel@tonic-gate {
63957c478bd9Sstevel@tonic-gate 	int i;
63967c478bd9Sstevel@tonic-gate 	long avail, blksize;
63977c478bd9Sstevel@tonic-gate 	time_t now;
63987c478bd9Sstevel@tonic-gate 	static time_t nextupdate = 0;
63997c478bd9Sstevel@tonic-gate 
64007c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
64017c478bd9Sstevel@tonic-gate 	/* only the daemon updates this structure */
64027c478bd9Sstevel@tonic-gate 	if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
64037c478bd9Sstevel@tonic-gate 		return;
64047c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
64057c478bd9Sstevel@tonic-gate 	now = curtime();
64067c478bd9Sstevel@tonic-gate 	if (now < nextupdate)
64077c478bd9Sstevel@tonic-gate 		return;
64087c478bd9Sstevel@tonic-gate 	nextupdate = now + FILESYS_UPDATE_INTERVAL;
64097c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
64107c478bd9Sstevel@tonic-gate 	{
64117c478bd9Sstevel@tonic-gate 		FILESYS *fs = &FILE_SYS(i);
64127c478bd9Sstevel@tonic-gate 
64137c478bd9Sstevel@tonic-gate 		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
64147c478bd9Sstevel@tonic-gate 		if (avail < 0 || blksize <= 0)
64157c478bd9Sstevel@tonic-gate 		{
64167c478bd9Sstevel@tonic-gate 			if (LogLevel > 5)
64177c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
64187c478bd9Sstevel@tonic-gate 					"filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
64197c478bd9Sstevel@tonic-gate 					sm_errstring(errno),
64207c478bd9Sstevel@tonic-gate 					FILE_SYS_NAME(i), avail, blksize);
64217c478bd9Sstevel@tonic-gate 			fs->fs_avail = 0;
64227c478bd9Sstevel@tonic-gate 			fs->fs_blksize = 1024; /* avoid divide by zero */
64237c478bd9Sstevel@tonic-gate 			nextupdate = now + 2; /* let's do this soon again */
64247c478bd9Sstevel@tonic-gate 		}
64257c478bd9Sstevel@tonic-gate 		else
64267c478bd9Sstevel@tonic-gate 		{
64277c478bd9Sstevel@tonic-gate 			fs->fs_avail = avail;
64287c478bd9Sstevel@tonic-gate 			fs->fs_blksize = blksize;
64297c478bd9Sstevel@tonic-gate 		}
64307c478bd9Sstevel@tonic-gate 	}
64317c478bd9Sstevel@tonic-gate }
64327c478bd9Sstevel@tonic-gate 
64337c478bd9Sstevel@tonic-gate #if _FFR_ANY_FREE_FS
64347c478bd9Sstevel@tonic-gate /*
64357c478bd9Sstevel@tonic-gate **  FILESYS_FREE -- check whether there is at least one fs with enough space.
64367c478bd9Sstevel@tonic-gate **
64377c478bd9Sstevel@tonic-gate **	Parameters:
64387c478bd9Sstevel@tonic-gate **		fsize -- file size in bytes
64397c478bd9Sstevel@tonic-gate **
64407c478bd9Sstevel@tonic-gate **	Returns:
64417c478bd9Sstevel@tonic-gate **		true iff there is one fs with more than fsize bytes free.
64427c478bd9Sstevel@tonic-gate */
64437c478bd9Sstevel@tonic-gate 
64447c478bd9Sstevel@tonic-gate bool
64457c478bd9Sstevel@tonic-gate filesys_free(fsize)
64467c478bd9Sstevel@tonic-gate 	long fsize;
64477c478bd9Sstevel@tonic-gate {
64487c478bd9Sstevel@tonic-gate 	int i;
64497c478bd9Sstevel@tonic-gate 
64507c478bd9Sstevel@tonic-gate 	if (fsize <= 0)
64517c478bd9Sstevel@tonic-gate 		return true;
64527c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
64537c478bd9Sstevel@tonic-gate 	{
64547c478bd9Sstevel@tonic-gate 		long needed = 0;
64557c478bd9Sstevel@tonic-gate 
64567c478bd9Sstevel@tonic-gate 		if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
64577c478bd9Sstevel@tonic-gate 			continue;
64587c478bd9Sstevel@tonic-gate 		needed += fsize / FILE_SYS_BLKSIZE(i)
64597c478bd9Sstevel@tonic-gate 			  + ((fsize % FILE_SYS_BLKSIZE(i)
64607c478bd9Sstevel@tonic-gate 			      > 0) ? 1 : 0)
64617c478bd9Sstevel@tonic-gate 			  + MinBlocksFree;
64627c478bd9Sstevel@tonic-gate 		if (needed <= FILE_SYS_AVAIL(i))
64637c478bd9Sstevel@tonic-gate 			return true;
64647c478bd9Sstevel@tonic-gate 	}
64657c478bd9Sstevel@tonic-gate 	return false;
64667c478bd9Sstevel@tonic-gate }
64677c478bd9Sstevel@tonic-gate #endif /* _FFR_ANY_FREE_FS */
64687c478bd9Sstevel@tonic-gate 
64697c478bd9Sstevel@tonic-gate #if _FFR_CONTROL_MSTAT
64707c478bd9Sstevel@tonic-gate /*
64717c478bd9Sstevel@tonic-gate **  DISK_STATUS -- show amount of free space in queue directories
64727c478bd9Sstevel@tonic-gate **
64737c478bd9Sstevel@tonic-gate **	Parameters:
64747c478bd9Sstevel@tonic-gate **		out -- output file pointer.
64757c478bd9Sstevel@tonic-gate **		prefix -- string to output in front of each line.
64767c478bd9Sstevel@tonic-gate **
64777c478bd9Sstevel@tonic-gate **	Returns:
64787c478bd9Sstevel@tonic-gate **		none.
64797c478bd9Sstevel@tonic-gate */
64807c478bd9Sstevel@tonic-gate 
64817c478bd9Sstevel@tonic-gate void
64827c478bd9Sstevel@tonic-gate disk_status(out, prefix)
64837c478bd9Sstevel@tonic-gate 	SM_FILE_T *out;
64847c478bd9Sstevel@tonic-gate 	char *prefix;
64857c478bd9Sstevel@tonic-gate {
64867c478bd9Sstevel@tonic-gate 	int i;
64877c478bd9Sstevel@tonic-gate 	long avail, blksize;
64887c478bd9Sstevel@tonic-gate 	long free;
64897c478bd9Sstevel@tonic-gate 
64907c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
64917c478bd9Sstevel@tonic-gate 	{
64927c478bd9Sstevel@tonic-gate 		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
64937c478bd9Sstevel@tonic-gate 		if (avail >= 0 && blksize > 0)
64947c478bd9Sstevel@tonic-gate 		{
64957c478bd9Sstevel@tonic-gate 			free = (long)((double) avail *
64967c478bd9Sstevel@tonic-gate 				((double) blksize / 1024));
64977c478bd9Sstevel@tonic-gate 		}
64987c478bd9Sstevel@tonic-gate 		else
64997c478bd9Sstevel@tonic-gate 			free = -1;
65007c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
65017c478bd9Sstevel@tonic-gate 				"%s%d/%s/%ld\r\n",
65027c478bd9Sstevel@tonic-gate 				prefix, i,
65037c478bd9Sstevel@tonic-gate 				FILE_SYS_NAME(i),
65047c478bd9Sstevel@tonic-gate 					free);
65057c478bd9Sstevel@tonic-gate 	}
65067c478bd9Sstevel@tonic-gate }
65077c478bd9Sstevel@tonic-gate #endif /* _FFR_CONTROL_MSTAT */
65087c478bd9Sstevel@tonic-gate 
65097c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
65107c478bd9Sstevel@tonic-gate 
65117c478bd9Sstevel@tonic-gate /*
65127c478bd9Sstevel@tonic-gate **  INIT_SEM -- initialize semaphore system
65137c478bd9Sstevel@tonic-gate **
65147c478bd9Sstevel@tonic-gate **	Parameters:
65157c478bd9Sstevel@tonic-gate **		owner -- is this the owner of semaphores?
65167c478bd9Sstevel@tonic-gate **
65177c478bd9Sstevel@tonic-gate **	Returns:
65187c478bd9Sstevel@tonic-gate **		none.
65197c478bd9Sstevel@tonic-gate */
65207c478bd9Sstevel@tonic-gate 
65217c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
65227c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
65237c478bd9Sstevel@tonic-gate static int SemId = -1;		/* Semaphore Id */
65247c478bd9Sstevel@tonic-gate int SemKey = SM_SEM_KEY;
65257c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
65267c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
65277c478bd9Sstevel@tonic-gate 
65287c478bd9Sstevel@tonic-gate static void init_sem __P((bool));
65297c478bd9Sstevel@tonic-gate 
65307c478bd9Sstevel@tonic-gate static void
65317c478bd9Sstevel@tonic-gate init_sem(owner)
65327c478bd9Sstevel@tonic-gate 	bool owner;
65337c478bd9Sstevel@tonic-gate {
65347c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
65357c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
65367c478bd9Sstevel@tonic-gate 	SemId = sm_sem_start(SemKey, 1, 0, owner);
65377c478bd9Sstevel@tonic-gate 	if (SemId < 0)
65387c478bd9Sstevel@tonic-gate 	{
65397c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID,
65407c478bd9Sstevel@tonic-gate 			"func=init_sem, sem_key=%ld, sm_sem_start=%d",
65417c478bd9Sstevel@tonic-gate 			(long) SemKey, SemId);
65427c478bd9Sstevel@tonic-gate 		return;
65437c478bd9Sstevel@tonic-gate 	}
65447c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
65457c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
65467c478bd9Sstevel@tonic-gate 	return;
65477c478bd9Sstevel@tonic-gate }
65487c478bd9Sstevel@tonic-gate 
65497c478bd9Sstevel@tonic-gate /*
65507c478bd9Sstevel@tonic-gate **  STOP_SEM -- stop semaphore system
65517c478bd9Sstevel@tonic-gate **
65527c478bd9Sstevel@tonic-gate **	Parameters:
65537c478bd9Sstevel@tonic-gate **		owner -- is this the owner of semaphores?
65547c478bd9Sstevel@tonic-gate **
65557c478bd9Sstevel@tonic-gate **	Returns:
65567c478bd9Sstevel@tonic-gate **		none.
65577c478bd9Sstevel@tonic-gate */
65587c478bd9Sstevel@tonic-gate 
65597c478bd9Sstevel@tonic-gate static void stop_sem __P((bool));
65607c478bd9Sstevel@tonic-gate 
65617c478bd9Sstevel@tonic-gate static void
65627c478bd9Sstevel@tonic-gate stop_sem(owner)
65637c478bd9Sstevel@tonic-gate 	bool owner;
65647c478bd9Sstevel@tonic-gate {
65657c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
65667c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
65677c478bd9Sstevel@tonic-gate 	if (owner && SemId >= 0)
65687c478bd9Sstevel@tonic-gate 		sm_sem_stop(SemId);
65697c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
65707c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
65717c478bd9Sstevel@tonic-gate 	return;
65727c478bd9Sstevel@tonic-gate }
65737c478bd9Sstevel@tonic-gate 
65747c478bd9Sstevel@tonic-gate /*
65757c478bd9Sstevel@tonic-gate **  UPD_QS -- update information about queue when adding/deleting an entry
65767c478bd9Sstevel@tonic-gate **
65777c478bd9Sstevel@tonic-gate **	Parameters:
65787c478bd9Sstevel@tonic-gate **		e -- envelope.
65797c478bd9Sstevel@tonic-gate **		count -- add/remove entry (+1/0/-1: add/no change/remove)
65807c478bd9Sstevel@tonic-gate **		space -- update the space available as well.
65817c478bd9Sstevel@tonic-gate **			(>0/0/<0: add/no change/remove)
65827c478bd9Sstevel@tonic-gate **		where -- caller (for logging)
65837c478bd9Sstevel@tonic-gate **
65847c478bd9Sstevel@tonic-gate **	Returns:
65857c478bd9Sstevel@tonic-gate **		none.
65867c478bd9Sstevel@tonic-gate **
65877c478bd9Sstevel@tonic-gate **	Side Effects:
65887c478bd9Sstevel@tonic-gate **		Modifies available space in filesystem.
65897c478bd9Sstevel@tonic-gate **		Changes number of entries in queue directory.
65907c478bd9Sstevel@tonic-gate */
65917c478bd9Sstevel@tonic-gate 
65927c478bd9Sstevel@tonic-gate void
65937c478bd9Sstevel@tonic-gate upd_qs(e, count, space, where)
65947c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
65957c478bd9Sstevel@tonic-gate 	int count;
65967c478bd9Sstevel@tonic-gate 	int space;
65977c478bd9Sstevel@tonic-gate 	char *where;
65987c478bd9Sstevel@tonic-gate {
65997c478bd9Sstevel@tonic-gate 	short fidx;
66007c478bd9Sstevel@tonic-gate 	int idx;
66017c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66027c478bd9Sstevel@tonic-gate 	int r;
66037c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66047c478bd9Sstevel@tonic-gate 	long s;
66057c478bd9Sstevel@tonic-gate 
66067c478bd9Sstevel@tonic-gate 	if (ShmId == SM_SHM_NO_ID || e == NULL)
66077c478bd9Sstevel@tonic-gate 		return;
66087c478bd9Sstevel@tonic-gate 	if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
66097c478bd9Sstevel@tonic-gate 		return;
66107c478bd9Sstevel@tonic-gate 	idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
66117c478bd9Sstevel@tonic-gate 	if (tTd(73,2))
66127c478bd9Sstevel@tonic-gate 		sm_dprintf("func=upd_qs, count=%d, space=%d, where=%s, idx=%d, entries=%d\n",
66137c478bd9Sstevel@tonic-gate 			count, space, where, idx, QSHM_ENTRIES(idx));
66147c478bd9Sstevel@tonic-gate 
66157c478bd9Sstevel@tonic-gate 	/* XXX in theory this needs to be protected with a mutex */
66167c478bd9Sstevel@tonic-gate 	if (QSHM_ENTRIES(idx) >= 0 && count != 0)
66177c478bd9Sstevel@tonic-gate 	{
66187c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66197c478bd9Sstevel@tonic-gate 		r = sm_sem_acq(SemId, 0, 1);
66207c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66217c478bd9Sstevel@tonic-gate 		QSHM_ENTRIES(idx) += count;
66227c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66237c478bd9Sstevel@tonic-gate 		if (r >= 0)
66247c478bd9Sstevel@tonic-gate 			r = sm_sem_rel(SemId, 0, 1);
66257c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66267c478bd9Sstevel@tonic-gate 	}
66277c478bd9Sstevel@tonic-gate 
66287c478bd9Sstevel@tonic-gate 	fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
66297c478bd9Sstevel@tonic-gate 	if (fidx < 0)
66307c478bd9Sstevel@tonic-gate 		return;
66317c478bd9Sstevel@tonic-gate 
66327c478bd9Sstevel@tonic-gate 	/* update available space also?  (might be loseqfile) */
66337c478bd9Sstevel@tonic-gate 	if (space == 0)
66347c478bd9Sstevel@tonic-gate 		return;
66357c478bd9Sstevel@tonic-gate 
66367c478bd9Sstevel@tonic-gate 	/* convert size to blocks; this causes rounding errors */
66377c478bd9Sstevel@tonic-gate 	s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
66387c478bd9Sstevel@tonic-gate 	if (s == 0)
66397c478bd9Sstevel@tonic-gate 		return;
66407c478bd9Sstevel@tonic-gate 
66417c478bd9Sstevel@tonic-gate 	/* XXX in theory this needs to be protected with a mutex */
66427c478bd9Sstevel@tonic-gate 	if (space > 0)
66437c478bd9Sstevel@tonic-gate 		FILE_SYS_AVAIL(fidx) += s;
66447c478bd9Sstevel@tonic-gate 	else
66457c478bd9Sstevel@tonic-gate 		FILE_SYS_AVAIL(fidx) -= s;
66467c478bd9Sstevel@tonic-gate 
66477c478bd9Sstevel@tonic-gate }
66487c478bd9Sstevel@tonic-gate 
66497c478bd9Sstevel@tonic-gate #if _FFR_SELECT_SHM
66507c478bd9Sstevel@tonic-gate 
66517c478bd9Sstevel@tonic-gate static bool write_key_file __P((char *, long));
66527c478bd9Sstevel@tonic-gate static long read_key_file __P((char *, long));
66537c478bd9Sstevel@tonic-gate 
66547c478bd9Sstevel@tonic-gate /*
66557c478bd9Sstevel@tonic-gate **  WRITE_KEY_FILE -- record some key into a file.
66567c478bd9Sstevel@tonic-gate **
66577c478bd9Sstevel@tonic-gate **	Parameters:
66587c478bd9Sstevel@tonic-gate **		keypath -- file name.
66597c478bd9Sstevel@tonic-gate **		key -- key to write.
66607c478bd9Sstevel@tonic-gate **
66617c478bd9Sstevel@tonic-gate **	Returns:
66627c478bd9Sstevel@tonic-gate **		true iff file could be written.
66637c478bd9Sstevel@tonic-gate **
66647c478bd9Sstevel@tonic-gate **	Side Effects:
66657c478bd9Sstevel@tonic-gate **		writes file.
66667c478bd9Sstevel@tonic-gate */
66677c478bd9Sstevel@tonic-gate 
66687c478bd9Sstevel@tonic-gate static bool
66697c478bd9Sstevel@tonic-gate write_key_file(keypath, key)
66707c478bd9Sstevel@tonic-gate 	char *keypath;
66717c478bd9Sstevel@tonic-gate 	long key;
66727c478bd9Sstevel@tonic-gate {
66737c478bd9Sstevel@tonic-gate 	bool ok;
66747c478bd9Sstevel@tonic-gate 	long sff;
66757c478bd9Sstevel@tonic-gate 	SM_FILE_T *keyf;
66767c478bd9Sstevel@tonic-gate 
66777c478bd9Sstevel@tonic-gate 	ok = false;
66787c478bd9Sstevel@tonic-gate 	if (keypath == NULL || *keypath == '\0')
66797c478bd9Sstevel@tonic-gate 		return ok;
66807c478bd9Sstevel@tonic-gate 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
66817c478bd9Sstevel@tonic-gate 	if (TrustedUid != 0 && RealUid == TrustedUid)
66827c478bd9Sstevel@tonic-gate 		sff |= SFF_OPENASROOT;
66837c478bd9Sstevel@tonic-gate 	keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff);
66847c478bd9Sstevel@tonic-gate 	if (keyf == NULL)
66857c478bd9Sstevel@tonic-gate 	{
66867c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
66877c478bd9Sstevel@tonic-gate 			  keypath, sm_errstring(errno));
66887c478bd9Sstevel@tonic-gate 	}
66897c478bd9Sstevel@tonic-gate 	else
66907c478bd9Sstevel@tonic-gate 	{
669149218d4fSjbeck 		if (geteuid() == 0 && RunAsUid != 0)
669249218d4fSjbeck 		{
669349218d4fSjbeck #  if HASFCHOWN
669449218d4fSjbeck 			int fd;
669549218d4fSjbeck 
669649218d4fSjbeck 			fd = keyf->f_file;
669749218d4fSjbeck 			if (fd >= 0 && fchown(fd, RunAsUid, -1) < 0)
669849218d4fSjbeck 			{
669949218d4fSjbeck 				int err = errno;
670049218d4fSjbeck 
670149218d4fSjbeck 				sm_syslog(LOG_ALERT, NOQID,
670249218d4fSjbeck 					  "ownership change on %s to %d failed: %s",
670349218d4fSjbeck 					  keypath, RunAsUid, sm_errstring(err));
670449218d4fSjbeck 			}
670549218d4fSjbeck #  endif /* HASFCHOWN */
670649218d4fSjbeck 		}
67077c478bd9Sstevel@tonic-gate 		ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
67087c478bd9Sstevel@tonic-gate 		     SM_IO_EOF;
67097c478bd9Sstevel@tonic-gate 		ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok;
67107c478bd9Sstevel@tonic-gate 	}
67117c478bd9Sstevel@tonic-gate 	return ok;
67127c478bd9Sstevel@tonic-gate }
67137c478bd9Sstevel@tonic-gate 
67147c478bd9Sstevel@tonic-gate /*
67157c478bd9Sstevel@tonic-gate **  READ_KEY_FILE -- read a key from a file.
67167c478bd9Sstevel@tonic-gate **
67177c478bd9Sstevel@tonic-gate **	Parameters:
67187c478bd9Sstevel@tonic-gate **		keypath -- file name.
67197c478bd9Sstevel@tonic-gate **		key -- default key.
67207c478bd9Sstevel@tonic-gate **
67217c478bd9Sstevel@tonic-gate **	Returns:
67227c478bd9Sstevel@tonic-gate **		key.
67237c478bd9Sstevel@tonic-gate */
67247c478bd9Sstevel@tonic-gate 
67257c478bd9Sstevel@tonic-gate static long
67267c478bd9Sstevel@tonic-gate read_key_file(keypath, key)
67277c478bd9Sstevel@tonic-gate 	char *keypath;
67287c478bd9Sstevel@tonic-gate 	long key;
67297c478bd9Sstevel@tonic-gate {
67307c478bd9Sstevel@tonic-gate 	int r;
67317c478bd9Sstevel@tonic-gate 	long sff, n;
67327c478bd9Sstevel@tonic-gate 	SM_FILE_T *keyf;
67337c478bd9Sstevel@tonic-gate 
67347c478bd9Sstevel@tonic-gate 	if (keypath == NULL || *keypath == '\0')
67357c478bd9Sstevel@tonic-gate 		return key;
67367c478bd9Sstevel@tonic-gate 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
67377c478bd9Sstevel@tonic-gate 	if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid))
67387c478bd9Sstevel@tonic-gate 		sff |= SFF_OPENASROOT;
67397c478bd9Sstevel@tonic-gate 	keyf = safefopen(keypath, O_RDONLY, FileMode, sff);
67407c478bd9Sstevel@tonic-gate 	if (keyf == NULL)
67417c478bd9Sstevel@tonic-gate 	{
67427c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s",
67437c478bd9Sstevel@tonic-gate 			  keypath, sm_errstring(errno));
67447c478bd9Sstevel@tonic-gate 	}
67457c478bd9Sstevel@tonic-gate 	else
67467c478bd9Sstevel@tonic-gate 	{
67477c478bd9Sstevel@tonic-gate 		r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n);
67487c478bd9Sstevel@tonic-gate 		if (r == 1)
67497c478bd9Sstevel@tonic-gate 			key = n;
67507c478bd9Sstevel@tonic-gate 		(void) sm_io_close(keyf, SM_TIME_DEFAULT);
67517c478bd9Sstevel@tonic-gate 	}
67527c478bd9Sstevel@tonic-gate 	return key;
67537c478bd9Sstevel@tonic-gate }
67547c478bd9Sstevel@tonic-gate #endif /* _FFR_SELECT_SHM */
67557c478bd9Sstevel@tonic-gate 
67567c478bd9Sstevel@tonic-gate /*
67577c478bd9Sstevel@tonic-gate **  INIT_SHM -- initialize shared memory structure
67587c478bd9Sstevel@tonic-gate **
67597c478bd9Sstevel@tonic-gate **	Initialize or attach to shared memory segment.
67607c478bd9Sstevel@tonic-gate **	Currently it is not a fatal error if this doesn't work.
67617c478bd9Sstevel@tonic-gate **	However, it causes us to have a "fallback" storage location
67627c478bd9Sstevel@tonic-gate **	for everything that is supposed to be in the shared memory,
67637c478bd9Sstevel@tonic-gate **	which makes the code slightly ugly.
67647c478bd9Sstevel@tonic-gate **
67657c478bd9Sstevel@tonic-gate **	Parameters:
67667c478bd9Sstevel@tonic-gate **		qn -- number of queue directories.
67677c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory.
67687c478bd9Sstevel@tonic-gate **		hash -- identifies data that is stored in shared memory.
67697c478bd9Sstevel@tonic-gate **
67707c478bd9Sstevel@tonic-gate **	Returns:
67717c478bd9Sstevel@tonic-gate **		none.
67727c478bd9Sstevel@tonic-gate */
67737c478bd9Sstevel@tonic-gate 
67747c478bd9Sstevel@tonic-gate static void init_shm __P((int, bool, unsigned int));
67757c478bd9Sstevel@tonic-gate 
67767c478bd9Sstevel@tonic-gate static void
67777c478bd9Sstevel@tonic-gate init_shm(qn, owner, hash)
67787c478bd9Sstevel@tonic-gate 	int qn;
67797c478bd9Sstevel@tonic-gate 	bool owner;
67807c478bd9Sstevel@tonic-gate 	unsigned int hash;
67817c478bd9Sstevel@tonic-gate {
67827c478bd9Sstevel@tonic-gate 	int i;
67837c478bd9Sstevel@tonic-gate 	int count;
67847c478bd9Sstevel@tonic-gate 	int save_errno;
67857c478bd9Sstevel@tonic-gate #if _FFR_SELECT_SHM
67867c478bd9Sstevel@tonic-gate 	bool keyselect;
67877c478bd9Sstevel@tonic-gate #endif /* _FFR_SELECT_SHM */
67887c478bd9Sstevel@tonic-gate 
67897c478bd9Sstevel@tonic-gate 	PtrFileSys = &FileSys[0];
67907c478bd9Sstevel@tonic-gate 	PNumFileSys = &Numfilesys;
67917c478bd9Sstevel@tonic-gate #if _FFR_SELECT_SHM
67927c478bd9Sstevel@tonic-gate /* if this "key" is specified: select one yourself */
67937c478bd9Sstevel@tonic-gate # define SEL_SHM_KEY	((key_t) -1)
67947c478bd9Sstevel@tonic-gate # define FIRST_SHM_KEY	25
67957c478bd9Sstevel@tonic-gate #endif /* _FFR_SELECT_SHM */
67967c478bd9Sstevel@tonic-gate 
67977c478bd9Sstevel@tonic-gate 	/* This allows us to disable shared memory at runtime. */
67987c478bd9Sstevel@tonic-gate 	if (ShmKey == 0)
67997c478bd9Sstevel@tonic-gate 		return;
68007c478bd9Sstevel@tonic-gate 
68017c478bd9Sstevel@tonic-gate 	count = 0;
68027c478bd9Sstevel@tonic-gate 	shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
68037c478bd9Sstevel@tonic-gate #if _FFR_SELECT_SHM
68047c478bd9Sstevel@tonic-gate 	keyselect = ShmKey == SEL_SHM_KEY;
68057c478bd9Sstevel@tonic-gate 	if (keyselect)
68067c478bd9Sstevel@tonic-gate 	{
68077c478bd9Sstevel@tonic-gate 		if (owner)
68087c478bd9Sstevel@tonic-gate 			ShmKey = FIRST_SHM_KEY;
68097c478bd9Sstevel@tonic-gate 		else
68107c478bd9Sstevel@tonic-gate 		{
68117c478bd9Sstevel@tonic-gate 			ShmKey = read_key_file(ShmKeyFile, ShmKey);
68127c478bd9Sstevel@tonic-gate 			keyselect = false;
68137c478bd9Sstevel@tonic-gate 			if (ShmKey == SEL_SHM_KEY)
68147c478bd9Sstevel@tonic-gate 				goto error;
68157c478bd9Sstevel@tonic-gate 		}
68167c478bd9Sstevel@tonic-gate 	}
68177c478bd9Sstevel@tonic-gate #endif /* _FFR_SELECT_SHM */
68187c478bd9Sstevel@tonic-gate 	for (;;)
68197c478bd9Sstevel@tonic-gate 	{
68207c478bd9Sstevel@tonic-gate 		/* allow read/write access for group? */
68217c478bd9Sstevel@tonic-gate 		Pshm = sm_shmstart(ShmKey, shms,
68227c478bd9Sstevel@tonic-gate 				SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3),
68237c478bd9Sstevel@tonic-gate 				&ShmId, owner);
68247c478bd9Sstevel@tonic-gate 		save_errno = errno;
68257c478bd9Sstevel@tonic-gate 		if (Pshm != NULL || !sm_file_exists(save_errno))
68267c478bd9Sstevel@tonic-gate 			break;
68277c478bd9Sstevel@tonic-gate 		if (++count >= 3)
68287c478bd9Sstevel@tonic-gate 		{
68297c478bd9Sstevel@tonic-gate #if _FFR_SELECT_SHM
68307c478bd9Sstevel@tonic-gate 			if (keyselect)
68317c478bd9Sstevel@tonic-gate 			{
68327c478bd9Sstevel@tonic-gate 				++ShmKey;
68337c478bd9Sstevel@tonic-gate 
68347c478bd9Sstevel@tonic-gate 				/* back where we started? */
68357c478bd9Sstevel@tonic-gate 				if (ShmKey == SEL_SHM_KEY)
68367c478bd9Sstevel@tonic-gate 					break;
68377c478bd9Sstevel@tonic-gate 				continue;
68387c478bd9Sstevel@tonic-gate 			}
68397c478bd9Sstevel@tonic-gate #endif /* _FFR_SELECT_SHM */
68407c478bd9Sstevel@tonic-gate 			break;
68417c478bd9Sstevel@tonic-gate 		}
68427c478bd9Sstevel@tonic-gate #if _FFR_SELECT_SHM
68437c478bd9Sstevel@tonic-gate 		/* only sleep if we are at the first key */
68447c478bd9Sstevel@tonic-gate 		if (!keyselect || ShmKey == SEL_SHM_KEY)
68457c478bd9Sstevel@tonic-gate #endif /* _FFR_SELECT_SHM */
68467c478bd9Sstevel@tonic-gate 		sleep(count);
68477c478bd9Sstevel@tonic-gate 	}
68487c478bd9Sstevel@tonic-gate 	if (Pshm != NULL)
68497c478bd9Sstevel@tonic-gate 	{
68507c478bd9Sstevel@tonic-gate 		int *p;
68517c478bd9Sstevel@tonic-gate 
68527c478bd9Sstevel@tonic-gate #if _FFR_SELECT_SHM
68537c478bd9Sstevel@tonic-gate 		if (keyselect)
68547c478bd9Sstevel@tonic-gate 			(void) write_key_file(ShmKeyFile, (long) ShmKey);
68557c478bd9Sstevel@tonic-gate #endif /* _FFR_SELECT_SHM */
68567c478bd9Sstevel@tonic-gate 		if (owner && RunAsUid != 0)
68577c478bd9Sstevel@tonic-gate 		{
6858*445f2479Sjbeck 			i = sm_shmsetowner(ShmId, RunAsUid, RunAsGid, 0660);
68597c478bd9Sstevel@tonic-gate 			if (i != 0)
68607c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
68617c478bd9Sstevel@tonic-gate 					"key=%ld, sm_shmsetowner=%d, RunAsUid=%d, RunAsGid=%d",
6862*445f2479Sjbeck 					(long) ShmKey, i, RunAsUid, RunAsGid);
68637c478bd9Sstevel@tonic-gate 		}
68647c478bd9Sstevel@tonic-gate 		p = (int *) Pshm;
68657c478bd9Sstevel@tonic-gate 		if (owner)
68667c478bd9Sstevel@tonic-gate 		{
68677c478bd9Sstevel@tonic-gate 			*p = (int) shms;
68687c478bd9Sstevel@tonic-gate 			*((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
68697c478bd9Sstevel@tonic-gate 			p = (int *) SHM_OFF_TAG(Pshm);
68707c478bd9Sstevel@tonic-gate 			*p = hash;
68717c478bd9Sstevel@tonic-gate 		}
68727c478bd9Sstevel@tonic-gate 		else
68737c478bd9Sstevel@tonic-gate 		{
68747c478bd9Sstevel@tonic-gate 			if (*p != (int) shms)
68757c478bd9Sstevel@tonic-gate 			{
68767c478bd9Sstevel@tonic-gate 				save_errno = EINVAL;
68777c478bd9Sstevel@tonic-gate 				cleanup_shm(false);
68787c478bd9Sstevel@tonic-gate 				goto error;
68797c478bd9Sstevel@tonic-gate 			}
68807c478bd9Sstevel@tonic-gate 			p = (int *) SHM_OFF_TAG(Pshm);
68817c478bd9Sstevel@tonic-gate 			if (*p != (int) hash)
68827c478bd9Sstevel@tonic-gate 			{
68837c478bd9Sstevel@tonic-gate 				save_errno = EINVAL;
68847c478bd9Sstevel@tonic-gate 				cleanup_shm(false);
68857c478bd9Sstevel@tonic-gate 				goto error;
68867c478bd9Sstevel@tonic-gate 			}
68877c478bd9Sstevel@tonic-gate 
68887c478bd9Sstevel@tonic-gate 			/*
68897c478bd9Sstevel@tonic-gate 			**  XXX how to check the pid?
68907c478bd9Sstevel@tonic-gate 			**  Read it from the pid-file? That does
68917c478bd9Sstevel@tonic-gate 			**  not need to exist.
68927c478bd9Sstevel@tonic-gate 			**  We could disable shm if we can't confirm
68937c478bd9Sstevel@tonic-gate 			**  that it is the right one.
68947c478bd9Sstevel@tonic-gate 			*/
68957c478bd9Sstevel@tonic-gate 		}
68967c478bd9Sstevel@tonic-gate 
68977c478bd9Sstevel@tonic-gate 		PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
68987c478bd9Sstevel@tonic-gate 		PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
68997c478bd9Sstevel@tonic-gate 		QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
69007c478bd9Sstevel@tonic-gate 		PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
69017c478bd9Sstevel@tonic-gate 		*PRSATmpCnt = 0;
69027c478bd9Sstevel@tonic-gate 		if (owner)
69037c478bd9Sstevel@tonic-gate 		{
69047c478bd9Sstevel@tonic-gate 			/* initialize values in shared memory */
69057c478bd9Sstevel@tonic-gate 			NumFileSys = 0;
69067c478bd9Sstevel@tonic-gate 			for (i = 0; i < qn; i++)
69077c478bd9Sstevel@tonic-gate 				QShm[i].qs_entries = -1;
69087c478bd9Sstevel@tonic-gate 		}
69097c478bd9Sstevel@tonic-gate 		init_sem(owner);
69107c478bd9Sstevel@tonic-gate 		return;
69117c478bd9Sstevel@tonic-gate 	}
69127c478bd9Sstevel@tonic-gate   error:
69137c478bd9Sstevel@tonic-gate 	if (LogLevel > (owner ? 8 : 11))
69147c478bd9Sstevel@tonic-gate 	{
69157c478bd9Sstevel@tonic-gate 		sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
69167c478bd9Sstevel@tonic-gate 			  "can't %s shared memory, key=%ld: %s",
69177c478bd9Sstevel@tonic-gate 			  owner ? "initialize" : "attach to",
69187c478bd9Sstevel@tonic-gate 			  (long) ShmKey, sm_errstring(save_errno));
69197c478bd9Sstevel@tonic-gate 	}
69207c478bd9Sstevel@tonic-gate }
69217c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
69227c478bd9Sstevel@tonic-gate 
69237c478bd9Sstevel@tonic-gate 
69247c478bd9Sstevel@tonic-gate /*
69257c478bd9Sstevel@tonic-gate **  SETUP_QUEUES -- setup all queue groups
69267c478bd9Sstevel@tonic-gate **
69277c478bd9Sstevel@tonic-gate **	Parameters:
69287c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory.
69297c478bd9Sstevel@tonic-gate **
69307c478bd9Sstevel@tonic-gate **	Returns:
69317c478bd9Sstevel@tonic-gate **		none.
69327c478bd9Sstevel@tonic-gate **
69337c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
69347c478bd9Sstevel@tonic-gate **	Side Effects:
69357c478bd9Sstevel@tonic-gate **		attaches shared memory.
69367c478bd9Sstevel@tonic-gate #endif * SM_CONF_SHM *
69377c478bd9Sstevel@tonic-gate */
69387c478bd9Sstevel@tonic-gate 
69397c478bd9Sstevel@tonic-gate void
69407c478bd9Sstevel@tonic-gate setup_queues(owner)
69417c478bd9Sstevel@tonic-gate 	bool owner;
69427c478bd9Sstevel@tonic-gate {
69437c478bd9Sstevel@tonic-gate 	int i, qn, len;
69447c478bd9Sstevel@tonic-gate 	unsigned int hashval;
69457c478bd9Sstevel@tonic-gate 	time_t now;
69467c478bd9Sstevel@tonic-gate 	char basedir[MAXPATHLEN];
69477c478bd9Sstevel@tonic-gate 	struct stat st;
69487c478bd9Sstevel@tonic-gate 
69497c478bd9Sstevel@tonic-gate 	/*
69507c478bd9Sstevel@tonic-gate 	**  Determine basedir for all queue directories.
69517c478bd9Sstevel@tonic-gate 	**  All queue directories must be (first level) subdirectories
69527c478bd9Sstevel@tonic-gate 	**  of the basedir.  The basedir is the QueueDir
69537c478bd9Sstevel@tonic-gate 	**  without wildcards, but with trailing /
69547c478bd9Sstevel@tonic-gate 	*/
69557c478bd9Sstevel@tonic-gate 
69567c478bd9Sstevel@tonic-gate 	hashval = 0;
69577c478bd9Sstevel@tonic-gate 	errno = 0;
69587c478bd9Sstevel@tonic-gate 	len = sm_strlcpy(basedir, QueueDir, sizeof basedir);
69597c478bd9Sstevel@tonic-gate 
69607c478bd9Sstevel@tonic-gate 	/* Provide space for trailing '/' */
69617c478bd9Sstevel@tonic-gate 	if (len >= sizeof basedir - 1)
69627c478bd9Sstevel@tonic-gate 	{
69637c478bd9Sstevel@tonic-gate 		syserr("QueueDirectory: path too long: %d,  max %d",
69647c478bd9Sstevel@tonic-gate 			len, (int) sizeof basedir - 1);
69657c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
69667c478bd9Sstevel@tonic-gate 		return;
69677c478bd9Sstevel@tonic-gate 	}
69687c478bd9Sstevel@tonic-gate 	SM_ASSERT(len > 0);
69697c478bd9Sstevel@tonic-gate 	if (basedir[len - 1] == '*')
69707c478bd9Sstevel@tonic-gate 	{
69717c478bd9Sstevel@tonic-gate 		char *cp;
69727c478bd9Sstevel@tonic-gate 
69737c478bd9Sstevel@tonic-gate 		cp = SM_LAST_DIR_DELIM(basedir);
69747c478bd9Sstevel@tonic-gate 		if (cp == NULL)
69757c478bd9Sstevel@tonic-gate 		{
69767c478bd9Sstevel@tonic-gate 			syserr("QueueDirectory: can not wildcard relative path \"%s\"",
69777c478bd9Sstevel@tonic-gate 				QueueDir);
69787c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
69797c478bd9Sstevel@tonic-gate 				sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
69807c478bd9Sstevel@tonic-gate 					QueueDir);
69817c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
69827c478bd9Sstevel@tonic-gate 			return;
69837c478bd9Sstevel@tonic-gate 		}
69847c478bd9Sstevel@tonic-gate 
69857c478bd9Sstevel@tonic-gate 		/* cut off wildcard pattern */
69867c478bd9Sstevel@tonic-gate 		*++cp = '\0';
69877c478bd9Sstevel@tonic-gate 		len = cp - basedir;
69887c478bd9Sstevel@tonic-gate 	}
69897c478bd9Sstevel@tonic-gate 	else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
69907c478bd9Sstevel@tonic-gate 	{
69917c478bd9Sstevel@tonic-gate 		/* append trailing slash since it is a directory */
69927c478bd9Sstevel@tonic-gate 		basedir[len] = '/';
69937c478bd9Sstevel@tonic-gate 		basedir[++len] = '\0';
69947c478bd9Sstevel@tonic-gate 	}
69957c478bd9Sstevel@tonic-gate 
69967c478bd9Sstevel@tonic-gate 	/* len counts up to the last directory delimiter */
69977c478bd9Sstevel@tonic-gate 	SM_ASSERT(basedir[len - 1] == '/');
69987c478bd9Sstevel@tonic-gate 
69997c478bd9Sstevel@tonic-gate 	if (chdir(basedir) < 0)
70007c478bd9Sstevel@tonic-gate 	{
70017c478bd9Sstevel@tonic-gate 		int save_errno = errno;
70027c478bd9Sstevel@tonic-gate 
70037c478bd9Sstevel@tonic-gate 		syserr("can not chdir(%s)", basedir);
70047c478bd9Sstevel@tonic-gate 		if (save_errno == EACCES)
70057c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
70067c478bd9Sstevel@tonic-gate 				"Program mode requires special privileges, e.g., root or TrustedUser.\n");
70077c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
70087c478bd9Sstevel@tonic-gate 			sm_dprintf("setup_queues: \"%s\": %s\n",
70097c478bd9Sstevel@tonic-gate 				   basedir, sm_errstring(errno));
70107c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
70117c478bd9Sstevel@tonic-gate 		return;
70127c478bd9Sstevel@tonic-gate 	}
70137c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
70147c478bd9Sstevel@tonic-gate 	hashval = hash_q(basedir, hashval);
70157c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
70167c478bd9Sstevel@tonic-gate 
70177c478bd9Sstevel@tonic-gate 	/* initialize for queue runs */
70187c478bd9Sstevel@tonic-gate 	DoQueueRun = false;
70197c478bd9Sstevel@tonic-gate 	now = curtime();
70207c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
70217c478bd9Sstevel@tonic-gate 		Queue[i]->qg_nextrun = now;
70227c478bd9Sstevel@tonic-gate 
70237c478bd9Sstevel@tonic-gate 
70247c478bd9Sstevel@tonic-gate 	if (UseMSP && OpMode != MD_TEST)
70257c478bd9Sstevel@tonic-gate 	{
70267c478bd9Sstevel@tonic-gate 		long sff = SFF_CREAT;
70277c478bd9Sstevel@tonic-gate 
70287c478bd9Sstevel@tonic-gate 		if (stat(".", &st) < 0)
70297c478bd9Sstevel@tonic-gate 		{
70307c478bd9Sstevel@tonic-gate 			syserr("can not stat(%s)", basedir);
70317c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
70327c478bd9Sstevel@tonic-gate 				sm_dprintf("setup_queues: \"%s\": %s\n",
70337c478bd9Sstevel@tonic-gate 					   basedir, sm_errstring(errno));
70347c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
70357c478bd9Sstevel@tonic-gate 			return;
70367c478bd9Sstevel@tonic-gate 		}
70377c478bd9Sstevel@tonic-gate 		if (RunAsUid == 0)
70387c478bd9Sstevel@tonic-gate 			sff |= SFF_ROOTOK;
70397c478bd9Sstevel@tonic-gate 
70407c478bd9Sstevel@tonic-gate 		/*
70417c478bd9Sstevel@tonic-gate 		**  Check queue directory permissions.
70427c478bd9Sstevel@tonic-gate 		**	Can we write to a group writable queue directory?
70437c478bd9Sstevel@tonic-gate 		*/
70447c478bd9Sstevel@tonic-gate 
70457c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode) &&
70467c478bd9Sstevel@tonic-gate 		    bitset(S_IWGRP, st.st_mode) &&
70477c478bd9Sstevel@tonic-gate 		    safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
70487c478bd9Sstevel@tonic-gate 			     QueueFileMode, NULL) != 0)
70497c478bd9Sstevel@tonic-gate 		{
70507c478bd9Sstevel@tonic-gate 			syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
70517c478bd9Sstevel@tonic-gate 				basedir, (int) RunAsGid, (int) st.st_gid);
70527c478bd9Sstevel@tonic-gate 		}
70537c478bd9Sstevel@tonic-gate 		if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
70547c478bd9Sstevel@tonic-gate 		{
70557c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
70567c478bd9Sstevel@tonic-gate 			syserr("dangerous permissions=%o on queue directory %s",
70577c478bd9Sstevel@tonic-gate 				(int) st.st_mode, basedir);
70587c478bd9Sstevel@tonic-gate #else /* _FFR_MSP_PARANOIA */
70597c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
70607c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
70617c478bd9Sstevel@tonic-gate 					  "dangerous permissions=%o on queue directory %s",
70627c478bd9Sstevel@tonic-gate 					  (int) st.st_mode, basedir);
70637c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
70647c478bd9Sstevel@tonic-gate 		}
70657c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
70667c478bd9Sstevel@tonic-gate 		if (NumQueue > 1)
70677c478bd9Sstevel@tonic-gate 			syserr("can not use multiple queues for MSP");
70687c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
70697c478bd9Sstevel@tonic-gate 	}
70707c478bd9Sstevel@tonic-gate 
70717c478bd9Sstevel@tonic-gate 	/* initial number of queue directories */
70727c478bd9Sstevel@tonic-gate 	qn = 0;
70737c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
70747c478bd9Sstevel@tonic-gate 		qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
70757c478bd9Sstevel@tonic-gate 
70767c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
70777c478bd9Sstevel@tonic-gate 	init_shm(qn, owner, hashval);
70787c478bd9Sstevel@tonic-gate 	i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
70797c478bd9Sstevel@tonic-gate 	if (i == FSF_NOT_FOUND)
70807c478bd9Sstevel@tonic-gate 	{
70817c478bd9Sstevel@tonic-gate 		/*
70827c478bd9Sstevel@tonic-gate 		**  We didn't get the right filesystem data
70837c478bd9Sstevel@tonic-gate 		**  This may happen if we don't have the right shared memory.
70847c478bd9Sstevel@tonic-gate 		**  So let's do this without shared memory.
70857c478bd9Sstevel@tonic-gate 		*/
70867c478bd9Sstevel@tonic-gate 
70877c478bd9Sstevel@tonic-gate 		SM_ASSERT(!owner);
70887c478bd9Sstevel@tonic-gate 		cleanup_shm(false);	/* release shared memory */
70897c478bd9Sstevel@tonic-gate 		i = filesys_setup(false);
70907c478bd9Sstevel@tonic-gate 		if (i < 0)
70917c478bd9Sstevel@tonic-gate 			syserr("filesys_setup failed twice, result=%d", i);
70927c478bd9Sstevel@tonic-gate 		else if (LogLevel > 8)
70937c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
70947c478bd9Sstevel@tonic-gate 				  "shared memory does not contain expected data, ignored");
70957c478bd9Sstevel@tonic-gate 	}
70967c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
70977c478bd9Sstevel@tonic-gate 	i = filesys_setup(true);
70987c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
70997c478bd9Sstevel@tonic-gate 	if (i < 0)
71007c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
71017c478bd9Sstevel@tonic-gate }
71027c478bd9Sstevel@tonic-gate 
71037c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
71047c478bd9Sstevel@tonic-gate /*
71057c478bd9Sstevel@tonic-gate **  CLEANUP_SHM -- do some cleanup work for shared memory etc
71067c478bd9Sstevel@tonic-gate **
71077c478bd9Sstevel@tonic-gate **	Parameters:
71087c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory?
71097c478bd9Sstevel@tonic-gate **
71107c478bd9Sstevel@tonic-gate **	Returns:
71117c478bd9Sstevel@tonic-gate **		none.
71127c478bd9Sstevel@tonic-gate **
71137c478bd9Sstevel@tonic-gate **	Side Effects:
71147c478bd9Sstevel@tonic-gate **		detaches shared memory.
71157c478bd9Sstevel@tonic-gate */
71167c478bd9Sstevel@tonic-gate 
71177c478bd9Sstevel@tonic-gate void
71187c478bd9Sstevel@tonic-gate cleanup_shm(owner)
71197c478bd9Sstevel@tonic-gate 	bool owner;
71207c478bd9Sstevel@tonic-gate {
71217c478bd9Sstevel@tonic-gate 	if (ShmId != SM_SHM_NO_ID)
71227c478bd9Sstevel@tonic-gate 	{
71237c478bd9Sstevel@tonic-gate 		if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
71247c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s",
71257c478bd9Sstevel@tonic-gate 				  sm_errstring(errno));
71267c478bd9Sstevel@tonic-gate 		Pshm = NULL;
71277c478bd9Sstevel@tonic-gate 		ShmId = SM_SHM_NO_ID;
71287c478bd9Sstevel@tonic-gate 	}
71297c478bd9Sstevel@tonic-gate 	stop_sem(owner);
71307c478bd9Sstevel@tonic-gate }
71317c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
71327c478bd9Sstevel@tonic-gate 
71337c478bd9Sstevel@tonic-gate /*
71347c478bd9Sstevel@tonic-gate **  CLEANUP_QUEUES -- do some cleanup work for queues
71357c478bd9Sstevel@tonic-gate **
71367c478bd9Sstevel@tonic-gate **	Parameters:
71377c478bd9Sstevel@tonic-gate **		none.
71387c478bd9Sstevel@tonic-gate **
71397c478bd9Sstevel@tonic-gate **	Returns:
71407c478bd9Sstevel@tonic-gate **		none.
71417c478bd9Sstevel@tonic-gate **
71427c478bd9Sstevel@tonic-gate */
71437c478bd9Sstevel@tonic-gate 
71447c478bd9Sstevel@tonic-gate void
71457c478bd9Sstevel@tonic-gate cleanup_queues()
71467c478bd9Sstevel@tonic-gate {
71477c478bd9Sstevel@tonic-gate 	sync_queue_time();
71487c478bd9Sstevel@tonic-gate }
71497c478bd9Sstevel@tonic-gate /*
71507c478bd9Sstevel@tonic-gate **  SET_DEF_QUEUEVAL -- set default values for a queue group.
71517c478bd9Sstevel@tonic-gate **
71527c478bd9Sstevel@tonic-gate **	Parameters:
71537c478bd9Sstevel@tonic-gate **		qg -- queue group
71547c478bd9Sstevel@tonic-gate **		all -- set all values (true for default group)?
71557c478bd9Sstevel@tonic-gate **
71567c478bd9Sstevel@tonic-gate **	Returns:
71577c478bd9Sstevel@tonic-gate **		none.
71587c478bd9Sstevel@tonic-gate **
71597c478bd9Sstevel@tonic-gate **	Side Effects:
71607c478bd9Sstevel@tonic-gate **		sets default values for the queue group.
71617c478bd9Sstevel@tonic-gate */
71627c478bd9Sstevel@tonic-gate 
71637c478bd9Sstevel@tonic-gate void
71647c478bd9Sstevel@tonic-gate set_def_queueval(qg, all)
71657c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
71667c478bd9Sstevel@tonic-gate 	bool all;
71677c478bd9Sstevel@tonic-gate {
71687c478bd9Sstevel@tonic-gate 	if (bitnset(QD_DEFINED, qg->qg_flags))
71697c478bd9Sstevel@tonic-gate 		return;
71707c478bd9Sstevel@tonic-gate 	if (all)
71717c478bd9Sstevel@tonic-gate 		qg->qg_qdir = QueueDir;
71727c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_GROUP_SORTORDER
71737c478bd9Sstevel@tonic-gate 	qg->qg_sortorder = QueueSortOrder;
71747c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_GROUP_SORTORDER */
71757c478bd9Sstevel@tonic-gate 	qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
71767c478bd9Sstevel@tonic-gate 	qg->qg_nice = NiceQueueRun;
71777c478bd9Sstevel@tonic-gate }
71787c478bd9Sstevel@tonic-gate /*
71797c478bd9Sstevel@tonic-gate **  MAKEQUEUE -- define a new queue.
71807c478bd9Sstevel@tonic-gate **
71817c478bd9Sstevel@tonic-gate **	Parameters:
71827c478bd9Sstevel@tonic-gate **		line -- description of queue.  This is in labeled fields.
71837c478bd9Sstevel@tonic-gate **			The fields are:
71847c478bd9Sstevel@tonic-gate **			   F -- the flags associated with the queue
71857c478bd9Sstevel@tonic-gate **			   I -- the interval between running the queue
71867c478bd9Sstevel@tonic-gate **			   J -- the maximum # of jobs in work list
71877c478bd9Sstevel@tonic-gate **			   [M -- the maximum # of jobs in a queue run]
71887c478bd9Sstevel@tonic-gate **			   N -- the niceness at which to run
71897c478bd9Sstevel@tonic-gate **			   P -- the path to the queue
71907c478bd9Sstevel@tonic-gate **			   S -- the queue sorting order
71917c478bd9Sstevel@tonic-gate **			   R -- number of parallel queue runners
71927c478bd9Sstevel@tonic-gate **			   r -- max recipients per envelope
71937c478bd9Sstevel@tonic-gate **			The first word is the canonical name of the queue.
71947c478bd9Sstevel@tonic-gate **		qdef -- this is a 'Q' definition from .cf
71957c478bd9Sstevel@tonic-gate **
71967c478bd9Sstevel@tonic-gate **	Returns:
71977c478bd9Sstevel@tonic-gate **		none.
71987c478bd9Sstevel@tonic-gate **
71997c478bd9Sstevel@tonic-gate **	Side Effects:
72007c478bd9Sstevel@tonic-gate **		enters the queue into the queue table.
72017c478bd9Sstevel@tonic-gate */
72027c478bd9Sstevel@tonic-gate 
72037c478bd9Sstevel@tonic-gate void
72047c478bd9Sstevel@tonic-gate makequeue(line, qdef)
72057c478bd9Sstevel@tonic-gate 	char *line;
72067c478bd9Sstevel@tonic-gate 	bool qdef;
72077c478bd9Sstevel@tonic-gate {
72087c478bd9Sstevel@tonic-gate 	register char *p;
72097c478bd9Sstevel@tonic-gate 	register QUEUEGRP *qg;
72107c478bd9Sstevel@tonic-gate 	register STAB *s;
72117c478bd9Sstevel@tonic-gate 	int i;
72127c478bd9Sstevel@tonic-gate 	char fcode;
72137c478bd9Sstevel@tonic-gate 
72147c478bd9Sstevel@tonic-gate 	/* allocate a queue and set up defaults */
72157c478bd9Sstevel@tonic-gate 	qg = (QUEUEGRP *) xalloc(sizeof *qg);
72167c478bd9Sstevel@tonic-gate 	memset((char *) qg, '\0', sizeof *qg);
72177c478bd9Sstevel@tonic-gate 
72187c478bd9Sstevel@tonic-gate 	if (line[0] == '\0')
72197c478bd9Sstevel@tonic-gate 	{
72207c478bd9Sstevel@tonic-gate 		syserr("name required for queue");
72217c478bd9Sstevel@tonic-gate 		return;
72227c478bd9Sstevel@tonic-gate 	}
72237c478bd9Sstevel@tonic-gate 
72247c478bd9Sstevel@tonic-gate 	/* collect the queue name */
72257c478bd9Sstevel@tonic-gate 	for (p = line;
72267c478bd9Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
72277c478bd9Sstevel@tonic-gate 	     p++)
72287c478bd9Sstevel@tonic-gate 		continue;
72297c478bd9Sstevel@tonic-gate 	if (*p != '\0')
72307c478bd9Sstevel@tonic-gate 		*p++ = '\0';
72317c478bd9Sstevel@tonic-gate 	qg->qg_name = newstr(line);
72327c478bd9Sstevel@tonic-gate 
72337c478bd9Sstevel@tonic-gate 	/* set default values, can be overridden below */
72347c478bd9Sstevel@tonic-gate 	set_def_queueval(qg, false);
72357c478bd9Sstevel@tonic-gate 
72367c478bd9Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
72377c478bd9Sstevel@tonic-gate 	while (*p != '\0')
72387c478bd9Sstevel@tonic-gate 	{
72397c478bd9Sstevel@tonic-gate 		auto char *delimptr;
72407c478bd9Sstevel@tonic-gate 
72417c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
72427c478bd9Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
72437c478bd9Sstevel@tonic-gate 			p++;
72447c478bd9Sstevel@tonic-gate 
72457c478bd9Sstevel@tonic-gate 		/* p now points to field code */
72467c478bd9Sstevel@tonic-gate 		fcode = *p;
72477c478bd9Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
72487c478bd9Sstevel@tonic-gate 			p++;
72497c478bd9Sstevel@tonic-gate 		if (*p++ != '=')
72507c478bd9Sstevel@tonic-gate 		{
72517c478bd9Sstevel@tonic-gate 			syserr("queue %s: `=' expected", qg->qg_name);
72527c478bd9Sstevel@tonic-gate 			return;
72537c478bd9Sstevel@tonic-gate 		}
72547c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
72557c478bd9Sstevel@tonic-gate 			p++;
72567c478bd9Sstevel@tonic-gate 
72577c478bd9Sstevel@tonic-gate 		/* p now points to the field body */
72587c478bd9Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
72597c478bd9Sstevel@tonic-gate 
72607c478bd9Sstevel@tonic-gate 		/* install the field into the queue struct */
72617c478bd9Sstevel@tonic-gate 		switch (fcode)
72627c478bd9Sstevel@tonic-gate 		{
72637c478bd9Sstevel@tonic-gate 		  case 'P':		/* pathname */
72647c478bd9Sstevel@tonic-gate 			if (*p == '\0')
72657c478bd9Sstevel@tonic-gate 				syserr("queue %s: empty path name",
72667c478bd9Sstevel@tonic-gate 					qg->qg_name);
72677c478bd9Sstevel@tonic-gate 			else
72687c478bd9Sstevel@tonic-gate 				qg->qg_qdir = newstr(p);
72697c478bd9Sstevel@tonic-gate 			break;
72707c478bd9Sstevel@tonic-gate 
72717c478bd9Sstevel@tonic-gate 		  case 'F':		/* flags */
72727c478bd9Sstevel@tonic-gate 			for (; *p != '\0'; p++)
72737c478bd9Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
72747c478bd9Sstevel@tonic-gate 					setbitn(*p, qg->qg_flags);
72757c478bd9Sstevel@tonic-gate 			break;
72767c478bd9Sstevel@tonic-gate 
72777c478bd9Sstevel@tonic-gate 			/*
72787c478bd9Sstevel@tonic-gate 			**  Do we need two intervals here:
72797c478bd9Sstevel@tonic-gate 			**  One for persistent queue runners,
72807c478bd9Sstevel@tonic-gate 			**  one for "normal" queue runs?
72817c478bd9Sstevel@tonic-gate 			*/
72827c478bd9Sstevel@tonic-gate 
72837c478bd9Sstevel@tonic-gate 		  case 'I':	/* interval between running the queue */
72847c478bd9Sstevel@tonic-gate 			qg->qg_queueintvl = convtime(p, 'm');
72857c478bd9Sstevel@tonic-gate 			break;
72867c478bd9Sstevel@tonic-gate 
72877c478bd9Sstevel@tonic-gate 		  case 'N':		/* run niceness */
72887c478bd9Sstevel@tonic-gate 			qg->qg_nice = atoi(p);
72897c478bd9Sstevel@tonic-gate 			break;
72907c478bd9Sstevel@tonic-gate 
72917c478bd9Sstevel@tonic-gate 		  case 'R':		/* maximum # of runners for the group */
72927c478bd9Sstevel@tonic-gate 			i = atoi(p);
72937c478bd9Sstevel@tonic-gate 
72947c478bd9Sstevel@tonic-gate 			/* can't have more runners than allowed total */
72957c478bd9Sstevel@tonic-gate 			if (MaxQueueChildren > 0 && i > MaxQueueChildren)
72967c478bd9Sstevel@tonic-gate 			{
72977c478bd9Sstevel@tonic-gate 				qg->qg_maxqrun = MaxQueueChildren;
72987c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
72997c478bd9Sstevel@tonic-gate 						     "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
73007c478bd9Sstevel@tonic-gate 						     qg->qg_name, i,
73017c478bd9Sstevel@tonic-gate 						     MaxQueueChildren);
73027c478bd9Sstevel@tonic-gate 			}
73037c478bd9Sstevel@tonic-gate 			else
73047c478bd9Sstevel@tonic-gate 				qg->qg_maxqrun = i;
73057c478bd9Sstevel@tonic-gate 			break;
73067c478bd9Sstevel@tonic-gate 
73077c478bd9Sstevel@tonic-gate 		  case 'J':		/* maximum # of jobs in work list */
73087c478bd9Sstevel@tonic-gate 			qg->qg_maxlist = atoi(p);
73097c478bd9Sstevel@tonic-gate 			break;
73107c478bd9Sstevel@tonic-gate 
73117c478bd9Sstevel@tonic-gate 		  case 'r':		/* max recipients per envelope */
73127c478bd9Sstevel@tonic-gate 			qg->qg_maxrcpt = atoi(p);
73137c478bd9Sstevel@tonic-gate 			break;
73147c478bd9Sstevel@tonic-gate 
73157c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_GROUP_SORTORDER
73167c478bd9Sstevel@tonic-gate 		  case 'S':		/* queue sorting order */
73177c478bd9Sstevel@tonic-gate 			switch (*p)
73187c478bd9Sstevel@tonic-gate 			{
73197c478bd9Sstevel@tonic-gate 			  case 'h':	/* Host first */
73207c478bd9Sstevel@tonic-gate 			  case 'H':
73217c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYHOST;
73227c478bd9Sstevel@tonic-gate 				break;
73237c478bd9Sstevel@tonic-gate 
73247c478bd9Sstevel@tonic-gate 			  case 'p':	/* Priority order */
73257c478bd9Sstevel@tonic-gate 			  case 'P':
73267c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYPRIORITY;
73277c478bd9Sstevel@tonic-gate 				break;
73287c478bd9Sstevel@tonic-gate 
73297c478bd9Sstevel@tonic-gate 			  case 't':	/* Submission time */
73307c478bd9Sstevel@tonic-gate 			  case 'T':
73317c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYTIME;
73327c478bd9Sstevel@tonic-gate 				break;
73337c478bd9Sstevel@tonic-gate 
73347c478bd9Sstevel@tonic-gate 			  case 'f':	/* File name */
73357c478bd9Sstevel@tonic-gate 			  case 'F':
73367c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYFILENAME;
73377c478bd9Sstevel@tonic-gate 				break;
73387c478bd9Sstevel@tonic-gate 
73397c478bd9Sstevel@tonic-gate 			  case 'm':	/* Modification time */
73407c478bd9Sstevel@tonic-gate 			  case 'M':
73417c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYMODTIME;
73427c478bd9Sstevel@tonic-gate 				break;
73437c478bd9Sstevel@tonic-gate 
73447c478bd9Sstevel@tonic-gate 			  case 'r':	/* Random */
73457c478bd9Sstevel@tonic-gate 			  case 'R':
73467c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_RANDOM;
73477c478bd9Sstevel@tonic-gate 				break;
73487c478bd9Sstevel@tonic-gate 
73497c478bd9Sstevel@tonic-gate # if _FFR_RHS
73507c478bd9Sstevel@tonic-gate 			  case 's':	/* Shuffled host name */
73517c478bd9Sstevel@tonic-gate 			  case 'S':
73527c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYSHUFFLE;
73537c478bd9Sstevel@tonic-gate 				break;
73547c478bd9Sstevel@tonic-gate # endif /* _FFR_RHS */
73557c478bd9Sstevel@tonic-gate 
73567c478bd9Sstevel@tonic-gate 			  case 'n':	/* none */
73577c478bd9Sstevel@tonic-gate 			  case 'N':
73587c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_NONE;
73597c478bd9Sstevel@tonic-gate 				break;
73607c478bd9Sstevel@tonic-gate 
73617c478bd9Sstevel@tonic-gate 			  default:
73627c478bd9Sstevel@tonic-gate 				syserr("Invalid queue sort order \"%s\"", p);
73637c478bd9Sstevel@tonic-gate 			}
73647c478bd9Sstevel@tonic-gate 			break;
73657c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_GROUP_SORTORDER */
73667c478bd9Sstevel@tonic-gate 
73677c478bd9Sstevel@tonic-gate 		  default:
73687c478bd9Sstevel@tonic-gate 			syserr("Q%s: unknown queue equate %c=",
73697c478bd9Sstevel@tonic-gate 			       qg->qg_name, fcode);
73707c478bd9Sstevel@tonic-gate 			break;
73717c478bd9Sstevel@tonic-gate 		}
73727c478bd9Sstevel@tonic-gate 
73737c478bd9Sstevel@tonic-gate 		p = delimptr;
73747c478bd9Sstevel@tonic-gate 	}
73757c478bd9Sstevel@tonic-gate 
73767c478bd9Sstevel@tonic-gate #if !HASNICE
73777c478bd9Sstevel@tonic-gate 	if (qg->qg_nice != NiceQueueRun)
73787c478bd9Sstevel@tonic-gate 	{
73797c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
73807c478bd9Sstevel@tonic-gate 				     "Q%s: Warning: N= set on system that doesn't support nice()\n",
73817c478bd9Sstevel@tonic-gate 				     qg->qg_name);
73827c478bd9Sstevel@tonic-gate 	}
73837c478bd9Sstevel@tonic-gate #endif /* !HASNICE */
73847c478bd9Sstevel@tonic-gate 
73857c478bd9Sstevel@tonic-gate 	/* do some rationality checking */
73867c478bd9Sstevel@tonic-gate 	if (NumQueue >= MAXQUEUEGROUPS)
73877c478bd9Sstevel@tonic-gate 	{
73887c478bd9Sstevel@tonic-gate 		syserr("too many queue groups defined (%d max)",
73897c478bd9Sstevel@tonic-gate 			MAXQUEUEGROUPS);
73907c478bd9Sstevel@tonic-gate 		return;
73917c478bd9Sstevel@tonic-gate 	}
73927c478bd9Sstevel@tonic-gate 
73937c478bd9Sstevel@tonic-gate 	if (qg->qg_qdir == NULL)
73947c478bd9Sstevel@tonic-gate 	{
73957c478bd9Sstevel@tonic-gate 		if (QueueDir == NULL || *QueueDir == '\0')
73967c478bd9Sstevel@tonic-gate 		{
73977c478bd9Sstevel@tonic-gate 			syserr("QueueDir must be defined before queue groups");
73987c478bd9Sstevel@tonic-gate 			return;
73997c478bd9Sstevel@tonic-gate 		}
74007c478bd9Sstevel@tonic-gate 		qg->qg_qdir = newstr(QueueDir);
74017c478bd9Sstevel@tonic-gate 	}
74027c478bd9Sstevel@tonic-gate 
74037c478bd9Sstevel@tonic-gate 	if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
74047c478bd9Sstevel@tonic-gate 	{
74057c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
74067c478bd9Sstevel@tonic-gate 				     "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
74077c478bd9Sstevel@tonic-gate 				     qg->qg_name, qg->qg_maxqrun, QD_FORK);
74087c478bd9Sstevel@tonic-gate 	}
74097c478bd9Sstevel@tonic-gate 
74107c478bd9Sstevel@tonic-gate 	/* enter the queue into the symbol table */
74117c478bd9Sstevel@tonic-gate 	if (tTd(37, 8))
74127c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
74137c478bd9Sstevel@tonic-gate 			  "Adding %s to stab, path: %s", qg->qg_name,
74147c478bd9Sstevel@tonic-gate 			  qg->qg_qdir);
74157c478bd9Sstevel@tonic-gate 	s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
74167c478bd9Sstevel@tonic-gate 	if (s->s_quegrp != NULL)
74177c478bd9Sstevel@tonic-gate 	{
74187c478bd9Sstevel@tonic-gate 		i = s->s_quegrp->qg_index;
74197c478bd9Sstevel@tonic-gate 
74207c478bd9Sstevel@tonic-gate 		/* XXX what about the pointers inside this struct? */
74217c478bd9Sstevel@tonic-gate 		sm_free(s->s_quegrp); /* XXX */
74227c478bd9Sstevel@tonic-gate 	}
74237c478bd9Sstevel@tonic-gate 	else
74247c478bd9Sstevel@tonic-gate 		i = NumQueue++;
74257c478bd9Sstevel@tonic-gate 	Queue[i] = s->s_quegrp = qg;
74267c478bd9Sstevel@tonic-gate 	qg->qg_index = i;
74277c478bd9Sstevel@tonic-gate 
74287c478bd9Sstevel@tonic-gate 	/* set default value for max queue runners */
74297c478bd9Sstevel@tonic-gate 	if (qg->qg_maxqrun < 0)
74307c478bd9Sstevel@tonic-gate 	{
74317c478bd9Sstevel@tonic-gate 		if (MaxRunnersPerQueue > 0)
74327c478bd9Sstevel@tonic-gate 			qg->qg_maxqrun = MaxRunnersPerQueue;
74337c478bd9Sstevel@tonic-gate 		else
74347c478bd9Sstevel@tonic-gate 			qg->qg_maxqrun = 1;
74357c478bd9Sstevel@tonic-gate 	}
74367c478bd9Sstevel@tonic-gate 	if (qdef)
74377c478bd9Sstevel@tonic-gate 		setbitn(QD_DEFINED, qg->qg_flags);
74387c478bd9Sstevel@tonic-gate }
74397c478bd9Sstevel@tonic-gate #if 0
74407c478bd9Sstevel@tonic-gate /*
74417c478bd9Sstevel@tonic-gate **  HASHFQN -- calculate a hash value for a fully qualified host name
74427c478bd9Sstevel@tonic-gate **
74437c478bd9Sstevel@tonic-gate **	Arguments:
74447c478bd9Sstevel@tonic-gate **		fqn -- an all lower-case host.domain string
74457c478bd9Sstevel@tonic-gate **		buckets -- the number of buckets (queue directories)
74467c478bd9Sstevel@tonic-gate **
74477c478bd9Sstevel@tonic-gate **	Returns:
74487c478bd9Sstevel@tonic-gate **		a bucket number (signed integer)
74497c478bd9Sstevel@tonic-gate **		-1 on error
74507c478bd9Sstevel@tonic-gate **
74517c478bd9Sstevel@tonic-gate **	Contributed by Exactis.com, Inc.
74527c478bd9Sstevel@tonic-gate */
74537c478bd9Sstevel@tonic-gate 
74547c478bd9Sstevel@tonic-gate int
74557c478bd9Sstevel@tonic-gate hashfqn(fqn, buckets)
74567c478bd9Sstevel@tonic-gate 	register char *fqn;
74577c478bd9Sstevel@tonic-gate 	int buckets;
74587c478bd9Sstevel@tonic-gate {
74597c478bd9Sstevel@tonic-gate 	register char *p;
74607c478bd9Sstevel@tonic-gate 	register int h = 0, hash, cnt;
74617c478bd9Sstevel@tonic-gate 
74627c478bd9Sstevel@tonic-gate 	if (fqn == NULL)
74637c478bd9Sstevel@tonic-gate 		return -1;
74647c478bd9Sstevel@tonic-gate 
74657c478bd9Sstevel@tonic-gate 	/*
74667c478bd9Sstevel@tonic-gate 	**  A variation on the gdb hash
74677c478bd9Sstevel@tonic-gate 	**  This is the best as of Feb 19, 1996 --bcx
74687c478bd9Sstevel@tonic-gate 	*/
74697c478bd9Sstevel@tonic-gate 
74707c478bd9Sstevel@tonic-gate 	p = fqn;
74717c478bd9Sstevel@tonic-gate 	h = 0x238F13AF * strlen(p);
74727c478bd9Sstevel@tonic-gate 	for (cnt = 0; *p != 0; ++p, cnt++)
74737c478bd9Sstevel@tonic-gate 	{
74747c478bd9Sstevel@tonic-gate 		h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
74757c478bd9Sstevel@tonic-gate 	}
74767c478bd9Sstevel@tonic-gate 	h = (1103515243 * h + 12345) & 0x7FFFFFFF;
74777c478bd9Sstevel@tonic-gate 	if (buckets < 2)
74787c478bd9Sstevel@tonic-gate 		hash = 0;
74797c478bd9Sstevel@tonic-gate 	else
74807c478bd9Sstevel@tonic-gate 		hash = (h % buckets);
74817c478bd9Sstevel@tonic-gate 
74827c478bd9Sstevel@tonic-gate 	return hash;
74837c478bd9Sstevel@tonic-gate }
74847c478bd9Sstevel@tonic-gate #endif /* 0 */
74857c478bd9Sstevel@tonic-gate 
74867c478bd9Sstevel@tonic-gate /*
74877c478bd9Sstevel@tonic-gate **  A structure for sorting Queue according to maxqrun without
74887c478bd9Sstevel@tonic-gate **	screwing up Queue itself.
74897c478bd9Sstevel@tonic-gate */
74907c478bd9Sstevel@tonic-gate 
74917c478bd9Sstevel@tonic-gate struct sortqgrp
74927c478bd9Sstevel@tonic-gate {
74937c478bd9Sstevel@tonic-gate 	int sg_idx;		/* original index */
74947c478bd9Sstevel@tonic-gate 	int sg_maxqrun;		/* max queue runners */
74957c478bd9Sstevel@tonic-gate };
74967c478bd9Sstevel@tonic-gate typedef struct sortqgrp	SORTQGRP_T;
74977c478bd9Sstevel@tonic-gate static int cmpidx __P((const void *, const void *));
74987c478bd9Sstevel@tonic-gate 
74997c478bd9Sstevel@tonic-gate static int
75007c478bd9Sstevel@tonic-gate cmpidx(a, b)
75017c478bd9Sstevel@tonic-gate 	const void *a;
75027c478bd9Sstevel@tonic-gate 	const void *b;
75037c478bd9Sstevel@tonic-gate {
75047c478bd9Sstevel@tonic-gate 	/* The sort is highest to lowest, so the comparison is reversed */
75057c478bd9Sstevel@tonic-gate 	if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
75067c478bd9Sstevel@tonic-gate 		return 1;
75077c478bd9Sstevel@tonic-gate 	else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
75087c478bd9Sstevel@tonic-gate 		return -1;
75097c478bd9Sstevel@tonic-gate 	else
75107c478bd9Sstevel@tonic-gate 		return 0;
75117c478bd9Sstevel@tonic-gate }
75127c478bd9Sstevel@tonic-gate 
75137c478bd9Sstevel@tonic-gate /*
75147c478bd9Sstevel@tonic-gate **  MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
75157c478bd9Sstevel@tonic-gate **
75167c478bd9Sstevel@tonic-gate **  Take the now defined queue groups and assign them to work groups.
75177c478bd9Sstevel@tonic-gate **  This is done to balance out the number of concurrently active
75187c478bd9Sstevel@tonic-gate **  queue runners such that MaxQueueChildren is not exceeded. This may
75197c478bd9Sstevel@tonic-gate **  result in more than one queue group per work group. In such a case
75207c478bd9Sstevel@tonic-gate **  the number of running queue groups in that work group will have no
75217c478bd9Sstevel@tonic-gate **  more than the work group maximum number of runners (a "fair" portion
75227c478bd9Sstevel@tonic-gate **  of MaxQueueRunners). All queue groups within a work group will get a
75237c478bd9Sstevel@tonic-gate **  chance at running.
75247c478bd9Sstevel@tonic-gate **
75257c478bd9Sstevel@tonic-gate **	Parameters:
75267c478bd9Sstevel@tonic-gate **		none.
75277c478bd9Sstevel@tonic-gate **
75287c478bd9Sstevel@tonic-gate **	Returns:
75297c478bd9Sstevel@tonic-gate **		nothing.
75307c478bd9Sstevel@tonic-gate **
75317c478bd9Sstevel@tonic-gate **	Side Effects:
75327c478bd9Sstevel@tonic-gate **		Sets up WorkGrp structure.
75337c478bd9Sstevel@tonic-gate */
75347c478bd9Sstevel@tonic-gate 
75357c478bd9Sstevel@tonic-gate void
75367c478bd9Sstevel@tonic-gate makeworkgroups()
75377c478bd9Sstevel@tonic-gate {
75387c478bd9Sstevel@tonic-gate 	int i, j, total_runners, dir, h;
75397c478bd9Sstevel@tonic-gate 	SORTQGRP_T si[MAXQUEUEGROUPS + 1];
75407c478bd9Sstevel@tonic-gate 
75417c478bd9Sstevel@tonic-gate 	total_runners = 0;
75427c478bd9Sstevel@tonic-gate 	if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
75437c478bd9Sstevel@tonic-gate 	{
75447c478bd9Sstevel@tonic-gate 		/*
75457c478bd9Sstevel@tonic-gate 		**  There is only the "mqueue" queue group (a default)
75467c478bd9Sstevel@tonic-gate 		**  containing all of the queues. We want to provide to
75477c478bd9Sstevel@tonic-gate 		**  this queue group the maximum allowable queue runners.
75487c478bd9Sstevel@tonic-gate 		**  To match older behavior (8.10/8.11) we'll try for
75497c478bd9Sstevel@tonic-gate 		**  1 runner per queue capping it at MaxQueueChildren.
75507c478bd9Sstevel@tonic-gate 		**  So if there are N queues, then there will be N runners
75517c478bd9Sstevel@tonic-gate 		**  for the "mqueue" queue group (where N is kept less than
75527c478bd9Sstevel@tonic-gate 		**  MaxQueueChildren).
75537c478bd9Sstevel@tonic-gate 		*/
75547c478bd9Sstevel@tonic-gate 
75557c478bd9Sstevel@tonic-gate 		NumWorkGroups = 1;
75567c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_numqgrp = 1;
75577c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
75587c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_qgs[0] = Queue[0];
75597c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
75607c478bd9Sstevel@tonic-gate 		    Queue[0]->qg_numqueues > MaxQueueChildren)
75617c478bd9Sstevel@tonic-gate 			WorkGrp[0].wg_runners = MaxQueueChildren;
75627c478bd9Sstevel@tonic-gate 		else
75637c478bd9Sstevel@tonic-gate 			WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
75647c478bd9Sstevel@tonic-gate 
75657c478bd9Sstevel@tonic-gate 		Queue[0]->qg_wgrp = 0;
75667c478bd9Sstevel@tonic-gate 
75677c478bd9Sstevel@tonic-gate 		/* can't have more runners than allowed total */
75687c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
75697c478bd9Sstevel@tonic-gate 		    Queue[0]->qg_maxqrun > MaxQueueChildren)
75707c478bd9Sstevel@tonic-gate 			Queue[0]->qg_maxqrun = MaxQueueChildren;
75717c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
75727c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
75737c478bd9Sstevel@tonic-gate 		return;
75747c478bd9Sstevel@tonic-gate 	}
75757c478bd9Sstevel@tonic-gate 
75767c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
75777c478bd9Sstevel@tonic-gate 	{
75787c478bd9Sstevel@tonic-gate 		si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
75797c478bd9Sstevel@tonic-gate 		si[i].sg_idx = i;
75807c478bd9Sstevel@tonic-gate 	}
75817c478bd9Sstevel@tonic-gate 	qsort(si, NumQueue, sizeof(si[0]), cmpidx);
75827c478bd9Sstevel@tonic-gate 
75837c478bd9Sstevel@tonic-gate 	NumWorkGroups = 0;
75847c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
75857c478bd9Sstevel@tonic-gate 	{
75867c478bd9Sstevel@tonic-gate 		total_runners += si[i].sg_maxqrun;
75877c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
75887c478bd9Sstevel@tonic-gate 			NumWorkGroups++;
75897c478bd9Sstevel@tonic-gate 		else
75907c478bd9Sstevel@tonic-gate 			break;
75917c478bd9Sstevel@tonic-gate 	}
75927c478bd9Sstevel@tonic-gate 
75937c478bd9Sstevel@tonic-gate 	if (NumWorkGroups < 1)
75947c478bd9Sstevel@tonic-gate 		NumWorkGroups = 1; /* gotta have one at least */
75957c478bd9Sstevel@tonic-gate 	else if (NumWorkGroups > MAXWORKGROUPS)
75967c478bd9Sstevel@tonic-gate 		NumWorkGroups = MAXWORKGROUPS; /* the limit */
75977c478bd9Sstevel@tonic-gate 
75987c478bd9Sstevel@tonic-gate 	/*
75997c478bd9Sstevel@tonic-gate 	**  We now know the number of work groups to pack the queue groups
76007c478bd9Sstevel@tonic-gate 	**  into. The queue groups in 'Queue' are sorted from highest
76017c478bd9Sstevel@tonic-gate 	**  to lowest for the number of runners per queue group.
76027c478bd9Sstevel@tonic-gate 	**  We put the queue groups with the largest number of runners
76037c478bd9Sstevel@tonic-gate 	**  into work groups first. Then the smaller ones are fitted in
76047c478bd9Sstevel@tonic-gate 	**  where it looks best.
76057c478bd9Sstevel@tonic-gate 	*/
76067c478bd9Sstevel@tonic-gate 
76077c478bd9Sstevel@tonic-gate 	j = 0;
76087c478bd9Sstevel@tonic-gate 	dir = 1;
76097c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76107c478bd9Sstevel@tonic-gate 	{
76117c478bd9Sstevel@tonic-gate 		/* a to-and-fro packing scheme, continue from last position */
76127c478bd9Sstevel@tonic-gate 		if (j >= NumWorkGroups)
76137c478bd9Sstevel@tonic-gate 		{
76147c478bd9Sstevel@tonic-gate 			dir = -1;
76157c478bd9Sstevel@tonic-gate 			j = NumWorkGroups - 1;
76167c478bd9Sstevel@tonic-gate 		}
76177c478bd9Sstevel@tonic-gate 		else if (j < 0)
76187c478bd9Sstevel@tonic-gate 		{
76197c478bd9Sstevel@tonic-gate 			j = 0;
76207c478bd9Sstevel@tonic-gate 			dir = 1;
76217c478bd9Sstevel@tonic-gate 		}
76227c478bd9Sstevel@tonic-gate 
76237c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_qgs == NULL)
76247c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) *
76257c478bd9Sstevel@tonic-gate 							(WorkGrp[j].wg_numqgrp + 1));
76267c478bd9Sstevel@tonic-gate 		else
76277c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
76287c478bd9Sstevel@tonic-gate 							sizeof(QUEUEGRP *) *
76297c478bd9Sstevel@tonic-gate 							(WorkGrp[j].wg_numqgrp + 1));
76307c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_qgs == NULL)
76317c478bd9Sstevel@tonic-gate 		{
76327c478bd9Sstevel@tonic-gate 			syserr("!cannot allocate memory for work queues, need %d bytes",
76337c478bd9Sstevel@tonic-gate 			       (int) (sizeof(QUEUEGRP *) *
76347c478bd9Sstevel@tonic-gate 				      (WorkGrp[j].wg_numqgrp + 1)));
76357c478bd9Sstevel@tonic-gate 		}
76367c478bd9Sstevel@tonic-gate 
76377c478bd9Sstevel@tonic-gate 		h = si[i].sg_idx;
76387c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[h];
76397c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_numqgrp++;
76407c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_runners += Queue[h]->qg_maxqrun;
76417c478bd9Sstevel@tonic-gate 		Queue[h]->qg_wgrp = j;
76427c478bd9Sstevel@tonic-gate 
76437c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_maxact == 0)
76447c478bd9Sstevel@tonic-gate 		{
76457c478bd9Sstevel@tonic-gate 			/* can't have more runners than allowed total */
76467c478bd9Sstevel@tonic-gate 			if (MaxQueueChildren > 0 &&
76477c478bd9Sstevel@tonic-gate 			    Queue[h]->qg_maxqrun > MaxQueueChildren)
76487c478bd9Sstevel@tonic-gate 				Queue[h]->qg_maxqrun = MaxQueueChildren;
76497c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_maxact = Queue[h]->qg_maxqrun;
76507c478bd9Sstevel@tonic-gate 		}
76517c478bd9Sstevel@tonic-gate 
76527c478bd9Sstevel@tonic-gate 		/*
76537c478bd9Sstevel@tonic-gate 		**  XXX: must wg_lowqintvl be the GCD?
76547c478bd9Sstevel@tonic-gate 		**  qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
76557c478bd9Sstevel@tonic-gate 		**  qg2 occur?
76567c478bd9Sstevel@tonic-gate 		*/
76577c478bd9Sstevel@tonic-gate 
76587c478bd9Sstevel@tonic-gate 		/* keep track of the lowest interval for a persistent runner */
76597c478bd9Sstevel@tonic-gate 		if (Queue[h]->qg_queueintvl > 0 &&
76607c478bd9Sstevel@tonic-gate 		    WorkGrp[j].wg_lowqintvl < Queue[h]->qg_queueintvl)
76617c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_lowqintvl = Queue[h]->qg_queueintvl;
76627c478bd9Sstevel@tonic-gate 		j += dir;
76637c478bd9Sstevel@tonic-gate 	}
76647c478bd9Sstevel@tonic-gate 	if (tTd(41, 9))
76657c478bd9Sstevel@tonic-gate 	{
76667c478bd9Sstevel@tonic-gate 		for (i = 0; i < NumWorkGroups; i++)
76677c478bd9Sstevel@tonic-gate 		{
76687c478bd9Sstevel@tonic-gate 			sm_dprintf("Workgroup[%d]=", i);
76697c478bd9Sstevel@tonic-gate 			for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
76707c478bd9Sstevel@tonic-gate 			{
76717c478bd9Sstevel@tonic-gate 				sm_dprintf("%s, ",
76727c478bd9Sstevel@tonic-gate 					WorkGrp[i].wg_qgs[j]->qg_name);
76737c478bd9Sstevel@tonic-gate 			}
76747c478bd9Sstevel@tonic-gate 			sm_dprintf("\n");
76757c478bd9Sstevel@tonic-gate 		}
76767c478bd9Sstevel@tonic-gate 	}
76777c478bd9Sstevel@tonic-gate }
76787c478bd9Sstevel@tonic-gate 
76797c478bd9Sstevel@tonic-gate /*
76807c478bd9Sstevel@tonic-gate **  DUP_DF -- duplicate envelope data file
76817c478bd9Sstevel@tonic-gate **
76827c478bd9Sstevel@tonic-gate **	Copy the data file from the 'old' envelope to the 'new' envelope
76837c478bd9Sstevel@tonic-gate **	in the most efficient way possible.
76847c478bd9Sstevel@tonic-gate **
76857c478bd9Sstevel@tonic-gate **	Create a hard link from the 'old' data file to the 'new' data file.
76867c478bd9Sstevel@tonic-gate **	If the old and new queue directories are on different file systems,
76877c478bd9Sstevel@tonic-gate **	then the new data file link is created in the old queue directory,
76887c478bd9Sstevel@tonic-gate **	and the new queue file will contain a 'd' record pointing to the
76897c478bd9Sstevel@tonic-gate **	directory containing the new data file.
76907c478bd9Sstevel@tonic-gate **
76917c478bd9Sstevel@tonic-gate **	Parameters:
76927c478bd9Sstevel@tonic-gate **		old -- old envelope.
76937c478bd9Sstevel@tonic-gate **		new -- new envelope.
76947c478bd9Sstevel@tonic-gate **
76957c478bd9Sstevel@tonic-gate **	Results:
76967c478bd9Sstevel@tonic-gate **		Returns true on success, false on failure.
76977c478bd9Sstevel@tonic-gate **
76987c478bd9Sstevel@tonic-gate **	Side Effects:
76997c478bd9Sstevel@tonic-gate **		On success, the new data file is created.
77007c478bd9Sstevel@tonic-gate **		On fatal failure, EF_FATALERRS is set in old->e_flags.
77017c478bd9Sstevel@tonic-gate */
77027c478bd9Sstevel@tonic-gate 
77037c478bd9Sstevel@tonic-gate static bool	dup_df __P((ENVELOPE *, ENVELOPE *));
77047c478bd9Sstevel@tonic-gate 
77057c478bd9Sstevel@tonic-gate static bool
77067c478bd9Sstevel@tonic-gate dup_df(old, new)
77077c478bd9Sstevel@tonic-gate 	ENVELOPE *old;
77087c478bd9Sstevel@tonic-gate 	ENVELOPE *new;
77097c478bd9Sstevel@tonic-gate {
77107c478bd9Sstevel@tonic-gate 	int ofs, nfs, r;
77117c478bd9Sstevel@tonic-gate 	char opath[MAXPATHLEN];
77127c478bd9Sstevel@tonic-gate 	char npath[MAXPATHLEN];
77137c478bd9Sstevel@tonic-gate 
77147c478bd9Sstevel@tonic-gate 	if (!bitset(EF_HAS_DF, old->e_flags))
77157c478bd9Sstevel@tonic-gate 	{
77167c478bd9Sstevel@tonic-gate 		/*
77177c478bd9Sstevel@tonic-gate 		**  this can happen if: SuperSafe != True
77187c478bd9Sstevel@tonic-gate 		**  and a bounce mail is sent that is split.
77197c478bd9Sstevel@tonic-gate 		*/
77207c478bd9Sstevel@tonic-gate 
77217c478bd9Sstevel@tonic-gate 		queueup(old, false, true);
77227c478bd9Sstevel@tonic-gate 	}
77237c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
77247c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
77257c478bd9Sstevel@tonic-gate 
77267c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof opath);
77277c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath);
77287c478bd9Sstevel@tonic-gate 
77297c478bd9Sstevel@tonic-gate 	if (old->e_dfp != NULL)
77307c478bd9Sstevel@tonic-gate 	{
77317c478bd9Sstevel@tonic-gate 		r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
77327c478bd9Sstevel@tonic-gate 		if (r < 0 && errno != EINVAL)
77337c478bd9Sstevel@tonic-gate 		{
77347c478bd9Sstevel@tonic-gate 			syserr("@can't commit %s", opath);
77357c478bd9Sstevel@tonic-gate 			old->e_flags |= EF_FATALERRS;
77367c478bd9Sstevel@tonic-gate 			return false;
77377c478bd9Sstevel@tonic-gate 		}
77387c478bd9Sstevel@tonic-gate 	}
77397c478bd9Sstevel@tonic-gate 
77407c478bd9Sstevel@tonic-gate 	/*
77417c478bd9Sstevel@tonic-gate 	**  Attempt to create a hard link, if we think both old and new
77427c478bd9Sstevel@tonic-gate 	**  are on the same file system, otherwise copy the file.
77437c478bd9Sstevel@tonic-gate 	**
77447c478bd9Sstevel@tonic-gate 	**  Don't waste time attempting a hard link unless old and new
77457c478bd9Sstevel@tonic-gate 	**  are on the same file system.
77467c478bd9Sstevel@tonic-gate 	*/
77477c478bd9Sstevel@tonic-gate 
774849218d4fSjbeck 	SM_REQUIRE(ISVALIDQGRP(old->e_dfqgrp) && ISVALIDQDIR(old->e_dfqdir));
774949218d4fSjbeck 	SM_REQUIRE(ISVALIDQGRP(new->e_dfqgrp) && ISVALIDQDIR(new->e_dfqdir));
775049218d4fSjbeck 
775149218d4fSjbeck 	ofs = Queue[old->e_dfqgrp]->qg_qpaths[old->e_dfqdir].qp_fsysidx;
775249218d4fSjbeck 	nfs = Queue[new->e_dfqgrp]->qg_qpaths[new->e_dfqdir].qp_fsysidx;
77537c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
77547c478bd9Sstevel@tonic-gate 	{
77557c478bd9Sstevel@tonic-gate 		if (link(opath, npath) == 0)
77567c478bd9Sstevel@tonic-gate 		{
77577c478bd9Sstevel@tonic-gate 			new->e_flags |= EF_HAS_DF;
77587c478bd9Sstevel@tonic-gate 			SYNC_DIR(npath, true);
77597c478bd9Sstevel@tonic-gate 			return true;
77607c478bd9Sstevel@tonic-gate 		}
77617c478bd9Sstevel@tonic-gate 		goto error;
77627c478bd9Sstevel@tonic-gate 	}
77637c478bd9Sstevel@tonic-gate 
77647c478bd9Sstevel@tonic-gate 	/*
77657c478bd9Sstevel@tonic-gate 	**  Can't link across queue directories, so try to create a hard
77667c478bd9Sstevel@tonic-gate 	**  link in the same queue directory as the old df file.
77677c478bd9Sstevel@tonic-gate 	**  The qf file will refer to the new df file using a 'd' record.
77687c478bd9Sstevel@tonic-gate 	*/
77697c478bd9Sstevel@tonic-gate 
77707c478bd9Sstevel@tonic-gate 	new->e_dfqgrp = old->e_dfqgrp;
77717c478bd9Sstevel@tonic-gate 	new->e_dfqdir = old->e_dfqdir;
77727c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof npath);
77737c478bd9Sstevel@tonic-gate 	if (link(opath, npath) == 0)
77747c478bd9Sstevel@tonic-gate 	{
77757c478bd9Sstevel@tonic-gate 		new->e_flags |= EF_HAS_DF;
77767c478bd9Sstevel@tonic-gate 		SYNC_DIR(npath, true);
77777c478bd9Sstevel@tonic-gate 		return true;
77787c478bd9Sstevel@tonic-gate 	}
77797c478bd9Sstevel@tonic-gate 
77807c478bd9Sstevel@tonic-gate   error:
77817c478bd9Sstevel@tonic-gate 	if (LogLevel > 0)
77827c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, old->e_id,
77837c478bd9Sstevel@tonic-gate 			  "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
77847c478bd9Sstevel@tonic-gate 			  opath, npath, sm_errstring(errno));
77857c478bd9Sstevel@tonic-gate 	return false;
77867c478bd9Sstevel@tonic-gate }
77877c478bd9Sstevel@tonic-gate 
77887c478bd9Sstevel@tonic-gate /*
77897c478bd9Sstevel@tonic-gate **  SPLIT_ENV -- Allocate a new envelope based on a given envelope.
77907c478bd9Sstevel@tonic-gate **
77917c478bd9Sstevel@tonic-gate **	Parameters:
77927c478bd9Sstevel@tonic-gate **		e -- envelope.
77937c478bd9Sstevel@tonic-gate **		sendqueue -- sendqueue for new envelope.
77947c478bd9Sstevel@tonic-gate **		qgrp -- index of queue group.
77957c478bd9Sstevel@tonic-gate **		qdir -- queue directory.
77967c478bd9Sstevel@tonic-gate **
77977c478bd9Sstevel@tonic-gate **	Results:
77987c478bd9Sstevel@tonic-gate **		new envelope.
77997c478bd9Sstevel@tonic-gate **
78007c478bd9Sstevel@tonic-gate */
78017c478bd9Sstevel@tonic-gate 
78027c478bd9Sstevel@tonic-gate static ENVELOPE	*split_env __P((ENVELOPE *, ADDRESS *, int, int));
78037c478bd9Sstevel@tonic-gate 
78047c478bd9Sstevel@tonic-gate static ENVELOPE *
78057c478bd9Sstevel@tonic-gate split_env(e, sendqueue, qgrp, qdir)
78067c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
78077c478bd9Sstevel@tonic-gate 	ADDRESS *sendqueue;
78087c478bd9Sstevel@tonic-gate 	int qgrp;
78097c478bd9Sstevel@tonic-gate 	int qdir;
78107c478bd9Sstevel@tonic-gate {
78117c478bd9Sstevel@tonic-gate 	ENVELOPE *ee;
78127c478bd9Sstevel@tonic-gate 
78137c478bd9Sstevel@tonic-gate 	ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof *ee);
78147c478bd9Sstevel@tonic-gate 	STRUCTCOPY(*e, *ee);
78157c478bd9Sstevel@tonic-gate 	ee->e_message = NULL;	/* XXX use original message? */
78167c478bd9Sstevel@tonic-gate 	ee->e_id = NULL;
78177c478bd9Sstevel@tonic-gate 	assign_queueid(ee);
78187c478bd9Sstevel@tonic-gate 	ee->e_sendqueue = sendqueue;
78197c478bd9Sstevel@tonic-gate 	ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
78207c478bd9Sstevel@tonic-gate 			 |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
78217c478bd9Sstevel@tonic-gate 	ee->e_flags |= EF_NORECEIPT;	/* XXX really? */
78227c478bd9Sstevel@tonic-gate 	ee->e_from.q_state = QS_SENDER;
78237c478bd9Sstevel@tonic-gate 	ee->e_dfp = NULL;
78247c478bd9Sstevel@tonic-gate 	ee->e_lockfp = NULL;
78257c478bd9Sstevel@tonic-gate 	if (e->e_xfp != NULL)
78267c478bd9Sstevel@tonic-gate 		ee->e_xfp = sm_io_dup(e->e_xfp);
78277c478bd9Sstevel@tonic-gate 
78287c478bd9Sstevel@tonic-gate 	/* failed to dup e->e_xfp, start a new transcript */
78297c478bd9Sstevel@tonic-gate 	if (ee->e_xfp == NULL)
78307c478bd9Sstevel@tonic-gate 		openxscript(ee);
78317c478bd9Sstevel@tonic-gate 
78327c478bd9Sstevel@tonic-gate 	ee->e_qgrp = ee->e_dfqgrp = qgrp;
78337c478bd9Sstevel@tonic-gate 	ee->e_qdir = ee->e_dfqdir = qdir;
78347c478bd9Sstevel@tonic-gate 	ee->e_errormode = EM_MAIL;
78357c478bd9Sstevel@tonic-gate 	ee->e_statmsg = NULL;
78367c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg != NULL)
78377c478bd9Sstevel@tonic-gate 		ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
78387c478bd9Sstevel@tonic-gate 						  e->e_quarmsg);
78397c478bd9Sstevel@tonic-gate 
78407c478bd9Sstevel@tonic-gate 	/*
78417c478bd9Sstevel@tonic-gate 	**  XXX Not sure if this copying is necessary.
78427c478bd9Sstevel@tonic-gate 	**  sendall() does this copying, but I (dm) don't know if that is
78437c478bd9Sstevel@tonic-gate 	**  because of the storage management discipline we were using
78447c478bd9Sstevel@tonic-gate 	**  before rpools were introduced, or if it is because these lists
78457c478bd9Sstevel@tonic-gate 	**  can be modified later.
78467c478bd9Sstevel@tonic-gate 	*/
78477c478bd9Sstevel@tonic-gate 
78487c478bd9Sstevel@tonic-gate 	ee->e_header = copyheader(e->e_header, ee->e_rpool);
78497c478bd9Sstevel@tonic-gate 	ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
78507c478bd9Sstevel@tonic-gate 
78517c478bd9Sstevel@tonic-gate 	return ee;
78527c478bd9Sstevel@tonic-gate }
78537c478bd9Sstevel@tonic-gate 
78547c478bd9Sstevel@tonic-gate /* return values from split functions, check also below! */
78557c478bd9Sstevel@tonic-gate #define SM_SPLIT_FAIL	(0)
78567c478bd9Sstevel@tonic-gate #define SM_SPLIT_NONE	(1)
78577c478bd9Sstevel@tonic-gate #define SM_SPLIT_NEW(n)	(1 + (n))
78587c478bd9Sstevel@tonic-gate 
78597c478bd9Sstevel@tonic-gate /*
78607c478bd9Sstevel@tonic-gate **  SPLIT_ACROSS_QUEUE_GROUPS
78617c478bd9Sstevel@tonic-gate **
78627c478bd9Sstevel@tonic-gate **	This function splits an envelope across multiple queue groups
78637c478bd9Sstevel@tonic-gate **	based on the queue group of each recipient.
78647c478bd9Sstevel@tonic-gate **
78657c478bd9Sstevel@tonic-gate **	Parameters:
78667c478bd9Sstevel@tonic-gate **		e -- envelope.
78677c478bd9Sstevel@tonic-gate **
78687c478bd9Sstevel@tonic-gate **	Results:
78697c478bd9Sstevel@tonic-gate **		SM_SPLIT_FAIL on failure
78707c478bd9Sstevel@tonic-gate **		SM_SPLIT_NONE if no splitting occurred,
78717c478bd9Sstevel@tonic-gate **		or 1 + the number of additional envelopes created.
78727c478bd9Sstevel@tonic-gate **
78737c478bd9Sstevel@tonic-gate **	Side Effects:
78747c478bd9Sstevel@tonic-gate **		On success, e->e_sibling points to a list of zero or more
78757c478bd9Sstevel@tonic-gate **		additional envelopes, and the associated data files exist
78767c478bd9Sstevel@tonic-gate **		on disk.  But the queue files are not created.
78777c478bd9Sstevel@tonic-gate **
78787c478bd9Sstevel@tonic-gate **		On failure, e->e_sibling is not changed.
78797c478bd9Sstevel@tonic-gate **		The order of recipients in e->e_sendqueue is permuted.
78807c478bd9Sstevel@tonic-gate **		Abandoned data files for additional envelopes that failed
78817c478bd9Sstevel@tonic-gate **		to be created may exist on disk.
78827c478bd9Sstevel@tonic-gate */
78837c478bd9Sstevel@tonic-gate 
78847c478bd9Sstevel@tonic-gate static int	q_qgrp_compare __P((const void *, const void *));
78857c478bd9Sstevel@tonic-gate static int	e_filesys_compare __P((const void *, const void *));
78867c478bd9Sstevel@tonic-gate 
78877c478bd9Sstevel@tonic-gate static int
78887c478bd9Sstevel@tonic-gate q_qgrp_compare(p1, p2)
78897c478bd9Sstevel@tonic-gate 	const void *p1;
78907c478bd9Sstevel@tonic-gate 	const void *p2;
78917c478bd9Sstevel@tonic-gate {
78927c478bd9Sstevel@tonic-gate 	ADDRESS **pq1 = (ADDRESS **) p1;
78937c478bd9Sstevel@tonic-gate 	ADDRESS **pq2 = (ADDRESS **) p2;
78947c478bd9Sstevel@tonic-gate 
78957c478bd9Sstevel@tonic-gate 	return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
78967c478bd9Sstevel@tonic-gate }
78977c478bd9Sstevel@tonic-gate 
78987c478bd9Sstevel@tonic-gate static int
78997c478bd9Sstevel@tonic-gate e_filesys_compare(p1, p2)
79007c478bd9Sstevel@tonic-gate 	const void *p1;
79017c478bd9Sstevel@tonic-gate 	const void *p2;
79027c478bd9Sstevel@tonic-gate {
79037c478bd9Sstevel@tonic-gate 	ENVELOPE **pe1 = (ENVELOPE **) p1;
79047c478bd9Sstevel@tonic-gate 	ENVELOPE **pe2 = (ENVELOPE **) p2;
79057c478bd9Sstevel@tonic-gate 	int fs1, fs2;
79067c478bd9Sstevel@tonic-gate 
79077c478bd9Sstevel@tonic-gate 	fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
79087c478bd9Sstevel@tonic-gate 	fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
79097c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
79107c478bd9Sstevel@tonic-gate 		return -1;
79117c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
79127c478bd9Sstevel@tonic-gate 		return 1;
79137c478bd9Sstevel@tonic-gate 	return 0;
79147c478bd9Sstevel@tonic-gate }
79157c478bd9Sstevel@tonic-gate 
79167c478bd9Sstevel@tonic-gate static int
79177c478bd9Sstevel@tonic-gate split_across_queue_groups(e)
79187c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
79197c478bd9Sstevel@tonic-gate {
79207c478bd9Sstevel@tonic-gate 	int naddrs, nsplits, i;
79217c478bd9Sstevel@tonic-gate 	bool changed;
79227c478bd9Sstevel@tonic-gate 	char **pvp;
79237c478bd9Sstevel@tonic-gate 	ADDRESS *q, **addrs;
79247c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *es;
79257c478bd9Sstevel@tonic-gate 	ENVELOPE *splits[MAXQUEUEGROUPS];
79267c478bd9Sstevel@tonic-gate 	char pvpbuf[PSBUFSIZE];
79277c478bd9Sstevel@tonic-gate 
79287c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
79297c478bd9Sstevel@tonic-gate 
79307c478bd9Sstevel@tonic-gate 	/* Count addresses and assign queue groups. */
79317c478bd9Sstevel@tonic-gate 	naddrs = 0;
79327c478bd9Sstevel@tonic-gate 	changed = false;
79337c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
79347c478bd9Sstevel@tonic-gate 	{
79357c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
79367c478bd9Sstevel@tonic-gate 			continue;
79377c478bd9Sstevel@tonic-gate 		++naddrs;
79387c478bd9Sstevel@tonic-gate 
79397c478bd9Sstevel@tonic-gate 		/* bad addresses and those already sent stay put */
79407c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(q->q_state) ||
79417c478bd9Sstevel@tonic-gate 		    QS_IS_SENT(q->q_state))
79427c478bd9Sstevel@tonic-gate 			q->q_qgrp = e->e_qgrp;
79437c478bd9Sstevel@tonic-gate 		else if (!ISVALIDQGRP(q->q_qgrp))
79447c478bd9Sstevel@tonic-gate 		{
79457c478bd9Sstevel@tonic-gate 			/* call ruleset which should return a queue group */
79467c478bd9Sstevel@tonic-gate 			i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
79477c478bd9Sstevel@tonic-gate 				  pvpbuf, sizeof(pvpbuf));
79487c478bd9Sstevel@tonic-gate 			if (i == EX_OK &&
79497c478bd9Sstevel@tonic-gate 			    pvp != NULL && pvp[0] != NULL &&
79507c478bd9Sstevel@tonic-gate 			    (pvp[0][0] & 0377) == CANONNET &&
79517c478bd9Sstevel@tonic-gate 			    pvp[1] != NULL && pvp[1][0] != '\0')
79527c478bd9Sstevel@tonic-gate 			{
79537c478bd9Sstevel@tonic-gate 				i = name2qid(pvp[1]);
79547c478bd9Sstevel@tonic-gate 				if (ISVALIDQGRP(i))
79557c478bd9Sstevel@tonic-gate 				{
79567c478bd9Sstevel@tonic-gate 					q->q_qgrp = i;
79577c478bd9Sstevel@tonic-gate 					changed = true;
79587c478bd9Sstevel@tonic-gate 					if (tTd(20, 4))
79597c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_INFO, NOQID,
79607c478bd9Sstevel@tonic-gate 							"queue group name %s -> %d",
79617c478bd9Sstevel@tonic-gate 							pvp[1], i);
79627c478bd9Sstevel@tonic-gate 					continue;
79637c478bd9Sstevel@tonic-gate 				}
79647c478bd9Sstevel@tonic-gate 				else if (LogLevel > 10)
79657c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
79667c478bd9Sstevel@tonic-gate 						"can't find queue group name %s, selection ignored",
79677c478bd9Sstevel@tonic-gate 						pvp[1]);
79687c478bd9Sstevel@tonic-gate 			}
79697c478bd9Sstevel@tonic-gate 			if (q->q_mailer != NULL &&
79707c478bd9Sstevel@tonic-gate 			    ISVALIDQGRP(q->q_mailer->m_qgrp))
79717c478bd9Sstevel@tonic-gate 			{
79727c478bd9Sstevel@tonic-gate 				changed = true;
79737c478bd9Sstevel@tonic-gate 				q->q_qgrp = q->q_mailer->m_qgrp;
79747c478bd9Sstevel@tonic-gate 			}
79757c478bd9Sstevel@tonic-gate 			else if (ISVALIDQGRP(e->e_qgrp))
79767c478bd9Sstevel@tonic-gate 				q->q_qgrp = e->e_qgrp;
79777c478bd9Sstevel@tonic-gate 			else
79787c478bd9Sstevel@tonic-gate 				q->q_qgrp = 0;
79797c478bd9Sstevel@tonic-gate 		}
79807c478bd9Sstevel@tonic-gate 	}
79817c478bd9Sstevel@tonic-gate 
79827c478bd9Sstevel@tonic-gate 	/* only one address? nothing to split. */
79837c478bd9Sstevel@tonic-gate 	if (naddrs <= 1 && !changed)
79847c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
79857c478bd9Sstevel@tonic-gate 
79867c478bd9Sstevel@tonic-gate 	/* sort the addresses by queue group */
79877c478bd9Sstevel@tonic-gate 	addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
79887c478bd9Sstevel@tonic-gate 	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
79897c478bd9Sstevel@tonic-gate 	{
79907c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
79917c478bd9Sstevel@tonic-gate 			continue;
79927c478bd9Sstevel@tonic-gate 		addrs[i++] = q;
79937c478bd9Sstevel@tonic-gate 	}
79947c478bd9Sstevel@tonic-gate 	qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
79957c478bd9Sstevel@tonic-gate 
79967c478bd9Sstevel@tonic-gate 	/* split into multiple envelopes, by queue group */
79977c478bd9Sstevel@tonic-gate 	nsplits = 0;
79987c478bd9Sstevel@tonic-gate 	es = NULL;
79997c478bd9Sstevel@tonic-gate 	e->e_sendqueue = NULL;
80007c478bd9Sstevel@tonic-gate 	for (i = 0; i < naddrs; ++i)
80017c478bd9Sstevel@tonic-gate 	{
80027c478bd9Sstevel@tonic-gate 		if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
80037c478bd9Sstevel@tonic-gate 			addrs[i]->q_next = NULL;
80047c478bd9Sstevel@tonic-gate 		else
80057c478bd9Sstevel@tonic-gate 			addrs[i]->q_next = addrs[i + 1];
80067c478bd9Sstevel@tonic-gate 
80077c478bd9Sstevel@tonic-gate 		/* same queue group as original envelope? */
80087c478bd9Sstevel@tonic-gate 		if (addrs[i]->q_qgrp == e->e_qgrp)
80097c478bd9Sstevel@tonic-gate 		{
80107c478bd9Sstevel@tonic-gate 			if (e->e_sendqueue == NULL)
80117c478bd9Sstevel@tonic-gate 				e->e_sendqueue = addrs[i];
80127c478bd9Sstevel@tonic-gate 			continue;
80137c478bd9Sstevel@tonic-gate 		}
80147c478bd9Sstevel@tonic-gate 
80157c478bd9Sstevel@tonic-gate 		/* different queue group than original envelope */
80167c478bd9Sstevel@tonic-gate 		if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
80177c478bd9Sstevel@tonic-gate 		{
80187c478bd9Sstevel@tonic-gate 			ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
80197c478bd9Sstevel@tonic-gate 			es = ee;
80207c478bd9Sstevel@tonic-gate 			splits[nsplits++] = ee;
80217c478bd9Sstevel@tonic-gate 		}
80227c478bd9Sstevel@tonic-gate 	}
80237c478bd9Sstevel@tonic-gate 
80247c478bd9Sstevel@tonic-gate 	/* no splits? return right now. */
80257c478bd9Sstevel@tonic-gate 	if (nsplits <= 0)
80267c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
80277c478bd9Sstevel@tonic-gate 
80287c478bd9Sstevel@tonic-gate 	/* assign a queue directory to each additional envelope */
80297c478bd9Sstevel@tonic-gate 	for (i = 0; i < nsplits; ++i)
80307c478bd9Sstevel@tonic-gate 	{
80317c478bd9Sstevel@tonic-gate 		es = splits[i];
80327c478bd9Sstevel@tonic-gate #if 0
80337c478bd9Sstevel@tonic-gate 		es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
80347c478bd9Sstevel@tonic-gate #endif /* 0 */
80357c478bd9Sstevel@tonic-gate 		if (!setnewqueue(es))
80367c478bd9Sstevel@tonic-gate 			goto failure;
80377c478bd9Sstevel@tonic-gate 	}
80387c478bd9Sstevel@tonic-gate 
80397c478bd9Sstevel@tonic-gate 	/* sort the additional envelopes by queue file system */
80407c478bd9Sstevel@tonic-gate 	qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
80417c478bd9Sstevel@tonic-gate 
80427c478bd9Sstevel@tonic-gate 	/* create data files for each additional envelope */
80437c478bd9Sstevel@tonic-gate 	if (!dup_df(e, splits[0]))
80447c478bd9Sstevel@tonic-gate 	{
80457c478bd9Sstevel@tonic-gate 		i = 0;
80467c478bd9Sstevel@tonic-gate 		goto failure;
80477c478bd9Sstevel@tonic-gate 	}
80487c478bd9Sstevel@tonic-gate 	for (i = 1; i < nsplits; ++i)
80497c478bd9Sstevel@tonic-gate 	{
80507c478bd9Sstevel@tonic-gate 		/* copy or link to the previous data file */
80517c478bd9Sstevel@tonic-gate 		if (!dup_df(splits[i - 1], splits[i]))
80527c478bd9Sstevel@tonic-gate 			goto failure;
80537c478bd9Sstevel@tonic-gate 	}
80547c478bd9Sstevel@tonic-gate 
80557c478bd9Sstevel@tonic-gate 	/* success: prepend the new envelopes to the e->e_sibling list */
80567c478bd9Sstevel@tonic-gate 	for (i = 0; i < nsplits; ++i)
80577c478bd9Sstevel@tonic-gate 	{
80587c478bd9Sstevel@tonic-gate 		es = splits[i];
80597c478bd9Sstevel@tonic-gate 		es->e_sibling = e->e_sibling;
80607c478bd9Sstevel@tonic-gate 		e->e_sibling = es;
80617c478bd9Sstevel@tonic-gate 	}
80627c478bd9Sstevel@tonic-gate 	return SM_SPLIT_NEW(nsplits);
80637c478bd9Sstevel@tonic-gate 
80647c478bd9Sstevel@tonic-gate 	/* failure: clean up */
80657c478bd9Sstevel@tonic-gate   failure:
80667c478bd9Sstevel@tonic-gate 	if (i > 0)
80677c478bd9Sstevel@tonic-gate 	{
80687c478bd9Sstevel@tonic-gate 		int j;
80697c478bd9Sstevel@tonic-gate 
80707c478bd9Sstevel@tonic-gate 		for (j = 0; j < i; j++)
80717c478bd9Sstevel@tonic-gate 			(void) unlink(queuename(splits[j], DATAFL_LETTER));
80727c478bd9Sstevel@tonic-gate 	}
80737c478bd9Sstevel@tonic-gate 	e->e_sendqueue = addrs[0];
80747c478bd9Sstevel@tonic-gate 	for (i = 0; i < naddrs - 1; ++i)
80757c478bd9Sstevel@tonic-gate 		addrs[i]->q_next = addrs[i + 1];
80767c478bd9Sstevel@tonic-gate 	addrs[naddrs - 1]->q_next = NULL;
80777c478bd9Sstevel@tonic-gate 	return SM_SPLIT_FAIL;
80787c478bd9Sstevel@tonic-gate }
80797c478bd9Sstevel@tonic-gate 
80807c478bd9Sstevel@tonic-gate /*
80817c478bd9Sstevel@tonic-gate **  SPLIT_WITHIN_QUEUE
80827c478bd9Sstevel@tonic-gate **
80837c478bd9Sstevel@tonic-gate **	Split an envelope with multiple recipients into several
80847c478bd9Sstevel@tonic-gate **	envelopes within the same queue directory, if the number of
80857c478bd9Sstevel@tonic-gate **	recipients exceeds the limit for the queue group.
80867c478bd9Sstevel@tonic-gate **
80877c478bd9Sstevel@tonic-gate **	Parameters:
80887c478bd9Sstevel@tonic-gate **		e -- envelope.
80897c478bd9Sstevel@tonic-gate **
80907c478bd9Sstevel@tonic-gate **	Results:
80917c478bd9Sstevel@tonic-gate **		SM_SPLIT_FAIL on failure
80927c478bd9Sstevel@tonic-gate **		SM_SPLIT_NONE if no splitting occurred,
80937c478bd9Sstevel@tonic-gate **		or 1 + the number of additional envelopes created.
80947c478bd9Sstevel@tonic-gate */
80957c478bd9Sstevel@tonic-gate 
80967c478bd9Sstevel@tonic-gate #define SPLIT_LOG_LEVEL	8
80977c478bd9Sstevel@tonic-gate 
80987c478bd9Sstevel@tonic-gate static int	split_within_queue __P((ENVELOPE *));
80997c478bd9Sstevel@tonic-gate 
81007c478bd9Sstevel@tonic-gate static int
81017c478bd9Sstevel@tonic-gate split_within_queue(e)
81027c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
81037c478bd9Sstevel@tonic-gate {
81047c478bd9Sstevel@tonic-gate 	int maxrcpt, nrcpt, ndead, nsplit, i;
81057c478bd9Sstevel@tonic-gate 	int j, l;
81067c478bd9Sstevel@tonic-gate 	char *lsplits;
81077c478bd9Sstevel@tonic-gate 	ADDRESS *q, **addrs;
81087c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *firstsibling;
81097c478bd9Sstevel@tonic-gate 
81107c478bd9Sstevel@tonic-gate 	if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
81117c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81127c478bd9Sstevel@tonic-gate 
81137c478bd9Sstevel@tonic-gate 	/* don't bother if there is no recipient limit */
81147c478bd9Sstevel@tonic-gate 	maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
81157c478bd9Sstevel@tonic-gate 	if (maxrcpt <= 0)
81167c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81177c478bd9Sstevel@tonic-gate 
81187c478bd9Sstevel@tonic-gate 	/* count recipients */
81197c478bd9Sstevel@tonic-gate 	nrcpt = 0;
81207c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
81217c478bd9Sstevel@tonic-gate 	{
81227c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
81237c478bd9Sstevel@tonic-gate 			continue;
81247c478bd9Sstevel@tonic-gate 		++nrcpt;
81257c478bd9Sstevel@tonic-gate 	}
81267c478bd9Sstevel@tonic-gate 	if (nrcpt <= maxrcpt)
81277c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81287c478bd9Sstevel@tonic-gate 
81297c478bd9Sstevel@tonic-gate 	/*
81307c478bd9Sstevel@tonic-gate 	**  Preserve the recipient list
81317c478bd9Sstevel@tonic-gate 	**  so that we can restore it in case of error.
81327c478bd9Sstevel@tonic-gate 	**  (But we discard dead addresses.)
81337c478bd9Sstevel@tonic-gate 	*/
81347c478bd9Sstevel@tonic-gate 
81357c478bd9Sstevel@tonic-gate 	addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
81367c478bd9Sstevel@tonic-gate 	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
81377c478bd9Sstevel@tonic-gate 	{
81387c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
81397c478bd9Sstevel@tonic-gate 			continue;
81407c478bd9Sstevel@tonic-gate 		addrs[i++] = q;
81417c478bd9Sstevel@tonic-gate 	}
81427c478bd9Sstevel@tonic-gate 
81437c478bd9Sstevel@tonic-gate 	/*
81447c478bd9Sstevel@tonic-gate 	**  Partition the recipient list so that bad and sent addresses
81457c478bd9Sstevel@tonic-gate 	**  come first. These will go with the original envelope, and
81467c478bd9Sstevel@tonic-gate 	**  do not count towards the maxrcpt limit.
81477c478bd9Sstevel@tonic-gate 	**  addrs[] does not contain QS_IS_DEAD() addresses.
81487c478bd9Sstevel@tonic-gate 	*/
81497c478bd9Sstevel@tonic-gate 
81507c478bd9Sstevel@tonic-gate 	ndead = 0;
81517c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrcpt; ++i)
81527c478bd9Sstevel@tonic-gate 	{
81537c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(addrs[i]->q_state) ||
81547c478bd9Sstevel@tonic-gate 		    QS_IS_SENT(addrs[i]->q_state) ||
81557c478bd9Sstevel@tonic-gate 		    QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
81567c478bd9Sstevel@tonic-gate 		{
81577c478bd9Sstevel@tonic-gate 			if (i > ndead)
81587c478bd9Sstevel@tonic-gate 			{
81597c478bd9Sstevel@tonic-gate 				ADDRESS *tmp = addrs[i];
81607c478bd9Sstevel@tonic-gate 
81617c478bd9Sstevel@tonic-gate 				addrs[i] = addrs[ndead];
81627c478bd9Sstevel@tonic-gate 				addrs[ndead] = tmp;
81637c478bd9Sstevel@tonic-gate 			}
81647c478bd9Sstevel@tonic-gate 			++ndead;
81657c478bd9Sstevel@tonic-gate 		}
81667c478bd9Sstevel@tonic-gate 	}
81677c478bd9Sstevel@tonic-gate 
81687c478bd9Sstevel@tonic-gate 	/* Check if no splitting required. */
81697c478bd9Sstevel@tonic-gate 	if (nrcpt - ndead <= maxrcpt)
81707c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81717c478bd9Sstevel@tonic-gate 
81727c478bd9Sstevel@tonic-gate 	/* fix links */
81737c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrcpt - 1; ++i)
81747c478bd9Sstevel@tonic-gate 		addrs[i]->q_next = addrs[i + 1];
81757c478bd9Sstevel@tonic-gate 	addrs[nrcpt - 1]->q_next = NULL;
81767c478bd9Sstevel@tonic-gate 	e->e_sendqueue = addrs[0];
81777c478bd9Sstevel@tonic-gate 
81787c478bd9Sstevel@tonic-gate 	/* prepare buffer for logging */
81797c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL)
81807c478bd9Sstevel@tonic-gate 	{
81817c478bd9Sstevel@tonic-gate 		l = MAXLINE;
81827c478bd9Sstevel@tonic-gate 		lsplits = sm_malloc(l);
81837c478bd9Sstevel@tonic-gate 		if (lsplits != NULL)
81847c478bd9Sstevel@tonic-gate 			*lsplits = '\0';
81857c478bd9Sstevel@tonic-gate 		j = 0;
81867c478bd9Sstevel@tonic-gate 	}
81877c478bd9Sstevel@tonic-gate 	else
81887c478bd9Sstevel@tonic-gate 	{
81897c478bd9Sstevel@tonic-gate 		/* get rid of stupid compiler warnings */
81907c478bd9Sstevel@tonic-gate 		lsplits = NULL;
81917c478bd9Sstevel@tonic-gate 		j = l = 0;
81927c478bd9Sstevel@tonic-gate 	}
81937c478bd9Sstevel@tonic-gate 
81947c478bd9Sstevel@tonic-gate 	/* split the envelope */
81957c478bd9Sstevel@tonic-gate 	firstsibling = e->e_sibling;
81967c478bd9Sstevel@tonic-gate 	i = maxrcpt + ndead;
81977c478bd9Sstevel@tonic-gate 	nsplit = 0;
81987c478bd9Sstevel@tonic-gate 	for (;;)
81997c478bd9Sstevel@tonic-gate 	{
82007c478bd9Sstevel@tonic-gate 		addrs[i - 1]->q_next = NULL;
82017c478bd9Sstevel@tonic-gate 		ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
82027c478bd9Sstevel@tonic-gate 		if (!dup_df(e, ee))
82037c478bd9Sstevel@tonic-gate 		{
82047c478bd9Sstevel@tonic-gate 
82057c478bd9Sstevel@tonic-gate 			ee = firstsibling;
82067c478bd9Sstevel@tonic-gate 			while (ee != NULL)
82077c478bd9Sstevel@tonic-gate 			{
82087c478bd9Sstevel@tonic-gate 				(void) unlink(queuename(ee, DATAFL_LETTER));
82097c478bd9Sstevel@tonic-gate 				ee = ee->e_sibling;
82107c478bd9Sstevel@tonic-gate 			}
82117c478bd9Sstevel@tonic-gate 
82127c478bd9Sstevel@tonic-gate 			/* Error.  Restore e's sibling & recipient lists. */
82137c478bd9Sstevel@tonic-gate 			e->e_sibling = firstsibling;
82147c478bd9Sstevel@tonic-gate 			for (i = 0; i < nrcpt - 1; ++i)
82157c478bd9Sstevel@tonic-gate 				addrs[i]->q_next = addrs[i + 1];
82167c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
82177c478bd9Sstevel@tonic-gate 				sm_free(lsplits);
82187c478bd9Sstevel@tonic-gate 			return SM_SPLIT_FAIL;
82197c478bd9Sstevel@tonic-gate 		}
82207c478bd9Sstevel@tonic-gate 
82217c478bd9Sstevel@tonic-gate 		/* prepend the new envelope to e->e_sibling */
82227c478bd9Sstevel@tonic-gate 		ee->e_sibling = e->e_sibling;
82237c478bd9Sstevel@tonic-gate 		e->e_sibling = ee;
82247c478bd9Sstevel@tonic-gate 		++nsplit;
82257c478bd9Sstevel@tonic-gate 		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
82267c478bd9Sstevel@tonic-gate 		{
82277c478bd9Sstevel@tonic-gate 			if (j >= l - strlen(ee->e_id) - 3)
82287c478bd9Sstevel@tonic-gate 			{
82297c478bd9Sstevel@tonic-gate 				char *p;
82307c478bd9Sstevel@tonic-gate 
82317c478bd9Sstevel@tonic-gate 				l += MAXLINE;
82327c478bd9Sstevel@tonic-gate 				p = sm_realloc(lsplits, l);
82337c478bd9Sstevel@tonic-gate 				if (p == NULL)
82347c478bd9Sstevel@tonic-gate 				{
82357c478bd9Sstevel@tonic-gate 					/* let's try to get this done */
82367c478bd9Sstevel@tonic-gate 					sm_free(lsplits);
82377c478bd9Sstevel@tonic-gate 					lsplits = NULL;
82387c478bd9Sstevel@tonic-gate 				}
82397c478bd9Sstevel@tonic-gate 				else
82407c478bd9Sstevel@tonic-gate 					lsplits = p;
82417c478bd9Sstevel@tonic-gate 			}
82427c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
82437c478bd9Sstevel@tonic-gate 			{
82447c478bd9Sstevel@tonic-gate 				if (j == 0)
82457c478bd9Sstevel@tonic-gate 					j += sm_strlcat(lsplits + j,
82467c478bd9Sstevel@tonic-gate 							ee->e_id,
82477c478bd9Sstevel@tonic-gate 							l - j);
82487c478bd9Sstevel@tonic-gate 				else
82497c478bd9Sstevel@tonic-gate 					j += sm_strlcat2(lsplits + j,
82507c478bd9Sstevel@tonic-gate 							 "; ",
82517c478bd9Sstevel@tonic-gate 							 ee->e_id,
82527c478bd9Sstevel@tonic-gate 							 l - j);
82537c478bd9Sstevel@tonic-gate 				SM_ASSERT(j < l);
82547c478bd9Sstevel@tonic-gate 			}
82557c478bd9Sstevel@tonic-gate 		}
82567c478bd9Sstevel@tonic-gate 		if (nrcpt - i <= maxrcpt)
82577c478bd9Sstevel@tonic-gate 			break;
82587c478bd9Sstevel@tonic-gate 		i += maxrcpt;
82597c478bd9Sstevel@tonic-gate 	}
82607c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
82617c478bd9Sstevel@tonic-gate 	{
82627c478bd9Sstevel@tonic-gate 		if (nsplit > 0)
82637c478bd9Sstevel@tonic-gate 		{
82647c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_NOTICE, e->e_id,
82657c478bd9Sstevel@tonic-gate 				  "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
82667c478bd9Sstevel@tonic-gate 				  maxrcpt, nrcpt - ndead, nsplit,
82677c478bd9Sstevel@tonic-gate 				  nsplit > 1 ? "s" : "", lsplits);
82687c478bd9Sstevel@tonic-gate 		}
82697c478bd9Sstevel@tonic-gate 		sm_free(lsplits);
82707c478bd9Sstevel@tonic-gate 	}
82717c478bd9Sstevel@tonic-gate 	return SM_SPLIT_NEW(nsplit);
82727c478bd9Sstevel@tonic-gate }
82737c478bd9Sstevel@tonic-gate /*
82747c478bd9Sstevel@tonic-gate **  SPLIT_BY_RECIPIENT
82757c478bd9Sstevel@tonic-gate **
82767c478bd9Sstevel@tonic-gate **	Split an envelope with multiple recipients into multiple
82777c478bd9Sstevel@tonic-gate **	envelopes as required by the sendmail configuration.
82787c478bd9Sstevel@tonic-gate **
82797c478bd9Sstevel@tonic-gate **	Parameters:
82807c478bd9Sstevel@tonic-gate **		e -- envelope.
82817c478bd9Sstevel@tonic-gate **
82827c478bd9Sstevel@tonic-gate **	Results:
82837c478bd9Sstevel@tonic-gate **		Returns true on success, false on failure.
82847c478bd9Sstevel@tonic-gate **
82857c478bd9Sstevel@tonic-gate **	Side Effects:
82867c478bd9Sstevel@tonic-gate **		see split_across_queue_groups(), split_within_queue(e)
82877c478bd9Sstevel@tonic-gate */
82887c478bd9Sstevel@tonic-gate 
82897c478bd9Sstevel@tonic-gate bool
82907c478bd9Sstevel@tonic-gate split_by_recipient(e)
82917c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
82927c478bd9Sstevel@tonic-gate {
82937c478bd9Sstevel@tonic-gate 	int split, n, i, j, l;
82947c478bd9Sstevel@tonic-gate 	char *lsplits;
82957c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *next, *firstsibling;
82967c478bd9Sstevel@tonic-gate 
82977c478bd9Sstevel@tonic-gate 	if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
82987c478bd9Sstevel@tonic-gate 	    bitset(EF_SPLIT, e->e_flags))
82997c478bd9Sstevel@tonic-gate 		return true;
83007c478bd9Sstevel@tonic-gate 	n = split_across_queue_groups(e);
83017c478bd9Sstevel@tonic-gate 	if (n == SM_SPLIT_FAIL)
83027c478bd9Sstevel@tonic-gate 		return false;
83037c478bd9Sstevel@tonic-gate 	firstsibling = ee = e->e_sibling;
83047c478bd9Sstevel@tonic-gate 	if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
83057c478bd9Sstevel@tonic-gate 	{
83067c478bd9Sstevel@tonic-gate 		l = MAXLINE;
83077c478bd9Sstevel@tonic-gate 		lsplits = sm_malloc(l);
83087c478bd9Sstevel@tonic-gate 		if (lsplits != NULL)
83097c478bd9Sstevel@tonic-gate 			*lsplits = '\0';
83107c478bd9Sstevel@tonic-gate 		j = 0;
83117c478bd9Sstevel@tonic-gate 	}
83127c478bd9Sstevel@tonic-gate 	else
83137c478bd9Sstevel@tonic-gate 	{
83147c478bd9Sstevel@tonic-gate 		/* get rid of stupid compiler warnings */
83157c478bd9Sstevel@tonic-gate 		lsplits = NULL;
83167c478bd9Sstevel@tonic-gate 		j = l = 0;
83177c478bd9Sstevel@tonic-gate 	}
83187c478bd9Sstevel@tonic-gate 	for (i = 1; i < n; ++i)
83197c478bd9Sstevel@tonic-gate 	{
83207c478bd9Sstevel@tonic-gate 		next = ee->e_sibling;
83217c478bd9Sstevel@tonic-gate 		if (split_within_queue(ee) == SM_SPLIT_FAIL)
83227c478bd9Sstevel@tonic-gate 		{
83237c478bd9Sstevel@tonic-gate 			e->e_sibling = firstsibling;
83247c478bd9Sstevel@tonic-gate 			return false;
83257c478bd9Sstevel@tonic-gate 		}
83267c478bd9Sstevel@tonic-gate 		ee->e_flags |= EF_SPLIT;
83277c478bd9Sstevel@tonic-gate 		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
83287c478bd9Sstevel@tonic-gate 		{
83297c478bd9Sstevel@tonic-gate 			if (j >= l - strlen(ee->e_id) - 3)
83307c478bd9Sstevel@tonic-gate 			{
83317c478bd9Sstevel@tonic-gate 				char *p;
83327c478bd9Sstevel@tonic-gate 
83337c478bd9Sstevel@tonic-gate 				l += MAXLINE;
83347c478bd9Sstevel@tonic-gate 				p = sm_realloc(lsplits, l);
83357c478bd9Sstevel@tonic-gate 				if (p == NULL)
83367c478bd9Sstevel@tonic-gate 				{
83377c478bd9Sstevel@tonic-gate 					/* let's try to get this done */
83387c478bd9Sstevel@tonic-gate 					sm_free(lsplits);
83397c478bd9Sstevel@tonic-gate 					lsplits = NULL;
83407c478bd9Sstevel@tonic-gate 				}
83417c478bd9Sstevel@tonic-gate 				else
83427c478bd9Sstevel@tonic-gate 					lsplits = p;
83437c478bd9Sstevel@tonic-gate 			}
83447c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
83457c478bd9Sstevel@tonic-gate 			{
83467c478bd9Sstevel@tonic-gate 				if (j == 0)
83477c478bd9Sstevel@tonic-gate 					j += sm_strlcat(lsplits + j,
83487c478bd9Sstevel@tonic-gate 							ee->e_id, l - j);
83497c478bd9Sstevel@tonic-gate 				else
83507c478bd9Sstevel@tonic-gate 					j += sm_strlcat2(lsplits + j, "; ",
83517c478bd9Sstevel@tonic-gate 							 ee->e_id, l - j);
83527c478bd9Sstevel@tonic-gate 				SM_ASSERT(j < l);
83537c478bd9Sstevel@tonic-gate 			}
83547c478bd9Sstevel@tonic-gate 		}
83557c478bd9Sstevel@tonic-gate 		ee = next;
83567c478bd9Sstevel@tonic-gate 	}
83577c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
83587c478bd9Sstevel@tonic-gate 	{
83597c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
83607c478bd9Sstevel@tonic-gate 			  n - 1, n > 2 ? "s" : "", lsplits);
83617c478bd9Sstevel@tonic-gate 		sm_free(lsplits);
83627c478bd9Sstevel@tonic-gate 	}
83637c478bd9Sstevel@tonic-gate 	split = split_within_queue(e) != SM_SPLIT_FAIL;
83647c478bd9Sstevel@tonic-gate 	if (split)
83657c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_SPLIT;
83667c478bd9Sstevel@tonic-gate 	return split;
83677c478bd9Sstevel@tonic-gate }
83687c478bd9Sstevel@tonic-gate 
83697c478bd9Sstevel@tonic-gate /*
83707c478bd9Sstevel@tonic-gate **  QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
83717c478bd9Sstevel@tonic-gate **
83727c478bd9Sstevel@tonic-gate **	Add/remove quarantine reason and requeue appropriately.
83737c478bd9Sstevel@tonic-gate **
83747c478bd9Sstevel@tonic-gate **	Parameters:
83757c478bd9Sstevel@tonic-gate **		qgrp -- queue group for the item
83767c478bd9Sstevel@tonic-gate **		qdir -- queue directory in the given queue group
83777c478bd9Sstevel@tonic-gate **		e -- envelope information for the item
83787c478bd9Sstevel@tonic-gate **		reason -- quarantine reason, NULL means unquarantine.
83797c478bd9Sstevel@tonic-gate **
83807c478bd9Sstevel@tonic-gate **	Results:
83817c478bd9Sstevel@tonic-gate **		true if item changed, false otherwise
83827c478bd9Sstevel@tonic-gate **
83837c478bd9Sstevel@tonic-gate **	Side Effects:
83847c478bd9Sstevel@tonic-gate **		Changes quarantine tag in queue file and renames it.
83857c478bd9Sstevel@tonic-gate */
83867c478bd9Sstevel@tonic-gate 
83877c478bd9Sstevel@tonic-gate static bool
83887c478bd9Sstevel@tonic-gate quarantine_queue_item(qgrp, qdir, e, reason)
83897c478bd9Sstevel@tonic-gate 	int qgrp;
83907c478bd9Sstevel@tonic-gate 	int qdir;
83917c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
83927c478bd9Sstevel@tonic-gate 	char *reason;
83937c478bd9Sstevel@tonic-gate {
83947c478bd9Sstevel@tonic-gate 	bool dirty = false;
83957c478bd9Sstevel@tonic-gate 	bool failing = false;
83967c478bd9Sstevel@tonic-gate 	bool foundq = false;
83977c478bd9Sstevel@tonic-gate 	bool finished = false;
83987c478bd9Sstevel@tonic-gate 	int fd;
83997c478bd9Sstevel@tonic-gate 	int flags;
84007c478bd9Sstevel@tonic-gate 	int oldtype;
84017c478bd9Sstevel@tonic-gate 	int newtype;
84027c478bd9Sstevel@tonic-gate 	int save_errno;
84037c478bd9Sstevel@tonic-gate 	MODE_T oldumask = 0;
84047c478bd9Sstevel@tonic-gate 	SM_FILE_T *oldqfp, *tempqfp;
84057c478bd9Sstevel@tonic-gate 	char *bp;
84067c478bd9Sstevel@tonic-gate 	char oldqf[MAXPATHLEN];
84077c478bd9Sstevel@tonic-gate 	char tempqf[MAXPATHLEN];
84087c478bd9Sstevel@tonic-gate 	char newqf[MAXPATHLEN];
84097c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
84107c478bd9Sstevel@tonic-gate 
84117c478bd9Sstevel@tonic-gate 	oldtype = queue_letter(e, ANYQFL_LETTER);
84127c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof oldqf);
84137c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof tempqf);
84147c478bd9Sstevel@tonic-gate 
84157c478bd9Sstevel@tonic-gate 	/*
84167c478bd9Sstevel@tonic-gate 	**  Instead of duplicating all the open
84177c478bd9Sstevel@tonic-gate 	**  and lock code here, tell readqf() to
84187c478bd9Sstevel@tonic-gate 	**  do that work and return the open
84197c478bd9Sstevel@tonic-gate 	**  file pointer in e_lockfp.  Note that
84207c478bd9Sstevel@tonic-gate 	**  we must release the locks properly when
84217c478bd9Sstevel@tonic-gate 	**  we are done.
84227c478bd9Sstevel@tonic-gate 	*/
84237c478bd9Sstevel@tonic-gate 
84247c478bd9Sstevel@tonic-gate 	if (!readqf(e, true))
84257c478bd9Sstevel@tonic-gate 	{
84267c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84277c478bd9Sstevel@tonic-gate 				     "Skipping %s\n", qid_printname(e));
84287c478bd9Sstevel@tonic-gate 		return false;
84297c478bd9Sstevel@tonic-gate 	}
84307c478bd9Sstevel@tonic-gate 	oldqfp = e->e_lockfp;
84317c478bd9Sstevel@tonic-gate 
84327c478bd9Sstevel@tonic-gate 	/* open the new queue file */
84337c478bd9Sstevel@tonic-gate 	flags = O_CREAT|O_WRONLY|O_EXCL;
84347c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
84357c478bd9Sstevel@tonic-gate 		oldumask = umask(002);
84367c478bd9Sstevel@tonic-gate 	fd = open(tempqf, flags, QueueFileMode);
84377c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
84387c478bd9Sstevel@tonic-gate 		(void) umask(oldumask);
84397c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
84407c478bd9Sstevel@tonic-gate 
84417c478bd9Sstevel@tonic-gate 	if (fd < 0)
84427c478bd9Sstevel@tonic-gate 	{
84437c478bd9Sstevel@tonic-gate 		save_errno = errno;
84447c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84457c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not open %s: %s\n",
84467c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf,
84477c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
84487c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
84497c478bd9Sstevel@tonic-gate 		return false;
84507c478bd9Sstevel@tonic-gate 	}
84517c478bd9Sstevel@tonic-gate 	if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
84527c478bd9Sstevel@tonic-gate 	{
84537c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84547c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not lock %s\n",
84557c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf);
84567c478bd9Sstevel@tonic-gate 		(void) close(fd);
84577c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
84587c478bd9Sstevel@tonic-gate 		return false;
84597c478bd9Sstevel@tonic-gate 	}
84607c478bd9Sstevel@tonic-gate 
84617c478bd9Sstevel@tonic-gate 	tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
84627c478bd9Sstevel@tonic-gate 			     SM_IO_WRONLY_B, NULL);
84637c478bd9Sstevel@tonic-gate 	if (tempqfp == NULL)
84647c478bd9Sstevel@tonic-gate 	{
84657c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84667c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not lock %s\n",
84677c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf);
84687c478bd9Sstevel@tonic-gate 		(void) close(fd);
84697c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
84707c478bd9Sstevel@tonic-gate 		return false;
84717c478bd9Sstevel@tonic-gate 	}
84727c478bd9Sstevel@tonic-gate 
84737c478bd9Sstevel@tonic-gate 	/* Copy the data over, changing the quarantine reason */
84747c478bd9Sstevel@tonic-gate 	while ((bp = fgetfolded(buf, sizeof buf, oldqfp)) != NULL)
84757c478bd9Sstevel@tonic-gate 	{
84767c478bd9Sstevel@tonic-gate 		if (tTd(40, 4))
84777c478bd9Sstevel@tonic-gate 			sm_dprintf("+++++ %s\n", bp);
84787c478bd9Sstevel@tonic-gate 		switch (bp[0])
84797c478bd9Sstevel@tonic-gate 		{
84807c478bd9Sstevel@tonic-gate 		  case 'q':		/* quarantine reason */
84817c478bd9Sstevel@tonic-gate 			foundq = true;
84827c478bd9Sstevel@tonic-gate 			if (reason == NULL)
84837c478bd9Sstevel@tonic-gate 			{
84847c478bd9Sstevel@tonic-gate 				if (Verbose)
84857c478bd9Sstevel@tonic-gate 				{
84867c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
84877c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
84887c478bd9Sstevel@tonic-gate 							     "%s: Removed quarantine of \"%s\"\n",
84897c478bd9Sstevel@tonic-gate 							     e->e_id, &bp[1]);
84907c478bd9Sstevel@tonic-gate 				}
84917c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "unquarantine");
84927c478bd9Sstevel@tonic-gate 				dirty = true;
84937c478bd9Sstevel@tonic-gate 				continue;
84947c478bd9Sstevel@tonic-gate 			}
84957c478bd9Sstevel@tonic-gate 			else if (strcmp(reason, &bp[1]) == 0)
84967c478bd9Sstevel@tonic-gate 			{
84977c478bd9Sstevel@tonic-gate 				if (Verbose)
84987c478bd9Sstevel@tonic-gate 				{
84997c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85007c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85017c478bd9Sstevel@tonic-gate 							     "%s: Already quarantined with \"%s\"\n",
85027c478bd9Sstevel@tonic-gate 							     e->e_id, reason);
85037c478bd9Sstevel@tonic-gate 				}
85047c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85057c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85067c478bd9Sstevel@tonic-gate 			}
85077c478bd9Sstevel@tonic-gate 			else
85087c478bd9Sstevel@tonic-gate 			{
85097c478bd9Sstevel@tonic-gate 				if (Verbose)
85107c478bd9Sstevel@tonic-gate 				{
85117c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85127c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85137c478bd9Sstevel@tonic-gate 							     "%s: Quarantine changed from \"%s\" to \"%s\"\n",
85147c478bd9Sstevel@tonic-gate 							     e->e_id, &bp[1],
85157c478bd9Sstevel@tonic-gate 							     reason);
85167c478bd9Sstevel@tonic-gate 				}
85177c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85187c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85197c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
85207c478bd9Sstevel@tonic-gate 					  reason);
85217c478bd9Sstevel@tonic-gate 				dirty = true;
85227c478bd9Sstevel@tonic-gate 			}
85237c478bd9Sstevel@tonic-gate 			break;
85247c478bd9Sstevel@tonic-gate 
85257c478bd9Sstevel@tonic-gate 		  case 'S':
85267c478bd9Sstevel@tonic-gate 			/*
85277c478bd9Sstevel@tonic-gate 			**  If we are quarantining an unquarantined item,
85287c478bd9Sstevel@tonic-gate 			**  need to put in a new 'q' line before it's
85297c478bd9Sstevel@tonic-gate 			**  too late.
85307c478bd9Sstevel@tonic-gate 			*/
85317c478bd9Sstevel@tonic-gate 
85327c478bd9Sstevel@tonic-gate 			if (!foundq && reason != NULL)
85337c478bd9Sstevel@tonic-gate 			{
85347c478bd9Sstevel@tonic-gate 				if (Verbose)
85357c478bd9Sstevel@tonic-gate 				{
85367c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85377c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85387c478bd9Sstevel@tonic-gate 							     "%s: Quarantined with \"%s\"\n",
85397c478bd9Sstevel@tonic-gate 							     e->e_id, reason);
85407c478bd9Sstevel@tonic-gate 				}
85417c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85427c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85437c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
85447c478bd9Sstevel@tonic-gate 					  reason);
85457c478bd9Sstevel@tonic-gate 				foundq = true;
85467c478bd9Sstevel@tonic-gate 				dirty = true;
85477c478bd9Sstevel@tonic-gate 			}
85487c478bd9Sstevel@tonic-gate 
85497c478bd9Sstevel@tonic-gate 			/* Copy the line to the new file */
85507c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85517c478bd9Sstevel@tonic-gate 					     "%s\n", bp);
85527c478bd9Sstevel@tonic-gate 			break;
85537c478bd9Sstevel@tonic-gate 
85547c478bd9Sstevel@tonic-gate 		  case '.':
85557c478bd9Sstevel@tonic-gate 			finished = true;
85567c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
85577c478bd9Sstevel@tonic-gate 
85587c478bd9Sstevel@tonic-gate 		  default:
85597c478bd9Sstevel@tonic-gate 			/* Copy the line to the new file */
85607c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85617c478bd9Sstevel@tonic-gate 					     "%s\n", bp);
85627c478bd9Sstevel@tonic-gate 			break;
85637c478bd9Sstevel@tonic-gate 		}
85647c478bd9Sstevel@tonic-gate 	}
85657c478bd9Sstevel@tonic-gate 
85667c478bd9Sstevel@tonic-gate 	/* Make sure we read the whole old file */
85677c478bd9Sstevel@tonic-gate 	errno = sm_io_error(tempqfp);
85687c478bd9Sstevel@tonic-gate 	if (errno != 0 && errno != SM_IO_EOF)
85697c478bd9Sstevel@tonic-gate 	{
85707c478bd9Sstevel@tonic-gate 		save_errno = errno;
85717c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
85727c478bd9Sstevel@tonic-gate 				     "Skipping %s: Error reading %s: %s\n",
85737c478bd9Sstevel@tonic-gate 				     qid_printname(e), oldqf,
85747c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
85757c478bd9Sstevel@tonic-gate 		failing = true;
85767c478bd9Sstevel@tonic-gate 	}
85777c478bd9Sstevel@tonic-gate 
85787c478bd9Sstevel@tonic-gate 	if (!failing && !finished)
85797c478bd9Sstevel@tonic-gate 	{
85807c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
85817c478bd9Sstevel@tonic-gate 				     "Skipping %s: Incomplete file: %s\n",
85827c478bd9Sstevel@tonic-gate 				     qid_printname(e), oldqf);
85837c478bd9Sstevel@tonic-gate 		failing = true;
85847c478bd9Sstevel@tonic-gate 	}
85857c478bd9Sstevel@tonic-gate 
85867c478bd9Sstevel@tonic-gate 	/* Check if we actually changed anything or we can just bail now */
85877c478bd9Sstevel@tonic-gate 	if (!dirty)
85887c478bd9Sstevel@tonic-gate 	{
85897c478bd9Sstevel@tonic-gate 		/* pretend we failed, even though we technically didn't */
85907c478bd9Sstevel@tonic-gate 		failing = true;
85917c478bd9Sstevel@tonic-gate 	}
85927c478bd9Sstevel@tonic-gate 
85937c478bd9Sstevel@tonic-gate 	/* Make sure we wrote things out safely */
85947c478bd9Sstevel@tonic-gate 	if (!failing &&
85957c478bd9Sstevel@tonic-gate 	    (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
85967c478bd9Sstevel@tonic-gate 	     ((SuperSafe == SAFE_REALLY ||
85977c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_REALLY_POSTMILTER ||
85987c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_INTERACTIVE) &&
85997c478bd9Sstevel@tonic-gate 	      fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
86007c478bd9Sstevel@tonic-gate 	     ((errno = sm_io_error(tempqfp)) != 0)))
86017c478bd9Sstevel@tonic-gate 	{
86027c478bd9Sstevel@tonic-gate 		save_errno = errno;
86037c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86047c478bd9Sstevel@tonic-gate 				     "Skipping %s: Error writing %s: %s\n",
86057c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf,
86067c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86077c478bd9Sstevel@tonic-gate 		failing = true;
86087c478bd9Sstevel@tonic-gate 	}
86097c478bd9Sstevel@tonic-gate 
86107c478bd9Sstevel@tonic-gate 
86117c478bd9Sstevel@tonic-gate 	/* Figure out the new filename */
86127c478bd9Sstevel@tonic-gate 	newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
86137c478bd9Sstevel@tonic-gate 	if (oldtype == newtype)
86147c478bd9Sstevel@tonic-gate 	{
86157c478bd9Sstevel@tonic-gate 		/* going to rename tempqf to oldqf */
86167c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(newqf, oldqf, sizeof newqf);
86177c478bd9Sstevel@tonic-gate 	}
86187c478bd9Sstevel@tonic-gate 	else
86197c478bd9Sstevel@tonic-gate 	{
86207c478bd9Sstevel@tonic-gate 		/* going to rename tempqf to new name based on newtype */
86217c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(newqf, queuename(e, newtype), sizeof newqf);
86227c478bd9Sstevel@tonic-gate 	}
86237c478bd9Sstevel@tonic-gate 
86247c478bd9Sstevel@tonic-gate 	save_errno = 0;
86257c478bd9Sstevel@tonic-gate 
86267c478bd9Sstevel@tonic-gate 	/* rename tempqf to newqf */
86277c478bd9Sstevel@tonic-gate 	if (!failing &&
86287c478bd9Sstevel@tonic-gate 	    rename(tempqf, newqf) < 0)
86297c478bd9Sstevel@tonic-gate 		save_errno = (errno == 0) ? EINVAL : errno;
86307c478bd9Sstevel@tonic-gate 
86317c478bd9Sstevel@tonic-gate 	/* Check rename() success */
86327c478bd9Sstevel@tonic-gate 	if (!failing && save_errno != 0)
86337c478bd9Sstevel@tonic-gate 	{
86347c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id,
86357c478bd9Sstevel@tonic-gate 			  "quarantine_queue_item: rename(%s, %s): %s",
86367c478bd9Sstevel@tonic-gate 			  tempqf, newqf, sm_errstring(save_errno));
86377c478bd9Sstevel@tonic-gate 
86387c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86397c478bd9Sstevel@tonic-gate 				     "Error renaming %s to %s: %s\n",
86407c478bd9Sstevel@tonic-gate 				     tempqf, newqf,
86417c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86427c478bd9Sstevel@tonic-gate 		if (oldtype == newtype)
86437c478bd9Sstevel@tonic-gate 		{
86447c478bd9Sstevel@tonic-gate 			/*
86457c478bd9Sstevel@tonic-gate 			**  Bail here since we don't know the state of
86467c478bd9Sstevel@tonic-gate 			**  the filesystem and may need to keep tempqf
86477c478bd9Sstevel@tonic-gate 			**  for the user to rescue us.
86487c478bd9Sstevel@tonic-gate 			*/
86497c478bd9Sstevel@tonic-gate 
86507c478bd9Sstevel@tonic-gate 			RELEASE_QUEUE;
86517c478bd9Sstevel@tonic-gate 			errno = save_errno;
86527c478bd9Sstevel@tonic-gate 			syserr("!452 Error renaming control file %s", tempqf);
86537c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
86547c478bd9Sstevel@tonic-gate 		}
86557c478bd9Sstevel@tonic-gate 		else
86567c478bd9Sstevel@tonic-gate 		{
86577c478bd9Sstevel@tonic-gate 			/* remove new file (if rename() half completed) */
86587c478bd9Sstevel@tonic-gate 			if (xunlink(newqf) < 0)
86597c478bd9Sstevel@tonic-gate 			{
86607c478bd9Sstevel@tonic-gate 				save_errno = errno;
86617c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86627c478bd9Sstevel@tonic-gate 						     "Error removing %s: %s\n",
86637c478bd9Sstevel@tonic-gate 						     newqf,
86647c478bd9Sstevel@tonic-gate 						     sm_errstring(save_errno));
86657c478bd9Sstevel@tonic-gate 			}
86667c478bd9Sstevel@tonic-gate 
86677c478bd9Sstevel@tonic-gate 			/* tempqf removed below */
86687c478bd9Sstevel@tonic-gate 			failing = true;
86697c478bd9Sstevel@tonic-gate 		}
86707c478bd9Sstevel@tonic-gate 
86717c478bd9Sstevel@tonic-gate 	}
86727c478bd9Sstevel@tonic-gate 
86737c478bd9Sstevel@tonic-gate 	/* If changing file types, need to remove old type */
86747c478bd9Sstevel@tonic-gate 	if (!failing && oldtype != newtype)
86757c478bd9Sstevel@tonic-gate 	{
86767c478bd9Sstevel@tonic-gate 		if (xunlink(oldqf) < 0)
86777c478bd9Sstevel@tonic-gate 		{
86787c478bd9Sstevel@tonic-gate 			save_errno = errno;
86797c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86807c478bd9Sstevel@tonic-gate 					     "Error removing %s: %s\n",
86817c478bd9Sstevel@tonic-gate 					     oldqf, sm_errstring(save_errno));
86827c478bd9Sstevel@tonic-gate 		}
86837c478bd9Sstevel@tonic-gate 	}
86847c478bd9Sstevel@tonic-gate 
86857c478bd9Sstevel@tonic-gate 	/* see if anything above failed */
86867c478bd9Sstevel@tonic-gate 	if (failing)
86877c478bd9Sstevel@tonic-gate 	{
86887c478bd9Sstevel@tonic-gate 		/* Something failed: remove new file, old file still there */
86897c478bd9Sstevel@tonic-gate 		(void) xunlink(tempqf);
86907c478bd9Sstevel@tonic-gate 	}
86917c478bd9Sstevel@tonic-gate 
86927c478bd9Sstevel@tonic-gate 	/*
86937c478bd9Sstevel@tonic-gate 	**  fsync() after file operations to make sure metadata is
86947c478bd9Sstevel@tonic-gate 	**  written to disk on filesystems in which renames are
86957c478bd9Sstevel@tonic-gate 	**  not guaranteed.  It's ok if they fail, mail won't be lost.
86967c478bd9Sstevel@tonic-gate 	*/
86977c478bd9Sstevel@tonic-gate 
86987c478bd9Sstevel@tonic-gate 	if (SuperSafe != SAFE_NO)
86997c478bd9Sstevel@tonic-gate 	{
87007c478bd9Sstevel@tonic-gate 		/* for soft-updates */
87017c478bd9Sstevel@tonic-gate 		(void) fsync(sm_io_getinfo(tempqfp,
87027c478bd9Sstevel@tonic-gate 					   SM_IO_WHAT_FD, NULL));
87037c478bd9Sstevel@tonic-gate 
87047c478bd9Sstevel@tonic-gate 		if (!failing)
87057c478bd9Sstevel@tonic-gate 		{
87067c478bd9Sstevel@tonic-gate 			/* for soft-updates */
87077c478bd9Sstevel@tonic-gate 			(void) fsync(sm_io_getinfo(oldqfp,
87087c478bd9Sstevel@tonic-gate 						   SM_IO_WHAT_FD, NULL));
87097c478bd9Sstevel@tonic-gate 		}
87107c478bd9Sstevel@tonic-gate 
87117c478bd9Sstevel@tonic-gate 		/* for other odd filesystems */
87127c478bd9Sstevel@tonic-gate 		SYNC_DIR(tempqf, false);
87137c478bd9Sstevel@tonic-gate 	}
87147c478bd9Sstevel@tonic-gate 
87157c478bd9Sstevel@tonic-gate 	/* Close up shop */
87167c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
87177c478bd9Sstevel@tonic-gate 	if (tempqfp != NULL)
87187c478bd9Sstevel@tonic-gate 		(void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
87197c478bd9Sstevel@tonic-gate 	if (oldqfp != NULL)
87207c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
87217c478bd9Sstevel@tonic-gate 
87227c478bd9Sstevel@tonic-gate 	/* All went well */
87237c478bd9Sstevel@tonic-gate 	return !failing;
87247c478bd9Sstevel@tonic-gate }
87257c478bd9Sstevel@tonic-gate 
87267c478bd9Sstevel@tonic-gate /*
87277c478bd9Sstevel@tonic-gate **  QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
87287c478bd9Sstevel@tonic-gate **
87297c478bd9Sstevel@tonic-gate **	Read all matching queue items, add/remove quarantine
87307c478bd9Sstevel@tonic-gate **	reason, and requeue appropriately.
87317c478bd9Sstevel@tonic-gate **
87327c478bd9Sstevel@tonic-gate **	Parameters:
87337c478bd9Sstevel@tonic-gate **		reason -- quarantine reason, "." means unquarantine.
87347c478bd9Sstevel@tonic-gate **		qgrplimit -- limit to single queue group unless NOQGRP
87357c478bd9Sstevel@tonic-gate **
87367c478bd9Sstevel@tonic-gate **	Results:
87377c478bd9Sstevel@tonic-gate **		none.
87387c478bd9Sstevel@tonic-gate **
87397c478bd9Sstevel@tonic-gate **	Side Effects:
87407c478bd9Sstevel@tonic-gate **		Lots of changes to the queue.
87417c478bd9Sstevel@tonic-gate */
87427c478bd9Sstevel@tonic-gate 
87437c478bd9Sstevel@tonic-gate void
87447c478bd9Sstevel@tonic-gate quarantine_queue(reason, qgrplimit)
87457c478bd9Sstevel@tonic-gate 	char *reason;
87467c478bd9Sstevel@tonic-gate 	int qgrplimit;
87477c478bd9Sstevel@tonic-gate {
87487c478bd9Sstevel@tonic-gate 	int changed = 0;
87497c478bd9Sstevel@tonic-gate 	int qgrp;
87507c478bd9Sstevel@tonic-gate 
87517c478bd9Sstevel@tonic-gate 	/* Convert internal representation of unquarantine */
87527c478bd9Sstevel@tonic-gate 	if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
87537c478bd9Sstevel@tonic-gate 		reason = NULL;
87547c478bd9Sstevel@tonic-gate 
87557c478bd9Sstevel@tonic-gate 	if (reason != NULL)
87567c478bd9Sstevel@tonic-gate 	{
87577c478bd9Sstevel@tonic-gate 		/* clean it */
87587c478bd9Sstevel@tonic-gate 		reason = newstr(denlstring(reason, true, true));
87597c478bd9Sstevel@tonic-gate 	}
87607c478bd9Sstevel@tonic-gate 
87617c478bd9Sstevel@tonic-gate 	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
87627c478bd9Sstevel@tonic-gate 	{
87637c478bd9Sstevel@tonic-gate 		int qdir;
87647c478bd9Sstevel@tonic-gate 
87657c478bd9Sstevel@tonic-gate 		if (qgrplimit != NOQGRP && qgrplimit != qgrp)
87667c478bd9Sstevel@tonic-gate 			continue;
87677c478bd9Sstevel@tonic-gate 
87687c478bd9Sstevel@tonic-gate 		for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
87697c478bd9Sstevel@tonic-gate 		{
87707c478bd9Sstevel@tonic-gate 			int i;
87717c478bd9Sstevel@tonic-gate 			int nrequests;
87727c478bd9Sstevel@tonic-gate 
87737c478bd9Sstevel@tonic-gate 			if (StopRequest)
87747c478bd9Sstevel@tonic-gate 				stop_sendmail();
87757c478bd9Sstevel@tonic-gate 
87767c478bd9Sstevel@tonic-gate 			nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
87777c478bd9Sstevel@tonic-gate 
87787c478bd9Sstevel@tonic-gate 			/* first see if there is anything */
87797c478bd9Sstevel@tonic-gate 			if (nrequests <= 0)
87807c478bd9Sstevel@tonic-gate 			{
87817c478bd9Sstevel@tonic-gate 				if (Verbose)
87827c478bd9Sstevel@tonic-gate 				{
87837c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
87847c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT, "%s: no matches\n",
87857c478bd9Sstevel@tonic-gate 							     qid_printqueue(qgrp, qdir));
87867c478bd9Sstevel@tonic-gate 				}
87877c478bd9Sstevel@tonic-gate 				continue;
87887c478bd9Sstevel@tonic-gate 			}
87897c478bd9Sstevel@tonic-gate 
87907c478bd9Sstevel@tonic-gate 			if (Verbose)
87917c478bd9Sstevel@tonic-gate 			{
87927c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout,
87937c478bd9Sstevel@tonic-gate 						     SM_TIME_DEFAULT, "Processing %s:\n",
87947c478bd9Sstevel@tonic-gate 						     qid_printqueue(qgrp, qdir));
87957c478bd9Sstevel@tonic-gate 			}
87967c478bd9Sstevel@tonic-gate 
87977c478bd9Sstevel@tonic-gate 			for (i = 0; i < WorkListCount; i++)
87987c478bd9Sstevel@tonic-gate 			{
87997c478bd9Sstevel@tonic-gate 				ENVELOPE e;
88007c478bd9Sstevel@tonic-gate 
88017c478bd9Sstevel@tonic-gate 				if (StopRequest)
88027c478bd9Sstevel@tonic-gate 					stop_sendmail();
88037c478bd9Sstevel@tonic-gate 
88047c478bd9Sstevel@tonic-gate 				/* setup envelope */
88057c478bd9Sstevel@tonic-gate 				clearenvelope(&e, true, sm_rpool_new_x(NULL));
88067c478bd9Sstevel@tonic-gate 				e.e_id = WorkList[i].w_name + 2;
88077c478bd9Sstevel@tonic-gate 				e.e_qgrp = qgrp;
88087c478bd9Sstevel@tonic-gate 				e.e_qdir = qdir;
88097c478bd9Sstevel@tonic-gate 
88107c478bd9Sstevel@tonic-gate 				if (tTd(70, 101))
88117c478bd9Sstevel@tonic-gate 				{
88127c478bd9Sstevel@tonic-gate 					sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88137c478bd9Sstevel@tonic-gate 						      "Would do %s\n", e.e_id);
88147c478bd9Sstevel@tonic-gate 					changed++;
88157c478bd9Sstevel@tonic-gate 				}
88167c478bd9Sstevel@tonic-gate 				else if (quarantine_queue_item(qgrp, qdir,
88177c478bd9Sstevel@tonic-gate 							       &e, reason))
88187c478bd9Sstevel@tonic-gate 					changed++;
88197c478bd9Sstevel@tonic-gate 
88207c478bd9Sstevel@tonic-gate 				/* clean up */
88217c478bd9Sstevel@tonic-gate 				sm_rpool_free(e.e_rpool);
88227c478bd9Sstevel@tonic-gate 				e.e_rpool = NULL;
88237c478bd9Sstevel@tonic-gate 			}
88247c478bd9Sstevel@tonic-gate 			if (WorkList != NULL)
88257c478bd9Sstevel@tonic-gate 				sm_free(WorkList); /* XXX */
88267c478bd9Sstevel@tonic-gate 			WorkList = NULL;
88277c478bd9Sstevel@tonic-gate 			WorkListSize = 0;
88287c478bd9Sstevel@tonic-gate 			WorkListCount = 0;
88297c478bd9Sstevel@tonic-gate 		}
88307c478bd9Sstevel@tonic-gate 	}
88317c478bd9Sstevel@tonic-gate 	if (Verbose)
88327c478bd9Sstevel@tonic-gate 	{
88337c478bd9Sstevel@tonic-gate 		if (changed == 0)
88347c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88357c478bd9Sstevel@tonic-gate 					     "No changes\n");
88367c478bd9Sstevel@tonic-gate 		else
88377c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88387c478bd9Sstevel@tonic-gate 					     "%d change%s\n",
88397c478bd9Sstevel@tonic-gate 					     changed,
88407c478bd9Sstevel@tonic-gate 					     changed == 1 ? "" : "s");
88417c478bd9Sstevel@tonic-gate 	}
88427c478bd9Sstevel@tonic-gate }
8843