xref: /illumos-gate/usr/src/cmd/sendmail/src/queue.c (revision 058561cb)
17c478bd9Sstevel@tonic-gate /*
23ee0e492Sjbeck  * Copyright (c) 1998-2006 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*058561cbSjbeck SM_RCSID("@(#)$Id: queue.c,v 8.970 2006/12/19 01:15:07 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 */
152*058561cbSjbeck 
153*058561cbSjbeck /*
154*058561cbSjbeck **  Note: workcmpf?() don't use a prototype because it will cause a conflict
155*058561cbSjbeck **  with the qsort() call (which expects something like
156*058561cbSjbeck **  int (*compar)(const void *, const void *), not (WORK *, WORK *))
157*058561cbSjbeck */
158*058561cbSjbeck 
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 */
194*058561cbSjbeck 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);
385*058561cbSjbeck 	(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 
395*058561cbSjbeck 		(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,
4327c478bd9Sstevel@tonic-gate 							  "queueup: cannot create %s, uid=%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 
514*058561cbSjbeck 	(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;
575*058561cbSjbeck 		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 
776*058561cbSjbeck 	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";
780*058561cbSjbeck 	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 		{
798*058561cbSjbeck 			(void) expand(h->h_value, buf, sizeof(buf), e);
7997c478bd9Sstevel@tonic-gate 			if (buf[0] == '\0')
8007c478bd9Sstevel@tonic-gate 				continue;
8017c478bd9Sstevel@tonic-gate 		}
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 		/* output this header */
8047c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "H?");
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 		/* output conditional macro if present */
8077c478bd9Sstevel@tonic-gate 		if (h->h_macro != '\0')
8087c478bd9Sstevel@tonic-gate 		{
8097c478bd9Sstevel@tonic-gate 			if (bitset(0200, h->h_macro))
8107c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
8117c478bd9Sstevel@tonic-gate 						     "${%s}",
8127c478bd9Sstevel@tonic-gate 						      macname(bitidx(h->h_macro)));
8137c478bd9Sstevel@tonic-gate 			else
8147c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT,
8157c478bd9Sstevel@tonic-gate 						     "$%c", h->h_macro);
8167c478bd9Sstevel@tonic-gate 		}
8177c478bd9Sstevel@tonic-gate 		else if (!bitzerop(h->h_mflags) &&
8187c478bd9Sstevel@tonic-gate 			 bitset(H_CHECK|H_ACHECK, h->h_flags))
8197c478bd9Sstevel@tonic-gate 		{
8207c478bd9Sstevel@tonic-gate 			int j;
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 			/* if conditional, output the set of conditions */
8237c478bd9Sstevel@tonic-gate 			for (j = '\0'; j <= '\177'; j++)
8247c478bd9Sstevel@tonic-gate 				if (bitnset(j, h->h_mflags))
8257c478bd9Sstevel@tonic-gate 					(void) sm_io_putc(tfp, SM_TIME_DEFAULT,
8267c478bd9Sstevel@tonic-gate 							  j);
8277c478bd9Sstevel@tonic-gate 		}
8287c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, '?');
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 		/* output the header: expand macros, convert addresses */
8317c478bd9Sstevel@tonic-gate 		if (bitset(H_DEFAULT, h->h_flags) &&
8327c478bd9Sstevel@tonic-gate 		    !bitset(H_BINDLATE, h->h_flags))
8337c478bd9Sstevel@tonic-gate 		{
834*058561cbSjbeck 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n",
8357c478bd9Sstevel@tonic-gate 					     h->h_field,
8367c478bd9Sstevel@tonic-gate 					     denlstring(buf, false, true));
8377c478bd9Sstevel@tonic-gate 		}
8387c478bd9Sstevel@tonic-gate 		else if (bitset(H_FROM|H_RCPT, h->h_flags) &&
8397c478bd9Sstevel@tonic-gate 			 !bitset(H_BINDLATE, h->h_flags))
8407c478bd9Sstevel@tonic-gate 		{
8417c478bd9Sstevel@tonic-gate 			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
8427c478bd9Sstevel@tonic-gate 			SM_FILE_T *savetrace = TrafficLogFile;
8437c478bd9Sstevel@tonic-gate 
8447c478bd9Sstevel@tonic-gate 			TrafficLogFile = NULL;
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 			if (bitset(H_FROM, h->h_flags))
8477c478bd9Sstevel@tonic-gate 				oldstyle = false;
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 			commaize(h, h->h_value, oldstyle, &mcibuf, e);
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 			TrafficLogFile = savetrace;
8527c478bd9Sstevel@tonic-gate 		}
8537c478bd9Sstevel@tonic-gate 		else
8547c478bd9Sstevel@tonic-gate 		{
855*058561cbSjbeck 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s:%s\n",
8567c478bd9Sstevel@tonic-gate 					     h->h_field,
8577c478bd9Sstevel@tonic-gate 					     denlstring(h->h_value, false,
8587c478bd9Sstevel@tonic-gate 							true));
8597c478bd9Sstevel@tonic-gate 		}
8607c478bd9Sstevel@tonic-gate 	}
8617c478bd9Sstevel@tonic-gate 
8627c478bd9Sstevel@tonic-gate 	/*
8637c478bd9Sstevel@tonic-gate 	**  Clean up.
8647c478bd9Sstevel@tonic-gate 	**
8657c478bd9Sstevel@tonic-gate 	**	Write a terminator record -- this is to prevent
8667c478bd9Sstevel@tonic-gate 	**	scurrilous crackers from appending any data.
8677c478bd9Sstevel@tonic-gate 	*/
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ".\n");
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	if (sm_io_flush(tfp, SM_TIME_DEFAULT) != 0 ||
8727c478bd9Sstevel@tonic-gate 	    ((SuperSafe == SAFE_REALLY ||
8737c478bd9Sstevel@tonic-gate 	      SuperSafe == SAFE_REALLY_POSTMILTER ||
8747c478bd9Sstevel@tonic-gate 	      (SuperSafe == SAFE_INTERACTIVE && msync)) &&
8757c478bd9Sstevel@tonic-gate 	     fsync(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL)) < 0) ||
8767c478bd9Sstevel@tonic-gate 	    sm_io_error(tfp))
8777c478bd9Sstevel@tonic-gate 	{
8787c478bd9Sstevel@tonic-gate 		if (newid)
8797c478bd9Sstevel@tonic-gate 			syserr("!552 Error writing control file %s", tf);
8807c478bd9Sstevel@tonic-gate 		else
8817c478bd9Sstevel@tonic-gate 			syserr("!452 Error writing control file %s", tf);
8827c478bd9Sstevel@tonic-gate 	}
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	if (!newid)
8857c478bd9Sstevel@tonic-gate 	{
8867c478bd9Sstevel@tonic-gate 		char new = queue_letter(e, ANYQFL_LETTER);
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate 		/* rename (locked) tf to be (locked) [qh]f */
8897c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER),
890*058561cbSjbeck 				  sizeof(qf));
8917c478bd9Sstevel@tonic-gate 		if (rename(tf, qf) < 0)
8927c478bd9Sstevel@tonic-gate 			syserr("cannot rename(%s, %s), uid=%d",
8937c478bd9Sstevel@tonic-gate 				tf, qf, (int) geteuid());
8947c478bd9Sstevel@tonic-gate 		else
8957c478bd9Sstevel@tonic-gate 		{
8967c478bd9Sstevel@tonic-gate 			/*
8977c478bd9Sstevel@tonic-gate 			**  Check if type has changed and only
8987c478bd9Sstevel@tonic-gate 			**  remove the old item if the rename above
8997c478bd9Sstevel@tonic-gate 			**  succeeded.
9007c478bd9Sstevel@tonic-gate 			*/
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 			if (e->e_qfletter != '\0' &&
9037c478bd9Sstevel@tonic-gate 			    e->e_qfletter != new)
9047c478bd9Sstevel@tonic-gate 			{
9057c478bd9Sstevel@tonic-gate 				if (tTd(40, 5))
9067c478bd9Sstevel@tonic-gate 				{
9077c478bd9Sstevel@tonic-gate 					sm_dprintf("type changed from %c to %c\n",
9087c478bd9Sstevel@tonic-gate 						   e->e_qfletter, new);
9097c478bd9Sstevel@tonic-gate 				}
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 				if (unlink(queuename(e, e->e_qfletter)) < 0)
9127c478bd9Sstevel@tonic-gate 				{
9137c478bd9Sstevel@tonic-gate 					/* XXX: something more drastic? */
9147c478bd9Sstevel@tonic-gate 					if (LogLevel > 0)
9157c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, e->e_id,
9167c478bd9Sstevel@tonic-gate 							  "queueup: unlink(%s) failed: %s",
9177c478bd9Sstevel@tonic-gate 							  queuename(e, e->e_qfletter),
9187c478bd9Sstevel@tonic-gate 							  sm_errstring(errno));
9197c478bd9Sstevel@tonic-gate 				}
9207c478bd9Sstevel@tonic-gate 			}
9217c478bd9Sstevel@tonic-gate 		}
9227c478bd9Sstevel@tonic-gate 		e->e_qfletter = new;
9237c478bd9Sstevel@tonic-gate 
9247c478bd9Sstevel@tonic-gate 		/*
9257c478bd9Sstevel@tonic-gate 		**  fsync() after renaming to make sure metadata is
9267c478bd9Sstevel@tonic-gate 		**  written to disk on filesystems in which renames are
9277c478bd9Sstevel@tonic-gate 		**  not guaranteed.
9287c478bd9Sstevel@tonic-gate 		*/
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 		if (SuperSafe != SAFE_NO)
9317c478bd9Sstevel@tonic-gate 		{
9327c478bd9Sstevel@tonic-gate 			/* for softupdates */
9337c478bd9Sstevel@tonic-gate 			if (tfd >= 0 && fsync(tfd) < 0)
9347c478bd9Sstevel@tonic-gate 			{
9357c478bd9Sstevel@tonic-gate 				syserr("!queueup: cannot fsync queue temp file %s",
9367c478bd9Sstevel@tonic-gate 				       tf);
9377c478bd9Sstevel@tonic-gate 			}
9387c478bd9Sstevel@tonic-gate 			SYNC_DIR(qf, true);
9397c478bd9Sstevel@tonic-gate 		}
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 		/* close and unlock old (locked) queue file */
9427c478bd9Sstevel@tonic-gate 		if (e->e_lockfp != NULL)
9437c478bd9Sstevel@tonic-gate 			(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
9447c478bd9Sstevel@tonic-gate 		e->e_lockfp = tfp;
9457c478bd9Sstevel@tonic-gate 
9467c478bd9Sstevel@tonic-gate 		/* save log info */
9477c478bd9Sstevel@tonic-gate 		if (LogLevel > 79)
9487c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", qf);
9497c478bd9Sstevel@tonic-gate 	}
9507c478bd9Sstevel@tonic-gate 	else
9517c478bd9Sstevel@tonic-gate 	{
9527c478bd9Sstevel@tonic-gate 		/* save log info */
9537c478bd9Sstevel@tonic-gate 		if (LogLevel > 79)
9547c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "queueup %s", tf);
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 		e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
9577c478bd9Sstevel@tonic-gate 	}
9587c478bd9Sstevel@tonic-gate 
9597c478bd9Sstevel@tonic-gate 	errno = 0;
9607c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_INQUEUE;
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
9637c478bd9Sstevel@tonic-gate 		sm_dprintf("<<<<< done queueing %s <<<<<\n\n", e->e_id);
9647c478bd9Sstevel@tonic-gate 	return;
9657c478bd9Sstevel@tonic-gate }
9667c478bd9Sstevel@tonic-gate 
9677c478bd9Sstevel@tonic-gate /*
9687c478bd9Sstevel@tonic-gate **  PRINTCTLADDR -- print control address to file.
9697c478bd9Sstevel@tonic-gate **
9707c478bd9Sstevel@tonic-gate **	Parameters:
9717c478bd9Sstevel@tonic-gate **		a -- address.
9727c478bd9Sstevel@tonic-gate **		tfp -- file pointer.
9737c478bd9Sstevel@tonic-gate **
9747c478bd9Sstevel@tonic-gate **	Returns:
9757c478bd9Sstevel@tonic-gate **		none.
9767c478bd9Sstevel@tonic-gate **
9777c478bd9Sstevel@tonic-gate **	Side Effects:
9787c478bd9Sstevel@tonic-gate **		The control address (if changed) is printed to the file.
9797c478bd9Sstevel@tonic-gate **		The last control address and uid are saved.
9807c478bd9Sstevel@tonic-gate */
9817c478bd9Sstevel@tonic-gate 
9827c478bd9Sstevel@tonic-gate static void
9837c478bd9Sstevel@tonic-gate printctladdr(a, tfp)
9847c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
9857c478bd9Sstevel@tonic-gate 	SM_FILE_T *tfp;
9867c478bd9Sstevel@tonic-gate {
9877c478bd9Sstevel@tonic-gate 	char *user;
9887c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
9897c478bd9Sstevel@tonic-gate 	uid_t uid;
9907c478bd9Sstevel@tonic-gate 	gid_t gid;
9917c478bd9Sstevel@tonic-gate 	static ADDRESS *lastctladdr = NULL;
9927c478bd9Sstevel@tonic-gate 	static uid_t lastuid;
9937c478bd9Sstevel@tonic-gate 
9947c478bd9Sstevel@tonic-gate 	/* initialization */
9957c478bd9Sstevel@tonic-gate 	if (a == NULL || a->q_alias == NULL || tfp == NULL)
9967c478bd9Sstevel@tonic-gate 	{
9977c478bd9Sstevel@tonic-gate 		if (lastctladdr != NULL && tfp != NULL)
9987c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C\n");
9997c478bd9Sstevel@tonic-gate 		lastctladdr = NULL;
10007c478bd9Sstevel@tonic-gate 		lastuid = 0;
10017c478bd9Sstevel@tonic-gate 		return;
10027c478bd9Sstevel@tonic-gate 	}
10037c478bd9Sstevel@tonic-gate 
10047c478bd9Sstevel@tonic-gate 	/* find the active uid */
10057c478bd9Sstevel@tonic-gate 	q = getctladdr(a);
10067c478bd9Sstevel@tonic-gate 	if (q == NULL)
10077c478bd9Sstevel@tonic-gate 	{
10087c478bd9Sstevel@tonic-gate 		user = NULL;
10097c478bd9Sstevel@tonic-gate 		uid = 0;
10107c478bd9Sstevel@tonic-gate 		gid = 0;
10117c478bd9Sstevel@tonic-gate 	}
10127c478bd9Sstevel@tonic-gate 	else
10137c478bd9Sstevel@tonic-gate 	{
10147c478bd9Sstevel@tonic-gate 		user = q->q_ruser != NULL ? q->q_ruser : q->q_user;
10157c478bd9Sstevel@tonic-gate 		uid = q->q_uid;
10167c478bd9Sstevel@tonic-gate 		gid = q->q_gid;
10177c478bd9Sstevel@tonic-gate 	}
10187c478bd9Sstevel@tonic-gate 	a = a->q_alias;
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 	/* check to see if this is the same as last time */
10217c478bd9Sstevel@tonic-gate 	if (lastctladdr != NULL && uid == lastuid &&
10227c478bd9Sstevel@tonic-gate 	    strcmp(lastctladdr->q_paddr, a->q_paddr) == 0)
10237c478bd9Sstevel@tonic-gate 		return;
10247c478bd9Sstevel@tonic-gate 	lastuid = uid;
10257c478bd9Sstevel@tonic-gate 	lastctladdr = a;
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate 	if (uid == 0 || user == NULL || user[0] == '\0')
10287c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C");
10297c478bd9Sstevel@tonic-gate 	else
10307c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "C%s:%ld:%ld",
10317c478bd9Sstevel@tonic-gate 				     denlstring(user, true, false), (long) uid,
10327c478bd9Sstevel@tonic-gate 				     (long) gid);
10337c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, ":%s\n",
10347c478bd9Sstevel@tonic-gate 			     denlstring(a->q_paddr, true, false));
10357c478bd9Sstevel@tonic-gate }
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate /*
10387c478bd9Sstevel@tonic-gate **  RUNNERS_SIGTERM -- propagate a SIGTERM to queue runner process
10397c478bd9Sstevel@tonic-gate **
10407c478bd9Sstevel@tonic-gate **	This propagates the signal to the child processes that are queue
10417c478bd9Sstevel@tonic-gate **	runners. This is for a queue runner "cleanup". After all of the
10427c478bd9Sstevel@tonic-gate **	child queue runner processes are signaled (it should be SIGTERM
10437c478bd9Sstevel@tonic-gate **	being the sig) then the old signal handler (Oldsh) is called
10447c478bd9Sstevel@tonic-gate **	to handle any cleanup set for this process (provided it is not
10457c478bd9Sstevel@tonic-gate **	SIG_DFL or SIG_IGN). The signal may not be handled immediately
10467c478bd9Sstevel@tonic-gate **	if the BlockOldsh flag is set. If the current process doesn't
10477c478bd9Sstevel@tonic-gate **	have a parent then handle the signal immediately, regardless of
10487c478bd9Sstevel@tonic-gate **	BlockOldsh.
10497c478bd9Sstevel@tonic-gate **
10507c478bd9Sstevel@tonic-gate **	Parameters:
10517c478bd9Sstevel@tonic-gate **		sig -- the signal number being sent
10527c478bd9Sstevel@tonic-gate **
10537c478bd9Sstevel@tonic-gate **	Returns:
10547c478bd9Sstevel@tonic-gate **		none.
10557c478bd9Sstevel@tonic-gate **
10567c478bd9Sstevel@tonic-gate **	Side Effects:
10577c478bd9Sstevel@tonic-gate **		Sets the NoMoreRunners boolean to true to stop more runners
10587c478bd9Sstevel@tonic-gate **		from being started in runqueue().
10597c478bd9Sstevel@tonic-gate **
10607c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
10617c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
10627c478bd9Sstevel@tonic-gate **		DOING.
10637c478bd9Sstevel@tonic-gate */
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate static bool		volatile NoMoreRunners = false;
10667c478bd9Sstevel@tonic-gate static sigfunc_t	Oldsh_term = SIG_DFL;
10677c478bd9Sstevel@tonic-gate static sigfunc_t	Oldsh_hup = SIG_DFL;
10687c478bd9Sstevel@tonic-gate static sigfunc_t	volatile Oldsh = SIG_DFL;
10697c478bd9Sstevel@tonic-gate static bool		BlockOldsh = false;
10707c478bd9Sstevel@tonic-gate static int		volatile Oldsig = 0;
10717c478bd9Sstevel@tonic-gate static SIGFUNC_DECL	runners_sigterm __P((int));
10727c478bd9Sstevel@tonic-gate static SIGFUNC_DECL	runners_sighup __P((int));
10737c478bd9Sstevel@tonic-gate 
10747c478bd9Sstevel@tonic-gate static SIGFUNC_DECL
10757c478bd9Sstevel@tonic-gate runners_sigterm(sig)
10767c478bd9Sstevel@tonic-gate 	int sig;
10777c478bd9Sstevel@tonic-gate {
10787c478bd9Sstevel@tonic-gate 	int save_errno = errno;
10797c478bd9Sstevel@tonic-gate 
10807c478bd9Sstevel@tonic-gate 	FIX_SYSV_SIGNAL(sig, runners_sigterm);
10817c478bd9Sstevel@tonic-gate 	errno = save_errno;
10827c478bd9Sstevel@tonic-gate 	CHECK_CRITICAL(sig);
10837c478bd9Sstevel@tonic-gate 	NoMoreRunners = true;
10847c478bd9Sstevel@tonic-gate 	Oldsh = Oldsh_term;
10857c478bd9Sstevel@tonic-gate 	Oldsig = sig;
10867c478bd9Sstevel@tonic-gate 	proc_list_signal(PROC_QUEUE, sig);
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate 	if (!BlockOldsh || getppid() <= 1)
10897c478bd9Sstevel@tonic-gate 	{
10907c478bd9Sstevel@tonic-gate 		/* Check that a valid 'old signal handler' is callable */
10917c478bd9Sstevel@tonic-gate 		if (Oldsh_term != SIG_DFL && Oldsh_term != SIG_IGN &&
10927c478bd9Sstevel@tonic-gate 		    Oldsh_term != runners_sigterm)
10937c478bd9Sstevel@tonic-gate 			(*Oldsh_term)(sig);
10947c478bd9Sstevel@tonic-gate 	}
10957c478bd9Sstevel@tonic-gate 	errno = save_errno;
10967c478bd9Sstevel@tonic-gate 	return SIGFUNC_RETURN;
10977c478bd9Sstevel@tonic-gate }
10987c478bd9Sstevel@tonic-gate /*
10997c478bd9Sstevel@tonic-gate **  RUNNERS_SIGHUP -- propagate a SIGHUP to queue runner process
11007c478bd9Sstevel@tonic-gate **
11017c478bd9Sstevel@tonic-gate **	This propagates the signal to the child processes that are queue
11027c478bd9Sstevel@tonic-gate **	runners. This is for a queue runner "cleanup". After all of the
11037c478bd9Sstevel@tonic-gate **	child queue runner processes are signaled (it should be SIGHUP
11047c478bd9Sstevel@tonic-gate **	being the sig) then the old signal handler (Oldsh) is called to
11057c478bd9Sstevel@tonic-gate **	handle any cleanup set for this process (provided it is not SIG_DFL
11067c478bd9Sstevel@tonic-gate **	or SIG_IGN). The signal may not be handled immediately if the
11077c478bd9Sstevel@tonic-gate **	BlockOldsh flag is set. If the current process doesn't have
11087c478bd9Sstevel@tonic-gate **	a parent then handle the signal immediately, regardless of
11097c478bd9Sstevel@tonic-gate **	BlockOldsh.
11107c478bd9Sstevel@tonic-gate **
11117c478bd9Sstevel@tonic-gate **	Parameters:
11127c478bd9Sstevel@tonic-gate **		sig -- the signal number being sent
11137c478bd9Sstevel@tonic-gate **
11147c478bd9Sstevel@tonic-gate **	Returns:
11157c478bd9Sstevel@tonic-gate **		none.
11167c478bd9Sstevel@tonic-gate **
11177c478bd9Sstevel@tonic-gate **	Side Effects:
11187c478bd9Sstevel@tonic-gate **		Sets the NoMoreRunners boolean to true to stop more runners
11197c478bd9Sstevel@tonic-gate **		from being started in runqueue().
11207c478bd9Sstevel@tonic-gate **
11217c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11227c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
11237c478bd9Sstevel@tonic-gate **		DOING.
11247c478bd9Sstevel@tonic-gate */
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate static SIGFUNC_DECL
11277c478bd9Sstevel@tonic-gate runners_sighup(sig)
11287c478bd9Sstevel@tonic-gate 	int sig;
11297c478bd9Sstevel@tonic-gate {
11307c478bd9Sstevel@tonic-gate 	int save_errno = errno;
11317c478bd9Sstevel@tonic-gate 
11327c478bd9Sstevel@tonic-gate 	FIX_SYSV_SIGNAL(sig, runners_sighup);
11337c478bd9Sstevel@tonic-gate 	errno = save_errno;
11347c478bd9Sstevel@tonic-gate 	CHECK_CRITICAL(sig);
11357c478bd9Sstevel@tonic-gate 	NoMoreRunners = true;
11367c478bd9Sstevel@tonic-gate 	Oldsh = Oldsh_hup;
11377c478bd9Sstevel@tonic-gate 	Oldsig = sig;
11387c478bd9Sstevel@tonic-gate 	proc_list_signal(PROC_QUEUE, sig);
11397c478bd9Sstevel@tonic-gate 
11407c478bd9Sstevel@tonic-gate 	if (!BlockOldsh || getppid() <= 1)
11417c478bd9Sstevel@tonic-gate 	{
11427c478bd9Sstevel@tonic-gate 		/* Check that a valid 'old signal handler' is callable */
11437c478bd9Sstevel@tonic-gate 		if (Oldsh_hup != SIG_DFL && Oldsh_hup != SIG_IGN &&
11447c478bd9Sstevel@tonic-gate 		    Oldsh_hup != runners_sighup)
11457c478bd9Sstevel@tonic-gate 			(*Oldsh_hup)(sig);
11467c478bd9Sstevel@tonic-gate 	}
11477c478bd9Sstevel@tonic-gate 	errno = save_errno;
11487c478bd9Sstevel@tonic-gate 	return SIGFUNC_RETURN;
11497c478bd9Sstevel@tonic-gate }
11507c478bd9Sstevel@tonic-gate /*
11517c478bd9Sstevel@tonic-gate **  MARK_WORK_GROUP_RESTART -- mark a work group as needing a restart
11527c478bd9Sstevel@tonic-gate **
11537c478bd9Sstevel@tonic-gate **  Sets a workgroup for restarting.
11547c478bd9Sstevel@tonic-gate **
11557c478bd9Sstevel@tonic-gate **	Parameters:
11567c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to restart.
11577c478bd9Sstevel@tonic-gate **		reason -- why (signal?), -1 to turn off restart
11587c478bd9Sstevel@tonic-gate **
11597c478bd9Sstevel@tonic-gate **	Returns:
11607c478bd9Sstevel@tonic-gate **		none.
11617c478bd9Sstevel@tonic-gate **
11627c478bd9Sstevel@tonic-gate **	Side effects:
11637c478bd9Sstevel@tonic-gate **		May set global RestartWorkGroup to true.
11647c478bd9Sstevel@tonic-gate **
11657c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
11667c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
11677c478bd9Sstevel@tonic-gate **		DOING.
11687c478bd9Sstevel@tonic-gate */
11697c478bd9Sstevel@tonic-gate 
11707c478bd9Sstevel@tonic-gate void
11717c478bd9Sstevel@tonic-gate mark_work_group_restart(wgrp, reason)
11727c478bd9Sstevel@tonic-gate 	int wgrp;
11737c478bd9Sstevel@tonic-gate 	int reason;
11747c478bd9Sstevel@tonic-gate {
11757c478bd9Sstevel@tonic-gate 	if (wgrp < 0 || wgrp > NumWorkGroups)
11767c478bd9Sstevel@tonic-gate 		return;
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 	WorkGrp[wgrp].wg_restart = reason;
11797c478bd9Sstevel@tonic-gate 	if (reason >= 0)
11807c478bd9Sstevel@tonic-gate 		RestartWorkGroup = true;
11817c478bd9Sstevel@tonic-gate }
11827c478bd9Sstevel@tonic-gate /*
11837c478bd9Sstevel@tonic-gate **  RESTART_MARKED_WORK_GROUPS -- restart work groups marked as needing restart
11847c478bd9Sstevel@tonic-gate **
11857c478bd9Sstevel@tonic-gate **  Restart any workgroup marked as needing a restart provided more
11867c478bd9Sstevel@tonic-gate **  runners are allowed.
11877c478bd9Sstevel@tonic-gate **
11887c478bd9Sstevel@tonic-gate **	Parameters:
11897c478bd9Sstevel@tonic-gate **		none.
11907c478bd9Sstevel@tonic-gate **
11917c478bd9Sstevel@tonic-gate **	Returns:
11927c478bd9Sstevel@tonic-gate **		none.
11937c478bd9Sstevel@tonic-gate **
11947c478bd9Sstevel@tonic-gate **	Side effects:
11957c478bd9Sstevel@tonic-gate **		Sets global RestartWorkGroup to false.
11967c478bd9Sstevel@tonic-gate */
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate void
11997c478bd9Sstevel@tonic-gate restart_marked_work_groups()
12007c478bd9Sstevel@tonic-gate {
12017c478bd9Sstevel@tonic-gate 	int i;
12027c478bd9Sstevel@tonic-gate 	int wasblocked;
12037c478bd9Sstevel@tonic-gate 
12047c478bd9Sstevel@tonic-gate 	if (NoMoreRunners)
12057c478bd9Sstevel@tonic-gate 		return;
12067c478bd9Sstevel@tonic-gate 
12077c478bd9Sstevel@tonic-gate 	/* Block SIGCHLD so reapchild() doesn't mess with us */
12087c478bd9Sstevel@tonic-gate 	wasblocked = sm_blocksignal(SIGCHLD);
12097c478bd9Sstevel@tonic-gate 
12107c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumWorkGroups; i++)
12117c478bd9Sstevel@tonic-gate 	{
12127c478bd9Sstevel@tonic-gate 		if (WorkGrp[i].wg_restart >= 0)
12137c478bd9Sstevel@tonic-gate 		{
12147c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
12157c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
12167c478bd9Sstevel@tonic-gate 					  "restart queue runner=%d due to signal 0x%x",
12177c478bd9Sstevel@tonic-gate 					  i, WorkGrp[i].wg_restart);
12187c478bd9Sstevel@tonic-gate 			restart_work_group(i);
12197c478bd9Sstevel@tonic-gate 		}
12207c478bd9Sstevel@tonic-gate 	}
12217c478bd9Sstevel@tonic-gate 	RestartWorkGroup = false;
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	if (wasblocked == 0)
12247c478bd9Sstevel@tonic-gate 		(void) sm_releasesignal(SIGCHLD);
12257c478bd9Sstevel@tonic-gate }
12267c478bd9Sstevel@tonic-gate /*
12277c478bd9Sstevel@tonic-gate **  RESTART_WORK_GROUP -- restart a specific work group
12287c478bd9Sstevel@tonic-gate **
12297c478bd9Sstevel@tonic-gate **  Restart a specific workgroup provided more runners are allowed.
12307c478bd9Sstevel@tonic-gate **  If the requested work group has been restarted too many times log
12317c478bd9Sstevel@tonic-gate **  this and refuse to restart.
12327c478bd9Sstevel@tonic-gate **
12337c478bd9Sstevel@tonic-gate **	Parameters:
12347c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to restart
12357c478bd9Sstevel@tonic-gate **
12367c478bd9Sstevel@tonic-gate **	Returns:
12377c478bd9Sstevel@tonic-gate **		none.
12387c478bd9Sstevel@tonic-gate **
12397c478bd9Sstevel@tonic-gate **	Side Effects:
12407c478bd9Sstevel@tonic-gate **		starts another process doing the work of wgrp
12417c478bd9Sstevel@tonic-gate */
12427c478bd9Sstevel@tonic-gate 
12437c478bd9Sstevel@tonic-gate #define MAX_PERSIST_RESTART	10	/* max allowed number of restarts */
12447c478bd9Sstevel@tonic-gate 
12457c478bd9Sstevel@tonic-gate static void
12467c478bd9Sstevel@tonic-gate restart_work_group(wgrp)
12477c478bd9Sstevel@tonic-gate 	int wgrp;
12487c478bd9Sstevel@tonic-gate {
12497c478bd9Sstevel@tonic-gate 	if (NoMoreRunners ||
12507c478bd9Sstevel@tonic-gate 	    wgrp < 0 || wgrp > NumWorkGroups)
12517c478bd9Sstevel@tonic-gate 		return;
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	WorkGrp[wgrp].wg_restart = -1;
12547c478bd9Sstevel@tonic-gate 	if (WorkGrp[wgrp].wg_restartcnt < MAX_PERSIST_RESTART)
12557c478bd9Sstevel@tonic-gate 	{
12567c478bd9Sstevel@tonic-gate 		/* avoid overflow; increment here */
12577c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_restartcnt++;
12587c478bd9Sstevel@tonic-gate 		(void) run_work_group(wgrp, RWG_FORK|RWG_PERSISTENT|RWG_RUNALL);
12597c478bd9Sstevel@tonic-gate 	}
12607c478bd9Sstevel@tonic-gate 	else
12617c478bd9Sstevel@tonic-gate 	{
12627c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID,
12637c478bd9Sstevel@tonic-gate 			  "ERROR: persistent queue runner=%d restarted too many times, queue runner lost",
12647c478bd9Sstevel@tonic-gate 			  wgrp);
12657c478bd9Sstevel@tonic-gate 	}
12667c478bd9Sstevel@tonic-gate }
12677c478bd9Sstevel@tonic-gate /*
12687c478bd9Sstevel@tonic-gate **  SCHEDULE_QUEUE_RUNS -- schedule the next queue run for a work group.
12697c478bd9Sstevel@tonic-gate **
12707c478bd9Sstevel@tonic-gate **	Parameters:
12717c478bd9Sstevel@tonic-gate **		runall -- schedule even if individual bit is not set.
12727c478bd9Sstevel@tonic-gate **		wgrp -- the work group id to schedule.
12737c478bd9Sstevel@tonic-gate **		didit -- the queue run was performed for this work group.
12747c478bd9Sstevel@tonic-gate **
12757c478bd9Sstevel@tonic-gate **	Returns:
12767c478bd9Sstevel@tonic-gate **		nothing
12777c478bd9Sstevel@tonic-gate */
12787c478bd9Sstevel@tonic-gate 
12797c478bd9Sstevel@tonic-gate #define INCR_MOD(v, m)	if (++v >= m)	\
12807c478bd9Sstevel@tonic-gate 				v = 0;	\
12817c478bd9Sstevel@tonic-gate 			else
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate static void
12847c478bd9Sstevel@tonic-gate schedule_queue_runs(runall, wgrp, didit)
12857c478bd9Sstevel@tonic-gate 	bool runall;
12867c478bd9Sstevel@tonic-gate 	int wgrp;
12877c478bd9Sstevel@tonic-gate 	bool didit;
12887c478bd9Sstevel@tonic-gate {
12897c478bd9Sstevel@tonic-gate 	int qgrp, cgrp, endgrp;
12907c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
12917c478bd9Sstevel@tonic-gate 	time_t lastsched;
12927c478bd9Sstevel@tonic-gate 	bool sched;
12937c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
12947c478bd9Sstevel@tonic-gate 	time_t now;
12957c478bd9Sstevel@tonic-gate 	time_t minqintvl;
12967c478bd9Sstevel@tonic-gate 
12977c478bd9Sstevel@tonic-gate 	/*
12987c478bd9Sstevel@tonic-gate 	**  This is a bit ugly since we have to duplicate the
12997c478bd9Sstevel@tonic-gate 	**  code that "walks" through a work queue group.
13007c478bd9Sstevel@tonic-gate 	*/
13017c478bd9Sstevel@tonic-gate 
13027c478bd9Sstevel@tonic-gate 	now = curtime();
13037c478bd9Sstevel@tonic-gate 	minqintvl = 0;
13047c478bd9Sstevel@tonic-gate 	cgrp = endgrp = WorkGrp[wgrp].wg_curqgrp;
13057c478bd9Sstevel@tonic-gate 	do
13067c478bd9Sstevel@tonic-gate 	{
13077c478bd9Sstevel@tonic-gate 		time_t qintvl;
13087c478bd9Sstevel@tonic-gate 
13097c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13107c478bd9Sstevel@tonic-gate 		lastsched = 0;
13117c478bd9Sstevel@tonic-gate 		sched = false;
13127c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13137c478bd9Sstevel@tonic-gate 		qgrp = WorkGrp[wgrp].wg_qgs[cgrp]->qg_index;
13147c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_queueintvl > 0)
13157c478bd9Sstevel@tonic-gate 			qintvl = Queue[qgrp]->qg_queueintvl;
13167c478bd9Sstevel@tonic-gate 		else if (QueueIntvl > 0)
13177c478bd9Sstevel@tonic-gate 			qintvl = QueueIntvl;
13187c478bd9Sstevel@tonic-gate 		else
13197c478bd9Sstevel@tonic-gate 			qintvl = (time_t) 0;
13207c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13217c478bd9Sstevel@tonic-gate 		lastsched = Queue[qgrp]->qg_nextrun;
13227c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13237c478bd9Sstevel@tonic-gate 		if ((runall || Queue[qgrp]->qg_nextrun <= now) && qintvl > 0)
13247c478bd9Sstevel@tonic-gate 		{
13257c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13267c478bd9Sstevel@tonic-gate 			sched = true;
13277c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13287c478bd9Sstevel@tonic-gate 			if (minqintvl == 0 || qintvl < minqintvl)
13297c478bd9Sstevel@tonic-gate 				minqintvl = qintvl;
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 			/*
13327c478bd9Sstevel@tonic-gate 			**  Only set a new time if a queue run was performed
13337c478bd9Sstevel@tonic-gate 			**  for this queue group.  If the queue was not run,
13347c478bd9Sstevel@tonic-gate 			**  we could starve it by setting a new time on each
13357c478bd9Sstevel@tonic-gate 			**  call.
13367c478bd9Sstevel@tonic-gate 			*/
13377c478bd9Sstevel@tonic-gate 
13387c478bd9Sstevel@tonic-gate 			if (didit)
13397c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_nextrun += qintvl;
13407c478bd9Sstevel@tonic-gate 		}
13417c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
13427c478bd9Sstevel@tonic-gate 		if (tTd(69, 10))
13437c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
13447c478bd9Sstevel@tonic-gate 				"sqr: wgrp=%d, cgrp=%d, qgrp=%d, intvl=%ld, QI=%ld, runall=%d, lastrun=%ld, nextrun=%ld, sched=%d",
13457c478bd9Sstevel@tonic-gate 				wgrp, cgrp, qgrp, Queue[qgrp]->qg_queueintvl,
13467c478bd9Sstevel@tonic-gate 				QueueIntvl, runall, lastsched,
13477c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_nextrun, sched);
13487c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
13497c478bd9Sstevel@tonic-gate 		INCR_MOD(cgrp, WorkGrp[wgrp].wg_numqgrp);
13507c478bd9Sstevel@tonic-gate 	} while (endgrp != cgrp);
13517c478bd9Sstevel@tonic-gate 	if (minqintvl > 0)
13527c478bd9Sstevel@tonic-gate 		(void) sm_setevent(minqintvl, runqueueevent, 0);
13537c478bd9Sstevel@tonic-gate }
13547c478bd9Sstevel@tonic-gate 
13557c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_RUN_PARANOIA
13567c478bd9Sstevel@tonic-gate /*
13577c478bd9Sstevel@tonic-gate **  CHECKQUEUERUNNER -- check whether a queue group hasn't been run.
13587c478bd9Sstevel@tonic-gate **
13597c478bd9Sstevel@tonic-gate **	Use this if events may get lost and hence queue runners may not
13607c478bd9Sstevel@tonic-gate **	be started and mail will pile up in a queue.
13617c478bd9Sstevel@tonic-gate **
13627c478bd9Sstevel@tonic-gate **	Parameters:
13637c478bd9Sstevel@tonic-gate **		none.
13647c478bd9Sstevel@tonic-gate **
13657c478bd9Sstevel@tonic-gate **	Returns:
13667c478bd9Sstevel@tonic-gate **		true if a queue run is necessary.
13677c478bd9Sstevel@tonic-gate **
13687c478bd9Sstevel@tonic-gate **	Side Effects:
13697c478bd9Sstevel@tonic-gate **		may schedule a queue run.
13707c478bd9Sstevel@tonic-gate */
13717c478bd9Sstevel@tonic-gate 
13727c478bd9Sstevel@tonic-gate bool
13737c478bd9Sstevel@tonic-gate checkqueuerunner()
13747c478bd9Sstevel@tonic-gate {
13757c478bd9Sstevel@tonic-gate 	int qgrp;
13767c478bd9Sstevel@tonic-gate 	time_t now, minqintvl;
13777c478bd9Sstevel@tonic-gate 
13787c478bd9Sstevel@tonic-gate 	now = curtime();
13797c478bd9Sstevel@tonic-gate 	minqintvl = 0;
13807c478bd9Sstevel@tonic-gate 	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
13817c478bd9Sstevel@tonic-gate 	{
13827c478bd9Sstevel@tonic-gate 		time_t qintvl;
13837c478bd9Sstevel@tonic-gate 
13847c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_queueintvl > 0)
13857c478bd9Sstevel@tonic-gate 			qintvl = Queue[qgrp]->qg_queueintvl;
13867c478bd9Sstevel@tonic-gate 		else if (QueueIntvl > 0)
13877c478bd9Sstevel@tonic-gate 			qintvl = QueueIntvl;
13887c478bd9Sstevel@tonic-gate 		else
13897c478bd9Sstevel@tonic-gate 			qintvl = (time_t) 0;
13907c478bd9Sstevel@tonic-gate 		if (Queue[qgrp]->qg_nextrun <= now - qintvl)
13917c478bd9Sstevel@tonic-gate 		{
13927c478bd9Sstevel@tonic-gate 			if (minqintvl == 0 || qintvl < minqintvl)
13937c478bd9Sstevel@tonic-gate 				minqintvl = qintvl;
13947c478bd9Sstevel@tonic-gate 			if (LogLevel > 1)
13957c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, NOQID,
13967c478bd9Sstevel@tonic-gate 					"checkqueuerunner: queue %d should have been run at %s, queue interval %ld",
13977c478bd9Sstevel@tonic-gate 					qgrp,
13987c478bd9Sstevel@tonic-gate 					arpadate(ctime(&Queue[qgrp]->qg_nextrun)),
13997c478bd9Sstevel@tonic-gate 					qintvl);
14007c478bd9Sstevel@tonic-gate 		}
14017c478bd9Sstevel@tonic-gate 	}
14027c478bd9Sstevel@tonic-gate 	if (minqintvl > 0)
14037c478bd9Sstevel@tonic-gate 	{
14047c478bd9Sstevel@tonic-gate 		(void) sm_setevent(minqintvl, runqueueevent, 0);
14057c478bd9Sstevel@tonic-gate 		return true;
14067c478bd9Sstevel@tonic-gate 	}
14077c478bd9Sstevel@tonic-gate 	return false;
14087c478bd9Sstevel@tonic-gate }
14097c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_RUN_PARANOIA */
14107c478bd9Sstevel@tonic-gate 
14117c478bd9Sstevel@tonic-gate /*
14127c478bd9Sstevel@tonic-gate **  RUNQUEUE -- run the jobs in the queue.
14137c478bd9Sstevel@tonic-gate **
14147c478bd9Sstevel@tonic-gate **	Gets the stuff out of the queue in some presumably logical
14157c478bd9Sstevel@tonic-gate **	order and processes them.
14167c478bd9Sstevel@tonic-gate **
14177c478bd9Sstevel@tonic-gate **	Parameters:
14187c478bd9Sstevel@tonic-gate **		forkflag -- true if the queue scanning should be done in
14197c478bd9Sstevel@tonic-gate **			a child process.  We double-fork so it is not our
14207c478bd9Sstevel@tonic-gate **			child and we don't have to clean up after it.
14217c478bd9Sstevel@tonic-gate **			false can be ignored if we have multiple queues.
14227c478bd9Sstevel@tonic-gate **		verbose -- if true, print out status information.
14237c478bd9Sstevel@tonic-gate **		persistent -- persistent queue runner?
14247c478bd9Sstevel@tonic-gate **		runall -- run all groups or only a subset (DoQueueRun)?
14257c478bd9Sstevel@tonic-gate **
14267c478bd9Sstevel@tonic-gate **	Returns:
14277c478bd9Sstevel@tonic-gate **		true if the queue run successfully began.
14287c478bd9Sstevel@tonic-gate **
14297c478bd9Sstevel@tonic-gate **	Side Effects:
14307c478bd9Sstevel@tonic-gate **		runs things in the mail queue using run_work_group().
14317c478bd9Sstevel@tonic-gate **		maybe schedules next queue run.
14327c478bd9Sstevel@tonic-gate */
14337c478bd9Sstevel@tonic-gate 
14347c478bd9Sstevel@tonic-gate static ENVELOPE	QueueEnvelope;		/* the queue run envelope */
14357c478bd9Sstevel@tonic-gate static time_t	LastQueueTime = 0;	/* last time a queue ID assigned */
14367c478bd9Sstevel@tonic-gate static pid_t	LastQueuePid = -1;	/* last PID which had a queue ID */
14377c478bd9Sstevel@tonic-gate 
14387c478bd9Sstevel@tonic-gate /* values for qp_supdirs */
14397c478bd9Sstevel@tonic-gate #define QP_NOSUB	0x0000	/* No subdirectories */
14407c478bd9Sstevel@tonic-gate #define QP_SUBDF	0x0001	/* "df" subdirectory */
14417c478bd9Sstevel@tonic-gate #define QP_SUBQF	0x0002	/* "qf" subdirectory */
14427c478bd9Sstevel@tonic-gate #define QP_SUBXF	0x0004	/* "xf" subdirectory */
14437c478bd9Sstevel@tonic-gate 
14447c478bd9Sstevel@tonic-gate bool
14457c478bd9Sstevel@tonic-gate runqueue(forkflag, verbose, persistent, runall)
14467c478bd9Sstevel@tonic-gate 	bool forkflag;
14477c478bd9Sstevel@tonic-gate 	bool verbose;
14487c478bd9Sstevel@tonic-gate 	bool persistent;
14497c478bd9Sstevel@tonic-gate 	bool runall;
14507c478bd9Sstevel@tonic-gate {
14517c478bd9Sstevel@tonic-gate 	int i;
14527c478bd9Sstevel@tonic-gate 	bool ret = true;
14537c478bd9Sstevel@tonic-gate 	static int curnum = 0;
14547c478bd9Sstevel@tonic-gate 	sigfunc_t cursh;
14557c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
14567c478bd9Sstevel@tonic-gate 	SM_NONVOLATILE int oldgroup = 0;
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	if (sm_debug_active(&DebugLeakQ, 1))
14597c478bd9Sstevel@tonic-gate 	{
14607c478bd9Sstevel@tonic-gate 		oldgroup = sm_heap_group();
14617c478bd9Sstevel@tonic-gate 		sm_heap_newgroup();
14627c478bd9Sstevel@tonic-gate 		sm_dprintf("runqueue() heap group #%d\n", sm_heap_group());
14637c478bd9Sstevel@tonic-gate 	}
14647c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate 	/* queue run has been started, don't do any more this time */
14677c478bd9Sstevel@tonic-gate 	DoQueueRun = false;
14687c478bd9Sstevel@tonic-gate 
14697c478bd9Sstevel@tonic-gate 	/* more than one queue or more than one directory per queue */
14707c478bd9Sstevel@tonic-gate 	if (!forkflag && !verbose &&
14717c478bd9Sstevel@tonic-gate 	    (WorkGrp[0].wg_qgs[0]->qg_numqueues > 1 || NumWorkGroups > 1 ||
14727c478bd9Sstevel@tonic-gate 	     WorkGrp[0].wg_numqgrp > 1))
14737c478bd9Sstevel@tonic-gate 		forkflag = true;
14747c478bd9Sstevel@tonic-gate 
14757c478bd9Sstevel@tonic-gate 	/*
14767c478bd9Sstevel@tonic-gate 	**  For controlling queue runners via signals sent to this process.
14777c478bd9Sstevel@tonic-gate 	**  Oldsh* will get called too by runners_sig* (if it is not SIG_IGN
14787c478bd9Sstevel@tonic-gate 	**  or SIG_DFL) to preserve cleanup behavior. Now that this process
14797c478bd9Sstevel@tonic-gate 	**  will have children (and perhaps grandchildren) this handler will
14807c478bd9Sstevel@tonic-gate 	**  be left in place. This is because this process, once it has
14817c478bd9Sstevel@tonic-gate 	**  finished spinning off queue runners, may go back to doing something
14827c478bd9Sstevel@tonic-gate 	**  else (like being a daemon). And we still want on a SIG{TERM,HUP} to
14837c478bd9Sstevel@tonic-gate 	**  clean up the child queue runners. Only install 'runners_sig*' once
14847c478bd9Sstevel@tonic-gate 	**  else we'll get stuck looping forever.
14857c478bd9Sstevel@tonic-gate 	*/
14867c478bd9Sstevel@tonic-gate 
14877c478bd9Sstevel@tonic-gate 	cursh = sm_signal(SIGTERM, runners_sigterm);
14887c478bd9Sstevel@tonic-gate 	if (cursh != runners_sigterm)
14897c478bd9Sstevel@tonic-gate 		Oldsh_term = cursh;
14907c478bd9Sstevel@tonic-gate 	cursh = sm_signal(SIGHUP, runners_sighup);
14917c478bd9Sstevel@tonic-gate 	if (cursh != runners_sighup)
14927c478bd9Sstevel@tonic-gate 		Oldsh_hup = cursh;
14937c478bd9Sstevel@tonic-gate 
14947c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumWorkGroups && !NoMoreRunners; i++)
14957c478bd9Sstevel@tonic-gate 	{
14967c478bd9Sstevel@tonic-gate 		int rwgflags = RWG_NONE;
14977c478bd9Sstevel@tonic-gate 
14987c478bd9Sstevel@tonic-gate 		/*
14997c478bd9Sstevel@tonic-gate 		**  If MaxQueueChildren active then test whether the start
15007c478bd9Sstevel@tonic-gate 		**  of the next queue group's additional queue runners (maximum)
15017c478bd9Sstevel@tonic-gate 		**  will result in MaxQueueChildren being exceeded.
15027c478bd9Sstevel@tonic-gate 		**
15037c478bd9Sstevel@tonic-gate 		**  Note: do not use continue; even though another workgroup
15047c478bd9Sstevel@tonic-gate 		**	may have fewer queue runners, this would be "unfair",
15057c478bd9Sstevel@tonic-gate 		**	i.e., this work group might "starve" then.
15067c478bd9Sstevel@tonic-gate 		*/
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
15097c478bd9Sstevel@tonic-gate 		if (tTd(69, 10))
15107c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
15117c478bd9Sstevel@tonic-gate 				"rq: curnum=%d, MaxQueueChildren=%d, CurRunners=%d, WorkGrp[curnum].wg_maxact=%d",
15127c478bd9Sstevel@tonic-gate 				curnum, MaxQueueChildren, CurRunners,
15137c478bd9Sstevel@tonic-gate 				WorkGrp[curnum].wg_maxact);
15147c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
15157c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
15167c478bd9Sstevel@tonic-gate 		    CurRunners + WorkGrp[curnum].wg_maxact > MaxQueueChildren)
15177c478bd9Sstevel@tonic-gate 			break;
15187c478bd9Sstevel@tonic-gate 
15197c478bd9Sstevel@tonic-gate 		/*
15207c478bd9Sstevel@tonic-gate 		**  Pick up where we left off (curnum), in case we
15217c478bd9Sstevel@tonic-gate 		**  used up all the children last time without finishing.
15227c478bd9Sstevel@tonic-gate 		**  This give a round-robin fairness to queue runs.
15237c478bd9Sstevel@tonic-gate 		**
15247c478bd9Sstevel@tonic-gate 		**  Increment CurRunners before calling run_work_group()
15257c478bd9Sstevel@tonic-gate 		**  to avoid a "race condition" with proc_list_drop() which
15267c478bd9Sstevel@tonic-gate 		**  decrements CurRunners if the queue runners terminate.
15277c478bd9Sstevel@tonic-gate 		**  Notice: CurRunners is an upper limit, in some cases
15287c478bd9Sstevel@tonic-gate 		**  (too few jobs in the queue) this value is larger than
15297c478bd9Sstevel@tonic-gate 		**  the actual number of queue runners. The discrepancy can
15307c478bd9Sstevel@tonic-gate 		**  increase if some queue runners "hang" for a long time.
15317c478bd9Sstevel@tonic-gate 		*/
15327c478bd9Sstevel@tonic-gate 
15337c478bd9Sstevel@tonic-gate 		CurRunners += WorkGrp[curnum].wg_maxact;
15347c478bd9Sstevel@tonic-gate 		if (forkflag)
15357c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_FORK;
15367c478bd9Sstevel@tonic-gate 		if (verbose)
15377c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_VERBOSE;
15387c478bd9Sstevel@tonic-gate 		if (persistent)
15397c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_PERSISTENT;
15407c478bd9Sstevel@tonic-gate 		if (runall)
15417c478bd9Sstevel@tonic-gate 			rwgflags |= RWG_RUNALL;
15427c478bd9Sstevel@tonic-gate 		ret = run_work_group(curnum, rwgflags);
15437c478bd9Sstevel@tonic-gate 
15447c478bd9Sstevel@tonic-gate 		/*
15457c478bd9Sstevel@tonic-gate 		**  Failure means a message was printed for ETRN
15467c478bd9Sstevel@tonic-gate 		**  and subsequent queues are likely to fail as well.
15477c478bd9Sstevel@tonic-gate 		**  Decrement CurRunners in that case because
15487c478bd9Sstevel@tonic-gate 		**  none have been started.
15497c478bd9Sstevel@tonic-gate 		*/
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate 		if (!ret)
15527c478bd9Sstevel@tonic-gate 		{
15537c478bd9Sstevel@tonic-gate 			CurRunners -= WorkGrp[curnum].wg_maxact;
15547c478bd9Sstevel@tonic-gate 			break;
15557c478bd9Sstevel@tonic-gate 		}
15567c478bd9Sstevel@tonic-gate 
15577c478bd9Sstevel@tonic-gate 		if (!persistent)
15587c478bd9Sstevel@tonic-gate 			schedule_queue_runs(runall, curnum, true);
15597c478bd9Sstevel@tonic-gate 		INCR_MOD(curnum, NumWorkGroups);
15607c478bd9Sstevel@tonic-gate 	}
15617c478bd9Sstevel@tonic-gate 
15627c478bd9Sstevel@tonic-gate 	/* schedule left over queue runs */
15637c478bd9Sstevel@tonic-gate 	if (i < NumWorkGroups && !NoMoreRunners && !persistent)
15647c478bd9Sstevel@tonic-gate 	{
15657c478bd9Sstevel@tonic-gate 		int h;
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate 		for (h = curnum; i < NumWorkGroups; i++)
15687c478bd9Sstevel@tonic-gate 		{
15697c478bd9Sstevel@tonic-gate 			schedule_queue_runs(runall, h, false);
15707c478bd9Sstevel@tonic-gate 			INCR_MOD(h, NumWorkGroups);
15717c478bd9Sstevel@tonic-gate 		}
15727c478bd9Sstevel@tonic-gate 	}
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 
15757c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
15767c478bd9Sstevel@tonic-gate 	if (sm_debug_active(&DebugLeakQ, 1))
15777c478bd9Sstevel@tonic-gate 		sm_heap_setgroup(oldgroup);
15787c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
15797c478bd9Sstevel@tonic-gate 	return ret;
15807c478bd9Sstevel@tonic-gate }
15817c478bd9Sstevel@tonic-gate 
15827c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
15837c478bd9Sstevel@tonic-gate /*
15847c478bd9Sstevel@tonic-gate **  SKIP_DOMAINS -- Skip 'skip' number of domains in the WorkQ.
15857c478bd9Sstevel@tonic-gate **
15867c478bd9Sstevel@tonic-gate **  Added by Stephen Frost <sfrost@snowman.net> to support
15877c478bd9Sstevel@tonic-gate **  having each runner process every N'th domain instead of
15887c478bd9Sstevel@tonic-gate **  every N'th message.
15897c478bd9Sstevel@tonic-gate **
15907c478bd9Sstevel@tonic-gate **	Parameters:
15917c478bd9Sstevel@tonic-gate **		skip -- number of domains in WorkQ to skip.
15927c478bd9Sstevel@tonic-gate **
15937c478bd9Sstevel@tonic-gate **	Returns:
15947c478bd9Sstevel@tonic-gate **		total number of messages skipped.
15957c478bd9Sstevel@tonic-gate **
15967c478bd9Sstevel@tonic-gate **	Side Effects:
15977c478bd9Sstevel@tonic-gate **		may change WorkQ
15987c478bd9Sstevel@tonic-gate */
15997c478bd9Sstevel@tonic-gate 
16007c478bd9Sstevel@tonic-gate static int
16017c478bd9Sstevel@tonic-gate skip_domains(skip)
16027c478bd9Sstevel@tonic-gate 	int skip;
16037c478bd9Sstevel@tonic-gate {
16047c478bd9Sstevel@tonic-gate 	int n, seqjump;
16057c478bd9Sstevel@tonic-gate 
16067c478bd9Sstevel@tonic-gate 	for (n = 0, seqjump = 0; n < skip && WorkQ != NULL; seqjump++)
16077c478bd9Sstevel@tonic-gate 	{
16087c478bd9Sstevel@tonic-gate 		if (WorkQ->w_next != NULL)
16097c478bd9Sstevel@tonic-gate 		{
16107c478bd9Sstevel@tonic-gate 			if (WorkQ->w_host != NULL &&
16117c478bd9Sstevel@tonic-gate 			    WorkQ->w_next->w_host != NULL)
16127c478bd9Sstevel@tonic-gate 			{
16137c478bd9Sstevel@tonic-gate 				if (sm_strcasecmp(WorkQ->w_host,
16147c478bd9Sstevel@tonic-gate 						WorkQ->w_next->w_host) != 0)
16157c478bd9Sstevel@tonic-gate 					n++;
16167c478bd9Sstevel@tonic-gate 			}
16177c478bd9Sstevel@tonic-gate 			else
16187c478bd9Sstevel@tonic-gate 			{
16197c478bd9Sstevel@tonic-gate 				if ((WorkQ->w_host != NULL &&
16207c478bd9Sstevel@tonic-gate 				     WorkQ->w_next->w_host == NULL) ||
16217c478bd9Sstevel@tonic-gate 				    (WorkQ->w_host == NULL &&
16227c478bd9Sstevel@tonic-gate 				     WorkQ->w_next->w_host != NULL))
16237c478bd9Sstevel@tonic-gate 					     n++;
16247c478bd9Sstevel@tonic-gate 			}
16257c478bd9Sstevel@tonic-gate 		}
16267c478bd9Sstevel@tonic-gate 		WorkQ = WorkQ->w_next;
16277c478bd9Sstevel@tonic-gate 	}
16287c478bd9Sstevel@tonic-gate 	return seqjump;
16297c478bd9Sstevel@tonic-gate }
16307c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
16317c478bd9Sstevel@tonic-gate 
16327c478bd9Sstevel@tonic-gate /*
16337c478bd9Sstevel@tonic-gate **  RUNNER_WORK -- have a queue runner do its work
16347c478bd9Sstevel@tonic-gate **
16357c478bd9Sstevel@tonic-gate **  Have a queue runner do its work a list of entries.
16367c478bd9Sstevel@tonic-gate **  When work isn't directly being done then this process can take a signal
16377c478bd9Sstevel@tonic-gate **  and terminate immediately (in a clean fashion of course).
16387c478bd9Sstevel@tonic-gate **  When work is directly being done, it's not to be interrupted
16397c478bd9Sstevel@tonic-gate **  immediately: the work should be allowed to finish at a clean point
16407c478bd9Sstevel@tonic-gate **  before termination (in a clean fashion of course).
16417c478bd9Sstevel@tonic-gate **
16427c478bd9Sstevel@tonic-gate **	Parameters:
16437c478bd9Sstevel@tonic-gate **		e -- envelope.
16447c478bd9Sstevel@tonic-gate **		sequenceno -- 'th process to run WorkQ.
16457c478bd9Sstevel@tonic-gate **		didfork -- did the calling process fork()?
16467c478bd9Sstevel@tonic-gate **		skip -- process only each skip'th item.
16477c478bd9Sstevel@tonic-gate **		njobs -- number of jobs in WorkQ.
16487c478bd9Sstevel@tonic-gate **
16497c478bd9Sstevel@tonic-gate **	Returns:
16507c478bd9Sstevel@tonic-gate **		none.
16517c478bd9Sstevel@tonic-gate **
16527c478bd9Sstevel@tonic-gate **	Side Effects:
16537c478bd9Sstevel@tonic-gate **		runs things in the mail queue.
16547c478bd9Sstevel@tonic-gate */
16557c478bd9Sstevel@tonic-gate 
16567c478bd9Sstevel@tonic-gate static void
16577c478bd9Sstevel@tonic-gate runner_work(e, sequenceno, didfork, skip, njobs)
16587c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
16597c478bd9Sstevel@tonic-gate 	int sequenceno;
16607c478bd9Sstevel@tonic-gate 	bool didfork;
16617c478bd9Sstevel@tonic-gate 	int skip;
16627c478bd9Sstevel@tonic-gate 	int njobs;
16637c478bd9Sstevel@tonic-gate {
16647c478bd9Sstevel@tonic-gate 	int n, seqjump;
16657c478bd9Sstevel@tonic-gate 	WORK *w;
16667c478bd9Sstevel@tonic-gate 	time_t now;
16677c478bd9Sstevel@tonic-gate 
16687c478bd9Sstevel@tonic-gate 	SM_GET_LA(now);
16697c478bd9Sstevel@tonic-gate 
16707c478bd9Sstevel@tonic-gate 	/*
16717c478bd9Sstevel@tonic-gate 	**  Here we temporarily block the second calling of the handlers.
16727c478bd9Sstevel@tonic-gate 	**  This allows us to handle the signal without terminating in the
16737c478bd9Sstevel@tonic-gate 	**  middle of direct work. If a signal does come, the test for
16747c478bd9Sstevel@tonic-gate 	**  NoMoreRunners will find it.
16757c478bd9Sstevel@tonic-gate 	*/
16767c478bd9Sstevel@tonic-gate 
16777c478bd9Sstevel@tonic-gate 	BlockOldsh = true;
16787c478bd9Sstevel@tonic-gate 	seqjump = skip;
16797c478bd9Sstevel@tonic-gate 
16807c478bd9Sstevel@tonic-gate 	/* process them once at a time */
16817c478bd9Sstevel@tonic-gate 	while (WorkQ != NULL)
16827c478bd9Sstevel@tonic-gate 	{
16837c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
16847c478bd9Sstevel@tonic-gate 		SM_NONVOLATILE int oldgroup = 0;
16857c478bd9Sstevel@tonic-gate 
16867c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&DebugLeakQ, 1))
16877c478bd9Sstevel@tonic-gate 		{
16887c478bd9Sstevel@tonic-gate 			oldgroup = sm_heap_group();
16897c478bd9Sstevel@tonic-gate 			sm_heap_newgroup();
16907c478bd9Sstevel@tonic-gate 			sm_dprintf("run_queue_group() heap group #%d\n",
16917c478bd9Sstevel@tonic-gate 				sm_heap_group());
16927c478bd9Sstevel@tonic-gate 		}
16937c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate 		/* do no more work */
16967c478bd9Sstevel@tonic-gate 		if (NoMoreRunners)
16977c478bd9Sstevel@tonic-gate 		{
16987c478bd9Sstevel@tonic-gate 			/* Check that a valid signal handler is callable */
16997c478bd9Sstevel@tonic-gate 			if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
17007c478bd9Sstevel@tonic-gate 			    Oldsh != runners_sighup &&
17017c478bd9Sstevel@tonic-gate 			    Oldsh != runners_sigterm)
17027c478bd9Sstevel@tonic-gate 				(*Oldsh)(Oldsig);
17037c478bd9Sstevel@tonic-gate 			break;
17047c478bd9Sstevel@tonic-gate 		}
17057c478bd9Sstevel@tonic-gate 
17067c478bd9Sstevel@tonic-gate 		w = WorkQ; /* assign current work item */
17077c478bd9Sstevel@tonic-gate 
17087c478bd9Sstevel@tonic-gate 		/*
17097c478bd9Sstevel@tonic-gate 		**  Set the head of the WorkQ to the next work item.
17107c478bd9Sstevel@tonic-gate 		**  It is set 'skip' ahead (the number of parallel queue
17117c478bd9Sstevel@tonic-gate 		**  runners working on WorkQ together) since each runner
17127c478bd9Sstevel@tonic-gate 		**  works on every 'skip'th (N-th) item.
17137c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
17147c478bd9Sstevel@tonic-gate 		**  In the case of the BYHOST Queue Sort Order, the 'item'
17157c478bd9Sstevel@tonic-gate 		**  is a domain, so we work on every 'skip'th (N-th) domain.
17167c478bd9Sstevel@tonic-gate #endif * _FFR_SKIP_DOMAINS *
17177c478bd9Sstevel@tonic-gate 		*/
17187c478bd9Sstevel@tonic-gate 
17197c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
17207c478bd9Sstevel@tonic-gate 		if (QueueSortOrder == QSO_BYHOST)
17217c478bd9Sstevel@tonic-gate 		{
17227c478bd9Sstevel@tonic-gate 			seqjump = 1;
17237c478bd9Sstevel@tonic-gate 			if (WorkQ->w_next != NULL)
17247c478bd9Sstevel@tonic-gate 			{
17257c478bd9Sstevel@tonic-gate 				if (WorkQ->w_host != NULL &&
17267c478bd9Sstevel@tonic-gate 				    WorkQ->w_next->w_host != NULL)
17277c478bd9Sstevel@tonic-gate 				{
17287c478bd9Sstevel@tonic-gate 					if (sm_strcasecmp(WorkQ->w_host,
17297c478bd9Sstevel@tonic-gate 							WorkQ->w_next->w_host)
17307c478bd9Sstevel@tonic-gate 								!= 0)
17317c478bd9Sstevel@tonic-gate 						seqjump = skip_domains(skip);
17327c478bd9Sstevel@tonic-gate 					else
17337c478bd9Sstevel@tonic-gate 						WorkQ = WorkQ->w_next;
17347c478bd9Sstevel@tonic-gate 				}
17357c478bd9Sstevel@tonic-gate 				else
17367c478bd9Sstevel@tonic-gate 				{
17377c478bd9Sstevel@tonic-gate 					if ((WorkQ->w_host != NULL &&
17387c478bd9Sstevel@tonic-gate 					     WorkQ->w_next->w_host == NULL) ||
17397c478bd9Sstevel@tonic-gate 					    (WorkQ->w_host == NULL &&
17407c478bd9Sstevel@tonic-gate 					     WorkQ->w_next->w_host != NULL))
17417c478bd9Sstevel@tonic-gate 						seqjump = skip_domains(skip);
17427c478bd9Sstevel@tonic-gate 					else
17437c478bd9Sstevel@tonic-gate 						WorkQ = WorkQ->w_next;
17447c478bd9Sstevel@tonic-gate 				}
17457c478bd9Sstevel@tonic-gate 			}
17467c478bd9Sstevel@tonic-gate 			else
17477c478bd9Sstevel@tonic-gate 				WorkQ = WorkQ->w_next;
17487c478bd9Sstevel@tonic-gate 		}
17497c478bd9Sstevel@tonic-gate 		else
17507c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
17517c478bd9Sstevel@tonic-gate 		{
17527c478bd9Sstevel@tonic-gate 			for (n = 0; n < skip && WorkQ != NULL; n++)
17537c478bd9Sstevel@tonic-gate 				WorkQ = WorkQ->w_next;
17547c478bd9Sstevel@tonic-gate 		}
17557c478bd9Sstevel@tonic-gate 
17567c478bd9Sstevel@tonic-gate 		e->e_to = NULL;
17577c478bd9Sstevel@tonic-gate 
17587c478bd9Sstevel@tonic-gate 		/*
17597c478bd9Sstevel@tonic-gate 		**  Ignore jobs that are too expensive for the moment.
17607c478bd9Sstevel@tonic-gate 		**
17617c478bd9Sstevel@tonic-gate 		**	Get new load average every GET_NEW_LA_TIME seconds.
17627c478bd9Sstevel@tonic-gate 		*/
17637c478bd9Sstevel@tonic-gate 
17647c478bd9Sstevel@tonic-gate 		SM_GET_LA(now);
17657c478bd9Sstevel@tonic-gate 		if (shouldqueue(WkRecipFact, Current_LA_time))
17667c478bd9Sstevel@tonic-gate 		{
17677c478bd9Sstevel@tonic-gate 			char *msg = "Aborting queue run: load average too high";
17687c478bd9Sstevel@tonic-gate 
17697c478bd9Sstevel@tonic-gate 			if (Verbose)
17707c478bd9Sstevel@tonic-gate 				message("%s", msg);
17717c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
17727c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
17737c478bd9Sstevel@tonic-gate 			break;
17747c478bd9Sstevel@tonic-gate 		}
17757c478bd9Sstevel@tonic-gate 		if (shouldqueue(w->w_pri, w->w_ctime))
17767c478bd9Sstevel@tonic-gate 		{
17777c478bd9Sstevel@tonic-gate 			if (Verbose)
17787c478bd9Sstevel@tonic-gate 				message(EmptyString);
17797c478bd9Sstevel@tonic-gate 			if (QueueSortOrder == QSO_BYPRIORITY)
17807c478bd9Sstevel@tonic-gate 			{
17817c478bd9Sstevel@tonic-gate 				if (Verbose)
17827c478bd9Sstevel@tonic-gate 					message("Skipping %s/%s (sequence %d of %d) and flushing rest of queue",
17837c478bd9Sstevel@tonic-gate 						qid_printqueue(w->w_qgrp,
17847c478bd9Sstevel@tonic-gate 							       w->w_qdir),
17857c478bd9Sstevel@tonic-gate 						w->w_name + 2, sequenceno,
17867c478bd9Sstevel@tonic-gate 						njobs);
17877c478bd9Sstevel@tonic-gate 				if (LogLevel > 8)
17887c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
17897c478bd9Sstevel@tonic-gate 						  "runqueue: Flushing queue from %s/%s (pri %ld, LA %d, %d of %d)",
17907c478bd9Sstevel@tonic-gate 						  qid_printqueue(w->w_qgrp,
17917c478bd9Sstevel@tonic-gate 								 w->w_qdir),
17927c478bd9Sstevel@tonic-gate 						  w->w_name + 2, w->w_pri,
17937c478bd9Sstevel@tonic-gate 						  CurrentLA, sequenceno,
17947c478bd9Sstevel@tonic-gate 						  njobs);
17957c478bd9Sstevel@tonic-gate 				break;
17967c478bd9Sstevel@tonic-gate 			}
17977c478bd9Sstevel@tonic-gate 			else if (Verbose)
17987c478bd9Sstevel@tonic-gate 				message("Skipping %s/%s (sequence %d of %d)",
17997c478bd9Sstevel@tonic-gate 					qid_printqueue(w->w_qgrp, w->w_qdir),
18007c478bd9Sstevel@tonic-gate 					w->w_name + 2, sequenceno, njobs);
18017c478bd9Sstevel@tonic-gate 		}
18027c478bd9Sstevel@tonic-gate 		else
18037c478bd9Sstevel@tonic-gate 		{
18047c478bd9Sstevel@tonic-gate 			if (Verbose)
18057c478bd9Sstevel@tonic-gate 			{
18067c478bd9Sstevel@tonic-gate 				message(EmptyString);
18077c478bd9Sstevel@tonic-gate 				message("Running %s/%s (sequence %d of %d)",
18087c478bd9Sstevel@tonic-gate 					qid_printqueue(w->w_qgrp, w->w_qdir),
18097c478bd9Sstevel@tonic-gate 					w->w_name + 2, sequenceno, njobs);
18107c478bd9Sstevel@tonic-gate 			}
18117c478bd9Sstevel@tonic-gate 			if (didfork && MaxQueueChildren > 0)
18127c478bd9Sstevel@tonic-gate 			{
18137c478bd9Sstevel@tonic-gate 				sm_blocksignal(SIGCHLD);
18147c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, reapchild);
18157c478bd9Sstevel@tonic-gate 			}
18167c478bd9Sstevel@tonic-gate 			if (tTd(63, 100))
18177c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_DEBUG, NOQID,
18187c478bd9Sstevel@tonic-gate 					  "runqueue %s dowork(%s)",
18197c478bd9Sstevel@tonic-gate 					  qid_printqueue(w->w_qgrp, w->w_qdir),
18207c478bd9Sstevel@tonic-gate 					  w->w_name + 2);
18217c478bd9Sstevel@tonic-gate 
18227c478bd9Sstevel@tonic-gate 			(void) dowork(w->w_qgrp, w->w_qdir, w->w_name + 2,
18237c478bd9Sstevel@tonic-gate 				      ForkQueueRuns, false, e);
18247c478bd9Sstevel@tonic-gate 			errno = 0;
18257c478bd9Sstevel@tonic-gate 		}
18267c478bd9Sstevel@tonic-gate 		sm_free(w->w_name); /* XXX */
18277c478bd9Sstevel@tonic-gate 		if (w->w_host != NULL)
18287c478bd9Sstevel@tonic-gate 			sm_free(w->w_host); /* XXX */
18297c478bd9Sstevel@tonic-gate 		sm_free((char *) w); /* XXX */
18307c478bd9Sstevel@tonic-gate 		sequenceno += seqjump; /* next sequence number */
18317c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
18327c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&DebugLeakQ, 1))
18337c478bd9Sstevel@tonic-gate 			sm_heap_setgroup(oldgroup);
18347c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
18357c478bd9Sstevel@tonic-gate 	}
18367c478bd9Sstevel@tonic-gate 
18377c478bd9Sstevel@tonic-gate 	BlockOldsh = false;
18387c478bd9Sstevel@tonic-gate 
18397c478bd9Sstevel@tonic-gate 	/* check the signals didn't happen during the revert */
18407c478bd9Sstevel@tonic-gate 	if (NoMoreRunners)
18417c478bd9Sstevel@tonic-gate 	{
18427c478bd9Sstevel@tonic-gate 		/* Check that a valid signal handler is callable */
18437c478bd9Sstevel@tonic-gate 		if (Oldsh != SIG_DFL && Oldsh != SIG_IGN &&
18447c478bd9Sstevel@tonic-gate 		    Oldsh != runners_sighup && Oldsh != runners_sigterm)
18457c478bd9Sstevel@tonic-gate 			(*Oldsh)(Oldsig);
18467c478bd9Sstevel@tonic-gate 	}
18477c478bd9Sstevel@tonic-gate 
18487c478bd9Sstevel@tonic-gate 	Oldsh = SIG_DFL; /* after the NoMoreRunners check */
18497c478bd9Sstevel@tonic-gate }
18507c478bd9Sstevel@tonic-gate /*
18517c478bd9Sstevel@tonic-gate **  RUN_WORK_GROUP -- run the jobs in a queue group from a work group.
18527c478bd9Sstevel@tonic-gate **
18537c478bd9Sstevel@tonic-gate **	Gets the stuff out of the queue in some presumably logical
18547c478bd9Sstevel@tonic-gate **	order and processes them.
18557c478bd9Sstevel@tonic-gate **
18567c478bd9Sstevel@tonic-gate **	Parameters:
18577c478bd9Sstevel@tonic-gate **		wgrp -- work group to process.
18587c478bd9Sstevel@tonic-gate **		flags -- RWG_* flags
18597c478bd9Sstevel@tonic-gate **
18607c478bd9Sstevel@tonic-gate **	Returns:
18617c478bd9Sstevel@tonic-gate **		true if the queue run successfully began.
18627c478bd9Sstevel@tonic-gate **
18637c478bd9Sstevel@tonic-gate **	Side Effects:
18647c478bd9Sstevel@tonic-gate **		runs things in the mail queue.
18657c478bd9Sstevel@tonic-gate */
18667c478bd9Sstevel@tonic-gate 
18677c478bd9Sstevel@tonic-gate /* Minimum sleep time for persistent queue runners */
18687c478bd9Sstevel@tonic-gate #define MIN_SLEEP_TIME	5
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate bool
18717c478bd9Sstevel@tonic-gate run_work_group(wgrp, flags)
18727c478bd9Sstevel@tonic-gate 	int wgrp;
18737c478bd9Sstevel@tonic-gate 	int flags;
18747c478bd9Sstevel@tonic-gate {
18757c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
18767c478bd9Sstevel@tonic-gate 	int njobs, qdir;
18777c478bd9Sstevel@tonic-gate 	int sequenceno = 1;
18787c478bd9Sstevel@tonic-gate 	int qgrp, endgrp, h, i;
18797c478bd9Sstevel@tonic-gate 	time_t now;
18807c478bd9Sstevel@tonic-gate 	bool full, more;
18817c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
18827c478bd9Sstevel@tonic-gate 	extern ENVELOPE BlankEnvelope;
18837c478bd9Sstevel@tonic-gate 	extern SIGFUNC_DECL reapchild __P((int));
18847c478bd9Sstevel@tonic-gate 
18857c478bd9Sstevel@tonic-gate 	if (wgrp < 0)
18867c478bd9Sstevel@tonic-gate 		return false;
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate 	/*
18897c478bd9Sstevel@tonic-gate 	**  If no work will ever be selected, don't even bother reading
18907c478bd9Sstevel@tonic-gate 	**  the queue.
18917c478bd9Sstevel@tonic-gate 	*/
18927c478bd9Sstevel@tonic-gate 
18937c478bd9Sstevel@tonic-gate 	SM_GET_LA(now);
18947c478bd9Sstevel@tonic-gate 
18957c478bd9Sstevel@tonic-gate 	if (!bitset(RWG_PERSISTENT, flags) &&
18967c478bd9Sstevel@tonic-gate 	    shouldqueue(WkRecipFact, Current_LA_time))
18977c478bd9Sstevel@tonic-gate 	{
18987c478bd9Sstevel@tonic-gate 		char *msg = "Skipping queue run -- load average too high";
18997c478bd9Sstevel@tonic-gate 
19007c478bd9Sstevel@tonic-gate 		if (bitset(RWG_VERBOSE, flags))
19017c478bd9Sstevel@tonic-gate 			message("458 %s\n", msg);
19027c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
19037c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "runqueue: %s", msg);
19047c478bd9Sstevel@tonic-gate 		return false;
19057c478bd9Sstevel@tonic-gate 	}
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	/*
19087c478bd9Sstevel@tonic-gate 	**  See if we already have too many children.
19097c478bd9Sstevel@tonic-gate 	*/
19107c478bd9Sstevel@tonic-gate 
19117c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags) &&
19127c478bd9Sstevel@tonic-gate 	    WorkGrp[wgrp].wg_lowqintvl > 0 &&
19137c478bd9Sstevel@tonic-gate 	    !bitset(RWG_PERSISTENT, flags) &&
19147c478bd9Sstevel@tonic-gate 	    MaxChildren > 0 && CurChildren >= MaxChildren)
19157c478bd9Sstevel@tonic-gate 	{
19167c478bd9Sstevel@tonic-gate 		char *msg = "Skipping queue run -- too many children";
19177c478bd9Sstevel@tonic-gate 
19187c478bd9Sstevel@tonic-gate 		if (bitset(RWG_VERBOSE, flags))
19197c478bd9Sstevel@tonic-gate 			message("458 %s (%d)\n", msg, CurChildren);
19207c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
19217c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "runqueue: %s (%d)",
19227c478bd9Sstevel@tonic-gate 				  msg, CurChildren);
19237c478bd9Sstevel@tonic-gate 		return false;
19247c478bd9Sstevel@tonic-gate 	}
19257c478bd9Sstevel@tonic-gate 
19267c478bd9Sstevel@tonic-gate 	/*
19277c478bd9Sstevel@tonic-gate 	**  See if we want to go off and do other useful work.
19287c478bd9Sstevel@tonic-gate 	*/
19297c478bd9Sstevel@tonic-gate 
19307c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
19317c478bd9Sstevel@tonic-gate 	{
19327c478bd9Sstevel@tonic-gate 		pid_t pid;
19337c478bd9Sstevel@tonic-gate 
19347c478bd9Sstevel@tonic-gate 		(void) sm_blocksignal(SIGCHLD);
19357c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGCHLD, reapchild);
19367c478bd9Sstevel@tonic-gate 
19377c478bd9Sstevel@tonic-gate 		pid = dofork();
19387c478bd9Sstevel@tonic-gate 		if (pid == -1)
19397c478bd9Sstevel@tonic-gate 		{
19407c478bd9Sstevel@tonic-gate 			const char *msg = "Skipping queue run -- fork() failed";
19417c478bd9Sstevel@tonic-gate 			const char *err = sm_errstring(errno);
19427c478bd9Sstevel@tonic-gate 
19437c478bd9Sstevel@tonic-gate 			if (bitset(RWG_VERBOSE, flags))
19447c478bd9Sstevel@tonic-gate 				message("458 %s: %s\n", msg, err);
19457c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
19467c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID, "runqueue: %s: %s",
19477c478bd9Sstevel@tonic-gate 					  msg, err);
19487c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGCHLD);
19497c478bd9Sstevel@tonic-gate 			return false;
19507c478bd9Sstevel@tonic-gate 		}
19517c478bd9Sstevel@tonic-gate 		if (pid != 0)
19527c478bd9Sstevel@tonic-gate 		{
19537c478bd9Sstevel@tonic-gate 			/* parent -- pick up intermediate zombie */
19547c478bd9Sstevel@tonic-gate 			(void) sm_blocksignal(SIGALRM);
19557c478bd9Sstevel@tonic-gate 
19567c478bd9Sstevel@tonic-gate 			/* wgrp only used when queue runners are persistent */
19577c478bd9Sstevel@tonic-gate 			proc_list_add(pid, "Queue runner", PROC_QUEUE,
19587c478bd9Sstevel@tonic-gate 				      WorkGrp[wgrp].wg_maxact,
19597c478bd9Sstevel@tonic-gate 				      bitset(RWG_PERSISTENT, flags) ? wgrp : -1,
19607c478bd9Sstevel@tonic-gate 				      NULL);
19617c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGALRM);
19627c478bd9Sstevel@tonic-gate 			(void) sm_releasesignal(SIGCHLD);
19637c478bd9Sstevel@tonic-gate 			return true;
19647c478bd9Sstevel@tonic-gate 		}
19657c478bd9Sstevel@tonic-gate 
19667c478bd9Sstevel@tonic-gate 		/* child -- clean up signals */
19677c478bd9Sstevel@tonic-gate 
19687c478bd9Sstevel@tonic-gate 		/* Reset global flags */
19697c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
19707c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
19717c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
19727c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
19737c478bd9Sstevel@tonic-gate 		CurrentPid = getpid();
19747c478bd9Sstevel@tonic-gate 		close_sendmail_pid();
19757c478bd9Sstevel@tonic-gate 
19767c478bd9Sstevel@tonic-gate 		/*
19777c478bd9Sstevel@tonic-gate 		**  Initialize exception stack and default exception
19787c478bd9Sstevel@tonic-gate 		**  handler for child process.
19797c478bd9Sstevel@tonic-gate 		*/
19807c478bd9Sstevel@tonic-gate 
19817c478bd9Sstevel@tonic-gate 		sm_exc_newthread(fatal_error);
19827c478bd9Sstevel@tonic-gate 		clrcontrol();
19837c478bd9Sstevel@tonic-gate 		proc_list_clear();
19847c478bd9Sstevel@tonic-gate 
19857c478bd9Sstevel@tonic-gate 		/* Add parent process as first child item */
19867c478bd9Sstevel@tonic-gate 		proc_list_add(CurrentPid, "Queue runner child process",
19877c478bd9Sstevel@tonic-gate 			      PROC_QUEUE_CHILD, 0, -1, NULL);
19887c478bd9Sstevel@tonic-gate 		(void) sm_releasesignal(SIGCHLD);
19897c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGCHLD, SIG_DFL);
19907c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGHUP, SIG_DFL);
19917c478bd9Sstevel@tonic-gate 		(void) sm_signal(SIGTERM, intsig);
19927c478bd9Sstevel@tonic-gate 	}
19937c478bd9Sstevel@tonic-gate 
19947c478bd9Sstevel@tonic-gate 	/*
19957c478bd9Sstevel@tonic-gate 	**  Release any resources used by the daemon code.
19967c478bd9Sstevel@tonic-gate 	*/
19977c478bd9Sstevel@tonic-gate 
19987c478bd9Sstevel@tonic-gate 	clrdaemon();
19997c478bd9Sstevel@tonic-gate 
20007c478bd9Sstevel@tonic-gate 	/* force it to run expensive jobs */
20017c478bd9Sstevel@tonic-gate 	NoConnect = false;
20027c478bd9Sstevel@tonic-gate 
20037c478bd9Sstevel@tonic-gate 	/* drop privileges */
20047c478bd9Sstevel@tonic-gate 	if (geteuid() == (uid_t) 0)
20057c478bd9Sstevel@tonic-gate 		(void) drop_privileges(false);
20067c478bd9Sstevel@tonic-gate 
20077c478bd9Sstevel@tonic-gate 	/*
20087c478bd9Sstevel@tonic-gate 	**  Create ourselves an envelope
20097c478bd9Sstevel@tonic-gate 	*/
20107c478bd9Sstevel@tonic-gate 
20117c478bd9Sstevel@tonic-gate 	CurEnv = &QueueEnvelope;
20127c478bd9Sstevel@tonic-gate 	rpool = sm_rpool_new_x(NULL);
20137c478bd9Sstevel@tonic-gate 	e = newenvelope(&QueueEnvelope, CurEnv, rpool);
20147c478bd9Sstevel@tonic-gate 	e->e_flags = BlankEnvelope.e_flags;
20157c478bd9Sstevel@tonic-gate 	e->e_parent = NULL;
20167c478bd9Sstevel@tonic-gate 
20177c478bd9Sstevel@tonic-gate 	/* make sure we have disconnected from parent */
20187c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
20197c478bd9Sstevel@tonic-gate 	{
20207c478bd9Sstevel@tonic-gate 		disconnect(1, e);
20217c478bd9Sstevel@tonic-gate 		QuickAbort = false;
20227c478bd9Sstevel@tonic-gate 	}
20237c478bd9Sstevel@tonic-gate 
20247c478bd9Sstevel@tonic-gate 	/*
20257c478bd9Sstevel@tonic-gate 	**  If we are running part of the queue, always ignore stored
20267c478bd9Sstevel@tonic-gate 	**  host status.
20277c478bd9Sstevel@tonic-gate 	*/
20287c478bd9Sstevel@tonic-gate 
20297c478bd9Sstevel@tonic-gate 	if (QueueLimitId != NULL || QueueLimitSender != NULL ||
20307c478bd9Sstevel@tonic-gate 	    QueueLimitQuarantine != NULL ||
20317c478bd9Sstevel@tonic-gate 	    QueueLimitRecipient != NULL)
20327c478bd9Sstevel@tonic-gate 	{
20337c478bd9Sstevel@tonic-gate 		IgnoreHostStatus = true;
20347c478bd9Sstevel@tonic-gate 		MinQueueAge = 0;
20357c478bd9Sstevel@tonic-gate 	}
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 	/*
20387c478bd9Sstevel@tonic-gate 	**  Here is where we choose the queue group from the work group.
20397c478bd9Sstevel@tonic-gate 	**  The caller of the "domorework" label must setup a new envelope.
20407c478bd9Sstevel@tonic-gate 	*/
20417c478bd9Sstevel@tonic-gate 
20427c478bd9Sstevel@tonic-gate 	endgrp = WorkGrp[wgrp].wg_curqgrp; /* to not spin endlessly */
20437c478bd9Sstevel@tonic-gate 
20447c478bd9Sstevel@tonic-gate   domorework:
20457c478bd9Sstevel@tonic-gate 
20467c478bd9Sstevel@tonic-gate 	/*
20477c478bd9Sstevel@tonic-gate 	**  Run a queue group if:
20487c478bd9Sstevel@tonic-gate 	**  RWG_RUNALL bit is set or the bit for this group is set.
20497c478bd9Sstevel@tonic-gate 	*/
20507c478bd9Sstevel@tonic-gate 
20517c478bd9Sstevel@tonic-gate 	now = curtime();
20527c478bd9Sstevel@tonic-gate 	for (;;)
20537c478bd9Sstevel@tonic-gate 	{
20547c478bd9Sstevel@tonic-gate 		/*
20557c478bd9Sstevel@tonic-gate 		**  Find the next queue group within the work group that
20567c478bd9Sstevel@tonic-gate 		**  has been marked as needing a run.
20577c478bd9Sstevel@tonic-gate 		*/
20587c478bd9Sstevel@tonic-gate 
20597c478bd9Sstevel@tonic-gate 		qgrp = WorkGrp[wgrp].wg_qgs[WorkGrp[wgrp].wg_curqgrp]->qg_index;
20607c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_curqgrp++; /* advance */
20617c478bd9Sstevel@tonic-gate 		WorkGrp[wgrp].wg_curqgrp %= WorkGrp[wgrp].wg_numqgrp; /* wrap */
20627c478bd9Sstevel@tonic-gate 		if (bitset(RWG_RUNALL, flags) ||
20637c478bd9Sstevel@tonic-gate 		    (Queue[qgrp]->qg_nextrun <= now &&
20647c478bd9Sstevel@tonic-gate 		     Queue[qgrp]->qg_nextrun != (time_t) -1))
20657c478bd9Sstevel@tonic-gate 			break;
20667c478bd9Sstevel@tonic-gate 		if (endgrp == WorkGrp[wgrp].wg_curqgrp)
20677c478bd9Sstevel@tonic-gate 		{
20687c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
20697c478bd9Sstevel@tonic-gate 			if (bitset(RWG_FORK, flags))
20707c478bd9Sstevel@tonic-gate 				finis(true, true, ExitStat);
20717c478bd9Sstevel@tonic-gate 			return true; /* we're done */
20727c478bd9Sstevel@tonic-gate 		}
20737c478bd9Sstevel@tonic-gate 	}
20747c478bd9Sstevel@tonic-gate 
20757c478bd9Sstevel@tonic-gate 	qdir = Queue[qgrp]->qg_curnum; /* round-robin init of queue position */
20767c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
20777c478bd9Sstevel@tonic-gate 	if (tTd(69, 12))
20787c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
20797c478bd9Sstevel@tonic-gate 			"rwg: wgrp=%d, qgrp=%d, qdir=%d, name=%s, curqgrp=%d, numgrps=%d",
20807c478bd9Sstevel@tonic-gate 			wgrp, qgrp, qdir, qid_printqueue(qgrp, qdir),
20817c478bd9Sstevel@tonic-gate 			WorkGrp[wgrp].wg_curqgrp, WorkGrp[wgrp].wg_numqgrp);
20827c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
20837c478bd9Sstevel@tonic-gate 
20847c478bd9Sstevel@tonic-gate #if HASNICE
20857c478bd9Sstevel@tonic-gate 	/* tweak niceness of queue runs */
20867c478bd9Sstevel@tonic-gate 	if (Queue[qgrp]->qg_nice > 0)
20877c478bd9Sstevel@tonic-gate 		(void) nice(Queue[qgrp]->qg_nice);
20887c478bd9Sstevel@tonic-gate #endif /* HASNICE */
20897c478bd9Sstevel@tonic-gate 
20907c478bd9Sstevel@tonic-gate 	/* XXX running queue group... */
20917c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, CurEnv, "running queue: %s",
20927c478bd9Sstevel@tonic-gate 			qid_printqueue(qgrp, qdir));
20937c478bd9Sstevel@tonic-gate 
20947c478bd9Sstevel@tonic-gate 	if (LogLevel > 69 || tTd(63, 99))
20957c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, NOQID,
20967c478bd9Sstevel@tonic-gate 			  "runqueue %s, pid=%d, forkflag=%d",
20977c478bd9Sstevel@tonic-gate 			  qid_printqueue(qgrp, qdir), (int) CurrentPid,
20987c478bd9Sstevel@tonic-gate 			  bitset(RWG_FORK, flags));
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 	/*
21017c478bd9Sstevel@tonic-gate 	**  Start making passes through the queue.
21027c478bd9Sstevel@tonic-gate 	**	First, read and sort the entire queue.
21037c478bd9Sstevel@tonic-gate 	**	Then, process the work in that order.
21047c478bd9Sstevel@tonic-gate 	**		But if you take too long, start over.
21057c478bd9Sstevel@tonic-gate 	*/
21067c478bd9Sstevel@tonic-gate 
21077c478bd9Sstevel@tonic-gate 	for (i = 0; i < Queue[qgrp]->qg_numqueues; i++)
21087c478bd9Sstevel@tonic-gate 	{
21097c478bd9Sstevel@tonic-gate 		h = gatherq(qgrp, qdir, false, &full, &more);
21107c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
21117c478bd9Sstevel@tonic-gate 		if (ShmId != SM_SHM_NO_ID)
21127c478bd9Sstevel@tonic-gate 			QSHM_ENTRIES(Queue[qgrp]->qg_qpaths[qdir].qp_idx) = h;
21137c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
21147c478bd9Sstevel@tonic-gate 		/* If there are no more items in this queue advance */
21157c478bd9Sstevel@tonic-gate 		if (!more)
21167c478bd9Sstevel@tonic-gate 		{
21177c478bd9Sstevel@tonic-gate 			/* A round-robin advance */
21187c478bd9Sstevel@tonic-gate 			qdir++;
21197c478bd9Sstevel@tonic-gate 			qdir %= Queue[qgrp]->qg_numqueues;
21207c478bd9Sstevel@tonic-gate 		}
21217c478bd9Sstevel@tonic-gate 
21227c478bd9Sstevel@tonic-gate 		/* Has the WorkList reached the limit? */
21237c478bd9Sstevel@tonic-gate 		if (full)
21247c478bd9Sstevel@tonic-gate 			break; /* don't try to gather more */
21257c478bd9Sstevel@tonic-gate 	}
21267c478bd9Sstevel@tonic-gate 
21277c478bd9Sstevel@tonic-gate 	/* order the existing work requests */
21287c478bd9Sstevel@tonic-gate 	njobs = sortq(Queue[qgrp]->qg_maxlist);
21297c478bd9Sstevel@tonic-gate 	Queue[qgrp]->qg_curnum = qdir; /* update */
21307c478bd9Sstevel@tonic-gate 
21317c478bd9Sstevel@tonic-gate 
21327c478bd9Sstevel@tonic-gate 	if (!Verbose && bitnset(QD_FORK, Queue[qgrp]->qg_flags))
21337c478bd9Sstevel@tonic-gate 	{
21347c478bd9Sstevel@tonic-gate 		int loop, maxrunners;
21357c478bd9Sstevel@tonic-gate 		pid_t pid;
21367c478bd9Sstevel@tonic-gate 
21377c478bd9Sstevel@tonic-gate 		/*
21387c478bd9Sstevel@tonic-gate 		**  For this WorkQ we want to fork off N children (maxrunners)
21397c478bd9Sstevel@tonic-gate 		**  at this point. Each child has a copy of WorkQ. Each child
21407c478bd9Sstevel@tonic-gate 		**  will process every N-th item. The parent will wait for all
21417c478bd9Sstevel@tonic-gate 		**  of the children to finish before moving on to the next
21427c478bd9Sstevel@tonic-gate 		**  queue group within the work group. This saves us forking
21437c478bd9Sstevel@tonic-gate 		**  a new runner-child for each work item.
21447c478bd9Sstevel@tonic-gate 		**  It's valid for qg_maxqrun == 0 since this may be an
21457c478bd9Sstevel@tonic-gate 		**  explicit "don't run this queue" setting.
21467c478bd9Sstevel@tonic-gate 		*/
21477c478bd9Sstevel@tonic-gate 
21487c478bd9Sstevel@tonic-gate 		maxrunners = Queue[qgrp]->qg_maxqrun;
21497c478bd9Sstevel@tonic-gate 
21507c478bd9Sstevel@tonic-gate 		/* No need to have more runners then there are jobs */
21517c478bd9Sstevel@tonic-gate 		if (maxrunners > njobs)
21527c478bd9Sstevel@tonic-gate 			maxrunners = njobs;
21537c478bd9Sstevel@tonic-gate 		for (loop = 0; loop < maxrunners; loop++)
21547c478bd9Sstevel@tonic-gate 		{
21557c478bd9Sstevel@tonic-gate 			/*
21567c478bd9Sstevel@tonic-gate 			**  Since the delivery may happen in a child and the
21577c478bd9Sstevel@tonic-gate 			**  parent does not wait, the parent may close the
21587c478bd9Sstevel@tonic-gate 			**  maps thereby removing any shared memory used by
21597c478bd9Sstevel@tonic-gate 			**  the map.  Therefore, close the maps now so the
21607c478bd9Sstevel@tonic-gate 			**  child will dynamically open them if necessary.
21617c478bd9Sstevel@tonic-gate 			*/
21627c478bd9Sstevel@tonic-gate 
21637c478bd9Sstevel@tonic-gate 			closemaps(false);
21647c478bd9Sstevel@tonic-gate 
21657c478bd9Sstevel@tonic-gate 			pid = fork();
21667c478bd9Sstevel@tonic-gate 			if (pid < 0)
21677c478bd9Sstevel@tonic-gate 			{
21687c478bd9Sstevel@tonic-gate 				syserr("run_work_group: cannot fork");
21697c478bd9Sstevel@tonic-gate 				return false;
21707c478bd9Sstevel@tonic-gate 			}
21717c478bd9Sstevel@tonic-gate 			else if (pid > 0)
21727c478bd9Sstevel@tonic-gate 			{
21737c478bd9Sstevel@tonic-gate 				/* parent -- clean out connection cache */
21747c478bd9Sstevel@tonic-gate 				mci_flush(false, NULL);
21757c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
21767c478bd9Sstevel@tonic-gate 				if (QueueSortOrder == QSO_BYHOST)
21777c478bd9Sstevel@tonic-gate 				{
21787c478bd9Sstevel@tonic-gate 					sequenceno += skip_domains(1);
21797c478bd9Sstevel@tonic-gate 				}
21807c478bd9Sstevel@tonic-gate 				else
21817c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
21827c478bd9Sstevel@tonic-gate 				{
21837c478bd9Sstevel@tonic-gate 					/* for the skip */
21847c478bd9Sstevel@tonic-gate 					WorkQ = WorkQ->w_next;
21857c478bd9Sstevel@tonic-gate 					sequenceno++;
21867c478bd9Sstevel@tonic-gate 				}
21877c478bd9Sstevel@tonic-gate 				proc_list_add(pid, "Queue child runner process",
21887c478bd9Sstevel@tonic-gate 					      PROC_QUEUE_CHILD, 0, -1, NULL);
21897c478bd9Sstevel@tonic-gate 
21907c478bd9Sstevel@tonic-gate 				/* No additional work, no additional runners */
21917c478bd9Sstevel@tonic-gate 				if (WorkQ == NULL)
21927c478bd9Sstevel@tonic-gate 					break;
21937c478bd9Sstevel@tonic-gate 			}
21947c478bd9Sstevel@tonic-gate 			else
21957c478bd9Sstevel@tonic-gate 			{
21967c478bd9Sstevel@tonic-gate 				/* child -- Reset global flags */
21977c478bd9Sstevel@tonic-gate 				RestartRequest = NULL;
21987c478bd9Sstevel@tonic-gate 				RestartWorkGroup = false;
21997c478bd9Sstevel@tonic-gate 				ShutdownRequest = NULL;
22007c478bd9Sstevel@tonic-gate 				PendingSignal = 0;
22017c478bd9Sstevel@tonic-gate 				CurrentPid = getpid();
22027c478bd9Sstevel@tonic-gate 				close_sendmail_pid();
22037c478bd9Sstevel@tonic-gate 
22047c478bd9Sstevel@tonic-gate 				/*
22057c478bd9Sstevel@tonic-gate 				**  Initialize exception stack and default
22067c478bd9Sstevel@tonic-gate 				**  exception handler for child process.
22077c478bd9Sstevel@tonic-gate 				**  When fork()'d the child now has a private
22087c478bd9Sstevel@tonic-gate 				**  copy of WorkQ at its current position.
22097c478bd9Sstevel@tonic-gate 				*/
22107c478bd9Sstevel@tonic-gate 
22117c478bd9Sstevel@tonic-gate 				sm_exc_newthread(fatal_error);
22127c478bd9Sstevel@tonic-gate 
22137c478bd9Sstevel@tonic-gate 				/*
22147c478bd9Sstevel@tonic-gate 				**  SMTP processes (whether -bd or -bs) set
22157c478bd9Sstevel@tonic-gate 				**  SIGCHLD to reapchild to collect
22167c478bd9Sstevel@tonic-gate 				**  children status.  However, at delivery
22177c478bd9Sstevel@tonic-gate 				**  time, that status must be collected
22187c478bd9Sstevel@tonic-gate 				**  by sm_wait() to be dealt with properly
22197c478bd9Sstevel@tonic-gate 				**  (check success of delivery based
22207c478bd9Sstevel@tonic-gate 				**  on status code, etc).  Therefore, if we
22217c478bd9Sstevel@tonic-gate 				**  are an SMTP process, reset SIGCHLD
22227c478bd9Sstevel@tonic-gate 				**  back to the default so reapchild
22237c478bd9Sstevel@tonic-gate 				**  doesn't collect status before
22247c478bd9Sstevel@tonic-gate 				**  sm_wait().
22257c478bd9Sstevel@tonic-gate 				*/
22267c478bd9Sstevel@tonic-gate 
22277c478bd9Sstevel@tonic-gate 				if (OpMode == MD_SMTP ||
22287c478bd9Sstevel@tonic-gate 				    OpMode == MD_DAEMON ||
22297c478bd9Sstevel@tonic-gate 				    MaxQueueChildren > 0)
22307c478bd9Sstevel@tonic-gate 				{
22317c478bd9Sstevel@tonic-gate 					proc_list_clear();
22327c478bd9Sstevel@tonic-gate 					sm_releasesignal(SIGCHLD);
22337c478bd9Sstevel@tonic-gate 					(void) sm_signal(SIGCHLD, SIG_DFL);
22347c478bd9Sstevel@tonic-gate 				}
22357c478bd9Sstevel@tonic-gate 
22367c478bd9Sstevel@tonic-gate 				/* child -- error messages to the transcript */
22377c478bd9Sstevel@tonic-gate 				QuickAbort = OnlyOneError = false;
22387c478bd9Sstevel@tonic-gate 				runner_work(e, sequenceno, true,
22397c478bd9Sstevel@tonic-gate 					    maxrunners, njobs);
22407c478bd9Sstevel@tonic-gate 
22417c478bd9Sstevel@tonic-gate 				/* This child is done */
22427c478bd9Sstevel@tonic-gate 				finis(true, true, ExitStat);
22437c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
22447c478bd9Sstevel@tonic-gate 			}
22457c478bd9Sstevel@tonic-gate 		}
22467c478bd9Sstevel@tonic-gate 
22477c478bd9Sstevel@tonic-gate 		sm_releasesignal(SIGCHLD);
22487c478bd9Sstevel@tonic-gate 
22497c478bd9Sstevel@tonic-gate 		/*
22507c478bd9Sstevel@tonic-gate 		**  Wait until all of the runners have completed before
22517c478bd9Sstevel@tonic-gate 		**  seeing if there is another queue group in the
22527c478bd9Sstevel@tonic-gate 		**  work group to process.
22537c478bd9Sstevel@tonic-gate 		**  XXX Future enhancement: don't wait() for all children
22547c478bd9Sstevel@tonic-gate 		**  here, just go ahead and make sure that overall the number
22557c478bd9Sstevel@tonic-gate 		**  of children is not exceeded.
22567c478bd9Sstevel@tonic-gate 		*/
22577c478bd9Sstevel@tonic-gate 
22587c478bd9Sstevel@tonic-gate 		while (CurChildren > 0)
22597c478bd9Sstevel@tonic-gate 		{
22607c478bd9Sstevel@tonic-gate 			int status;
22617c478bd9Sstevel@tonic-gate 			pid_t ret;
22627c478bd9Sstevel@tonic-gate 
22637c478bd9Sstevel@tonic-gate 			while ((ret = sm_wait(&status)) <= 0)
22647c478bd9Sstevel@tonic-gate 				continue;
22657c478bd9Sstevel@tonic-gate 			proc_list_drop(ret, status, NULL);
22667c478bd9Sstevel@tonic-gate 		}
22677c478bd9Sstevel@tonic-gate 	}
22687c478bd9Sstevel@tonic-gate 	else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags))
22697c478bd9Sstevel@tonic-gate 	{
22707c478bd9Sstevel@tonic-gate 		/*
22717c478bd9Sstevel@tonic-gate 		**  When current process will not fork children to do the work,
22727c478bd9Sstevel@tonic-gate 		**  it will do the work itself. The 'skip' will be 1 since
22737c478bd9Sstevel@tonic-gate 		**  there are no child runners to divide the work across.
22747c478bd9Sstevel@tonic-gate 		*/
22757c478bd9Sstevel@tonic-gate 
22767c478bd9Sstevel@tonic-gate 		runner_work(e, sequenceno, false, 1, njobs);
22777c478bd9Sstevel@tonic-gate 	}
22787c478bd9Sstevel@tonic-gate 
22797c478bd9Sstevel@tonic-gate 	/* free memory allocated by newenvelope() above */
22807c478bd9Sstevel@tonic-gate 	sm_rpool_free(rpool);
22817c478bd9Sstevel@tonic-gate 	QueueEnvelope.e_rpool = NULL;
22827c478bd9Sstevel@tonic-gate 
22837c478bd9Sstevel@tonic-gate 	/* Are there still more queues in the work group to process? */
22847c478bd9Sstevel@tonic-gate 	if (endgrp != WorkGrp[wgrp].wg_curqgrp)
22857c478bd9Sstevel@tonic-gate 	{
22867c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
22877c478bd9Sstevel@tonic-gate 		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
22887c478bd9Sstevel@tonic-gate 		e->e_flags = BlankEnvelope.e_flags;
22897c478bd9Sstevel@tonic-gate 		goto domorework;
22907c478bd9Sstevel@tonic-gate 	}
22917c478bd9Sstevel@tonic-gate 
22927c478bd9Sstevel@tonic-gate 	/* No more queues in work group to process. Now check persistent. */
22937c478bd9Sstevel@tonic-gate 	if (bitset(RWG_PERSISTENT, flags))
22947c478bd9Sstevel@tonic-gate 	{
22957c478bd9Sstevel@tonic-gate 		sequenceno = 1;
22967c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, CurEnv, "running queue: %s",
22977c478bd9Sstevel@tonic-gate 				qid_printqueue(qgrp, qdir));
22987c478bd9Sstevel@tonic-gate 
22997c478bd9Sstevel@tonic-gate 		/*
23007c478bd9Sstevel@tonic-gate 		**  close bogus maps, i.e., maps which caused a tempfail,
23017c478bd9Sstevel@tonic-gate 		**	so we get fresh map connections on the next lookup.
23027c478bd9Sstevel@tonic-gate 		**  closemaps() is also called when children are started.
23037c478bd9Sstevel@tonic-gate 		*/
23047c478bd9Sstevel@tonic-gate 
23057c478bd9Sstevel@tonic-gate 		closemaps(true);
23067c478bd9Sstevel@tonic-gate 
23077c478bd9Sstevel@tonic-gate 		/* Close any cached connections. */
23087c478bd9Sstevel@tonic-gate 		mci_flush(true, NULL);
23097c478bd9Sstevel@tonic-gate 
23107c478bd9Sstevel@tonic-gate 		/* Clean out expired related entries. */
23117c478bd9Sstevel@tonic-gate 		rmexpstab();
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate #if NAMED_BIND
23147c478bd9Sstevel@tonic-gate 		/* Update MX records for FallbackMX. */
23157c478bd9Sstevel@tonic-gate 		if (FallbackMX != NULL)
23167c478bd9Sstevel@tonic-gate 			(void) getfallbackmxrr(FallbackMX);
23177c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
23187c478bd9Sstevel@tonic-gate 
23197c478bd9Sstevel@tonic-gate #if USERDB
23207c478bd9Sstevel@tonic-gate 		/* close UserDatabase */
23217c478bd9Sstevel@tonic-gate 		_udbx_close();
23227c478bd9Sstevel@tonic-gate #endif /* USERDB */
23237c478bd9Sstevel@tonic-gate 
23247c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
23257c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&SmHeapCheck, 2)
23267c478bd9Sstevel@tonic-gate 		    && access("memdump", F_OK) == 0
23277c478bd9Sstevel@tonic-gate 		   )
23287c478bd9Sstevel@tonic-gate 		{
23297c478bd9Sstevel@tonic-gate 			SM_FILE_T *out;
23307c478bd9Sstevel@tonic-gate 
23317c478bd9Sstevel@tonic-gate 			remove("memdump");
23327c478bd9Sstevel@tonic-gate 			out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
23337c478bd9Sstevel@tonic-gate 					 "memdump.out", SM_IO_APPEND, NULL);
23347c478bd9Sstevel@tonic-gate 			if (out != NULL)
23357c478bd9Sstevel@tonic-gate 			{
23367c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
23377c478bd9Sstevel@tonic-gate 				sm_heap_report(out,
23387c478bd9Sstevel@tonic-gate 					sm_debug_level(&SmHeapCheck) - 1);
23397c478bd9Sstevel@tonic-gate 				(void) sm_io_close(out, SM_TIME_DEFAULT);
23407c478bd9Sstevel@tonic-gate 			}
23417c478bd9Sstevel@tonic-gate 		}
23427c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
23437c478bd9Sstevel@tonic-gate 
23447c478bd9Sstevel@tonic-gate 		/* let me rest for a second to catch my breath */
23457c478bd9Sstevel@tonic-gate 		if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
23467c478bd9Sstevel@tonic-gate 			sleep(MIN_SLEEP_TIME);
23477c478bd9Sstevel@tonic-gate 		else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
23487c478bd9Sstevel@tonic-gate 			sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
23497c478bd9Sstevel@tonic-gate 		else
23507c478bd9Sstevel@tonic-gate 			sleep(WorkGrp[wgrp].wg_lowqintvl);
23517c478bd9Sstevel@tonic-gate 
23527c478bd9Sstevel@tonic-gate 		/*
23537c478bd9Sstevel@tonic-gate 		**  Get the LA outside the WorkQ loop if necessary.
23547c478bd9Sstevel@tonic-gate 		**  In a persistent queue runner the code is repeated over
23557c478bd9Sstevel@tonic-gate 		**  and over but gatherq() may ignore entries due to
23567c478bd9Sstevel@tonic-gate 		**  shouldqueue() (do we really have to do this twice?).
23577c478bd9Sstevel@tonic-gate 		**  Hence the queue runners would just idle around when once
23587c478bd9Sstevel@tonic-gate 		**  CurrentLA caused all entries in a queue to be ignored.
23597c478bd9Sstevel@tonic-gate 		*/
23607c478bd9Sstevel@tonic-gate 
23617c478bd9Sstevel@tonic-gate 		if (njobs == 0)
23627c478bd9Sstevel@tonic-gate 			SM_GET_LA(now);
23637c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
23647c478bd9Sstevel@tonic-gate 		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
23657c478bd9Sstevel@tonic-gate 		e->e_flags = BlankEnvelope.e_flags;
23667c478bd9Sstevel@tonic-gate 		goto domorework;
23677c478bd9Sstevel@tonic-gate 	}
23687c478bd9Sstevel@tonic-gate 
23697c478bd9Sstevel@tonic-gate 	/* exit without the usual cleanup */
23707c478bd9Sstevel@tonic-gate 	e->e_id = NULL;
23717c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
23727c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
23737c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
23747c478bd9Sstevel@tonic-gate 	return true;
23757c478bd9Sstevel@tonic-gate }
23767c478bd9Sstevel@tonic-gate 
23777c478bd9Sstevel@tonic-gate /*
23787c478bd9Sstevel@tonic-gate **  DOQUEUERUN -- do a queue run?
23797c478bd9Sstevel@tonic-gate */
23807c478bd9Sstevel@tonic-gate 
23817c478bd9Sstevel@tonic-gate bool
23827c478bd9Sstevel@tonic-gate doqueuerun()
23837c478bd9Sstevel@tonic-gate {
23847c478bd9Sstevel@tonic-gate 	return DoQueueRun;
23857c478bd9Sstevel@tonic-gate }
23867c478bd9Sstevel@tonic-gate 
23877c478bd9Sstevel@tonic-gate /*
23887c478bd9Sstevel@tonic-gate **  RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done.
23897c478bd9Sstevel@tonic-gate **
23907c478bd9Sstevel@tonic-gate **	Parameters:
23917c478bd9Sstevel@tonic-gate **		none.
23927c478bd9Sstevel@tonic-gate **
23937c478bd9Sstevel@tonic-gate **	Returns:
23947c478bd9Sstevel@tonic-gate **		none.
23957c478bd9Sstevel@tonic-gate **
23967c478bd9Sstevel@tonic-gate **	Side Effects:
23977c478bd9Sstevel@tonic-gate **		The invocation of this function via an alarm may interrupt
23987c478bd9Sstevel@tonic-gate **		a set of actions. Thus errno may be set in that context.
23997c478bd9Sstevel@tonic-gate **		We need to restore errno at the end of this function to ensure
24007c478bd9Sstevel@tonic-gate **		that any work done here that sets errno doesn't return a
24017c478bd9Sstevel@tonic-gate **		misleading/false errno value. Errno may	be EINTR upon entry to
24027c478bd9Sstevel@tonic-gate **		this function because of non-restartable/continuable system
24037c478bd9Sstevel@tonic-gate **		API was active. Iff this is true we will override errno as
24047c478bd9Sstevel@tonic-gate **		a timeout (as a more accurate error message).
24057c478bd9Sstevel@tonic-gate **
24067c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
24077c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
24087c478bd9Sstevel@tonic-gate **		DOING.
24097c478bd9Sstevel@tonic-gate */
24107c478bd9Sstevel@tonic-gate 
24117c478bd9Sstevel@tonic-gate void
24127c478bd9Sstevel@tonic-gate runqueueevent(ignore)
24137c478bd9Sstevel@tonic-gate 	int ignore;
24147c478bd9Sstevel@tonic-gate {
24157c478bd9Sstevel@tonic-gate 	int save_errno = errno;
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate 	/*
24187c478bd9Sstevel@tonic-gate 	**  Set the general bit that we want a queue run,
24197c478bd9Sstevel@tonic-gate 	**  tested in doqueuerun()
24207c478bd9Sstevel@tonic-gate 	*/
24217c478bd9Sstevel@tonic-gate 
24227c478bd9Sstevel@tonic-gate 	DoQueueRun = true;
24237c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
24247c478bd9Sstevel@tonic-gate 	if (tTd(69, 10))
24257c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID, "rqe: done");
24267c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
24277c478bd9Sstevel@tonic-gate 
24287c478bd9Sstevel@tonic-gate 	errno = save_errno;
24297c478bd9Sstevel@tonic-gate 	if (errno == EINTR)
24307c478bd9Sstevel@tonic-gate 		errno = ETIMEDOUT;
24317c478bd9Sstevel@tonic-gate }
24327c478bd9Sstevel@tonic-gate /*
24337c478bd9Sstevel@tonic-gate **  GATHERQ -- gather messages from the message queue(s) the work queue.
24347c478bd9Sstevel@tonic-gate **
24357c478bd9Sstevel@tonic-gate **	Parameters:
24367c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group.
24377c478bd9Sstevel@tonic-gate **		qdir -- the index of the queue directory.
24387c478bd9Sstevel@tonic-gate **		doall -- if set, include everything in the queue (even
24397c478bd9Sstevel@tonic-gate **			the jobs that cannot be run because the load
24407c478bd9Sstevel@tonic-gate **			average is too high, or MaxQueueRun is reached).
24417c478bd9Sstevel@tonic-gate **			Otherwise, exclude those jobs.
24427c478bd9Sstevel@tonic-gate **		full -- (optional) to be set 'true' if WorkList is full
24437c478bd9Sstevel@tonic-gate **		more -- (optional) to be set 'true' if there are still more
24447c478bd9Sstevel@tonic-gate **			messages in this queue not added to WorkList
24457c478bd9Sstevel@tonic-gate **
24467c478bd9Sstevel@tonic-gate **	Returns:
24477c478bd9Sstevel@tonic-gate **		The number of request in the queue (not necessarily
24487c478bd9Sstevel@tonic-gate **		the number of requests in WorkList however).
24497c478bd9Sstevel@tonic-gate **
24507c478bd9Sstevel@tonic-gate **	Side Effects:
24517c478bd9Sstevel@tonic-gate **		prepares available work into WorkList
24527c478bd9Sstevel@tonic-gate */
24537c478bd9Sstevel@tonic-gate 
24547c478bd9Sstevel@tonic-gate #define NEED_P		0001	/* 'P': priority */
24557c478bd9Sstevel@tonic-gate #define NEED_T		0002	/* 'T': time */
24567c478bd9Sstevel@tonic-gate #define NEED_R		0004	/* 'R': recipient */
24577c478bd9Sstevel@tonic-gate #define NEED_S		0010	/* 'S': sender */
24587c478bd9Sstevel@tonic-gate #define NEED_H		0020	/* host */
24597c478bd9Sstevel@tonic-gate #define HAS_QUARANTINE	0040	/* has an unexpected 'q' line */
24607c478bd9Sstevel@tonic-gate #define NEED_QUARANTINE	0100	/* 'q': reason */
24617c478bd9Sstevel@tonic-gate 
24627c478bd9Sstevel@tonic-gate static WORK	*WorkList = NULL;	/* list of unsort work */
24637c478bd9Sstevel@tonic-gate static int	WorkListSize = 0;	/* current max size of WorkList */
24647c478bd9Sstevel@tonic-gate static int	WorkListCount = 0;	/* # of work items in WorkList */
24657c478bd9Sstevel@tonic-gate 
24667c478bd9Sstevel@tonic-gate static int
24677c478bd9Sstevel@tonic-gate gatherq(qgrp, qdir, doall, full, more)
24687c478bd9Sstevel@tonic-gate 	int qgrp;
24697c478bd9Sstevel@tonic-gate 	int qdir;
24707c478bd9Sstevel@tonic-gate 	bool doall;
24717c478bd9Sstevel@tonic-gate 	bool *full;
24727c478bd9Sstevel@tonic-gate 	bool *more;
24737c478bd9Sstevel@tonic-gate {
24747c478bd9Sstevel@tonic-gate 	register struct dirent *d;
24757c478bd9Sstevel@tonic-gate 	register WORK *w;
24767c478bd9Sstevel@tonic-gate 	register char *p;
24777c478bd9Sstevel@tonic-gate 	DIR *f;
24787c478bd9Sstevel@tonic-gate 	int i, num_ent;
24797c478bd9Sstevel@tonic-gate 	int wn;
24807c478bd9Sstevel@tonic-gate 	QUEUE_CHAR *check;
24817c478bd9Sstevel@tonic-gate 	char qd[MAXPATHLEN];
24827c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
24837c478bd9Sstevel@tonic-gate 
24847c478bd9Sstevel@tonic-gate 	wn = WorkListCount - 1;
24857c478bd9Sstevel@tonic-gate 	num_ent = 0;
24867c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
2487*058561cbSjbeck 		(void) sm_strlcpy(qd, ".", sizeof(qd));
24887c478bd9Sstevel@tonic-gate 	else
2489*058561cbSjbeck 		(void) sm_strlcpyn(qd, sizeof(qd), 2,
24907c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
24917c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBQF,
24927c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
24937c478bd9Sstevel@tonic-gate 					? "/qf" : ""));
24947c478bd9Sstevel@tonic-gate 
24957c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
24967c478bd9Sstevel@tonic-gate 	{
24977c478bd9Sstevel@tonic-gate 		sm_dprintf("gatherq:\n");
24987c478bd9Sstevel@tonic-gate 
24997c478bd9Sstevel@tonic-gate 		check = QueueLimitId;
25007c478bd9Sstevel@tonic-gate 		while (check != NULL)
25017c478bd9Sstevel@tonic-gate 		{
25027c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitId = %s%s\n",
25037c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25047c478bd9Sstevel@tonic-gate 				check->queue_match);
25057c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25067c478bd9Sstevel@tonic-gate 		}
25077c478bd9Sstevel@tonic-gate 
25087c478bd9Sstevel@tonic-gate 		check = QueueLimitSender;
25097c478bd9Sstevel@tonic-gate 		while (check != NULL)
25107c478bd9Sstevel@tonic-gate 		{
25117c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitSender = %s%s\n",
25127c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25137c478bd9Sstevel@tonic-gate 				check->queue_match);
25147c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25157c478bd9Sstevel@tonic-gate 		}
25167c478bd9Sstevel@tonic-gate 
25177c478bd9Sstevel@tonic-gate 		check = QueueLimitRecipient;
25187c478bd9Sstevel@tonic-gate 		while (check != NULL)
25197c478bd9Sstevel@tonic-gate 		{
25207c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitRecipient = %s%s\n",
25217c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25227c478bd9Sstevel@tonic-gate 				check->queue_match);
25237c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25247c478bd9Sstevel@tonic-gate 		}
25257c478bd9Sstevel@tonic-gate 
25267c478bd9Sstevel@tonic-gate 		if (QueueMode == QM_QUARANTINE)
25277c478bd9Sstevel@tonic-gate 		{
25287c478bd9Sstevel@tonic-gate 			check = QueueLimitQuarantine;
25297c478bd9Sstevel@tonic-gate 			while (check != NULL)
25307c478bd9Sstevel@tonic-gate 			{
25317c478bd9Sstevel@tonic-gate 				sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
25327c478bd9Sstevel@tonic-gate 					   check->queue_negate ? "!" : "",
25337c478bd9Sstevel@tonic-gate 					   check->queue_match);
25347c478bd9Sstevel@tonic-gate 				check = check->queue_next;
25357c478bd9Sstevel@tonic-gate 			}
25367c478bd9Sstevel@tonic-gate 		}
25377c478bd9Sstevel@tonic-gate 	}
25387c478bd9Sstevel@tonic-gate 
25397c478bd9Sstevel@tonic-gate 	/* open the queue directory */
25407c478bd9Sstevel@tonic-gate 	f = opendir(qd);
25417c478bd9Sstevel@tonic-gate 	if (f == NULL)
25427c478bd9Sstevel@tonic-gate 	{
25437c478bd9Sstevel@tonic-gate 		syserr("gatherq: cannot open \"%s\"",
25447c478bd9Sstevel@tonic-gate 			qid_printqueue(qgrp, qdir));
25457c478bd9Sstevel@tonic-gate 		if (full != NULL)
25467c478bd9Sstevel@tonic-gate 			*full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
25477c478bd9Sstevel@tonic-gate 		if (more != NULL)
25487c478bd9Sstevel@tonic-gate 			*more = false;
25497c478bd9Sstevel@tonic-gate 		return 0;
25507c478bd9Sstevel@tonic-gate 	}
25517c478bd9Sstevel@tonic-gate 
25527c478bd9Sstevel@tonic-gate 	/*
25537c478bd9Sstevel@tonic-gate 	**  Read the work directory.
25547c478bd9Sstevel@tonic-gate 	*/
25557c478bd9Sstevel@tonic-gate 
25567c478bd9Sstevel@tonic-gate 	while ((d = readdir(f)) != NULL)
25577c478bd9Sstevel@tonic-gate 	{
25587c478bd9Sstevel@tonic-gate 		SM_FILE_T *cf;
25597c478bd9Sstevel@tonic-gate 		int qfver = 0;
25607c478bd9Sstevel@tonic-gate 		char lbuf[MAXNAME + 1];
25617c478bd9Sstevel@tonic-gate 		struct stat sbuf;
25627c478bd9Sstevel@tonic-gate 
25637c478bd9Sstevel@tonic-gate 		if (tTd(41, 50))
25647c478bd9Sstevel@tonic-gate 			sm_dprintf("gatherq: checking %s..", d->d_name);
25657c478bd9Sstevel@tonic-gate 
25667c478bd9Sstevel@tonic-gate 		/* is this an interesting entry? */
25677c478bd9Sstevel@tonic-gate 		if (!(((QueueMode == QM_NORMAL &&
25687c478bd9Sstevel@tonic-gate 			d->d_name[0] == NORMQF_LETTER) ||
25697c478bd9Sstevel@tonic-gate 		       (QueueMode == QM_QUARANTINE &&
25707c478bd9Sstevel@tonic-gate 			d->d_name[0] == QUARQF_LETTER) ||
25717c478bd9Sstevel@tonic-gate 		       (QueueMode == QM_LOST &&
25727c478bd9Sstevel@tonic-gate 			d->d_name[0] == LOSEQF_LETTER)) &&
25737c478bd9Sstevel@tonic-gate 		      d->d_name[1] == 'f'))
25747c478bd9Sstevel@tonic-gate 		{
25757c478bd9Sstevel@tonic-gate 			if (tTd(41, 50))
25767c478bd9Sstevel@tonic-gate 				sm_dprintf("  skipping\n");
25777c478bd9Sstevel@tonic-gate 			continue;
25787c478bd9Sstevel@tonic-gate 		}
25797c478bd9Sstevel@tonic-gate 		if (tTd(41, 50))
25807c478bd9Sstevel@tonic-gate 			sm_dprintf("\n");
25817c478bd9Sstevel@tonic-gate 
25827c478bd9Sstevel@tonic-gate 		if (strlen(d->d_name) >= MAXQFNAME)
25837c478bd9Sstevel@tonic-gate 		{
25847c478bd9Sstevel@tonic-gate 			if (Verbose)
25857c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
25867c478bd9Sstevel@tonic-gate 						     "gatherq: %s too long, %d max characters\n",
25877c478bd9Sstevel@tonic-gate 						     d->d_name, MAXQFNAME);
25887c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
25897c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ALERT, NOQID,
25907c478bd9Sstevel@tonic-gate 					  "gatherq: %s too long, %d max characters",
25917c478bd9Sstevel@tonic-gate 					  d->d_name, MAXQFNAME);
25927c478bd9Sstevel@tonic-gate 			continue;
25937c478bd9Sstevel@tonic-gate 		}
25947c478bd9Sstevel@tonic-gate 
25957c478bd9Sstevel@tonic-gate 		check = QueueLimitId;
25967c478bd9Sstevel@tonic-gate 		while (check != NULL)
25977c478bd9Sstevel@tonic-gate 		{
25987c478bd9Sstevel@tonic-gate 			if (strcontainedin(false, check->queue_match,
25997c478bd9Sstevel@tonic-gate 					   d->d_name) != check->queue_negate)
26007c478bd9Sstevel@tonic-gate 				break;
26017c478bd9Sstevel@tonic-gate 			else
26027c478bd9Sstevel@tonic-gate 				check = check->queue_next;
26037c478bd9Sstevel@tonic-gate 		}
26047c478bd9Sstevel@tonic-gate 		if (QueueLimitId != NULL && check == NULL)
26057c478bd9Sstevel@tonic-gate 			continue;
26067c478bd9Sstevel@tonic-gate 
26077c478bd9Sstevel@tonic-gate 		/* grow work list if necessary */
26087c478bd9Sstevel@tonic-gate 		if (++wn >= MaxQueueRun && MaxQueueRun > 0)
26097c478bd9Sstevel@tonic-gate 		{
26107c478bd9Sstevel@tonic-gate 			if (wn == MaxQueueRun && LogLevel > 0)
26117c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, NOQID,
26127c478bd9Sstevel@tonic-gate 					  "WorkList for %s maxed out at %d",
26137c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
26147c478bd9Sstevel@tonic-gate 					  MaxQueueRun);
26157c478bd9Sstevel@tonic-gate 			if (doall)
26167c478bd9Sstevel@tonic-gate 				continue;	/* just count entries */
26177c478bd9Sstevel@tonic-gate 			break;
26187c478bd9Sstevel@tonic-gate 		}
26197c478bd9Sstevel@tonic-gate 		if (wn >= WorkListSize)
26207c478bd9Sstevel@tonic-gate 		{
26217c478bd9Sstevel@tonic-gate 			grow_wlist(qgrp, qdir);
26227c478bd9Sstevel@tonic-gate 			if (wn >= WorkListSize)
26237c478bd9Sstevel@tonic-gate 				continue;
26247c478bd9Sstevel@tonic-gate 		}
26257c478bd9Sstevel@tonic-gate 		SM_ASSERT(wn >= 0);
26267c478bd9Sstevel@tonic-gate 		w = &WorkList[wn];
26277c478bd9Sstevel@tonic-gate 
2628*058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", d->d_name);
26297c478bd9Sstevel@tonic-gate 		if (stat(qf, &sbuf) < 0)
26307c478bd9Sstevel@tonic-gate 		{
26317c478bd9Sstevel@tonic-gate 			if (errno != ENOENT)
26327c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
26337c478bd9Sstevel@tonic-gate 					  "gatherq: can't stat %s/%s",
26347c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
26357c478bd9Sstevel@tonic-gate 					  d->d_name);
26367c478bd9Sstevel@tonic-gate 			wn--;
26377c478bd9Sstevel@tonic-gate 			continue;
26387c478bd9Sstevel@tonic-gate 		}
26397c478bd9Sstevel@tonic-gate 		if (!bitset(S_IFREG, sbuf.st_mode))
26407c478bd9Sstevel@tonic-gate 		{
26417c478bd9Sstevel@tonic-gate 			/* Yikes!  Skip it or we will hang on open! */
26427c478bd9Sstevel@tonic-gate 			if (!((d->d_name[0] == DATAFL_LETTER ||
26437c478bd9Sstevel@tonic-gate 			       d->d_name[0] == NORMQF_LETTER ||
26447c478bd9Sstevel@tonic-gate 			       d->d_name[0] == QUARQF_LETTER ||
26457c478bd9Sstevel@tonic-gate 			       d->d_name[0] == LOSEQF_LETTER ||
26467c478bd9Sstevel@tonic-gate 			       d->d_name[0] == XSCRPT_LETTER) &&
26477c478bd9Sstevel@tonic-gate 			      d->d_name[1] == 'f' && d->d_name[2] == '\0'))
26487c478bd9Sstevel@tonic-gate 				syserr("gatherq: %s/%s is not a regular file",
26497c478bd9Sstevel@tonic-gate 				       qid_printqueue(qgrp, qdir), d->d_name);
26507c478bd9Sstevel@tonic-gate 			wn--;
26517c478bd9Sstevel@tonic-gate 			continue;
26527c478bd9Sstevel@tonic-gate 		}
26537c478bd9Sstevel@tonic-gate 
26547c478bd9Sstevel@tonic-gate 		/* avoid work if possible */
26557c478bd9Sstevel@tonic-gate 		if ((QueueSortOrder == QSO_BYFILENAME ||
26567c478bd9Sstevel@tonic-gate 		     QueueSortOrder == QSO_BYMODTIME ||
26571daa5768Sjbeck 		     QueueSortOrder == QSO_NONE ||
26587c478bd9Sstevel@tonic-gate 		     QueueSortOrder == QSO_RANDOM) &&
26597c478bd9Sstevel@tonic-gate 		    QueueLimitQuarantine == NULL &&
26607c478bd9Sstevel@tonic-gate 		    QueueLimitSender == NULL &&
26617c478bd9Sstevel@tonic-gate 		    QueueLimitRecipient == NULL)
26627c478bd9Sstevel@tonic-gate 		{
26637c478bd9Sstevel@tonic-gate 			w->w_qgrp = qgrp;
26647c478bd9Sstevel@tonic-gate 			w->w_qdir = qdir;
26657c478bd9Sstevel@tonic-gate 			w->w_name = newstr(d->d_name);
26667c478bd9Sstevel@tonic-gate 			w->w_host = NULL;
26677c478bd9Sstevel@tonic-gate 			w->w_lock = w->w_tooyoung = false;
26687c478bd9Sstevel@tonic-gate 			w->w_pri = 0;
26697c478bd9Sstevel@tonic-gate 			w->w_ctime = 0;
26707c478bd9Sstevel@tonic-gate 			w->w_mtime = sbuf.st_mtime;
26717c478bd9Sstevel@tonic-gate 			++num_ent;
26727c478bd9Sstevel@tonic-gate 			continue;
26737c478bd9Sstevel@tonic-gate 		}
26747c478bd9Sstevel@tonic-gate 
26757c478bd9Sstevel@tonic-gate 		/* open control file */
26767c478bd9Sstevel@tonic-gate 		cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
26777c478bd9Sstevel@tonic-gate 				NULL);
26787c478bd9Sstevel@tonic-gate 		if (cf == NULL && OpMode != MD_PRINT)
26797c478bd9Sstevel@tonic-gate 		{
26807c478bd9Sstevel@tonic-gate 			/* this may be some random person sending hir msgs */
26817c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
26827c478bd9Sstevel@tonic-gate 				sm_dprintf("gatherq: cannot open %s: %s\n",
26837c478bd9Sstevel@tonic-gate 					d->d_name, sm_errstring(errno));
26847c478bd9Sstevel@tonic-gate 			errno = 0;
26857c478bd9Sstevel@tonic-gate 			wn--;
26867c478bd9Sstevel@tonic-gate 			continue;
26877c478bd9Sstevel@tonic-gate 		}
26887c478bd9Sstevel@tonic-gate 		w->w_qgrp = qgrp;
26897c478bd9Sstevel@tonic-gate 		w->w_qdir = qdir;
26907c478bd9Sstevel@tonic-gate 		w->w_name = newstr(d->d_name);
26917c478bd9Sstevel@tonic-gate 		w->w_host = NULL;
26927c478bd9Sstevel@tonic-gate 		if (cf != NULL)
26937c478bd9Sstevel@tonic-gate 		{
26947c478bd9Sstevel@tonic-gate 			w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
26957c478bd9Sstevel@tonic-gate 							    NULL),
26967c478bd9Sstevel@tonic-gate 					      w->w_name, NULL,
26977c478bd9Sstevel@tonic-gate 					      LOCK_SH|LOCK_NB);
26987c478bd9Sstevel@tonic-gate 		}
26997c478bd9Sstevel@tonic-gate 		w->w_tooyoung = false;
27007c478bd9Sstevel@tonic-gate 
27017c478bd9Sstevel@tonic-gate 		/* make sure jobs in creation don't clog queue */
27027c478bd9Sstevel@tonic-gate 		w->w_pri = 0x7fffffff;
27037c478bd9Sstevel@tonic-gate 		w->w_ctime = 0;
27047c478bd9Sstevel@tonic-gate 		w->w_mtime = sbuf.st_mtime;
27057c478bd9Sstevel@tonic-gate 
27067c478bd9Sstevel@tonic-gate 		/* extract useful information */
27077c478bd9Sstevel@tonic-gate 		i = NEED_P|NEED_T;
27087c478bd9Sstevel@tonic-gate 		if (QueueSortOrder == QSO_BYHOST
27097c478bd9Sstevel@tonic-gate #if _FFR_RHS
27107c478bd9Sstevel@tonic-gate 		    || QueueSortOrder == QSO_BYSHUFFLE
27117c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
27127c478bd9Sstevel@tonic-gate 		   )
27137c478bd9Sstevel@tonic-gate 		{
27147c478bd9Sstevel@tonic-gate 			/* need w_host set for host sort order */
27157c478bd9Sstevel@tonic-gate 			i |= NEED_H;
27167c478bd9Sstevel@tonic-gate 		}
27177c478bd9Sstevel@tonic-gate 		if (QueueLimitSender != NULL)
27187c478bd9Sstevel@tonic-gate 			i |= NEED_S;
27197c478bd9Sstevel@tonic-gate 		if (QueueLimitRecipient != NULL)
27207c478bd9Sstevel@tonic-gate 			i |= NEED_R;
27217c478bd9Sstevel@tonic-gate 		if (QueueLimitQuarantine != NULL)
27227c478bd9Sstevel@tonic-gate 			i |= NEED_QUARANTINE;
27237c478bd9Sstevel@tonic-gate 		while (cf != NULL && i != 0 &&
27247c478bd9Sstevel@tonic-gate 		       sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
2725*058561cbSjbeck 				   sizeof(lbuf)) != NULL)
27267c478bd9Sstevel@tonic-gate 		{
27277c478bd9Sstevel@tonic-gate 			int c;
27287c478bd9Sstevel@tonic-gate 			time_t age;
27297c478bd9Sstevel@tonic-gate 
27307c478bd9Sstevel@tonic-gate 			p = strchr(lbuf, '\n');
27317c478bd9Sstevel@tonic-gate 			if (p != NULL)
27327c478bd9Sstevel@tonic-gate 				*p = '\0';
27337c478bd9Sstevel@tonic-gate 			else
27347c478bd9Sstevel@tonic-gate 			{
27357c478bd9Sstevel@tonic-gate 				/* flush rest of overly long line */
27367c478bd9Sstevel@tonic-gate 				while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
27377c478bd9Sstevel@tonic-gate 				       != SM_IO_EOF && c != '\n')
27387c478bd9Sstevel@tonic-gate 					continue;
27397c478bd9Sstevel@tonic-gate 			}
27407c478bd9Sstevel@tonic-gate 
27417c478bd9Sstevel@tonic-gate 			switch (lbuf[0])
27427c478bd9Sstevel@tonic-gate 			{
27437c478bd9Sstevel@tonic-gate 			  case 'V':
27447c478bd9Sstevel@tonic-gate 				qfver = atoi(&lbuf[1]);
27457c478bd9Sstevel@tonic-gate 				break;
27467c478bd9Sstevel@tonic-gate 
27477c478bd9Sstevel@tonic-gate 			  case 'P':
27487c478bd9Sstevel@tonic-gate 				w->w_pri = atol(&lbuf[1]);
27497c478bd9Sstevel@tonic-gate 				i &= ~NEED_P;
27507c478bd9Sstevel@tonic-gate 				break;
27517c478bd9Sstevel@tonic-gate 
27527c478bd9Sstevel@tonic-gate 			  case 'T':
27537c478bd9Sstevel@tonic-gate 				w->w_ctime = atol(&lbuf[1]);
27547c478bd9Sstevel@tonic-gate 				i &= ~NEED_T;
27557c478bd9Sstevel@tonic-gate 				break;
27567c478bd9Sstevel@tonic-gate 
27577c478bd9Sstevel@tonic-gate 			  case 'q':
27587c478bd9Sstevel@tonic-gate 				if (QueueMode != QM_QUARANTINE &&
27597c478bd9Sstevel@tonic-gate 				    QueueMode != QM_LOST)
27607c478bd9Sstevel@tonic-gate 				{
27617c478bd9Sstevel@tonic-gate 					if (tTd(41, 49))
27627c478bd9Sstevel@tonic-gate 						sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
27637c478bd9Sstevel@tonic-gate 							   w->w_name);
27647c478bd9Sstevel@tonic-gate 					i |= HAS_QUARANTINE;
27657c478bd9Sstevel@tonic-gate 				}
27667c478bd9Sstevel@tonic-gate 				else if (QueueMode == QM_QUARANTINE)
27677c478bd9Sstevel@tonic-gate 				{
27687c478bd9Sstevel@tonic-gate 					if (QueueLimitQuarantine == NULL)
27697c478bd9Sstevel@tonic-gate 					{
27707c478bd9Sstevel@tonic-gate 						i &= ~NEED_QUARANTINE;
27717c478bd9Sstevel@tonic-gate 						break;
27727c478bd9Sstevel@tonic-gate 					}
27737c478bd9Sstevel@tonic-gate 					p = &lbuf[1];
27747c478bd9Sstevel@tonic-gate 					check = QueueLimitQuarantine;
27757c478bd9Sstevel@tonic-gate 					while (check != NULL)
27767c478bd9Sstevel@tonic-gate 					{
27777c478bd9Sstevel@tonic-gate 						if (strcontainedin(false,
27787c478bd9Sstevel@tonic-gate 								   check->queue_match,
27797c478bd9Sstevel@tonic-gate 								   p) !=
27807c478bd9Sstevel@tonic-gate 						    check->queue_negate)
27817c478bd9Sstevel@tonic-gate 							break;
27827c478bd9Sstevel@tonic-gate 						else
27837c478bd9Sstevel@tonic-gate 							check = check->queue_next;
27847c478bd9Sstevel@tonic-gate 					}
27857c478bd9Sstevel@tonic-gate 					if (check != NULL)
27867c478bd9Sstevel@tonic-gate 						i &= ~NEED_QUARANTINE;
27877c478bd9Sstevel@tonic-gate 				}
27887c478bd9Sstevel@tonic-gate 				break;
27897c478bd9Sstevel@tonic-gate 
27907c478bd9Sstevel@tonic-gate 			  case 'R':
27917c478bd9Sstevel@tonic-gate 				if (w->w_host == NULL &&
27927c478bd9Sstevel@tonic-gate 				    (p = strrchr(&lbuf[1], '@')) != NULL)
27937c478bd9Sstevel@tonic-gate 				{
27947c478bd9Sstevel@tonic-gate #if _FFR_RHS
27957c478bd9Sstevel@tonic-gate 					if (QueueSortOrder == QSO_BYSHUFFLE)
27967c478bd9Sstevel@tonic-gate 						w->w_host = newstr(&p[1]);
27977c478bd9Sstevel@tonic-gate 					else
27987c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
27997c478bd9Sstevel@tonic-gate 						w->w_host = strrev(&p[1]);
28007c478bd9Sstevel@tonic-gate 					makelower(w->w_host);
28017c478bd9Sstevel@tonic-gate 					i &= ~NEED_H;
28027c478bd9Sstevel@tonic-gate 				}
28037c478bd9Sstevel@tonic-gate 				if (QueueLimitRecipient == NULL)
28047c478bd9Sstevel@tonic-gate 				{
28057c478bd9Sstevel@tonic-gate 					i &= ~NEED_R;
28067c478bd9Sstevel@tonic-gate 					break;
28077c478bd9Sstevel@tonic-gate 				}
28087c478bd9Sstevel@tonic-gate 				if (qfver > 0)
28097c478bd9Sstevel@tonic-gate 				{
28107c478bd9Sstevel@tonic-gate 					p = strchr(&lbuf[1], ':');
28117c478bd9Sstevel@tonic-gate 					if (p == NULL)
28127c478bd9Sstevel@tonic-gate 						p = &lbuf[1];
28137c478bd9Sstevel@tonic-gate 					else
28147c478bd9Sstevel@tonic-gate 						++p; /* skip over ':' */
28157c478bd9Sstevel@tonic-gate 				}
28167c478bd9Sstevel@tonic-gate 				else
28177c478bd9Sstevel@tonic-gate 					p = &lbuf[1];
28187c478bd9Sstevel@tonic-gate 				check = QueueLimitRecipient;
28197c478bd9Sstevel@tonic-gate 				while (check != NULL)
28207c478bd9Sstevel@tonic-gate 				{
28217c478bd9Sstevel@tonic-gate 					if (strcontainedin(true,
28227c478bd9Sstevel@tonic-gate 							   check->queue_match,
28237c478bd9Sstevel@tonic-gate 							   p) !=
28247c478bd9Sstevel@tonic-gate 					    check->queue_negate)
28257c478bd9Sstevel@tonic-gate 						break;
28267c478bd9Sstevel@tonic-gate 					else
28277c478bd9Sstevel@tonic-gate 						check = check->queue_next;
28287c478bd9Sstevel@tonic-gate 				}
28297c478bd9Sstevel@tonic-gate 				if (check != NULL)
28307c478bd9Sstevel@tonic-gate 					i &= ~NEED_R;
28317c478bd9Sstevel@tonic-gate 				break;
28327c478bd9Sstevel@tonic-gate 
28337c478bd9Sstevel@tonic-gate 			  case 'S':
28347c478bd9Sstevel@tonic-gate 				check = QueueLimitSender;
28357c478bd9Sstevel@tonic-gate 				while (check != NULL)
28367c478bd9Sstevel@tonic-gate 				{
28377c478bd9Sstevel@tonic-gate 					if (strcontainedin(true,
28387c478bd9Sstevel@tonic-gate 							   check->queue_match,
28397c478bd9Sstevel@tonic-gate 							   &lbuf[1]) !=
28407c478bd9Sstevel@tonic-gate 					    check->queue_negate)
28417c478bd9Sstevel@tonic-gate 						break;
28427c478bd9Sstevel@tonic-gate 					else
28437c478bd9Sstevel@tonic-gate 						check = check->queue_next;
28447c478bd9Sstevel@tonic-gate 				}
28457c478bd9Sstevel@tonic-gate 				if (check != NULL)
28467c478bd9Sstevel@tonic-gate 					i &= ~NEED_S;
28477c478bd9Sstevel@tonic-gate 				break;
28487c478bd9Sstevel@tonic-gate 
28497c478bd9Sstevel@tonic-gate 			  case 'K':
28507c478bd9Sstevel@tonic-gate 				age = curtime() - (time_t) atol(&lbuf[1]);
28517c478bd9Sstevel@tonic-gate 				if (age >= 0 && MinQueueAge > 0 &&
28527c478bd9Sstevel@tonic-gate 				    age < MinQueueAge)
28537c478bd9Sstevel@tonic-gate 					w->w_tooyoung = true;
28547c478bd9Sstevel@tonic-gate 				break;
28557c478bd9Sstevel@tonic-gate 
28567c478bd9Sstevel@tonic-gate 			  case 'N':
28577c478bd9Sstevel@tonic-gate 				if (atol(&lbuf[1]) == 0)
28587c478bd9Sstevel@tonic-gate 					w->w_tooyoung = false;
28597c478bd9Sstevel@tonic-gate 				break;
28607c478bd9Sstevel@tonic-gate 			}
28617c478bd9Sstevel@tonic-gate 		}
28627c478bd9Sstevel@tonic-gate 		if (cf != NULL)
28637c478bd9Sstevel@tonic-gate 			(void) sm_io_close(cf, SM_TIME_DEFAULT);
28647c478bd9Sstevel@tonic-gate 
2865445f2479Sjbeck 		if ((!doall && (shouldqueue(w->w_pri, w->w_ctime) ||
2866445f2479Sjbeck 		    w->w_tooyoung)) ||
28677c478bd9Sstevel@tonic-gate 		    bitset(HAS_QUARANTINE, i) ||
28687c478bd9Sstevel@tonic-gate 		    bitset(NEED_QUARANTINE, i) ||
28697c478bd9Sstevel@tonic-gate 		    bitset(NEED_R|NEED_S, i))
28707c478bd9Sstevel@tonic-gate 		{
28717c478bd9Sstevel@tonic-gate 			/* don't even bother sorting this job in */
28727c478bd9Sstevel@tonic-gate 			if (tTd(41, 49))
28737c478bd9Sstevel@tonic-gate 				sm_dprintf("skipping %s (%x)\n", w->w_name, i);
28747c478bd9Sstevel@tonic-gate 			sm_free(w->w_name); /* XXX */
28757c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
28767c478bd9Sstevel@tonic-gate 				sm_free(w->w_host); /* XXX */
28777c478bd9Sstevel@tonic-gate 			wn--;
28787c478bd9Sstevel@tonic-gate 		}
28797c478bd9Sstevel@tonic-gate 		else
28807c478bd9Sstevel@tonic-gate 			++num_ent;
28817c478bd9Sstevel@tonic-gate 	}
28827c478bd9Sstevel@tonic-gate 	(void) closedir(f);
28837c478bd9Sstevel@tonic-gate 	wn++;
28847c478bd9Sstevel@tonic-gate 
28857c478bd9Sstevel@tonic-gate 	i = wn - WorkListCount;
28867c478bd9Sstevel@tonic-gate 	WorkListCount += SM_MIN(num_ent, WorkListSize);
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate 	if (more != NULL)
28897c478bd9Sstevel@tonic-gate 		*more = WorkListCount < wn;
28907c478bd9Sstevel@tonic-gate 
28917c478bd9Sstevel@tonic-gate 	if (full != NULL)
28927c478bd9Sstevel@tonic-gate 		*full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
28937c478bd9Sstevel@tonic-gate 			(WorkList == NULL && wn > 0);
28947c478bd9Sstevel@tonic-gate 
28957c478bd9Sstevel@tonic-gate 	return i;
28967c478bd9Sstevel@tonic-gate }
28977c478bd9Sstevel@tonic-gate /*
28987c478bd9Sstevel@tonic-gate **  SORTQ -- sort the work list
28997c478bd9Sstevel@tonic-gate **
29007c478bd9Sstevel@tonic-gate **	First the old WorkQ is cleared away. Then the WorkList is sorted
29017c478bd9Sstevel@tonic-gate **	for all items so that important (higher sorting value) items are not
29027c478bd9Sstevel@tonic-gate **	trunctated off. Then the most important items are moved from
29037c478bd9Sstevel@tonic-gate **	WorkList to WorkQ. The lower count of 'max' or MaxListCount items
29047c478bd9Sstevel@tonic-gate **	are moved.
29057c478bd9Sstevel@tonic-gate **
29067c478bd9Sstevel@tonic-gate **	Parameters:
29077c478bd9Sstevel@tonic-gate **		max -- maximum number of items to be placed in WorkQ
29087c478bd9Sstevel@tonic-gate **
29097c478bd9Sstevel@tonic-gate **	Returns:
29107c478bd9Sstevel@tonic-gate **		the number of items in WorkQ
29117c478bd9Sstevel@tonic-gate **
29127c478bd9Sstevel@tonic-gate **	Side Effects:
29137c478bd9Sstevel@tonic-gate **		WorkQ gets released and filled with new work. WorkList
29147c478bd9Sstevel@tonic-gate **		gets released. Work items get sorted in order.
29157c478bd9Sstevel@tonic-gate */
29167c478bd9Sstevel@tonic-gate 
29177c478bd9Sstevel@tonic-gate static int
29187c478bd9Sstevel@tonic-gate sortq(max)
29197c478bd9Sstevel@tonic-gate 	int max;
29207c478bd9Sstevel@tonic-gate {
29217c478bd9Sstevel@tonic-gate 	register int i;			/* local counter */
29227c478bd9Sstevel@tonic-gate 	register WORK *w;		/* tmp item pointer */
29237c478bd9Sstevel@tonic-gate 	int wc = WorkListCount;		/* trim size for WorkQ */
29247c478bd9Sstevel@tonic-gate 
29257c478bd9Sstevel@tonic-gate 	if (WorkQ != NULL)
29267c478bd9Sstevel@tonic-gate 	{
29277c478bd9Sstevel@tonic-gate 		WORK *nw;
29287c478bd9Sstevel@tonic-gate 
29297c478bd9Sstevel@tonic-gate 		/* Clear out old WorkQ. */
29307c478bd9Sstevel@tonic-gate 		for (w = WorkQ; w != NULL; w = nw)
29317c478bd9Sstevel@tonic-gate 		{
29327c478bd9Sstevel@tonic-gate 			nw = w->w_next;
29337c478bd9Sstevel@tonic-gate 			sm_free(w->w_name); /* XXX */
29347c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
29357c478bd9Sstevel@tonic-gate 				sm_free(w->w_host); /* XXX */
29367c478bd9Sstevel@tonic-gate 			sm_free((char *) w); /* XXX */
29377c478bd9Sstevel@tonic-gate 		}
29387c478bd9Sstevel@tonic-gate 		WorkQ = NULL;
29397c478bd9Sstevel@tonic-gate 	}
29407c478bd9Sstevel@tonic-gate 
29417c478bd9Sstevel@tonic-gate 	if (WorkList == NULL || wc <= 0)
29427c478bd9Sstevel@tonic-gate 		return 0;
29437c478bd9Sstevel@tonic-gate 
29447c478bd9Sstevel@tonic-gate 	/*
29457c478bd9Sstevel@tonic-gate 	**  The sort now takes place using all of the items in WorkList.
29467c478bd9Sstevel@tonic-gate 	**  The list gets trimmed to the most important items after the sort.
29477c478bd9Sstevel@tonic-gate 	**  If the trim were to happen before the sort then one or more
29487c478bd9Sstevel@tonic-gate 	**  important items might get truncated off -- not what we want.
29497c478bd9Sstevel@tonic-gate 	*/
29507c478bd9Sstevel@tonic-gate 
29517c478bd9Sstevel@tonic-gate 	if (QueueSortOrder == QSO_BYHOST)
29527c478bd9Sstevel@tonic-gate 	{
29537c478bd9Sstevel@tonic-gate 		/*
29547c478bd9Sstevel@tonic-gate 		**  Sort the work directory for the first time,
29557c478bd9Sstevel@tonic-gate 		**  based on host name, lock status, and priority.
29567c478bd9Sstevel@tonic-gate 		*/
29577c478bd9Sstevel@tonic-gate 
2958*058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf1);
29597c478bd9Sstevel@tonic-gate 
29607c478bd9Sstevel@tonic-gate 		/*
29617c478bd9Sstevel@tonic-gate 		**  If one message to host is locked, "lock" all messages
29627c478bd9Sstevel@tonic-gate 		**  to that host.
29637c478bd9Sstevel@tonic-gate 		*/
29647c478bd9Sstevel@tonic-gate 
29657c478bd9Sstevel@tonic-gate 		i = 0;
29667c478bd9Sstevel@tonic-gate 		while (i < wc)
29677c478bd9Sstevel@tonic-gate 		{
29687c478bd9Sstevel@tonic-gate 			if (!WorkList[i].w_lock)
29697c478bd9Sstevel@tonic-gate 			{
29707c478bd9Sstevel@tonic-gate 				i++;
29717c478bd9Sstevel@tonic-gate 				continue;
29727c478bd9Sstevel@tonic-gate 			}
29737c478bd9Sstevel@tonic-gate 			w = &WorkList[i];
29747c478bd9Sstevel@tonic-gate 			while (++i < wc)
29757c478bd9Sstevel@tonic-gate 			{
29767c478bd9Sstevel@tonic-gate 				if (WorkList[i].w_host == NULL &&
29777c478bd9Sstevel@tonic-gate 				    w->w_host == NULL)
29787c478bd9Sstevel@tonic-gate 					WorkList[i].w_lock = true;
29797c478bd9Sstevel@tonic-gate 				else if (WorkList[i].w_host != NULL &&
29807c478bd9Sstevel@tonic-gate 					 w->w_host != NULL &&
29817c478bd9Sstevel@tonic-gate 					 sm_strcasecmp(WorkList[i].w_host,
29827c478bd9Sstevel@tonic-gate 						       w->w_host) == 0)
29837c478bd9Sstevel@tonic-gate 					WorkList[i].w_lock = true;
29847c478bd9Sstevel@tonic-gate 				else
29857c478bd9Sstevel@tonic-gate 					break;
29867c478bd9Sstevel@tonic-gate 			}
29877c478bd9Sstevel@tonic-gate 		}
29887c478bd9Sstevel@tonic-gate 
29897c478bd9Sstevel@tonic-gate 		/*
29907c478bd9Sstevel@tonic-gate 		**  Sort the work directory for the second time,
29917c478bd9Sstevel@tonic-gate 		**  based on lock status, host name, and priority.
29927c478bd9Sstevel@tonic-gate 		*/
29937c478bd9Sstevel@tonic-gate 
2994*058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf2);
29957c478bd9Sstevel@tonic-gate 	}
29967c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYTIME)
29977c478bd9Sstevel@tonic-gate 	{
29987c478bd9Sstevel@tonic-gate 		/*
29997c478bd9Sstevel@tonic-gate 		**  Simple sort based on submission time only.
30007c478bd9Sstevel@tonic-gate 		*/
30017c478bd9Sstevel@tonic-gate 
3002*058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf3);
30037c478bd9Sstevel@tonic-gate 	}
30047c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYFILENAME)
30057c478bd9Sstevel@tonic-gate 	{
30067c478bd9Sstevel@tonic-gate 		/*
30077c478bd9Sstevel@tonic-gate 		**  Sort based on queue filename.
30087c478bd9Sstevel@tonic-gate 		*/
30097c478bd9Sstevel@tonic-gate 
3010*058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf4);
30117c478bd9Sstevel@tonic-gate 	}
30127c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_RANDOM)
30137c478bd9Sstevel@tonic-gate 	{
30147c478bd9Sstevel@tonic-gate 		/*
30157c478bd9Sstevel@tonic-gate 		**  Sort randomly.  To avoid problems with an instable sort,
30167c478bd9Sstevel@tonic-gate 		**  use a random index into the queue file name to start
30177c478bd9Sstevel@tonic-gate 		**  comparison.
30187c478bd9Sstevel@tonic-gate 		*/
30197c478bd9Sstevel@tonic-gate 
30207c478bd9Sstevel@tonic-gate 		randi = get_rand_mod(MAXQFNAME);
30217c478bd9Sstevel@tonic-gate 		if (randi < 2)
30227c478bd9Sstevel@tonic-gate 			randi = 3;
3023*058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf5);
30247c478bd9Sstevel@tonic-gate 	}
30257c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYMODTIME)
30267c478bd9Sstevel@tonic-gate 	{
30277c478bd9Sstevel@tonic-gate 		/*
30287c478bd9Sstevel@tonic-gate 		**  Simple sort based on modification time of queue file.
30297c478bd9Sstevel@tonic-gate 		**  This puts the oldest items first.
30307c478bd9Sstevel@tonic-gate 		*/
30317c478bd9Sstevel@tonic-gate 
3032*058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf6);
30337c478bd9Sstevel@tonic-gate 	}
30347c478bd9Sstevel@tonic-gate #if _FFR_RHS
30357c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYSHUFFLE)
30367c478bd9Sstevel@tonic-gate 	{
30377c478bd9Sstevel@tonic-gate 		/*
30387c478bd9Sstevel@tonic-gate 		**  Simple sort based on shuffled host name.
30397c478bd9Sstevel@tonic-gate 		*/
30407c478bd9Sstevel@tonic-gate 
30417c478bd9Sstevel@tonic-gate 		init_shuffle_alphabet();
3042*058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf7);
30437c478bd9Sstevel@tonic-gate 	}
30447c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
30457c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYPRIORITY)
30467c478bd9Sstevel@tonic-gate 	{
30477c478bd9Sstevel@tonic-gate 		/*
30487c478bd9Sstevel@tonic-gate 		**  Simple sort based on queue priority only.
30497c478bd9Sstevel@tonic-gate 		*/
30507c478bd9Sstevel@tonic-gate 
3051*058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf0);
30527c478bd9Sstevel@tonic-gate 	}
30537c478bd9Sstevel@tonic-gate 	/* else don't sort at all */
30547c478bd9Sstevel@tonic-gate 
305549218d4fSjbeck 	/* Check if the per queue group item limit will be exceeded */
305649218d4fSjbeck 	if (wc > max && max > 0)
305749218d4fSjbeck 		wc = max;
305849218d4fSjbeck 
30597c478bd9Sstevel@tonic-gate 	/*
30607c478bd9Sstevel@tonic-gate 	**  Convert the work list into canonical form.
30617c478bd9Sstevel@tonic-gate 	**	Should be turning it into a list of envelopes here perhaps.
30627c478bd9Sstevel@tonic-gate 	**  Only take the most important items up to the per queue group
30637c478bd9Sstevel@tonic-gate 	**  maximum.
30647c478bd9Sstevel@tonic-gate 	*/
30657c478bd9Sstevel@tonic-gate 
30667c478bd9Sstevel@tonic-gate 	for (i = wc; --i >= 0; )
30677c478bd9Sstevel@tonic-gate 	{
3068*058561cbSjbeck 		w = (WORK *) xalloc(sizeof(*w));
30697c478bd9Sstevel@tonic-gate 		w->w_qgrp = WorkList[i].w_qgrp;
30707c478bd9Sstevel@tonic-gate 		w->w_qdir = WorkList[i].w_qdir;
30717c478bd9Sstevel@tonic-gate 		w->w_name = WorkList[i].w_name;
30727c478bd9Sstevel@tonic-gate 		w->w_host = WorkList[i].w_host;
30737c478bd9Sstevel@tonic-gate 		w->w_lock = WorkList[i].w_lock;
30747c478bd9Sstevel@tonic-gate 		w->w_tooyoung = WorkList[i].w_tooyoung;
30757c478bd9Sstevel@tonic-gate 		w->w_pri = WorkList[i].w_pri;
30767c478bd9Sstevel@tonic-gate 		w->w_ctime = WorkList[i].w_ctime;
30777c478bd9Sstevel@tonic-gate 		w->w_mtime = WorkList[i].w_mtime;
30787c478bd9Sstevel@tonic-gate 		w->w_next = WorkQ;
30797c478bd9Sstevel@tonic-gate 		WorkQ = w;
30807c478bd9Sstevel@tonic-gate 	}
30817c478bd9Sstevel@tonic-gate 
30827c478bd9Sstevel@tonic-gate 	/* free the rest of the list */
30837c478bd9Sstevel@tonic-gate 	for (i = WorkListCount; --i >= wc; )
30847c478bd9Sstevel@tonic-gate 	{
30857c478bd9Sstevel@tonic-gate 		sm_free(WorkList[i].w_name);
30867c478bd9Sstevel@tonic-gate 		if (WorkList[i].w_host != NULL)
30877c478bd9Sstevel@tonic-gate 			sm_free(WorkList[i].w_host);
30887c478bd9Sstevel@tonic-gate 	}
30897c478bd9Sstevel@tonic-gate 
30907c478bd9Sstevel@tonic-gate 	if (WorkList != NULL)
30917c478bd9Sstevel@tonic-gate 		sm_free(WorkList); /* XXX */
30927c478bd9Sstevel@tonic-gate 	WorkList = NULL;
30937c478bd9Sstevel@tonic-gate 	WorkListSize = 0;
30947c478bd9Sstevel@tonic-gate 	WorkListCount = 0;
30957c478bd9Sstevel@tonic-gate 
30967c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
30977c478bd9Sstevel@tonic-gate 	{
30987c478bd9Sstevel@tonic-gate 		for (w = WorkQ; w != NULL; w = w->w_next)
30997c478bd9Sstevel@tonic-gate 		{
31007c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
31017c478bd9Sstevel@tonic-gate 				sm_dprintf("%22s: pri=%ld %s\n",
31027c478bd9Sstevel@tonic-gate 					w->w_name, w->w_pri, w->w_host);
31037c478bd9Sstevel@tonic-gate 			else
31047c478bd9Sstevel@tonic-gate 				sm_dprintf("%32s: pri=%ld\n",
31057c478bd9Sstevel@tonic-gate 					w->w_name, w->w_pri);
31067c478bd9Sstevel@tonic-gate 		}
31077c478bd9Sstevel@tonic-gate 	}
31087c478bd9Sstevel@tonic-gate 
31097c478bd9Sstevel@tonic-gate 	return wc; /* return number of WorkQ items */
31107c478bd9Sstevel@tonic-gate }
31117c478bd9Sstevel@tonic-gate /*
31127c478bd9Sstevel@tonic-gate **  GROW_WLIST -- make the work list larger
31137c478bd9Sstevel@tonic-gate **
31147c478bd9Sstevel@tonic-gate **	Parameters:
31157c478bd9Sstevel@tonic-gate **		qgrp -- the index for the queue group.
31167c478bd9Sstevel@tonic-gate **		qdir -- the index for the queue directory.
31177c478bd9Sstevel@tonic-gate **
31187c478bd9Sstevel@tonic-gate **	Returns:
31197c478bd9Sstevel@tonic-gate **		none.
31207c478bd9Sstevel@tonic-gate **
31217c478bd9Sstevel@tonic-gate **	Side Effects:
31227c478bd9Sstevel@tonic-gate **		Adds another QUEUESEGSIZE entries to WorkList if possible.
31237c478bd9Sstevel@tonic-gate **		It can fail if there isn't enough memory, so WorkListSize
31247c478bd9Sstevel@tonic-gate **		should be checked again upon return.
31257c478bd9Sstevel@tonic-gate */
31267c478bd9Sstevel@tonic-gate 
31277c478bd9Sstevel@tonic-gate static void
31287c478bd9Sstevel@tonic-gate grow_wlist(qgrp, qdir)
31297c478bd9Sstevel@tonic-gate 	int qgrp;
31307c478bd9Sstevel@tonic-gate 	int qdir;
31317c478bd9Sstevel@tonic-gate {
31327c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
31337c478bd9Sstevel@tonic-gate 		sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
31347c478bd9Sstevel@tonic-gate 	if (WorkList == NULL)
31357c478bd9Sstevel@tonic-gate 	{
3136*058561cbSjbeck 		WorkList = (WORK *) xalloc((sizeof(*WorkList)) *
31377c478bd9Sstevel@tonic-gate 					   (QUEUESEGSIZE + 1));
31387c478bd9Sstevel@tonic-gate 		WorkListSize = QUEUESEGSIZE;
31397c478bd9Sstevel@tonic-gate 	}
31407c478bd9Sstevel@tonic-gate 	else
31417c478bd9Sstevel@tonic-gate 	{
31427c478bd9Sstevel@tonic-gate 		int newsize = WorkListSize + QUEUESEGSIZE;
31437c478bd9Sstevel@tonic-gate 		WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
31447c478bd9Sstevel@tonic-gate 					  (unsigned) sizeof(WORK) * (newsize + 1));
31457c478bd9Sstevel@tonic-gate 
31467c478bd9Sstevel@tonic-gate 		if (newlist != NULL)
31477c478bd9Sstevel@tonic-gate 		{
31487c478bd9Sstevel@tonic-gate 			WorkListSize = newsize;
31497c478bd9Sstevel@tonic-gate 			WorkList = newlist;
31507c478bd9Sstevel@tonic-gate 			if (LogLevel > 1)
31517c478bd9Sstevel@tonic-gate 			{
31527c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
31537c478bd9Sstevel@tonic-gate 					  "grew WorkList for %s to %d",
31547c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
31557c478bd9Sstevel@tonic-gate 					  WorkListSize);
31567c478bd9Sstevel@tonic-gate 			}
31577c478bd9Sstevel@tonic-gate 		}
31587c478bd9Sstevel@tonic-gate 		else if (LogLevel > 0)
31597c478bd9Sstevel@tonic-gate 		{
31607c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, NOQID,
31617c478bd9Sstevel@tonic-gate 				  "FAILED to grow WorkList for %s to %d",
31627c478bd9Sstevel@tonic-gate 				  qid_printqueue(qgrp, qdir), newsize);
31637c478bd9Sstevel@tonic-gate 		}
31647c478bd9Sstevel@tonic-gate 	}
31657c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
31667c478bd9Sstevel@tonic-gate 		sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
31677c478bd9Sstevel@tonic-gate }
31687c478bd9Sstevel@tonic-gate /*
31697c478bd9Sstevel@tonic-gate **  WORKCMPF0 -- simple priority-only compare function.
31707c478bd9Sstevel@tonic-gate **
31717c478bd9Sstevel@tonic-gate **	Parameters:
31727c478bd9Sstevel@tonic-gate **		a -- the first argument.
31737c478bd9Sstevel@tonic-gate **		b -- the second argument.
31747c478bd9Sstevel@tonic-gate **
31757c478bd9Sstevel@tonic-gate **	Returns:
31767c478bd9Sstevel@tonic-gate **		-1 if a < b
31777c478bd9Sstevel@tonic-gate **		 0 if a == b
31787c478bd9Sstevel@tonic-gate **		+1 if a > b
31797c478bd9Sstevel@tonic-gate **
31807c478bd9Sstevel@tonic-gate */
31817c478bd9Sstevel@tonic-gate 
31827c478bd9Sstevel@tonic-gate static int
31837c478bd9Sstevel@tonic-gate workcmpf0(a, b)
31847c478bd9Sstevel@tonic-gate 	register WORK *a;
31857c478bd9Sstevel@tonic-gate 	register WORK *b;
31867c478bd9Sstevel@tonic-gate {
31877c478bd9Sstevel@tonic-gate 	long pa = a->w_pri;
31887c478bd9Sstevel@tonic-gate 	long pb = b->w_pri;
31897c478bd9Sstevel@tonic-gate 
31907c478bd9Sstevel@tonic-gate 	if (pa == pb)
31917c478bd9Sstevel@tonic-gate 		return 0;
31927c478bd9Sstevel@tonic-gate 	else if (pa > pb)
31937c478bd9Sstevel@tonic-gate 		return 1;
31947c478bd9Sstevel@tonic-gate 	else
31957c478bd9Sstevel@tonic-gate 		return -1;
31967c478bd9Sstevel@tonic-gate }
31977c478bd9Sstevel@tonic-gate /*
31987c478bd9Sstevel@tonic-gate **  WORKCMPF1 -- first compare function for ordering work based on host name.
31997c478bd9Sstevel@tonic-gate **
32007c478bd9Sstevel@tonic-gate **	Sorts on host name, lock status, and priority in that order.
32017c478bd9Sstevel@tonic-gate **
32027c478bd9Sstevel@tonic-gate **	Parameters:
32037c478bd9Sstevel@tonic-gate **		a -- the first argument.
32047c478bd9Sstevel@tonic-gate **		b -- the second argument.
32057c478bd9Sstevel@tonic-gate **
32067c478bd9Sstevel@tonic-gate **	Returns:
32077c478bd9Sstevel@tonic-gate **		<0 if a < b
32087c478bd9Sstevel@tonic-gate **		 0 if a == b
32097c478bd9Sstevel@tonic-gate **		>0 if a > b
32107c478bd9Sstevel@tonic-gate **
32117c478bd9Sstevel@tonic-gate */
32127c478bd9Sstevel@tonic-gate 
32137c478bd9Sstevel@tonic-gate static int
32147c478bd9Sstevel@tonic-gate workcmpf1(a, b)
32157c478bd9Sstevel@tonic-gate 	register WORK *a;
32167c478bd9Sstevel@tonic-gate 	register WORK *b;
32177c478bd9Sstevel@tonic-gate {
32187c478bd9Sstevel@tonic-gate 	int i;
32197c478bd9Sstevel@tonic-gate 
32207c478bd9Sstevel@tonic-gate 	/* host name */
32217c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
32227c478bd9Sstevel@tonic-gate 		return 1;
32237c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
32247c478bd9Sstevel@tonic-gate 		return -1;
32257c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
32267c478bd9Sstevel@tonic-gate 	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
32277c478bd9Sstevel@tonic-gate 		return i;
32287c478bd9Sstevel@tonic-gate 
32297c478bd9Sstevel@tonic-gate 	/* lock status */
32307c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
32317c478bd9Sstevel@tonic-gate 		return b->w_lock - a->w_lock;
32327c478bd9Sstevel@tonic-gate 
32337c478bd9Sstevel@tonic-gate 	/* job priority */
32347c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
32357c478bd9Sstevel@tonic-gate }
32367c478bd9Sstevel@tonic-gate /*
32377c478bd9Sstevel@tonic-gate **  WORKCMPF2 -- second compare function for ordering work based on host name.
32387c478bd9Sstevel@tonic-gate **
32397c478bd9Sstevel@tonic-gate **	Sorts on lock status, host name, and priority in that order.
32407c478bd9Sstevel@tonic-gate **
32417c478bd9Sstevel@tonic-gate **	Parameters:
32427c478bd9Sstevel@tonic-gate **		a -- the first argument.
32437c478bd9Sstevel@tonic-gate **		b -- the second argument.
32447c478bd9Sstevel@tonic-gate **
32457c478bd9Sstevel@tonic-gate **	Returns:
32467c478bd9Sstevel@tonic-gate **		<0 if a < b
32477c478bd9Sstevel@tonic-gate **		 0 if a == b
32487c478bd9Sstevel@tonic-gate **		>0 if a > b
32497c478bd9Sstevel@tonic-gate **
32507c478bd9Sstevel@tonic-gate */
32517c478bd9Sstevel@tonic-gate 
32527c478bd9Sstevel@tonic-gate static int
32537c478bd9Sstevel@tonic-gate workcmpf2(a, b)
32547c478bd9Sstevel@tonic-gate 	register WORK *a;
32557c478bd9Sstevel@tonic-gate 	register WORK *b;
32567c478bd9Sstevel@tonic-gate {
32577c478bd9Sstevel@tonic-gate 	int i;
32587c478bd9Sstevel@tonic-gate 
32597c478bd9Sstevel@tonic-gate 	/* lock status */
32607c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
32617c478bd9Sstevel@tonic-gate 		return a->w_lock - b->w_lock;
32627c478bd9Sstevel@tonic-gate 
32637c478bd9Sstevel@tonic-gate 	/* host name */
32647c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
32657c478bd9Sstevel@tonic-gate 		return 1;
32667c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
32677c478bd9Sstevel@tonic-gate 		return -1;
32687c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
32697c478bd9Sstevel@tonic-gate 	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
32707c478bd9Sstevel@tonic-gate 		return i;
32717c478bd9Sstevel@tonic-gate 
32727c478bd9Sstevel@tonic-gate 	/* job priority */
32737c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
32747c478bd9Sstevel@tonic-gate }
32757c478bd9Sstevel@tonic-gate /*
32767c478bd9Sstevel@tonic-gate **  WORKCMPF3 -- simple submission-time-only compare function.
32777c478bd9Sstevel@tonic-gate **
32787c478bd9Sstevel@tonic-gate **	Parameters:
32797c478bd9Sstevel@tonic-gate **		a -- the first argument.
32807c478bd9Sstevel@tonic-gate **		b -- the second argument.
32817c478bd9Sstevel@tonic-gate **
32827c478bd9Sstevel@tonic-gate **	Returns:
32837c478bd9Sstevel@tonic-gate **		-1 if a < b
32847c478bd9Sstevel@tonic-gate **		 0 if a == b
32857c478bd9Sstevel@tonic-gate **		+1 if a > b
32867c478bd9Sstevel@tonic-gate **
32877c478bd9Sstevel@tonic-gate */
32887c478bd9Sstevel@tonic-gate 
32897c478bd9Sstevel@tonic-gate static int
32907c478bd9Sstevel@tonic-gate workcmpf3(a, b)
32917c478bd9Sstevel@tonic-gate 	register WORK *a;
32927c478bd9Sstevel@tonic-gate 	register WORK *b;
32937c478bd9Sstevel@tonic-gate {
32947c478bd9Sstevel@tonic-gate 	if (a->w_ctime > b->w_ctime)
32957c478bd9Sstevel@tonic-gate 		return 1;
32967c478bd9Sstevel@tonic-gate 	else if (a->w_ctime < b->w_ctime)
32977c478bd9Sstevel@tonic-gate 		return -1;
32987c478bd9Sstevel@tonic-gate 	else
32997c478bd9Sstevel@tonic-gate 		return 0;
33007c478bd9Sstevel@tonic-gate }
33017c478bd9Sstevel@tonic-gate /*
33027c478bd9Sstevel@tonic-gate **  WORKCMPF4 -- compare based on file name
33037c478bd9Sstevel@tonic-gate **
33047c478bd9Sstevel@tonic-gate **	Parameters:
33057c478bd9Sstevel@tonic-gate **		a -- the first argument.
33067c478bd9Sstevel@tonic-gate **		b -- the second argument.
33077c478bd9Sstevel@tonic-gate **
33087c478bd9Sstevel@tonic-gate **	Returns:
33097c478bd9Sstevel@tonic-gate **		-1 if a < b
33107c478bd9Sstevel@tonic-gate **		 0 if a == b
33117c478bd9Sstevel@tonic-gate **		+1 if a > b
33127c478bd9Sstevel@tonic-gate **
33137c478bd9Sstevel@tonic-gate */
33147c478bd9Sstevel@tonic-gate 
33157c478bd9Sstevel@tonic-gate static int
33167c478bd9Sstevel@tonic-gate workcmpf4(a, b)
33177c478bd9Sstevel@tonic-gate 	register WORK *a;
33187c478bd9Sstevel@tonic-gate 	register WORK *b;
33197c478bd9Sstevel@tonic-gate {
33207c478bd9Sstevel@tonic-gate 	return strcmp(a->w_name, b->w_name);
33217c478bd9Sstevel@tonic-gate }
33227c478bd9Sstevel@tonic-gate /*
33237c478bd9Sstevel@tonic-gate **  WORKCMPF5 -- compare based on assigned random number
33247c478bd9Sstevel@tonic-gate **
33257c478bd9Sstevel@tonic-gate **	Parameters:
33267c478bd9Sstevel@tonic-gate **		a -- the first argument (ignored).
33277c478bd9Sstevel@tonic-gate **		b -- the second argument (ignored).
33287c478bd9Sstevel@tonic-gate **
33297c478bd9Sstevel@tonic-gate **	Returns:
33307c478bd9Sstevel@tonic-gate **		randomly 1/-1
33317c478bd9Sstevel@tonic-gate */
33327c478bd9Sstevel@tonic-gate 
33337c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
33347c478bd9Sstevel@tonic-gate static int
33357c478bd9Sstevel@tonic-gate workcmpf5(a, b)
33367c478bd9Sstevel@tonic-gate 	register WORK *a;
33377c478bd9Sstevel@tonic-gate 	register WORK *b;
33387c478bd9Sstevel@tonic-gate {
33397c478bd9Sstevel@tonic-gate 	if (strlen(a->w_name) < randi || strlen(b->w_name) < randi)
33407c478bd9Sstevel@tonic-gate 		return -1;
33417c478bd9Sstevel@tonic-gate 	return a->w_name[randi] - b->w_name[randi];
33427c478bd9Sstevel@tonic-gate }
33437c478bd9Sstevel@tonic-gate /*
33447c478bd9Sstevel@tonic-gate **  WORKCMPF6 -- simple modification-time-only compare function.
33457c478bd9Sstevel@tonic-gate **
33467c478bd9Sstevel@tonic-gate **	Parameters:
33477c478bd9Sstevel@tonic-gate **		a -- the first argument.
33487c478bd9Sstevel@tonic-gate **		b -- the second argument.
33497c478bd9Sstevel@tonic-gate **
33507c478bd9Sstevel@tonic-gate **	Returns:
33517c478bd9Sstevel@tonic-gate **		-1 if a < b
33527c478bd9Sstevel@tonic-gate **		 0 if a == b
33537c478bd9Sstevel@tonic-gate **		+1 if a > b
33547c478bd9Sstevel@tonic-gate **
33557c478bd9Sstevel@tonic-gate */
33567c478bd9Sstevel@tonic-gate 
33577c478bd9Sstevel@tonic-gate static int
33587c478bd9Sstevel@tonic-gate workcmpf6(a, b)
33597c478bd9Sstevel@tonic-gate 	register WORK *a;
33607c478bd9Sstevel@tonic-gate 	register WORK *b;
33617c478bd9Sstevel@tonic-gate {
33627c478bd9Sstevel@tonic-gate 	if (a->w_mtime > b->w_mtime)
33637c478bd9Sstevel@tonic-gate 		return 1;
33647c478bd9Sstevel@tonic-gate 	else if (a->w_mtime < b->w_mtime)
33657c478bd9Sstevel@tonic-gate 		return -1;
33667c478bd9Sstevel@tonic-gate 	else
33677c478bd9Sstevel@tonic-gate 		return 0;
33687c478bd9Sstevel@tonic-gate }
33697c478bd9Sstevel@tonic-gate #if _FFR_RHS
33707c478bd9Sstevel@tonic-gate /*
33717c478bd9Sstevel@tonic-gate **  WORKCMPF7 -- compare function for ordering work based on shuffled host name.
33727c478bd9Sstevel@tonic-gate **
33737c478bd9Sstevel@tonic-gate **	Sorts on lock status, host name, and priority in that order.
33747c478bd9Sstevel@tonic-gate **
33757c478bd9Sstevel@tonic-gate **	Parameters:
33767c478bd9Sstevel@tonic-gate **		a -- the first argument.
33777c478bd9Sstevel@tonic-gate **		b -- the second argument.
33787c478bd9Sstevel@tonic-gate **
33797c478bd9Sstevel@tonic-gate **	Returns:
33807c478bd9Sstevel@tonic-gate **		<0 if a < b
33817c478bd9Sstevel@tonic-gate **		 0 if a == b
33827c478bd9Sstevel@tonic-gate **		>0 if a > b
33837c478bd9Sstevel@tonic-gate **
33847c478bd9Sstevel@tonic-gate */
33857c478bd9Sstevel@tonic-gate 
33867c478bd9Sstevel@tonic-gate static int
33877c478bd9Sstevel@tonic-gate workcmpf7(a, b)
33887c478bd9Sstevel@tonic-gate 	register WORK *a;
33897c478bd9Sstevel@tonic-gate 	register WORK *b;
33907c478bd9Sstevel@tonic-gate {
33917c478bd9Sstevel@tonic-gate 	int i;
33927c478bd9Sstevel@tonic-gate 
33937c478bd9Sstevel@tonic-gate 	/* lock status */
33947c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
33957c478bd9Sstevel@tonic-gate 		return a->w_lock - b->w_lock;
33967c478bd9Sstevel@tonic-gate 
33977c478bd9Sstevel@tonic-gate 	/* host name */
33987c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
33997c478bd9Sstevel@tonic-gate 		return 1;
34007c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
34017c478bd9Sstevel@tonic-gate 		return -1;
34027c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
34037c478bd9Sstevel@tonic-gate 	    (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
34047c478bd9Sstevel@tonic-gate 		return i;
34057c478bd9Sstevel@tonic-gate 
34067c478bd9Sstevel@tonic-gate 	/* job priority */
34077c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
34087c478bd9Sstevel@tonic-gate }
34097c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
34107c478bd9Sstevel@tonic-gate /*
34117c478bd9Sstevel@tonic-gate **  STRREV -- reverse string
34127c478bd9Sstevel@tonic-gate **
34137c478bd9Sstevel@tonic-gate **	Returns a pointer to a new string that is the reverse of
34147c478bd9Sstevel@tonic-gate **	the string pointed to by fwd.  The space for the new
34157c478bd9Sstevel@tonic-gate **	string is obtained using xalloc().
34167c478bd9Sstevel@tonic-gate **
34177c478bd9Sstevel@tonic-gate **	Parameters:
34187c478bd9Sstevel@tonic-gate **		fwd -- the string to reverse.
34197c478bd9Sstevel@tonic-gate **
34207c478bd9Sstevel@tonic-gate **	Returns:
34217c478bd9Sstevel@tonic-gate **		the reversed string.
34227c478bd9Sstevel@tonic-gate */
34237c478bd9Sstevel@tonic-gate 
34247c478bd9Sstevel@tonic-gate static char *
34257c478bd9Sstevel@tonic-gate strrev(fwd)
34267c478bd9Sstevel@tonic-gate 	char *fwd;
34277c478bd9Sstevel@tonic-gate {
34287c478bd9Sstevel@tonic-gate 	char *rev = NULL;
34297c478bd9Sstevel@tonic-gate 	int len, cnt;
34307c478bd9Sstevel@tonic-gate 
34317c478bd9Sstevel@tonic-gate 	len = strlen(fwd);
34327c478bd9Sstevel@tonic-gate 	rev = xalloc(len + 1);
34337c478bd9Sstevel@tonic-gate 	for (cnt = 0; cnt < len; ++cnt)
34347c478bd9Sstevel@tonic-gate 		rev[cnt] = fwd[len - cnt - 1];
34357c478bd9Sstevel@tonic-gate 	rev[len] = '\0';
34367c478bd9Sstevel@tonic-gate 	return rev;
34377c478bd9Sstevel@tonic-gate }
34387c478bd9Sstevel@tonic-gate 
34397c478bd9Sstevel@tonic-gate #if _FFR_RHS
34407c478bd9Sstevel@tonic-gate 
34417c478bd9Sstevel@tonic-gate # define NASCII	128
34427c478bd9Sstevel@tonic-gate # define NCHAR	256
34437c478bd9Sstevel@tonic-gate 
34447c478bd9Sstevel@tonic-gate static unsigned char ShuffledAlphabet[NCHAR];
34457c478bd9Sstevel@tonic-gate 
34467c478bd9Sstevel@tonic-gate void
34477c478bd9Sstevel@tonic-gate init_shuffle_alphabet()
34487c478bd9Sstevel@tonic-gate {
34497c478bd9Sstevel@tonic-gate 	static bool init = false;
34507c478bd9Sstevel@tonic-gate 	int i;
34517c478bd9Sstevel@tonic-gate 
34527c478bd9Sstevel@tonic-gate 	if (init)
34537c478bd9Sstevel@tonic-gate 		return;
34547c478bd9Sstevel@tonic-gate 
34557c478bd9Sstevel@tonic-gate 	/* fill the ShuffledAlphabet */
345649218d4fSjbeck 	for (i = 0; i < NASCII; i++)
34577c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = i;
34587c478bd9Sstevel@tonic-gate 
34597c478bd9Sstevel@tonic-gate 	/* mix it */
346049218d4fSjbeck 	for (i = 1; i < NASCII; i++)
34617c478bd9Sstevel@tonic-gate 	{
346249218d4fSjbeck 		register int j = get_random() % NASCII;
34637c478bd9Sstevel@tonic-gate 		register int tmp;
34647c478bd9Sstevel@tonic-gate 
34657c478bd9Sstevel@tonic-gate 		tmp = ShuffledAlphabet[j];
34667c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[j] = ShuffledAlphabet[i];
34677c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = tmp;
34687c478bd9Sstevel@tonic-gate 	}
34697c478bd9Sstevel@tonic-gate 
34707c478bd9Sstevel@tonic-gate 	/* make it case insensitive */
34717c478bd9Sstevel@tonic-gate 	for (i = 'A'; i <= 'Z'; i++)
34727c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
34737c478bd9Sstevel@tonic-gate 
34747c478bd9Sstevel@tonic-gate 	/* fill the upper part */
347549218d4fSjbeck 	for (i = 0; i < NASCII; i++)
347649218d4fSjbeck 		ShuffledAlphabet[i + NASCII] = ShuffledAlphabet[i];
34777c478bd9Sstevel@tonic-gate 	init = true;
34787c478bd9Sstevel@tonic-gate }
34797c478bd9Sstevel@tonic-gate 
34807c478bd9Sstevel@tonic-gate static int
34817c478bd9Sstevel@tonic-gate sm_strshufflecmp(a, b)
34827c478bd9Sstevel@tonic-gate 	char *a;
34837c478bd9Sstevel@tonic-gate 	char *b;
34847c478bd9Sstevel@tonic-gate {
34857c478bd9Sstevel@tonic-gate 	const unsigned char *us1 = (const unsigned char *) a;
34867c478bd9Sstevel@tonic-gate 	const unsigned char *us2 = (const unsigned char *) b;
34877c478bd9Sstevel@tonic-gate 
34887c478bd9Sstevel@tonic-gate 	while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
34897c478bd9Sstevel@tonic-gate 	{
34907c478bd9Sstevel@tonic-gate 		if (*us1++ == '\0')
34917c478bd9Sstevel@tonic-gate 			return 0;
34927c478bd9Sstevel@tonic-gate 	}
34937c478bd9Sstevel@tonic-gate 	return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
34947c478bd9Sstevel@tonic-gate }
34957c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
34967c478bd9Sstevel@tonic-gate 
34977c478bd9Sstevel@tonic-gate /*
34987c478bd9Sstevel@tonic-gate **  DOWORK -- do a work request.
34997c478bd9Sstevel@tonic-gate **
35007c478bd9Sstevel@tonic-gate **	Parameters:
35017c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group for the job.
35027c478bd9Sstevel@tonic-gate **		qdir -- the index of the queue directory for the job.
35037c478bd9Sstevel@tonic-gate **		id -- the ID of the job to run.
35047c478bd9Sstevel@tonic-gate **		forkflag -- if set, run this in background.
35057c478bd9Sstevel@tonic-gate **		requeueflag -- if set, reinstantiate the queue quickly.
35067c478bd9Sstevel@tonic-gate **			This is used when expanding aliases in the queue.
35077c478bd9Sstevel@tonic-gate **			If forkflag is also set, it doesn't wait for the
35087c478bd9Sstevel@tonic-gate **			child.
35097c478bd9Sstevel@tonic-gate **		e - the envelope in which to run it.
35107c478bd9Sstevel@tonic-gate **
35117c478bd9Sstevel@tonic-gate **	Returns:
35127c478bd9Sstevel@tonic-gate **		process id of process that is running the queue job.
35137c478bd9Sstevel@tonic-gate **
35147c478bd9Sstevel@tonic-gate **	Side Effects:
35157c478bd9Sstevel@tonic-gate **		The work request is satisfied if possible.
35167c478bd9Sstevel@tonic-gate */
35177c478bd9Sstevel@tonic-gate 
35187c478bd9Sstevel@tonic-gate pid_t
35197c478bd9Sstevel@tonic-gate dowork(qgrp, qdir, id, forkflag, requeueflag, e)
35207c478bd9Sstevel@tonic-gate 	int qgrp;
35217c478bd9Sstevel@tonic-gate 	int qdir;
35227c478bd9Sstevel@tonic-gate 	char *id;
35237c478bd9Sstevel@tonic-gate 	bool forkflag;
35247c478bd9Sstevel@tonic-gate 	bool requeueflag;
35257c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
35267c478bd9Sstevel@tonic-gate {
35277c478bd9Sstevel@tonic-gate 	register pid_t pid;
35287c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
35297c478bd9Sstevel@tonic-gate 
35307c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
35317c478bd9Sstevel@tonic-gate 		sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
35327c478bd9Sstevel@tonic-gate 
35337c478bd9Sstevel@tonic-gate 	/*
35347c478bd9Sstevel@tonic-gate 	**  Fork for work.
35357c478bd9Sstevel@tonic-gate 	*/
35367c478bd9Sstevel@tonic-gate 
35377c478bd9Sstevel@tonic-gate 	if (forkflag)
35387c478bd9Sstevel@tonic-gate 	{
35397c478bd9Sstevel@tonic-gate 		/*
35407c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the
35417c478bd9Sstevel@tonic-gate 		**  parent does not wait, the parent may close the
35427c478bd9Sstevel@tonic-gate 		**  maps thereby removing any shared memory used by
35437c478bd9Sstevel@tonic-gate 		**  the map.  Therefore, close the maps now so the
35447c478bd9Sstevel@tonic-gate 		**  child will dynamically open them if necessary.
35457c478bd9Sstevel@tonic-gate 		*/
35467c478bd9Sstevel@tonic-gate 
35477c478bd9Sstevel@tonic-gate 		closemaps(false);
35487c478bd9Sstevel@tonic-gate 
35497c478bd9Sstevel@tonic-gate 		pid = fork();
35507c478bd9Sstevel@tonic-gate 		if (pid < 0)
35517c478bd9Sstevel@tonic-gate 		{
35527c478bd9Sstevel@tonic-gate 			syserr("dowork: cannot fork");
35537c478bd9Sstevel@tonic-gate 			return 0;
35547c478bd9Sstevel@tonic-gate 		}
35557c478bd9Sstevel@tonic-gate 		else if (pid > 0)
35567c478bd9Sstevel@tonic-gate 		{
35577c478bd9Sstevel@tonic-gate 			/* parent -- clean out connection cache */
35587c478bd9Sstevel@tonic-gate 			mci_flush(false, NULL);
35597c478bd9Sstevel@tonic-gate 		}
35607c478bd9Sstevel@tonic-gate 		else
35617c478bd9Sstevel@tonic-gate 		{
35627c478bd9Sstevel@tonic-gate 			/*
35637c478bd9Sstevel@tonic-gate 			**  Initialize exception stack and default exception
35647c478bd9Sstevel@tonic-gate 			**  handler for child process.
35657c478bd9Sstevel@tonic-gate 			*/
35667c478bd9Sstevel@tonic-gate 
35677c478bd9Sstevel@tonic-gate 			/* Reset global flags */
35687c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
35697c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
35707c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
35717c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
35727c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
35737c478bd9Sstevel@tonic-gate 			sm_exc_newthread(fatal_error);
35747c478bd9Sstevel@tonic-gate 
35757c478bd9Sstevel@tonic-gate 			/*
35767c478bd9Sstevel@tonic-gate 			**  See note above about SMTP processes and SIGCHLD.
35777c478bd9Sstevel@tonic-gate 			*/
35787c478bd9Sstevel@tonic-gate 
35797c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP ||
35807c478bd9Sstevel@tonic-gate 			    OpMode == MD_DAEMON ||
35817c478bd9Sstevel@tonic-gate 			    MaxQueueChildren > 0)
35827c478bd9Sstevel@tonic-gate 			{
35837c478bd9Sstevel@tonic-gate 				proc_list_clear();
35847c478bd9Sstevel@tonic-gate 				sm_releasesignal(SIGCHLD);
35857c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, SIG_DFL);
35867c478bd9Sstevel@tonic-gate 			}
35877c478bd9Sstevel@tonic-gate 
35887c478bd9Sstevel@tonic-gate 			/* child -- error messages to the transcript */
35897c478bd9Sstevel@tonic-gate 			QuickAbort = OnlyOneError = false;
35907c478bd9Sstevel@tonic-gate 		}
35917c478bd9Sstevel@tonic-gate 	}
35927c478bd9Sstevel@tonic-gate 	else
35937c478bd9Sstevel@tonic-gate 	{
35947c478bd9Sstevel@tonic-gate 		pid = 0;
35957c478bd9Sstevel@tonic-gate 	}
35967c478bd9Sstevel@tonic-gate 
35977c478bd9Sstevel@tonic-gate 	if (pid == 0)
35987c478bd9Sstevel@tonic-gate 	{
35997c478bd9Sstevel@tonic-gate 		/*
36007c478bd9Sstevel@tonic-gate 		**  CHILD
36017c478bd9Sstevel@tonic-gate 		**	Lock the control file to avoid duplicate deliveries.
36027c478bd9Sstevel@tonic-gate 		**		Then run the file as though we had just read it.
36037c478bd9Sstevel@tonic-gate 		**	We save an idea of the temporary name so we
36047c478bd9Sstevel@tonic-gate 		**		can recover on interrupt.
36057c478bd9Sstevel@tonic-gate 		*/
36067c478bd9Sstevel@tonic-gate 
36077c478bd9Sstevel@tonic-gate 		if (forkflag)
36087c478bd9Sstevel@tonic-gate 		{
36097c478bd9Sstevel@tonic-gate 			/* Reset global flags */
36107c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
36117c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
36127c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
36137c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
36147c478bd9Sstevel@tonic-gate 		}
36157c478bd9Sstevel@tonic-gate 
36167c478bd9Sstevel@tonic-gate 		/* set basic modes, etc. */
36177c478bd9Sstevel@tonic-gate 		sm_clear_events();
36187c478bd9Sstevel@tonic-gate 		clearstats();
36197c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
36207c478bd9Sstevel@tonic-gate 		clearenvelope(e, false, rpool);
36217c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
36227c478bd9Sstevel@tonic-gate 		set_delivery_mode(SM_DELIVER, e);
36237c478bd9Sstevel@tonic-gate 		e->e_errormode = EM_MAIL;
36247c478bd9Sstevel@tonic-gate 		e->e_id = id;
36257c478bd9Sstevel@tonic-gate 		e->e_qgrp = qgrp;
36267c478bd9Sstevel@tonic-gate 		e->e_qdir = qdir;
36277c478bd9Sstevel@tonic-gate 		GrabTo = UseErrorsTo = false;
36287c478bd9Sstevel@tonic-gate 		ExitStat = EX_OK;
36297c478bd9Sstevel@tonic-gate 		if (forkflag)
36307c478bd9Sstevel@tonic-gate 		{
36317c478bd9Sstevel@tonic-gate 			disconnect(1, e);
36327c478bd9Sstevel@tonic-gate 			set_op_mode(MD_QUEUERUN);
36337c478bd9Sstevel@tonic-gate 		}
36347c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, e, "%s from queue", qid_printname(e));
36357c478bd9Sstevel@tonic-gate 		if (LogLevel > 76)
36367c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
36377c478bd9Sstevel@tonic-gate 				  (int) CurrentPid);
36387c478bd9Sstevel@tonic-gate 
36397c478bd9Sstevel@tonic-gate 		/* don't use the headers from sendmail.cf... */
36407c478bd9Sstevel@tonic-gate 		e->e_header = NULL;
36417c478bd9Sstevel@tonic-gate 
36427c478bd9Sstevel@tonic-gate 		/* read the queue control file -- return if locked */
36437c478bd9Sstevel@tonic-gate 		if (!readqf(e, false))
36447c478bd9Sstevel@tonic-gate 		{
36457c478bd9Sstevel@tonic-gate 			if (tTd(40, 4) && e->e_id != NULL)
36467c478bd9Sstevel@tonic-gate 				sm_dprintf("readqf(%s) failed\n",
36477c478bd9Sstevel@tonic-gate 					qid_printname(e));
36487c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
36497c478bd9Sstevel@tonic-gate 			if (forkflag)
36507c478bd9Sstevel@tonic-gate 				finis(false, true, EX_OK);
36517c478bd9Sstevel@tonic-gate 			else
36527c478bd9Sstevel@tonic-gate 			{
36537c478bd9Sstevel@tonic-gate 				/* adding this frees 8 bytes */
36547c478bd9Sstevel@tonic-gate 				clearenvelope(e, false, rpool);
36557c478bd9Sstevel@tonic-gate 
36567c478bd9Sstevel@tonic-gate 				/* adding this frees 12 bytes */
36577c478bd9Sstevel@tonic-gate 				sm_rpool_free(rpool);
36587c478bd9Sstevel@tonic-gate 				e->e_rpool = NULL;
36597c478bd9Sstevel@tonic-gate 				return 0;
36607c478bd9Sstevel@tonic-gate 			}
36617c478bd9Sstevel@tonic-gate 		}
36627c478bd9Sstevel@tonic-gate 
36637c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_INQUEUE;
36647c478bd9Sstevel@tonic-gate 		eatheader(e, requeueflag, true);
36657c478bd9Sstevel@tonic-gate 
36667c478bd9Sstevel@tonic-gate 		if (requeueflag)
36677c478bd9Sstevel@tonic-gate 			queueup(e, false, false);
36687c478bd9Sstevel@tonic-gate 
36697c478bd9Sstevel@tonic-gate 		/* do the delivery */
36707c478bd9Sstevel@tonic-gate 		sendall(e, SM_DELIVER);
36717c478bd9Sstevel@tonic-gate 
36727c478bd9Sstevel@tonic-gate 		/* finish up and exit */
36737c478bd9Sstevel@tonic-gate 		if (forkflag)
36747c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
36757c478bd9Sstevel@tonic-gate 		else
36767c478bd9Sstevel@tonic-gate 		{
36777c478bd9Sstevel@tonic-gate 			dropenvelope(e, true, false);
36787c478bd9Sstevel@tonic-gate 			sm_rpool_free(rpool);
36797c478bd9Sstevel@tonic-gate 			e->e_rpool = NULL;
36807c478bd9Sstevel@tonic-gate 		}
36817c478bd9Sstevel@tonic-gate 	}
36827c478bd9Sstevel@tonic-gate 	e->e_id = NULL;
36837c478bd9Sstevel@tonic-gate 	return pid;
36847c478bd9Sstevel@tonic-gate }
36857c478bd9Sstevel@tonic-gate 
36867c478bd9Sstevel@tonic-gate /*
36877c478bd9Sstevel@tonic-gate **  DOWORKLIST -- process a list of envelopes as work requests
36887c478bd9Sstevel@tonic-gate **
36897c478bd9Sstevel@tonic-gate **	Similar to dowork(), except that after forking, it processes an
36907c478bd9Sstevel@tonic-gate **	envelope and its siblings, treating each envelope as a work request.
36917c478bd9Sstevel@tonic-gate **
36927c478bd9Sstevel@tonic-gate **	Parameters:
36937c478bd9Sstevel@tonic-gate **		el -- envelope to be processed including its siblings.
36947c478bd9Sstevel@tonic-gate **		forkflag -- if set, run this in background.
36957c478bd9Sstevel@tonic-gate **		requeueflag -- if set, reinstantiate the queue quickly.
36967c478bd9Sstevel@tonic-gate **			This is used when expanding aliases in the queue.
36977c478bd9Sstevel@tonic-gate **			If forkflag is also set, it doesn't wait for the
36987c478bd9Sstevel@tonic-gate **			child.
36997c478bd9Sstevel@tonic-gate **
37007c478bd9Sstevel@tonic-gate **	Returns:
37017c478bd9Sstevel@tonic-gate **		process id of process that is running the queue job.
37027c478bd9Sstevel@tonic-gate **
37037c478bd9Sstevel@tonic-gate **	Side Effects:
37047c478bd9Sstevel@tonic-gate **		The work request is satisfied if possible.
37057c478bd9Sstevel@tonic-gate */
37067c478bd9Sstevel@tonic-gate 
37077c478bd9Sstevel@tonic-gate pid_t
37087c478bd9Sstevel@tonic-gate doworklist(el, forkflag, requeueflag)
37097c478bd9Sstevel@tonic-gate 	ENVELOPE *el;
37107c478bd9Sstevel@tonic-gate 	bool forkflag;
37117c478bd9Sstevel@tonic-gate 	bool requeueflag;
37127c478bd9Sstevel@tonic-gate {
37137c478bd9Sstevel@tonic-gate 	register pid_t pid;
37147c478bd9Sstevel@tonic-gate 	ENVELOPE *ei;
37157c478bd9Sstevel@tonic-gate 
37167c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
37177c478bd9Sstevel@tonic-gate 		sm_dprintf("doworklist()\n");
37187c478bd9Sstevel@tonic-gate 
37197c478bd9Sstevel@tonic-gate 	/*
37207c478bd9Sstevel@tonic-gate 	**  Fork for work.
37217c478bd9Sstevel@tonic-gate 	*/
37227c478bd9Sstevel@tonic-gate 
37237c478bd9Sstevel@tonic-gate 	if (forkflag)
37247c478bd9Sstevel@tonic-gate 	{
37257c478bd9Sstevel@tonic-gate 		/*
37267c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the
37277c478bd9Sstevel@tonic-gate 		**  parent does not wait, the parent may close the
37287c478bd9Sstevel@tonic-gate 		**  maps thereby removing any shared memory used by
37297c478bd9Sstevel@tonic-gate 		**  the map.  Therefore, close the maps now so the
37307c478bd9Sstevel@tonic-gate 		**  child will dynamically open them if necessary.
37317c478bd9Sstevel@tonic-gate 		*/
37327c478bd9Sstevel@tonic-gate 
37337c478bd9Sstevel@tonic-gate 		closemaps(false);
37347c478bd9Sstevel@tonic-gate 
37357c478bd9Sstevel@tonic-gate 		pid = fork();
37367c478bd9Sstevel@tonic-gate 		if (pid < 0)
37377c478bd9Sstevel@tonic-gate 		{
37387c478bd9Sstevel@tonic-gate 			syserr("doworklist: cannot fork");
37397c478bd9Sstevel@tonic-gate 			return 0;
37407c478bd9Sstevel@tonic-gate 		}
37417c478bd9Sstevel@tonic-gate 		else if (pid > 0)
37427c478bd9Sstevel@tonic-gate 		{
37437c478bd9Sstevel@tonic-gate 			/* parent -- clean out connection cache */
37447c478bd9Sstevel@tonic-gate 			mci_flush(false, NULL);
37457c478bd9Sstevel@tonic-gate 		}
37467c478bd9Sstevel@tonic-gate 		else
37477c478bd9Sstevel@tonic-gate 		{
37487c478bd9Sstevel@tonic-gate 			/*
37497c478bd9Sstevel@tonic-gate 			**  Initialize exception stack and default exception
37507c478bd9Sstevel@tonic-gate 			**  handler for child process.
37517c478bd9Sstevel@tonic-gate 			*/
37527c478bd9Sstevel@tonic-gate 
37537c478bd9Sstevel@tonic-gate 			/* Reset global flags */
37547c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
37557c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
37567c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
37577c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
37587c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
37597c478bd9Sstevel@tonic-gate 			sm_exc_newthread(fatal_error);
37607c478bd9Sstevel@tonic-gate 
37617c478bd9Sstevel@tonic-gate 			/*
37627c478bd9Sstevel@tonic-gate 			**  See note above about SMTP processes and SIGCHLD.
37637c478bd9Sstevel@tonic-gate 			*/
37647c478bd9Sstevel@tonic-gate 
37657c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP ||
37667c478bd9Sstevel@tonic-gate 			    OpMode == MD_DAEMON ||
37677c478bd9Sstevel@tonic-gate 			    MaxQueueChildren > 0)
37687c478bd9Sstevel@tonic-gate 			{
37697c478bd9Sstevel@tonic-gate 				proc_list_clear();
37707c478bd9Sstevel@tonic-gate 				sm_releasesignal(SIGCHLD);
37717c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, SIG_DFL);
37727c478bd9Sstevel@tonic-gate 			}
37737c478bd9Sstevel@tonic-gate 
37747c478bd9Sstevel@tonic-gate 			/* child -- error messages to the transcript */
37757c478bd9Sstevel@tonic-gate 			QuickAbort = OnlyOneError = false;
37767c478bd9Sstevel@tonic-gate 		}
37777c478bd9Sstevel@tonic-gate 	}
37787c478bd9Sstevel@tonic-gate 	else
37797c478bd9Sstevel@tonic-gate 	{
37807c478bd9Sstevel@tonic-gate 		pid = 0;
37817c478bd9Sstevel@tonic-gate 	}
37827c478bd9Sstevel@tonic-gate 
37837c478bd9Sstevel@tonic-gate 	if (pid != 0)
37847c478bd9Sstevel@tonic-gate 		return pid;
37857c478bd9Sstevel@tonic-gate 
37867c478bd9Sstevel@tonic-gate 	/*
37877c478bd9Sstevel@tonic-gate 	**  IN CHILD
37887c478bd9Sstevel@tonic-gate 	**	Lock the control file to avoid duplicate deliveries.
37897c478bd9Sstevel@tonic-gate 	**		Then run the file as though we had just read it.
37907c478bd9Sstevel@tonic-gate 	**	We save an idea of the temporary name so we
37917c478bd9Sstevel@tonic-gate 	**		can recover on interrupt.
37927c478bd9Sstevel@tonic-gate 	*/
37937c478bd9Sstevel@tonic-gate 
37947c478bd9Sstevel@tonic-gate 	if (forkflag)
37957c478bd9Sstevel@tonic-gate 	{
37967c478bd9Sstevel@tonic-gate 		/* Reset global flags */
37977c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
37987c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
37997c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
38007c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
38017c478bd9Sstevel@tonic-gate 	}
38027c478bd9Sstevel@tonic-gate 
38037c478bd9Sstevel@tonic-gate 	/* set basic modes, etc. */
38047c478bd9Sstevel@tonic-gate 	sm_clear_events();
38057c478bd9Sstevel@tonic-gate 	clearstats();
38067c478bd9Sstevel@tonic-gate 	GrabTo = UseErrorsTo = false;
38077c478bd9Sstevel@tonic-gate 	ExitStat = EX_OK;
38087c478bd9Sstevel@tonic-gate 	if (forkflag)
38097c478bd9Sstevel@tonic-gate 	{
38107c478bd9Sstevel@tonic-gate 		disconnect(1, el);
38117c478bd9Sstevel@tonic-gate 		set_op_mode(MD_QUEUERUN);
38127c478bd9Sstevel@tonic-gate 	}
38137c478bd9Sstevel@tonic-gate 	if (LogLevel > 76)
38147c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
38157c478bd9Sstevel@tonic-gate 			  (int) CurrentPid);
38167c478bd9Sstevel@tonic-gate 
38177c478bd9Sstevel@tonic-gate 	for (ei = el; ei != NULL; ei = ei->e_sibling)
38187c478bd9Sstevel@tonic-gate 	{
38197c478bd9Sstevel@tonic-gate 		ENVELOPE e;
38207c478bd9Sstevel@tonic-gate 		SM_RPOOL_T *rpool;
38217c478bd9Sstevel@tonic-gate 
38227c478bd9Sstevel@tonic-gate 		if (WILL_BE_QUEUED(ei->e_sendmode))
38237c478bd9Sstevel@tonic-gate 			continue;
38247c478bd9Sstevel@tonic-gate 		else if (QueueMode != QM_QUARANTINE &&
38257c478bd9Sstevel@tonic-gate 			 ei->e_quarmsg != NULL)
38267c478bd9Sstevel@tonic-gate 			continue;
38277c478bd9Sstevel@tonic-gate 
38287c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
38297c478bd9Sstevel@tonic-gate 		clearenvelope(&e, true, rpool);
38307c478bd9Sstevel@tonic-gate 		e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
38317c478bd9Sstevel@tonic-gate 		set_delivery_mode(SM_DELIVER, &e);
38327c478bd9Sstevel@tonic-gate 		e.e_errormode = EM_MAIL;
38337c478bd9Sstevel@tonic-gate 		e.e_id = ei->e_id;
38347c478bd9Sstevel@tonic-gate 		e.e_qgrp = ei->e_qgrp;
38357c478bd9Sstevel@tonic-gate 		e.e_qdir = ei->e_qdir;
38367c478bd9Sstevel@tonic-gate 		openxscript(&e);
38377c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
38387c478bd9Sstevel@tonic-gate 
38397c478bd9Sstevel@tonic-gate 		/* don't use the headers from sendmail.cf... */
38407c478bd9Sstevel@tonic-gate 		e.e_header = NULL;
38417c478bd9Sstevel@tonic-gate 		CurEnv = &e;
38427c478bd9Sstevel@tonic-gate 
38437c478bd9Sstevel@tonic-gate 		/* read the queue control file -- return if locked */
38447c478bd9Sstevel@tonic-gate 		if (readqf(&e, false))
38457c478bd9Sstevel@tonic-gate 		{
38467c478bd9Sstevel@tonic-gate 			e.e_flags |= EF_INQUEUE;
38477c478bd9Sstevel@tonic-gate 			eatheader(&e, requeueflag, true);
38487c478bd9Sstevel@tonic-gate 
38497c478bd9Sstevel@tonic-gate 			if (requeueflag)
38507c478bd9Sstevel@tonic-gate 				queueup(&e, false, false);
38517c478bd9Sstevel@tonic-gate 
38527c478bd9Sstevel@tonic-gate 			/* do the delivery */
38537c478bd9Sstevel@tonic-gate 			sendall(&e, SM_DELIVER);
38547c478bd9Sstevel@tonic-gate 			dropenvelope(&e, true, false);
38557c478bd9Sstevel@tonic-gate 		}
38567c478bd9Sstevel@tonic-gate 		else
38577c478bd9Sstevel@tonic-gate 		{
38587c478bd9Sstevel@tonic-gate 			if (tTd(40, 4) && e.e_id != NULL)
38597c478bd9Sstevel@tonic-gate 				sm_dprintf("readqf(%s) failed\n",
38607c478bd9Sstevel@tonic-gate 					qid_printname(&e));
38617c478bd9Sstevel@tonic-gate 		}
38627c478bd9Sstevel@tonic-gate 		sm_rpool_free(rpool);
38637c478bd9Sstevel@tonic-gate 		ei->e_id = NULL;
38647c478bd9Sstevel@tonic-gate 	}
38657c478bd9Sstevel@tonic-gate 
38667c478bd9Sstevel@tonic-gate 	/* restore CurEnv */
38677c478bd9Sstevel@tonic-gate 	CurEnv = el;
38687c478bd9Sstevel@tonic-gate 
38697c478bd9Sstevel@tonic-gate 	/* finish up and exit */
38707c478bd9Sstevel@tonic-gate 	if (forkflag)
38717c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
38727c478bd9Sstevel@tonic-gate 	return 0;
38737c478bd9Sstevel@tonic-gate }
38747c478bd9Sstevel@tonic-gate /*
38757c478bd9Sstevel@tonic-gate **  READQF -- read queue file and set up environment.
38767c478bd9Sstevel@tonic-gate **
38777c478bd9Sstevel@tonic-gate **	Parameters:
38787c478bd9Sstevel@tonic-gate **		e -- the envelope of the job to run.
38797c478bd9Sstevel@tonic-gate **		openonly -- only open the qf (returned as e_lockfp)
38807c478bd9Sstevel@tonic-gate **
38817c478bd9Sstevel@tonic-gate **	Returns:
38827c478bd9Sstevel@tonic-gate **		true if it successfully read the queue file.
38837c478bd9Sstevel@tonic-gate **		false otherwise.
38847c478bd9Sstevel@tonic-gate **
38857c478bd9Sstevel@tonic-gate **	Side Effects:
38867c478bd9Sstevel@tonic-gate **		The queue file is returned locked.
38877c478bd9Sstevel@tonic-gate */
38887c478bd9Sstevel@tonic-gate 
38897c478bd9Sstevel@tonic-gate static bool
38907c478bd9Sstevel@tonic-gate readqf(e, openonly)
38917c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
38927c478bd9Sstevel@tonic-gate 	bool openonly;
38937c478bd9Sstevel@tonic-gate {
38947c478bd9Sstevel@tonic-gate 	register SM_FILE_T *qfp;
38957c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
38967c478bd9Sstevel@tonic-gate 	struct stat st, stf;
38977c478bd9Sstevel@tonic-gate 	char *bp;
38987c478bd9Sstevel@tonic-gate 	int qfver = 0;
38997c478bd9Sstevel@tonic-gate 	long hdrsize = 0;
39007c478bd9Sstevel@tonic-gate 	register char *p;
39017c478bd9Sstevel@tonic-gate 	char *frcpt = NULL;
39027c478bd9Sstevel@tonic-gate 	char *orcpt = NULL;
39037c478bd9Sstevel@tonic-gate 	bool nomore = false;
39047c478bd9Sstevel@tonic-gate 	bool bogus = false;
39057c478bd9Sstevel@tonic-gate 	MODE_T qsafe;
39067c478bd9Sstevel@tonic-gate 	char *err;
39077c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
39087c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
3909*058561cbSjbeck 	int bufsize;
39107c478bd9Sstevel@tonic-gate 
39117c478bd9Sstevel@tonic-gate 	/*
39127c478bd9Sstevel@tonic-gate 	**  Read and process the file.
39137c478bd9Sstevel@tonic-gate 	*/
39147c478bd9Sstevel@tonic-gate 
39153ee0e492Sjbeck 	bp = NULL;
3916*058561cbSjbeck 	(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof(qf));
39177c478bd9Sstevel@tonic-gate 	qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR_B, NULL);
39187c478bd9Sstevel@tonic-gate 	if (qfp == NULL)
39197c478bd9Sstevel@tonic-gate 	{
39207c478bd9Sstevel@tonic-gate 		int save_errno = errno;
39217c478bd9Sstevel@tonic-gate 
39227c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39237c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
39247c478bd9Sstevel@tonic-gate 				qf, sm_errstring(errno));
39257c478bd9Sstevel@tonic-gate 		errno = save_errno;
39267c478bd9Sstevel@tonic-gate 		if (errno != ENOENT
39277c478bd9Sstevel@tonic-gate 		    )
39287c478bd9Sstevel@tonic-gate 			syserr("readqf: no control file %s", qf);
39297c478bd9Sstevel@tonic-gate 		RELEASE_QUEUE;
39307c478bd9Sstevel@tonic-gate 		return false;
39317c478bd9Sstevel@tonic-gate 	}
39327c478bd9Sstevel@tonic-gate 
39337c478bd9Sstevel@tonic-gate 	if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
39347c478bd9Sstevel@tonic-gate 		      LOCK_EX|LOCK_NB))
39357c478bd9Sstevel@tonic-gate 	{
39367c478bd9Sstevel@tonic-gate 		/* being processed by another queuer */
39377c478bd9Sstevel@tonic-gate 		if (Verbose)
39387c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
39397c478bd9Sstevel@tonic-gate 					     "%s: locked\n", e->e_id);
39407c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39417c478bd9Sstevel@tonic-gate 			sm_dprintf("%s: locked\n", e->e_id);
39427c478bd9Sstevel@tonic-gate 		if (LogLevel > 19)
39437c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "locked");
39447c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39457c478bd9Sstevel@tonic-gate 		RELEASE_QUEUE;
39467c478bd9Sstevel@tonic-gate 		return false;
39477c478bd9Sstevel@tonic-gate 	}
39487c478bd9Sstevel@tonic-gate 
39497c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
39507c478bd9Sstevel@tonic-gate 
39517c478bd9Sstevel@tonic-gate 	/*
39527c478bd9Sstevel@tonic-gate 	**  Prevent locking race condition.
39537c478bd9Sstevel@tonic-gate 	**
39547c478bd9Sstevel@tonic-gate 	**  Process A: readqf(): qfp = fopen(qffile)
39557c478bd9Sstevel@tonic-gate 	**  Process B: queueup(): rename(tf, qf)
39567c478bd9Sstevel@tonic-gate 	**  Process B: unlocks(tf)
39577c478bd9Sstevel@tonic-gate 	**  Process A: lockfile(qf);
39587c478bd9Sstevel@tonic-gate 	**
39597c478bd9Sstevel@tonic-gate 	**  Process A (us) has the old qf file (before the rename deleted
39607c478bd9Sstevel@tonic-gate 	**  the directory entry) and will be delivering based on old data.
39617c478bd9Sstevel@tonic-gate 	**  This can lead to multiple deliveries of the same recipients.
39627c478bd9Sstevel@tonic-gate 	**
39637c478bd9Sstevel@tonic-gate 	**  Catch this by checking if the underlying qf file has changed
39647c478bd9Sstevel@tonic-gate 	**  *after* acquiring our lock and if so, act as though the file
39657c478bd9Sstevel@tonic-gate 	**  was still locked (i.e., just return like the lockfile() case
39667c478bd9Sstevel@tonic-gate 	**  above.
39677c478bd9Sstevel@tonic-gate 	*/
39687c478bd9Sstevel@tonic-gate 
39697c478bd9Sstevel@tonic-gate 	if (stat(qf, &stf) < 0 ||
39707c478bd9Sstevel@tonic-gate 	    fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
39717c478bd9Sstevel@tonic-gate 	{
39727c478bd9Sstevel@tonic-gate 		/* must have been being processed by someone else */
39737c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39747c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
39757c478bd9Sstevel@tonic-gate 				qf, sm_errstring(errno));
39767c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39777c478bd9Sstevel@tonic-gate 		return false;
39787c478bd9Sstevel@tonic-gate 	}
39797c478bd9Sstevel@tonic-gate 
39807c478bd9Sstevel@tonic-gate 	if (st.st_nlink != stf.st_nlink ||
39817c478bd9Sstevel@tonic-gate 	    st.st_dev != stf.st_dev ||
39827c478bd9Sstevel@tonic-gate 	    ST_INODE(st) != ST_INODE(stf) ||
39837c478bd9Sstevel@tonic-gate #if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
39847c478bd9Sstevel@tonic-gate 	    st.st_gen != stf.st_gen ||
39857c478bd9Sstevel@tonic-gate #endif /* HAS_ST_GEN && 0 */
39867c478bd9Sstevel@tonic-gate 	    st.st_uid != stf.st_uid ||
39877c478bd9Sstevel@tonic-gate 	    st.st_gid != stf.st_gid ||
39887c478bd9Sstevel@tonic-gate 	    st.st_size != stf.st_size)
39897c478bd9Sstevel@tonic-gate 	{
39907c478bd9Sstevel@tonic-gate 		/* changed after opened */
39917c478bd9Sstevel@tonic-gate 		if (Verbose)
39927c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
39937c478bd9Sstevel@tonic-gate 					     "%s: changed\n", e->e_id);
39947c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39957c478bd9Sstevel@tonic-gate 			sm_dprintf("%s: changed\n", e->e_id);
39967c478bd9Sstevel@tonic-gate 		if (LogLevel > 19)
39977c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "changed");
39987c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39997c478bd9Sstevel@tonic-gate 		return false;
40007c478bd9Sstevel@tonic-gate 	}
40017c478bd9Sstevel@tonic-gate 
40027c478bd9Sstevel@tonic-gate 	/*
40037c478bd9Sstevel@tonic-gate 	**  Check the queue file for plausibility to avoid attacks.
40047c478bd9Sstevel@tonic-gate 	*/
40057c478bd9Sstevel@tonic-gate 
40067c478bd9Sstevel@tonic-gate 	qsafe = S_IWOTH|S_IWGRP;
40077c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
40087c478bd9Sstevel@tonic-gate 		qsafe &= ~S_IWGRP;
40097c478bd9Sstevel@tonic-gate 
40107c478bd9Sstevel@tonic-gate 	bogus = st.st_uid != geteuid() &&
40117c478bd9Sstevel@tonic-gate 		st.st_uid != TrustedUid &&
40127c478bd9Sstevel@tonic-gate 		geteuid() != RealUid;
40137c478bd9Sstevel@tonic-gate 
40147c478bd9Sstevel@tonic-gate 	/*
40157c478bd9Sstevel@tonic-gate 	**  If this qf file results from a set-group-ID binary, then
40167c478bd9Sstevel@tonic-gate 	**  we check whether the directory is group-writable,
40177c478bd9Sstevel@tonic-gate 	**  the queue file mode contains the group-writable bit, and
40187c478bd9Sstevel@tonic-gate 	**  the groups are the same.
40197c478bd9Sstevel@tonic-gate 	**  Notice: this requires that the set-group-ID binary is used to
40207c478bd9Sstevel@tonic-gate 	**  run the queue!
40217c478bd9Sstevel@tonic-gate 	*/
40227c478bd9Sstevel@tonic-gate 
40237c478bd9Sstevel@tonic-gate 	if (bogus && st.st_gid == getegid() && UseMSP)
40247c478bd9Sstevel@tonic-gate 	{
40257c478bd9Sstevel@tonic-gate 		char delim;
40267c478bd9Sstevel@tonic-gate 		struct stat dst;
40277c478bd9Sstevel@tonic-gate 
40287c478bd9Sstevel@tonic-gate 		bp = SM_LAST_DIR_DELIM(qf);
40297c478bd9Sstevel@tonic-gate 		if (bp == NULL)
40307c478bd9Sstevel@tonic-gate 			delim = '\0';
40317c478bd9Sstevel@tonic-gate 		else
40327c478bd9Sstevel@tonic-gate 		{
40337c478bd9Sstevel@tonic-gate 			delim = *bp;
40347c478bd9Sstevel@tonic-gate 			*bp = '\0';
40357c478bd9Sstevel@tonic-gate 		}
40367c478bd9Sstevel@tonic-gate 		if (stat(delim == '\0' ? "." : qf, &dst) < 0)
40377c478bd9Sstevel@tonic-gate 			syserr("readqf: cannot stat directory %s",
40387c478bd9Sstevel@tonic-gate 				delim == '\0' ? "." : qf);
40397c478bd9Sstevel@tonic-gate 		else
40407c478bd9Sstevel@tonic-gate 		{
40417c478bd9Sstevel@tonic-gate 			bogus = !(bitset(S_IWGRP, QueueFileMode) &&
40427c478bd9Sstevel@tonic-gate 				  bitset(S_IWGRP, dst.st_mode) &&
40437c478bd9Sstevel@tonic-gate 				  dst.st_gid == st.st_gid);
40447c478bd9Sstevel@tonic-gate 		}
40457c478bd9Sstevel@tonic-gate 		if (delim != '\0')
40467c478bd9Sstevel@tonic-gate 			*bp = delim;
40473ee0e492Sjbeck 		bp = NULL;
40487c478bd9Sstevel@tonic-gate 	}
40497c478bd9Sstevel@tonic-gate 	if (!bogus)
40507c478bd9Sstevel@tonic-gate 		bogus = bitset(qsafe, st.st_mode);
40517c478bd9Sstevel@tonic-gate 	if (bogus)
40527c478bd9Sstevel@tonic-gate 	{
40537c478bd9Sstevel@tonic-gate 		if (LogLevel > 0)
40547c478bd9Sstevel@tonic-gate 		{
40557c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, e->e_id,
40567c478bd9Sstevel@tonic-gate 				  "bogus queue file, uid=%d, gid=%d, mode=%o",
40577c478bd9Sstevel@tonic-gate 				  st.st_uid, st.st_gid, st.st_mode);
40587c478bd9Sstevel@tonic-gate 		}
40597c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
40607c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): bogus file\n", qf);
40617c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_INQUEUE;
40627c478bd9Sstevel@tonic-gate 		if (!openonly)
40637c478bd9Sstevel@tonic-gate 			loseqfile(e, "bogus file uid/gid in mqueue");
40647c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40657c478bd9Sstevel@tonic-gate 		return false;
40667c478bd9Sstevel@tonic-gate 	}
40677c478bd9Sstevel@tonic-gate 
40687c478bd9Sstevel@tonic-gate 	if (st.st_size == 0)
40697c478bd9Sstevel@tonic-gate 	{
40707c478bd9Sstevel@tonic-gate 		/* must be a bogus file -- if also old, just remove it */
40717c478bd9Sstevel@tonic-gate 		if (!openonly && st.st_ctime + 10 * 60 < curtime())
40727c478bd9Sstevel@tonic-gate 		{
40737c478bd9Sstevel@tonic-gate 			(void) xunlink(queuename(e, DATAFL_LETTER));
40747c478bd9Sstevel@tonic-gate 			(void) xunlink(queuename(e, ANYQFL_LETTER));
40757c478bd9Sstevel@tonic-gate 		}
40767c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40777c478bd9Sstevel@tonic-gate 		return false;
40787c478bd9Sstevel@tonic-gate 	}
40797c478bd9Sstevel@tonic-gate 
40807c478bd9Sstevel@tonic-gate 	if (st.st_nlink == 0)
40817c478bd9Sstevel@tonic-gate 	{
40827c478bd9Sstevel@tonic-gate 		/*
40837c478bd9Sstevel@tonic-gate 		**  Race condition -- we got a file just as it was being
40847c478bd9Sstevel@tonic-gate 		**  unlinked.  Just assume it is zero length.
40857c478bd9Sstevel@tonic-gate 		*/
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 _FFR_TRUSTED_QF
40927c478bd9Sstevel@tonic-gate 	/*
40937c478bd9Sstevel@tonic-gate 	**  If we don't own the file mark it as unsafe.
40947c478bd9Sstevel@tonic-gate 	**  However, allow TrustedUser to own it as well
40957c478bd9Sstevel@tonic-gate 	**  in case TrustedUser manipulates the queue.
40967c478bd9Sstevel@tonic-gate 	*/
40977c478bd9Sstevel@tonic-gate 
40987c478bd9Sstevel@tonic-gate 	if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
40997c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_UNSAFE;
41007c478bd9Sstevel@tonic-gate #else /* _FFR_TRUSTED_QF */
41017c478bd9Sstevel@tonic-gate 	/* If we don't own the file mark it as unsafe */
41027c478bd9Sstevel@tonic-gate 	if (st.st_uid != geteuid())
41037c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_UNSAFE;
41047c478bd9Sstevel@tonic-gate #endif /* _FFR_TRUSTED_QF */
41057c478bd9Sstevel@tonic-gate 
41067c478bd9Sstevel@tonic-gate 	/* good file -- save this lock */
41077c478bd9Sstevel@tonic-gate 	e->e_lockfp = qfp;
41087c478bd9Sstevel@tonic-gate 
41097c478bd9Sstevel@tonic-gate 	/* Just wanted the open file */
41107c478bd9Sstevel@tonic-gate 	if (openonly)
41117c478bd9Sstevel@tonic-gate 		return true;
41127c478bd9Sstevel@tonic-gate 
41137c478bd9Sstevel@tonic-gate 	/* do basic system initialization */
41147c478bd9Sstevel@tonic-gate 	initsys(e);
41157c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
41167c478bd9Sstevel@tonic-gate 
41177c478bd9Sstevel@tonic-gate 	LineNumber = 0;
41187c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_GLOBALERRS;
41197c478bd9Sstevel@tonic-gate 	set_op_mode(MD_QUEUERUN);
41207c478bd9Sstevel@tonic-gate 	ctladdr = NULL;
41217c478bd9Sstevel@tonic-gate 	e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
41227c478bd9Sstevel@tonic-gate 	e->e_dfqgrp = e->e_qgrp;
41237c478bd9Sstevel@tonic-gate 	e->e_dfqdir = e->e_qdir;
41247c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_MACRO
41257c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
41267c478bd9Sstevel@tonic-gate 		  qid_printqueue(e->e_qgrp, e->e_qdir));
41277c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_MACRO */
41287c478bd9Sstevel@tonic-gate 	e->e_dfino = -1;
41297c478bd9Sstevel@tonic-gate 	e->e_msgsize = -1;
4130*058561cbSjbeck 	while (bufsize = sizeof(buf),
4131*058561cbSjbeck 	       (bp = fgetfolded(buf, &bufsize, qfp)) != NULL)
41327c478bd9Sstevel@tonic-gate 	{
41337c478bd9Sstevel@tonic-gate 		unsigned long qflags;
41347c478bd9Sstevel@tonic-gate 		ADDRESS *q;
41357c478bd9Sstevel@tonic-gate 		int r;
41367c478bd9Sstevel@tonic-gate 		time_t now;
41377c478bd9Sstevel@tonic-gate 		auto char *ep;
41387c478bd9Sstevel@tonic-gate 
41397c478bd9Sstevel@tonic-gate 		if (tTd(40, 4))
41407c478bd9Sstevel@tonic-gate 			sm_dprintf("+++++ %s\n", bp);
41417c478bd9Sstevel@tonic-gate 		if (nomore)
41427c478bd9Sstevel@tonic-gate 		{
41437c478bd9Sstevel@tonic-gate 			/* hack attack */
41447c478bd9Sstevel@tonic-gate   hackattack:
41457c478bd9Sstevel@tonic-gate 			syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
41467c478bd9Sstevel@tonic-gate 			       bp);
41477c478bd9Sstevel@tonic-gate 			err = "bogus queue line";
41487c478bd9Sstevel@tonic-gate 			goto fail;
41497c478bd9Sstevel@tonic-gate 		}
41507c478bd9Sstevel@tonic-gate 		switch (bp[0])
41517c478bd9Sstevel@tonic-gate 		{
41527c478bd9Sstevel@tonic-gate 		  case 'A':		/* AUTH= parameter */
41537c478bd9Sstevel@tonic-gate 			if (!xtextok(&bp[1]))
41547c478bd9Sstevel@tonic-gate 				goto hackattack;
41557c478bd9Sstevel@tonic-gate 			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
41567c478bd9Sstevel@tonic-gate 			break;
41577c478bd9Sstevel@tonic-gate 
41587c478bd9Sstevel@tonic-gate 		  case 'B':		/* body type */
41597c478bd9Sstevel@tonic-gate 			r = check_bodytype(&bp[1]);
41607c478bd9Sstevel@tonic-gate 			if (!BODYTYPE_VALID(r))
41617c478bd9Sstevel@tonic-gate 				goto hackattack;
41627c478bd9Sstevel@tonic-gate 			e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
41637c478bd9Sstevel@tonic-gate 			break;
41647c478bd9Sstevel@tonic-gate 
41657c478bd9Sstevel@tonic-gate 		  case 'C':		/* specify controlling user */
41667c478bd9Sstevel@tonic-gate 			ctladdr = setctluser(&bp[1], qfver, e);
41677c478bd9Sstevel@tonic-gate 			break;
41687c478bd9Sstevel@tonic-gate 
41697c478bd9Sstevel@tonic-gate 		  case 'D':		/* data file name */
41707c478bd9Sstevel@tonic-gate 			/* obsolete -- ignore */
41717c478bd9Sstevel@tonic-gate 			break;
41727c478bd9Sstevel@tonic-gate 
41737c478bd9Sstevel@tonic-gate 		  case 'd':		/* data file directory name */
41747c478bd9Sstevel@tonic-gate 			{
41757c478bd9Sstevel@tonic-gate 				int qgrp, qdir;
41767c478bd9Sstevel@tonic-gate 
41777c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
41787c478bd9Sstevel@tonic-gate 				/* forbid queue groups in MSP? */
41797c478bd9Sstevel@tonic-gate 				if (UseMSP)
41807c478bd9Sstevel@tonic-gate 					goto hackattack;
41817c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
41827c478bd9Sstevel@tonic-gate 				for (qgrp = 0;
41837c478bd9Sstevel@tonic-gate 				     qgrp < NumQueue && Queue[qgrp] != NULL;
41847c478bd9Sstevel@tonic-gate 				     ++qgrp)
41857c478bd9Sstevel@tonic-gate 				{
41867c478bd9Sstevel@tonic-gate 					for (qdir = 0;
41877c478bd9Sstevel@tonic-gate 					     qdir < Queue[qgrp]->qg_numqueues;
41887c478bd9Sstevel@tonic-gate 					     ++qdir)
41897c478bd9Sstevel@tonic-gate 					{
41907c478bd9Sstevel@tonic-gate 						if (strcmp(&bp[1],
41917c478bd9Sstevel@tonic-gate 							   Queue[qgrp]->qg_qpaths[qdir].qp_name)
41927c478bd9Sstevel@tonic-gate 						    == 0)
41937c478bd9Sstevel@tonic-gate 						{
41947c478bd9Sstevel@tonic-gate 							e->e_dfqgrp = qgrp;
41957c478bd9Sstevel@tonic-gate 							e->e_dfqdir = qdir;
41967c478bd9Sstevel@tonic-gate 							goto done;
41977c478bd9Sstevel@tonic-gate 						}
41987c478bd9Sstevel@tonic-gate 					}
41997c478bd9Sstevel@tonic-gate 				}
42007c478bd9Sstevel@tonic-gate 				err = "bogus queue file directory";
42017c478bd9Sstevel@tonic-gate 				goto fail;
42027c478bd9Sstevel@tonic-gate 			  done:
42037c478bd9Sstevel@tonic-gate 				break;
42047c478bd9Sstevel@tonic-gate 			}
42057c478bd9Sstevel@tonic-gate 
42067c478bd9Sstevel@tonic-gate 		  case 'E':		/* specify error recipient */
42077c478bd9Sstevel@tonic-gate 			/* no longer used */
42087c478bd9Sstevel@tonic-gate 			break;
42097c478bd9Sstevel@tonic-gate 
42107c478bd9Sstevel@tonic-gate 		  case 'F':		/* flag bits */
42117c478bd9Sstevel@tonic-gate 			if (strncmp(bp, "From ", 5) == 0)
42127c478bd9Sstevel@tonic-gate 			{
42137c478bd9Sstevel@tonic-gate 				/* we are being spoofed! */
42147c478bd9Sstevel@tonic-gate 				syserr("SECURITY ALERT: bogus qf line %s", bp);
42157c478bd9Sstevel@tonic-gate 				err = "bogus queue line";
42167c478bd9Sstevel@tonic-gate 				goto fail;
42177c478bd9Sstevel@tonic-gate 			}
42187c478bd9Sstevel@tonic-gate 			for (p = &bp[1]; *p != '\0'; p++)
42197c478bd9Sstevel@tonic-gate 			{
42207c478bd9Sstevel@tonic-gate 				switch (*p)
42217c478bd9Sstevel@tonic-gate 				{
42227c478bd9Sstevel@tonic-gate 				  case '8':	/* has 8 bit data */
42237c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_HAS8BIT;
42247c478bd9Sstevel@tonic-gate 					break;
42257c478bd9Sstevel@tonic-gate 
42267c478bd9Sstevel@tonic-gate 				  case 'b':	/* delete Bcc: header */
42277c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_DELETE_BCC;
42287c478bd9Sstevel@tonic-gate 					break;
42297c478bd9Sstevel@tonic-gate 
42307c478bd9Sstevel@tonic-gate 				  case 'd':	/* envelope has DSN RET= */
42317c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_RET_PARAM;
42327c478bd9Sstevel@tonic-gate 					break;
42337c478bd9Sstevel@tonic-gate 
42347c478bd9Sstevel@tonic-gate 				  case 'n':	/* don't return body */
42357c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_NO_BODY_RETN;
42367c478bd9Sstevel@tonic-gate 					break;
42377c478bd9Sstevel@tonic-gate 
42387c478bd9Sstevel@tonic-gate 				  case 'r':	/* response */
42397c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_RESPONSE;
42407c478bd9Sstevel@tonic-gate 					break;
42417c478bd9Sstevel@tonic-gate 
42427c478bd9Sstevel@tonic-gate 				  case 's':	/* split */
42437c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_SPLIT;
42447c478bd9Sstevel@tonic-gate 					break;
42457c478bd9Sstevel@tonic-gate 
42467c478bd9Sstevel@tonic-gate 				  case 'w':	/* warning sent */
42477c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_WARNING;
42487c478bd9Sstevel@tonic-gate 					break;
42497c478bd9Sstevel@tonic-gate 				}
42507c478bd9Sstevel@tonic-gate 			}
42517c478bd9Sstevel@tonic-gate 			break;
42527c478bd9Sstevel@tonic-gate 
42537c478bd9Sstevel@tonic-gate 		  case 'q':		/* quarantine reason */
42547c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
42557c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
42567c478bd9Sstevel@tonic-gate 				  macid("{quarantine}"), e->e_quarmsg);
42577c478bd9Sstevel@tonic-gate 			break;
42587c478bd9Sstevel@tonic-gate 
42597c478bd9Sstevel@tonic-gate 		  case 'H':		/* header */
42607c478bd9Sstevel@tonic-gate 
42617c478bd9Sstevel@tonic-gate 			/*
42627c478bd9Sstevel@tonic-gate 			**  count size before chompheader() destroys the line.
42637c478bd9Sstevel@tonic-gate 			**  this isn't accurate due to macro expansion, but
42647c478bd9Sstevel@tonic-gate 			**  better than before. "-3" to skip H?? at least.
42657c478bd9Sstevel@tonic-gate 			*/
42667c478bd9Sstevel@tonic-gate 
42677c478bd9Sstevel@tonic-gate 			hdrsize += strlen(bp) - 3;
42687c478bd9Sstevel@tonic-gate 			(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
42697c478bd9Sstevel@tonic-gate 			break;
42707c478bd9Sstevel@tonic-gate 
42717c478bd9Sstevel@tonic-gate 		  case 'I':		/* data file's inode number */
42727c478bd9Sstevel@tonic-gate 			/* regenerated below */
42737c478bd9Sstevel@tonic-gate 			break;
42747c478bd9Sstevel@tonic-gate 
42757c478bd9Sstevel@tonic-gate 		  case 'K':		/* time of last delivery attempt */
42767c478bd9Sstevel@tonic-gate 			e->e_dtime = atol(&buf[1]);
42777c478bd9Sstevel@tonic-gate 			break;
42787c478bd9Sstevel@tonic-gate 
42797c478bd9Sstevel@tonic-gate 		  case 'L':		/* Solaris Content-Length: */
42807c478bd9Sstevel@tonic-gate 		  case 'M':		/* message */
42817c478bd9Sstevel@tonic-gate 			/* ignore this; we want a new message next time */
42827c478bd9Sstevel@tonic-gate 			break;
42837c478bd9Sstevel@tonic-gate 
42847c478bd9Sstevel@tonic-gate 		  case 'N':		/* number of delivery attempts */
42857c478bd9Sstevel@tonic-gate 			e->e_ntries = atoi(&buf[1]);
42867c478bd9Sstevel@tonic-gate 
42877c478bd9Sstevel@tonic-gate 			/* if this has been tried recently, let it be */
42887c478bd9Sstevel@tonic-gate 			now = curtime();
42897c478bd9Sstevel@tonic-gate 			if (e->e_ntries > 0 && e->e_dtime <= now &&
42907c478bd9Sstevel@tonic-gate 			    now < e->e_dtime + MinQueueAge)
42917c478bd9Sstevel@tonic-gate 			{
42927c478bd9Sstevel@tonic-gate 				char *howlong;
42937c478bd9Sstevel@tonic-gate 
42947c478bd9Sstevel@tonic-gate 				howlong = pintvl(now - e->e_dtime, true);
42957c478bd9Sstevel@tonic-gate 				if (Verbose)
42967c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
42977c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
42987c478bd9Sstevel@tonic-gate 							     "%s: too young (%s)\n",
42997c478bd9Sstevel@tonic-gate 							     e->e_id, howlong);
43007c478bd9Sstevel@tonic-gate 				if (tTd(40, 8))
43017c478bd9Sstevel@tonic-gate 					sm_dprintf("%s: too young (%s)\n",
43027c478bd9Sstevel@tonic-gate 						e->e_id, howlong);
43037c478bd9Sstevel@tonic-gate 				if (LogLevel > 19)
43047c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_DEBUG, e->e_id,
43057c478bd9Sstevel@tonic-gate 						  "too young (%s)",
43067c478bd9Sstevel@tonic-gate 						  howlong);
43077c478bd9Sstevel@tonic-gate 				e->e_id = NULL;
43087c478bd9Sstevel@tonic-gate 				unlockqueue(e);
4309*058561cbSjbeck 				if (bp != buf)
4310*058561cbSjbeck 					sm_free(bp);
43117c478bd9Sstevel@tonic-gate 				return false;
43127c478bd9Sstevel@tonic-gate 			}
43137c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP,
43147c478bd9Sstevel@tonic-gate 				macid("{ntries}"), &buf[1]);
43157c478bd9Sstevel@tonic-gate 
43167c478bd9Sstevel@tonic-gate #if NAMED_BIND
43177c478bd9Sstevel@tonic-gate 			/* adjust BIND parameters immediately */
43187c478bd9Sstevel@tonic-gate 			if (e->e_ntries == 0)
43197c478bd9Sstevel@tonic-gate 			{
43207c478bd9Sstevel@tonic-gate 				_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
43217c478bd9Sstevel@tonic-gate 				_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
43227c478bd9Sstevel@tonic-gate 			}
43237c478bd9Sstevel@tonic-gate 			else
43247c478bd9Sstevel@tonic-gate 			{
43257c478bd9Sstevel@tonic-gate 				_res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
43267c478bd9Sstevel@tonic-gate 				_res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
43277c478bd9Sstevel@tonic-gate 			}
43287c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
43297c478bd9Sstevel@tonic-gate 			break;
43307c478bd9Sstevel@tonic-gate 
43317c478bd9Sstevel@tonic-gate 		  case 'P':		/* message priority */
43327c478bd9Sstevel@tonic-gate 			e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
43337c478bd9Sstevel@tonic-gate 			break;
43347c478bd9Sstevel@tonic-gate 
43357c478bd9Sstevel@tonic-gate 		  case 'Q':		/* original recipient */
43367c478bd9Sstevel@tonic-gate 			orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
43377c478bd9Sstevel@tonic-gate 			break;
43387c478bd9Sstevel@tonic-gate 
43397c478bd9Sstevel@tonic-gate 		  case 'r':		/* final recipient */
43407c478bd9Sstevel@tonic-gate 			frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
43417c478bd9Sstevel@tonic-gate 			break;
43427c478bd9Sstevel@tonic-gate 
43437c478bd9Sstevel@tonic-gate 		  case 'R':		/* specify recipient */
43447c478bd9Sstevel@tonic-gate 			p = bp;
43457c478bd9Sstevel@tonic-gate 			qflags = 0;
43467c478bd9Sstevel@tonic-gate 			if (qfver >= 1)
43477c478bd9Sstevel@tonic-gate 			{
43487c478bd9Sstevel@tonic-gate 				/* get flag bits */
43497c478bd9Sstevel@tonic-gate 				while (*++p != '\0' && *p != ':')
43507c478bd9Sstevel@tonic-gate 				{
43517c478bd9Sstevel@tonic-gate 					switch (*p)
43527c478bd9Sstevel@tonic-gate 					{
43537c478bd9Sstevel@tonic-gate 					  case 'N':
43547c478bd9Sstevel@tonic-gate 						qflags |= QHASNOTIFY;
43557c478bd9Sstevel@tonic-gate 						break;
43567c478bd9Sstevel@tonic-gate 
43577c478bd9Sstevel@tonic-gate 					  case 'S':
43587c478bd9Sstevel@tonic-gate 						qflags |= QPINGONSUCCESS;
43597c478bd9Sstevel@tonic-gate 						break;
43607c478bd9Sstevel@tonic-gate 
43617c478bd9Sstevel@tonic-gate 					  case 'F':
43627c478bd9Sstevel@tonic-gate 						qflags |= QPINGONFAILURE;
43637c478bd9Sstevel@tonic-gate 						break;
43647c478bd9Sstevel@tonic-gate 
43657c478bd9Sstevel@tonic-gate 					  case 'D':
43667c478bd9Sstevel@tonic-gate 						qflags |= QPINGONDELAY;
43677c478bd9Sstevel@tonic-gate 						break;
43687c478bd9Sstevel@tonic-gate 
43697c478bd9Sstevel@tonic-gate 					  case 'P':
43707c478bd9Sstevel@tonic-gate 						qflags |= QPRIMARY;
43717c478bd9Sstevel@tonic-gate 						break;
43727c478bd9Sstevel@tonic-gate 
43737c478bd9Sstevel@tonic-gate 					  case 'A':
43747c478bd9Sstevel@tonic-gate 						if (ctladdr != NULL)
43757c478bd9Sstevel@tonic-gate 							ctladdr->q_flags |= QALIAS;
43767c478bd9Sstevel@tonic-gate 						break;
43777c478bd9Sstevel@tonic-gate 
43787c478bd9Sstevel@tonic-gate 					  default: /* ignore or complain? */
43797c478bd9Sstevel@tonic-gate 						break;
43807c478bd9Sstevel@tonic-gate 					}
43817c478bd9Sstevel@tonic-gate 				}
43827c478bd9Sstevel@tonic-gate 			}
43837c478bd9Sstevel@tonic-gate 			else
43847c478bd9Sstevel@tonic-gate 				qflags |= QPRIMARY;
43857c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
43867c478bd9Sstevel@tonic-gate 				"e r");
43877c478bd9Sstevel@tonic-gate 			if (*p != '\0')
43887c478bd9Sstevel@tonic-gate 				q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0',
43897c478bd9Sstevel@tonic-gate 						NULL, e, true);
43907c478bd9Sstevel@tonic-gate 			else
43917c478bd9Sstevel@tonic-gate 				q = NULL;
43927c478bd9Sstevel@tonic-gate 			if (q != NULL)
43937c478bd9Sstevel@tonic-gate 			{
43947c478bd9Sstevel@tonic-gate 				/* make sure we keep the current qgrp */
43957c478bd9Sstevel@tonic-gate 				if (ISVALIDQGRP(e->e_qgrp))
43967c478bd9Sstevel@tonic-gate 					q->q_qgrp = e->e_qgrp;
43977c478bd9Sstevel@tonic-gate 				q->q_alias = ctladdr;
43987c478bd9Sstevel@tonic-gate 				if (qfver >= 1)
43997c478bd9Sstevel@tonic-gate 					q->q_flags &= ~Q_PINGFLAGS;
44007c478bd9Sstevel@tonic-gate 				q->q_flags |= qflags;
44017c478bd9Sstevel@tonic-gate 				q->q_finalrcpt = frcpt;
44027c478bd9Sstevel@tonic-gate 				q->q_orcpt = orcpt;
44037c478bd9Sstevel@tonic-gate 				(void) recipient(q, &e->e_sendqueue, 0, e);
44047c478bd9Sstevel@tonic-gate 			}
44057c478bd9Sstevel@tonic-gate 			frcpt = NULL;
44067c478bd9Sstevel@tonic-gate 			orcpt = NULL;
44077c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
44087c478bd9Sstevel@tonic-gate 				NULL);
44097c478bd9Sstevel@tonic-gate 			break;
44107c478bd9Sstevel@tonic-gate 
44117c478bd9Sstevel@tonic-gate 		  case 'S':		/* sender */
44127c478bd9Sstevel@tonic-gate 			setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
44137c478bd9Sstevel@tonic-gate 				  e, NULL, '\0', true);
44147c478bd9Sstevel@tonic-gate 			break;
44157c478bd9Sstevel@tonic-gate 
44167c478bd9Sstevel@tonic-gate 		  case 'T':		/* init time */
44177c478bd9Sstevel@tonic-gate 			e->e_ctime = atol(&bp[1]);
44187c478bd9Sstevel@tonic-gate 			break;
44197c478bd9Sstevel@tonic-gate 
44207c478bd9Sstevel@tonic-gate 		  case 'V':		/* queue file version number */
44217c478bd9Sstevel@tonic-gate 			qfver = atoi(&bp[1]);
44227c478bd9Sstevel@tonic-gate 			if (qfver <= QF_VERSION)
44237c478bd9Sstevel@tonic-gate 				break;
44247c478bd9Sstevel@tonic-gate 			syserr("Version number in queue file (%d) greater than max (%d)",
44257c478bd9Sstevel@tonic-gate 				qfver, QF_VERSION);
44267c478bd9Sstevel@tonic-gate 			err = "unsupported queue file version";
44277c478bd9Sstevel@tonic-gate 			goto fail;
44287c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
44297c478bd9Sstevel@tonic-gate 			break;
44307c478bd9Sstevel@tonic-gate 
44317c478bd9Sstevel@tonic-gate 		  case 'Z':		/* original envelope id from ESMTP */
44327c478bd9Sstevel@tonic-gate 			e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
44337c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
44347c478bd9Sstevel@tonic-gate 				macid("{dsn_envid}"), e->e_envid);
44357c478bd9Sstevel@tonic-gate 			break;
44367c478bd9Sstevel@tonic-gate 
44377c478bd9Sstevel@tonic-gate 		  case '!':		/* deliver by */
44387c478bd9Sstevel@tonic-gate 
44397c478bd9Sstevel@tonic-gate 			/* format: flag (1 char) space long-integer */
44407c478bd9Sstevel@tonic-gate 			e->e_dlvr_flag = buf[1];
44417c478bd9Sstevel@tonic-gate 			e->e_deliver_by = strtol(&buf[3], NULL, 10);
44427c478bd9Sstevel@tonic-gate 
44437c478bd9Sstevel@tonic-gate 		  case '$':		/* define macro */
44447c478bd9Sstevel@tonic-gate 			{
44457c478bd9Sstevel@tonic-gate 				char *p;
44467c478bd9Sstevel@tonic-gate 
44477c478bd9Sstevel@tonic-gate 				/* XXX elimate p? */
44487c478bd9Sstevel@tonic-gate 				r = macid_parse(&bp[1], &ep);
44497c478bd9Sstevel@tonic-gate 				if (r == 0)
44507c478bd9Sstevel@tonic-gate 					break;
44517c478bd9Sstevel@tonic-gate 				p = sm_rpool_strdup_x(e->e_rpool, ep);
44527c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM, r, p);
44537c478bd9Sstevel@tonic-gate 			}
44547c478bd9Sstevel@tonic-gate 			break;
44557c478bd9Sstevel@tonic-gate 
44567c478bd9Sstevel@tonic-gate 		  case '.':		/* terminate file */
44577c478bd9Sstevel@tonic-gate 			nomore = true;
44587c478bd9Sstevel@tonic-gate 			break;
44597c478bd9Sstevel@tonic-gate 
44607c478bd9Sstevel@tonic-gate #if _FFR_QUEUEDELAY
44617c478bd9Sstevel@tonic-gate 		  case 'G':
44627c478bd9Sstevel@tonic-gate 		  case 'Y':
44637c478bd9Sstevel@tonic-gate 
44647c478bd9Sstevel@tonic-gate 			/*
44657c478bd9Sstevel@tonic-gate 			**  Maintain backward compatibility for
44667c478bd9Sstevel@tonic-gate 			**  users who defined _FFR_QUEUEDELAY in
44677c478bd9Sstevel@tonic-gate 			**  previous releases.  Remove this
44687c478bd9Sstevel@tonic-gate 			**  code in 8.14 or 8.15.
44697c478bd9Sstevel@tonic-gate 			*/
44707c478bd9Sstevel@tonic-gate 
44717c478bd9Sstevel@tonic-gate 			if (qfver == 5 || qfver == 7)
44727c478bd9Sstevel@tonic-gate 				break;
44737c478bd9Sstevel@tonic-gate 
44747c478bd9Sstevel@tonic-gate 			/* If not qfver 5 or 7, then 'G' or 'Y' is invalid */
44757c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
44767c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUEDELAY */
44777c478bd9Sstevel@tonic-gate 
44787c478bd9Sstevel@tonic-gate 		  default:
44797c478bd9Sstevel@tonic-gate 			syserr("readqf: %s: line %d: bad line \"%s\"",
44807c478bd9Sstevel@tonic-gate 				qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
44817c478bd9Sstevel@tonic-gate 			err = "unrecognized line";
44827c478bd9Sstevel@tonic-gate 			goto fail;
44837c478bd9Sstevel@tonic-gate 		}
44847c478bd9Sstevel@tonic-gate 
44857c478bd9Sstevel@tonic-gate 		if (bp != buf)
4486*058561cbSjbeck 			SM_FREE(bp);
44877c478bd9Sstevel@tonic-gate 	}
44887c478bd9Sstevel@tonic-gate 
44897c478bd9Sstevel@tonic-gate 	/*
44907c478bd9Sstevel@tonic-gate 	**  If we haven't read any lines, this queue file is empty.
44917c478bd9Sstevel@tonic-gate 	**  Arrange to remove it without referencing any null pointers.
44927c478bd9Sstevel@tonic-gate 	*/
44937c478bd9Sstevel@tonic-gate 
44947c478bd9Sstevel@tonic-gate 	if (LineNumber == 0)
44957c478bd9Sstevel@tonic-gate 	{
44967c478bd9Sstevel@tonic-gate 		errno = 0;
44977c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
44987c478bd9Sstevel@tonic-gate 		return true;
44997c478bd9Sstevel@tonic-gate 	}
45007c478bd9Sstevel@tonic-gate 
45017c478bd9Sstevel@tonic-gate 	/* Check to make sure we have a complete queue file read */
45027c478bd9Sstevel@tonic-gate 	if (!nomore)
45037c478bd9Sstevel@tonic-gate 	{
45047c478bd9Sstevel@tonic-gate 		syserr("readqf: %s: incomplete queue file read", qf);
45057c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
45067c478bd9Sstevel@tonic-gate 		return false;
45077c478bd9Sstevel@tonic-gate 	}
45087c478bd9Sstevel@tonic-gate 
45097c478bd9Sstevel@tonic-gate 	/* possibly set ${dsn_ret} macro */
45107c478bd9Sstevel@tonic-gate 	if (bitset(EF_RET_PARAM, e->e_flags))
45117c478bd9Sstevel@tonic-gate 	{
45127c478bd9Sstevel@tonic-gate 		if (bitset(EF_NO_BODY_RETN, e->e_flags))
45137c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
45147c478bd9Sstevel@tonic-gate 				macid("{dsn_ret}"), "hdrs");
45157c478bd9Sstevel@tonic-gate 		else
45167c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
45177c478bd9Sstevel@tonic-gate 				macid("{dsn_ret}"), "full");
45187c478bd9Sstevel@tonic-gate 	}
45197c478bd9Sstevel@tonic-gate 
45207c478bd9Sstevel@tonic-gate 	/*
45217c478bd9Sstevel@tonic-gate 	**  Arrange to read the data file.
45227c478bd9Sstevel@tonic-gate 	*/
45237c478bd9Sstevel@tonic-gate 
45247c478bd9Sstevel@tonic-gate 	p = queuename(e, DATAFL_LETTER);
45257c478bd9Sstevel@tonic-gate 	e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY_B,
45267c478bd9Sstevel@tonic-gate 			      NULL);
45277c478bd9Sstevel@tonic-gate 	if (e->e_dfp == NULL)
45287c478bd9Sstevel@tonic-gate 	{
45297c478bd9Sstevel@tonic-gate 		syserr("readqf: cannot open %s", p);
45307c478bd9Sstevel@tonic-gate 	}
45317c478bd9Sstevel@tonic-gate 	else
45327c478bd9Sstevel@tonic-gate 	{
45337c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
45347c478bd9Sstevel@tonic-gate 		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
45357c478bd9Sstevel@tonic-gate 		    >= 0)
45367c478bd9Sstevel@tonic-gate 		{
45377c478bd9Sstevel@tonic-gate 			e->e_msgsize = st.st_size + hdrsize;
45387c478bd9Sstevel@tonic-gate 			e->e_dfdev = st.st_dev;
45397c478bd9Sstevel@tonic-gate 			e->e_dfino = ST_INODE(st);
4540*058561cbSjbeck 			(void) sm_snprintf(buf, sizeof(buf), "%ld",
45417c478bd9Sstevel@tonic-gate 					   e->e_msgsize);
45427c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"),
45437c478bd9Sstevel@tonic-gate 				  buf);
45447c478bd9Sstevel@tonic-gate 		}
45457c478bd9Sstevel@tonic-gate 	}
45467c478bd9Sstevel@tonic-gate 
45477c478bd9Sstevel@tonic-gate 	return true;
45487c478bd9Sstevel@tonic-gate 
45497c478bd9Sstevel@tonic-gate   fail:
45507c478bd9Sstevel@tonic-gate 	/*
45517c478bd9Sstevel@tonic-gate 	**  There was some error reading the qf file (reason is in err var.)
45527c478bd9Sstevel@tonic-gate 	**  Cleanup:
45537c478bd9Sstevel@tonic-gate 	**	close file; clear e_lockfp since it is the same as qfp,
45547c478bd9Sstevel@tonic-gate 	**	hence it is invalid (as file) after qfp is closed;
45557c478bd9Sstevel@tonic-gate 	**	the qf file is on disk, so set the flag to avoid calling
45567c478bd9Sstevel@tonic-gate 	**	queueup() with bogus data.
45577c478bd9Sstevel@tonic-gate 	*/
45587c478bd9Sstevel@tonic-gate 
4559*058561cbSjbeck 	if (bp != buf)
4560*058561cbSjbeck 		SM_FREE(bp);
45617c478bd9Sstevel@tonic-gate 	if (qfp != NULL)
45627c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
45637c478bd9Sstevel@tonic-gate 	e->e_lockfp = NULL;
45647c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_INQUEUE;
45657c478bd9Sstevel@tonic-gate 	loseqfile(e, err);
45667c478bd9Sstevel@tonic-gate 	return false;
45677c478bd9Sstevel@tonic-gate }
45687c478bd9Sstevel@tonic-gate /*
45697c478bd9Sstevel@tonic-gate **  PRTSTR -- print a string, "unprintable" characters are shown as \oct
45707c478bd9Sstevel@tonic-gate **
45717c478bd9Sstevel@tonic-gate **	Parameters:
45727c478bd9Sstevel@tonic-gate **		s -- string to print
45737c478bd9Sstevel@tonic-gate **		ml -- maximum length of output
45747c478bd9Sstevel@tonic-gate **
45757c478bd9Sstevel@tonic-gate **	Returns:
45767c478bd9Sstevel@tonic-gate **		number of entries
45777c478bd9Sstevel@tonic-gate **
45787c478bd9Sstevel@tonic-gate **	Side Effects:
45797c478bd9Sstevel@tonic-gate **		Prints a string on stdout.
45807c478bd9Sstevel@tonic-gate */
45817c478bd9Sstevel@tonic-gate 
4582*058561cbSjbeck static void prtstr __P((char *, int));
4583*058561cbSjbeck 
45847c478bd9Sstevel@tonic-gate static void
45857c478bd9Sstevel@tonic-gate prtstr(s, ml)
45867c478bd9Sstevel@tonic-gate 	char *s;
45877c478bd9Sstevel@tonic-gate 	int ml;
45887c478bd9Sstevel@tonic-gate {
45897c478bd9Sstevel@tonic-gate 	int c;
45907c478bd9Sstevel@tonic-gate 
45917c478bd9Sstevel@tonic-gate 	if (s == NULL)
45927c478bd9Sstevel@tonic-gate 		return;
45937c478bd9Sstevel@tonic-gate 	while (ml-- > 0 && ((c = *s++) != '\0'))
45947c478bd9Sstevel@tonic-gate 	{
45957c478bd9Sstevel@tonic-gate 		if (c == '\\')
45967c478bd9Sstevel@tonic-gate 		{
45977c478bd9Sstevel@tonic-gate 			if (ml-- > 0)
45987c478bd9Sstevel@tonic-gate 			{
45997c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46007c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46017c478bd9Sstevel@tonic-gate 			}
46027c478bd9Sstevel@tonic-gate 		}
46037c478bd9Sstevel@tonic-gate 		else if (isascii(c) && isprint(c))
46047c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46057c478bd9Sstevel@tonic-gate 		else
46067c478bd9Sstevel@tonic-gate 		{
46077c478bd9Sstevel@tonic-gate 			if ((ml -= 3) > 0)
46087c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
46097c478bd9Sstevel@tonic-gate 						     "\\%03o", c & 0xFF);
46107c478bd9Sstevel@tonic-gate 		}
46117c478bd9Sstevel@tonic-gate 	}
46127c478bd9Sstevel@tonic-gate }
46137c478bd9Sstevel@tonic-gate /*
46147c478bd9Sstevel@tonic-gate **  PRINTNQE -- print out number of entries in the mail queue
46157c478bd9Sstevel@tonic-gate **
46167c478bd9Sstevel@tonic-gate **	Parameters:
46177c478bd9Sstevel@tonic-gate **		out -- output file pointer.
46187c478bd9Sstevel@tonic-gate **		prefix -- string to output in front of each line.
46197c478bd9Sstevel@tonic-gate **
46207c478bd9Sstevel@tonic-gate **	Returns:
46217c478bd9Sstevel@tonic-gate **		none.
46227c478bd9Sstevel@tonic-gate */
46237c478bd9Sstevel@tonic-gate 
46247c478bd9Sstevel@tonic-gate void
46257c478bd9Sstevel@tonic-gate printnqe(out, prefix)
46267c478bd9Sstevel@tonic-gate 	SM_FILE_T *out;
46277c478bd9Sstevel@tonic-gate 	char *prefix;
46287c478bd9Sstevel@tonic-gate {
46297c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
46307c478bd9Sstevel@tonic-gate 	int i, k = 0, nrequests = 0;
46317c478bd9Sstevel@tonic-gate 	bool unknown = false;
46327c478bd9Sstevel@tonic-gate 
46337c478bd9Sstevel@tonic-gate 	if (ShmId == SM_SHM_NO_ID)
46347c478bd9Sstevel@tonic-gate 	{
46357c478bd9Sstevel@tonic-gate 		if (prefix == NULL)
46367c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46377c478bd9Sstevel@tonic-gate 					"Data unavailable: shared memory not updated\n");
46387c478bd9Sstevel@tonic-gate 		else
46397c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46407c478bd9Sstevel@tonic-gate 					"%sNOTCONFIGURED:-1\r\n", prefix);
46417c478bd9Sstevel@tonic-gate 		return;
46427c478bd9Sstevel@tonic-gate 	}
46437c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
46447c478bd9Sstevel@tonic-gate 	{
46457c478bd9Sstevel@tonic-gate 		int j;
46467c478bd9Sstevel@tonic-gate 
46477c478bd9Sstevel@tonic-gate 		k++;
46487c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; j++)
46497c478bd9Sstevel@tonic-gate 		{
46507c478bd9Sstevel@tonic-gate 			int n;
46517c478bd9Sstevel@tonic-gate 
46527c478bd9Sstevel@tonic-gate 			if (StopRequest)
46537c478bd9Sstevel@tonic-gate 				stop_sendmail();
46547c478bd9Sstevel@tonic-gate 
46557c478bd9Sstevel@tonic-gate 			n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
46567c478bd9Sstevel@tonic-gate 			if (prefix != NULL)
46577c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46587c478bd9Sstevel@tonic-gate 					"%s%s:%d\r\n",
46597c478bd9Sstevel@tonic-gate 					prefix, qid_printqueue(i, j), n);
46607c478bd9Sstevel@tonic-gate 			else if (n < 0)
46617c478bd9Sstevel@tonic-gate 			{
46627c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46637c478bd9Sstevel@tonic-gate 					"%s: unknown number of entries\n",
46647c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j));
46657c478bd9Sstevel@tonic-gate 				unknown = true;
46667c478bd9Sstevel@tonic-gate 			}
46677c478bd9Sstevel@tonic-gate 			else if (n == 0)
46687c478bd9Sstevel@tonic-gate 			{
46697c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46707c478bd9Sstevel@tonic-gate 					"%s is empty\n",
46717c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j));
46727c478bd9Sstevel@tonic-gate 			}
46737c478bd9Sstevel@tonic-gate 			else if (n > 0)
46747c478bd9Sstevel@tonic-gate 			{
46757c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46767c478bd9Sstevel@tonic-gate 					"%s: entries=%d\n",
46777c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j), n);
46787c478bd9Sstevel@tonic-gate 				nrequests += n;
46797c478bd9Sstevel@tonic-gate 				k++;
46807c478bd9Sstevel@tonic-gate 			}
46817c478bd9Sstevel@tonic-gate 		}
46827c478bd9Sstevel@tonic-gate 	}
46837c478bd9Sstevel@tonic-gate 	if (prefix == NULL && k > 1)
46847c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46857c478bd9Sstevel@tonic-gate 				     "\t\tTotal requests: %d%s\n",
46867c478bd9Sstevel@tonic-gate 				     nrequests, unknown ? " (about)" : "");
46877c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
46887c478bd9Sstevel@tonic-gate 	if (prefix == NULL)
46897c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46907c478bd9Sstevel@tonic-gate 			     "Data unavailable without shared memory support\n");
46917c478bd9Sstevel@tonic-gate 	else
46927c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46937c478bd9Sstevel@tonic-gate 			     "%sNOTAVAILABLE:-1\r\n", prefix);
46947c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
46957c478bd9Sstevel@tonic-gate }
46967c478bd9Sstevel@tonic-gate /*
46977c478bd9Sstevel@tonic-gate **  PRINTQUEUE -- print out a representation of the mail queue
46987c478bd9Sstevel@tonic-gate **
46997c478bd9Sstevel@tonic-gate **	Parameters:
47007c478bd9Sstevel@tonic-gate **		none.
47017c478bd9Sstevel@tonic-gate **
47027c478bd9Sstevel@tonic-gate **	Returns:
47037c478bd9Sstevel@tonic-gate **		none.
47047c478bd9Sstevel@tonic-gate **
47057c478bd9Sstevel@tonic-gate **	Side Effects:
47067c478bd9Sstevel@tonic-gate **		Prints a listing of the mail queue on the standard output.
47077c478bd9Sstevel@tonic-gate */
47087c478bd9Sstevel@tonic-gate 
47097c478bd9Sstevel@tonic-gate void
47107c478bd9Sstevel@tonic-gate printqueue()
47117c478bd9Sstevel@tonic-gate {
47127c478bd9Sstevel@tonic-gate 	int i, k = 0, nrequests = 0;
47137c478bd9Sstevel@tonic-gate 
47147c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
47157c478bd9Sstevel@tonic-gate 	{
47167c478bd9Sstevel@tonic-gate 		int j;
47177c478bd9Sstevel@tonic-gate 
47187c478bd9Sstevel@tonic-gate 		k++;
47197c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; j++)
47207c478bd9Sstevel@tonic-gate 		{
47217c478bd9Sstevel@tonic-gate 			if (StopRequest)
47227c478bd9Sstevel@tonic-gate 				stop_sendmail();
47237c478bd9Sstevel@tonic-gate 			nrequests += print_single_queue(i, j);
47247c478bd9Sstevel@tonic-gate 			k++;
47257c478bd9Sstevel@tonic-gate 		}
47267c478bd9Sstevel@tonic-gate 	}
47277c478bd9Sstevel@tonic-gate 	if (k > 1)
47287c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
47297c478bd9Sstevel@tonic-gate 				     "\t\tTotal requests: %d\n",
47307c478bd9Sstevel@tonic-gate 				     nrequests);
47317c478bd9Sstevel@tonic-gate }
47327c478bd9Sstevel@tonic-gate /*
47337c478bd9Sstevel@tonic-gate **  PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
47347c478bd9Sstevel@tonic-gate **
47357c478bd9Sstevel@tonic-gate **	Parameters:
47367c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group.
47377c478bd9Sstevel@tonic-gate **		qdir -- the queue directory.
47387c478bd9Sstevel@tonic-gate **
47397c478bd9Sstevel@tonic-gate **	Returns:
47407c478bd9Sstevel@tonic-gate **		number of requests in mail queue.
47417c478bd9Sstevel@tonic-gate **
47427c478bd9Sstevel@tonic-gate **	Side Effects:
47437c478bd9Sstevel@tonic-gate **		Prints a listing of the mail queue on the standard output.
47447c478bd9Sstevel@tonic-gate */
47457c478bd9Sstevel@tonic-gate 
47467c478bd9Sstevel@tonic-gate int
47477c478bd9Sstevel@tonic-gate print_single_queue(qgrp, qdir)
47487c478bd9Sstevel@tonic-gate 	int qgrp;
47497c478bd9Sstevel@tonic-gate 	int qdir;
47507c478bd9Sstevel@tonic-gate {
47517c478bd9Sstevel@tonic-gate 	register WORK *w;
47527c478bd9Sstevel@tonic-gate 	SM_FILE_T *f;
47537c478bd9Sstevel@tonic-gate 	int nrequests;
47547c478bd9Sstevel@tonic-gate 	char qd[MAXPATHLEN];
47557c478bd9Sstevel@tonic-gate 	char qddf[MAXPATHLEN];
47567c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
47577c478bd9Sstevel@tonic-gate 
47587c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
47597c478bd9Sstevel@tonic-gate 	{
4760*058561cbSjbeck 		(void) sm_strlcpy(qd, ".", sizeof(qd));
4761*058561cbSjbeck 		(void) sm_strlcpy(qddf, ".", sizeof(qddf));
47627c478bd9Sstevel@tonic-gate 	}
47637c478bd9Sstevel@tonic-gate 	else
47647c478bd9Sstevel@tonic-gate 	{
4765*058561cbSjbeck 		(void) sm_strlcpyn(qd, sizeof(qd), 2,
47667c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
47677c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBQF,
47687c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
47697c478bd9Sstevel@tonic-gate 					? "/qf" : ""));
4770*058561cbSjbeck 		(void) sm_strlcpyn(qddf, sizeof(qddf), 2,
47717c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
47727c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBDF,
47737c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
47747c478bd9Sstevel@tonic-gate 					? "/df" : ""));
47757c478bd9Sstevel@tonic-gate 	}
47767c478bd9Sstevel@tonic-gate 
47777c478bd9Sstevel@tonic-gate 	/*
47787c478bd9Sstevel@tonic-gate 	**  Check for permission to print the queue
47797c478bd9Sstevel@tonic-gate 	*/
47807c478bd9Sstevel@tonic-gate 
47817c478bd9Sstevel@tonic-gate 	if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
47827c478bd9Sstevel@tonic-gate 	{
47837c478bd9Sstevel@tonic-gate 		struct stat st;
47847c478bd9Sstevel@tonic-gate #ifdef NGROUPS_MAX
47857c478bd9Sstevel@tonic-gate 		int n;
47867c478bd9Sstevel@tonic-gate 		extern GIDSET_T InitialGidSet[NGROUPS_MAX];
47877c478bd9Sstevel@tonic-gate #endif /* NGROUPS_MAX */
47887c478bd9Sstevel@tonic-gate 
47897c478bd9Sstevel@tonic-gate 		if (stat(qd, &st) < 0)
47907c478bd9Sstevel@tonic-gate 		{
47917c478bd9Sstevel@tonic-gate 			syserr("Cannot stat %s",
47927c478bd9Sstevel@tonic-gate 				qid_printqueue(qgrp, qdir));
47937c478bd9Sstevel@tonic-gate 			return 0;
47947c478bd9Sstevel@tonic-gate 		}
47957c478bd9Sstevel@tonic-gate #ifdef NGROUPS_MAX
47967c478bd9Sstevel@tonic-gate 		n = NGROUPS_MAX;
47977c478bd9Sstevel@tonic-gate 		while (--n >= 0)
47987c478bd9Sstevel@tonic-gate 		{
47997c478bd9Sstevel@tonic-gate 			if (InitialGidSet[n] == st.st_gid)
48007c478bd9Sstevel@tonic-gate 				break;
48017c478bd9Sstevel@tonic-gate 		}
48027c478bd9Sstevel@tonic-gate 		if (n < 0 && RealGid != st.st_gid)
48037c478bd9Sstevel@tonic-gate #else /* NGROUPS_MAX */
48047c478bd9Sstevel@tonic-gate 		if (RealGid != st.st_gid)
48057c478bd9Sstevel@tonic-gate #endif /* NGROUPS_MAX */
48067c478bd9Sstevel@tonic-gate 		{
48077c478bd9Sstevel@tonic-gate 			usrerr("510 You are not permitted to see the queue");
48087c478bd9Sstevel@tonic-gate 			setstat(EX_NOPERM);
48097c478bd9Sstevel@tonic-gate 			return 0;
48107c478bd9Sstevel@tonic-gate 		}
48117c478bd9Sstevel@tonic-gate 	}
48127c478bd9Sstevel@tonic-gate 
48137c478bd9Sstevel@tonic-gate 	/*
48147c478bd9Sstevel@tonic-gate 	**  Read and order the queue.
48157c478bd9Sstevel@tonic-gate 	*/
48167c478bd9Sstevel@tonic-gate 
48177c478bd9Sstevel@tonic-gate 	nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
48187c478bd9Sstevel@tonic-gate 	(void) sortq(Queue[qgrp]->qg_maxlist);
48197c478bd9Sstevel@tonic-gate 
48207c478bd9Sstevel@tonic-gate 	/*
48217c478bd9Sstevel@tonic-gate 	**  Print the work list that we have read.
48227c478bd9Sstevel@tonic-gate 	*/
48237c478bd9Sstevel@tonic-gate 
48247c478bd9Sstevel@tonic-gate 	/* first see if there is anything */
48257c478bd9Sstevel@tonic-gate 	if (nrequests <= 0)
48267c478bd9Sstevel@tonic-gate 	{
48277c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
48287c478bd9Sstevel@tonic-gate 				     qid_printqueue(qgrp, qdir));
48297c478bd9Sstevel@tonic-gate 		return 0;
48307c478bd9Sstevel@tonic-gate 	}
48317c478bd9Sstevel@tonic-gate 
48327c478bd9Sstevel@tonic-gate 	sm_getla();	/* get load average */
48337c478bd9Sstevel@tonic-gate 
48347c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
48357c478bd9Sstevel@tonic-gate 			     qid_printqueue(qgrp, qdir),
48367c478bd9Sstevel@tonic-gate 			     nrequests, nrequests == 1 ? "" : "s");
48377c478bd9Sstevel@tonic-gate 	if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
48387c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48397c478bd9Sstevel@tonic-gate 				     ", only %d printed", MaxQueueRun);
48407c478bd9Sstevel@tonic-gate 	if (Verbose)
48417c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48427c478bd9Sstevel@tonic-gate 			")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
48437c478bd9Sstevel@tonic-gate 	else
48447c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout,  SM_TIME_DEFAULT,
48457c478bd9Sstevel@tonic-gate 			")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
48467c478bd9Sstevel@tonic-gate 	for (w = WorkQ; w != NULL; w = w->w_next)
48477c478bd9Sstevel@tonic-gate 	{
48487c478bd9Sstevel@tonic-gate 		struct stat st;
48497c478bd9Sstevel@tonic-gate 		auto time_t submittime = 0;
48507c478bd9Sstevel@tonic-gate 		long dfsize;
48517c478bd9Sstevel@tonic-gate 		int flags = 0;
48527c478bd9Sstevel@tonic-gate 		int qfver;
48537c478bd9Sstevel@tonic-gate 		char quarmsg[MAXLINE];
48547c478bd9Sstevel@tonic-gate 		char statmsg[MAXLINE];
48557c478bd9Sstevel@tonic-gate 		char bodytype[MAXNAME + 1];
48567c478bd9Sstevel@tonic-gate 		char qf[MAXPATHLEN];
48577c478bd9Sstevel@tonic-gate 
48587c478bd9Sstevel@tonic-gate 		if (StopRequest)
48597c478bd9Sstevel@tonic-gate 			stop_sendmail();
48607c478bd9Sstevel@tonic-gate 
48617c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
48627c478bd9Sstevel@tonic-gate 				     w->w_name + 2);
4863*058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", w->w_name);
48647c478bd9Sstevel@tonic-gate 		f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
48657c478bd9Sstevel@tonic-gate 			       NULL);
48667c478bd9Sstevel@tonic-gate 		if (f == NULL)
48677c478bd9Sstevel@tonic-gate 		{
48687c478bd9Sstevel@tonic-gate 			if (errno == EPERM)
48697c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48707c478bd9Sstevel@tonic-gate 						     " (permission denied)\n");
48717c478bd9Sstevel@tonic-gate 			else if (errno == ENOENT)
48727c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48737c478bd9Sstevel@tonic-gate 						     " (job completed)\n");
48747c478bd9Sstevel@tonic-gate 			else
48757c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48767c478bd9Sstevel@tonic-gate 						     " (%s)\n",
48777c478bd9Sstevel@tonic-gate 						     sm_errstring(errno));
48787c478bd9Sstevel@tonic-gate 			errno = 0;
48797c478bd9Sstevel@tonic-gate 			continue;
48807c478bd9Sstevel@tonic-gate 		}
48817c478bd9Sstevel@tonic-gate 		w->w_name[0] = DATAFL_LETTER;
4882*058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qddf, "/", w->w_name);
48837c478bd9Sstevel@tonic-gate 		if (stat(qf, &st) >= 0)
48847c478bd9Sstevel@tonic-gate 			dfsize = st.st_size;
48857c478bd9Sstevel@tonic-gate 		else
48867c478bd9Sstevel@tonic-gate 		{
48877c478bd9Sstevel@tonic-gate 			ENVELOPE e;
48887c478bd9Sstevel@tonic-gate 
48897c478bd9Sstevel@tonic-gate 			/*
48907c478bd9Sstevel@tonic-gate 			**  Maybe the df file can't be statted because
48917c478bd9Sstevel@tonic-gate 			**  it is in a different directory than the qf file.
48927c478bd9Sstevel@tonic-gate 			**  In order to find out, we must read the qf file.
48937c478bd9Sstevel@tonic-gate 			*/
48947c478bd9Sstevel@tonic-gate 
48957c478bd9Sstevel@tonic-gate 			newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
48967c478bd9Sstevel@tonic-gate 			e.e_id = w->w_name + 2;
48977c478bd9Sstevel@tonic-gate 			e.e_qgrp = qgrp;
48987c478bd9Sstevel@tonic-gate 			e.e_qdir = qdir;
48997c478bd9Sstevel@tonic-gate 			dfsize = -1;
49007c478bd9Sstevel@tonic-gate 			if (readqf(&e, false))
49017c478bd9Sstevel@tonic-gate 			{
49027c478bd9Sstevel@tonic-gate 				char *df = queuename(&e, DATAFL_LETTER);
49037c478bd9Sstevel@tonic-gate 				if (stat(df, &st) >= 0)
49047c478bd9Sstevel@tonic-gate 					dfsize = st.st_size;
49057c478bd9Sstevel@tonic-gate 			}
49067c478bd9Sstevel@tonic-gate 			if (e.e_lockfp != NULL)
49077c478bd9Sstevel@tonic-gate 			{
49087c478bd9Sstevel@tonic-gate 				(void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
49097c478bd9Sstevel@tonic-gate 				e.e_lockfp = NULL;
49107c478bd9Sstevel@tonic-gate 			}
49117c478bd9Sstevel@tonic-gate 			clearenvelope(&e, false, e.e_rpool);
49127c478bd9Sstevel@tonic-gate 			sm_rpool_free(e.e_rpool);
49137c478bd9Sstevel@tonic-gate 		}
49147c478bd9Sstevel@tonic-gate 		if (w->w_lock)
49157c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
49167c478bd9Sstevel@tonic-gate 		else if (QueueMode == QM_LOST)
49177c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
49187c478bd9Sstevel@tonic-gate 		else if (w->w_tooyoung)
49197c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
49207c478bd9Sstevel@tonic-gate 		else if (shouldqueue(w->w_pri, w->w_ctime))
49217c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
49227c478bd9Sstevel@tonic-gate 		else
49237c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
49247c478bd9Sstevel@tonic-gate 
49257c478bd9Sstevel@tonic-gate 		errno = 0;
49267c478bd9Sstevel@tonic-gate 
49277c478bd9Sstevel@tonic-gate 		quarmsg[0] = '\0';
49287c478bd9Sstevel@tonic-gate 		statmsg[0] = bodytype[0] = '\0';
49297c478bd9Sstevel@tonic-gate 		qfver = 0;
4930*058561cbSjbeck 		while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
49317c478bd9Sstevel@tonic-gate 		{
49327c478bd9Sstevel@tonic-gate 			register int i;
49337c478bd9Sstevel@tonic-gate 			register char *p;
49347c478bd9Sstevel@tonic-gate 
49357c478bd9Sstevel@tonic-gate 			if (StopRequest)
49367c478bd9Sstevel@tonic-gate 				stop_sendmail();
49377c478bd9Sstevel@tonic-gate 
49387c478bd9Sstevel@tonic-gate 			fixcrlf(buf, true);
49397c478bd9Sstevel@tonic-gate 			switch (buf[0])
49407c478bd9Sstevel@tonic-gate 			{
49417c478bd9Sstevel@tonic-gate 			  case 'V':	/* queue file version */
49427c478bd9Sstevel@tonic-gate 				qfver = atoi(&buf[1]);
49437c478bd9Sstevel@tonic-gate 				break;
49447c478bd9Sstevel@tonic-gate 
49457c478bd9Sstevel@tonic-gate 			  case 'M':	/* error message */
4946*058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(statmsg))
4947*058561cbSjbeck 					i = sizeof(statmsg) - 1;
49487c478bd9Sstevel@tonic-gate 				memmove(statmsg, &buf[1], i);
49497c478bd9Sstevel@tonic-gate 				statmsg[i] = '\0';
49507c478bd9Sstevel@tonic-gate 				break;
49517c478bd9Sstevel@tonic-gate 
49527c478bd9Sstevel@tonic-gate 			  case 'q':	/* quarantine reason */
4953*058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(quarmsg))
4954*058561cbSjbeck 					i = sizeof(quarmsg) - 1;
49557c478bd9Sstevel@tonic-gate 				memmove(quarmsg, &buf[1], i);
49567c478bd9Sstevel@tonic-gate 				quarmsg[i] = '\0';
49577c478bd9Sstevel@tonic-gate 				break;
49587c478bd9Sstevel@tonic-gate 
49597c478bd9Sstevel@tonic-gate 			  case 'B':	/* body type */
4960*058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(bodytype))
4961*058561cbSjbeck 					i = sizeof(bodytype) - 1;
49627c478bd9Sstevel@tonic-gate 				memmove(bodytype, &buf[1], i);
49637c478bd9Sstevel@tonic-gate 				bodytype[i] = '\0';
49647c478bd9Sstevel@tonic-gate 				break;
49657c478bd9Sstevel@tonic-gate 
49667c478bd9Sstevel@tonic-gate 			  case 'S':	/* sender name */
49677c478bd9Sstevel@tonic-gate 				if (Verbose)
49687c478bd9Sstevel@tonic-gate 				{
49697c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
49707c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
49717c478bd9Sstevel@tonic-gate 						"%8ld %10ld%c%.12s ",
49727c478bd9Sstevel@tonic-gate 						dfsize,
49737c478bd9Sstevel@tonic-gate 						w->w_pri,
49747c478bd9Sstevel@tonic-gate 						bitset(EF_WARNING, flags)
49757c478bd9Sstevel@tonic-gate 							? '+' : ' ',
49767c478bd9Sstevel@tonic-gate 						ctime(&submittime) + 4);
49777c478bd9Sstevel@tonic-gate 					prtstr(&buf[1], 78);
49787c478bd9Sstevel@tonic-gate 				}
49797c478bd9Sstevel@tonic-gate 				else
49807c478bd9Sstevel@tonic-gate 				{
49817c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
49827c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
49837c478bd9Sstevel@tonic-gate 						"%8ld %.16s ",
49847c478bd9Sstevel@tonic-gate 						dfsize,
49857c478bd9Sstevel@tonic-gate 						ctime(&submittime));
49867c478bd9Sstevel@tonic-gate 					prtstr(&buf[1], 39);
49877c478bd9Sstevel@tonic-gate 				}
49887c478bd9Sstevel@tonic-gate 
49897c478bd9Sstevel@tonic-gate 				if (quarmsg[0] != '\0')
49907c478bd9Sstevel@tonic-gate 				{
49917c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
49927c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
49937c478bd9Sstevel@tonic-gate 							     "\n     QUARANTINE: %.*s",
49947c478bd9Sstevel@tonic-gate 							     Verbose ? 100 : 60,
49957c478bd9Sstevel@tonic-gate 							     quarmsg);
49967c478bd9Sstevel@tonic-gate 					quarmsg[0] = '\0';
49977c478bd9Sstevel@tonic-gate 				}
49987c478bd9Sstevel@tonic-gate 
49997c478bd9Sstevel@tonic-gate 				if (statmsg[0] != '\0' || bodytype[0] != '\0')
50007c478bd9Sstevel@tonic-gate 				{
50017c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50027c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50037c478bd9Sstevel@tonic-gate 						"\n    %10.10s",
50047c478bd9Sstevel@tonic-gate 						bodytype);
50057c478bd9Sstevel@tonic-gate 					if (statmsg[0] != '\0')
50067c478bd9Sstevel@tonic-gate 						(void) sm_io_fprintf(smioout,
50077c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50087c478bd9Sstevel@tonic-gate 							"   (%.*s)",
50097c478bd9Sstevel@tonic-gate 							Verbose ? 100 : 60,
50107c478bd9Sstevel@tonic-gate 							statmsg);
50117c478bd9Sstevel@tonic-gate 					statmsg[0] = '\0';
50127c478bd9Sstevel@tonic-gate 				}
50137c478bd9Sstevel@tonic-gate 				break;
50147c478bd9Sstevel@tonic-gate 
50157c478bd9Sstevel@tonic-gate 			  case 'C':	/* controlling user */
50167c478bd9Sstevel@tonic-gate 				if (Verbose)
50177c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50187c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50197c478bd9Sstevel@tonic-gate 						"\n\t\t\t\t\t\t(---%.64s---)",
50207c478bd9Sstevel@tonic-gate 						&buf[1]);
50217c478bd9Sstevel@tonic-gate 				break;
50227c478bd9Sstevel@tonic-gate 
50237c478bd9Sstevel@tonic-gate 			  case 'R':	/* recipient name */
50247c478bd9Sstevel@tonic-gate 				p = &buf[1];
50257c478bd9Sstevel@tonic-gate 				if (qfver >= 1)
50267c478bd9Sstevel@tonic-gate 				{
50277c478bd9Sstevel@tonic-gate 					p = strchr(p, ':');
50287c478bd9Sstevel@tonic-gate 					if (p == NULL)
50297c478bd9Sstevel@tonic-gate 						break;
50307c478bd9Sstevel@tonic-gate 					p++;
50317c478bd9Sstevel@tonic-gate 				}
50327c478bd9Sstevel@tonic-gate 				if (Verbose)
50337c478bd9Sstevel@tonic-gate 				{
50347c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50357c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50367c478bd9Sstevel@tonic-gate 							"\n\t\t\t\t\t\t");
50377c478bd9Sstevel@tonic-gate 					prtstr(p, 71);
50387c478bd9Sstevel@tonic-gate 				}
50397c478bd9Sstevel@tonic-gate 				else
50407c478bd9Sstevel@tonic-gate 				{
50417c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50427c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50437c478bd9Sstevel@tonic-gate 							"\n\t\t\t\t\t ");
50447c478bd9Sstevel@tonic-gate 					prtstr(p, 38);
50457c478bd9Sstevel@tonic-gate 				}
50467c478bd9Sstevel@tonic-gate 				if (Verbose && statmsg[0] != '\0')
50477c478bd9Sstevel@tonic-gate 				{
50487c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50497c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50507c478bd9Sstevel@tonic-gate 							"\n\t\t (%.100s)",
50517c478bd9Sstevel@tonic-gate 							statmsg);
50527c478bd9Sstevel@tonic-gate 					statmsg[0] = '\0';
50537c478bd9Sstevel@tonic-gate 				}
50547c478bd9Sstevel@tonic-gate 				break;
50557c478bd9Sstevel@tonic-gate 
50567c478bd9Sstevel@tonic-gate 			  case 'T':	/* creation time */
50577c478bd9Sstevel@tonic-gate 				submittime = atol(&buf[1]);
50587c478bd9Sstevel@tonic-gate 				break;
50597c478bd9Sstevel@tonic-gate 
50607c478bd9Sstevel@tonic-gate 			  case 'F':	/* flag bits */
50617c478bd9Sstevel@tonic-gate 				for (p = &buf[1]; *p != '\0'; p++)
50627c478bd9Sstevel@tonic-gate 				{
50637c478bd9Sstevel@tonic-gate 					switch (*p)
50647c478bd9Sstevel@tonic-gate 					{
50657c478bd9Sstevel@tonic-gate 					  case 'w':
50667c478bd9Sstevel@tonic-gate 						flags |= EF_WARNING;
50677c478bd9Sstevel@tonic-gate 						break;
50687c478bd9Sstevel@tonic-gate 					}
50697c478bd9Sstevel@tonic-gate 				}
50707c478bd9Sstevel@tonic-gate 			}
50717c478bd9Sstevel@tonic-gate 		}
50727c478bd9Sstevel@tonic-gate 		if (submittime == (time_t) 0)
50737c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
50747c478bd9Sstevel@tonic-gate 					     " (no control file)");
50757c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
50767c478bd9Sstevel@tonic-gate 		(void) sm_io_close(f, SM_TIME_DEFAULT);
50777c478bd9Sstevel@tonic-gate 	}
50787c478bd9Sstevel@tonic-gate 	return nrequests;
50797c478bd9Sstevel@tonic-gate }
50807c478bd9Sstevel@tonic-gate 
50817c478bd9Sstevel@tonic-gate /*
50827c478bd9Sstevel@tonic-gate **  QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
50837c478bd9Sstevel@tonic-gate **
50847c478bd9Sstevel@tonic-gate **	Parameters:
50857c478bd9Sstevel@tonic-gate **		e -- envelope to build it in/from.
50867c478bd9Sstevel@tonic-gate **		type -- the file type, used as the first character
50877c478bd9Sstevel@tonic-gate **			of the file name.
50887c478bd9Sstevel@tonic-gate **
50897c478bd9Sstevel@tonic-gate **	Returns:
50907c478bd9Sstevel@tonic-gate **		the letter to use
50917c478bd9Sstevel@tonic-gate */
50927c478bd9Sstevel@tonic-gate 
50937c478bd9Sstevel@tonic-gate static char
50947c478bd9Sstevel@tonic-gate queue_letter(e, type)
50957c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
50967c478bd9Sstevel@tonic-gate 	int type;
50977c478bd9Sstevel@tonic-gate {
50987c478bd9Sstevel@tonic-gate 	/* Change type according to QueueMode */
50997c478bd9Sstevel@tonic-gate 	if (type == ANYQFL_LETTER)
51007c478bd9Sstevel@tonic-gate 	{
51017c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg != NULL)
51027c478bd9Sstevel@tonic-gate 			type = QUARQF_LETTER;
51037c478bd9Sstevel@tonic-gate 		else
51047c478bd9Sstevel@tonic-gate 		{
51057c478bd9Sstevel@tonic-gate 			switch (QueueMode)
51067c478bd9Sstevel@tonic-gate 			{
51077c478bd9Sstevel@tonic-gate 			  case QM_NORMAL:
51087c478bd9Sstevel@tonic-gate 				type = NORMQF_LETTER;
51097c478bd9Sstevel@tonic-gate 				break;
51107c478bd9Sstevel@tonic-gate 
51117c478bd9Sstevel@tonic-gate 			  case QM_QUARANTINE:
51127c478bd9Sstevel@tonic-gate 				type = QUARQF_LETTER;
51137c478bd9Sstevel@tonic-gate 				break;
51147c478bd9Sstevel@tonic-gate 
51157c478bd9Sstevel@tonic-gate 			  case QM_LOST:
51167c478bd9Sstevel@tonic-gate 				type = LOSEQF_LETTER;
51177c478bd9Sstevel@tonic-gate 				break;
51187c478bd9Sstevel@tonic-gate 
51197c478bd9Sstevel@tonic-gate 			  default:
51207c478bd9Sstevel@tonic-gate 				/* should never happen */
51217c478bd9Sstevel@tonic-gate 				abort();
51227c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
51237c478bd9Sstevel@tonic-gate 			}
51247c478bd9Sstevel@tonic-gate 		}
51257c478bd9Sstevel@tonic-gate 	}
51267c478bd9Sstevel@tonic-gate 	return type;
51277c478bd9Sstevel@tonic-gate }
51287c478bd9Sstevel@tonic-gate 
51297c478bd9Sstevel@tonic-gate /*
51307c478bd9Sstevel@tonic-gate **  QUEUENAME -- build a file name in the queue directory for this envelope.
51317c478bd9Sstevel@tonic-gate **
51327c478bd9Sstevel@tonic-gate **	Parameters:
51337c478bd9Sstevel@tonic-gate **		e -- envelope to build it in/from.
51347c478bd9Sstevel@tonic-gate **		type -- the file type, used as the first character
51357c478bd9Sstevel@tonic-gate **			of the file name.
51367c478bd9Sstevel@tonic-gate **
51377c478bd9Sstevel@tonic-gate **	Returns:
51387c478bd9Sstevel@tonic-gate **		a pointer to the queue name (in a static buffer).
51397c478bd9Sstevel@tonic-gate **
51407c478bd9Sstevel@tonic-gate **	Side Effects:
51417c478bd9Sstevel@tonic-gate **		If no id code is already assigned, queuename() will
51427c478bd9Sstevel@tonic-gate **		assign an id code with assign_queueid().  If no queue
51437c478bd9Sstevel@tonic-gate **		directory is assigned, one will be set with setnewqueue().
51447c478bd9Sstevel@tonic-gate */
51457c478bd9Sstevel@tonic-gate 
51467c478bd9Sstevel@tonic-gate char *
51477c478bd9Sstevel@tonic-gate queuename(e, type)
51487c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
51497c478bd9Sstevel@tonic-gate 	int type;
51507c478bd9Sstevel@tonic-gate {
51517c478bd9Sstevel@tonic-gate 	int qd, qg;
51527c478bd9Sstevel@tonic-gate 	char *sub = "/";
51537c478bd9Sstevel@tonic-gate 	char pref[3];
51547c478bd9Sstevel@tonic-gate 	static char buf[MAXPATHLEN];
51557c478bd9Sstevel@tonic-gate 
51567c478bd9Sstevel@tonic-gate 	/* Assign an ID if needed */
51577c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
51587c478bd9Sstevel@tonic-gate 		assign_queueid(e);
51597c478bd9Sstevel@tonic-gate 	type = queue_letter(e, type);
51607c478bd9Sstevel@tonic-gate 
51617c478bd9Sstevel@tonic-gate 	/* begin of filename */
51627c478bd9Sstevel@tonic-gate 	pref[0] = (char) type;
51637c478bd9Sstevel@tonic-gate 	pref[1] = 'f';
51647c478bd9Sstevel@tonic-gate 	pref[2] = '\0';
51657c478bd9Sstevel@tonic-gate 
51667c478bd9Sstevel@tonic-gate 	/* Assign a queue group/directory if needed */
51677c478bd9Sstevel@tonic-gate 	if (type == XSCRPT_LETTER)
51687c478bd9Sstevel@tonic-gate 	{
51697c478bd9Sstevel@tonic-gate 		/*
51707c478bd9Sstevel@tonic-gate 		**  We don't want to call setnewqueue() if we are fetching
51717c478bd9Sstevel@tonic-gate 		**  the pathname of the transcript file, because setnewqueue
51727c478bd9Sstevel@tonic-gate 		**  chooses a queue, and sometimes we need to write to the
51737c478bd9Sstevel@tonic-gate 		**  transcript file before we have gathered enough information
51747c478bd9Sstevel@tonic-gate 		**  to choose a queue.
51757c478bd9Sstevel@tonic-gate 		*/
51767c478bd9Sstevel@tonic-gate 
51777c478bd9Sstevel@tonic-gate 		if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
51787c478bd9Sstevel@tonic-gate 		{
51797c478bd9Sstevel@tonic-gate 			if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
51807c478bd9Sstevel@tonic-gate 			{
51817c478bd9Sstevel@tonic-gate 				e->e_xfqgrp = e->e_qgrp;
51827c478bd9Sstevel@tonic-gate 				e->e_xfqdir = e->e_qdir;
51837c478bd9Sstevel@tonic-gate 			}
51847c478bd9Sstevel@tonic-gate 			else
51857c478bd9Sstevel@tonic-gate 			{
51867c478bd9Sstevel@tonic-gate 				e->e_xfqgrp = 0;
51877c478bd9Sstevel@tonic-gate 				if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
51887c478bd9Sstevel@tonic-gate 					e->e_xfqdir = 0;
51897c478bd9Sstevel@tonic-gate 				else
51907c478bd9Sstevel@tonic-gate 				{
51917c478bd9Sstevel@tonic-gate 					e->e_xfqdir = get_rand_mod(
51927c478bd9Sstevel@tonic-gate 					      Queue[e->e_xfqgrp]->qg_numqueues);
51937c478bd9Sstevel@tonic-gate 				}
51947c478bd9Sstevel@tonic-gate 			}
51957c478bd9Sstevel@tonic-gate 		}
51967c478bd9Sstevel@tonic-gate 		qd = e->e_xfqdir;
51977c478bd9Sstevel@tonic-gate 		qg = e->e_xfqgrp;
51987c478bd9Sstevel@tonic-gate 	}
51997c478bd9Sstevel@tonic-gate 	else
52007c478bd9Sstevel@tonic-gate 	{
52017c478bd9Sstevel@tonic-gate 		if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
52023ee0e492Sjbeck 			(void) setnewqueue(e);
52037c478bd9Sstevel@tonic-gate 		if (type ==  DATAFL_LETTER)
52047c478bd9Sstevel@tonic-gate 		{
52057c478bd9Sstevel@tonic-gate 			qd = e->e_dfqdir;
52067c478bd9Sstevel@tonic-gate 			qg = e->e_dfqgrp;
52077c478bd9Sstevel@tonic-gate 		}
52087c478bd9Sstevel@tonic-gate 		else
52097c478bd9Sstevel@tonic-gate 		{
52107c478bd9Sstevel@tonic-gate 			qd = e->e_qdir;
52117c478bd9Sstevel@tonic-gate 			qg = e->e_qgrp;
52127c478bd9Sstevel@tonic-gate 		}
52137c478bd9Sstevel@tonic-gate 	}
52147c478bd9Sstevel@tonic-gate 
52157c478bd9Sstevel@tonic-gate 	/* xf files always have a valid qd and qg picked above */
52163ee0e492Sjbeck 	if ((qd == NOQDIR || qg == NOQGRP) && type != XSCRPT_LETTER)
5217*058561cbSjbeck 		(void) sm_strlcpyn(buf, sizeof(buf), 2, pref, e->e_id);
52187c478bd9Sstevel@tonic-gate 	else
52197c478bd9Sstevel@tonic-gate 	{
52207c478bd9Sstevel@tonic-gate 		switch (type)
52217c478bd9Sstevel@tonic-gate 		{
52227c478bd9Sstevel@tonic-gate 		  case DATAFL_LETTER:
52237c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52247c478bd9Sstevel@tonic-gate 				sub = "/df/";
52257c478bd9Sstevel@tonic-gate 			break;
52267c478bd9Sstevel@tonic-gate 
52277c478bd9Sstevel@tonic-gate 		  case QUARQF_LETTER:
52287c478bd9Sstevel@tonic-gate 		  case TEMPQF_LETTER:
52297c478bd9Sstevel@tonic-gate 		  case NEWQFL_LETTER:
52307c478bd9Sstevel@tonic-gate 		  case LOSEQF_LETTER:
52317c478bd9Sstevel@tonic-gate 		  case NORMQF_LETTER:
52327c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52337c478bd9Sstevel@tonic-gate 				sub = "/qf/";
52347c478bd9Sstevel@tonic-gate 			break;
52357c478bd9Sstevel@tonic-gate 
52367c478bd9Sstevel@tonic-gate 		  case XSCRPT_LETTER:
52377c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52387c478bd9Sstevel@tonic-gate 				sub = "/xf/";
52397c478bd9Sstevel@tonic-gate 			break;
52407c478bd9Sstevel@tonic-gate 
52417c478bd9Sstevel@tonic-gate 		  default:
52427c478bd9Sstevel@tonic-gate 			sm_abort("queuename: bad queue file type %d", type);
52437c478bd9Sstevel@tonic-gate 		}
52447c478bd9Sstevel@tonic-gate 
5245*058561cbSjbeck 		(void) sm_strlcpyn(buf, sizeof(buf), 4,
52467c478bd9Sstevel@tonic-gate 				Queue[qg]->qg_qpaths[qd].qp_name,
52477c478bd9Sstevel@tonic-gate 				sub, pref, e->e_id);
52487c478bd9Sstevel@tonic-gate 	}
52497c478bd9Sstevel@tonic-gate 
52507c478bd9Sstevel@tonic-gate 	if (tTd(7, 2))
52517c478bd9Sstevel@tonic-gate 		sm_dprintf("queuename: %s\n", buf);
52527c478bd9Sstevel@tonic-gate 	return buf;
52537c478bd9Sstevel@tonic-gate }
52547c478bd9Sstevel@tonic-gate 
52557c478bd9Sstevel@tonic-gate /*
52567c478bd9Sstevel@tonic-gate **  INIT_QID_ALG -- Initialize the (static) parameters that are used to
52577c478bd9Sstevel@tonic-gate **	generate a queue ID.
52587c478bd9Sstevel@tonic-gate **
52597c478bd9Sstevel@tonic-gate **	This function is called by the daemon to reset
52607c478bd9Sstevel@tonic-gate **	LastQueueTime and LastQueuePid which are used by assign_queueid().
52617c478bd9Sstevel@tonic-gate **	Otherwise the algorithm may cause problems because
52627c478bd9Sstevel@tonic-gate **	LastQueueTime and LastQueuePid are set indirectly by main()
52637c478bd9Sstevel@tonic-gate **	before the daemon process is started, hence LastQueuePid is not
52647c478bd9Sstevel@tonic-gate **	the pid of the daemon and therefore a child of the daemon can
52657c478bd9Sstevel@tonic-gate **	actually have the same pid as LastQueuePid which means the section
52667c478bd9Sstevel@tonic-gate **	in  assign_queueid():
52677c478bd9Sstevel@tonic-gate **	* see if we need to get a new base time/pid *
52687c478bd9Sstevel@tonic-gate **	is NOT triggered which will cause the same queue id to be generated.
52697c478bd9Sstevel@tonic-gate **
52707c478bd9Sstevel@tonic-gate **	Parameters:
52717c478bd9Sstevel@tonic-gate **		none
52727c478bd9Sstevel@tonic-gate **
52737c478bd9Sstevel@tonic-gate **	Returns:
52747c478bd9Sstevel@tonic-gate **		none.
52757c478bd9Sstevel@tonic-gate */
52767c478bd9Sstevel@tonic-gate 
52777c478bd9Sstevel@tonic-gate void
52787c478bd9Sstevel@tonic-gate init_qid_alg()
52797c478bd9Sstevel@tonic-gate {
52807c478bd9Sstevel@tonic-gate 	LastQueueTime = 0;
52817c478bd9Sstevel@tonic-gate 	LastQueuePid = -1;
52827c478bd9Sstevel@tonic-gate }
52837c478bd9Sstevel@tonic-gate 
52847c478bd9Sstevel@tonic-gate /*
52857c478bd9Sstevel@tonic-gate **  ASSIGN_QUEUEID -- assign a queue ID for this envelope.
52867c478bd9Sstevel@tonic-gate **
52877c478bd9Sstevel@tonic-gate **	Assigns an id code if one does not already exist.
52887c478bd9Sstevel@tonic-gate **	This code assumes that nothing will remain in the queue for
52897c478bd9Sstevel@tonic-gate **	longer than 60 years.  It is critical that files with the given
52907c478bd9Sstevel@tonic-gate **	name do not already exist in the queue.
52917c478bd9Sstevel@tonic-gate **	[No longer initializes e_qdir to NOQDIR.]
52927c478bd9Sstevel@tonic-gate **
52937c478bd9Sstevel@tonic-gate **	Parameters:
52947c478bd9Sstevel@tonic-gate **		e -- envelope to set it in.
52957c478bd9Sstevel@tonic-gate **
52967c478bd9Sstevel@tonic-gate **	Returns:
52977c478bd9Sstevel@tonic-gate **		none.
52987c478bd9Sstevel@tonic-gate */
52997c478bd9Sstevel@tonic-gate 
53007c478bd9Sstevel@tonic-gate static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
53017c478bd9Sstevel@tonic-gate # define QIC_LEN	60
53027c478bd9Sstevel@tonic-gate # define QIC_LEN_R	62
53037c478bd9Sstevel@tonic-gate 
53047c478bd9Sstevel@tonic-gate /*
53057c478bd9Sstevel@tonic-gate **  Note: the length is "officially" 60 because minutes and seconds are
53067c478bd9Sstevel@tonic-gate **	usually only 0-59.  However (Linux):
53077c478bd9Sstevel@tonic-gate **       tm_sec The number of seconds after the minute, normally in
53087c478bd9Sstevel@tonic-gate **		the range 0 to 59, but can be up to 61 to allow for
53097c478bd9Sstevel@tonic-gate **		leap seconds.
53107c478bd9Sstevel@tonic-gate **	Hence the real length of the string is 62 to take this into account.
53117c478bd9Sstevel@tonic-gate **	Alternatively % QIC_LEN can (should) be used for access everywhere.
53127c478bd9Sstevel@tonic-gate */
53137c478bd9Sstevel@tonic-gate 
53147c478bd9Sstevel@tonic-gate # define queuenextid() CurrentPid
53157c478bd9Sstevel@tonic-gate 
53167c478bd9Sstevel@tonic-gate 
53177c478bd9Sstevel@tonic-gate void
53187c478bd9Sstevel@tonic-gate assign_queueid(e)
53197c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
53207c478bd9Sstevel@tonic-gate {
53217c478bd9Sstevel@tonic-gate 	pid_t pid = queuenextid();
53227c478bd9Sstevel@tonic-gate 	static int cX = 0;
53237c478bd9Sstevel@tonic-gate 	static long random_offset;
53247c478bd9Sstevel@tonic-gate 	struct tm *tm;
53257c478bd9Sstevel@tonic-gate 	char idbuf[MAXQFNAME - 2];
53267c478bd9Sstevel@tonic-gate 	int seq;
53277c478bd9Sstevel@tonic-gate 
53287c478bd9Sstevel@tonic-gate 	if (e->e_id != NULL)
53297c478bd9Sstevel@tonic-gate 		return;
53307c478bd9Sstevel@tonic-gate 
53317c478bd9Sstevel@tonic-gate 	/* see if we need to get a new base time/pid */
53327c478bd9Sstevel@tonic-gate 	if (cX >= QIC_LEN * QIC_LEN || LastQueueTime == 0 ||
53337c478bd9Sstevel@tonic-gate 	    LastQueuePid != pid)
53347c478bd9Sstevel@tonic-gate 	{
53357c478bd9Sstevel@tonic-gate 		time_t then = LastQueueTime;
53367c478bd9Sstevel@tonic-gate 
53377c478bd9Sstevel@tonic-gate 		/* if the first time through, pick a random offset */
53387c478bd9Sstevel@tonic-gate 		if (LastQueueTime == 0)
53397c478bd9Sstevel@tonic-gate 			random_offset = get_random();
53407c478bd9Sstevel@tonic-gate 
53417c478bd9Sstevel@tonic-gate 		while ((LastQueueTime = curtime()) == then &&
53427c478bd9Sstevel@tonic-gate 		       LastQueuePid == pid)
53437c478bd9Sstevel@tonic-gate 		{
53447c478bd9Sstevel@tonic-gate 			(void) sleep(1);
53457c478bd9Sstevel@tonic-gate 		}
53467c478bd9Sstevel@tonic-gate 		LastQueuePid = queuenextid();
53477c478bd9Sstevel@tonic-gate 		cX = 0;
53487c478bd9Sstevel@tonic-gate 	}
53497c478bd9Sstevel@tonic-gate 
53507c478bd9Sstevel@tonic-gate 	/*
53517c478bd9Sstevel@tonic-gate 	**  Generate a new sequence number between 0 and QIC_LEN*QIC_LEN-1.
53527c478bd9Sstevel@tonic-gate 	**  This lets us generate up to QIC_LEN*QIC_LEN unique queue ids
53537c478bd9Sstevel@tonic-gate 	**  per second, per process.  With envelope splitting,
53547c478bd9Sstevel@tonic-gate 	**  a single message can consume many queue ids.
53557c478bd9Sstevel@tonic-gate 	*/
53567c478bd9Sstevel@tonic-gate 
53577c478bd9Sstevel@tonic-gate 	seq = (int)((cX + random_offset) % (QIC_LEN * QIC_LEN));
53587c478bd9Sstevel@tonic-gate 	++cX;
53597c478bd9Sstevel@tonic-gate 	if (tTd(7, 50))
53607c478bd9Sstevel@tonic-gate 		sm_dprintf("assign_queueid: random_offset = %ld (%d)\n",
53617c478bd9Sstevel@tonic-gate 			random_offset, seq);
53627c478bd9Sstevel@tonic-gate 
53637c478bd9Sstevel@tonic-gate 	tm = gmtime(&LastQueueTime);
53647c478bd9Sstevel@tonic-gate 	idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
53657c478bd9Sstevel@tonic-gate 	idbuf[1] = QueueIdChars[tm->tm_mon];
53667c478bd9Sstevel@tonic-gate 	idbuf[2] = QueueIdChars[tm->tm_mday];
53677c478bd9Sstevel@tonic-gate 	idbuf[3] = QueueIdChars[tm->tm_hour];
53687c478bd9Sstevel@tonic-gate 	idbuf[4] = QueueIdChars[tm->tm_min % QIC_LEN_R];
53697c478bd9Sstevel@tonic-gate 	idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R];
53707c478bd9Sstevel@tonic-gate 	idbuf[6] = QueueIdChars[seq / QIC_LEN];
53717c478bd9Sstevel@tonic-gate 	idbuf[7] = QueueIdChars[seq % QIC_LEN];
5372*058561cbSjbeck 	(void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d",
53737c478bd9Sstevel@tonic-gate 			   (int) LastQueuePid);
53747c478bd9Sstevel@tonic-gate 	e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
53757c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
53767c478bd9Sstevel@tonic-gate #if 0
53777c478bd9Sstevel@tonic-gate 	/* XXX: inherited from MainEnvelope */
53787c478bd9Sstevel@tonic-gate 	e->e_qgrp = NOQGRP;  /* too early to do anything else */
53797c478bd9Sstevel@tonic-gate 	e->e_qdir = NOQDIR;
53807c478bd9Sstevel@tonic-gate 	e->e_xfqgrp = NOQGRP;
53817c478bd9Sstevel@tonic-gate #endif /* 0 */
53827c478bd9Sstevel@tonic-gate 
53837c478bd9Sstevel@tonic-gate 	/* New ID means it's not on disk yet */
53847c478bd9Sstevel@tonic-gate 	e->e_qfletter = '\0';
53857c478bd9Sstevel@tonic-gate 
53867c478bd9Sstevel@tonic-gate 	if (tTd(7, 1))
53877c478bd9Sstevel@tonic-gate 		sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
53887c478bd9Sstevel@tonic-gate 			e->e_id, e);
53897c478bd9Sstevel@tonic-gate 	if (LogLevel > 93)
53907c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
53917c478bd9Sstevel@tonic-gate }
53927c478bd9Sstevel@tonic-gate /*
53937c478bd9Sstevel@tonic-gate **  SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
53947c478bd9Sstevel@tonic-gate **
53957c478bd9Sstevel@tonic-gate **	Make sure one PID can't be used by two processes in any one second.
53967c478bd9Sstevel@tonic-gate **
53977c478bd9Sstevel@tonic-gate **		If the system rotates PIDs fast enough, may get the
53987c478bd9Sstevel@tonic-gate **		same pid in the same second for two distinct processes.
53997c478bd9Sstevel@tonic-gate **		This will interfere with the queue file naming system.
54007c478bd9Sstevel@tonic-gate **
54017c478bd9Sstevel@tonic-gate **	Parameters:
54027c478bd9Sstevel@tonic-gate **		none
54037c478bd9Sstevel@tonic-gate **
54047c478bd9Sstevel@tonic-gate **	Returns:
54057c478bd9Sstevel@tonic-gate **		none
54067c478bd9Sstevel@tonic-gate */
54077c478bd9Sstevel@tonic-gate 
54087c478bd9Sstevel@tonic-gate void
54097c478bd9Sstevel@tonic-gate sync_queue_time()
54107c478bd9Sstevel@tonic-gate {
54117c478bd9Sstevel@tonic-gate #if FAST_PID_RECYCLE
54127c478bd9Sstevel@tonic-gate 	if (OpMode != MD_TEST &&
54137c478bd9Sstevel@tonic-gate 	    OpMode != MD_VERIFY &&
54147c478bd9Sstevel@tonic-gate 	    LastQueueTime > 0 &&
54157c478bd9Sstevel@tonic-gate 	    LastQueuePid == CurrentPid &&
54167c478bd9Sstevel@tonic-gate 	    curtime() == LastQueueTime)
54177c478bd9Sstevel@tonic-gate 		(void) sleep(1);
54187c478bd9Sstevel@tonic-gate #endif /* FAST_PID_RECYCLE */
54197c478bd9Sstevel@tonic-gate }
54207c478bd9Sstevel@tonic-gate /*
54217c478bd9Sstevel@tonic-gate **  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
54227c478bd9Sstevel@tonic-gate **
54237c478bd9Sstevel@tonic-gate **	Parameters:
54247c478bd9Sstevel@tonic-gate **		e -- the envelope to unlock.
54257c478bd9Sstevel@tonic-gate **
54267c478bd9Sstevel@tonic-gate **	Returns:
54277c478bd9Sstevel@tonic-gate **		none
54287c478bd9Sstevel@tonic-gate **
54297c478bd9Sstevel@tonic-gate **	Side Effects:
54307c478bd9Sstevel@tonic-gate **		unlocks the queue for `e'.
54317c478bd9Sstevel@tonic-gate */
54327c478bd9Sstevel@tonic-gate 
54337c478bd9Sstevel@tonic-gate void
54347c478bd9Sstevel@tonic-gate unlockqueue(e)
54357c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
54367c478bd9Sstevel@tonic-gate {
54377c478bd9Sstevel@tonic-gate 	if (tTd(51, 4))
54387c478bd9Sstevel@tonic-gate 		sm_dprintf("unlockqueue(%s)\n",
54397c478bd9Sstevel@tonic-gate 			e->e_id == NULL ? "NOQUEUE" : e->e_id);
54407c478bd9Sstevel@tonic-gate 
54417c478bd9Sstevel@tonic-gate 
54427c478bd9Sstevel@tonic-gate 	/* if there is a lock file in the envelope, close it */
54437c478bd9Sstevel@tonic-gate 	if (e->e_lockfp != NULL)
54447c478bd9Sstevel@tonic-gate 		(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
54457c478bd9Sstevel@tonic-gate 	e->e_lockfp = NULL;
54467c478bd9Sstevel@tonic-gate 
54477c478bd9Sstevel@tonic-gate 	/* don't create a queue id if we don't already have one */
54487c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
54497c478bd9Sstevel@tonic-gate 		return;
54507c478bd9Sstevel@tonic-gate 
54517c478bd9Sstevel@tonic-gate 	/* remove the transcript */
54527c478bd9Sstevel@tonic-gate 	if (LogLevel > 87)
54537c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id, "unlock");
54547c478bd9Sstevel@tonic-gate 	if (!tTd(51, 104))
54557c478bd9Sstevel@tonic-gate 		(void) xunlink(queuename(e, XSCRPT_LETTER));
54567c478bd9Sstevel@tonic-gate }
54577c478bd9Sstevel@tonic-gate /*
54587c478bd9Sstevel@tonic-gate **  SETCTLUSER -- create a controlling address
54597c478bd9Sstevel@tonic-gate **
54607c478bd9Sstevel@tonic-gate **	Create a fake "address" given only a local login name; this is
54617c478bd9Sstevel@tonic-gate **	used as a "controlling user" for future recipient addresses.
54627c478bd9Sstevel@tonic-gate **
54637c478bd9Sstevel@tonic-gate **	Parameters:
54647c478bd9Sstevel@tonic-gate **		user -- the user name of the controlling user.
54657c478bd9Sstevel@tonic-gate **		qfver -- the version stamp of this queue file.
54667c478bd9Sstevel@tonic-gate **		e -- envelope
54677c478bd9Sstevel@tonic-gate **
54687c478bd9Sstevel@tonic-gate **	Returns:
54697c478bd9Sstevel@tonic-gate **		An address descriptor for the controlling user,
54707c478bd9Sstevel@tonic-gate **		using storage allocated from e->e_rpool.
54717c478bd9Sstevel@tonic-gate **
54727c478bd9Sstevel@tonic-gate */
54737c478bd9Sstevel@tonic-gate 
54747c478bd9Sstevel@tonic-gate static ADDRESS *
54757c478bd9Sstevel@tonic-gate setctluser(user, qfver, e)
54767c478bd9Sstevel@tonic-gate 	char *user;
54777c478bd9Sstevel@tonic-gate 	int qfver;
54787c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
54797c478bd9Sstevel@tonic-gate {
54807c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
54817c478bd9Sstevel@tonic-gate 	struct passwd *pw;
54827c478bd9Sstevel@tonic-gate 	char *p;
54837c478bd9Sstevel@tonic-gate 
54847c478bd9Sstevel@tonic-gate 	/*
54857c478bd9Sstevel@tonic-gate 	**  See if this clears our concept of controlling user.
54867c478bd9Sstevel@tonic-gate 	*/
54877c478bd9Sstevel@tonic-gate 
54887c478bd9Sstevel@tonic-gate 	if (user == NULL || *user == '\0')
54897c478bd9Sstevel@tonic-gate 		return NULL;
54907c478bd9Sstevel@tonic-gate 
54917c478bd9Sstevel@tonic-gate 	/*
54927c478bd9Sstevel@tonic-gate 	**  Set up addr fields for controlling user.
54937c478bd9Sstevel@tonic-gate 	*/
54947c478bd9Sstevel@tonic-gate 
5495*058561cbSjbeck 	a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a));
5496*058561cbSjbeck 	memset((char *) a, '\0', sizeof(*a));
54977c478bd9Sstevel@tonic-gate 
54987c478bd9Sstevel@tonic-gate 	if (*user == ':')
54997c478bd9Sstevel@tonic-gate 	{
55007c478bd9Sstevel@tonic-gate 		p = &user[1];
55017c478bd9Sstevel@tonic-gate 		a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
55027c478bd9Sstevel@tonic-gate 	}
55037c478bd9Sstevel@tonic-gate 	else
55047c478bd9Sstevel@tonic-gate 	{
55057c478bd9Sstevel@tonic-gate 		p = strtok(user, ":");
55067c478bd9Sstevel@tonic-gate 		a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
55077c478bd9Sstevel@tonic-gate 		if (qfver >= 2)
55087c478bd9Sstevel@tonic-gate 		{
55097c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55107c478bd9Sstevel@tonic-gate 				a->q_uid = atoi(p);
55117c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55127c478bd9Sstevel@tonic-gate 				a->q_gid = atoi(p);
55137c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55147c478bd9Sstevel@tonic-gate 			{
55157c478bd9Sstevel@tonic-gate 				char *o;
55167c478bd9Sstevel@tonic-gate 
55177c478bd9Sstevel@tonic-gate 				a->q_flags |= QGOODUID;
55187c478bd9Sstevel@tonic-gate 
55197c478bd9Sstevel@tonic-gate 				/* if there is another ':': restore it */
55207c478bd9Sstevel@tonic-gate 				if ((o = strtok(NULL, ":")) != NULL && o > p)
55217c478bd9Sstevel@tonic-gate 					o[-1] = ':';
55227c478bd9Sstevel@tonic-gate 			}
55237c478bd9Sstevel@tonic-gate 		}
55247c478bd9Sstevel@tonic-gate 		else if ((pw = sm_getpwnam(user)) != NULL)
55257c478bd9Sstevel@tonic-gate 		{
55267c478bd9Sstevel@tonic-gate 			if (*pw->pw_dir == '\0')
55277c478bd9Sstevel@tonic-gate 				a->q_home = NULL;
55287c478bd9Sstevel@tonic-gate 			else if (strcmp(pw->pw_dir, "/") == 0)
55297c478bd9Sstevel@tonic-gate 				a->q_home = "";
55307c478bd9Sstevel@tonic-gate 			else
55317c478bd9Sstevel@tonic-gate 				a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
55327c478bd9Sstevel@tonic-gate 			a->q_uid = pw->pw_uid;
55337c478bd9Sstevel@tonic-gate 			a->q_gid = pw->pw_gid;
55347c478bd9Sstevel@tonic-gate 			a->q_flags |= QGOODUID;
55357c478bd9Sstevel@tonic-gate 		}
55367c478bd9Sstevel@tonic-gate 	}
55377c478bd9Sstevel@tonic-gate 
55387c478bd9Sstevel@tonic-gate 	a->q_flags |= QPRIMARY;		/* flag as a "ctladdr" */
55397c478bd9Sstevel@tonic-gate 	a->q_mailer = LocalMailer;
55407c478bd9Sstevel@tonic-gate 	if (p == NULL)
55417c478bd9Sstevel@tonic-gate 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
55427c478bd9Sstevel@tonic-gate 	else
55437c478bd9Sstevel@tonic-gate 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
55447c478bd9Sstevel@tonic-gate 	return a;
55457c478bd9Sstevel@tonic-gate }
55467c478bd9Sstevel@tonic-gate /*
55477c478bd9Sstevel@tonic-gate **  LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
55487c478bd9Sstevel@tonic-gate **
55497c478bd9Sstevel@tonic-gate **	Parameters:
55507c478bd9Sstevel@tonic-gate **		e -- the envelope (e->e_id will be used).
55517c478bd9Sstevel@tonic-gate **		why -- reported to whomever can hear.
55527c478bd9Sstevel@tonic-gate **
55537c478bd9Sstevel@tonic-gate **	Returns:
55547c478bd9Sstevel@tonic-gate **		none.
55557c478bd9Sstevel@tonic-gate */
55567c478bd9Sstevel@tonic-gate 
55577c478bd9Sstevel@tonic-gate void
55587c478bd9Sstevel@tonic-gate loseqfile(e, why)
55597c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
55607c478bd9Sstevel@tonic-gate 	char *why;
55617c478bd9Sstevel@tonic-gate {
55627c478bd9Sstevel@tonic-gate 	bool loseit = true;
55637c478bd9Sstevel@tonic-gate 	char *p;
55647c478bd9Sstevel@tonic-gate 	char buf[MAXPATHLEN];
55657c478bd9Sstevel@tonic-gate 
55667c478bd9Sstevel@tonic-gate 	if (e == NULL || e->e_id == NULL)
55677c478bd9Sstevel@tonic-gate 		return;
55687c478bd9Sstevel@tonic-gate 	p = queuename(e, ANYQFL_LETTER);
5569*058561cbSjbeck 	if (sm_strlcpy(buf, p, sizeof(buf)) >= sizeof(buf))
55707c478bd9Sstevel@tonic-gate 		return;
55717c478bd9Sstevel@tonic-gate 	if (!bitset(EF_INQUEUE, e->e_flags))
55727c478bd9Sstevel@tonic-gate 		queueup(e, false, true);
55737c478bd9Sstevel@tonic-gate 	else if (QueueMode == QM_LOST)
55747c478bd9Sstevel@tonic-gate 		loseit = false;
55757c478bd9Sstevel@tonic-gate 
55767c478bd9Sstevel@tonic-gate 	/* if already lost, no need to re-lose */
55777c478bd9Sstevel@tonic-gate 	if (loseit)
55787c478bd9Sstevel@tonic-gate 	{
55797c478bd9Sstevel@tonic-gate 		p = queuename(e, LOSEQF_LETTER);
55807c478bd9Sstevel@tonic-gate 		if (rename(buf, p) < 0)
55817c478bd9Sstevel@tonic-gate 			syserr("cannot rename(%s, %s), uid=%d",
55827c478bd9Sstevel@tonic-gate 			       buf, p, (int) geteuid());
55837c478bd9Sstevel@tonic-gate 		else if (LogLevel > 0)
55847c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, e->e_id,
55857c478bd9Sstevel@tonic-gate 				  "Losing %s: %s", buf, why);
55867c478bd9Sstevel@tonic-gate 	}
55877c478bd9Sstevel@tonic-gate 	if (e->e_dfp != NULL)
55887c478bd9Sstevel@tonic-gate 	{
55897c478bd9Sstevel@tonic-gate 		(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
55907c478bd9Sstevel@tonic-gate 		e->e_dfp = NULL;
55917c478bd9Sstevel@tonic-gate 	}
55927c478bd9Sstevel@tonic-gate 	e->e_flags &= ~EF_HAS_DF;
55937c478bd9Sstevel@tonic-gate }
55947c478bd9Sstevel@tonic-gate /*
55957c478bd9Sstevel@tonic-gate **  NAME2QID -- translate a queue group name to a queue group id
55967c478bd9Sstevel@tonic-gate **
55977c478bd9Sstevel@tonic-gate **	Parameters:
55987c478bd9Sstevel@tonic-gate **		queuename -- name of queue group.
55997c478bd9Sstevel@tonic-gate **
56007c478bd9Sstevel@tonic-gate **	Returns:
56017c478bd9Sstevel@tonic-gate **		queue group id if found.
56027c478bd9Sstevel@tonic-gate **		NOQGRP otherwise.
56037c478bd9Sstevel@tonic-gate */
56047c478bd9Sstevel@tonic-gate 
56057c478bd9Sstevel@tonic-gate int
56067c478bd9Sstevel@tonic-gate name2qid(queuename)
56077c478bd9Sstevel@tonic-gate 	char *queuename;
56087c478bd9Sstevel@tonic-gate {
56097c478bd9Sstevel@tonic-gate 	register STAB *s;
56107c478bd9Sstevel@tonic-gate 
56117c478bd9Sstevel@tonic-gate 	s = stab(queuename, ST_QUEUE, ST_FIND);
56127c478bd9Sstevel@tonic-gate 	if (s == NULL)
56137c478bd9Sstevel@tonic-gate 		return NOQGRP;
56147c478bd9Sstevel@tonic-gate 	return s->s_quegrp->qg_index;
56157c478bd9Sstevel@tonic-gate }
56167c478bd9Sstevel@tonic-gate /*
56177c478bd9Sstevel@tonic-gate **  QID_PRINTNAME -- create externally printable version of queue id
56187c478bd9Sstevel@tonic-gate **
56197c478bd9Sstevel@tonic-gate **	Parameters:
56207c478bd9Sstevel@tonic-gate **		e -- the envelope.
56217c478bd9Sstevel@tonic-gate **
56227c478bd9Sstevel@tonic-gate **	Returns:
56237c478bd9Sstevel@tonic-gate **		a printable version
56247c478bd9Sstevel@tonic-gate */
56257c478bd9Sstevel@tonic-gate 
56267c478bd9Sstevel@tonic-gate char *
56277c478bd9Sstevel@tonic-gate qid_printname(e)
56287c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
56297c478bd9Sstevel@tonic-gate {
56307c478bd9Sstevel@tonic-gate 	char *id;
56317c478bd9Sstevel@tonic-gate 	static char idbuf[MAXQFNAME + 34];
56327c478bd9Sstevel@tonic-gate 
56337c478bd9Sstevel@tonic-gate 	if (e == NULL)
56347c478bd9Sstevel@tonic-gate 		return "";
56357c478bd9Sstevel@tonic-gate 
56367c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
56377c478bd9Sstevel@tonic-gate 		id = "";
56387c478bd9Sstevel@tonic-gate 	else
56397c478bd9Sstevel@tonic-gate 		id = e->e_id;
56407c478bd9Sstevel@tonic-gate 
56417c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR)
56427c478bd9Sstevel@tonic-gate 		return id;
56437c478bd9Sstevel@tonic-gate 
5644*058561cbSjbeck 	(void) sm_snprintf(idbuf, sizeof(idbuf), "%.32s/%s",
56457c478bd9Sstevel@tonic-gate 			   Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
56467c478bd9Sstevel@tonic-gate 			   id);
56477c478bd9Sstevel@tonic-gate 	return idbuf;
56487c478bd9Sstevel@tonic-gate }
56497c478bd9Sstevel@tonic-gate /*
56507c478bd9Sstevel@tonic-gate **  QID_PRINTQUEUE -- create full version of queue directory for data files
56517c478bd9Sstevel@tonic-gate **
56527c478bd9Sstevel@tonic-gate **	Parameters:
56537c478bd9Sstevel@tonic-gate **		qgrp -- index in queue group.
56547c478bd9Sstevel@tonic-gate **		qdir -- the short version of the queue directory
56557c478bd9Sstevel@tonic-gate **
56567c478bd9Sstevel@tonic-gate **	Returns:
56577c478bd9Sstevel@tonic-gate **		the full pathname to the queue (might point to a static var)
56587c478bd9Sstevel@tonic-gate */
56597c478bd9Sstevel@tonic-gate 
56607c478bd9Sstevel@tonic-gate char *
56617c478bd9Sstevel@tonic-gate qid_printqueue(qgrp, qdir)
56627c478bd9Sstevel@tonic-gate 	int qgrp;
56637c478bd9Sstevel@tonic-gate 	int qdir;
56647c478bd9Sstevel@tonic-gate {
56657c478bd9Sstevel@tonic-gate 	char *subdir;
56667c478bd9Sstevel@tonic-gate 	static char dir[MAXPATHLEN];
56677c478bd9Sstevel@tonic-gate 
56687c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
56697c478bd9Sstevel@tonic-gate 		return Queue[qgrp]->qg_qdir;
56707c478bd9Sstevel@tonic-gate 
56717c478bd9Sstevel@tonic-gate 	if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
56727c478bd9Sstevel@tonic-gate 		subdir = NULL;
56737c478bd9Sstevel@tonic-gate 	else
56747c478bd9Sstevel@tonic-gate 		subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
56757c478bd9Sstevel@tonic-gate 
5676*058561cbSjbeck 	(void) sm_strlcpyn(dir, sizeof(dir), 4,
56777c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qdir,
56787c478bd9Sstevel@tonic-gate 			subdir == NULL ? "" : "/",
56797c478bd9Sstevel@tonic-gate 			subdir == NULL ? "" : subdir,
56807c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBDF,
56817c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
56827c478bd9Sstevel@tonic-gate 					? "/df" : ""));
56837c478bd9Sstevel@tonic-gate 	return dir;
56847c478bd9Sstevel@tonic-gate }
56857c478bd9Sstevel@tonic-gate 
56867c478bd9Sstevel@tonic-gate /*
56877c478bd9Sstevel@tonic-gate **  PICKQDIR -- Pick a queue directory from a queue group
56887c478bd9Sstevel@tonic-gate **
56897c478bd9Sstevel@tonic-gate **	Parameters:
56907c478bd9Sstevel@tonic-gate **		qg -- queue group
56917c478bd9Sstevel@tonic-gate **		fsize -- file size in bytes
56927c478bd9Sstevel@tonic-gate **		e -- envelope, or NULL
56937c478bd9Sstevel@tonic-gate **
56947c478bd9Sstevel@tonic-gate **	Result:
56957c478bd9Sstevel@tonic-gate **		NOQDIR if no queue directory in qg has enough free space to
56967c478bd9Sstevel@tonic-gate **		hold a file of size 'fsize', otherwise the index of
56977c478bd9Sstevel@tonic-gate **		a randomly selected queue directory which resides on a
56987c478bd9Sstevel@tonic-gate **		file system with enough disk space.
56997c478bd9Sstevel@tonic-gate **		XXX This could be extended to select a queuedir with
57007c478bd9Sstevel@tonic-gate **			a few (the fewest?) number of entries. That data
57017c478bd9Sstevel@tonic-gate **			is available if shared memory is used.
57027c478bd9Sstevel@tonic-gate **
57037c478bd9Sstevel@tonic-gate **	Side Effects:
57047c478bd9Sstevel@tonic-gate **		If the request fails and e != NULL then sm_syslog is called.
57057c478bd9Sstevel@tonic-gate */
57067c478bd9Sstevel@tonic-gate 
57077c478bd9Sstevel@tonic-gate int
57087c478bd9Sstevel@tonic-gate pickqdir(qg, fsize, e)
57097c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
57107c478bd9Sstevel@tonic-gate 	long fsize;
57117c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
57127c478bd9Sstevel@tonic-gate {
57137c478bd9Sstevel@tonic-gate 	int qdir;
57147c478bd9Sstevel@tonic-gate 	int i;
57157c478bd9Sstevel@tonic-gate 	long avail = 0;
57167c478bd9Sstevel@tonic-gate 
57177c478bd9Sstevel@tonic-gate 	/* Pick a random directory, as a starting point. */
57187c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues <= 1)
57197c478bd9Sstevel@tonic-gate 		qdir = 0;
57207c478bd9Sstevel@tonic-gate 	else
57217c478bd9Sstevel@tonic-gate 		qdir = get_rand_mod(qg->qg_numqueues);
57227c478bd9Sstevel@tonic-gate 
57237c478bd9Sstevel@tonic-gate 	if (MinBlocksFree <= 0 && fsize <= 0)
57247c478bd9Sstevel@tonic-gate 		return qdir;
57257c478bd9Sstevel@tonic-gate 
57267c478bd9Sstevel@tonic-gate 	/*
57277c478bd9Sstevel@tonic-gate 	**  Now iterate over the queue directories,
57287c478bd9Sstevel@tonic-gate 	**  looking for a directory with enough space for this message.
57297c478bd9Sstevel@tonic-gate 	*/
57307c478bd9Sstevel@tonic-gate 
57317c478bd9Sstevel@tonic-gate 	i = qdir;
57327c478bd9Sstevel@tonic-gate 	do
57337c478bd9Sstevel@tonic-gate 	{
57347c478bd9Sstevel@tonic-gate 		QPATHS *qp = &qg->qg_qpaths[i];
57357c478bd9Sstevel@tonic-gate 		long needed = 0;
57367c478bd9Sstevel@tonic-gate 		long fsavail = 0;
57377c478bd9Sstevel@tonic-gate 
57387c478bd9Sstevel@tonic-gate 		if (fsize > 0)
57397c478bd9Sstevel@tonic-gate 			needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
57407c478bd9Sstevel@tonic-gate 				  + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
57417c478bd9Sstevel@tonic-gate 				      > 0) ? 1 : 0);
57427c478bd9Sstevel@tonic-gate 		if (MinBlocksFree > 0)
57437c478bd9Sstevel@tonic-gate 			needed += MinBlocksFree;
57447c478bd9Sstevel@tonic-gate 		fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
57457c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
57467c478bd9Sstevel@tonic-gate 		if (fsavail <= 0)
57477c478bd9Sstevel@tonic-gate 		{
57487c478bd9Sstevel@tonic-gate 			long blksize;
57497c478bd9Sstevel@tonic-gate 
57507c478bd9Sstevel@tonic-gate 			/*
57517c478bd9Sstevel@tonic-gate 			**  might be not correctly updated,
57527c478bd9Sstevel@tonic-gate 			**  let's try to get the info directly.
57537c478bd9Sstevel@tonic-gate 			*/
57547c478bd9Sstevel@tonic-gate 
57557c478bd9Sstevel@tonic-gate 			fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
57567c478bd9Sstevel@tonic-gate 						&blksize);
57577c478bd9Sstevel@tonic-gate 			if (fsavail < 0)
57587c478bd9Sstevel@tonic-gate 				fsavail = 0;
57597c478bd9Sstevel@tonic-gate 		}
57607c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
57617c478bd9Sstevel@tonic-gate 		if (needed <= fsavail)
57627c478bd9Sstevel@tonic-gate 			return i;
57637c478bd9Sstevel@tonic-gate 		if (avail < fsavail)
57647c478bd9Sstevel@tonic-gate 			avail = fsavail;
57657c478bd9Sstevel@tonic-gate 
57667c478bd9Sstevel@tonic-gate 		if (qg->qg_numqueues > 0)
57677c478bd9Sstevel@tonic-gate 			i = (i + 1) % qg->qg_numqueues;
57687c478bd9Sstevel@tonic-gate 	} while (i != qdir);
57697c478bd9Sstevel@tonic-gate 
57707c478bd9Sstevel@tonic-gate 	if (e != NULL && LogLevel > 0)
57717c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ALERT, e->e_id,
57727c478bd9Sstevel@tonic-gate 			"low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
57737c478bd9Sstevel@tonic-gate 			CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
57747c478bd9Sstevel@tonic-gate 			fsize, MinBlocksFree,
57757c478bd9Sstevel@tonic-gate 			qg->qg_qdir, avail);
57767c478bd9Sstevel@tonic-gate 	return NOQDIR;
57777c478bd9Sstevel@tonic-gate }
57787c478bd9Sstevel@tonic-gate /*
57797c478bd9Sstevel@tonic-gate **  SETNEWQUEUE -- Sets a new queue group and directory
57807c478bd9Sstevel@tonic-gate **
57817c478bd9Sstevel@tonic-gate **	Assign a queue group and directory to an envelope and store the
57827c478bd9Sstevel@tonic-gate **	directory in e->e_qdir.
57837c478bd9Sstevel@tonic-gate **
57847c478bd9Sstevel@tonic-gate **	Parameters:
57857c478bd9Sstevel@tonic-gate **		e -- envelope to assign a queue for.
57867c478bd9Sstevel@tonic-gate **
57877c478bd9Sstevel@tonic-gate **	Returns:
57887c478bd9Sstevel@tonic-gate **		true if successful
57897c478bd9Sstevel@tonic-gate **		false otherwise
57907c478bd9Sstevel@tonic-gate **
57917c478bd9Sstevel@tonic-gate **	Side Effects:
57927c478bd9Sstevel@tonic-gate **		On success, e->e_qgrp and e->e_qdir are non-negative.
57937c478bd9Sstevel@tonic-gate **		On failure (not enough disk space),
57947c478bd9Sstevel@tonic-gate **		e->qgrp = NOQGRP, e->e_qdir = NOQDIR
57957c478bd9Sstevel@tonic-gate **		and usrerr() is invoked (which could raise an exception).
57967c478bd9Sstevel@tonic-gate */
57977c478bd9Sstevel@tonic-gate 
57987c478bd9Sstevel@tonic-gate bool
57997c478bd9Sstevel@tonic-gate setnewqueue(e)
58007c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
58017c478bd9Sstevel@tonic-gate {
58027c478bd9Sstevel@tonic-gate 	if (tTd(41, 20))
58037c478bd9Sstevel@tonic-gate 		sm_dprintf("setnewqueue: called\n");
58047c478bd9Sstevel@tonic-gate 
58057c478bd9Sstevel@tonic-gate 	/* not set somewhere else */
58067c478bd9Sstevel@tonic-gate 	if (e->e_qgrp == NOQGRP)
58077c478bd9Sstevel@tonic-gate 	{
58087c478bd9Sstevel@tonic-gate 		ADDRESS *q;
58097c478bd9Sstevel@tonic-gate 
58107c478bd9Sstevel@tonic-gate 		/*
58117c478bd9Sstevel@tonic-gate 		**  Use the queue group of the "first" recipient, as set by
58127c478bd9Sstevel@tonic-gate 		**  the "queuegroup" rule set.  If that is not defined, then
58137c478bd9Sstevel@tonic-gate 		**  use the queue group of the mailer of the first recipient.
58147c478bd9Sstevel@tonic-gate 		**  If that is not defined either, then use the default
58157c478bd9Sstevel@tonic-gate 		**  queue group.
58167c478bd9Sstevel@tonic-gate 		**  Notice: "first" depends on the sorting of sendqueue
58177c478bd9Sstevel@tonic-gate 		**  in recipient().
58187c478bd9Sstevel@tonic-gate 		**  To avoid problems with "bad" recipients look
58197c478bd9Sstevel@tonic-gate 		**  for a valid address first.
58207c478bd9Sstevel@tonic-gate 		*/
58217c478bd9Sstevel@tonic-gate 
58227c478bd9Sstevel@tonic-gate 		q = e->e_sendqueue;
58237c478bd9Sstevel@tonic-gate 		while (q != NULL &&
58247c478bd9Sstevel@tonic-gate 		       (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state)))
58257c478bd9Sstevel@tonic-gate 		{
58267c478bd9Sstevel@tonic-gate 			q = q->q_next;
58277c478bd9Sstevel@tonic-gate 		}
58287c478bd9Sstevel@tonic-gate 		if (q == NULL)
58297c478bd9Sstevel@tonic-gate 			e->e_qgrp = 0;
58307c478bd9Sstevel@tonic-gate 		else if (q->q_qgrp >= 0)
58317c478bd9Sstevel@tonic-gate 			e->e_qgrp = q->q_qgrp;
58327c478bd9Sstevel@tonic-gate 		else if (q->q_mailer != NULL &&
58337c478bd9Sstevel@tonic-gate 			 ISVALIDQGRP(q->q_mailer->m_qgrp))
58347c478bd9Sstevel@tonic-gate 			e->e_qgrp = q->q_mailer->m_qgrp;
58357c478bd9Sstevel@tonic-gate 		else
58367c478bd9Sstevel@tonic-gate 			e->e_qgrp = 0;
58377c478bd9Sstevel@tonic-gate 		e->e_dfqgrp = e->e_qgrp;
58387c478bd9Sstevel@tonic-gate 	}
58397c478bd9Sstevel@tonic-gate 
58407c478bd9Sstevel@tonic-gate 	if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
58417c478bd9Sstevel@tonic-gate 	{
58427c478bd9Sstevel@tonic-gate 		if (tTd(41, 20))
58437c478bd9Sstevel@tonic-gate 			sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
58447c478bd9Sstevel@tonic-gate 				qid_printqueue(e->e_qgrp, e->e_qdir));
58457c478bd9Sstevel@tonic-gate 		return true;
58467c478bd9Sstevel@tonic-gate 	}
58477c478bd9Sstevel@tonic-gate 
58487c478bd9Sstevel@tonic-gate 	filesys_update();
58497c478bd9Sstevel@tonic-gate 	e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
58507c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR)
58517c478bd9Sstevel@tonic-gate 	{
58527c478bd9Sstevel@tonic-gate 		e->e_qgrp = NOQGRP;
58537c478bd9Sstevel@tonic-gate 		if (!bitset(EF_FATALERRS, e->e_flags))
58547c478bd9Sstevel@tonic-gate 			usrerr("452 4.4.5 Insufficient disk space; try again later");
58557c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_FATALERRS;
58567c478bd9Sstevel@tonic-gate 		return false;
58577c478bd9Sstevel@tonic-gate 	}
58587c478bd9Sstevel@tonic-gate 
58597c478bd9Sstevel@tonic-gate 	if (tTd(41, 3))
58607c478bd9Sstevel@tonic-gate 		sm_dprintf("setnewqueue: Assigned queue directory %s\n",
58617c478bd9Sstevel@tonic-gate 			qid_printqueue(e->e_qgrp, e->e_qdir));
58627c478bd9Sstevel@tonic-gate 
58637c478bd9Sstevel@tonic-gate 	if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
58647c478bd9Sstevel@tonic-gate 	{
58657c478bd9Sstevel@tonic-gate 		e->e_xfqgrp = e->e_qgrp;
58667c478bd9Sstevel@tonic-gate 		e->e_xfqdir = e->e_qdir;
58677c478bd9Sstevel@tonic-gate 	}
58687c478bd9Sstevel@tonic-gate 	e->e_dfqdir = e->e_qdir;
58697c478bd9Sstevel@tonic-gate 	return true;
58707c478bd9Sstevel@tonic-gate }
58717c478bd9Sstevel@tonic-gate /*
58727c478bd9Sstevel@tonic-gate **  CHKQDIR -- check a queue directory
58737c478bd9Sstevel@tonic-gate **
58747c478bd9Sstevel@tonic-gate **	Parameters:
58757c478bd9Sstevel@tonic-gate **		name -- name of queue directory
58767c478bd9Sstevel@tonic-gate **		sff -- flags for safefile()
58777c478bd9Sstevel@tonic-gate **
58787c478bd9Sstevel@tonic-gate **	Returns:
58797c478bd9Sstevel@tonic-gate **		is it a queue directory?
58807c478bd9Sstevel@tonic-gate */
58817c478bd9Sstevel@tonic-gate 
5882*058561cbSjbeck static bool chkqdir __P((char *, long));
5883*058561cbSjbeck 
58847c478bd9Sstevel@tonic-gate static bool
58857c478bd9Sstevel@tonic-gate chkqdir(name, sff)
58867c478bd9Sstevel@tonic-gate 	char *name;
58877c478bd9Sstevel@tonic-gate 	long sff;
58887c478bd9Sstevel@tonic-gate {
58897c478bd9Sstevel@tonic-gate 	struct stat statb;
58907c478bd9Sstevel@tonic-gate 	int i;
58917c478bd9Sstevel@tonic-gate 
58927c478bd9Sstevel@tonic-gate 	/* skip over . and .. directories */
58937c478bd9Sstevel@tonic-gate 	if (name[0] == '.' &&
58947c478bd9Sstevel@tonic-gate 	    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
58957c478bd9Sstevel@tonic-gate 		return false;
58967c478bd9Sstevel@tonic-gate #if HASLSTAT
58977c478bd9Sstevel@tonic-gate 	if (lstat(name, &statb) < 0)
58987c478bd9Sstevel@tonic-gate #else /* HASLSTAT */
58997c478bd9Sstevel@tonic-gate 	if (stat(name, &statb) < 0)
59007c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
59017c478bd9Sstevel@tonic-gate 	{
59027c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59037c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
59047c478bd9Sstevel@tonic-gate 				   name, sm_errstring(errno));
59057c478bd9Sstevel@tonic-gate 		return false;
59067c478bd9Sstevel@tonic-gate 	}
59077c478bd9Sstevel@tonic-gate #if HASLSTAT
59087c478bd9Sstevel@tonic-gate 	if (S_ISLNK(statb.st_mode))
59097c478bd9Sstevel@tonic-gate 	{
59107c478bd9Sstevel@tonic-gate 		/*
59117c478bd9Sstevel@tonic-gate 		**  For a symlink we need to make sure the
59127c478bd9Sstevel@tonic-gate 		**  target is a directory
59137c478bd9Sstevel@tonic-gate 		*/
59147c478bd9Sstevel@tonic-gate 
59157c478bd9Sstevel@tonic-gate 		if (stat(name, &statb) < 0)
59167c478bd9Sstevel@tonic-gate 		{
59177c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
59187c478bd9Sstevel@tonic-gate 				sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
59197c478bd9Sstevel@tonic-gate 					   name, sm_errstring(errno));
59207c478bd9Sstevel@tonic-gate 			return false;
59217c478bd9Sstevel@tonic-gate 		}
59227c478bd9Sstevel@tonic-gate 	}
59237c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
59247c478bd9Sstevel@tonic-gate 
59257c478bd9Sstevel@tonic-gate 	if (!S_ISDIR(statb.st_mode))
59267c478bd9Sstevel@tonic-gate 	{
59277c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59287c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: \"%s\": Not a directory\n",
59297c478bd9Sstevel@tonic-gate 				name);
59307c478bd9Sstevel@tonic-gate 		return false;
59317c478bd9Sstevel@tonic-gate 	}
59327c478bd9Sstevel@tonic-gate 
59337c478bd9Sstevel@tonic-gate 	/* Print a warning if unsafe (but still use it) */
59347c478bd9Sstevel@tonic-gate 	/* XXX do this only if we want the warning? */
59357c478bd9Sstevel@tonic-gate 	i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
59367c478bd9Sstevel@tonic-gate 	if (i != 0)
59377c478bd9Sstevel@tonic-gate 	{
59387c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59397c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
59407c478bd9Sstevel@tonic-gate 				   name, sm_errstring(i));
59417c478bd9Sstevel@tonic-gate #if _FFR_CHK_QUEUE
59427c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
59437c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
59447c478bd9Sstevel@tonic-gate 				  "queue directory \"%s\": Not safe: %s",
59457c478bd9Sstevel@tonic-gate 				  name, sm_errstring(i));
59467c478bd9Sstevel@tonic-gate #endif /* _FFR_CHK_QUEUE */
59477c478bd9Sstevel@tonic-gate 	}
59487c478bd9Sstevel@tonic-gate 	return true;
59497c478bd9Sstevel@tonic-gate }
59507c478bd9Sstevel@tonic-gate /*
59517c478bd9Sstevel@tonic-gate **  MULTIQUEUE_CACHE -- cache a list of paths to queues.
59527c478bd9Sstevel@tonic-gate **
59537c478bd9Sstevel@tonic-gate **	Each potential queue is checked as the cache is built.
59547c478bd9Sstevel@tonic-gate **	Thereafter, each is blindly trusted.
59557c478bd9Sstevel@tonic-gate **	Note that we can be called again after a timeout to rebuild
59567c478bd9Sstevel@tonic-gate **	(although code for that is not ready yet).
59577c478bd9Sstevel@tonic-gate **
59587c478bd9Sstevel@tonic-gate **	Parameters:
59597c478bd9Sstevel@tonic-gate **		basedir -- base of all queue directories.
59607c478bd9Sstevel@tonic-gate **		blen -- strlen(basedir).
59617c478bd9Sstevel@tonic-gate **		qg -- queue group.
59627c478bd9Sstevel@tonic-gate **		qn -- number of queue directories already cached.
59637c478bd9Sstevel@tonic-gate **		phash -- pointer to hash value over queue dirs.
59647c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
59657c478bd9Sstevel@tonic-gate **			only used if shared memory is active.
59667c478bd9Sstevel@tonic-gate #endif * SM_CONF_SHM *
59677c478bd9Sstevel@tonic-gate **
59687c478bd9Sstevel@tonic-gate **	Returns:
59697c478bd9Sstevel@tonic-gate **		new number of queue directories.
59707c478bd9Sstevel@tonic-gate */
59717c478bd9Sstevel@tonic-gate 
59727c478bd9Sstevel@tonic-gate #define INITIAL_SLOTS	20
59737c478bd9Sstevel@tonic-gate #define ADD_SLOTS	10
59747c478bd9Sstevel@tonic-gate 
59757c478bd9Sstevel@tonic-gate static int
59767c478bd9Sstevel@tonic-gate multiqueue_cache(basedir, blen, qg, qn, phash)
59777c478bd9Sstevel@tonic-gate 	char *basedir;
59787c478bd9Sstevel@tonic-gate 	int blen;
59797c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
59807c478bd9Sstevel@tonic-gate 	int qn;
59817c478bd9Sstevel@tonic-gate 	unsigned int *phash;
59827c478bd9Sstevel@tonic-gate {
59837c478bd9Sstevel@tonic-gate 	char *cp;
59847c478bd9Sstevel@tonic-gate 	int i, len;
59857c478bd9Sstevel@tonic-gate 	int slotsleft = 0;
59867c478bd9Sstevel@tonic-gate 	long sff = SFF_ANYFILE;
59877c478bd9Sstevel@tonic-gate 	char qpath[MAXPATHLEN];
59887c478bd9Sstevel@tonic-gate 	char subdir[MAXPATHLEN];
59897c478bd9Sstevel@tonic-gate 	char prefix[MAXPATHLEN];	/* dir relative to basedir */
59907c478bd9Sstevel@tonic-gate 
59917c478bd9Sstevel@tonic-gate 	if (tTd(41, 20))
59927c478bd9Sstevel@tonic-gate 		sm_dprintf("multiqueue_cache: called\n");
59937c478bd9Sstevel@tonic-gate 
59947c478bd9Sstevel@tonic-gate 	/* Initialize to current directory */
59957c478bd9Sstevel@tonic-gate 	prefix[0] = '.';
59967c478bd9Sstevel@tonic-gate 	prefix[1] = '\0';
59977c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
59987c478bd9Sstevel@tonic-gate 	{
59997c478bd9Sstevel@tonic-gate 		for (i = 0; i < qg->qg_numqueues; i++)
60007c478bd9Sstevel@tonic-gate 		{
60017c478bd9Sstevel@tonic-gate 			if (qg->qg_qpaths[i].qp_name != NULL)
60027c478bd9Sstevel@tonic-gate 				(void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
60037c478bd9Sstevel@tonic-gate 		}
60047c478bd9Sstevel@tonic-gate 		(void) sm_free((char *) qg->qg_qpaths); /* XXX */
60057c478bd9Sstevel@tonic-gate 		qg->qg_qpaths = NULL;
60067c478bd9Sstevel@tonic-gate 		qg->qg_numqueues = 0;
60077c478bd9Sstevel@tonic-gate 	}
60087c478bd9Sstevel@tonic-gate 
60097c478bd9Sstevel@tonic-gate 	/* If running as root, allow safedirpath() checks to use privs */
60107c478bd9Sstevel@tonic-gate 	if (RunAsUid == 0)
60117c478bd9Sstevel@tonic-gate 		sff |= SFF_ROOTOK;
60127c478bd9Sstevel@tonic-gate #if _FFR_CHK_QUEUE
60137c478bd9Sstevel@tonic-gate 	sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES;
60147c478bd9Sstevel@tonic-gate 	if (!UseMSP)
60157c478bd9Sstevel@tonic-gate 		sff |= SFF_NOGWFILES;
60167c478bd9Sstevel@tonic-gate #endif /* _FFR_CHK_QUEUE */
60177c478bd9Sstevel@tonic-gate 
60187c478bd9Sstevel@tonic-gate 	if (!SM_IS_DIR_START(qg->qg_qdir))
60197c478bd9Sstevel@tonic-gate 	{
60207c478bd9Sstevel@tonic-gate 		/*
60217c478bd9Sstevel@tonic-gate 		**  XXX we could add basedir, but then we have to realloc()
60227c478bd9Sstevel@tonic-gate 		**  the string... Maybe another time.
60237c478bd9Sstevel@tonic-gate 		*/
60247c478bd9Sstevel@tonic-gate 
60257c478bd9Sstevel@tonic-gate 		syserr("QueuePath %s not absolute", qg->qg_qdir);
60267c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60277c478bd9Sstevel@tonic-gate 		return qn;
60287c478bd9Sstevel@tonic-gate 	}
60297c478bd9Sstevel@tonic-gate 
60307c478bd9Sstevel@tonic-gate 	/* qpath: directory of current workgroup */
6031*058561cbSjbeck 	len = sm_strlcpy(qpath, qg->qg_qdir, sizeof(qpath));
6032*058561cbSjbeck 	if (len >= sizeof(qpath))
60337c478bd9Sstevel@tonic-gate 	{
60347c478bd9Sstevel@tonic-gate 		syserr("QueuePath %.256s too long (%d max)",
6035*058561cbSjbeck 		       qg->qg_qdir, (int) sizeof(qpath));
60367c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60377c478bd9Sstevel@tonic-gate 		return qn;
60387c478bd9Sstevel@tonic-gate 	}
60397c478bd9Sstevel@tonic-gate 
60407c478bd9Sstevel@tonic-gate 	/* begin of qpath must be same as basedir */
60417c478bd9Sstevel@tonic-gate 	if (strncmp(basedir, qpath, blen) != 0 &&
60427c478bd9Sstevel@tonic-gate 	    (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
60437c478bd9Sstevel@tonic-gate 	{
60447c478bd9Sstevel@tonic-gate 		syserr("QueuePath %s not subpath of QueueDirectory %s",
60457c478bd9Sstevel@tonic-gate 			qpath, basedir);
60467c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60477c478bd9Sstevel@tonic-gate 		return qn;
60487c478bd9Sstevel@tonic-gate 	}
60497c478bd9Sstevel@tonic-gate 
60507c478bd9Sstevel@tonic-gate 	/* Do we have a nested subdirectory? */
60517c478bd9Sstevel@tonic-gate 	if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
60527c478bd9Sstevel@tonic-gate 	{
60537c478bd9Sstevel@tonic-gate 
60547c478bd9Sstevel@tonic-gate 		/* Copy subdirectory into prefix for later use */
6055*058561cbSjbeck 		if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof(prefix)) >=
6056*058561cbSjbeck 		    sizeof(prefix))
60577c478bd9Sstevel@tonic-gate 		{
60587c478bd9Sstevel@tonic-gate 			syserr("QueuePath %.256s too long (%d max)",
6059*058561cbSjbeck 				qg->qg_qdir, (int) sizeof(qpath));
60607c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
60617c478bd9Sstevel@tonic-gate 			return qn;
60627c478bd9Sstevel@tonic-gate 		}
60637c478bd9Sstevel@tonic-gate 		cp = SM_LAST_DIR_DELIM(prefix);
60647c478bd9Sstevel@tonic-gate 		SM_ASSERT(cp != NULL);
60657c478bd9Sstevel@tonic-gate 		*cp = '\0';	/* cut off trailing / */
60667c478bd9Sstevel@tonic-gate 	}
60677c478bd9Sstevel@tonic-gate 
60687c478bd9Sstevel@tonic-gate 	/* This is guaranteed by the basedir check above */
60697c478bd9Sstevel@tonic-gate 	SM_ASSERT(len >= blen - 1);
60707c478bd9Sstevel@tonic-gate 	cp = &qpath[len - 1];
60717c478bd9Sstevel@tonic-gate 	if (*cp == '*')
60727c478bd9Sstevel@tonic-gate 	{
60737c478bd9Sstevel@tonic-gate 		register DIR *dp;
60747c478bd9Sstevel@tonic-gate 		register struct dirent *d;
60757c478bd9Sstevel@tonic-gate 		int off;
60767c478bd9Sstevel@tonic-gate 		char *delim;
60777c478bd9Sstevel@tonic-gate 		char relpath[MAXPATHLEN];
60787c478bd9Sstevel@tonic-gate 
60797c478bd9Sstevel@tonic-gate 		*cp = '\0';	/* Overwrite wildcard */
60807c478bd9Sstevel@tonic-gate 		if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
60817c478bd9Sstevel@tonic-gate 		{
60827c478bd9Sstevel@tonic-gate 			syserr("QueueDirectory: can not wildcard relative path");
60837c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
60847c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
60857c478bd9Sstevel@tonic-gate 					qpath);
60867c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
60877c478bd9Sstevel@tonic-gate 			return qn;
60887c478bd9Sstevel@tonic-gate 		}
60897c478bd9Sstevel@tonic-gate 		if (cp == qpath)
60907c478bd9Sstevel@tonic-gate 		{
60917c478bd9Sstevel@tonic-gate 			/*
60927c478bd9Sstevel@tonic-gate 			**  Special case of top level wildcard, like /foo*
60937c478bd9Sstevel@tonic-gate 			**	Change to //foo*
60947c478bd9Sstevel@tonic-gate 			*/
60957c478bd9Sstevel@tonic-gate 
6096*058561cbSjbeck 			(void) sm_strlcpy(qpath + 1, qpath, sizeof(qpath) - 1);
60977c478bd9Sstevel@tonic-gate 			++cp;
60987c478bd9Sstevel@tonic-gate 		}
60997c478bd9Sstevel@tonic-gate 		delim = cp;
61007c478bd9Sstevel@tonic-gate 		*(cp++) = '\0';		/* Replace / with \0 */
61017c478bd9Sstevel@tonic-gate 		len = strlen(cp);	/* Last component of queue directory */
61027c478bd9Sstevel@tonic-gate 
61037c478bd9Sstevel@tonic-gate 		/*
61047c478bd9Sstevel@tonic-gate 		**  Path relative to basedir, with trailing /
61057c478bd9Sstevel@tonic-gate 		**  It will be modified below to specify the subdirectories
61067c478bd9Sstevel@tonic-gate 		**  so they can be opened without chdir().
61077c478bd9Sstevel@tonic-gate 		*/
61087c478bd9Sstevel@tonic-gate 
6109*058561cbSjbeck 		off = sm_strlcpyn(relpath, sizeof(relpath), 2, prefix, "/");
6110*058561cbSjbeck 		SM_ASSERT(off < sizeof(relpath));
61117c478bd9Sstevel@tonic-gate 
61127c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
61137c478bd9Sstevel@tonic-gate 			sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
61147c478bd9Sstevel@tonic-gate 				   relpath, cp);
61157c478bd9Sstevel@tonic-gate 
61167c478bd9Sstevel@tonic-gate 		/* It is always basedir: we don't need to store it per group */
61177c478bd9Sstevel@tonic-gate 		/* XXX: optimize this! -> one more global? */
61187c478bd9Sstevel@tonic-gate 		qg->qg_qdir = newstr(basedir);
61197c478bd9Sstevel@tonic-gate 		qg->qg_qdir[blen - 1] = '\0';	/* cut off trailing / */
61207c478bd9Sstevel@tonic-gate 
61217c478bd9Sstevel@tonic-gate 		/*
61227c478bd9Sstevel@tonic-gate 		**  XXX Should probably wrap this whole loop in a timeout
61237c478bd9Sstevel@tonic-gate 		**  in case some wag decides to NFS mount the queues.
61247c478bd9Sstevel@tonic-gate 		*/
61257c478bd9Sstevel@tonic-gate 
61267c478bd9Sstevel@tonic-gate 		/* Test path to get warning messages. */
61277c478bd9Sstevel@tonic-gate 		if (qn == 0)
61287c478bd9Sstevel@tonic-gate 		{
61297c478bd9Sstevel@tonic-gate 			/*  XXX qg_runasuid and qg_runasgid for specials? */
61307c478bd9Sstevel@tonic-gate 			i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
61317c478bd9Sstevel@tonic-gate 					sff, 0, 0);
61327c478bd9Sstevel@tonic-gate 			if (i != 0 && tTd(41, 2))
61337c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
61347c478bd9Sstevel@tonic-gate 					   basedir, sm_errstring(i));
61357c478bd9Sstevel@tonic-gate 		}
61367c478bd9Sstevel@tonic-gate 
61377c478bd9Sstevel@tonic-gate 		if ((dp = opendir(prefix)) == NULL)
61387c478bd9Sstevel@tonic-gate 		{
61397c478bd9Sstevel@tonic-gate 			syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
61407c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
61417c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
61427c478bd9Sstevel@tonic-gate 					   qg->qg_qdir, prefix,
61437c478bd9Sstevel@tonic-gate 					   sm_errstring(errno));
61447c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
61457c478bd9Sstevel@tonic-gate 			return qn;
61467c478bd9Sstevel@tonic-gate 		}
61477c478bd9Sstevel@tonic-gate 		while ((d = readdir(dp)) != NULL)
61487c478bd9Sstevel@tonic-gate 		{
6149*058561cbSjbeck 			/* Skip . and .. directories */
6150*058561cbSjbeck 			if (strcmp(d->d_name, ".") == 0 ||
6151*058561cbSjbeck 			    strcmp(d->d_name, "..") == 0)
6152*058561cbSjbeck 				continue;
6153*058561cbSjbeck 
61547c478bd9Sstevel@tonic-gate 			i = strlen(d->d_name);
61557c478bd9Sstevel@tonic-gate 			if (i < len || strncmp(d->d_name, cp, len) != 0)
61567c478bd9Sstevel@tonic-gate 			{
61577c478bd9Sstevel@tonic-gate 				if (tTd(41, 5))
61587c478bd9Sstevel@tonic-gate 					sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
61597c478bd9Sstevel@tonic-gate 						d->d_name);
61607c478bd9Sstevel@tonic-gate 				continue;
61617c478bd9Sstevel@tonic-gate 			}
61627c478bd9Sstevel@tonic-gate 
61637c478bd9Sstevel@tonic-gate 			/* Create relative pathname: prefix + local directory */
61647c478bd9Sstevel@tonic-gate 			i = sizeof(relpath) - off;
61657c478bd9Sstevel@tonic-gate 			if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
61667c478bd9Sstevel@tonic-gate 				continue;	/* way too long */
61677c478bd9Sstevel@tonic-gate 
61687c478bd9Sstevel@tonic-gate 			if (!chkqdir(relpath, sff))
61697c478bd9Sstevel@tonic-gate 				continue;
61707c478bd9Sstevel@tonic-gate 
61717c478bd9Sstevel@tonic-gate 			if (qg->qg_qpaths == NULL)
61727c478bd9Sstevel@tonic-gate 			{
61737c478bd9Sstevel@tonic-gate 				slotsleft = INITIAL_SLOTS;
6174*058561cbSjbeck 				qg->qg_qpaths = (QPATHS *)xalloc((sizeof(*qg->qg_qpaths)) *
61757c478bd9Sstevel@tonic-gate 								slotsleft);
61767c478bd9Sstevel@tonic-gate 				qg->qg_numqueues = 0;
61777c478bd9Sstevel@tonic-gate 			}
61787c478bd9Sstevel@tonic-gate 			else if (slotsleft < 1)
61797c478bd9Sstevel@tonic-gate 			{
61807c478bd9Sstevel@tonic-gate 				qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
6181*058561cbSjbeck 							  (sizeof(*qg->qg_qpaths)) *
61827c478bd9Sstevel@tonic-gate 							  (qg->qg_numqueues +
61837c478bd9Sstevel@tonic-gate 							   ADD_SLOTS));
61847c478bd9Sstevel@tonic-gate 				if (qg->qg_qpaths == NULL)
61857c478bd9Sstevel@tonic-gate 				{
61867c478bd9Sstevel@tonic-gate 					(void) closedir(dp);
61877c478bd9Sstevel@tonic-gate 					return qn;
61887c478bd9Sstevel@tonic-gate 				}
61897c478bd9Sstevel@tonic-gate 				slotsleft += ADD_SLOTS;
61907c478bd9Sstevel@tonic-gate 			}
61917c478bd9Sstevel@tonic-gate 
61927c478bd9Sstevel@tonic-gate 			/* check subdirs */
61937c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
61947c478bd9Sstevel@tonic-gate 
61957c478bd9Sstevel@tonic-gate #define CHKRSUBDIR(name, flag)	\
6196*058561cbSjbeck 	(void) sm_strlcpyn(subdir, sizeof(subdir), 3, relpath, "/", name); \
61977c478bd9Sstevel@tonic-gate 	if (chkqdir(subdir, sff))	\
61987c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag;	\
61997c478bd9Sstevel@tonic-gate 	else
62007c478bd9Sstevel@tonic-gate 
62017c478bd9Sstevel@tonic-gate 
62027c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("qf", QP_SUBQF);
62037c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("df", QP_SUBDF);
62047c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("xf", QP_SUBXF);
62057c478bd9Sstevel@tonic-gate 
62067c478bd9Sstevel@tonic-gate 			/* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
62077c478bd9Sstevel@tonic-gate 			/* maybe even - 17 (subdirs) */
62087c478bd9Sstevel@tonic-gate 
62097c478bd9Sstevel@tonic-gate 			if (prefix[0] != '.')
62107c478bd9Sstevel@tonic-gate 				qg->qg_qpaths[qg->qg_numqueues].qp_name =
62117c478bd9Sstevel@tonic-gate 					newstr(relpath);
62127c478bd9Sstevel@tonic-gate 			else
62137c478bd9Sstevel@tonic-gate 				qg->qg_qpaths[qg->qg_numqueues].qp_name =
62147c478bd9Sstevel@tonic-gate 					newstr(d->d_name);
62157c478bd9Sstevel@tonic-gate 
62167c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
62177c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
62187c478bd9Sstevel@tonic-gate 					qg->qg_numqueues, relpath,
62197c478bd9Sstevel@tonic-gate 					qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
62207c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
62217c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
62227c478bd9Sstevel@tonic-gate 			*phash = hash_q(relpath, *phash);
62237c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
62247c478bd9Sstevel@tonic-gate 			qg->qg_numqueues++;
62257c478bd9Sstevel@tonic-gate 			++qn;
62267c478bd9Sstevel@tonic-gate 			slotsleft--;
62277c478bd9Sstevel@tonic-gate 		}
62287c478bd9Sstevel@tonic-gate 		(void) closedir(dp);
62297c478bd9Sstevel@tonic-gate 
62307c478bd9Sstevel@tonic-gate 		/* undo damage */
62317c478bd9Sstevel@tonic-gate 		*delim = '/';
62327c478bd9Sstevel@tonic-gate 	}
62337c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues == 0)
62347c478bd9Sstevel@tonic-gate 	{
6235*058561cbSjbeck 		qg->qg_qpaths = (QPATHS *) xalloc(sizeof(*qg->qg_qpaths));
62367c478bd9Sstevel@tonic-gate 
62377c478bd9Sstevel@tonic-gate 		/* test path to get warning messages */
62387c478bd9Sstevel@tonic-gate 		i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
62397c478bd9Sstevel@tonic-gate 		if (i == ENOENT)
62407c478bd9Sstevel@tonic-gate 		{
62417c478bd9Sstevel@tonic-gate 			syserr("can not opendir(%s)", qpath);
62427c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
62437c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
62447c478bd9Sstevel@tonic-gate 					   qpath, sm_errstring(i));
62457c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
62467c478bd9Sstevel@tonic-gate 			return qn;
62477c478bd9Sstevel@tonic-gate 		}
62487c478bd9Sstevel@tonic-gate 
62497c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
62507c478bd9Sstevel@tonic-gate 		qg->qg_numqueues = 1;
62517c478bd9Sstevel@tonic-gate 
62527c478bd9Sstevel@tonic-gate 		/* check subdirs */
62537c478bd9Sstevel@tonic-gate #define CHKSUBDIR(name, flag)	\
6254*058561cbSjbeck 	(void) sm_strlcpyn(subdir, sizeof(subdir), 3, qg->qg_qdir, "/", name); \
62557c478bd9Sstevel@tonic-gate 	if (chkqdir(subdir, sff))	\
62567c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_subdirs |= flag;	\
62577c478bd9Sstevel@tonic-gate 	else
62587c478bd9Sstevel@tonic-gate 
62597c478bd9Sstevel@tonic-gate 		CHKSUBDIR("qf", QP_SUBQF);
62607c478bd9Sstevel@tonic-gate 		CHKSUBDIR("df", QP_SUBDF);
62617c478bd9Sstevel@tonic-gate 		CHKSUBDIR("xf", QP_SUBXF);
62627c478bd9Sstevel@tonic-gate 
62637c478bd9Sstevel@tonic-gate 		if (qg->qg_qdir[blen - 1] != '\0' &&
62647c478bd9Sstevel@tonic-gate 		    qg->qg_qdir[blen] != '\0')
62657c478bd9Sstevel@tonic-gate 		{
62667c478bd9Sstevel@tonic-gate 			/*
62677c478bd9Sstevel@tonic-gate 			**  Copy the last component into qpaths and
62687c478bd9Sstevel@tonic-gate 			**  cut off qdir
62697c478bd9Sstevel@tonic-gate 			*/
62707c478bd9Sstevel@tonic-gate 
62717c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
62727c478bd9Sstevel@tonic-gate 			qg->qg_qdir[blen - 1] = '\0';
62737c478bd9Sstevel@tonic-gate 		}
62747c478bd9Sstevel@tonic-gate 		else
62757c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[0].qp_name = newstr(".");
62767c478bd9Sstevel@tonic-gate 
62777c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
62787c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_idx = qn;
62797c478bd9Sstevel@tonic-gate 		*phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
62807c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
62817c478bd9Sstevel@tonic-gate 		++qn;
62827c478bd9Sstevel@tonic-gate 	}
62837c478bd9Sstevel@tonic-gate 	return qn;
62847c478bd9Sstevel@tonic-gate }
62857c478bd9Sstevel@tonic-gate 
62867c478bd9Sstevel@tonic-gate /*
62877c478bd9Sstevel@tonic-gate **  FILESYS_FIND -- find entry in FileSys table, or add new one
62887c478bd9Sstevel@tonic-gate **
62897c478bd9Sstevel@tonic-gate **	Given the pathname of a directory, determine the file system
62907c478bd9Sstevel@tonic-gate **	in which that directory resides, and return a pointer to the
62917c478bd9Sstevel@tonic-gate **	entry in the FileSys table that describes the file system.
62927c478bd9Sstevel@tonic-gate **	A new entry is added if necessary (and requested).
62937c478bd9Sstevel@tonic-gate **	If the directory does not exist, -1 is returned.
62947c478bd9Sstevel@tonic-gate **
62957c478bd9Sstevel@tonic-gate **	Parameters:
629649218d4fSjbeck **		name -- name of directory (must be persistent!)
629749218d4fSjbeck **		path -- pathname of directory (name plus maybe "/df")
62987c478bd9Sstevel@tonic-gate **		add -- add to structure if not found.
62997c478bd9Sstevel@tonic-gate **
63007c478bd9Sstevel@tonic-gate **	Returns:
63017c478bd9Sstevel@tonic-gate **		>=0: found: index in file system table
63027c478bd9Sstevel@tonic-gate **		<0: some error, i.e.,
63037c478bd9Sstevel@tonic-gate **		FSF_TOO_MANY: too many filesystems (-> syserr())
63047c478bd9Sstevel@tonic-gate **		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
63057c478bd9Sstevel@tonic-gate **		FSF_NOT_FOUND: not in list
63067c478bd9Sstevel@tonic-gate */
63077c478bd9Sstevel@tonic-gate 
6308*058561cbSjbeck static short filesys_find __P((const char *, const char *, bool));
63097c478bd9Sstevel@tonic-gate 
63107c478bd9Sstevel@tonic-gate #define FSF_NOT_FOUND	(-1)
63117c478bd9Sstevel@tonic-gate #define FSF_STAT_FAIL	(-2)
63127c478bd9Sstevel@tonic-gate #define FSF_TOO_MANY	(-3)
63137c478bd9Sstevel@tonic-gate 
63147c478bd9Sstevel@tonic-gate static short
631549218d4fSjbeck filesys_find(name, path, add)
6316*058561cbSjbeck 	const char *name;
6317*058561cbSjbeck 	const char *path;
63187c478bd9Sstevel@tonic-gate 	bool add;
63197c478bd9Sstevel@tonic-gate {
63207c478bd9Sstevel@tonic-gate 	struct stat st;
63217c478bd9Sstevel@tonic-gate 	short i;
63227c478bd9Sstevel@tonic-gate 
63237c478bd9Sstevel@tonic-gate 	if (stat(path, &st) < 0)
63247c478bd9Sstevel@tonic-gate 	{
63257c478bd9Sstevel@tonic-gate 		syserr("cannot stat queue directory %s", path);
63267c478bd9Sstevel@tonic-gate 		return FSF_STAT_FAIL;
63277c478bd9Sstevel@tonic-gate 	}
63287c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
63297c478bd9Sstevel@tonic-gate 	{
63307c478bd9Sstevel@tonic-gate 		if (FILE_SYS_DEV(i) == st.st_dev)
63311daa5768Sjbeck 		{
63321daa5768Sjbeck 			/*
63331daa5768Sjbeck 			**  Make sure the file system (FS) name is set:
63341daa5768Sjbeck 			**  even though the source code indicates that
63351daa5768Sjbeck 			**  FILE_SYS_DEV() is only set below, it could be
63361daa5768Sjbeck 			**  set via shared memory, hence we need to perform
63371daa5768Sjbeck 			**  this check/assignment here.
63381daa5768Sjbeck 			*/
63391daa5768Sjbeck 
63401daa5768Sjbeck 			if (NULL == FILE_SYS_NAME(i))
63411daa5768Sjbeck 				FILE_SYS_NAME(i) = name;
63427c478bd9Sstevel@tonic-gate 			return i;
63431daa5768Sjbeck 		}
63447c478bd9Sstevel@tonic-gate 	}
63457c478bd9Sstevel@tonic-gate 	if (i >= MAXFILESYS)
63467c478bd9Sstevel@tonic-gate 	{
63477c478bd9Sstevel@tonic-gate 		syserr("too many queue file systems (%d max)", MAXFILESYS);
63487c478bd9Sstevel@tonic-gate 		return FSF_TOO_MANY;
63497c478bd9Sstevel@tonic-gate 	}
63507c478bd9Sstevel@tonic-gate 	if (!add)
63517c478bd9Sstevel@tonic-gate 		return FSF_NOT_FOUND;
63527c478bd9Sstevel@tonic-gate 
63537c478bd9Sstevel@tonic-gate 	++NumFileSys;
635449218d4fSjbeck 	FILE_SYS_NAME(i) = name;
63557c478bd9Sstevel@tonic-gate 	FILE_SYS_DEV(i) = st.st_dev;
63567c478bd9Sstevel@tonic-gate 	FILE_SYS_AVAIL(i) = 0;
63577c478bd9Sstevel@tonic-gate 	FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
63587c478bd9Sstevel@tonic-gate 	return i;
63597c478bd9Sstevel@tonic-gate }
63607c478bd9Sstevel@tonic-gate 
63617c478bd9Sstevel@tonic-gate /*
63627c478bd9Sstevel@tonic-gate **  FILESYS_SETUP -- set up mapping from queue directories to file systems
63637c478bd9Sstevel@tonic-gate **
63647c478bd9Sstevel@tonic-gate **	This data structure is used to efficiently check the amount of
63657c478bd9Sstevel@tonic-gate **	free space available in a set of queue directories.
63667c478bd9Sstevel@tonic-gate **
63677c478bd9Sstevel@tonic-gate **	Parameters:
63687c478bd9Sstevel@tonic-gate **		add -- initialize structure if necessary.
63697c478bd9Sstevel@tonic-gate **
63707c478bd9Sstevel@tonic-gate **	Returns:
63717c478bd9Sstevel@tonic-gate **		0: success
63727c478bd9Sstevel@tonic-gate **		<0: some error, i.e.,
63737c478bd9Sstevel@tonic-gate **		FSF_NOT_FOUND: not in list
63747c478bd9Sstevel@tonic-gate **		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
63757c478bd9Sstevel@tonic-gate **		FSF_TOO_MANY: too many filesystems (-> syserr())
63767c478bd9Sstevel@tonic-gate */
63777c478bd9Sstevel@tonic-gate 
63787c478bd9Sstevel@tonic-gate static int filesys_setup __P((bool));
63797c478bd9Sstevel@tonic-gate 
63807c478bd9Sstevel@tonic-gate static int
63817c478bd9Sstevel@tonic-gate filesys_setup(add)
63827c478bd9Sstevel@tonic-gate 	bool add;
63837c478bd9Sstevel@tonic-gate {
63847c478bd9Sstevel@tonic-gate 	int i, j;
63857c478bd9Sstevel@tonic-gate 	short fs;
63867c478bd9Sstevel@tonic-gate 	int ret;
63877c478bd9Sstevel@tonic-gate 
63887c478bd9Sstevel@tonic-gate 	ret = 0;
63897c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
63907c478bd9Sstevel@tonic-gate 	{
63917c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; ++j)
63927c478bd9Sstevel@tonic-gate 		{
63937c478bd9Sstevel@tonic-gate 			QPATHS *qp = &Queue[i]->qg_qpaths[j];
639449218d4fSjbeck 			char qddf[MAXPATHLEN];
63957c478bd9Sstevel@tonic-gate 
6396*058561cbSjbeck 			(void) sm_strlcpyn(qddf, sizeof(qddf), 2, qp->qp_name,
639749218d4fSjbeck 					(bitset(QP_SUBDF, qp->qp_subdirs)
639849218d4fSjbeck 						? "/df" : ""));
639949218d4fSjbeck 			fs = filesys_find(qp->qp_name, qddf, add);
64007c478bd9Sstevel@tonic-gate 			if (fs >= 0)
64017c478bd9Sstevel@tonic-gate 				qp->qp_fsysidx = fs;
64027c478bd9Sstevel@tonic-gate 			else
64037c478bd9Sstevel@tonic-gate 				qp->qp_fsysidx = 0;
64047c478bd9Sstevel@tonic-gate 			if (fs < ret)
64057c478bd9Sstevel@tonic-gate 				ret = fs;
64067c478bd9Sstevel@tonic-gate 		}
64077c478bd9Sstevel@tonic-gate 	}
64087c478bd9Sstevel@tonic-gate 	return ret;
64097c478bd9Sstevel@tonic-gate }
64107c478bd9Sstevel@tonic-gate 
64117c478bd9Sstevel@tonic-gate /*
64127c478bd9Sstevel@tonic-gate **  FILESYS_UPDATE -- update amount of free space on all file systems
64137c478bd9Sstevel@tonic-gate **
64147c478bd9Sstevel@tonic-gate **	The FileSys table is used to cache the amount of free space
64157c478bd9Sstevel@tonic-gate **	available on all queue directory file systems.
64167c478bd9Sstevel@tonic-gate **	This function updates the cached information if it has expired.
64177c478bd9Sstevel@tonic-gate **
64187c478bd9Sstevel@tonic-gate **	Parameters:
64197c478bd9Sstevel@tonic-gate **		none.
64207c478bd9Sstevel@tonic-gate **
64217c478bd9Sstevel@tonic-gate **	Returns:
64227c478bd9Sstevel@tonic-gate **		none.
64237c478bd9Sstevel@tonic-gate **
64247c478bd9Sstevel@tonic-gate **	Side Effects:
64257c478bd9Sstevel@tonic-gate **		Updates FileSys table.
64267c478bd9Sstevel@tonic-gate */
64277c478bd9Sstevel@tonic-gate 
64287c478bd9Sstevel@tonic-gate void
64297c478bd9Sstevel@tonic-gate filesys_update()
64307c478bd9Sstevel@tonic-gate {
64317c478bd9Sstevel@tonic-gate 	int i;
64327c478bd9Sstevel@tonic-gate 	long avail, blksize;
64337c478bd9Sstevel@tonic-gate 	time_t now;
64347c478bd9Sstevel@tonic-gate 	static time_t nextupdate = 0;
64357c478bd9Sstevel@tonic-gate 
64367c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
6437*058561cbSjbeck 	/*
6438*058561cbSjbeck 	**  Only the daemon updates the shared memory, i.e.,
6439*058561cbSjbeck 	**  if shared memory is available but the pid is not the
6440*058561cbSjbeck 	**  one of the daemon, then don't do anything.
6441*058561cbSjbeck 	*/
6442*058561cbSjbeck 
64431daa5768Sjbeck 	if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
64447c478bd9Sstevel@tonic-gate 		return;
64457c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
64467c478bd9Sstevel@tonic-gate 	now = curtime();
64477c478bd9Sstevel@tonic-gate 	if (now < nextupdate)
64487c478bd9Sstevel@tonic-gate 		return;
64497c478bd9Sstevel@tonic-gate 	nextupdate = now + FILESYS_UPDATE_INTERVAL;
64507c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
64517c478bd9Sstevel@tonic-gate 	{
64527c478bd9Sstevel@tonic-gate 		FILESYS *fs = &FILE_SYS(i);
64537c478bd9Sstevel@tonic-gate 
64547c478bd9Sstevel@tonic-gate 		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
64557c478bd9Sstevel@tonic-gate 		if (avail < 0 || blksize <= 0)
64567c478bd9Sstevel@tonic-gate 		{
64577c478bd9Sstevel@tonic-gate 			if (LogLevel > 5)
64587c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
64597c478bd9Sstevel@tonic-gate 					"filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
64607c478bd9Sstevel@tonic-gate 					sm_errstring(errno),
64617c478bd9Sstevel@tonic-gate 					FILE_SYS_NAME(i), avail, blksize);
64627c478bd9Sstevel@tonic-gate 			fs->fs_avail = 0;
64637c478bd9Sstevel@tonic-gate 			fs->fs_blksize = 1024; /* avoid divide by zero */
64647c478bd9Sstevel@tonic-gate 			nextupdate = now + 2; /* let's do this soon again */
64657c478bd9Sstevel@tonic-gate 		}
64667c478bd9Sstevel@tonic-gate 		else
64677c478bd9Sstevel@tonic-gate 		{
64687c478bd9Sstevel@tonic-gate 			fs->fs_avail = avail;
64697c478bd9Sstevel@tonic-gate 			fs->fs_blksize = blksize;
64707c478bd9Sstevel@tonic-gate 		}
64717c478bd9Sstevel@tonic-gate 	}
64727c478bd9Sstevel@tonic-gate }
64737c478bd9Sstevel@tonic-gate 
64747c478bd9Sstevel@tonic-gate #if _FFR_ANY_FREE_FS
64757c478bd9Sstevel@tonic-gate /*
64767c478bd9Sstevel@tonic-gate **  FILESYS_FREE -- check whether there is at least one fs with enough space.
64777c478bd9Sstevel@tonic-gate **
64787c478bd9Sstevel@tonic-gate **	Parameters:
64797c478bd9Sstevel@tonic-gate **		fsize -- file size in bytes
64807c478bd9Sstevel@tonic-gate **
64817c478bd9Sstevel@tonic-gate **	Returns:
64827c478bd9Sstevel@tonic-gate **		true iff there is one fs with more than fsize bytes free.
64837c478bd9Sstevel@tonic-gate */
64847c478bd9Sstevel@tonic-gate 
64857c478bd9Sstevel@tonic-gate bool
64867c478bd9Sstevel@tonic-gate filesys_free(fsize)
64877c478bd9Sstevel@tonic-gate 	long fsize;
64887c478bd9Sstevel@tonic-gate {
64897c478bd9Sstevel@tonic-gate 	int i;
64907c478bd9Sstevel@tonic-gate 
64917c478bd9Sstevel@tonic-gate 	if (fsize <= 0)
64927c478bd9Sstevel@tonic-gate 		return true;
64937c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
64947c478bd9Sstevel@tonic-gate 	{
64957c478bd9Sstevel@tonic-gate 		long needed = 0;
64967c478bd9Sstevel@tonic-gate 
64977c478bd9Sstevel@tonic-gate 		if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
64987c478bd9Sstevel@tonic-gate 			continue;
64997c478bd9Sstevel@tonic-gate 		needed += fsize / FILE_SYS_BLKSIZE(i)
65007c478bd9Sstevel@tonic-gate 			  + ((fsize % FILE_SYS_BLKSIZE(i)
65017c478bd9Sstevel@tonic-gate 			      > 0) ? 1 : 0)
65027c478bd9Sstevel@tonic-gate 			  + MinBlocksFree;
65037c478bd9Sstevel@tonic-gate 		if (needed <= FILE_SYS_AVAIL(i))
65047c478bd9Sstevel@tonic-gate 			return true;
65057c478bd9Sstevel@tonic-gate 	}
65067c478bd9Sstevel@tonic-gate 	return false;
65077c478bd9Sstevel@tonic-gate }
65087c478bd9Sstevel@tonic-gate #endif /* _FFR_ANY_FREE_FS */
65097c478bd9Sstevel@tonic-gate 
65107c478bd9Sstevel@tonic-gate /*
65117c478bd9Sstevel@tonic-gate **  DISK_STATUS -- show amount of free space in queue directories
65127c478bd9Sstevel@tonic-gate **
65137c478bd9Sstevel@tonic-gate **	Parameters:
65147c478bd9Sstevel@tonic-gate **		out -- output file pointer.
65157c478bd9Sstevel@tonic-gate **		prefix -- string to output in front of each line.
65167c478bd9Sstevel@tonic-gate **
65177c478bd9Sstevel@tonic-gate **	Returns:
65187c478bd9Sstevel@tonic-gate **		none.
65197c478bd9Sstevel@tonic-gate */
65207c478bd9Sstevel@tonic-gate 
65217c478bd9Sstevel@tonic-gate void
65227c478bd9Sstevel@tonic-gate disk_status(out, prefix)
65237c478bd9Sstevel@tonic-gate 	SM_FILE_T *out;
65247c478bd9Sstevel@tonic-gate 	char *prefix;
65257c478bd9Sstevel@tonic-gate {
65267c478bd9Sstevel@tonic-gate 	int i;
65277c478bd9Sstevel@tonic-gate 	long avail, blksize;
65287c478bd9Sstevel@tonic-gate 	long free;
65297c478bd9Sstevel@tonic-gate 
65307c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
65317c478bd9Sstevel@tonic-gate 	{
65327c478bd9Sstevel@tonic-gate 		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
65337c478bd9Sstevel@tonic-gate 		if (avail >= 0 && blksize > 0)
65347c478bd9Sstevel@tonic-gate 		{
65357c478bd9Sstevel@tonic-gate 			free = (long)((double) avail *
65367c478bd9Sstevel@tonic-gate 				((double) blksize / 1024));
65377c478bd9Sstevel@tonic-gate 		}
65387c478bd9Sstevel@tonic-gate 		else
65397c478bd9Sstevel@tonic-gate 			free = -1;
65407c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
65417c478bd9Sstevel@tonic-gate 				"%s%d/%s/%ld\r\n",
65427c478bd9Sstevel@tonic-gate 				prefix, i,
65437c478bd9Sstevel@tonic-gate 				FILE_SYS_NAME(i),
65447c478bd9Sstevel@tonic-gate 					free);
65457c478bd9Sstevel@tonic-gate 	}
65467c478bd9Sstevel@tonic-gate }
65477c478bd9Sstevel@tonic-gate 
65487c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
65497c478bd9Sstevel@tonic-gate 
65507c478bd9Sstevel@tonic-gate /*
65517c478bd9Sstevel@tonic-gate **  INIT_SEM -- initialize semaphore system
65527c478bd9Sstevel@tonic-gate **
65537c478bd9Sstevel@tonic-gate **	Parameters:
65547c478bd9Sstevel@tonic-gate **		owner -- is this the owner of semaphores?
65557c478bd9Sstevel@tonic-gate **
65567c478bd9Sstevel@tonic-gate **	Returns:
65577c478bd9Sstevel@tonic-gate **		none.
65587c478bd9Sstevel@tonic-gate */
65597c478bd9Sstevel@tonic-gate 
65607c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
65617c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
65627c478bd9Sstevel@tonic-gate static int SemId = -1;		/* Semaphore Id */
65637c478bd9Sstevel@tonic-gate int SemKey = SM_SEM_KEY;
65647c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
65657c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
65667c478bd9Sstevel@tonic-gate 
65677c478bd9Sstevel@tonic-gate static void init_sem __P((bool));
65687c478bd9Sstevel@tonic-gate 
65697c478bd9Sstevel@tonic-gate static void
65707c478bd9Sstevel@tonic-gate init_sem(owner)
65717c478bd9Sstevel@tonic-gate 	bool owner;
65727c478bd9Sstevel@tonic-gate {
65737c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
65747c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
65757c478bd9Sstevel@tonic-gate 	SemId = sm_sem_start(SemKey, 1, 0, owner);
65767c478bd9Sstevel@tonic-gate 	if (SemId < 0)
65777c478bd9Sstevel@tonic-gate 	{
65787c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID,
65797c478bd9Sstevel@tonic-gate 			"func=init_sem, sem_key=%ld, sm_sem_start=%d",
65807c478bd9Sstevel@tonic-gate 			(long) SemKey, SemId);
65817c478bd9Sstevel@tonic-gate 		return;
65827c478bd9Sstevel@tonic-gate 	}
65837c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
65847c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
65857c478bd9Sstevel@tonic-gate 	return;
65867c478bd9Sstevel@tonic-gate }
65877c478bd9Sstevel@tonic-gate 
65887c478bd9Sstevel@tonic-gate /*
65897c478bd9Sstevel@tonic-gate **  STOP_SEM -- stop semaphore system
65907c478bd9Sstevel@tonic-gate **
65917c478bd9Sstevel@tonic-gate **	Parameters:
65927c478bd9Sstevel@tonic-gate **		owner -- is this the owner of semaphores?
65937c478bd9Sstevel@tonic-gate **
65947c478bd9Sstevel@tonic-gate **	Returns:
65957c478bd9Sstevel@tonic-gate **		none.
65967c478bd9Sstevel@tonic-gate */
65977c478bd9Sstevel@tonic-gate 
65987c478bd9Sstevel@tonic-gate static void stop_sem __P((bool));
65997c478bd9Sstevel@tonic-gate 
66007c478bd9Sstevel@tonic-gate static void
66017c478bd9Sstevel@tonic-gate stop_sem(owner)
66027c478bd9Sstevel@tonic-gate 	bool owner;
66037c478bd9Sstevel@tonic-gate {
66047c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
66057c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
66067c478bd9Sstevel@tonic-gate 	if (owner && SemId >= 0)
66077c478bd9Sstevel@tonic-gate 		sm_sem_stop(SemId);
66087c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
66097c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
66107c478bd9Sstevel@tonic-gate 	return;
66117c478bd9Sstevel@tonic-gate }
66127c478bd9Sstevel@tonic-gate 
66137c478bd9Sstevel@tonic-gate /*
66147c478bd9Sstevel@tonic-gate **  UPD_QS -- update information about queue when adding/deleting an entry
66157c478bd9Sstevel@tonic-gate **
66167c478bd9Sstevel@tonic-gate **	Parameters:
66177c478bd9Sstevel@tonic-gate **		e -- envelope.
66187c478bd9Sstevel@tonic-gate **		count -- add/remove entry (+1/0/-1: add/no change/remove)
66197c478bd9Sstevel@tonic-gate **		space -- update the space available as well.
66207c478bd9Sstevel@tonic-gate **			(>0/0/<0: add/no change/remove)
66217c478bd9Sstevel@tonic-gate **		where -- caller (for logging)
66227c478bd9Sstevel@tonic-gate **
66237c478bd9Sstevel@tonic-gate **	Returns:
66247c478bd9Sstevel@tonic-gate **		none.
66257c478bd9Sstevel@tonic-gate **
66267c478bd9Sstevel@tonic-gate **	Side Effects:
66277c478bd9Sstevel@tonic-gate **		Modifies available space in filesystem.
66287c478bd9Sstevel@tonic-gate **		Changes number of entries in queue directory.
66297c478bd9Sstevel@tonic-gate */
66307c478bd9Sstevel@tonic-gate 
66317c478bd9Sstevel@tonic-gate void
66327c478bd9Sstevel@tonic-gate upd_qs(e, count, space, where)
66337c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
66347c478bd9Sstevel@tonic-gate 	int count;
66357c478bd9Sstevel@tonic-gate 	int space;
66367c478bd9Sstevel@tonic-gate 	char *where;
66377c478bd9Sstevel@tonic-gate {
66387c478bd9Sstevel@tonic-gate 	short fidx;
66397c478bd9Sstevel@tonic-gate 	int idx;
66407c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66417c478bd9Sstevel@tonic-gate 	int r;
66427c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66437c478bd9Sstevel@tonic-gate 	long s;
66447c478bd9Sstevel@tonic-gate 
66457c478bd9Sstevel@tonic-gate 	if (ShmId == SM_SHM_NO_ID || e == NULL)
66467c478bd9Sstevel@tonic-gate 		return;
66477c478bd9Sstevel@tonic-gate 	if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
66487c478bd9Sstevel@tonic-gate 		return;
66497c478bd9Sstevel@tonic-gate 	idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
66507c478bd9Sstevel@tonic-gate 	if (tTd(73,2))
66517c478bd9Sstevel@tonic-gate 		sm_dprintf("func=upd_qs, count=%d, space=%d, where=%s, idx=%d, entries=%d\n",
66527c478bd9Sstevel@tonic-gate 			count, space, where, idx, QSHM_ENTRIES(idx));
66537c478bd9Sstevel@tonic-gate 
66547c478bd9Sstevel@tonic-gate 	/* XXX in theory this needs to be protected with a mutex */
66557c478bd9Sstevel@tonic-gate 	if (QSHM_ENTRIES(idx) >= 0 && count != 0)
66567c478bd9Sstevel@tonic-gate 	{
66577c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66587c478bd9Sstevel@tonic-gate 		r = sm_sem_acq(SemId, 0, 1);
66597c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66607c478bd9Sstevel@tonic-gate 		QSHM_ENTRIES(idx) += count;
66617c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66627c478bd9Sstevel@tonic-gate 		if (r >= 0)
66637c478bd9Sstevel@tonic-gate 			r = sm_sem_rel(SemId, 0, 1);
66647c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66657c478bd9Sstevel@tonic-gate 	}
66667c478bd9Sstevel@tonic-gate 
66677c478bd9Sstevel@tonic-gate 	fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
66687c478bd9Sstevel@tonic-gate 	if (fidx < 0)
66697c478bd9Sstevel@tonic-gate 		return;
66707c478bd9Sstevel@tonic-gate 
66717c478bd9Sstevel@tonic-gate 	/* update available space also?  (might be loseqfile) */
66727c478bd9Sstevel@tonic-gate 	if (space == 0)
66737c478bd9Sstevel@tonic-gate 		return;
66747c478bd9Sstevel@tonic-gate 
66757c478bd9Sstevel@tonic-gate 	/* convert size to blocks; this causes rounding errors */
66767c478bd9Sstevel@tonic-gate 	s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
66777c478bd9Sstevel@tonic-gate 	if (s == 0)
66787c478bd9Sstevel@tonic-gate 		return;
66797c478bd9Sstevel@tonic-gate 
66807c478bd9Sstevel@tonic-gate 	/* XXX in theory this needs to be protected with a mutex */
66817c478bd9Sstevel@tonic-gate 	if (space > 0)
66827c478bd9Sstevel@tonic-gate 		FILE_SYS_AVAIL(fidx) += s;
66837c478bd9Sstevel@tonic-gate 	else
66847c478bd9Sstevel@tonic-gate 		FILE_SYS_AVAIL(fidx) -= s;
66857c478bd9Sstevel@tonic-gate 
66867c478bd9Sstevel@tonic-gate }
66877c478bd9Sstevel@tonic-gate 
66887c478bd9Sstevel@tonic-gate static bool write_key_file __P((char *, long));
66897c478bd9Sstevel@tonic-gate static long read_key_file __P((char *, long));
66907c478bd9Sstevel@tonic-gate 
66917c478bd9Sstevel@tonic-gate /*
66927c478bd9Sstevel@tonic-gate **  WRITE_KEY_FILE -- record some key into a file.
66937c478bd9Sstevel@tonic-gate **
66947c478bd9Sstevel@tonic-gate **	Parameters:
66957c478bd9Sstevel@tonic-gate **		keypath -- file name.
66967c478bd9Sstevel@tonic-gate **		key -- key to write.
66977c478bd9Sstevel@tonic-gate **
66987c478bd9Sstevel@tonic-gate **	Returns:
66997c478bd9Sstevel@tonic-gate **		true iff file could be written.
67007c478bd9Sstevel@tonic-gate **
67017c478bd9Sstevel@tonic-gate **	Side Effects:
67027c478bd9Sstevel@tonic-gate **		writes file.
67037c478bd9Sstevel@tonic-gate */
67047c478bd9Sstevel@tonic-gate 
67057c478bd9Sstevel@tonic-gate static bool
67067c478bd9Sstevel@tonic-gate write_key_file(keypath, key)
67077c478bd9Sstevel@tonic-gate 	char *keypath;
67087c478bd9Sstevel@tonic-gate 	long key;
67097c478bd9Sstevel@tonic-gate {
67107c478bd9Sstevel@tonic-gate 	bool ok;
67117c478bd9Sstevel@tonic-gate 	long sff;
67127c478bd9Sstevel@tonic-gate 	SM_FILE_T *keyf;
67137c478bd9Sstevel@tonic-gate 
67147c478bd9Sstevel@tonic-gate 	ok = false;
67157c478bd9Sstevel@tonic-gate 	if (keypath == NULL || *keypath == '\0')
67167c478bd9Sstevel@tonic-gate 		return ok;
67177c478bd9Sstevel@tonic-gate 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
67187c478bd9Sstevel@tonic-gate 	if (TrustedUid != 0 && RealUid == TrustedUid)
67197c478bd9Sstevel@tonic-gate 		sff |= SFF_OPENASROOT;
67207c478bd9Sstevel@tonic-gate 	keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff);
67217c478bd9Sstevel@tonic-gate 	if (keyf == NULL)
67227c478bd9Sstevel@tonic-gate 	{
67237c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
67247c478bd9Sstevel@tonic-gate 			  keypath, sm_errstring(errno));
67257c478bd9Sstevel@tonic-gate 	}
67267c478bd9Sstevel@tonic-gate 	else
67277c478bd9Sstevel@tonic-gate 	{
672849218d4fSjbeck 		if (geteuid() == 0 && RunAsUid != 0)
672949218d4fSjbeck 		{
673049218d4fSjbeck #  if HASFCHOWN
673149218d4fSjbeck 			int fd;
673249218d4fSjbeck 
673349218d4fSjbeck 			fd = keyf->f_file;
673449218d4fSjbeck 			if (fd >= 0 && fchown(fd, RunAsUid, -1) < 0)
673549218d4fSjbeck 			{
673649218d4fSjbeck 				int err = errno;
673749218d4fSjbeck 
673849218d4fSjbeck 				sm_syslog(LOG_ALERT, NOQID,
673949218d4fSjbeck 					  "ownership change on %s to %d failed: %s",
674049218d4fSjbeck 					  keypath, RunAsUid, sm_errstring(err));
674149218d4fSjbeck 			}
674249218d4fSjbeck #  endif /* HASFCHOWN */
674349218d4fSjbeck 		}
67447c478bd9Sstevel@tonic-gate 		ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
67457c478bd9Sstevel@tonic-gate 		     SM_IO_EOF;
67467c478bd9Sstevel@tonic-gate 		ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok;
67477c478bd9Sstevel@tonic-gate 	}
67487c478bd9Sstevel@tonic-gate 	return ok;
67497c478bd9Sstevel@tonic-gate }
67507c478bd9Sstevel@tonic-gate 
67517c478bd9Sstevel@tonic-gate /*
67527c478bd9Sstevel@tonic-gate **  READ_KEY_FILE -- read a key from a file.
67537c478bd9Sstevel@tonic-gate **
67547c478bd9Sstevel@tonic-gate **	Parameters:
67557c478bd9Sstevel@tonic-gate **		keypath -- file name.
67567c478bd9Sstevel@tonic-gate **		key -- default key.
67577c478bd9Sstevel@tonic-gate **
67587c478bd9Sstevel@tonic-gate **	Returns:
67597c478bd9Sstevel@tonic-gate **		key.
67607c478bd9Sstevel@tonic-gate */
67617c478bd9Sstevel@tonic-gate 
67627c478bd9Sstevel@tonic-gate static long
67637c478bd9Sstevel@tonic-gate read_key_file(keypath, key)
67647c478bd9Sstevel@tonic-gate 	char *keypath;
67657c478bd9Sstevel@tonic-gate 	long key;
67667c478bd9Sstevel@tonic-gate {
67677c478bd9Sstevel@tonic-gate 	int r;
67687c478bd9Sstevel@tonic-gate 	long sff, n;
67697c478bd9Sstevel@tonic-gate 	SM_FILE_T *keyf;
67707c478bd9Sstevel@tonic-gate 
67717c478bd9Sstevel@tonic-gate 	if (keypath == NULL || *keypath == '\0')
67727c478bd9Sstevel@tonic-gate 		return key;
67737c478bd9Sstevel@tonic-gate 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
67747c478bd9Sstevel@tonic-gate 	if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid))
67757c478bd9Sstevel@tonic-gate 		sff |= SFF_OPENASROOT;
67767c478bd9Sstevel@tonic-gate 	keyf = safefopen(keypath, O_RDONLY, FileMode, sff);
67777c478bd9Sstevel@tonic-gate 	if (keyf == NULL)
67787c478bd9Sstevel@tonic-gate 	{
67797c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s",
67807c478bd9Sstevel@tonic-gate 			  keypath, sm_errstring(errno));
67817c478bd9Sstevel@tonic-gate 	}
67827c478bd9Sstevel@tonic-gate 	else
67837c478bd9Sstevel@tonic-gate 	{
67847c478bd9Sstevel@tonic-gate 		r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n);
67857c478bd9Sstevel@tonic-gate 		if (r == 1)
67867c478bd9Sstevel@tonic-gate 			key = n;
67877c478bd9Sstevel@tonic-gate 		(void) sm_io_close(keyf, SM_TIME_DEFAULT);
67887c478bd9Sstevel@tonic-gate 	}
67897c478bd9Sstevel@tonic-gate 	return key;
67907c478bd9Sstevel@tonic-gate }
67917c478bd9Sstevel@tonic-gate 
67927c478bd9Sstevel@tonic-gate /*
67937c478bd9Sstevel@tonic-gate **  INIT_SHM -- initialize shared memory structure
67947c478bd9Sstevel@tonic-gate **
67957c478bd9Sstevel@tonic-gate **	Initialize or attach to shared memory segment.
67967c478bd9Sstevel@tonic-gate **	Currently it is not a fatal error if this doesn't work.
67977c478bd9Sstevel@tonic-gate **	However, it causes us to have a "fallback" storage location
67987c478bd9Sstevel@tonic-gate **	for everything that is supposed to be in the shared memory,
67997c478bd9Sstevel@tonic-gate **	which makes the code slightly ugly.
68007c478bd9Sstevel@tonic-gate **
68017c478bd9Sstevel@tonic-gate **	Parameters:
68027c478bd9Sstevel@tonic-gate **		qn -- number of queue directories.
68037c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory.
68047c478bd9Sstevel@tonic-gate **		hash -- identifies data that is stored in shared memory.
68057c478bd9Sstevel@tonic-gate **
68067c478bd9Sstevel@tonic-gate **	Returns:
68077c478bd9Sstevel@tonic-gate **		none.
68087c478bd9Sstevel@tonic-gate */
68097c478bd9Sstevel@tonic-gate 
68107c478bd9Sstevel@tonic-gate static void init_shm __P((int, bool, unsigned int));
68117c478bd9Sstevel@tonic-gate 
68127c478bd9Sstevel@tonic-gate static void
68137c478bd9Sstevel@tonic-gate init_shm(qn, owner, hash)
68147c478bd9Sstevel@tonic-gate 	int qn;
68157c478bd9Sstevel@tonic-gate 	bool owner;
68167c478bd9Sstevel@tonic-gate 	unsigned int hash;
68177c478bd9Sstevel@tonic-gate {
68187c478bd9Sstevel@tonic-gate 	int i;
68197c478bd9Sstevel@tonic-gate 	int count;
68207c478bd9Sstevel@tonic-gate 	int save_errno;
68217c478bd9Sstevel@tonic-gate 	bool keyselect;
68227c478bd9Sstevel@tonic-gate 
68237c478bd9Sstevel@tonic-gate 	PtrFileSys = &FileSys[0];
68247c478bd9Sstevel@tonic-gate 	PNumFileSys = &Numfilesys;
68257c478bd9Sstevel@tonic-gate /* if this "key" is specified: select one yourself */
6826*058561cbSjbeck #define SEL_SHM_KEY	((key_t) -1)
6827*058561cbSjbeck #define FIRST_SHM_KEY	25
68287c478bd9Sstevel@tonic-gate 
68297c478bd9Sstevel@tonic-gate 	/* This allows us to disable shared memory at runtime. */
68307c478bd9Sstevel@tonic-gate 	if (ShmKey == 0)
68317c478bd9Sstevel@tonic-gate 		return;
68327c478bd9Sstevel@tonic-gate 
68337c478bd9Sstevel@tonic-gate 	count = 0;
68347c478bd9Sstevel@tonic-gate 	shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
68357c478bd9Sstevel@tonic-gate 	keyselect = ShmKey == SEL_SHM_KEY;
68367c478bd9Sstevel@tonic-gate 	if (keyselect)
68377c478bd9Sstevel@tonic-gate 	{
68387c478bd9Sstevel@tonic-gate 		if (owner)
68397c478bd9Sstevel@tonic-gate 			ShmKey = FIRST_SHM_KEY;
68407c478bd9Sstevel@tonic-gate 		else
68417c478bd9Sstevel@tonic-gate 		{
6842*058561cbSjbeck 			errno = 0;
68437c478bd9Sstevel@tonic-gate 			ShmKey = read_key_file(ShmKeyFile, ShmKey);
68447c478bd9Sstevel@tonic-gate 			keyselect = false;
68457c478bd9Sstevel@tonic-gate 			if (ShmKey == SEL_SHM_KEY)
6846*058561cbSjbeck 			{
6847*058561cbSjbeck 				save_errno = (errno != 0) ? errno : EINVAL;
68487c478bd9Sstevel@tonic-gate 				goto error;
6849*058561cbSjbeck 			}
68507c478bd9Sstevel@tonic-gate 		}
68517c478bd9Sstevel@tonic-gate 	}
68527c478bd9Sstevel@tonic-gate 	for (;;)
68537c478bd9Sstevel@tonic-gate 	{
68547c478bd9Sstevel@tonic-gate 		/* allow read/write access for group? */
68557c478bd9Sstevel@tonic-gate 		Pshm = sm_shmstart(ShmKey, shms,
68567c478bd9Sstevel@tonic-gate 				SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3),
68577c478bd9Sstevel@tonic-gate 				&ShmId, owner);
68587c478bd9Sstevel@tonic-gate 		save_errno = errno;
68597c478bd9Sstevel@tonic-gate 		if (Pshm != NULL || !sm_file_exists(save_errno))
68607c478bd9Sstevel@tonic-gate 			break;
68617c478bd9Sstevel@tonic-gate 		if (++count >= 3)
68627c478bd9Sstevel@tonic-gate 		{
68637c478bd9Sstevel@tonic-gate 			if (keyselect)
68647c478bd9Sstevel@tonic-gate 			{
68657c478bd9Sstevel@tonic-gate 				++ShmKey;
68667c478bd9Sstevel@tonic-gate 
68677c478bd9Sstevel@tonic-gate 				/* back where we started? */
68687c478bd9Sstevel@tonic-gate 				if (ShmKey == SEL_SHM_KEY)
68697c478bd9Sstevel@tonic-gate 					break;
68707c478bd9Sstevel@tonic-gate 				continue;
68717c478bd9Sstevel@tonic-gate 			}
68727c478bd9Sstevel@tonic-gate 			break;
68737c478bd9Sstevel@tonic-gate 		}
6874*058561cbSjbeck 
68757c478bd9Sstevel@tonic-gate 		/* only sleep if we are at the first key */
68767c478bd9Sstevel@tonic-gate 		if (!keyselect || ShmKey == SEL_SHM_KEY)
6877*058561cbSjbeck 			sleep(count);
68787c478bd9Sstevel@tonic-gate 	}
68797c478bd9Sstevel@tonic-gate 	if (Pshm != NULL)
68807c478bd9Sstevel@tonic-gate 	{
68817c478bd9Sstevel@tonic-gate 		int *p;
68827c478bd9Sstevel@tonic-gate 
68837c478bd9Sstevel@tonic-gate 		if (keyselect)
68847c478bd9Sstevel@tonic-gate 			(void) write_key_file(ShmKeyFile, (long) ShmKey);
68857c478bd9Sstevel@tonic-gate 		if (owner && RunAsUid != 0)
68867c478bd9Sstevel@tonic-gate 		{
6887445f2479Sjbeck 			i = sm_shmsetowner(ShmId, RunAsUid, RunAsGid, 0660);
68887c478bd9Sstevel@tonic-gate 			if (i != 0)
68897c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
6890445f2479Sjbeck 					"key=%ld, sm_shmsetowner=%d, RunAsUid=%d, RunAsGid=%d",
6891445f2479Sjbeck 					(long) ShmKey, i, RunAsUid, RunAsGid);
68927c478bd9Sstevel@tonic-gate 		}
68937c478bd9Sstevel@tonic-gate 		p = (int *) Pshm;
68947c478bd9Sstevel@tonic-gate 		if (owner)
68957c478bd9Sstevel@tonic-gate 		{
68967c478bd9Sstevel@tonic-gate 			*p = (int) shms;
68977c478bd9Sstevel@tonic-gate 			*((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
68987c478bd9Sstevel@tonic-gate 			p = (int *) SHM_OFF_TAG(Pshm);
68997c478bd9Sstevel@tonic-gate 			*p = hash;
69007c478bd9Sstevel@tonic-gate 		}
69017c478bd9Sstevel@tonic-gate 		else
69027c478bd9Sstevel@tonic-gate 		{
69037c478bd9Sstevel@tonic-gate 			if (*p != (int) shms)
69047c478bd9Sstevel@tonic-gate 			{
69057c478bd9Sstevel@tonic-gate 				save_errno = EINVAL;
69067c478bd9Sstevel@tonic-gate 				cleanup_shm(false);
69077c478bd9Sstevel@tonic-gate 				goto error;
69087c478bd9Sstevel@tonic-gate 			}
69097c478bd9Sstevel@tonic-gate 			p = (int *) SHM_OFF_TAG(Pshm);
69107c478bd9Sstevel@tonic-gate 			if (*p != (int) hash)
69117c478bd9Sstevel@tonic-gate 			{
69127c478bd9Sstevel@tonic-gate 				save_errno = EINVAL;
69137c478bd9Sstevel@tonic-gate 				cleanup_shm(false);
69147c478bd9Sstevel@tonic-gate 				goto error;
69157c478bd9Sstevel@tonic-gate 			}
69167c478bd9Sstevel@tonic-gate 
69177c478bd9Sstevel@tonic-gate 			/*
69187c478bd9Sstevel@tonic-gate 			**  XXX how to check the pid?
69197c478bd9Sstevel@tonic-gate 			**  Read it from the pid-file? That does
69207c478bd9Sstevel@tonic-gate 			**  not need to exist.
69217c478bd9Sstevel@tonic-gate 			**  We could disable shm if we can't confirm
69227c478bd9Sstevel@tonic-gate 			**  that it is the right one.
69237c478bd9Sstevel@tonic-gate 			*/
69247c478bd9Sstevel@tonic-gate 		}
69257c478bd9Sstevel@tonic-gate 
69267c478bd9Sstevel@tonic-gate 		PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
69277c478bd9Sstevel@tonic-gate 		PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
69287c478bd9Sstevel@tonic-gate 		QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
69297c478bd9Sstevel@tonic-gate 		PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
69307c478bd9Sstevel@tonic-gate 		*PRSATmpCnt = 0;
69317c478bd9Sstevel@tonic-gate 		if (owner)
69327c478bd9Sstevel@tonic-gate 		{
69337c478bd9Sstevel@tonic-gate 			/* initialize values in shared memory */
69347c478bd9Sstevel@tonic-gate 			NumFileSys = 0;
69357c478bd9Sstevel@tonic-gate 			for (i = 0; i < qn; i++)
69367c478bd9Sstevel@tonic-gate 				QShm[i].qs_entries = -1;
69377c478bd9Sstevel@tonic-gate 		}
69387c478bd9Sstevel@tonic-gate 		init_sem(owner);
69397c478bd9Sstevel@tonic-gate 		return;
69407c478bd9Sstevel@tonic-gate 	}
69417c478bd9Sstevel@tonic-gate   error:
69427c478bd9Sstevel@tonic-gate 	if (LogLevel > (owner ? 8 : 11))
69437c478bd9Sstevel@tonic-gate 	{
69447c478bd9Sstevel@tonic-gate 		sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
69457c478bd9Sstevel@tonic-gate 			  "can't %s shared memory, key=%ld: %s",
69467c478bd9Sstevel@tonic-gate 			  owner ? "initialize" : "attach to",
69477c478bd9Sstevel@tonic-gate 			  (long) ShmKey, sm_errstring(save_errno));
69487c478bd9Sstevel@tonic-gate 	}
69497c478bd9Sstevel@tonic-gate }
69507c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
69517c478bd9Sstevel@tonic-gate 
69527c478bd9Sstevel@tonic-gate 
69537c478bd9Sstevel@tonic-gate /*
6954*058561cbSjbeck **  SETUP_QUEUES -- set up all queue groups
69557c478bd9Sstevel@tonic-gate **
69567c478bd9Sstevel@tonic-gate **	Parameters:
6957*058561cbSjbeck **		owner -- owner of shared memory?
69587c478bd9Sstevel@tonic-gate **
69597c478bd9Sstevel@tonic-gate **	Returns:
69607c478bd9Sstevel@tonic-gate **		none.
69617c478bd9Sstevel@tonic-gate **
69627c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
69637c478bd9Sstevel@tonic-gate **	Side Effects:
69647c478bd9Sstevel@tonic-gate **		attaches shared memory.
69657c478bd9Sstevel@tonic-gate #endif * SM_CONF_SHM *
69667c478bd9Sstevel@tonic-gate */
69677c478bd9Sstevel@tonic-gate 
69687c478bd9Sstevel@tonic-gate void
69697c478bd9Sstevel@tonic-gate setup_queues(owner)
69707c478bd9Sstevel@tonic-gate 	bool owner;
69717c478bd9Sstevel@tonic-gate {
69727c478bd9Sstevel@tonic-gate 	int i, qn, len;
69737c478bd9Sstevel@tonic-gate 	unsigned int hashval;
69747c478bd9Sstevel@tonic-gate 	time_t now;
69757c478bd9Sstevel@tonic-gate 	char basedir[MAXPATHLEN];
69767c478bd9Sstevel@tonic-gate 	struct stat st;
69777c478bd9Sstevel@tonic-gate 
69787c478bd9Sstevel@tonic-gate 	/*
69797c478bd9Sstevel@tonic-gate 	**  Determine basedir for all queue directories.
69807c478bd9Sstevel@tonic-gate 	**  All queue directories must be (first level) subdirectories
69817c478bd9Sstevel@tonic-gate 	**  of the basedir.  The basedir is the QueueDir
69827c478bd9Sstevel@tonic-gate 	**  without wildcards, but with trailing /
69837c478bd9Sstevel@tonic-gate 	*/
69847c478bd9Sstevel@tonic-gate 
69857c478bd9Sstevel@tonic-gate 	hashval = 0;
69867c478bd9Sstevel@tonic-gate 	errno = 0;
6987*058561cbSjbeck 	len = sm_strlcpy(basedir, QueueDir, sizeof(basedir));
69887c478bd9Sstevel@tonic-gate 
69897c478bd9Sstevel@tonic-gate 	/* Provide space for trailing '/' */
6990*058561cbSjbeck 	if (len >= sizeof(basedir) - 1)
69917c478bd9Sstevel@tonic-gate 	{
69927c478bd9Sstevel@tonic-gate 		syserr("QueueDirectory: path too long: %d,  max %d",
6993*058561cbSjbeck 			len, (int) sizeof(basedir) - 1);
69947c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
69957c478bd9Sstevel@tonic-gate 		return;
69967c478bd9Sstevel@tonic-gate 	}
69977c478bd9Sstevel@tonic-gate 	SM_ASSERT(len > 0);
69987c478bd9Sstevel@tonic-gate 	if (basedir[len - 1] == '*')
69997c478bd9Sstevel@tonic-gate 	{
70007c478bd9Sstevel@tonic-gate 		char *cp;
70017c478bd9Sstevel@tonic-gate 
70027c478bd9Sstevel@tonic-gate 		cp = SM_LAST_DIR_DELIM(basedir);
70037c478bd9Sstevel@tonic-gate 		if (cp == NULL)
70047c478bd9Sstevel@tonic-gate 		{
70057c478bd9Sstevel@tonic-gate 			syserr("QueueDirectory: can not wildcard relative path \"%s\"",
70067c478bd9Sstevel@tonic-gate 				QueueDir);
70077c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
70087c478bd9Sstevel@tonic-gate 				sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
70097c478bd9Sstevel@tonic-gate 					QueueDir);
70107c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
70117c478bd9Sstevel@tonic-gate 			return;
70127c478bd9Sstevel@tonic-gate 		}
70137c478bd9Sstevel@tonic-gate 
70147c478bd9Sstevel@tonic-gate 		/* cut off wildcard pattern */
70157c478bd9Sstevel@tonic-gate 		*++cp = '\0';
70167c478bd9Sstevel@tonic-gate 		len = cp - basedir;
70177c478bd9Sstevel@tonic-gate 	}
70187c478bd9Sstevel@tonic-gate 	else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
70197c478bd9Sstevel@tonic-gate 	{
70207c478bd9Sstevel@tonic-gate 		/* append trailing slash since it is a directory */
70217c478bd9Sstevel@tonic-gate 		basedir[len] = '/';
70227c478bd9Sstevel@tonic-gate 		basedir[++len] = '\0';
70237c478bd9Sstevel@tonic-gate 	}
70247c478bd9Sstevel@tonic-gate 
70257c478bd9Sstevel@tonic-gate 	/* len counts up to the last directory delimiter */
70267c478bd9Sstevel@tonic-gate 	SM_ASSERT(basedir[len - 1] == '/');
70277c478bd9Sstevel@tonic-gate 
70287c478bd9Sstevel@tonic-gate 	if (chdir(basedir) < 0)
70297c478bd9Sstevel@tonic-gate 	{
70307c478bd9Sstevel@tonic-gate 		int save_errno = errno;
70317c478bd9Sstevel@tonic-gate 
70327c478bd9Sstevel@tonic-gate 		syserr("can not chdir(%s)", basedir);
70337c478bd9Sstevel@tonic-gate 		if (save_errno == EACCES)
70347c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
70357c478bd9Sstevel@tonic-gate 				"Program mode requires special privileges, e.g., root or TrustedUser.\n");
70367c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
70377c478bd9Sstevel@tonic-gate 			sm_dprintf("setup_queues: \"%s\": %s\n",
70387c478bd9Sstevel@tonic-gate 				   basedir, sm_errstring(errno));
70397c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
70407c478bd9Sstevel@tonic-gate 		return;
70417c478bd9Sstevel@tonic-gate 	}
70427c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
70437c478bd9Sstevel@tonic-gate 	hashval = hash_q(basedir, hashval);
70447c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
70457c478bd9Sstevel@tonic-gate 
70467c478bd9Sstevel@tonic-gate 	/* initialize for queue runs */
70477c478bd9Sstevel@tonic-gate 	DoQueueRun = false;
70487c478bd9Sstevel@tonic-gate 	now = curtime();
70497c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
70507c478bd9Sstevel@tonic-gate 		Queue[i]->qg_nextrun = now;
70517c478bd9Sstevel@tonic-gate 
70527c478bd9Sstevel@tonic-gate 
70537c478bd9Sstevel@tonic-gate 	if (UseMSP && OpMode != MD_TEST)
70547c478bd9Sstevel@tonic-gate 	{
70557c478bd9Sstevel@tonic-gate 		long sff = SFF_CREAT;
70567c478bd9Sstevel@tonic-gate 
70577c478bd9Sstevel@tonic-gate 		if (stat(".", &st) < 0)
70587c478bd9Sstevel@tonic-gate 		{
70597c478bd9Sstevel@tonic-gate 			syserr("can not stat(%s)", basedir);
70607c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
70617c478bd9Sstevel@tonic-gate 				sm_dprintf("setup_queues: \"%s\": %s\n",
70627c478bd9Sstevel@tonic-gate 					   basedir, sm_errstring(errno));
70637c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
70647c478bd9Sstevel@tonic-gate 			return;
70657c478bd9Sstevel@tonic-gate 		}
70667c478bd9Sstevel@tonic-gate 		if (RunAsUid == 0)
70677c478bd9Sstevel@tonic-gate 			sff |= SFF_ROOTOK;
70687c478bd9Sstevel@tonic-gate 
70697c478bd9Sstevel@tonic-gate 		/*
70707c478bd9Sstevel@tonic-gate 		**  Check queue directory permissions.
70717c478bd9Sstevel@tonic-gate 		**	Can we write to a group writable queue directory?
70727c478bd9Sstevel@tonic-gate 		*/
70737c478bd9Sstevel@tonic-gate 
70747c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode) &&
70757c478bd9Sstevel@tonic-gate 		    bitset(S_IWGRP, st.st_mode) &&
70767c478bd9Sstevel@tonic-gate 		    safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
70777c478bd9Sstevel@tonic-gate 			     QueueFileMode, NULL) != 0)
70787c478bd9Sstevel@tonic-gate 		{
70797c478bd9Sstevel@tonic-gate 			syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
70807c478bd9Sstevel@tonic-gate 				basedir, (int) RunAsGid, (int) st.st_gid);
70817c478bd9Sstevel@tonic-gate 		}
70827c478bd9Sstevel@tonic-gate 		if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
70837c478bd9Sstevel@tonic-gate 		{
70847c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
70857c478bd9Sstevel@tonic-gate 			syserr("dangerous permissions=%o on queue directory %s",
70867c478bd9Sstevel@tonic-gate 				(int) st.st_mode, basedir);
70877c478bd9Sstevel@tonic-gate #else /* _FFR_MSP_PARANOIA */
70887c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
70897c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
70907c478bd9Sstevel@tonic-gate 					  "dangerous permissions=%o on queue directory %s",
70917c478bd9Sstevel@tonic-gate 					  (int) st.st_mode, basedir);
70927c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
70937c478bd9Sstevel@tonic-gate 		}
70947c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
70957c478bd9Sstevel@tonic-gate 		if (NumQueue > 1)
70967c478bd9Sstevel@tonic-gate 			syserr("can not use multiple queues for MSP");
70977c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
70987c478bd9Sstevel@tonic-gate 	}
70997c478bd9Sstevel@tonic-gate 
71007c478bd9Sstevel@tonic-gate 	/* initial number of queue directories */
71017c478bd9Sstevel@tonic-gate 	qn = 0;
71027c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
71037c478bd9Sstevel@tonic-gate 		qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
71047c478bd9Sstevel@tonic-gate 
71057c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
71067c478bd9Sstevel@tonic-gate 	init_shm(qn, owner, hashval);
71077c478bd9Sstevel@tonic-gate 	i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
71087c478bd9Sstevel@tonic-gate 	if (i == FSF_NOT_FOUND)
71097c478bd9Sstevel@tonic-gate 	{
71107c478bd9Sstevel@tonic-gate 		/*
71117c478bd9Sstevel@tonic-gate 		**  We didn't get the right filesystem data
71127c478bd9Sstevel@tonic-gate 		**  This may happen if we don't have the right shared memory.
71137c478bd9Sstevel@tonic-gate 		**  So let's do this without shared memory.
71147c478bd9Sstevel@tonic-gate 		*/
71157c478bd9Sstevel@tonic-gate 
71167c478bd9Sstevel@tonic-gate 		SM_ASSERT(!owner);
71177c478bd9Sstevel@tonic-gate 		cleanup_shm(false);	/* release shared memory */
71187c478bd9Sstevel@tonic-gate 		i = filesys_setup(false);
71197c478bd9Sstevel@tonic-gate 		if (i < 0)
71207c478bd9Sstevel@tonic-gate 			syserr("filesys_setup failed twice, result=%d", i);
71217c478bd9Sstevel@tonic-gate 		else if (LogLevel > 8)
71227c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
71237c478bd9Sstevel@tonic-gate 				  "shared memory does not contain expected data, ignored");
71247c478bd9Sstevel@tonic-gate 	}
71257c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
71267c478bd9Sstevel@tonic-gate 	i = filesys_setup(true);
71277c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
71287c478bd9Sstevel@tonic-gate 	if (i < 0)
71297c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
71307c478bd9Sstevel@tonic-gate }
71317c478bd9Sstevel@tonic-gate 
71327c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
71337c478bd9Sstevel@tonic-gate /*
71347c478bd9Sstevel@tonic-gate **  CLEANUP_SHM -- do some cleanup work for shared memory etc
71357c478bd9Sstevel@tonic-gate **
71367c478bd9Sstevel@tonic-gate **	Parameters:
71377c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory?
71387c478bd9Sstevel@tonic-gate **
71397c478bd9Sstevel@tonic-gate **	Returns:
71407c478bd9Sstevel@tonic-gate **		none.
71417c478bd9Sstevel@tonic-gate **
71427c478bd9Sstevel@tonic-gate **	Side Effects:
71437c478bd9Sstevel@tonic-gate **		detaches shared memory.
71447c478bd9Sstevel@tonic-gate */
71457c478bd9Sstevel@tonic-gate 
71467c478bd9Sstevel@tonic-gate void
71477c478bd9Sstevel@tonic-gate cleanup_shm(owner)
71487c478bd9Sstevel@tonic-gate 	bool owner;
71497c478bd9Sstevel@tonic-gate {
71507c478bd9Sstevel@tonic-gate 	if (ShmId != SM_SHM_NO_ID)
71517c478bd9Sstevel@tonic-gate 	{
71527c478bd9Sstevel@tonic-gate 		if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
71537c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s",
71547c478bd9Sstevel@tonic-gate 				  sm_errstring(errno));
71557c478bd9Sstevel@tonic-gate 		Pshm = NULL;
71567c478bd9Sstevel@tonic-gate 		ShmId = SM_SHM_NO_ID;
71577c478bd9Sstevel@tonic-gate 	}
71587c478bd9Sstevel@tonic-gate 	stop_sem(owner);
71597c478bd9Sstevel@tonic-gate }
71607c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
71617c478bd9Sstevel@tonic-gate 
71627c478bd9Sstevel@tonic-gate /*
71637c478bd9Sstevel@tonic-gate **  CLEANUP_QUEUES -- do some cleanup work for queues
71647c478bd9Sstevel@tonic-gate **
71657c478bd9Sstevel@tonic-gate **	Parameters:
71667c478bd9Sstevel@tonic-gate **		none.
71677c478bd9Sstevel@tonic-gate **
71687c478bd9Sstevel@tonic-gate **	Returns:
71697c478bd9Sstevel@tonic-gate **		none.
71707c478bd9Sstevel@tonic-gate **
71717c478bd9Sstevel@tonic-gate */
71727c478bd9Sstevel@tonic-gate 
71737c478bd9Sstevel@tonic-gate void
71747c478bd9Sstevel@tonic-gate cleanup_queues()
71757c478bd9Sstevel@tonic-gate {
71767c478bd9Sstevel@tonic-gate 	sync_queue_time();
71777c478bd9Sstevel@tonic-gate }
71787c478bd9Sstevel@tonic-gate /*
71797c478bd9Sstevel@tonic-gate **  SET_DEF_QUEUEVAL -- set default values for a queue group.
71807c478bd9Sstevel@tonic-gate **
71817c478bd9Sstevel@tonic-gate **	Parameters:
71827c478bd9Sstevel@tonic-gate **		qg -- queue group
71837c478bd9Sstevel@tonic-gate **		all -- set all values (true for default group)?
71847c478bd9Sstevel@tonic-gate **
71857c478bd9Sstevel@tonic-gate **	Returns:
71867c478bd9Sstevel@tonic-gate **		none.
71877c478bd9Sstevel@tonic-gate **
71887c478bd9Sstevel@tonic-gate **	Side Effects:
71897c478bd9Sstevel@tonic-gate **		sets default values for the queue group.
71907c478bd9Sstevel@tonic-gate */
71917c478bd9Sstevel@tonic-gate 
71927c478bd9Sstevel@tonic-gate void
71937c478bd9Sstevel@tonic-gate set_def_queueval(qg, all)
71947c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
71957c478bd9Sstevel@tonic-gate 	bool all;
71967c478bd9Sstevel@tonic-gate {
71977c478bd9Sstevel@tonic-gate 	if (bitnset(QD_DEFINED, qg->qg_flags))
71987c478bd9Sstevel@tonic-gate 		return;
71997c478bd9Sstevel@tonic-gate 	if (all)
72007c478bd9Sstevel@tonic-gate 		qg->qg_qdir = QueueDir;
72017c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_GROUP_SORTORDER
72027c478bd9Sstevel@tonic-gate 	qg->qg_sortorder = QueueSortOrder;
72037c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_GROUP_SORTORDER */
72047c478bd9Sstevel@tonic-gate 	qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
72057c478bd9Sstevel@tonic-gate 	qg->qg_nice = NiceQueueRun;
72067c478bd9Sstevel@tonic-gate }
72077c478bd9Sstevel@tonic-gate /*
72087c478bd9Sstevel@tonic-gate **  MAKEQUEUE -- define a new queue.
72097c478bd9Sstevel@tonic-gate **
72107c478bd9Sstevel@tonic-gate **	Parameters:
72117c478bd9Sstevel@tonic-gate **		line -- description of queue.  This is in labeled fields.
72127c478bd9Sstevel@tonic-gate **			The fields are:
72137c478bd9Sstevel@tonic-gate **			   F -- the flags associated with the queue
72147c478bd9Sstevel@tonic-gate **			   I -- the interval between running the queue
72157c478bd9Sstevel@tonic-gate **			   J -- the maximum # of jobs in work list
72167c478bd9Sstevel@tonic-gate **			   [M -- the maximum # of jobs in a queue run]
72177c478bd9Sstevel@tonic-gate **			   N -- the niceness at which to run
72187c478bd9Sstevel@tonic-gate **			   P -- the path to the queue
72197c478bd9Sstevel@tonic-gate **			   S -- the queue sorting order
72207c478bd9Sstevel@tonic-gate **			   R -- number of parallel queue runners
72217c478bd9Sstevel@tonic-gate **			   r -- max recipients per envelope
72227c478bd9Sstevel@tonic-gate **			The first word is the canonical name of the queue.
72237c478bd9Sstevel@tonic-gate **		qdef -- this is a 'Q' definition from .cf
72247c478bd9Sstevel@tonic-gate **
72257c478bd9Sstevel@tonic-gate **	Returns:
72267c478bd9Sstevel@tonic-gate **		none.
72277c478bd9Sstevel@tonic-gate **
72287c478bd9Sstevel@tonic-gate **	Side Effects:
72297c478bd9Sstevel@tonic-gate **		enters the queue into the queue table.
72307c478bd9Sstevel@tonic-gate */
72317c478bd9Sstevel@tonic-gate 
72327c478bd9Sstevel@tonic-gate void
72337c478bd9Sstevel@tonic-gate makequeue(line, qdef)
72347c478bd9Sstevel@tonic-gate 	char *line;
72357c478bd9Sstevel@tonic-gate 	bool qdef;
72367c478bd9Sstevel@tonic-gate {
72377c478bd9Sstevel@tonic-gate 	register char *p;
72387c478bd9Sstevel@tonic-gate 	register QUEUEGRP *qg;
72397c478bd9Sstevel@tonic-gate 	register STAB *s;
72407c478bd9Sstevel@tonic-gate 	int i;
72417c478bd9Sstevel@tonic-gate 	char fcode;
72427c478bd9Sstevel@tonic-gate 
72437c478bd9Sstevel@tonic-gate 	/* allocate a queue and set up defaults */
7244*058561cbSjbeck 	qg = (QUEUEGRP *) xalloc(sizeof(*qg));
7245*058561cbSjbeck 	memset((char *) qg, '\0', sizeof(*qg));
72467c478bd9Sstevel@tonic-gate 
72477c478bd9Sstevel@tonic-gate 	if (line[0] == '\0')
72487c478bd9Sstevel@tonic-gate 	{
72497c478bd9Sstevel@tonic-gate 		syserr("name required for queue");
72507c478bd9Sstevel@tonic-gate 		return;
72517c478bd9Sstevel@tonic-gate 	}
72527c478bd9Sstevel@tonic-gate 
72537c478bd9Sstevel@tonic-gate 	/* collect the queue name */
72547c478bd9Sstevel@tonic-gate 	for (p = line;
72557c478bd9Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
72567c478bd9Sstevel@tonic-gate 	     p++)
72577c478bd9Sstevel@tonic-gate 		continue;
72587c478bd9Sstevel@tonic-gate 	if (*p != '\0')
72597c478bd9Sstevel@tonic-gate 		*p++ = '\0';
72607c478bd9Sstevel@tonic-gate 	qg->qg_name = newstr(line);
72617c478bd9Sstevel@tonic-gate 
72627c478bd9Sstevel@tonic-gate 	/* set default values, can be overridden below */
72637c478bd9Sstevel@tonic-gate 	set_def_queueval(qg, false);
72647c478bd9Sstevel@tonic-gate 
72657c478bd9Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
72667c478bd9Sstevel@tonic-gate 	while (*p != '\0')
72677c478bd9Sstevel@tonic-gate 	{
72687c478bd9Sstevel@tonic-gate 		auto char *delimptr;
72697c478bd9Sstevel@tonic-gate 
72707c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
72717c478bd9Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
72727c478bd9Sstevel@tonic-gate 			p++;
72737c478bd9Sstevel@tonic-gate 
72747c478bd9Sstevel@tonic-gate 		/* p now points to field code */
72757c478bd9Sstevel@tonic-gate 		fcode = *p;
72767c478bd9Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
72777c478bd9Sstevel@tonic-gate 			p++;
72787c478bd9Sstevel@tonic-gate 		if (*p++ != '=')
72797c478bd9Sstevel@tonic-gate 		{
72807c478bd9Sstevel@tonic-gate 			syserr("queue %s: `=' expected", qg->qg_name);
72817c478bd9Sstevel@tonic-gate 			return;
72827c478bd9Sstevel@tonic-gate 		}
72837c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
72847c478bd9Sstevel@tonic-gate 			p++;
72857c478bd9Sstevel@tonic-gate 
72867c478bd9Sstevel@tonic-gate 		/* p now points to the field body */
72877c478bd9Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
72887c478bd9Sstevel@tonic-gate 
72897c478bd9Sstevel@tonic-gate 		/* install the field into the queue struct */
72907c478bd9Sstevel@tonic-gate 		switch (fcode)
72917c478bd9Sstevel@tonic-gate 		{
72927c478bd9Sstevel@tonic-gate 		  case 'P':		/* pathname */
72937c478bd9Sstevel@tonic-gate 			if (*p == '\0')
72947c478bd9Sstevel@tonic-gate 				syserr("queue %s: empty path name",
72957c478bd9Sstevel@tonic-gate 					qg->qg_name);
72967c478bd9Sstevel@tonic-gate 			else
72977c478bd9Sstevel@tonic-gate 				qg->qg_qdir = newstr(p);
72987c478bd9Sstevel@tonic-gate 			break;
72997c478bd9Sstevel@tonic-gate 
73007c478bd9Sstevel@tonic-gate 		  case 'F':		/* flags */
73017c478bd9Sstevel@tonic-gate 			for (; *p != '\0'; p++)
73027c478bd9Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
73037c478bd9Sstevel@tonic-gate 					setbitn(*p, qg->qg_flags);
73047c478bd9Sstevel@tonic-gate 			break;
73057c478bd9Sstevel@tonic-gate 
73067c478bd9Sstevel@tonic-gate 			/*
73077c478bd9Sstevel@tonic-gate 			**  Do we need two intervals here:
73087c478bd9Sstevel@tonic-gate 			**  One for persistent queue runners,
73097c478bd9Sstevel@tonic-gate 			**  one for "normal" queue runs?
73107c478bd9Sstevel@tonic-gate 			*/
73117c478bd9Sstevel@tonic-gate 
73127c478bd9Sstevel@tonic-gate 		  case 'I':	/* interval between running the queue */
73137c478bd9Sstevel@tonic-gate 			qg->qg_queueintvl = convtime(p, 'm');
73147c478bd9Sstevel@tonic-gate 			break;
73157c478bd9Sstevel@tonic-gate 
73167c478bd9Sstevel@tonic-gate 		  case 'N':		/* run niceness */
73177c478bd9Sstevel@tonic-gate 			qg->qg_nice = atoi(p);
73187c478bd9Sstevel@tonic-gate 			break;
73197c478bd9Sstevel@tonic-gate 
73207c478bd9Sstevel@tonic-gate 		  case 'R':		/* maximum # of runners for the group */
73217c478bd9Sstevel@tonic-gate 			i = atoi(p);
73227c478bd9Sstevel@tonic-gate 
73237c478bd9Sstevel@tonic-gate 			/* can't have more runners than allowed total */
73247c478bd9Sstevel@tonic-gate 			if (MaxQueueChildren > 0 && i > MaxQueueChildren)
73257c478bd9Sstevel@tonic-gate 			{
73267c478bd9Sstevel@tonic-gate 				qg->qg_maxqrun = MaxQueueChildren;
73277c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
73287c478bd9Sstevel@tonic-gate 						     "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
73297c478bd9Sstevel@tonic-gate 						     qg->qg_name, i,
73307c478bd9Sstevel@tonic-gate 						     MaxQueueChildren);
73317c478bd9Sstevel@tonic-gate 			}
73327c478bd9Sstevel@tonic-gate 			else
73337c478bd9Sstevel@tonic-gate 				qg->qg_maxqrun = i;
73347c478bd9Sstevel@tonic-gate 			break;
73357c478bd9Sstevel@tonic-gate 
73367c478bd9Sstevel@tonic-gate 		  case 'J':		/* maximum # of jobs in work list */
73377c478bd9Sstevel@tonic-gate 			qg->qg_maxlist = atoi(p);
73387c478bd9Sstevel@tonic-gate 			break;
73397c478bd9Sstevel@tonic-gate 
73407c478bd9Sstevel@tonic-gate 		  case 'r':		/* max recipients per envelope */
73417c478bd9Sstevel@tonic-gate 			qg->qg_maxrcpt = atoi(p);
73427c478bd9Sstevel@tonic-gate 			break;
73437c478bd9Sstevel@tonic-gate 
73447c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_GROUP_SORTORDER
73457c478bd9Sstevel@tonic-gate 		  case 'S':		/* queue sorting order */
73467c478bd9Sstevel@tonic-gate 			switch (*p)
73477c478bd9Sstevel@tonic-gate 			{
73487c478bd9Sstevel@tonic-gate 			  case 'h':	/* Host first */
73497c478bd9Sstevel@tonic-gate 			  case 'H':
73507c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYHOST;
73517c478bd9Sstevel@tonic-gate 				break;
73527c478bd9Sstevel@tonic-gate 
73537c478bd9Sstevel@tonic-gate 			  case 'p':	/* Priority order */
73547c478bd9Sstevel@tonic-gate 			  case 'P':
73557c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYPRIORITY;
73567c478bd9Sstevel@tonic-gate 				break;
73577c478bd9Sstevel@tonic-gate 
73587c478bd9Sstevel@tonic-gate 			  case 't':	/* Submission time */
73597c478bd9Sstevel@tonic-gate 			  case 'T':
73607c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYTIME;
73617c478bd9Sstevel@tonic-gate 				break;
73627c478bd9Sstevel@tonic-gate 
73637c478bd9Sstevel@tonic-gate 			  case 'f':	/* File name */
73647c478bd9Sstevel@tonic-gate 			  case 'F':
73657c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYFILENAME;
73667c478bd9Sstevel@tonic-gate 				break;
73677c478bd9Sstevel@tonic-gate 
73687c478bd9Sstevel@tonic-gate 			  case 'm':	/* Modification time */
73697c478bd9Sstevel@tonic-gate 			  case 'M':
73707c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYMODTIME;
73717c478bd9Sstevel@tonic-gate 				break;
73727c478bd9Sstevel@tonic-gate 
73737c478bd9Sstevel@tonic-gate 			  case 'r':	/* Random */
73747c478bd9Sstevel@tonic-gate 			  case 'R':
73757c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_RANDOM;
73767c478bd9Sstevel@tonic-gate 				break;
73777c478bd9Sstevel@tonic-gate 
73787c478bd9Sstevel@tonic-gate # if _FFR_RHS
73797c478bd9Sstevel@tonic-gate 			  case 's':	/* Shuffled host name */
73807c478bd9Sstevel@tonic-gate 			  case 'S':
73817c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYSHUFFLE;
73827c478bd9Sstevel@tonic-gate 				break;
73837c478bd9Sstevel@tonic-gate # endif /* _FFR_RHS */
73847c478bd9Sstevel@tonic-gate 
73857c478bd9Sstevel@tonic-gate 			  case 'n':	/* none */
73867c478bd9Sstevel@tonic-gate 			  case 'N':
73877c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_NONE;
73887c478bd9Sstevel@tonic-gate 				break;
73897c478bd9Sstevel@tonic-gate 
73907c478bd9Sstevel@tonic-gate 			  default:
73917c478bd9Sstevel@tonic-gate 				syserr("Invalid queue sort order \"%s\"", p);
73927c478bd9Sstevel@tonic-gate 			}
73937c478bd9Sstevel@tonic-gate 			break;
73947c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_GROUP_SORTORDER */
73957c478bd9Sstevel@tonic-gate 
73967c478bd9Sstevel@tonic-gate 		  default:
73977c478bd9Sstevel@tonic-gate 			syserr("Q%s: unknown queue equate %c=",
73987c478bd9Sstevel@tonic-gate 			       qg->qg_name, fcode);
73997c478bd9Sstevel@tonic-gate 			break;
74007c478bd9Sstevel@tonic-gate 		}
74017c478bd9Sstevel@tonic-gate 
74027c478bd9Sstevel@tonic-gate 		p = delimptr;
74037c478bd9Sstevel@tonic-gate 	}
74047c478bd9Sstevel@tonic-gate 
74057c478bd9Sstevel@tonic-gate #if !HASNICE
74067c478bd9Sstevel@tonic-gate 	if (qg->qg_nice != NiceQueueRun)
74077c478bd9Sstevel@tonic-gate 	{
74087c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
74097c478bd9Sstevel@tonic-gate 				     "Q%s: Warning: N= set on system that doesn't support nice()\n",
74107c478bd9Sstevel@tonic-gate 				     qg->qg_name);
74117c478bd9Sstevel@tonic-gate 	}
74127c478bd9Sstevel@tonic-gate #endif /* !HASNICE */
74137c478bd9Sstevel@tonic-gate 
74147c478bd9Sstevel@tonic-gate 	/* do some rationality checking */
74157c478bd9Sstevel@tonic-gate 	if (NumQueue >= MAXQUEUEGROUPS)
74167c478bd9Sstevel@tonic-gate 	{
74177c478bd9Sstevel@tonic-gate 		syserr("too many queue groups defined (%d max)",
74187c478bd9Sstevel@tonic-gate 			MAXQUEUEGROUPS);
74197c478bd9Sstevel@tonic-gate 		return;
74207c478bd9Sstevel@tonic-gate 	}
74217c478bd9Sstevel@tonic-gate 
74227c478bd9Sstevel@tonic-gate 	if (qg->qg_qdir == NULL)
74237c478bd9Sstevel@tonic-gate 	{
74247c478bd9Sstevel@tonic-gate 		if (QueueDir == NULL || *QueueDir == '\0')
74257c478bd9Sstevel@tonic-gate 		{
74267c478bd9Sstevel@tonic-gate 			syserr("QueueDir must be defined before queue groups");
74277c478bd9Sstevel@tonic-gate 			return;
74287c478bd9Sstevel@tonic-gate 		}
74297c478bd9Sstevel@tonic-gate 		qg->qg_qdir = newstr(QueueDir);
74307c478bd9Sstevel@tonic-gate 	}
74317c478bd9Sstevel@tonic-gate 
74327c478bd9Sstevel@tonic-gate 	if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
74337c478bd9Sstevel@tonic-gate 	{
74347c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
74357c478bd9Sstevel@tonic-gate 				     "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
74367c478bd9Sstevel@tonic-gate 				     qg->qg_name, qg->qg_maxqrun, QD_FORK);
74377c478bd9Sstevel@tonic-gate 	}
74387c478bd9Sstevel@tonic-gate 
74397c478bd9Sstevel@tonic-gate 	/* enter the queue into the symbol table */
74407c478bd9Sstevel@tonic-gate 	if (tTd(37, 8))
74417c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
74427c478bd9Sstevel@tonic-gate 			  "Adding %s to stab, path: %s", qg->qg_name,
74437c478bd9Sstevel@tonic-gate 			  qg->qg_qdir);
74447c478bd9Sstevel@tonic-gate 	s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
74457c478bd9Sstevel@tonic-gate 	if (s->s_quegrp != NULL)
74467c478bd9Sstevel@tonic-gate 	{
74477c478bd9Sstevel@tonic-gate 		i = s->s_quegrp->qg_index;
74487c478bd9Sstevel@tonic-gate 
74497c478bd9Sstevel@tonic-gate 		/* XXX what about the pointers inside this struct? */
74507c478bd9Sstevel@tonic-gate 		sm_free(s->s_quegrp); /* XXX */
74517c478bd9Sstevel@tonic-gate 	}
74527c478bd9Sstevel@tonic-gate 	else
74537c478bd9Sstevel@tonic-gate 		i = NumQueue++;
74547c478bd9Sstevel@tonic-gate 	Queue[i] = s->s_quegrp = qg;
74557c478bd9Sstevel@tonic-gate 	qg->qg_index = i;
74567c478bd9Sstevel@tonic-gate 
74577c478bd9Sstevel@tonic-gate 	/* set default value for max queue runners */
74587c478bd9Sstevel@tonic-gate 	if (qg->qg_maxqrun < 0)
74597c478bd9Sstevel@tonic-gate 	{
74607c478bd9Sstevel@tonic-gate 		if (MaxRunnersPerQueue > 0)
74617c478bd9Sstevel@tonic-gate 			qg->qg_maxqrun = MaxRunnersPerQueue;
74627c478bd9Sstevel@tonic-gate 		else
74637c478bd9Sstevel@tonic-gate 			qg->qg_maxqrun = 1;
74647c478bd9Sstevel@tonic-gate 	}
74657c478bd9Sstevel@tonic-gate 	if (qdef)
74667c478bd9Sstevel@tonic-gate 		setbitn(QD_DEFINED, qg->qg_flags);
74677c478bd9Sstevel@tonic-gate }
74687c478bd9Sstevel@tonic-gate #if 0
74697c478bd9Sstevel@tonic-gate /*
74707c478bd9Sstevel@tonic-gate **  HASHFQN -- calculate a hash value for a fully qualified host name
74717c478bd9Sstevel@tonic-gate **
74727c478bd9Sstevel@tonic-gate **	Arguments:
74737c478bd9Sstevel@tonic-gate **		fqn -- an all lower-case host.domain string
74747c478bd9Sstevel@tonic-gate **		buckets -- the number of buckets (queue directories)
74757c478bd9Sstevel@tonic-gate **
74767c478bd9Sstevel@tonic-gate **	Returns:
74777c478bd9Sstevel@tonic-gate **		a bucket number (signed integer)
74787c478bd9Sstevel@tonic-gate **		-1 on error
74797c478bd9Sstevel@tonic-gate **
74807c478bd9Sstevel@tonic-gate **	Contributed by Exactis.com, Inc.
74817c478bd9Sstevel@tonic-gate */
74827c478bd9Sstevel@tonic-gate 
74837c478bd9Sstevel@tonic-gate int
74847c478bd9Sstevel@tonic-gate hashfqn(fqn, buckets)
74857c478bd9Sstevel@tonic-gate 	register char *fqn;
74867c478bd9Sstevel@tonic-gate 	int buckets;
74877c478bd9Sstevel@tonic-gate {
74887c478bd9Sstevel@tonic-gate 	register char *p;
74897c478bd9Sstevel@tonic-gate 	register int h = 0, hash, cnt;
74907c478bd9Sstevel@tonic-gate 
74917c478bd9Sstevel@tonic-gate 	if (fqn == NULL)
74927c478bd9Sstevel@tonic-gate 		return -1;
74937c478bd9Sstevel@tonic-gate 
74947c478bd9Sstevel@tonic-gate 	/*
74957c478bd9Sstevel@tonic-gate 	**  A variation on the gdb hash
74967c478bd9Sstevel@tonic-gate 	**  This is the best as of Feb 19, 1996 --bcx
74977c478bd9Sstevel@tonic-gate 	*/
74987c478bd9Sstevel@tonic-gate 
74997c478bd9Sstevel@tonic-gate 	p = fqn;
75007c478bd9Sstevel@tonic-gate 	h = 0x238F13AF * strlen(p);
75017c478bd9Sstevel@tonic-gate 	for (cnt = 0; *p != 0; ++p, cnt++)
75027c478bd9Sstevel@tonic-gate 	{
75037c478bd9Sstevel@tonic-gate 		h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
75047c478bd9Sstevel@tonic-gate 	}
75057c478bd9Sstevel@tonic-gate 	h = (1103515243 * h + 12345) & 0x7FFFFFFF;
75067c478bd9Sstevel@tonic-gate 	if (buckets < 2)
75077c478bd9Sstevel@tonic-gate 		hash = 0;
75087c478bd9Sstevel@tonic-gate 	else
75097c478bd9Sstevel@tonic-gate 		hash = (h % buckets);
75107c478bd9Sstevel@tonic-gate 
75117c478bd9Sstevel@tonic-gate 	return hash;
75127c478bd9Sstevel@tonic-gate }
75137c478bd9Sstevel@tonic-gate #endif /* 0 */
75147c478bd9Sstevel@tonic-gate 
75157c478bd9Sstevel@tonic-gate /*
75167c478bd9Sstevel@tonic-gate **  A structure for sorting Queue according to maxqrun without
75177c478bd9Sstevel@tonic-gate **	screwing up Queue itself.
75187c478bd9Sstevel@tonic-gate */
75197c478bd9Sstevel@tonic-gate 
75207c478bd9Sstevel@tonic-gate struct sortqgrp
75217c478bd9Sstevel@tonic-gate {
75227c478bd9Sstevel@tonic-gate 	int sg_idx;		/* original index */
75237c478bd9Sstevel@tonic-gate 	int sg_maxqrun;		/* max queue runners */
75247c478bd9Sstevel@tonic-gate };
75257c478bd9Sstevel@tonic-gate typedef struct sortqgrp	SORTQGRP_T;
75267c478bd9Sstevel@tonic-gate static int cmpidx __P((const void *, const void *));
75277c478bd9Sstevel@tonic-gate 
75287c478bd9Sstevel@tonic-gate static int
75297c478bd9Sstevel@tonic-gate cmpidx(a, b)
75307c478bd9Sstevel@tonic-gate 	const void *a;
75317c478bd9Sstevel@tonic-gate 	const void *b;
75327c478bd9Sstevel@tonic-gate {
75337c478bd9Sstevel@tonic-gate 	/* The sort is highest to lowest, so the comparison is reversed */
75347c478bd9Sstevel@tonic-gate 	if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
75357c478bd9Sstevel@tonic-gate 		return 1;
75367c478bd9Sstevel@tonic-gate 	else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
75377c478bd9Sstevel@tonic-gate 		return -1;
75387c478bd9Sstevel@tonic-gate 	else
75397c478bd9Sstevel@tonic-gate 		return 0;
75407c478bd9Sstevel@tonic-gate }
75417c478bd9Sstevel@tonic-gate 
75427c478bd9Sstevel@tonic-gate /*
75437c478bd9Sstevel@tonic-gate **  MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
75447c478bd9Sstevel@tonic-gate **
75457c478bd9Sstevel@tonic-gate **  Take the now defined queue groups and assign them to work groups.
75467c478bd9Sstevel@tonic-gate **  This is done to balance out the number of concurrently active
75477c478bd9Sstevel@tonic-gate **  queue runners such that MaxQueueChildren is not exceeded. This may
75487c478bd9Sstevel@tonic-gate **  result in more than one queue group per work group. In such a case
75497c478bd9Sstevel@tonic-gate **  the number of running queue groups in that work group will have no
75507c478bd9Sstevel@tonic-gate **  more than the work group maximum number of runners (a "fair" portion
75517c478bd9Sstevel@tonic-gate **  of MaxQueueRunners). All queue groups within a work group will get a
75527c478bd9Sstevel@tonic-gate **  chance at running.
75537c478bd9Sstevel@tonic-gate **
75547c478bd9Sstevel@tonic-gate **	Parameters:
75557c478bd9Sstevel@tonic-gate **		none.
75567c478bd9Sstevel@tonic-gate **
75577c478bd9Sstevel@tonic-gate **	Returns:
75587c478bd9Sstevel@tonic-gate **		nothing.
75597c478bd9Sstevel@tonic-gate **
75607c478bd9Sstevel@tonic-gate **	Side Effects:
75617c478bd9Sstevel@tonic-gate **		Sets up WorkGrp structure.
75627c478bd9Sstevel@tonic-gate */
75637c478bd9Sstevel@tonic-gate 
75647c478bd9Sstevel@tonic-gate void
75657c478bd9Sstevel@tonic-gate makeworkgroups()
75667c478bd9Sstevel@tonic-gate {
75677c478bd9Sstevel@tonic-gate 	int i, j, total_runners, dir, h;
75687c478bd9Sstevel@tonic-gate 	SORTQGRP_T si[MAXQUEUEGROUPS + 1];
75697c478bd9Sstevel@tonic-gate 
75707c478bd9Sstevel@tonic-gate 	total_runners = 0;
75717c478bd9Sstevel@tonic-gate 	if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
75727c478bd9Sstevel@tonic-gate 	{
75737c478bd9Sstevel@tonic-gate 		/*
75747c478bd9Sstevel@tonic-gate 		**  There is only the "mqueue" queue group (a default)
75757c478bd9Sstevel@tonic-gate 		**  containing all of the queues. We want to provide to
75767c478bd9Sstevel@tonic-gate 		**  this queue group the maximum allowable queue runners.
75777c478bd9Sstevel@tonic-gate 		**  To match older behavior (8.10/8.11) we'll try for
75787c478bd9Sstevel@tonic-gate 		**  1 runner per queue capping it at MaxQueueChildren.
75797c478bd9Sstevel@tonic-gate 		**  So if there are N queues, then there will be N runners
75807c478bd9Sstevel@tonic-gate 		**  for the "mqueue" queue group (where N is kept less than
75817c478bd9Sstevel@tonic-gate 		**  MaxQueueChildren).
75827c478bd9Sstevel@tonic-gate 		*/
75837c478bd9Sstevel@tonic-gate 
75847c478bd9Sstevel@tonic-gate 		NumWorkGroups = 1;
75857c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_numqgrp = 1;
75867c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
75877c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_qgs[0] = Queue[0];
75887c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
75897c478bd9Sstevel@tonic-gate 		    Queue[0]->qg_numqueues > MaxQueueChildren)
75907c478bd9Sstevel@tonic-gate 			WorkGrp[0].wg_runners = MaxQueueChildren;
75917c478bd9Sstevel@tonic-gate 		else
75927c478bd9Sstevel@tonic-gate 			WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
75937c478bd9Sstevel@tonic-gate 
75947c478bd9Sstevel@tonic-gate 		Queue[0]->qg_wgrp = 0;
75957c478bd9Sstevel@tonic-gate 
75967c478bd9Sstevel@tonic-gate 		/* can't have more runners than allowed total */
75977c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
75987c478bd9Sstevel@tonic-gate 		    Queue[0]->qg_maxqrun > MaxQueueChildren)
75997c478bd9Sstevel@tonic-gate 			Queue[0]->qg_maxqrun = MaxQueueChildren;
76007c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
76017c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
76027c478bd9Sstevel@tonic-gate 		return;
76037c478bd9Sstevel@tonic-gate 	}
76047c478bd9Sstevel@tonic-gate 
76057c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76067c478bd9Sstevel@tonic-gate 	{
76077c478bd9Sstevel@tonic-gate 		si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
76087c478bd9Sstevel@tonic-gate 		si[i].sg_idx = i;
76097c478bd9Sstevel@tonic-gate 	}
76107c478bd9Sstevel@tonic-gate 	qsort(si, NumQueue, sizeof(si[0]), cmpidx);
76117c478bd9Sstevel@tonic-gate 
76127c478bd9Sstevel@tonic-gate 	NumWorkGroups = 0;
76137c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76147c478bd9Sstevel@tonic-gate 	{
76157c478bd9Sstevel@tonic-gate 		total_runners += si[i].sg_maxqrun;
76167c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
76177c478bd9Sstevel@tonic-gate 			NumWorkGroups++;
76187c478bd9Sstevel@tonic-gate 		else
76197c478bd9Sstevel@tonic-gate 			break;
76207c478bd9Sstevel@tonic-gate 	}
76217c478bd9Sstevel@tonic-gate 
76227c478bd9Sstevel@tonic-gate 	if (NumWorkGroups < 1)
76237c478bd9Sstevel@tonic-gate 		NumWorkGroups = 1; /* gotta have one at least */
76247c478bd9Sstevel@tonic-gate 	else if (NumWorkGroups > MAXWORKGROUPS)
76257c478bd9Sstevel@tonic-gate 		NumWorkGroups = MAXWORKGROUPS; /* the limit */
76267c478bd9Sstevel@tonic-gate 
76277c478bd9Sstevel@tonic-gate 	/*
76287c478bd9Sstevel@tonic-gate 	**  We now know the number of work groups to pack the queue groups
76297c478bd9Sstevel@tonic-gate 	**  into. The queue groups in 'Queue' are sorted from highest
76307c478bd9Sstevel@tonic-gate 	**  to lowest for the number of runners per queue group.
76317c478bd9Sstevel@tonic-gate 	**  We put the queue groups with the largest number of runners
76327c478bd9Sstevel@tonic-gate 	**  into work groups first. Then the smaller ones are fitted in
76337c478bd9Sstevel@tonic-gate 	**  where it looks best.
76347c478bd9Sstevel@tonic-gate 	*/
76357c478bd9Sstevel@tonic-gate 
76367c478bd9Sstevel@tonic-gate 	j = 0;
76377c478bd9Sstevel@tonic-gate 	dir = 1;
76387c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76397c478bd9Sstevel@tonic-gate 	{
76407c478bd9Sstevel@tonic-gate 		/* a to-and-fro packing scheme, continue from last position */
76417c478bd9Sstevel@tonic-gate 		if (j >= NumWorkGroups)
76427c478bd9Sstevel@tonic-gate 		{
76437c478bd9Sstevel@tonic-gate 			dir = -1;
76447c478bd9Sstevel@tonic-gate 			j = NumWorkGroups - 1;
76457c478bd9Sstevel@tonic-gate 		}
76467c478bd9Sstevel@tonic-gate 		else if (j < 0)
76477c478bd9Sstevel@tonic-gate 		{
76487c478bd9Sstevel@tonic-gate 			j = 0;
76497c478bd9Sstevel@tonic-gate 			dir = 1;
76507c478bd9Sstevel@tonic-gate 		}
76517c478bd9Sstevel@tonic-gate 
76527c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_qgs == NULL)
76537c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) *
76547c478bd9Sstevel@tonic-gate 							(WorkGrp[j].wg_numqgrp + 1));
76557c478bd9Sstevel@tonic-gate 		else
76567c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
76577c478bd9Sstevel@tonic-gate 							sizeof(QUEUEGRP *) *
76587c478bd9Sstevel@tonic-gate 							(WorkGrp[j].wg_numqgrp + 1));
76597c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_qgs == NULL)
76607c478bd9Sstevel@tonic-gate 		{
76617c478bd9Sstevel@tonic-gate 			syserr("!cannot allocate memory for work queues, need %d bytes",
76627c478bd9Sstevel@tonic-gate 			       (int) (sizeof(QUEUEGRP *) *
76637c478bd9Sstevel@tonic-gate 				      (WorkGrp[j].wg_numqgrp + 1)));
76647c478bd9Sstevel@tonic-gate 		}
76657c478bd9Sstevel@tonic-gate 
76667c478bd9Sstevel@tonic-gate 		h = si[i].sg_idx;
76677c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[h];
76687c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_numqgrp++;
76697c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_runners += Queue[h]->qg_maxqrun;
76707c478bd9Sstevel@tonic-gate 		Queue[h]->qg_wgrp = j;
76717c478bd9Sstevel@tonic-gate 
76727c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_maxact == 0)
76737c478bd9Sstevel@tonic-gate 		{
76747c478bd9Sstevel@tonic-gate 			/* can't have more runners than allowed total */
76757c478bd9Sstevel@tonic-gate 			if (MaxQueueChildren > 0 &&
76767c478bd9Sstevel@tonic-gate 			    Queue[h]->qg_maxqrun > MaxQueueChildren)
76777c478bd9Sstevel@tonic-gate 				Queue[h]->qg_maxqrun = MaxQueueChildren;
76787c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_maxact = Queue[h]->qg_maxqrun;
76797c478bd9Sstevel@tonic-gate 		}
76807c478bd9Sstevel@tonic-gate 
76817c478bd9Sstevel@tonic-gate 		/*
76827c478bd9Sstevel@tonic-gate 		**  XXX: must wg_lowqintvl be the GCD?
76837c478bd9Sstevel@tonic-gate 		**  qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
76847c478bd9Sstevel@tonic-gate 		**  qg2 occur?
76857c478bd9Sstevel@tonic-gate 		*/
76867c478bd9Sstevel@tonic-gate 
76877c478bd9Sstevel@tonic-gate 		/* keep track of the lowest interval for a persistent runner */
76887c478bd9Sstevel@tonic-gate 		if (Queue[h]->qg_queueintvl > 0 &&
76897c478bd9Sstevel@tonic-gate 		    WorkGrp[j].wg_lowqintvl < Queue[h]->qg_queueintvl)
76907c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_lowqintvl = Queue[h]->qg_queueintvl;
76917c478bd9Sstevel@tonic-gate 		j += dir;
76927c478bd9Sstevel@tonic-gate 	}
76937c478bd9Sstevel@tonic-gate 	if (tTd(41, 9))
76947c478bd9Sstevel@tonic-gate 	{
76957c478bd9Sstevel@tonic-gate 		for (i = 0; i < NumWorkGroups; i++)
76967c478bd9Sstevel@tonic-gate 		{
76977c478bd9Sstevel@tonic-gate 			sm_dprintf("Workgroup[%d]=", i);
76987c478bd9Sstevel@tonic-gate 			for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
76997c478bd9Sstevel@tonic-gate 			{
77007c478bd9Sstevel@tonic-gate 				sm_dprintf("%s, ",
77017c478bd9Sstevel@tonic-gate 					WorkGrp[i].wg_qgs[j]->qg_name);
77027c478bd9Sstevel@tonic-gate 			}
77037c478bd9Sstevel@tonic-gate 			sm_dprintf("\n");
77047c478bd9Sstevel@tonic-gate 		}
77057c478bd9Sstevel@tonic-gate 	}
77067c478bd9Sstevel@tonic-gate }
77077c478bd9Sstevel@tonic-gate 
77087c478bd9Sstevel@tonic-gate /*
77097c478bd9Sstevel@tonic-gate **  DUP_DF -- duplicate envelope data file
77107c478bd9Sstevel@tonic-gate **
77117c478bd9Sstevel@tonic-gate **	Copy the data file from the 'old' envelope to the 'new' envelope
77127c478bd9Sstevel@tonic-gate **	in the most efficient way possible.
77137c478bd9Sstevel@tonic-gate **
77147c478bd9Sstevel@tonic-gate **	Create a hard link from the 'old' data file to the 'new' data file.
77157c478bd9Sstevel@tonic-gate **	If the old and new queue directories are on different file systems,
77167c478bd9Sstevel@tonic-gate **	then the new data file link is created in the old queue directory,
77177c478bd9Sstevel@tonic-gate **	and the new queue file will contain a 'd' record pointing to the
77187c478bd9Sstevel@tonic-gate **	directory containing the new data file.
77197c478bd9Sstevel@tonic-gate **
77207c478bd9Sstevel@tonic-gate **	Parameters:
77217c478bd9Sstevel@tonic-gate **		old -- old envelope.
77227c478bd9Sstevel@tonic-gate **		new -- new envelope.
77237c478bd9Sstevel@tonic-gate **
77247c478bd9Sstevel@tonic-gate **	Results:
77257c478bd9Sstevel@tonic-gate **		Returns true on success, false on failure.
77267c478bd9Sstevel@tonic-gate **
77277c478bd9Sstevel@tonic-gate **	Side Effects:
77287c478bd9Sstevel@tonic-gate **		On success, the new data file is created.
77297c478bd9Sstevel@tonic-gate **		On fatal failure, EF_FATALERRS is set in old->e_flags.
77307c478bd9Sstevel@tonic-gate */
77317c478bd9Sstevel@tonic-gate 
77327c478bd9Sstevel@tonic-gate static bool	dup_df __P((ENVELOPE *, ENVELOPE *));
77337c478bd9Sstevel@tonic-gate 
77347c478bd9Sstevel@tonic-gate static bool
77357c478bd9Sstevel@tonic-gate dup_df(old, new)
77367c478bd9Sstevel@tonic-gate 	ENVELOPE *old;
77377c478bd9Sstevel@tonic-gate 	ENVELOPE *new;
77387c478bd9Sstevel@tonic-gate {
77397c478bd9Sstevel@tonic-gate 	int ofs, nfs, r;
77407c478bd9Sstevel@tonic-gate 	char opath[MAXPATHLEN];
77417c478bd9Sstevel@tonic-gate 	char npath[MAXPATHLEN];
77427c478bd9Sstevel@tonic-gate 
77437c478bd9Sstevel@tonic-gate 	if (!bitset(EF_HAS_DF, old->e_flags))
77447c478bd9Sstevel@tonic-gate 	{
77457c478bd9Sstevel@tonic-gate 		/*
77467c478bd9Sstevel@tonic-gate 		**  this can happen if: SuperSafe != True
77477c478bd9Sstevel@tonic-gate 		**  and a bounce mail is sent that is split.
77487c478bd9Sstevel@tonic-gate 		*/
77497c478bd9Sstevel@tonic-gate 
77507c478bd9Sstevel@tonic-gate 		queueup(old, false, true);
77517c478bd9Sstevel@tonic-gate 	}
77527c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
77537c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
77547c478bd9Sstevel@tonic-gate 
7755*058561cbSjbeck 	(void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof(opath));
7756*058561cbSjbeck 	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
77577c478bd9Sstevel@tonic-gate 
77587c478bd9Sstevel@tonic-gate 	if (old->e_dfp != NULL)
77597c478bd9Sstevel@tonic-gate 	{
77607c478bd9Sstevel@tonic-gate 		r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
77617c478bd9Sstevel@tonic-gate 		if (r < 0 && errno != EINVAL)
77627c478bd9Sstevel@tonic-gate 		{
77637c478bd9Sstevel@tonic-gate 			syserr("@can't commit %s", opath);
77647c478bd9Sstevel@tonic-gate 			old->e_flags |= EF_FATALERRS;
77657c478bd9Sstevel@tonic-gate 			return false;
77667c478bd9Sstevel@tonic-gate 		}
77677c478bd9Sstevel@tonic-gate 	}
77687c478bd9Sstevel@tonic-gate 
77697c478bd9Sstevel@tonic-gate 	/*
77707c478bd9Sstevel@tonic-gate 	**  Attempt to create a hard link, if we think both old and new
77717c478bd9Sstevel@tonic-gate 	**  are on the same file system, otherwise copy the file.
77727c478bd9Sstevel@tonic-gate 	**
77737c478bd9Sstevel@tonic-gate 	**  Don't waste time attempting a hard link unless old and new
77747c478bd9Sstevel@tonic-gate 	**  are on the same file system.
77757c478bd9Sstevel@tonic-gate 	*/
77767c478bd9Sstevel@tonic-gate 
777749218d4fSjbeck 	SM_REQUIRE(ISVALIDQGRP(old->e_dfqgrp) && ISVALIDQDIR(old->e_dfqdir));
777849218d4fSjbeck 	SM_REQUIRE(ISVALIDQGRP(new->e_dfqgrp) && ISVALIDQDIR(new->e_dfqdir));
777949218d4fSjbeck 
778049218d4fSjbeck 	ofs = Queue[old->e_dfqgrp]->qg_qpaths[old->e_dfqdir].qp_fsysidx;
778149218d4fSjbeck 	nfs = Queue[new->e_dfqgrp]->qg_qpaths[new->e_dfqdir].qp_fsysidx;
77827c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
77837c478bd9Sstevel@tonic-gate 	{
77847c478bd9Sstevel@tonic-gate 		if (link(opath, npath) == 0)
77857c478bd9Sstevel@tonic-gate 		{
77867c478bd9Sstevel@tonic-gate 			new->e_flags |= EF_HAS_DF;
77877c478bd9Sstevel@tonic-gate 			SYNC_DIR(npath, true);
77887c478bd9Sstevel@tonic-gate 			return true;
77897c478bd9Sstevel@tonic-gate 		}
77907c478bd9Sstevel@tonic-gate 		goto error;
77917c478bd9Sstevel@tonic-gate 	}
77927c478bd9Sstevel@tonic-gate 
77937c478bd9Sstevel@tonic-gate 	/*
77947c478bd9Sstevel@tonic-gate 	**  Can't link across queue directories, so try to create a hard
77957c478bd9Sstevel@tonic-gate 	**  link in the same queue directory as the old df file.
77967c478bd9Sstevel@tonic-gate 	**  The qf file will refer to the new df file using a 'd' record.
77977c478bd9Sstevel@tonic-gate 	*/
77987c478bd9Sstevel@tonic-gate 
77997c478bd9Sstevel@tonic-gate 	new->e_dfqgrp = old->e_dfqgrp;
78007c478bd9Sstevel@tonic-gate 	new->e_dfqdir = old->e_dfqdir;
7801*058561cbSjbeck 	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
78027c478bd9Sstevel@tonic-gate 	if (link(opath, npath) == 0)
78037c478bd9Sstevel@tonic-gate 	{
78047c478bd9Sstevel@tonic-gate 		new->e_flags |= EF_HAS_DF;
78057c478bd9Sstevel@tonic-gate 		SYNC_DIR(npath, true);
78067c478bd9Sstevel@tonic-gate 		return true;
78077c478bd9Sstevel@tonic-gate 	}
78087c478bd9Sstevel@tonic-gate 
78097c478bd9Sstevel@tonic-gate   error:
78107c478bd9Sstevel@tonic-gate 	if (LogLevel > 0)
78117c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, old->e_id,
78127c478bd9Sstevel@tonic-gate 			  "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
78137c478bd9Sstevel@tonic-gate 			  opath, npath, sm_errstring(errno));
78147c478bd9Sstevel@tonic-gate 	return false;
78157c478bd9Sstevel@tonic-gate }
78167c478bd9Sstevel@tonic-gate 
78177c478bd9Sstevel@tonic-gate /*
78187c478bd9Sstevel@tonic-gate **  SPLIT_ENV -- Allocate a new envelope based on a given envelope.
78197c478bd9Sstevel@tonic-gate **
78207c478bd9Sstevel@tonic-gate **	Parameters:
78217c478bd9Sstevel@tonic-gate **		e -- envelope.
78227c478bd9Sstevel@tonic-gate **		sendqueue -- sendqueue for new envelope.
78237c478bd9Sstevel@tonic-gate **		qgrp -- index of queue group.
78247c478bd9Sstevel@tonic-gate **		qdir -- queue directory.
78257c478bd9Sstevel@tonic-gate **
78267c478bd9Sstevel@tonic-gate **	Results:
78277c478bd9Sstevel@tonic-gate **		new envelope.
78287c478bd9Sstevel@tonic-gate **
78297c478bd9Sstevel@tonic-gate */
78307c478bd9Sstevel@tonic-gate 
78317c478bd9Sstevel@tonic-gate static ENVELOPE	*split_env __P((ENVELOPE *, ADDRESS *, int, int));
78327c478bd9Sstevel@tonic-gate 
78337c478bd9Sstevel@tonic-gate static ENVELOPE *
78347c478bd9Sstevel@tonic-gate split_env(e, sendqueue, qgrp, qdir)
78357c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
78367c478bd9Sstevel@tonic-gate 	ADDRESS *sendqueue;
78377c478bd9Sstevel@tonic-gate 	int qgrp;
78387c478bd9Sstevel@tonic-gate 	int qdir;
78397c478bd9Sstevel@tonic-gate {
78407c478bd9Sstevel@tonic-gate 	ENVELOPE *ee;
78417c478bd9Sstevel@tonic-gate 
7842*058561cbSjbeck 	ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof(*ee));
78437c478bd9Sstevel@tonic-gate 	STRUCTCOPY(*e, *ee);
78447c478bd9Sstevel@tonic-gate 	ee->e_message = NULL;	/* XXX use original message? */
78457c478bd9Sstevel@tonic-gate 	ee->e_id = NULL;
78467c478bd9Sstevel@tonic-gate 	assign_queueid(ee);
78477c478bd9Sstevel@tonic-gate 	ee->e_sendqueue = sendqueue;
78487c478bd9Sstevel@tonic-gate 	ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
78497c478bd9Sstevel@tonic-gate 			 |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
78507c478bd9Sstevel@tonic-gate 	ee->e_flags |= EF_NORECEIPT;	/* XXX really? */
78517c478bd9Sstevel@tonic-gate 	ee->e_from.q_state = QS_SENDER;
78527c478bd9Sstevel@tonic-gate 	ee->e_dfp = NULL;
78537c478bd9Sstevel@tonic-gate 	ee->e_lockfp = NULL;
78547c478bd9Sstevel@tonic-gate 	if (e->e_xfp != NULL)
78557c478bd9Sstevel@tonic-gate 		ee->e_xfp = sm_io_dup(e->e_xfp);
78567c478bd9Sstevel@tonic-gate 
78577c478bd9Sstevel@tonic-gate 	/* failed to dup e->e_xfp, start a new transcript */
78587c478bd9Sstevel@tonic-gate 	if (ee->e_xfp == NULL)
78597c478bd9Sstevel@tonic-gate 		openxscript(ee);
78607c478bd9Sstevel@tonic-gate 
78617c478bd9Sstevel@tonic-gate 	ee->e_qgrp = ee->e_dfqgrp = qgrp;
78627c478bd9Sstevel@tonic-gate 	ee->e_qdir = ee->e_dfqdir = qdir;
78637c478bd9Sstevel@tonic-gate 	ee->e_errormode = EM_MAIL;
78647c478bd9Sstevel@tonic-gate 	ee->e_statmsg = NULL;
78657c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg != NULL)
78667c478bd9Sstevel@tonic-gate 		ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
78677c478bd9Sstevel@tonic-gate 						  e->e_quarmsg);
78687c478bd9Sstevel@tonic-gate 
78697c478bd9Sstevel@tonic-gate 	/*
78707c478bd9Sstevel@tonic-gate 	**  XXX Not sure if this copying is necessary.
78717c478bd9Sstevel@tonic-gate 	**  sendall() does this copying, but I (dm) don't know if that is
78727c478bd9Sstevel@tonic-gate 	**  because of the storage management discipline we were using
78737c478bd9Sstevel@tonic-gate 	**  before rpools were introduced, or if it is because these lists
78747c478bd9Sstevel@tonic-gate 	**  can be modified later.
78757c478bd9Sstevel@tonic-gate 	*/
78767c478bd9Sstevel@tonic-gate 
78777c478bd9Sstevel@tonic-gate 	ee->e_header = copyheader(e->e_header, ee->e_rpool);
78787c478bd9Sstevel@tonic-gate 	ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
78797c478bd9Sstevel@tonic-gate 
78807c478bd9Sstevel@tonic-gate 	return ee;
78817c478bd9Sstevel@tonic-gate }
78827c478bd9Sstevel@tonic-gate 
78837c478bd9Sstevel@tonic-gate /* return values from split functions, check also below! */
78847c478bd9Sstevel@tonic-gate #define SM_SPLIT_FAIL	(0)
78857c478bd9Sstevel@tonic-gate #define SM_SPLIT_NONE	(1)
78867c478bd9Sstevel@tonic-gate #define SM_SPLIT_NEW(n)	(1 + (n))
78877c478bd9Sstevel@tonic-gate 
78887c478bd9Sstevel@tonic-gate /*
78897c478bd9Sstevel@tonic-gate **  SPLIT_ACROSS_QUEUE_GROUPS
78907c478bd9Sstevel@tonic-gate **
78917c478bd9Sstevel@tonic-gate **	This function splits an envelope across multiple queue groups
78927c478bd9Sstevel@tonic-gate **	based on the queue group of each recipient.
78937c478bd9Sstevel@tonic-gate **
78947c478bd9Sstevel@tonic-gate **	Parameters:
78957c478bd9Sstevel@tonic-gate **		e -- envelope.
78967c478bd9Sstevel@tonic-gate **
78977c478bd9Sstevel@tonic-gate **	Results:
78987c478bd9Sstevel@tonic-gate **		SM_SPLIT_FAIL on failure
78997c478bd9Sstevel@tonic-gate **		SM_SPLIT_NONE if no splitting occurred,
79007c478bd9Sstevel@tonic-gate **		or 1 + the number of additional envelopes created.
79017c478bd9Sstevel@tonic-gate **
79027c478bd9Sstevel@tonic-gate **	Side Effects:
79037c478bd9Sstevel@tonic-gate **		On success, e->e_sibling points to a list of zero or more
79047c478bd9Sstevel@tonic-gate **		additional envelopes, and the associated data files exist
79057c478bd9Sstevel@tonic-gate **		on disk.  But the queue files are not created.
79067c478bd9Sstevel@tonic-gate **
79077c478bd9Sstevel@tonic-gate **		On failure, e->e_sibling is not changed.
79087c478bd9Sstevel@tonic-gate **		The order of recipients in e->e_sendqueue is permuted.
79097c478bd9Sstevel@tonic-gate **		Abandoned data files for additional envelopes that failed
79107c478bd9Sstevel@tonic-gate **		to be created may exist on disk.
79117c478bd9Sstevel@tonic-gate */
79127c478bd9Sstevel@tonic-gate 
79137c478bd9Sstevel@tonic-gate static int	q_qgrp_compare __P((const void *, const void *));
79147c478bd9Sstevel@tonic-gate static int	e_filesys_compare __P((const void *, const void *));
79157c478bd9Sstevel@tonic-gate 
79167c478bd9Sstevel@tonic-gate static int
79177c478bd9Sstevel@tonic-gate q_qgrp_compare(p1, p2)
79187c478bd9Sstevel@tonic-gate 	const void *p1;
79197c478bd9Sstevel@tonic-gate 	const void *p2;
79207c478bd9Sstevel@tonic-gate {
79217c478bd9Sstevel@tonic-gate 	ADDRESS **pq1 = (ADDRESS **) p1;
79227c478bd9Sstevel@tonic-gate 	ADDRESS **pq2 = (ADDRESS **) p2;
79237c478bd9Sstevel@tonic-gate 
79247c478bd9Sstevel@tonic-gate 	return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
79257c478bd9Sstevel@tonic-gate }
79267c478bd9Sstevel@tonic-gate 
79277c478bd9Sstevel@tonic-gate static int
79287c478bd9Sstevel@tonic-gate e_filesys_compare(p1, p2)
79297c478bd9Sstevel@tonic-gate 	const void *p1;
79307c478bd9Sstevel@tonic-gate 	const void *p2;
79317c478bd9Sstevel@tonic-gate {
79327c478bd9Sstevel@tonic-gate 	ENVELOPE **pe1 = (ENVELOPE **) p1;
79337c478bd9Sstevel@tonic-gate 	ENVELOPE **pe2 = (ENVELOPE **) p2;
79347c478bd9Sstevel@tonic-gate 	int fs1, fs2;
79357c478bd9Sstevel@tonic-gate 
79367c478bd9Sstevel@tonic-gate 	fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
79377c478bd9Sstevel@tonic-gate 	fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
79387c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
79397c478bd9Sstevel@tonic-gate 		return -1;
79407c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
79417c478bd9Sstevel@tonic-gate 		return 1;
79427c478bd9Sstevel@tonic-gate 	return 0;
79437c478bd9Sstevel@tonic-gate }
79447c478bd9Sstevel@tonic-gate 
7945*058561cbSjbeck static int split_across_queue_groups __P((ENVELOPE *));
79467c478bd9Sstevel@tonic-gate static int
79477c478bd9Sstevel@tonic-gate split_across_queue_groups(e)
79487c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
79497c478bd9Sstevel@tonic-gate {
79507c478bd9Sstevel@tonic-gate 	int naddrs, nsplits, i;
79517c478bd9Sstevel@tonic-gate 	bool changed;
79527c478bd9Sstevel@tonic-gate 	char **pvp;
79537c478bd9Sstevel@tonic-gate 	ADDRESS *q, **addrs;
79547c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *es;
79557c478bd9Sstevel@tonic-gate 	ENVELOPE *splits[MAXQUEUEGROUPS];
79567c478bd9Sstevel@tonic-gate 	char pvpbuf[PSBUFSIZE];
79577c478bd9Sstevel@tonic-gate 
79587c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
79597c478bd9Sstevel@tonic-gate 
79607c478bd9Sstevel@tonic-gate 	/* Count addresses and assign queue groups. */
79617c478bd9Sstevel@tonic-gate 	naddrs = 0;
79627c478bd9Sstevel@tonic-gate 	changed = false;
79637c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
79647c478bd9Sstevel@tonic-gate 	{
79657c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
79667c478bd9Sstevel@tonic-gate 			continue;
79677c478bd9Sstevel@tonic-gate 		++naddrs;
79687c478bd9Sstevel@tonic-gate 
79697c478bd9Sstevel@tonic-gate 		/* bad addresses and those already sent stay put */
79707c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(q->q_state) ||
79717c478bd9Sstevel@tonic-gate 		    QS_IS_SENT(q->q_state))
79727c478bd9Sstevel@tonic-gate 			q->q_qgrp = e->e_qgrp;
79737c478bd9Sstevel@tonic-gate 		else if (!ISVALIDQGRP(q->q_qgrp))
79747c478bd9Sstevel@tonic-gate 		{
79757c478bd9Sstevel@tonic-gate 			/* call ruleset which should return a queue group */
79767c478bd9Sstevel@tonic-gate 			i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
79777c478bd9Sstevel@tonic-gate 				  pvpbuf, sizeof(pvpbuf));
79787c478bd9Sstevel@tonic-gate 			if (i == EX_OK &&
79797c478bd9Sstevel@tonic-gate 			    pvp != NULL && pvp[0] != NULL &&
79807c478bd9Sstevel@tonic-gate 			    (pvp[0][0] & 0377) == CANONNET &&
79817c478bd9Sstevel@tonic-gate 			    pvp[1] != NULL && pvp[1][0] != '\0')
79827c478bd9Sstevel@tonic-gate 			{
79837c478bd9Sstevel@tonic-gate 				i = name2qid(pvp[1]);
79847c478bd9Sstevel@tonic-gate 				if (ISVALIDQGRP(i))
79857c478bd9Sstevel@tonic-gate 				{
79867c478bd9Sstevel@tonic-gate 					q->q_qgrp = i;
79877c478bd9Sstevel@tonic-gate 					changed = true;
79887c478bd9Sstevel@tonic-gate 					if (tTd(20, 4))
79897c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_INFO, NOQID,
79907c478bd9Sstevel@tonic-gate 							"queue group name %s -> %d",
79917c478bd9Sstevel@tonic-gate 							pvp[1], i);
79927c478bd9Sstevel@tonic-gate 					continue;
79937c478bd9Sstevel@tonic-gate 				}
79947c478bd9Sstevel@tonic-gate 				else if (LogLevel > 10)
79957c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
79967c478bd9Sstevel@tonic-gate 						"can't find queue group name %s, selection ignored",
79977c478bd9Sstevel@tonic-gate 						pvp[1]);
79987c478bd9Sstevel@tonic-gate 			}
79997c478bd9Sstevel@tonic-gate 			if (q->q_mailer != NULL &&
80007c478bd9Sstevel@tonic-gate 			    ISVALIDQGRP(q->q_mailer->m_qgrp))
80017c478bd9Sstevel@tonic-gate 			{
80027c478bd9Sstevel@tonic-gate 				changed = true;
80037c478bd9Sstevel@tonic-gate 				q->q_qgrp = q->q_mailer->m_qgrp;
80047c478bd9Sstevel@tonic-gate 			}
80057c478bd9Sstevel@tonic-gate 			else if (ISVALIDQGRP(e->e_qgrp))
80067c478bd9Sstevel@tonic-gate 				q->q_qgrp = e->e_qgrp;
80077c478bd9Sstevel@tonic-gate 			else
80087c478bd9Sstevel@tonic-gate 				q->q_qgrp = 0;
80097c478bd9Sstevel@tonic-gate 		}
80107c478bd9Sstevel@tonic-gate 	}
80117c478bd9Sstevel@tonic-gate 
80127c478bd9Sstevel@tonic-gate 	/* only one address? nothing to split. */
80137c478bd9Sstevel@tonic-gate 	if (naddrs <= 1 && !changed)
80147c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
80157c478bd9Sstevel@tonic-gate 
80167c478bd9Sstevel@tonic-gate 	/* sort the addresses by queue group */
80177c478bd9Sstevel@tonic-gate 	addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
80187c478bd9Sstevel@tonic-gate 	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
80197c478bd9Sstevel@tonic-gate 	{
80207c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
80217c478bd9Sstevel@tonic-gate 			continue;
80227c478bd9Sstevel@tonic-gate 		addrs[i++] = q;
80237c478bd9Sstevel@tonic-gate 	}
80247c478bd9Sstevel@tonic-gate 	qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
80257c478bd9Sstevel@tonic-gate 
80267c478bd9Sstevel@tonic-gate 	/* split into multiple envelopes, by queue group */
80277c478bd9Sstevel@tonic-gate 	nsplits = 0;
80287c478bd9Sstevel@tonic-gate 	es = NULL;
80297c478bd9Sstevel@tonic-gate 	e->e_sendqueue = NULL;
80307c478bd9Sstevel@tonic-gate 	for (i = 0; i < naddrs; ++i)
80317c478bd9Sstevel@tonic-gate 	{
80327c478bd9Sstevel@tonic-gate 		if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
80337c478bd9Sstevel@tonic-gate 			addrs[i]->q_next = NULL;
80347c478bd9Sstevel@tonic-gate 		else
80357c478bd9Sstevel@tonic-gate 			addrs[i]->q_next = addrs[i + 1];
80367c478bd9Sstevel@tonic-gate 
80377c478bd9Sstevel@tonic-gate 		/* same queue group as original envelope? */
80387c478bd9Sstevel@tonic-gate 		if (addrs[i]->q_qgrp == e->e_qgrp)
80397c478bd9Sstevel@tonic-gate 		{
80407c478bd9Sstevel@tonic-gate 			if (e->e_sendqueue == NULL)
80417c478bd9Sstevel@tonic-gate 				e->e_sendqueue = addrs[i];
80427c478bd9Sstevel@tonic-gate 			continue;
80437c478bd9Sstevel@tonic-gate 		}
80447c478bd9Sstevel@tonic-gate 
80457c478bd9Sstevel@tonic-gate 		/* different queue group than original envelope */
80467c478bd9Sstevel@tonic-gate 		if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
80477c478bd9Sstevel@tonic-gate 		{
80487c478bd9Sstevel@tonic-gate 			ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
80497c478bd9Sstevel@tonic-gate 			es = ee;
80507c478bd9Sstevel@tonic-gate 			splits[nsplits++] = ee;
80517c478bd9Sstevel@tonic-gate 		}
80527c478bd9Sstevel@tonic-gate 	}
80537c478bd9Sstevel@tonic-gate 
80547c478bd9Sstevel@tonic-gate 	/* no splits? return right now. */
80557c478bd9Sstevel@tonic-gate 	if (nsplits <= 0)
80567c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
80577c478bd9Sstevel@tonic-gate 
80587c478bd9Sstevel@tonic-gate 	/* assign a queue directory to each additional envelope */
80597c478bd9Sstevel@tonic-gate 	for (i = 0; i < nsplits; ++i)
80607c478bd9Sstevel@tonic-gate 	{
80617c478bd9Sstevel@tonic-gate 		es = splits[i];
80627c478bd9Sstevel@tonic-gate #if 0
80637c478bd9Sstevel@tonic-gate 		es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
80647c478bd9Sstevel@tonic-gate #endif /* 0 */
80657c478bd9Sstevel@tonic-gate 		if (!setnewqueue(es))
80667c478bd9Sstevel@tonic-gate 			goto failure;
80677c478bd9Sstevel@tonic-gate 	}
80687c478bd9Sstevel@tonic-gate 
80697c478bd9Sstevel@tonic-gate 	/* sort the additional envelopes by queue file system */
80707c478bd9Sstevel@tonic-gate 	qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
80717c478bd9Sstevel@tonic-gate 
80727c478bd9Sstevel@tonic-gate 	/* create data files for each additional envelope */
80737c478bd9Sstevel@tonic-gate 	if (!dup_df(e, splits[0]))
80747c478bd9Sstevel@tonic-gate 	{
80757c478bd9Sstevel@tonic-gate 		i = 0;
80767c478bd9Sstevel@tonic-gate 		goto failure;
80777c478bd9Sstevel@tonic-gate 	}
80787c478bd9Sstevel@tonic-gate 	for (i = 1; i < nsplits; ++i)
80797c478bd9Sstevel@tonic-gate 	{
80807c478bd9Sstevel@tonic-gate 		/* copy or link to the previous data file */
80817c478bd9Sstevel@tonic-gate 		if (!dup_df(splits[i - 1], splits[i]))
80827c478bd9Sstevel@tonic-gate 			goto failure;
80837c478bd9Sstevel@tonic-gate 	}
80847c478bd9Sstevel@tonic-gate 
80857c478bd9Sstevel@tonic-gate 	/* success: prepend the new envelopes to the e->e_sibling list */
80867c478bd9Sstevel@tonic-gate 	for (i = 0; i < nsplits; ++i)
80877c478bd9Sstevel@tonic-gate 	{
80887c478bd9Sstevel@tonic-gate 		es = splits[i];
80897c478bd9Sstevel@tonic-gate 		es->e_sibling = e->e_sibling;
80907c478bd9Sstevel@tonic-gate 		e->e_sibling = es;
80917c478bd9Sstevel@tonic-gate 	}
80927c478bd9Sstevel@tonic-gate 	return SM_SPLIT_NEW(nsplits);
80937c478bd9Sstevel@tonic-gate 
80947c478bd9Sstevel@tonic-gate 	/* failure: clean up */
80957c478bd9Sstevel@tonic-gate   failure:
80967c478bd9Sstevel@tonic-gate 	if (i > 0)
80977c478bd9Sstevel@tonic-gate 	{
80987c478bd9Sstevel@tonic-gate 		int j;
80997c478bd9Sstevel@tonic-gate 
81007c478bd9Sstevel@tonic-gate 		for (j = 0; j < i; j++)
81017c478bd9Sstevel@tonic-gate 			(void) unlink(queuename(splits[j], DATAFL_LETTER));
81027c478bd9Sstevel@tonic-gate 	}
81037c478bd9Sstevel@tonic-gate 	e->e_sendqueue = addrs[0];
81047c478bd9Sstevel@tonic-gate 	for (i = 0; i < naddrs - 1; ++i)
81057c478bd9Sstevel@tonic-gate 		addrs[i]->q_next = addrs[i + 1];
81067c478bd9Sstevel@tonic-gate 	addrs[naddrs - 1]->q_next = NULL;
81077c478bd9Sstevel@tonic-gate 	return SM_SPLIT_FAIL;
81087c478bd9Sstevel@tonic-gate }
81097c478bd9Sstevel@tonic-gate 
81107c478bd9Sstevel@tonic-gate /*
81117c478bd9Sstevel@tonic-gate **  SPLIT_WITHIN_QUEUE
81127c478bd9Sstevel@tonic-gate **
81137c478bd9Sstevel@tonic-gate **	Split an envelope with multiple recipients into several
81147c478bd9Sstevel@tonic-gate **	envelopes within the same queue directory, if the number of
81157c478bd9Sstevel@tonic-gate **	recipients exceeds the limit for the queue group.
81167c478bd9Sstevel@tonic-gate **
81177c478bd9Sstevel@tonic-gate **	Parameters:
81187c478bd9Sstevel@tonic-gate **		e -- envelope.
81197c478bd9Sstevel@tonic-gate **
81207c478bd9Sstevel@tonic-gate **	Results:
81217c478bd9Sstevel@tonic-gate **		SM_SPLIT_FAIL on failure
81227c478bd9Sstevel@tonic-gate **		SM_SPLIT_NONE if no splitting occurred,
81237c478bd9Sstevel@tonic-gate **		or 1 + the number of additional envelopes created.
81247c478bd9Sstevel@tonic-gate */
81257c478bd9Sstevel@tonic-gate 
81267c478bd9Sstevel@tonic-gate #define SPLIT_LOG_LEVEL	8
81277c478bd9Sstevel@tonic-gate 
81287c478bd9Sstevel@tonic-gate static int	split_within_queue __P((ENVELOPE *));
81297c478bd9Sstevel@tonic-gate 
81307c478bd9Sstevel@tonic-gate static int
81317c478bd9Sstevel@tonic-gate split_within_queue(e)
81327c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
81337c478bd9Sstevel@tonic-gate {
81347c478bd9Sstevel@tonic-gate 	int maxrcpt, nrcpt, ndead, nsplit, i;
81357c478bd9Sstevel@tonic-gate 	int j, l;
81367c478bd9Sstevel@tonic-gate 	char *lsplits;
81377c478bd9Sstevel@tonic-gate 	ADDRESS *q, **addrs;
81387c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *firstsibling;
81397c478bd9Sstevel@tonic-gate 
81407c478bd9Sstevel@tonic-gate 	if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
81417c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81427c478bd9Sstevel@tonic-gate 
81437c478bd9Sstevel@tonic-gate 	/* don't bother if there is no recipient limit */
81447c478bd9Sstevel@tonic-gate 	maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
81457c478bd9Sstevel@tonic-gate 	if (maxrcpt <= 0)
81467c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81477c478bd9Sstevel@tonic-gate 
81487c478bd9Sstevel@tonic-gate 	/* count recipients */
81497c478bd9Sstevel@tonic-gate 	nrcpt = 0;
81507c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
81517c478bd9Sstevel@tonic-gate 	{
81527c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
81537c478bd9Sstevel@tonic-gate 			continue;
81547c478bd9Sstevel@tonic-gate 		++nrcpt;
81557c478bd9Sstevel@tonic-gate 	}
81567c478bd9Sstevel@tonic-gate 	if (nrcpt <= maxrcpt)
81577c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81587c478bd9Sstevel@tonic-gate 
81597c478bd9Sstevel@tonic-gate 	/*
81607c478bd9Sstevel@tonic-gate 	**  Preserve the recipient list
81617c478bd9Sstevel@tonic-gate 	**  so that we can restore it in case of error.
81627c478bd9Sstevel@tonic-gate 	**  (But we discard dead addresses.)
81637c478bd9Sstevel@tonic-gate 	*/
81647c478bd9Sstevel@tonic-gate 
81657c478bd9Sstevel@tonic-gate 	addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
81667c478bd9Sstevel@tonic-gate 	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
81677c478bd9Sstevel@tonic-gate 	{
81687c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
81697c478bd9Sstevel@tonic-gate 			continue;
81707c478bd9Sstevel@tonic-gate 		addrs[i++] = q;
81717c478bd9Sstevel@tonic-gate 	}
81727c478bd9Sstevel@tonic-gate 
81737c478bd9Sstevel@tonic-gate 	/*
81747c478bd9Sstevel@tonic-gate 	**  Partition the recipient list so that bad and sent addresses
81757c478bd9Sstevel@tonic-gate 	**  come first. These will go with the original envelope, and
81767c478bd9Sstevel@tonic-gate 	**  do not count towards the maxrcpt limit.
81777c478bd9Sstevel@tonic-gate 	**  addrs[] does not contain QS_IS_DEAD() addresses.
81787c478bd9Sstevel@tonic-gate 	*/
81797c478bd9Sstevel@tonic-gate 
81807c478bd9Sstevel@tonic-gate 	ndead = 0;
81817c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrcpt; ++i)
81827c478bd9Sstevel@tonic-gate 	{
81837c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(addrs[i]->q_state) ||
81847c478bd9Sstevel@tonic-gate 		    QS_IS_SENT(addrs[i]->q_state) ||
81857c478bd9Sstevel@tonic-gate 		    QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
81867c478bd9Sstevel@tonic-gate 		{
81877c478bd9Sstevel@tonic-gate 			if (i > ndead)
81887c478bd9Sstevel@tonic-gate 			{
81897c478bd9Sstevel@tonic-gate 				ADDRESS *tmp = addrs[i];
81907c478bd9Sstevel@tonic-gate 
81917c478bd9Sstevel@tonic-gate 				addrs[i] = addrs[ndead];
81927c478bd9Sstevel@tonic-gate 				addrs[ndead] = tmp;
81937c478bd9Sstevel@tonic-gate 			}
81947c478bd9Sstevel@tonic-gate 			++ndead;
81957c478bd9Sstevel@tonic-gate 		}
81967c478bd9Sstevel@tonic-gate 	}
81977c478bd9Sstevel@tonic-gate 
81987c478bd9Sstevel@tonic-gate 	/* Check if no splitting required. */
81997c478bd9Sstevel@tonic-gate 	if (nrcpt - ndead <= maxrcpt)
82007c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
82017c478bd9Sstevel@tonic-gate 
82027c478bd9Sstevel@tonic-gate 	/* fix links */
82037c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrcpt - 1; ++i)
82047c478bd9Sstevel@tonic-gate 		addrs[i]->q_next = addrs[i + 1];
82057c478bd9Sstevel@tonic-gate 	addrs[nrcpt - 1]->q_next = NULL;
82067c478bd9Sstevel@tonic-gate 	e->e_sendqueue = addrs[0];
82077c478bd9Sstevel@tonic-gate 
82087c478bd9Sstevel@tonic-gate 	/* prepare buffer for logging */
82097c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL)
82107c478bd9Sstevel@tonic-gate 	{
82117c478bd9Sstevel@tonic-gate 		l = MAXLINE;
82127c478bd9Sstevel@tonic-gate 		lsplits = sm_malloc(l);
82137c478bd9Sstevel@tonic-gate 		if (lsplits != NULL)
82147c478bd9Sstevel@tonic-gate 			*lsplits = '\0';
82157c478bd9Sstevel@tonic-gate 		j = 0;
82167c478bd9Sstevel@tonic-gate 	}
82177c478bd9Sstevel@tonic-gate 	else
82187c478bd9Sstevel@tonic-gate 	{
82197c478bd9Sstevel@tonic-gate 		/* get rid of stupid compiler warnings */
82207c478bd9Sstevel@tonic-gate 		lsplits = NULL;
82217c478bd9Sstevel@tonic-gate 		j = l = 0;
82227c478bd9Sstevel@tonic-gate 	}
82237c478bd9Sstevel@tonic-gate 
82247c478bd9Sstevel@tonic-gate 	/* split the envelope */
82257c478bd9Sstevel@tonic-gate 	firstsibling = e->e_sibling;
82267c478bd9Sstevel@tonic-gate 	i = maxrcpt + ndead;
82277c478bd9Sstevel@tonic-gate 	nsplit = 0;
82287c478bd9Sstevel@tonic-gate 	for (;;)
82297c478bd9Sstevel@tonic-gate 	{
82307c478bd9Sstevel@tonic-gate 		addrs[i - 1]->q_next = NULL;
82317c478bd9Sstevel@tonic-gate 		ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
82327c478bd9Sstevel@tonic-gate 		if (!dup_df(e, ee))
82337c478bd9Sstevel@tonic-gate 		{
82347c478bd9Sstevel@tonic-gate 
82357c478bd9Sstevel@tonic-gate 			ee = firstsibling;
82367c478bd9Sstevel@tonic-gate 			while (ee != NULL)
82377c478bd9Sstevel@tonic-gate 			{
82387c478bd9Sstevel@tonic-gate 				(void) unlink(queuename(ee, DATAFL_LETTER));
82397c478bd9Sstevel@tonic-gate 				ee = ee->e_sibling;
82407c478bd9Sstevel@tonic-gate 			}
82417c478bd9Sstevel@tonic-gate 
82427c478bd9Sstevel@tonic-gate 			/* Error.  Restore e's sibling & recipient lists. */
82437c478bd9Sstevel@tonic-gate 			e->e_sibling = firstsibling;
82447c478bd9Sstevel@tonic-gate 			for (i = 0; i < nrcpt - 1; ++i)
82457c478bd9Sstevel@tonic-gate 				addrs[i]->q_next = addrs[i + 1];
82467c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
82477c478bd9Sstevel@tonic-gate 				sm_free(lsplits);
82487c478bd9Sstevel@tonic-gate 			return SM_SPLIT_FAIL;
82497c478bd9Sstevel@tonic-gate 		}
82507c478bd9Sstevel@tonic-gate 
82517c478bd9Sstevel@tonic-gate 		/* prepend the new envelope to e->e_sibling */
82527c478bd9Sstevel@tonic-gate 		ee->e_sibling = e->e_sibling;
82537c478bd9Sstevel@tonic-gate 		e->e_sibling = ee;
82547c478bd9Sstevel@tonic-gate 		++nsplit;
82557c478bd9Sstevel@tonic-gate 		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
82567c478bd9Sstevel@tonic-gate 		{
82577c478bd9Sstevel@tonic-gate 			if (j >= l - strlen(ee->e_id) - 3)
82587c478bd9Sstevel@tonic-gate 			{
82597c478bd9Sstevel@tonic-gate 				char *p;
82607c478bd9Sstevel@tonic-gate 
82617c478bd9Sstevel@tonic-gate 				l += MAXLINE;
82627c478bd9Sstevel@tonic-gate 				p = sm_realloc(lsplits, l);
82637c478bd9Sstevel@tonic-gate 				if (p == NULL)
82647c478bd9Sstevel@tonic-gate 				{
82657c478bd9Sstevel@tonic-gate 					/* let's try to get this done */
82667c478bd9Sstevel@tonic-gate 					sm_free(lsplits);
82677c478bd9Sstevel@tonic-gate 					lsplits = NULL;
82687c478bd9Sstevel@tonic-gate 				}
82697c478bd9Sstevel@tonic-gate 				else
82707c478bd9Sstevel@tonic-gate 					lsplits = p;
82717c478bd9Sstevel@tonic-gate 			}
82727c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
82737c478bd9Sstevel@tonic-gate 			{
82747c478bd9Sstevel@tonic-gate 				if (j == 0)
82757c478bd9Sstevel@tonic-gate 					j += sm_strlcat(lsplits + j,
82767c478bd9Sstevel@tonic-gate 							ee->e_id,
82777c478bd9Sstevel@tonic-gate 							l - j);
82787c478bd9Sstevel@tonic-gate 				else
82797c478bd9Sstevel@tonic-gate 					j += sm_strlcat2(lsplits + j,
82807c478bd9Sstevel@tonic-gate 							 "; ",
82817c478bd9Sstevel@tonic-gate 							 ee->e_id,
82827c478bd9Sstevel@tonic-gate 							 l - j);
82837c478bd9Sstevel@tonic-gate 				SM_ASSERT(j < l);
82847c478bd9Sstevel@tonic-gate 			}
82857c478bd9Sstevel@tonic-gate 		}
82867c478bd9Sstevel@tonic-gate 		if (nrcpt - i <= maxrcpt)
82877c478bd9Sstevel@tonic-gate 			break;
82887c478bd9Sstevel@tonic-gate 		i += maxrcpt;
82897c478bd9Sstevel@tonic-gate 	}
82907c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
82917c478bd9Sstevel@tonic-gate 	{
82927c478bd9Sstevel@tonic-gate 		if (nsplit > 0)
82937c478bd9Sstevel@tonic-gate 		{
82947c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_NOTICE, e->e_id,
82957c478bd9Sstevel@tonic-gate 				  "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
82967c478bd9Sstevel@tonic-gate 				  maxrcpt, nrcpt - ndead, nsplit,
82977c478bd9Sstevel@tonic-gate 				  nsplit > 1 ? "s" : "", lsplits);
82987c478bd9Sstevel@tonic-gate 		}
82997c478bd9Sstevel@tonic-gate 		sm_free(lsplits);
83007c478bd9Sstevel@tonic-gate 	}
83017c478bd9Sstevel@tonic-gate 	return SM_SPLIT_NEW(nsplit);
83027c478bd9Sstevel@tonic-gate }
83037c478bd9Sstevel@tonic-gate /*
83047c478bd9Sstevel@tonic-gate **  SPLIT_BY_RECIPIENT
83057c478bd9Sstevel@tonic-gate **
83067c478bd9Sstevel@tonic-gate **	Split an envelope with multiple recipients into multiple
83077c478bd9Sstevel@tonic-gate **	envelopes as required by the sendmail configuration.
83087c478bd9Sstevel@tonic-gate **
83097c478bd9Sstevel@tonic-gate **	Parameters:
83107c478bd9Sstevel@tonic-gate **		e -- envelope.
83117c478bd9Sstevel@tonic-gate **
83127c478bd9Sstevel@tonic-gate **	Results:
83137c478bd9Sstevel@tonic-gate **		Returns true on success, false on failure.
83147c478bd9Sstevel@tonic-gate **
83157c478bd9Sstevel@tonic-gate **	Side Effects:
83167c478bd9Sstevel@tonic-gate **		see split_across_queue_groups(), split_within_queue(e)
83177c478bd9Sstevel@tonic-gate */
83187c478bd9Sstevel@tonic-gate 
83197c478bd9Sstevel@tonic-gate bool
83207c478bd9Sstevel@tonic-gate split_by_recipient(e)
83217c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
83227c478bd9Sstevel@tonic-gate {
83237c478bd9Sstevel@tonic-gate 	int split, n, i, j, l;
83247c478bd9Sstevel@tonic-gate 	char *lsplits;
83257c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *next, *firstsibling;
83267c478bd9Sstevel@tonic-gate 
83277c478bd9Sstevel@tonic-gate 	if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
83287c478bd9Sstevel@tonic-gate 	    bitset(EF_SPLIT, e->e_flags))
83297c478bd9Sstevel@tonic-gate 		return true;
83307c478bd9Sstevel@tonic-gate 	n = split_across_queue_groups(e);
83317c478bd9Sstevel@tonic-gate 	if (n == SM_SPLIT_FAIL)
83327c478bd9Sstevel@tonic-gate 		return false;
83337c478bd9Sstevel@tonic-gate 	firstsibling = ee = e->e_sibling;
83347c478bd9Sstevel@tonic-gate 	if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
83357c478bd9Sstevel@tonic-gate 	{
83367c478bd9Sstevel@tonic-gate 		l = MAXLINE;
83377c478bd9Sstevel@tonic-gate 		lsplits = sm_malloc(l);
83387c478bd9Sstevel@tonic-gate 		if (lsplits != NULL)
83397c478bd9Sstevel@tonic-gate 			*lsplits = '\0';
83407c478bd9Sstevel@tonic-gate 		j = 0;
83417c478bd9Sstevel@tonic-gate 	}
83427c478bd9Sstevel@tonic-gate 	else
83437c478bd9Sstevel@tonic-gate 	{
83447c478bd9Sstevel@tonic-gate 		/* get rid of stupid compiler warnings */
83457c478bd9Sstevel@tonic-gate 		lsplits = NULL;
83467c478bd9Sstevel@tonic-gate 		j = l = 0;
83477c478bd9Sstevel@tonic-gate 	}
83487c478bd9Sstevel@tonic-gate 	for (i = 1; i < n; ++i)
83497c478bd9Sstevel@tonic-gate 	{
83507c478bd9Sstevel@tonic-gate 		next = ee->e_sibling;
83517c478bd9Sstevel@tonic-gate 		if (split_within_queue(ee) == SM_SPLIT_FAIL)
83527c478bd9Sstevel@tonic-gate 		{
83537c478bd9Sstevel@tonic-gate 			e->e_sibling = firstsibling;
83547c478bd9Sstevel@tonic-gate 			return false;
83557c478bd9Sstevel@tonic-gate 		}
83567c478bd9Sstevel@tonic-gate 		ee->e_flags |= EF_SPLIT;
83577c478bd9Sstevel@tonic-gate 		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
83587c478bd9Sstevel@tonic-gate 		{
83597c478bd9Sstevel@tonic-gate 			if (j >= l - strlen(ee->e_id) - 3)
83607c478bd9Sstevel@tonic-gate 			{
83617c478bd9Sstevel@tonic-gate 				char *p;
83627c478bd9Sstevel@tonic-gate 
83637c478bd9Sstevel@tonic-gate 				l += MAXLINE;
83647c478bd9Sstevel@tonic-gate 				p = sm_realloc(lsplits, l);
83657c478bd9Sstevel@tonic-gate 				if (p == NULL)
83667c478bd9Sstevel@tonic-gate 				{
83677c478bd9Sstevel@tonic-gate 					/* let's try to get this done */
83687c478bd9Sstevel@tonic-gate 					sm_free(lsplits);
83697c478bd9Sstevel@tonic-gate 					lsplits = NULL;
83707c478bd9Sstevel@tonic-gate 				}
83717c478bd9Sstevel@tonic-gate 				else
83727c478bd9Sstevel@tonic-gate 					lsplits = p;
83737c478bd9Sstevel@tonic-gate 			}
83747c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
83757c478bd9Sstevel@tonic-gate 			{
83767c478bd9Sstevel@tonic-gate 				if (j == 0)
83777c478bd9Sstevel@tonic-gate 					j += sm_strlcat(lsplits + j,
83787c478bd9Sstevel@tonic-gate 							ee->e_id, l - j);
83797c478bd9Sstevel@tonic-gate 				else
83807c478bd9Sstevel@tonic-gate 					j += sm_strlcat2(lsplits + j, "; ",
83817c478bd9Sstevel@tonic-gate 							 ee->e_id, l - j);
83827c478bd9Sstevel@tonic-gate 				SM_ASSERT(j < l);
83837c478bd9Sstevel@tonic-gate 			}
83847c478bd9Sstevel@tonic-gate 		}
83857c478bd9Sstevel@tonic-gate 		ee = next;
83867c478bd9Sstevel@tonic-gate 	}
83877c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
83887c478bd9Sstevel@tonic-gate 	{
83897c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
83907c478bd9Sstevel@tonic-gate 			  n - 1, n > 2 ? "s" : "", lsplits);
83917c478bd9Sstevel@tonic-gate 		sm_free(lsplits);
83927c478bd9Sstevel@tonic-gate 	}
83937c478bd9Sstevel@tonic-gate 	split = split_within_queue(e) != SM_SPLIT_FAIL;
83947c478bd9Sstevel@tonic-gate 	if (split)
83957c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_SPLIT;
83967c478bd9Sstevel@tonic-gate 	return split;
83977c478bd9Sstevel@tonic-gate }
83987c478bd9Sstevel@tonic-gate 
83997c478bd9Sstevel@tonic-gate /*
84007c478bd9Sstevel@tonic-gate **  QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
84017c478bd9Sstevel@tonic-gate **
84027c478bd9Sstevel@tonic-gate **	Add/remove quarantine reason and requeue appropriately.
84037c478bd9Sstevel@tonic-gate **
84047c478bd9Sstevel@tonic-gate **	Parameters:
84057c478bd9Sstevel@tonic-gate **		qgrp -- queue group for the item
84067c478bd9Sstevel@tonic-gate **		qdir -- queue directory in the given queue group
84077c478bd9Sstevel@tonic-gate **		e -- envelope information for the item
84087c478bd9Sstevel@tonic-gate **		reason -- quarantine reason, NULL means unquarantine.
84097c478bd9Sstevel@tonic-gate **
84107c478bd9Sstevel@tonic-gate **	Results:
84117c478bd9Sstevel@tonic-gate **		true if item changed, false otherwise
84127c478bd9Sstevel@tonic-gate **
84137c478bd9Sstevel@tonic-gate **	Side Effects:
84147c478bd9Sstevel@tonic-gate **		Changes quarantine tag in queue file and renames it.
84157c478bd9Sstevel@tonic-gate */
84167c478bd9Sstevel@tonic-gate 
84177c478bd9Sstevel@tonic-gate static bool
84187c478bd9Sstevel@tonic-gate quarantine_queue_item(qgrp, qdir, e, reason)
84197c478bd9Sstevel@tonic-gate 	int qgrp;
84207c478bd9Sstevel@tonic-gate 	int qdir;
84217c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
84227c478bd9Sstevel@tonic-gate 	char *reason;
84237c478bd9Sstevel@tonic-gate {
84247c478bd9Sstevel@tonic-gate 	bool dirty = false;
84257c478bd9Sstevel@tonic-gate 	bool failing = false;
84267c478bd9Sstevel@tonic-gate 	bool foundq = false;
84277c478bd9Sstevel@tonic-gate 	bool finished = false;
84287c478bd9Sstevel@tonic-gate 	int fd;
84297c478bd9Sstevel@tonic-gate 	int flags;
84307c478bd9Sstevel@tonic-gate 	int oldtype;
84317c478bd9Sstevel@tonic-gate 	int newtype;
84327c478bd9Sstevel@tonic-gate 	int save_errno;
84337c478bd9Sstevel@tonic-gate 	MODE_T oldumask = 0;
84347c478bd9Sstevel@tonic-gate 	SM_FILE_T *oldqfp, *tempqfp;
84357c478bd9Sstevel@tonic-gate 	char *bp;
8436*058561cbSjbeck 	int bufsize;
84377c478bd9Sstevel@tonic-gate 	char oldqf[MAXPATHLEN];
84387c478bd9Sstevel@tonic-gate 	char tempqf[MAXPATHLEN];
84397c478bd9Sstevel@tonic-gate 	char newqf[MAXPATHLEN];
84407c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
84417c478bd9Sstevel@tonic-gate 
84427c478bd9Sstevel@tonic-gate 	oldtype = queue_letter(e, ANYQFL_LETTER);
8443*058561cbSjbeck 	(void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof(oldqf));
8444*058561cbSjbeck 	(void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof(tempqf));
84457c478bd9Sstevel@tonic-gate 
84467c478bd9Sstevel@tonic-gate 	/*
84477c478bd9Sstevel@tonic-gate 	**  Instead of duplicating all the open
84487c478bd9Sstevel@tonic-gate 	**  and lock code here, tell readqf() to
84497c478bd9Sstevel@tonic-gate 	**  do that work and return the open
84507c478bd9Sstevel@tonic-gate 	**  file pointer in e_lockfp.  Note that
84517c478bd9Sstevel@tonic-gate 	**  we must release the locks properly when
84527c478bd9Sstevel@tonic-gate 	**  we are done.
84537c478bd9Sstevel@tonic-gate 	*/
84547c478bd9Sstevel@tonic-gate 
84557c478bd9Sstevel@tonic-gate 	if (!readqf(e, true))
84567c478bd9Sstevel@tonic-gate 	{
84577c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84587c478bd9Sstevel@tonic-gate 				     "Skipping %s\n", qid_printname(e));
84597c478bd9Sstevel@tonic-gate 		return false;
84607c478bd9Sstevel@tonic-gate 	}
84617c478bd9Sstevel@tonic-gate 	oldqfp = e->e_lockfp;
84627c478bd9Sstevel@tonic-gate 
84637c478bd9Sstevel@tonic-gate 	/* open the new queue file */
84647c478bd9Sstevel@tonic-gate 	flags = O_CREAT|O_WRONLY|O_EXCL;
84657c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
84667c478bd9Sstevel@tonic-gate 		oldumask = umask(002);
84677c478bd9Sstevel@tonic-gate 	fd = open(tempqf, flags, QueueFileMode);
84687c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
84697c478bd9Sstevel@tonic-gate 		(void) umask(oldumask);
84707c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
84717c478bd9Sstevel@tonic-gate 
84727c478bd9Sstevel@tonic-gate 	if (fd < 0)
84737c478bd9Sstevel@tonic-gate 	{
84747c478bd9Sstevel@tonic-gate 		save_errno = errno;
84757c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84767c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not open %s: %s\n",
84777c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf,
84787c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
84797c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
84807c478bd9Sstevel@tonic-gate 		return false;
84817c478bd9Sstevel@tonic-gate 	}
84827c478bd9Sstevel@tonic-gate 	if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
84837c478bd9Sstevel@tonic-gate 	{
84847c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84857c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not lock %s\n",
84867c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf);
84877c478bd9Sstevel@tonic-gate 		(void) close(fd);
84887c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
84897c478bd9Sstevel@tonic-gate 		return false;
84907c478bd9Sstevel@tonic-gate 	}
84917c478bd9Sstevel@tonic-gate 
84927c478bd9Sstevel@tonic-gate 	tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
84937c478bd9Sstevel@tonic-gate 			     SM_IO_WRONLY_B, NULL);
84947c478bd9Sstevel@tonic-gate 	if (tempqfp == NULL)
84957c478bd9Sstevel@tonic-gate 	{
84967c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
84977c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not lock %s\n",
84987c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf);
84997c478bd9Sstevel@tonic-gate 		(void) close(fd);
85007c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
85017c478bd9Sstevel@tonic-gate 		return false;
85027c478bd9Sstevel@tonic-gate 	}
85037c478bd9Sstevel@tonic-gate 
85047c478bd9Sstevel@tonic-gate 	/* Copy the data over, changing the quarantine reason */
8505*058561cbSjbeck 	while (bufsize = sizeof(buf),
8506*058561cbSjbeck 	       (bp = fgetfolded(buf, &bufsize, oldqfp)) != NULL)
85077c478bd9Sstevel@tonic-gate 	{
85087c478bd9Sstevel@tonic-gate 		if (tTd(40, 4))
85097c478bd9Sstevel@tonic-gate 			sm_dprintf("+++++ %s\n", bp);
85107c478bd9Sstevel@tonic-gate 		switch (bp[0])
85117c478bd9Sstevel@tonic-gate 		{
85127c478bd9Sstevel@tonic-gate 		  case 'q':		/* quarantine reason */
85137c478bd9Sstevel@tonic-gate 			foundq = true;
85147c478bd9Sstevel@tonic-gate 			if (reason == NULL)
85157c478bd9Sstevel@tonic-gate 			{
85167c478bd9Sstevel@tonic-gate 				if (Verbose)
85177c478bd9Sstevel@tonic-gate 				{
85187c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85197c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85207c478bd9Sstevel@tonic-gate 							     "%s: Removed quarantine of \"%s\"\n",
85217c478bd9Sstevel@tonic-gate 							     e->e_id, &bp[1]);
85227c478bd9Sstevel@tonic-gate 				}
85237c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "unquarantine");
85247c478bd9Sstevel@tonic-gate 				dirty = true;
85257c478bd9Sstevel@tonic-gate 			}
85267c478bd9Sstevel@tonic-gate 			else if (strcmp(reason, &bp[1]) == 0)
85277c478bd9Sstevel@tonic-gate 			{
85287c478bd9Sstevel@tonic-gate 				if (Verbose)
85297c478bd9Sstevel@tonic-gate 				{
85307c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85317c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85327c478bd9Sstevel@tonic-gate 							     "%s: Already quarantined with \"%s\"\n",
85337c478bd9Sstevel@tonic-gate 							     e->e_id, reason);
85347c478bd9Sstevel@tonic-gate 				}
85357c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85367c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85377c478bd9Sstevel@tonic-gate 			}
85387c478bd9Sstevel@tonic-gate 			else
85397c478bd9Sstevel@tonic-gate 			{
85407c478bd9Sstevel@tonic-gate 				if (Verbose)
85417c478bd9Sstevel@tonic-gate 				{
85427c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85437c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85447c478bd9Sstevel@tonic-gate 							     "%s: Quarantine changed from \"%s\" to \"%s\"\n",
85457c478bd9Sstevel@tonic-gate 							     e->e_id, &bp[1],
85467c478bd9Sstevel@tonic-gate 							     reason);
85477c478bd9Sstevel@tonic-gate 				}
85487c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85497c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85507c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
85517c478bd9Sstevel@tonic-gate 					  reason);
85527c478bd9Sstevel@tonic-gate 				dirty = true;
85537c478bd9Sstevel@tonic-gate 			}
85547c478bd9Sstevel@tonic-gate 			break;
85557c478bd9Sstevel@tonic-gate 
85567c478bd9Sstevel@tonic-gate 		  case 'S':
85577c478bd9Sstevel@tonic-gate 			/*
85587c478bd9Sstevel@tonic-gate 			**  If we are quarantining an unquarantined item,
85597c478bd9Sstevel@tonic-gate 			**  need to put in a new 'q' line before it's
85607c478bd9Sstevel@tonic-gate 			**  too late.
85617c478bd9Sstevel@tonic-gate 			*/
85627c478bd9Sstevel@tonic-gate 
85637c478bd9Sstevel@tonic-gate 			if (!foundq && reason != NULL)
85647c478bd9Sstevel@tonic-gate 			{
85657c478bd9Sstevel@tonic-gate 				if (Verbose)
85667c478bd9Sstevel@tonic-gate 				{
85677c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85687c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85697c478bd9Sstevel@tonic-gate 							     "%s: Quarantined with \"%s\"\n",
85707c478bd9Sstevel@tonic-gate 							     e->e_id, reason);
85717c478bd9Sstevel@tonic-gate 				}
85727c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85737c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85747c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
85757c478bd9Sstevel@tonic-gate 					  reason);
85767c478bd9Sstevel@tonic-gate 				foundq = true;
85777c478bd9Sstevel@tonic-gate 				dirty = true;
85787c478bd9Sstevel@tonic-gate 			}
85797c478bd9Sstevel@tonic-gate 
85807c478bd9Sstevel@tonic-gate 			/* Copy the line to the new file */
85817c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85827c478bd9Sstevel@tonic-gate 					     "%s\n", bp);
85837c478bd9Sstevel@tonic-gate 			break;
85847c478bd9Sstevel@tonic-gate 
85857c478bd9Sstevel@tonic-gate 		  case '.':
85867c478bd9Sstevel@tonic-gate 			finished = true;
85877c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
85887c478bd9Sstevel@tonic-gate 
85897c478bd9Sstevel@tonic-gate 		  default:
85907c478bd9Sstevel@tonic-gate 			/* Copy the line to the new file */
85917c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85927c478bd9Sstevel@tonic-gate 					     "%s\n", bp);
85937c478bd9Sstevel@tonic-gate 			break;
85947c478bd9Sstevel@tonic-gate 		}
8595*058561cbSjbeck 		if (bp != buf)
8596*058561cbSjbeck 			sm_free(bp);
85977c478bd9Sstevel@tonic-gate 	}
85987c478bd9Sstevel@tonic-gate 
85997c478bd9Sstevel@tonic-gate 	/* Make sure we read the whole old file */
86007c478bd9Sstevel@tonic-gate 	errno = sm_io_error(tempqfp);
86017c478bd9Sstevel@tonic-gate 	if (errno != 0 && errno != SM_IO_EOF)
86027c478bd9Sstevel@tonic-gate 	{
86037c478bd9Sstevel@tonic-gate 		save_errno = errno;
86047c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86057c478bd9Sstevel@tonic-gate 				     "Skipping %s: Error reading %s: %s\n",
86067c478bd9Sstevel@tonic-gate 				     qid_printname(e), oldqf,
86077c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86087c478bd9Sstevel@tonic-gate 		failing = true;
86097c478bd9Sstevel@tonic-gate 	}
86107c478bd9Sstevel@tonic-gate 
86117c478bd9Sstevel@tonic-gate 	if (!failing && !finished)
86127c478bd9Sstevel@tonic-gate 	{
86137c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86147c478bd9Sstevel@tonic-gate 				     "Skipping %s: Incomplete file: %s\n",
86157c478bd9Sstevel@tonic-gate 				     qid_printname(e), oldqf);
86167c478bd9Sstevel@tonic-gate 		failing = true;
86177c478bd9Sstevel@tonic-gate 	}
86187c478bd9Sstevel@tonic-gate 
86197c478bd9Sstevel@tonic-gate 	/* Check if we actually changed anything or we can just bail now */
86207c478bd9Sstevel@tonic-gate 	if (!dirty)
86217c478bd9Sstevel@tonic-gate 	{
86227c478bd9Sstevel@tonic-gate 		/* pretend we failed, even though we technically didn't */
86237c478bd9Sstevel@tonic-gate 		failing = true;
86247c478bd9Sstevel@tonic-gate 	}
86257c478bd9Sstevel@tonic-gate 
86267c478bd9Sstevel@tonic-gate 	/* Make sure we wrote things out safely */
86277c478bd9Sstevel@tonic-gate 	if (!failing &&
86287c478bd9Sstevel@tonic-gate 	    (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
86297c478bd9Sstevel@tonic-gate 	     ((SuperSafe == SAFE_REALLY ||
86307c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_REALLY_POSTMILTER ||
86317c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_INTERACTIVE) &&
86327c478bd9Sstevel@tonic-gate 	      fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
86337c478bd9Sstevel@tonic-gate 	     ((errno = sm_io_error(tempqfp)) != 0)))
86347c478bd9Sstevel@tonic-gate 	{
86357c478bd9Sstevel@tonic-gate 		save_errno = errno;
86367c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86377c478bd9Sstevel@tonic-gate 				     "Skipping %s: Error writing %s: %s\n",
86387c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf,
86397c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86407c478bd9Sstevel@tonic-gate 		failing = true;
86417c478bd9Sstevel@tonic-gate 	}
86427c478bd9Sstevel@tonic-gate 
86437c478bd9Sstevel@tonic-gate 
86447c478bd9Sstevel@tonic-gate 	/* Figure out the new filename */
86457c478bd9Sstevel@tonic-gate 	newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
86467c478bd9Sstevel@tonic-gate 	if (oldtype == newtype)
86477c478bd9Sstevel@tonic-gate 	{
86487c478bd9Sstevel@tonic-gate 		/* going to rename tempqf to oldqf */
8649*058561cbSjbeck 		(void) sm_strlcpy(newqf, oldqf, sizeof(newqf));
86507c478bd9Sstevel@tonic-gate 	}
86517c478bd9Sstevel@tonic-gate 	else
86527c478bd9Sstevel@tonic-gate 	{
86537c478bd9Sstevel@tonic-gate 		/* going to rename tempqf to new name based on newtype */
8654*058561cbSjbeck 		(void) sm_strlcpy(newqf, queuename(e, newtype), sizeof(newqf));
86557c478bd9Sstevel@tonic-gate 	}
86567c478bd9Sstevel@tonic-gate 
86577c478bd9Sstevel@tonic-gate 	save_errno = 0;
86587c478bd9Sstevel@tonic-gate 
86597c478bd9Sstevel@tonic-gate 	/* rename tempqf to newqf */
86607c478bd9Sstevel@tonic-gate 	if (!failing &&
86617c478bd9Sstevel@tonic-gate 	    rename(tempqf, newqf) < 0)
86627c478bd9Sstevel@tonic-gate 		save_errno = (errno == 0) ? EINVAL : errno;
86637c478bd9Sstevel@tonic-gate 
86647c478bd9Sstevel@tonic-gate 	/* Check rename() success */
86657c478bd9Sstevel@tonic-gate 	if (!failing && save_errno != 0)
86667c478bd9Sstevel@tonic-gate 	{
86677c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id,
86687c478bd9Sstevel@tonic-gate 			  "quarantine_queue_item: rename(%s, %s): %s",
86697c478bd9Sstevel@tonic-gate 			  tempqf, newqf, sm_errstring(save_errno));
86707c478bd9Sstevel@tonic-gate 
86717c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86727c478bd9Sstevel@tonic-gate 				     "Error renaming %s to %s: %s\n",
86737c478bd9Sstevel@tonic-gate 				     tempqf, newqf,
86747c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86757c478bd9Sstevel@tonic-gate 		if (oldtype == newtype)
86767c478bd9Sstevel@tonic-gate 		{
86777c478bd9Sstevel@tonic-gate 			/*
86787c478bd9Sstevel@tonic-gate 			**  Bail here since we don't know the state of
86797c478bd9Sstevel@tonic-gate 			**  the filesystem and may need to keep tempqf
86807c478bd9Sstevel@tonic-gate 			**  for the user to rescue us.
86817c478bd9Sstevel@tonic-gate 			*/
86827c478bd9Sstevel@tonic-gate 
86837c478bd9Sstevel@tonic-gate 			RELEASE_QUEUE;
86847c478bd9Sstevel@tonic-gate 			errno = save_errno;
86857c478bd9Sstevel@tonic-gate 			syserr("!452 Error renaming control file %s", tempqf);
86867c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
86877c478bd9Sstevel@tonic-gate 		}
86887c478bd9Sstevel@tonic-gate 		else
86897c478bd9Sstevel@tonic-gate 		{
86907c478bd9Sstevel@tonic-gate 			/* remove new file (if rename() half completed) */
86917c478bd9Sstevel@tonic-gate 			if (xunlink(newqf) < 0)
86927c478bd9Sstevel@tonic-gate 			{
86937c478bd9Sstevel@tonic-gate 				save_errno = errno;
86947c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86957c478bd9Sstevel@tonic-gate 						     "Error removing %s: %s\n",
86967c478bd9Sstevel@tonic-gate 						     newqf,
86977c478bd9Sstevel@tonic-gate 						     sm_errstring(save_errno));
86987c478bd9Sstevel@tonic-gate 			}
86997c478bd9Sstevel@tonic-gate 
87007c478bd9Sstevel@tonic-gate 			/* tempqf removed below */
87017c478bd9Sstevel@tonic-gate 			failing = true;
87027c478bd9Sstevel@tonic-gate 		}
87037c478bd9Sstevel@tonic-gate 
87047c478bd9Sstevel@tonic-gate 	}
87057c478bd9Sstevel@tonic-gate 
87067c478bd9Sstevel@tonic-gate 	/* If changing file types, need to remove old type */
87077c478bd9Sstevel@tonic-gate 	if (!failing && oldtype != newtype)
87087c478bd9Sstevel@tonic-gate 	{
87097c478bd9Sstevel@tonic-gate 		if (xunlink(oldqf) < 0)
87107c478bd9Sstevel@tonic-gate 		{
87117c478bd9Sstevel@tonic-gate 			save_errno = errno;
87127c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
87137c478bd9Sstevel@tonic-gate 					     "Error removing %s: %s\n",
87147c478bd9Sstevel@tonic-gate 					     oldqf, sm_errstring(save_errno));
87157c478bd9Sstevel@tonic-gate 		}
87167c478bd9Sstevel@tonic-gate 	}
87177c478bd9Sstevel@tonic-gate 
87187c478bd9Sstevel@tonic-gate 	/* see if anything above failed */
87197c478bd9Sstevel@tonic-gate 	if (failing)
87207c478bd9Sstevel@tonic-gate 	{
87217c478bd9Sstevel@tonic-gate 		/* Something failed: remove new file, old file still there */
87227c478bd9Sstevel@tonic-gate 		(void) xunlink(tempqf);
87237c478bd9Sstevel@tonic-gate 	}
87247c478bd9Sstevel@tonic-gate 
87257c478bd9Sstevel@tonic-gate 	/*
87267c478bd9Sstevel@tonic-gate 	**  fsync() after file operations to make sure metadata is
87277c478bd9Sstevel@tonic-gate 	**  written to disk on filesystems in which renames are
87287c478bd9Sstevel@tonic-gate 	**  not guaranteed.  It's ok if they fail, mail won't be lost.
87297c478bd9Sstevel@tonic-gate 	*/
87307c478bd9Sstevel@tonic-gate 
87317c478bd9Sstevel@tonic-gate 	if (SuperSafe != SAFE_NO)
87327c478bd9Sstevel@tonic-gate 	{
87337c478bd9Sstevel@tonic-gate 		/* for soft-updates */
87347c478bd9Sstevel@tonic-gate 		(void) fsync(sm_io_getinfo(tempqfp,
87357c478bd9Sstevel@tonic-gate 					   SM_IO_WHAT_FD, NULL));
87367c478bd9Sstevel@tonic-gate 
87377c478bd9Sstevel@tonic-gate 		if (!failing)
87387c478bd9Sstevel@tonic-gate 		{
87397c478bd9Sstevel@tonic-gate 			/* for soft-updates */
87407c478bd9Sstevel@tonic-gate 			(void) fsync(sm_io_getinfo(oldqfp,
87417c478bd9Sstevel@tonic-gate 						   SM_IO_WHAT_FD, NULL));
87427c478bd9Sstevel@tonic-gate 		}
87437c478bd9Sstevel@tonic-gate 
87447c478bd9Sstevel@tonic-gate 		/* for other odd filesystems */
87457c478bd9Sstevel@tonic-gate 		SYNC_DIR(tempqf, false);
87467c478bd9Sstevel@tonic-gate 	}
87477c478bd9Sstevel@tonic-gate 
87487c478bd9Sstevel@tonic-gate 	/* Close up shop */
87497c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
87507c478bd9Sstevel@tonic-gate 	if (tempqfp != NULL)
87517c478bd9Sstevel@tonic-gate 		(void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
87527c478bd9Sstevel@tonic-gate 	if (oldqfp != NULL)
87537c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
87547c478bd9Sstevel@tonic-gate 
87557c478bd9Sstevel@tonic-gate 	/* All went well */
87567c478bd9Sstevel@tonic-gate 	return !failing;
87577c478bd9Sstevel@tonic-gate }
87587c478bd9Sstevel@tonic-gate 
87597c478bd9Sstevel@tonic-gate /*
87607c478bd9Sstevel@tonic-gate **  QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
87617c478bd9Sstevel@tonic-gate **
87627c478bd9Sstevel@tonic-gate **	Read all matching queue items, add/remove quarantine
87637c478bd9Sstevel@tonic-gate **	reason, and requeue appropriately.
87647c478bd9Sstevel@tonic-gate **
87657c478bd9Sstevel@tonic-gate **	Parameters:
87667c478bd9Sstevel@tonic-gate **		reason -- quarantine reason, "." means unquarantine.
87677c478bd9Sstevel@tonic-gate **		qgrplimit -- limit to single queue group unless NOQGRP
87687c478bd9Sstevel@tonic-gate **
87697c478bd9Sstevel@tonic-gate **	Results:
87707c478bd9Sstevel@tonic-gate **		none.
87717c478bd9Sstevel@tonic-gate **
87727c478bd9Sstevel@tonic-gate **	Side Effects:
87737c478bd9Sstevel@tonic-gate **		Lots of changes to the queue.
87747c478bd9Sstevel@tonic-gate */
87757c478bd9Sstevel@tonic-gate 
87767c478bd9Sstevel@tonic-gate void
87777c478bd9Sstevel@tonic-gate quarantine_queue(reason, qgrplimit)
87787c478bd9Sstevel@tonic-gate 	char *reason;
87797c478bd9Sstevel@tonic-gate 	int qgrplimit;
87807c478bd9Sstevel@tonic-gate {
87817c478bd9Sstevel@tonic-gate 	int changed = 0;
87827c478bd9Sstevel@tonic-gate 	int qgrp;
87837c478bd9Sstevel@tonic-gate 
87847c478bd9Sstevel@tonic-gate 	/* Convert internal representation of unquarantine */
87857c478bd9Sstevel@tonic-gate 	if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
87867c478bd9Sstevel@tonic-gate 		reason = NULL;
87877c478bd9Sstevel@tonic-gate 
87887c478bd9Sstevel@tonic-gate 	if (reason != NULL)
87897c478bd9Sstevel@tonic-gate 	{
87907c478bd9Sstevel@tonic-gate 		/* clean it */
87917c478bd9Sstevel@tonic-gate 		reason = newstr(denlstring(reason, true, true));
87927c478bd9Sstevel@tonic-gate 	}
87937c478bd9Sstevel@tonic-gate 
87947c478bd9Sstevel@tonic-gate 	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
87957c478bd9Sstevel@tonic-gate 	{
87967c478bd9Sstevel@tonic-gate 		int qdir;
87977c478bd9Sstevel@tonic-gate 
87987c478bd9Sstevel@tonic-gate 		if (qgrplimit != NOQGRP && qgrplimit != qgrp)
87997c478bd9Sstevel@tonic-gate 			continue;
88007c478bd9Sstevel@tonic-gate 
88017c478bd9Sstevel@tonic-gate 		for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
88027c478bd9Sstevel@tonic-gate 		{
88037c478bd9Sstevel@tonic-gate 			int i;
88047c478bd9Sstevel@tonic-gate 			int nrequests;
88057c478bd9Sstevel@tonic-gate 
88067c478bd9Sstevel@tonic-gate 			if (StopRequest)
88077c478bd9Sstevel@tonic-gate 				stop_sendmail();
88087c478bd9Sstevel@tonic-gate 
88097c478bd9Sstevel@tonic-gate 			nrequests = gatherq(qgrp, qdir, true, NULL, NULL);
88107c478bd9Sstevel@tonic-gate 
88117c478bd9Sstevel@tonic-gate 			/* first see if there is anything */
88127c478bd9Sstevel@tonic-gate 			if (nrequests <= 0)
88137c478bd9Sstevel@tonic-gate 			{
88147c478bd9Sstevel@tonic-gate 				if (Verbose)
88157c478bd9Sstevel@tonic-gate 				{
88167c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
88177c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT, "%s: no matches\n",
88187c478bd9Sstevel@tonic-gate 							     qid_printqueue(qgrp, qdir));
88197c478bd9Sstevel@tonic-gate 				}
88207c478bd9Sstevel@tonic-gate 				continue;
88217c478bd9Sstevel@tonic-gate 			}
88227c478bd9Sstevel@tonic-gate 
88237c478bd9Sstevel@tonic-gate 			if (Verbose)
88247c478bd9Sstevel@tonic-gate 			{
88257c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout,
88267c478bd9Sstevel@tonic-gate 						     SM_TIME_DEFAULT, "Processing %s:\n",
88277c478bd9Sstevel@tonic-gate 						     qid_printqueue(qgrp, qdir));
88287c478bd9Sstevel@tonic-gate 			}
88297c478bd9Sstevel@tonic-gate 
88307c478bd9Sstevel@tonic-gate 			for (i = 0; i < WorkListCount; i++)
88317c478bd9Sstevel@tonic-gate 			{
88327c478bd9Sstevel@tonic-gate 				ENVELOPE e;
88337c478bd9Sstevel@tonic-gate 
88347c478bd9Sstevel@tonic-gate 				if (StopRequest)
88357c478bd9Sstevel@tonic-gate 					stop_sendmail();
88367c478bd9Sstevel@tonic-gate 
88377c478bd9Sstevel@tonic-gate 				/* setup envelope */
88387c478bd9Sstevel@tonic-gate 				clearenvelope(&e, true, sm_rpool_new_x(NULL));
88397c478bd9Sstevel@tonic-gate 				e.e_id = WorkList[i].w_name + 2;
88407c478bd9Sstevel@tonic-gate 				e.e_qgrp = qgrp;
88417c478bd9Sstevel@tonic-gate 				e.e_qdir = qdir;
88427c478bd9Sstevel@tonic-gate 
88437c478bd9Sstevel@tonic-gate 				if (tTd(70, 101))
88447c478bd9Sstevel@tonic-gate 				{
88457c478bd9Sstevel@tonic-gate 					sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88467c478bd9Sstevel@tonic-gate 						      "Would do %s\n", e.e_id);
88477c478bd9Sstevel@tonic-gate 					changed++;
88487c478bd9Sstevel@tonic-gate 				}
88497c478bd9Sstevel@tonic-gate 				else if (quarantine_queue_item(qgrp, qdir,
88507c478bd9Sstevel@tonic-gate 							       &e, reason))
88517c478bd9Sstevel@tonic-gate 					changed++;
88527c478bd9Sstevel@tonic-gate 
88537c478bd9Sstevel@tonic-gate 				/* clean up */
88547c478bd9Sstevel@tonic-gate 				sm_rpool_free(e.e_rpool);
88557c478bd9Sstevel@tonic-gate 				e.e_rpool = NULL;
88567c478bd9Sstevel@tonic-gate 			}
88577c478bd9Sstevel@tonic-gate 			if (WorkList != NULL)
88587c478bd9Sstevel@tonic-gate 				sm_free(WorkList); /* XXX */
88597c478bd9Sstevel@tonic-gate 			WorkList = NULL;
88607c478bd9Sstevel@tonic-gate 			WorkListSize = 0;
88617c478bd9Sstevel@tonic-gate 			WorkListCount = 0;
88627c478bd9Sstevel@tonic-gate 		}
88637c478bd9Sstevel@tonic-gate 	}
88647c478bd9Sstevel@tonic-gate 	if (Verbose)
88657c478bd9Sstevel@tonic-gate 	{
88667c478bd9Sstevel@tonic-gate 		if (changed == 0)
88677c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88687c478bd9Sstevel@tonic-gate 					     "No changes\n");
88697c478bd9Sstevel@tonic-gate 		else
88707c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
88717c478bd9Sstevel@tonic-gate 					     "%d change%s\n",
88727c478bd9Sstevel@tonic-gate 					     changed,
88737c478bd9Sstevel@tonic-gate 					     changed == 1 ? "" : "s");
88747c478bd9Sstevel@tonic-gate 	}
88757c478bd9Sstevel@tonic-gate }
8876