xref: /illumos-gate/usr/src/cmd/sendmail/src/queue.c (revision 7800901e)
17c478bd9Sstevel@tonic-gate /*
24aac33d3Sjbeck  * Copyright (c) 1998-2007 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*7800901eSjbeck SM_RCSID("@(#)$Id: queue.c,v 8.975 2007/06/18 20:08:40 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 */
152058561cbSjbeck 
153058561cbSjbeck /*
154058561cbSjbeck **  Note: workcmpf?() don't use a prototype because it will cause a conflict
155058561cbSjbeck **  with the qsort() call (which expects something like
156058561cbSjbeck **  int (*compar)(const void *, const void *), not (WORK *, WORK *))
157058561cbSjbeck */
158058561cbSjbeck 
1597c478bd9Sstevel@tonic-gate static int	workcmpf0();
1607c478bd9Sstevel@tonic-gate static int	workcmpf1();
1617c478bd9Sstevel@tonic-gate static int	workcmpf2();
1627c478bd9Sstevel@tonic-gate static int	workcmpf3();
1637c478bd9Sstevel@tonic-gate static int	workcmpf4();
1647c478bd9Sstevel@tonic-gate static int	randi = 3;	/* index for workcmpf5() */
1657c478bd9Sstevel@tonic-gate static int	workcmpf5();
1667c478bd9Sstevel@tonic-gate static int	workcmpf6();
1677c478bd9Sstevel@tonic-gate #if _FFR_RHS
1687c478bd9Sstevel@tonic-gate static int	workcmpf7();
1697c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate #if RANDOMSHIFT
1727c478bd9Sstevel@tonic-gate # define get_rand_mod(m)	((get_random() >> RANDOMSHIFT) % (m))
1737c478bd9Sstevel@tonic-gate #else /* RANDOMSHIFT */
1747c478bd9Sstevel@tonic-gate # define get_rand_mod(m)	(get_random() % (m))
1757c478bd9Sstevel@tonic-gate #endif /* RANDOMSHIFT */
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate /*
1787c478bd9Sstevel@tonic-gate **  File system definition.
1797c478bd9Sstevel@tonic-gate **	Used to keep track of how much free space is available
1807c478bd9Sstevel@tonic-gate **	on a file system in which one or more queue directories reside.
1817c478bd9Sstevel@tonic-gate */
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate typedef struct filesys_shared	FILESYS;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate struct filesys_shared
1867c478bd9Sstevel@tonic-gate {
1877c478bd9Sstevel@tonic-gate 	dev_t	fs_dev;		/* unique device id */
1887c478bd9Sstevel@tonic-gate 	long	fs_avail;	/* number of free blocks available */
1897c478bd9Sstevel@tonic-gate 	long	fs_blksize;	/* block size, in bytes */
1907c478bd9Sstevel@tonic-gate };
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate /* probably kept in shared memory */
1937c478bd9Sstevel@tonic-gate static FILESYS	FileSys[MAXFILESYS];	/* queue file systems */
194058561cbSjbeck static const char *FSPath[MAXFILESYS];	/* pathnames for file systems */
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate /*
1997c478bd9Sstevel@tonic-gate **  Shared memory data
2007c478bd9Sstevel@tonic-gate **
2017c478bd9Sstevel@tonic-gate **  Current layout:
2027c478bd9Sstevel@tonic-gate **	size -- size of shared memory segment
2037c478bd9Sstevel@tonic-gate **	pid -- pid of owner, should be a unique id to avoid misinterpretations
2047c478bd9Sstevel@tonic-gate **		by other processes.
2057c478bd9Sstevel@tonic-gate **	tag -- should be a unique id to avoid misinterpretations by others.
2067c478bd9Sstevel@tonic-gate **		idea: hash over configuration data that will be stored here.
2077c478bd9Sstevel@tonic-gate **	NumFileSys -- number of file systems.
2087c478bd9Sstevel@tonic-gate **	FileSys -- (arrary of) structure for used file systems.
2097c478bd9Sstevel@tonic-gate **	RSATmpCnt -- counter for number of uses of ephemeral RSA key.
2107c478bd9Sstevel@tonic-gate **	QShm -- (array of) structure for information about queue directories.
2117c478bd9Sstevel@tonic-gate */
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate /*
2147c478bd9Sstevel@tonic-gate **  Queue data in shared memory
2157c478bd9Sstevel@tonic-gate */
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate typedef struct queue_shared	QUEUE_SHM_T;
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate struct queue_shared
2207c478bd9Sstevel@tonic-gate {
2217c478bd9Sstevel@tonic-gate 	int	qs_entries;	/* number of entries */
2227c478bd9Sstevel@tonic-gate 	/* XXX more to follow? */
2237c478bd9Sstevel@tonic-gate };
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate static void	*Pshm;		/* pointer to shared memory */
2267c478bd9Sstevel@tonic-gate static FILESYS	*PtrFileSys;	/* pointer to queue file system array */
2277c478bd9Sstevel@tonic-gate int		ShmId = SM_SHM_NO_ID;	/* shared memory id */
2287c478bd9Sstevel@tonic-gate static QUEUE_SHM_T	*QShm;		/* pointer to shared queue data */
2297c478bd9Sstevel@tonic-gate static size_t shms;
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate # define SHM_OFF_PID(p)	(((char *) (p)) + sizeof(int))
2327c478bd9Sstevel@tonic-gate # define SHM_OFF_TAG(p)	(((char *) (p)) + sizeof(pid_t) + sizeof(int))
2337c478bd9Sstevel@tonic-gate # define SHM_OFF_HEAD	(sizeof(pid_t) + sizeof(int) * 2)
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate /* how to access FileSys */
2367c478bd9Sstevel@tonic-gate # define FILE_SYS(i)	(PtrFileSys[i])
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate /* first entry is a tag, for now just the size */
2397c478bd9Sstevel@tonic-gate # define OFF_FILE_SYS(p)	(((char *) (p)) + SHM_OFF_HEAD)
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate /* offset for PNumFileSys */
2427c478bd9Sstevel@tonic-gate # define OFF_NUM_FILE_SYS(p)	(((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys))
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate /* offset for PRSATmpCnt */
2457c478bd9Sstevel@tonic-gate # define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int))
2467c478bd9Sstevel@tonic-gate int	*PRSATmpCnt;
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate /* offset for queue_shm */
2497c478bd9Sstevel@tonic-gate # define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate # define QSHM_ENTRIES(i)	QShm[i].qs_entries
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate /* basic size of shared memory segment */
2547c478bd9Sstevel@tonic-gate # define SM_T_SIZE	(SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate static unsigned int	hash_q __P((char *, unsigned int));
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate /*
2597c478bd9Sstevel@tonic-gate **  HASH_Q -- simple hash function
2607c478bd9Sstevel@tonic-gate **
2617c478bd9Sstevel@tonic-gate **	Parameters:
2627c478bd9Sstevel@tonic-gate **		p -- string to hash.
2637c478bd9Sstevel@tonic-gate **		h -- hash start value (from previous run).
2647c478bd9Sstevel@tonic-gate **
2657c478bd9Sstevel@tonic-gate **	Returns:
2667c478bd9Sstevel@tonic-gate **		hash value.
2677c478bd9Sstevel@tonic-gate */
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate static unsigned int
2707c478bd9Sstevel@tonic-gate hash_q(p, h)
2717c478bd9Sstevel@tonic-gate 	char *p;
2727c478bd9Sstevel@tonic-gate 	unsigned int h;
2737c478bd9Sstevel@tonic-gate {
2747c478bd9Sstevel@tonic-gate 	int c, d;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	while (*p != '\0')
2777c478bd9Sstevel@tonic-gate 	{
2787c478bd9Sstevel@tonic-gate 		d = *p++;
2797c478bd9Sstevel@tonic-gate 		c = d;
2807c478bd9Sstevel@tonic-gate 		c ^= c<<6;
2817c478bd9Sstevel@tonic-gate 		h += (c<<11) ^ (c>>1);
2827c478bd9Sstevel@tonic-gate 		h ^= (d<<14) + (d<<7) + (d<<4) + d;
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 	return h;
2857c478bd9Sstevel@tonic-gate }
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
2897c478bd9Sstevel@tonic-gate # define FILE_SYS(i)	FileSys[i]
2907c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate /* access to the various components of file system data */
2937c478bd9Sstevel@tonic-gate #define FILE_SYS_NAME(i)	FSPath[i]
2947c478bd9Sstevel@tonic-gate #define FILE_SYS_AVAIL(i)	FILE_SYS(i).fs_avail
2957c478bd9Sstevel@tonic-gate #define FILE_SYS_BLKSIZE(i)	FILE_SYS(i).fs_blksize
2967c478bd9Sstevel@tonic-gate #define FILE_SYS_DEV(i)	FILE_SYS(i).fs_dev
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate /*
3007c478bd9Sstevel@tonic-gate **  Current qf file field assignments:
3017c478bd9Sstevel@tonic-gate **
3027c478bd9Sstevel@tonic-gate **	A	AUTH= parameter
3037c478bd9Sstevel@tonic-gate **	B	body type
3047c478bd9Sstevel@tonic-gate **	C	controlling user
3057c478bd9Sstevel@tonic-gate **	D	data file name
3067c478bd9Sstevel@tonic-gate **	d	data file directory name (added in 8.12)
3077c478bd9Sstevel@tonic-gate **	E	error recipient
3087c478bd9Sstevel@tonic-gate **	F	flag bits
3097c478bd9Sstevel@tonic-gate **	G	free (was: queue delay algorithm if _FFR_QUEUEDELAY)
3107c478bd9Sstevel@tonic-gate **	H	header
3117c478bd9Sstevel@tonic-gate **	I	data file's inode number
3127c478bd9Sstevel@tonic-gate **	K	time of last delivery attempt
3137c478bd9Sstevel@tonic-gate **	L	Solaris Content-Length: header (obsolete)
3147c478bd9Sstevel@tonic-gate **	M	message
3157c478bd9Sstevel@tonic-gate **	N	number of delivery attempts
3167c478bd9Sstevel@tonic-gate **	P	message priority
3177c478bd9Sstevel@tonic-gate **	q	quarantine reason
3187c478bd9Sstevel@tonic-gate **	Q	original recipient (ORCPT=)
3197c478bd9Sstevel@tonic-gate **	r	final recipient (Final-Recipient: DSN field)
3207c478bd9Sstevel@tonic-gate **	R	recipient
3217c478bd9Sstevel@tonic-gate **	S	sender
3227c478bd9Sstevel@tonic-gate **	T	init time
3237c478bd9Sstevel@tonic-gate **	V	queue file version
3247c478bd9Sstevel@tonic-gate **	X	free (was: character set if _FFR_SAVE_CHARSET)
3257c478bd9Sstevel@tonic-gate **	Y	free (was: current delay if _FFR_QUEUEDELAY)
3267c478bd9Sstevel@tonic-gate **	Z	original envelope id from ESMTP
3277c478bd9Sstevel@tonic-gate **	!	deliver by (added in 8.12)
3287c478bd9Sstevel@tonic-gate **	$	define macro
3297c478bd9Sstevel@tonic-gate **	.	terminate file
3307c478bd9Sstevel@tonic-gate */
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate /*
3337c478bd9Sstevel@tonic-gate **  QUEUEUP -- queue a message up for future transmission.
3347c478bd9Sstevel@tonic-gate **
3357c478bd9Sstevel@tonic-gate **	Parameters:
3367c478bd9Sstevel@tonic-gate **		e -- the envelope to queue up.
3377c478bd9Sstevel@tonic-gate **		announce -- if true, tell when you are queueing up.
3387c478bd9Sstevel@tonic-gate **		msync -- if true, then fsync() if SuperSafe interactive mode.
3397c478bd9Sstevel@tonic-gate **
3407c478bd9Sstevel@tonic-gate **	Returns:
3417c478bd9Sstevel@tonic-gate **		none.
3427c478bd9Sstevel@tonic-gate **
3437c478bd9Sstevel@tonic-gate **	Side Effects:
3447c478bd9Sstevel@tonic-gate **		The current request is saved in a control file.
3457c478bd9Sstevel@tonic-gate **		The queue file is left locked.
3467c478bd9Sstevel@tonic-gate */
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate void
3497c478bd9Sstevel@tonic-gate queueup(e, announce, msync)
3507c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
3517c478bd9Sstevel@tonic-gate 	bool announce;
3527c478bd9Sstevel@tonic-gate 	bool msync;
3537c478bd9Sstevel@tonic-gate {
3547c478bd9Sstevel@tonic-gate 	register SM_FILE_T *tfp;
3557c478bd9Sstevel@tonic-gate 	register HDR *h;
3567c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
3577c478bd9Sstevel@tonic-gate 	int tfd = -1;
3587c478bd9Sstevel@tonic-gate 	int i;
3597c478bd9Sstevel@tonic-gate 	bool newid;
3607c478bd9Sstevel@tonic-gate 	register char *p;
3617c478bd9Sstevel@tonic-gate 	MAILER nullmailer;
3627c478bd9Sstevel@tonic-gate 	MCI mcibuf;
3637c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
3647c478bd9Sstevel@tonic-gate 	char tf[MAXPATHLEN];
3657c478bd9Sstevel@tonic-gate 	char df[MAXPATHLEN];
3667c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	/*
3697c478bd9Sstevel@tonic-gate 	**  Create control file.
3707c478bd9Sstevel@tonic-gate 	*/
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate #define OPEN_TF	do							\
3737c478bd9Sstevel@tonic-gate 		{							\
3747c478bd9Sstevel@tonic-gate 			MODE_T oldumask = 0;				\
3757c478bd9Sstevel@tonic-gate 									\
3767c478bd9Sstevel@tonic-gate 			if (bitset(S_IWGRP, QueueFileMode))		\
3777c478bd9Sstevel@tonic-gate 				oldumask = umask(002);			\
3787c478bd9Sstevel@tonic-gate 			tfd = open(tf, TF_OPEN_FLAGS, QueueFileMode);	\
3797c478bd9Sstevel@tonic-gate 			if (bitset(S_IWGRP, QueueFileMode))		\
3807c478bd9Sstevel@tonic-gate 				(void) umask(oldumask);			\
3817c478bd9Sstevel@tonic-gate 		} while (0)
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
385058561cbSjbeck 	(void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof(tf));
3867c478bd9Sstevel@tonic-gate 	tfp = e->e_lockfp;
3877c478bd9Sstevel@tonic-gate 	if (tfp == NULL && newid)
3887c478bd9Sstevel@tonic-gate 	{
3897c478bd9Sstevel@tonic-gate 		/*
3907c478bd9Sstevel@tonic-gate 		**  open qf file directly: this will give an error if the file
3917c478bd9Sstevel@tonic-gate 		**  already exists and hence prevent problems if a queue-id
3927c478bd9Sstevel@tonic-gate 		**  is reused (e.g., because the clock is set back).
3937c478bd9Sstevel@tonic-gate 		*/
3947c478bd9Sstevel@tonic-gate 
395058561cbSjbeck 		(void) sm_strlcpy(tf, queuename(e, ANYQFL_LETTER), sizeof(tf));
3967c478bd9Sstevel@tonic-gate 		OPEN_TF;
3977c478bd9Sstevel@tonic-gate 		if (tfd < 0 ||
3987c478bd9Sstevel@tonic-gate #if !SM_OPEN_EXLOCK
3997c478bd9Sstevel@tonic-gate 		    !lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB) ||
4007c478bd9Sstevel@tonic-gate #endif /* !SM_OPEN_EXLOCK */
4017c478bd9Sstevel@tonic-gate 		    (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
4027c478bd9Sstevel@tonic-gate 					 (void *) &tfd, SM_IO_WRONLY,
4037c478bd9Sstevel@tonic-gate 					 NULL)) == NULL)
4047c478bd9Sstevel@tonic-gate 		{
4057c478bd9Sstevel@tonic-gate 			int save_errno = errno;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 			printopenfds(true);
4087c478bd9Sstevel@tonic-gate 			errno = save_errno;
4097c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create queue file %s, euid=%d, fd=%d, fp=%p",
4107c478bd9Sstevel@tonic-gate 				tf, (int) geteuid(), tfd, tfp);
4117c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4127c478bd9Sstevel@tonic-gate 		}
4137c478bd9Sstevel@tonic-gate 		e->e_lockfp = tfp;
4147c478bd9Sstevel@tonic-gate 		upd_qs(e, 1, 0, "queueup");
4157c478bd9Sstevel@tonic-gate 	}
4167c478bd9Sstevel@tonic-gate 
4177c478bd9Sstevel@tonic-gate 	/* if newid, write the queue file directly (instead of temp file) */
4187c478bd9Sstevel@tonic-gate 	if (!newid)
4197c478bd9Sstevel@tonic-gate 	{
4207c478bd9Sstevel@tonic-gate 		/* get a locked tf file */
4217c478bd9Sstevel@tonic-gate 		for (i = 0; i < 128; i++)
4227c478bd9Sstevel@tonic-gate 		{
4237c478bd9Sstevel@tonic-gate 			if (tfd < 0)
4247c478bd9Sstevel@tonic-gate 			{
4257c478bd9Sstevel@tonic-gate 				OPEN_TF;
4267c478bd9Sstevel@tonic-gate 				if (tfd < 0)
4277c478bd9Sstevel@tonic-gate 				{
4287c478bd9Sstevel@tonic-gate 					if (errno != EEXIST)
4297c478bd9Sstevel@tonic-gate 						break;
4307c478bd9Sstevel@tonic-gate 					if (LogLevel > 0 && (i % 32) == 0)
4317c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ALERT, e->e_id,
432*7800901eSjbeck 							  "queueup: cannot create %s, euid=%d: %s",
4337c478bd9Sstevel@tonic-gate 							  tf, (int) geteuid(),
4347c478bd9Sstevel@tonic-gate 							  sm_errstring(errno));
4357c478bd9Sstevel@tonic-gate 				}
4367c478bd9Sstevel@tonic-gate #if SM_OPEN_EXLOCK
4377c478bd9Sstevel@tonic-gate 				else
4387c478bd9Sstevel@tonic-gate 					break;
4397c478bd9Sstevel@tonic-gate #endif /* SM_OPEN_EXLOCK */
4407c478bd9Sstevel@tonic-gate 			}
4417c478bd9Sstevel@tonic-gate 			if (tfd >= 0)
4427c478bd9Sstevel@tonic-gate 			{
4437c478bd9Sstevel@tonic-gate #if SM_OPEN_EXLOCK
4447c478bd9Sstevel@tonic-gate 				/* file is locked by open() */
4457c478bd9Sstevel@tonic-gate 				break;
4467c478bd9Sstevel@tonic-gate #else /* SM_OPEN_EXLOCK */
4477c478bd9Sstevel@tonic-gate 				if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
4487c478bd9Sstevel@tonic-gate 					break;
4497c478bd9Sstevel@tonic-gate 				else
4507c478bd9Sstevel@tonic-gate #endif /* SM_OPEN_EXLOCK */
4517c478bd9Sstevel@tonic-gate 				if (LogLevel > 0 && (i % 32) == 0)
4527c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ALERT, e->e_id,
4537c478bd9Sstevel@tonic-gate 						  "queueup: cannot lock %s: %s",
4547c478bd9Sstevel@tonic-gate 						  tf, sm_errstring(errno));
4557c478bd9Sstevel@tonic-gate 				if ((i % 32) == 31)
4567c478bd9Sstevel@tonic-gate 				{
4577c478bd9Sstevel@tonic-gate 					(void) close(tfd);
4587c478bd9Sstevel@tonic-gate 					tfd = -1;
4597c478bd9Sstevel@tonic-gate 				}
4607c478bd9Sstevel@tonic-gate 			}
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 			if ((i % 32) == 31)
4637c478bd9Sstevel@tonic-gate 			{
4647c478bd9Sstevel@tonic-gate 				/* save the old temp file away */
4657c478bd9Sstevel@tonic-gate 				(void) rename(tf, queuename(e, TEMPQF_LETTER));
4667c478bd9Sstevel@tonic-gate 			}
4677c478bd9Sstevel@tonic-gate 			else
4687c478bd9Sstevel@tonic-gate 				(void) sleep(i % 32);
4697c478bd9Sstevel@tonic-gate 		}
4707c478bd9Sstevel@tonic-gate 		if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
4717c478bd9Sstevel@tonic-gate 						 (void *) &tfd, SM_IO_WRONLY_B,
4727c478bd9Sstevel@tonic-gate 						 NULL)) == NULL)
4737c478bd9Sstevel@tonic-gate 		{
4747c478bd9Sstevel@tonic-gate 			int save_errno = errno;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 			printopenfds(true);
4777c478bd9Sstevel@tonic-gate 			errno = save_errno;
4787c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create queue temp file %s, uid=%d",
4797c478bd9Sstevel@tonic-gate 				tf, (int) geteuid());
4807c478bd9Sstevel@tonic-gate 		}
4817c478bd9Sstevel@tonic-gate 	}
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
4847c478bd9Sstevel@tonic-gate 		sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n",
4857c478bd9Sstevel@tonic-gate 			   qid_printqueue(e->e_qgrp, e->e_qdir),
4867c478bd9Sstevel@tonic-gate 			   queuename(e, ANYQFL_LETTER),
4877c478bd9Sstevel@tonic-gate 			   newid ? " (new id)" : "");
4887c478bd9Sstevel@tonic-gate 	if (tTd(40, 3))
4897c478bd9Sstevel@tonic-gate 	{
4907c478bd9Sstevel@tonic-gate 		sm_dprintf("  e_flags=");
4917c478bd9Sstevel@tonic-gate 		printenvflags(e);
4927c478bd9Sstevel@tonic-gate 	}
4937c478bd9Sstevel@tonic-gate 	if (tTd(40, 32))
4947c478bd9Sstevel@tonic-gate 	{
4957c478bd9Sstevel@tonic-gate 		sm_dprintf("  sendq=");
4967c478bd9Sstevel@tonic-gate 		printaddr(sm_debug_file(), e->e_sendqueue, true);
4977c478bd9Sstevel@tonic-gate 	}
4987c478bd9Sstevel@tonic-gate 	if (tTd(40, 9))
4997c478bd9Sstevel@tonic-gate 	{
5007c478bd9Sstevel@tonic-gate 		sm_dprintf("  tfp=");
5017c478bd9Sstevel@tonic-gate 		dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false);
5027c478bd9Sstevel@tonic-gate 		sm_dprintf("  lockfp=");
5037c478bd9Sstevel@tonic-gate 		if (e->e_lockfp == NULL)
5047c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL\n");
5057c478bd9Sstevel@tonic-gate 		else
5067c478bd9Sstevel@tonic-gate 			dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL),
5077c478bd9Sstevel@tonic-gate 			       true, false);
5087c478bd9Sstevel@tonic-gate 	}
5097c478bd9Sstevel@tonic-gate 
5107c478bd9Sstevel@tonic-gate 	/*
5117c478bd9Sstevel@tonic-gate 	**  If there is no data file yet, create one.
5127c478bd9Sstevel@tonic-gate 	*/
5137c478bd9Sstevel@tonic-gate 
514058561cbSjbeck 	(void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof(df));
5157c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS_DF, e->e_flags))
5167c478bd9Sstevel@tonic-gate 	{
5177c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5187c478bd9Sstevel@tonic-gate 		    SuperSafe != SAFE_REALLY &&
5197c478bd9Sstevel@tonic-gate 		    SuperSafe != SAFE_REALLY_POSTMILTER &&
5207c478bd9Sstevel@tonic-gate 		    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
5217c478bd9Sstevel@tonic-gate 		    errno != EINVAL)
5227c478bd9Sstevel@tonic-gate 		{
5237c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot commit data file %s, uid=%d",
5247c478bd9Sstevel@tonic-gate 			       queuename(e, DATAFL_LETTER), (int) geteuid());
5257c478bd9Sstevel@tonic-gate 		}
5267c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5277c478bd9Sstevel@tonic-gate 		    SuperSafe == SAFE_INTERACTIVE && msync)
5287c478bd9Sstevel@tonic-gate 		{
5297c478bd9Sstevel@tonic-gate 			if (tTd(40,32))
5307c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
5317c478bd9Sstevel@tonic-gate 					  "queueup: fsync(e->e_dfp)");
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 			if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD,
5347c478bd9Sstevel@tonic-gate 						NULL)) < 0)
5357c478bd9Sstevel@tonic-gate 			{
5367c478bd9Sstevel@tonic-gate 				if (newid)
5377c478bd9Sstevel@tonic-gate 					syserr("!552 Error writing data file %s",
5387c478bd9Sstevel@tonic-gate 					       df);
5397c478bd9Sstevel@tonic-gate 				else
5407c478bd9Sstevel@tonic-gate 					syserr("!452 Error writing data file %s",
5417c478bd9Sstevel@tonic-gate 					       df);
5427c478bd9Sstevel@tonic-gate 			}
5437c478bd9Sstevel@tonic-gate 		}
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 	else
5467c478bd9Sstevel@tonic-gate 	{
5477c478bd9Sstevel@tonic-gate 		int dfd;
5487c478bd9Sstevel@tonic-gate 		MODE_T oldumask = 0;
5497c478bd9Sstevel@tonic-gate 		register SM_FILE_T *dfp = NULL;
5507c478bd9Sstevel@tonic-gate 		struct stat stbuf;
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5537c478bd9Sstevel@tonic-gate 		    sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
5547c478bd9Sstevel@tonic-gate 			syserr("committing over bf file");
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode))
5577c478bd9Sstevel@tonic-gate 			oldumask = umask(002);
5587c478bd9Sstevel@tonic-gate 		dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC|QF_O_EXTRA,
5597c478bd9Sstevel@tonic-gate 			   QueueFileMode);
5607c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode))
5617c478bd9Sstevel@tonic-gate 			(void) umask(oldumask);
5627c478bd9Sstevel@tonic-gate 		if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
5637c478bd9Sstevel@tonic-gate 						 (void *) &dfd, SM_IO_WRONLY_B,
5647c478bd9Sstevel@tonic-gate 						 NULL)) == NULL)
5657c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create data temp file %s, uid=%d",
5667c478bd9Sstevel@tonic-gate 				df, (int) geteuid());
5677c478bd9Sstevel@tonic-gate 		if (fstat(dfd, &stbuf) < 0)
5687c478bd9Sstevel@tonic-gate 			e->e_dfino = -1;
5697c478bd9Sstevel@tonic-gate 		else
5707c478bd9Sstevel@tonic-gate 		{
5717c478bd9Sstevel@tonic-gate 			e->e_dfdev = stbuf.st_dev;
5727c478bd9Sstevel@tonic-gate 			e->e_dfino = ST_INODE(stbuf);
5737c478bd9Sstevel@tonic-gate 		}
5747c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
575058561cbSjbeck 		memset(&mcibuf, '\0', sizeof(mcibuf));
5767c478bd9Sstevel@tonic-gate 		mcibuf.mci_out = dfp;
5777c478bd9Sstevel@tonic-gate 		mcibuf.mci_mailer = FileMailer;
5787c478bd9Sstevel@tonic-gate 		(*e->e_putbody)(&mcibuf, e, NULL);
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 		if (SuperSafe == SAFE_REALLY ||
5817c478bd9Sstevel@tonic-gate 		    SuperSafe == SAFE_REALLY_POSTMILTER ||
5827c478bd9Sstevel@tonic-gate 		    (SuperSafe == SAFE_INTERACTIVE && msync))
5837c478bd9Sstevel@tonic-gate 		{
5847c478bd9Sstevel@tonic-gate 			if (tTd(40,32))
5857c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
5867c478bd9Sstevel@tonic-gate 					  "queueup: fsync(dfp)");
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 			if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0)
5897c478bd9Sstevel@tonic-gate 			{
5907c478bd9Sstevel@tonic-gate 				if (newid)
5917c478bd9Sstevel@tonic-gate 					syserr("!552 Error writing data file %s",
5927c478bd9Sstevel@tonic-gate 					       df);
5937c478bd9Sstevel@tonic-gate 				else
5947c478bd9Sstevel@tonic-gate 					syserr("!452 Error writing data file %s",
5957c478bd9Sstevel@tonic-gate 					       df);
5967c478bd9Sstevel@tonic-gate 			}
5977c478bd9Sstevel@tonic-gate 		}
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 		if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0)
6007c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot save data temp file %s, uid=%d",
6017c478bd9Sstevel@tonic-gate 				df, (int) geteuid());
6027c478bd9Sstevel@tonic-gate 		e->e_putbody = putbody;
6037c478bd9Sstevel@tonic-gate 	}
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	/*
6067c478bd9Sstevel@tonic-gate 	**  Output future work requests.
6077c478bd9Sstevel@tonic-gate 	**	Priority and creation time should be first, since
6087c478bd9Sstevel@tonic-gate 	**	they are required by gatherq.
6097c478bd9Sstevel@tonic-gate 	*/
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 	/* output queue version number (must be first!) */
6127c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION);
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	/* output creation time */
6157c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime);
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 	/* output last delivery time */
6187c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 	/* output number of delivery attempts */
6217c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries);
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	/* output message priority */
6247c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	/*
6277c478bd9Sstevel@tonic-gate 	**  If data file is in a different directory than the queue file,
6287c478bd9Sstevel@tonic-gate 	**  output a "d" record naming the directory of the data file.
6297c478bd9Sstevel@tonic-gate 	*/
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	if (e->e_dfqgrp != e->e_qgrp)
6327c478bd9Sstevel@tonic-gate 	{
6337c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n",
6347c478bd9Sstevel@tonic-gate 			Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name);
6357c478bd9Sstevel@tonic-gate 	}
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate 	/* output inode number of data file */
6387c478bd9Sstevel@tonic-gate 	/* XXX should probably include device major/minor too */
6397c478bd9Sstevel@tonic-gate 	if (e->e_dfino != -1)
6407c478bd9Sstevel@tonic-gate 	{
6417c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n",
6427c478bd9Sstevel@tonic-gate 				     (long) major(e->e_dfdev),
6437c478bd9Sstevel@tonic-gate 				     (long) minor(e->e_dfdev),
6447c478bd9Sstevel@tonic-gate 				     (ULONGLONG_T) e->e_dfino);
6457c478bd9Sstevel@tonic-gate 	}
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 	/* output body type */
6487c478bd9Sstevel@tonic-gate 	if (e->e_bodytype != NULL)
6497c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n",
6507c478bd9Sstevel@tonic-gate 				     denlstring(e->e_bodytype, true, false));
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	/* quarantine reason */
6537c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg != NULL)
6547c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n",
6557c478bd9Sstevel@tonic-gate 				     denlstring(e->e_quarmsg, true, false));
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	/* message from envelope, if it exists */
6587c478bd9Sstevel@tonic-gate 	if (e->e_message != NULL)
6597c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
6607c478bd9Sstevel@tonic-gate 				     denlstring(e->e_message, true, false));
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	/* send various flag bits through */
6637c478bd9Sstevel@tonic-gate 	p = buf;
6647c478bd9Sstevel@tonic-gate 	if (bitset(EF_WARNING, e->e_flags))
6657c478bd9Sstevel@tonic-gate 		*p++ = 'w';
6667c478bd9Sstevel@tonic-gate 	if (bitset(EF_RESPONSE, e->e_flags))
6677c478bd9Sstevel@tonic-gate 		*p++ = 'r';
6687c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS8BIT, e->e_flags))
6697c478bd9Sstevel@tonic-gate 		*p++ = '8';
6707c478bd9Sstevel@tonic-gate 	if (bitset(EF_DELETE_BCC, e->e_flags))
6717c478bd9Sstevel@tonic-gate 		*p++ = 'b';
6727c478bd9Sstevel@tonic-gate 	if (bitset(EF_RET_PARAM, e->e_flags))
6737c478bd9Sstevel@tonic-gate 		*p++ = 'd';
6747c478bd9Sstevel@tonic-gate 	if (bitset(EF_NO_BODY_RETN, e->e_flags))
6757c478bd9Sstevel@tonic-gate 		*p++ = 'n';
6767c478bd9Sstevel@tonic-gate 	if (bitset(EF_SPLIT, e->e_flags))
6777c478bd9Sstevel@tonic-gate 		*p++ = 's';
6787c478bd9Sstevel@tonic-gate 	*p++ = '\0';
6797c478bd9Sstevel@tonic-gate 	if (buf[0] != '\0')
6807c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf);
6817c478bd9Sstevel@tonic-gate 
6827c478bd9Sstevel@tonic-gate 	/* save $={persistentMacros} macro values */
6837c478bd9Sstevel@tonic-gate 	queueup_macros(macid("{persistentMacros}"), tfp, e);
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 	/* output name of sender */
6867c478bd9Sstevel@tonic-gate 	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
6877c478bd9Sstevel@tonic-gate 		p = e->e_sender;
6887c478bd9Sstevel@tonic-gate 	else
6897c478bd9Sstevel@tonic-gate 		p = e->e_from.q_paddr;
6907c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n",
6917c478bd9Sstevel@tonic-gate 			     denlstring(p, true, false));
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	/* output ESMTP-supplied "original" information */
6947c478bd9Sstevel@tonic-gate 	if (e->e_envid != NULL)
6957c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n",
6967c478bd9Sstevel@tonic-gate 				     denlstring(e->e_envid, true, false));
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate 	/* output AUTH= parameter */
6997c478bd9Sstevel@tonic-gate 	if (e->e_auth_param != NULL)
7007c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n",
7017c478bd9Sstevel@tonic-gate 				     denlstring(e->e_auth_param, true, false));
7027c478bd9Sstevel@tonic-gate 	if (e->e_dlvr_flag != 0)
7037c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n",
7047c478bd9Sstevel@tonic-gate 				     (char) e->e_dlvr_flag, e->e_deliver_by);
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 	/* output list of recipient addresses */
7077c478bd9Sstevel@tonic-gate 	printctladdr(NULL, NULL);
7087c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
7097c478bd9Sstevel@tonic-gate 	{
7107c478bd9Sstevel@tonic-gate 		if (!QS_IS_UNDELIVERED(q->q_state))
7117c478bd9Sstevel@tonic-gate 			continue;
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 		/* message for this recipient, if it exists */
7147c478bd9Sstevel@tonic-gate 		if (q->q_message != NULL)
7157c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
7167c478bd9Sstevel@tonic-gate 					     denlstring(q->q_message, true,
7177c478bd9Sstevel@tonic-gate 							false));
7187c478bd9Sstevel@tonic-gate 
7197c478bd9Sstevel@tonic-gate 		printctladdr(q, tfp);
7207c478bd9Sstevel@tonic-gate 		if (q->q_orcpt != NULL)
7217c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n",
7227c478bd9Sstevel@tonic-gate 					     denlstring(q->q_orcpt, true,
7237c478bd9Sstevel@tonic-gate 							false));
7247c478bd9Sstevel@tonic-gate 		if (q->q_finalrcpt != NULL)
7257c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n",
7267c478bd9Sstevel@tonic-gate 					     denlstring(q->q_finalrcpt, true,
7277c478bd9Sstevel@tonic-gate 							false));
7287c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R');
7297c478bd9Sstevel@tonic-gate 		if (bitset(QPRIMARY, q->q_flags))
7307c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P');
7317c478bd9Sstevel@tonic-gate 		if (bitset(QHASNOTIFY, q->q_flags))
7327c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N');
7337c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONSUCCESS, q->q_flags))
7347c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S');
7357c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONFAILURE, q->q_flags))
7367c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F');
7377c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONDELAY, q->q_flags))
7387c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D');
7397c478bd9Sstevel@tonic-gate 		if (q->q_alias != NULL &&
7407c478bd9Sstevel@tonic-gate 		    bitset(QALIAS, q->q_alias->q_flags))
7417c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A');
7427c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':');
7437c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n",
7447c478bd9Sstevel@tonic-gate 				     denlstring(q->q_paddr, true, false));
7457c478bd9Sstevel@tonic-gate 		if (announce)
7467c478bd9Sstevel@tonic-gate 		{
7477c478bd9Sstevel@tonic-gate 			char *tag = "queued";
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 			if (e->e_quarmsg != NULL)
7507c478bd9Sstevel@tonic-gate 				tag = "quarantined";
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 			e->e_to = q->q_paddr;
7537c478bd9Sstevel@tonic-gate 			message(tag);
7547c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
7557c478bd9Sstevel@tonic-gate 				logdelivery(q->q_mailer, NULL, q->q_status,
7567c478bd9Sstevel@tonic-gate 					    tag, NULL, (time_t) 0, e);
7577c478bd9Sstevel@tonic-gate 			e->e_to = NULL;
7587c478bd9Sstevel@tonic-gate 		}
7597c478bd9Sstevel@tonic-gate 		if (tTd(40, 1))
7607c478bd9Sstevel@tonic-gate 		{
7617c478bd9Sstevel@tonic-gate 			sm_dprintf("queueing ");
7627c478bd9Sstevel@tonic-gate 			printaddr(sm_debug_file(), q, false);
7637c478bd9Sstevel@tonic-gate 		}
7647c478bd9Sstevel@tonic-gate 	}
7657c478bd9Sstevel@tonic-gate 
7667c478bd9Sstevel@tonic-gate 	/*
7677c478bd9Sstevel@tonic-gate 	**  Output headers for this message.
7687c478bd9Sstevel@tonic-gate 	**	Expand macros completely here.  Queue run will deal with
7697c478bd9Sstevel@tonic-gate 	**	everything as absolute headers.
7707c478bd9Sstevel@tonic-gate 	**		All headers that must be relative to the recipient
7717c478bd9Sstevel@tonic-gate 	**		can be cracked later.
7727c478bd9Sstevel@tonic-gate 	**	We set up a "null mailer" -- i.e., a mailer that will have
7737c478bd9Sstevel@tonic-gate 	**	no effect on the addresses as they are output.
7747c478bd9Sstevel@tonic-gate 	*/
7757c478bd9Sstevel@tonic-gate 
776058561cbSjbeck 	memset((char *) &nullmailer, '\0', sizeof(nullmailer));
7777c478bd9Sstevel@tonic-gate 	nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
7787c478bd9Sstevel@tonic-gate 			nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
7797c478bd9Sstevel@tonic-gate 	nullmailer.m_eol = "\n";
780058561cbSjbeck 	memset(&mcibuf, '\0', sizeof(mcibuf));
7817c478bd9Sstevel@tonic-gate 	mcibuf.mci_mailer = &nullmailer;
7827c478bd9Sstevel@tonic-gate 	mcibuf.mci_out = tfp;
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'g', "\201f");
7857c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
7867c478bd9Sstevel@tonic-gate 	{
7877c478bd9Sstevel@tonic-gate 		if (h->h_value == NULL)
7887c478bd9Sstevel@tonic-gate 			continue;
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate 		/* don't output resent headers on non-resent messages */
7917c478bd9Sstevel@tonic-gate 		if (bitset(H_RESENT, h->h_flags) &&
7927c478bd9Sstevel@tonic-gate 		    !bitset(EF_RESENT, e->e_flags))
7937c478bd9Sstevel@tonic-gate 			continue;
7947c478bd9Sstevel@tonic-gate 
7957c478bd9Sstevel@tonic-gate 		/* expand macros; if null, don't output header at all */
7967c478bd9Sstevel@tonic-gate 		if (bitset(H_DEFAULT, h->h_flags))
7977c478bd9Sstevel@tonic-gate 		{
798058561cbSjbeck 			(void) expand(h->h_value, buf, sizeof(buf), e);
7997c478bd9Sstevel@tonic-gate 			if (buf[0] == '\0')
8007c478bd9Sstevel@tonic-gate 				continue;
8014aac33d3Sjbeck 			if (buf[0] == ' ' && buf[1] == '\0')
8024aac33d3Sjbeck 				continue;
8037c478bd9Sstevel@tonic-gate 		}
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 		/* output this header */
8067c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?");
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 		/* output conditional macro if present */
8097c478bd9Sstevel@tonic-gate 		if (h->h_macro != '\0')
8107c478bd9Sstevel@tonic-gate 		{
8117c478bd9Sstevel@tonic-gate 			if (bitset(0200, h->h_macro))
8127c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
8137c478bd9Sstevel@tonic-gate 						     "${%s}",
8147c478bd9Sstevel@tonic-gate 						      macname(bitidx(h->h_macro)));
8157c478bd9Sstevel@tonic-gate 			else
8167c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
8177c478bd9Sstevel@tonic-gate 						     "$%c", h->h_macro);
8187c478bd9Sstevel@tonic-gate 		}
8197c478bd9Sstevel@tonic-gate 		else if (!bitzerop(h->h_mflags) &&
8207c478bd9Sstevel@tonic-gate 			 bitset(H_CHECK|H_ACHECK, h->h_flags))
8217c478bd9Sstevel@tonic-gate 		{
8227c478bd9Sstevel@tonic-gate 			int j;
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 			/* if conditional, output the set of conditions */
8257c478bd9Sstevel@tonic-gate 			for (j = '\0'; j <= '\177'; j++)
8267c478bd9Sstevel@tonic-gate 				if (bitnset(j, h->h_mflags))
8277c478bd9Sstevel@tonic-gate 					(void) sm_io_putc(tfp, SM_TIME_DEFAULT,
8287c478bd9Sstevel@tonic-gate 							  j);
8297c478bd9Sstevel@tonic-gate 		}
8307c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?');
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 		/* output the header: expand macros, convert addresses */
8337c478bd9Sstevel@tonic-gate 		if (bitset(H_DEFAULT, h->h_flags) &&
8347c478bd9Sstevel@tonic-gate 		    !bitset(H_BINDLATE, h->h_flags))
8357c478bd9Sstevel@tonic-gate 		{
836058561cbSjbeck 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n",
8377c478bd9Sstevel@tonic-gate 					     h->h_field,
8387c478bd9Sstevel@tonic-gate 					     denlstring(buf, false, true));
8397c478bd9Sstevel@tonic-gate 		}
8407c478bd9Sstevel@tonic-gate 		else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
8417c478bd9Sstevel@tonic-gate 			 !bitset(H_BINDLATE, h->h_flags))
8427c478bd9Sstevel@tonic-gate 		{
8437c478bd9Sstevel@tonic-gate 			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
8447c478bd9Sstevel@tonic-gate 			SM_FILE_T *savetrace = TrafficLogFile;
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 			TrafficLogFile = NULL;
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 			if (bitset(H_FROM, h->h_flags))
8497c478bd9Sstevel@tonic-gate 				oldstyle = false;
850*7800901eSjbeck 			commaize(h, h->h_value, oldstyle, &mcibuf, e,
851*7800901eSjbeck 				 PXLF_HEADER);
8527c478bd9Sstevel@tonic-gate 
8537c478bd9Sstevel@tonic-gate 			TrafficLogFile = savetrace;
8547c478bd9Sstevel@tonic-gate 		}
8557c478bd9Sstevel@tonic-gate 		else
8567c478bd9Sstevel@tonic-gate 		{
857058561cbSjbeck 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n",
8587c478bd9Sstevel@tonic-gate 					     h->h_field,
8597c478bd9Sstevel@tonic-gate 					     denlstring(h->h_value, false,
8607c478bd9Sstevel@tonic-gate 							true));
8617c478bd9Sstevel@tonic-gate 		}
8627c478bd9Sstevel@tonic-gate 	}
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	/*
8657c478bd9Sstevel@tonic-gate 	**  Clean up.
8667c478bd9Sstevel@tonic-gate 	**
8677c478bd9Sstevel@tonic-gate 	**	Write a terminator record -- this is to prevent
8687c478bd9Sstevel@tonic-gate 	**	scurrilous crackers from appending any data.
8697c478bd9Sstevel@tonic-gate 	*/
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n");
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 ||
8747c478bd9Sstevel@tonic-gate 	    ((SuperSafe == SAFE_REALLY ||
8757c478bd9Sstevel@tonic-gate 	      SuperSafe == SAFE_REALLY_POSTMILTER ||
8767c478bd9Sstevel@tonic-gate 	      (SuperSafe == SAFE_INTERACTIVE && msync)) &&
8777c478bd9Sstevel@tonic-gate 	     fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) ||
8787c478bd9Sstevel@tonic-gate 	    sm_io_error(tfp))
8797c478bd9Sstevel@tonic-gate 	{
8807c478bd9Sstevel@tonic-gate 		if (newid)
8817c478bd9Sstevel@tonic-gate 			syserr("!552 Error writing control file %s", tf);
8827c478bd9Sstevel@tonic-gate 		else
8837c478bd9Sstevel@tonic-gate 			syserr("!452 Error writing control file %s", tf);
8847c478bd9Sstevel@tonic-gate 	}
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	if (!newid)
8877c478bd9Sstevel@tonic-gate 	{
8887c478bd9Sstevel@tonic-gate 		char new = queue_letter(e, ANYQFL_LETTER);
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 		/* rename (locked) tf to be (locked) [qh]f */
8917c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER),
892058561cbSjbeck 				  sizeof(qf));
8937c478bd9Sstevel@tonic-gate 		if (rename(tf, qf) < 0)
8947c478bd9Sstevel@tonic-gate 			syserr("cannot rename(%s, %s), uid=%d",
8957c478bd9Sstevel@tonic-gate 				tf, qf, (int) geteuid());
8967c478bd9Sstevel@tonic-gate 		else
8977c478bd9Sstevel@tonic-gate 		{
8987c478bd9Sstevel@tonic-gate 			/*
8997c478bd9Sstevel@tonic-gate 			**  Check if type has changed and only
9007c478bd9Sstevel@tonic-gate 			**  remove the old item if the rename above
9017c478bd9Sstevel@tonic-gate 			**  succeeded.
9027c478bd9Sstevel@tonic-gate 			*/
9037c478bd9Sstevel@tonic-gate 
9047c478bd9Sstevel@tonic-gate 			if (e->e_qfletter != '\0' &&
9057c478bd9Sstevel@tonic-gate 			    e->e_qfletter != new)
9067c478bd9Sstevel@tonic-gate 			{
9077c478bd9Sstevel@tonic-gate 				if (tTd(40, 5))
9087c478bd9Sstevel@tonic-gate 				{
9097c478bd9Sstevel@tonic-gate 					sm_dprintf("type changed from %c to %c\n",
9107c478bd9Sstevel@tonic-gate 						   e->e_qfletter, new);
9117c478bd9Sstevel@tonic-gate 				}
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate 				if (unlink(queuename(e, e->e_qfletter)) < 0)
9147c478bd9Sstevel@tonic-gate 				{
9157c478bd9Sstevel@tonic-gate 					/* XXX: something more drastic? */
9167c478bd9Sstevel@tonic-gate 					if (LogLevel > 0)
9177c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
9187c478bd9Sstevel@tonic-gate 							  "queueup: unlink(%s) failed: %s",
9197c478bd9Sstevel@tonic-gate 							  queuename(e, e->e_qfletter),
9207c478bd9Sstevel@tonic-gate 							  sm_errstring(errno));
9217c478bd9Sstevel@tonic-gate 				}
9227c478bd9Sstevel@tonic-gate 			}
9237c478bd9Sstevel@tonic-gate 		}
9247c478bd9Sstevel@tonic-gate 		e->e_qfletter = new;
9257c478bd9Sstevel@tonic-gate 
9267c478bd9Sstevel@tonic-gate 		/*
9277c478bd9Sstevel@tonic-gate 		**  fsync() after renaming to make sure metadata is
9287c478bd9Sstevel@tonic-gate 		**  written to disk on filesystems in which renames are
9297c478bd9Sstevel@tonic-gate 		**  not guaranteed.
9307c478bd9Sstevel@tonic-gate 		*/
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 		if (SuperSafe != SAFE_NO)
9337c478bd9Sstevel@tonic-gate 		{
9347c478bd9Sstevel@tonic-gate 			/* for softupdates */
9357c478bd9Sstevel@tonic-gate 			if (tfd >= 0 && fsync(tfd) < 0)
9367c478bd9Sstevel@tonic-gate 			{
9377c478bd9Sstevel@tonic-gate 				syserr("!queueup: cannot fsync queue temp file %s",
9387c478bd9Sstevel@tonic-gate 				       tf);
9397c478bd9Sstevel@tonic-gate 			}
9407c478bd9Sstevel@tonic-gate 			SYNC_DIR(qf, true);
9417c478bd9Sstevel@tonic-gate 		}
9427c478bd9Sstevel@tonic-gate 
9437c478bd9Sstevel@tonic-gate 		/* close and unlock old (locked) queue file */
9447c478bd9Sstevel@tonic-gate 		if (e->e_lockfp != NULL)
9457c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
9467c478bd9Sstevel@tonic-gate 		e->e_lockfp = tfp;
9477c478bd9Sstevel@tonic-gate 
9487c478bd9Sstevel@tonic-gate 		/* save log info */
9497c478bd9Sstevel@tonic-gate 		if (LogLevel > 79)
9507c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf);
9517c478bd9Sstevel@tonic-gate 	}
9527c478bd9Sstevel@tonic-gate 	else
9537c478bd9Sstevel@tonic-gate 	{
9547c478bd9Sstevel@tonic-gate 		/* save log info */
9557c478bd9Sstevel@tonic-gate 		if (LogLevel > 79)
9567c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf);
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 		e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
9597c478bd9Sstevel@tonic-gate 	}
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	errno = 0;
9627c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_INQUEUE;
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
9657c478bd9Sstevel@tonic-gate 		sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
9667c478bd9Sstevel@tonic-gate 	return;
9677c478bd9Sstevel@tonic-gate }
9687c478bd9Sstevel@tonic-gate 
9697c478bd9Sstevel@tonic-gate /*
9707c478bd9Sstevel@tonic-gate **  PRINTCTLADDR -- print control address to file.
9717c478bd9Sstevel@tonic-gate **
9727c478bd9Sstevel@tonic-gate **	Parameters:
9737c478bd9Sstevel@tonic-gate **		a -- address.
9747c478bd9Sstevel@tonic-gate **		tfp -- file pointer.
9757c478bd9Sstevel@tonic-gate **
9767c478bd9Sstevel@tonic-gate **	Returns:
9777c478bd9Sstevel@tonic-gate **		none.
9787c478bd9Sstevel@tonic-gate **
9797c478bd9Sstevel@tonic-gate **	Side Effects:
9807c478bd9Sstevel@tonic-gate **		The control address (if changed) is printed to the file.
9817c478bd9Sstevel@tonic-gate **		The last control address and uid are saved.
9827c478bd9Sstevel@tonic-gate */
9837c478bd9Sstevel@tonic-gate 
9847c478bd9Sstevel@tonic-gate static void
9857c478bd9Sstevel@tonic-gate printctladdr(a, tfp)
9867c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
9877c478bd9Sstevel@tonic-gate 	SM_FILE_T *tfp;
9887c478bd9Sstevel@tonic-gate {
9897c478bd9Sstevel@tonic-gate 	char *user;
9907c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
9917c478bd9Sstevel@tonic-gate 	uid_t uid;
9927c478bd9Sstevel@tonic-gate 	gid_t gid;
9937c478bd9Sstevel@tonic-gate 	static ADDRESS *lastctladdr = NULL;
9947c478bd9Sstevel@tonic-gate 	static uid_t lastuid;
9957c478bd9Sstevel@tonic-gate 
9967c478bd9Sstevel@tonic-gate 	/* initialization */
9977c478bd9Sstevel@tonic-gate 	if (a == NULL || a->q_alias == NULL || tfp == NULL)
9987c478bd9Sstevel@tonic-gate 	{
9997c478bd9Sstevel@tonic-gate 		if (lastctladdr != NULL && tfp != NULL)
10007c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n");
10017c478bd9Sstevel@tonic-gate 		lastctladdr = NULL;
10027c478bd9Sstevel@tonic-gate 		lastuid = 0;
10037c478bd9Sstevel@tonic-gate 		return;
10047c478bd9Sstevel@tonic-gate 	}
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate 	/* find the active uid */
10077c478bd9Sstevel@tonic-gate 	q = getctladdr(a);
10087c478bd9Sstevel@tonic-gate 	if (q == NULL)
10097c478bd9Sstevel@tonic-gate 	{
10107c478bd9Sstevel@tonic-gate 		user = NULL;
10117c478bd9Sstevel@tonic-gate 		uid = 0;
10127c478bd9Sstevel@tonic-gate 		gid = 0;
10137c478bd9Sstevel@tonic-gate 	}
10147c478bd9Sstevel@tonic-gate 	else
10157c478bd9Sstevel@tonic-gate 	{
10167c478bd9Sstevel@tonic-gate 		user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
10177c478bd9Sstevel@tonic-gate 		uid = q->q_uid;
10187c478bd9Sstevel@tonic-gate 		gid = q->q_gid;
10197c478bd9Sstevel@tonic-gate 	}
10207c478bd9Sstevel@tonic-gate 	a = a->q_alias;
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	/* check to see if this is the same as last time */
10237c478bd9Sstevel@tonic-gate 	if (lastctladdr != NULL && uid == lastuid &&
10247c478bd9Sstevel@tonic-gate 	    strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
10257c478bd9Sstevel@tonic-gate 		return;
10267c478bd9Sstevel@tonic-gate 	lastuid = uid;
10277c478bd9Sstevel@tonic-gate 	lastctladdr = a;
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 	if (uid == 0 || user == NULL || user[0] == '\0')
10307c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C");
10317c478bd9Sstevel@tonic-gate 	else
10327c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld",
10337c478bd9Sstevel@tonic-gate 				     denlstring(user, true, false), (long) uid,
10347c478bd9Sstevel@tonic-gate 				     (long) gid);
10357c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n",
10367c478bd9Sstevel@tonic-gate 			     denlstring(a->q_paddr, true, false));
10377c478bd9Sstevel@tonic-gate }
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate /*
10407c478bd9Sstevel@tonic-gate **  RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process
10417c478bd9Sstevel@tonic-gate **
10427c478bd9Sstevel@tonic-gate **	This propagates the signal to the child processes that are queue
10437c478bd9Sstevel@tonic-gate **	runners. This is for a queue runner "cleanup". After all of the
10447c478bd9Sstevel@tonic-gate **	child queue runner processes are signaled (it should be SIGTERM
10457c478bd9Sstevel@tonic-gate **	being the sig) then the old signal handler (Oldsh) is called
10467c478bd9Sstevel@tonic-gate **	to handle any cleanup set for this process (provided it is not
10477c478bd9Sstevel@tonic-gate **	SIG_DFL or SIG_IGN). The signal may not be handled immediately
10487c478bd9Sstevel@tonic-gate **	if the BlockOldsh flag is set. If the current process doesn't
10497c478bd9Sstevel@tonic-gate **	have a parent then handle the signal immediately, regardless of
10507c478bd9Sstevel@tonic-gate **	BlockOldsh.
10517c478bd9Sstevel@tonic-gate **
10527c478bd9Sstevel@tonic-gate **	Parameters:
10537c478bd9Sstevel@tonic-gate **		sig -- the signal number being sent
10547c478bd9Sstevel@tonic-gate **
10557c478bd9Sstevel@tonic-gate **	Returns:
10567c478bd9Sstevel@tonic-gate **		none.
10577c478bd9Sstevel@tonic-gate **
10587c478bd9Sstevel@tonic-gate **	Side Effects:
10597c478bd9Sstevel@tonic-gate **		Sets the NoMoreRunners boolean to true to stop more runners
10607c478bd9Sstevel@tonic-gate **		from being started in runqueue().
10617c478bd9Sstevel@tonic-gate **
10627c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
10637c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
10647c478bd9Sstevel@tonic-gate **		DOING.
10657c478bd9Sstevel@tonic-gate */
10667c478bd9Sstevel@tonic-gate 
10677c478bd9Sstevel@tonic-gate static bool		volatile NoMoreRunners = false;
10687c478bd9Sstevel@tonic-gate static sigfunc_t	Oldsh_term = SIG_DFL;
10697c478bd9Sstevel@tonic-gate static sigfunc_t	Oldsh_hup = SIG_DFL;
10707c478bd9Sstevel@tonic-gate static sigfunc_t	volatile Oldsh = SIG_DFL;
10717c478bd9Sstevel@tonic-gate static bool		BlockOldsh = false;
10727c478bd9Sstevel@tonic-gate static int		volatile Oldsig = 0;
10737c478bd9Sstevel@tonic-gate static SIGFUNC_DECL	runners_sigterm __P((int));
10747c478bd9Sstevel@tonic-gate static SIGFUNC_DECL	runners_sighup __P((int));
10757c478bd9Sstevel@tonic-gate 
10767c478bd9Sstevel@tonic-gate static SIGFUNC_DECL
10777c478bd9Sstevel@tonic-gate runners_sigterm(sig)
10787c478bd9Sstevel@tonic-gate 	int sig;
10797c478bd9Sstevel@tonic-gate {
10807c478bd9Sstevel@tonic-gate 	int save_errno = errno;
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	FIX_SYSV_SIGNAL(sig, runners_sigterm);
10837c478bd9Sstevel@tonic-gate 	errno = save_errno;
10847c478bd9Sstevel@tonic-gate 	CHECK_CRITICAL(sig);
10857c478bd9Sstevel@tonic-gate 	NoMoreRunners = true;
10867c478bd9Sstevel@tonic-gate 	Oldsh = Oldsh_term;
10877c478bd9Sstevel@tonic-gate 	Oldsig = sig;
10887c478bd9Sstevel@tonic-gate 	proc_list_signal(PROC_QUEUE, sig);
10897c478bd9Sstevel@tonic-gate 
10907c478bd9Sstevel@tonic-gate 	if (!BlockOldsh || getppid() <= 1)
10917c478bd9Sstevel@tonic-gate 	{
10927c478bd9Sstevel@tonic-gate 		/* Check that a valid 'old signal handler' is callable */
10937c478bd9Sstevel@tonic-gate 		if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN &&
10947c478bd9Sstevel@tonic-gate 		    Oldsh_term != runners_sigterm)
10957c478bd9Sstevel@tonic-gate 			(*Oldsh_term)(sig);
10967c478bd9Sstevel@tonic-gate 	}
10977c478bd9Sstevel@tonic-gate 	errno = save_errno;
10987c478bd9Sstevel@tonic-gate 	return SIGFUNC_RETURN;
10997c478bd9Sstevel@tonic-gate }
11007c478bd9Sstevel@tonic-gate /*
11017c478bd9Sstevel@tonic-gate **  RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process
11027c478bd9Sstevel@tonic-gate **
11037c478bd9Sstevel@tonic-gate **	This propagates the signal to the child processes that are queue
11047c478bd9Sstevel@tonic-gate **	runners. This is for a queue runner "cleanup". After all of the
11057c478bd9Sstevel@tonic-gate **	child queue runner processes are signaled (it should be SIGHUP
11067c478bd9Sstevel@tonic-gate **	being the sig) then the old signal handler (Oldsh) is called to
11077c478bd9Sstevel@tonic-gate **	handle any cleanup set for this process (provided it is not SIG_DFL
11087c478bd9Sstevel@tonic-gate **	or SIG_IGN). The signal may not be handled immediately if the
11097c478bd9Sstevel@tonic-gate **	BlockOldsh flag is set. If the current process doesn't have
11107c478bd9Sstevel@tonic-gate **	a parent then handle the signal immediately, regardless of
11117c478bd9Sstevel@tonic-gate **	BlockOldsh.
11127c478bd9Sstevel@tonic-gate **
11137c478bd9Sstevel@tonic-gate **	Parameters:
11147c478bd9Sstevel@tonic-gate **		sig -- the signal number being sent
11157c478bd9Sstevel@tonic-gate **
11167c478bd9Sstevel@tonic-gate **	Returns:
11177c478bd9Sstevel@tonic-gate **		none.
11187c478bd9Sstevel@tonic-gate **
11197c478bd9Sstevel@tonic-gate **	Side Effects:
11207c478bd9Sstevel@tonic-gate **		Sets the NoMoreRunners boolean to true to stop more runners
11217c478bd9Sstevel@tonic-gate **		from being started in runqueue().
11227c478bd9Sstevel@tonic-gate **
11237c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11247c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
11257c478bd9Sstevel@tonic-gate **		DOING.
11267c478bd9Sstevel@tonic-gate */
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate static SIGFUNC_DECL
11297c478bd9Sstevel@tonic-gate runners_sighup(sig)
11307c478bd9Sstevel@tonic-gate 	int sig;
11317c478bd9Sstevel@tonic-gate {
11327c478bd9Sstevel@tonic-gate 	int save_errno = errno;
11337c478bd9Sstevel@tonic-gate 
11347c478bd9Sstevel@tonic-gate 	FIX_SYSV_SIGNAL(sig, runners_sighup);
11357c478bd9Sstevel@tonic-gate 	errno = save_errno;
11367c478bd9Sstevel@tonic-gate 	CHECK_CRITICAL(sig);
11377c478bd9Sstevel@tonic-gate 	NoMoreRunners = true;
11387c478bd9Sstevel@tonic-gate 	Oldsh = Oldsh_hup;
11397c478bd9Sstevel@tonic-gate 	Oldsig = sig;
11407c478bd9Sstevel@tonic-gate 	proc_list_signal(PROC_QUEUE, sig);
11417c478bd9Sstevel@tonic-gate 
11427c478bd9Sstevel@tonic-gate 	if (!BlockOldsh || getppid() <= 1)
11437c478bd9Sstevel@tonic-gate 	{
11447c478bd9Sstevel@tonic-gate 		/* Check that a valid 'old signal handler' is callable */
11457c478bd9Sstevel@tonic-gate 		if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN &&
11467c478bd9Sstevel@tonic-gate 		    Oldsh_hup != runners_sighup)
11477c478bd9Sstevel@tonic-gate 			(*Oldsh_hup)(sig);
11487c478bd9Sstevel@tonic-gate 	}
11497c478bd9Sstevel@tonic-gate 	errno = save_errno;
11507c478bd9Sstevel@tonic-gate 	return SIGFUNC_RETURN;
11517c478bd9Sstevel@tonic-gate }
11527c478bd9Sstevel@tonic-gate /*
11537c478bd9Sstevel@tonic-gate **  MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart
11547c478bd9Sstevel@tonic-gate **
11557c478bd9Sstevel@tonic-gate **  Sets a workgroup for restarting.
11567c478bd9Sstevel@tonic-gate **
11577c478bd9Sstevel@tonic-gate **	Parameters:
11587c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to restart.
11597c478bd9Sstevel@tonic-gate **		reason -- why (signal?), -1 to turn off restart
11607c478bd9Sstevel@tonic-gate **
11617c478bd9Sstevel@tonic-gate **	Returns:
11627c478bd9Sstevel@tonic-gate **		none.
11637c478bd9Sstevel@tonic-gate **
11647c478bd9Sstevel@tonic-gate **	Side effects:
11657c478bd9Sstevel@tonic-gate **		May set global RestartWorkGroup to true.
11667c478bd9Sstevel@tonic-gate **
11677c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11687c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
11697c478bd9Sstevel@tonic-gate **		DOING.
11707c478bd9Sstevel@tonic-gate */
11717c478bd9Sstevel@tonic-gate 
11727c478bd9Sstevel@tonic-gate void
11737c478bd9Sstevel@tonic-gate mark_work_group_restart(wgrp, reason)
11747c478bd9Sstevel@tonic-gate 	int wgrp;
11757c478bd9Sstevel@tonic-gate 	int reason;
11767c478bd9Sstevel@tonic-gate {
11777c478bd9Sstevel@tonic-gate 	if (wgrp < 0 || wgrp > NumWorkGroups)
11787c478bd9Sstevel@tonic-gate 		return;
11797c478bd9Sstevel@tonic-gate 
11807c478bd9Sstevel@tonic-gate 	WorkGrp[wgrp].wg_restart = reason;
11817c478bd9Sstevel@tonic-gate 	if (reason >= 0)
11827c478bd9Sstevel@tonic-gate 		RestartWorkGroup = true;
11837c478bd9Sstevel@tonic-gate }
11847c478bd9Sstevel@tonic-gate /*
11857c478bd9Sstevel@tonic-gate **  RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart
11867c478bd9Sstevel@tonic-gate **
11877c478bd9Sstevel@tonic-gate **  Restart any workgroup marked as needing a restart provided more
11887c478bd9Sstevel@tonic-gate **  runners are allowed.
11897c478bd9Sstevel@tonic-gate **
11907c478bd9Sstevel@tonic-gate **	Parameters:
11917c478bd9Sstevel@tonic-gate **		none.
11927c478bd9Sstevel@tonic-gate **
11937c478bd9Sstevel@tonic-gate **	Returns:
11947c478bd9Sstevel@tonic-gate **		none.
11957c478bd9Sstevel@tonic-gate **
11967c478bd9Sstevel@tonic-gate **	Side effects:
11977c478bd9Sstevel@tonic-gate **		Sets global RestartWorkGroup to false.
11987c478bd9Sstevel@tonic-gate */
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate void
12017c478bd9Sstevel@tonic-gate restart_marked_work_groups()
12027c478bd9Sstevel@tonic-gate {
12037c478bd9Sstevel@tonic-gate 	int i;
12047c478bd9Sstevel@tonic-gate 	int wasblocked;
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 	if (NoMoreRunners)
12077c478bd9Sstevel@tonic-gate 		return;
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate 	/* Block SIGCHLD so reapchild() doesn't mess with us */
12107c478bd9Sstevel@tonic-gate 	wasblocked = sm_blocksignal(SIGCHLD);
12117c478bd9Sstevel@tonic-gate 
12127c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumWorkGroups; i++)
12137c478bd9Sstevel@tonic-gate 	{
12147c478bd9Sstevel@tonic-gate 		if (WorkGrp[i].wg_restart >= 0)
12157c478bd9Sstevel@tonic-gate 		{
12167c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
12177c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
12187c478bd9Sstevel@tonic-gate 					  "restart queue runner=%d due to signal 0x%x",
12197c478bd9Sstevel@tonic-gate 					  i, WorkGrp[i].wg_restart);
12207c478bd9Sstevel@tonic-gate 			restart_work_group(i);
12217c478bd9Sstevel@tonic-gate 		}
12227c478bd9Sstevel@tonic-gate 	}
12237c478bd9Sstevel@tonic-gate 	RestartWorkGroup = false;
12247c478bd9Sstevel@tonic-gate 
12257c478bd9Sstevel@tonic-gate 	if (wasblocked == 0)
12267c478bd9Sstevel@tonic-gate 		(void) sm_releasesignal(SIGCHLD);
12277c478bd9Sstevel@tonic-gate }
12287c478bd9Sstevel@tonic-gate /*
12297c478bd9Sstevel@tonic-gate **  RESTART_WORK_GROUP -- restart a specific work group
12307c478bd9Sstevel@tonic-gate **
12317c478bd9Sstevel@tonic-gate **  Restart a specific workgroup provided more runners are allowed.
12327c478bd9Sstevel@tonic-gate **  If the requested work group has been restarted too many times log
12337c478bd9Sstevel@tonic-gate **  this and refuse to restart.
12347c478bd9Sstevel@tonic-gate **
12357c478bd9Sstevel@tonic-gate **	Parameters:
12367c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to restart
12377c478bd9Sstevel@tonic-gate **
12387c478bd9Sstevel@tonic-gate **	Returns:
12397c478bd9Sstevel@tonic-gate **		none.
12407c478bd9Sstevel@tonic-gate **
12417c478bd9Sstevel@tonic-gate **	Side Effects:
12427c478bd9Sstevel@tonic-gate **		starts another process doing the work of wgrp
12437c478bd9Sstevel@tonic-gate */
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate #define MAX_PERSIST_RESTART	10	/* max allowed number of restarts */
12467c478bd9Sstevel@tonic-gate 
12477c478bd9Sstevel@tonic-gate static void
12487c478bd9Sstevel@tonic-gate restart_work_group(wgrp)
12497c478bd9Sstevel@tonic-gate 	int wgrp;
12507c478bd9Sstevel@tonic-gate {
12517c478bd9Sstevel@tonic-gate 	if (NoMoreRunners ||
12527c478bd9Sstevel@tonic-gate 	    wgrp < 0 || wgrp > NumWorkGroups)
12537c478bd9Sstevel@tonic-gate 		return;
12547c478bd9Sstevel@tonic-gate 
12557c478bd9Sstevel@tonic-gate 	WorkGrp[wgrp].wg_restart = -1;
12567c478bd9Sstevel@tonic-gate 	if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART)
12577c478bd9Sstevel@tonic-gate 	{
12587c478bd9Sstevel@tonic-gate 		/* avoid overflow; increment here */
12597c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_restartcnt++;
12607c478bd9Sstevel@tonic-gate 		(void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL);
12617c478bd9Sstevel@tonic-gate 	}
12627c478bd9Sstevel@tonic-gate 	else
12637c478bd9Sstevel@tonic-gate 	{
12647c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID,
12657c478bd9Sstevel@tonic-gate 			  "ERROR: persistent queue runner=%d restarted too many times, queue runner lost",
12667c478bd9Sstevel@tonic-gate 			  wgrp);
12677c478bd9Sstevel@tonic-gate 	}
12687c478bd9Sstevel@tonic-gate }
12697c478bd9Sstevel@tonic-gate /*
12707c478bd9Sstevel@tonic-gate **  SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group.
12717c478bd9Sstevel@tonic-gate **
12727c478bd9Sstevel@tonic-gate **	Parameters:
12737c478bd9Sstevel@tonic-gate **		runall -- schedule even if individual bit is not set.
12747c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to schedule.
12757c478bd9Sstevel@tonic-gate **		didit -- the queue run was performed for this work group.
12767c478bd9Sstevel@tonic-gate **
12777c478bd9Sstevel@tonic-gate **	Returns:
12787c478bd9Sstevel@tonic-gate **		nothing
12797c478bd9Sstevel@tonic-gate */
12807c478bd9Sstevel@tonic-gate 
12817c478bd9Sstevel@tonic-gate #define INCR_MOD(v, m)	if (++v >= m)	\
12827c478bd9Sstevel@tonic-gate 				v = 0;	\
12837c478bd9Sstevel@tonic-gate 			else
12847c478bd9Sstevel@tonic-gate 
12857c478bd9Sstevel@tonic-gate static void
12867c478bd9Sstevel@tonic-gate schedule_queue_runs(runall, wgrp, didit)
12877c478bd9Sstevel@tonic-gate 	bool runall;
12887c478bd9Sstevel@tonic-gate 	int wgrp;
12897c478bd9Sstevel@tonic-gate 	bool didit;
12907c478bd9Sstevel@tonic-gate {
12917c478bd9Sstevel@tonic-gate 	int qgrp, cgrp, endgrp;
12927c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
12937c478bd9Sstevel@tonic-gate 	time_t lastsched;
12947c478bd9Sstevel@tonic-gate 	bool sched;
12957c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
12967c478bd9Sstevel@tonic-gate 	time_t now;
12977c478bd9Sstevel@tonic-gate 	time_t minqintvl;
12987c478bd9Sstevel@tonic-gate 
12997c478bd9Sstevel@tonic-gate 	/*
13007c478bd9Sstevel@tonic-gate 	**  This is a bit ugly since we have to duplicate the
13017c478bd9Sstevel@tonic-gate 	**  code that "walks" through a work queue group.
13027c478bd9Sstevel@tonic-gate 	*/
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate 	now = curtime();
13057c478bd9Sstevel@tonic-gate 	minqintvl = 0;
13067c478bd9Sstevel@tonic-gate 	cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp;
13077c478bd9Sstevel@tonic-gate 	do
13087c478bd9Sstevel@tonic-gate 	{
13097c478bd9Sstevel@tonic-gate 		time_t qintvl;
13107c478bd9Sstevel@tonic-gate 
13117c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13127c478bd9Sstevel@tonic-gate 		lastsched = 0;
13137c478bd9Sstevel@tonic-gate 		sched = false;
13147c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13157c478bd9Sstevel@tonic-gate 		qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index;
13167c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_queueintvl > 0)
13177c478bd9Sstevel@tonic-gate 			qintvl = Queue[qgrp]->qg_queueintvl;
13187c478bd9Sstevel@tonic-gate 		else if (QueueIntvl > 0)
13197c478bd9Sstevel@tonic-gate 			qintvl = QueueIntvl;
13207c478bd9Sstevel@tonic-gate 		else
13217c478bd9Sstevel@tonic-gate 			qintvl = (time_t) 0;
13227c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13237c478bd9Sstevel@tonic-gate 		lastsched = Queue[qgrp]->qg_nextrun;
13247c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13257c478bd9Sstevel@tonic-gate 		if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0)
13267c478bd9Sstevel@tonic-gate 		{
13277c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13287c478bd9Sstevel@tonic-gate 			sched = true;
13297c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13307c478bd9Sstevel@tonic-gate 			if (minqintvl == 0 || qintvl < minqintvl)
13317c478bd9Sstevel@tonic-gate 				minqintvl = qintvl;
13327c478bd9Sstevel@tonic-gate 
13337c478bd9Sstevel@tonic-gate 			/*
13347c478bd9Sstevel@tonic-gate 			**  Only set a new time if a queue run was performed
13357c478bd9Sstevel@tonic-gate 			**  for this queue group.  If the queue was not run,
13367c478bd9Sstevel@tonic-gate 			**  we could starve it by setting a new time on each
13377c478bd9Sstevel@tonic-gate 			**  call.
13387c478bd9Sstevel@tonic-gate 			*/
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate 			if (didit)
13417c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_nextrun += qintvl;
13427c478bd9Sstevel@tonic-gate 		}
13437c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13447c478bd9Sstevel@tonic-gate 		if (tTd(69, 10))
13457c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
13467c478bd9Sstevel@tonic-gate 				"sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d",
13477c478bd9Sstevel@tonic-gate 				wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl,
13487c478bd9Sstevel@tonic-gate 				QueueIntvl, runall, lastsched,
13497c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_nextrun, sched);
13507c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13517c478bd9Sstevel@tonic-gate 		INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp);
13527c478bd9Sstevel@tonic-gate 	} while (endgrp != cgrp);
13537c478bd9Sstevel@tonic-gate 	if (minqintvl > 0)
13547c478bd9Sstevel@tonic-gate 		(void) sm_setevent(minqintvl, runqueueevent, 0);
13557c478bd9Sstevel@tonic-gate }
13567c478bd9Sstevel@tonic-gate 
13577c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_RUN_PARANOIA
13587c478bd9Sstevel@tonic-gate /*
13597c478bd9Sstevel@tonic-gate **  CHECKQUEUERUNNER -- check whether a queue group hasn't been run.
13607c478bd9Sstevel@tonic-gate **
13617c478bd9Sstevel@tonic-gate **	Use this if events may get lost and hence queue runners may not
13627c478bd9Sstevel@tonic-gate **	be started and mail will pile up in a queue.
13637c478bd9Sstevel@tonic-gate **
13647c478bd9Sstevel@tonic-gate **	Parameters:
13657c478bd9Sstevel@tonic-gate **		none.
13667c478bd9Sstevel@tonic-gate **
13677c478bd9Sstevel@tonic-gate **	Returns:
13687c478bd9Sstevel@tonic-gate **		true if a queue run is necessary.
13697c478bd9Sstevel@tonic-gate **
13707c478bd9Sstevel@tonic-gate **	Side Effects:
13717c478bd9Sstevel@tonic-gate **		may schedule a queue run.
13727c478bd9Sstevel@tonic-gate */
13737c478bd9Sstevel@tonic-gate 
13747c478bd9Sstevel@tonic-gate bool
13757c478bd9Sstevel@tonic-gate checkqueuerunner()
13767c478bd9Sstevel@tonic-gate {
13777c478bd9Sstevel@tonic-gate 	int qgrp;
13787c478bd9Sstevel@tonic-gate 	time_t now, minqintvl;
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate 	now = curtime();
13817c478bd9Sstevel@tonic-gate 	minqintvl = 0;
13827c478bd9Sstevel@tonic-gate 	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
13837c478bd9Sstevel@tonic-gate 	{
13847c478bd9Sstevel@tonic-gate 		time_t qintvl;
13857c478bd9Sstevel@tonic-gate 
13867c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_queueintvl > 0)
13877c478bd9Sstevel@tonic-gate 			qintvl = Queue[qgrp]->qg_queueintvl;
13887c478bd9Sstevel@tonic-gate 		else if (QueueIntvl > 0)
13897c478bd9Sstevel@tonic-gate 			qintvl = QueueIntvl;
13907c478bd9Sstevel@tonic-gate 		else
13917c478bd9Sstevel@tonic-gate 			qintvl = (time_t) 0;
13927c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_nextrun <= now - qintvl)
13937c478bd9Sstevel@tonic-gate 		{
13947c478bd9Sstevel@tonic-gate 			if (minqintvl == 0 || qintvl < minqintvl)
13957c478bd9Sstevel@tonic-gate 				minqintvl = qintvl;
13967c478bd9Sstevel@tonic-gate 			if (LogLevel > 1)
13977c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, NOQID,
13987c478bd9Sstevel@tonic-gate 					"checkqueuerunner: queue %d should have been run at %s, queue interval %ld",
13997c478bd9Sstevel@tonic-gate 					qgrp,
14007c478bd9Sstevel@tonic-gate 					arpadate(ctime(&Queue[qgrp]->qg_nextrun)),
14017c478bd9Sstevel@tonic-gate 					qintvl);
14027c478bd9Sstevel@tonic-gate 		}
14037c478bd9Sstevel@tonic-gate 	}
14047c478bd9Sstevel@tonic-gate 	if (minqintvl > 0)
14057c478bd9Sstevel@tonic-gate 	{
14067c478bd9Sstevel@tonic-gate 		(void) sm_setevent(minqintvl, runqueueevent, 0);
14077c478bd9Sstevel@tonic-gate 		return true;
14087c478bd9Sstevel@tonic-gate 	}
14097c478bd9Sstevel@tonic-gate 	return false;
14107c478bd9Sstevel@tonic-gate }
14117c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_RUN_PARANOIA */
14127c478bd9Sstevel@tonic-gate 
14137c478bd9Sstevel@tonic-gate /*
14147c478bd9Sstevel@tonic-gate **  RUNQUEUE -- run the jobs in the queue.
14157c478bd9Sstevel@tonic-gate **
14167c478bd9Sstevel@tonic-gate **	Gets the stuff out of the queue in some presumably logical
14177c478bd9Sstevel@tonic-gate **	order and processes them.
14187c478bd9Sstevel@tonic-gate **
14197c478bd9Sstevel@tonic-gate **	Parameters:
14207c478bd9Sstevel@tonic-gate **		forkflag -- true if the queue scanning should be done in
14217c478bd9Sstevel@tonic-gate **			a child process.  We double-fork so it is not our
14227c478bd9Sstevel@tonic-gate **			child and we don't have to clean up after it.
14237c478bd9Sstevel@tonic-gate **			false can be ignored if we have multiple queues.
14247c478bd9Sstevel@tonic-gate **		verbose -- if true, print out status information.
14257c478bd9Sstevel@tonic-gate **		persistent -- persistent queue runner?
14267c478bd9Sstevel@tonic-gate **		runall -- run all groups or only a subset (DoQueueRun)?
14277c478bd9Sstevel@tonic-gate **
14287c478bd9Sstevel@tonic-gate **	Returns:
14297c478bd9Sstevel@tonic-gate **		true if the queue run successfully began.
14307c478bd9Sstevel@tonic-gate **
14317c478bd9Sstevel@tonic-gate **	Side Effects:
14327c478bd9Sstevel@tonic-gate **		runs things in the mail queue using run_work_group().
14337c478bd9Sstevel@tonic-gate **		maybe schedules next queue run.
14347c478bd9Sstevel@tonic-gate */
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate static ENVELOPE	QueueEnvelope;		/* the queue run envelope */
14377c478bd9Sstevel@tonic-gate static time_t	LastQueueTime = 0;	/* last time a queue ID assigned */
14387c478bd9Sstevel@tonic-gate static pid_t	LastQueuePid = -1;	/* last PID which had a queue ID */
14397c478bd9Sstevel@tonic-gate 
14407c478bd9Sstevel@tonic-gate /* values for qp_supdirs */
14417c478bd9Sstevel@tonic-gate #define QP_NOSUB	0x0000	/* No subdirectories */
14427c478bd9Sstevel@tonic-gate #define QP_SUBDF	0x0001	/* "df" subdirectory */
14437c478bd9Sstevel@tonic-gate #define QP_SUBQF	0x0002	/* "qf" subdirectory */
14447c478bd9Sstevel@tonic-gate #define QP_SUBXF	0x0004	/* "xf" subdirectory */
14457c478bd9Sstevel@tonic-gate 
14467c478bd9Sstevel@tonic-gate bool
14477c478bd9Sstevel@tonic-gate runqueue(forkflag, verbose, persistent, runall)
14487c478bd9Sstevel@tonic-gate 	bool forkflag;
14497c478bd9Sstevel@tonic-gate 	bool verbose;
14507c478bd9Sstevel@tonic-gate 	bool persistent;
14517c478bd9Sstevel@tonic-gate 	bool runall;
14527c478bd9Sstevel@tonic-gate {
14537c478bd9Sstevel@tonic-gate 	int i;
14547c478bd9Sstevel@tonic-gate 	bool ret = true;
14557c478bd9Sstevel@tonic-gate 	static int curnum = 0;
14567c478bd9Sstevel@tonic-gate 	sigfunc_t cursh;
14577c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
14587c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE int oldgroup = 0;
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 	if (sm_debug_active(&DebugLeakQ, 1))
14617c478bd9Sstevel@tonic-gate 	{
14627c478bd9Sstevel@tonic-gate 		oldgroup = sm_heap_group();
14637c478bd9Sstevel@tonic-gate 		sm_heap_newgroup();
14647c478bd9Sstevel@tonic-gate 		sm_dprintf("runqueue() heap group #%d\n", sm_heap_group());
14657c478bd9Sstevel@tonic-gate 	}
14667c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
14677c478bd9Sstevel@tonic-gate 
14687c478bd9Sstevel@tonic-gate 	/* queue run has been started, don't do any more this time */
14697c478bd9Sstevel@tonic-gate 	DoQueueRun = false;
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 	/* more than one queue or more than one directory per queue */
14727c478bd9Sstevel@tonic-gate 	if (!forkflag && !verbose &&
14737c478bd9Sstevel@tonic-gate 	    (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 ||
14747c478bd9Sstevel@tonic-gate 	     WorkGrp[0].wg_numqgrp > 1))
14757c478bd9Sstevel@tonic-gate 		forkflag = true;
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate 	/*
14787c478bd9Sstevel@tonic-gate 	**  For controlling queue runners via signals sent to this process.
14797c478bd9Sstevel@tonic-gate 	**  Oldsh* will get called too by runners_sig* (if it is not SIG_IGN
14807c478bd9Sstevel@tonic-gate 	**  or SIG_DFL) to preserve cleanup behavior. Now that this process
14817c478bd9Sstevel@tonic-gate 	**  will have children (and perhaps grandchildren) this handler will
14827c478bd9Sstevel@tonic-gate 	**  be left in place. This is because this process, once it has
14837c478bd9Sstevel@tonic-gate 	**  finished spinning off queue runners, may go back to doing something
14847c478bd9Sstevel@tonic-gate 	**  else (like being a daemon). And we still want on a SIG{TERM,HUP} to
14857c478bd9Sstevel@tonic-gate 	**  clean up the child queue runners. Only install 'runners_sig*' once
14867c478bd9Sstevel@tonic-gate 	**  else we'll get stuck looping forever.
14877c478bd9Sstevel@tonic-gate 	*/
14887c478bd9Sstevel@tonic-gate 
14897c478bd9Sstevel@tonic-gate 	cursh = sm_signal(SIGTERM, runners_sigterm);
14907c478bd9Sstevel@tonic-gate 	if (cursh != runners_sigterm)
14917c478bd9Sstevel@tonic-gate 		Oldsh_term = cursh;
14927c478bd9Sstevel@tonic-gate 	cursh = sm_signal(SIGHUP, runners_sighup);
14937c478bd9Sstevel@tonic-gate 	if (cursh != runners_sighup)
14947c478bd9Sstevel@tonic-gate 		Oldsh_hup = cursh;
14957c478bd9Sstevel@tonic-gate 
14967c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++)
14977c478bd9Sstevel@tonic-gate 	{
14987c478bd9Sstevel@tonic-gate 		int rwgflags = RWG_NONE;
14997c478bd9Sstevel@tonic-gate 
15007c478bd9Sstevel@tonic-gate 		/*
15017c478bd9Sstevel@tonic-gate 		**  If MaxQueueChildren active then test whether the start
15027c478bd9Sstevel@tonic-gate 		**  of the next queue group's additional queue runners (maximum)
15037c478bd9Sstevel@tonic-gate 		**  will result in MaxQueueChildren being exceeded.
15047c478bd9Sstevel@tonic-gate 		**
15057c478bd9Sstevel@tonic-gate 		**  Note: do not use continue; even though another workgroup
15067c478bd9Sstevel@tonic-gate 		**	may have fewer queue runners, this would be "unfair",
15077c478bd9Sstevel@tonic-gate 		**	i.e., this work group might "starve" then.
15087c478bd9Sstevel@tonic-gate 		*/
15097c478bd9Sstevel@tonic-gate 
15107c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
15117c478bd9Sstevel@tonic-gate 		if (tTd(69, 10))
15127c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
15137c478bd9Sstevel@tonic-gate 				"rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d",
15147c478bd9Sstevel@tonic-gate 				curnum, MaxQueueChildren, CurRunners,
15157c478bd9Sstevel@tonic-gate 				WorkGrp[curnum].wg_maxact);
15167c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
15177c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
15187c478bd9Sstevel@tonic-gate 		    CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren)
15197c478bd9Sstevel@tonic-gate 			break;
15207c478bd9Sstevel@tonic-gate 
15217c478bd9Sstevel@tonic-gate 		/*
15227c478bd9Sstevel@tonic-gate 		**  Pick up where we left off (curnum), in case we
15237c478bd9Sstevel@tonic-gate 		**  used up all the children last time without finishing.
15247c478bd9Sstevel@tonic-gate 		**  This give a round-robin fairness to queue runs.
15257c478bd9Sstevel@tonic-gate 		**
15267c478bd9Sstevel@tonic-gate 		**  Increment CurRunners before calling run_work_group()
15277c478bd9Sstevel@tonic-gate 		**  to avoid a "race condition" with proc_list_drop() which
15287c478bd9Sstevel@tonic-gate 		**  decrements CurRunners if the queue runners terminate.
15297c478bd9Sstevel@tonic-gate 		**  Notice: CurRunners is an upper limit, in some cases
15307c478bd9Sstevel@tonic-gate 		**  (too few jobs in the queue) this value is larger than
15317c478bd9Sstevel@tonic-gate 		**  the actual number of queue runners. The discrepancy can
15327c478bd9Sstevel@tonic-gate 		**  increase if some queue runners "hang" for a long time.
15337c478bd9Sstevel@tonic-gate 		*/
15347c478bd9Sstevel@tonic-gate 
15357c478bd9Sstevel@tonic-gate 		CurRunners += WorkGrp[curnum].wg_maxact;
15367c478bd9Sstevel@tonic-gate 		if (forkflag)
15377c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_FORK;
15387c478bd9Sstevel@tonic-gate 		if (verbose)
15397c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_VERBOSE;
15407c478bd9Sstevel@tonic-gate 		if (persistent)
15417c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_PERSISTENT;
15427c478bd9Sstevel@tonic-gate 		if (runall)
15437c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_RUNALL;
15447c478bd9Sstevel@tonic-gate 		ret = run_work_group(curnum, rwgflags);
15457c478bd9Sstevel@tonic-gate 
15467c478bd9Sstevel@tonic-gate 		/*
15477c478bd9Sstevel@tonic-gate 		**  Failure means a message was printed for ETRN
15487c478bd9Sstevel@tonic-gate 		**  and subsequent queues are likely to fail as well.
15497c478bd9Sstevel@tonic-gate 		**  Decrement CurRunners in that case because
15507c478bd9Sstevel@tonic-gate 		**  none have been started.
15517c478bd9Sstevel@tonic-gate 		*/
15527c478bd9Sstevel@tonic-gate 
15537c478bd9Sstevel@tonic-gate 		if (!ret)
15547c478bd9Sstevel@tonic-gate 		{
15557c478bd9Sstevel@tonic-gate 			CurRunners -= WorkGrp[curnum].wg_maxact;
15567c478bd9Sstevel@tonic-gate 			break;
15577c478bd9Sstevel@tonic-gate 		}
15587c478bd9Sstevel@tonic-gate 
15597c478bd9Sstevel@tonic-gate 		if (!persistent)
15607c478bd9Sstevel@tonic-gate 			schedule_queue_runs(runall, curnum, true);
15617c478bd9Sstevel@tonic-gate 		INCR_MOD(curnum, NumWorkGroups);
15627c478bd9Sstevel@tonic-gate 	}
15637c478bd9Sstevel@tonic-gate 
15647c478bd9Sstevel@tonic-gate 	/* schedule left over queue runs */
15657c478bd9Sstevel@tonic-gate 	if (i < NumWorkGroups && !NoMoreRunners && !persistent)
15667c478bd9Sstevel@tonic-gate 	{
15677c478bd9Sstevel@tonic-gate 		int h;
15687c478bd9Sstevel@tonic-gate 
15697c478bd9Sstevel@tonic-gate 		for (h = curnum; i < NumWorkGroups; i++)
15707c478bd9Sstevel@tonic-gate 		{
15717c478bd9Sstevel@tonic-gate 			schedule_queue_runs(runall, h, false);
15727c478bd9Sstevel@tonic-gate 			INCR_MOD(h, NumWorkGroups);
15737c478bd9Sstevel@tonic-gate 		}
15747c478bd9Sstevel@tonic-gate 	}
15757c478bd9Sstevel@tonic-gate 
15767c478bd9Sstevel@tonic-gate 
15777c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
15787c478bd9Sstevel@tonic-gate 	if (sm_debug_active(&DebugLeakQ, 1))
15797c478bd9Sstevel@tonic-gate 		sm_heap_setgroup(oldgroup);
15807c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
15817c478bd9Sstevel@tonic-gate 	return ret;
15827c478bd9Sstevel@tonic-gate }
15837c478bd9Sstevel@tonic-gate 
15847c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
15857c478bd9Sstevel@tonic-gate /*
15867c478bd9Sstevel@tonic-gate **  SKIP_DOMAINS -- Skip 'skip' number of domains in the WorkQ.
15877c478bd9Sstevel@tonic-gate **
15887c478bd9Sstevel@tonic-gate **  Added by Stephen Frost <sfrost@snowman.net> to support
15897c478bd9Sstevel@tonic-gate **  having each runner process every N'th domain instead of
15907c478bd9Sstevel@tonic-gate **  every N'th message.
15917c478bd9Sstevel@tonic-gate **
15927c478bd9Sstevel@tonic-gate **	Parameters:
15937c478bd9Sstevel@tonic-gate **		skip -- number of domains in WorkQ to skip.
15947c478bd9Sstevel@tonic-gate **
15957c478bd9Sstevel@tonic-gate **	Returns:
15967c478bd9Sstevel@tonic-gate **		total number of messages skipped.
15977c478bd9Sstevel@tonic-gate **
15987c478bd9Sstevel@tonic-gate **	Side Effects:
15997c478bd9Sstevel@tonic-gate **		may change WorkQ
16007c478bd9Sstevel@tonic-gate */
16017c478bd9Sstevel@tonic-gate 
16027c478bd9Sstevel@tonic-gate static int
16037c478bd9Sstevel@tonic-gate skip_domains(skip)
16047c478bd9Sstevel@tonic-gate 	int skip;
16057c478bd9Sstevel@tonic-gate {
16067c478bd9Sstevel@tonic-gate 	int n, seqjump;
16077c478bd9Sstevel@tonic-gate 
16087c478bd9Sstevel@tonic-gate 	for (n = 0, seqjump = 0; n < skip && WorkQ != NULL; seqjump++)
16097c478bd9Sstevel@tonic-gate 	{
16107c478bd9Sstevel@tonic-gate 		if (WorkQ->w_next != NULL)
16117c478bd9Sstevel@tonic-gate 		{
16127c478bd9Sstevel@tonic-gate 			if (WorkQ->w_host != NULL &&
16137c478bd9Sstevel@tonic-gate 			    WorkQ->w_next->w_host != NULL)
16147c478bd9Sstevel@tonic-gate 			{
16157c478bd9Sstevel@tonic-gate 				if (sm_strcasecmp(WorkQ->w_host,
16167c478bd9Sstevel@tonic-gate 						WorkQ->w_next->w_host) != 0)
16177c478bd9Sstevel@tonic-gate 					n++;
16187c478bd9Sstevel@tonic-gate 			}
16197c478bd9Sstevel@tonic-gate 			else
16207c478bd9Sstevel@tonic-gate 			{
16217c478bd9Sstevel@tonic-gate 				if ((WorkQ->w_host != NULL &&
16227c478bd9Sstevel@tonic-gate 				     WorkQ->w_next->w_host == NULL) ||
16237c478bd9Sstevel@tonic-gate 				    (WorkQ->w_host == NULL &&
16247c478bd9Sstevel@tonic-gate 				     WorkQ->w_next->w_host != NULL))
16257c478bd9Sstevel@tonic-gate 					     n++;
16267c478bd9Sstevel@tonic-gate 			}
16277c478bd9Sstevel@tonic-gate 		}
16287c478bd9Sstevel@tonic-gate 		WorkQ = WorkQ->w_next;
16297c478bd9Sstevel@tonic-gate 	}
16307c478bd9Sstevel@tonic-gate 	return seqjump;
16317c478bd9Sstevel@tonic-gate }
16327c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
16337c478bd9Sstevel@tonic-gate 
16347c478bd9Sstevel@tonic-gate /*
16357c478bd9Sstevel@tonic-gate **  RUNNER_WORK -- have a queue runner do its work
16367c478bd9Sstevel@tonic-gate **
16377c478bd9Sstevel@tonic-gate **  Have a queue runner do its work a list of entries.
16387c478bd9Sstevel@tonic-gate **  When work isn't directly being done then this process can take a signal
16397c478bd9Sstevel@tonic-gate **  and terminate immediately (in a clean fashion of course).
16407c478bd9Sstevel@tonic-gate **  When work is directly being done, it's not to be interrupted
16417c478bd9Sstevel@tonic-gate **  immediately: the work should be allowed to finish at a clean point
16427c478bd9Sstevel@tonic-gate **  before termination (in a clean fashion of course).
16437c478bd9Sstevel@tonic-gate **
16447c478bd9Sstevel@tonic-gate **	Parameters:
16457c478bd9Sstevel@tonic-gate **		e -- envelope.
16467c478bd9Sstevel@tonic-gate **		sequenceno -- 'th process to run WorkQ.
16477c478bd9Sstevel@tonic-gate **		didfork -- did the calling process fork()?
16487c478bd9Sstevel@tonic-gate **		skip -- process only each skip'th item.
16497c478bd9Sstevel@tonic-gate **		njobs -- number of jobs in WorkQ.
16507c478bd9Sstevel@tonic-gate **
16517c478bd9Sstevel@tonic-gate **	Returns:
16527c478bd9Sstevel@tonic-gate **		none.
16537c478bd9Sstevel@tonic-gate **
16547c478bd9Sstevel@tonic-gate **	Side Effects:
16557c478bd9Sstevel@tonic-gate **		runs things in the mail queue.
16567c478bd9Sstevel@tonic-gate */
16577c478bd9Sstevel@tonic-gate 
16587c478bd9Sstevel@tonic-gate static void
16597c478bd9Sstevel@tonic-gate runner_work(e, sequenceno, didfork, skip, njobs)
16607c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
16617c478bd9Sstevel@tonic-gate 	int sequenceno;
16627c478bd9Sstevel@tonic-gate 	bool didfork;
16637c478bd9Sstevel@tonic-gate 	int skip;
16647c478bd9Sstevel@tonic-gate 	int njobs;
16657c478bd9Sstevel@tonic-gate {
16667c478bd9Sstevel@tonic-gate 	int n, seqjump;
16677c478bd9Sstevel@tonic-gate 	WORK *w;
16687c478bd9Sstevel@tonic-gate 	time_t now;
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate 	SM_GET_LA(now);
16717c478bd9Sstevel@tonic-gate 
16727c478bd9Sstevel@tonic-gate 	/*
16737c478bd9Sstevel@tonic-gate 	**  Here we temporarily block the second calling of the handlers.
16747c478bd9Sstevel@tonic-gate 	**  This allows us to handle the signal without terminating in the
16757c478bd9Sstevel@tonic-gate 	**  middle of direct work. If a signal does come, the test for
16767c478bd9Sstevel@tonic-gate 	**  NoMoreRunners will find it.
16777c478bd9Sstevel@tonic-gate 	*/
16787c478bd9Sstevel@tonic-gate 
16797c478bd9Sstevel@tonic-gate 	BlockOldsh = true;
16807c478bd9Sstevel@tonic-gate 	seqjump = skip;
16817c478bd9Sstevel@tonic-gate 
16827c478bd9Sstevel@tonic-gate 	/* process them once at a time */
16837c478bd9Sstevel@tonic-gate 	while (WorkQ != NULL)
16847c478bd9Sstevel@tonic-gate 	{
16857c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
16867c478bd9Sstevel@tonic-gate 		SM_NONVOLATILE int oldgroup = 0;
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&DebugLeakQ, 1))
16897c478bd9Sstevel@tonic-gate 		{
16907c478bd9Sstevel@tonic-gate 			oldgroup = sm_heap_group();
16917c478bd9Sstevel@tonic-gate 			sm_heap_newgroup();
16927c478bd9Sstevel@tonic-gate 			sm_dprintf("run_queue_group() heap group #%d\n",
16937c478bd9Sstevel@tonic-gate 				sm_heap_group());
16947c478bd9Sstevel@tonic-gate 		}
16957c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
16967c478bd9Sstevel@tonic-gate 
16977c478bd9Sstevel@tonic-gate 		/* do no more work */
16987c478bd9Sstevel@tonic-gate 		if (NoMoreRunners)
16997c478bd9Sstevel@tonic-gate 		{
17007c478bd9Sstevel@tonic-gate 			/* Check that a valid signal handler is callable */
17017c478bd9Sstevel@tonic-gate 			if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
17027c478bd9Sstevel@tonic-gate 			    Oldsh != runners_sighup &&
17037c478bd9Sstevel@tonic-gate 			    Oldsh != runners_sigterm)
17047c478bd9Sstevel@tonic-gate 				(*Oldsh)(Oldsig);
17057c478bd9Sstevel@tonic-gate 			break;
17067c478bd9Sstevel@tonic-gate 		}
17077c478bd9Sstevel@tonic-gate 
17087c478bd9Sstevel@tonic-gate 		w = WorkQ; /* assign current work item */
17097c478bd9Sstevel@tonic-gate 
17107c478bd9Sstevel@tonic-gate 		/*
17117c478bd9Sstevel@tonic-gate 		**  Set the head of the WorkQ to the next work item.
17127c478bd9Sstevel@tonic-gate 		**  It is set 'skip' ahead (the number of parallel queue
17137c478bd9Sstevel@tonic-gate 		**  runners working on WorkQ together) since each runner
17147c478bd9Sstevel@tonic-gate 		**  works on every 'skip'th (N-th) item.
17157c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
17167c478bd9Sstevel@tonic-gate 		**  In the case of the BYHOST Queue Sort Order, the 'item'
17177c478bd9Sstevel@tonic-gate 		**  is a domain, so we work on every 'skip'th (N-th) domain.
17187c478bd9Sstevel@tonic-gate #endif * _FFR_SKIP_DOMAINS *
17197c478bd9Sstevel@tonic-gate 		*/
17207c478bd9Sstevel@tonic-gate 
17217c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
17227c478bd9Sstevel@tonic-gate 		if (QueueSortOrder == QSO_BYHOST)
17237c478bd9Sstevel@tonic-gate 		{
17247c478bd9Sstevel@tonic-gate 			seqjump = 1;
17257c478bd9Sstevel@tonic-gate 			if (WorkQ->w_next != NULL)
17267c478bd9Sstevel@tonic-gate 			{
17277c478bd9Sstevel@tonic-gate 				if (WorkQ->w_host != NULL &&
17287c478bd9Sstevel@tonic-gate 				    WorkQ->w_next->w_host != NULL)
17297c478bd9Sstevel@tonic-gate 				{
17307c478bd9Sstevel@tonic-gate 					if (sm_strcasecmp(WorkQ->w_host,
17317c478bd9Sstevel@tonic-gate 							WorkQ->w_next->w_host)
17327c478bd9Sstevel@tonic-gate 								!= 0)
17337c478bd9Sstevel@tonic-gate 						seqjump = skip_domains(skip);
17347c478bd9Sstevel@tonic-gate 					else
17357c478bd9Sstevel@tonic-gate 						WorkQ = WorkQ->w_next;
17367c478bd9Sstevel@tonic-gate 				}
17377c478bd9Sstevel@tonic-gate 				else
17387c478bd9Sstevel@tonic-gate 				{
17397c478bd9Sstevel@tonic-gate 					if ((WorkQ->w_host != NULL &&
17407c478bd9Sstevel@tonic-gate 					     WorkQ->w_next->w_host == NULL) ||
17417c478bd9Sstevel@tonic-gate 					    (WorkQ->w_host == NULL &&
17427c478bd9Sstevel@tonic-gate 					     WorkQ->w_next->w_host != NULL))
17437c478bd9Sstevel@tonic-gate 						seqjump = skip_domains(skip);
17447c478bd9Sstevel@tonic-gate 					else
17457c478bd9Sstevel@tonic-gate 						WorkQ = WorkQ->w_next;
17467c478bd9Sstevel@tonic-gate 				}
17477c478bd9Sstevel@tonic-gate 			}
17487c478bd9Sstevel@tonic-gate 			else
17497c478bd9Sstevel@tonic-gate 				WorkQ = WorkQ->w_next;
17507c478bd9Sstevel@tonic-gate 		}
17517c478bd9Sstevel@tonic-gate 		else
17527c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
17537c478bd9Sstevel@tonic-gate 		{
17547c478bd9Sstevel@tonic-gate 			for (n = 0; n < skip && WorkQ != NULL; n++)
17557c478bd9Sstevel@tonic-gate 				WorkQ = WorkQ->w_next;
17567c478bd9Sstevel@tonic-gate 		}
17577c478bd9Sstevel@tonic-gate 
17587c478bd9Sstevel@tonic-gate 		e->e_to = NULL;
17597c478bd9Sstevel@tonic-gate 
17607c478bd9Sstevel@tonic-gate 		/*
17617c478bd9Sstevel@tonic-gate 		**  Ignore jobs that are too expensive for the moment.
17627c478bd9Sstevel@tonic-gate 		**
17637c478bd9Sstevel@tonic-gate 		**	Get new load average every GET_NEW_LA_TIME seconds.
17647c478bd9Sstevel@tonic-gate 		*/
17657c478bd9Sstevel@tonic-gate 
17667c478bd9Sstevel@tonic-gate 		SM_GET_LA(now);
17677c478bd9Sstevel@tonic-gate 		if (shouldqueue(WkRecipFact, Current_LA_time))
17687c478bd9Sstevel@tonic-gate 		{
17697c478bd9Sstevel@tonic-gate 			char *msg = "Aborting queue run: load average too high";
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 			if (Verbose)
17727c478bd9Sstevel@tonic-gate 				message("%s", msg);
17737c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
17747c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
17757c478bd9Sstevel@tonic-gate 			break;
17767c478bd9Sstevel@tonic-gate 		}
17777c478bd9Sstevel@tonic-gate 		if (shouldqueue(w->w_pri, w->w_ctime))
17787c478bd9Sstevel@tonic-gate 		{
17797c478bd9Sstevel@tonic-gate 			if (Verbose)
17807c478bd9Sstevel@tonic-gate 				message(EmptyString);
17817c478bd9Sstevel@tonic-gate 			if (QueueSortOrder == QSO_BYPRIORITY)
17827c478bd9Sstevel@tonic-gate 			{
17837c478bd9Sstevel@tonic-gate 				if (Verbose)
17847c478bd9Sstevel@tonic-gate 					message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
17857c478bd9Sstevel@tonic-gate 						qid_printqueue(w->w_qgrp,
17867c478bd9Sstevel@tonic-gate 							       w->w_qdir),
17877c478bd9Sstevel@tonic-gate 						w->w_name + 2, sequenceno,
17887c478bd9Sstevel@tonic-gate 						njobs);
17897c478bd9Sstevel@tonic-gate 				if (LogLevel > 8)
17907c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
17917c478bd9Sstevel@tonic-gate 						  "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
17927c478bd9Sstevel@tonic-gate 						  qid_printqueue(w->w_qgrp,
17937c478bd9Sstevel@tonic-gate 								 w->w_qdir),
17947c478bd9Sstevel@tonic-gate 						  w->w_name + 2, w->w_pri,
17957c478bd9Sstevel@tonic-gate 						  CurrentLA, sequenceno,
17967c478bd9Sstevel@tonic-gate 						  njobs);
17977c478bd9Sstevel@tonic-gate 				break;
17987c478bd9Sstevel@tonic-gate 			}
17997c478bd9Sstevel@tonic-gate 			else if (Verbose)
18007c478bd9Sstevel@tonic-gate 				message("Skipping %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 		else
18057c478bd9Sstevel@tonic-gate 		{
18067c478bd9Sstevel@tonic-gate 			if (Verbose)
18077c478bd9Sstevel@tonic-gate 			{
18087c478bd9Sstevel@tonic-gate 				message(EmptyString);
18097c478bd9Sstevel@tonic-gate 				message("Running %s/%s (sequence %d of %d)",
18107c478bd9Sstevel@tonic-gate 					qid_printqueue(w->w_qgrp, w->w_qdir),
18117c478bd9Sstevel@tonic-gate 					w->w_name + 2, sequenceno, njobs);
18127c478bd9Sstevel@tonic-gate 			}
18137c478bd9Sstevel@tonic-gate 			if (didfork && MaxQueueChildren > 0)
18147c478bd9Sstevel@tonic-gate 			{
18157c478bd9Sstevel@tonic-gate 				sm_blocksignal(SIGCHLD);
18167c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, reapchild);
18177c478bd9Sstevel@tonic-gate 			}
18187c478bd9Sstevel@tonic-gate 			if (tTd(63, 100))
18197c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_DEBUG, NOQID,
18207c478bd9Sstevel@tonic-gate 					  "runqueue %s dowork(%s)",
18217c478bd9Sstevel@tonic-gate 					  qid_printqueue(w->w_qgrp, w->w_qdir),
18227c478bd9Sstevel@tonic-gate 					  w->w_name + 2);
18237c478bd9Sstevel@tonic-gate 
18247c478bd9Sstevel@tonic-gate 			(void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
18257c478bd9Sstevel@tonic-gate 				      ForkQueueRuns, false, e);
18267c478bd9Sstevel@tonic-gate 			errno = 0;
18277c478bd9Sstevel@tonic-gate 		}
18287c478bd9Sstevel@tonic-gate 		sm_free(w->w_name); /* XXX */
18297c478bd9Sstevel@tonic-gate 		if (w->w_host != NULL)
18307c478bd9Sstevel@tonic-gate 			sm_free(w->w_host); /* XXX */
18317c478bd9Sstevel@tonic-gate 		sm_free((char *) w); /* XXX */
18327c478bd9Sstevel@tonic-gate 		sequenceno += seqjump; /* next sequence number */
18337c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
18347c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&DebugLeakQ, 1))
18357c478bd9Sstevel@tonic-gate 			sm_heap_setgroup(oldgroup);
18367c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
18377c478bd9Sstevel@tonic-gate 	}
18387c478bd9Sstevel@tonic-gate 
18397c478bd9Sstevel@tonic-gate 	BlockOldsh = false;
18407c478bd9Sstevel@tonic-gate 
18417c478bd9Sstevel@tonic-gate 	/* check the signals didn't happen during the revert */
18427c478bd9Sstevel@tonic-gate 	if (NoMoreRunners)
18437c478bd9Sstevel@tonic-gate 	{
18447c478bd9Sstevel@tonic-gate 		/* Check that a valid signal handler is callable */
18457c478bd9Sstevel@tonic-gate 		if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
18467c478bd9Sstevel@tonic-gate 		    Oldsh != runners_sighup && Oldsh != runners_sigterm)
18477c478bd9Sstevel@tonic-gate 			(*Oldsh)(Oldsig);
18487c478bd9Sstevel@tonic-gate 	}
18497c478bd9Sstevel@tonic-gate 
18507c478bd9Sstevel@tonic-gate 	Oldsh = SIG_DFL; /* after the NoMoreRunners check */
18517c478bd9Sstevel@tonic-gate }
18527c478bd9Sstevel@tonic-gate /*
18537c478bd9Sstevel@tonic-gate **  RUN_WORK_GROUP -- run the jobs in a queue group from a work group.
18547c478bd9Sstevel@tonic-gate **
18557c478bd9Sstevel@tonic-gate **	Gets the stuff out of the queue in some presumably logical
18567c478bd9Sstevel@tonic-gate **	order and processes them.
18577c478bd9Sstevel@tonic-gate **
18587c478bd9Sstevel@tonic-gate **	Parameters:
18597c478bd9Sstevel@tonic-gate **		wgrp -- work group to process.
18607c478bd9Sstevel@tonic-gate **		flags -- RWG_* flags
18617c478bd9Sstevel@tonic-gate **
18627c478bd9Sstevel@tonic-gate **	Returns:
18637c478bd9Sstevel@tonic-gate **		true if the queue run successfully began.
18647c478bd9Sstevel@tonic-gate **
18657c478bd9Sstevel@tonic-gate **	Side Effects:
18667c478bd9Sstevel@tonic-gate **		runs things in the mail queue.
18677c478bd9Sstevel@tonic-gate */
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate /* Minimum sleep time for persistent queue runners */
18707c478bd9Sstevel@tonic-gate #define MIN_SLEEP_TIME	5
18717c478bd9Sstevel@tonic-gate 
18727c478bd9Sstevel@tonic-gate bool
18737c478bd9Sstevel@tonic-gate run_work_group(wgrp, flags)
18747c478bd9Sstevel@tonic-gate 	int wgrp;
18757c478bd9Sstevel@tonic-gate 	int flags;
18767c478bd9Sstevel@tonic-gate {
18777c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
18787c478bd9Sstevel@tonic-gate 	int njobs, qdir;
18797c478bd9Sstevel@tonic-gate 	int sequenceno = 1;
18807c478bd9Sstevel@tonic-gate 	int qgrp, endgrp, h, i;
18817c478bd9Sstevel@tonic-gate 	time_t now;
18827c478bd9Sstevel@tonic-gate 	bool full, more;
18837c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
18847c478bd9Sstevel@tonic-gate 	extern ENVELOPE BlankEnvelope;
18857c478bd9Sstevel@tonic-gate 	extern SIGFUNC_DECL reapchild __P((int));
18867c478bd9Sstevel@tonic-gate 
18877c478bd9Sstevel@tonic-gate 	if (wgrp < 0)
18887c478bd9Sstevel@tonic-gate 		return false;
18897c478bd9Sstevel@tonic-gate 
18907c478bd9Sstevel@tonic-gate 	/*
18917c478bd9Sstevel@tonic-gate 	**  If no work will ever be selected, don't even bother reading
18927c478bd9Sstevel@tonic-gate 	**  the queue.
18937c478bd9Sstevel@tonic-gate 	*/
18947c478bd9Sstevel@tonic-gate 
18957c478bd9Sstevel@tonic-gate 	SM_GET_LA(now);
18967c478bd9Sstevel@tonic-gate 
18977c478bd9Sstevel@tonic-gate 	if (!bitset(RWG_PERSISTENT, flags) &&
18987c478bd9Sstevel@tonic-gate 	    shouldqueue(WkRecipFact, Current_LA_time))
18997c478bd9Sstevel@tonic-gate 	{
19007c478bd9Sstevel@tonic-gate 		char *msg = "Skipping queue run -- load average too high";
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 		if (bitset(RWG_VERBOSE, flags))
19037c478bd9Sstevel@tonic-gate 			message("458 %s\n", msg);
19047c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
19057c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
19067c478bd9Sstevel@tonic-gate 		return false;
19077c478bd9Sstevel@tonic-gate 	}
19087c478bd9Sstevel@tonic-gate 
19097c478bd9Sstevel@tonic-gate 	/*
19107c478bd9Sstevel@tonic-gate 	**  See if we already have too many children.
19117c478bd9Sstevel@tonic-gate 	*/
19127c478bd9Sstevel@tonic-gate 
19137c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags) &&
19147c478bd9Sstevel@tonic-gate 	    WorkGrp[wgrp].wg_lowqintvl > 0 &&
19157c478bd9Sstevel@tonic-gate 	    !bitset(RWG_PERSISTENT, flags) &&
19167c478bd9Sstevel@tonic-gate 	    MaxChildren > 0 && CurChildren >= MaxChildren)
19177c478bd9Sstevel@tonic-gate 	{
19187c478bd9Sstevel@tonic-gate 		char *msg = "Skipping queue run -- too many children";
19197c478bd9Sstevel@tonic-gate 
19207c478bd9Sstevel@tonic-gate 		if (bitset(RWG_VERBOSE, flags))
19217c478bd9Sstevel@tonic-gate 			message("458 %s (%d)\n", msg, CurChildren);
19227c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
19237c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)",
19247c478bd9Sstevel@tonic-gate 				  msg, CurChildren);
19257c478bd9Sstevel@tonic-gate 		return false;
19267c478bd9Sstevel@tonic-gate 	}
19277c478bd9Sstevel@tonic-gate 
19287c478bd9Sstevel@tonic-gate 	/*
19297c478bd9Sstevel@tonic-gate 	**  See if we want to go off and do other useful work.
19307c478bd9Sstevel@tonic-gate 	*/
19317c478bd9Sstevel@tonic-gate 
19327c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
19337c478bd9Sstevel@tonic-gate 	{
19347c478bd9Sstevel@tonic-gate 		pid_t pid;
19357c478bd9Sstevel@tonic-gate 
19367c478bd9Sstevel@tonic-gate 		(void) sm_blocksignal(SIGCHLD);
19377c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGCHLD, reapchild);
19387c478bd9Sstevel@tonic-gate 
19397c478bd9Sstevel@tonic-gate 		pid = dofork();
19407c478bd9Sstevel@tonic-gate 		if (pid == -1)
19417c478bd9Sstevel@tonic-gate 		{
19427c478bd9Sstevel@tonic-gate 			const char *msg = "Skipping queue run -- fork() failed";
19437c478bd9Sstevel@tonic-gate 			const char *err = sm_errstring(errno);
19447c478bd9Sstevel@tonic-gate 
19457c478bd9Sstevel@tonic-gate 			if (bitset(RWG_VERBOSE, flags))
19467c478bd9Sstevel@tonic-gate 				message("458 %s: %s\n", msg, err);
19477c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
19487c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s",
19497c478bd9Sstevel@tonic-gate 					  msg, err);
19507c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGCHLD);
19517c478bd9Sstevel@tonic-gate 			return false;
19527c478bd9Sstevel@tonic-gate 		}
19537c478bd9Sstevel@tonic-gate 		if (pid != 0)
19547c478bd9Sstevel@tonic-gate 		{
19557c478bd9Sstevel@tonic-gate 			/* parent -- pick up intermediate zombie */
19567c478bd9Sstevel@tonic-gate 			(void) sm_blocksignal(SIGALRM);
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 			/* wgrp only used when queue runners are persistent */
19597c478bd9Sstevel@tonic-gate 			proc_list_add(pid, "Queue runner", PROC_QUEUE,
19607c478bd9Sstevel@tonic-gate 				      WorkGrp[wgrp].wg_maxact,
19617c478bd9Sstevel@tonic-gate 				      bitset(RWG_PERSISTENT, flags) ? wgrp : -1,
19627c478bd9Sstevel@tonic-gate 				      NULL);
19637c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGALRM);
19647c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGCHLD);
19657c478bd9Sstevel@tonic-gate 			return true;
19667c478bd9Sstevel@tonic-gate 		}
19677c478bd9Sstevel@tonic-gate 
19687c478bd9Sstevel@tonic-gate 		/* child -- clean up signals */
19697c478bd9Sstevel@tonic-gate 
19707c478bd9Sstevel@tonic-gate 		/* Reset global flags */
19717c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
19727c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
19737c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
19747c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
19757c478bd9Sstevel@tonic-gate 		CurrentPid = getpid();
19767c478bd9Sstevel@tonic-gate 		close_sendmail_pid();
19777c478bd9Sstevel@tonic-gate 
19787c478bd9Sstevel@tonic-gate 		/*
19797c478bd9Sstevel@tonic-gate 		**  Initialize exception stack and default exception
19807c478bd9Sstevel@tonic-gate 		**  handler for child process.
19817c478bd9Sstevel@tonic-gate 		*/
19827c478bd9Sstevel@tonic-gate 
19837c478bd9Sstevel@tonic-gate 		sm_exc_newthread(fatal_error);
19847c478bd9Sstevel@tonic-gate 		clrcontrol();
19857c478bd9Sstevel@tonic-gate 		proc_list_clear();
19867c478bd9Sstevel@tonic-gate 
19877c478bd9Sstevel@tonic-gate 		/* Add parent process as first child item */
19887c478bd9Sstevel@tonic-gate 		proc_list_add(CurrentPid, "Queue runner child process",
19897c478bd9Sstevel@tonic-gate 			      PROC_QUEUE_CHILD, 0, -1, NULL);
19907c478bd9Sstevel@tonic-gate 		(void) sm_releasesignal(SIGCHLD);
19917c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGCHLD, SIG_DFL);
19927c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGHUP, SIG_DFL);
19937c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGTERM, intsig);
19947c478bd9Sstevel@tonic-gate 	}
19957c478bd9Sstevel@tonic-gate 
19967c478bd9Sstevel@tonic-gate 	/*
19977c478bd9Sstevel@tonic-gate 	**  Release any resources used by the daemon code.
19987c478bd9Sstevel@tonic-gate 	*/
19997c478bd9Sstevel@tonic-gate 
20007c478bd9Sstevel@tonic-gate 	clrdaemon();
20017c478bd9Sstevel@tonic-gate 
20027c478bd9Sstevel@tonic-gate 	/* force it to run expensive jobs */
20037c478bd9Sstevel@tonic-gate 	NoConnect = false;
20047c478bd9Sstevel@tonic-gate 
20057c478bd9Sstevel@tonic-gate 	/* drop privileges */
20067c478bd9Sstevel@tonic-gate 	if (geteuid() == (uid_t) 0)
20077c478bd9Sstevel@tonic-gate 		(void) drop_privileges(false);
20087c478bd9Sstevel@tonic-gate 
20097c478bd9Sstevel@tonic-gate 	/*
20107c478bd9Sstevel@tonic-gate 	**  Create ourselves an envelope
20117c478bd9Sstevel@tonic-gate 	*/
20127c478bd9Sstevel@tonic-gate 
20137c478bd9Sstevel@tonic-gate 	CurEnv = &QueueEnvelope;
20147c478bd9Sstevel@tonic-gate 	rpool = sm_rpool_new_x(NULL);
20157c478bd9Sstevel@tonic-gate 	e = newenvelope(&QueueEnvelope, CurEnv, rpool);
20167c478bd9Sstevel@tonic-gate 	e->e_flags = BlankEnvelope.e_flags;
20177c478bd9Sstevel@tonic-gate 	e->e_parent = NULL;
20187c478bd9Sstevel@tonic-gate 
20197c478bd9Sstevel@tonic-gate 	/* make sure we have disconnected from parent */
20207c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
20217c478bd9Sstevel@tonic-gate 	{
20227c478bd9Sstevel@tonic-gate 		disconnect(1, e);
20237c478bd9Sstevel@tonic-gate 		QuickAbort = false;
20247c478bd9Sstevel@tonic-gate 	}
20257c478bd9Sstevel@tonic-gate 
20267c478bd9Sstevel@tonic-gate 	/*
20277c478bd9Sstevel@tonic-gate 	**  If we are running part of the queue, always ignore stored
20287c478bd9Sstevel@tonic-gate 	**  host status.
20297c478bd9Sstevel@tonic-gate 	*/
20307c478bd9Sstevel@tonic-gate 
20317c478bd9Sstevel@tonic-gate 	if (QueueLimitId != NULL || QueueLimitSender != NULL ||
20327c478bd9Sstevel@tonic-gate 	    QueueLimitQuarantine != NULL ||
20337c478bd9Sstevel@tonic-gate 	    QueueLimitRecipient != NULL)
20347c478bd9Sstevel@tonic-gate 	{
20357c478bd9Sstevel@tonic-gate 		IgnoreHostStatus = true;
20367c478bd9Sstevel@tonic-gate 		MinQueueAge = 0;
20377c478bd9Sstevel@tonic-gate 	}
20387c478bd9Sstevel@tonic-gate 
20397c478bd9Sstevel@tonic-gate 	/*
20407c478bd9Sstevel@tonic-gate 	**  Here is where we choose the queue group from the work group.
20417c478bd9Sstevel@tonic-gate 	**  The caller of the "domorework" label must setup a new envelope.
20427c478bd9Sstevel@tonic-gate 	*/
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate 	endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
20457c478bd9Sstevel@tonic-gate 
20467c478bd9Sstevel@tonic-gate   domorework:
20477c478bd9Sstevel@tonic-gate 
20487c478bd9Sstevel@tonic-gate 	/*
20497c478bd9Sstevel@tonic-gate 	**  Run a queue group if:
20507c478bd9Sstevel@tonic-gate 	**  RWG_RUNALL bit is set or the bit for this group is set.
20517c478bd9Sstevel@tonic-gate 	*/
20527c478bd9Sstevel@tonic-gate 
20537c478bd9Sstevel@tonic-gate 	now = curtime();
20547c478bd9Sstevel@tonic-gate 	for (;;)
20557c478bd9Sstevel@tonic-gate 	{
20567c478bd9Sstevel@tonic-gate 		/*
20577c478bd9Sstevel@tonic-gate 		**  Find the next queue group within the work group that
20587c478bd9Sstevel@tonic-gate 		**  has been marked as needing a run.
20597c478bd9Sstevel@tonic-gate 		*/
20607c478bd9Sstevel@tonic-gate 
20617c478bd9Sstevel@tonic-gate 		qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index;
20627c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_curqgrp++; /* advance */
20637c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */
20647c478bd9Sstevel@tonic-gate 		if (bitset(RWG_RUNALL, flags) ||
20657c478bd9Sstevel@tonic-gate 		    (Queue[qgrp]->qg_nextrun <= now &&
20667c478bd9Sstevel@tonic-gate 		     Queue[qgrp]->qg_nextrun != (time_t) -1))
20677c478bd9Sstevel@tonic-gate 			break;
20687c478bd9Sstevel@tonic-gate 		if (endgrp == WorkGrp[wgrp].wg_curqgrp)
20697c478bd9Sstevel@tonic-gate 		{
20707c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
20717c478bd9Sstevel@tonic-gate 			if (bitset(RWG_FORK, flags))
20727c478bd9Sstevel@tonic-gate 				finis(true, true, ExitStat);
20737c478bd9Sstevel@tonic-gate 			return true; /* we're done */
20747c478bd9Sstevel@tonic-gate 		}
20757c478bd9Sstevel@tonic-gate 	}
20767c478bd9Sstevel@tonic-gate 
20777c478bd9Sstevel@tonic-gate 	qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */
20787c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
20797c478bd9Sstevel@tonic-gate 	if (tTd(69, 12))
20807c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
20817c478bd9Sstevel@tonic-gate 			"rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d",
20827c478bd9Sstevel@tonic-gate 			wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir),
20837c478bd9Sstevel@tonic-gate 			WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp);
20847c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate #if HASNICE
20877c478bd9Sstevel@tonic-gate 	/* tweak niceness of queue runs */
20887c478bd9Sstevel@tonic-gate 	if (Queue[qgrp]->qg_nice > 0)
20897c478bd9Sstevel@tonic-gate 		(void) nice(Queue[qgrp]->qg_nice);
20907c478bd9Sstevel@tonic-gate #endif /* HASNICE */
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 	/* XXX running queue group... */
20937c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, CurEnv, "running queue: %s",
20947c478bd9Sstevel@tonic-gate 			qid_printqueue(qgrp, qdir));
20957c478bd9Sstevel@tonic-gate 
20967c478bd9Sstevel@tonic-gate 	if (LogLevel > 69 || tTd(63, 99))
20977c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, NOQID,
20987c478bd9Sstevel@tonic-gate 			  "runqueue %s, pid=%d, forkflag=%d",
20997c478bd9Sstevel@tonic-gate 			  qid_printqueue(qgrp, qdir), (int) CurrentPid,
21007c478bd9Sstevel@tonic-gate 			  bitset(RWG_FORK, flags));
21017c478bd9Sstevel@tonic-gate 
21027c478bd9Sstevel@tonic-gate 	/*
21037c478bd9Sstevel@tonic-gate 	**  Start making passes through the queue.
21047c478bd9Sstevel@tonic-gate 	**	First, read and sort the entire queue.
21057c478bd9Sstevel@tonic-gate 	**	Then, process the work in that order.
21067c478bd9Sstevel@tonic-gate 	**		But if you take too long, start over.
21077c478bd9Sstevel@tonic-gate 	*/
21087c478bd9Sstevel@tonic-gate 
21097c478bd9Sstevel@tonic-gate 	for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
21107c478bd9Sstevel@tonic-gate 	{
21117c478bd9Sstevel@tonic-gate 		h = gatherq(qgrp, qdir, false, &full, &more);
21127c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
21137c478bd9Sstevel@tonic-gate 		if (ShmId != SM_SHM_NO_ID)
21147c478bd9Sstevel@tonic-gate 			QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h;
21157c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
21167c478bd9Sstevel@tonic-gate 		/* If there are no more items in this queue advance */
21177c478bd9Sstevel@tonic-gate 		if (!more)
21187c478bd9Sstevel@tonic-gate 		{
21197c478bd9Sstevel@tonic-gate 			/* A round-robin advance */
21207c478bd9Sstevel@tonic-gate 			qdir++;
21217c478bd9Sstevel@tonic-gate 			qdir %= Queue[qgrp]->qg_numqueues;
21227c478bd9Sstevel@tonic-gate 		}
21237c478bd9Sstevel@tonic-gate 
21247c478bd9Sstevel@tonic-gate 		/* Has the WorkList reached the limit? */
21257c478bd9Sstevel@tonic-gate 		if (full)
21267c478bd9Sstevel@tonic-gate 			break; /* don't try to gather more */
21277c478bd9Sstevel@tonic-gate 	}
21287c478bd9Sstevel@tonic-gate 
21297c478bd9Sstevel@tonic-gate 	/* order the existing work requests */
21307c478bd9Sstevel@tonic-gate 	njobs = sortq(Queue[qgrp]->qg_maxlist);
21317c478bd9Sstevel@tonic-gate 	Queue[qgrp]->qg_curnum = qdir; /* update */
21327c478bd9Sstevel@tonic-gate 
21337c478bd9Sstevel@tonic-gate 
21347c478bd9Sstevel@tonic-gate 	if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
21357c478bd9Sstevel@tonic-gate 	{
21367c478bd9Sstevel@tonic-gate 		int loop, maxrunners;
21377c478bd9Sstevel@tonic-gate 		pid_t pid;
21387c478bd9Sstevel@tonic-gate 
21397c478bd9Sstevel@tonic-gate 		/*
21407c478bd9Sstevel@tonic-gate 		**  For this WorkQ we want to fork off N children (maxrunners)
21417c478bd9Sstevel@tonic-gate 		**  at this point. Each child has a copy of WorkQ. Each child
21427c478bd9Sstevel@tonic-gate 		**  will process every N-th item. The parent will wait for all
21437c478bd9Sstevel@tonic-gate 		**  of the children to finish before moving on to the next
21447c478bd9Sstevel@tonic-gate 		**  queue group within the work group. This saves us forking
21457c478bd9Sstevel@tonic-gate 		**  a new runner-child for each work item.
21467c478bd9Sstevel@tonic-gate 		**  It's valid for qg_maxqrun == 0 since this may be an
21477c478bd9Sstevel@tonic-gate 		**  explicit "don't run this queue" setting.
21487c478bd9Sstevel@tonic-gate 		*/
21497c478bd9Sstevel@tonic-gate 
21507c478bd9Sstevel@tonic-gate 		maxrunners = Queue[qgrp]->qg_maxqrun;
21517c478bd9Sstevel@tonic-gate 
2152*7800901eSjbeck 		/*
2153*7800901eSjbeck 		**  If no runners are configured for this group but
2154*7800901eSjbeck 		**  the queue is "forced" then lets use 1 runner.
2155*7800901eSjbeck 		*/
2156*7800901eSjbeck 
2157*7800901eSjbeck 		if (maxrunners == 0 && bitset(RWG_FORCE, flags))
2158*7800901eSjbeck 			maxrunners = 1;
2159*7800901eSjbeck 
21607c478bd9Sstevel@tonic-gate 		/* No need to have more runners then there are jobs */
21617c478bd9Sstevel@tonic-gate 		if (maxrunners > njobs)
21627c478bd9Sstevel@tonic-gate 			maxrunners = njobs;
21637c478bd9Sstevel@tonic-gate 		for (loop = 0; loop < maxrunners; loop++)
21647c478bd9Sstevel@tonic-gate 		{
21657c478bd9Sstevel@tonic-gate 			/*
21667c478bd9Sstevel@tonic-gate 			**  Since the delivery may happen in a child and the
21677c478bd9Sstevel@tonic-gate 			**  parent does not wait, the parent may close the
21687c478bd9Sstevel@tonic-gate 			**  maps thereby removing any shared memory used by
21697c478bd9Sstevel@tonic-gate 			**  the map.  Therefore, close the maps now so the
21707c478bd9Sstevel@tonic-gate 			**  child will dynamically open them if necessary.
21717c478bd9Sstevel@tonic-gate 			*/
21727c478bd9Sstevel@tonic-gate 
21737c478bd9Sstevel@tonic-gate 			closemaps(false);
21747c478bd9Sstevel@tonic-gate 
21757c478bd9Sstevel@tonic-gate 			pid = fork();
21767c478bd9Sstevel@tonic-gate 			if (pid < 0)
21777c478bd9Sstevel@tonic-gate 			{
21787c478bd9Sstevel@tonic-gate 				syserr("run_work_group: cannot fork");
21797c478bd9Sstevel@tonic-gate 				return false;
21807c478bd9Sstevel@tonic-gate 			}
21817c478bd9Sstevel@tonic-gate 			else if (pid > 0)
21827c478bd9Sstevel@tonic-gate 			{
21837c478bd9Sstevel@tonic-gate 				/* parent -- clean out connection cache */
21847c478bd9Sstevel@tonic-gate 				mci_flush(false, NULL);
21857c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
21867c478bd9Sstevel@tonic-gate 				if (QueueSortOrder == QSO_BYHOST)
21877c478bd9Sstevel@tonic-gate 				{
21887c478bd9Sstevel@tonic-gate 					sequenceno += skip_domains(1);
21897c478bd9Sstevel@tonic-gate 				}
21907c478bd9Sstevel@tonic-gate 				else
21917c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
21927c478bd9Sstevel@tonic-gate 				{
21937c478bd9Sstevel@tonic-gate 					/* for the skip */
21947c478bd9Sstevel@tonic-gate 					WorkQ = WorkQ->w_next;
21957c478bd9Sstevel@tonic-gate 					sequenceno++;
21967c478bd9Sstevel@tonic-gate 				}
21977c478bd9Sstevel@tonic-gate 				proc_list_add(pid, "Queue child runner process",
21987c478bd9Sstevel@tonic-gate 					      PROC_QUEUE_CHILD, 0, -1, NULL);
21997c478bd9Sstevel@tonic-gate 
22007c478bd9Sstevel@tonic-gate 				/* No additional work, no additional runners */
22017c478bd9Sstevel@tonic-gate 				if (WorkQ == NULL)
22027c478bd9Sstevel@tonic-gate 					break;
22037c478bd9Sstevel@tonic-gate 			}
22047c478bd9Sstevel@tonic-gate 			else
22057c478bd9Sstevel@tonic-gate 			{
22067c478bd9Sstevel@tonic-gate 				/* child -- Reset global flags */
22077c478bd9Sstevel@tonic-gate 				RestartRequest = NULL;
22087c478bd9Sstevel@tonic-gate 				RestartWorkGroup = false;
22097c478bd9Sstevel@tonic-gate 				ShutdownRequest = NULL;
22107c478bd9Sstevel@tonic-gate 				PendingSignal = 0;
22117c478bd9Sstevel@tonic-gate 				CurrentPid = getpid();
22127c478bd9Sstevel@tonic-gate 				close_sendmail_pid();
22137c478bd9Sstevel@tonic-gate 
22147c478bd9Sstevel@tonic-gate 				/*
22157c478bd9Sstevel@tonic-gate 				**  Initialize exception stack and default
22167c478bd9Sstevel@tonic-gate 				**  exception handler for child process.
22177c478bd9Sstevel@tonic-gate 				**  When fork()'d the child now has a private
22187c478bd9Sstevel@tonic-gate 				**  copy of WorkQ at its current position.
22197c478bd9Sstevel@tonic-gate 				*/
22207c478bd9Sstevel@tonic-gate 
22217c478bd9Sstevel@tonic-gate 				sm_exc_newthread(fatal_error);
22227c478bd9Sstevel@tonic-gate 
22237c478bd9Sstevel@tonic-gate 				/*
22247c478bd9Sstevel@tonic-gate 				**  SMTP processes (whether -bd or -bs) set
22257c478bd9Sstevel@tonic-gate 				**  SIGCHLD to reapchild to collect
22267c478bd9Sstevel@tonic-gate 				**  children status.  However, at delivery
22277c478bd9Sstevel@tonic-gate 				**  time, that status must be collected
22287c478bd9Sstevel@tonic-gate 				**  by sm_wait() to be dealt with properly
22297c478bd9Sstevel@tonic-gate 				**  (check success of delivery based
22307c478bd9Sstevel@tonic-gate 				**  on status code, etc).  Therefore, if we
22317c478bd9Sstevel@tonic-gate 				**  are an SMTP process, reset SIGCHLD
22327c478bd9Sstevel@tonic-gate 				**  back to the default so reapchild
22337c478bd9Sstevel@tonic-gate 				**  doesn't collect status before
22347c478bd9Sstevel@tonic-gate 				**  sm_wait().
22357c478bd9Sstevel@tonic-gate 				*/
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate 				if (OpMode == MD_SMTP ||
22387c478bd9Sstevel@tonic-gate 				    OpMode == MD_DAEMON ||
22397c478bd9Sstevel@tonic-gate 				    MaxQueueChildren > 0)
22407c478bd9Sstevel@tonic-gate 				{
22417c478bd9Sstevel@tonic-gate 					proc_list_clear();
22427c478bd9Sstevel@tonic-gate 					sm_releasesignal(SIGCHLD);
22437c478bd9Sstevel@tonic-gate 					(void) sm_signal(SIGCHLD, SIG_DFL);
22447c478bd9Sstevel@tonic-gate 				}
22457c478bd9Sstevel@tonic-gate 
22467c478bd9Sstevel@tonic-gate 				/* child -- error messages to the transcript */
22477c478bd9Sstevel@tonic-gate 				QuickAbort = OnlyOneError = false;
22487c478bd9Sstevel@tonic-gate 				runner_work(e, sequenceno, true,
22497c478bd9Sstevel@tonic-gate 					    maxrunners, njobs);
22507c478bd9Sstevel@tonic-gate 
22517c478bd9Sstevel@tonic-gate 				/* This child is done */
22527c478bd9Sstevel@tonic-gate 				finis(true, true, ExitStat);
22537c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
22547c478bd9Sstevel@tonic-gate 			}
22557c478bd9Sstevel@tonic-gate 		}
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate 		sm_releasesignal(SIGCHLD);
22587c478bd9Sstevel@tonic-gate 
22597c478bd9Sstevel@tonic-gate 		/*
22607c478bd9Sstevel@tonic-gate 		**  Wait until all of the runners have completed before
22617c478bd9Sstevel@tonic-gate 		**  seeing if there is another queue group in the
22627c478bd9Sstevel@tonic-gate 		**  work group to process.
22637c478bd9Sstevel@tonic-gate 		**  XXX Future enhancement: don't wait() for all children
22647c478bd9Sstevel@tonic-gate 		**  here, just go ahead and make sure that overall the number
22657c478bd9Sstevel@tonic-gate 		**  of children is not exceeded.
22667c478bd9Sstevel@tonic-gate 		*/
22677c478bd9Sstevel@tonic-gate 
22687c478bd9Sstevel@tonic-gate 		while (CurChildren > 0)
22697c478bd9Sstevel@tonic-gate 		{
22707c478bd9Sstevel@tonic-gate 			int status;
22717c478bd9Sstevel@tonic-gate 			pid_t ret;
22727c478bd9Sstevel@tonic-gate 
22737c478bd9Sstevel@tonic-gate 			while ((ret = sm_wait(&status)) <= 0)
22747c478bd9Sstevel@tonic-gate 				continue;
22757c478bd9Sstevel@tonic-gate 			proc_list_drop(ret, status, NULL);
22767c478bd9Sstevel@tonic-gate 		}
22777c478bd9Sstevel@tonic-gate 	}
22787c478bd9Sstevel@tonic-gate 	else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags))
22797c478bd9Sstevel@tonic-gate 	{
22807c478bd9Sstevel@tonic-gate 		/*
22817c478bd9Sstevel@tonic-gate 		**  When current process will not fork children to do the work,
22827c478bd9Sstevel@tonic-gate 		**  it will do the work itself. The 'skip' will be 1 since
22837c478bd9Sstevel@tonic-gate 		**  there are no child runners to divide the work across.
22847c478bd9Sstevel@tonic-gate 		*/
22857c478bd9Sstevel@tonic-gate 
22867c478bd9Sstevel@tonic-gate 		runner_work(e, sequenceno, false, 1, njobs);
22877c478bd9Sstevel@tonic-gate 	}
22887c478bd9Sstevel@tonic-gate 
22897c478bd9Sstevel@tonic-gate 	/* free memory allocated by newenvelope() above */
22907c478bd9Sstevel@tonic-gate 	sm_rpool_free(rpool);
22917c478bd9Sstevel@tonic-gate 	QueueEnvelope.e_rpool = NULL;
22927c478bd9Sstevel@tonic-gate 
22937c478bd9Sstevel@tonic-gate 	/* Are there still more queues in the work group to process? */
22947c478bd9Sstevel@tonic-gate 	if (endgrp != WorkGrp[wgrp].wg_curqgrp)
22957c478bd9Sstevel@tonic-gate 	{
22967c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
22977c478bd9Sstevel@tonic-gate 		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
22987c478bd9Sstevel@tonic-gate 		e->e_flags = BlankEnvelope.e_flags;
22997c478bd9Sstevel@tonic-gate 		goto domorework;
23007c478bd9Sstevel@tonic-gate 	}
23017c478bd9Sstevel@tonic-gate 
23027c478bd9Sstevel@tonic-gate 	/* No more queues in work group to process. Now check persistent. */
23037c478bd9Sstevel@tonic-gate 	if (bitset(RWG_PERSISTENT, flags))
23047c478bd9Sstevel@tonic-gate 	{
23057c478bd9Sstevel@tonic-gate 		sequenceno = 1;
23067c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, CurEnv, "running queue: %s",
23077c478bd9Sstevel@tonic-gate 				qid_printqueue(qgrp, qdir));
23087c478bd9Sstevel@tonic-gate 
23097c478bd9Sstevel@tonic-gate 		/*
23107c478bd9Sstevel@tonic-gate 		**  close bogus maps, i.e., maps which caused a tempfail,
23117c478bd9Sstevel@tonic-gate 		**	so we get fresh map connections on the next lookup.
23127c478bd9Sstevel@tonic-gate 		**  closemaps() is also called when children are started.
23137c478bd9Sstevel@tonic-gate 		*/
23147c478bd9Sstevel@tonic-gate 
23157c478bd9Sstevel@tonic-gate 		closemaps(true);
23167c478bd9Sstevel@tonic-gate 
23177c478bd9Sstevel@tonic-gate 		/* Close any cached connections. */
23187c478bd9Sstevel@tonic-gate 		mci_flush(true, NULL);
23197c478bd9Sstevel@tonic-gate 
23207c478bd9Sstevel@tonic-gate 		/* Clean out expired related entries. */
23217c478bd9Sstevel@tonic-gate 		rmexpstab();
23227c478bd9Sstevel@tonic-gate 
23237c478bd9Sstevel@tonic-gate #if NAMED_BIND
23247c478bd9Sstevel@tonic-gate 		/* Update MX records for FallbackMX. */
23257c478bd9Sstevel@tonic-gate 		if (FallbackMX != NULL)
23267c478bd9Sstevel@tonic-gate 			(void) getfallbackmxrr(FallbackMX);
23277c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
23287c478bd9Sstevel@tonic-gate 
23297c478bd9Sstevel@tonic-gate #if USERDB
23307c478bd9Sstevel@tonic-gate 		/* close UserDatabase */
23317c478bd9Sstevel@tonic-gate 		_udbx_close();
23327c478bd9Sstevel@tonic-gate #endif /* USERDB */
23337c478bd9Sstevel@tonic-gate 
23347c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
23357c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&SmHeapCheck, 2)
23367c478bd9Sstevel@tonic-gate 		    && access("memdump", F_OK) == 0
23377c478bd9Sstevel@tonic-gate 		   )
23387c478bd9Sstevel@tonic-gate 		{
23397c478bd9Sstevel@tonic-gate 			SM_FILE_T *out;
23407c478bd9Sstevel@tonic-gate 
23417c478bd9Sstevel@tonic-gate 			remove("memdump");
23427c478bd9Sstevel@tonic-gate 			out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
23437c478bd9Sstevel@tonic-gate 					 "memdump.out", SM_IO_APPEND, NULL);
23447c478bd9Sstevel@tonic-gate 			if (out != NULL)
23457c478bd9Sstevel@tonic-gate 			{
23467c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
23477c478bd9Sstevel@tonic-gate 				sm_heap_report(out,
23487c478bd9Sstevel@tonic-gate 					sm_debug_level(&SmHeapCheck) - 1);
23497c478bd9Sstevel@tonic-gate 				(void) sm_io_close(out, SM_TIME_DEFAULT);
23507c478bd9Sstevel@tonic-gate 			}
23517c478bd9Sstevel@tonic-gate 		}
23527c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
23537c478bd9Sstevel@tonic-gate 
23547c478bd9Sstevel@tonic-gate 		/* let me rest for a second to catch my breath */
23557c478bd9Sstevel@tonic-gate 		if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
23567c478bd9Sstevel@tonic-gate 			sleep(MIN_SLEEP_TIME);
23577c478bd9Sstevel@tonic-gate 		else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
23587c478bd9Sstevel@tonic-gate 			sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
23597c478bd9Sstevel@tonic-gate 		else
23607c478bd9Sstevel@tonic-gate 			sleep(WorkGrp[wgrp].wg_lowqintvl);
23617c478bd9Sstevel@tonic-gate 
23627c478bd9Sstevel@tonic-gate 		/*
23637c478bd9Sstevel@tonic-gate 		**  Get the LA outside the WorkQ loop if necessary.
23647c478bd9Sstevel@tonic-gate 		**  In a persistent queue runner the code is repeated over
23657c478bd9Sstevel@tonic-gate 		**  and over but gatherq() may ignore entries due to
23667c478bd9Sstevel@tonic-gate 		**  shouldqueue() (do we really have to do this twice?).
23677c478bd9Sstevel@tonic-gate 		**  Hence the queue runners would just idle around when once
23687c478bd9Sstevel@tonic-gate 		**  CurrentLA caused all entries in a queue to be ignored.
23697c478bd9Sstevel@tonic-gate 		*/
23707c478bd9Sstevel@tonic-gate 
23717c478bd9Sstevel@tonic-gate 		if (njobs == 0)
23727c478bd9Sstevel@tonic-gate 			SM_GET_LA(now);
23737c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
23747c478bd9Sstevel@tonic-gate 		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
23757c478bd9Sstevel@tonic-gate 		e->e_flags = BlankEnvelope.e_flags;
23767c478bd9Sstevel@tonic-gate 		goto domorework;
23777c478bd9Sstevel@tonic-gate 	}
23787c478bd9Sstevel@tonic-gate 
23797c478bd9Sstevel@tonic-gate 	/* exit without the usual cleanup */
23807c478bd9Sstevel@tonic-gate 	e->e_id = NULL;
23817c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
23827c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
23837c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
23847c478bd9Sstevel@tonic-gate 	return true;
23857c478bd9Sstevel@tonic-gate }
23867c478bd9Sstevel@tonic-gate 
23877c478bd9Sstevel@tonic-gate /*
23887c478bd9Sstevel@tonic-gate **  DOQUEUERUN -- do a queue run?
23897c478bd9Sstevel@tonic-gate */
23907c478bd9Sstevel@tonic-gate 
23917c478bd9Sstevel@tonic-gate bool
23927c478bd9Sstevel@tonic-gate doqueuerun()
23937c478bd9Sstevel@tonic-gate {
23947c478bd9Sstevel@tonic-gate 	return DoQueueRun;
23957c478bd9Sstevel@tonic-gate }
23967c478bd9Sstevel@tonic-gate 
23977c478bd9Sstevel@tonic-gate /*
23987c478bd9Sstevel@tonic-gate **  RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done.
23997c478bd9Sstevel@tonic-gate **
24007c478bd9Sstevel@tonic-gate **	Parameters:
24017c478bd9Sstevel@tonic-gate **		none.
24027c478bd9Sstevel@tonic-gate **
24037c478bd9Sstevel@tonic-gate **	Returns:
24047c478bd9Sstevel@tonic-gate **		none.
24057c478bd9Sstevel@tonic-gate **
24067c478bd9Sstevel@tonic-gate **	Side Effects:
24077c478bd9Sstevel@tonic-gate **		The invocation of this function via an alarm may interrupt
24087c478bd9Sstevel@tonic-gate **		a set of actions. Thus errno may be set in that context.
24097c478bd9Sstevel@tonic-gate **		We need to restore errno at the end of this function to ensure
24107c478bd9Sstevel@tonic-gate **		that any work done here that sets errno doesn't return a
24117c478bd9Sstevel@tonic-gate **		misleading/false errno value. Errno may	be EINTR upon entry to
24127c478bd9Sstevel@tonic-gate **		this function because of non-restartable/continuable system
24137c478bd9Sstevel@tonic-gate **		API was active. Iff this is true we will override errno as
24147c478bd9Sstevel@tonic-gate **		a timeout (as a more accurate error message).
24157c478bd9Sstevel@tonic-gate **
24167c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
24177c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
24187c478bd9Sstevel@tonic-gate **		DOING.
24197c478bd9Sstevel@tonic-gate */
24207c478bd9Sstevel@tonic-gate 
24217c478bd9Sstevel@tonic-gate void
24227c478bd9Sstevel@tonic-gate runqueueevent(ignore)
24237c478bd9Sstevel@tonic-gate 	int ignore;
24247c478bd9Sstevel@tonic-gate {
24257c478bd9Sstevel@tonic-gate 	int save_errno = errno;
24267c478bd9Sstevel@tonic-gate 
24277c478bd9Sstevel@tonic-gate 	/*
24287c478bd9Sstevel@tonic-gate 	**  Set the general bit that we want a queue run,
24297c478bd9Sstevel@tonic-gate 	**  tested in doqueuerun()
24307c478bd9Sstevel@tonic-gate 	*/
24317c478bd9Sstevel@tonic-gate 
24327c478bd9Sstevel@tonic-gate 	DoQueueRun = true;
24337c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
24347c478bd9Sstevel@tonic-gate 	if (tTd(69, 10))
24357c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID, "rqe: done");
24367c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
24377c478bd9Sstevel@tonic-gate 
24387c478bd9Sstevel@tonic-gate 	errno = save_errno;
24397c478bd9Sstevel@tonic-gate 	if (errno == EINTR)
24407c478bd9Sstevel@tonic-gate 		errno = ETIMEDOUT;
24417c478bd9Sstevel@tonic-gate }
24427c478bd9Sstevel@tonic-gate /*
24437c478bd9Sstevel@tonic-gate **  GATHERQ -- gather messages from the message queue(s) the work queue.
24447c478bd9Sstevel@tonic-gate **
24457c478bd9Sstevel@tonic-gate **	Parameters:
24467c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group.
24477c478bd9Sstevel@tonic-gate **		qdir -- the index of the queue directory.
24487c478bd9Sstevel@tonic-gate **		doall -- if set, include everything in the queue (even
24497c478bd9Sstevel@tonic-gate **			the jobs that cannot be run because the load
24507c478bd9Sstevel@tonic-gate **			average is too high, or MaxQueueRun is reached).
24517c478bd9Sstevel@tonic-gate **			Otherwise, exclude those jobs.
24527c478bd9Sstevel@tonic-gate **		full -- (optional) to be set 'true' if WorkList is full
24537c478bd9Sstevel@tonic-gate **		more -- (optional) to be set 'true' if there are still more
24547c478bd9Sstevel@tonic-gate **			messages in this queue not added to WorkList
24557c478bd9Sstevel@tonic-gate **
24567c478bd9Sstevel@tonic-gate **	Returns:
24577c478bd9Sstevel@tonic-gate **		The number of request in the queue (not necessarily
24587c478bd9Sstevel@tonic-gate **		the number of requests in WorkList however).
24597c478bd9Sstevel@tonic-gate **
24607c478bd9Sstevel@tonic-gate **	Side Effects:
24617c478bd9Sstevel@tonic-gate **		prepares available work into WorkList
24627c478bd9Sstevel@tonic-gate */
24637c478bd9Sstevel@tonic-gate 
24647c478bd9Sstevel@tonic-gate #define NEED_P		0001	/* 'P': priority */
24657c478bd9Sstevel@tonic-gate #define NEED_T		0002	/* 'T': time */
24667c478bd9Sstevel@tonic-gate #define NEED_R		0004	/* 'R': recipient */
24677c478bd9Sstevel@tonic-gate #define NEED_S		0010	/* 'S': sender */
24687c478bd9Sstevel@tonic-gate #define NEED_H		0020	/* host */
24697c478bd9Sstevel@tonic-gate #define HAS_QUARANTINE	0040	/* has an unexpected 'q' line */
24707c478bd9Sstevel@tonic-gate #define NEED_QUARANTINE	0100	/* 'q': reason */
24717c478bd9Sstevel@tonic-gate 
24727c478bd9Sstevel@tonic-gate static WORK	*WorkList = NULL;	/* list of unsort work */
24737c478bd9Sstevel@tonic-gate static int	WorkListSize = 0;	/* current max size of WorkList */
24747c478bd9Sstevel@tonic-gate static int	WorkListCount = 0;	/* # of work items in WorkList */
24757c478bd9Sstevel@tonic-gate 
24767c478bd9Sstevel@tonic-gate static int
24777c478bd9Sstevel@tonic-gate gatherq(qgrp, qdir, doall, full, more)
24787c478bd9Sstevel@tonic-gate 	int qgrp;
24797c478bd9Sstevel@tonic-gate 	int qdir;
24807c478bd9Sstevel@tonic-gate 	bool doall;
24817c478bd9Sstevel@tonic-gate 	bool *full;
24827c478bd9Sstevel@tonic-gate 	bool *more;
24837c478bd9Sstevel@tonic-gate {
24847c478bd9Sstevel@tonic-gate 	register struct dirent *d;
24857c478bd9Sstevel@tonic-gate 	register WORK *w;
24867c478bd9Sstevel@tonic-gate 	register char *p;
24877c478bd9Sstevel@tonic-gate 	DIR *f;
24887c478bd9Sstevel@tonic-gate 	int i, num_ent;
24897c478bd9Sstevel@tonic-gate 	int wn;
24907c478bd9Sstevel@tonic-gate 	QUEUE_CHAR *check;
24917c478bd9Sstevel@tonic-gate 	char qd[MAXPATHLEN];
24927c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
24937c478bd9Sstevel@tonic-gate 
24947c478bd9Sstevel@tonic-gate 	wn = WorkListCount - 1;
24957c478bd9Sstevel@tonic-gate 	num_ent = 0;
24967c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
2497058561cbSjbeck 		(void) sm_strlcpy(qd, ".", sizeof(qd));
24987c478bd9Sstevel@tonic-gate 	else
2499058561cbSjbeck 		(void) sm_strlcpyn(qd, sizeof(qd), 2,
25007c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
25017c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBQF,
25027c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
25037c478bd9Sstevel@tonic-gate 					? "/qf" : ""));
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
25067c478bd9Sstevel@tonic-gate 	{
25077c478bd9Sstevel@tonic-gate 		sm_dprintf("gatherq:\n");
25087c478bd9Sstevel@tonic-gate 
25097c478bd9Sstevel@tonic-gate 		check = QueueLimitId;
25107c478bd9Sstevel@tonic-gate 		while (check != NULL)
25117c478bd9Sstevel@tonic-gate 		{
25127c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitId = %s%s\n",
25137c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25147c478bd9Sstevel@tonic-gate 				check->queue_match);
25157c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25167c478bd9Sstevel@tonic-gate 		}
25177c478bd9Sstevel@tonic-gate 
25187c478bd9Sstevel@tonic-gate 		check = QueueLimitSender;
25197c478bd9Sstevel@tonic-gate 		while (check != NULL)
25207c478bd9Sstevel@tonic-gate 		{
25217c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitSender = %s%s\n",
25227c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25237c478bd9Sstevel@tonic-gate 				check->queue_match);
25247c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25257c478bd9Sstevel@tonic-gate 		}
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 		check = QueueLimitRecipient;
25287c478bd9Sstevel@tonic-gate 		while (check != NULL)
25297c478bd9Sstevel@tonic-gate 		{
25307c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitRecipient = %s%s\n",
25317c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25327c478bd9Sstevel@tonic-gate 				check->queue_match);
25337c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25347c478bd9Sstevel@tonic-gate 		}
25357c478bd9Sstevel@tonic-gate 
25367c478bd9Sstevel@tonic-gate 		if (QueueMode == QM_QUARANTINE)
25377c478bd9Sstevel@tonic-gate 		{
25387c478bd9Sstevel@tonic-gate 			check = QueueLimitQuarantine;
25397c478bd9Sstevel@tonic-gate 			while (check != NULL)
25407c478bd9Sstevel@tonic-gate 			{
25417c478bd9Sstevel@tonic-gate 				sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
25427c478bd9Sstevel@tonic-gate 					   check->queue_negate ? "!" : "",
25437c478bd9Sstevel@tonic-gate 					   check->queue_match);
25447c478bd9Sstevel@tonic-gate 				check = check->queue_next;
25457c478bd9Sstevel@tonic-gate 			}
25467c478bd9Sstevel@tonic-gate 		}
25477c478bd9Sstevel@tonic-gate 	}
25487c478bd9Sstevel@tonic-gate 
25497c478bd9Sstevel@tonic-gate 	/* open the queue directory */
25507c478bd9Sstevel@tonic-gate 	f = opendir(qd);
25517c478bd9Sstevel@tonic-gate 	if (f == NULL)
25527c478bd9Sstevel@tonic-gate 	{
25537c478bd9Sstevel@tonic-gate 		syserr("gatherq: cannot open \"%s\"",
25547c478bd9Sstevel@tonic-gate 			qid_printqueue(qgrp, qdir));
25557c478bd9Sstevel@tonic-gate 		if (full != NULL)
25567c478bd9Sstevel@tonic-gate 			*full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
25577c478bd9Sstevel@tonic-gate 		if (more != NULL)
25587c478bd9Sstevel@tonic-gate 			*more = false;
25597c478bd9Sstevel@tonic-gate 		return 0;
25607c478bd9Sstevel@tonic-gate 	}
25617c478bd9Sstevel@tonic-gate 
25627c478bd9Sstevel@tonic-gate 	/*
25637c478bd9Sstevel@tonic-gate 	**  Read the work directory.
25647c478bd9Sstevel@tonic-gate 	*/
25657c478bd9Sstevel@tonic-gate 
25667c478bd9Sstevel@tonic-gate 	while ((d = readdir(f)) != NULL)
25677c478bd9Sstevel@tonic-gate 	{
25687c478bd9Sstevel@tonic-gate 		SM_FILE_T *cf;
25697c478bd9Sstevel@tonic-gate 		int qfver = 0;
25707c478bd9Sstevel@tonic-gate 		char lbuf[MAXNAME + 1];
25717c478bd9Sstevel@tonic-gate 		struct stat sbuf;
25727c478bd9Sstevel@tonic-gate 
25737c478bd9Sstevel@tonic-gate 		if (tTd(41, 50))
25747c478bd9Sstevel@tonic-gate 			sm_dprintf("gatherq: checking %s..", d->d_name);
25757c478bd9Sstevel@tonic-gate 
25767c478bd9Sstevel@tonic-gate 		/* is this an interesting entry? */
25777c478bd9Sstevel@tonic-gate 		if (!(((QueueMode == QM_NORMAL &&
25787c478bd9Sstevel@tonic-gate 			d->d_name[0] == NORMQF_LETTER) ||
25797c478bd9Sstevel@tonic-gate 		       (QueueMode == QM_QUARANTINE &&
25807c478bd9Sstevel@tonic-gate 			d->d_name[0] == QUARQF_LETTER) ||
25817c478bd9Sstevel@tonic-gate 		       (QueueMode == QM_LOST &&
25827c478bd9Sstevel@tonic-gate 			d->d_name[0] == LOSEQF_LETTER)) &&
25837c478bd9Sstevel@tonic-gate 		      d->d_name[1] == 'f'))
25847c478bd9Sstevel@tonic-gate 		{
25857c478bd9Sstevel@tonic-gate 			if (tTd(41, 50))
25867c478bd9Sstevel@tonic-gate 				sm_dprintf("  skipping\n");
25877c478bd9Sstevel@tonic-gate 			continue;
25887c478bd9Sstevel@tonic-gate 		}
25897c478bd9Sstevel@tonic-gate 		if (tTd(41, 50))
25907c478bd9Sstevel@tonic-gate 			sm_dprintf("\n");
25917c478bd9Sstevel@tonic-gate 
25927c478bd9Sstevel@tonic-gate 		if (strlen(d->d_name) >= MAXQFNAME)
25937c478bd9Sstevel@tonic-gate 		{
25947c478bd9Sstevel@tonic-gate 			if (Verbose)
25957c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
25967c478bd9Sstevel@tonic-gate 						     "gatherq: %s too long, %d max characters\n",
25977c478bd9Sstevel@tonic-gate 						     d->d_name, MAXQFNAME);
25987c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
25997c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ALERT, NOQID,
26007c478bd9Sstevel@tonic-gate 					  "gatherq: %s too long, %d max characters",
26017c478bd9Sstevel@tonic-gate 					  d->d_name, MAXQFNAME);
26027c478bd9Sstevel@tonic-gate 			continue;
26037c478bd9Sstevel@tonic-gate 		}
26047c478bd9Sstevel@tonic-gate 
26057c478bd9Sstevel@tonic-gate 		check = QueueLimitId;
26067c478bd9Sstevel@tonic-gate 		while (check != NULL)
26077c478bd9Sstevel@tonic-gate 		{
26087c478bd9Sstevel@tonic-gate 			if (strcontainedin(false, check->queue_match,
26097c478bd9Sstevel@tonic-gate 					   d->d_name) != check->queue_negate)
26107c478bd9Sstevel@tonic-gate 				break;
26117c478bd9Sstevel@tonic-gate 			else
26127c478bd9Sstevel@tonic-gate 				check = check->queue_next;
26137c478bd9Sstevel@tonic-gate 		}
26147c478bd9Sstevel@tonic-gate 		if (QueueLimitId != NULL && check == NULL)
26157c478bd9Sstevel@tonic-gate 			continue;
26167c478bd9Sstevel@tonic-gate 
26177c478bd9Sstevel@tonic-gate 		/* grow work list if necessary */
26187c478bd9Sstevel@tonic-gate 		if (++wn >= MaxQueueRun && MaxQueueRun > 0)
26197c478bd9Sstevel@tonic-gate 		{
26207c478bd9Sstevel@tonic-gate 			if (wn == MaxQueueRun && LogLevel > 0)
26217c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, NOQID,
26227c478bd9Sstevel@tonic-gate 					  "WorkList for %s maxed out at %d",
26237c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
26247c478bd9Sstevel@tonic-gate 					  MaxQueueRun);
26257c478bd9Sstevel@tonic-gate 			if (doall)
26267c478bd9Sstevel@tonic-gate 				continue;	/* just count entries */
26277c478bd9Sstevel@tonic-gate 			break;
26287c478bd9Sstevel@tonic-gate 		}
26297c478bd9Sstevel@tonic-gate 		if (wn >= WorkListSize)
26307c478bd9Sstevel@tonic-gate 		{
26317c478bd9Sstevel@tonic-gate 			grow_wlist(qgrp, qdir);
26327c478bd9Sstevel@tonic-gate 			if (wn >= WorkListSize)
26337c478bd9Sstevel@tonic-gate 				continue;
26347c478bd9Sstevel@tonic-gate 		}
26357c478bd9Sstevel@tonic-gate 		SM_ASSERT(wn >= 0);
26367c478bd9Sstevel@tonic-gate 		w = &WorkList[wn];
26377c478bd9Sstevel@tonic-gate 
2638058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", d->d_name);
26397c478bd9Sstevel@tonic-gate 		if (stat(qf, &sbuf) < 0)
26407c478bd9Sstevel@tonic-gate 		{
26417c478bd9Sstevel@tonic-gate 			if (errno != ENOENT)
26427c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
26437c478bd9Sstevel@tonic-gate 					  "gatherq: can't stat %s/%s",
26447c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
26457c478bd9Sstevel@tonic-gate 					  d->d_name);
26467c478bd9Sstevel@tonic-gate 			wn--;
26477c478bd9Sstevel@tonic-gate 			continue;
26487c478bd9Sstevel@tonic-gate 		}
26497c478bd9Sstevel@tonic-gate 		if (!bitset(S_IFREG, sbuf.st_mode))
26507c478bd9Sstevel@tonic-gate 		{
26517c478bd9Sstevel@tonic-gate 			/* Yikes!  Skip it or we will hang on open! */
26527c478bd9Sstevel@tonic-gate 			if (!((d->d_name[0] == DATAFL_LETTER ||
26537c478bd9Sstevel@tonic-gate 			       d->d_name[0] == NORMQF_LETTER ||
26547c478bd9Sstevel@tonic-gate 			       d->d_name[0] == QUARQF_LETTER ||
26557c478bd9Sstevel@tonic-gate 			       d->d_name[0] == LOSEQF_LETTER ||
26567c478bd9Sstevel@tonic-gate 			       d->d_name[0] == XSCRPT_LETTER) &&
26577c478bd9Sstevel@tonic-gate 			      d->d_name[1] == 'f' && d->d_name[2] == '\0'))
26587c478bd9Sstevel@tonic-gate 				syserr("gatherq: %s/%s is not a regular file",
26597c478bd9Sstevel@tonic-gate 				       qid_printqueue(qgrp, qdir), d->d_name);
26607c478bd9Sstevel@tonic-gate 			wn--;
26617c478bd9Sstevel@tonic-gate 			continue;
26627c478bd9Sstevel@tonic-gate 		}
26637c478bd9Sstevel@tonic-gate 
26647c478bd9Sstevel@tonic-gate 		/* avoid work if possible */
26657c478bd9Sstevel@tonic-gate 		if ((QueueSortOrder == QSO_BYFILENAME ||
26667c478bd9Sstevel@tonic-gate 		     QueueSortOrder == QSO_BYMODTIME ||
26671daa5768Sjbeck 		     QueueSortOrder == QSO_NONE ||
26687c478bd9Sstevel@tonic-gate 		     QueueSortOrder == QSO_RANDOM) &&
26697c478bd9Sstevel@tonic-gate 		    QueueLimitQuarantine == NULL &&
26707c478bd9Sstevel@tonic-gate 		    QueueLimitSender == NULL &&
26717c478bd9Sstevel@tonic-gate 		    QueueLimitRecipient == NULL)
26727c478bd9Sstevel@tonic-gate 		{
26737c478bd9Sstevel@tonic-gate 			w->w_qgrp = qgrp;
26747c478bd9Sstevel@tonic-gate 			w->w_qdir = qdir;
26757c478bd9Sstevel@tonic-gate 			w->w_name = newstr(d->d_name);
26767c478bd9Sstevel@tonic-gate 			w->w_host = NULL;
26777c478bd9Sstevel@tonic-gate 			w->w_lock = w->w_tooyoung = false;
26787c478bd9Sstevel@tonic-gate 			w->w_pri = 0;
26797c478bd9Sstevel@tonic-gate 			w->w_ctime = 0;
26807c478bd9Sstevel@tonic-gate 			w->w_mtime = sbuf.st_mtime;
26817c478bd9Sstevel@tonic-gate 			++num_ent;
26827c478bd9Sstevel@tonic-gate 			continue;
26837c478bd9Sstevel@tonic-gate 		}
26847c478bd9Sstevel@tonic-gate 
26857c478bd9Sstevel@tonic-gate 		/* open control file */
26867c478bd9Sstevel@tonic-gate 		cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
26877c478bd9Sstevel@tonic-gate 				NULL);
26887c478bd9Sstevel@tonic-gate 		if (cf == NULL && OpMode != MD_PRINT)
26897c478bd9Sstevel@tonic-gate 		{
26907c478bd9Sstevel@tonic-gate 			/* this may be some random person sending hir msgs */
26917c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
26927c478bd9Sstevel@tonic-gate 				sm_dprintf("gatherq: cannot open %s: %s\n",
26937c478bd9Sstevel@tonic-gate 					d->d_name, sm_errstring(errno));
26947c478bd9Sstevel@tonic-gate 			errno = 0;
26957c478bd9Sstevel@tonic-gate 			wn--;
26967c478bd9Sstevel@tonic-gate 			continue;
26977c478bd9Sstevel@tonic-gate 		}
26987c478bd9Sstevel@tonic-gate 		w->w_qgrp = qgrp;
26997c478bd9Sstevel@tonic-gate 		w->w_qdir = qdir;
27007c478bd9Sstevel@tonic-gate 		w->w_name = newstr(d->d_name);
27017c478bd9Sstevel@tonic-gate 		w->w_host = NULL;
27027c478bd9Sstevel@tonic-gate 		if (cf != NULL)
27037c478bd9Sstevel@tonic-gate 		{
27047c478bd9Sstevel@tonic-gate 			w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
27057c478bd9Sstevel@tonic-gate 							    NULL),
27067c478bd9Sstevel@tonic-gate 					      w->w_name, NULL,
27077c478bd9Sstevel@tonic-gate 					      LOCK_SH|LOCK_NB);
27087c478bd9Sstevel@tonic-gate 		}
27097c478bd9Sstevel@tonic-gate 		w->w_tooyoung = false;
27107c478bd9Sstevel@tonic-gate 
27117c478bd9Sstevel@tonic-gate 		/* make sure jobs in creation don't clog queue */
27127c478bd9Sstevel@tonic-gate 		w->w_pri = 0x7fffffff;
27137c478bd9Sstevel@tonic-gate 		w->w_ctime = 0;
27147c478bd9Sstevel@tonic-gate 		w->w_mtime = sbuf.st_mtime;
27157c478bd9Sstevel@tonic-gate 
27167c478bd9Sstevel@tonic-gate 		/* extract useful information */
27177c478bd9Sstevel@tonic-gate 		i = NEED_P|NEED_T;
27187c478bd9Sstevel@tonic-gate 		if (QueueSortOrder == QSO_BYHOST
27197c478bd9Sstevel@tonic-gate #if _FFR_RHS
27207c478bd9Sstevel@tonic-gate 		    || QueueSortOrder == QSO_BYSHUFFLE
27217c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
27227c478bd9Sstevel@tonic-gate 		   )
27237c478bd9Sstevel@tonic-gate 		{
27247c478bd9Sstevel@tonic-gate 			/* need w_host set for host sort order */
27257c478bd9Sstevel@tonic-gate 			i |= NEED_H;
27267c478bd9Sstevel@tonic-gate 		}
27277c478bd9Sstevel@tonic-gate 		if (QueueLimitSender != NULL)
27287c478bd9Sstevel@tonic-gate 			i |= NEED_S;
27297c478bd9Sstevel@tonic-gate 		if (QueueLimitRecipient != NULL)
27307c478bd9Sstevel@tonic-gate 			i |= NEED_R;
27317c478bd9Sstevel@tonic-gate 		if (QueueLimitQuarantine != NULL)
27327c478bd9Sstevel@tonic-gate 			i |= NEED_QUARANTINE;
27337c478bd9Sstevel@tonic-gate 		while (cf != NULL && i != 0 &&
27347c478bd9Sstevel@tonic-gate 		       sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
2735058561cbSjbeck 				   sizeof(lbuf)) != NULL)
27367c478bd9Sstevel@tonic-gate 		{
27377c478bd9Sstevel@tonic-gate 			int c;
27387c478bd9Sstevel@tonic-gate 			time_t age;
27397c478bd9Sstevel@tonic-gate 
27407c478bd9Sstevel@tonic-gate 			p = strchr(lbuf, '\n');
27417c478bd9Sstevel@tonic-gate 			if (p != NULL)
27427c478bd9Sstevel@tonic-gate 				*p = '\0';
27437c478bd9Sstevel@tonic-gate 			else
27447c478bd9Sstevel@tonic-gate 			{
27457c478bd9Sstevel@tonic-gate 				/* flush rest of overly long line */
27467c478bd9Sstevel@tonic-gate 				while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
27477c478bd9Sstevel@tonic-gate 				       != SM_IO_EOF && c != '\n')
27487c478bd9Sstevel@tonic-gate 					continue;
27497c478bd9Sstevel@tonic-gate 			}
27507c478bd9Sstevel@tonic-gate 
27517c478bd9Sstevel@tonic-gate 			switch (lbuf[0])
27527c478bd9Sstevel@tonic-gate 			{
27537c478bd9Sstevel@tonic-gate 			  case 'V':
27547c478bd9Sstevel@tonic-gate 				qfver = atoi(&lbuf[1]);
27557c478bd9Sstevel@tonic-gate 				break;
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate 			  case 'P':
27587c478bd9Sstevel@tonic-gate 				w->w_pri = atol(&lbuf[1]);
27597c478bd9Sstevel@tonic-gate 				i &= ~NEED_P;
27607c478bd9Sstevel@tonic-gate 				break;
27617c478bd9Sstevel@tonic-gate 
27627c478bd9Sstevel@tonic-gate 			  case 'T':
27637c478bd9Sstevel@tonic-gate 				w->w_ctime = atol(&lbuf[1]);
27647c478bd9Sstevel@tonic-gate 				i &= ~NEED_T;
27657c478bd9Sstevel@tonic-gate 				break;
27667c478bd9Sstevel@tonic-gate 
27677c478bd9Sstevel@tonic-gate 			  case 'q':
27687c478bd9Sstevel@tonic-gate 				if (QueueMode != QM_QUARANTINE &&
27697c478bd9Sstevel@tonic-gate 				    QueueMode != QM_LOST)
27707c478bd9Sstevel@tonic-gate 				{
27717c478bd9Sstevel@tonic-gate 					if (tTd(41, 49))
27727c478bd9Sstevel@tonic-gate 						sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
27737c478bd9Sstevel@tonic-gate 							   w->w_name);
27747c478bd9Sstevel@tonic-gate 					i |= HAS_QUARANTINE;
27757c478bd9Sstevel@tonic-gate 				}
27767c478bd9Sstevel@tonic-gate 				else if (QueueMode == QM_QUARANTINE)
27777c478bd9Sstevel@tonic-gate 				{
27787c478bd9Sstevel@tonic-gate 					if (QueueLimitQuarantine == NULL)
27797c478bd9Sstevel@tonic-gate 					{
27807c478bd9Sstevel@tonic-gate 						i &= ~NEED_QUARANTINE;
27817c478bd9Sstevel@tonic-gate 						break;
27827c478bd9Sstevel@tonic-gate 					}
27837c478bd9Sstevel@tonic-gate 					p = &lbuf[1];
27847c478bd9Sstevel@tonic-gate 					check = QueueLimitQuarantine;
27857c478bd9Sstevel@tonic-gate 					while (check != NULL)
27867c478bd9Sstevel@tonic-gate 					{
27877c478bd9Sstevel@tonic-gate 						if (strcontainedin(false,
27887c478bd9Sstevel@tonic-gate 								   check->queue_match,
27897c478bd9Sstevel@tonic-gate 								   p) !=
27907c478bd9Sstevel@tonic-gate 						    check->queue_negate)
27917c478bd9Sstevel@tonic-gate 							break;
27927c478bd9Sstevel@tonic-gate 						else
27937c478bd9Sstevel@tonic-gate 							check = check->queue_next;
27947c478bd9Sstevel@tonic-gate 					}
27957c478bd9Sstevel@tonic-gate 					if (check != NULL)
27967c478bd9Sstevel@tonic-gate 						i &= ~NEED_QUARANTINE;
27977c478bd9Sstevel@tonic-gate 				}
27987c478bd9Sstevel@tonic-gate 				break;
27997c478bd9Sstevel@tonic-gate 
28007c478bd9Sstevel@tonic-gate 			  case 'R':
28017c478bd9Sstevel@tonic-gate 				if (w->w_host == NULL &&
28027c478bd9Sstevel@tonic-gate 				    (p = strrchr(&lbuf[1], '@')) != NULL)
28037c478bd9Sstevel@tonic-gate 				{
28047c478bd9Sstevel@tonic-gate #if _FFR_RHS
28057c478bd9Sstevel@tonic-gate 					if (QueueSortOrder == QSO_BYSHUFFLE)
28067c478bd9Sstevel@tonic-gate 						w->w_host = newstr(&p[1]);
28077c478bd9Sstevel@tonic-gate 					else
28087c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
28097c478bd9Sstevel@tonic-gate 						w->w_host = strrev(&p[1]);
28107c478bd9Sstevel@tonic-gate 					makelower(w->w_host);
28117c478bd9Sstevel@tonic-gate 					i &= ~NEED_H;
28127c478bd9Sstevel@tonic-gate 				}
28137c478bd9Sstevel@tonic-gate 				if (QueueLimitRecipient == NULL)
28147c478bd9Sstevel@tonic-gate 				{
28157c478bd9Sstevel@tonic-gate 					i &= ~NEED_R;
28167c478bd9Sstevel@tonic-gate 					break;
28177c478bd9Sstevel@tonic-gate 				}
28187c478bd9Sstevel@tonic-gate 				if (qfver > 0)
28197c478bd9Sstevel@tonic-gate 				{
28207c478bd9Sstevel@tonic-gate 					p = strchr(&lbuf[1], ':');
28217c478bd9Sstevel@tonic-gate 					if (p == NULL)
28227c478bd9Sstevel@tonic-gate 						p = &lbuf[1];
28237c478bd9Sstevel@tonic-gate 					else
28247c478bd9Sstevel@tonic-gate 						++p; /* skip over ':' */
28257c478bd9Sstevel@tonic-gate 				}
28267c478bd9Sstevel@tonic-gate 				else
28277c478bd9Sstevel@tonic-gate 					p = &lbuf[1];
28287c478bd9Sstevel@tonic-gate 				check = QueueLimitRecipient;
28297c478bd9Sstevel@tonic-gate 				while (check != NULL)
28307c478bd9Sstevel@tonic-gate 				{
28317c478bd9Sstevel@tonic-gate 					if (strcontainedin(true,
28327c478bd9Sstevel@tonic-gate 							   check->queue_match,
28337c478bd9Sstevel@tonic-gate 							   p) !=
28347c478bd9Sstevel@tonic-gate 					    check->queue_negate)
28357c478bd9Sstevel@tonic-gate 						break;
28367c478bd9Sstevel@tonic-gate 					else
28377c478bd9Sstevel@tonic-gate 						check = check->queue_next;
28387c478bd9Sstevel@tonic-gate 				}
28397c478bd9Sstevel@tonic-gate 				if (check != NULL)
28407c478bd9Sstevel@tonic-gate 					i &= ~NEED_R;
28417c478bd9Sstevel@tonic-gate 				break;
28427c478bd9Sstevel@tonic-gate 
28437c478bd9Sstevel@tonic-gate 			  case 'S':
28447c478bd9Sstevel@tonic-gate 				check = QueueLimitSender;
28457c478bd9Sstevel@tonic-gate 				while (check != NULL)
28467c478bd9Sstevel@tonic-gate 				{
28477c478bd9Sstevel@tonic-gate 					if (strcontainedin(true,
28487c478bd9Sstevel@tonic-gate 							   check->queue_match,
28497c478bd9Sstevel@tonic-gate 							   &lbuf[1]) !=
28507c478bd9Sstevel@tonic-gate 					    check->queue_negate)
28517c478bd9Sstevel@tonic-gate 						break;
28527c478bd9Sstevel@tonic-gate 					else
28537c478bd9Sstevel@tonic-gate 						check = check->queue_next;
28547c478bd9Sstevel@tonic-gate 				}
28557c478bd9Sstevel@tonic-gate 				if (check != NULL)
28567c478bd9Sstevel@tonic-gate 					i &= ~NEED_S;
28577c478bd9Sstevel@tonic-gate 				break;
28587c478bd9Sstevel@tonic-gate 
28597c478bd9Sstevel@tonic-gate 			  case 'K':
28607c478bd9Sstevel@tonic-gate 				age = curtime() - (time_t) atol(&lbuf[1]);
28617c478bd9Sstevel@tonic-gate 				if (age >= 0 && MinQueueAge > 0 &&
28627c478bd9Sstevel@tonic-gate 				    age < MinQueueAge)
28637c478bd9Sstevel@tonic-gate 					w->w_tooyoung = true;
28647c478bd9Sstevel@tonic-gate 				break;
28657c478bd9Sstevel@tonic-gate 
28667c478bd9Sstevel@tonic-gate 			  case 'N':
28677c478bd9Sstevel@tonic-gate 				if (atol(&lbuf[1]) == 0)
28687c478bd9Sstevel@tonic-gate 					w->w_tooyoung = false;
28697c478bd9Sstevel@tonic-gate 				break;
28707c478bd9Sstevel@tonic-gate 			}
28717c478bd9Sstevel@tonic-gate 		}
28727c478bd9Sstevel@tonic-gate 		if (cf != NULL)
28737c478bd9Sstevel@tonic-gate 			(void) sm_io_close(cf, SM_TIME_DEFAULT);
28747c478bd9Sstevel@tonic-gate 
2875445f2479Sjbeck 		if ((!doall && (shouldqueue(w->w_pri, w->w_ctime) ||
2876445f2479Sjbeck 		    w->w_tooyoung)) ||
28777c478bd9Sstevel@tonic-gate 		    bitset(HAS_QUARANTINE, i) ||
28787c478bd9Sstevel@tonic-gate 		    bitset(NEED_QUARANTINE, i) ||
28797c478bd9Sstevel@tonic-gate 		    bitset(NEED_R|NEED_S, i))
28807c478bd9Sstevel@tonic-gate 		{
28817c478bd9Sstevel@tonic-gate 			/* don't even bother sorting this job in */
28827c478bd9Sstevel@tonic-gate 			if (tTd(41, 49))
28837c478bd9Sstevel@tonic-gate 				sm_dprintf("skipping %s (%x)\n", w->w_name, i);
28847c478bd9Sstevel@tonic-gate 			sm_free(w->w_name); /* XXX */
28857c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
28867c478bd9Sstevel@tonic-gate 				sm_free(w->w_host); /* XXX */
28877c478bd9Sstevel@tonic-gate 			wn--;
28887c478bd9Sstevel@tonic-gate 		}
28897c478bd9Sstevel@tonic-gate 		else
28907c478bd9Sstevel@tonic-gate 			++num_ent;
28917c478bd9Sstevel@tonic-gate 	}
28927c478bd9Sstevel@tonic-gate 	(void) closedir(f);
28937c478bd9Sstevel@tonic-gate 	wn++;
28947c478bd9Sstevel@tonic-gate 
28957c478bd9Sstevel@tonic-gate 	i = wn - WorkListCount;
28967c478bd9Sstevel@tonic-gate 	WorkListCount += SM_MIN(num_ent, WorkListSize);
28977c478bd9Sstevel@tonic-gate 
28987c478bd9Sstevel@tonic-gate 	if (more != NULL)
28997c478bd9Sstevel@tonic-gate 		*more = WorkListCount < wn;
29007c478bd9Sstevel@tonic-gate 
29017c478bd9Sstevel@tonic-gate 	if (full != NULL)
29027c478bd9Sstevel@tonic-gate 		*full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
29037c478bd9Sstevel@tonic-gate 			(WorkList == NULL && wn > 0);
29047c478bd9Sstevel@tonic-gate 
29057c478bd9Sstevel@tonic-gate 	return i;
29067c478bd9Sstevel@tonic-gate }
29077c478bd9Sstevel@tonic-gate /*
29087c478bd9Sstevel@tonic-gate **  SORTQ -- sort the work list
29097c478bd9Sstevel@tonic-gate **
29107c478bd9Sstevel@tonic-gate **	First the old WorkQ is cleared away. Then the WorkList is sorted
29117c478bd9Sstevel@tonic-gate **	for all items so that important (higher sorting value) items are not
29127c478bd9Sstevel@tonic-gate **	trunctated off. Then the most important items are moved from
29137c478bd9Sstevel@tonic-gate **	WorkList to WorkQ. The lower count of 'max' or MaxListCount items
29147c478bd9Sstevel@tonic-gate **	are moved.
29157c478bd9Sstevel@tonic-gate **
29167c478bd9Sstevel@tonic-gate **	Parameters:
29177c478bd9Sstevel@tonic-gate **		max -- maximum number of items to be placed in WorkQ
29187c478bd9Sstevel@tonic-gate **
29197c478bd9Sstevel@tonic-gate **	Returns:
29207c478bd9Sstevel@tonic-gate **		the number of items in WorkQ
29217c478bd9Sstevel@tonic-gate **
29227c478bd9Sstevel@tonic-gate **	Side Effects:
29237c478bd9Sstevel@tonic-gate **		WorkQ gets released and filled with new work. WorkList
29247c478bd9Sstevel@tonic-gate **		gets released. Work items get sorted in order.
29257c478bd9Sstevel@tonic-gate */
29267c478bd9Sstevel@tonic-gate 
29277c478bd9Sstevel@tonic-gate static int
29287c478bd9Sstevel@tonic-gate sortq(max)
29297c478bd9Sstevel@tonic-gate 	int max;
29307c478bd9Sstevel@tonic-gate {
29317c478bd9Sstevel@tonic-gate 	register int i;			/* local counter */
29327c478bd9Sstevel@tonic-gate 	register WORK *w;		/* tmp item pointer */
29337c478bd9Sstevel@tonic-gate 	int wc = WorkListCount;		/* trim size for WorkQ */
29347c478bd9Sstevel@tonic-gate 
29357c478bd9Sstevel@tonic-gate 	if (WorkQ != NULL)
29367c478bd9Sstevel@tonic-gate 	{
29377c478bd9Sstevel@tonic-gate 		WORK *nw;
29387c478bd9Sstevel@tonic-gate 
29397c478bd9Sstevel@tonic-gate 		/* Clear out old WorkQ. */
29407c478bd9Sstevel@tonic-gate 		for (w = WorkQ; w != NULL; w = nw)
29417c478bd9Sstevel@tonic-gate 		{
29427c478bd9Sstevel@tonic-gate 			nw = w->w_next;
29437c478bd9Sstevel@tonic-gate 			sm_free(w->w_name); /* XXX */
29447c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
29457c478bd9Sstevel@tonic-gate 				sm_free(w->w_host); /* XXX */
29467c478bd9Sstevel@tonic-gate 			sm_free((char *) w); /* XXX */
29477c478bd9Sstevel@tonic-gate 		}
29487c478bd9Sstevel@tonic-gate 		WorkQ = NULL;
29497c478bd9Sstevel@tonic-gate 	}
29507c478bd9Sstevel@tonic-gate 
29517c478bd9Sstevel@tonic-gate 	if (WorkList == NULL || wc <= 0)
29527c478bd9Sstevel@tonic-gate 		return 0;
29537c478bd9Sstevel@tonic-gate 
29547c478bd9Sstevel@tonic-gate 	/*
29557c478bd9Sstevel@tonic-gate 	**  The sort now takes place using all of the items in WorkList.
29567c478bd9Sstevel@tonic-gate 	**  The list gets trimmed to the most important items after the sort.
29577c478bd9Sstevel@tonic-gate 	**  If the trim were to happen before the sort then one or more
29587c478bd9Sstevel@tonic-gate 	**  important items might get truncated off -- not what we want.
29597c478bd9Sstevel@tonic-gate 	*/
29607c478bd9Sstevel@tonic-gate 
29617c478bd9Sstevel@tonic-gate 	if (QueueSortOrder == QSO_BYHOST)
29627c478bd9Sstevel@tonic-gate 	{
29637c478bd9Sstevel@tonic-gate 		/*
29647c478bd9Sstevel@tonic-gate 		**  Sort the work directory for the first time,
29657c478bd9Sstevel@tonic-gate 		**  based on host name, lock status, and priority.
29667c478bd9Sstevel@tonic-gate 		*/
29677c478bd9Sstevel@tonic-gate 
2968058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf1);
29697c478bd9Sstevel@tonic-gate 
29707c478bd9Sstevel@tonic-gate 		/*
29717c478bd9Sstevel@tonic-gate 		**  If one message to host is locked, "lock" all messages
29727c478bd9Sstevel@tonic-gate 		**  to that host.
29737c478bd9Sstevel@tonic-gate 		*/
29747c478bd9Sstevel@tonic-gate 
29757c478bd9Sstevel@tonic-gate 		i = 0;
29767c478bd9Sstevel@tonic-gate 		while (i < wc)
29777c478bd9Sstevel@tonic-gate 		{
29787c478bd9Sstevel@tonic-gate 			if (!WorkList[i].w_lock)
29797c478bd9Sstevel@tonic-gate 			{
29807c478bd9Sstevel@tonic-gate 				i++;
29817c478bd9Sstevel@tonic-gate 				continue;
29827c478bd9Sstevel@tonic-gate 			}
29837c478bd9Sstevel@tonic-gate 			w = &WorkList[i];
29847c478bd9Sstevel@tonic-gate 			while (++i < wc)
29857c478bd9Sstevel@tonic-gate 			{
29867c478bd9Sstevel@tonic-gate 				if (WorkList[i].w_host == NULL &&
29877c478bd9Sstevel@tonic-gate 				    w->w_host == NULL)
29887c478bd9Sstevel@tonic-gate 					WorkList[i].w_lock = true;
29897c478bd9Sstevel@tonic-gate 				else if (WorkList[i].w_host != NULL &&
29907c478bd9Sstevel@tonic-gate 					 w->w_host != NULL &&
29917c478bd9Sstevel@tonic-gate 					 sm_strcasecmp(WorkList[i].w_host,
29927c478bd9Sstevel@tonic-gate 						       w->w_host) == 0)
29937c478bd9Sstevel@tonic-gate 					WorkList[i].w_lock = true;
29947c478bd9Sstevel@tonic-gate 				else
29957c478bd9Sstevel@tonic-gate 					break;
29967c478bd9Sstevel@tonic-gate 			}
29977c478bd9Sstevel@tonic-gate 		}
29987c478bd9Sstevel@tonic-gate 
29997c478bd9Sstevel@tonic-gate 		/*
30007c478bd9Sstevel@tonic-gate 		**  Sort the work directory for the second time,
30017c478bd9Sstevel@tonic-gate 		**  based on lock status, host name, and priority.
30027c478bd9Sstevel@tonic-gate 		*/
30037c478bd9Sstevel@tonic-gate 
3004058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf2);
30057c478bd9Sstevel@tonic-gate 	}
30067c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYTIME)
30077c478bd9Sstevel@tonic-gate 	{
30087c478bd9Sstevel@tonic-gate 		/*
30097c478bd9Sstevel@tonic-gate 		**  Simple sort based on submission time only.
30107c478bd9Sstevel@tonic-gate 		*/
30117c478bd9Sstevel@tonic-gate 
3012058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf3);
30137c478bd9Sstevel@tonic-gate 	}
30147c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYFILENAME)
30157c478bd9Sstevel@tonic-gate 	{
30167c478bd9Sstevel@tonic-gate 		/*
30177c478bd9Sstevel@tonic-gate 		**  Sort based on queue filename.
30187c478bd9Sstevel@tonic-gate 		*/
30197c478bd9Sstevel@tonic-gate 
3020058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf4);
30217c478bd9Sstevel@tonic-gate 	}
30227c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_RANDOM)
30237c478bd9Sstevel@tonic-gate 	{
30247c478bd9Sstevel@tonic-gate 		/*
30257c478bd9Sstevel@tonic-gate 		**  Sort randomly.  To avoid problems with an instable sort,
30267c478bd9Sstevel@tonic-gate 		**  use a random index into the queue file name to start
30277c478bd9Sstevel@tonic-gate 		**  comparison.
30287c478bd9Sstevel@tonic-gate 		*/
30297c478bd9Sstevel@tonic-gate 
30307c478bd9Sstevel@tonic-gate 		randi = get_rand_mod(MAXQFNAME);
30317c478bd9Sstevel@tonic-gate 		if (randi < 2)
30327c478bd9Sstevel@tonic-gate 			randi = 3;
3033058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf5);
30347c478bd9Sstevel@tonic-gate 	}
30357c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYMODTIME)
30367c478bd9Sstevel@tonic-gate 	{
30377c478bd9Sstevel@tonic-gate 		/*
30387c478bd9Sstevel@tonic-gate 		**  Simple sort based on modification time of queue file.
30397c478bd9Sstevel@tonic-gate 		**  This puts the oldest items first.
30407c478bd9Sstevel@tonic-gate 		*/
30417c478bd9Sstevel@tonic-gate 
3042058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf6);
30437c478bd9Sstevel@tonic-gate 	}
30447c478bd9Sstevel@tonic-gate #if _FFR_RHS
30457c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYSHUFFLE)
30467c478bd9Sstevel@tonic-gate 	{
30477c478bd9Sstevel@tonic-gate 		/*
30487c478bd9Sstevel@tonic-gate 		**  Simple sort based on shuffled host name.
30497c478bd9Sstevel@tonic-gate 		*/
30507c478bd9Sstevel@tonic-gate 
30517c478bd9Sstevel@tonic-gate 		init_shuffle_alphabet();
3052058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf7);
30537c478bd9Sstevel@tonic-gate 	}
30547c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
30557c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYPRIORITY)
30567c478bd9Sstevel@tonic-gate 	{
30577c478bd9Sstevel@tonic-gate 		/*
30587c478bd9Sstevel@tonic-gate 		**  Simple sort based on queue priority only.
30597c478bd9Sstevel@tonic-gate 		*/
30607c478bd9Sstevel@tonic-gate 
3061058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf0);
30627c478bd9Sstevel@tonic-gate 	}
30637c478bd9Sstevel@tonic-gate 	/* else don't sort at all */
30647c478bd9Sstevel@tonic-gate 
306549218d4fSjbeck 	/* Check if the per queue group item limit will be exceeded */
306649218d4fSjbeck 	if (wc > max && max > 0)
306749218d4fSjbeck 		wc = max;
306849218d4fSjbeck 
30697c478bd9Sstevel@tonic-gate 	/*
30707c478bd9Sstevel@tonic-gate 	**  Convert the work list into canonical form.
30717c478bd9Sstevel@tonic-gate 	**	Should be turning it into a list of envelopes here perhaps.
30727c478bd9Sstevel@tonic-gate 	**  Only take the most important items up to the per queue group
30737c478bd9Sstevel@tonic-gate 	**  maximum.
30747c478bd9Sstevel@tonic-gate 	*/
30757c478bd9Sstevel@tonic-gate 
30767c478bd9Sstevel@tonic-gate 	for (i = wc; --i >= 0; )
30777c478bd9Sstevel@tonic-gate 	{
3078058561cbSjbeck 		w = (WORK *) xalloc(sizeof(*w));
30797c478bd9Sstevel@tonic-gate 		w->w_qgrp = WorkList[i].w_qgrp;
30807c478bd9Sstevel@tonic-gate 		w->w_qdir = WorkList[i].w_qdir;
30817c478bd9Sstevel@tonic-gate 		w->w_name = WorkList[i].w_name;
30827c478bd9Sstevel@tonic-gate 		w->w_host = WorkList[i].w_host;
30837c478bd9Sstevel@tonic-gate 		w->w_lock = WorkList[i].w_lock;
30847c478bd9Sstevel@tonic-gate 		w->w_tooyoung = WorkList[i].w_tooyoung;
30857c478bd9Sstevel@tonic-gate 		w->w_pri = WorkList[i].w_pri;
30867c478bd9Sstevel@tonic-gate 		w->w_ctime = WorkList[i].w_ctime;
30877c478bd9Sstevel@tonic-gate 		w->w_mtime = WorkList[i].w_mtime;
30887c478bd9Sstevel@tonic-gate 		w->w_next = WorkQ;
30897c478bd9Sstevel@tonic-gate 		WorkQ = w;
30907c478bd9Sstevel@tonic-gate 	}
30917c478bd9Sstevel@tonic-gate 
30927c478bd9Sstevel@tonic-gate 	/* free the rest of the list */
30937c478bd9Sstevel@tonic-gate 	for (i = WorkListCount; --i >= wc; )
30947c478bd9Sstevel@tonic-gate 	{
30957c478bd9Sstevel@tonic-gate 		sm_free(WorkList[i].w_name);
30967c478bd9Sstevel@tonic-gate 		if (WorkList[i].w_host != NULL)
30977c478bd9Sstevel@tonic-gate 			sm_free(WorkList[i].w_host);
30987c478bd9Sstevel@tonic-gate 	}
30997c478bd9Sstevel@tonic-gate 
31007c478bd9Sstevel@tonic-gate 	if (WorkList != NULL)
31017c478bd9Sstevel@tonic-gate 		sm_free(WorkList); /* XXX */
31027c478bd9Sstevel@tonic-gate 	WorkList = NULL;
31037c478bd9Sstevel@tonic-gate 	WorkListSize = 0;
31047c478bd9Sstevel@tonic-gate 	WorkListCount = 0;
31057c478bd9Sstevel@tonic-gate 
31067c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
31077c478bd9Sstevel@tonic-gate 	{
31087c478bd9Sstevel@tonic-gate 		for (w = WorkQ; w != NULL; w = w->w_next)
31097c478bd9Sstevel@tonic-gate 		{
31107c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
31117c478bd9Sstevel@tonic-gate 				sm_dprintf("%22s: pri=%ld %s\n",
31127c478bd9Sstevel@tonic-gate 					w->w_name, w->w_pri, w->w_host);
31137c478bd9Sstevel@tonic-gate 			else
31147c478bd9Sstevel@tonic-gate 				sm_dprintf("%32s: pri=%ld\n",
31157c478bd9Sstevel@tonic-gate 					w->w_name, w->w_pri);
31167c478bd9Sstevel@tonic-gate 		}
31177c478bd9Sstevel@tonic-gate 	}
31187c478bd9Sstevel@tonic-gate 
31197c478bd9Sstevel@tonic-gate 	return wc; /* return number of WorkQ items */
31207c478bd9Sstevel@tonic-gate }
31217c478bd9Sstevel@tonic-gate /*
31227c478bd9Sstevel@tonic-gate **  GROW_WLIST -- make the work list larger
31237c478bd9Sstevel@tonic-gate **
31247c478bd9Sstevel@tonic-gate **	Parameters:
31257c478bd9Sstevel@tonic-gate **		qgrp -- the index for the queue group.
31267c478bd9Sstevel@tonic-gate **		qdir -- the index for the queue directory.
31277c478bd9Sstevel@tonic-gate **
31287c478bd9Sstevel@tonic-gate **	Returns:
31297c478bd9Sstevel@tonic-gate **		none.
31307c478bd9Sstevel@tonic-gate **
31317c478bd9Sstevel@tonic-gate **	Side Effects:
31327c478bd9Sstevel@tonic-gate **		Adds another QUEUESEGSIZE entries to WorkList if possible.
31337c478bd9Sstevel@tonic-gate **		It can fail if there isn't enough memory, so WorkListSize
31347c478bd9Sstevel@tonic-gate **		should be checked again upon return.
31357c478bd9Sstevel@tonic-gate */
31367c478bd9Sstevel@tonic-gate 
31377c478bd9Sstevel@tonic-gate static void
31387c478bd9Sstevel@tonic-gate grow_wlist(qgrp, qdir)
31397c478bd9Sstevel@tonic-gate 	int qgrp;
31407c478bd9Sstevel@tonic-gate 	int qdir;
31417c478bd9Sstevel@tonic-gate {
31427c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
31437c478bd9Sstevel@tonic-gate 		sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
31447c478bd9Sstevel@tonic-gate 	if (WorkList == NULL)
31457c478bd9Sstevel@tonic-gate 	{
3146058561cbSjbeck 		WorkList = (WORK *) xalloc((sizeof(*WorkList)) *
31477c478bd9Sstevel@tonic-gate 					   (QUEUESEGSIZE + 1));
31487c478bd9Sstevel@tonic-gate 		WorkListSize = QUEUESEGSIZE;
31497c478bd9Sstevel@tonic-gate 	}
31507c478bd9Sstevel@tonic-gate 	else
31517c478bd9Sstevel@tonic-gate 	{
31527c478bd9Sstevel@tonic-gate 		int newsize = WorkListSize + QUEUESEGSIZE;
31537c478bd9Sstevel@tonic-gate 		WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
31547c478bd9Sstevel@tonic-gate 					  (unsigned) sizeof(WORK) * (newsize + 1));
31557c478bd9Sstevel@tonic-gate 
31567c478bd9Sstevel@tonic-gate 		if (newlist != NULL)
31577c478bd9Sstevel@tonic-gate 		{
31587c478bd9Sstevel@tonic-gate 			WorkListSize = newsize;
31597c478bd9Sstevel@tonic-gate 			WorkList = newlist;
31607c478bd9Sstevel@tonic-gate 			if (LogLevel > 1)
31617c478bd9Sstevel@tonic-gate 			{
31627c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
31637c478bd9Sstevel@tonic-gate 					  "grew WorkList for %s to %d",
31647c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
31657c478bd9Sstevel@tonic-gate 					  WorkListSize);
31667c478bd9Sstevel@tonic-gate 			}
31677c478bd9Sstevel@tonic-gate 		}
31687c478bd9Sstevel@tonic-gate 		else if (LogLevel > 0)
31697c478bd9Sstevel@tonic-gate 		{
31707c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, NOQID,
31717c478bd9Sstevel@tonic-gate 				  "FAILED to grow WorkList for %s to %d",
31727c478bd9Sstevel@tonic-gate 				  qid_printqueue(qgrp, qdir), newsize);
31737c478bd9Sstevel@tonic-gate 		}
31747c478bd9Sstevel@tonic-gate 	}
31757c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
31767c478bd9Sstevel@tonic-gate 		sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
31777c478bd9Sstevel@tonic-gate }
31787c478bd9Sstevel@tonic-gate /*
31797c478bd9Sstevel@tonic-gate **  WORKCMPF0 -- simple priority-only compare function.
31807c478bd9Sstevel@tonic-gate **
31817c478bd9Sstevel@tonic-gate **	Parameters:
31827c478bd9Sstevel@tonic-gate **		a -- the first argument.
31837c478bd9Sstevel@tonic-gate **		b -- the second argument.
31847c478bd9Sstevel@tonic-gate **
31857c478bd9Sstevel@tonic-gate **	Returns:
31867c478bd9Sstevel@tonic-gate **		-1 if a < b
31877c478bd9Sstevel@tonic-gate **		 0 if a == b
31887c478bd9Sstevel@tonic-gate **		+1 if a > b
31897c478bd9Sstevel@tonic-gate **
31907c478bd9Sstevel@tonic-gate */
31917c478bd9Sstevel@tonic-gate 
31927c478bd9Sstevel@tonic-gate static int
31937c478bd9Sstevel@tonic-gate workcmpf0(a, b)
31947c478bd9Sstevel@tonic-gate 	register WORK *a;
31957c478bd9Sstevel@tonic-gate 	register WORK *b;
31967c478bd9Sstevel@tonic-gate {
31977c478bd9Sstevel@tonic-gate 	long pa = a->w_pri;
31987c478bd9Sstevel@tonic-gate 	long pb = b->w_pri;
31997c478bd9Sstevel@tonic-gate 
32007c478bd9Sstevel@tonic-gate 	if (pa == pb)
32017c478bd9Sstevel@tonic-gate 		return 0;
32027c478bd9Sstevel@tonic-gate 	else if (pa > pb)
32037c478bd9Sstevel@tonic-gate 		return 1;
32047c478bd9Sstevel@tonic-gate 	else
32057c478bd9Sstevel@tonic-gate 		return -1;
32067c478bd9Sstevel@tonic-gate }
32077c478bd9Sstevel@tonic-gate /*
32087c478bd9Sstevel@tonic-gate **  WORKCMPF1 -- first compare function for ordering work based on host name.
32097c478bd9Sstevel@tonic-gate **
32107c478bd9Sstevel@tonic-gate **	Sorts on host name, lock status, and priority in that order.
32117c478bd9Sstevel@tonic-gate **
32127c478bd9Sstevel@tonic-gate **	Parameters:
32137c478bd9Sstevel@tonic-gate **		a -- the first argument.
32147c478bd9Sstevel@tonic-gate **		b -- the second argument.
32157c478bd9Sstevel@tonic-gate **
32167c478bd9Sstevel@tonic-gate **	Returns:
32177c478bd9Sstevel@tonic-gate **		<0 if a < b
32187c478bd9Sstevel@tonic-gate **		 0 if a == b
32197c478bd9Sstevel@tonic-gate **		>0 if a > b
32207c478bd9Sstevel@tonic-gate **
32217c478bd9Sstevel@tonic-gate */
32227c478bd9Sstevel@tonic-gate 
32237c478bd9Sstevel@tonic-gate static int
32247c478bd9Sstevel@tonic-gate workcmpf1(a, b)
32257c478bd9Sstevel@tonic-gate 	register WORK *a;
32267c478bd9Sstevel@tonic-gate 	register WORK *b;
32277c478bd9Sstevel@tonic-gate {
32287c478bd9Sstevel@tonic-gate 	int i;
32297c478bd9Sstevel@tonic-gate 
32307c478bd9Sstevel@tonic-gate 	/* host name */
32317c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
32327c478bd9Sstevel@tonic-gate 		return 1;
32337c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
32347c478bd9Sstevel@tonic-gate 		return -1;
32357c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
32367c478bd9Sstevel@tonic-gate 	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
32377c478bd9Sstevel@tonic-gate 		return i;
32387c478bd9Sstevel@tonic-gate 
32397c478bd9Sstevel@tonic-gate 	/* lock status */
32407c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
32417c478bd9Sstevel@tonic-gate 		return b->w_lock - a->w_lock;
32427c478bd9Sstevel@tonic-gate 
32437c478bd9Sstevel@tonic-gate 	/* job priority */
32447c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
32457c478bd9Sstevel@tonic-gate }
32467c478bd9Sstevel@tonic-gate /*
32477c478bd9Sstevel@tonic-gate **  WORKCMPF2 -- second compare function for ordering work based on host name.
32487c478bd9Sstevel@tonic-gate **
32497c478bd9Sstevel@tonic-gate **	Sorts on lock status, host name, and priority in that order.
32507c478bd9Sstevel@tonic-gate **
32517c478bd9Sstevel@tonic-gate **	Parameters:
32527c478bd9Sstevel@tonic-gate **		a -- the first argument.
32537c478bd9Sstevel@tonic-gate **		b -- the second argument.
32547c478bd9Sstevel@tonic-gate **
32557c478bd9Sstevel@tonic-gate **	Returns:
32567c478bd9Sstevel@tonic-gate **		<0 if a < b
32577c478bd9Sstevel@tonic-gate **		 0 if a == b
32587c478bd9Sstevel@tonic-gate **		>0 if a > b
32597c478bd9Sstevel@tonic-gate **
32607c478bd9Sstevel@tonic-gate */
32617c478bd9Sstevel@tonic-gate 
32627c478bd9Sstevel@tonic-gate static int
32637c478bd9Sstevel@tonic-gate workcmpf2(a, b)
32647c478bd9Sstevel@tonic-gate 	register WORK *a;
32657c478bd9Sstevel@tonic-gate 	register WORK *b;
32667c478bd9Sstevel@tonic-gate {
32677c478bd9Sstevel@tonic-gate 	int i;
32687c478bd9Sstevel@tonic-gate 
32697c478bd9Sstevel@tonic-gate 	/* lock status */
32707c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
32717c478bd9Sstevel@tonic-gate 		return a->w_lock - b->w_lock;
32727c478bd9Sstevel@tonic-gate 
32737c478bd9Sstevel@tonic-gate 	/* host name */
32747c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
32757c478bd9Sstevel@tonic-gate 		return 1;
32767c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
32777c478bd9Sstevel@tonic-gate 		return -1;
32787c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
32797c478bd9Sstevel@tonic-gate 	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
32807c478bd9Sstevel@tonic-gate 		return i;
32817c478bd9Sstevel@tonic-gate 
32827c478bd9Sstevel@tonic-gate 	/* job priority */
32837c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
32847c478bd9Sstevel@tonic-gate }
32857c478bd9Sstevel@tonic-gate /*
32867c478bd9Sstevel@tonic-gate **  WORKCMPF3 -- simple submission-time-only compare function.
32877c478bd9Sstevel@tonic-gate **
32887c478bd9Sstevel@tonic-gate **	Parameters:
32897c478bd9Sstevel@tonic-gate **		a -- the first argument.
32907c478bd9Sstevel@tonic-gate **		b -- the second argument.
32917c478bd9Sstevel@tonic-gate **
32927c478bd9Sstevel@tonic-gate **	Returns:
32937c478bd9Sstevel@tonic-gate **		-1 if a < b
32947c478bd9Sstevel@tonic-gate **		 0 if a == b
32957c478bd9Sstevel@tonic-gate **		+1 if a > b
32967c478bd9Sstevel@tonic-gate **
32977c478bd9Sstevel@tonic-gate */
32987c478bd9Sstevel@tonic-gate 
32997c478bd9Sstevel@tonic-gate static int
33007c478bd9Sstevel@tonic-gate workcmpf3(a, b)
33017c478bd9Sstevel@tonic-gate 	register WORK *a;
33027c478bd9Sstevel@tonic-gate 	register WORK *b;
33037c478bd9Sstevel@tonic-gate {
33047c478bd9Sstevel@tonic-gate 	if (a->w_ctime > b->w_ctime)
33057c478bd9Sstevel@tonic-gate 		return 1;
33067c478bd9Sstevel@tonic-gate 	else if (a->w_ctime < b->w_ctime)
33077c478bd9Sstevel@tonic-gate 		return -1;
33087c478bd9Sstevel@tonic-gate 	else
33097c478bd9Sstevel@tonic-gate 		return 0;
33107c478bd9Sstevel@tonic-gate }
33117c478bd9Sstevel@tonic-gate /*
33127c478bd9Sstevel@tonic-gate **  WORKCMPF4 -- compare based on file name
33137c478bd9Sstevel@tonic-gate **
33147c478bd9Sstevel@tonic-gate **	Parameters:
33157c478bd9Sstevel@tonic-gate **		a -- the first argument.
33167c478bd9Sstevel@tonic-gate **		b -- the second argument.
33177c478bd9Sstevel@tonic-gate **
33187c478bd9Sstevel@tonic-gate **	Returns:
33197c478bd9Sstevel@tonic-gate **		-1 if a < b
33207c478bd9Sstevel@tonic-gate **		 0 if a == b
33217c478bd9Sstevel@tonic-gate **		+1 if a > b
33227c478bd9Sstevel@tonic-gate **
33237c478bd9Sstevel@tonic-gate */
33247c478bd9Sstevel@tonic-gate 
33257c478bd9Sstevel@tonic-gate static int
33267c478bd9Sstevel@tonic-gate workcmpf4(a, b)
33277c478bd9Sstevel@tonic-gate 	register WORK *a;
33287c478bd9Sstevel@tonic-gate 	register WORK *b;
33297c478bd9Sstevel@tonic-gate {
33307c478bd9Sstevel@tonic-gate 	return strcmp(a->w_name, b->w_name);
33317c478bd9Sstevel@tonic-gate }
33327c478bd9Sstevel@tonic-gate /*
33337c478bd9Sstevel@tonic-gate **  WORKCMPF5 -- compare based on assigned random number
33347c478bd9Sstevel@tonic-gate **
33357c478bd9Sstevel@tonic-gate **	Parameters:
33367c478bd9Sstevel@tonic-gate **		a -- the first argument (ignored).
33377c478bd9Sstevel@tonic-gate **		b -- the second argument (ignored).
33387c478bd9Sstevel@tonic-gate **
33397c478bd9Sstevel@tonic-gate **	Returns:
33407c478bd9Sstevel@tonic-gate **		randomly 1/-1
33417c478bd9Sstevel@tonic-gate */
33427c478bd9Sstevel@tonic-gate 
33437c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
33447c478bd9Sstevel@tonic-gate static int
33457c478bd9Sstevel@tonic-gate workcmpf5(a, b)
33467c478bd9Sstevel@tonic-gate 	register WORK *a;
33477c478bd9Sstevel@tonic-gate 	register WORK *b;
33487c478bd9Sstevel@tonic-gate {
33497c478bd9Sstevel@tonic-gate 	if (strlen(a->w_name) < randi || strlen(b->w_name) < randi)
33507c478bd9Sstevel@tonic-gate 		return -1;
33517c478bd9Sstevel@tonic-gate 	return a->w_name[randi] - b->w_name[randi];
33527c478bd9Sstevel@tonic-gate }
33537c478bd9Sstevel@tonic-gate /*
33547c478bd9Sstevel@tonic-gate **  WORKCMPF6 -- simple modification-time-only compare function.
33557c478bd9Sstevel@tonic-gate **
33567c478bd9Sstevel@tonic-gate **	Parameters:
33577c478bd9Sstevel@tonic-gate **		a -- the first argument.
33587c478bd9Sstevel@tonic-gate **		b -- the second argument.
33597c478bd9Sstevel@tonic-gate **
33607c478bd9Sstevel@tonic-gate **	Returns:
33617c478bd9Sstevel@tonic-gate **		-1 if a < b
33627c478bd9Sstevel@tonic-gate **		 0 if a == b
33637c478bd9Sstevel@tonic-gate **		+1 if a > b
33647c478bd9Sstevel@tonic-gate **
33657c478bd9Sstevel@tonic-gate */
33667c478bd9Sstevel@tonic-gate 
33677c478bd9Sstevel@tonic-gate static int
33687c478bd9Sstevel@tonic-gate workcmpf6(a, b)
33697c478bd9Sstevel@tonic-gate 	register WORK *a;
33707c478bd9Sstevel@tonic-gate 	register WORK *b;
33717c478bd9Sstevel@tonic-gate {
33727c478bd9Sstevel@tonic-gate 	if (a->w_mtime > b->w_mtime)
33737c478bd9Sstevel@tonic-gate 		return 1;
33747c478bd9Sstevel@tonic-gate 	else if (a->w_mtime < b->w_mtime)
33757c478bd9Sstevel@tonic-gate 		return -1;
33767c478bd9Sstevel@tonic-gate 	else
33777c478bd9Sstevel@tonic-gate 		return 0;
33787c478bd9Sstevel@tonic-gate }
33797c478bd9Sstevel@tonic-gate #if _FFR_RHS
33807c478bd9Sstevel@tonic-gate /*
33817c478bd9Sstevel@tonic-gate **  WORKCMPF7 -- compare function for ordering work based on shuffled host name.
33827c478bd9Sstevel@tonic-gate **
33837c478bd9Sstevel@tonic-gate **	Sorts on lock status, host name, and priority in that order.
33847c478bd9Sstevel@tonic-gate **
33857c478bd9Sstevel@tonic-gate **	Parameters:
33867c478bd9Sstevel@tonic-gate **		a -- the first argument.
33877c478bd9Sstevel@tonic-gate **		b -- the second argument.
33887c478bd9Sstevel@tonic-gate **
33897c478bd9Sstevel@tonic-gate **	Returns:
33907c478bd9Sstevel@tonic-gate **		<0 if a < b
33917c478bd9Sstevel@tonic-gate **		 0 if a == b
33927c478bd9Sstevel@tonic-gate **		>0 if a > b
33937c478bd9Sstevel@tonic-gate **
33947c478bd9Sstevel@tonic-gate */
33957c478bd9Sstevel@tonic-gate 
33967c478bd9Sstevel@tonic-gate static int
33977c478bd9Sstevel@tonic-gate workcmpf7(a, b)
33987c478bd9Sstevel@tonic-gate 	register WORK *a;
33997c478bd9Sstevel@tonic-gate 	register WORK *b;
34007c478bd9Sstevel@tonic-gate {
34017c478bd9Sstevel@tonic-gate 	int i;
34027c478bd9Sstevel@tonic-gate 
34037c478bd9Sstevel@tonic-gate 	/* lock status */
34047c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
34057c478bd9Sstevel@tonic-gate 		return a->w_lock - b->w_lock;
34067c478bd9Sstevel@tonic-gate 
34077c478bd9Sstevel@tonic-gate 	/* host name */
34087c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
34097c478bd9Sstevel@tonic-gate 		return 1;
34107c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
34117c478bd9Sstevel@tonic-gate 		return -1;
34127c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
34137c478bd9Sstevel@tonic-gate 	    (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
34147c478bd9Sstevel@tonic-gate 		return i;
34157c478bd9Sstevel@tonic-gate 
34167c478bd9Sstevel@tonic-gate 	/* job priority */
34177c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
34187c478bd9Sstevel@tonic-gate }
34197c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
34207c478bd9Sstevel@tonic-gate /*
34217c478bd9Sstevel@tonic-gate **  STRREV -- reverse string
34227c478bd9Sstevel@tonic-gate **
34237c478bd9Sstevel@tonic-gate **	Returns a pointer to a new string that is the reverse of
34247c478bd9Sstevel@tonic-gate **	the string pointed to by fwd.  The space for the new
34257c478bd9Sstevel@tonic-gate **	string is obtained using xalloc().
34267c478bd9Sstevel@tonic-gate **
34277c478bd9Sstevel@tonic-gate **	Parameters:
34287c478bd9Sstevel@tonic-gate **		fwd -- the string to reverse.
34297c478bd9Sstevel@tonic-gate **
34307c478bd9Sstevel@tonic-gate **	Returns:
34317c478bd9Sstevel@tonic-gate **		the reversed string.
34327c478bd9Sstevel@tonic-gate */
34337c478bd9Sstevel@tonic-gate 
34347c478bd9Sstevel@tonic-gate static char *
34357c478bd9Sstevel@tonic-gate strrev(fwd)
34367c478bd9Sstevel@tonic-gate 	char *fwd;
34377c478bd9Sstevel@tonic-gate {
34387c478bd9Sstevel@tonic-gate 	char *rev = NULL;
34397c478bd9Sstevel@tonic-gate 	int len, cnt;
34407c478bd9Sstevel@tonic-gate 
34417c478bd9Sstevel@tonic-gate 	len = strlen(fwd);
34427c478bd9Sstevel@tonic-gate 	rev = xalloc(len + 1);
34437c478bd9Sstevel@tonic-gate 	for (cnt = 0; cnt < len; ++cnt)
34447c478bd9Sstevel@tonic-gate 		rev[cnt] = fwd[len - cnt - 1];
34457c478bd9Sstevel@tonic-gate 	rev[len] = '\0';
34467c478bd9Sstevel@tonic-gate 	return rev;
34477c478bd9Sstevel@tonic-gate }
34487c478bd9Sstevel@tonic-gate 
34497c478bd9Sstevel@tonic-gate #if _FFR_RHS
34507c478bd9Sstevel@tonic-gate 
34517c478bd9Sstevel@tonic-gate # define NASCII	128
34527c478bd9Sstevel@tonic-gate # define NCHAR	256
34537c478bd9Sstevel@tonic-gate 
34547c478bd9Sstevel@tonic-gate static unsigned char ShuffledAlphabet[NCHAR];
34557c478bd9Sstevel@tonic-gate 
34567c478bd9Sstevel@tonic-gate void
34577c478bd9Sstevel@tonic-gate init_shuffle_alphabet()
34587c478bd9Sstevel@tonic-gate {
34597c478bd9Sstevel@tonic-gate 	static bool init = false;
34607c478bd9Sstevel@tonic-gate 	int i;
34617c478bd9Sstevel@tonic-gate 
34627c478bd9Sstevel@tonic-gate 	if (init)
34637c478bd9Sstevel@tonic-gate 		return;
34647c478bd9Sstevel@tonic-gate 
34657c478bd9Sstevel@tonic-gate 	/* fill the ShuffledAlphabet */
346649218d4fSjbeck 	for (i = 0; i < NASCII; i++)
34677c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = i;
34687c478bd9Sstevel@tonic-gate 
34697c478bd9Sstevel@tonic-gate 	/* mix it */
347049218d4fSjbeck 	for (i = 1; i < NASCII; i++)
34717c478bd9Sstevel@tonic-gate 	{
347249218d4fSjbeck 		register int j = get_random() % NASCII;
34737c478bd9Sstevel@tonic-gate 		register int tmp;
34747c478bd9Sstevel@tonic-gate 
34757c478bd9Sstevel@tonic-gate 		tmp = ShuffledAlphabet[j];
34767c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[j] = ShuffledAlphabet[i];
34777c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = tmp;
34787c478bd9Sstevel@tonic-gate 	}
34797c478bd9Sstevel@tonic-gate 
34807c478bd9Sstevel@tonic-gate 	/* make it case insensitive */
34817c478bd9Sstevel@tonic-gate 	for (i = 'A'; i <= 'Z'; i++)
34827c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
34837c478bd9Sstevel@tonic-gate 
34847c478bd9Sstevel@tonic-gate 	/* fill the upper part */
348549218d4fSjbeck 	for (i = 0; i < NASCII; i++)
348649218d4fSjbeck 		ShuffledAlphabet[i + NASCII] = ShuffledAlphabet[i];
34877c478bd9Sstevel@tonic-gate 	init = true;
34887c478bd9Sstevel@tonic-gate }
34897c478bd9Sstevel@tonic-gate 
34907c478bd9Sstevel@tonic-gate static int
34917c478bd9Sstevel@tonic-gate sm_strshufflecmp(a, b)
34927c478bd9Sstevel@tonic-gate 	char *a;
34937c478bd9Sstevel@tonic-gate 	char *b;
34947c478bd9Sstevel@tonic-gate {
34957c478bd9Sstevel@tonic-gate 	const unsigned char *us1 = (const unsigned char *) a;
34967c478bd9Sstevel@tonic-gate 	const unsigned char *us2 = (const unsigned char *) b;
34977c478bd9Sstevel@tonic-gate 
34987c478bd9Sstevel@tonic-gate 	while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
34997c478bd9Sstevel@tonic-gate 	{
35007c478bd9Sstevel@tonic-gate 		if (*us1++ == '\0')
35017c478bd9Sstevel@tonic-gate 			return 0;
35027c478bd9Sstevel@tonic-gate 	}
35037c478bd9Sstevel@tonic-gate 	return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
35047c478bd9Sstevel@tonic-gate }
35057c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
35067c478bd9Sstevel@tonic-gate 
35077c478bd9Sstevel@tonic-gate /*
35087c478bd9Sstevel@tonic-gate **  DOWORK -- do a work request.
35097c478bd9Sstevel@tonic-gate **
35107c478bd9Sstevel@tonic-gate **	Parameters:
35117c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group for the job.
35127c478bd9Sstevel@tonic-gate **		qdir -- the index of the queue directory for the job.
35137c478bd9Sstevel@tonic-gate **		id -- the ID of the job to run.
35147c478bd9Sstevel@tonic-gate **		forkflag -- if set, run this in background.
35157c478bd9Sstevel@tonic-gate **		requeueflag -- if set, reinstantiate the queue quickly.
35167c478bd9Sstevel@tonic-gate **			This is used when expanding aliases in the queue.
35177c478bd9Sstevel@tonic-gate **			If forkflag is also set, it doesn't wait for the
35187c478bd9Sstevel@tonic-gate **			child.
35197c478bd9Sstevel@tonic-gate **		e - the envelope in which to run it.
35207c478bd9Sstevel@tonic-gate **
35217c478bd9Sstevel@tonic-gate **	Returns:
35227c478bd9Sstevel@tonic-gate **		process id of process that is running the queue job.
35237c478bd9Sstevel@tonic-gate **
35247c478bd9Sstevel@tonic-gate **	Side Effects:
35257c478bd9Sstevel@tonic-gate **		The work request is satisfied if possible.
35267c478bd9Sstevel@tonic-gate */
35277c478bd9Sstevel@tonic-gate 
35287c478bd9Sstevel@tonic-gate pid_t
35297c478bd9Sstevel@tonic-gate dowork(qgrp, qdir, id, forkflag, requeueflag, e)
35307c478bd9Sstevel@tonic-gate 	int qgrp;
35317c478bd9Sstevel@tonic-gate 	int qdir;
35327c478bd9Sstevel@tonic-gate 	char *id;
35337c478bd9Sstevel@tonic-gate 	bool forkflag;
35347c478bd9Sstevel@tonic-gate 	bool requeueflag;
35357c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
35367c478bd9Sstevel@tonic-gate {
35377c478bd9Sstevel@tonic-gate 	register pid_t pid;
35387c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
35397c478bd9Sstevel@tonic-gate 
35407c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
35417c478bd9Sstevel@tonic-gate 		sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
35427c478bd9Sstevel@tonic-gate 
35437c478bd9Sstevel@tonic-gate 	/*
35447c478bd9Sstevel@tonic-gate 	**  Fork for work.
35457c478bd9Sstevel@tonic-gate 	*/
35467c478bd9Sstevel@tonic-gate 
35477c478bd9Sstevel@tonic-gate 	if (forkflag)
35487c478bd9Sstevel@tonic-gate 	{
35497c478bd9Sstevel@tonic-gate 		/*
35507c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the
35517c478bd9Sstevel@tonic-gate 		**  parent does not wait, the parent may close the
35527c478bd9Sstevel@tonic-gate 		**  maps thereby removing any shared memory used by
35537c478bd9Sstevel@tonic-gate 		**  the map.  Therefore, close the maps now so the
35547c478bd9Sstevel@tonic-gate 		**  child will dynamically open them if necessary.
35557c478bd9Sstevel@tonic-gate 		*/
35567c478bd9Sstevel@tonic-gate 
35577c478bd9Sstevel@tonic-gate 		closemaps(false);
35587c478bd9Sstevel@tonic-gate 
35597c478bd9Sstevel@tonic-gate 		pid = fork();
35607c478bd9Sstevel@tonic-gate 		if (pid < 0)
35617c478bd9Sstevel@tonic-gate 		{
35627c478bd9Sstevel@tonic-gate 			syserr("dowork: cannot fork");
35637c478bd9Sstevel@tonic-gate 			return 0;
35647c478bd9Sstevel@tonic-gate 		}
35657c478bd9Sstevel@tonic-gate 		else if (pid > 0)
35667c478bd9Sstevel@tonic-gate 		{
35677c478bd9Sstevel@tonic-gate 			/* parent -- clean out connection cache */
35687c478bd9Sstevel@tonic-gate 			mci_flush(false, NULL);
35697c478bd9Sstevel@tonic-gate 		}
35707c478bd9Sstevel@tonic-gate 		else
35717c478bd9Sstevel@tonic-gate 		{
35727c478bd9Sstevel@tonic-gate 			/*
35737c478bd9Sstevel@tonic-gate 			**  Initialize exception stack and default exception
35747c478bd9Sstevel@tonic-gate 			**  handler for child process.
35757c478bd9Sstevel@tonic-gate 			*/
35767c478bd9Sstevel@tonic-gate 
35777c478bd9Sstevel@tonic-gate 			/* Reset global flags */
35787c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
35797c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
35807c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
35817c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
35827c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
35837c478bd9Sstevel@tonic-gate 			sm_exc_newthread(fatal_error);
35847c478bd9Sstevel@tonic-gate 
35857c478bd9Sstevel@tonic-gate 			/*
35867c478bd9Sstevel@tonic-gate 			**  See note above about SMTP processes and SIGCHLD.
35877c478bd9Sstevel@tonic-gate 			*/
35887c478bd9Sstevel@tonic-gate 
35897c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP ||
35907c478bd9Sstevel@tonic-gate 			    OpMode == MD_DAEMON ||
35917c478bd9Sstevel@tonic-gate 			    MaxQueueChildren > 0)
35927c478bd9Sstevel@tonic-gate 			{
35937c478bd9Sstevel@tonic-gate 				proc_list_clear();
35947c478bd9Sstevel@tonic-gate 				sm_releasesignal(SIGCHLD);
35957c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, SIG_DFL);
35967c478bd9Sstevel@tonic-gate 			}
35977c478bd9Sstevel@tonic-gate 
35987c478bd9Sstevel@tonic-gate 			/* child -- error messages to the transcript */
35997c478bd9Sstevel@tonic-gate 			QuickAbort = OnlyOneError = false;
36007c478bd9Sstevel@tonic-gate 		}
36017c478bd9Sstevel@tonic-gate 	}
36027c478bd9Sstevel@tonic-gate 	else
36037c478bd9Sstevel@tonic-gate 	{
36047c478bd9Sstevel@tonic-gate 		pid = 0;
36057c478bd9Sstevel@tonic-gate 	}
36067c478bd9Sstevel@tonic-gate 
36077c478bd9Sstevel@tonic-gate 	if (pid == 0)
36087c478bd9Sstevel@tonic-gate 	{
36097c478bd9Sstevel@tonic-gate 		/*
36107c478bd9Sstevel@tonic-gate 		**  CHILD
36117c478bd9Sstevel@tonic-gate 		**	Lock the control file to avoid duplicate deliveries.
36127c478bd9Sstevel@tonic-gate 		**		Then run the file as though we had just read it.
36137c478bd9Sstevel@tonic-gate 		**	We save an idea of the temporary name so we
36147c478bd9Sstevel@tonic-gate 		**		can recover on interrupt.
36157c478bd9Sstevel@tonic-gate 		*/
36167c478bd9Sstevel@tonic-gate 
36177c478bd9Sstevel@tonic-gate 		if (forkflag)
36187c478bd9Sstevel@tonic-gate 		{
36197c478bd9Sstevel@tonic-gate 			/* Reset global flags */
36207c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
36217c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
36227c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
36237c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
36247c478bd9Sstevel@tonic-gate 		}
36257c478bd9Sstevel@tonic-gate 
36267c478bd9Sstevel@tonic-gate 		/* set basic modes, etc. */
36277c478bd9Sstevel@tonic-gate 		sm_clear_events();
36287c478bd9Sstevel@tonic-gate 		clearstats();
36297c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
36307c478bd9Sstevel@tonic-gate 		clearenvelope(e, false, rpool);
36317c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
36327c478bd9Sstevel@tonic-gate 		set_delivery_mode(SM_DELIVER, e);
36337c478bd9Sstevel@tonic-gate 		e->e_errormode = EM_MAIL;
36347c478bd9Sstevel@tonic-gate 		e->e_id = id;
36357c478bd9Sstevel@tonic-gate 		e->e_qgrp = qgrp;
36367c478bd9Sstevel@tonic-gate 		e->e_qdir = qdir;
36377c478bd9Sstevel@tonic-gate 		GrabTo = UseErrorsTo = false;
36387c478bd9Sstevel@tonic-gate 		ExitStat = EX_OK;
36397c478bd9Sstevel@tonic-gate 		if (forkflag)
36407c478bd9Sstevel@tonic-gate 		{
36417c478bd9Sstevel@tonic-gate 			disconnect(1, e);
36427c478bd9Sstevel@tonic-gate 			set_op_mode(MD_QUEUERUN);
36437c478bd9Sstevel@tonic-gate 		}
36447c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, e, "%s from queue", qid_printname(e));
36457c478bd9Sstevel@tonic-gate 		if (LogLevel > 76)
36467c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
36477c478bd9Sstevel@tonic-gate 				  (int) CurrentPid);
36487c478bd9Sstevel@tonic-gate 
36497c478bd9Sstevel@tonic-gate 		/* don't use the headers from sendmail.cf... */
36507c478bd9Sstevel@tonic-gate 		e->e_header = NULL;
36517c478bd9Sstevel@tonic-gate 
36527c478bd9Sstevel@tonic-gate 		/* read the queue control file -- return if locked */
36537c478bd9Sstevel@tonic-gate 		if (!readqf(e, false))
36547c478bd9Sstevel@tonic-gate 		{
36557c478bd9Sstevel@tonic-gate 			if (tTd(40, 4) && e->e_id != NULL)
36567c478bd9Sstevel@tonic-gate 				sm_dprintf("readqf(%s) failed\n",
36577c478bd9Sstevel@tonic-gate 					qid_printname(e));
36587c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
36597c478bd9Sstevel@tonic-gate 			if (forkflag)
36607c478bd9Sstevel@tonic-gate 				finis(false, true, EX_OK);
36617c478bd9Sstevel@tonic-gate 			else
36627c478bd9Sstevel@tonic-gate 			{
36637c478bd9Sstevel@tonic-gate 				/* adding this frees 8 bytes */
36647c478bd9Sstevel@tonic-gate 				clearenvelope(e, false, rpool);
36657c478bd9Sstevel@tonic-gate 
36667c478bd9Sstevel@tonic-gate 				/* adding this frees 12 bytes */
36677c478bd9Sstevel@tonic-gate 				sm_rpool_free(rpool);
36687c478bd9Sstevel@tonic-gate 				e->e_rpool = NULL;
36697c478bd9Sstevel@tonic-gate 				return 0;
36707c478bd9Sstevel@tonic-gate 			}
36717c478bd9Sstevel@tonic-gate 		}
36727c478bd9Sstevel@tonic-gate 
36737c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_INQUEUE;
36747c478bd9Sstevel@tonic-gate 		eatheader(e, requeueflag, true);
36757c478bd9Sstevel@tonic-gate 
36767c478bd9Sstevel@tonic-gate 		if (requeueflag)
36777c478bd9Sstevel@tonic-gate 			queueup(e, false, false);
36787c478bd9Sstevel@tonic-gate 
36797c478bd9Sstevel@tonic-gate 		/* do the delivery */
36807c478bd9Sstevel@tonic-gate 		sendall(e, SM_DELIVER);
36817c478bd9Sstevel@tonic-gate 
36827c478bd9Sstevel@tonic-gate 		/* finish up and exit */
36837c478bd9Sstevel@tonic-gate 		if (forkflag)
36847c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
36857c478bd9Sstevel@tonic-gate 		else
36867c478bd9Sstevel@tonic-gate 		{
36877c478bd9Sstevel@tonic-gate 			dropenvelope(e, true, false);
36887c478bd9Sstevel@tonic-gate 			sm_rpool_free(rpool);
36897c478bd9Sstevel@tonic-gate 			e->e_rpool = NULL;
36907c478bd9Sstevel@tonic-gate 		}
36917c478bd9Sstevel@tonic-gate 	}
36927c478bd9Sstevel@tonic-gate 	e->e_id = NULL;
36937c478bd9Sstevel@tonic-gate 	return pid;
36947c478bd9Sstevel@tonic-gate }
36957c478bd9Sstevel@tonic-gate 
36967c478bd9Sstevel@tonic-gate /*
36977c478bd9Sstevel@tonic-gate **  DOWORKLIST -- process a list of envelopes as work requests
36987c478bd9Sstevel@tonic-gate **
36997c478bd9Sstevel@tonic-gate **	Similar to dowork(), except that after forking, it processes an
37007c478bd9Sstevel@tonic-gate **	envelope and its siblings, treating each envelope as a work request.
37017c478bd9Sstevel@tonic-gate **
37027c478bd9Sstevel@tonic-gate **	Parameters:
37037c478bd9Sstevel@tonic-gate **		el -- envelope to be processed including its siblings.
37047c478bd9Sstevel@tonic-gate **		forkflag -- if set, run this in background.
37057c478bd9Sstevel@tonic-gate **		requeueflag -- if set, reinstantiate the queue quickly.
37067c478bd9Sstevel@tonic-gate **			This is used when expanding aliases in the queue.
37077c478bd9Sstevel@tonic-gate **			If forkflag is also set, it doesn't wait for the
37087c478bd9Sstevel@tonic-gate **			child.
37097c478bd9Sstevel@tonic-gate **
37107c478bd9Sstevel@tonic-gate **	Returns:
37117c478bd9Sstevel@tonic-gate **		process id of process that is running the queue job.
37127c478bd9Sstevel@tonic-gate **
37137c478bd9Sstevel@tonic-gate **	Side Effects:
37147c478bd9Sstevel@tonic-gate **		The work request is satisfied if possible.
37157c478bd9Sstevel@tonic-gate */
37167c478bd9Sstevel@tonic-gate 
37177c478bd9Sstevel@tonic-gate pid_t
37187c478bd9Sstevel@tonic-gate doworklist(el, forkflag, requeueflag)
37197c478bd9Sstevel@tonic-gate 	ENVELOPE *el;
37207c478bd9Sstevel@tonic-gate 	bool forkflag;
37217c478bd9Sstevel@tonic-gate 	bool requeueflag;
37227c478bd9Sstevel@tonic-gate {
37237c478bd9Sstevel@tonic-gate 	register pid_t pid;
37247c478bd9Sstevel@tonic-gate 	ENVELOPE *ei;
37257c478bd9Sstevel@tonic-gate 
37267c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
37277c478bd9Sstevel@tonic-gate 		sm_dprintf("doworklist()\n");
37287c478bd9Sstevel@tonic-gate 
37297c478bd9Sstevel@tonic-gate 	/*
37307c478bd9Sstevel@tonic-gate 	**  Fork for work.
37317c478bd9Sstevel@tonic-gate 	*/
37327c478bd9Sstevel@tonic-gate 
37337c478bd9Sstevel@tonic-gate 	if (forkflag)
37347c478bd9Sstevel@tonic-gate 	{
37357c478bd9Sstevel@tonic-gate 		/*
37367c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the
37377c478bd9Sstevel@tonic-gate 		**  parent does not wait, the parent may close the
37387c478bd9Sstevel@tonic-gate 		**  maps thereby removing any shared memory used by
37397c478bd9Sstevel@tonic-gate 		**  the map.  Therefore, close the maps now so the
37407c478bd9Sstevel@tonic-gate 		**  child will dynamically open them if necessary.
37417c478bd9Sstevel@tonic-gate 		*/
37427c478bd9Sstevel@tonic-gate 
37437c478bd9Sstevel@tonic-gate 		closemaps(false);
37447c478bd9Sstevel@tonic-gate 
37457c478bd9Sstevel@tonic-gate 		pid = fork();
37467c478bd9Sstevel@tonic-gate 		if (pid < 0)
37477c478bd9Sstevel@tonic-gate 		{
37487c478bd9Sstevel@tonic-gate 			syserr("doworklist: cannot fork");
37497c478bd9Sstevel@tonic-gate 			return 0;
37507c478bd9Sstevel@tonic-gate 		}
37517c478bd9Sstevel@tonic-gate 		else if (pid > 0)
37527c478bd9Sstevel@tonic-gate 		{
37537c478bd9Sstevel@tonic-gate 			/* parent -- clean out connection cache */
37547c478bd9Sstevel@tonic-gate 			mci_flush(false, NULL);
37557c478bd9Sstevel@tonic-gate 		}
37567c478bd9Sstevel@tonic-gate 		else
37577c478bd9Sstevel@tonic-gate 		{
37587c478bd9Sstevel@tonic-gate 			/*
37597c478bd9Sstevel@tonic-gate 			**  Initialize exception stack and default exception
37607c478bd9Sstevel@tonic-gate 			**  handler for child process.
37617c478bd9Sstevel@tonic-gate 			*/
37627c478bd9Sstevel@tonic-gate 
37637c478bd9Sstevel@tonic-gate 			/* Reset global flags */
37647c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
37657c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
37667c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
37677c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
37687c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
37697c478bd9Sstevel@tonic-gate 			sm_exc_newthread(fatal_error);
37707c478bd9Sstevel@tonic-gate 
37717c478bd9Sstevel@tonic-gate 			/*
37727c478bd9Sstevel@tonic-gate 			**  See note above about SMTP processes and SIGCHLD.
37737c478bd9Sstevel@tonic-gate 			*/
37747c478bd9Sstevel@tonic-gate 
37757c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP ||
37767c478bd9Sstevel@tonic-gate 			    OpMode == MD_DAEMON ||
37777c478bd9Sstevel@tonic-gate 			    MaxQueueChildren > 0)
37787c478bd9Sstevel@tonic-gate 			{
37797c478bd9Sstevel@tonic-gate 				proc_list_clear();
37807c478bd9Sstevel@tonic-gate 				sm_releasesignal(SIGCHLD);
37817c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, SIG_DFL);
37827c478bd9Sstevel@tonic-gate 			}
37837c478bd9Sstevel@tonic-gate 
37847c478bd9Sstevel@tonic-gate 			/* child -- error messages to the transcript */
37857c478bd9Sstevel@tonic-gate 			QuickAbort = OnlyOneError = false;
37867c478bd9Sstevel@tonic-gate 		}
37877c478bd9Sstevel@tonic-gate 	}
37887c478bd9Sstevel@tonic-gate 	else
37897c478bd9Sstevel@tonic-gate 	{
37907c478bd9Sstevel@tonic-gate 		pid = 0;
37917c478bd9Sstevel@tonic-gate 	}
37927c478bd9Sstevel@tonic-gate 
37937c478bd9Sstevel@tonic-gate 	if (pid != 0)
37947c478bd9Sstevel@tonic-gate 		return pid;
37957c478bd9Sstevel@tonic-gate 
37967c478bd9Sstevel@tonic-gate 	/*
37977c478bd9Sstevel@tonic-gate 	**  IN CHILD
37987c478bd9Sstevel@tonic-gate 	**	Lock the control file to avoid duplicate deliveries.
37997c478bd9Sstevel@tonic-gate 	**		Then run the file as though we had just read it.
38007c478bd9Sstevel@tonic-gate 	**	We save an idea of the temporary name so we
38017c478bd9Sstevel@tonic-gate 	**		can recover on interrupt.
38027c478bd9Sstevel@tonic-gate 	*/
38037c478bd9Sstevel@tonic-gate 
38047c478bd9Sstevel@tonic-gate 	if (forkflag)
38057c478bd9Sstevel@tonic-gate 	{
38067c478bd9Sstevel@tonic-gate 		/* Reset global flags */
38077c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
38087c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
38097c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
38107c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
38117c478bd9Sstevel@tonic-gate 	}
38127c478bd9Sstevel@tonic-gate 
38137c478bd9Sstevel@tonic-gate 	/* set basic modes, etc. */
38147c478bd9Sstevel@tonic-gate 	sm_clear_events();
38157c478bd9Sstevel@tonic-gate 	clearstats();
38167c478bd9Sstevel@tonic-gate 	GrabTo = UseErrorsTo = false;
38177c478bd9Sstevel@tonic-gate 	ExitStat = EX_OK;
38187c478bd9Sstevel@tonic-gate 	if (forkflag)
38197c478bd9Sstevel@tonic-gate 	{
38207c478bd9Sstevel@tonic-gate 		disconnect(1, el);
38217c478bd9Sstevel@tonic-gate 		set_op_mode(MD_QUEUERUN);
38227c478bd9Sstevel@tonic-gate 	}
38237c478bd9Sstevel@tonic-gate 	if (LogLevel > 76)
38247c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
38257c478bd9Sstevel@tonic-gate 			  (int) CurrentPid);
38267c478bd9Sstevel@tonic-gate 
38277c478bd9Sstevel@tonic-gate 	for (ei = el; ei != NULL; ei = ei->e_sibling)
38287c478bd9Sstevel@tonic-gate 	{
38297c478bd9Sstevel@tonic-gate 		ENVELOPE e;
38307c478bd9Sstevel@tonic-gate 		SM_RPOOL_T *rpool;
38317c478bd9Sstevel@tonic-gate 
38327c478bd9Sstevel@tonic-gate 		if (WILL_BE_QUEUED(ei->e_sendmode))
38337c478bd9Sstevel@tonic-gate 			continue;
38347c478bd9Sstevel@tonic-gate 		else if (QueueMode != QM_QUARANTINE &&
38357c478bd9Sstevel@tonic-gate 			 ei->e_quarmsg != NULL)
38367c478bd9Sstevel@tonic-gate 			continue;
38377c478bd9Sstevel@tonic-gate 
38387c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
38397c478bd9Sstevel@tonic-gate 		clearenvelope(&e, true, rpool);
38407c478bd9Sstevel@tonic-gate 		e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
38417c478bd9Sstevel@tonic-gate 		set_delivery_mode(SM_DELIVER, &e);
38427c478bd9Sstevel@tonic-gate 		e.e_errormode = EM_MAIL;
38437c478bd9Sstevel@tonic-gate 		e.e_id = ei->e_id;
38447c478bd9Sstevel@tonic-gate 		e.e_qgrp = ei->e_qgrp;
38457c478bd9Sstevel@tonic-gate 		e.e_qdir = ei->e_qdir;
38467c478bd9Sstevel@tonic-gate 		openxscript(&e);
38477c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
38487c478bd9Sstevel@tonic-gate 
38497c478bd9Sstevel@tonic-gate 		/* don't use the headers from sendmail.cf... */
38507c478bd9Sstevel@tonic-gate 		e.e_header = NULL;
38517c478bd9Sstevel@tonic-gate 		CurEnv = &e;
38527c478bd9Sstevel@tonic-gate 
38537c478bd9Sstevel@tonic-gate 		/* read the queue control file -- return if locked */
38547c478bd9Sstevel@tonic-gate 		if (readqf(&e, false))
38557c478bd9Sstevel@tonic-gate 		{
38567c478bd9Sstevel@tonic-gate 			e.e_flags |= EF_INQUEUE;
38577c478bd9Sstevel@tonic-gate 			eatheader(&e, requeueflag, true);
38587c478bd9Sstevel@tonic-gate 
38597c478bd9Sstevel@tonic-gate 			if (requeueflag)
38607c478bd9Sstevel@tonic-gate 				queueup(&e, false, false);
38617c478bd9Sstevel@tonic-gate 
38627c478bd9Sstevel@tonic-gate 			/* do the delivery */
38637c478bd9Sstevel@tonic-gate 			sendall(&e, SM_DELIVER);
38647c478bd9Sstevel@tonic-gate 			dropenvelope(&e, true, false);
38657c478bd9Sstevel@tonic-gate 		}
38667c478bd9Sstevel@tonic-gate 		else
38677c478bd9Sstevel@tonic-gate 		{
38687c478bd9Sstevel@tonic-gate 			if (tTd(40, 4) && e.e_id != NULL)
38697c478bd9Sstevel@tonic-gate 				sm_dprintf("readqf(%s) failed\n",
38707c478bd9Sstevel@tonic-gate 					qid_printname(&e));
38717c478bd9Sstevel@tonic-gate 		}
38727c478bd9Sstevel@tonic-gate 		sm_rpool_free(rpool);
38737c478bd9Sstevel@tonic-gate 		ei->e_id = NULL;
38747c478bd9Sstevel@tonic-gate 	}
38757c478bd9Sstevel@tonic-gate 
38767c478bd9Sstevel@tonic-gate 	/* restore CurEnv */
38777c478bd9Sstevel@tonic-gate 	CurEnv = el;
38787c478bd9Sstevel@tonic-gate 
38797c478bd9Sstevel@tonic-gate 	/* finish up and exit */
38807c478bd9Sstevel@tonic-gate 	if (forkflag)
38817c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
38827c478bd9Sstevel@tonic-gate 	return 0;
38837c478bd9Sstevel@tonic-gate }
38847c478bd9Sstevel@tonic-gate /*
38857c478bd9Sstevel@tonic-gate **  READQF -- read queue file and set up environment.
38867c478bd9Sstevel@tonic-gate **
38877c478bd9Sstevel@tonic-gate **	Parameters:
38887c478bd9Sstevel@tonic-gate **		e -- the envelope of the job to run.
38897c478bd9Sstevel@tonic-gate **		openonly -- only open the qf (returned as e_lockfp)
38907c478bd9Sstevel@tonic-gate **
38917c478bd9Sstevel@tonic-gate **	Returns:
38927c478bd9Sstevel@tonic-gate **		true if it successfully read the queue file.
38937c478bd9Sstevel@tonic-gate **		false otherwise.
38947c478bd9Sstevel@tonic-gate **
38957c478bd9Sstevel@tonic-gate **	Side Effects:
38967c478bd9Sstevel@tonic-gate **		The queue file is returned locked.
38977c478bd9Sstevel@tonic-gate */
38987c478bd9Sstevel@tonic-gate 
38997c478bd9Sstevel@tonic-gate static bool
39007c478bd9Sstevel@tonic-gate readqf(e, openonly)
39017c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
39027c478bd9Sstevel@tonic-gate 	bool openonly;
39037c478bd9Sstevel@tonic-gate {
39047c478bd9Sstevel@tonic-gate 	register SM_FILE_T *qfp;
39057c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
39067c478bd9Sstevel@tonic-gate 	struct stat st, stf;
39077c478bd9Sstevel@tonic-gate 	char *bp;
39087c478bd9Sstevel@tonic-gate 	int qfver = 0;
39097c478bd9Sstevel@tonic-gate 	long hdrsize = 0;
39107c478bd9Sstevel@tonic-gate 	register char *p;
39117c478bd9Sstevel@tonic-gate 	char *frcpt = NULL;
39127c478bd9Sstevel@tonic-gate 	char *orcpt = NULL;
39137c478bd9Sstevel@tonic-gate 	bool nomore = false;
39147c478bd9Sstevel@tonic-gate 	bool bogus = false;
39157c478bd9Sstevel@tonic-gate 	MODE_T qsafe;
39167c478bd9Sstevel@tonic-gate 	char *err;
39177c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
39187c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
3919058561cbSjbeck 	int bufsize;
39207c478bd9Sstevel@tonic-gate 
39217c478bd9Sstevel@tonic-gate 	/*
39227c478bd9Sstevel@tonic-gate 	**  Read and process the file.
39237c478bd9Sstevel@tonic-gate 	*/
39247c478bd9Sstevel@tonic-gate 
39254aac33d3Sjbeck 	SM_REQUIRE(e != NULL);
39263ee0e492Sjbeck 	bp = NULL;
3927058561cbSjbeck 	(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof(qf));
39287c478bd9Sstevel@tonic-gate 	qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR_B, NULL);
39297c478bd9Sstevel@tonic-gate 	if (qfp == NULL)
39307c478bd9Sstevel@tonic-gate 	{
39317c478bd9Sstevel@tonic-gate 		int save_errno = errno;
39327c478bd9Sstevel@tonic-gate 
39337c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39347c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
39357c478bd9Sstevel@tonic-gate 				qf, sm_errstring(errno));
39367c478bd9Sstevel@tonic-gate 		errno = save_errno;
39377c478bd9Sstevel@tonic-gate 		if (errno != ENOENT
39387c478bd9Sstevel@tonic-gate 		    )
39397c478bd9Sstevel@tonic-gate 			syserr("readqf: no control file %s", qf);
39407c478bd9Sstevel@tonic-gate 		RELEASE_QUEUE;
39417c478bd9Sstevel@tonic-gate 		return false;
39427c478bd9Sstevel@tonic-gate 	}
39437c478bd9Sstevel@tonic-gate 
39447c478bd9Sstevel@tonic-gate 	if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
39457c478bd9Sstevel@tonic-gate 		      LOCK_EX|LOCK_NB))
39467c478bd9Sstevel@tonic-gate 	{
39477c478bd9Sstevel@tonic-gate 		/* being processed by another queuer */
39487c478bd9Sstevel@tonic-gate 		if (Verbose)
39497c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
39507c478bd9Sstevel@tonic-gate 					     "%s: locked\n", e->e_id);
39517c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39527c478bd9Sstevel@tonic-gate 			sm_dprintf("%s: locked\n", e->e_id);
39537c478bd9Sstevel@tonic-gate 		if (LogLevel > 19)
39547c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "locked");
39557c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39567c478bd9Sstevel@tonic-gate 		RELEASE_QUEUE;
39577c478bd9Sstevel@tonic-gate 		return false;
39587c478bd9Sstevel@tonic-gate 	}
39597c478bd9Sstevel@tonic-gate 
39607c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
39617c478bd9Sstevel@tonic-gate 
39627c478bd9Sstevel@tonic-gate 	/*
39637c478bd9Sstevel@tonic-gate 	**  Prevent locking race condition.
39647c478bd9Sstevel@tonic-gate 	**
39657c478bd9Sstevel@tonic-gate 	**  Process A: readqf(): qfp = fopen(qffile)
39667c478bd9Sstevel@tonic-gate 	**  Process B: queueup(): rename(tf, qf)
39677c478bd9Sstevel@tonic-gate 	**  Process B: unlocks(tf)
39687c478bd9Sstevel@tonic-gate 	**  Process A: lockfile(qf);
39697c478bd9Sstevel@tonic-gate 	**
39707c478bd9Sstevel@tonic-gate 	**  Process A (us) has the old qf file (before the rename deleted
39717c478bd9Sstevel@tonic-gate 	**  the directory entry) and will be delivering based on old data.
39727c478bd9Sstevel@tonic-gate 	**  This can lead to multiple deliveries of the same recipients.
39737c478bd9Sstevel@tonic-gate 	**
39747c478bd9Sstevel@tonic-gate 	**  Catch this by checking if the underlying qf file has changed
39757c478bd9Sstevel@tonic-gate 	**  *after* acquiring our lock and if so, act as though the file
39767c478bd9Sstevel@tonic-gate 	**  was still locked (i.e., just return like the lockfile() case
39777c478bd9Sstevel@tonic-gate 	**  above.
39787c478bd9Sstevel@tonic-gate 	*/
39797c478bd9Sstevel@tonic-gate 
39807c478bd9Sstevel@tonic-gate 	if (stat(qf, &stf) < 0 ||
39817c478bd9Sstevel@tonic-gate 	    fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
39827c478bd9Sstevel@tonic-gate 	{
39837c478bd9Sstevel@tonic-gate 		/* must have been being processed by someone else */
39847c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39857c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
39867c478bd9Sstevel@tonic-gate 				qf, sm_errstring(errno));
39877c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39887c478bd9Sstevel@tonic-gate 		return false;
39897c478bd9Sstevel@tonic-gate 	}
39907c478bd9Sstevel@tonic-gate 
39917c478bd9Sstevel@tonic-gate 	if (st.st_nlink != stf.st_nlink ||
39927c478bd9Sstevel@tonic-gate 	    st.st_dev != stf.st_dev ||
39937c478bd9Sstevel@tonic-gate 	    ST_INODE(st) != ST_INODE(stf) ||
39947c478bd9Sstevel@tonic-gate #if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
39957c478bd9Sstevel@tonic-gate 	    st.st_gen != stf.st_gen ||
39967c478bd9Sstevel@tonic-gate #endif /* HAS_ST_GEN && 0 */
39977c478bd9Sstevel@tonic-gate 	    st.st_uid != stf.st_uid ||
39987c478bd9Sstevel@tonic-gate 	    st.st_gid != stf.st_gid ||
39997c478bd9Sstevel@tonic-gate 	    st.st_size != stf.st_size)
40007c478bd9Sstevel@tonic-gate 	{
40017c478bd9Sstevel@tonic-gate 		/* changed after opened */
40027c478bd9Sstevel@tonic-gate 		if (Verbose)
40037c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
40047c478bd9Sstevel@tonic-gate 					     "%s: changed\n", e->e_id);
40057c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
40067c478bd9Sstevel@tonic-gate 			sm_dprintf("%s: changed\n", e->e_id);
40077c478bd9Sstevel@tonic-gate 		if (LogLevel > 19)
40087c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "changed");
40097c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40107c478bd9Sstevel@tonic-gate 		return false;
40117c478bd9Sstevel@tonic-gate 	}
40127c478bd9Sstevel@tonic-gate 
40137c478bd9Sstevel@tonic-gate 	/*
40147c478bd9Sstevel@tonic-gate 	**  Check the queue file for plausibility to avoid attacks.
40157c478bd9Sstevel@tonic-gate 	*/
40167c478bd9Sstevel@tonic-gate 
40177c478bd9Sstevel@tonic-gate 	qsafe = S_IWOTH|S_IWGRP;
40187c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
40197c478bd9Sstevel@tonic-gate 		qsafe &= ~S_IWGRP;
40207c478bd9Sstevel@tonic-gate 
40217c478bd9Sstevel@tonic-gate 	bogus = st.st_uid != geteuid() &&
40227c478bd9Sstevel@tonic-gate 		st.st_uid != TrustedUid &&
40237c478bd9Sstevel@tonic-gate 		geteuid() != RealUid;
40247c478bd9Sstevel@tonic-gate 
40257c478bd9Sstevel@tonic-gate 	/*
40267c478bd9Sstevel@tonic-gate 	**  If this qf file results from a set-group-ID binary, then
40277c478bd9Sstevel@tonic-gate 	**  we check whether the directory is group-writable,
40287c478bd9Sstevel@tonic-gate 	**  the queue file mode contains the group-writable bit, and
40297c478bd9Sstevel@tonic-gate 	**  the groups are the same.
40307c478bd9Sstevel@tonic-gate 	**  Notice: this requires that the set-group-ID binary is used to
40317c478bd9Sstevel@tonic-gate 	**  run the queue!
40327c478bd9Sstevel@tonic-gate 	*/
40337c478bd9Sstevel@tonic-gate 
40347c478bd9Sstevel@tonic-gate 	if (bogus && st.st_gid == getegid() && UseMSP)
40357c478bd9Sstevel@tonic-gate 	{
40367c478bd9Sstevel@tonic-gate 		char delim;
40377c478bd9Sstevel@tonic-gate 		struct stat dst;
40387c478bd9Sstevel@tonic-gate 
40397c478bd9Sstevel@tonic-gate 		bp = SM_LAST_DIR_DELIM(qf);
40407c478bd9Sstevel@tonic-gate 		if (bp == NULL)
40417c478bd9Sstevel@tonic-gate 			delim = '\0';
40427c478bd9Sstevel@tonic-gate 		else
40437c478bd9Sstevel@tonic-gate 		{
40447c478bd9Sstevel@tonic-gate 			delim = *bp;
40457c478bd9Sstevel@tonic-gate 			*bp = '\0';
40467c478bd9Sstevel@tonic-gate 		}
40477c478bd9Sstevel@tonic-gate 		if (stat(delim == '\0' ? "." : qf, &dst) < 0)
40487c478bd9Sstevel@tonic-gate 			syserr("readqf: cannot stat directory %s",
40497c478bd9Sstevel@tonic-gate 				delim == '\0' ? "." : qf);
40507c478bd9Sstevel@tonic-gate 		else
40517c478bd9Sstevel@tonic-gate 		{
40527c478bd9Sstevel@tonic-gate 			bogus = !(bitset(S_IWGRP, QueueFileMode) &&
40537c478bd9Sstevel@tonic-gate 				  bitset(S_IWGRP, dst.st_mode) &&
40547c478bd9Sstevel@tonic-gate 				  dst.st_gid == st.st_gid);
40557c478bd9Sstevel@tonic-gate 		}
40567c478bd9Sstevel@tonic-gate 		if (delim != '\0')
40577c478bd9Sstevel@tonic-gate 			*bp = delim;
40583ee0e492Sjbeck 		bp = NULL;
40597c478bd9Sstevel@tonic-gate 	}
40607c478bd9Sstevel@tonic-gate 	if (!bogus)
40617c478bd9Sstevel@tonic-gate 		bogus = bitset(qsafe, st.st_mode);
40627c478bd9Sstevel@tonic-gate 	if (bogus)
40637c478bd9Sstevel@tonic-gate 	{
40647c478bd9Sstevel@tonic-gate 		if (LogLevel > 0)
40657c478bd9Sstevel@tonic-gate 		{
40667c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, e->e_id,
40677c478bd9Sstevel@tonic-gate 				  "bogus queue file, uid=%d, gid=%d, mode=%o",
40687c478bd9Sstevel@tonic-gate 				  st.st_uid, st.st_gid, st.st_mode);
40697c478bd9Sstevel@tonic-gate 		}
40707c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
40717c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): bogus file\n", qf);
40727c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_INQUEUE;
40737c478bd9Sstevel@tonic-gate 		if (!openonly)
40747c478bd9Sstevel@tonic-gate 			loseqfile(e, "bogus file uid/gid in mqueue");
40757c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40767c478bd9Sstevel@tonic-gate 		return false;
40777c478bd9Sstevel@tonic-gate 	}
40787c478bd9Sstevel@tonic-gate 
40797c478bd9Sstevel@tonic-gate 	if (st.st_size == 0)
40807c478bd9Sstevel@tonic-gate 	{
40817c478bd9Sstevel@tonic-gate 		/* must be a bogus file -- if also old, just remove it */
40827c478bd9Sstevel@tonic-gate 		if (!openonly && st.st_ctime + 10 * 60 < curtime())
40837c478bd9Sstevel@tonic-gate 		{
40847c478bd9Sstevel@tonic-gate 			(void) xunlink(queuename(e, DATAFL_LETTER));
40857c478bd9Sstevel@tonic-gate 			(void) xunlink(queuename(e, ANYQFL_LETTER));
40867c478bd9Sstevel@tonic-gate 		}
40877c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40887c478bd9Sstevel@tonic-gate 		return false;
40897c478bd9Sstevel@tonic-gate 	}
40907c478bd9Sstevel@tonic-gate 
40917c478bd9Sstevel@tonic-gate 	if (st.st_nlink == 0)
40927c478bd9Sstevel@tonic-gate 	{
40937c478bd9Sstevel@tonic-gate 		/*
40947c478bd9Sstevel@tonic-gate 		**  Race condition -- we got a file just as it was being
40957c478bd9Sstevel@tonic-gate 		**  unlinked.  Just assume it is zero length.
40967c478bd9Sstevel@tonic-gate 		*/
40977c478bd9Sstevel@tonic-gate 
40987c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40997c478bd9Sstevel@tonic-gate 		return false;
41007c478bd9Sstevel@tonic-gate 	}
41017c478bd9Sstevel@tonic-gate 
41027c478bd9Sstevel@tonic-gate #if _FFR_TRUSTED_QF
41037c478bd9Sstevel@tonic-gate 	/*
41047c478bd9Sstevel@tonic-gate 	**  If we don't own the file mark it as unsafe.
41057c478bd9Sstevel@tonic-gate 	**  However, allow TrustedUser to own it as well
41067c478bd9Sstevel@tonic-gate 	**  in case TrustedUser manipulates the queue.
41077c478bd9Sstevel@tonic-gate 	*/
41087c478bd9Sstevel@tonic-gate 
41097c478bd9Sstevel@tonic-gate 	if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
41107c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_UNSAFE;
41117c478bd9Sstevel@tonic-gate #else /* _FFR_TRUSTED_QF */
41127c478bd9Sstevel@tonic-gate 	/* If we don't own the file mark it as unsafe */
41137c478bd9Sstevel@tonic-gate 	if (st.st_uid != geteuid())
41147c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_UNSAFE;
41157c478bd9Sstevel@tonic-gate #endif /* _FFR_TRUSTED_QF */
41167c478bd9Sstevel@tonic-gate 
41177c478bd9Sstevel@tonic-gate 	/* good file -- save this lock */
41187c478bd9Sstevel@tonic-gate 	e->e_lockfp = qfp;
41197c478bd9Sstevel@tonic-gate 
41207c478bd9Sstevel@tonic-gate 	/* Just wanted the open file */
41217c478bd9Sstevel@tonic-gate 	if (openonly)
41227c478bd9Sstevel@tonic-gate 		return true;
41237c478bd9Sstevel@tonic-gate 
41247c478bd9Sstevel@tonic-gate 	/* do basic system initialization */
41257c478bd9Sstevel@tonic-gate 	initsys(e);
41267c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
41277c478bd9Sstevel@tonic-gate 
41287c478bd9Sstevel@tonic-gate 	LineNumber = 0;
41297c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_GLOBALERRS;
41307c478bd9Sstevel@tonic-gate 	set_op_mode(MD_QUEUERUN);
41317c478bd9Sstevel@tonic-gate 	ctladdr = NULL;
41327c478bd9Sstevel@tonic-gate 	e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
41337c478bd9Sstevel@tonic-gate 	e->e_dfqgrp = e->e_qgrp;
41347c478bd9Sstevel@tonic-gate 	e->e_dfqdir = e->e_qdir;
41357c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_MACRO
41367c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
41377c478bd9Sstevel@tonic-gate 		  qid_printqueue(e->e_qgrp, e->e_qdir));
41387c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_MACRO */
41397c478bd9Sstevel@tonic-gate 	e->e_dfino = -1;
41407c478bd9Sstevel@tonic-gate 	e->e_msgsize = -1;
4141058561cbSjbeck 	while (bufsize = sizeof(buf),
4142058561cbSjbeck 	       (bp = fgetfolded(buf, &bufsize, qfp)) != NULL)
41437c478bd9Sstevel@tonic-gate 	{
41447c478bd9Sstevel@tonic-gate 		unsigned long qflags;
41457c478bd9Sstevel@tonic-gate 		ADDRESS *q;
41467c478bd9Sstevel@tonic-gate 		int r;
41477c478bd9Sstevel@tonic-gate 		time_t now;
41487c478bd9Sstevel@tonic-gate 		auto char *ep;
41497c478bd9Sstevel@tonic-gate 
41507c478bd9Sstevel@tonic-gate 		if (tTd(40, 4))
41517c478bd9Sstevel@tonic-gate 			sm_dprintf("+++++ %s\n", bp);
41527c478bd9Sstevel@tonic-gate 		if (nomore)
41537c478bd9Sstevel@tonic-gate 		{
41547c478bd9Sstevel@tonic-gate 			/* hack attack */
41557c478bd9Sstevel@tonic-gate   hackattack:
41567c478bd9Sstevel@tonic-gate 			syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
41577c478bd9Sstevel@tonic-gate 			       bp);
41587c478bd9Sstevel@tonic-gate 			err = "bogus queue line";
41597c478bd9Sstevel@tonic-gate 			goto fail;
41607c478bd9Sstevel@tonic-gate 		}
41617c478bd9Sstevel@tonic-gate 		switch (bp[0])
41627c478bd9Sstevel@tonic-gate 		{
41637c478bd9Sstevel@tonic-gate 		  case 'A':		/* AUTH= parameter */
41647c478bd9Sstevel@tonic-gate 			if (!xtextok(&bp[1]))
41657c478bd9Sstevel@tonic-gate 				goto hackattack;
41667c478bd9Sstevel@tonic-gate 			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
41677c478bd9Sstevel@tonic-gate 			break;
41687c478bd9Sstevel@tonic-gate 
41697c478bd9Sstevel@tonic-gate 		  case 'B':		/* body type */
41707c478bd9Sstevel@tonic-gate 			r = check_bodytype(&bp[1]);
41717c478bd9Sstevel@tonic-gate 			if (!BODYTYPE_VALID(r))
41727c478bd9Sstevel@tonic-gate 				goto hackattack;
41737c478bd9Sstevel@tonic-gate 			e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
41747c478bd9Sstevel@tonic-gate 			break;
41757c478bd9Sstevel@tonic-gate 
41767c478bd9Sstevel@tonic-gate 		  case 'C':		/* specify controlling user */
41777c478bd9Sstevel@tonic-gate 			ctladdr = setctluser(&bp[1], qfver, e);
41787c478bd9Sstevel@tonic-gate 			break;
41797c478bd9Sstevel@tonic-gate 
41807c478bd9Sstevel@tonic-gate 		  case 'D':		/* data file name */
41817c478bd9Sstevel@tonic-gate 			/* obsolete -- ignore */
41827c478bd9Sstevel@tonic-gate 			break;
41837c478bd9Sstevel@tonic-gate 
41847c478bd9Sstevel@tonic-gate 		  case 'd':		/* data file directory name */
41857c478bd9Sstevel@tonic-gate 			{
41867c478bd9Sstevel@tonic-gate 				int qgrp, qdir;
41877c478bd9Sstevel@tonic-gate 
41887c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
41897c478bd9Sstevel@tonic-gate 				/* forbid queue groups in MSP? */
41907c478bd9Sstevel@tonic-gate 				if (UseMSP)
41917c478bd9Sstevel@tonic-gate 					goto hackattack;
41927c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
41937c478bd9Sstevel@tonic-gate 				for (qgrp = 0;
41947c478bd9Sstevel@tonic-gate 				     qgrp < NumQueue && Queue[qgrp] != NULL;
41957c478bd9Sstevel@tonic-gate 				     ++qgrp)
41967c478bd9Sstevel@tonic-gate 				{
41977c478bd9Sstevel@tonic-gate 					for (qdir = 0;
41987c478bd9Sstevel@tonic-gate 					     qdir < Queue[qgrp]->qg_numqueues;
41997c478bd9Sstevel@tonic-gate 					     ++qdir)
42007c478bd9Sstevel@tonic-gate 					{
42017c478bd9Sstevel@tonic-gate 						if (strcmp(&bp[1],
42027c478bd9Sstevel@tonic-gate 							   Queue[qgrp]->qg_qpaths[qdir].qp_name)
42037c478bd9Sstevel@tonic-gate 						    == 0)
42047c478bd9Sstevel@tonic-gate 						{
42057c478bd9Sstevel@tonic-gate 							e->e_dfqgrp = qgrp;
42067c478bd9Sstevel@tonic-gate 							e->e_dfqdir = qdir;
42077c478bd9Sstevel@tonic-gate 							goto done;
42087c478bd9Sstevel@tonic-gate 						}
42097c478bd9Sstevel@tonic-gate 					}
42107c478bd9Sstevel@tonic-gate 				}
42117c478bd9Sstevel@tonic-gate 				err = "bogus queue file directory";
42127c478bd9Sstevel@tonic-gate 				goto fail;
42137c478bd9Sstevel@tonic-gate 			  done:
42147c478bd9Sstevel@tonic-gate 				break;
42157c478bd9Sstevel@tonic-gate 			}
42167c478bd9Sstevel@tonic-gate 
42177c478bd9Sstevel@tonic-gate 		  case 'E':		/* specify error recipient */
42187c478bd9Sstevel@tonic-gate 			/* no longer used */
42197c478bd9Sstevel@tonic-gate 			break;
42207c478bd9Sstevel@tonic-gate 
42217c478bd9Sstevel@tonic-gate 		  case 'F':		/* flag bits */
42227c478bd9Sstevel@tonic-gate 			if (strncmp(bp, "From ", 5) == 0)
42237c478bd9Sstevel@tonic-gate 			{
42247c478bd9Sstevel@tonic-gate 				/* we are being spoofed! */
42257c478bd9Sstevel@tonic-gate 				syserr("SECURITY ALERT: bogus qf line %s", bp);
42267c478bd9Sstevel@tonic-gate 				err = "bogus queue line";
42277c478bd9Sstevel@tonic-gate 				goto fail;
42287c478bd9Sstevel@tonic-gate 			}
42297c478bd9Sstevel@tonic-gate 			for (p = &bp[1]; *p != '\0'; p++)
42307c478bd9Sstevel@tonic-gate 			{
42317c478bd9Sstevel@tonic-gate 				switch (*p)
42327c478bd9Sstevel@tonic-gate 				{
42337c478bd9Sstevel@tonic-gate 				  case '8':	/* has 8 bit data */
42347c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_HAS8BIT;
42357c478bd9Sstevel@tonic-gate 					break;
42367c478bd9Sstevel@tonic-gate 
42377c478bd9Sstevel@tonic-gate 				  case 'b':	/* delete Bcc: header */
42387c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_DELETE_BCC;
42397c478bd9Sstevel@tonic-gate 					break;
42407c478bd9Sstevel@tonic-gate 
42417c478bd9Sstevel@tonic-gate 				  case 'd':	/* envelope has DSN RET= */
42427c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_RET_PARAM;
42437c478bd9Sstevel@tonic-gate 					break;
42447c478bd9Sstevel@tonic-gate 
42457c478bd9Sstevel@tonic-gate 				  case 'n':	/* don't return body */
42467c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_NO_BODY_RETN;
42477c478bd9Sstevel@tonic-gate 					break;
42487c478bd9Sstevel@tonic-gate 
42497c478bd9Sstevel@tonic-gate 				  case 'r':	/* response */
42507c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_RESPONSE;
42517c478bd9Sstevel@tonic-gate 					break;
42527c478bd9Sstevel@tonic-gate 
42537c478bd9Sstevel@tonic-gate 				  case 's':	/* split */
42547c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_SPLIT;
42557c478bd9Sstevel@tonic-gate 					break;
42567c478bd9Sstevel@tonic-gate 
42577c478bd9Sstevel@tonic-gate 				  case 'w':	/* warning sent */
42587c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_WARNING;
42597c478bd9Sstevel@tonic-gate 					break;
42607c478bd9Sstevel@tonic-gate 				}
42617c478bd9Sstevel@tonic-gate 			}
42627c478bd9Sstevel@tonic-gate 			break;
42637c478bd9Sstevel@tonic-gate 
42647c478bd9Sstevel@tonic-gate 		  case 'q':		/* quarantine reason */
42657c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
42667c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
42677c478bd9Sstevel@tonic-gate 				  macid("{quarantine}"), e->e_quarmsg);
42687c478bd9Sstevel@tonic-gate 			break;
42697c478bd9Sstevel@tonic-gate 
42707c478bd9Sstevel@tonic-gate 		  case 'H':		/* header */
42717c478bd9Sstevel@tonic-gate 
42727c478bd9Sstevel@tonic-gate 			/*
42737c478bd9Sstevel@tonic-gate 			**  count size before chompheader() destroys the line.
42747c478bd9Sstevel@tonic-gate 			**  this isn't accurate due to macro expansion, but
42757c478bd9Sstevel@tonic-gate 			**  better than before. "-3" to skip H?? at least.
42767c478bd9Sstevel@tonic-gate 			*/
42777c478bd9Sstevel@tonic-gate 
42787c478bd9Sstevel@tonic-gate 			hdrsize += strlen(bp) - 3;
42797c478bd9Sstevel@tonic-gate 			(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
42807c478bd9Sstevel@tonic-gate 			break;
42817c478bd9Sstevel@tonic-gate 
42827c478bd9Sstevel@tonic-gate 		  case 'I':		/* data file's inode number */
42837c478bd9Sstevel@tonic-gate 			/* regenerated below */
42847c478bd9Sstevel@tonic-gate 			break;
42857c478bd9Sstevel@tonic-gate 
42867c478bd9Sstevel@tonic-gate 		  case 'K':		/* time of last delivery attempt */
42877c478bd9Sstevel@tonic-gate 			e->e_dtime = atol(&buf[1]);
42887c478bd9Sstevel@tonic-gate 			break;
42897c478bd9Sstevel@tonic-gate 
42907c478bd9Sstevel@tonic-gate 		  case 'L':		/* Solaris Content-Length: */
42917c478bd9Sstevel@tonic-gate 		  case 'M':		/* message */
42927c478bd9Sstevel@tonic-gate 			/* ignore this; we want a new message next time */
42937c478bd9Sstevel@tonic-gate 			break;
42947c478bd9Sstevel@tonic-gate 
42957c478bd9Sstevel@tonic-gate 		  case 'N':		/* number of delivery attempts */
42967c478bd9Sstevel@tonic-gate 			e->e_ntries = atoi(&buf[1]);
42977c478bd9Sstevel@tonic-gate 
42987c478bd9Sstevel@tonic-gate 			/* if this has been tried recently, let it be */
42997c478bd9Sstevel@tonic-gate 			now = curtime();
43007c478bd9Sstevel@tonic-gate 			if (e->e_ntries > 0 && e->e_dtime <= now &&
43017c478bd9Sstevel@tonic-gate 			    now < e->e_dtime + MinQueueAge)
43027c478bd9Sstevel@tonic-gate 			{
43037c478bd9Sstevel@tonic-gate 				char *howlong;
43047c478bd9Sstevel@tonic-gate 
43057c478bd9Sstevel@tonic-gate 				howlong = pintvl(now - e->e_dtime, true);
43067c478bd9Sstevel@tonic-gate 				if (Verbose)
43077c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
43087c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
43097c478bd9Sstevel@tonic-gate 							     "%s: too young (%s)\n",
43107c478bd9Sstevel@tonic-gate 							     e->e_id, howlong);
43117c478bd9Sstevel@tonic-gate 				if (tTd(40, 8))
43127c478bd9Sstevel@tonic-gate 					sm_dprintf("%s: too young (%s)\n",
43137c478bd9Sstevel@tonic-gate 						e->e_id, howlong);
43147c478bd9Sstevel@tonic-gate 				if (LogLevel > 19)
43157c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_DEBUG, e->e_id,
43167c478bd9Sstevel@tonic-gate 						  "too young (%s)",
43177c478bd9Sstevel@tonic-gate 						  howlong);
43187c478bd9Sstevel@tonic-gate 				e->e_id = NULL;
43197c478bd9Sstevel@tonic-gate 				unlockqueue(e);
4320058561cbSjbeck 				if (bp != buf)
4321058561cbSjbeck 					sm_free(bp);
43227c478bd9Sstevel@tonic-gate 				return false;
43237c478bd9Sstevel@tonic-gate 			}
43247c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP,
43257c478bd9Sstevel@tonic-gate 				macid("{ntries}"), &buf[1]);
43267c478bd9Sstevel@tonic-gate 
43277c478bd9Sstevel@tonic-gate #if NAMED_BIND
43287c478bd9Sstevel@tonic-gate 			/* adjust BIND parameters immediately */
43297c478bd9Sstevel@tonic-gate 			if (e->e_ntries == 0)
43307c478bd9Sstevel@tonic-gate 			{
43317c478bd9Sstevel@tonic-gate 				_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
43327c478bd9Sstevel@tonic-gate 				_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
43337c478bd9Sstevel@tonic-gate 			}
43347c478bd9Sstevel@tonic-gate 			else
43357c478bd9Sstevel@tonic-gate 			{
43367c478bd9Sstevel@tonic-gate 				_res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
43377c478bd9Sstevel@tonic-gate 				_res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
43387c478bd9Sstevel@tonic-gate 			}
43397c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
43407c478bd9Sstevel@tonic-gate 			break;
43417c478bd9Sstevel@tonic-gate 
43427c478bd9Sstevel@tonic-gate 		  case 'P':		/* message priority */
43437c478bd9Sstevel@tonic-gate 			e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
43447c478bd9Sstevel@tonic-gate 			break;
43457c478bd9Sstevel@tonic-gate 
43467c478bd9Sstevel@tonic-gate 		  case 'Q':		/* original recipient */
43477c478bd9Sstevel@tonic-gate 			orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
43487c478bd9Sstevel@tonic-gate 			break;
43497c478bd9Sstevel@tonic-gate 
43507c478bd9Sstevel@tonic-gate 		  case 'r':		/* final recipient */
43517c478bd9Sstevel@tonic-gate 			frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
43527c478bd9Sstevel@tonic-gate 			break;
43537c478bd9Sstevel@tonic-gate 
43547c478bd9Sstevel@tonic-gate 		  case 'R':		/* specify recipient */
43557c478bd9Sstevel@tonic-gate 			p = bp;
43567c478bd9Sstevel@tonic-gate 			qflags = 0;
43577c478bd9Sstevel@tonic-gate 			if (qfver >= 1)
43587c478bd9Sstevel@tonic-gate 			{
43597c478bd9Sstevel@tonic-gate 				/* get flag bits */
43607c478bd9Sstevel@tonic-gate 				while (*++p != '\0' && *p != ':')
43617c478bd9Sstevel@tonic-gate 				{
43627c478bd9Sstevel@tonic-gate 					switch (*p)
43637c478bd9Sstevel@tonic-gate 					{
43647c478bd9Sstevel@tonic-gate 					  case 'N':
43657c478bd9Sstevel@tonic-gate 						qflags |= QHASNOTIFY;
43667c478bd9Sstevel@tonic-gate 						break;
43677c478bd9Sstevel@tonic-gate 
43687c478bd9Sstevel@tonic-gate 					  case 'S':
43697c478bd9Sstevel@tonic-gate 						qflags |= QPINGONSUCCESS;
43707c478bd9Sstevel@tonic-gate 						break;
43717c478bd9Sstevel@tonic-gate 
43727c478bd9Sstevel@tonic-gate 					  case 'F':
43737c478bd9Sstevel@tonic-gate 						qflags |= QPINGONFAILURE;
43747c478bd9Sstevel@tonic-gate 						break;
43757c478bd9Sstevel@tonic-gate 
43767c478bd9Sstevel@tonic-gate 					  case 'D':
43777c478bd9Sstevel@tonic-gate 						qflags |= QPINGONDELAY;
43787c478bd9Sstevel@tonic-gate 						break;
43797c478bd9Sstevel@tonic-gate 
43807c478bd9Sstevel@tonic-gate 					  case 'P':
43817c478bd9Sstevel@tonic-gate 						qflags |= QPRIMARY;
43827c478bd9Sstevel@tonic-gate 						break;
43837c478bd9Sstevel@tonic-gate 
43847c478bd9Sstevel@tonic-gate 					  case 'A':
43857c478bd9Sstevel@tonic-gate 						if (ctladdr != NULL)
43867c478bd9Sstevel@tonic-gate 							ctladdr->q_flags |= QALIAS;
43877c478bd9Sstevel@tonic-gate 						break;
43887c478bd9Sstevel@tonic-gate 
43897c478bd9Sstevel@tonic-gate 					  default: /* ignore or complain? */
43907c478bd9Sstevel@tonic-gate 						break;
43917c478bd9Sstevel@tonic-gate 					}
43927c478bd9Sstevel@tonic-gate 				}
43937c478bd9Sstevel@tonic-gate 			}
43947c478bd9Sstevel@tonic-gate 			else
43957c478bd9Sstevel@tonic-gate 				qflags |= QPRIMARY;
43967c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
43977c478bd9Sstevel@tonic-gate 				"e r");
43987c478bd9Sstevel@tonic-gate 			if (*p != '\0')
43997c478bd9Sstevel@tonic-gate 				q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0',
44007c478bd9Sstevel@tonic-gate 						NULL, e, true);
44017c478bd9Sstevel@tonic-gate 			else
44027c478bd9Sstevel@tonic-gate 				q = NULL;
44037c478bd9Sstevel@tonic-gate 			if (q != NULL)
44047c478bd9Sstevel@tonic-gate 			{
44057c478bd9Sstevel@tonic-gate 				/* make sure we keep the current qgrp */
44067c478bd9Sstevel@tonic-gate 				if (ISVALIDQGRP(e->e_qgrp))
44077c478bd9Sstevel@tonic-gate 					q->q_qgrp = e->e_qgrp;
44087c478bd9Sstevel@tonic-gate 				q->q_alias = ctladdr;
44097c478bd9Sstevel@tonic-gate 				if (qfver >= 1)
44107c478bd9Sstevel@tonic-gate 					q->q_flags &= ~Q_PINGFLAGS;
44117c478bd9Sstevel@tonic-gate 				q->q_flags |= qflags;
44127c478bd9Sstevel@tonic-gate 				q->q_finalrcpt = frcpt;
44137c478bd9Sstevel@tonic-gate 				q->q_orcpt = orcpt;
44147c478bd9Sstevel@tonic-gate 				(void) recipient(q, &e->e_sendqueue, 0, e);
44157c478bd9Sstevel@tonic-gate 			}
44167c478bd9Sstevel@tonic-gate 			frcpt = NULL;
44177c478bd9Sstevel@tonic-gate 			orcpt = NULL;
44187c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
44197c478bd9Sstevel@tonic-gate 				NULL);
44207c478bd9Sstevel@tonic-gate 			break;
44217c478bd9Sstevel@tonic-gate 
44227c478bd9Sstevel@tonic-gate 		  case 'S':		/* sender */
44237c478bd9Sstevel@tonic-gate 			setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
44247c478bd9Sstevel@tonic-gate 				  e, NULL, '\0', true);
44257c478bd9Sstevel@tonic-gate 			break;
44267c478bd9Sstevel@tonic-gate 
44277c478bd9Sstevel@tonic-gate 		  case 'T':		/* init time */
44287c478bd9Sstevel@tonic-gate 			e->e_ctime = atol(&bp[1]);
44297c478bd9Sstevel@tonic-gate 			break;
44307c478bd9Sstevel@tonic-gate 
44317c478bd9Sstevel@tonic-gate 		  case 'V':		/* queue file version number */
44327c478bd9Sstevel@tonic-gate 			qfver = atoi(&bp[1]);
44337c478bd9Sstevel@tonic-gate 			if (qfver <= QF_VERSION)
44347c478bd9Sstevel@tonic-gate 				break;
44357c478bd9Sstevel@tonic-gate 			syserr("Version number in queue file (%d) greater than max (%d)",
44367c478bd9Sstevel@tonic-gate 				qfver, QF_VERSION);
44377c478bd9Sstevel@tonic-gate 			err = "unsupported queue file version";
44387c478bd9Sstevel@tonic-gate 			goto fail;
44397c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
44407c478bd9Sstevel@tonic-gate 			break;
44417c478bd9Sstevel@tonic-gate 
44427c478bd9Sstevel@tonic-gate 		  case 'Z':		/* original envelope id from ESMTP */
44437c478bd9Sstevel@tonic-gate 			e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
44447c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
44457c478bd9Sstevel@tonic-gate 				macid("{dsn_envid}"), e->e_envid);
44467c478bd9Sstevel@tonic-gate 			break;
44477c478bd9Sstevel@tonic-gate 
44487c478bd9Sstevel@tonic-gate 		  case '!':		/* deliver by */
44497c478bd9Sstevel@tonic-gate 
44507c478bd9Sstevel@tonic-gate 			/* format: flag (1 char) space long-integer */
44517c478bd9Sstevel@tonic-gate 			e->e_dlvr_flag = buf[1];
44527c478bd9Sstevel@tonic-gate 			e->e_deliver_by = strtol(&buf[3], NULL, 10);
44537c478bd9Sstevel@tonic-gate 
44547c478bd9Sstevel@tonic-gate 		  case '$':		/* define macro */
44557c478bd9Sstevel@tonic-gate 			{
44567c478bd9Sstevel@tonic-gate 				char *p;
44577c478bd9Sstevel@tonic-gate 
44587c478bd9Sstevel@tonic-gate 				/* XXX elimate p? */
44597c478bd9Sstevel@tonic-gate 				r = macid_parse(&bp[1], &ep);
44607c478bd9Sstevel@tonic-gate 				if (r == 0)
44617c478bd9Sstevel@tonic-gate 					break;
44627c478bd9Sstevel@tonic-gate 				p = sm_rpool_strdup_x(e->e_rpool, ep);
44637c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM, r, p);
44647c478bd9Sstevel@tonic-gate 			}
44657c478bd9Sstevel@tonic-gate 			break;
44667c478bd9Sstevel@tonic-gate 
44677c478bd9Sstevel@tonic-gate 		  case '.':		/* terminate file */
44687c478bd9Sstevel@tonic-gate 			nomore = true;
44697c478bd9Sstevel@tonic-gate 			break;
44707c478bd9Sstevel@tonic-gate 
44717c478bd9Sstevel@tonic-gate #if _FFR_QUEUEDELAY
44727c478bd9Sstevel@tonic-gate 		  case 'G':
44737c478bd9Sstevel@tonic-gate 		  case 'Y':
44747c478bd9Sstevel@tonic-gate 
44757c478bd9Sstevel@tonic-gate 			/*
44767c478bd9Sstevel@tonic-gate 			**  Maintain backward compatibility for
44777c478bd9Sstevel@tonic-gate 			**  users who defined _FFR_QUEUEDELAY in
44787c478bd9Sstevel@tonic-gate 			**  previous releases.  Remove this
44797c478bd9Sstevel@tonic-gate 			**  code in 8.14 or 8.15.
44807c478bd9Sstevel@tonic-gate 			*/
44817c478bd9Sstevel@tonic-gate 
44827c478bd9Sstevel@tonic-gate 			if (qfver == 5 || qfver == 7)
44837c478bd9Sstevel@tonic-gate 				break;
44847c478bd9Sstevel@tonic-gate 
44857c478bd9Sstevel@tonic-gate 			/* If not qfver 5 or 7, then 'G' or 'Y' is invalid */
44867c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
44877c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUEDELAY */
44887c478bd9Sstevel@tonic-gate 
44897c478bd9Sstevel@tonic-gate 		  default:
44907c478bd9Sstevel@tonic-gate 			syserr("readqf: %s: line %d: bad line \"%s\"",
44917c478bd9Sstevel@tonic-gate 				qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
44927c478bd9Sstevel@tonic-gate 			err = "unrecognized line";
44937c478bd9Sstevel@tonic-gate 			goto fail;
44947c478bd9Sstevel@tonic-gate 		}
44957c478bd9Sstevel@tonic-gate 
44967c478bd9Sstevel@tonic-gate 		if (bp != buf)
4497058561cbSjbeck 			SM_FREE(bp);
44987c478bd9Sstevel@tonic-gate 	}
44997c478bd9Sstevel@tonic-gate 
45007c478bd9Sstevel@tonic-gate 	/*
45017c478bd9Sstevel@tonic-gate 	**  If we haven't read any lines, this queue file is empty.
45027c478bd9Sstevel@tonic-gate 	**  Arrange to remove it without referencing any null pointers.
45037c478bd9Sstevel@tonic-gate 	*/
45047c478bd9Sstevel@tonic-gate 
45057c478bd9Sstevel@tonic-gate 	if (LineNumber == 0)
45067c478bd9Sstevel@tonic-gate 	{
45077c478bd9Sstevel@tonic-gate 		errno = 0;
45087c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
45097c478bd9Sstevel@tonic-gate 		return true;
45107c478bd9Sstevel@tonic-gate 	}
45117c478bd9Sstevel@tonic-gate 
45127c478bd9Sstevel@tonic-gate 	/* Check to make sure we have a complete queue file read */
45137c478bd9Sstevel@tonic-gate 	if (!nomore)
45147c478bd9Sstevel@tonic-gate 	{
45157c478bd9Sstevel@tonic-gate 		syserr("readqf: %s: incomplete queue file read", qf);
45167c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
45177c478bd9Sstevel@tonic-gate 		return false;
45187c478bd9Sstevel@tonic-gate 	}
45194aac33d3Sjbeck 
45204aac33d3Sjbeck #if _FFR_QF_PARANOIA
45214aac33d3Sjbeck 	/* Check to make sure key fields were read */
45224aac33d3Sjbeck 	if (e->e_from.q_mailer == NULL)
45234aac33d3Sjbeck 	{
45244aac33d3Sjbeck 		syserr("readqf: %s: sender not specified in queue file", qf);
45254aac33d3Sjbeck 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
45264aac33d3Sjbeck 		return false;
45274aac33d3Sjbeck 	}
45284aac33d3Sjbeck 	/* other checks? */
45294aac33d3Sjbeck #endif /* _FFR_QF_PARANOIA */
45307c478bd9Sstevel@tonic-gate 
45317c478bd9Sstevel@tonic-gate 	/* possibly set ${dsn_ret} macro */
45327c478bd9Sstevel@tonic-gate 	if (bitset(EF_RET_PARAM, e->e_flags))
45337c478bd9Sstevel@tonic-gate 	{
45347c478bd9Sstevel@tonic-gate 		if (bitset(EF_NO_BODY_RETN, e->e_flags))
45357c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
45367c478bd9Sstevel@tonic-gate 				macid("{dsn_ret}"), "hdrs");
45377c478bd9Sstevel@tonic-gate 		else
45387c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
45397c478bd9Sstevel@tonic-gate 				macid("{dsn_ret}"), "full");
45407c478bd9Sstevel@tonic-gate 	}
45417c478bd9Sstevel@tonic-gate 
45427c478bd9Sstevel@tonic-gate 	/*
45437c478bd9Sstevel@tonic-gate 	**  Arrange to read the data file.
45447c478bd9Sstevel@tonic-gate 	*/
45457c478bd9Sstevel@tonic-gate 
45467c478bd9Sstevel@tonic-gate 	p = queuename(e, DATAFL_LETTER);
45477c478bd9Sstevel@tonic-gate 	e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY_B,
45487c478bd9Sstevel@tonic-gate 			      NULL);
45497c478bd9Sstevel@tonic-gate 	if (e->e_dfp == NULL)
45507c478bd9Sstevel@tonic-gate 	{
45517c478bd9Sstevel@tonic-gate 		syserr("readqf: cannot open %s", p);
45527c478bd9Sstevel@tonic-gate 	}
45537c478bd9Sstevel@tonic-gate 	else
45547c478bd9Sstevel@tonic-gate 	{
45557c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
45567c478bd9Sstevel@tonic-gate 		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
45577c478bd9Sstevel@tonic-gate 		    >= 0)
45587c478bd9Sstevel@tonic-gate 		{
45597c478bd9Sstevel@tonic-gate 			e->e_msgsize = st.st_size + hdrsize;
45607c478bd9Sstevel@tonic-gate 			e->e_dfdev = st.st_dev;
45617c478bd9Sstevel@tonic-gate 			e->e_dfino = ST_INODE(st);
4562058561cbSjbeck 			(void) sm_snprintf(buf, sizeof(buf), "%ld",
45637c478bd9Sstevel@tonic-gate 					   e->e_msgsize);
45647c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"),
45657c478bd9Sstevel@tonic-gate 				  buf);
45667c478bd9Sstevel@tonic-gate 		}
45677c478bd9Sstevel@tonic-gate 	}
45687c478bd9Sstevel@tonic-gate 
45697c478bd9Sstevel@tonic-gate 	return true;
45707c478bd9Sstevel@tonic-gate 
45717c478bd9Sstevel@tonic-gate   fail:
45727c478bd9Sstevel@tonic-gate 	/*
45737c478bd9Sstevel@tonic-gate 	**  There was some error reading the qf file (reason is in err var.)
45747c478bd9Sstevel@tonic-gate 	**  Cleanup:
45757c478bd9Sstevel@tonic-gate 	**	close file; clear e_lockfp since it is the same as qfp,
45767c478bd9Sstevel@tonic-gate 	**	hence it is invalid (as file) after qfp is closed;
45777c478bd9Sstevel@tonic-gate 	**	the qf file is on disk, so set the flag to avoid calling
45787c478bd9Sstevel@tonic-gate 	**	queueup() with bogus data.
45797c478bd9Sstevel@tonic-gate 	*/
45807c478bd9Sstevel@tonic-gate 
4581058561cbSjbeck 	if (bp != buf)
4582058561cbSjbeck 		SM_FREE(bp);
45837c478bd9Sstevel@tonic-gate 	if (qfp != NULL)
45847c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
45857c478bd9Sstevel@tonic-gate 	e->e_lockfp = NULL;
45867c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_INQUEUE;
45877c478bd9Sstevel@tonic-gate 	loseqfile(e, err);
45887c478bd9Sstevel@tonic-gate 	return false;
45897c478bd9Sstevel@tonic-gate }
45907c478bd9Sstevel@tonic-gate /*
45917c478bd9Sstevel@tonic-gate **  PRTSTR -- print a string, "unprintable" characters are shown as \oct
45927c478bd9Sstevel@tonic-gate **
45937c478bd9Sstevel@tonic-gate **	Parameters:
45947c478bd9Sstevel@tonic-gate **		s -- string to print
45957c478bd9Sstevel@tonic-gate **		ml -- maximum length of output
45967c478bd9Sstevel@tonic-gate **
45977c478bd9Sstevel@tonic-gate **	Returns:
45987c478bd9Sstevel@tonic-gate **		number of entries
45997c478bd9Sstevel@tonic-gate **
46007c478bd9Sstevel@tonic-gate **	Side Effects:
46017c478bd9Sstevel@tonic-gate **		Prints a string on stdout.
46027c478bd9Sstevel@tonic-gate */
46037c478bd9Sstevel@tonic-gate 
4604058561cbSjbeck static void prtstr __P((char *, int));
4605058561cbSjbeck 
46067c478bd9Sstevel@tonic-gate static void
46077c478bd9Sstevel@tonic-gate prtstr(s, ml)
46087c478bd9Sstevel@tonic-gate 	char *s;
46097c478bd9Sstevel@tonic-gate 	int ml;
46107c478bd9Sstevel@tonic-gate {
46117c478bd9Sstevel@tonic-gate 	int c;
46127c478bd9Sstevel@tonic-gate 
46137c478bd9Sstevel@tonic-gate 	if (s == NULL)
46147c478bd9Sstevel@tonic-gate 		return;
46157c478bd9Sstevel@tonic-gate 	while (ml-- > 0 && ((c = *s++) != '\0'))
46167c478bd9Sstevel@tonic-gate 	{
46177c478bd9Sstevel@tonic-gate 		if (c == '\\')
46187c478bd9Sstevel@tonic-gate 		{
46197c478bd9Sstevel@tonic-gate 			if (ml-- > 0)
46207c478bd9Sstevel@tonic-gate 			{
46217c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46227c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46237c478bd9Sstevel@tonic-gate 			}
46247c478bd9Sstevel@tonic-gate 		}
46257c478bd9Sstevel@tonic-gate 		else if (isascii(c) && isprint(c))
46267c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46277c478bd9Sstevel@tonic-gate 		else
46287c478bd9Sstevel@tonic-gate 		{
46297c478bd9Sstevel@tonic-gate 			if ((ml -= 3) > 0)
46307c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
46317c478bd9Sstevel@tonic-gate 						     "\\%03o", c & 0xFF);
46327c478bd9Sstevel@tonic-gate 		}
46337c478bd9Sstevel@tonic-gate 	}
46347c478bd9Sstevel@tonic-gate }
46357c478bd9Sstevel@tonic-gate /*
46367c478bd9Sstevel@tonic-gate **  PRINTNQE -- print out number of entries in the mail queue
46377c478bd9Sstevel@tonic-gate **
46387c478bd9Sstevel@tonic-gate **	Parameters:
46397c478bd9Sstevel@tonic-gate **		out -- output file pointer.
46407c478bd9Sstevel@tonic-gate **		prefix -- string to output in front of each line.
46417c478bd9Sstevel@tonic-gate **
46427c478bd9Sstevel@tonic-gate **	Returns:
46437c478bd9Sstevel@tonic-gate **		none.
46447c478bd9Sstevel@tonic-gate */
46457c478bd9Sstevel@tonic-gate 
46467c478bd9Sstevel@tonic-gate void
46477c478bd9Sstevel@tonic-gate printnqe(out, prefix)
46487c478bd9Sstevel@tonic-gate 	SM_FILE_T *out;
46497c478bd9Sstevel@tonic-gate 	char *prefix;
46507c478bd9Sstevel@tonic-gate {
46517c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
46527c478bd9Sstevel@tonic-gate 	int i, k = 0, nrequests = 0;
46537c478bd9Sstevel@tonic-gate 	bool unknown = false;
46547c478bd9Sstevel@tonic-gate 
46557c478bd9Sstevel@tonic-gate 	if (ShmId == SM_SHM_NO_ID)
46567c478bd9Sstevel@tonic-gate 	{
46577c478bd9Sstevel@tonic-gate 		if (prefix == NULL)
46587c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46597c478bd9Sstevel@tonic-gate 					"Data unavailable: shared memory not updated\n");
46607c478bd9Sstevel@tonic-gate 		else
46617c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46627c478bd9Sstevel@tonic-gate 					"%sNOTCONFIGURED:-1\r\n", prefix);
46637c478bd9Sstevel@tonic-gate 		return;
46647c478bd9Sstevel@tonic-gate 	}
46657c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
46667c478bd9Sstevel@tonic-gate 	{
46677c478bd9Sstevel@tonic-gate 		int j;
46687c478bd9Sstevel@tonic-gate 
46697c478bd9Sstevel@tonic-gate 		k++;
46707c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; j++)
46717c478bd9Sstevel@tonic-gate 		{
46727c478bd9Sstevel@tonic-gate 			int n;
46737c478bd9Sstevel@tonic-gate 
46747c478bd9Sstevel@tonic-gate 			if (StopRequest)
46757c478bd9Sstevel@tonic-gate 				stop_sendmail();
46767c478bd9Sstevel@tonic-gate 
46777c478bd9Sstevel@tonic-gate 			n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
46787c478bd9Sstevel@tonic-gate 			if (prefix != NULL)
46797c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46807c478bd9Sstevel@tonic-gate 					"%s%s:%d\r\n",
46817c478bd9Sstevel@tonic-gate 					prefix, qid_printqueue(i, j), n);
46827c478bd9Sstevel@tonic-gate 			else if (n < 0)
46837c478bd9Sstevel@tonic-gate 			{
46847c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46857c478bd9Sstevel@tonic-gate 					"%s: unknown number of entries\n",
46867c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j));
46877c478bd9Sstevel@tonic-gate 				unknown = true;
46887c478bd9Sstevel@tonic-gate 			}
46897c478bd9Sstevel@tonic-gate 			else if (n == 0)
46907c478bd9Sstevel@tonic-gate 			{
46917c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46927c478bd9Sstevel@tonic-gate 					"%s is empty\n",
46937c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j));
46947c478bd9Sstevel@tonic-gate 			}
46957c478bd9Sstevel@tonic-gate 			else if (n > 0)
46967c478bd9Sstevel@tonic-gate 			{
46977c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46987c478bd9Sstevel@tonic-gate 					"%s: entries=%d\n",
46997c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j), n);
47007c478bd9Sstevel@tonic-gate 				nrequests += n;
47017c478bd9Sstevel@tonic-gate 				k++;
47027c478bd9Sstevel@tonic-gate 			}
47037c478bd9Sstevel@tonic-gate 		}
47047c478bd9Sstevel@tonic-gate 	}
47057c478bd9Sstevel@tonic-gate 	if (prefix == NULL && k > 1)
47067c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47077c478bd9Sstevel@tonic-gate 				     "\t\tTotal requests: %d%s\n",
47087c478bd9Sstevel@tonic-gate 				     nrequests, unknown ? " (about)" : "");
47097c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
47107c478bd9Sstevel@tonic-gate 	if (prefix == NULL)
47117c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47127c478bd9Sstevel@tonic-gate 			     "Data unavailable without shared memory support\n");
47137c478bd9Sstevel@tonic-gate 	else
47147c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47157c478bd9Sstevel@tonic-gate 			     "%sNOTAVAILABLE:-1\r\n", prefix);
47167c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
47177c478bd9Sstevel@tonic-gate }
47187c478bd9Sstevel@tonic-gate /*
47197c478bd9Sstevel@tonic-gate **  PRINTQUEUE -- print out a representation of the mail queue
47207c478bd9Sstevel@tonic-gate **
47217c478bd9Sstevel@tonic-gate **	Parameters:
47227c478bd9Sstevel@tonic-gate **		none.
47237c478bd9Sstevel@tonic-gate **
47247c478bd9Sstevel@tonic-gate **	Returns:
47257c478bd9Sstevel@tonic-gate **		none.
47267c478bd9Sstevel@tonic-gate **
47277c478bd9Sstevel@tonic-gate **	Side Effects:
47287c478bd9Sstevel@tonic-gate **		Prints a listing of the mail queue on the standard output.
47297c478bd9Sstevel@tonic-gate */
47307c478bd9Sstevel@tonic-gate 
47317c478bd9Sstevel@tonic-gate void
47327c478bd9Sstevel@tonic-gate printqueue()
47337c478bd9Sstevel@tonic-gate {
47347c478bd9Sstevel@tonic-gate 	int i, k = 0, nrequests = 0;
47357c478bd9Sstevel@tonic-gate 
47367c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
47377c478bd9Sstevel@tonic-gate 	{
47387c478bd9Sstevel@tonic-gate 		int j;
47397c478bd9Sstevel@tonic-gate 
47407c478bd9Sstevel@tonic-gate 		k++;
47417c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; j++)
47427c478bd9Sstevel@tonic-gate 		{
47437c478bd9Sstevel@tonic-gate 			if (StopRequest)
47447c478bd9Sstevel@tonic-gate 				stop_sendmail();
47457c478bd9Sstevel@tonic-gate 			nrequests += print_single_queue(i, j);
47467c478bd9Sstevel@tonic-gate 			k++;
47477c478bd9Sstevel@tonic-gate 		}
47487c478bd9Sstevel@tonic-gate 	}
47497c478bd9Sstevel@tonic-gate 	if (k > 1)
47507c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
47517c478bd9Sstevel@tonic-gate 				     "\t\tTotal requests: %d\n",
47527c478bd9Sstevel@tonic-gate 				     nrequests);
47537c478bd9Sstevel@tonic-gate }
47547c478bd9Sstevel@tonic-gate /*
47557c478bd9Sstevel@tonic-gate **  PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
47567c478bd9Sstevel@tonic-gate **
47577c478bd9Sstevel@tonic-gate **	Parameters:
47587c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group.
47597c478bd9Sstevel@tonic-gate **		qdir -- the queue directory.
47607c478bd9Sstevel@tonic-gate **
47617c478bd9Sstevel@tonic-gate **	Returns:
47627c478bd9Sstevel@tonic-gate **		number of requests in mail queue.
47637c478bd9Sstevel@tonic-gate **
47647c478bd9Sstevel@tonic-gate **	Side Effects:
47657c478bd9Sstevel@tonic-gate **		Prints a listing of the mail queue on the standard output.
47667c478bd9Sstevel@tonic-gate */
47677c478bd9Sstevel@tonic-gate 
47687c478bd9Sstevel@tonic-gate int
47697c478bd9Sstevel@tonic-gate print_single_queue(qgrp, qdir)
47707c478bd9Sstevel@tonic-gate 	int qgrp;
47717c478bd9Sstevel@tonic-gate 	int qdir;
47727c478bd9Sstevel@tonic-gate {
47737c478bd9Sstevel@tonic-gate 	register WORK *w;
47747c478bd9Sstevel@tonic-gate 	SM_FILE_T *f;
47757c478bd9Sstevel@tonic-gate 	int nrequests;
47767c478bd9Sstevel@tonic-gate 	char qd[MAXPATHLEN];
47777c478bd9Sstevel@tonic-gate 	char qddf[MAXPATHLEN];
47787c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
47797c478bd9Sstevel@tonic-gate 
47807c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
47817c478bd9Sstevel@tonic-gate 	{
4782058561cbSjbeck 		(void) sm_strlcpy(qd, ".", sizeof(qd));
4783058561cbSjbeck 		(void) sm_strlcpy(qddf, ".", sizeof(qddf));
47847c478bd9Sstevel@tonic-gate 	}
47857c478bd9Sstevel@tonic-gate 	else
47867c478bd9Sstevel@tonic-gate 	{
4787058561cbSjbeck 		(void) sm_strlcpyn(qd, sizeof(qd), 2,
47887c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
47897c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBQF,
47907c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
47917c478bd9Sstevel@tonic-gate 					? "/qf" : ""));
4792058561cbSjbeck 		(void) sm_strlcpyn(qddf, sizeof(qddf), 2,
47937c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
47947c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBDF,
47957c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
47967c478bd9Sstevel@tonic-gate 					? "/df" : ""));
47977c478bd9Sstevel@tonic-gate 	}
47987c478bd9Sstevel@tonic-gate 
47997c478bd9Sstevel@tonic-gate 	/*
48007c478bd9Sstevel@tonic-gate 	**  Check for permission to print the queue
48017c478bd9Sstevel@tonic-gate 	*/
48027c478bd9Sstevel@tonic-gate 
48037c478bd9Sstevel@tonic-gate 	if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
48047c478bd9Sstevel@tonic-gate 	{
48057c478bd9Sstevel@tonic-gate 		struct stat st;
48067c478bd9Sstevel@tonic-gate #ifdef NGROUPS_MAX
48077c478bd9Sstevel@tonic-gate 		int n;
48087c478bd9Sstevel@tonic-gate 		extern GIDSET_T InitialGidSet[NGROUPS_MAX];
48097c478bd9Sstevel@tonic-gate #endif /* NGROUPS_MAX */
48107c478bd9Sstevel@tonic-gate 
48117c478bd9Sstevel@tonic-gate 		if (stat(qd, &st) < 0)
48127c478bd9Sstevel@tonic-gate 		{
48137c478bd9Sstevel@tonic-gate 			syserr("Cannot stat %s",
48147c478bd9Sstevel@tonic-gate 				qid_printqueue(qgrp, qdir));
48157c478bd9Sstevel@tonic-gate 			return 0;
48167c478bd9Sstevel@tonic-gate 		}
48177c478bd9Sstevel@tonic-gate #ifdef NGROUPS_MAX
48187c478bd9Sstevel@tonic-gate 		n = NGROUPS_MAX;
48197c478bd9Sstevel@tonic-gate 		while (--n >= 0)
48207c478bd9Sstevel@tonic-gate 		{
48217c478bd9Sstevel@tonic-gate 			if (InitialGidSet[n] == st.st_gid)
48227c478bd9Sstevel@tonic-gate 				break;
48237c478bd9Sstevel@tonic-gate 		}
48247c478bd9Sstevel@tonic-gate 		if (n < 0 && RealGid != st.st_gid)
48257c478bd9Sstevel@tonic-gate #else /* NGROUPS_MAX */
48267c478bd9Sstevel@tonic-gate 		if (RealGid != st.st_gid)
48277c478bd9Sstevel@tonic-gate #endif /* NGROUPS_MAX */
48287c478bd9Sstevel@tonic-gate 		{
48297c478bd9Sstevel@tonic-gate 			usrerr("510 You are not permitted to see the queue");
48307c478bd9Sstevel@tonic-gate 			setstat(EX_NOPERM);
48317c478bd9Sstevel@tonic-gate 			return 0;
48327c478bd9Sstevel@tonic-gate 		}
48337c478bd9Sstevel@tonic-gate 	}
48347c478bd9Sstevel@tonic-gate 
48357c478bd9Sstevel@tonic-gate 	/*
48367c478bd9Sstevel@tonic-gate 	**  Read and order the queue.
48377c478bd9Sstevel@tonic-gate 	*/
48387c478bd9Sstevel@tonic-gate 
48397c478bd9Sstevel@tonic-gate 	nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
48407c478bd9Sstevel@tonic-gate 	(void) sortq(Queue[qgrp]->qg_maxlist);
48417c478bd9Sstevel@tonic-gate 
48427c478bd9Sstevel@tonic-gate 	/*
48437c478bd9Sstevel@tonic-gate 	**  Print the work list that we have read.
48447c478bd9Sstevel@tonic-gate 	*/
48457c478bd9Sstevel@tonic-gate 
48467c478bd9Sstevel@tonic-gate 	/* first see if there is anything */
48477c478bd9Sstevel@tonic-gate 	if (nrequests <= 0)
48487c478bd9Sstevel@tonic-gate 	{
48497c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
48507c478bd9Sstevel@tonic-gate 				     qid_printqueue(qgrp, qdir));
48517c478bd9Sstevel@tonic-gate 		return 0;
48527c478bd9Sstevel@tonic-gate 	}
48537c478bd9Sstevel@tonic-gate 
48547c478bd9Sstevel@tonic-gate 	sm_getla();	/* get load average */
48557c478bd9Sstevel@tonic-gate 
48567c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
48577c478bd9Sstevel@tonic-gate 			     qid_printqueue(qgrp, qdir),
48587c478bd9Sstevel@tonic-gate 			     nrequests, nrequests == 1 ? "" : "s");
48597c478bd9Sstevel@tonic-gate 	if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
48607c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48617c478bd9Sstevel@tonic-gate 				     ", only %d printed", MaxQueueRun);
48627c478bd9Sstevel@tonic-gate 	if (Verbose)
48637c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48647c478bd9Sstevel@tonic-gate 			")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
48657c478bd9Sstevel@tonic-gate 	else
48667c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout,  SM_TIME_DEFAULT,
48677c478bd9Sstevel@tonic-gate 			")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
48687c478bd9Sstevel@tonic-gate 	for (w = WorkQ; w != NULL; w = w->w_next)
48697c478bd9Sstevel@tonic-gate 	{
48707c478bd9Sstevel@tonic-gate 		struct stat st;
48717c478bd9Sstevel@tonic-gate 		auto time_t submittime = 0;
48727c478bd9Sstevel@tonic-gate 		long dfsize;
48737c478bd9Sstevel@tonic-gate 		int flags = 0;
48747c478bd9Sstevel@tonic-gate 		int qfver;
48757c478bd9Sstevel@tonic-gate 		char quarmsg[MAXLINE];
48767c478bd9Sstevel@tonic-gate 		char statmsg[MAXLINE];
48777c478bd9Sstevel@tonic-gate 		char bodytype[MAXNAME + 1];
48787c478bd9Sstevel@tonic-gate 		char qf[MAXPATHLEN];
48797c478bd9Sstevel@tonic-gate 
48807c478bd9Sstevel@tonic-gate 		if (StopRequest)
48817c478bd9Sstevel@tonic-gate 			stop_sendmail();
48827c478bd9Sstevel@tonic-gate 
48837c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
48847c478bd9Sstevel@tonic-gate 				     w->w_name + 2);
4885058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", w->w_name);
48867c478bd9Sstevel@tonic-gate 		f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
48877c478bd9Sstevel@tonic-gate 			       NULL);
48887c478bd9Sstevel@tonic-gate 		if (f == NULL)
48897c478bd9Sstevel@tonic-gate 		{
48907c478bd9Sstevel@tonic-gate 			if (errno == EPERM)
48917c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48927c478bd9Sstevel@tonic-gate 						     " (permission denied)\n");
48937c478bd9Sstevel@tonic-gate 			else if (errno == ENOENT)
48947c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48957c478bd9Sstevel@tonic-gate 						     " (job completed)\n");
48967c478bd9Sstevel@tonic-gate 			else
48977c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48987c478bd9Sstevel@tonic-gate 						     " (%s)\n",
48997c478bd9Sstevel@tonic-gate 						     sm_errstring(errno));
49007c478bd9Sstevel@tonic-gate 			errno = 0;
49017c478bd9Sstevel@tonic-gate 			continue;
49027c478bd9Sstevel@tonic-gate 		}
49037c478bd9Sstevel@tonic-gate 		w->w_name[0] = DATAFL_LETTER;
4904058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qddf, "/", w->w_name);
49057c478bd9Sstevel@tonic-gate 		if (stat(qf, &st) >= 0)
49067c478bd9Sstevel@tonic-gate 			dfsize = st.st_size;
49077c478bd9Sstevel@tonic-gate 		else
49087c478bd9Sstevel@tonic-gate 		{
49097c478bd9Sstevel@tonic-gate 			ENVELOPE e;
49107c478bd9Sstevel@tonic-gate 
49117c478bd9Sstevel@tonic-gate 			/*
49127c478bd9Sstevel@tonic-gate 			**  Maybe the df file can't be statted because
49137c478bd9Sstevel@tonic-gate 			**  it is in a different directory than the qf file.
49147c478bd9Sstevel@tonic-gate 			**  In order to find out, we must read the qf file.
49157c478bd9Sstevel@tonic-gate 			*/
49167c478bd9Sstevel@tonic-gate 
49177c478bd9Sstevel@tonic-gate 			newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
49187c478bd9Sstevel@tonic-gate 			e.e_id = w->w_name + 2;
49197c478bd9Sstevel@tonic-gate 			e.e_qgrp = qgrp;
49207c478bd9Sstevel@tonic-gate 			e.e_qdir = qdir;
49217c478bd9Sstevel@tonic-gate 			dfsize = -1;
49227c478bd9Sstevel@tonic-gate 			if (readqf(&e, false))
49237c478bd9Sstevel@tonic-gate 			{
49247c478bd9Sstevel@tonic-gate 				char *df = queuename(&e, DATAFL_LETTER);
49257c478bd9Sstevel@tonic-gate 				if (stat(df, &st) >= 0)
49267c478bd9Sstevel@tonic-gate 					dfsize = st.st_size;
49277c478bd9Sstevel@tonic-gate 			}
49287c478bd9Sstevel@tonic-gate 			if (e.e_lockfp != NULL)
49297c478bd9Sstevel@tonic-gate 			{
49307c478bd9Sstevel@tonic-gate 				(void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
49317c478bd9Sstevel@tonic-gate 				e.e_lockfp = NULL;
49327c478bd9Sstevel@tonic-gate 			}
49337c478bd9Sstevel@tonic-gate 			clearenvelope(&e, false, e.e_rpool);
49347c478bd9Sstevel@tonic-gate 			sm_rpool_free(e.e_rpool);
49357c478bd9Sstevel@tonic-gate 		}
49367c478bd9Sstevel@tonic-gate 		if (w->w_lock)
49377c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
49387c478bd9Sstevel@tonic-gate 		else if (QueueMode == QM_LOST)
49397c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
49407c478bd9Sstevel@tonic-gate 		else if (w->w_tooyoung)
49417c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
49427c478bd9Sstevel@tonic-gate 		else if (shouldqueue(w->w_pri, w->w_ctime))
49437c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
49447c478bd9Sstevel@tonic-gate 		else
49457c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
49467c478bd9Sstevel@tonic-gate 
49477c478bd9Sstevel@tonic-gate 		errno = 0;
49487c478bd9Sstevel@tonic-gate 
49497c478bd9Sstevel@tonic-gate 		quarmsg[0] = '\0';
49507c478bd9Sstevel@tonic-gate 		statmsg[0] = bodytype[0] = '\0';
49517c478bd9Sstevel@tonic-gate 		qfver = 0;
4952058561cbSjbeck 		while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
49537c478bd9Sstevel@tonic-gate 		{
49547c478bd9Sstevel@tonic-gate 			register int i;
49557c478bd9Sstevel@tonic-gate 			register char *p;
49567c478bd9Sstevel@tonic-gate 
49577c478bd9Sstevel@tonic-gate 			if (StopRequest)
49587c478bd9Sstevel@tonic-gate 				stop_sendmail();
49597c478bd9Sstevel@tonic-gate 
49607c478bd9Sstevel@tonic-gate 			fixcrlf(buf, true);
49617c478bd9Sstevel@tonic-gate 			switch (buf[0])
49627c478bd9Sstevel@tonic-gate 			{
49637c478bd9Sstevel@tonic-gate 			  case 'V':	/* queue file version */
49647c478bd9Sstevel@tonic-gate 				qfver = atoi(&buf[1]);
49657c478bd9Sstevel@tonic-gate 				break;
49667c478bd9Sstevel@tonic-gate 
49677c478bd9Sstevel@tonic-gate 			  case 'M':	/* error message */
4968058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(statmsg))
4969058561cbSjbeck 					i = sizeof(statmsg) - 1;
49707c478bd9Sstevel@tonic-gate 				memmove(statmsg, &buf[1], i);
49717c478bd9Sstevel@tonic-gate 				statmsg[i] = '\0';
49727c478bd9Sstevel@tonic-gate 				break;
49737c478bd9Sstevel@tonic-gate 
49747c478bd9Sstevel@tonic-gate 			  case 'q':	/* quarantine reason */
4975058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(quarmsg))
4976058561cbSjbeck 					i = sizeof(quarmsg) - 1;
49777c478bd9Sstevel@tonic-gate 				memmove(quarmsg, &buf[1], i);
49787c478bd9Sstevel@tonic-gate 				quarmsg[i] = '\0';
49797c478bd9Sstevel@tonic-gate 				break;
49807c478bd9Sstevel@tonic-gate 
49817c478bd9Sstevel@tonic-gate 			  case 'B':	/* body type */
4982058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(bodytype))
4983058561cbSjbeck 					i = sizeof(bodytype) - 1;
49847c478bd9Sstevel@tonic-gate 				memmove(bodytype, &buf[1], i);
49857c478bd9Sstevel@tonic-gate 				bodytype[i] = '\0';
49867c478bd9Sstevel@tonic-gate 				break;
49877c478bd9Sstevel@tonic-gate 
49887c478bd9Sstevel@tonic-gate 			  case 'S':	/* sender name */
49897c478bd9Sstevel@tonic-gate 				if (Verbose)
49907c478bd9Sstevel@tonic-gate 				{
49917c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
49927c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
49937c478bd9Sstevel@tonic-gate 						"%8ld %10ld%c%.12s ",
49947c478bd9Sstevel@tonic-gate 						dfsize,
49957c478bd9Sstevel@tonic-gate 						w->w_pri,
49967c478bd9Sstevel@tonic-gate 						bitset(EF_WARNING, flags)
49977c478bd9Sstevel@tonic-gate 							? '+' : ' ',
49987c478bd9Sstevel@tonic-gate 						ctime(&submittime) + 4);
49997c478bd9Sstevel@tonic-gate 					prtstr(&buf[1], 78);
50007c478bd9Sstevel@tonic-gate 				}
50017c478bd9Sstevel@tonic-gate 				else
50027c478bd9Sstevel@tonic-gate 				{
50037c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50047c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50057c478bd9Sstevel@tonic-gate 						"%8ld %.16s ",
50067c478bd9Sstevel@tonic-gate 						dfsize,
50077c478bd9Sstevel@tonic-gate 						ctime(&submittime));
50087c478bd9Sstevel@tonic-gate 					prtstr(&buf[1], 39);
50097c478bd9Sstevel@tonic-gate 				}
50107c478bd9Sstevel@tonic-gate 
50117c478bd9Sstevel@tonic-gate 				if (quarmsg[0] != '\0')
50127c478bd9Sstevel@tonic-gate 				{
50137c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50147c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
50157c478bd9Sstevel@tonic-gate 							     "\n     QUARANTINE: %.*s",
50167c478bd9Sstevel@tonic-gate 							     Verbose ? 100 : 60,
50177c478bd9Sstevel@tonic-gate 							     quarmsg);
50187c478bd9Sstevel@tonic-gate 					quarmsg[0] = '\0';
50197c478bd9Sstevel@tonic-gate 				}
50207c478bd9Sstevel@tonic-gate 
50217c478bd9Sstevel@tonic-gate 				if (statmsg[0] != '\0' || bodytype[0] != '\0')
50227c478bd9Sstevel@tonic-gate 				{
50237c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50247c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50257c478bd9Sstevel@tonic-gate 						"\n    %10.10s",
50267c478bd9Sstevel@tonic-gate 						bodytype);
50277c478bd9Sstevel@tonic-gate 					if (statmsg[0] != '\0')
50287c478bd9Sstevel@tonic-gate 						(void) sm_io_fprintf(smioout,
50297c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50307c478bd9Sstevel@tonic-gate 							"   (%.*s)",
50317c478bd9Sstevel@tonic-gate 							Verbose ? 100 : 60,
50327c478bd9Sstevel@tonic-gate 							statmsg);
50337c478bd9Sstevel@tonic-gate 					statmsg[0] = '\0';
50347c478bd9Sstevel@tonic-gate 				}
50357c478bd9Sstevel@tonic-gate 				break;
50367c478bd9Sstevel@tonic-gate 
50377c478bd9Sstevel@tonic-gate 			  case 'C':	/* controlling user */
50387c478bd9Sstevel@tonic-gate 				if (Verbose)
50397c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50407c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50417c478bd9Sstevel@tonic-gate 						"\n\t\t\t\t\t\t(---%.64s---)",
50427c478bd9Sstevel@tonic-gate 						&buf[1]);
50437c478bd9Sstevel@tonic-gate 				break;
50447c478bd9Sstevel@tonic-gate 
50457c478bd9Sstevel@tonic-gate 			  case 'R':	/* recipient name */
50467c478bd9Sstevel@tonic-gate 				p = &buf[1];
50477c478bd9Sstevel@tonic-gate 				if (qfver >= 1)
50487c478bd9Sstevel@tonic-gate 				{
50497c478bd9Sstevel@tonic-gate 					p = strchr(p, ':');
50507c478bd9Sstevel@tonic-gate 					if (p == NULL)
50517c478bd9Sstevel@tonic-gate 						break;
50527c478bd9Sstevel@tonic-gate 					p++;
50537c478bd9Sstevel@tonic-gate 				}
50547c478bd9Sstevel@tonic-gate 				if (Verbose)
50557c478bd9Sstevel@tonic-gate 				{
50567c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50577c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50587c478bd9Sstevel@tonic-gate 							"\n\t\t\t\t\t\t");
50597c478bd9Sstevel@tonic-gate 					prtstr(p, 71);
50607c478bd9Sstevel@tonic-gate 				}
50617c478bd9Sstevel@tonic-gate 				else
50627c478bd9Sstevel@tonic-gate 				{
50637c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50647c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50657c478bd9Sstevel@tonic-gate 							"\n\t\t\t\t\t ");
50667c478bd9Sstevel@tonic-gate 					prtstr(p, 38);
50677c478bd9Sstevel@tonic-gate 				}
50687c478bd9Sstevel@tonic-gate 				if (Verbose && statmsg[0] != '\0')
50697c478bd9Sstevel@tonic-gate 				{
50707c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50717c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50727c478bd9Sstevel@tonic-gate 							"\n\t\t (%.100s)",
50737c478bd9Sstevel@tonic-gate 							statmsg);
50747c478bd9Sstevel@tonic-gate 					statmsg[0] = '\0';
50757c478bd9Sstevel@tonic-gate 				}
50767c478bd9Sstevel@tonic-gate 				break;
50777c478bd9Sstevel@tonic-gate 
50787c478bd9Sstevel@tonic-gate 			  case 'T':	/* creation time */
50797c478bd9Sstevel@tonic-gate 				submittime = atol(&buf[1]);
50807c478bd9Sstevel@tonic-gate 				break;
50817c478bd9Sstevel@tonic-gate 
50827c478bd9Sstevel@tonic-gate 			  case 'F':	/* flag bits */
50837c478bd9Sstevel@tonic-gate 				for (p = &buf[1]; *p != '\0'; p++)
50847c478bd9Sstevel@tonic-gate 				{
50857c478bd9Sstevel@tonic-gate 					switch (*p)
50867c478bd9Sstevel@tonic-gate 					{
50877c478bd9Sstevel@tonic-gate 					  case 'w':
50887c478bd9Sstevel@tonic-gate 						flags |= EF_WARNING;
50897c478bd9Sstevel@tonic-gate 						break;
50907c478bd9Sstevel@tonic-gate 					}
50917c478bd9Sstevel@tonic-gate 				}
50927c478bd9Sstevel@tonic-gate 			}
50937c478bd9Sstevel@tonic-gate 		}
50947c478bd9Sstevel@tonic-gate 		if (submittime == (time_t) 0)
50957c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
50967c478bd9Sstevel@tonic-gate 					     " (no control file)");
50977c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
50987c478bd9Sstevel@tonic-gate 		(void) sm_io_close(f, SM_TIME_DEFAULT);
50997c478bd9Sstevel@tonic-gate 	}
51007c478bd9Sstevel@tonic-gate 	return nrequests;
51017c478bd9Sstevel@tonic-gate }
51027c478bd9Sstevel@tonic-gate 
51037c478bd9Sstevel@tonic-gate /*
51047c478bd9Sstevel@tonic-gate **  QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
51057c478bd9Sstevel@tonic-gate **
51067c478bd9Sstevel@tonic-gate **	Parameters:
51077c478bd9Sstevel@tonic-gate **		e -- envelope to build it in/from.
51087c478bd9Sstevel@tonic-gate **		type -- the file type, used as the first character
51097c478bd9Sstevel@tonic-gate **			of the file name.
51107c478bd9Sstevel@tonic-gate **
51117c478bd9Sstevel@tonic-gate **	Returns:
51127c478bd9Sstevel@tonic-gate **		the letter to use
51137c478bd9Sstevel@tonic-gate */
51147c478bd9Sstevel@tonic-gate 
51157c478bd9Sstevel@tonic-gate static char
51167c478bd9Sstevel@tonic-gate queue_letter(e, type)
51177c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
51187c478bd9Sstevel@tonic-gate 	int type;
51197c478bd9Sstevel@tonic-gate {
51207c478bd9Sstevel@tonic-gate 	/* Change type according to QueueMode */
51217c478bd9Sstevel@tonic-gate 	if (type == ANYQFL_LETTER)
51227c478bd9Sstevel@tonic-gate 	{
51237c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg != NULL)
51247c478bd9Sstevel@tonic-gate 			type = QUARQF_LETTER;
51257c478bd9Sstevel@tonic-gate 		else
51267c478bd9Sstevel@tonic-gate 		{
51277c478bd9Sstevel@tonic-gate 			switch (QueueMode)
51287c478bd9Sstevel@tonic-gate 			{
51297c478bd9Sstevel@tonic-gate 			  case QM_NORMAL:
51307c478bd9Sstevel@tonic-gate 				type = NORMQF_LETTER;
51317c478bd9Sstevel@tonic-gate 				break;
51327c478bd9Sstevel@tonic-gate 
51337c478bd9Sstevel@tonic-gate 			  case QM_QUARANTINE:
51347c478bd9Sstevel@tonic-gate 				type = QUARQF_LETTER;
51357c478bd9Sstevel@tonic-gate 				break;
51367c478bd9Sstevel@tonic-gate 
51377c478bd9Sstevel@tonic-gate 			  case QM_LOST:
51387c478bd9Sstevel@tonic-gate 				type = LOSEQF_LETTER;
51397c478bd9Sstevel@tonic-gate 				break;
51407c478bd9Sstevel@tonic-gate 
51417c478bd9Sstevel@tonic-gate 			  default:
51427c478bd9Sstevel@tonic-gate 				/* should never happen */
51437c478bd9Sstevel@tonic-gate 				abort();
51447c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
51457c478bd9Sstevel@tonic-gate 			}
51467c478bd9Sstevel@tonic-gate 		}
51477c478bd9Sstevel@tonic-gate 	}
51487c478bd9Sstevel@tonic-gate 	return type;
51497c478bd9Sstevel@tonic-gate }
51507c478bd9Sstevel@tonic-gate 
51517c478bd9Sstevel@tonic-gate /*
51527c478bd9Sstevel@tonic-gate **  QUEUENAME -- build a file name in the queue directory for this envelope.
51537c478bd9Sstevel@tonic-gate **
51547c478bd9Sstevel@tonic-gate **	Parameters:
51557c478bd9Sstevel@tonic-gate **		e -- envelope to build it in/from.
51567c478bd9Sstevel@tonic-gate **		type -- the file type, used as the first character
51577c478bd9Sstevel@tonic-gate **			of the file name.
51587c478bd9Sstevel@tonic-gate **
51597c478bd9Sstevel@tonic-gate **	Returns:
51607c478bd9Sstevel@tonic-gate **		a pointer to the queue name (in a static buffer).
51617c478bd9Sstevel@tonic-gate **
51627c478bd9Sstevel@tonic-gate **	Side Effects:
51637c478bd9Sstevel@tonic-gate **		If no id code is already assigned, queuename() will
51647c478bd9Sstevel@tonic-gate **		assign an id code with assign_queueid().  If no queue
51657c478bd9Sstevel@tonic-gate **		directory is assigned, one will be set with setnewqueue().
51667c478bd9Sstevel@tonic-gate */
51677c478bd9Sstevel@tonic-gate 
51687c478bd9Sstevel@tonic-gate char *
51697c478bd9Sstevel@tonic-gate queuename(e, type)
51707c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
51717c478bd9Sstevel@tonic-gate 	int type;
51727c478bd9Sstevel@tonic-gate {
51737c478bd9Sstevel@tonic-gate 	int qd, qg;
51747c478bd9Sstevel@tonic-gate 	char *sub = "/";
51757c478bd9Sstevel@tonic-gate 	char pref[3];
51767c478bd9Sstevel@tonic-gate 	static char buf[MAXPATHLEN];
51777c478bd9Sstevel@tonic-gate 
51787c478bd9Sstevel@tonic-gate 	/* Assign an ID if needed */
51797c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
51807c478bd9Sstevel@tonic-gate 		assign_queueid(e);
51817c478bd9Sstevel@tonic-gate 	type = queue_letter(e, type);
51827c478bd9Sstevel@tonic-gate 
51837c478bd9Sstevel@tonic-gate 	/* begin of filename */
51847c478bd9Sstevel@tonic-gate 	pref[0] = (char) type;
51857c478bd9Sstevel@tonic-gate 	pref[1] = 'f';
51867c478bd9Sstevel@tonic-gate 	pref[2] = '\0';
51877c478bd9Sstevel@tonic-gate 
51887c478bd9Sstevel@tonic-gate 	/* Assign a queue group/directory if needed */
51897c478bd9Sstevel@tonic-gate 	if (type == XSCRPT_LETTER)
51907c478bd9Sstevel@tonic-gate 	{
51917c478bd9Sstevel@tonic-gate 		/*
51927c478bd9Sstevel@tonic-gate 		**  We don't want to call setnewqueue() if we are fetching
51937c478bd9Sstevel@tonic-gate 		**  the pathname of the transcript file, because setnewqueue
51947c478bd9Sstevel@tonic-gate 		**  chooses a queue, and sometimes we need to write to the
51957c478bd9Sstevel@tonic-gate 		**  transcript file before we have gathered enough information
51967c478bd9Sstevel@tonic-gate 		**  to choose a queue.
51977c478bd9Sstevel@tonic-gate 		*/
51987c478bd9Sstevel@tonic-gate 
51997c478bd9Sstevel@tonic-gate 		if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
52007c478bd9Sstevel@tonic-gate 		{
52017c478bd9Sstevel@tonic-gate 			if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
52027c478bd9Sstevel@tonic-gate 			{
52037c478bd9Sstevel@tonic-gate 				e->e_xfqgrp = e->e_qgrp;
52047c478bd9Sstevel@tonic-gate 				e->e_xfqdir = e->e_qdir;
52057c478bd9Sstevel@tonic-gate 			}
52067c478bd9Sstevel@tonic-gate 			else
52077c478bd9Sstevel@tonic-gate 			{
52087c478bd9Sstevel@tonic-gate 				e->e_xfqgrp = 0;
52097c478bd9Sstevel@tonic-gate 				if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
52107c478bd9Sstevel@tonic-gate 					e->e_xfqdir = 0;
52117c478bd9Sstevel@tonic-gate 				else
52127c478bd9Sstevel@tonic-gate 				{
52137c478bd9Sstevel@tonic-gate 					e->e_xfqdir = get_rand_mod(
52147c478bd9Sstevel@tonic-gate 					      Queue[e->e_xfqgrp]->qg_numqueues);
52157c478bd9Sstevel@tonic-gate 				}
52167c478bd9Sstevel@tonic-gate 			}
52177c478bd9Sstevel@tonic-gate 		}
52187c478bd9Sstevel@tonic-gate 		qd = e->e_xfqdir;
52197c478bd9Sstevel@tonic-gate 		qg = e->e_xfqgrp;
52207c478bd9Sstevel@tonic-gate 	}
52217c478bd9Sstevel@tonic-gate 	else
52227c478bd9Sstevel@tonic-gate 	{
52237c478bd9Sstevel@tonic-gate 		if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
52243ee0e492Sjbeck 			(void) setnewqueue(e);
52257c478bd9Sstevel@tonic-gate 		if (type ==  DATAFL_LETTER)
52267c478bd9Sstevel@tonic-gate 		{
52277c478bd9Sstevel@tonic-gate 			qd = e->e_dfqdir;
52287c478bd9Sstevel@tonic-gate 			qg = e->e_dfqgrp;
52297c478bd9Sstevel@tonic-gate 		}
52307c478bd9Sstevel@tonic-gate 		else
52317c478bd9Sstevel@tonic-gate 		{
52327c478bd9Sstevel@tonic-gate 			qd = e->e_qdir;
52337c478bd9Sstevel@tonic-gate 			qg = e->e_qgrp;
52347c478bd9Sstevel@tonic-gate 		}
52357c478bd9Sstevel@tonic-gate 	}
52367c478bd9Sstevel@tonic-gate 
52377c478bd9Sstevel@tonic-gate 	/* xf files always have a valid qd and qg picked above */
52383ee0e492Sjbeck 	if ((qd == NOQDIR || qg == NOQGRP) && type != XSCRPT_LETTER)
5239058561cbSjbeck 		(void) sm_strlcpyn(buf, sizeof(buf), 2, pref, e->e_id);
52407c478bd9Sstevel@tonic-gate 	else
52417c478bd9Sstevel@tonic-gate 	{
52427c478bd9Sstevel@tonic-gate 		switch (type)
52437c478bd9Sstevel@tonic-gate 		{
52447c478bd9Sstevel@tonic-gate 		  case DATAFL_LETTER:
52457c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52467c478bd9Sstevel@tonic-gate 				sub = "/df/";
52477c478bd9Sstevel@tonic-gate 			break;
52487c478bd9Sstevel@tonic-gate 
52497c478bd9Sstevel@tonic-gate 		  case QUARQF_LETTER:
52507c478bd9Sstevel@tonic-gate 		  case TEMPQF_LETTER:
52517c478bd9Sstevel@tonic-gate 		  case NEWQFL_LETTER:
52527c478bd9Sstevel@tonic-gate 		  case LOSEQF_LETTER:
52537c478bd9Sstevel@tonic-gate 		  case NORMQF_LETTER:
52547c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52557c478bd9Sstevel@tonic-gate 				sub = "/qf/";
52567c478bd9Sstevel@tonic-gate 			break;
52577c478bd9Sstevel@tonic-gate 
52587c478bd9Sstevel@tonic-gate 		  case XSCRPT_LETTER:
52597c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52607c478bd9Sstevel@tonic-gate 				sub = "/xf/";
52617c478bd9Sstevel@tonic-gate 			break;
52627c478bd9Sstevel@tonic-gate 
52637c478bd9Sstevel@tonic-gate 		  default:
52647c478bd9Sstevel@tonic-gate 			sm_abort("queuename: bad queue file type %d", type);
52657c478bd9Sstevel@tonic-gate 		}
52667c478bd9Sstevel@tonic-gate 
5267058561cbSjbeck 		(void) sm_strlcpyn(buf, sizeof(buf), 4,
52687c478bd9Sstevel@tonic-gate 				Queue[qg]->qg_qpaths[qd].qp_name,
52697c478bd9Sstevel@tonic-gate 				sub, pref, e->e_id);
52707c478bd9Sstevel@tonic-gate 	}
52717c478bd9Sstevel@tonic-gate 
52727c478bd9Sstevel@tonic-gate 	if (tTd(7, 2))
52737c478bd9Sstevel@tonic-gate 		sm_dprintf("queuename: %s\n", buf);
52747c478bd9Sstevel@tonic-gate 	return buf;
52757c478bd9Sstevel@tonic-gate }
52767c478bd9Sstevel@tonic-gate 
52777c478bd9Sstevel@tonic-gate /*
52787c478bd9Sstevel@tonic-gate **  INIT_QID_ALG -- Initialize the (static) parameters that are used to
52797c478bd9Sstevel@tonic-gate **	generate a queue ID.
52807c478bd9Sstevel@tonic-gate **
52817c478bd9Sstevel@tonic-gate **	This function is called by the daemon to reset
52827c478bd9Sstevel@tonic-gate **	LastQueueTime and LastQueuePid which are used by assign_queueid().
52837c478bd9Sstevel@tonic-gate **	Otherwise the algorithm may cause problems because
52847c478bd9Sstevel@tonic-gate **	LastQueueTime and LastQueuePid are set indirectly by main()
52857c478bd9Sstevel@tonic-gate **	before the daemon process is started, hence LastQueuePid is not
52867c478bd9Sstevel@tonic-gate **	the pid of the daemon and therefore a child of the daemon can
52877c478bd9Sstevel@tonic-gate **	actually have the same pid as LastQueuePid which means the section
52887c478bd9Sstevel@tonic-gate **	in  assign_queueid():
52897c478bd9Sstevel@tonic-gate **	* see if we need to get a new base time/pid *
52907c478bd9Sstevel@tonic-gate **	is NOT triggered which will cause the same queue id to be generated.
52917c478bd9Sstevel@tonic-gate **
52927c478bd9Sstevel@tonic-gate **	Parameters:
52937c478bd9Sstevel@tonic-gate **		none
52947c478bd9Sstevel@tonic-gate **
52957c478bd9Sstevel@tonic-gate **	Returns:
52967c478bd9Sstevel@tonic-gate **		none.
52977c478bd9Sstevel@tonic-gate */
52987c478bd9Sstevel@tonic-gate 
52997c478bd9Sstevel@tonic-gate void
53007c478bd9Sstevel@tonic-gate init_qid_alg()
53017c478bd9Sstevel@tonic-gate {
53027c478bd9Sstevel@tonic-gate 	LastQueueTime = 0;
53037c478bd9Sstevel@tonic-gate 	LastQueuePid = -1;
53047c478bd9Sstevel@tonic-gate }
53057c478bd9Sstevel@tonic-gate 
53067c478bd9Sstevel@tonic-gate /*
53077c478bd9Sstevel@tonic-gate **  ASSIGN_QUEUEID -- assign a queue ID for this envelope.
53087c478bd9Sstevel@tonic-gate **
53097c478bd9Sstevel@tonic-gate **	Assigns an id code if one does not already exist.
53107c478bd9Sstevel@tonic-gate **	This code assumes that nothing will remain in the queue for
53117c478bd9Sstevel@tonic-gate **	longer than 60 years.  It is critical that files with the given
53127c478bd9Sstevel@tonic-gate **	name do not already exist in the queue.
53137c478bd9Sstevel@tonic-gate **	[No longer initializes e_qdir to NOQDIR.]
53147c478bd9Sstevel@tonic-gate **
53157c478bd9Sstevel@tonic-gate **	Parameters:
53167c478bd9Sstevel@tonic-gate **		e -- envelope to set it in.
53177c478bd9Sstevel@tonic-gate **
53187c478bd9Sstevel@tonic-gate **	Returns:
53197c478bd9Sstevel@tonic-gate **		none.
53207c478bd9Sstevel@tonic-gate */
53217c478bd9Sstevel@tonic-gate 
53227c478bd9Sstevel@tonic-gate static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
53237c478bd9Sstevel@tonic-gate # define QIC_LEN	60
53247c478bd9Sstevel@tonic-gate # define QIC_LEN_R	62
53257c478bd9Sstevel@tonic-gate 
53267c478bd9Sstevel@tonic-gate /*
53277c478bd9Sstevel@tonic-gate **  Note: the length is "officially" 60 because minutes and seconds are
53287c478bd9Sstevel@tonic-gate **	usually only 0-59.  However (Linux):
53297c478bd9Sstevel@tonic-gate **       tm_sec The number of seconds after the minute, normally in
53307c478bd9Sstevel@tonic-gate **		the range 0 to 59, but can be up to 61 to allow for
53317c478bd9Sstevel@tonic-gate **		leap seconds.
53327c478bd9Sstevel@tonic-gate **	Hence the real length of the string is 62 to take this into account.
53337c478bd9Sstevel@tonic-gate **	Alternatively % QIC_LEN can (should) be used for access everywhere.
53347c478bd9Sstevel@tonic-gate */
53357c478bd9Sstevel@tonic-gate 
53367c478bd9Sstevel@tonic-gate # define queuenextid() CurrentPid
53377c478bd9Sstevel@tonic-gate 
53387c478bd9Sstevel@tonic-gate 
53397c478bd9Sstevel@tonic-gate void
53407c478bd9Sstevel@tonic-gate assign_queueid(e)
53417c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
53427c478bd9Sstevel@tonic-gate {
53437c478bd9Sstevel@tonic-gate 	pid_t pid = queuenextid();
53447c478bd9Sstevel@tonic-gate 	static int cX = 0;
53457c478bd9Sstevel@tonic-gate 	static long random_offset;
53467c478bd9Sstevel@tonic-gate 	struct tm *tm;
53477c478bd9Sstevel@tonic-gate 	char idbuf[MAXQFNAME - 2];
53487c478bd9Sstevel@tonic-gate 	int seq;
53497c478bd9Sstevel@tonic-gate 
53507c478bd9Sstevel@tonic-gate 	if (e->e_id != NULL)
53517c478bd9Sstevel@tonic-gate 		return;
53527c478bd9Sstevel@tonic-gate 
53537c478bd9Sstevel@tonic-gate 	/* see if we need to get a new base time/pid */
53547c478bd9Sstevel@tonic-gate 	if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 ||
53557c478bd9Sstevel@tonic-gate 	    LastQueuePid != pid)
53567c478bd9Sstevel@tonic-gate 	{
53577c478bd9Sstevel@tonic-gate 		time_t then = LastQueueTime;
53587c478bd9Sstevel@tonic-gate 
53597c478bd9Sstevel@tonic-gate 		/* if the first time through, pick a random offset */
53607c478bd9Sstevel@tonic-gate 		if (LastQueueTime == 0)
53617c478bd9Sstevel@tonic-gate 			random_offset = get_random();
53627c478bd9Sstevel@tonic-gate 
53637c478bd9Sstevel@tonic-gate 		while ((LastQueueTime = curtime()) == then &&
53647c478bd9Sstevel@tonic-gate 		       LastQueuePid == pid)
53657c478bd9Sstevel@tonic-gate 		{
53667c478bd9Sstevel@tonic-gate 			(void) sleep(1);
53677c478bd9Sstevel@tonic-gate 		}
53687c478bd9Sstevel@tonic-gate 		LastQueuePid = queuenextid();
53697c478bd9Sstevel@tonic-gate 		cX = 0;
53707c478bd9Sstevel@tonic-gate 	}
53717c478bd9Sstevel@tonic-gate 
53727c478bd9Sstevel@tonic-gate 	/*
53737c478bd9Sstevel@tonic-gate 	**  Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1.
53747c478bd9Sstevel@tonic-gate 	**  This lets us generate up to QIC_LEN*QIC_LEN unique queue ids
53757c478bd9Sstevel@tonic-gate 	**  per second, per process.  With envelope splitting,
53767c478bd9Sstevel@tonic-gate 	**  a single message can consume many queue ids.
53777c478bd9Sstevel@tonic-gate 	*/
53787c478bd9Sstevel@tonic-gate 
53797c478bd9Sstevel@tonic-gate 	seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN));
53807c478bd9Sstevel@tonic-gate 	++cX;
53817c478bd9Sstevel@tonic-gate 	if (tTd(7, 50))
53827c478bd9Sstevel@tonic-gate 		sm_dprintf("assign_queueid: random_offset = %ld (%d)\n",
53837c478bd9Sstevel@tonic-gate 			random_offset, seq);
53847c478bd9Sstevel@tonic-gate 
53857c478bd9Sstevel@tonic-gate 	tm = gmtime(&LastQueueTime);
53867c478bd9Sstevel@tonic-gate 	idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
53877c478bd9Sstevel@tonic-gate 	idbuf[1] = QueueIdChars[tm->tm_mon];
53887c478bd9Sstevel@tonic-gate 	idbuf[2] = QueueIdChars[tm->tm_mday];
53897c478bd9Sstevel@tonic-gate 	idbuf[3] = QueueIdChars[tm->tm_hour];
53907c478bd9Sstevel@tonic-gate 	idbuf[4] = QueueIdChars[tm->tm_min % QIC_LEN_R];
53917c478bd9Sstevel@tonic-gate 	idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R];
53927c478bd9Sstevel@tonic-gate 	idbuf[6] = QueueIdChars[seq / QIC_LEN];
53937c478bd9Sstevel@tonic-gate 	idbuf[7] = QueueIdChars[seq % QIC_LEN];
5394058561cbSjbeck 	(void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d",
53957c478bd9Sstevel@tonic-gate 			   (int) LastQueuePid);
53967c478bd9Sstevel@tonic-gate 	e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
53977c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
53987c478bd9Sstevel@tonic-gate #if 0
53997c478bd9Sstevel@tonic-gate 	/* XXX: inherited from MainEnvelope */
54007c478bd9Sstevel@tonic-gate 	e->e_qgrp = NOQGRP;  /* too early to do anything else */
54017c478bd9Sstevel@tonic-gate 	e->e_qdir = NOQDIR;
54027c478bd9Sstevel@tonic-gate 	e->e_xfqgrp = NOQGRP;
54037c478bd9Sstevel@tonic-gate #endif /* 0 */
54047c478bd9Sstevel@tonic-gate 
54057c478bd9Sstevel@tonic-gate 	/* New ID means it's not on disk yet */
54067c478bd9Sstevel@tonic-gate 	e->e_qfletter = '\0';
54077c478bd9Sstevel@tonic-gate 
54087c478bd9Sstevel@tonic-gate 	if (tTd(7, 1))
54097c478bd9Sstevel@tonic-gate 		sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
54107c478bd9Sstevel@tonic-gate 			e->e_id, e);
54117c478bd9Sstevel@tonic-gate 	if (LogLevel > 93)
54127c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
54137c478bd9Sstevel@tonic-gate }
54147c478bd9Sstevel@tonic-gate /*
54157c478bd9Sstevel@tonic-gate **  SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
54167c478bd9Sstevel@tonic-gate **
54177c478bd9Sstevel@tonic-gate **	Make sure one PID can't be used by two processes in any one second.
54187c478bd9Sstevel@tonic-gate **
54197c478bd9Sstevel@tonic-gate **		If the system rotates PIDs fast enough, may get the
54207c478bd9Sstevel@tonic-gate **		same pid in the same second for two distinct processes.
54217c478bd9Sstevel@tonic-gate **		This will interfere with the queue file naming system.
54227c478bd9Sstevel@tonic-gate **
54237c478bd9Sstevel@tonic-gate **	Parameters:
54247c478bd9Sstevel@tonic-gate **		none
54257c478bd9Sstevel@tonic-gate **
54267c478bd9Sstevel@tonic-gate **	Returns:
54277c478bd9Sstevel@tonic-gate **		none
54287c478bd9Sstevel@tonic-gate */
54297c478bd9Sstevel@tonic-gate 
54307c478bd9Sstevel@tonic-gate void
54317c478bd9Sstevel@tonic-gate sync_queue_time()
54327c478bd9Sstevel@tonic-gate {
54337c478bd9Sstevel@tonic-gate #if FAST_PID_RECYCLE
54347c478bd9Sstevel@tonic-gate 	if (OpMode != MD_TEST &&
54357c478bd9Sstevel@tonic-gate 	    OpMode != MD_VERIFY &&
54367c478bd9Sstevel@tonic-gate 	    LastQueueTime > 0 &&
54377c478bd9Sstevel@tonic-gate 	    LastQueuePid == CurrentPid &&
54387c478bd9Sstevel@tonic-gate 	    curtime() == LastQueueTime)
54397c478bd9Sstevel@tonic-gate 		(void) sleep(1);
54407c478bd9Sstevel@tonic-gate #endif /* FAST_PID_RECYCLE */
54417c478bd9Sstevel@tonic-gate }
54427c478bd9Sstevel@tonic-gate /*
54437c478bd9Sstevel@tonic-gate **  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
54447c478bd9Sstevel@tonic-gate **
54457c478bd9Sstevel@tonic-gate **	Parameters:
54467c478bd9Sstevel@tonic-gate **		e -- the envelope to unlock.
54477c478bd9Sstevel@tonic-gate **
54487c478bd9Sstevel@tonic-gate **	Returns:
54497c478bd9Sstevel@tonic-gate **		none
54507c478bd9Sstevel@tonic-gate **
54517c478bd9Sstevel@tonic-gate **	Side Effects:
54527c478bd9Sstevel@tonic-gate **		unlocks the queue for `e'.
54537c478bd9Sstevel@tonic-gate */
54547c478bd9Sstevel@tonic-gate 
54557c478bd9Sstevel@tonic-gate void
54567c478bd9Sstevel@tonic-gate unlockqueue(e)
54577c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
54587c478bd9Sstevel@tonic-gate {
54597c478bd9Sstevel@tonic-gate 	if (tTd(51, 4))
54607c478bd9Sstevel@tonic-gate 		sm_dprintf("unlockqueue(%s)\n",
54617c478bd9Sstevel@tonic-gate 			e->e_id == NULL ? "NOQUEUE" : e->e_id);
54627c478bd9Sstevel@tonic-gate 
54637c478bd9Sstevel@tonic-gate 
54647c478bd9Sstevel@tonic-gate 	/* if there is a lock file in the envelope, close it */
54657c478bd9Sstevel@tonic-gate 	if (e->e_lockfp != NULL)
54667c478bd9Sstevel@tonic-gate 		(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
54677c478bd9Sstevel@tonic-gate 	e->e_lockfp = NULL;
54687c478bd9Sstevel@tonic-gate 
54697c478bd9Sstevel@tonic-gate 	/* don't create a queue id if we don't already have one */
54707c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
54717c478bd9Sstevel@tonic-gate 		return;
54727c478bd9Sstevel@tonic-gate 
54737c478bd9Sstevel@tonic-gate 	/* remove the transcript */
54747c478bd9Sstevel@tonic-gate 	if (LogLevel > 87)
54757c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id, "unlock");
54767c478bd9Sstevel@tonic-gate 	if (!tTd(51, 104))
54777c478bd9Sstevel@tonic-gate 		(void) xunlink(queuename(e, XSCRPT_LETTER));
54787c478bd9Sstevel@tonic-gate }
54797c478bd9Sstevel@tonic-gate /*
54807c478bd9Sstevel@tonic-gate **  SETCTLUSER -- create a controlling address
54817c478bd9Sstevel@tonic-gate **
54827c478bd9Sstevel@tonic-gate **	Create a fake "address" given only a local login name; this is
54837c478bd9Sstevel@tonic-gate **	used as a "controlling user" for future recipient addresses.
54847c478bd9Sstevel@tonic-gate **
54857c478bd9Sstevel@tonic-gate **	Parameters:
54867c478bd9Sstevel@tonic-gate **		user -- the user name of the controlling user.
54877c478bd9Sstevel@tonic-gate **		qfver -- the version stamp of this queue file.
54887c478bd9Sstevel@tonic-gate **		e -- envelope
54897c478bd9Sstevel@tonic-gate **
54907c478bd9Sstevel@tonic-gate **	Returns:
54917c478bd9Sstevel@tonic-gate **		An address descriptor for the controlling user,
54927c478bd9Sstevel@tonic-gate **		using storage allocated from e->e_rpool.
54937c478bd9Sstevel@tonic-gate **
54947c478bd9Sstevel@tonic-gate */
54957c478bd9Sstevel@tonic-gate 
54967c478bd9Sstevel@tonic-gate static ADDRESS *
54977c478bd9Sstevel@tonic-gate setctluser(user, qfver, e)
54987c478bd9Sstevel@tonic-gate 	char *user;
54997c478bd9Sstevel@tonic-gate 	int qfver;
55007c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
55017c478bd9Sstevel@tonic-gate {
55027c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
55037c478bd9Sstevel@tonic-gate 	struct passwd *pw;
55047c478bd9Sstevel@tonic-gate 	char *p;
55057c478bd9Sstevel@tonic-gate 
55067c478bd9Sstevel@tonic-gate 	/*
55077c478bd9Sstevel@tonic-gate 	**  See if this clears our concept of controlling user.
55087c478bd9Sstevel@tonic-gate 	*/
55097c478bd9Sstevel@tonic-gate 
55107c478bd9Sstevel@tonic-gate 	if (user == NULL || *user == '\0')
55117c478bd9Sstevel@tonic-gate 		return NULL;
55127c478bd9Sstevel@tonic-gate 
55137c478bd9Sstevel@tonic-gate 	/*
55147c478bd9Sstevel@tonic-gate 	**  Set up addr fields for controlling user.
55157c478bd9Sstevel@tonic-gate 	*/
55167c478bd9Sstevel@tonic-gate 
5517058561cbSjbeck 	a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a));
5518058561cbSjbeck 	memset((char *) a, '\0', sizeof(*a));
55197c478bd9Sstevel@tonic-gate 
55207c478bd9Sstevel@tonic-gate 	if (*user == ':')
55217c478bd9Sstevel@tonic-gate 	{
55227c478bd9Sstevel@tonic-gate 		p = &user[1];
55237c478bd9Sstevel@tonic-gate 		a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
55247c478bd9Sstevel@tonic-gate 	}
55257c478bd9Sstevel@tonic-gate 	else
55267c478bd9Sstevel@tonic-gate 	{
55277c478bd9Sstevel@tonic-gate 		p = strtok(user, ":");
55287c478bd9Sstevel@tonic-gate 		a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
55297c478bd9Sstevel@tonic-gate 		if (qfver >= 2)
55307c478bd9Sstevel@tonic-gate 		{
55317c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55327c478bd9Sstevel@tonic-gate 				a->q_uid = atoi(p);
55337c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55347c478bd9Sstevel@tonic-gate 				a->q_gid = atoi(p);
55357c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55367c478bd9Sstevel@tonic-gate 			{
55377c478bd9Sstevel@tonic-gate 				char *o;
55387c478bd9Sstevel@tonic-gate 
55397c478bd9Sstevel@tonic-gate 				a->q_flags |= QGOODUID;
55407c478bd9Sstevel@tonic-gate 
55417c478bd9Sstevel@tonic-gate 				/* if there is another ':': restore it */
55427c478bd9Sstevel@tonic-gate 				if ((o = strtok(NULL, ":")) != NULL && o > p)
55437c478bd9Sstevel@tonic-gate 					o[-1] = ':';
55447c478bd9Sstevel@tonic-gate 			}
55457c478bd9Sstevel@tonic-gate 		}
55467c478bd9Sstevel@tonic-gate 		else if ((pw = sm_getpwnam(user)) != NULL)
55477c478bd9Sstevel@tonic-gate 		{
55487c478bd9Sstevel@tonic-gate 			if (*pw->pw_dir == '\0')
55497c478bd9Sstevel@tonic-gate 				a->q_home = NULL;
55507c478bd9Sstevel@tonic-gate 			else if (strcmp(pw->pw_dir, "/") == 0)
55517c478bd9Sstevel@tonic-gate 				a->q_home = "";
55527c478bd9Sstevel@tonic-gate 			else
55537c478bd9Sstevel@tonic-gate 				a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
55547c478bd9Sstevel@tonic-gate 			a->q_uid = pw->pw_uid;
55557c478bd9Sstevel@tonic-gate 			a->q_gid = pw->pw_gid;
55567c478bd9Sstevel@tonic-gate 			a->q_flags |= QGOODUID;
55577c478bd9Sstevel@tonic-gate 		}
55587c478bd9Sstevel@tonic-gate 	}
55597c478bd9Sstevel@tonic-gate 
55607c478bd9Sstevel@tonic-gate 	a->q_flags |= QPRIMARY;		/* flag as a "ctladdr" */
55617c478bd9Sstevel@tonic-gate 	a->q_mailer = LocalMailer;
55627c478bd9Sstevel@tonic-gate 	if (p == NULL)
55637c478bd9Sstevel@tonic-gate 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
55647c478bd9Sstevel@tonic-gate 	else
55657c478bd9Sstevel@tonic-gate 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
55667c478bd9Sstevel@tonic-gate 	return a;
55677c478bd9Sstevel@tonic-gate }
55687c478bd9Sstevel@tonic-gate /*
55697c478bd9Sstevel@tonic-gate **  LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
55707c478bd9Sstevel@tonic-gate **
55717c478bd9Sstevel@tonic-gate **	Parameters:
55727c478bd9Sstevel@tonic-gate **		e -- the envelope (e->e_id will be used).
55737c478bd9Sstevel@tonic-gate **		why -- reported to whomever can hear.
55747c478bd9Sstevel@tonic-gate **
55757c478bd9Sstevel@tonic-gate **	Returns:
55767c478bd9Sstevel@tonic-gate **		none.
55777c478bd9Sstevel@tonic-gate */
55787c478bd9Sstevel@tonic-gate 
55797c478bd9Sstevel@tonic-gate void
55807c478bd9Sstevel@tonic-gate loseqfile(e, why)
55817c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
55827c478bd9Sstevel@tonic-gate 	char *why;
55837c478bd9Sstevel@tonic-gate {
55847c478bd9Sstevel@tonic-gate 	bool loseit = true;
55857c478bd9Sstevel@tonic-gate 	char *p;
55867c478bd9Sstevel@tonic-gate 	char buf[MAXPATHLEN];
55877c478bd9Sstevel@tonic-gate 
55887c478bd9Sstevel@tonic-gate 	if (e == NULL || e->e_id == NULL)
55897c478bd9Sstevel@tonic-gate 		return;
55907c478bd9Sstevel@tonic-gate 	p = queuename(e, ANYQFL_LETTER);
5591058561cbSjbeck 	if (sm_strlcpy(buf, p, sizeof(buf)) >= sizeof(buf))
55927c478bd9Sstevel@tonic-gate 		return;
55937c478bd9Sstevel@tonic-gate 	if (!bitset(EF_INQUEUE, e->e_flags))
55947c478bd9Sstevel@tonic-gate 		queueup(e, false, true);
55957c478bd9Sstevel@tonic-gate 	else if (QueueMode == QM_LOST)
55967c478bd9Sstevel@tonic-gate 		loseit = false;
55977c478bd9Sstevel@tonic-gate 
55987c478bd9Sstevel@tonic-gate 	/* if already lost, no need to re-lose */
55997c478bd9Sstevel@tonic-gate 	if (loseit)
56007c478bd9Sstevel@tonic-gate 	{
56017c478bd9Sstevel@tonic-gate 		p = queuename(e, LOSEQF_LETTER);
56027c478bd9Sstevel@tonic-gate 		if (rename(buf, p) < 0)
56037c478bd9Sstevel@tonic-gate 			syserr("cannot rename(%s, %s), uid=%d",
56047c478bd9Sstevel@tonic-gate 			       buf, p, (int) geteuid());
56057c478bd9Sstevel@tonic-gate 		else if (LogLevel > 0)
56067c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, e->e_id,
56077c478bd9Sstevel@tonic-gate 				  "Losing %s: %s", buf, why);
56087c478bd9Sstevel@tonic-gate 	}
56097c478bd9Sstevel@tonic-gate 	if (e->e_dfp != NULL)
56107c478bd9Sstevel@tonic-gate 	{
56117c478bd9Sstevel@tonic-gate 		(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
56127c478bd9Sstevel@tonic-gate 		e->e_dfp = NULL;
56137c478bd9Sstevel@tonic-gate 	}
56147c478bd9Sstevel@tonic-gate 	e->e_flags &= ~EF_HAS_DF;
56157c478bd9Sstevel@tonic-gate }
56167c478bd9Sstevel@tonic-gate /*
56177c478bd9Sstevel@tonic-gate **  NAME2QID -- translate a queue group name to a queue group id
56187c478bd9Sstevel@tonic-gate **
56197c478bd9Sstevel@tonic-gate **	Parameters:
56207c478bd9Sstevel@tonic-gate **		queuename -- name of queue group.
56217c478bd9Sstevel@tonic-gate **
56227c478bd9Sstevel@tonic-gate **	Returns:
56237c478bd9Sstevel@tonic-gate **		queue group id if found.
56247c478bd9Sstevel@tonic-gate **		NOQGRP otherwise.
56257c478bd9Sstevel@tonic-gate */
56267c478bd9Sstevel@tonic-gate 
56277c478bd9Sstevel@tonic-gate int
56287c478bd9Sstevel@tonic-gate name2qid(queuename)
56297c478bd9Sstevel@tonic-gate 	char *queuename;
56307c478bd9Sstevel@tonic-gate {
56317c478bd9Sstevel@tonic-gate 	register STAB *s;
56327c478bd9Sstevel@tonic-gate 
56337c478bd9Sstevel@tonic-gate 	s = stab(queuename, ST_QUEUE, ST_FIND);
56347c478bd9Sstevel@tonic-gate 	if (s == NULL)
56357c478bd9Sstevel@tonic-gate 		return NOQGRP;
56367c478bd9Sstevel@tonic-gate 	return s->s_quegrp->qg_index;
56377c478bd9Sstevel@tonic-gate }
56387c478bd9Sstevel@tonic-gate /*
56397c478bd9Sstevel@tonic-gate **  QID_PRINTNAME -- create externally printable version of queue id
56407c478bd9Sstevel@tonic-gate **
56417c478bd9Sstevel@tonic-gate **	Parameters:
56427c478bd9Sstevel@tonic-gate **		e -- the envelope.
56437c478bd9Sstevel@tonic-gate **
56447c478bd9Sstevel@tonic-gate **	Returns:
56457c478bd9Sstevel@tonic-gate **		a printable version
56467c478bd9Sstevel@tonic-gate */
56477c478bd9Sstevel@tonic-gate 
56487c478bd9Sstevel@tonic-gate char *
56497c478bd9Sstevel@tonic-gate qid_printname(e)
56507c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
56517c478bd9Sstevel@tonic-gate {
56527c478bd9Sstevel@tonic-gate 	char *id;
56537c478bd9Sstevel@tonic-gate 	static char idbuf[MAXQFNAME + 34];
56547c478bd9Sstevel@tonic-gate 
56557c478bd9Sstevel@tonic-gate 	if (e == NULL)
56567c478bd9Sstevel@tonic-gate 		return "";
56577c478bd9Sstevel@tonic-gate 
56587c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
56597c478bd9Sstevel@tonic-gate 		id = "";
56607c478bd9Sstevel@tonic-gate 	else
56617c478bd9Sstevel@tonic-gate 		id = e->e_id;
56627c478bd9Sstevel@tonic-gate 
56637c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR)
56647c478bd9Sstevel@tonic-gate 		return id;
56657c478bd9Sstevel@tonic-gate 
5666058561cbSjbeck 	(void) sm_snprintf(idbuf, sizeof(idbuf), "%.32s/%s",
56677c478bd9Sstevel@tonic-gate 			   Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
56687c478bd9Sstevel@tonic-gate 			   id);
56697c478bd9Sstevel@tonic-gate 	return idbuf;
56707c478bd9Sstevel@tonic-gate }
56717c478bd9Sstevel@tonic-gate /*
56727c478bd9Sstevel@tonic-gate **  QID_PRINTQUEUE -- create full version of queue directory for data files
56737c478bd9Sstevel@tonic-gate **
56747c478bd9Sstevel@tonic-gate **	Parameters:
56757c478bd9Sstevel@tonic-gate **		qgrp -- index in queue group.
56767c478bd9Sstevel@tonic-gate **		qdir -- the short version of the queue directory
56777c478bd9Sstevel@tonic-gate **
56787c478bd9Sstevel@tonic-gate **	Returns:
56797c478bd9Sstevel@tonic-gate **		the full pathname to the queue (might point to a static var)
56807c478bd9Sstevel@tonic-gate */
56817c478bd9Sstevel@tonic-gate 
56827c478bd9Sstevel@tonic-gate char *
56837c478bd9Sstevel@tonic-gate qid_printqueue(qgrp, qdir)
56847c478bd9Sstevel@tonic-gate 	int qgrp;
56857c478bd9Sstevel@tonic-gate 	int qdir;
56867c478bd9Sstevel@tonic-gate {
56877c478bd9Sstevel@tonic-gate 	char *subdir;
56887c478bd9Sstevel@tonic-gate 	static char dir[MAXPATHLEN];
56897c478bd9Sstevel@tonic-gate 
56907c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
56917c478bd9Sstevel@tonic-gate 		return Queue[qgrp]->qg_qdir;
56927c478bd9Sstevel@tonic-gate 
56937c478bd9Sstevel@tonic-gate 	if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
56947c478bd9Sstevel@tonic-gate 		subdir = NULL;
56957c478bd9Sstevel@tonic-gate 	else
56967c478bd9Sstevel@tonic-gate 		subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
56977c478bd9Sstevel@tonic-gate 
5698058561cbSjbeck 	(void) sm_strlcpyn(dir, sizeof(dir), 4,
56997c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qdir,
57007c478bd9Sstevel@tonic-gate 			subdir == NULL ? "" : "/",
57017c478bd9Sstevel@tonic-gate 			subdir == NULL ? "" : subdir,
57027c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBDF,
57037c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
57047c478bd9Sstevel@tonic-gate 					? "/df" : ""));
57057c478bd9Sstevel@tonic-gate 	return dir;
57067c478bd9Sstevel@tonic-gate }
57077c478bd9Sstevel@tonic-gate 
57087c478bd9Sstevel@tonic-gate /*
57097c478bd9Sstevel@tonic-gate **  PICKQDIR -- Pick a queue directory from a queue group
57107c478bd9Sstevel@tonic-gate **
57117c478bd9Sstevel@tonic-gate **	Parameters:
57127c478bd9Sstevel@tonic-gate **		qg -- queue group
57137c478bd9Sstevel@tonic-gate **		fsize -- file size in bytes
57147c478bd9Sstevel@tonic-gate **		e -- envelope, or NULL
57157c478bd9Sstevel@tonic-gate **
57167c478bd9Sstevel@tonic-gate **	Result:
57177c478bd9Sstevel@tonic-gate **		NOQDIR if no queue directory in qg has enough free space to
57187c478bd9Sstevel@tonic-gate **		hold a file of size 'fsize', otherwise the index of
57197c478bd9Sstevel@tonic-gate **		a randomly selected queue directory which resides on a
57207c478bd9Sstevel@tonic-gate **		file system with enough disk space.
57217c478bd9Sstevel@tonic-gate **		XXX This could be extended to select a queuedir with
57227c478bd9Sstevel@tonic-gate **			a few (the fewest?) number of entries. That data
57237c478bd9Sstevel@tonic-gate **			is available if shared memory is used.
57247c478bd9Sstevel@tonic-gate **
57257c478bd9Sstevel@tonic-gate **	Side Effects:
57267c478bd9Sstevel@tonic-gate **		If the request fails and e != NULL then sm_syslog is called.
57277c478bd9Sstevel@tonic-gate */
57287c478bd9Sstevel@tonic-gate 
57297c478bd9Sstevel@tonic-gate int
57307c478bd9Sstevel@tonic-gate pickqdir(qg, fsize, e)
57317c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
57327c478bd9Sstevel@tonic-gate 	long fsize;
57337c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
57347c478bd9Sstevel@tonic-gate {
57357c478bd9Sstevel@tonic-gate 	int qdir;
57367c478bd9Sstevel@tonic-gate 	int i;
57377c478bd9Sstevel@tonic-gate 	long avail = 0;
57387c478bd9Sstevel@tonic-gate 
57397c478bd9Sstevel@tonic-gate 	/* Pick a random directory, as a starting point. */
57407c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues <= 1)
57417c478bd9Sstevel@tonic-gate 		qdir = 0;
57427c478bd9Sstevel@tonic-gate 	else
57437c478bd9Sstevel@tonic-gate 		qdir = get_rand_mod(qg->qg_numqueues);
57447c478bd9Sstevel@tonic-gate 
57457c478bd9Sstevel@tonic-gate 	if (MinBlocksFree <= 0 && fsize <= 0)
57467c478bd9Sstevel@tonic-gate 		return qdir;
57477c478bd9Sstevel@tonic-gate 
57487c478bd9Sstevel@tonic-gate 	/*
57497c478bd9Sstevel@tonic-gate 	**  Now iterate over the queue directories,
57507c478bd9Sstevel@tonic-gate 	**  looking for a directory with enough space for this message.
57517c478bd9Sstevel@tonic-gate 	*/
57527c478bd9Sstevel@tonic-gate 
57537c478bd9Sstevel@tonic-gate 	i = qdir;
57547c478bd9Sstevel@tonic-gate 	do
57557c478bd9Sstevel@tonic-gate 	{
57567c478bd9Sstevel@tonic-gate 		QPATHS *qp = &qg->qg_qpaths[i];
57577c478bd9Sstevel@tonic-gate 		long needed = 0;
57587c478bd9Sstevel@tonic-gate 		long fsavail = 0;
57597c478bd9Sstevel@tonic-gate 
57607c478bd9Sstevel@tonic-gate 		if (fsize > 0)
57617c478bd9Sstevel@tonic-gate 			needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
57627c478bd9Sstevel@tonic-gate 				  + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
57637c478bd9Sstevel@tonic-gate 				      > 0) ? 1 : 0);
57647c478bd9Sstevel@tonic-gate 		if (MinBlocksFree > 0)
57657c478bd9Sstevel@tonic-gate 			needed += MinBlocksFree;
57667c478bd9Sstevel@tonic-gate 		fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
57677c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
57687c478bd9Sstevel@tonic-gate 		if (fsavail <= 0)
57697c478bd9Sstevel@tonic-gate 		{
57707c478bd9Sstevel@tonic-gate 			long blksize;
57717c478bd9Sstevel@tonic-gate 
57727c478bd9Sstevel@tonic-gate 			/*
57737c478bd9Sstevel@tonic-gate 			**  might be not correctly updated,
57747c478bd9Sstevel@tonic-gate 			**  let's try to get the info directly.
57757c478bd9Sstevel@tonic-gate 			*/
57767c478bd9Sstevel@tonic-gate 
57777c478bd9Sstevel@tonic-gate 			fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
57787c478bd9Sstevel@tonic-gate 						&blksize);
57797c478bd9Sstevel@tonic-gate 			if (fsavail < 0)
57807c478bd9Sstevel@tonic-gate 				fsavail = 0;
57817c478bd9Sstevel@tonic-gate 		}
57827c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
57837c478bd9Sstevel@tonic-gate 		if (needed <= fsavail)
57847c478bd9Sstevel@tonic-gate 			return i;
57857c478bd9Sstevel@tonic-gate 		if (avail < fsavail)
57867c478bd9Sstevel@tonic-gate 			avail = fsavail;
57877c478bd9Sstevel@tonic-gate 
57887c478bd9Sstevel@tonic-gate 		if (qg->qg_numqueues > 0)
57897c478bd9Sstevel@tonic-gate 			i = (i + 1) % qg->qg_numqueues;
57907c478bd9Sstevel@tonic-gate 	} while (i != qdir);
57917c478bd9Sstevel@tonic-gate 
57927c478bd9Sstevel@tonic-gate 	if (e != NULL && LogLevel > 0)
57937c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ALERT, e->e_id,
57947c478bd9Sstevel@tonic-gate 			"low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
57957c478bd9Sstevel@tonic-gate 			CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
57967c478bd9Sstevel@tonic-gate 			fsize, MinBlocksFree,
57977c478bd9Sstevel@tonic-gate 			qg->qg_qdir, avail);
57987c478bd9Sstevel@tonic-gate 	return NOQDIR;
57997c478bd9Sstevel@tonic-gate }
58007c478bd9Sstevel@tonic-gate /*
58017c478bd9Sstevel@tonic-gate **  SETNEWQUEUE -- Sets a new queue group and directory
58027c478bd9Sstevel@tonic-gate **
58037c478bd9Sstevel@tonic-gate **	Assign a queue group and directory to an envelope and store the
58047c478bd9Sstevel@tonic-gate **	directory in e->e_qdir.
58057c478bd9Sstevel@tonic-gate **
58067c478bd9Sstevel@tonic-gate **	Parameters:
58077c478bd9Sstevel@tonic-gate **		e -- envelope to assign a queue for.
58087c478bd9Sstevel@tonic-gate **
58097c478bd9Sstevel@tonic-gate **	Returns:
58107c478bd9Sstevel@tonic-gate **		true if successful
58117c478bd9Sstevel@tonic-gate **		false otherwise
58127c478bd9Sstevel@tonic-gate **
58137c478bd9Sstevel@tonic-gate **	Side Effects:
58147c478bd9Sstevel@tonic-gate **		On success, e->e_qgrp and e->e_qdir are non-negative.
58157c478bd9Sstevel@tonic-gate **		On failure (not enough disk space),
58167c478bd9Sstevel@tonic-gate **		e->qgrp = NOQGRP, e->e_qdir = NOQDIR
58177c478bd9Sstevel@tonic-gate **		and usrerr() is invoked (which could raise an exception).
58187c478bd9Sstevel@tonic-gate */
58197c478bd9Sstevel@tonic-gate 
58207c478bd9Sstevel@tonic-gate bool
58217c478bd9Sstevel@tonic-gate setnewqueue(e)
58227c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
58237c478bd9Sstevel@tonic-gate {
58247c478bd9Sstevel@tonic-gate 	if (tTd(41, 20))
58257c478bd9Sstevel@tonic-gate 		sm_dprintf("setnewqueue: called\n");
58267c478bd9Sstevel@tonic-gate 
58277c478bd9Sstevel@tonic-gate 	/* not set somewhere else */
58287c478bd9Sstevel@tonic-gate 	if (e->e_qgrp == NOQGRP)
58297c478bd9Sstevel@tonic-gate 	{
58307c478bd9Sstevel@tonic-gate 		ADDRESS *q;
58317c478bd9Sstevel@tonic-gate 
58327c478bd9Sstevel@tonic-gate 		/*
58337c478bd9Sstevel@tonic-gate 		**  Use the queue group of the "first" recipient, as set by
58347c478bd9Sstevel@tonic-gate 		**  the "queuegroup" rule set.  If that is not defined, then
58357c478bd9Sstevel@tonic-gate 		**  use the queue group of the mailer of the first recipient.
58367c478bd9Sstevel@tonic-gate 		**  If that is not defined either, then use the default
58377c478bd9Sstevel@tonic-gate 		**  queue group.
58387c478bd9Sstevel@tonic-gate 		**  Notice: "first" depends on the sorting of sendqueue
58397c478bd9Sstevel@tonic-gate 		**  in recipient().
58407c478bd9Sstevel@tonic-gate 		**  To avoid problems with "bad" recipients look
58417c478bd9Sstevel@tonic-gate 		**  for a valid address first.
58427c478bd9Sstevel@tonic-gate 		*/
58437c478bd9Sstevel@tonic-gate 
58447c478bd9Sstevel@tonic-gate 		q = e->e_sendqueue;
58457c478bd9Sstevel@tonic-gate 		while (q != NULL &&
58467c478bd9Sstevel@tonic-gate 		       (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state)))
58477c478bd9Sstevel@tonic-gate 		{
58487c478bd9Sstevel@tonic-gate 			q = q->q_next;
58497c478bd9Sstevel@tonic-gate 		}
58507c478bd9Sstevel@tonic-gate 		if (q == NULL)
58517c478bd9Sstevel@tonic-gate 			e->e_qgrp = 0;
58527c478bd9Sstevel@tonic-gate 		else if (q->q_qgrp >= 0)
58537c478bd9Sstevel@tonic-gate 			e->e_qgrp = q->q_qgrp;
58547c478bd9Sstevel@tonic-gate 		else if (q->q_mailer != NULL &&
58557c478bd9Sstevel@tonic-gate 			 ISVALIDQGRP(q->q_mailer->m_qgrp))
58567c478bd9Sstevel@tonic-gate 			e->e_qgrp = q->q_mailer->m_qgrp;
58577c478bd9Sstevel@tonic-gate 		else
58587c478bd9Sstevel@tonic-gate 			e->e_qgrp = 0;
58597c478bd9Sstevel@tonic-gate 		e->e_dfqgrp = e->e_qgrp;
58607c478bd9Sstevel@tonic-gate 	}
58617c478bd9Sstevel@tonic-gate 
58627c478bd9Sstevel@tonic-gate 	if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
58637c478bd9Sstevel@tonic-gate 	{
58647c478bd9Sstevel@tonic-gate 		if (tTd(41, 20))
58657c478bd9Sstevel@tonic-gate 			sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
58667c478bd9Sstevel@tonic-gate 				qid_printqueue(e->e_qgrp, e->e_qdir));
58677c478bd9Sstevel@tonic-gate 		return true;
58687c478bd9Sstevel@tonic-gate 	}
58697c478bd9Sstevel@tonic-gate 
58707c478bd9Sstevel@tonic-gate 	filesys_update();
58717c478bd9Sstevel@tonic-gate 	e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
58727c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR)
58737c478bd9Sstevel@tonic-gate 	{
58747c478bd9Sstevel@tonic-gate 		e->e_qgrp = NOQGRP;
58757c478bd9Sstevel@tonic-gate 		if (!bitset(EF_FATALERRS, e->e_flags))
58767c478bd9Sstevel@tonic-gate 			usrerr("452 4.4.5 Insufficient disk space; try again later");
58777c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_FATALERRS;
58787c478bd9Sstevel@tonic-gate 		return false;
58797c478bd9Sstevel@tonic-gate 	}
58807c478bd9Sstevel@tonic-gate 
58817c478bd9Sstevel@tonic-gate 	if (tTd(41, 3))
58827c478bd9Sstevel@tonic-gate 		sm_dprintf("setnewqueue: Assigned queue directory %s\n",
58837c478bd9Sstevel@tonic-gate 			qid_printqueue(e->e_qgrp, e->e_qdir));
58847c478bd9Sstevel@tonic-gate 
58857c478bd9Sstevel@tonic-gate 	if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
58867c478bd9Sstevel@tonic-gate 	{
58877c478bd9Sstevel@tonic-gate 		e->e_xfqgrp = e->e_qgrp;
58887c478bd9Sstevel@tonic-gate 		e->e_xfqdir = e->e_qdir;
58897c478bd9Sstevel@tonic-gate 	}
58907c478bd9Sstevel@tonic-gate 	e->e_dfqdir = e->e_qdir;
58917c478bd9Sstevel@tonic-gate 	return true;
58927c478bd9Sstevel@tonic-gate }
58937c478bd9Sstevel@tonic-gate /*
58947c478bd9Sstevel@tonic-gate **  CHKQDIR -- check a queue directory
58957c478bd9Sstevel@tonic-gate **
58967c478bd9Sstevel@tonic-gate **	Parameters:
58977c478bd9Sstevel@tonic-gate **		name -- name of queue directory
58987c478bd9Sstevel@tonic-gate **		sff -- flags for safefile()
58997c478bd9Sstevel@tonic-gate **
59007c478bd9Sstevel@tonic-gate **	Returns:
59017c478bd9Sstevel@tonic-gate **		is it a queue directory?
59027c478bd9Sstevel@tonic-gate */
59037c478bd9Sstevel@tonic-gate 
5904058561cbSjbeck static bool chkqdir __P((char *, long));
5905058561cbSjbeck 
59067c478bd9Sstevel@tonic-gate static bool
59077c478bd9Sstevel@tonic-gate chkqdir(name, sff)
59087c478bd9Sstevel@tonic-gate 	char *name;
59097c478bd9Sstevel@tonic-gate 	long sff;
59107c478bd9Sstevel@tonic-gate {
59117c478bd9Sstevel@tonic-gate 	struct stat statb;
59127c478bd9Sstevel@tonic-gate 	int i;
59137c478bd9Sstevel@tonic-gate 
59147c478bd9Sstevel@tonic-gate 	/* skip over . and .. directories */
59157c478bd9Sstevel@tonic-gate 	if (name[0] == '.' &&
59167c478bd9Sstevel@tonic-gate 	    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
59177c478bd9Sstevel@tonic-gate 		return false;
59187c478bd9Sstevel@tonic-gate #if HASLSTAT
59197c478bd9Sstevel@tonic-gate 	if (lstat(name, &statb) < 0)
59207c478bd9Sstevel@tonic-gate #else /* HASLSTAT */
59217c478bd9Sstevel@tonic-gate 	if (stat(name, &statb) < 0)
59227c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
59237c478bd9Sstevel@tonic-gate 	{
59247c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59257c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
59267c478bd9Sstevel@tonic-gate 				   name, sm_errstring(errno));
59277c478bd9Sstevel@tonic-gate 		return false;
59287c478bd9Sstevel@tonic-gate 	}
59297c478bd9Sstevel@tonic-gate #if HASLSTAT
59307c478bd9Sstevel@tonic-gate 	if (S_ISLNK(statb.st_mode))
59317c478bd9Sstevel@tonic-gate 	{
59327c478bd9Sstevel@tonic-gate 		/*
59337c478bd9Sstevel@tonic-gate 		**  For a symlink we need to make sure the
59347c478bd9Sstevel@tonic-gate 		**  target is a directory
59357c478bd9Sstevel@tonic-gate 		*/
59367c478bd9Sstevel@tonic-gate 
59377c478bd9Sstevel@tonic-gate 		if (stat(name, &statb) < 0)
59387c478bd9Sstevel@tonic-gate 		{
59397c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
59407c478bd9Sstevel@tonic-gate 				sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
59417c478bd9Sstevel@tonic-gate 					   name, sm_errstring(errno));
59427c478bd9Sstevel@tonic-gate 			return false;
59437c478bd9Sstevel@tonic-gate 		}
59447c478bd9Sstevel@tonic-gate 	}
59457c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
59467c478bd9Sstevel@tonic-gate 
59477c478bd9Sstevel@tonic-gate 	if (!S_ISDIR(statb.st_mode))
59487c478bd9Sstevel@tonic-gate 	{
59497c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59507c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: \"%s\": Not a directory\n",
59517c478bd9Sstevel@tonic-gate 				name);
59527c478bd9Sstevel@tonic-gate 		return false;
59537c478bd9Sstevel@tonic-gate 	}
59547c478bd9Sstevel@tonic-gate 
59557c478bd9Sstevel@tonic-gate 	/* Print a warning if unsafe (but still use it) */
59567c478bd9Sstevel@tonic-gate 	/* XXX do this only if we want the warning? */
59577c478bd9Sstevel@tonic-gate 	i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
59587c478bd9Sstevel@tonic-gate 	if (i != 0)
59597c478bd9Sstevel@tonic-gate 	{
59607c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59617c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
59627c478bd9Sstevel@tonic-gate 				   name, sm_errstring(i));
59637c478bd9Sstevel@tonic-gate #if _FFR_CHK_QUEUE
59647c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
59657c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
59667c478bd9Sstevel@tonic-gate 				  "queue directory \"%s\": Not safe: %s",
59677c478bd9Sstevel@tonic-gate 				  name, sm_errstring(i));
59687c478bd9Sstevel@tonic-gate #endif /* _FFR_CHK_QUEUE */
59697c478bd9Sstevel@tonic-gate 	}
59707c478bd9Sstevel@tonic-gate 	return true;
59717c478bd9Sstevel@tonic-gate }
59727c478bd9Sstevel@tonic-gate /*
59737c478bd9Sstevel@tonic-gate **  MULTIQUEUE_CACHE -- cache a list of paths to queues.
59747c478bd9Sstevel@tonic-gate **
59757c478bd9Sstevel@tonic-gate **	Each potential queue is checked as the cache is built.
59767c478bd9Sstevel@tonic-gate **	Thereafter, each is blindly trusted.
59777c478bd9Sstevel@tonic-gate **	Note that we can be called again after a timeout to rebuild
59787c478bd9Sstevel@tonic-gate **	(although code for that is not ready yet).
59797c478bd9Sstevel@tonic-gate **
59807c478bd9Sstevel@tonic-gate **	Parameters:
59817c478bd9Sstevel@tonic-gate **		basedir -- base of all queue directories.
59827c478bd9Sstevel@tonic-gate **		blen -- strlen(basedir).
59837c478bd9Sstevel@tonic-gate **		qg -- queue group.
59847c478bd9Sstevel@tonic-gate **		qn -- number of queue directories already cached.
59857c478bd9Sstevel@tonic-gate **		phash -- pointer to hash value over queue dirs.
59867c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
59877c478bd9Sstevel@tonic-gate **			only used if shared memory is active.
59887c478bd9Sstevel@tonic-gate #endif * SM_CONF_SHM *
59897c478bd9Sstevel@tonic-gate **
59907c478bd9Sstevel@tonic-gate **	Returns:
59917c478bd9Sstevel@tonic-gate **		new number of queue directories.
59927c478bd9Sstevel@tonic-gate */
59937c478bd9Sstevel@tonic-gate 
59947c478bd9Sstevel@tonic-gate #define INITIAL_SLOTS	20
59957c478bd9Sstevel@tonic-gate #define ADD_SLOTS	10
59967c478bd9Sstevel@tonic-gate 
59977c478bd9Sstevel@tonic-gate static int
59987c478bd9Sstevel@tonic-gate multiqueue_cache(basedir, blen, qg, qn, phash)
59997c478bd9Sstevel@tonic-gate 	char *basedir;
60007c478bd9Sstevel@tonic-gate 	int blen;
60017c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
60027c478bd9Sstevel@tonic-gate 	int qn;
60037c478bd9Sstevel@tonic-gate 	unsigned int *phash;
60047c478bd9Sstevel@tonic-gate {
60057c478bd9Sstevel@tonic-gate 	char *cp;
60067c478bd9Sstevel@tonic-gate 	int i, len;
60077c478bd9Sstevel@tonic-gate 	int slotsleft = 0;
60087c478bd9Sstevel@tonic-gate 	long sff = SFF_ANYFILE;
60097c478bd9Sstevel@tonic-gate 	char qpath[MAXPATHLEN];
60107c478bd9Sstevel@tonic-gate 	char subdir[MAXPATHLEN];
60117c478bd9Sstevel@tonic-gate 	char prefix[MAXPATHLEN];	/* dir relative to basedir */
60127c478bd9Sstevel@tonic-gate 
60137c478bd9Sstevel@tonic-gate 	if (tTd(41, 20))
60147c478bd9Sstevel@tonic-gate 		sm_dprintf("multiqueue_cache: called\n");
60157c478bd9Sstevel@tonic-gate 
60167c478bd9Sstevel@tonic-gate 	/* Initialize to current directory */
60177c478bd9Sstevel@tonic-gate 	prefix[0] = '.';
60187c478bd9Sstevel@tonic-gate 	prefix[1] = '\0';
60197c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
60207c478bd9Sstevel@tonic-gate 	{
60217c478bd9Sstevel@tonic-gate 		for (i = 0; i < qg->qg_numqueues; i++)
60227c478bd9Sstevel@tonic-gate 		{
60237c478bd9Sstevel@tonic-gate 			if (qg->qg_qpaths[i].qp_name != NULL)
60247c478bd9Sstevel@tonic-gate 				(void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
60257c478bd9Sstevel@tonic-gate 		}
60267c478bd9Sstevel@tonic-gate 		(void) sm_free((char *) qg->qg_qpaths); /* XXX */
60277c478bd9Sstevel@tonic-gate 		qg->qg_qpaths = NULL;
60287c478bd9Sstevel@tonic-gate 		qg->qg_numqueues = 0;
60297c478bd9Sstevel@tonic-gate 	}
60307c478bd9Sstevel@tonic-gate 
60317c478bd9Sstevel@tonic-gate 	/* If running as root, allow safedirpath() checks to use privs */
60327c478bd9Sstevel@tonic-gate 	if (RunAsUid == 0)
60337c478bd9Sstevel@tonic-gate 		sff |= SFF_ROOTOK;
60347c478bd9Sstevel@tonic-gate #if _FFR_CHK_QUEUE
60357c478bd9Sstevel@tonic-gate 	sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES;
60367c478bd9Sstevel@tonic-gate 	if (!UseMSP)
60377c478bd9Sstevel@tonic-gate 		sff |= SFF_NOGWFILES;
60387c478bd9Sstevel@tonic-gate #endif /* _FFR_CHK_QUEUE */
60397c478bd9Sstevel@tonic-gate 
60407c478bd9Sstevel@tonic-gate 	if (!SM_IS_DIR_START(qg->qg_qdir))
60417c478bd9Sstevel@tonic-gate 	{
60427c478bd9Sstevel@tonic-gate 		/*
60437c478bd9Sstevel@tonic-gate 		**  XXX we could add basedir, but then we have to realloc()
60447c478bd9Sstevel@tonic-gate 		**  the string... Maybe another time.
60457c478bd9Sstevel@tonic-gate 		*/
60467c478bd9Sstevel@tonic-gate 
60477c478bd9Sstevel@tonic-gate 		syserr("QueuePath %s not absolute", qg->qg_qdir);
60487c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60497c478bd9Sstevel@tonic-gate 		return qn;
60507c478bd9Sstevel@tonic-gate 	}
60517c478bd9Sstevel@tonic-gate 
60527c478bd9Sstevel@tonic-gate 	/* qpath: directory of current workgroup */
6053058561cbSjbeck 	len = sm_strlcpy(qpath, qg->qg_qdir, sizeof(qpath));
6054058561cbSjbeck 	if (len >= sizeof(qpath))
60557c478bd9Sstevel@tonic-gate 	{
60567c478bd9Sstevel@tonic-gate 		syserr("QueuePath %.256s too long (%d max)",
6057058561cbSjbeck 		       qg->qg_qdir, (int) sizeof(qpath));
60587c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60597c478bd9Sstevel@tonic-gate 		return qn;
60607c478bd9Sstevel@tonic-gate 	}
60617c478bd9Sstevel@tonic-gate 
60627c478bd9Sstevel@tonic-gate 	/* begin of qpath must be same as basedir */
60637c478bd9Sstevel@tonic-gate 	if (strncmp(basedir, qpath, blen) != 0 &&
60647c478bd9Sstevel@tonic-gate 	    (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
60657c478bd9Sstevel@tonic-gate 	{
60667c478bd9Sstevel@tonic-gate 		syserr("QueuePath %s not subpath of QueueDirectory %s",
60677c478bd9Sstevel@tonic-gate 			qpath, basedir);
60687c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60697c478bd9Sstevel@tonic-gate 		return qn;
60707c478bd9Sstevel@tonic-gate 	}
60717c478bd9Sstevel@tonic-gate 
60727c478bd9Sstevel@tonic-gate 	/* Do we have a nested subdirectory? */
60737c478bd9Sstevel@tonic-gate 	if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
60747c478bd9Sstevel@tonic-gate 	{
60757c478bd9Sstevel@tonic-gate 
60767c478bd9Sstevel@tonic-gate 		/* Copy subdirectory into prefix for later use */
6077058561cbSjbeck 		if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof(prefix)) >=
6078058561cbSjbeck 		    sizeof(prefix))
60797c478bd9Sstevel@tonic-gate 		{
60807c478bd9Sstevel@tonic-gate 			syserr("QueuePath %.256s too long (%d max)",
6081058561cbSjbeck 				qg->qg_qdir, (int) sizeof(qpath));
60827c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
60837c478bd9Sstevel@tonic-gate 			return qn;
60847c478bd9Sstevel@tonic-gate 		}
60857c478bd9Sstevel@tonic-gate 		cp = SM_LAST_DIR_DELIM(prefix);
60867c478bd9Sstevel@tonic-gate 		SM_ASSERT(cp != NULL);
60877c478bd9Sstevel@tonic-gate 		*cp = '\0';	/* cut off trailing / */
60887c478bd9Sstevel@tonic-gate 	}
60897c478bd9Sstevel@tonic-gate 
60907c478bd9Sstevel@tonic-gate 	/* This is guaranteed by the basedir check above */
60917c478bd9Sstevel@tonic-gate 	SM_ASSERT(len >= blen - 1);
60927c478bd9Sstevel@tonic-gate 	cp = &qpath[len - 1];
60937c478bd9Sstevel@tonic-gate 	if (*cp == '*')
60947c478bd9Sstevel@tonic-gate 	{
60957c478bd9Sstevel@tonic-gate 		register DIR *dp;
60967c478bd9Sstevel@tonic-gate 		register struct dirent *d;
60977c478bd9Sstevel@tonic-gate 		int off;
60987c478bd9Sstevel@tonic-gate 		char *delim;
60997c478bd9Sstevel@tonic-gate 		char relpath[MAXPATHLEN];
61007c478bd9Sstevel@tonic-gate 
61017c478bd9Sstevel@tonic-gate 		*cp = '\0';	/* Overwrite wildcard */
61027c478bd9Sstevel@tonic-gate 		if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
61037c478bd9Sstevel@tonic-gate 		{
61047c478bd9Sstevel@tonic-gate 			syserr("QueueDirectory: can not wildcard relative path");
61057c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
61067c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
61077c478bd9Sstevel@tonic-gate 					qpath);
61087c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
61097c478bd9Sstevel@tonic-gate 			return qn;
61107c478bd9Sstevel@tonic-gate 		}
61117c478bd9Sstevel@tonic-gate 		if (cp == qpath)
61127c478bd9Sstevel@tonic-gate 		{
61137c478bd9Sstevel@tonic-gate 			/*
61147c478bd9Sstevel@tonic-gate 			**  Special case of top level wildcard, like /foo*
61157c478bd9Sstevel@tonic-gate 			**	Change to //foo*
61167c478bd9Sstevel@tonic-gate 			*/
61177c478bd9Sstevel@tonic-gate 
6118058561cbSjbeck 			(void) sm_strlcpy(qpath + 1, qpath, sizeof(qpath) - 1);
61197c478bd9Sstevel@tonic-gate 			++cp;
61207c478bd9Sstevel@tonic-gate 		}
61217c478bd9Sstevel@tonic-gate 		delim = cp;
61227c478bd9Sstevel@tonic-gate 		*(cp++) = '\0';		/* Replace / with \0 */
61237c478bd9Sstevel@tonic-gate 		len = strlen(cp);	/* Last component of queue directory */
61247c478bd9Sstevel@tonic-gate 
61257c478bd9Sstevel@tonic-gate 		/*
61267c478bd9Sstevel@tonic-gate 		**  Path relative to basedir, with trailing /
61277c478bd9Sstevel@tonic-gate 		**  It will be modified below to specify the subdirectories
61287c478bd9Sstevel@tonic-gate 		**  so they can be opened without chdir().
61297c478bd9Sstevel@tonic-gate 		*/
61307c478bd9Sstevel@tonic-gate 
6131058561cbSjbeck 		off = sm_strlcpyn(relpath, sizeof(relpath), 2, prefix, "/");
6132058561cbSjbeck 		SM_ASSERT(off < sizeof(relpath));
61337c478bd9Sstevel@tonic-gate 
61347c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
61357c478bd9Sstevel@tonic-gate 			sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
61367c478bd9Sstevel@tonic-gate 				   relpath, cp);
61377c478bd9Sstevel@tonic-gate 
61387c478bd9Sstevel@tonic-gate 		/* It is always basedir: we don't need to store it per group */
61397c478bd9Sstevel@tonic-gate 		/* XXX: optimize this! -> one more global? */
61407c478bd9Sstevel@tonic-gate 		qg->qg_qdir = newstr(basedir);
61417c478bd9Sstevel@tonic-gate 		qg->qg_qdir[blen - 1] = '\0';	/* cut off trailing / */
61427c478bd9Sstevel@tonic-gate 
61437c478bd9Sstevel@tonic-gate 		/*
61447c478bd9Sstevel@tonic-gate 		**  XXX Should probably wrap this whole loop in a timeout
61457c478bd9Sstevel@tonic-gate 		**  in case some wag decides to NFS mount the queues.
61467c478bd9Sstevel@tonic-gate 		*/
61477c478bd9Sstevel@tonic-gate 
61487c478bd9Sstevel@tonic-gate 		/* Test path to get warning messages. */
61497c478bd9Sstevel@tonic-gate 		if (qn == 0)
61507c478bd9Sstevel@tonic-gate 		{
61517c478bd9Sstevel@tonic-gate 			/*  XXX qg_runasuid and qg_runasgid for specials? */
61527c478bd9Sstevel@tonic-gate 			i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
61537c478bd9Sstevel@tonic-gate 					sff, 0, 0);
61547c478bd9Sstevel@tonic-gate 			if (i != 0 && tTd(41, 2))
61557c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
61567c478bd9Sstevel@tonic-gate 					   basedir, sm_errstring(i));
61577c478bd9Sstevel@tonic-gate 		}
61587c478bd9Sstevel@tonic-gate 
61597c478bd9Sstevel@tonic-gate 		if ((dp = opendir(prefix)) == NULL)
61607c478bd9Sstevel@tonic-gate 		{
61617c478bd9Sstevel@tonic-gate 			syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
61627c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
61637c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
61647c478bd9Sstevel@tonic-gate 					   qg->qg_qdir, prefix,
61657c478bd9Sstevel@tonic-gate 					   sm_errstring(errno));
61667c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
61677c478bd9Sstevel@tonic-gate 			return qn;
61687c478bd9Sstevel@tonic-gate 		}
61697c478bd9Sstevel@tonic-gate 		while ((d = readdir(dp)) != NULL)
61707c478bd9Sstevel@tonic-gate 		{
6171058561cbSjbeck 			/* Skip . and .. directories */
6172058561cbSjbeck 			if (strcmp(d->d_name, ".") == 0 ||
6173058561cbSjbeck 			    strcmp(d->d_name, "..") == 0)
6174058561cbSjbeck 				continue;
6175058561cbSjbeck 
61767c478bd9Sstevel@tonic-gate 			i = strlen(d->d_name);
61777c478bd9Sstevel@tonic-gate 			if (i < len || strncmp(d->d_name, cp, len) != 0)
61787c478bd9Sstevel@tonic-gate 			{
61797c478bd9Sstevel@tonic-gate 				if (tTd(41, 5))
61807c478bd9Sstevel@tonic-gate 					sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
61817c478bd9Sstevel@tonic-gate 						d->d_name);
61827c478bd9Sstevel@tonic-gate 				continue;
61837c478bd9Sstevel@tonic-gate 			}
61847c478bd9Sstevel@tonic-gate 
61857c478bd9Sstevel@tonic-gate 			/* Create relative pathname: prefix + local directory */
61867c478bd9Sstevel@tonic-gate 			i = sizeof(relpath) - off;
61877c478bd9Sstevel@tonic-gate 			if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
61887c478bd9Sstevel@tonic-gate 				continue;	/* way too long */
61897c478bd9Sstevel@tonic-gate 
61907c478bd9Sstevel@tonic-gate 			if (!chkqdir(relpath, sff))
61917c478bd9Sstevel@tonic-gate 				continue;
61927c478bd9Sstevel@tonic-gate 
61937c478bd9Sstevel@tonic-gate 			if (qg->qg_qpaths == NULL)
61947c478bd9Sstevel@tonic-gate 			{
61957c478bd9Sstevel@tonic-gate 				slotsleft = INITIAL_SLOTS;
6196058561cbSjbeck 				qg->qg_qpaths = (QPATHS *)xalloc((sizeof(*qg->qg_qpaths)) *
61977c478bd9Sstevel@tonic-gate 								slotsleft);
61987c478bd9Sstevel@tonic-gate 				qg->qg_numqueues = 0;
61997c478bd9Sstevel@tonic-gate 			}
62007c478bd9Sstevel@tonic-gate 			else if (slotsleft < 1)
62017c478bd9Sstevel@tonic-gate 			{
62027c478bd9Sstevel@tonic-gate 				qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
6203058561cbSjbeck 							  (sizeof(*qg->qg_qpaths)) *
62047c478bd9Sstevel@tonic-gate 							  (qg->qg_numqueues +
62057c478bd9Sstevel@tonic-gate 							   ADD_SLOTS));
62067c478bd9Sstevel@tonic-gate 				if (qg->qg_qpaths == NULL)
62077c478bd9Sstevel@tonic-gate 				{
62087c478bd9Sstevel@tonic-gate 					(void) closedir(dp);
62097c478bd9Sstevel@tonic-gate 					return qn;
62107c478bd9Sstevel@tonic-gate 				}
62117c478bd9Sstevel@tonic-gate 				slotsleft += ADD_SLOTS;
62127c478bd9Sstevel@tonic-gate 			}
62137c478bd9Sstevel@tonic-gate 
62147c478bd9Sstevel@tonic-gate 			/* check subdirs */
62157c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
62167c478bd9Sstevel@tonic-gate 
62177c478bd9Sstevel@tonic-gate #define CHKRSUBDIR(name, flag)	\
6218058561cbSjbeck 	(void) sm_strlcpyn(subdir, sizeof(subdir), 3, relpath, "/", name); \
62197c478bd9Sstevel@tonic-gate 	if (chkqdir(subdir, sff))	\
62207c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag;	\
62217c478bd9Sstevel@tonic-gate 	else
62227c478bd9Sstevel@tonic-gate 
62237c478bd9Sstevel@tonic-gate 
62247c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("qf", QP_SUBQF);
62257c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("df", QP_SUBDF);
62267c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("xf", QP_SUBXF);
62277c478bd9Sstevel@tonic-gate 
62287c478bd9Sstevel@tonic-gate 			/* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
62297c478bd9Sstevel@tonic-gate 			/* maybe even - 17 (subdirs) */
62307c478bd9Sstevel@tonic-gate 
62317c478bd9Sstevel@tonic-gate 			if (prefix[0] != '.')
62327c478bd9Sstevel@tonic-gate 				qg->qg_qpaths[qg->qg_numqueues].qp_name =
62337c478bd9Sstevel@tonic-gate 					newstr(relpath);
62347c478bd9Sstevel@tonic-gate 			else
62357c478bd9Sstevel@tonic-gate 				qg->qg_qpaths[qg->qg_numqueues].qp_name =
62367c478bd9Sstevel@tonic-gate 					newstr(d->d_name);
62377c478bd9Sstevel@tonic-gate 
62387c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
62397c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
62407c478bd9Sstevel@tonic-gate 					qg->qg_numqueues, relpath,
62417c478bd9Sstevel@tonic-gate 					qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
62427c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
62437c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
62447c478bd9Sstevel@tonic-gate 			*phash = hash_q(relpath, *phash);
62457c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
62467c478bd9Sstevel@tonic-gate 			qg->qg_numqueues++;
62477c478bd9Sstevel@tonic-gate 			++qn;
62487c478bd9Sstevel@tonic-gate 			slotsleft--;
62497c478bd9Sstevel@tonic-gate 		}
62507c478bd9Sstevel@tonic-gate 		(void) closedir(dp);
62517c478bd9Sstevel@tonic-gate 
62527c478bd9Sstevel@tonic-gate 		/* undo damage */
62537c478bd9Sstevel@tonic-gate 		*delim = '/';
62547c478bd9Sstevel@tonic-gate 	}
62557c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues == 0)
62567c478bd9Sstevel@tonic-gate 	{
6257058561cbSjbeck 		qg->qg_qpaths = (QPATHS *) xalloc(sizeof(*qg->qg_qpaths));
62587c478bd9Sstevel@tonic-gate 
62597c478bd9Sstevel@tonic-gate 		/* test path to get warning messages */
62607c478bd9Sstevel@tonic-gate 		i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
62617c478bd9Sstevel@tonic-gate 		if (i == ENOENT)
62627c478bd9Sstevel@tonic-gate 		{
62637c478bd9Sstevel@tonic-gate 			syserr("can not opendir(%s)", qpath);
62647c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
62657c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
62667c478bd9Sstevel@tonic-gate 					   qpath, sm_errstring(i));
62677c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
62687c478bd9Sstevel@tonic-gate 			return qn;
62697c478bd9Sstevel@tonic-gate 		}
62707c478bd9Sstevel@tonic-gate 
62717c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
62727c478bd9Sstevel@tonic-gate 		qg->qg_numqueues = 1;
62737c478bd9Sstevel@tonic-gate 
62747c478bd9Sstevel@tonic-gate 		/* check subdirs */
62757c478bd9Sstevel@tonic-gate #define CHKSUBDIR(name, flag)	\
6276058561cbSjbeck 	(void) sm_strlcpyn(subdir, sizeof(subdir), 3, qg->qg_qdir, "/", name); \
62777c478bd9Sstevel@tonic-gate 	if (chkqdir(subdir, sff))	\
62787c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_subdirs |= flag;	\
62797c478bd9Sstevel@tonic-gate 	else
62807c478bd9Sstevel@tonic-gate 
62817c478bd9Sstevel@tonic-gate 		CHKSUBDIR("qf", QP_SUBQF);
62827c478bd9Sstevel@tonic-gate 		CHKSUBDIR("df", QP_SUBDF);
62837c478bd9Sstevel@tonic-gate 		CHKSUBDIR("xf", QP_SUBXF);
62847c478bd9Sstevel@tonic-gate 
62857c478bd9Sstevel@tonic-gate 		if (qg->qg_qdir[blen - 1] != '\0' &&
62867c478bd9Sstevel@tonic-gate 		    qg->qg_qdir[blen] != '\0')
62877c478bd9Sstevel@tonic-gate 		{
62887c478bd9Sstevel@tonic-gate 			/*
62897c478bd9Sstevel@tonic-gate 			**  Copy the last component into qpaths and
62907c478bd9Sstevel@tonic-gate 			**  cut off qdir
62917c478bd9Sstevel@tonic-gate 			*/
62927c478bd9Sstevel@tonic-gate 
62937c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
62947c478bd9Sstevel@tonic-gate 			qg->qg_qdir[blen - 1] = '\0';
62957c478bd9Sstevel@tonic-gate 		}
62967c478bd9Sstevel@tonic-gate 		else
62977c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[0].qp_name = newstr(".");
62987c478bd9Sstevel@tonic-gate 
62997c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
63007c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_idx = qn;
63017c478bd9Sstevel@tonic-gate 		*phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
63027c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
63037c478bd9Sstevel@tonic-gate 		++qn;
63047c478bd9Sstevel@tonic-gate 	}
63057c478bd9Sstevel@tonic-gate 	return qn;
63067c478bd9Sstevel@tonic-gate }
63077c478bd9Sstevel@tonic-gate 
63087c478bd9Sstevel@tonic-gate /*
63097c478bd9Sstevel@tonic-gate **  FILESYS_FIND -- find entry in FileSys table, or add new one
63107c478bd9Sstevel@tonic-gate **
63117c478bd9Sstevel@tonic-gate **	Given the pathname of a directory, determine the file system
63127c478bd9Sstevel@tonic-gate **	in which that directory resides, and return a pointer to the
63137c478bd9Sstevel@tonic-gate **	entry in the FileSys table that describes the file system.
63147c478bd9Sstevel@tonic-gate **	A new entry is added if necessary (and requested).
63157c478bd9Sstevel@tonic-gate **	If the directory does not exist, -1 is returned.
63167c478bd9Sstevel@tonic-gate **
63177c478bd9Sstevel@tonic-gate **	Parameters:
631849218d4fSjbeck **		name -- name of directory (must be persistent!)
631949218d4fSjbeck **		path -- pathname of directory (name plus maybe "/df")
63207c478bd9Sstevel@tonic-gate **		add -- add to structure if not found.
63217c478bd9Sstevel@tonic-gate **
63227c478bd9Sstevel@tonic-gate **	Returns:
63237c478bd9Sstevel@tonic-gate **		>=0: found: index in file system table
63247c478bd9Sstevel@tonic-gate **		<0: some error, i.e.,
63257c478bd9Sstevel@tonic-gate **		FSF_TOO_MANY: too many filesystems (-> syserr())
63267c478bd9Sstevel@tonic-gate **		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
63277c478bd9Sstevel@tonic-gate **		FSF_NOT_FOUND: not in list
63287c478bd9Sstevel@tonic-gate */
63297c478bd9Sstevel@tonic-gate 
6330058561cbSjbeck static short filesys_find __P((const char *, const char *, bool));
63317c478bd9Sstevel@tonic-gate 
63327c478bd9Sstevel@tonic-gate #define FSF_NOT_FOUND	(-1)
63337c478bd9Sstevel@tonic-gate #define FSF_STAT_FAIL	(-2)
63347c478bd9Sstevel@tonic-gate #define FSF_TOO_MANY	(-3)
63357c478bd9Sstevel@tonic-gate 
63367c478bd9Sstevel@tonic-gate static short
633749218d4fSjbeck filesys_find(name, path, add)
6338058561cbSjbeck 	const char *name;
6339058561cbSjbeck 	const char *path;
63407c478bd9Sstevel@tonic-gate 	bool add;
63417c478bd9Sstevel@tonic-gate {
63427c478bd9Sstevel@tonic-gate 	struct stat st;
63437c478bd9Sstevel@tonic-gate 	short i;
63447c478bd9Sstevel@tonic-gate 
63457c478bd9Sstevel@tonic-gate 	if (stat(path, &st) < 0)
63467c478bd9Sstevel@tonic-gate 	{
63477c478bd9Sstevel@tonic-gate 		syserr("cannot stat queue directory %s", path);
63487c478bd9Sstevel@tonic-gate 		return FSF_STAT_FAIL;
63497c478bd9Sstevel@tonic-gate 	}
63507c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
63517c478bd9Sstevel@tonic-gate 	{
63527c478bd9Sstevel@tonic-gate 		if (FILE_SYS_DEV(i) == st.st_dev)
63531daa5768Sjbeck 		{
63541daa5768Sjbeck 			/*
63551daa5768Sjbeck 			**  Make sure the file system (FS) name is set:
63561daa5768Sjbeck 			**  even though the source code indicates that
63571daa5768Sjbeck 			**  FILE_SYS_DEV() is only set below, it could be
63581daa5768Sjbeck 			**  set via shared memory, hence we need to perform
63591daa5768Sjbeck 			**  this check/assignment here.
63601daa5768Sjbeck 			*/
63611daa5768Sjbeck 
63621daa5768Sjbeck 			if (NULL == FILE_SYS_NAME(i))
63631daa5768Sjbeck 				FILE_SYS_NAME(i) = name;
63647c478bd9Sstevel@tonic-gate 			return i;
63651daa5768Sjbeck 		}
63667c478bd9Sstevel@tonic-gate 	}
63677c478bd9Sstevel@tonic-gate 	if (i >= MAXFILESYS)
63687c478bd9Sstevel@tonic-gate 	{
63697c478bd9Sstevel@tonic-gate 		syserr("too many queue file systems (%d max)", MAXFILESYS);
63707c478bd9Sstevel@tonic-gate 		return FSF_TOO_MANY;
63717c478bd9Sstevel@tonic-gate 	}
63727c478bd9Sstevel@tonic-gate 	if (!add)
63737c478bd9Sstevel@tonic-gate 		return FSF_NOT_FOUND;
63747c478bd9Sstevel@tonic-gate 
63757c478bd9Sstevel@tonic-gate 	++NumFileSys;
637649218d4fSjbeck 	FILE_SYS_NAME(i) = name;
63777c478bd9Sstevel@tonic-gate 	FILE_SYS_DEV(i) = st.st_dev;
63787c478bd9Sstevel@tonic-gate 	FILE_SYS_AVAIL(i) = 0;
63797c478bd9Sstevel@tonic-gate 	FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
63807c478bd9Sstevel@tonic-gate 	return i;
63817c478bd9Sstevel@tonic-gate }
63827c478bd9Sstevel@tonic-gate 
63837c478bd9Sstevel@tonic-gate /*
63847c478bd9Sstevel@tonic-gate **  FILESYS_SETUP -- set up mapping from queue directories to file systems
63857c478bd9Sstevel@tonic-gate **
63867c478bd9Sstevel@tonic-gate **	This data structure is used to efficiently check the amount of
63877c478bd9Sstevel@tonic-gate **	free space available in a set of queue directories.
63887c478bd9Sstevel@tonic-gate **
63897c478bd9Sstevel@tonic-gate **	Parameters:
63907c478bd9Sstevel@tonic-gate **		add -- initialize structure if necessary.
63917c478bd9Sstevel@tonic-gate **
63927c478bd9Sstevel@tonic-gate **	Returns:
63937c478bd9Sstevel@tonic-gate **		0: success
63947c478bd9Sstevel@tonic-gate **		<0: some error, i.e.,
63957c478bd9Sstevel@tonic-gate **		FSF_NOT_FOUND: not in list
63967c478bd9Sstevel@tonic-gate **		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
63977c478bd9Sstevel@tonic-gate **		FSF_TOO_MANY: too many filesystems (-> syserr())
63987c478bd9Sstevel@tonic-gate */
63997c478bd9Sstevel@tonic-gate 
64007c478bd9Sstevel@tonic-gate static int filesys_setup __P((bool));
64017c478bd9Sstevel@tonic-gate 
64027c478bd9Sstevel@tonic-gate static int
64037c478bd9Sstevel@tonic-gate filesys_setup(add)
64047c478bd9Sstevel@tonic-gate 	bool add;
64057c478bd9Sstevel@tonic-gate {
64067c478bd9Sstevel@tonic-gate 	int i, j;
64077c478bd9Sstevel@tonic-gate 	short fs;
64087c478bd9Sstevel@tonic-gate 	int ret;
64097c478bd9Sstevel@tonic-gate 
64107c478bd9Sstevel@tonic-gate 	ret = 0;
64117c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
64127c478bd9Sstevel@tonic-gate 	{
64137c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; ++j)
64147c478bd9Sstevel@tonic-gate 		{
64157c478bd9Sstevel@tonic-gate 			QPATHS *qp = &Queue[i]->qg_qpaths[j];
641649218d4fSjbeck 			char qddf[MAXPATHLEN];
64177c478bd9Sstevel@tonic-gate 
6418058561cbSjbeck 			(void) sm_strlcpyn(qddf, sizeof(qddf), 2, qp->qp_name,
641949218d4fSjbeck 					(bitset(QP_SUBDF, qp->qp_subdirs)
642049218d4fSjbeck 						? "/df" : ""));
642149218d4fSjbeck 			fs = filesys_find(qp->qp_name, qddf, add);
64227c478bd9Sstevel@tonic-gate 			if (fs >= 0)
64237c478bd9Sstevel@tonic-gate 				qp->qp_fsysidx = fs;
64247c478bd9Sstevel@tonic-gate 			else
64257c478bd9Sstevel@tonic-gate 				qp->qp_fsysidx = 0;
64267c478bd9Sstevel@tonic-gate 			if (fs < ret)
64277c478bd9Sstevel@tonic-gate 				ret = fs;
64287c478bd9Sstevel@tonic-gate 		}
64297c478bd9Sstevel@tonic-gate 	}
64307c478bd9Sstevel@tonic-gate 	return ret;
64317c478bd9Sstevel@tonic-gate }
64327c478bd9Sstevel@tonic-gate 
64337c478bd9Sstevel@tonic-gate /*
64347c478bd9Sstevel@tonic-gate **  FILESYS_UPDATE -- update amount of free space on all file systems
64357c478bd9Sstevel@tonic-gate **
64367c478bd9Sstevel@tonic-gate **	The FileSys table is used to cache the amount of free space
64377c478bd9Sstevel@tonic-gate **	available on all queue directory file systems.
64387c478bd9Sstevel@tonic-gate **	This function updates the cached information if it has expired.
64397c478bd9Sstevel@tonic-gate **
64407c478bd9Sstevel@tonic-gate **	Parameters:
64417c478bd9Sstevel@tonic-gate **		none.
64427c478bd9Sstevel@tonic-gate **
64437c478bd9Sstevel@tonic-gate **	Returns:
64447c478bd9Sstevel@tonic-gate **		none.
64457c478bd9Sstevel@tonic-gate **
64467c478bd9Sstevel@tonic-gate **	Side Effects:
64477c478bd9Sstevel@tonic-gate **		Updates FileSys table.
64487c478bd9Sstevel@tonic-gate */
64497c478bd9Sstevel@tonic-gate 
64507c478bd9Sstevel@tonic-gate void
64517c478bd9Sstevel@tonic-gate filesys_update()
64527c478bd9Sstevel@tonic-gate {
64537c478bd9Sstevel@tonic-gate 	int i;
64547c478bd9Sstevel@tonic-gate 	long avail, blksize;
64557c478bd9Sstevel@tonic-gate 	time_t now;
64567c478bd9Sstevel@tonic-gate 	static time_t nextupdate = 0;
64577c478bd9Sstevel@tonic-gate 
64587c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
6459058561cbSjbeck 	/*
6460058561cbSjbeck 	**  Only the daemon updates the shared memory, i.e.,
6461058561cbSjbeck 	**  if shared memory is available but the pid is not the
6462058561cbSjbeck 	**  one of the daemon, then don't do anything.
6463058561cbSjbeck 	*/
6464058561cbSjbeck 
64651daa5768Sjbeck 	if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
64667c478bd9Sstevel@tonic-gate 		return;
64677c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
64687c478bd9Sstevel@tonic-gate 	now = curtime();
64697c478bd9Sstevel@tonic-gate 	if (now < nextupdate)
64707c478bd9Sstevel@tonic-gate 		return;
64717c478bd9Sstevel@tonic-gate 	nextupdate = now + FILESYS_UPDATE_INTERVAL;
64727c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
64737c478bd9Sstevel@tonic-gate 	{
64747c478bd9Sstevel@tonic-gate 		FILESYS *fs = &FILE_SYS(i);
64757c478bd9Sstevel@tonic-gate 
64767c478bd9Sstevel@tonic-gate 		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
64777c478bd9Sstevel@tonic-gate 		if (avail < 0 || blksize <= 0)
64787c478bd9Sstevel@tonic-gate 		{
64797c478bd9Sstevel@tonic-gate 			if (LogLevel > 5)
64807c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
64817c478bd9Sstevel@tonic-gate 					"filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
64827c478bd9Sstevel@tonic-gate 					sm_errstring(errno),
64837c478bd9Sstevel@tonic-gate 					FILE_SYS_NAME(i), avail, blksize);
64847c478bd9Sstevel@tonic-gate 			fs->fs_avail = 0;
64857c478bd9Sstevel@tonic-gate 			fs->fs_blksize = 1024; /* avoid divide by zero */
64867c478bd9Sstevel@tonic-gate 			nextupdate = now + 2; /* let's do this soon again */
64877c478bd9Sstevel@tonic-gate 		}
64887c478bd9Sstevel@tonic-gate 		else
64897c478bd9Sstevel@tonic-gate 		{
64907c478bd9Sstevel@tonic-gate 			fs->fs_avail = avail;
64917c478bd9Sstevel@tonic-gate 			fs->fs_blksize = blksize;
64927c478bd9Sstevel@tonic-gate 		}
64937c478bd9Sstevel@tonic-gate 	}
64947c478bd9Sstevel@tonic-gate }
64957c478bd9Sstevel@tonic-gate 
64967c478bd9Sstevel@tonic-gate #if _FFR_ANY_FREE_FS
64977c478bd9Sstevel@tonic-gate /*
64987c478bd9Sstevel@tonic-gate **  FILESYS_FREE -- check whether there is at least one fs with enough space.
64997c478bd9Sstevel@tonic-gate **
65007c478bd9Sstevel@tonic-gate **	Parameters:
65017c478bd9Sstevel@tonic-gate **		fsize -- file size in bytes
65027c478bd9Sstevel@tonic-gate **
65037c478bd9Sstevel@tonic-gate **	Returns:
65047c478bd9Sstevel@tonic-gate **		true iff there is one fs with more than fsize bytes free.
65057c478bd9Sstevel@tonic-gate */
65067c478bd9Sstevel@tonic-gate 
65077c478bd9Sstevel@tonic-gate bool
65087c478bd9Sstevel@tonic-gate filesys_free(fsize)
65097c478bd9Sstevel@tonic-gate 	long fsize;
65107c478bd9Sstevel@tonic-gate {
65117c478bd9Sstevel@tonic-gate 	int i;
65127c478bd9Sstevel@tonic-gate 
65137c478bd9Sstevel@tonic-gate 	if (fsize <= 0)
65147c478bd9Sstevel@tonic-gate 		return true;
65157c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
65167c478bd9Sstevel@tonic-gate 	{
65177c478bd9Sstevel@tonic-gate 		long needed = 0;
65187c478bd9Sstevel@tonic-gate 
65197c478bd9Sstevel@tonic-gate 		if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
65207c478bd9Sstevel@tonic-gate 			continue;
65217c478bd9Sstevel@tonic-gate 		needed += fsize / FILE_SYS_BLKSIZE(i)
65227c478bd9Sstevel@tonic-gate 			  + ((fsize % FILE_SYS_BLKSIZE(i)
65237c478bd9Sstevel@tonic-gate 			      > 0) ? 1 : 0)
65247c478bd9Sstevel@tonic-gate 			  + MinBlocksFree;
65257c478bd9Sstevel@tonic-gate 		if (needed <= FILE_SYS_AVAIL(i))
65267c478bd9Sstevel@tonic-gate 			return true;
65277c478bd9Sstevel@tonic-gate 	}
65287c478bd9Sstevel@tonic-gate 	return false;
65297c478bd9Sstevel@tonic-gate }
65307c478bd9Sstevel@tonic-gate #endif /* _FFR_ANY_FREE_FS */
65317c478bd9Sstevel@tonic-gate 
65327c478bd9Sstevel@tonic-gate /*
65337c478bd9Sstevel@tonic-gate **  DISK_STATUS -- show amount of free space in queue directories
65347c478bd9Sstevel@tonic-gate **
65357c478bd9Sstevel@tonic-gate **	Parameters:
65367c478bd9Sstevel@tonic-gate **		out -- output file pointer.
65377c478bd9Sstevel@tonic-gate **		prefix -- string to output in front of each line.
65387c478bd9Sstevel@tonic-gate **
65397c478bd9Sstevel@tonic-gate **	Returns:
65407c478bd9Sstevel@tonic-gate **		none.
65417c478bd9Sstevel@tonic-gate */
65427c478bd9Sstevel@tonic-gate 
65437c478bd9Sstevel@tonic-gate void
65447c478bd9Sstevel@tonic-gate disk_status(out, prefix)
65457c478bd9Sstevel@tonic-gate 	SM_FILE_T *out;
65467c478bd9Sstevel@tonic-gate 	char *prefix;
65477c478bd9Sstevel@tonic-gate {
65487c478bd9Sstevel@tonic-gate 	int i;
65497c478bd9Sstevel@tonic-gate 	long avail, blksize;
65507c478bd9Sstevel@tonic-gate 	long free;
65517c478bd9Sstevel@tonic-gate 
65527c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
65537c478bd9Sstevel@tonic-gate 	{
65547c478bd9Sstevel@tonic-gate 		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
65557c478bd9Sstevel@tonic-gate 		if (avail >= 0 && blksize > 0)
65567c478bd9Sstevel@tonic-gate 		{
65577c478bd9Sstevel@tonic-gate 			free = (long)((double) avail *
65587c478bd9Sstevel@tonic-gate 				((double) blksize / 1024));
65597c478bd9Sstevel@tonic-gate 		}
65607c478bd9Sstevel@tonic-gate 		else
65617c478bd9Sstevel@tonic-gate 			free = -1;
65627c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
65637c478bd9Sstevel@tonic-gate 				"%s%d/%s/%ld\r\n",
65647c478bd9Sstevel@tonic-gate 				prefix, i,
65657c478bd9Sstevel@tonic-gate 				FILE_SYS_NAME(i),
65667c478bd9Sstevel@tonic-gate 					free);
65677c478bd9Sstevel@tonic-gate 	}
65687c478bd9Sstevel@tonic-gate }
65697c478bd9Sstevel@tonic-gate 
65707c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
65717c478bd9Sstevel@tonic-gate 
65727c478bd9Sstevel@tonic-gate /*
65737c478bd9Sstevel@tonic-gate **  INIT_SEM -- initialize semaphore system
65747c478bd9Sstevel@tonic-gate **
65757c478bd9Sstevel@tonic-gate **	Parameters:
65767c478bd9Sstevel@tonic-gate **		owner -- is this the owner of semaphores?
65777c478bd9Sstevel@tonic-gate **
65787c478bd9Sstevel@tonic-gate **	Returns:
65797c478bd9Sstevel@tonic-gate **		none.
65807c478bd9Sstevel@tonic-gate */
65817c478bd9Sstevel@tonic-gate 
65827c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
65837c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
65847c478bd9Sstevel@tonic-gate static int SemId = -1;		/* Semaphore Id */
65857c478bd9Sstevel@tonic-gate int SemKey = SM_SEM_KEY;
65867c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
65877c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
65887c478bd9Sstevel@tonic-gate 
65897c478bd9Sstevel@tonic-gate static void init_sem __P((bool));
65907c478bd9Sstevel@tonic-gate 
65917c478bd9Sstevel@tonic-gate static void
65927c478bd9Sstevel@tonic-gate init_sem(owner)
65937c478bd9Sstevel@tonic-gate 	bool owner;
65947c478bd9Sstevel@tonic-gate {
65957c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
65967c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
65977c478bd9Sstevel@tonic-gate 	SemId = sm_sem_start(SemKey, 1, 0, owner);
65987c478bd9Sstevel@tonic-gate 	if (SemId < 0)
65997c478bd9Sstevel@tonic-gate 	{
66007c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID,
66017c478bd9Sstevel@tonic-gate 			"func=init_sem, sem_key=%ld, sm_sem_start=%d",
66027c478bd9Sstevel@tonic-gate 			(long) SemKey, SemId);
66037c478bd9Sstevel@tonic-gate 		return;
66047c478bd9Sstevel@tonic-gate 	}
66057c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
66067c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
66077c478bd9Sstevel@tonic-gate 	return;
66087c478bd9Sstevel@tonic-gate }
66097c478bd9Sstevel@tonic-gate 
66107c478bd9Sstevel@tonic-gate /*
66117c478bd9Sstevel@tonic-gate **  STOP_SEM -- stop semaphore system
66127c478bd9Sstevel@tonic-gate **
66137c478bd9Sstevel@tonic-gate **	Parameters:
66147c478bd9Sstevel@tonic-gate **		owner -- is this the owner of semaphores?
66157c478bd9Sstevel@tonic-gate **
66167c478bd9Sstevel@tonic-gate **	Returns:
66177c478bd9Sstevel@tonic-gate **		none.
66187c478bd9Sstevel@tonic-gate */
66197c478bd9Sstevel@tonic-gate 
66207c478bd9Sstevel@tonic-gate static void stop_sem __P((bool));
66217c478bd9Sstevel@tonic-gate 
66227c478bd9Sstevel@tonic-gate static void
66237c478bd9Sstevel@tonic-gate stop_sem(owner)
66247c478bd9Sstevel@tonic-gate 	bool owner;
66257c478bd9Sstevel@tonic-gate {
66267c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
66277c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
66287c478bd9Sstevel@tonic-gate 	if (owner && SemId >= 0)
66297c478bd9Sstevel@tonic-gate 		sm_sem_stop(SemId);
66307c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
66317c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
66327c478bd9Sstevel@tonic-gate 	return;
66337c478bd9Sstevel@tonic-gate }
66347c478bd9Sstevel@tonic-gate 
66357c478bd9Sstevel@tonic-gate /*
66367c478bd9Sstevel@tonic-gate **  UPD_QS -- update information about queue when adding/deleting an entry
66377c478bd9Sstevel@tonic-gate **
66387c478bd9Sstevel@tonic-gate **	Parameters:
66397c478bd9Sstevel@tonic-gate **		e -- envelope.
66407c478bd9Sstevel@tonic-gate **		count -- add/remove entry (+1/0/-1: add/no change/remove)
66417c478bd9Sstevel@tonic-gate **		space -- update the space available as well.
66427c478bd9Sstevel@tonic-gate **			(>0/0/<0: add/no change/remove)
66437c478bd9Sstevel@tonic-gate **		where -- caller (for logging)
66447c478bd9Sstevel@tonic-gate **
66457c478bd9Sstevel@tonic-gate **	Returns:
66467c478bd9Sstevel@tonic-gate **		none.
66477c478bd9Sstevel@tonic-gate **
66487c478bd9Sstevel@tonic-gate **	Side Effects:
66497c478bd9Sstevel@tonic-gate **		Modifies available space in filesystem.
66507c478bd9Sstevel@tonic-gate **		Changes number of entries in queue directory.
66517c478bd9Sstevel@tonic-gate */
66527c478bd9Sstevel@tonic-gate 
66537c478bd9Sstevel@tonic-gate void
66547c478bd9Sstevel@tonic-gate upd_qs(e, count, space, where)
66557c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
66567c478bd9Sstevel@tonic-gate 	int count;
66577c478bd9Sstevel@tonic-gate 	int space;
66587c478bd9Sstevel@tonic-gate 	char *where;
66597c478bd9Sstevel@tonic-gate {
66607c478bd9Sstevel@tonic-gate 	short fidx;
66617c478bd9Sstevel@tonic-gate 	int idx;
66627c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66637c478bd9Sstevel@tonic-gate 	int r;
66647c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66657c478bd9Sstevel@tonic-gate 	long s;
66667c478bd9Sstevel@tonic-gate 
66677c478bd9Sstevel@tonic-gate 	if (ShmId == SM_SHM_NO_ID || e == NULL)
66687c478bd9Sstevel@tonic-gate 		return;
66697c478bd9Sstevel@tonic-gate 	if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
66707c478bd9Sstevel@tonic-gate 		return;
66717c478bd9Sstevel@tonic-gate 	idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
66727c478bd9Sstevel@tonic-gate 	if (tTd(73,2))
66737c478bd9Sstevel@tonic-gate 		sm_dprintf("func=upd_qs, count=%d, space=%d, where=%s, idx=%d, entries=%d\n",
66747c478bd9Sstevel@tonic-gate 			count, space, where, idx, QSHM_ENTRIES(idx));
66757c478bd9Sstevel@tonic-gate 
66767c478bd9Sstevel@tonic-gate 	/* XXX in theory this needs to be protected with a mutex */
66777c478bd9Sstevel@tonic-gate 	if (QSHM_ENTRIES(idx) >= 0 && count != 0)
66787c478bd9Sstevel@tonic-gate 	{
66797c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66807c478bd9Sstevel@tonic-gate 		r = sm_sem_acq(SemId, 0, 1);
66817c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66827c478bd9Sstevel@tonic-gate 		QSHM_ENTRIES(idx) += count;
66837c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66847c478bd9Sstevel@tonic-gate 		if (r >= 0)
66857c478bd9Sstevel@tonic-gate 			r = sm_sem_rel(SemId, 0, 1);
66867c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66877c478bd9Sstevel@tonic-gate 	}
66887c478bd9Sstevel@tonic-gate 
66897c478bd9Sstevel@tonic-gate 	fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
66907c478bd9Sstevel@tonic-gate 	if (fidx < 0)
66917c478bd9Sstevel@tonic-gate 		return;
66927c478bd9Sstevel@tonic-gate 
66937c478bd9Sstevel@tonic-gate 	/* update available space also?  (might be loseqfile) */
66947c478bd9Sstevel@tonic-gate 	if (space == 0)
66957c478bd9Sstevel@tonic-gate 		return;
66967c478bd9Sstevel@tonic-gate 
66977c478bd9Sstevel@tonic-gate 	/* convert size to blocks; this causes rounding errors */
66987c478bd9Sstevel@tonic-gate 	s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
66997c478bd9Sstevel@tonic-gate 	if (s == 0)
67007c478bd9Sstevel@tonic-gate 		return;
67017c478bd9Sstevel@tonic-gate 
67027c478bd9Sstevel@tonic-gate 	/* XXX in theory this needs to be protected with a mutex */
67037c478bd9Sstevel@tonic-gate 	if (space > 0)
67047c478bd9Sstevel@tonic-gate 		FILE_SYS_AVAIL(fidx) += s;
67057c478bd9Sstevel@tonic-gate 	else
67067c478bd9Sstevel@tonic-gate 		FILE_SYS_AVAIL(fidx) -= s;
67077c478bd9Sstevel@tonic-gate 
67087c478bd9Sstevel@tonic-gate }
67097c478bd9Sstevel@tonic-gate 
67107c478bd9Sstevel@tonic-gate static bool write_key_file __P((char *, long));
67117c478bd9Sstevel@tonic-gate static long read_key_file __P((char *, long));
67127c478bd9Sstevel@tonic-gate 
67137c478bd9Sstevel@tonic-gate /*
67147c478bd9Sstevel@tonic-gate **  WRITE_KEY_FILE -- record some key into a file.
67157c478bd9Sstevel@tonic-gate **
67167c478bd9Sstevel@tonic-gate **	Parameters:
67177c478bd9Sstevel@tonic-gate **		keypath -- file name.
67187c478bd9Sstevel@tonic-gate **		key -- key to write.
67197c478bd9Sstevel@tonic-gate **
67207c478bd9Sstevel@tonic-gate **	Returns:
67217c478bd9Sstevel@tonic-gate **		true iff file could be written.
67227c478bd9Sstevel@tonic-gate **
67237c478bd9Sstevel@tonic-gate **	Side Effects:
67247c478bd9Sstevel@tonic-gate **		writes file.
67257c478bd9Sstevel@tonic-gate */
67267c478bd9Sstevel@tonic-gate 
67277c478bd9Sstevel@tonic-gate static bool
67287c478bd9Sstevel@tonic-gate write_key_file(keypath, key)
67297c478bd9Sstevel@tonic-gate 	char *keypath;
67307c478bd9Sstevel@tonic-gate 	long key;
67317c478bd9Sstevel@tonic-gate {
67327c478bd9Sstevel@tonic-gate 	bool ok;
67337c478bd9Sstevel@tonic-gate 	long sff;
67347c478bd9Sstevel@tonic-gate 	SM_FILE_T *keyf;
67357c478bd9Sstevel@tonic-gate 
67367c478bd9Sstevel@tonic-gate 	ok = false;
67377c478bd9Sstevel@tonic-gate 	if (keypath == NULL || *keypath == '\0')
67387c478bd9Sstevel@tonic-gate 		return ok;
67397c478bd9Sstevel@tonic-gate 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
67407c478bd9Sstevel@tonic-gate 	if (TrustedUid != 0 && RealUid == TrustedUid)
67417c478bd9Sstevel@tonic-gate 		sff |= SFF_OPENASROOT;
67427c478bd9Sstevel@tonic-gate 	keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff);
67437c478bd9Sstevel@tonic-gate 	if (keyf == NULL)
67447c478bd9Sstevel@tonic-gate 	{
67457c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
67467c478bd9Sstevel@tonic-gate 			  keypath, sm_errstring(errno));
67477c478bd9Sstevel@tonic-gate 	}
67487c478bd9Sstevel@tonic-gate 	else
67497c478bd9Sstevel@tonic-gate 	{
675049218d4fSjbeck 		if (geteuid() == 0 && RunAsUid != 0)
675149218d4fSjbeck 		{
675249218d4fSjbeck #  if HASFCHOWN
675349218d4fSjbeck 			int fd;
675449218d4fSjbeck 
675549218d4fSjbeck 			fd = keyf->f_file;
675649218d4fSjbeck 			if (fd >= 0 && fchown(fd, RunAsUid, -1) < 0)
675749218d4fSjbeck 			{
675849218d4fSjbeck 				int err = errno;
675949218d4fSjbeck 
676049218d4fSjbeck 				sm_syslog(LOG_ALERT, NOQID,
676149218d4fSjbeck 					  "ownership change on %s to %d failed: %s",
676249218d4fSjbeck 					  keypath, RunAsUid, sm_errstring(err));
676349218d4fSjbeck 			}
676449218d4fSjbeck #  endif /* HASFCHOWN */
676549218d4fSjbeck 		}
67667c478bd9Sstevel@tonic-gate 		ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
67677c478bd9Sstevel@tonic-gate 		     SM_IO_EOF;
67687c478bd9Sstevel@tonic-gate 		ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok;
67697c478bd9Sstevel@tonic-gate 	}
67707c478bd9Sstevel@tonic-gate 	return ok;
67717c478bd9Sstevel@tonic-gate }
67727c478bd9Sstevel@tonic-gate 
67737c478bd9Sstevel@tonic-gate /*
67747c478bd9Sstevel@tonic-gate **  READ_KEY_FILE -- read a key from a file.
67757c478bd9Sstevel@tonic-gate **
67767c478bd9Sstevel@tonic-gate **	Parameters:
67777c478bd9Sstevel@tonic-gate **		keypath -- file name.
67787c478bd9Sstevel@tonic-gate **		key -- default key.
67797c478bd9Sstevel@tonic-gate **
67807c478bd9Sstevel@tonic-gate **	Returns:
67817c478bd9Sstevel@tonic-gate **		key.
67827c478bd9Sstevel@tonic-gate */
67837c478bd9Sstevel@tonic-gate 
67847c478bd9Sstevel@tonic-gate static long
67857c478bd9Sstevel@tonic-gate read_key_file(keypath, key)
67867c478bd9Sstevel@tonic-gate 	char *keypath;
67877c478bd9Sstevel@tonic-gate 	long key;
67887c478bd9Sstevel@tonic-gate {
67897c478bd9Sstevel@tonic-gate 	int r;
67907c478bd9Sstevel@tonic-gate 	long sff, n;
67917c478bd9Sstevel@tonic-gate 	SM_FILE_T *keyf;
67927c478bd9Sstevel@tonic-gate 
67937c478bd9Sstevel@tonic-gate 	if (keypath == NULL || *keypath == '\0')
67947c478bd9Sstevel@tonic-gate 		return key;
67957c478bd9Sstevel@tonic-gate 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
67967c478bd9Sstevel@tonic-gate 	if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid))
67977c478bd9Sstevel@tonic-gate 		sff |= SFF_OPENASROOT;
67987c478bd9Sstevel@tonic-gate 	keyf = safefopen(keypath, O_RDONLY, FileMode, sff);
67997c478bd9Sstevel@tonic-gate 	if (keyf == NULL)
68007c478bd9Sstevel@tonic-gate 	{
68017c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s",
68027c478bd9Sstevel@tonic-gate 			  keypath, sm_errstring(errno));
68037c478bd9Sstevel@tonic-gate 	}
68047c478bd9Sstevel@tonic-gate 	else
68057c478bd9Sstevel@tonic-gate 	{
68067c478bd9Sstevel@tonic-gate 		r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n);
68077c478bd9Sstevel@tonic-gate 		if (r == 1)
68087c478bd9Sstevel@tonic-gate 			key = n;
68097c478bd9Sstevel@tonic-gate 		(void) sm_io_close(keyf, SM_TIME_DEFAULT);
68107c478bd9Sstevel@tonic-gate 	}
68117c478bd9Sstevel@tonic-gate 	return key;
68127c478bd9Sstevel@tonic-gate }
68137c478bd9Sstevel@tonic-gate 
68147c478bd9Sstevel@tonic-gate /*
68157c478bd9Sstevel@tonic-gate **  INIT_SHM -- initialize shared memory structure
68167c478bd9Sstevel@tonic-gate **
68177c478bd9Sstevel@tonic-gate **	Initialize or attach to shared memory segment.
68187c478bd9Sstevel@tonic-gate **	Currently it is not a fatal error if this doesn't work.
68197c478bd9Sstevel@tonic-gate **	However, it causes us to have a "fallback" storage location
68207c478bd9Sstevel@tonic-gate **	for everything that is supposed to be in the shared memory,
68217c478bd9Sstevel@tonic-gate **	which makes the code slightly ugly.
68227c478bd9Sstevel@tonic-gate **
68237c478bd9Sstevel@tonic-gate **	Parameters:
68247c478bd9Sstevel@tonic-gate **		qn -- number of queue directories.
68257c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory.
68267c478bd9Sstevel@tonic-gate **		hash -- identifies data that is stored in shared memory.
68277c478bd9Sstevel@tonic-gate **
68287c478bd9Sstevel@tonic-gate **	Returns:
68297c478bd9Sstevel@tonic-gate **		none.
68307c478bd9Sstevel@tonic-gate */
68317c478bd9Sstevel@tonic-gate 
68327c478bd9Sstevel@tonic-gate static void init_shm __P((int, bool, unsigned int));
68337c478bd9Sstevel@tonic-gate 
68347c478bd9Sstevel@tonic-gate static void
68357c478bd9Sstevel@tonic-gate init_shm(qn, owner, hash)
68367c478bd9Sstevel@tonic-gate 	int qn;
68377c478bd9Sstevel@tonic-gate 	bool owner;
68387c478bd9Sstevel@tonic-gate 	unsigned int hash;
68397c478bd9Sstevel@tonic-gate {
68407c478bd9Sstevel@tonic-gate 	int i;
68417c478bd9Sstevel@tonic-gate 	int count;
68427c478bd9Sstevel@tonic-gate 	int save_errno;
68437c478bd9Sstevel@tonic-gate 	bool keyselect;
68447c478bd9Sstevel@tonic-gate 
68457c478bd9Sstevel@tonic-gate 	PtrFileSys = &FileSys[0];
68467c478bd9Sstevel@tonic-gate 	PNumFileSys = &Numfilesys;
68477c478bd9Sstevel@tonic-gate /* if this "key" is specified: select one yourself */
6848058561cbSjbeck #define SEL_SHM_KEY	((key_t) -1)
6849058561cbSjbeck #define FIRST_SHM_KEY	25
68507c478bd9Sstevel@tonic-gate 
68517c478bd9Sstevel@tonic-gate 	/* This allows us to disable shared memory at runtime. */
68527c478bd9Sstevel@tonic-gate 	if (ShmKey == 0)
68537c478bd9Sstevel@tonic-gate 		return;
68547c478bd9Sstevel@tonic-gate 
68557c478bd9Sstevel@tonic-gate 	count = 0;
68567c478bd9Sstevel@tonic-gate 	shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
68577c478bd9Sstevel@tonic-gate 	keyselect = ShmKey == SEL_SHM_KEY;
68587c478bd9Sstevel@tonic-gate 	if (keyselect)
68597c478bd9Sstevel@tonic-gate 	{
68607c478bd9Sstevel@tonic-gate 		if (owner)
68617c478bd9Sstevel@tonic-gate 			ShmKey = FIRST_SHM_KEY;
68627c478bd9Sstevel@tonic-gate 		else
68637c478bd9Sstevel@tonic-gate 		{
6864058561cbSjbeck 			errno = 0;
68657c478bd9Sstevel@tonic-gate 			ShmKey = read_key_file(ShmKeyFile, ShmKey);
68667c478bd9Sstevel@tonic-gate 			keyselect = false;
68677c478bd9Sstevel@tonic-gate 			if (ShmKey == SEL_SHM_KEY)
6868058561cbSjbeck 			{
6869058561cbSjbeck 				save_errno = (errno != 0) ? errno : EINVAL;
68707c478bd9Sstevel@tonic-gate 				goto error;
6871058561cbSjbeck 			}
68727c478bd9Sstevel@tonic-gate 		}
68737c478bd9Sstevel@tonic-gate 	}
68747c478bd9Sstevel@tonic-gate 	for (;;)
68757c478bd9Sstevel@tonic-gate 	{
68767c478bd9Sstevel@tonic-gate 		/* allow read/write access for group? */
68777c478bd9Sstevel@tonic-gate 		Pshm = sm_shmstart(ShmKey, shms,
68787c478bd9Sstevel@tonic-gate 				SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3),
68797c478bd9Sstevel@tonic-gate 				&ShmId, owner);
68807c478bd9Sstevel@tonic-gate 		save_errno = errno;
68817c478bd9Sstevel@tonic-gate 		if (Pshm != NULL || !sm_file_exists(save_errno))
68827c478bd9Sstevel@tonic-gate 			break;
68837c478bd9Sstevel@tonic-gate 		if (++count >= 3)
68847c478bd9Sstevel@tonic-gate 		{
68857c478bd9Sstevel@tonic-gate 			if (keyselect)
68867c478bd9Sstevel@tonic-gate 			{
68877c478bd9Sstevel@tonic-gate 				++ShmKey;
68887c478bd9Sstevel@tonic-gate 
68897c478bd9Sstevel@tonic-gate 				/* back where we started? */
68907c478bd9Sstevel@tonic-gate 				if (ShmKey == SEL_SHM_KEY)
68917c478bd9Sstevel@tonic-gate 					break;
68927c478bd9Sstevel@tonic-gate 				continue;
68937c478bd9Sstevel@tonic-gate 			}
68947c478bd9Sstevel@tonic-gate 			break;
68957c478bd9Sstevel@tonic-gate 		}
6896058561cbSjbeck 
68977c478bd9Sstevel@tonic-gate 		/* only sleep if we are at the first key */
68987c478bd9Sstevel@tonic-gate 		if (!keyselect || ShmKey == SEL_SHM_KEY)
6899058561cbSjbeck 			sleep(count);
69007c478bd9Sstevel@tonic-gate 	}
69017c478bd9Sstevel@tonic-gate 	if (Pshm != NULL)
69027c478bd9Sstevel@tonic-gate 	{
69037c478bd9Sstevel@tonic-gate 		int *p;
69047c478bd9Sstevel@tonic-gate 
69057c478bd9Sstevel@tonic-gate 		if (keyselect)
69067c478bd9Sstevel@tonic-gate 			(void) write_key_file(ShmKeyFile, (long) ShmKey);
69077c478bd9Sstevel@tonic-gate 		if (owner && RunAsUid != 0)
69087c478bd9Sstevel@tonic-gate 		{
6909445f2479Sjbeck 			i = sm_shmsetowner(ShmId, RunAsUid, RunAsGid, 0660);
69107c478bd9Sstevel@tonic-gate 			if (i != 0)
69117c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
6912445f2479Sjbeck 					"key=%ld, sm_shmsetowner=%d, RunAsUid=%d, RunAsGid=%d",
6913445f2479Sjbeck 					(long) ShmKey, i, RunAsUid, RunAsGid);
69147c478bd9Sstevel@tonic-gate 		}
69157c478bd9Sstevel@tonic-gate 		p = (int *) Pshm;
69167c478bd9Sstevel@tonic-gate 		if (owner)
69177c478bd9Sstevel@tonic-gate 		{
69187c478bd9Sstevel@tonic-gate 			*p = (int) shms;
69197c478bd9Sstevel@tonic-gate 			*((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
69207c478bd9Sstevel@tonic-gate 			p = (int *) SHM_OFF_TAG(Pshm);
69217c478bd9Sstevel@tonic-gate 			*p = hash;
69227c478bd9Sstevel@tonic-gate 		}
69237c478bd9Sstevel@tonic-gate 		else
69247c478bd9Sstevel@tonic-gate 		{
69257c478bd9Sstevel@tonic-gate 			if (*p != (int) shms)
69267c478bd9Sstevel@tonic-gate 			{
69277c478bd9Sstevel@tonic-gate 				save_errno = EINVAL;
69287c478bd9Sstevel@tonic-gate 				cleanup_shm(false);
69297c478bd9Sstevel@tonic-gate 				goto error;
69307c478bd9Sstevel@tonic-gate 			}
69317c478bd9Sstevel@tonic-gate 			p = (int *) SHM_OFF_TAG(Pshm);
69327c478bd9Sstevel@tonic-gate 			if (*p != (int) hash)
69337c478bd9Sstevel@tonic-gate 			{
69347c478bd9Sstevel@tonic-gate 				save_errno = EINVAL;
69357c478bd9Sstevel@tonic-gate 				cleanup_shm(false);
69367c478bd9Sstevel@tonic-gate 				goto error;
69377c478bd9Sstevel@tonic-gate 			}
69387c478bd9Sstevel@tonic-gate 
69397c478bd9Sstevel@tonic-gate 			/*
69407c478bd9Sstevel@tonic-gate 			**  XXX how to check the pid?
69417c478bd9Sstevel@tonic-gate 			**  Read it from the pid-file? That does
69427c478bd9Sstevel@tonic-gate 			**  not need to exist.
69437c478bd9Sstevel@tonic-gate 			**  We could disable shm if we can't confirm
69447c478bd9Sstevel@tonic-gate 			**  that it is the right one.
69457c478bd9Sstevel@tonic-gate 			*/
69467c478bd9Sstevel@tonic-gate 		}
69477c478bd9Sstevel@tonic-gate 
69487c478bd9Sstevel@tonic-gate 		PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
69497c478bd9Sstevel@tonic-gate 		PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
69507c478bd9Sstevel@tonic-gate 		QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
69517c478bd9Sstevel@tonic-gate 		PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
69527c478bd9Sstevel@tonic-gate 		*PRSATmpCnt = 0;
69537c478bd9Sstevel@tonic-gate 		if (owner)
69547c478bd9Sstevel@tonic-gate 		{
69557c478bd9Sstevel@tonic-gate 			/* initialize values in shared memory */
69567c478bd9Sstevel@tonic-gate 			NumFileSys = 0;
69577c478bd9Sstevel@tonic-gate 			for (i = 0; i < qn; i++)
69587c478bd9Sstevel@tonic-gate 				QShm[i].qs_entries = -1;
69597c478bd9Sstevel@tonic-gate 		}
69607c478bd9Sstevel@tonic-gate 		init_sem(owner);
69617c478bd9Sstevel@tonic-gate 		return;
69627c478bd9Sstevel@tonic-gate 	}
69637c478bd9Sstevel@tonic-gate   error:
69647c478bd9Sstevel@tonic-gate 	if (LogLevel > (owner ? 8 : 11))
69657c478bd9Sstevel@tonic-gate 	{
69667c478bd9Sstevel@tonic-gate 		sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
69677c478bd9Sstevel@tonic-gate 			  "can't %s shared memory, key=%ld: %s",
69687c478bd9Sstevel@tonic-gate 			  owner ? "initialize" : "attach to",
69697c478bd9Sstevel@tonic-gate 			  (long) ShmKey, sm_errstring(save_errno));
69707c478bd9Sstevel@tonic-gate 	}
69717c478bd9Sstevel@tonic-gate }
69727c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
69737c478bd9Sstevel@tonic-gate 
69747c478bd9Sstevel@tonic-gate 
69757c478bd9Sstevel@tonic-gate /*
6976058561cbSjbeck **  SETUP_QUEUES -- set up all queue groups
69777c478bd9Sstevel@tonic-gate **
69787c478bd9Sstevel@tonic-gate **	Parameters:
6979058561cbSjbeck **		owner -- owner of shared memory?
69807c478bd9Sstevel@tonic-gate **
69817c478bd9Sstevel@tonic-gate **	Returns:
69827c478bd9Sstevel@tonic-gate **		none.
69837c478bd9Sstevel@tonic-gate **
69847c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
69857c478bd9Sstevel@tonic-gate **	Side Effects:
69867c478bd9Sstevel@tonic-gate **		attaches shared memory.
69877c478bd9Sstevel@tonic-gate #endif * SM_CONF_SHM *
69887c478bd9Sstevel@tonic-gate */
69897c478bd9Sstevel@tonic-gate 
69907c478bd9Sstevel@tonic-gate void
69917c478bd9Sstevel@tonic-gate setup_queues(owner)
69927c478bd9Sstevel@tonic-gate 	bool owner;
69937c478bd9Sstevel@tonic-gate {
69947c478bd9Sstevel@tonic-gate 	int i, qn, len;
69957c478bd9Sstevel@tonic-gate 	unsigned int hashval;
69967c478bd9Sstevel@tonic-gate 	time_t now;
69977c478bd9Sstevel@tonic-gate 	char basedir[MAXPATHLEN];
69987c478bd9Sstevel@tonic-gate 	struct stat st;
69997c478bd9Sstevel@tonic-gate 
70007c478bd9Sstevel@tonic-gate 	/*
70017c478bd9Sstevel@tonic-gate 	**  Determine basedir for all queue directories.
70027c478bd9Sstevel@tonic-gate 	**  All queue directories must be (first level) subdirectories
70037c478bd9Sstevel@tonic-gate 	**  of the basedir.  The basedir is the QueueDir
70047c478bd9Sstevel@tonic-gate 	**  without wildcards, but with trailing /
70057c478bd9Sstevel@tonic-gate 	*/
70067c478bd9Sstevel@tonic-gate 
70077c478bd9Sstevel@tonic-gate 	hashval = 0;
70087c478bd9Sstevel@tonic-gate 	errno = 0;
7009058561cbSjbeck 	len = sm_strlcpy(basedir, QueueDir, sizeof(basedir));
70107c478bd9Sstevel@tonic-gate 
70117c478bd9Sstevel@tonic-gate 	/* Provide space for trailing '/' */
7012058561cbSjbeck 	if (len >= sizeof(basedir) - 1)
70137c478bd9Sstevel@tonic-gate 	{
70147c478bd9Sstevel@tonic-gate 		syserr("QueueDirectory: path too long: %d,  max %d",
7015058561cbSjbeck 			len, (int) sizeof(basedir) - 1);
70167c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
70177c478bd9Sstevel@tonic-gate 		return;
70187c478bd9Sstevel@tonic-gate 	}
70197c478bd9Sstevel@tonic-gate 	SM_ASSERT(len > 0);
70207c478bd9Sstevel@tonic-gate 	if (basedir[len - 1] == '*')
70217c478bd9Sstevel@tonic-gate 	{
70227c478bd9Sstevel@tonic-gate 		char *cp;
70237c478bd9Sstevel@tonic-gate 
70247c478bd9Sstevel@tonic-gate 		cp = SM_LAST_DIR_DELIM(basedir);
70257c478bd9Sstevel@tonic-gate 		if (cp == NULL)
70267c478bd9Sstevel@tonic-gate 		{
70277c478bd9Sstevel@tonic-gate 			syserr("QueueDirectory: can not wildcard relative path \"%s\"",
70287c478bd9Sstevel@tonic-gate 				QueueDir);
70297c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
70307c478bd9Sstevel@tonic-gate 				sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
70317c478bd9Sstevel@tonic-gate 					QueueDir);
70327c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
70337c478bd9Sstevel@tonic-gate 			return;
70347c478bd9Sstevel@tonic-gate 		}
70357c478bd9Sstevel@tonic-gate 
70367c478bd9Sstevel@tonic-gate 		/* cut off wildcard pattern */
70377c478bd9Sstevel@tonic-gate 		*++cp = '\0';
70387c478bd9Sstevel@tonic-gate 		len = cp - basedir;
70397c478bd9Sstevel@tonic-gate 	}
70407c478bd9Sstevel@tonic-gate 	else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
70417c478bd9Sstevel@tonic-gate 	{
70427c478bd9Sstevel@tonic-gate 		/* append trailing slash since it is a directory */
70437c478bd9Sstevel@tonic-gate 		basedir[len] = '/';
70447c478bd9Sstevel@tonic-gate 		basedir[++len] = '\0';
70457c478bd9Sstevel@tonic-gate 	}
70467c478bd9Sstevel@tonic-gate 
70477c478bd9Sstevel@tonic-gate 	/* len counts up to the last directory delimiter */
70487c478bd9Sstevel@tonic-gate 	SM_ASSERT(basedir[len - 1] == '/');
70497c478bd9Sstevel@tonic-gate 
70507c478bd9Sstevel@tonic-gate 	if (chdir(basedir) < 0)
70517c478bd9Sstevel@tonic-gate 	{
70527c478bd9Sstevel@tonic-gate 		int save_errno = errno;
70537c478bd9Sstevel@tonic-gate 
70547c478bd9Sstevel@tonic-gate 		syserr("can not chdir(%s)", basedir);
70557c478bd9Sstevel@tonic-gate 		if (save_errno == EACCES)
70567c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
70577c478bd9Sstevel@tonic-gate 				"Program mode requires special privileges, e.g., root or TrustedUser.\n");
70587c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
70597c478bd9Sstevel@tonic-gate 			sm_dprintf("setup_queues: \"%s\": %s\n",
70607c478bd9Sstevel@tonic-gate 				   basedir, sm_errstring(errno));
70617c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
70627c478bd9Sstevel@tonic-gate 		return;
70637c478bd9Sstevel@tonic-gate 	}
70647c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
70657c478bd9Sstevel@tonic-gate 	hashval = hash_q(basedir, hashval);
70667c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
70677c478bd9Sstevel@tonic-gate 
70687c478bd9Sstevel@tonic-gate 	/* initialize for queue runs */
70697c478bd9Sstevel@tonic-gate 	DoQueueRun = false;
70707c478bd9Sstevel@tonic-gate 	now = curtime();
70717c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
70727c478bd9Sstevel@tonic-gate 		Queue[i]->qg_nextrun = now;
70737c478bd9Sstevel@tonic-gate 
70747c478bd9Sstevel@tonic-gate 
70757c478bd9Sstevel@tonic-gate 	if (UseMSP && OpMode != MD_TEST)
70767c478bd9Sstevel@tonic-gate 	{
70777c478bd9Sstevel@tonic-gate 		long sff = SFF_CREAT;
70787c478bd9Sstevel@tonic-gate 
70797c478bd9Sstevel@tonic-gate 		if (stat(".", &st) < 0)
70807c478bd9Sstevel@tonic-gate 		{
70817c478bd9Sstevel@tonic-gate 			syserr("can not stat(%s)", basedir);
70827c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
70837c478bd9Sstevel@tonic-gate 				sm_dprintf("setup_queues: \"%s\": %s\n",
70847c478bd9Sstevel@tonic-gate 					   basedir, sm_errstring(errno));
70857c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
70867c478bd9Sstevel@tonic-gate 			return;
70877c478bd9Sstevel@tonic-gate 		}
70887c478bd9Sstevel@tonic-gate 		if (RunAsUid == 0)
70897c478bd9Sstevel@tonic-gate 			sff |= SFF_ROOTOK;
70907c478bd9Sstevel@tonic-gate 
70917c478bd9Sstevel@tonic-gate 		/*
70927c478bd9Sstevel@tonic-gate 		**  Check queue directory permissions.
70937c478bd9Sstevel@tonic-gate 		**	Can we write to a group writable queue directory?
70947c478bd9Sstevel@tonic-gate 		*/
70957c478bd9Sstevel@tonic-gate 
70967c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode) &&
70977c478bd9Sstevel@tonic-gate 		    bitset(S_IWGRP, st.st_mode) &&
70987c478bd9Sstevel@tonic-gate 		    safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
70997c478bd9Sstevel@tonic-gate 			     QueueFileMode, NULL) != 0)
71007c478bd9Sstevel@tonic-gate 		{
71017c478bd9Sstevel@tonic-gate 			syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
71027c478bd9Sstevel@tonic-gate 				basedir, (int) RunAsGid, (int) st.st_gid);
71037c478bd9Sstevel@tonic-gate 		}
71047c478bd9Sstevel@tonic-gate 		if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
71057c478bd9Sstevel@tonic-gate 		{
71067c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
71077c478bd9Sstevel@tonic-gate 			syserr("dangerous permissions=%o on queue directory %s",
71087c478bd9Sstevel@tonic-gate 				(int) st.st_mode, basedir);
71097c478bd9Sstevel@tonic-gate #else /* _FFR_MSP_PARANOIA */
71107c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
71117c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
71127c478bd9Sstevel@tonic-gate 					  "dangerous permissions=%o on queue directory %s",
71137c478bd9Sstevel@tonic-gate 					  (int) st.st_mode, basedir);
71147c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
71157c478bd9Sstevel@tonic-gate 		}
71167c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
71177c478bd9Sstevel@tonic-gate 		if (NumQueue > 1)
71187c478bd9Sstevel@tonic-gate 			syserr("can not use multiple queues for MSP");
71197c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
71207c478bd9Sstevel@tonic-gate 	}
71217c478bd9Sstevel@tonic-gate 
71227c478bd9Sstevel@tonic-gate 	/* initial number of queue directories */
71237c478bd9Sstevel@tonic-gate 	qn = 0;
71247c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
71257c478bd9Sstevel@tonic-gate 		qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
71267c478bd9Sstevel@tonic-gate 
71277c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
71287c478bd9Sstevel@tonic-gate 	init_shm(qn, owner, hashval);
71297c478bd9Sstevel@tonic-gate 	i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
71307c478bd9Sstevel@tonic-gate 	if (i == FSF_NOT_FOUND)
71317c478bd9Sstevel@tonic-gate 	{
71327c478bd9Sstevel@tonic-gate 		/*
71337c478bd9Sstevel@tonic-gate 		**  We didn't get the right filesystem data
71347c478bd9Sstevel@tonic-gate 		**  This may happen if we don't have the right shared memory.
71357c478bd9Sstevel@tonic-gate 		**  So let's do this without shared memory.
71367c478bd9Sstevel@tonic-gate 		*/
71377c478bd9Sstevel@tonic-gate 
71387c478bd9Sstevel@tonic-gate 		SM_ASSERT(!owner);
71397c478bd9Sstevel@tonic-gate 		cleanup_shm(false);	/* release shared memory */
71407c478bd9Sstevel@tonic-gate 		i = filesys_setup(false);
71417c478bd9Sstevel@tonic-gate 		if (i < 0)
71427c478bd9Sstevel@tonic-gate 			syserr("filesys_setup failed twice, result=%d", i);
71437c478bd9Sstevel@tonic-gate 		else if (LogLevel > 8)
71447c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
71457c478bd9Sstevel@tonic-gate 				  "shared memory does not contain expected data, ignored");
71467c478bd9Sstevel@tonic-gate 	}
71477c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
71487c478bd9Sstevel@tonic-gate 	i = filesys_setup(true);
71497c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
71507c478bd9Sstevel@tonic-gate 	if (i < 0)
71517c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
71527c478bd9Sstevel@tonic-gate }
71537c478bd9Sstevel@tonic-gate 
71547c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
71557c478bd9Sstevel@tonic-gate /*
71567c478bd9Sstevel@tonic-gate **  CLEANUP_SHM -- do some cleanup work for shared memory etc
71577c478bd9Sstevel@tonic-gate **
71587c478bd9Sstevel@tonic-gate **	Parameters:
71597c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory?
71607c478bd9Sstevel@tonic-gate **
71617c478bd9Sstevel@tonic-gate **	Returns:
71627c478bd9Sstevel@tonic-gate **		none.
71637c478bd9Sstevel@tonic-gate **
71647c478bd9Sstevel@tonic-gate **	Side Effects:
71657c478bd9Sstevel@tonic-gate **		detaches shared memory.
71667c478bd9Sstevel@tonic-gate */
71677c478bd9Sstevel@tonic-gate 
71687c478bd9Sstevel@tonic-gate void
71697c478bd9Sstevel@tonic-gate cleanup_shm(owner)
71707c478bd9Sstevel@tonic-gate 	bool owner;
71717c478bd9Sstevel@tonic-gate {
71727c478bd9Sstevel@tonic-gate 	if (ShmId != SM_SHM_NO_ID)
71737c478bd9Sstevel@tonic-gate 	{
71747c478bd9Sstevel@tonic-gate 		if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
71757c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s",
71767c478bd9Sstevel@tonic-gate 				  sm_errstring(errno));
71777c478bd9Sstevel@tonic-gate 		Pshm = NULL;
71787c478bd9Sstevel@tonic-gate 		ShmId = SM_SHM_NO_ID;
71797c478bd9Sstevel@tonic-gate 	}
71807c478bd9Sstevel@tonic-gate 	stop_sem(owner);
71817c478bd9Sstevel@tonic-gate }
71827c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
71837c478bd9Sstevel@tonic-gate 
71847c478bd9Sstevel@tonic-gate /*
71857c478bd9Sstevel@tonic-gate **  CLEANUP_QUEUES -- do some cleanup work for queues
71867c478bd9Sstevel@tonic-gate **
71877c478bd9Sstevel@tonic-gate **	Parameters:
71887c478bd9Sstevel@tonic-gate **		none.
71897c478bd9Sstevel@tonic-gate **
71907c478bd9Sstevel@tonic-gate **	Returns:
71917c478bd9Sstevel@tonic-gate **		none.
71927c478bd9Sstevel@tonic-gate **
71937c478bd9Sstevel@tonic-gate */
71947c478bd9Sstevel@tonic-gate 
71957c478bd9Sstevel@tonic-gate void
71967c478bd9Sstevel@tonic-gate cleanup_queues()
71977c478bd9Sstevel@tonic-gate {
71987c478bd9Sstevel@tonic-gate 	sync_queue_time();
71997c478bd9Sstevel@tonic-gate }
72007c478bd9Sstevel@tonic-gate /*
72017c478bd9Sstevel@tonic-gate **  SET_DEF_QUEUEVAL -- set default values for a queue group.
72027c478bd9Sstevel@tonic-gate **
72037c478bd9Sstevel@tonic-gate **	Parameters:
72047c478bd9Sstevel@tonic-gate **		qg -- queue group
72057c478bd9Sstevel@tonic-gate **		all -- set all values (true for default group)?
72067c478bd9Sstevel@tonic-gate **
72077c478bd9Sstevel@tonic-gate **	Returns:
72087c478bd9Sstevel@tonic-gate **		none.
72097c478bd9Sstevel@tonic-gate **
72107c478bd9Sstevel@tonic-gate **	Side Effects:
72117c478bd9Sstevel@tonic-gate **		sets default values for the queue group.
72127c478bd9Sstevel@tonic-gate */
72137c478bd9Sstevel@tonic-gate 
72147c478bd9Sstevel@tonic-gate void
72157c478bd9Sstevel@tonic-gate set_def_queueval(qg, all)
72167c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
72177c478bd9Sstevel@tonic-gate 	bool all;
72187c478bd9Sstevel@tonic-gate {
72197c478bd9Sstevel@tonic-gate 	if (bitnset(QD_DEFINED, qg->qg_flags))
72207c478bd9Sstevel@tonic-gate 		return;
72217c478bd9Sstevel@tonic-gate 	if (all)
72227c478bd9Sstevel@tonic-gate 		qg->qg_qdir = QueueDir;
72237c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_GROUP_SORTORDER
72247c478bd9Sstevel@tonic-gate 	qg->qg_sortorder = QueueSortOrder;
72257c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_GROUP_SORTORDER */
72267c478bd9Sstevel@tonic-gate 	qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
72277c478bd9Sstevel@tonic-gate 	qg->qg_nice = NiceQueueRun;
72287c478bd9Sstevel@tonic-gate }
72297c478bd9Sstevel@tonic-gate /*
72307c478bd9Sstevel@tonic-gate **  MAKEQUEUE -- define a new queue.
72317c478bd9Sstevel@tonic-gate **
72327c478bd9Sstevel@tonic-gate **	Parameters:
72337c478bd9Sstevel@tonic-gate **		line -- description of queue.  This is in labeled fields.
72347c478bd9Sstevel@tonic-gate **			The fields are:
72357c478bd9Sstevel@tonic-gate **			   F -- the flags associated with the queue
72367c478bd9Sstevel@tonic-gate **			   I -- the interval between running the queue
72377c478bd9Sstevel@tonic-gate **			   J -- the maximum # of jobs in work list
72387c478bd9Sstevel@tonic-gate **			   [M -- the maximum # of jobs in a queue run]
72397c478bd9Sstevel@tonic-gate **			   N -- the niceness at which to run
72407c478bd9Sstevel@tonic-gate **			   P -- the path to the queue
72417c478bd9Sstevel@tonic-gate **			   S -- the queue sorting order
72427c478bd9Sstevel@tonic-gate **			   R -- number of parallel queue runners
72437c478bd9Sstevel@tonic-gate **			   r -- max recipients per envelope
72447c478bd9Sstevel@tonic-gate **			The first word is the canonical name of the queue.
72457c478bd9Sstevel@tonic-gate **		qdef -- this is a 'Q' definition from .cf
72467c478bd9Sstevel@tonic-gate **
72477c478bd9Sstevel@tonic-gate **	Returns:
72487c478bd9Sstevel@tonic-gate **		none.
72497c478bd9Sstevel@tonic-gate **
72507c478bd9Sstevel@tonic-gate **	Side Effects:
72517c478bd9Sstevel@tonic-gate **		enters the queue into the queue table.
72527c478bd9Sstevel@tonic-gate */
72537c478bd9Sstevel@tonic-gate 
72547c478bd9Sstevel@tonic-gate void
72557c478bd9Sstevel@tonic-gate makequeue(line, qdef)
72567c478bd9Sstevel@tonic-gate 	char *line;
72577c478bd9Sstevel@tonic-gate 	bool qdef;
72587c478bd9Sstevel@tonic-gate {
72597c478bd9Sstevel@tonic-gate 	register char *p;
72607c478bd9Sstevel@tonic-gate 	register QUEUEGRP *qg;
72617c478bd9Sstevel@tonic-gate 	register STAB *s;
72627c478bd9Sstevel@tonic-gate 	int i;
72637c478bd9Sstevel@tonic-gate 	char fcode;
72647c478bd9Sstevel@tonic-gate 
72657c478bd9Sstevel@tonic-gate 	/* allocate a queue and set up defaults */
7266058561cbSjbeck 	qg = (QUEUEGRP *) xalloc(sizeof(*qg));
7267058561cbSjbeck 	memset((char *) qg, '\0', sizeof(*qg));
72687c478bd9Sstevel@tonic-gate 
72697c478bd9Sstevel@tonic-gate 	if (line[0] == '\0')
72707c478bd9Sstevel@tonic-gate 	{
72717c478bd9Sstevel@tonic-gate 		syserr("name required for queue");
72727c478bd9Sstevel@tonic-gate 		return;
72737c478bd9Sstevel@tonic-gate 	}
72747c478bd9Sstevel@tonic-gate 
72757c478bd9Sstevel@tonic-gate 	/* collect the queue name */
72767c478bd9Sstevel@tonic-gate 	for (p = line;
72777c478bd9Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
72787c478bd9Sstevel@tonic-gate 	     p++)
72797c478bd9Sstevel@tonic-gate 		continue;
72807c478bd9Sstevel@tonic-gate 	if (*p != '\0')
72817c478bd9Sstevel@tonic-gate 		*p++ = '\0';
72827c478bd9Sstevel@tonic-gate 	qg->qg_name = newstr(line);
72837c478bd9Sstevel@tonic-gate 
72847c478bd9Sstevel@tonic-gate 	/* set default values, can be overridden below */
72857c478bd9Sstevel@tonic-gate 	set_def_queueval(qg, false);
72867c478bd9Sstevel@tonic-gate 
72877c478bd9Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
72887c478bd9Sstevel@tonic-gate 	while (*p != '\0')
72897c478bd9Sstevel@tonic-gate 	{
72907c478bd9Sstevel@tonic-gate 		auto char *delimptr;
72917c478bd9Sstevel@tonic-gate 
72927c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
72937c478bd9Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
72947c478bd9Sstevel@tonic-gate 			p++;
72957c478bd9Sstevel@tonic-gate 
72967c478bd9Sstevel@tonic-gate 		/* p now points to field code */
72977c478bd9Sstevel@tonic-gate 		fcode = *p;
72987c478bd9Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
72997c478bd9Sstevel@tonic-gate 			p++;
73007c478bd9Sstevel@tonic-gate 		if (*p++ != '=')
73017c478bd9Sstevel@tonic-gate 		{
73027c478bd9Sstevel@tonic-gate 			syserr("queue %s: `=' expected", qg->qg_name);
73037c478bd9Sstevel@tonic-gate 			return;
73047c478bd9Sstevel@tonic-gate 		}
73057c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
73067c478bd9Sstevel@tonic-gate 			p++;
73077c478bd9Sstevel@tonic-gate 
73087c478bd9Sstevel@tonic-gate 		/* p now points to the field body */
73097c478bd9Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
73107c478bd9Sstevel@tonic-gate 
73117c478bd9Sstevel@tonic-gate 		/* install the field into the queue struct */
73127c478bd9Sstevel@tonic-gate 		switch (fcode)
73137c478bd9Sstevel@tonic-gate 		{
73147c478bd9Sstevel@tonic-gate 		  case 'P':		/* pathname */
73157c478bd9Sstevel@tonic-gate 			if (*p == '\0')
73167c478bd9Sstevel@tonic-gate 				syserr("queue %s: empty path name",
73177c478bd9Sstevel@tonic-gate 					qg->qg_name);
73187c478bd9Sstevel@tonic-gate 			else
73197c478bd9Sstevel@tonic-gate 				qg->qg_qdir = newstr(p);
73207c478bd9Sstevel@tonic-gate 			break;
73217c478bd9Sstevel@tonic-gate 
73227c478bd9Sstevel@tonic-gate 		  case 'F':		/* flags */
73237c478bd9Sstevel@tonic-gate 			for (; *p != '\0'; p++)
73247c478bd9Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
73257c478bd9Sstevel@tonic-gate 					setbitn(*p, qg->qg_flags);
73267c478bd9Sstevel@tonic-gate 			break;
73277c478bd9Sstevel@tonic-gate 
73287c478bd9Sstevel@tonic-gate 			/*
73297c478bd9Sstevel@tonic-gate 			**  Do we need two intervals here:
73307c478bd9Sstevel@tonic-gate 			**  One for persistent queue runners,
73317c478bd9Sstevel@tonic-gate 			**  one for "normal" queue runs?
73327c478bd9Sstevel@tonic-gate 			*/
73337c478bd9Sstevel@tonic-gate 
73347c478bd9Sstevel@tonic-gate 		  case 'I':	/* interval between running the queue */
73357c478bd9Sstevel@tonic-gate 			qg->qg_queueintvl = convtime(p, 'm');
73367c478bd9Sstevel@tonic-gate 			break;
73377c478bd9Sstevel@tonic-gate 
73387c478bd9Sstevel@tonic-gate 		  case 'N':		/* run niceness */
73397c478bd9Sstevel@tonic-gate 			qg->qg_nice = atoi(p);
73407c478bd9Sstevel@tonic-gate 			break;
73417c478bd9Sstevel@tonic-gate 
73427c478bd9Sstevel@tonic-gate 		  case 'R':		/* maximum # of runners for the group */
73437c478bd9Sstevel@tonic-gate 			i = atoi(p);
73447c478bd9Sstevel@tonic-gate 
73457c478bd9Sstevel@tonic-gate 			/* can't have more runners than allowed total */
73467c478bd9Sstevel@tonic-gate 			if (MaxQueueChildren > 0 && i > MaxQueueChildren)
73477c478bd9Sstevel@tonic-gate 			{
73487c478bd9Sstevel@tonic-gate 				qg->qg_maxqrun = MaxQueueChildren;
73497c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
73507c478bd9Sstevel@tonic-gate 						     "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
73517c478bd9Sstevel@tonic-gate 						     qg->qg_name, i,
73527c478bd9Sstevel@tonic-gate 						     MaxQueueChildren);
73537c478bd9Sstevel@tonic-gate 			}
73547c478bd9Sstevel@tonic-gate 			else
73557c478bd9Sstevel@tonic-gate 				qg->qg_maxqrun = i;
73567c478bd9Sstevel@tonic-gate 			break;
73577c478bd9Sstevel@tonic-gate 
73587c478bd9Sstevel@tonic-gate 		  case 'J':		/* maximum # of jobs in work list */
73597c478bd9Sstevel@tonic-gate 			qg->qg_maxlist = atoi(p);
73607c478bd9Sstevel@tonic-gate 			break;
73617c478bd9Sstevel@tonic-gate 
73627c478bd9Sstevel@tonic-gate 		  case 'r':		/* max recipients per envelope */
73637c478bd9Sstevel@tonic-gate 			qg->qg_maxrcpt = atoi(p);
73647c478bd9Sstevel@tonic-gate 			break;
73657c478bd9Sstevel@tonic-gate 
73667c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_GROUP_SORTORDER
73677c478bd9Sstevel@tonic-gate 		  case 'S':		/* queue sorting order */
73687c478bd9Sstevel@tonic-gate 			switch (*p)
73697c478bd9Sstevel@tonic-gate 			{
73707c478bd9Sstevel@tonic-gate 			  case 'h':	/* Host first */
73717c478bd9Sstevel@tonic-gate 			  case 'H':
73727c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYHOST;
73737c478bd9Sstevel@tonic-gate 				break;
73747c478bd9Sstevel@tonic-gate 
73757c478bd9Sstevel@tonic-gate 			  case 'p':	/* Priority order */
73767c478bd9Sstevel@tonic-gate 			  case 'P':
73777c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYPRIORITY;
73787c478bd9Sstevel@tonic-gate 				break;
73797c478bd9Sstevel@tonic-gate 
73807c478bd9Sstevel@tonic-gate 			  case 't':	/* Submission time */
73817c478bd9Sstevel@tonic-gate 			  case 'T':
73827c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYTIME;
73837c478bd9Sstevel@tonic-gate 				break;
73847c478bd9Sstevel@tonic-gate 
73857c478bd9Sstevel@tonic-gate 			  case 'f':	/* File name */
73867c478bd9Sstevel@tonic-gate 			  case 'F':
73877c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYFILENAME;
73887c478bd9Sstevel@tonic-gate 				break;
73897c478bd9Sstevel@tonic-gate 
73907c478bd9Sstevel@tonic-gate 			  case 'm':	/* Modification time */
73917c478bd9Sstevel@tonic-gate 			  case 'M':
73927c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYMODTIME;
73937c478bd9Sstevel@tonic-gate 				break;
73947c478bd9Sstevel@tonic-gate 
73957c478bd9Sstevel@tonic-gate 			  case 'r':	/* Random */
73967c478bd9Sstevel@tonic-gate 			  case 'R':
73977c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_RANDOM;
73987c478bd9Sstevel@tonic-gate 				break;
73997c478bd9Sstevel@tonic-gate 
74007c478bd9Sstevel@tonic-gate # if _FFR_RHS
74017c478bd9Sstevel@tonic-gate 			  case 's':	/* Shuffled host name */
74027c478bd9Sstevel@tonic-gate 			  case 'S':
74037c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYSHUFFLE;
74047c478bd9Sstevel@tonic-gate 				break;
74057c478bd9Sstevel@tonic-gate # endif /* _FFR_RHS */
74067c478bd9Sstevel@tonic-gate 
74077c478bd9Sstevel@tonic-gate 			  case 'n':	/* none */
74087c478bd9Sstevel@tonic-gate 			  case 'N':
74097c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_NONE;
74107c478bd9Sstevel@tonic-gate 				break;
74117c478bd9Sstevel@tonic-gate 
74127c478bd9Sstevel@tonic-gate 			  default:
74137c478bd9Sstevel@tonic-gate 				syserr("Invalid queue sort order \"%s\"", p);
74147c478bd9Sstevel@tonic-gate 			}
74157c478bd9Sstevel@tonic-gate 			break;
74167c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_GROUP_SORTORDER */
74177c478bd9Sstevel@tonic-gate 
74187c478bd9Sstevel@tonic-gate 		  default:
74197c478bd9Sstevel@tonic-gate 			syserr("Q%s: unknown queue equate %c=",
74207c478bd9Sstevel@tonic-gate 			       qg->qg_name, fcode);
74217c478bd9Sstevel@tonic-gate 			break;
74227c478bd9Sstevel@tonic-gate 		}
74237c478bd9Sstevel@tonic-gate 
74247c478bd9Sstevel@tonic-gate 		p = delimptr;
74257c478bd9Sstevel@tonic-gate 	}
74267c478bd9Sstevel@tonic-gate 
74277c478bd9Sstevel@tonic-gate #if !HASNICE
74287c478bd9Sstevel@tonic-gate 	if (qg->qg_nice != NiceQueueRun)
74297c478bd9Sstevel@tonic-gate 	{
74307c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
74317c478bd9Sstevel@tonic-gate 				     "Q%s: Warning: N= set on system that doesn't support nice()\n",
74327c478bd9Sstevel@tonic-gate 				     qg->qg_name);
74337c478bd9Sstevel@tonic-gate 	}
74347c478bd9Sstevel@tonic-gate #endif /* !HASNICE */
74357c478bd9Sstevel@tonic-gate 
74367c478bd9Sstevel@tonic-gate 	/* do some rationality checking */
74377c478bd9Sstevel@tonic-gate 	if (NumQueue >= MAXQUEUEGROUPS)
74387c478bd9Sstevel@tonic-gate 	{
74397c478bd9Sstevel@tonic-gate 		syserr("too many queue groups defined (%d max)",
74407c478bd9Sstevel@tonic-gate 			MAXQUEUEGROUPS);
74417c478bd9Sstevel@tonic-gate 		return;
74427c478bd9Sstevel@tonic-gate 	}
74437c478bd9Sstevel@tonic-gate 
74447c478bd9Sstevel@tonic-gate 	if (qg->qg_qdir == NULL)
74457c478bd9Sstevel@tonic-gate 	{
74467c478bd9Sstevel@tonic-gate 		if (QueueDir == NULL || *QueueDir == '\0')
74477c478bd9Sstevel@tonic-gate 		{
74487c478bd9Sstevel@tonic-gate 			syserr("QueueDir must be defined before queue groups");
74497c478bd9Sstevel@tonic-gate 			return;
74507c478bd9Sstevel@tonic-gate 		}
74517c478bd9Sstevel@tonic-gate 		qg->qg_qdir = newstr(QueueDir);
74527c478bd9Sstevel@tonic-gate 	}
74537c478bd9Sstevel@tonic-gate 
74547c478bd9Sstevel@tonic-gate 	if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
74557c478bd9Sstevel@tonic-gate 	{
74567c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
74577c478bd9Sstevel@tonic-gate 				     "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
74587c478bd9Sstevel@tonic-gate 				     qg->qg_name, qg->qg_maxqrun, QD_FORK);
74597c478bd9Sstevel@tonic-gate 	}
74607c478bd9Sstevel@tonic-gate 
74617c478bd9Sstevel@tonic-gate 	/* enter the queue into the symbol table */
74627c478bd9Sstevel@tonic-gate 	if (tTd(37, 8))
74637c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
74647c478bd9Sstevel@tonic-gate 			  "Adding %s to stab, path: %s", qg->qg_name,
74657c478bd9Sstevel@tonic-gate 			  qg->qg_qdir);
74667c478bd9Sstevel@tonic-gate 	s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
74677c478bd9Sstevel@tonic-gate 	if (s->s_quegrp != NULL)
74687c478bd9Sstevel@tonic-gate 	{
74697c478bd9Sstevel@tonic-gate 		i = s->s_quegrp->qg_index;
74707c478bd9Sstevel@tonic-gate 
74717c478bd9Sstevel@tonic-gate 		/* XXX what about the pointers inside this struct? */
74727c478bd9Sstevel@tonic-gate 		sm_free(s->s_quegrp); /* XXX */
74737c478bd9Sstevel@tonic-gate 	}
74747c478bd9Sstevel@tonic-gate 	else
74757c478bd9Sstevel@tonic-gate 		i = NumQueue++;
74767c478bd9Sstevel@tonic-gate 	Queue[i] = s->s_quegrp = qg;
74777c478bd9Sstevel@tonic-gate 	qg->qg_index = i;
74787c478bd9Sstevel@tonic-gate 
74797c478bd9Sstevel@tonic-gate 	/* set default value for max queue runners */
74807c478bd9Sstevel@tonic-gate 	if (qg->qg_maxqrun < 0)
74817c478bd9Sstevel@tonic-gate 	{
74827c478bd9Sstevel@tonic-gate 		if (MaxRunnersPerQueue > 0)
74837c478bd9Sstevel@tonic-gate 			qg->qg_maxqrun = MaxRunnersPerQueue;
74847c478bd9Sstevel@tonic-gate 		else
74857c478bd9Sstevel@tonic-gate 			qg->qg_maxqrun = 1;
74867c478bd9Sstevel@tonic-gate 	}
74877c478bd9Sstevel@tonic-gate 	if (qdef)
74887c478bd9Sstevel@tonic-gate 		setbitn(QD_DEFINED, qg->qg_flags);
74897c478bd9Sstevel@tonic-gate }
74907c478bd9Sstevel@tonic-gate #if 0
74917c478bd9Sstevel@tonic-gate /*
74927c478bd9Sstevel@tonic-gate **  HASHFQN -- calculate a hash value for a fully qualified host name
74937c478bd9Sstevel@tonic-gate **
74947c478bd9Sstevel@tonic-gate **	Arguments:
74957c478bd9Sstevel@tonic-gate **		fqn -- an all lower-case host.domain string
74967c478bd9Sstevel@tonic-gate **		buckets -- the number of buckets (queue directories)
74977c478bd9Sstevel@tonic-gate **
74987c478bd9Sstevel@tonic-gate **	Returns:
74997c478bd9Sstevel@tonic-gate **		a bucket number (signed integer)
75007c478bd9Sstevel@tonic-gate **		-1 on error
75017c478bd9Sstevel@tonic-gate **
75027c478bd9Sstevel@tonic-gate **	Contributed by Exactis.com, Inc.
75037c478bd9Sstevel@tonic-gate */
75047c478bd9Sstevel@tonic-gate 
75057c478bd9Sstevel@tonic-gate int
75067c478bd9Sstevel@tonic-gate hashfqn(fqn, buckets)
75077c478bd9Sstevel@tonic-gate 	register char *fqn;
75087c478bd9Sstevel@tonic-gate 	int buckets;
75097c478bd9Sstevel@tonic-gate {
75107c478bd9Sstevel@tonic-gate 	register char *p;
75117c478bd9Sstevel@tonic-gate 	register int h = 0, hash, cnt;
75127c478bd9Sstevel@tonic-gate 
75137c478bd9Sstevel@tonic-gate 	if (fqn == NULL)
75147c478bd9Sstevel@tonic-gate 		return -1;
75157c478bd9Sstevel@tonic-gate 
75167c478bd9Sstevel@tonic-gate 	/*
75177c478bd9Sstevel@tonic-gate 	**  A variation on the gdb hash
75187c478bd9Sstevel@tonic-gate 	**  This is the best as of Feb 19, 1996 --bcx
75197c478bd9Sstevel@tonic-gate 	*/
75207c478bd9Sstevel@tonic-gate 
75217c478bd9Sstevel@tonic-gate 	p = fqn;
75227c478bd9Sstevel@tonic-gate 	h = 0x238F13AF * strlen(p);
75237c478bd9Sstevel@tonic-gate 	for (cnt = 0; *p != 0; ++p, cnt++)
75247c478bd9Sstevel@tonic-gate 	{
75257c478bd9Sstevel@tonic-gate 		h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
75267c478bd9Sstevel@tonic-gate 	}
75277c478bd9Sstevel@tonic-gate 	h = (1103515243 * h + 12345) & 0x7FFFFFFF;
75287c478bd9Sstevel@tonic-gate 	if (buckets < 2)
75297c478bd9Sstevel@tonic-gate 		hash = 0;
75307c478bd9Sstevel@tonic-gate 	else
75317c478bd9Sstevel@tonic-gate 		hash = (h % buckets);
75327c478bd9Sstevel@tonic-gate 
75337c478bd9Sstevel@tonic-gate 	return hash;
75347c478bd9Sstevel@tonic-gate }
75357c478bd9Sstevel@tonic-gate #endif /* 0 */
75367c478bd9Sstevel@tonic-gate 
75377c478bd9Sstevel@tonic-gate /*
75387c478bd9Sstevel@tonic-gate **  A structure for sorting Queue according to maxqrun without
75397c478bd9Sstevel@tonic-gate **	screwing up Queue itself.
75407c478bd9Sstevel@tonic-gate */
75417c478bd9Sstevel@tonic-gate 
75427c478bd9Sstevel@tonic-gate struct sortqgrp
75437c478bd9Sstevel@tonic-gate {
75447c478bd9Sstevel@tonic-gate 	int sg_idx;		/* original index */
75457c478bd9Sstevel@tonic-gate 	int sg_maxqrun;		/* max queue runners */
75467c478bd9Sstevel@tonic-gate };
75477c478bd9Sstevel@tonic-gate typedef struct sortqgrp	SORTQGRP_T;
75487c478bd9Sstevel@tonic-gate static int cmpidx __P((const void *, const void *));
75497c478bd9Sstevel@tonic-gate 
75507c478bd9Sstevel@tonic-gate static int
75517c478bd9Sstevel@tonic-gate cmpidx(a, b)
75527c478bd9Sstevel@tonic-gate 	const void *a;
75537c478bd9Sstevel@tonic-gate 	const void *b;
75547c478bd9Sstevel@tonic-gate {
75557c478bd9Sstevel@tonic-gate 	/* The sort is highest to lowest, so the comparison is reversed */
75567c478bd9Sstevel@tonic-gate 	if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
75577c478bd9Sstevel@tonic-gate 		return 1;
75587c478bd9Sstevel@tonic-gate 	else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
75597c478bd9Sstevel@tonic-gate 		return -1;
75607c478bd9Sstevel@tonic-gate 	else
75617c478bd9Sstevel@tonic-gate 		return 0;
75627c478bd9Sstevel@tonic-gate }
75637c478bd9Sstevel@tonic-gate 
75647c478bd9Sstevel@tonic-gate /*
75657c478bd9Sstevel@tonic-gate **  MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
75667c478bd9Sstevel@tonic-gate **
75677c478bd9Sstevel@tonic-gate **  Take the now defined queue groups and assign them to work groups.
75687c478bd9Sstevel@tonic-gate **  This is done to balance out the number of concurrently active
75697c478bd9Sstevel@tonic-gate **  queue runners such that MaxQueueChildren is not exceeded. This may
75707c478bd9Sstevel@tonic-gate **  result in more than one queue group per work group. In such a case
75717c478bd9Sstevel@tonic-gate **  the number of running queue groups in that work group will have no
75727c478bd9Sstevel@tonic-gate **  more than the work group maximum number of runners (a "fair" portion
75737c478bd9Sstevel@tonic-gate **  of MaxQueueRunners). All queue groups within a work group will get a
75747c478bd9Sstevel@tonic-gate **  chance at running.
75757c478bd9Sstevel@tonic-gate **
75767c478bd9Sstevel@tonic-gate **	Parameters:
75777c478bd9Sstevel@tonic-gate **		none.
75787c478bd9Sstevel@tonic-gate **
75797c478bd9Sstevel@tonic-gate **	Returns:
75807c478bd9Sstevel@tonic-gate **		nothing.
75817c478bd9Sstevel@tonic-gate **
75827c478bd9Sstevel@tonic-gate **	Side Effects:
75837c478bd9Sstevel@tonic-gate **		Sets up WorkGrp structure.
75847c478bd9Sstevel@tonic-gate */
75857c478bd9Sstevel@tonic-gate 
75867c478bd9Sstevel@tonic-gate void
75877c478bd9Sstevel@tonic-gate makeworkgroups()
75887c478bd9Sstevel@tonic-gate {
75897c478bd9Sstevel@tonic-gate 	int i, j, total_runners, dir, h;
75907c478bd9Sstevel@tonic-gate 	SORTQGRP_T si[MAXQUEUEGROUPS + 1];
75917c478bd9Sstevel@tonic-gate 
75927c478bd9Sstevel@tonic-gate 	total_runners = 0;
75937c478bd9Sstevel@tonic-gate 	if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
75947c478bd9Sstevel@tonic-gate 	{
75957c478bd9Sstevel@tonic-gate 		/*
75967c478bd9Sstevel@tonic-gate 		**  There is only the "mqueue" queue group (a default)
75977c478bd9Sstevel@tonic-gate 		**  containing all of the queues. We want to provide to
75987c478bd9Sstevel@tonic-gate 		**  this queue group the maximum allowable queue runners.
75997c478bd9Sstevel@tonic-gate 		**  To match older behavior (8.10/8.11) we'll try for
76007c478bd9Sstevel@tonic-gate 		**  1 runner per queue capping it at MaxQueueChildren.
76017c478bd9Sstevel@tonic-gate 		**  So if there are N queues, then there will be N runners
76027c478bd9Sstevel@tonic-gate 		**  for the "mqueue" queue group (where N is kept less than
76037c478bd9Sstevel@tonic-gate 		**  MaxQueueChildren).
76047c478bd9Sstevel@tonic-gate 		*/
76057c478bd9Sstevel@tonic-gate 
76067c478bd9Sstevel@tonic-gate 		NumWorkGroups = 1;
76077c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_numqgrp = 1;
76087c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
76097c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_qgs[0] = Queue[0];
76107c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
76117c478bd9Sstevel@tonic-gate 		    Queue[0]->qg_numqueues > MaxQueueChildren)
76127c478bd9Sstevel@tonic-gate 			WorkGrp[0].wg_runners = MaxQueueChildren;
76137c478bd9Sstevel@tonic-gate 		else
76147c478bd9Sstevel@tonic-gate 			WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
76157c478bd9Sstevel@tonic-gate 
76167c478bd9Sstevel@tonic-gate 		Queue[0]->qg_wgrp = 0;
76177c478bd9Sstevel@tonic-gate 
76187c478bd9Sstevel@tonic-gate 		/* can't have more runners than allowed total */
76197c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
76207c478bd9Sstevel@tonic-gate 		    Queue[0]->qg_maxqrun > MaxQueueChildren)
76217c478bd9Sstevel@tonic-gate 			Queue[0]->qg_maxqrun = MaxQueueChildren;
76227c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
76237c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
76247c478bd9Sstevel@tonic-gate 		return;
76257c478bd9Sstevel@tonic-gate 	}
76267c478bd9Sstevel@tonic-gate 
76277c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76287c478bd9Sstevel@tonic-gate 	{
76297c478bd9Sstevel@tonic-gate 		si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
76307c478bd9Sstevel@tonic-gate 		si[i].sg_idx = i;
76317c478bd9Sstevel@tonic-gate 	}
76327c478bd9Sstevel@tonic-gate 	qsort(si, NumQueue, sizeof(si[0]), cmpidx);
76337c478bd9Sstevel@tonic-gate 
76347c478bd9Sstevel@tonic-gate 	NumWorkGroups = 0;
76357c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76367c478bd9Sstevel@tonic-gate 	{
76377c478bd9Sstevel@tonic-gate 		total_runners += si[i].sg_maxqrun;
76387c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
76397c478bd9Sstevel@tonic-gate 			NumWorkGroups++;
76407c478bd9Sstevel@tonic-gate 		else
76417c478bd9Sstevel@tonic-gate 			break;
76427c478bd9Sstevel@tonic-gate 	}
76437c478bd9Sstevel@tonic-gate 
76447c478bd9Sstevel@tonic-gate 	if (NumWorkGroups < 1)
76457c478bd9Sstevel@tonic-gate 		NumWorkGroups = 1; /* gotta have one at least */
76467c478bd9Sstevel@tonic-gate 	else if (NumWorkGroups > MAXWORKGROUPS)
76477c478bd9Sstevel@tonic-gate 		NumWorkGroups = MAXWORKGROUPS; /* the limit */
76487c478bd9Sstevel@tonic-gate 
76497c478bd9Sstevel@tonic-gate 	/*
76507c478bd9Sstevel@tonic-gate 	**  We now know the number of work groups to pack the queue groups
76517c478bd9Sstevel@tonic-gate 	**  into. The queue groups in 'Queue' are sorted from highest
76527c478bd9Sstevel@tonic-gate 	**  to lowest for the number of runners per queue group.
76537c478bd9Sstevel@tonic-gate 	**  We put the queue groups with the largest number of runners
76547c478bd9Sstevel@tonic-gate 	**  into work groups first. Then the smaller ones are fitted in
76557c478bd9Sstevel@tonic-gate 	**  where it looks best.
76567c478bd9Sstevel@tonic-gate 	*/
76577c478bd9Sstevel@tonic-gate 
76587c478bd9Sstevel@tonic-gate 	j = 0;
76597c478bd9Sstevel@tonic-gate 	dir = 1;
76607c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76617c478bd9Sstevel@tonic-gate 	{
76627c478bd9Sstevel@tonic-gate 		/* a to-and-fro packing scheme, continue from last position */
76637c478bd9Sstevel@tonic-gate 		if (j >= NumWorkGroups)
76647c478bd9Sstevel@tonic-gate 		{
76657c478bd9Sstevel@tonic-gate 			dir = -1;
76667c478bd9Sstevel@tonic-gate 			j = NumWorkGroups - 1;
76677c478bd9Sstevel@tonic-gate 		}
76687c478bd9Sstevel@tonic-gate 		else if (j < 0)
76697c478bd9Sstevel@tonic-gate 		{
76707c478bd9Sstevel@tonic-gate 			j = 0;
76717c478bd9Sstevel@tonic-gate 			dir = 1;
76727c478bd9Sstevel@tonic-gate 		}
76737c478bd9Sstevel@tonic-gate 
76747c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_qgs == NULL)
76757c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) *
76767c478bd9Sstevel@tonic-gate 							(WorkGrp[j].wg_numqgrp + 1));
76777c478bd9Sstevel@tonic-gate 		else
76787c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
76797c478bd9Sstevel@tonic-gate 							sizeof(QUEUEGRP *) *
76807c478bd9Sstevel@tonic-gate 							(WorkGrp[j].wg_numqgrp + 1));
76817c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_qgs == NULL)
76827c478bd9Sstevel@tonic-gate 		{
76837c478bd9Sstevel@tonic-gate 			syserr("!cannot allocate memory for work queues, need %d bytes",
76847c478bd9Sstevel@tonic-gate 			       (int) (sizeof(QUEUEGRP *) *
76857c478bd9Sstevel@tonic-gate 				      (WorkGrp[j].wg_numqgrp + 1)));
76867c478bd9Sstevel@tonic-gate 		}
76877c478bd9Sstevel@tonic-gate 
76887c478bd9Sstevel@tonic-gate 		h = si[i].sg_idx;
76897c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[h];
76907c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_numqgrp++;
76917c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_runners += Queue[h]->qg_maxqrun;
76927c478bd9Sstevel@tonic-gate 		Queue[h]->qg_wgrp = j;
76937c478bd9Sstevel@tonic-gate 
76947c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_maxact == 0)
76957c478bd9Sstevel@tonic-gate 		{
76967c478bd9Sstevel@tonic-gate 			/* can't have more runners than allowed total */
76977c478bd9Sstevel@tonic-gate 			if (MaxQueueChildren > 0 &&
76987c478bd9Sstevel@tonic-gate 			    Queue[h]->qg_maxqrun > MaxQueueChildren)
76997c478bd9Sstevel@tonic-gate 				Queue[h]->qg_maxqrun = MaxQueueChildren;
77007c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_maxact = Queue[h]->qg_maxqrun;
77017c478bd9Sstevel@tonic-gate 		}
77027c478bd9Sstevel@tonic-gate 
77037c478bd9Sstevel@tonic-gate 		/*
77047c478bd9Sstevel@tonic-gate 		**  XXX: must wg_lowqintvl be the GCD?
77057c478bd9Sstevel@tonic-gate 		**  qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
77067c478bd9Sstevel@tonic-gate 		**  qg2 occur?
77077c478bd9Sstevel@tonic-gate 		*/
77087c478bd9Sstevel@tonic-gate 
77097c478bd9Sstevel@tonic-gate 		/* keep track of the lowest interval for a persistent runner */
77107c478bd9Sstevel@tonic-gate 		if (Queue[h]->qg_queueintvl > 0 &&
77117c478bd9Sstevel@tonic-gate 		    WorkGrp[j].wg_lowqintvl < Queue[h]->qg_queueintvl)
77127c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_lowqintvl = Queue[h]->qg_queueintvl;
77137c478bd9Sstevel@tonic-gate 		j += dir;
77147c478bd9Sstevel@tonic-gate 	}
77157c478bd9Sstevel@tonic-gate 	if (tTd(41, 9))
77167c478bd9Sstevel@tonic-gate 	{
77177c478bd9Sstevel@tonic-gate 		for (i = 0; i < NumWorkGroups; i++)
77187c478bd9Sstevel@tonic-gate 		{
77197c478bd9Sstevel@tonic-gate 			sm_dprintf("Workgroup[%d]=", i);
77207c478bd9Sstevel@tonic-gate 			for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
77217c478bd9Sstevel@tonic-gate 			{
77227c478bd9Sstevel@tonic-gate 				sm_dprintf("%s, ",
77237c478bd9Sstevel@tonic-gate 					WorkGrp[i].wg_qgs[j]->qg_name);
77247c478bd9Sstevel@tonic-gate 			}
77257c478bd9Sstevel@tonic-gate 			sm_dprintf("\n");
77267c478bd9Sstevel@tonic-gate 		}
77277c478bd9Sstevel@tonic-gate 	}
77287c478bd9Sstevel@tonic-gate }
77297c478bd9Sstevel@tonic-gate 
77307c478bd9Sstevel@tonic-gate /*
77317c478bd9Sstevel@tonic-gate **  DUP_DF -- duplicate envelope data file
77327c478bd9Sstevel@tonic-gate **
77337c478bd9Sstevel@tonic-gate **	Copy the data file from the 'old' envelope to the 'new' envelope
77347c478bd9Sstevel@tonic-gate **	in the most efficient way possible.
77357c478bd9Sstevel@tonic-gate **
77367c478bd9Sstevel@tonic-gate **	Create a hard link from the 'old' data file to the 'new' data file.
77377c478bd9Sstevel@tonic-gate **	If the old and new queue directories are on different file systems,
77387c478bd9Sstevel@tonic-gate **	then the new data file link is created in the old queue directory,
77397c478bd9Sstevel@tonic-gate **	and the new queue file will contain a 'd' record pointing to the
77407c478bd9Sstevel@tonic-gate **	directory containing the new data file.
77417c478bd9Sstevel@tonic-gate **
77427c478bd9Sstevel@tonic-gate **	Parameters:
77437c478bd9Sstevel@tonic-gate **		old -- old envelope.
77447c478bd9Sstevel@tonic-gate **		new -- new envelope.
77457c478bd9Sstevel@tonic-gate **
77467c478bd9Sstevel@tonic-gate **	Results:
77477c478bd9Sstevel@tonic-gate **		Returns true on success, false on failure.
77487c478bd9Sstevel@tonic-gate **
77497c478bd9Sstevel@tonic-gate **	Side Effects:
77507c478bd9Sstevel@tonic-gate **		On success, the new data file is created.
77517c478bd9Sstevel@tonic-gate **		On fatal failure, EF_FATALERRS is set in old->e_flags.
77527c478bd9Sstevel@tonic-gate */
77537c478bd9Sstevel@tonic-gate 
77547c478bd9Sstevel@tonic-gate static bool	dup_df __P((ENVELOPE *, ENVELOPE *));
77557c478bd9Sstevel@tonic-gate 
77567c478bd9Sstevel@tonic-gate static bool
77577c478bd9Sstevel@tonic-gate dup_df(old, new)
77587c478bd9Sstevel@tonic-gate 	ENVELOPE *old;
77597c478bd9Sstevel@tonic-gate 	ENVELOPE *new;
77607c478bd9Sstevel@tonic-gate {
77617c478bd9Sstevel@tonic-gate 	int ofs, nfs, r;
77627c478bd9Sstevel@tonic-gate 	char opath[MAXPATHLEN];
77637c478bd9Sstevel@tonic-gate 	char npath[MAXPATHLEN];
77647c478bd9Sstevel@tonic-gate 
77657c478bd9Sstevel@tonic-gate 	if (!bitset(EF_HAS_DF, old->e_flags))
77667c478bd9Sstevel@tonic-gate 	{
77677c478bd9Sstevel@tonic-gate 		/*
77687c478bd9Sstevel@tonic-gate 		**  this can happen if: SuperSafe != True
77697c478bd9Sstevel@tonic-gate 		**  and a bounce mail is sent that is split.
77707c478bd9Sstevel@tonic-gate 		*/
77717c478bd9Sstevel@tonic-gate 
77727c478bd9Sstevel@tonic-gate 		queueup(old, false, true);
77737c478bd9Sstevel@tonic-gate 	}
77747c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
77757c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
77767c478bd9Sstevel@tonic-gate 
7777058561cbSjbeck 	(void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof(opath));
7778058561cbSjbeck 	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
77797c478bd9Sstevel@tonic-gate 
77807c478bd9Sstevel@tonic-gate 	if (old->e_dfp != NULL)
77817c478bd9Sstevel@tonic-gate 	{
77827c478bd9Sstevel@tonic-gate 		r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
77837c478bd9Sstevel@tonic-gate 		if (r < 0 && errno != EINVAL)
77847c478bd9Sstevel@tonic-gate 		{
77857c478bd9Sstevel@tonic-gate 			syserr("@can't commit %s", opath);
77867c478bd9Sstevel@tonic-gate 			old->e_flags |= EF_FATALERRS;
77877c478bd9Sstevel@tonic-gate 			return false;
77887c478bd9Sstevel@tonic-gate 		}
77897c478bd9Sstevel@tonic-gate 	}
77907c478bd9Sstevel@tonic-gate 
77917c478bd9Sstevel@tonic-gate 	/*
77927c478bd9Sstevel@tonic-gate 	**  Attempt to create a hard link, if we think both old and new
77937c478bd9Sstevel@tonic-gate 	**  are on the same file system, otherwise copy the file.
77947c478bd9Sstevel@tonic-gate 	**
77957c478bd9Sstevel@tonic-gate 	**  Don't waste time attempting a hard link unless old and new
77967c478bd9Sstevel@tonic-gate 	**  are on the same file system.
77977c478bd9Sstevel@tonic-gate 	*/
77987c478bd9Sstevel@tonic-gate 
779949218d4fSjbeck 	SM_REQUIRE(ISVALIDQGRP(old->e_dfqgrp) && ISVALIDQDIR(old->e_dfqdir));
780049218d4fSjbeck 	SM_REQUIRE(ISVALIDQGRP(new->e_dfqgrp) && ISVALIDQDIR(new->e_dfqdir));
780149218d4fSjbeck 
780249218d4fSjbeck 	ofs = Queue[old->e_dfqgrp]->qg_qpaths[old->e_dfqdir].qp_fsysidx;
780349218d4fSjbeck 	nfs = Queue[new->e_dfqgrp]->qg_qpaths[new->e_dfqdir].qp_fsysidx;
78047c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
78057c478bd9Sstevel@tonic-gate 	{
78067c478bd9Sstevel@tonic-gate 		if (link(opath, npath) == 0)
78077c478bd9Sstevel@tonic-gate 		{
78087c478bd9Sstevel@tonic-gate 			new->e_flags |= EF_HAS_DF;
78097c478bd9Sstevel@tonic-gate 			SYNC_DIR(npath, true);
78107c478bd9Sstevel@tonic-gate 			return true;
78117c478bd9Sstevel@tonic-gate 		}
78127c478bd9Sstevel@tonic-gate 		goto error;
78137c478bd9Sstevel@tonic-gate 	}
78147c478bd9Sstevel@tonic-gate 
78157c478bd9Sstevel@tonic-gate 	/*
78167c478bd9Sstevel@tonic-gate 	**  Can't link across queue directories, so try to create a hard
78177c478bd9Sstevel@tonic-gate 	**  link in the same queue directory as the old df file.
78187c478bd9Sstevel@tonic-gate 	**  The qf file will refer to the new df file using a 'd' record.
78197c478bd9Sstevel@tonic-gate 	*/
78207c478bd9Sstevel@tonic-gate 
78217c478bd9Sstevel@tonic-gate 	new->e_dfqgrp = old->e_dfqgrp;
78227c478bd9Sstevel@tonic-gate 	new->e_dfqdir = old->e_dfqdir;
7823058561cbSjbeck 	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
78247c478bd9Sstevel@tonic-gate 	if (link(opath, npath) == 0)
78257c478bd9Sstevel@tonic-gate 	{
78267c478bd9Sstevel@tonic-gate 		new->e_flags |= EF_HAS_DF;
78277c478bd9Sstevel@tonic-gate 		SYNC_DIR(npath, true);
78287c478bd9Sstevel@tonic-gate 		return true;
78297c478bd9Sstevel@tonic-gate 	}
78307c478bd9Sstevel@tonic-gate 
78317c478bd9Sstevel@tonic-gate   error:
78327c478bd9Sstevel@tonic-gate 	if (LogLevel > 0)
78337c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, old->e_id,
78347c478bd9Sstevel@tonic-gate 			  "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
78357c478bd9Sstevel@tonic-gate 			  opath, npath, sm_errstring(errno));
78367c478bd9Sstevel@tonic-gate 	return false;
78377c478bd9Sstevel@tonic-gate }
78387c478bd9Sstevel@tonic-gate 
78397c478bd9Sstevel@tonic-gate /*
78407c478bd9Sstevel@tonic-gate **  SPLIT_ENV -- Allocate a new envelope based on a given envelope.
78417c478bd9Sstevel@tonic-gate **
78427c478bd9Sstevel@tonic-gate **	Parameters:
78437c478bd9Sstevel@tonic-gate **		e -- envelope.
78447c478bd9Sstevel@tonic-gate **		sendqueue -- sendqueue for new envelope.
78457c478bd9Sstevel@tonic-gate **		qgrp -- index of queue group.
78467c478bd9Sstevel@tonic-gate **		qdir -- queue directory.
78477c478bd9Sstevel@tonic-gate **
78487c478bd9Sstevel@tonic-gate **	Results:
78497c478bd9Sstevel@tonic-gate **		new envelope.
78507c478bd9Sstevel@tonic-gate **
78517c478bd9Sstevel@tonic-gate */
78527c478bd9Sstevel@tonic-gate 
78537c478bd9Sstevel@tonic-gate static ENVELOPE	*split_env __P((ENVELOPE *, ADDRESS *, int, int));
78547c478bd9Sstevel@tonic-gate 
78557c478bd9Sstevel@tonic-gate static ENVELOPE *
78567c478bd9Sstevel@tonic-gate split_env(e, sendqueue, qgrp, qdir)
78577c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
78587c478bd9Sstevel@tonic-gate 	ADDRESS *sendqueue;
78597c478bd9Sstevel@tonic-gate 	int qgrp;
78607c478bd9Sstevel@tonic-gate 	int qdir;
78617c478bd9Sstevel@tonic-gate {
78627c478bd9Sstevel@tonic-gate 	ENVELOPE *ee;
78637c478bd9Sstevel@tonic-gate 
7864058561cbSjbeck 	ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof(*ee));
78657c478bd9Sstevel@tonic-gate 	STRUCTCOPY(*e, *ee);
78667c478bd9Sstevel@tonic-gate 	ee->e_message = NULL;	/* XXX use original message? */
78677c478bd9Sstevel@tonic-gate 	ee->e_id = NULL;
78687c478bd9Sstevel@tonic-gate 	assign_queueid(ee);
78697c478bd9Sstevel@tonic-gate 	ee->e_sendqueue = sendqueue;
78707c478bd9Sstevel@tonic-gate 	ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
78717c478bd9Sstevel@tonic-gate 			 |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
78727c478bd9Sstevel@tonic-gate 	ee->e_flags |= EF_NORECEIPT;	/* XXX really? */
78737c478bd9Sstevel@tonic-gate 	ee->e_from.q_state = QS_SENDER;
78747c478bd9Sstevel@tonic-gate 	ee->e_dfp = NULL;
78757c478bd9Sstevel@tonic-gate 	ee->e_lockfp = NULL;
78767c478bd9Sstevel@tonic-gate 	if (e->e_xfp != NULL)
78777c478bd9Sstevel@tonic-gate 		ee->e_xfp = sm_io_dup(e->e_xfp);
78787c478bd9Sstevel@tonic-gate 
78797c478bd9Sstevel@tonic-gate 	/* failed to dup e->e_xfp, start a new transcript */
78807c478bd9Sstevel@tonic-gate 	if (ee->e_xfp == NULL)
78817c478bd9Sstevel@tonic-gate 		openxscript(ee);
78827c478bd9Sstevel@tonic-gate 
78837c478bd9Sstevel@tonic-gate 	ee->e_qgrp = ee->e_dfqgrp = qgrp;
78847c478bd9Sstevel@tonic-gate 	ee->e_qdir = ee->e_dfqdir = qdir;
78857c478bd9Sstevel@tonic-gate 	ee->e_errormode = EM_MAIL;
78867c478bd9Sstevel@tonic-gate 	ee->e_statmsg = NULL;
78877c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg != NULL)
78887c478bd9Sstevel@tonic-gate 		ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
78897c478bd9Sstevel@tonic-gate 						  e->e_quarmsg);
78907c478bd9Sstevel@tonic-gate 
78917c478bd9Sstevel@tonic-gate 	/*
78927c478bd9Sstevel@tonic-gate 	**  XXX Not sure if this copying is necessary.
78937c478bd9Sstevel@tonic-gate 	**  sendall() does this copying, but I (dm) don't know if that is
78947c478bd9Sstevel@tonic-gate 	**  because of the storage management discipline we were using
78957c478bd9Sstevel@tonic-gate 	**  before rpools were introduced, or if it is because these lists
78967c478bd9Sstevel@tonic-gate 	**  can be modified later.
78977c478bd9Sstevel@tonic-gate 	*/
78987c478bd9Sstevel@tonic-gate 
78997c478bd9Sstevel@tonic-gate 	ee->e_header = copyheader(e->e_header, ee->e_rpool);
79007c478bd9Sstevel@tonic-gate 	ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
79017c478bd9Sstevel@tonic-gate 
79027c478bd9Sstevel@tonic-gate 	return ee;
79037c478bd9Sstevel@tonic-gate }
79047c478bd9Sstevel@tonic-gate 
79057c478bd9Sstevel@tonic-gate /* return values from split functions, check also below! */
79067c478bd9Sstevel@tonic-gate #define SM_SPLIT_FAIL	(0)
79077c478bd9Sstevel@tonic-gate #define SM_SPLIT_NONE	(1)
79087c478bd9Sstevel@tonic-gate #define SM_SPLIT_NEW(n)	(1 + (n))
79097c478bd9Sstevel@tonic-gate 
79107c478bd9Sstevel@tonic-gate /*
79117c478bd9Sstevel@tonic-gate **  SPLIT_ACROSS_QUEUE_GROUPS
79127c478bd9Sstevel@tonic-gate **
79137c478bd9Sstevel@tonic-gate **	This function splits an envelope across multiple queue groups
79147c478bd9Sstevel@tonic-gate **	based on the queue group of each recipient.
79157c478bd9Sstevel@tonic-gate **
79167c478bd9Sstevel@tonic-gate **	Parameters:
79177c478bd9Sstevel@tonic-gate **		e -- envelope.
79187c478bd9Sstevel@tonic-gate **
79197c478bd9Sstevel@tonic-gate **	Results:
79207c478bd9Sstevel@tonic-gate **		SM_SPLIT_FAIL on failure
79217c478bd9Sstevel@tonic-gate **		SM_SPLIT_NONE if no splitting occurred,
79227c478bd9Sstevel@tonic-gate **		or 1 + the number of additional envelopes created.
79237c478bd9Sstevel@tonic-gate **
79247c478bd9Sstevel@tonic-gate **	Side Effects:
79257c478bd9Sstevel@tonic-gate **		On success, e->e_sibling points to a list of zero or more
79267c478bd9Sstevel@tonic-gate **		additional envelopes, and the associated data files exist
79277c478bd9Sstevel@tonic-gate **		on disk.  But the queue files are not created.
79287c478bd9Sstevel@tonic-gate **
79297c478bd9Sstevel@tonic-gate **		On failure, e->e_sibling is not changed.
79307c478bd9Sstevel@tonic-gate **		The order of recipients in e->e_sendqueue is permuted.
79317c478bd9Sstevel@tonic-gate **		Abandoned data files for additional envelopes that failed
79327c478bd9Sstevel@tonic-gate **		to be created may exist on disk.
79337c478bd9Sstevel@tonic-gate */
79347c478bd9Sstevel@tonic-gate 
79357c478bd9Sstevel@tonic-gate static int	q_qgrp_compare __P((const void *, const void *));
79367c478bd9Sstevel@tonic-gate static int	e_filesys_compare __P((const void *, const void *));
79377c478bd9Sstevel@tonic-gate 
79387c478bd9Sstevel@tonic-gate static int
79397c478bd9Sstevel@tonic-gate q_qgrp_compare(p1, p2)
79407c478bd9Sstevel@tonic-gate 	const void *p1;
79417c478bd9Sstevel@tonic-gate 	const void *p2;
79427c478bd9Sstevel@tonic-gate {
79437c478bd9Sstevel@tonic-gate 	ADDRESS **pq1 = (ADDRESS **) p1;
79447c478bd9Sstevel@tonic-gate 	ADDRESS **pq2 = (ADDRESS **) p2;
79457c478bd9Sstevel@tonic-gate 
79467c478bd9Sstevel@tonic-gate 	return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
79477c478bd9Sstevel@tonic-gate }
79487c478bd9Sstevel@tonic-gate 
79497c478bd9Sstevel@tonic-gate static int
79507c478bd9Sstevel@tonic-gate e_filesys_compare(p1, p2)
79517c478bd9Sstevel@tonic-gate 	const void *p1;
79527c478bd9Sstevel@tonic-gate 	const void *p2;
79537c478bd9Sstevel@tonic-gate {
79547c478bd9Sstevel@tonic-gate 	ENVELOPE **pe1 = (ENVELOPE **) p1;
79557c478bd9Sstevel@tonic-gate 	ENVELOPE **pe2 = (ENVELOPE **) p2;
79567c478bd9Sstevel@tonic-gate 	int fs1, fs2;
79577c478bd9Sstevel@tonic-gate 
79587c478bd9Sstevel@tonic-gate 	fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
79597c478bd9Sstevel@tonic-gate 	fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
79607c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
79617c478bd9Sstevel@tonic-gate 		return -1;
79627c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
79637c478bd9Sstevel@tonic-gate 		return 1;
79647c478bd9Sstevel@tonic-gate 	return 0;
79657c478bd9Sstevel@tonic-gate }
79667c478bd9Sstevel@tonic-gate 
7967058561cbSjbeck static int split_across_queue_groups __P((ENVELOPE *));
79687c478bd9Sstevel@tonic-gate static int
79697c478bd9Sstevel@tonic-gate split_across_queue_groups(e)
79707c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
79717c478bd9Sstevel@tonic-gate {
79727c478bd9Sstevel@tonic-gate 	int naddrs, nsplits, i;
79737c478bd9Sstevel@tonic-gate 	bool changed;
79747c478bd9Sstevel@tonic-gate 	char **pvp;
79757c478bd9Sstevel@tonic-gate 	ADDRESS *q, **addrs;
79767c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *es;
79777c478bd9Sstevel@tonic-gate 	ENVELOPE *splits[MAXQUEUEGROUPS];
79787c478bd9Sstevel@tonic-gate 	char pvpbuf[PSBUFSIZE];
79797c478bd9Sstevel@tonic-gate 
79807c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
79817c478bd9Sstevel@tonic-gate 
79827c478bd9Sstevel@tonic-gate 	/* Count addresses and assign queue groups. */
79837c478bd9Sstevel@tonic-gate 	naddrs = 0;
79847c478bd9Sstevel@tonic-gate 	changed = false;
79857c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
79867c478bd9Sstevel@tonic-gate 	{
79877c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
79887c478bd9Sstevel@tonic-gate 			continue;
79897c478bd9Sstevel@tonic-gate 		++naddrs;
79907c478bd9Sstevel@tonic-gate 
79917c478bd9Sstevel@tonic-gate 		/* bad addresses and those already sent stay put */
79927c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(q->q_state) ||
79937c478bd9Sstevel@tonic-gate 		    QS_IS_SENT(q->q_state))
79947c478bd9Sstevel@tonic-gate 			q->q_qgrp = e->e_qgrp;
79957c478bd9Sstevel@tonic-gate 		else if (!ISVALIDQGRP(q->q_qgrp))
79967c478bd9Sstevel@tonic-gate 		{
79977c478bd9Sstevel@tonic-gate 			/* call ruleset which should return a queue group */
79987c478bd9Sstevel@tonic-gate 			i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
79997c478bd9Sstevel@tonic-gate 				  pvpbuf, sizeof(pvpbuf));
80007c478bd9Sstevel@tonic-gate 			if (i == EX_OK &&
80017c478bd9Sstevel@tonic-gate 			    pvp != NULL && pvp[0] != NULL &&
80027c478bd9Sstevel@tonic-gate 			    (pvp[0][0] & 0377) == CANONNET &&
80037c478bd9Sstevel@tonic-gate 			    pvp[1] != NULL && pvp[1][0] != '\0')
80047c478bd9Sstevel@tonic-gate 			{
80057c478bd9Sstevel@tonic-gate 				i = name2qid(pvp[1]);
80067c478bd9Sstevel@tonic-gate 				if (ISVALIDQGRP(i))
80077c478bd9Sstevel@tonic-gate 				{
80087c478bd9Sstevel@tonic-gate 					q->q_qgrp = i;
80097c478bd9Sstevel@tonic-gate 					changed = true;
80107c478bd9Sstevel@tonic-gate 					if (tTd(20, 4))
80117c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_INFO, NOQID,
80127c478bd9Sstevel@tonic-gate 							"queue group name %s -> %d",
80137c478bd9Sstevel@tonic-gate 							pvp[1], i);
80147c478bd9Sstevel@tonic-gate 					continue;
80157c478bd9Sstevel@tonic-gate 				}
80167c478bd9Sstevel@tonic-gate 				else if (LogLevel > 10)
80177c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
80187c478bd9Sstevel@tonic-gate 						"can't find queue group name %s, selection ignored",
80197c478bd9Sstevel@tonic-gate 						pvp[1]);
80207c478bd9Sstevel@tonic-gate 			}
80217c478bd9Sstevel@tonic-gate 			if (q->q_mailer != NULL &&
80227c478bd9Sstevel@tonic-gate 			    ISVALIDQGRP(q->q_mailer->m_qgrp))
80237c478bd9Sstevel@tonic-gate 			{
80247c478bd9Sstevel@tonic-gate 				changed = true;
80257c478bd9Sstevel@tonic-gate 				q->q_qgrp = q->q_mailer->m_qgrp;
80267c478bd9Sstevel@tonic-gate 			}
80277c478bd9Sstevel@tonic-gate 			else if (ISVALIDQGRP(e->e_qgrp))
80287c478bd9Sstevel@tonic-gate 				q->q_qgrp = e->e_qgrp;
80297c478bd9Sstevel@tonic-gate 			else
80307c478bd9Sstevel@tonic-gate 				q->q_qgrp = 0;
80317c478bd9Sstevel@tonic-gate 		}
80327c478bd9Sstevel@tonic-gate 	}
80337c478bd9Sstevel@tonic-gate 
80347c478bd9Sstevel@tonic-gate 	/* only one address? nothing to split. */
80357c478bd9Sstevel@tonic-gate 	if (naddrs <= 1 && !changed)
80367c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
80377c478bd9Sstevel@tonic-gate 
80387c478bd9Sstevel@tonic-gate 	/* sort the addresses by queue group */
80397c478bd9Sstevel@tonic-gate 	addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
80407c478bd9Sstevel@tonic-gate 	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
80417c478bd9Sstevel@tonic-gate 	{
80427c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
80437c478bd9Sstevel@tonic-gate 			continue;
80447c478bd9Sstevel@tonic-gate 		addrs[i++] = q;
80457c478bd9Sstevel@tonic-gate 	}
80467c478bd9Sstevel@tonic-gate 	qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
80477c478bd9Sstevel@tonic-gate 
80487c478bd9Sstevel@tonic-gate 	/* split into multiple envelopes, by queue group */
80497c478bd9Sstevel@tonic-gate 	nsplits = 0;
80507c478bd9Sstevel@tonic-gate 	es = NULL;
80517c478bd9Sstevel@tonic-gate 	e->e_sendqueue = NULL;
80527c478bd9Sstevel@tonic-gate 	for (i = 0; i < naddrs; ++i)
80537c478bd9Sstevel@tonic-gate 	{
80547c478bd9Sstevel@tonic-gate 		if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
80557c478bd9Sstevel@tonic-gate 			addrs[i]->q_next = NULL;
80567c478bd9Sstevel@tonic-gate 		else
80577c478bd9Sstevel@tonic-gate 			addrs[i]->q_next = addrs[i + 1];
80587c478bd9Sstevel@tonic-gate 
80597c478bd9Sstevel@tonic-gate 		/* same queue group as original envelope? */
80607c478bd9Sstevel@tonic-gate 		if (addrs[i]->q_qgrp == e->e_qgrp)
80617c478bd9Sstevel@tonic-gate 		{
80627c478bd9Sstevel@tonic-gate 			if (e->e_sendqueue == NULL)
80637c478bd9Sstevel@tonic-gate 				e->e_sendqueue = addrs[i];
80647c478bd9Sstevel@tonic-gate 			continue;
80657c478bd9Sstevel@tonic-gate 		}
80667c478bd9Sstevel@tonic-gate 
80677c478bd9Sstevel@tonic-gate 		/* different queue group than original envelope */
80687c478bd9Sstevel@tonic-gate 		if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
80697c478bd9Sstevel@tonic-gate 		{
80707c478bd9Sstevel@tonic-gate 			ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
80717c478bd9Sstevel@tonic-gate 			es = ee;
80727c478bd9Sstevel@tonic-gate 			splits[nsplits++] = ee;
80737c478bd9Sstevel@tonic-gate 		}
80747c478bd9Sstevel@tonic-gate 	}
80757c478bd9Sstevel@tonic-gate 
80767c478bd9Sstevel@tonic-gate 	/* no splits? return right now. */
80777c478bd9Sstevel@tonic-gate 	if (nsplits <= 0)
80787c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
80797c478bd9Sstevel@tonic-gate 
80807c478bd9Sstevel@tonic-gate 	/* assign a queue directory to each additional envelope */
80817c478bd9Sstevel@tonic-gate 	for (i = 0; i < nsplits; ++i)
80827c478bd9Sstevel@tonic-gate 	{
80837c478bd9Sstevel@tonic-gate 		es = splits[i];
80847c478bd9Sstevel@tonic-gate #if 0
80857c478bd9Sstevel@tonic-gate 		es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
80867c478bd9Sstevel@tonic-gate #endif /* 0 */
80877c478bd9Sstevel@tonic-gate 		if (!setnewqueue(es))
80887c478bd9Sstevel@tonic-gate 			goto failure;
80897c478bd9Sstevel@tonic-gate 	}
80907c478bd9Sstevel@tonic-gate 
80917c478bd9Sstevel@tonic-gate 	/* sort the additional envelopes by queue file system */
80927c478bd9Sstevel@tonic-gate 	qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
80937c478bd9Sstevel@tonic-gate 
80947c478bd9Sstevel@tonic-gate 	/* create data files for each additional envelope */
80957c478bd9Sstevel@tonic-gate 	if (!dup_df(e, splits[0]))
80967c478bd9Sstevel@tonic-gate 	{
80977c478bd9Sstevel@tonic-gate 		i = 0;
80987c478bd9Sstevel@tonic-gate 		goto failure;
80997c478bd9Sstevel@tonic-gate 	}
81007c478bd9Sstevel@tonic-gate 	for (i = 1; i < nsplits; ++i)
81017c478bd9Sstevel@tonic-gate 	{
81027c478bd9Sstevel@tonic-gate 		/* copy or link to the previous data file */
81037c478bd9Sstevel@tonic-gate 		if (!dup_df(splits[i - 1], splits[i]))
81047c478bd9Sstevel@tonic-gate 			goto failure;
81057c478bd9Sstevel@tonic-gate 	}
81067c478bd9Sstevel@tonic-gate 
81077c478bd9Sstevel@tonic-gate 	/* success: prepend the new envelopes to the e->e_sibling list */
81087c478bd9Sstevel@tonic-gate 	for (i = 0; i < nsplits; ++i)
81097c478bd9Sstevel@tonic-gate 	{
81107c478bd9Sstevel@tonic-gate 		es = splits[i];
81117c478bd9Sstevel@tonic-gate 		es->e_sibling = e->e_sibling;
81127c478bd9Sstevel@tonic-gate 		e->e_sibling = es;
81137c478bd9Sstevel@tonic-gate 	}
81147c478bd9Sstevel@tonic-gate 	return SM_SPLIT_NEW(nsplits);
81157c478bd9Sstevel@tonic-gate 
81167c478bd9Sstevel@tonic-gate 	/* failure: clean up */
81177c478bd9Sstevel@tonic-gate   failure:
81187c478bd9Sstevel@tonic-gate 	if (i > 0)
81197c478bd9Sstevel@tonic-gate 	{
81207c478bd9Sstevel@tonic-gate 		int j;
81217c478bd9Sstevel@tonic-gate 
81227c478bd9Sstevel@tonic-gate 		for (j = 0; j < i; j++)
81237c478bd9Sstevel@tonic-gate 			(void) unlink(queuename(splits[j], DATAFL_LETTER));
81247c478bd9Sstevel@tonic-gate 	}
81257c478bd9Sstevel@tonic-gate 	e->e_sendqueue = addrs[0];
81267c478bd9Sstevel@tonic-gate 	for (i = 0; i < naddrs - 1; ++i)
81277c478bd9Sstevel@tonic-gate 		addrs[i]->q_next = addrs[i + 1];
81287c478bd9Sstevel@tonic-gate 	addrs[naddrs - 1]->q_next = NULL;
81297c478bd9Sstevel@tonic-gate 	return SM_SPLIT_FAIL;
81307c478bd9Sstevel@tonic-gate }
81317c478bd9Sstevel@tonic-gate 
81327c478bd9Sstevel@tonic-gate /*
81337c478bd9Sstevel@tonic-gate **  SPLIT_WITHIN_QUEUE
81347c478bd9Sstevel@tonic-gate **
81357c478bd9Sstevel@tonic-gate **	Split an envelope with multiple recipients into several
81367c478bd9Sstevel@tonic-gate **	envelopes within the same queue directory, if the number of
81377c478bd9Sstevel@tonic-gate **	recipients exceeds the limit for the queue group.
81387c478bd9Sstevel@tonic-gate **
81397c478bd9Sstevel@tonic-gate **	Parameters:
81407c478bd9Sstevel@tonic-gate **		e -- envelope.
81417c478bd9Sstevel@tonic-gate **
81427c478bd9Sstevel@tonic-gate **	Results:
81437c478bd9Sstevel@tonic-gate **		SM_SPLIT_FAIL on failure
81447c478bd9Sstevel@tonic-gate **		SM_SPLIT_NONE if no splitting occurred,
81457c478bd9Sstevel@tonic-gate **		or 1 + the number of additional envelopes created.
81467c478bd9Sstevel@tonic-gate */
81477c478bd9Sstevel@tonic-gate 
81487c478bd9Sstevel@tonic-gate #define SPLIT_LOG_LEVEL	8
81497c478bd9Sstevel@tonic-gate 
81507c478bd9Sstevel@tonic-gate static int	split_within_queue __P((ENVELOPE *));
81517c478bd9Sstevel@tonic-gate 
81527c478bd9Sstevel@tonic-gate static int
81537c478bd9Sstevel@tonic-gate split_within_queue(e)
81547c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
81557c478bd9Sstevel@tonic-gate {
81567c478bd9Sstevel@tonic-gate 	int maxrcpt, nrcpt, ndead, nsplit, i;
81577c478bd9Sstevel@tonic-gate 	int j, l;
81587c478bd9Sstevel@tonic-gate 	char *lsplits;
81597c478bd9Sstevel@tonic-gate 	ADDRESS *q, **addrs;
81607c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *firstsibling;
81617c478bd9Sstevel@tonic-gate 
81627c478bd9Sstevel@tonic-gate 	if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
81637c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81647c478bd9Sstevel@tonic-gate 
81657c478bd9Sstevel@tonic-gate 	/* don't bother if there is no recipient limit */
81667c478bd9Sstevel@tonic-gate 	maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
81677c478bd9Sstevel@tonic-gate 	if (maxrcpt <= 0)
81687c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81697c478bd9Sstevel@tonic-gate 
81707c478bd9Sstevel@tonic-gate 	/* count recipients */
81717c478bd9Sstevel@tonic-gate 	nrcpt = 0;
81727c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
81737c478bd9Sstevel@tonic-gate 	{
81747c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
81757c478bd9Sstevel@tonic-gate 			continue;
81767c478bd9Sstevel@tonic-gate 		++nrcpt;
81777c478bd9Sstevel@tonic-gate 	}
81787c478bd9Sstevel@tonic-gate 	if (nrcpt <= maxrcpt)
81797c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81807c478bd9Sstevel@tonic-gate 
81817c478bd9Sstevel@tonic-gate 	/*
81827c478bd9Sstevel@tonic-gate 	**  Preserve the recipient list
81837c478bd9Sstevel@tonic-gate 	**  so that we can restore it in case of error.
81847c478bd9Sstevel@tonic-gate 	**  (But we discard dead addresses.)
81857c478bd9Sstevel@tonic-gate 	*/
81867c478bd9Sstevel@tonic-gate 
81877c478bd9Sstevel@tonic-gate 	addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
81887c478bd9Sstevel@tonic-gate 	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
81897c478bd9Sstevel@tonic-gate 	{
81907c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
81917c478bd9Sstevel@tonic-gate 			continue;
81927c478bd9Sstevel@tonic-gate 		addrs[i++] = q;
81937c478bd9Sstevel@tonic-gate 	}
81947c478bd9Sstevel@tonic-gate 
81957c478bd9Sstevel@tonic-gate 	/*
81967c478bd9Sstevel@tonic-gate 	**  Partition the recipient list so that bad and sent addresses
81977c478bd9Sstevel@tonic-gate 	**  come first. These will go with the original envelope, and
81987c478bd9Sstevel@tonic-gate 	**  do not count towards the maxrcpt limit.
81997c478bd9Sstevel@tonic-gate 	**  addrs[] does not contain QS_IS_DEAD() addresses.
82007c478bd9Sstevel@tonic-gate 	*/
82017c478bd9Sstevel@tonic-gate 
82027c478bd9Sstevel@tonic-gate 	ndead = 0;
82037c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrcpt; ++i)
82047c478bd9Sstevel@tonic-gate 	{
82057c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(addrs[i]->q_state) ||
82067c478bd9Sstevel@tonic-gate 		    QS_IS_SENT(addrs[i]->q_state) ||
82077c478bd9Sstevel@tonic-gate 		    QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
82087c478bd9Sstevel@tonic-gate 		{
82097c478bd9Sstevel@tonic-gate 			if (i > ndead)
82107c478bd9Sstevel@tonic-gate 			{
82117c478bd9Sstevel@tonic-gate 				ADDRESS *tmp = addrs[i];
82127c478bd9Sstevel@tonic-gate 
82137c478bd9Sstevel@tonic-gate 				addrs[i] = addrs[ndead];
82147c478bd9Sstevel@tonic-gate 				addrs[ndead] = tmp;
82157c478bd9Sstevel@tonic-gate 			}
82167c478bd9Sstevel@tonic-gate 			++ndead;
82177c478bd9Sstevel@tonic-gate 		}
82187c478bd9Sstevel@tonic-gate 	}
82197c478bd9Sstevel@tonic-gate 
82207c478bd9Sstevel@tonic-gate 	/* Check if no splitting required. */
82217c478bd9Sstevel@tonic-gate 	if (nrcpt - ndead <= maxrcpt)
82227c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
82237c478bd9Sstevel@tonic-gate 
82247c478bd9Sstevel@tonic-gate 	/* fix links */
82257c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrcpt - 1; ++i)
82267c478bd9Sstevel@tonic-gate 		addrs[i]->q_next = addrs[i + 1];
82277c478bd9Sstevel@tonic-gate 	addrs[nrcpt - 1]->q_next = NULL;
82287c478bd9Sstevel@tonic-gate 	e->e_sendqueue = addrs[0];
82297c478bd9Sstevel@tonic-gate 
82307c478bd9Sstevel@tonic-gate 	/* prepare buffer for logging */
82317c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL)
82327c478bd9Sstevel@tonic-gate 	{
82337c478bd9Sstevel@tonic-gate 		l = MAXLINE;
82347c478bd9Sstevel@tonic-gate 		lsplits = sm_malloc(l);
82357c478bd9Sstevel@tonic-gate 		if (lsplits != NULL)
82367c478bd9Sstevel@tonic-gate 			*lsplits = '\0';
82377c478bd9Sstevel@tonic-gate 		j = 0;
82387c478bd9Sstevel@tonic-gate 	}
82397c478bd9Sstevel@tonic-gate 	else
82407c478bd9Sstevel@tonic-gate 	{
82417c478bd9Sstevel@tonic-gate 		/* get rid of stupid compiler warnings */
82427c478bd9Sstevel@tonic-gate 		lsplits = NULL;
82437c478bd9Sstevel@tonic-gate 		j = l = 0;
82447c478bd9Sstevel@tonic-gate 	}
82457c478bd9Sstevel@tonic-gate 
82467c478bd9Sstevel@tonic-gate 	/* split the envelope */
82477c478bd9Sstevel@tonic-gate 	firstsibling = e->e_sibling;
82487c478bd9Sstevel@tonic-gate 	i = maxrcpt + ndead;
82497c478bd9Sstevel@tonic-gate 	nsplit = 0;
82507c478bd9Sstevel@tonic-gate 	for (;;)
82517c478bd9Sstevel@tonic-gate 	{
82527c478bd9Sstevel@tonic-gate 		addrs[i - 1]->q_next = NULL;
82537c478bd9Sstevel@tonic-gate 		ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
82547c478bd9Sstevel@tonic-gate 		if (!dup_df(e, ee))
82557c478bd9Sstevel@tonic-gate 		{
82567c478bd9Sstevel@tonic-gate 
82577c478bd9Sstevel@tonic-gate 			ee = firstsibling;
82587c478bd9Sstevel@tonic-gate 			while (ee != NULL)
82597c478bd9Sstevel@tonic-gate 			{
82607c478bd9Sstevel@tonic-gate 				(void) unlink(queuename(ee, DATAFL_LETTER));
82617c478bd9Sstevel@tonic-gate 				ee = ee->e_sibling;
82627c478bd9Sstevel@tonic-gate 			}
82637c478bd9Sstevel@tonic-gate 
82647c478bd9Sstevel@tonic-gate 			/* Error.  Restore e's sibling & recipient lists. */
82657c478bd9Sstevel@tonic-gate 			e->e_sibling = firstsibling;
82667c478bd9Sstevel@tonic-gate 			for (i = 0; i < nrcpt - 1; ++i)
82677c478bd9Sstevel@tonic-gate 				addrs[i]->q_next = addrs[i + 1];
82687c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
82697c478bd9Sstevel@tonic-gate 				sm_free(lsplits);
82707c478bd9Sstevel@tonic-gate 			return SM_SPLIT_FAIL;
82717c478bd9Sstevel@tonic-gate 		}
82727c478bd9Sstevel@tonic-gate 
82737c478bd9Sstevel@tonic-gate 		/* prepend the new envelope to e->e_sibling */
82747c478bd9Sstevel@tonic-gate 		ee->e_sibling = e->e_sibling;
82757c478bd9Sstevel@tonic-gate 		e->e_sibling = ee;
82767c478bd9Sstevel@tonic-gate 		++nsplit;
82777c478bd9Sstevel@tonic-gate 		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
82787c478bd9Sstevel@tonic-gate 		{
82797c478bd9Sstevel@tonic-gate 			if (j >= l - strlen(ee->e_id) - 3)
82807c478bd9Sstevel@tonic-gate 			{
82817c478bd9Sstevel@tonic-gate 				char *p;
82827c478bd9Sstevel@tonic-gate 
82837c478bd9Sstevel@tonic-gate 				l += MAXLINE;
82847c478bd9Sstevel@tonic-gate 				p = sm_realloc(lsplits, l);
82857c478bd9Sstevel@tonic-gate 				if (p == NULL)
82867c478bd9Sstevel@tonic-gate 				{
82877c478bd9Sstevel@tonic-gate 					/* let's try to get this done */
82887c478bd9Sstevel@tonic-gate 					sm_free(lsplits);
82897c478bd9Sstevel@tonic-gate 					lsplits = NULL;
82907c478bd9Sstevel@tonic-gate 				}
82917c478bd9Sstevel@tonic-gate 				else
82927c478bd9Sstevel@tonic-gate 					lsplits = p;
82937c478bd9Sstevel@tonic-gate 			}
82947c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
82957c478bd9Sstevel@tonic-gate 			{
82967c478bd9Sstevel@tonic-gate 				if (j == 0)
82977c478bd9Sstevel@tonic-gate 					j += sm_strlcat(lsplits + j,
82987c478bd9Sstevel@tonic-gate 							ee->e_id,
82997c478bd9Sstevel@tonic-gate 							l - j);
83007c478bd9Sstevel@tonic-gate 				else
83017c478bd9Sstevel@tonic-gate 					j += sm_strlcat2(lsplits + j,
83027c478bd9Sstevel@tonic-gate 							 "; ",
83037c478bd9Sstevel@tonic-gate 							 ee->e_id,
83047c478bd9Sstevel@tonic-gate 							 l - j);
83057c478bd9Sstevel@tonic-gate 				SM_ASSERT(j < l);
83067c478bd9Sstevel@tonic-gate 			}
83077c478bd9Sstevel@tonic-gate 		}
83087c478bd9Sstevel@tonic-gate 		if (nrcpt - i <= maxrcpt)
83097c478bd9Sstevel@tonic-gate 			break;
83107c478bd9Sstevel@tonic-gate 		i += maxrcpt;
83117c478bd9Sstevel@tonic-gate 	}
83127c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
83137c478bd9Sstevel@tonic-gate 	{
83147c478bd9Sstevel@tonic-gate 		if (nsplit > 0)
83157c478bd9Sstevel@tonic-gate 		{
83167c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_NOTICE, e->e_id,
83177c478bd9Sstevel@tonic-gate 				  "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
83187c478bd9Sstevel@tonic-gate 				  maxrcpt, nrcpt - ndead, nsplit,
83197c478bd9Sstevel@tonic-gate 				  nsplit > 1 ? "s" : "", lsplits);
83207c478bd9Sstevel@tonic-gate 		}
83217c478bd9Sstevel@tonic-gate 		sm_free(lsplits);
83227c478bd9Sstevel@tonic-gate 	}
83237c478bd9Sstevel@tonic-gate 	return SM_SPLIT_NEW(nsplit);
83247c478bd9Sstevel@tonic-gate }
83257c478bd9Sstevel@tonic-gate /*
83267c478bd9Sstevel@tonic-gate **  SPLIT_BY_RECIPIENT
83277c478bd9Sstevel@tonic-gate **
83287c478bd9Sstevel@tonic-gate **	Split an envelope with multiple recipients into multiple
83297c478bd9Sstevel@tonic-gate **	envelopes as required by the sendmail configuration.
83307c478bd9Sstevel@tonic-gate **
83317c478bd9Sstevel@tonic-gate **	Parameters:
83327c478bd9Sstevel@tonic-gate **		e -- envelope.
83337c478bd9Sstevel@tonic-gate **
83347c478bd9Sstevel@tonic-gate **	Results:
83357c478bd9Sstevel@tonic-gate **		Returns true on success, false on failure.
83367c478bd9Sstevel@tonic-gate **
83377c478bd9Sstevel@tonic-gate **	Side Effects:
83387c478bd9Sstevel@tonic-gate **		see split_across_queue_groups(), split_within_queue(e)
83397c478bd9Sstevel@tonic-gate */
83407c478bd9Sstevel@tonic-gate 
83417c478bd9Sstevel@tonic-gate bool
83427c478bd9Sstevel@tonic-gate split_by_recipient(e)
83437c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
83447c478bd9Sstevel@tonic-gate {
83457c478bd9Sstevel@tonic-gate 	int split, n, i, j, l;
83467c478bd9Sstevel@tonic-gate 	char *lsplits;
83477c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *next, *firstsibling;
83487c478bd9Sstevel@tonic-gate 
83497c478bd9Sstevel@tonic-gate 	if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
83507c478bd9Sstevel@tonic-gate 	    bitset(EF_SPLIT, e->e_flags))
83517c478bd9Sstevel@tonic-gate 		return true;
83527c478bd9Sstevel@tonic-gate 	n = split_across_queue_groups(e);
83537c478bd9Sstevel@tonic-gate 	if (n == SM_SPLIT_FAIL)
83547c478bd9Sstevel@tonic-gate 		return false;
83557c478bd9Sstevel@tonic-gate 	firstsibling = ee = e->e_sibling;
83567c478bd9Sstevel@tonic-gate 	if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
83577c478bd9Sstevel@tonic-gate 	{
83587c478bd9Sstevel@tonic-gate 		l = MAXLINE;
83597c478bd9Sstevel@tonic-gate 		lsplits = sm_malloc(l);
83607c478bd9Sstevel@tonic-gate 		if (lsplits != NULL)
83617c478bd9Sstevel@tonic-gate 			*lsplits = '\0';
83627c478bd9Sstevel@tonic-gate 		j = 0;
83637c478bd9Sstevel@tonic-gate 	}
83647c478bd9Sstevel@tonic-gate 	else
83657c478bd9Sstevel@tonic-gate 	{
83667c478bd9Sstevel@tonic-gate 		/* get rid of stupid compiler warnings */
83677c478bd9Sstevel@tonic-gate 		lsplits = NULL;
83687c478bd9Sstevel@tonic-gate 		j = l = 0;
83697c478bd9Sstevel@tonic-gate 	}
83707c478bd9Sstevel@tonic-gate 	for (i = 1; i < n; ++i)
83717c478bd9Sstevel@tonic-gate 	{
83727c478bd9Sstevel@tonic-gate 		next = ee->e_sibling;
83737c478bd9Sstevel@tonic-gate 		if (split_within_queue(ee) == SM_SPLIT_FAIL)
83747c478bd9Sstevel@tonic-gate 		{
83757c478bd9Sstevel@tonic-gate 			e->e_sibling = firstsibling;
83767c478bd9Sstevel@tonic-gate 			return false;
83777c478bd9Sstevel@tonic-gate 		}
83787c478bd9Sstevel@tonic-gate 		ee->e_flags |= EF_SPLIT;
83797c478bd9Sstevel@tonic-gate 		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
83807c478bd9Sstevel@tonic-gate 		{
83817c478bd9Sstevel@tonic-gate 			if (j >= l - strlen(ee->e_id) - 3)
83827c478bd9Sstevel@tonic-gate 			{
83837c478bd9Sstevel@tonic-gate 				char *p;
83847c478bd9Sstevel@tonic-gate 
83857c478bd9Sstevel@tonic-gate 				l += MAXLINE;
83867c478bd9Sstevel@tonic-gate 				p = sm_realloc(lsplits, l);
83877c478bd9Sstevel@tonic-gate 				if (p == NULL)
83887c478bd9Sstevel@tonic-gate 				{
83897c478bd9Sstevel@tonic-gate 					/* let's try to get this done */
83907c478bd9Sstevel@tonic-gate 					sm_free(lsplits);
83917c478bd9Sstevel@tonic-gate 					lsplits = NULL;
83927c478bd9Sstevel@tonic-gate 				}
83937c478bd9Sstevel@tonic-gate 				else
83947c478bd9Sstevel@tonic-gate 					lsplits = p;
83957c478bd9Sstevel@tonic-gate 			}
83967c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
83977c478bd9Sstevel@tonic-gate 			{
83987c478bd9Sstevel@tonic-gate 				if (j == 0)
83997c478bd9Sstevel@tonic-gate 					j += sm_strlcat(lsplits + j,
84007c478bd9Sstevel@tonic-gate 							ee->e_id, l - j);
84017c478bd9Sstevel@tonic-gate 				else
84027c478bd9Sstevel@tonic-gate 					j += sm_strlcat2(lsplits + j, "; ",
84037c478bd9Sstevel@tonic-gate 							 ee->e_id, l - j);
84047c478bd9Sstevel@tonic-gate 				SM_ASSERT(j < l);
84057c478bd9Sstevel@tonic-gate 			}
84067c478bd9Sstevel@tonic-gate 		}
84077c478bd9Sstevel@tonic-gate 		ee = next;
84087c478bd9Sstevel@tonic-gate 	}
84097c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
84107c478bd9Sstevel@tonic-gate 	{
84117c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
84127c478bd9Sstevel@tonic-gate 			  n - 1, n > 2 ? "s" : "", lsplits);
84137c478bd9Sstevel@tonic-gate 		sm_free(lsplits);
84147c478bd9Sstevel@tonic-gate 	}
84157c478bd9Sstevel@tonic-gate 	split = split_within_queue(e) != SM_SPLIT_FAIL;
84167c478bd9Sstevel@tonic-gate 	if (split)
84177c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_SPLIT;
84187c478bd9Sstevel@tonic-gate 	return split;
84197c478bd9Sstevel@tonic-gate }
84207c478bd9Sstevel@tonic-gate 
84217c478bd9Sstevel@tonic-gate /*
84227c478bd9Sstevel@tonic-gate **  QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
84237c478bd9Sstevel@tonic-gate **
84247c478bd9Sstevel@tonic-gate **	Add/remove quarantine reason and requeue appropriately.
84257c478bd9Sstevel@tonic-gate **
84267c478bd9Sstevel@tonic-gate **	Parameters:
84277c478bd9Sstevel@tonic-gate **		qgrp -- queue group for the item
84287c478bd9Sstevel@tonic-gate **		qdir -- queue directory in the given queue group
84297c478bd9Sstevel@tonic-gate **		e -- envelope information for the item
84307c478bd9Sstevel@tonic-gate **		reason -- quarantine reason, NULL means unquarantine.
84317c478bd9Sstevel@tonic-gate **
84327c478bd9Sstevel@tonic-gate **	Results:
84337c478bd9Sstevel@tonic-gate **		true if item changed, false otherwise
84347c478bd9Sstevel@tonic-gate **
84357c478bd9Sstevel@tonic-gate **	Side Effects:
84367c478bd9Sstevel@tonic-gate **		Changes quarantine tag in queue file and renames it.
84377c478bd9Sstevel@tonic-gate */
84387c478bd9Sstevel@tonic-gate 
84397c478bd9Sstevel@tonic-gate static bool
84407c478bd9Sstevel@tonic-gate quarantine_queue_item(qgrp, qdir, e, reason)
84417c478bd9Sstevel@tonic-gate 	int qgrp;
84427c478bd9Sstevel@tonic-gate 	int qdir;
84437c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
84447c478bd9Sstevel@tonic-gate 	char *reason;
84457c478bd9Sstevel@tonic-gate {
84467c478bd9Sstevel@tonic-gate 	bool dirty = false;
84477c478bd9Sstevel@tonic-gate 	bool failing = false;
84487c478bd9Sstevel@tonic-gate 	bool foundq = false;
84497c478bd9Sstevel@tonic-gate 	bool finished = false;
84507c478bd9Sstevel@tonic-gate 	int fd;
84517c478bd9Sstevel@tonic-gate 	int flags;
84527c478bd9Sstevel@tonic-gate 	int oldtype;
84537c478bd9Sstevel@tonic-gate 	int newtype;
84547c478bd9Sstevel@tonic-gate 	int save_errno;
84557c478bd9Sstevel@tonic-gate 	MODE_T oldumask = 0;
84567c478bd9Sstevel@tonic-gate 	SM_FILE_T *oldqfp, *tempqfp;
84577c478bd9Sstevel@tonic-gate 	char *bp;
8458058561cbSjbeck 	int bufsize;
84597c478bd9Sstevel@tonic-gate 	char oldqf[MAXPATHLEN];
84607c478bd9Sstevel@tonic-gate 	char tempqf[MAXPATHLEN];
84617c478bd9Sstevel@tonic-gate 	char newqf[MAXPATHLEN];
84627c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
84637c478bd9Sstevel@tonic-gate 
84647c478bd9Sstevel@tonic-gate 	oldtype = queue_letter(e, ANYQFL_LETTER);
8465058561cbSjbeck 	(void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof(oldqf));
8466058561cbSjbeck 	(void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof(tempqf));
84677c478bd9Sstevel@tonic-gate 
84687c478bd9Sstevel@tonic-gate 	/*
84697c478bd9Sstevel@tonic-gate 	**  Instead of duplicating all the open
84707c478bd9Sstevel@tonic-gate 	**  and lock code here, tell readqf() to
84717c478bd9Sstevel@tonic-gate 	**  do that work and return the open
84727c478bd9Sstevel@tonic-gate 	**  file pointer in e_lockfp.  Note that
84737c478bd9Sstevel@tonic-gate 	**  we must release the locks properly when
84747c478bd9Sstevel@tonic-gate 	**  we are done.
84757c478bd9Sstevel@tonic-gate 	*/
84767c478bd9Sstevel@tonic-gate 
84777c478bd9Sstevel@tonic-gate 	if (!readqf(e, true))
84787c478bd9Sstevel@tonic-gate 	{
84797c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84807c478bd9Sstevel@tonic-gate 				     "Skipping %s\n", qid_printname(e));
84817c478bd9Sstevel@tonic-gate 		return false;
84827c478bd9Sstevel@tonic-gate 	}
84837c478bd9Sstevel@tonic-gate 	oldqfp = e->e_lockfp;
84847c478bd9Sstevel@tonic-gate 
84857c478bd9Sstevel@tonic-gate 	/* open the new queue file */
84867c478bd9Sstevel@tonic-gate 	flags = O_CREAT|O_WRONLY|O_EXCL;
84877c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
84887c478bd9Sstevel@tonic-gate 		oldumask = umask(002);
84897c478bd9Sstevel@tonic-gate 	fd = open(tempqf, flags, QueueFileMode);
84907c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
84917c478bd9Sstevel@tonic-gate 		(void) umask(oldumask);
84927c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
84937c478bd9Sstevel@tonic-gate 
84947c478bd9Sstevel@tonic-gate 	if (fd < 0)
84957c478bd9Sstevel@tonic-gate 	{
84967c478bd9Sstevel@tonic-gate 		save_errno = errno;
84977c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84987c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not open %s: %s\n",
84997c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf,
85007c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
85017c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
85027c478bd9Sstevel@tonic-gate 		return false;
85037c478bd9Sstevel@tonic-gate 	}
85047c478bd9Sstevel@tonic-gate 	if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
85057c478bd9Sstevel@tonic-gate 	{
85067c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
85077c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not lock %s\n",
85087c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf);
85097c478bd9Sstevel@tonic-gate 		(void) close(fd);
85107c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
85117c478bd9Sstevel@tonic-gate 		return false;
85127c478bd9Sstevel@tonic-gate 	}
85137c478bd9Sstevel@tonic-gate 
85147c478bd9Sstevel@tonic-gate 	tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
85157c478bd9Sstevel@tonic-gate 			     SM_IO_WRONLY_B, NULL);
85167c478bd9Sstevel@tonic-gate 	if (tempqfp == NULL)
85177c478bd9Sstevel@tonic-gate 	{
85187c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
85197c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not lock %s\n",
85207c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf);
85217c478bd9Sstevel@tonic-gate 		(void) close(fd);
85227c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
85237c478bd9Sstevel@tonic-gate 		return false;
85247c478bd9Sstevel@tonic-gate 	}
85257c478bd9Sstevel@tonic-gate 
85267c478bd9Sstevel@tonic-gate 	/* Copy the data over, changing the quarantine reason */
8527058561cbSjbeck 	while (bufsize = sizeof(buf),
8528058561cbSjbeck 	       (bp = fgetfolded(buf, &bufsize, oldqfp)) != NULL)
85297c478bd9Sstevel@tonic-gate 	{
85307c478bd9Sstevel@tonic-gate 		if (tTd(40, 4))
85317c478bd9Sstevel@tonic-gate 			sm_dprintf("+++++ %s\n", bp);
85327c478bd9Sstevel@tonic-gate 		switch (bp[0])
85337c478bd9Sstevel@tonic-gate 		{
85347c478bd9Sstevel@tonic-gate 		  case 'q':		/* quarantine reason */
85357c478bd9Sstevel@tonic-gate 			foundq = true;
85367c478bd9Sstevel@tonic-gate 			if (reason == NULL)
85377c478bd9Sstevel@tonic-gate 			{
85387c478bd9Sstevel@tonic-gate 				if (Verbose)
85397c478bd9Sstevel@tonic-gate 				{
85407c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85417c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85427c478bd9Sstevel@tonic-gate 							     "%s: Removed quarantine of \"%s\"\n",
85437c478bd9Sstevel@tonic-gate 							     e->e_id, &bp[1]);
85447c478bd9Sstevel@tonic-gate 				}
85457c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "unquarantine");
85467c478bd9Sstevel@tonic-gate 				dirty = true;
85477c478bd9Sstevel@tonic-gate 			}
85487c478bd9Sstevel@tonic-gate 			else if (strcmp(reason, &bp[1]) == 0)
85497c478bd9Sstevel@tonic-gate 			{
85507c478bd9Sstevel@tonic-gate 				if (Verbose)
85517c478bd9Sstevel@tonic-gate 				{
85527c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85537c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85547c478bd9Sstevel@tonic-gate 							     "%s: Already quarantined with \"%s\"\n",
85557c478bd9Sstevel@tonic-gate 							     e->e_id, reason);
85567c478bd9Sstevel@tonic-gate 				}
85577c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85587c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85597c478bd9Sstevel@tonic-gate 			}
85607c478bd9Sstevel@tonic-gate 			else
85617c478bd9Sstevel@tonic-gate 			{
85627c478bd9Sstevel@tonic-gate 				if (Verbose)
85637c478bd9Sstevel@tonic-gate 				{
85647c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85657c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85667c478bd9Sstevel@tonic-gate 							     "%s: Quarantine changed from \"%s\" to \"%s\"\n",
85677c478bd9Sstevel@tonic-gate 							     e->e_id, &bp[1],
85687c478bd9Sstevel@tonic-gate 							     reason);
85697c478bd9Sstevel@tonic-gate 				}
85707c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85717c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85727c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
85737c478bd9Sstevel@tonic-gate 					  reason);
85747c478bd9Sstevel@tonic-gate 				dirty = true;
85757c478bd9Sstevel@tonic-gate 			}
85767c478bd9Sstevel@tonic-gate 			break;
85777c478bd9Sstevel@tonic-gate 
85787c478bd9Sstevel@tonic-gate 		  case 'S':
85797c478bd9Sstevel@tonic-gate 			/*
85807c478bd9Sstevel@tonic-gate 			**  If we are quarantining an unquarantined item,
85817c478bd9Sstevel@tonic-gate 			**  need to put in a new 'q' line before it's
85827c478bd9Sstevel@tonic-gate 			**  too late.
85837c478bd9Sstevel@tonic-gate 			*/
85847c478bd9Sstevel@tonic-gate 
85857c478bd9Sstevel@tonic-gate 			if (!foundq && reason != NULL)
85867c478bd9Sstevel@tonic-gate 			{
85877c478bd9Sstevel@tonic-gate 				if (Verbose)
85887c478bd9Sstevel@tonic-gate 				{
85897c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85907c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85917c478bd9Sstevel@tonic-gate 							     "%s: Quarantined with \"%s\"\n",
85927c478bd9Sstevel@tonic-gate 							     e->e_id, reason);
85937c478bd9Sstevel@tonic-gate 				}
85947c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85957c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85967c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
85977c478bd9Sstevel@tonic-gate 					  reason);
85987c478bd9Sstevel@tonic-gate 				foundq = true;
85997c478bd9Sstevel@tonic-gate 				dirty = true;
86007c478bd9Sstevel@tonic-gate 			}
86017c478bd9Sstevel@tonic-gate 
86027c478bd9Sstevel@tonic-gate 			/* Copy the line to the new file */
86037c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
86047c478bd9Sstevel@tonic-gate 					     "%s\n", bp);
86057c478bd9Sstevel@tonic-gate 			break;
86067c478bd9Sstevel@tonic-gate 
86077c478bd9Sstevel@tonic-gate 		  case '.':
86087c478bd9Sstevel@tonic-gate 			finished = true;
86097c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
86107c478bd9Sstevel@tonic-gate 
86117c478bd9Sstevel@tonic-gate 		  default:
86127c478bd9Sstevel@tonic-gate 			/* Copy the line to the new file */
86137c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
86147c478bd9Sstevel@tonic-gate 					     "%s\n", bp);
86157c478bd9Sstevel@tonic-gate 			break;
86167c478bd9Sstevel@tonic-gate 		}
8617058561cbSjbeck 		if (bp != buf)
8618058561cbSjbeck 			sm_free(bp);
86197c478bd9Sstevel@tonic-gate 	}
86207c478bd9Sstevel@tonic-gate 
86217c478bd9Sstevel@tonic-gate 	/* Make sure we read the whole old file */
86227c478bd9Sstevel@tonic-gate 	errno = sm_io_error(tempqfp);
86237c478bd9Sstevel@tonic-gate 	if (errno != 0 && errno != SM_IO_EOF)
86247c478bd9Sstevel@tonic-gate 	{
86257c478bd9Sstevel@tonic-gate 		save_errno = errno;
86267c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86277c478bd9Sstevel@tonic-gate 				     "Skipping %s: Error reading %s: %s\n",
86287c478bd9Sstevel@tonic-gate 				     qid_printname(e), oldqf,
86297c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86307c478bd9Sstevel@tonic-gate 		failing = true;
86317c478bd9Sstevel@tonic-gate 	}
86327c478bd9Sstevel@tonic-gate 
86337c478bd9Sstevel@tonic-gate 	if (!failing && !finished)
86347c478bd9Sstevel@tonic-gate 	{
86357c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86367c478bd9Sstevel@tonic-gate 				     "Skipping %s: Incomplete file: %s\n",
86377c478bd9Sstevel@tonic-gate 				     qid_printname(e), oldqf);
86387c478bd9Sstevel@tonic-gate 		failing = true;
86397c478bd9Sstevel@tonic-gate 	}
86407c478bd9Sstevel@tonic-gate 
86417c478bd9Sstevel@tonic-gate 	/* Check if we actually changed anything or we can just bail now */
86427c478bd9Sstevel@tonic-gate 	if (!dirty)
86437c478bd9Sstevel@tonic-gate 	{
86447c478bd9Sstevel@tonic-gate 		/* pretend we failed, even though we technically didn't */
86457c478bd9Sstevel@tonic-gate 		failing = true;
86467c478bd9Sstevel@tonic-gate 	}
86477c478bd9Sstevel@tonic-gate 
86487c478bd9Sstevel@tonic-gate 	/* Make sure we wrote things out safely */
86497c478bd9Sstevel@tonic-gate 	if (!failing &&
86507c478bd9Sstevel@tonic-gate 	    (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
86517c478bd9Sstevel@tonic-gate 	     ((SuperSafe == SAFE_REALLY ||
86527c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_REALLY_POSTMILTER ||
86537c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_INTERACTIVE) &&
86547c478bd9Sstevel@tonic-gate 	      fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
86557c478bd9Sstevel@tonic-gate 	     ((errno = sm_io_error(tempqfp)) != 0)))
86567c478bd9Sstevel@tonic-gate 	{
86577c478bd9Sstevel@tonic-gate 		save_errno = errno;
86587c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86597c478bd9Sstevel@tonic-gate 				     "Skipping %s: Error writing %s: %s\n",
86607c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf,
86617c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86627c478bd9Sstevel@tonic-gate 		failing = true;
86637c478bd9Sstevel@tonic-gate 	}
86647c478bd9Sstevel@tonic-gate 
86657c478bd9Sstevel@tonic-gate 
86667c478bd9Sstevel@tonic-gate 	/* Figure out the new filename */
86677c478bd9Sstevel@tonic-gate 	newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
86687c478bd9Sstevel@tonic-gate 	if (oldtype == newtype)
86697c478bd9Sstevel@tonic-gate 	{
86707c478bd9Sstevel@tonic-gate 		/* going to rename tempqf to oldqf */
8671058561cbSjbeck 		(void) sm_strlcpy(newqf, oldqf, sizeof(newqf));
86727c478bd9Sstevel@tonic-gate 	}
86737c478bd9Sstevel@tonic-gate 	else
86747c478bd9Sstevel@tonic-gate 	{
86757c478bd9Sstevel@tonic-gate 		/* going to rename tempqf to new name based on newtype */
8676058561cbSjbeck 		(void) sm_strlcpy(newqf, queuename(e, newtype), sizeof(newqf));
86777c478bd9Sstevel@tonic-gate 	}
86787c478bd9Sstevel@tonic-gate 
86797c478bd9Sstevel@tonic-gate 	save_errno = 0;
86807c478bd9Sstevel@tonic-gate 
86817c478bd9Sstevel@tonic-gate 	/* rename tempqf to newqf */
86827c478bd9Sstevel@tonic-gate 	if (!failing &&
86837c478bd9Sstevel@tonic-gate 	    rename(tempqf, newqf) < 0)
86847c478bd9Sstevel@tonic-gate 		save_errno = (errno == 0) ? EINVAL : errno;
86857c478bd9Sstevel@tonic-gate 
86867c478bd9Sstevel@tonic-gate 	/* Check rename() success */
86877c478bd9Sstevel@tonic-gate 	if (!failing && save_errno != 0)
86887c478bd9Sstevel@tonic-gate 	{
86897c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id,
86907c478bd9Sstevel@tonic-gate 			  "quarantine_queue_item: rename(%s, %s): %s",
86917c478bd9Sstevel@tonic-gate 			  tempqf, newqf, sm_errstring(save_errno));
86927c478bd9Sstevel@tonic-gate 
86937c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86947c478bd9Sstevel@tonic-gate 				     "Error renaming %s to %s: %s\n",
86957c478bd9Sstevel@tonic-gate 				     tempqf, newqf,
86967c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86977c478bd9Sstevel@tonic-gate 		if (oldtype == newtype)
86987c478bd9Sstevel@tonic-gate 		{
86997c478bd9Sstevel@tonic-gate 			/*
87007c478bd9Sstevel@tonic-gate 			**  Bail here since we don't know the state of
87017c478bd9Sstevel@tonic-gate 			**  the filesystem and may need to keep tempqf
87027c478bd9Sstevel@tonic-gate 			**  for the user to rescue us.
87037c478bd9Sstevel@tonic-gate 			*/
87047c478bd9Sstevel@tonic-gate 
87057c478bd9Sstevel@tonic-gate 			RELEASE_QUEUE;
87067c478bd9Sstevel@tonic-gate 			errno = save_errno;
87077c478bd9Sstevel@tonic-gate 			syserr("!452 Error renaming control file %s", tempqf);
87087c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
87097c478bd9Sstevel@tonic-gate 		}
87107c478bd9Sstevel@tonic-gate 		else
87117c478bd9Sstevel@tonic-gate 		{
87127c478bd9Sstevel@tonic-gate 			/* remove new file (if rename() half completed) */
87137c478bd9Sstevel@tonic-gate 			if (xunlink(newqf) < 0)
87147c478bd9Sstevel@tonic-gate 			{
87157c478bd9Sstevel@tonic-gate 				save_errno = errno;
87167c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
87177c478bd9Sstevel@tonic-gate 						     "Error removing %s: %s\n",
87187c478bd9Sstevel@tonic-gate 						     newqf,
87197c478bd9Sstevel@tonic-gate 						     sm_errstring(save_errno));
87207c478bd9Sstevel@tonic-gate 			}
87217c478bd9Sstevel@tonic-gate 
87227c478bd9Sstevel@tonic-gate 			/* tempqf removed below */
87237c478bd9Sstevel@tonic-gate 			failing = true;
87247c478bd9Sstevel@tonic-gate 		}
87257c478bd9Sstevel@tonic-gate 
87267c478bd9Sstevel@tonic-gate 	}
87277c478bd9Sstevel@tonic-gate 
87287c478bd9Sstevel@tonic-gate 	/* If changing file types, need to remove old type */
87297c478bd9Sstevel@tonic-gate 	if (!failing && oldtype != newtype)
87307c478bd9Sstevel@tonic-gate 	{
87317c478bd9Sstevel@tonic-gate 		if (xunlink(oldqf) < 0)
87327c478bd9Sstevel@tonic-gate 		{
87337c478bd9Sstevel@tonic-gate 			save_errno = errno;
87347c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
87357c478bd9Sstevel@tonic-gate 					     "Error removing %s: %s\n",
87367c478bd9Sstevel@tonic-gate 					     oldqf, sm_errstring(save_errno));
87377c478bd9Sstevel@tonic-gate 		}
87387c478bd9Sstevel@tonic-gate 	}
87397c478bd9Sstevel@tonic-gate 
87407c478bd9Sstevel@tonic-gate 	/* see if anything above failed */
87417c478bd9Sstevel@tonic-gate 	if (failing)
87427c478bd9Sstevel@tonic-gate 	{
87437c478bd9Sstevel@tonic-gate 		/* Something failed: remove new file, old file still there */
87447c478bd9Sstevel@tonic-gate 		(void) xunlink(tempqf);
87457c478bd9Sstevel@tonic-gate 	}
87467c478bd9Sstevel@tonic-gate 
87477c478bd9Sstevel@tonic-gate 	/*
87487c478bd9Sstevel@tonic-gate 	**  fsync() after file operations to make sure metadata is
87497c478bd9Sstevel@tonic-gate 	**  written to disk on filesystems in which renames are
87507c478bd9Sstevel@tonic-gate 	**  not guaranteed.  It's ok if they fail, mail won't be lost.
87517c478bd9Sstevel@tonic-gate 	*/
87527c478bd9Sstevel@tonic-gate 
87537c478bd9Sstevel@tonic-gate 	if (SuperSafe != SAFE_NO)
87547c478bd9Sstevel@tonic-gate 	{
87557c478bd9Sstevel@tonic-gate 		/* for soft-updates */
87567c478bd9Sstevel@tonic-gate 		(void) fsync(sm_io_getinfo(tempqfp,
87577c478bd9Sstevel@tonic-gate 					   SM_IO_WHAT_FD, NULL));
87587c478bd9Sstevel@tonic-gate 
87597c478bd9Sstevel@tonic-gate 		if (!failing)
87607c478bd9Sstevel@tonic-gate 		{
87617c478bd9Sstevel@tonic-gate 			/* for soft-updates */
87627c478bd9Sstevel@tonic-gate 			(void) fsync(sm_io_getinfo(oldqfp,
87637c478bd9Sstevel@tonic-gate 						   SM_IO_WHAT_FD, NULL));
87647c478bd9Sstevel@tonic-gate 		}
87657c478bd9Sstevel@tonic-gate 
87667c478bd9Sstevel@tonic-gate 		/* for other odd filesystems */
87677c478bd9Sstevel@tonic-gate 		SYNC_DIR(tempqf, false);
87687c478bd9Sstevel@tonic-gate 	}
87697c478bd9Sstevel@tonic-gate 
87707c478bd9Sstevel@tonic-gate 	/* Close up shop */
87717c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
87727c478bd9Sstevel@tonic-gate 	if (tempqfp != NULL)
87737c478bd9Sstevel@tonic-gate 		(void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
87747c478bd9Sstevel@tonic-gate 	if (oldqfp != NULL)
87757c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
87767c478bd9Sstevel@tonic-gate 
87777c478bd9Sstevel@tonic-gate 	/* All went well */
87787c478bd9Sstevel@tonic-gate 	return !failing;
87797c478bd9Sstevel@tonic-gate }
87807c478bd9Sstevel@tonic-gate 
87817c478bd9Sstevel@tonic-gate /*
87827c478bd9Sstevel@tonic-gate **  QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
87837c478bd9Sstevel@tonic-gate **
87847c478bd9Sstevel@tonic-gate **	Read all matching queue items, add/remove quarantine
87857c478bd9Sstevel@tonic-gate **	reason, and requeue appropriately.
87867c478bd9Sstevel@tonic-gate **
87877c478bd9Sstevel@tonic-gate **	Parameters:
87887c478bd9Sstevel@tonic-gate **		reason -- quarantine reason, "." means unquarantine.
87897c478bd9Sstevel@tonic-gate **		qgrplimit -- limit to single queue group unless NOQGRP
87907c478bd9Sstevel@tonic-gate **
87917c478bd9Sstevel@tonic-gate **	Results:
87927c478bd9Sstevel@tonic-gate **		none.
87937c478bd9Sstevel@tonic-gate **
87947c478bd9Sstevel@tonic-gate **	Side Effects:
87957c478bd9Sstevel@tonic-gate **		Lots of changes to the queue.
87967c478bd9Sstevel@tonic-gate */
87977c478bd9Sstevel@tonic-gate 
87987c478bd9Sstevel@tonic-gate void
87997c478bd9Sstevel@tonic-gate quarantine_queue(reason, qgrplimit)
88007c478bd9Sstevel@tonic-gate 	char *reason;
88017c478bd9Sstevel@tonic-gate 	int qgrplimit;
88027c478bd9Sstevel@tonic-gate {
88037c478bd9Sstevel@tonic-gate 	int changed = 0;
88047c478bd9Sstevel@tonic-gate 	int qgrp;
88057c478bd9Sstevel@tonic-gate 
88067c478bd9Sstevel@tonic-gate 	/* Convert internal representation of unquarantine */
88077c478bd9Sstevel@tonic-gate 	if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
88087c478bd9Sstevel@tonic-gate 		reason = NULL;
88097c478bd9Sstevel@tonic-gate 
88107c478bd9Sstevel@tonic-gate 	if (reason != NULL)
88117c478bd9Sstevel@tonic-gate 	{
88127c478bd9Sstevel@tonic-gate 		/* clean it */
88137c478bd9Sstevel@tonic-gate 		reason = newstr(denlstring(reason, true, true));
88147c478bd9Sstevel@tonic-gate 	}
88157c478bd9Sstevel@tonic-gate 
88167c478bd9Sstevel@tonic-gate 	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
88177c478bd9Sstevel@tonic-gate 	{
88187c478bd9Sstevel@tonic-gate 		int qdir;
88197c478bd9Sstevel@tonic-gate 
88207c478bd9Sstevel@tonic-gate 		if (qgrplimit != NOQGRP && qgrplimit != qgrp)
88217c478bd9Sstevel@tonic-gate 			continue;
88227c478bd9Sstevel@tonic-gate 
88237c478bd9Sstevel@tonic-gate 		for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
88247c478bd9Sstevel@tonic-gate 		{
88257c478bd9Sstevel@tonic-gate 			int i;
88267c478bd9Sstevel@tonic-gate 			int nrequests;
88277c478bd9Sstevel@tonic-gate 
88287c478bd9Sstevel@tonic-gate 			if (StopRequest)
88297c478bd9Sstevel@tonic-gate 				stop_sendmail();
88307c478bd9Sstevel@tonic-gate 
88317c478bd9Sstevel@tonic-gate 			nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
88327c478bd9Sstevel@tonic-gate 
88337c478bd9Sstevel@tonic-gate 			/* first see if there is anything */
88347c478bd9Sstevel@tonic-gate 			if (nrequests <= 0)
88357c478bd9Sstevel@tonic-gate 			{
88367c478bd9Sstevel@tonic-gate 				if (Verbose)
88377c478bd9Sstevel@tonic-gate 				{
88387c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
88397c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT, "%s: no matches\n",
88407c478bd9Sstevel@tonic-gate 							     qid_printqueue(qgrp, qdir));
88417c478bd9Sstevel@tonic-gate 				}
88427c478bd9Sstevel@tonic-gate 				continue;
88437c478bd9Sstevel@tonic-gate 			}
88447c478bd9Sstevel@tonic-gate 
88457c478bd9Sstevel@tonic-gate 			if (Verbose)
88467c478bd9Sstevel@tonic-gate 			{
88477c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout,
88487c478bd9Sstevel@tonic-gate 						     SM_TIME_DEFAULT, "Processing %s:\n",
88497c478bd9Sstevel@tonic-gate 						     qid_printqueue(qgrp, qdir));
88507c478bd9Sstevel@tonic-gate 			}
88517c478bd9Sstevel@tonic-gate 
88527c478bd9Sstevel@tonic-gate 			for (i = 0; i < WorkListCount; i++)
88537c478bd9Sstevel@tonic-gate 			{
88547c478bd9Sstevel@tonic-gate 				ENVELOPE e;
88557c478bd9Sstevel@tonic-gate 
88567c478bd9Sstevel@tonic-gate 				if (StopRequest)
88577c478bd9Sstevel@tonic-gate 					stop_sendmail();
88587c478bd9Sstevel@tonic-gate 
88597c478bd9Sstevel@tonic-gate 				/* setup envelope */
88607c478bd9Sstevel@tonic-gate 				clearenvelope(&e, true, sm_rpool_new_x(NULL));
88617c478bd9Sstevel@tonic-gate 				e.e_id = WorkList[i].w_name + 2;
88627c478bd9Sstevel@tonic-gate 				e.e_qgrp = qgrp;
88637c478bd9Sstevel@tonic-gate 				e.e_qdir = qdir;
88647c478bd9Sstevel@tonic-gate 
88657c478bd9Sstevel@tonic-gate 				if (tTd(70, 101))
88667c478bd9Sstevel@tonic-gate 				{
88677c478bd9Sstevel@tonic-gate 					sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88687c478bd9Sstevel@tonic-gate 						      "Would do %s\n", e.e_id);
88697c478bd9Sstevel@tonic-gate 					changed++;
88707c478bd9Sstevel@tonic-gate 				}
88717c478bd9Sstevel@tonic-gate 				else if (quarantine_queue_item(qgrp, qdir,
88727c478bd9Sstevel@tonic-gate 							       &e, reason))
88737c478bd9Sstevel@tonic-gate 					changed++;
88747c478bd9Sstevel@tonic-gate 
88757c478bd9Sstevel@tonic-gate 				/* clean up */
88767c478bd9Sstevel@tonic-gate 				sm_rpool_free(e.e_rpool);
88777c478bd9Sstevel@tonic-gate 				e.e_rpool = NULL;
88787c478bd9Sstevel@tonic-gate 			}
88797c478bd9Sstevel@tonic-gate 			if (WorkList != NULL)
88807c478bd9Sstevel@tonic-gate 				sm_free(WorkList); /* XXX */
88817c478bd9Sstevel@tonic-gate 			WorkList = NULL;
88827c478bd9Sstevel@tonic-gate 			WorkListSize = 0;
88837c478bd9Sstevel@tonic-gate 			WorkListCount = 0;
88847c478bd9Sstevel@tonic-gate 		}
88857c478bd9Sstevel@tonic-gate 	}
88867c478bd9Sstevel@tonic-gate 	if (Verbose)
88877c478bd9Sstevel@tonic-gate 	{
88887c478bd9Sstevel@tonic-gate 		if (changed == 0)
88897c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88907c478bd9Sstevel@tonic-gate 					     "No changes\n");
88917c478bd9Sstevel@tonic-gate 		else
88927c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88937c478bd9Sstevel@tonic-gate 					     "%d change%s\n",
88947c478bd9Sstevel@tonic-gate 					     changed,
88957c478bd9Sstevel@tonic-gate 					     changed == 1 ? "" : "s");
88967c478bd9Sstevel@tonic-gate 	}
88977c478bd9Sstevel@tonic-gate }
8898