xref: /illumos-gate/usr/src/cmd/sendmail/src/queue.c (revision 55fea89dcaa64928bed4327112404dcb3e07b79f)
17c478bd9Sstevel@tonic-gate /*
2e9af4bc0SJohn Beck  * Copyright (c) 1998-2009 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 #include <sendmail.h>
157c478bd9Sstevel@tonic-gate #include <sm/sem.h>
167c478bd9Sstevel@tonic-gate 
17e9af4bc0SJohn Beck SM_RCSID("@(#)$Id: queue.c,v 8.987 2009/12/18 17:08:01 ca Exp $")
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate #include <dirent.h>
207c478bd9Sstevel@tonic-gate 
217c478bd9Sstevel@tonic-gate # define RELEASE_QUEUE	(void) 0
227c478bd9Sstevel@tonic-gate # define ST_INODE(st)	(st).st_ino
237c478bd9Sstevel@tonic-gate 
247c478bd9Sstevel@tonic-gate #  define sm_file_exists(errno) ((errno) == EEXIST)
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate # if HASFLOCK && defined(O_EXLOCK)
277c478bd9Sstevel@tonic-gate #   define SM_OPEN_EXLOCK 1
287c478bd9Sstevel@tonic-gate #   define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK)
297c478bd9Sstevel@tonic-gate # else /* HASFLOCK && defined(O_EXLOCK) */
307c478bd9Sstevel@tonic-gate #  define TF_OPEN_FLAGS (O_CREAT|O_WRONLY|O_EXCL)
317c478bd9Sstevel@tonic-gate # endif /* HASFLOCK && defined(O_EXLOCK) */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #ifndef SM_OPEN_EXLOCK
347c478bd9Sstevel@tonic-gate # define SM_OPEN_EXLOCK 0
357c478bd9Sstevel@tonic-gate #endif /* ! SM_OPEN_EXLOCK */
367c478bd9Sstevel@tonic-gate 
377c478bd9Sstevel@tonic-gate /*
387c478bd9Sstevel@tonic-gate **  Historical notes:
397c478bd9Sstevel@tonic-gate **	QF_VERSION == 4 was sendmail 8.10/8.11 without _FFR_QUEUEDELAY
407c478bd9Sstevel@tonic-gate **	QF_VERSION == 5 was sendmail 8.10/8.11 with    _FFR_QUEUEDELAY
417c478bd9Sstevel@tonic-gate **	QF_VERSION == 6 was sendmail 8.12      without _FFR_QUEUEDELAY
427c478bd9Sstevel@tonic-gate **	QF_VERSION == 7 was sendmail 8.12      with    _FFR_QUEUEDELAY
437c478bd9Sstevel@tonic-gate **	QF_VERSION == 8 is  sendmail 8.13
447c478bd9Sstevel@tonic-gate */
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #define QF_VERSION	8	/* version number of this queue format */
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate static char	queue_letter __P((ENVELOPE *, int));
497c478bd9Sstevel@tonic-gate static bool	quarantine_queue_item __P((int, int, ENVELOPE *, char *));
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate /* Naming convention: qgrp: index of queue group, qg: QUEUEGROUP */
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate **  Work queue.
557c478bd9Sstevel@tonic-gate */
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate struct work
587c478bd9Sstevel@tonic-gate {
597c478bd9Sstevel@tonic-gate 	char		*w_name;	/* name of control file */
607c478bd9Sstevel@tonic-gate 	char		*w_host;	/* name of recipient host */
617c478bd9Sstevel@tonic-gate 	bool		w_lock;		/* is message locked? */
627c478bd9Sstevel@tonic-gate 	bool		w_tooyoung;	/* is it too young to run? */
637c478bd9Sstevel@tonic-gate 	long		w_pri;		/* priority of message, see below */
647c478bd9Sstevel@tonic-gate 	time_t		w_ctime;	/* creation time */
657c478bd9Sstevel@tonic-gate 	time_t		w_mtime;	/* modification time */
667c478bd9Sstevel@tonic-gate 	int		w_qgrp;		/* queue group located in */
677c478bd9Sstevel@tonic-gate 	int		w_qdir;		/* queue directory located in */
687c478bd9Sstevel@tonic-gate 	struct work	*w_next;	/* next in queue */
697c478bd9Sstevel@tonic-gate };
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate typedef struct work	WORK;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate static WORK	*WorkQ;		/* queue of things to be done */
747c478bd9Sstevel@tonic-gate static int	NumWorkGroups;	/* number of work groups */
757c478bd9Sstevel@tonic-gate static time_t	Current_LA_time = 0;
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate /* Get new load average every 30 seconds. */
787c478bd9Sstevel@tonic-gate #define GET_NEW_LA_TIME	30
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate #define SM_GET_LA(now)	\
817c478bd9Sstevel@tonic-gate 	do							\
827c478bd9Sstevel@tonic-gate 	{							\
837c478bd9Sstevel@tonic-gate 		now = curtime();				\
847c478bd9Sstevel@tonic-gate 		if (Current_LA_time < now - GET_NEW_LA_TIME)	\
857c478bd9Sstevel@tonic-gate 		{						\
867c478bd9Sstevel@tonic-gate 			sm_getla();				\
877c478bd9Sstevel@tonic-gate 			Current_LA_time = now;			\
887c478bd9Sstevel@tonic-gate 		}						\
897c478bd9Sstevel@tonic-gate 	} while (0)
907c478bd9Sstevel@tonic-gate 
917c478bd9Sstevel@tonic-gate /*
927c478bd9Sstevel@tonic-gate **  DoQueueRun indicates that a queue run is needed.
937c478bd9Sstevel@tonic-gate **	Notice: DoQueueRun is modified in a signal handler!
947c478bd9Sstevel@tonic-gate */
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate static bool	volatile DoQueueRun; /* non-interrupt time queue run needed */
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate /*
997c478bd9Sstevel@tonic-gate **  Work group definition structure.
1007c478bd9Sstevel@tonic-gate **	Each work group contains one or more queue groups. This is done
1017c478bd9Sstevel@tonic-gate **	to manage the number of queue group runners active at the same time
1027c478bd9Sstevel@tonic-gate **	to be within the constraints of MaxQueueChildren (if it is set).
1037c478bd9Sstevel@tonic-gate **	The number of queue groups that can be run on the next work run
1047c478bd9Sstevel@tonic-gate **	is kept track of. The queue groups are run in a round robin.
1057c478bd9Sstevel@tonic-gate */
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate struct workgrp
1087c478bd9Sstevel@tonic-gate {
1097c478bd9Sstevel@tonic-gate 	int		wg_numqgrp;	/* number of queue groups in work grp */
1107c478bd9Sstevel@tonic-gate 	int		wg_runners;	/* total runners */
1117c478bd9Sstevel@tonic-gate 	int		wg_curqgrp;	/* current queue group */
1127c478bd9Sstevel@tonic-gate 	QUEUEGRP	**wg_qgs;	/* array of queue groups */
1137c478bd9Sstevel@tonic-gate 	int		wg_maxact;	/* max # of active runners */
1147c478bd9Sstevel@tonic-gate 	time_t		wg_lowqintvl;	/* lowest queue interval */
1157c478bd9Sstevel@tonic-gate 	int		wg_restart;	/* needs restarting? */
1167c478bd9Sstevel@tonic-gate 	int		wg_restartcnt;	/* count of times restarted */
1177c478bd9Sstevel@tonic-gate };
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate typedef struct workgrp WORKGRP;
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate static WORKGRP	volatile WorkGrp[MAXWORKGROUPS + 1];	/* work groups */
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
1247c478bd9Sstevel@tonic-gate static SM_DEBUG_T DebugLeakQ = SM_DEBUG_INITIALIZER("leak_q",
1257c478bd9Sstevel@tonic-gate 	"@(#)$Debug: leak_q - trace memory leaks during queue processing $");
1267c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate /*
1297c478bd9Sstevel@tonic-gate **  We use EmptyString instead of "" to avoid
1307c478bd9Sstevel@tonic-gate **  'zero-length format string' warnings from gcc
1317c478bd9Sstevel@tonic-gate */
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate static const char EmptyString[] = "";
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate static void	grow_wlist __P((int, int));
1367c478bd9Sstevel@tonic-gate static int	multiqueue_cache __P((char *, int, QUEUEGRP *, int, unsigned int *));
137e9af4bc0SJohn Beck static int	gatherq __P((int, int, bool, bool *, bool *, int *));
1387c478bd9Sstevel@tonic-gate static int	sortq __P((int));
1397c478bd9Sstevel@tonic-gate static void	printctladdr __P((ADDRESS *, SM_FILE_T *));
1407c478bd9Sstevel@tonic-gate static bool	readqf __P((ENVELOPE *, bool));
1417c478bd9Sstevel@tonic-gate static void	restart_work_group __P((int));
1427c478bd9Sstevel@tonic-gate static void	runner_work __P((ENVELOPE *, int, bool, int, int));
1437c478bd9Sstevel@tonic-gate static void	schedule_queue_runs __P((bool, int, bool));
1447c478bd9Sstevel@tonic-gate static char	*strrev __P((char *));
1457c478bd9Sstevel@tonic-gate static ADDRESS	*setctluser __P((char *, int, ENVELOPE *));
1467c478bd9Sstevel@tonic-gate #if _FFR_RHS
1477c478bd9Sstevel@tonic-gate static int	sm_strshufflecmp __P((char *, char *));
1487c478bd9Sstevel@tonic-gate static void	init_shuffle_alphabet __P(());
1497c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
150058561cbSjbeck 
151058561cbSjbeck /*
152058561cbSjbeck **  Note: workcmpf?() don't use a prototype because it will cause a conflict
153058561cbSjbeck **  with the qsort() call (which expects something like
154058561cbSjbeck **  int (*compar)(const void *, const void *), not (WORK *, WORK *))
155058561cbSjbeck */
156058561cbSjbeck 
1577c478bd9Sstevel@tonic-gate static int	workcmpf0();
1587c478bd9Sstevel@tonic-gate static int	workcmpf1();
1597c478bd9Sstevel@tonic-gate static int	workcmpf2();
1607c478bd9Sstevel@tonic-gate static int	workcmpf3();
1617c478bd9Sstevel@tonic-gate static int	workcmpf4();
1627c478bd9Sstevel@tonic-gate static int	randi = 3;	/* index for workcmpf5() */
1637c478bd9Sstevel@tonic-gate static int	workcmpf5();
1647c478bd9Sstevel@tonic-gate static int	workcmpf6();
1657c478bd9Sstevel@tonic-gate #if _FFR_RHS
1667c478bd9Sstevel@tonic-gate static int	workcmpf7();
1677c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate #if RANDOMSHIFT
1707c478bd9Sstevel@tonic-gate # define get_rand_mod(m)	((get_random() >> RANDOMSHIFT) % (m))
1717c478bd9Sstevel@tonic-gate #else /* RANDOMSHIFT */
1727c478bd9Sstevel@tonic-gate # define get_rand_mod(m)	(get_random() % (m))
1737c478bd9Sstevel@tonic-gate #endif /* RANDOMSHIFT */
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate /*
1767c478bd9Sstevel@tonic-gate **  File system definition.
1777c478bd9Sstevel@tonic-gate **	Used to keep track of how much free space is available
1787c478bd9Sstevel@tonic-gate **	on a file system in which one or more queue directories reside.
1797c478bd9Sstevel@tonic-gate */
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate typedef struct filesys_shared	FILESYS;
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate struct filesys_shared
1847c478bd9Sstevel@tonic-gate {
1857c478bd9Sstevel@tonic-gate 	dev_t	fs_dev;		/* unique device id */
1867c478bd9Sstevel@tonic-gate 	long	fs_avail;	/* number of free blocks available */
1877c478bd9Sstevel@tonic-gate 	long	fs_blksize;	/* block size, in bytes */
1887c478bd9Sstevel@tonic-gate };
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate /* probably kept in shared memory */
1917c478bd9Sstevel@tonic-gate static FILESYS	FileSys[MAXFILESYS];	/* queue file systems */
192058561cbSjbeck static const char *FSPath[MAXFILESYS];	/* pathnames for file systems */
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate /*
1977c478bd9Sstevel@tonic-gate **  Shared memory data
1987c478bd9Sstevel@tonic-gate **
1997c478bd9Sstevel@tonic-gate **  Current layout:
2007c478bd9Sstevel@tonic-gate **	size -- size of shared memory segment
2017c478bd9Sstevel@tonic-gate **	pid -- pid of owner, should be a unique id to avoid misinterpretations
2027c478bd9Sstevel@tonic-gate **		by other processes.
2037c478bd9Sstevel@tonic-gate **	tag -- should be a unique id to avoid misinterpretations by others.
2047c478bd9Sstevel@tonic-gate **		idea: hash over configuration data that will be stored here.
2057c478bd9Sstevel@tonic-gate **	NumFileSys -- number of file systems.
2067c478bd9Sstevel@tonic-gate **	FileSys -- (arrary of) structure for used file systems.
2077c478bd9Sstevel@tonic-gate **	RSATmpCnt -- counter for number of uses of ephemeral RSA key.
2087c478bd9Sstevel@tonic-gate **	QShm -- (array of) structure for information about queue directories.
2097c478bd9Sstevel@tonic-gate */
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate /*
2127c478bd9Sstevel@tonic-gate **  Queue data in shared memory
2137c478bd9Sstevel@tonic-gate */
2147c478bd9Sstevel@tonic-gate 
2157c478bd9Sstevel@tonic-gate typedef struct queue_shared	QUEUE_SHM_T;
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate struct queue_shared
2187c478bd9Sstevel@tonic-gate {
2197c478bd9Sstevel@tonic-gate 	int	qs_entries;	/* number of entries */
2207c478bd9Sstevel@tonic-gate 	/* XXX more to follow? */
2217c478bd9Sstevel@tonic-gate };
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate static void	*Pshm;		/* pointer to shared memory */
2247c478bd9Sstevel@tonic-gate static FILESYS	*PtrFileSys;	/* pointer to queue file system array */
2257c478bd9Sstevel@tonic-gate int		ShmId = SM_SHM_NO_ID;	/* shared memory id */
2267c478bd9Sstevel@tonic-gate static QUEUE_SHM_T	*QShm;		/* pointer to shared queue data */
2277c478bd9Sstevel@tonic-gate static size_t shms;
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate # define SHM_OFF_PID(p)	(((char *) (p)) + sizeof(int))
2307c478bd9Sstevel@tonic-gate # define SHM_OFF_TAG(p)	(((char *) (p)) + sizeof(pid_t) + sizeof(int))
2317c478bd9Sstevel@tonic-gate # define SHM_OFF_HEAD	(sizeof(pid_t) + sizeof(int) * 2)
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate /* how to access FileSys */
2347c478bd9Sstevel@tonic-gate # define FILE_SYS(i)	(PtrFileSys[i])
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate /* first entry is a tag, for now just the size */
2377c478bd9Sstevel@tonic-gate # define OFF_FILE_SYS(p)	(((char *) (p)) + SHM_OFF_HEAD)
2387c478bd9Sstevel@tonic-gate 
2397c478bd9Sstevel@tonic-gate /* offset for PNumFileSys */
2407c478bd9Sstevel@tonic-gate # define OFF_NUM_FILE_SYS(p)	(((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys))
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate /* offset for PRSATmpCnt */
2437c478bd9Sstevel@tonic-gate # define OFF_RSA_TMP_CNT(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int))
2447c478bd9Sstevel@tonic-gate int	*PRSATmpCnt;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate /* offset for queue_shm */
2477c478bd9Sstevel@tonic-gate # define OFF_QUEUE_SHM(p) (((char *) (p)) + SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate # define QSHM_ENTRIES(i)	QShm[i].qs_entries
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate /* basic size of shared memory segment */
2527c478bd9Sstevel@tonic-gate # define SM_T_SIZE	(SHM_OFF_HEAD + sizeof(FileSys) + sizeof(int) * 2)
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate static unsigned int	hash_q __P((char *, unsigned int));
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate /*
2577c478bd9Sstevel@tonic-gate **  HASH_Q -- simple hash function
2587c478bd9Sstevel@tonic-gate **
2597c478bd9Sstevel@tonic-gate **	Parameters:
2607c478bd9Sstevel@tonic-gate **		p -- string to hash.
2617c478bd9Sstevel@tonic-gate **		h -- hash start value (from previous run).
2627c478bd9Sstevel@tonic-gate **
2637c478bd9Sstevel@tonic-gate **	Returns:
2647c478bd9Sstevel@tonic-gate **		hash value.
2657c478bd9Sstevel@tonic-gate */
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate static unsigned int
hash_q(p,h)2687c478bd9Sstevel@tonic-gate hash_q(p, h)
2697c478bd9Sstevel@tonic-gate 	char *p;
2707c478bd9Sstevel@tonic-gate 	unsigned int h;
2717c478bd9Sstevel@tonic-gate {
2727c478bd9Sstevel@tonic-gate 	int c, d;
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	while (*p != '\0')
2757c478bd9Sstevel@tonic-gate 	{
2767c478bd9Sstevel@tonic-gate 		d = *p++;
2777c478bd9Sstevel@tonic-gate 		c = d;
2787c478bd9Sstevel@tonic-gate 		c ^= c<<6;
2797c478bd9Sstevel@tonic-gate 		h += (c<<11) ^ (c>>1);
2807c478bd9Sstevel@tonic-gate 		h ^= (d<<14) + (d<<7) + (d<<4) + d;
2817c478bd9Sstevel@tonic-gate 	}
2827c478bd9Sstevel@tonic-gate 	return h;
2837c478bd9Sstevel@tonic-gate }
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
2877c478bd9Sstevel@tonic-gate # define FILE_SYS(i)	FileSys[i]
2887c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
2897c478bd9Sstevel@tonic-gate 
2907c478bd9Sstevel@tonic-gate /* access to the various components of file system data */
2917c478bd9Sstevel@tonic-gate #define FILE_SYS_NAME(i)	FSPath[i]
2927c478bd9Sstevel@tonic-gate #define FILE_SYS_AVAIL(i)	FILE_SYS(i).fs_avail
2937c478bd9Sstevel@tonic-gate #define FILE_SYS_BLKSIZE(i)	FILE_SYS(i).fs_blksize
2947c478bd9Sstevel@tonic-gate #define FILE_SYS_DEV(i)	FILE_SYS(i).fs_dev
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate /*
2987c478bd9Sstevel@tonic-gate **  Current qf file field assignments:
2997c478bd9Sstevel@tonic-gate **
3007c478bd9Sstevel@tonic-gate **	A	AUTH= parameter
3017c478bd9Sstevel@tonic-gate **	B	body type
3027c478bd9Sstevel@tonic-gate **	C	controlling user
3037c478bd9Sstevel@tonic-gate **	D	data file name
3047c478bd9Sstevel@tonic-gate **	d	data file directory name (added in 8.12)
3057c478bd9Sstevel@tonic-gate **	E	error recipient
3067c478bd9Sstevel@tonic-gate **	F	flag bits
3077c478bd9Sstevel@tonic-gate **	G	free (was: queue delay algorithm if _FFR_QUEUEDELAY)
3087c478bd9Sstevel@tonic-gate **	H	header
3097c478bd9Sstevel@tonic-gate **	I	data file's inode number
3107c478bd9Sstevel@tonic-gate **	K	time of last delivery attempt
3117c478bd9Sstevel@tonic-gate **	L	Solaris Content-Length: header (obsolete)
3127c478bd9Sstevel@tonic-gate **	M	message
3137c478bd9Sstevel@tonic-gate **	N	number of delivery attempts
3147c478bd9Sstevel@tonic-gate **	P	message priority
3157c478bd9Sstevel@tonic-gate **	q	quarantine reason
3167c478bd9Sstevel@tonic-gate **	Q	original recipient (ORCPT=)
3177c478bd9Sstevel@tonic-gate **	r	final recipient (Final-Recipient: DSN field)
3187c478bd9Sstevel@tonic-gate **	R	recipient
3197c478bd9Sstevel@tonic-gate **	S	sender
3207c478bd9Sstevel@tonic-gate **	T	init time
3217c478bd9Sstevel@tonic-gate **	V	queue file version
3227c478bd9Sstevel@tonic-gate **	X	free (was: character set if _FFR_SAVE_CHARSET)
3237c478bd9Sstevel@tonic-gate **	Y	free (was: current delay if _FFR_QUEUEDELAY)
3247c478bd9Sstevel@tonic-gate **	Z	original envelope id from ESMTP
3257c478bd9Sstevel@tonic-gate **	!	deliver by (added in 8.12)
3267c478bd9Sstevel@tonic-gate **	$	define macro
3277c478bd9Sstevel@tonic-gate **	.	terminate file
3287c478bd9Sstevel@tonic-gate */
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate /*
3317c478bd9Sstevel@tonic-gate **  QUEUEUP -- queue a message up for future transmission.
3327c478bd9Sstevel@tonic-gate **
3337c478bd9Sstevel@tonic-gate **	Parameters:
3347c478bd9Sstevel@tonic-gate **		e -- the envelope to queue up.
3357c478bd9Sstevel@tonic-gate **		announce -- if true, tell when you are queueing up.
3367c478bd9Sstevel@tonic-gate **		msync -- if true, then fsync() if SuperSafe interactive mode.
3377c478bd9Sstevel@tonic-gate **
3387c478bd9Sstevel@tonic-gate **	Returns:
3397c478bd9Sstevel@tonic-gate **		none.
3407c478bd9Sstevel@tonic-gate **
3417c478bd9Sstevel@tonic-gate **	Side Effects:
3427c478bd9Sstevel@tonic-gate **		The current request is saved in a control file.
3437c478bd9Sstevel@tonic-gate **		The queue file is left locked.
3447c478bd9Sstevel@tonic-gate */
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate void
queueup(e,announce,msync)3477c478bd9Sstevel@tonic-gate queueup(e, announce, msync)
3487c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
3497c478bd9Sstevel@tonic-gate 	bool announce;
3507c478bd9Sstevel@tonic-gate 	bool msync;
3517c478bd9Sstevel@tonic-gate {
3527c478bd9Sstevel@tonic-gate 	register SM_FILE_T *tfp;
3537c478bd9Sstevel@tonic-gate 	register HDR *h;
3547c478bd9Sstevel@tonic-gate 	register ADDRESS *q;
3557c478bd9Sstevel@tonic-gate 	int tfd = -1;
3567c478bd9Sstevel@tonic-gate 	int i;
3577c478bd9Sstevel@tonic-gate 	bool newid;
3587c478bd9Sstevel@tonic-gate 	register char *p;
3597c478bd9Sstevel@tonic-gate 	MAILER nullmailer;
3607c478bd9Sstevel@tonic-gate 	MCI mcibuf;
3617c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
3627c478bd9Sstevel@tonic-gate 	char tf[MAXPATHLEN];
3637c478bd9Sstevel@tonic-gate 	char df[MAXPATHLEN];
3647c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	/*
3677c478bd9Sstevel@tonic-gate 	**  Create control file.
3687c478bd9Sstevel@tonic-gate 	*/
3697c478bd9Sstevel@tonic-gate 
3707c478bd9Sstevel@tonic-gate #define OPEN_TF	do							\
3717c478bd9Sstevel@tonic-gate 		{							\
3727c478bd9Sstevel@tonic-gate 			MODE_T oldumask = 0;				\
3737c478bd9Sstevel@tonic-gate 									\
3747c478bd9Sstevel@tonic-gate 			if (bitset(S_IWGRP, QueueFileMode))		\
3757c478bd9Sstevel@tonic-gate 				oldumask = umask(002);			\
3767c478bd9Sstevel@tonic-gate 			tfd = open(tf, TF_OPEN_FLAGS, QueueFileMode);	\
3777c478bd9Sstevel@tonic-gate 			if (bitset(S_IWGRP, QueueFileMode))		\
3787c478bd9Sstevel@tonic-gate 				(void) umask(oldumask);			\
3797c478bd9Sstevel@tonic-gate 		} while (0)
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	newid = (e->e_id == NULL) || !bitset(EF_INQUEUE, e->e_flags);
383058561cbSjbeck 	(void) sm_strlcpy(tf, queuename(e, NEWQFL_LETTER), sizeof(tf));
3847c478bd9Sstevel@tonic-gate 	tfp = e->e_lockfp;
3857c478bd9Sstevel@tonic-gate 	if (tfp == NULL && newid)
3867c478bd9Sstevel@tonic-gate 	{
3877c478bd9Sstevel@tonic-gate 		/*
3887c478bd9Sstevel@tonic-gate 		**  open qf file directly: this will give an error if the file
3897c478bd9Sstevel@tonic-gate 		**  already exists and hence prevent problems if a queue-id
3907c478bd9Sstevel@tonic-gate 		**  is reused (e.g., because the clock is set back).
3917c478bd9Sstevel@tonic-gate 		*/
3927c478bd9Sstevel@tonic-gate 
393058561cbSjbeck 		(void) sm_strlcpy(tf, queuename(e, ANYQFL_LETTER), sizeof(tf));
3947c478bd9Sstevel@tonic-gate 		OPEN_TF;
3957c478bd9Sstevel@tonic-gate 		if (tfd < 0 ||
3967c478bd9Sstevel@tonic-gate #if !SM_OPEN_EXLOCK
3977c478bd9Sstevel@tonic-gate 		    !lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB) ||
3987c478bd9Sstevel@tonic-gate #endif /* !SM_OPEN_EXLOCK */
3997c478bd9Sstevel@tonic-gate 		    (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
4007c478bd9Sstevel@tonic-gate 					 (void *) &tfd, SM_IO_WRONLY,
4017c478bd9Sstevel@tonic-gate 					 NULL)) == NULL)
4027c478bd9Sstevel@tonic-gate 		{
4037c478bd9Sstevel@tonic-gate 			int save_errno = errno;
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 			printopenfds(true);
4067c478bd9Sstevel@tonic-gate 			errno = save_errno;
4077c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create queue file %s, euid=%d, fd=%d, fp=%p",
4087c478bd9Sstevel@tonic-gate 				tf, (int) geteuid(), tfd, tfp);
4097c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4107c478bd9Sstevel@tonic-gate 		}
4117c478bd9Sstevel@tonic-gate 		e->e_lockfp = tfp;
4127c478bd9Sstevel@tonic-gate 		upd_qs(e, 1, 0, "queueup");
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 	/* if newid, write the queue file directly (instead of temp file) */
4167c478bd9Sstevel@tonic-gate 	if (!newid)
4177c478bd9Sstevel@tonic-gate 	{
4187c478bd9Sstevel@tonic-gate 		/* get a locked tf file */
4197c478bd9Sstevel@tonic-gate 		for (i = 0; i < 128; i++)
4207c478bd9Sstevel@tonic-gate 		{
4217c478bd9Sstevel@tonic-gate 			if (tfd < 0)
4227c478bd9Sstevel@tonic-gate 			{
4237c478bd9Sstevel@tonic-gate 				OPEN_TF;
4247c478bd9Sstevel@tonic-gate 				if (tfd < 0)
4257c478bd9Sstevel@tonic-gate 				{
4267c478bd9Sstevel@tonic-gate 					if (errno != EEXIST)
4277c478bd9Sstevel@tonic-gate 						break;
4287c478bd9Sstevel@tonic-gate 					if (LogLevel > 0 && (i % 32) == 0)
4297c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ALERT, e->e_id,
4307800901eSjbeck 							  "queueup: cannot create %s, euid=%d: %s",
4317c478bd9Sstevel@tonic-gate 							  tf, (int) geteuid(),
4327c478bd9Sstevel@tonic-gate 							  sm_errstring(errno));
4337c478bd9Sstevel@tonic-gate 				}
4347c478bd9Sstevel@tonic-gate #if SM_OPEN_EXLOCK
4357c478bd9Sstevel@tonic-gate 				else
4367c478bd9Sstevel@tonic-gate 					break;
4377c478bd9Sstevel@tonic-gate #endif /* SM_OPEN_EXLOCK */
4387c478bd9Sstevel@tonic-gate 			}
4397c478bd9Sstevel@tonic-gate 			if (tfd >= 0)
4407c478bd9Sstevel@tonic-gate 			{
4417c478bd9Sstevel@tonic-gate #if SM_OPEN_EXLOCK
4427c478bd9Sstevel@tonic-gate 				/* file is locked by open() */
4437c478bd9Sstevel@tonic-gate 				break;
4447c478bd9Sstevel@tonic-gate #else /* SM_OPEN_EXLOCK */
4457c478bd9Sstevel@tonic-gate 				if (lockfile(tfd, tf, NULL, LOCK_EX|LOCK_NB))
4467c478bd9Sstevel@tonic-gate 					break;
4477c478bd9Sstevel@tonic-gate 				else
4487c478bd9Sstevel@tonic-gate #endif /* SM_OPEN_EXLOCK */
4497c478bd9Sstevel@tonic-gate 				if (LogLevel > 0 && (i % 32) == 0)
4507c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ALERT, e->e_id,
4517c478bd9Sstevel@tonic-gate 						  "queueup: cannot lock %s: %s",
4527c478bd9Sstevel@tonic-gate 						  tf, sm_errstring(errno));
4537c478bd9Sstevel@tonic-gate 				if ((i % 32) == 31)
4547c478bd9Sstevel@tonic-gate 				{
4557c478bd9Sstevel@tonic-gate 					(void) close(tfd);
4567c478bd9Sstevel@tonic-gate 					tfd = -1;
4577c478bd9Sstevel@tonic-gate 				}
4587c478bd9Sstevel@tonic-gate 			}
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 			if ((i % 32) == 31)
4617c478bd9Sstevel@tonic-gate 			{
4627c478bd9Sstevel@tonic-gate 				/* save the old temp file away */
4637c478bd9Sstevel@tonic-gate 				(void) rename(tf, queuename(e, TEMPQF_LETTER));
4647c478bd9Sstevel@tonic-gate 			}
4657c478bd9Sstevel@tonic-gate 			else
4667c478bd9Sstevel@tonic-gate 				(void) sleep(i % 32);
4677c478bd9Sstevel@tonic-gate 		}
4687c478bd9Sstevel@tonic-gate 		if (tfd < 0 || (tfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
4697c478bd9Sstevel@tonic-gate 						 (void *) &tfd, SM_IO_WRONLY_B,
4707c478bd9Sstevel@tonic-gate 						 NULL)) == NULL)
4717c478bd9Sstevel@tonic-gate 		{
4727c478bd9Sstevel@tonic-gate 			int save_errno = errno;
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 			printopenfds(true);
4757c478bd9Sstevel@tonic-gate 			errno = save_errno;
4767c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create queue temp file %s, uid=%d",
4777c478bd9Sstevel@tonic-gate 				tf, (int) geteuid());
4787c478bd9Sstevel@tonic-gate 		}
4797c478bd9Sstevel@tonic-gate 	}
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
4827c478bd9Sstevel@tonic-gate 		sm_dprintf("\n>>>>> queueing %s/%s%s >>>>>\n",
4837c478bd9Sstevel@tonic-gate 			   qid_printqueue(e->e_qgrp, e->e_qdir),
4847c478bd9Sstevel@tonic-gate 			   queuename(e, ANYQFL_LETTER),
4857c478bd9Sstevel@tonic-gate 			   newid ? " (new id)" : "");
4867c478bd9Sstevel@tonic-gate 	if (tTd(40, 3))
4877c478bd9Sstevel@tonic-gate 	{
4887c478bd9Sstevel@tonic-gate 		sm_dprintf("  e_flags=");
4897c478bd9Sstevel@tonic-gate 		printenvflags(e);
4907c478bd9Sstevel@tonic-gate 	}
4917c478bd9Sstevel@tonic-gate 	if (tTd(40, 32))
4927c478bd9Sstevel@tonic-gate 	{
4937c478bd9Sstevel@tonic-gate 		sm_dprintf("  sendq=");
4947c478bd9Sstevel@tonic-gate 		printaddr(sm_debug_file(), e->e_sendqueue, true);
4957c478bd9Sstevel@tonic-gate 	}
4967c478bd9Sstevel@tonic-gate 	if (tTd(40, 9))
4977c478bd9Sstevel@tonic-gate 	{
4987c478bd9Sstevel@tonic-gate 		sm_dprintf("  tfp=");
4997c478bd9Sstevel@tonic-gate 		dumpfd(sm_io_getinfo(tfp, SM_IO_WHAT_FD, NULL), true, false);
5007c478bd9Sstevel@tonic-gate 		sm_dprintf("  lockfp=");
5017c478bd9Sstevel@tonic-gate 		if (e->e_lockfp == NULL)
5027c478bd9Sstevel@tonic-gate 			sm_dprintf("NULL\n");
5037c478bd9Sstevel@tonic-gate 		else
5047c478bd9Sstevel@tonic-gate 			dumpfd(sm_io_getinfo(e->e_lockfp, SM_IO_WHAT_FD, NULL),
5057c478bd9Sstevel@tonic-gate 			       true, false);
5067c478bd9Sstevel@tonic-gate 	}
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	/*
5097c478bd9Sstevel@tonic-gate 	**  If there is no data file yet, create one.
5107c478bd9Sstevel@tonic-gate 	*/
5117c478bd9Sstevel@tonic-gate 
512058561cbSjbeck 	(void) sm_strlcpy(df, queuename(e, DATAFL_LETTER), sizeof(df));
5137c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS_DF, e->e_flags))
5147c478bd9Sstevel@tonic-gate 	{
5157c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5167c478bd9Sstevel@tonic-gate 		    SuperSafe != SAFE_REALLY &&
5177c478bd9Sstevel@tonic-gate 		    SuperSafe != SAFE_REALLY_POSTMILTER &&
5187c478bd9Sstevel@tonic-gate 		    sm_io_setinfo(e->e_dfp, SM_BF_COMMIT, NULL) < 0 &&
5197c478bd9Sstevel@tonic-gate 		    errno != EINVAL)
5207c478bd9Sstevel@tonic-gate 		{
5217c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot commit data file %s, uid=%d",
5227c478bd9Sstevel@tonic-gate 			       queuename(e, DATAFL_LETTER), (int) geteuid());
5237c478bd9Sstevel@tonic-gate 		}
5247c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5257c478bd9Sstevel@tonic-gate 		    SuperSafe == SAFE_INTERACTIVE && msync)
5267c478bd9Sstevel@tonic-gate 		{
5277c478bd9Sstevel@tonic-gate 			if (tTd(40,32))
5287c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
5297c478bd9Sstevel@tonic-gate 					  "queueup: fsync(e->e_dfp)");
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 			if (fsync(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD,
5327c478bd9Sstevel@tonic-gate 						NULL)) < 0)
5337c478bd9Sstevel@tonic-gate 			{
5347c478bd9Sstevel@tonic-gate 				if (newid)
5357c478bd9Sstevel@tonic-gate 					syserr("!552 Error writing data file %s",
5367c478bd9Sstevel@tonic-gate 					       df);
5377c478bd9Sstevel@tonic-gate 				else
5387c478bd9Sstevel@tonic-gate 					syserr("!452 Error writing data file %s",
5397c478bd9Sstevel@tonic-gate 					       df);
5407c478bd9Sstevel@tonic-gate 			}
5417c478bd9Sstevel@tonic-gate 		}
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate 	else
5447c478bd9Sstevel@tonic-gate 	{
5457c478bd9Sstevel@tonic-gate 		int dfd;
5467c478bd9Sstevel@tonic-gate 		MODE_T oldumask = 0;
5477c478bd9Sstevel@tonic-gate 		register SM_FILE_T *dfp = NULL;
5487c478bd9Sstevel@tonic-gate 		struct stat stbuf;
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate 		if (e->e_dfp != NULL &&
5517c478bd9Sstevel@tonic-gate 		    sm_io_getinfo(e->e_dfp, SM_IO_WHAT_ISTYPE, BF_FILE_TYPE))
5527c478bd9Sstevel@tonic-gate 			syserr("committing over bf file");
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode))
5557c478bd9Sstevel@tonic-gate 			oldumask = umask(002);
5567c478bd9Sstevel@tonic-gate 		dfd = open(df, O_WRONLY|O_CREAT|O_TRUNC|QF_O_EXTRA,
5577c478bd9Sstevel@tonic-gate 			   QueueFileMode);
5587c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode))
5597c478bd9Sstevel@tonic-gate 			(void) umask(oldumask);
5607c478bd9Sstevel@tonic-gate 		if (dfd < 0 || (dfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
5617c478bd9Sstevel@tonic-gate 						 (void *) &dfd, SM_IO_WRONLY_B,
5627c478bd9Sstevel@tonic-gate 						 NULL)) == NULL)
5637c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot create data temp file %s, uid=%d",
5647c478bd9Sstevel@tonic-gate 				df, (int) geteuid());
5657c478bd9Sstevel@tonic-gate 		if (fstat(dfd, &stbuf) < 0)
5667c478bd9Sstevel@tonic-gate 			e->e_dfino = -1;
5677c478bd9Sstevel@tonic-gate 		else
5687c478bd9Sstevel@tonic-gate 		{
5697c478bd9Sstevel@tonic-gate 			e->e_dfdev = stbuf.st_dev;
5707c478bd9Sstevel@tonic-gate 			e->e_dfino = ST_INODE(stbuf);
5717c478bd9Sstevel@tonic-gate 		}
5727c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
573058561cbSjbeck 		memset(&mcibuf, '\0', sizeof(mcibuf));
5747c478bd9Sstevel@tonic-gate 		mcibuf.mci_out = dfp;
5757c478bd9Sstevel@tonic-gate 		mcibuf.mci_mailer = FileMailer;
5767c478bd9Sstevel@tonic-gate 		(*e->e_putbody)(&mcibuf, e, NULL);
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 		if (SuperSafe == SAFE_REALLY ||
5797c478bd9Sstevel@tonic-gate 		    SuperSafe == SAFE_REALLY_POSTMILTER ||
5807c478bd9Sstevel@tonic-gate 		    (SuperSafe == SAFE_INTERACTIVE && msync))
5817c478bd9Sstevel@tonic-gate 		{
5827c478bd9Sstevel@tonic-gate 			if (tTd(40,32))
5837c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
5847c478bd9Sstevel@tonic-gate 					  "queueup: fsync(dfp)");
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 			if (fsync(sm_io_getinfo(dfp, SM_IO_WHAT_FD, NULL)) < 0)
5877c478bd9Sstevel@tonic-gate 			{
5887c478bd9Sstevel@tonic-gate 				if (newid)
5897c478bd9Sstevel@tonic-gate 					syserr("!552 Error writing data file %s",
5907c478bd9Sstevel@tonic-gate 					       df);
5917c478bd9Sstevel@tonic-gate 				else
5927c478bd9Sstevel@tonic-gate 					syserr("!452 Error writing data file %s",
5937c478bd9Sstevel@tonic-gate 					       df);
5947c478bd9Sstevel@tonic-gate 			}
5957c478bd9Sstevel@tonic-gate 		}
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 		if (sm_io_close(dfp, SM_TIME_DEFAULT) < 0)
5987c478bd9Sstevel@tonic-gate 			syserr("!queueup: cannot save data temp file %s, uid=%d",
5997c478bd9Sstevel@tonic-gate 				df, (int) geteuid());
6007c478bd9Sstevel@tonic-gate 		e->e_putbody = putbody;
6017c478bd9Sstevel@tonic-gate 	}
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 	/*
6047c478bd9Sstevel@tonic-gate 	**  Output future work requests.
6057c478bd9Sstevel@tonic-gate 	**	Priority and creation time should be first, since
6067c478bd9Sstevel@tonic-gate 	**	they are required by gatherq.
6077c478bd9Sstevel@tonic-gate 	*/
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	/* output queue version number (must be first!) */
6107c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "V%d\n", QF_VERSION);
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	/* output creation time */
6137c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "T%ld\n", (long) e->e_ctime);
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	/* output last delivery time */
6167c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "K%ld\n", (long) e->e_dtime);
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate 	/* output number of delivery attempts */
6197c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "N%d\n", e->e_ntries);
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	/* output message priority */
6227c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "P%ld\n", e->e_msgpriority);
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	/*
6257c478bd9Sstevel@tonic-gate 	**  If data file is in a different directory than the queue file,
6267c478bd9Sstevel@tonic-gate 	**  output a "d" record naming the directory of the data file.
6277c478bd9Sstevel@tonic-gate 	*/
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	if (e->e_dfqgrp != e->e_qgrp)
6307c478bd9Sstevel@tonic-gate 	{
6317c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "d%s\n",
6327c478bd9Sstevel@tonic-gate 			Queue[e->e_dfqgrp]->qg_qpaths[e->e_dfqdir].qp_name);
6337c478bd9Sstevel@tonic-gate 	}
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 	/* output inode number of data file */
6367c478bd9Sstevel@tonic-gate 	/* XXX should probably include device major/minor too */
6377c478bd9Sstevel@tonic-gate 	if (e->e_dfino != -1)
6387c478bd9Sstevel@tonic-gate 	{
6397c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "I%ld/%ld/%llu\n",
6407c478bd9Sstevel@tonic-gate 				     (long) major(e->e_dfdev),
6417c478bd9Sstevel@tonic-gate 				     (long) minor(e->e_dfdev),
6427c478bd9Sstevel@tonic-gate 				     (ULONGLONG_T) e->e_dfino);
6437c478bd9Sstevel@tonic-gate 	}
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	/* output body type */
6467c478bd9Sstevel@tonic-gate 	if (e->e_bodytype != NULL)
6477c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "B%s\n",
6487c478bd9Sstevel@tonic-gate 				     denlstring(e->e_bodytype, true, false));
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	/* quarantine reason */
6517c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg != NULL)
6527c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "q%s\n",
6537c478bd9Sstevel@tonic-gate 				     denlstring(e->e_quarmsg, true, false));
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate 	/* message from envelope, if it exists */
6567c478bd9Sstevel@tonic-gate 	if (e->e_message != NULL)
6577c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
6587c478bd9Sstevel@tonic-gate 				     denlstring(e->e_message, true, false));
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	/* send various flag bits through */
6617c478bd9Sstevel@tonic-gate 	p = buf;
6627c478bd9Sstevel@tonic-gate 	if (bitset(EF_WARNING, e->e_flags))
6637c478bd9Sstevel@tonic-gate 		*p++ = 'w';
6647c478bd9Sstevel@tonic-gate 	if (bitset(EF_RESPONSE, e->e_flags))
6657c478bd9Sstevel@tonic-gate 		*p++ = 'r';
6667c478bd9Sstevel@tonic-gate 	if (bitset(EF_HAS8BIT, e->e_flags))
6677c478bd9Sstevel@tonic-gate 		*p++ = '8';
6687c478bd9Sstevel@tonic-gate 	if (bitset(EF_DELETE_BCC, e->e_flags))
6697c478bd9Sstevel@tonic-gate 		*p++ = 'b';
6707c478bd9Sstevel@tonic-gate 	if (bitset(EF_RET_PARAM, e->e_flags))
6717c478bd9Sstevel@tonic-gate 		*p++ = 'd';
6727c478bd9Sstevel@tonic-gate 	if (bitset(EF_NO_BODY_RETN, e->e_flags))
6737c478bd9Sstevel@tonic-gate 		*p++ = 'n';
6747c478bd9Sstevel@tonic-gate 	if (bitset(EF_SPLIT, e->e_flags))
6757c478bd9Sstevel@tonic-gate 		*p++ = 's';
6767c478bd9Sstevel@tonic-gate 	*p++ = '\0';
6777c478bd9Sstevel@tonic-gate 	if (buf[0] != '\0')
6787c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "F%s\n", buf);
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	/* save $={persistentMacros} macro values */
6817c478bd9Sstevel@tonic-gate 	queueup_macros(macid("{persistentMacros}"), tfp, e);
6827c478bd9Sstevel@tonic-gate 
6837c478bd9Sstevel@tonic-gate 	/* output name of sender */
6847c478bd9Sstevel@tonic-gate 	if (bitnset(M_UDBENVELOPE, e->e_from.q_mailer->m_flags))
6857c478bd9Sstevel@tonic-gate 		p = e->e_sender;
6867c478bd9Sstevel@tonic-gate 	else
6877c478bd9Sstevel@tonic-gate 		p = e->e_from.q_paddr;
6887c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "S%s\n",
6897c478bd9Sstevel@tonic-gate 			     denlstring(p, true, false));
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	/* output ESMTP-supplied "original" information */
6927c478bd9Sstevel@tonic-gate 	if (e->e_envid != NULL)
6937c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Z%s\n",
6947c478bd9Sstevel@tonic-gate 				     denlstring(e->e_envid, true, false));
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	/* output AUTH= parameter */
6977c478bd9Sstevel@tonic-gate 	if (e->e_auth_param != NULL)
6987c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "A%s\n",
6997c478bd9Sstevel@tonic-gate 				     denlstring(e->e_auth_param, true, false));
7007c478bd9Sstevel@tonic-gate 	if (e->e_dlvr_flag != 0)
7017c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "!%c %ld\n",
7027c478bd9Sstevel@tonic-gate 				     (char) e->e_dlvr_flag, e->e_deliver_by);
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 	/* output list of recipient addresses */
7057c478bd9Sstevel@tonic-gate 	printctladdr(NULL, NULL);
7067c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
7077c478bd9Sstevel@tonic-gate 	{
7087c478bd9Sstevel@tonic-gate 		if (!QS_IS_UNDELIVERED(q->q_state))
7097c478bd9Sstevel@tonic-gate 			continue;
7107c478bd9Sstevel@tonic-gate 
7117c478bd9Sstevel@tonic-gate 		/* message for this recipient, if it exists */
7127c478bd9Sstevel@tonic-gate 		if (q->q_message != NULL)
7137c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "M%s\n",
7147c478bd9Sstevel@tonic-gate 					     denlstring(q->q_message, true,
7157c478bd9Sstevel@tonic-gate 							false));
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 		printctladdr(q, tfp);
7187c478bd9Sstevel@tonic-gate 		if (q->q_orcpt != NULL)
7197c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "Q%s\n",
7207c478bd9Sstevel@tonic-gate 					     denlstring(q->q_orcpt, true,
7217c478bd9Sstevel@tonic-gate 							false));
7227c478bd9Sstevel@tonic-gate 		if (q->q_finalrcpt != NULL)
7237c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "r%s\n",
7247c478bd9Sstevel@tonic-gate 					     denlstring(q->q_finalrcpt, true,
7257c478bd9Sstevel@tonic-gate 							false));
7267c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'R');
7277c478bd9Sstevel@tonic-gate 		if (bitset(QPRIMARY, q->q_flags))
7287c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'P');
7297c478bd9Sstevel@tonic-gate 		if (bitset(QHASNOTIFY, q->q_flags))
7307c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'N');
7317c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONSUCCESS, q->q_flags))
7327c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'S');
7337c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONFAILURE, q->q_flags))
7347c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'F');
7357c478bd9Sstevel@tonic-gate 		if (bitset(QPINGONDELAY, q->q_flags))
7367c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'D');
7377c478bd9Sstevel@tonic-gate 		if (q->q_alias != NULL &&
7387c478bd9Sstevel@tonic-gate 		    bitset(QALIAS, q->q_alias->q_flags))
7397c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(tfp, SM_TIME_DEFAULT, 'A');
7407c478bd9Sstevel@tonic-gate 		(void) sm_io_putc(tfp, SM_TIME_DEFAULT, ':');
7417c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(tfp, SM_TIME_DEFAULT, "%s\n",
7427c478bd9Sstevel@tonic-gate 				     denlstring(q->q_paddr, true, false));
7437c478bd9Sstevel@tonic-gate 		if (announce)
7447c478bd9Sstevel@tonic-gate 		{
7457c478bd9Sstevel@tonic-gate 			char *tag = "queued";
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 			if (e->e_quarmsg != NULL)
7487c478bd9Sstevel@tonic-gate 				tag = "quarantined";
7497c478bd9Sstevel@tonic-gate 
7507c478bd9Sstevel@tonic-gate 			e->e_to = q->q_paddr;
7517c478bd9Sstevel@tonic-gate 			message(tag);
7527c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
7537c478bd9Sstevel@tonic-gate 				logdelivery(q->q_mailer, NULL, q->q_status,
7547c478bd9Sstevel@tonic-gate 					    tag, NULL, (time_t) 0, e);
7557c478bd9Sstevel@tonic-gate 			e->e_to = NULL;
7567c478bd9Sstevel@tonic-gate 		}
7577c478bd9Sstevel@tonic-gate 		if (tTd(40, 1))
7587c478bd9Sstevel@tonic-gate 		{
7597c478bd9Sstevel@tonic-gate 			sm_dprintf("queueing ");
7607c478bd9Sstevel@tonic-gate 			printaddr(sm_debug_file(), q, false);
7617c478bd9Sstevel@tonic-gate 		}
7627c478bd9Sstevel@tonic-gate 	}
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	/*
7657c478bd9Sstevel@tonic-gate 	**  Output headers for this message.
7667c478bd9Sstevel@tonic-gate 	**	Expand macros completely here.  Queue run will deal with
7677c478bd9Sstevel@tonic-gate 	**	everything as absolute headers.
7687c478bd9Sstevel@tonic-gate 	**		All headers that must be relative to the recipient
7697c478bd9Sstevel@tonic-gate 	**		can be cracked later.
7707c478bd9Sstevel@tonic-gate 	**	We set up a "null mailer" -- i.e., a mailer that will have
7717c478bd9Sstevel@tonic-gate 	**	no effect on the addresses as they are output.
7727c478bd9Sstevel@tonic-gate 	*/
7737c478bd9Sstevel@tonic-gate 
774058561cbSjbeck 	memset((char *) &nullmailer, '\0', sizeof(nullmailer));
7757c478bd9Sstevel@tonic-gate 	nullmailer.m_re_rwset = nullmailer.m_rh_rwset =
7767c478bd9Sstevel@tonic-gate 			nullmailer.m_se_rwset = nullmailer.m_sh_rwset = -1;
7777c478bd9Sstevel@tonic-gate 	nullmailer.m_eol = "\n";
778058561cbSjbeck 	memset(&mcibuf, '\0', sizeof(mcibuf));
7797c478bd9Sstevel@tonic-gate 	mcibuf.mci_mailer = &nullmailer;
7807c478bd9Sstevel@tonic-gate 	mcibuf.mci_out = tfp;
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'g', "\201f");
7837c478bd9Sstevel@tonic-gate 	for (h = e->e_header; h != NULL; h = h->h_link)
7847c478bd9Sstevel@tonic-gate 	{
7857c478bd9Sstevel@tonic-gate 		if (h->h_value == NULL)
7867c478bd9Sstevel@tonic-gate 			continue;
7877c478bd9Sstevel@tonic-gate 
7887c478bd9Sstevel@tonic-gate 		/* don't output resent headers on non-resent messages */
7897c478bd9Sstevel@tonic-gate 		if (bitset(H_RESENT, h->h_flags) &&
7907c478bd9Sstevel@tonic-gate 		    !bitset(EF_RESENT, e->e_flags))
7917c478bd9Sstevel@tonic-gate 			continue;
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 		/* expand macros; if null, don't output header at all */
7947c478bd9Sstevel@tonic-gate 		if (bitset(H_DEFAULT, h->h_flags))
7957c478bd9Sstevel@tonic-gate 		{
796058561cbSjbeck 			(void) expand(h->h_value, buf, sizeof(buf), e);
7977c478bd9Sstevel@tonic-gate 			if (buf[0] == '\0')
7987c478bd9Sstevel@tonic-gate 				continue;
7994aac33d3Sjbeck 			if (buf[0] == ' ' && buf[1] == '\0')
8004aac33d3Sjbeck 				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 		{
8347c478bd9Sstevel@tonic-gate 			(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;
8487800901eSjbeck 			commaize(h, h->h_value, oldstyle, &mcibuf, e,
8497800901eSjbeck 				 PXLF_HEADER);
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 			TrafficLogFile = savetrace;
8527c478bd9Sstevel@tonic-gate 		}
8537c478bd9Sstevel@tonic-gate 		else
8547c478bd9Sstevel@tonic-gate 		{
8557c478bd9Sstevel@tonic-gate 			(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),
890058561cbSjbeck 				  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
printctladdr(a,tfp)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
runners_sigterm(sig)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
runners_sighup(sig)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
mark_work_group_restart(wgrp,reason)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
restart_marked_work_groups()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
restart_work_group(wgrp)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
schedule_queue_runs(runall,wgrp,didit)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
checkqueuerunner()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
runqueue(forkflag,verbose,persistent,runall)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
skip_domains(skip)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
runner_work(e,sequenceno,didfork,skip,njobs)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
run_work_group(wgrp,flags)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 	{
2109e9af4bc0SJohn Beck 		(void) gatherq(qgrp, qdir, false, &full, &more, &h);
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 
21507800901eSjbeck 		/*
21517800901eSjbeck 		**  If no runners are configured for this group but
21527800901eSjbeck 		**  the queue is "forced" then lets use 1 runner.
21537800901eSjbeck 		*/
21547800901eSjbeck 
21557800901eSjbeck 		if (maxrunners == 0 && bitset(RWG_FORCE, flags))
21567800901eSjbeck 			maxrunners = 1;
21577800901eSjbeck 
21587c478bd9Sstevel@tonic-gate 		/* No need to have more runners then there are jobs */
21597c478bd9Sstevel@tonic-gate 		if (maxrunners > njobs)
21607c478bd9Sstevel@tonic-gate 			maxrunners = njobs;
21617c478bd9Sstevel@tonic-gate 		for (loop = 0; loop < maxrunners; loop++)
21627c478bd9Sstevel@tonic-gate 		{
21637c478bd9Sstevel@tonic-gate 			/*
21647c478bd9Sstevel@tonic-gate 			**  Since the delivery may happen in a child and the
21657c478bd9Sstevel@tonic-gate 			**  parent does not wait, the parent may close the
21667c478bd9Sstevel@tonic-gate 			**  maps thereby removing any shared memory used by
21677c478bd9Sstevel@tonic-gate 			**  the map.  Therefore, close the maps now so the
21687c478bd9Sstevel@tonic-gate 			**  child will dynamically open them if necessary.
21697c478bd9Sstevel@tonic-gate 			*/
21707c478bd9Sstevel@tonic-gate 
21717c478bd9Sstevel@tonic-gate 			closemaps(false);
21727c478bd9Sstevel@tonic-gate 
21737c478bd9Sstevel@tonic-gate 			pid = fork();
21747c478bd9Sstevel@tonic-gate 			if (pid < 0)
21757c478bd9Sstevel@tonic-gate 			{
21767c478bd9Sstevel@tonic-gate 				syserr("run_work_group: cannot fork");
21777c478bd9Sstevel@tonic-gate 				return false;
21787c478bd9Sstevel@tonic-gate 			}
21797c478bd9Sstevel@tonic-gate 			else if (pid > 0)
21807c478bd9Sstevel@tonic-gate 			{
21817c478bd9Sstevel@tonic-gate 				/* parent -- clean out connection cache */
21827c478bd9Sstevel@tonic-gate 				mci_flush(false, NULL);
21837c478bd9Sstevel@tonic-gate #if _FFR_SKIP_DOMAINS
21847c478bd9Sstevel@tonic-gate 				if (QueueSortOrder == QSO_BYHOST)
21857c478bd9Sstevel@tonic-gate 				{
21867c478bd9Sstevel@tonic-gate 					sequenceno += skip_domains(1);
21877c478bd9Sstevel@tonic-gate 				}
21887c478bd9Sstevel@tonic-gate 				else
21897c478bd9Sstevel@tonic-gate #endif /* _FFR_SKIP_DOMAINS */
21907c478bd9Sstevel@tonic-gate 				{
21917c478bd9Sstevel@tonic-gate 					/* for the skip */
21927c478bd9Sstevel@tonic-gate 					WorkQ = WorkQ->w_next;
21937c478bd9Sstevel@tonic-gate 					sequenceno++;
21947c478bd9Sstevel@tonic-gate 				}
21957c478bd9Sstevel@tonic-gate 				proc_list_add(pid, "Queue child runner process",
21967c478bd9Sstevel@tonic-gate 					      PROC_QUEUE_CHILD, 0, -1, NULL);
21977c478bd9Sstevel@tonic-gate 
21987c478bd9Sstevel@tonic-gate 				/* No additional work, no additional runners */
21997c478bd9Sstevel@tonic-gate 				if (WorkQ == NULL)
22007c478bd9Sstevel@tonic-gate 					break;
22017c478bd9Sstevel@tonic-gate 			}
22027c478bd9Sstevel@tonic-gate 			else
22037c478bd9Sstevel@tonic-gate 			{
22047c478bd9Sstevel@tonic-gate 				/* child -- Reset global flags */
22057c478bd9Sstevel@tonic-gate 				RestartRequest = NULL;
22067c478bd9Sstevel@tonic-gate 				RestartWorkGroup = false;
22077c478bd9Sstevel@tonic-gate 				ShutdownRequest = NULL;
22087c478bd9Sstevel@tonic-gate 				PendingSignal = 0;
22097c478bd9Sstevel@tonic-gate 				CurrentPid = getpid();
22107c478bd9Sstevel@tonic-gate 				close_sendmail_pid();
22117c478bd9Sstevel@tonic-gate 
22127c478bd9Sstevel@tonic-gate 				/*
22137c478bd9Sstevel@tonic-gate 				**  Initialize exception stack and default
22147c478bd9Sstevel@tonic-gate 				**  exception handler for child process.
22157c478bd9Sstevel@tonic-gate 				**  When fork()'d the child now has a private
22167c478bd9Sstevel@tonic-gate 				**  copy of WorkQ at its current position.
22177c478bd9Sstevel@tonic-gate 				*/
22187c478bd9Sstevel@tonic-gate 
22197c478bd9Sstevel@tonic-gate 				sm_exc_newthread(fatal_error);
22207c478bd9Sstevel@tonic-gate 
22217c478bd9Sstevel@tonic-gate 				/*
22227c478bd9Sstevel@tonic-gate 				**  SMTP processes (whether -bd or -bs) set
22237c478bd9Sstevel@tonic-gate 				**  SIGCHLD to reapchild to collect
22247c478bd9Sstevel@tonic-gate 				**  children status.  However, at delivery
22257c478bd9Sstevel@tonic-gate 				**  time, that status must be collected
22267c478bd9Sstevel@tonic-gate 				**  by sm_wait() to be dealt with properly
22277c478bd9Sstevel@tonic-gate 				**  (check success of delivery based
22287c478bd9Sstevel@tonic-gate 				**  on status code, etc).  Therefore, if we
22297c478bd9Sstevel@tonic-gate 				**  are an SMTP process, reset SIGCHLD
22307c478bd9Sstevel@tonic-gate 				**  back to the default so reapchild
22317c478bd9Sstevel@tonic-gate 				**  doesn't collect status before
22327c478bd9Sstevel@tonic-gate 				**  sm_wait().
22337c478bd9Sstevel@tonic-gate 				*/
22347c478bd9Sstevel@tonic-gate 
22357c478bd9Sstevel@tonic-gate 				if (OpMode == MD_SMTP ||
22367c478bd9Sstevel@tonic-gate 				    OpMode == MD_DAEMON ||
22377c478bd9Sstevel@tonic-gate 				    MaxQueueChildren > 0)
22387c478bd9Sstevel@tonic-gate 				{
22397c478bd9Sstevel@tonic-gate 					proc_list_clear();
22407c478bd9Sstevel@tonic-gate 					sm_releasesignal(SIGCHLD);
22417c478bd9Sstevel@tonic-gate 					(void) sm_signal(SIGCHLD, SIG_DFL);
22427c478bd9Sstevel@tonic-gate 				}
22437c478bd9Sstevel@tonic-gate 
22447c478bd9Sstevel@tonic-gate 				/* child -- error messages to the transcript */
22457c478bd9Sstevel@tonic-gate 				QuickAbort = OnlyOneError = false;
22467c478bd9Sstevel@tonic-gate 				runner_work(e, sequenceno, true,
22477c478bd9Sstevel@tonic-gate 					    maxrunners, njobs);
22487c478bd9Sstevel@tonic-gate 
22497c478bd9Sstevel@tonic-gate 				/* This child is done */
22507c478bd9Sstevel@tonic-gate 				finis(true, true, ExitStat);
22517c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
22527c478bd9Sstevel@tonic-gate 			}
22537c478bd9Sstevel@tonic-gate 		}
22547c478bd9Sstevel@tonic-gate 
22557c478bd9Sstevel@tonic-gate 		sm_releasesignal(SIGCHLD);
22567c478bd9Sstevel@tonic-gate 
22577c478bd9Sstevel@tonic-gate 		/*
22587c478bd9Sstevel@tonic-gate 		**  Wait until all of the runners have completed before
22597c478bd9Sstevel@tonic-gate 		**  seeing if there is another queue group in the
22607c478bd9Sstevel@tonic-gate 		**  work group to process.
22617c478bd9Sstevel@tonic-gate 		**  XXX Future enhancement: don't wait() for all children
22627c478bd9Sstevel@tonic-gate 		**  here, just go ahead and make sure that overall the number
22637c478bd9Sstevel@tonic-gate 		**  of children is not exceeded.
22647c478bd9Sstevel@tonic-gate 		*/
22657c478bd9Sstevel@tonic-gate 
22667c478bd9Sstevel@tonic-gate 		while (CurChildren > 0)
22677c478bd9Sstevel@tonic-gate 		{
22687c478bd9Sstevel@tonic-gate 			int status;
22697c478bd9Sstevel@tonic-gate 			pid_t ret;
22707c478bd9Sstevel@tonic-gate 
22717c478bd9Sstevel@tonic-gate 			while ((ret = sm_wait(&status)) <= 0)
22727c478bd9Sstevel@tonic-gate 				continue;
22737c478bd9Sstevel@tonic-gate 			proc_list_drop(ret, status, NULL);
22747c478bd9Sstevel@tonic-gate 		}
22757c478bd9Sstevel@tonic-gate 	}
22767c478bd9Sstevel@tonic-gate 	else if (Queue[qgrp]->qg_maxqrun > 0 || bitset(RWG_FORCE, flags))
22777c478bd9Sstevel@tonic-gate 	{
22787c478bd9Sstevel@tonic-gate 		/*
22797c478bd9Sstevel@tonic-gate 		**  When current process will not fork children to do the work,
22807c478bd9Sstevel@tonic-gate 		**  it will do the work itself. The 'skip' will be 1 since
22817c478bd9Sstevel@tonic-gate 		**  there are no child runners to divide the work across.
22827c478bd9Sstevel@tonic-gate 		*/
22837c478bd9Sstevel@tonic-gate 
22847c478bd9Sstevel@tonic-gate 		runner_work(e, sequenceno, false, 1, njobs);
22857c478bd9Sstevel@tonic-gate 	}
22867c478bd9Sstevel@tonic-gate 
22877c478bd9Sstevel@tonic-gate 	/* free memory allocated by newenvelope() above */
22887c478bd9Sstevel@tonic-gate 	sm_rpool_free(rpool);
22897c478bd9Sstevel@tonic-gate 	QueueEnvelope.e_rpool = NULL;
22907c478bd9Sstevel@tonic-gate 
22917c478bd9Sstevel@tonic-gate 	/* Are there still more queues in the work group to process? */
22927c478bd9Sstevel@tonic-gate 	if (endgrp != WorkGrp[wgrp].wg_curqgrp)
22937c478bd9Sstevel@tonic-gate 	{
22947c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
22957c478bd9Sstevel@tonic-gate 		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
22967c478bd9Sstevel@tonic-gate 		e->e_flags = BlankEnvelope.e_flags;
22977c478bd9Sstevel@tonic-gate 		goto domorework;
22987c478bd9Sstevel@tonic-gate 	}
22997c478bd9Sstevel@tonic-gate 
23007c478bd9Sstevel@tonic-gate 	/* No more queues in work group to process. Now check persistent. */
23017c478bd9Sstevel@tonic-gate 	if (bitset(RWG_PERSISTENT, flags))
23027c478bd9Sstevel@tonic-gate 	{
23037c478bd9Sstevel@tonic-gate 		sequenceno = 1;
23047c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, CurEnv, "running queue: %s",
23057c478bd9Sstevel@tonic-gate 				qid_printqueue(qgrp, qdir));
23067c478bd9Sstevel@tonic-gate 
23077c478bd9Sstevel@tonic-gate 		/*
23087c478bd9Sstevel@tonic-gate 		**  close bogus maps, i.e., maps which caused a tempfail,
23097c478bd9Sstevel@tonic-gate 		**	so we get fresh map connections on the next lookup.
23107c478bd9Sstevel@tonic-gate 		**  closemaps() is also called when children are started.
23117c478bd9Sstevel@tonic-gate 		*/
23127c478bd9Sstevel@tonic-gate 
23137c478bd9Sstevel@tonic-gate 		closemaps(true);
23147c478bd9Sstevel@tonic-gate 
23157c478bd9Sstevel@tonic-gate 		/* Close any cached connections. */
23167c478bd9Sstevel@tonic-gate 		mci_flush(true, NULL);
23177c478bd9Sstevel@tonic-gate 
23187c478bd9Sstevel@tonic-gate 		/* Clean out expired related entries. */
23197c478bd9Sstevel@tonic-gate 		rmexpstab();
23207c478bd9Sstevel@tonic-gate 
23217c478bd9Sstevel@tonic-gate #if NAMED_BIND
23227c478bd9Sstevel@tonic-gate 		/* Update MX records for FallbackMX. */
23237c478bd9Sstevel@tonic-gate 		if (FallbackMX != NULL)
23247c478bd9Sstevel@tonic-gate 			(void) getfallbackmxrr(FallbackMX);
23257c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
23267c478bd9Sstevel@tonic-gate 
23277c478bd9Sstevel@tonic-gate #if USERDB
23287c478bd9Sstevel@tonic-gate 		/* close UserDatabase */
23297c478bd9Sstevel@tonic-gate 		_udbx_close();
23307c478bd9Sstevel@tonic-gate #endif /* USERDB */
23317c478bd9Sstevel@tonic-gate 
23327c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
23337c478bd9Sstevel@tonic-gate 		if (sm_debug_active(&SmHeapCheck, 2)
23347c478bd9Sstevel@tonic-gate 		    && access("memdump", F_OK) == 0
23357c478bd9Sstevel@tonic-gate 		   )
23367c478bd9Sstevel@tonic-gate 		{
23377c478bd9Sstevel@tonic-gate 			SM_FILE_T *out;
23387c478bd9Sstevel@tonic-gate 
23397c478bd9Sstevel@tonic-gate 			remove("memdump");
23407c478bd9Sstevel@tonic-gate 			out = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
23417c478bd9Sstevel@tonic-gate 					 "memdump.out", SM_IO_APPEND, NULL);
23427c478bd9Sstevel@tonic-gate 			if (out != NULL)
23437c478bd9Sstevel@tonic-gate 			{
23447c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
23457c478bd9Sstevel@tonic-gate 				sm_heap_report(out,
23467c478bd9Sstevel@tonic-gate 					sm_debug_level(&SmHeapCheck) - 1);
23477c478bd9Sstevel@tonic-gate 				(void) sm_io_close(out, SM_TIME_DEFAULT);
23487c478bd9Sstevel@tonic-gate 			}
23497c478bd9Sstevel@tonic-gate 		}
23507c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
23517c478bd9Sstevel@tonic-gate 
23527c478bd9Sstevel@tonic-gate 		/* let me rest for a second to catch my breath */
23537c478bd9Sstevel@tonic-gate 		if (njobs == 0 && WorkGrp[wgrp].wg_lowqintvl < MIN_SLEEP_TIME)
23547c478bd9Sstevel@tonic-gate 			sleep(MIN_SLEEP_TIME);
23557c478bd9Sstevel@tonic-gate 		else if (WorkGrp[wgrp].wg_lowqintvl <= 0)
23567c478bd9Sstevel@tonic-gate 			sleep(QueueIntvl > 0 ? QueueIntvl : MIN_SLEEP_TIME);
23577c478bd9Sstevel@tonic-gate 		else
23587c478bd9Sstevel@tonic-gate 			sleep(WorkGrp[wgrp].wg_lowqintvl);
23597c478bd9Sstevel@tonic-gate 
23607c478bd9Sstevel@tonic-gate 		/*
23617c478bd9Sstevel@tonic-gate 		**  Get the LA outside the WorkQ loop if necessary.
23627c478bd9Sstevel@tonic-gate 		**  In a persistent queue runner the code is repeated over
23637c478bd9Sstevel@tonic-gate 		**  and over but gatherq() may ignore entries due to
23647c478bd9Sstevel@tonic-gate 		**  shouldqueue() (do we really have to do this twice?).
23657c478bd9Sstevel@tonic-gate 		**  Hence the queue runners would just idle around when once
23667c478bd9Sstevel@tonic-gate 		**  CurrentLA caused all entries in a queue to be ignored.
23677c478bd9Sstevel@tonic-gate 		*/
23687c478bd9Sstevel@tonic-gate 
23697c478bd9Sstevel@tonic-gate 		if (njobs == 0)
23707c478bd9Sstevel@tonic-gate 			SM_GET_LA(now);
23717c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
23727c478bd9Sstevel@tonic-gate 		e = newenvelope(&QueueEnvelope, CurEnv, rpool);
23737c478bd9Sstevel@tonic-gate 		e->e_flags = BlankEnvelope.e_flags;
23747c478bd9Sstevel@tonic-gate 		goto domorework;
23757c478bd9Sstevel@tonic-gate 	}
23767c478bd9Sstevel@tonic-gate 
23777c478bd9Sstevel@tonic-gate 	/* exit without the usual cleanup */
23787c478bd9Sstevel@tonic-gate 	e->e_id = NULL;
23797c478bd9Sstevel@tonic-gate 	if (bitset(RWG_FORK, flags))
23807c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
23817c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
23827c478bd9Sstevel@tonic-gate 	return true;
23837c478bd9Sstevel@tonic-gate }
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate /*
23867c478bd9Sstevel@tonic-gate **  DOQUEUERUN -- do a queue run?
23877c478bd9Sstevel@tonic-gate */
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate bool
doqueuerun()23907c478bd9Sstevel@tonic-gate doqueuerun()
23917c478bd9Sstevel@tonic-gate {
23927c478bd9Sstevel@tonic-gate 	return DoQueueRun;
23937c478bd9Sstevel@tonic-gate }
23947c478bd9Sstevel@tonic-gate 
23957c478bd9Sstevel@tonic-gate /*
23967c478bd9Sstevel@tonic-gate **  RUNQUEUEEVENT -- Sets a flag to indicate that a queue run should be done.
23977c478bd9Sstevel@tonic-gate **
23987c478bd9Sstevel@tonic-gate **	Parameters:
23997c478bd9Sstevel@tonic-gate **		none.
24007c478bd9Sstevel@tonic-gate **
24017c478bd9Sstevel@tonic-gate **	Returns:
24027c478bd9Sstevel@tonic-gate **		none.
24037c478bd9Sstevel@tonic-gate **
24047c478bd9Sstevel@tonic-gate **	Side Effects:
24057c478bd9Sstevel@tonic-gate **		The invocation of this function via an alarm may interrupt
24067c478bd9Sstevel@tonic-gate **		a set of actions. Thus errno may be set in that context.
24077c478bd9Sstevel@tonic-gate **		We need to restore errno at the end of this function to ensure
24087c478bd9Sstevel@tonic-gate **		that any work done here that sets errno doesn't return a
24097c478bd9Sstevel@tonic-gate **		misleading/false errno value. Errno may	be EINTR upon entry to
24107c478bd9Sstevel@tonic-gate **		this function because of non-restartable/continuable system
24117c478bd9Sstevel@tonic-gate **		API was active. Iff this is true we will override errno as
24127c478bd9Sstevel@tonic-gate **		a timeout (as a more accurate error message).
24137c478bd9Sstevel@tonic-gate **
24147c478bd9Sstevel@tonic-gate **	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
24157c478bd9Sstevel@tonic-gate **		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
24167c478bd9Sstevel@tonic-gate **		DOING.
24177c478bd9Sstevel@tonic-gate */
24187c478bd9Sstevel@tonic-gate 
24197c478bd9Sstevel@tonic-gate void
runqueueevent(ignore)24207c478bd9Sstevel@tonic-gate runqueueevent(ignore)
24217c478bd9Sstevel@tonic-gate 	int ignore;
24227c478bd9Sstevel@tonic-gate {
24237c478bd9Sstevel@tonic-gate 	int save_errno = errno;
24247c478bd9Sstevel@tonic-gate 
24257c478bd9Sstevel@tonic-gate 	/*
24267c478bd9Sstevel@tonic-gate 	**  Set the general bit that we want a queue run,
24277c478bd9Sstevel@tonic-gate 	**  tested in doqueuerun()
24287c478bd9Sstevel@tonic-gate 	*/
24297c478bd9Sstevel@tonic-gate 
24307c478bd9Sstevel@tonic-gate 	DoQueueRun = true;
24317c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_SCHED_DBG
24327c478bd9Sstevel@tonic-gate 	if (tTd(69, 10))
24337c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID, "rqe: done");
24347c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_SCHED_DBG */
24357c478bd9Sstevel@tonic-gate 
24367c478bd9Sstevel@tonic-gate 	errno = save_errno;
24377c478bd9Sstevel@tonic-gate 	if (errno == EINTR)
24387c478bd9Sstevel@tonic-gate 		errno = ETIMEDOUT;
24397c478bd9Sstevel@tonic-gate }
24407c478bd9Sstevel@tonic-gate /*
24417c478bd9Sstevel@tonic-gate **  GATHERQ -- gather messages from the message queue(s) the work queue.
24427c478bd9Sstevel@tonic-gate **
24437c478bd9Sstevel@tonic-gate **	Parameters:
24447c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group.
24457c478bd9Sstevel@tonic-gate **		qdir -- the index of the queue directory.
24467c478bd9Sstevel@tonic-gate **		doall -- if set, include everything in the queue (even
24477c478bd9Sstevel@tonic-gate **			the jobs that cannot be run because the load
24487c478bd9Sstevel@tonic-gate **			average is too high, or MaxQueueRun is reached).
24497c478bd9Sstevel@tonic-gate **			Otherwise, exclude those jobs.
24507c478bd9Sstevel@tonic-gate **		full -- (optional) to be set 'true' if WorkList is full
24517c478bd9Sstevel@tonic-gate **		more -- (optional) to be set 'true' if there are still more
24527c478bd9Sstevel@tonic-gate **			messages in this queue not added to WorkList
2453e9af4bc0SJohn Beck **		pnentries -- (optional) total nuber of entries in queue
24547c478bd9Sstevel@tonic-gate **
24557c478bd9Sstevel@tonic-gate **	Returns:
24567c478bd9Sstevel@tonic-gate **		The number of request in the queue (not necessarily
24577c478bd9Sstevel@tonic-gate **		the number of requests in WorkList however).
24587c478bd9Sstevel@tonic-gate **
24597c478bd9Sstevel@tonic-gate **	Side Effects:
24607c478bd9Sstevel@tonic-gate **		prepares available work into WorkList
24617c478bd9Sstevel@tonic-gate */
24627c478bd9Sstevel@tonic-gate 
24637c478bd9Sstevel@tonic-gate #define NEED_P		0001	/* 'P': priority */
24647c478bd9Sstevel@tonic-gate #define NEED_T		0002	/* 'T': time */
24657c478bd9Sstevel@tonic-gate #define NEED_R		0004	/* 'R': recipient */
24667c478bd9Sstevel@tonic-gate #define NEED_S		0010	/* 'S': sender */
24677c478bd9Sstevel@tonic-gate #define NEED_H		0020	/* host */
24687c478bd9Sstevel@tonic-gate #define HAS_QUARANTINE	0040	/* has an unexpected 'q' line */
24697c478bd9Sstevel@tonic-gate #define NEED_QUARANTINE	0100	/* 'q': reason */
24707c478bd9Sstevel@tonic-gate 
24717c478bd9Sstevel@tonic-gate static WORK	*WorkList = NULL;	/* list of unsort work */
24727c478bd9Sstevel@tonic-gate static int	WorkListSize = 0;	/* current max size of WorkList */
24737c478bd9Sstevel@tonic-gate static int	WorkListCount = 0;	/* # of work items in WorkList */
24747c478bd9Sstevel@tonic-gate 
24757c478bd9Sstevel@tonic-gate static int
gatherq(qgrp,qdir,doall,full,more,pnentries)2476e9af4bc0SJohn Beck gatherq(qgrp, qdir, doall, full, more, pnentries)
24777c478bd9Sstevel@tonic-gate 	int qgrp;
24787c478bd9Sstevel@tonic-gate 	int qdir;
24797c478bd9Sstevel@tonic-gate 	bool doall;
24807c478bd9Sstevel@tonic-gate 	bool *full;
24817c478bd9Sstevel@tonic-gate 	bool *more;
2482e9af4bc0SJohn Beck 	int *pnentries;
24837c478bd9Sstevel@tonic-gate {
24847c478bd9Sstevel@tonic-gate 	register struct dirent *d;
24857c478bd9Sstevel@tonic-gate 	register WORK *w;
24867c478bd9Sstevel@tonic-gate 	register char *p;
24877c478bd9Sstevel@tonic-gate 	DIR *f;
2488e9af4bc0SJohn Beck 	int i, num_ent, wn, nentries;
24897c478bd9Sstevel@tonic-gate 	QUEUE_CHAR *check;
24907c478bd9Sstevel@tonic-gate 	char qd[MAXPATHLEN];
24917c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
24927c478bd9Sstevel@tonic-gate 
24937c478bd9Sstevel@tonic-gate 	wn = WorkListCount - 1;
24947c478bd9Sstevel@tonic-gate 	num_ent = 0;
2495e9af4bc0SJohn Beck 	nentries = 0;
24967c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
2497058561cbSjbeck 		(void) sm_strlcpy(qd, ".", sizeof(qd));
24987c478bd9Sstevel@tonic-gate 	else
2499058561cbSjbeck 		(void) sm_strlcpyn(qd, sizeof(qd), 2,
25007c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
25017c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBQF,
25027c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
25037c478bd9Sstevel@tonic-gate 					? "/qf" : ""));
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
25067c478bd9Sstevel@tonic-gate 	{
25077c478bd9Sstevel@tonic-gate 		sm_dprintf("gatherq:\n");
25087c478bd9Sstevel@tonic-gate 
25097c478bd9Sstevel@tonic-gate 		check = QueueLimitId;
25107c478bd9Sstevel@tonic-gate 		while (check != NULL)
25117c478bd9Sstevel@tonic-gate 		{
25127c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitId = %s%s\n",
25137c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25147c478bd9Sstevel@tonic-gate 				check->queue_match);
25157c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25167c478bd9Sstevel@tonic-gate 		}
25177c478bd9Sstevel@tonic-gate 
25187c478bd9Sstevel@tonic-gate 		check = QueueLimitSender;
25197c478bd9Sstevel@tonic-gate 		while (check != NULL)
25207c478bd9Sstevel@tonic-gate 		{
25217c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitSender = %s%s\n",
25227c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25237c478bd9Sstevel@tonic-gate 				check->queue_match);
25247c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25257c478bd9Sstevel@tonic-gate 		}
25267c478bd9Sstevel@tonic-gate 
25277c478bd9Sstevel@tonic-gate 		check = QueueLimitRecipient;
25287c478bd9Sstevel@tonic-gate 		while (check != NULL)
25297c478bd9Sstevel@tonic-gate 		{
25307c478bd9Sstevel@tonic-gate 			sm_dprintf("\tQueueLimitRecipient = %s%s\n",
25317c478bd9Sstevel@tonic-gate 				check->queue_negate ? "!" : "",
25327c478bd9Sstevel@tonic-gate 				check->queue_match);
25337c478bd9Sstevel@tonic-gate 			check = check->queue_next;
25347c478bd9Sstevel@tonic-gate 		}
25357c478bd9Sstevel@tonic-gate 
25367c478bd9Sstevel@tonic-gate 		if (QueueMode == QM_QUARANTINE)
25377c478bd9Sstevel@tonic-gate 		{
25387c478bd9Sstevel@tonic-gate 			check = QueueLimitQuarantine;
25397c478bd9Sstevel@tonic-gate 			while (check != NULL)
25407c478bd9Sstevel@tonic-gate 			{
25417c478bd9Sstevel@tonic-gate 				sm_dprintf("\tQueueLimitQuarantine = %s%s\n",
25427c478bd9Sstevel@tonic-gate 					   check->queue_negate ? "!" : "",
25437c478bd9Sstevel@tonic-gate 					   check->queue_match);
25447c478bd9Sstevel@tonic-gate 				check = check->queue_next;
25457c478bd9Sstevel@tonic-gate 			}
25467c478bd9Sstevel@tonic-gate 		}
25477c478bd9Sstevel@tonic-gate 	}
25487c478bd9Sstevel@tonic-gate 
25497c478bd9Sstevel@tonic-gate 	/* open the queue directory */
25507c478bd9Sstevel@tonic-gate 	f = opendir(qd);
25517c478bd9Sstevel@tonic-gate 	if (f == NULL)
25527c478bd9Sstevel@tonic-gate 	{
25537c478bd9Sstevel@tonic-gate 		syserr("gatherq: cannot open \"%s\"",
25547c478bd9Sstevel@tonic-gate 			qid_printqueue(qgrp, qdir));
25557c478bd9Sstevel@tonic-gate 		if (full != NULL)
25567c478bd9Sstevel@tonic-gate 			*full = WorkListCount >= MaxQueueRun && MaxQueueRun > 0;
25577c478bd9Sstevel@tonic-gate 		if (more != NULL)
25587c478bd9Sstevel@tonic-gate 			*more = false;
25597c478bd9Sstevel@tonic-gate 		return 0;
25607c478bd9Sstevel@tonic-gate 	}
25617c478bd9Sstevel@tonic-gate 
25627c478bd9Sstevel@tonic-gate 	/*
25637c478bd9Sstevel@tonic-gate 	**  Read the work directory.
25647c478bd9Sstevel@tonic-gate 	*/
25657c478bd9Sstevel@tonic-gate 
25667c478bd9Sstevel@tonic-gate 	while ((d = readdir(f)) != NULL)
25677c478bd9Sstevel@tonic-gate 	{
25687c478bd9Sstevel@tonic-gate 		SM_FILE_T *cf;
25697c478bd9Sstevel@tonic-gate 		int qfver = 0;
25707c478bd9Sstevel@tonic-gate 		char lbuf[MAXNAME + 1];
25717c478bd9Sstevel@tonic-gate 		struct stat sbuf;
25727c478bd9Sstevel@tonic-gate 
25737c478bd9Sstevel@tonic-gate 		if (tTd(41, 50))
25747c478bd9Sstevel@tonic-gate 			sm_dprintf("gatherq: checking %s..", d->d_name);
25757c478bd9Sstevel@tonic-gate 
25767c478bd9Sstevel@tonic-gate 		/* is this an interesting entry? */
25777c478bd9Sstevel@tonic-gate 		if (!(((QueueMode == QM_NORMAL &&
25787c478bd9Sstevel@tonic-gate 			d->d_name[0] == NORMQF_LETTER) ||
25797c478bd9Sstevel@tonic-gate 		       (QueueMode == QM_QUARANTINE &&
25807c478bd9Sstevel@tonic-gate 			d->d_name[0] == QUARQF_LETTER) ||
25817c478bd9Sstevel@tonic-gate 		       (QueueMode == QM_LOST &&
25827c478bd9Sstevel@tonic-gate 			d->d_name[0] == LOSEQF_LETTER)) &&
25837c478bd9Sstevel@tonic-gate 		      d->d_name[1] == 'f'))
25847c478bd9Sstevel@tonic-gate 		{
25857c478bd9Sstevel@tonic-gate 			if (tTd(41, 50))
25867c478bd9Sstevel@tonic-gate 				sm_dprintf("  skipping\n");
25877c478bd9Sstevel@tonic-gate 			continue;
25887c478bd9Sstevel@tonic-gate 		}
25897c478bd9Sstevel@tonic-gate 		if (tTd(41, 50))
25907c478bd9Sstevel@tonic-gate 			sm_dprintf("\n");
25917c478bd9Sstevel@tonic-gate 
25927c478bd9Sstevel@tonic-gate 		if (strlen(d->d_name) >= MAXQFNAME)
25937c478bd9Sstevel@tonic-gate 		{
25947c478bd9Sstevel@tonic-gate 			if (Verbose)
25957c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
25967c478bd9Sstevel@tonic-gate 						     "gatherq: %s too long, %d max characters\n",
25977c478bd9Sstevel@tonic-gate 						     d->d_name, MAXQFNAME);
25987c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
25997c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ALERT, NOQID,
26007c478bd9Sstevel@tonic-gate 					  "gatherq: %s too long, %d max characters",
26017c478bd9Sstevel@tonic-gate 					  d->d_name, MAXQFNAME);
26027c478bd9Sstevel@tonic-gate 			continue;
26037c478bd9Sstevel@tonic-gate 		}
26047c478bd9Sstevel@tonic-gate 
2605e9af4bc0SJohn Beck 		++nentries;
26067c478bd9Sstevel@tonic-gate 		check = QueueLimitId;
26077c478bd9Sstevel@tonic-gate 		while (check != NULL)
26087c478bd9Sstevel@tonic-gate 		{
26097c478bd9Sstevel@tonic-gate 			if (strcontainedin(false, check->queue_match,
26107c478bd9Sstevel@tonic-gate 					   d->d_name) != check->queue_negate)
26117c478bd9Sstevel@tonic-gate 				break;
26127c478bd9Sstevel@tonic-gate 			else
26137c478bd9Sstevel@tonic-gate 				check = check->queue_next;
26147c478bd9Sstevel@tonic-gate 		}
26157c478bd9Sstevel@tonic-gate 		if (QueueLimitId != NULL && check == NULL)
26167c478bd9Sstevel@tonic-gate 			continue;
26177c478bd9Sstevel@tonic-gate 
26187c478bd9Sstevel@tonic-gate 		/* grow work list if necessary */
26197c478bd9Sstevel@tonic-gate 		if (++wn >= MaxQueueRun && MaxQueueRun > 0)
26207c478bd9Sstevel@tonic-gate 		{
26217c478bd9Sstevel@tonic-gate 			if (wn == MaxQueueRun && LogLevel > 0)
26227c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, NOQID,
26237c478bd9Sstevel@tonic-gate 					  "WorkList for %s maxed out at %d",
26247c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
26257c478bd9Sstevel@tonic-gate 					  MaxQueueRun);
26267c478bd9Sstevel@tonic-gate 			if (doall)
26277c478bd9Sstevel@tonic-gate 				continue;	/* just count entries */
26287c478bd9Sstevel@tonic-gate 			break;
26297c478bd9Sstevel@tonic-gate 		}
26307c478bd9Sstevel@tonic-gate 		if (wn >= WorkListSize)
26317c478bd9Sstevel@tonic-gate 		{
26327c478bd9Sstevel@tonic-gate 			grow_wlist(qgrp, qdir);
26337c478bd9Sstevel@tonic-gate 			if (wn >= WorkListSize)
26347c478bd9Sstevel@tonic-gate 				continue;
26357c478bd9Sstevel@tonic-gate 		}
26367c478bd9Sstevel@tonic-gate 		SM_ASSERT(wn >= 0);
26377c478bd9Sstevel@tonic-gate 		w = &WorkList[wn];
26387c478bd9Sstevel@tonic-gate 
2639058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", d->d_name);
26407c478bd9Sstevel@tonic-gate 		if (stat(qf, &sbuf) < 0)
26417c478bd9Sstevel@tonic-gate 		{
26427c478bd9Sstevel@tonic-gate 			if (errno != ENOENT)
26437c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
26447c478bd9Sstevel@tonic-gate 					  "gatherq: can't stat %s/%s",
26457c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
26467c478bd9Sstevel@tonic-gate 					  d->d_name);
26477c478bd9Sstevel@tonic-gate 			wn--;
26487c478bd9Sstevel@tonic-gate 			continue;
26497c478bd9Sstevel@tonic-gate 		}
26507c478bd9Sstevel@tonic-gate 		if (!bitset(S_IFREG, sbuf.st_mode))
26517c478bd9Sstevel@tonic-gate 		{
26527c478bd9Sstevel@tonic-gate 			/* Yikes!  Skip it or we will hang on open! */
26537c478bd9Sstevel@tonic-gate 			if (!((d->d_name[0] == DATAFL_LETTER ||
26547c478bd9Sstevel@tonic-gate 			       d->d_name[0] == NORMQF_LETTER ||
26557c478bd9Sstevel@tonic-gate 			       d->d_name[0] == QUARQF_LETTER ||
26567c478bd9Sstevel@tonic-gate 			       d->d_name[0] == LOSEQF_LETTER ||
26577c478bd9Sstevel@tonic-gate 			       d->d_name[0] == XSCRPT_LETTER) &&
26587c478bd9Sstevel@tonic-gate 			      d->d_name[1] == 'f' && d->d_name[2] == '\0'))
26597c478bd9Sstevel@tonic-gate 				syserr("gatherq: %s/%s is not a regular file",
26607c478bd9Sstevel@tonic-gate 				       qid_printqueue(qgrp, qdir), d->d_name);
26617c478bd9Sstevel@tonic-gate 			wn--;
26627c478bd9Sstevel@tonic-gate 			continue;
26637c478bd9Sstevel@tonic-gate 		}
26647c478bd9Sstevel@tonic-gate 
26657c478bd9Sstevel@tonic-gate 		/* avoid work if possible */
26667c478bd9Sstevel@tonic-gate 		if ((QueueSortOrder == QSO_BYFILENAME ||
26677c478bd9Sstevel@tonic-gate 		     QueueSortOrder == QSO_BYMODTIME ||
26681daa5768Sjbeck 		     QueueSortOrder == QSO_NONE ||
26697c478bd9Sstevel@tonic-gate 		     QueueSortOrder == QSO_RANDOM) &&
26707c478bd9Sstevel@tonic-gate 		    QueueLimitQuarantine == NULL &&
26717c478bd9Sstevel@tonic-gate 		    QueueLimitSender == NULL &&
26727c478bd9Sstevel@tonic-gate 		    QueueLimitRecipient == NULL)
26737c478bd9Sstevel@tonic-gate 		{
26747c478bd9Sstevel@tonic-gate 			w->w_qgrp = qgrp;
26757c478bd9Sstevel@tonic-gate 			w->w_qdir = qdir;
26767c478bd9Sstevel@tonic-gate 			w->w_name = newstr(d->d_name);
26777c478bd9Sstevel@tonic-gate 			w->w_host = NULL;
26787c478bd9Sstevel@tonic-gate 			w->w_lock = w->w_tooyoung = false;
26797c478bd9Sstevel@tonic-gate 			w->w_pri = 0;
26807c478bd9Sstevel@tonic-gate 			w->w_ctime = 0;
26817c478bd9Sstevel@tonic-gate 			w->w_mtime = sbuf.st_mtime;
26827c478bd9Sstevel@tonic-gate 			++num_ent;
26837c478bd9Sstevel@tonic-gate 			continue;
26847c478bd9Sstevel@tonic-gate 		}
26857c478bd9Sstevel@tonic-gate 
26867c478bd9Sstevel@tonic-gate 		/* open control file */
26877c478bd9Sstevel@tonic-gate 		cf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
26887c478bd9Sstevel@tonic-gate 				NULL);
26897c478bd9Sstevel@tonic-gate 		if (cf == NULL && OpMode != MD_PRINT)
26907c478bd9Sstevel@tonic-gate 		{
26917c478bd9Sstevel@tonic-gate 			/* this may be some random person sending hir msgs */
26927c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
26937c478bd9Sstevel@tonic-gate 				sm_dprintf("gatherq: cannot open %s: %s\n",
26947c478bd9Sstevel@tonic-gate 					d->d_name, sm_errstring(errno));
26957c478bd9Sstevel@tonic-gate 			errno = 0;
26967c478bd9Sstevel@tonic-gate 			wn--;
26977c478bd9Sstevel@tonic-gate 			continue;
26987c478bd9Sstevel@tonic-gate 		}
26997c478bd9Sstevel@tonic-gate 		w->w_qgrp = qgrp;
27007c478bd9Sstevel@tonic-gate 		w->w_qdir = qdir;
27017c478bd9Sstevel@tonic-gate 		w->w_name = newstr(d->d_name);
27027c478bd9Sstevel@tonic-gate 		w->w_host = NULL;
27037c478bd9Sstevel@tonic-gate 		if (cf != NULL)
27047c478bd9Sstevel@tonic-gate 		{
27057c478bd9Sstevel@tonic-gate 			w->w_lock = !lockfile(sm_io_getinfo(cf, SM_IO_WHAT_FD,
27067c478bd9Sstevel@tonic-gate 							    NULL),
27077c478bd9Sstevel@tonic-gate 					      w->w_name, NULL,
27087c478bd9Sstevel@tonic-gate 					      LOCK_SH|LOCK_NB);
27097c478bd9Sstevel@tonic-gate 		}
27107c478bd9Sstevel@tonic-gate 		w->w_tooyoung = false;
27117c478bd9Sstevel@tonic-gate 
27127c478bd9Sstevel@tonic-gate 		/* make sure jobs in creation don't clog queue */
27137c478bd9Sstevel@tonic-gate 		w->w_pri = 0x7fffffff;
27147c478bd9Sstevel@tonic-gate 		w->w_ctime = 0;
27157c478bd9Sstevel@tonic-gate 		w->w_mtime = sbuf.st_mtime;
27167c478bd9Sstevel@tonic-gate 
27177c478bd9Sstevel@tonic-gate 		/* extract useful information */
27187c478bd9Sstevel@tonic-gate 		i = NEED_P|NEED_T;
27197c478bd9Sstevel@tonic-gate 		if (QueueSortOrder == QSO_BYHOST
27207c478bd9Sstevel@tonic-gate #if _FFR_RHS
27217c478bd9Sstevel@tonic-gate 		    || QueueSortOrder == QSO_BYSHUFFLE
27227c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
27237c478bd9Sstevel@tonic-gate 		   )
27247c478bd9Sstevel@tonic-gate 		{
27257c478bd9Sstevel@tonic-gate 			/* need w_host set for host sort order */
27267c478bd9Sstevel@tonic-gate 			i |= NEED_H;
27277c478bd9Sstevel@tonic-gate 		}
27287c478bd9Sstevel@tonic-gate 		if (QueueLimitSender != NULL)
27297c478bd9Sstevel@tonic-gate 			i |= NEED_S;
27307c478bd9Sstevel@tonic-gate 		if (QueueLimitRecipient != NULL)
27317c478bd9Sstevel@tonic-gate 			i |= NEED_R;
27327c478bd9Sstevel@tonic-gate 		if (QueueLimitQuarantine != NULL)
27337c478bd9Sstevel@tonic-gate 			i |= NEED_QUARANTINE;
27347c478bd9Sstevel@tonic-gate 		while (cf != NULL && i != 0 &&
27357c478bd9Sstevel@tonic-gate 		       sm_io_fgets(cf, SM_TIME_DEFAULT, lbuf,
2736058561cbSjbeck 				   sizeof(lbuf)) != NULL)
27377c478bd9Sstevel@tonic-gate 		{
27387c478bd9Sstevel@tonic-gate 			int c;
27397c478bd9Sstevel@tonic-gate 			time_t age;
27407c478bd9Sstevel@tonic-gate 
27417c478bd9Sstevel@tonic-gate 			p = strchr(lbuf, '\n');
27427c478bd9Sstevel@tonic-gate 			if (p != NULL)
27437c478bd9Sstevel@tonic-gate 				*p = '\0';
27447c478bd9Sstevel@tonic-gate 			else
27457c478bd9Sstevel@tonic-gate 			{
27467c478bd9Sstevel@tonic-gate 				/* flush rest of overly long line */
27477c478bd9Sstevel@tonic-gate 				while ((c = sm_io_getc(cf, SM_TIME_DEFAULT))
27487c478bd9Sstevel@tonic-gate 				       != SM_IO_EOF && c != '\n')
27497c478bd9Sstevel@tonic-gate 					continue;
27507c478bd9Sstevel@tonic-gate 			}
27517c478bd9Sstevel@tonic-gate 
27527c478bd9Sstevel@tonic-gate 			switch (lbuf[0])
27537c478bd9Sstevel@tonic-gate 			{
27547c478bd9Sstevel@tonic-gate 			  case 'V':
27557c478bd9Sstevel@tonic-gate 				qfver = atoi(&lbuf[1]);
27567c478bd9Sstevel@tonic-gate 				break;
27577c478bd9Sstevel@tonic-gate 
27587c478bd9Sstevel@tonic-gate 			  case 'P':
27597c478bd9Sstevel@tonic-gate 				w->w_pri = atol(&lbuf[1]);
27607c478bd9Sstevel@tonic-gate 				i &= ~NEED_P;
27617c478bd9Sstevel@tonic-gate 				break;
27627c478bd9Sstevel@tonic-gate 
27637c478bd9Sstevel@tonic-gate 			  case 'T':
27647c478bd9Sstevel@tonic-gate 				w->w_ctime = atol(&lbuf[1]);
27657c478bd9Sstevel@tonic-gate 				i &= ~NEED_T;
27667c478bd9Sstevel@tonic-gate 				break;
27677c478bd9Sstevel@tonic-gate 
27687c478bd9Sstevel@tonic-gate 			  case 'q':
27697c478bd9Sstevel@tonic-gate 				if (QueueMode != QM_QUARANTINE &&
27707c478bd9Sstevel@tonic-gate 				    QueueMode != QM_LOST)
27717c478bd9Sstevel@tonic-gate 				{
27727c478bd9Sstevel@tonic-gate 					if (tTd(41, 49))
27737c478bd9Sstevel@tonic-gate 						sm_dprintf("%s not marked as quarantined but has a 'q' line\n",
27747c478bd9Sstevel@tonic-gate 							   w->w_name);
27757c478bd9Sstevel@tonic-gate 					i |= HAS_QUARANTINE;
27767c478bd9Sstevel@tonic-gate 				}
27777c478bd9Sstevel@tonic-gate 				else if (QueueMode == QM_QUARANTINE)
27787c478bd9Sstevel@tonic-gate 				{
27797c478bd9Sstevel@tonic-gate 					if (QueueLimitQuarantine == NULL)
27807c478bd9Sstevel@tonic-gate 					{
27817c478bd9Sstevel@tonic-gate 						i &= ~NEED_QUARANTINE;
27827c478bd9Sstevel@tonic-gate 						break;
27837c478bd9Sstevel@tonic-gate 					}
27847c478bd9Sstevel@tonic-gate 					p = &lbuf[1];
27857c478bd9Sstevel@tonic-gate 					check = QueueLimitQuarantine;
27867c478bd9Sstevel@tonic-gate 					while (check != NULL)
27877c478bd9Sstevel@tonic-gate 					{
27887c478bd9Sstevel@tonic-gate 						if (strcontainedin(false,
27897c478bd9Sstevel@tonic-gate 								   check->queue_match,
27907c478bd9Sstevel@tonic-gate 								   p) !=
27917c478bd9Sstevel@tonic-gate 						    check->queue_negate)
27927c478bd9Sstevel@tonic-gate 							break;
27937c478bd9Sstevel@tonic-gate 						else
27947c478bd9Sstevel@tonic-gate 							check = check->queue_next;
27957c478bd9Sstevel@tonic-gate 					}
27967c478bd9Sstevel@tonic-gate 					if (check != NULL)
27977c478bd9Sstevel@tonic-gate 						i &= ~NEED_QUARANTINE;
27987c478bd9Sstevel@tonic-gate 				}
27997c478bd9Sstevel@tonic-gate 				break;
28007c478bd9Sstevel@tonic-gate 
28017c478bd9Sstevel@tonic-gate 			  case 'R':
28027c478bd9Sstevel@tonic-gate 				if (w->w_host == NULL &&
28037c478bd9Sstevel@tonic-gate 				    (p = strrchr(&lbuf[1], '@')) != NULL)
28047c478bd9Sstevel@tonic-gate 				{
28057c478bd9Sstevel@tonic-gate #if _FFR_RHS
28067c478bd9Sstevel@tonic-gate 					if (QueueSortOrder == QSO_BYSHUFFLE)
28077c478bd9Sstevel@tonic-gate 						w->w_host = newstr(&p[1]);
28087c478bd9Sstevel@tonic-gate 					else
28097c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
28107c478bd9Sstevel@tonic-gate 						w->w_host = strrev(&p[1]);
28117c478bd9Sstevel@tonic-gate 					makelower(w->w_host);
28127c478bd9Sstevel@tonic-gate 					i &= ~NEED_H;
28137c478bd9Sstevel@tonic-gate 				}
28147c478bd9Sstevel@tonic-gate 				if (QueueLimitRecipient == NULL)
28157c478bd9Sstevel@tonic-gate 				{
28167c478bd9Sstevel@tonic-gate 					i &= ~NEED_R;
28177c478bd9Sstevel@tonic-gate 					break;
28187c478bd9Sstevel@tonic-gate 				}
28197c478bd9Sstevel@tonic-gate 				if (qfver > 0)
28207c478bd9Sstevel@tonic-gate 				{
28217c478bd9Sstevel@tonic-gate 					p = strchr(&lbuf[1], ':');
28227c478bd9Sstevel@tonic-gate 					if (p == NULL)
28237c478bd9Sstevel@tonic-gate 						p = &lbuf[1];
28247c478bd9Sstevel@tonic-gate 					else
28257c478bd9Sstevel@tonic-gate 						++p; /* skip over ':' */
28267c478bd9Sstevel@tonic-gate 				}
28277c478bd9Sstevel@tonic-gate 				else
28287c478bd9Sstevel@tonic-gate 					p = &lbuf[1];
28297c478bd9Sstevel@tonic-gate 				check = QueueLimitRecipient;
28307c478bd9Sstevel@tonic-gate 				while (check != NULL)
28317c478bd9Sstevel@tonic-gate 				{
28327c478bd9Sstevel@tonic-gate 					if (strcontainedin(true,
28337c478bd9Sstevel@tonic-gate 							   check->queue_match,
28347c478bd9Sstevel@tonic-gate 							   p) !=
28357c478bd9Sstevel@tonic-gate 					    check->queue_negate)
28367c478bd9Sstevel@tonic-gate 						break;
28377c478bd9Sstevel@tonic-gate 					else
28387c478bd9Sstevel@tonic-gate 						check = check->queue_next;
28397c478bd9Sstevel@tonic-gate 				}
28407c478bd9Sstevel@tonic-gate 				if (check != NULL)
28417c478bd9Sstevel@tonic-gate 					i &= ~NEED_R;
28427c478bd9Sstevel@tonic-gate 				break;
28437c478bd9Sstevel@tonic-gate 
28447c478bd9Sstevel@tonic-gate 			  case 'S':
28457c478bd9Sstevel@tonic-gate 				check = QueueLimitSender;
28467c478bd9Sstevel@tonic-gate 				while (check != NULL)
28477c478bd9Sstevel@tonic-gate 				{
28487c478bd9Sstevel@tonic-gate 					if (strcontainedin(true,
28497c478bd9Sstevel@tonic-gate 							   check->queue_match,
28507c478bd9Sstevel@tonic-gate 							   &lbuf[1]) !=
28517c478bd9Sstevel@tonic-gate 					    check->queue_negate)
28527c478bd9Sstevel@tonic-gate 						break;
28537c478bd9Sstevel@tonic-gate 					else
28547c478bd9Sstevel@tonic-gate 						check = check->queue_next;
28557c478bd9Sstevel@tonic-gate 				}
28567c478bd9Sstevel@tonic-gate 				if (check != NULL)
28577c478bd9Sstevel@tonic-gate 					i &= ~NEED_S;
28587c478bd9Sstevel@tonic-gate 				break;
28597c478bd9Sstevel@tonic-gate 
28607c478bd9Sstevel@tonic-gate 			  case 'K':
2861e9af4bc0SJohn Beck #if _FFR_EXPDELAY
2862e9af4bc0SJohn Beck 				if (MaxQueueAge > 0)
2863e9af4bc0SJohn Beck 				{
2864e9af4bc0SJohn Beck 					time_t lasttry, delay;
2865e9af4bc0SJohn Beck 
2866e9af4bc0SJohn Beck 					lasttry = (time_t) atol(&lbuf[1]);
2867e9af4bc0SJohn Beck 					delay = MIN(lasttry - w->w_ctime,
2868e9af4bc0SJohn Beck 						    MaxQueueAge);
2869e9af4bc0SJohn Beck 					age = curtime() - lasttry;
2870e9af4bc0SJohn Beck 					if (age < delay)
2871e9af4bc0SJohn Beck 						w->w_tooyoung = true;
2872e9af4bc0SJohn Beck 					break;
2873e9af4bc0SJohn Beck 				}
2874e9af4bc0SJohn Beck #endif /* _FFR_EXPDELAY */
2875e9af4bc0SJohn Beck 
28767c478bd9Sstevel@tonic-gate 				age = curtime() - (time_t) atol(&lbuf[1]);
28777c478bd9Sstevel@tonic-gate 				if (age >= 0 && MinQueueAge > 0 &&
28787c478bd9Sstevel@tonic-gate 				    age < MinQueueAge)
28797c478bd9Sstevel@tonic-gate 					w->w_tooyoung = true;
28807c478bd9Sstevel@tonic-gate 				break;
28817c478bd9Sstevel@tonic-gate 
28827c478bd9Sstevel@tonic-gate 			  case 'N':
28837c478bd9Sstevel@tonic-gate 				if (atol(&lbuf[1]) == 0)
28847c478bd9Sstevel@tonic-gate 					w->w_tooyoung = false;
28857c478bd9Sstevel@tonic-gate 				break;
28867c478bd9Sstevel@tonic-gate 			}
28877c478bd9Sstevel@tonic-gate 		}
28887c478bd9Sstevel@tonic-gate 		if (cf != NULL)
28897c478bd9Sstevel@tonic-gate 			(void) sm_io_close(cf, SM_TIME_DEFAULT);
28907c478bd9Sstevel@tonic-gate 
2891445f2479Sjbeck 		if ((!doall && (shouldqueue(w->w_pri, w->w_ctime) ||
2892445f2479Sjbeck 		    w->w_tooyoung)) ||
28937c478bd9Sstevel@tonic-gate 		    bitset(HAS_QUARANTINE, i) ||
28947c478bd9Sstevel@tonic-gate 		    bitset(NEED_QUARANTINE, i) ||
28957c478bd9Sstevel@tonic-gate 		    bitset(NEED_R|NEED_S, i))
28967c478bd9Sstevel@tonic-gate 		{
28977c478bd9Sstevel@tonic-gate 			/* don't even bother sorting this job in */
28987c478bd9Sstevel@tonic-gate 			if (tTd(41, 49))
28997c478bd9Sstevel@tonic-gate 				sm_dprintf("skipping %s (%x)\n", w->w_name, i);
29007c478bd9Sstevel@tonic-gate 			sm_free(w->w_name); /* XXX */
29017c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
29027c478bd9Sstevel@tonic-gate 				sm_free(w->w_host); /* XXX */
29037c478bd9Sstevel@tonic-gate 			wn--;
29047c478bd9Sstevel@tonic-gate 		}
29057c478bd9Sstevel@tonic-gate 		else
29067c478bd9Sstevel@tonic-gate 			++num_ent;
29077c478bd9Sstevel@tonic-gate 	}
29087c478bd9Sstevel@tonic-gate 	(void) closedir(f);
29097c478bd9Sstevel@tonic-gate 	wn++;
29107c478bd9Sstevel@tonic-gate 
29117c478bd9Sstevel@tonic-gate 	i = wn - WorkListCount;
29127c478bd9Sstevel@tonic-gate 	WorkListCount += SM_MIN(num_ent, WorkListSize);
29137c478bd9Sstevel@tonic-gate 
29147c478bd9Sstevel@tonic-gate 	if (more != NULL)
29157c478bd9Sstevel@tonic-gate 		*more = WorkListCount < wn;
29167c478bd9Sstevel@tonic-gate 
29177c478bd9Sstevel@tonic-gate 	if (full != NULL)
29187c478bd9Sstevel@tonic-gate 		*full = (wn >= MaxQueueRun && MaxQueueRun > 0) ||
29197c478bd9Sstevel@tonic-gate 			(WorkList == NULL && wn > 0);
29207c478bd9Sstevel@tonic-gate 
2921e9af4bc0SJohn Beck 	if (pnentries != NULL)
2922e9af4bc0SJohn Beck 		*pnentries = nentries;
29237c478bd9Sstevel@tonic-gate 	return i;
29247c478bd9Sstevel@tonic-gate }
29257c478bd9Sstevel@tonic-gate /*
29267c478bd9Sstevel@tonic-gate **  SORTQ -- sort the work list
29277c478bd9Sstevel@tonic-gate **
29287c478bd9Sstevel@tonic-gate **	First the old WorkQ is cleared away. Then the WorkList is sorted
29297c478bd9Sstevel@tonic-gate **	for all items so that important (higher sorting value) items are not
29307c478bd9Sstevel@tonic-gate **	trunctated off. Then the most important items are moved from
29317c478bd9Sstevel@tonic-gate **	WorkList to WorkQ. The lower count of 'max' or MaxListCount items
29327c478bd9Sstevel@tonic-gate **	are moved.
29337c478bd9Sstevel@tonic-gate **
29347c478bd9Sstevel@tonic-gate **	Parameters:
29357c478bd9Sstevel@tonic-gate **		max -- maximum number of items to be placed in WorkQ
29367c478bd9Sstevel@tonic-gate **
29377c478bd9Sstevel@tonic-gate **	Returns:
29387c478bd9Sstevel@tonic-gate **		the number of items in WorkQ
29397c478bd9Sstevel@tonic-gate **
29407c478bd9Sstevel@tonic-gate **	Side Effects:
29417c478bd9Sstevel@tonic-gate **		WorkQ gets released and filled with new work. WorkList
29427c478bd9Sstevel@tonic-gate **		gets released. Work items get sorted in order.
29437c478bd9Sstevel@tonic-gate */
29447c478bd9Sstevel@tonic-gate 
29457c478bd9Sstevel@tonic-gate static int
sortq(max)29467c478bd9Sstevel@tonic-gate sortq(max)
29477c478bd9Sstevel@tonic-gate 	int max;
29487c478bd9Sstevel@tonic-gate {
29497c478bd9Sstevel@tonic-gate 	register int i;			/* local counter */
29507c478bd9Sstevel@tonic-gate 	register WORK *w;		/* tmp item pointer */
29517c478bd9Sstevel@tonic-gate 	int wc = WorkListCount;		/* trim size for WorkQ */
29527c478bd9Sstevel@tonic-gate 
29537c478bd9Sstevel@tonic-gate 	if (WorkQ != NULL)
29547c478bd9Sstevel@tonic-gate 	{
29557c478bd9Sstevel@tonic-gate 		WORK *nw;
29567c478bd9Sstevel@tonic-gate 
29577c478bd9Sstevel@tonic-gate 		/* Clear out old WorkQ. */
29587c478bd9Sstevel@tonic-gate 		for (w = WorkQ; w != NULL; w = nw)
29597c478bd9Sstevel@tonic-gate 		{
29607c478bd9Sstevel@tonic-gate 			nw = w->w_next;
29617c478bd9Sstevel@tonic-gate 			sm_free(w->w_name); /* XXX */
29627c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
29637c478bd9Sstevel@tonic-gate 				sm_free(w->w_host); /* XXX */
29647c478bd9Sstevel@tonic-gate 			sm_free((char *) w); /* XXX */
29657c478bd9Sstevel@tonic-gate 		}
29667c478bd9Sstevel@tonic-gate 		WorkQ = NULL;
29677c478bd9Sstevel@tonic-gate 	}
29687c478bd9Sstevel@tonic-gate 
29697c478bd9Sstevel@tonic-gate 	if (WorkList == NULL || wc <= 0)
29707c478bd9Sstevel@tonic-gate 		return 0;
29717c478bd9Sstevel@tonic-gate 
29727c478bd9Sstevel@tonic-gate 	/*
29737c478bd9Sstevel@tonic-gate 	**  The sort now takes place using all of the items in WorkList.
29747c478bd9Sstevel@tonic-gate 	**  The list gets trimmed to the most important items after the sort.
29757c478bd9Sstevel@tonic-gate 	**  If the trim were to happen before the sort then one or more
29767c478bd9Sstevel@tonic-gate 	**  important items might get truncated off -- not what we want.
29777c478bd9Sstevel@tonic-gate 	*/
29787c478bd9Sstevel@tonic-gate 
29797c478bd9Sstevel@tonic-gate 	if (QueueSortOrder == QSO_BYHOST)
29807c478bd9Sstevel@tonic-gate 	{
29817c478bd9Sstevel@tonic-gate 		/*
29827c478bd9Sstevel@tonic-gate 		**  Sort the work directory for the first time,
29837c478bd9Sstevel@tonic-gate 		**  based on host name, lock status, and priority.
29847c478bd9Sstevel@tonic-gate 		*/
29857c478bd9Sstevel@tonic-gate 
2986058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf1);
29877c478bd9Sstevel@tonic-gate 
29887c478bd9Sstevel@tonic-gate 		/*
29897c478bd9Sstevel@tonic-gate 		**  If one message to host is locked, "lock" all messages
29907c478bd9Sstevel@tonic-gate 		**  to that host.
29917c478bd9Sstevel@tonic-gate 		*/
29927c478bd9Sstevel@tonic-gate 
29937c478bd9Sstevel@tonic-gate 		i = 0;
29947c478bd9Sstevel@tonic-gate 		while (i < wc)
29957c478bd9Sstevel@tonic-gate 		{
29967c478bd9Sstevel@tonic-gate 			if (!WorkList[i].w_lock)
29977c478bd9Sstevel@tonic-gate 			{
29987c478bd9Sstevel@tonic-gate 				i++;
29997c478bd9Sstevel@tonic-gate 				continue;
30007c478bd9Sstevel@tonic-gate 			}
30017c478bd9Sstevel@tonic-gate 			w = &WorkList[i];
30027c478bd9Sstevel@tonic-gate 			while (++i < wc)
30037c478bd9Sstevel@tonic-gate 			{
30047c478bd9Sstevel@tonic-gate 				if (WorkList[i].w_host == NULL &&
30057c478bd9Sstevel@tonic-gate 				    w->w_host == NULL)
30067c478bd9Sstevel@tonic-gate 					WorkList[i].w_lock = true;
30077c478bd9Sstevel@tonic-gate 				else if (WorkList[i].w_host != NULL &&
30087c478bd9Sstevel@tonic-gate 					 w->w_host != NULL &&
30097c478bd9Sstevel@tonic-gate 					 sm_strcasecmp(WorkList[i].w_host,
30107c478bd9Sstevel@tonic-gate 						       w->w_host) == 0)
30117c478bd9Sstevel@tonic-gate 					WorkList[i].w_lock = true;
30127c478bd9Sstevel@tonic-gate 				else
30137c478bd9Sstevel@tonic-gate 					break;
30147c478bd9Sstevel@tonic-gate 			}
30157c478bd9Sstevel@tonic-gate 		}
30167c478bd9Sstevel@tonic-gate 
30177c478bd9Sstevel@tonic-gate 		/*
30187c478bd9Sstevel@tonic-gate 		**  Sort the work directory for the second time,
30197c478bd9Sstevel@tonic-gate 		**  based on lock status, host name, and priority.
30207c478bd9Sstevel@tonic-gate 		*/
30217c478bd9Sstevel@tonic-gate 
3022058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf2);
30237c478bd9Sstevel@tonic-gate 	}
30247c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYTIME)
30257c478bd9Sstevel@tonic-gate 	{
30267c478bd9Sstevel@tonic-gate 		/*
30277c478bd9Sstevel@tonic-gate 		**  Simple sort based on submission time only.
30287c478bd9Sstevel@tonic-gate 		*/
30297c478bd9Sstevel@tonic-gate 
3030058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf3);
30317c478bd9Sstevel@tonic-gate 	}
30327c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYFILENAME)
30337c478bd9Sstevel@tonic-gate 	{
30347c478bd9Sstevel@tonic-gate 		/*
30357c478bd9Sstevel@tonic-gate 		**  Sort based on queue filename.
30367c478bd9Sstevel@tonic-gate 		*/
30377c478bd9Sstevel@tonic-gate 
3038058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf4);
30397c478bd9Sstevel@tonic-gate 	}
30407c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_RANDOM)
30417c478bd9Sstevel@tonic-gate 	{
30427c478bd9Sstevel@tonic-gate 		/*
30437c478bd9Sstevel@tonic-gate 		**  Sort randomly.  To avoid problems with an instable sort,
30447c478bd9Sstevel@tonic-gate 		**  use a random index into the queue file name to start
30457c478bd9Sstevel@tonic-gate 		**  comparison.
30467c478bd9Sstevel@tonic-gate 		*/
30477c478bd9Sstevel@tonic-gate 
30487c478bd9Sstevel@tonic-gate 		randi = get_rand_mod(MAXQFNAME);
30497c478bd9Sstevel@tonic-gate 		if (randi < 2)
30507c478bd9Sstevel@tonic-gate 			randi = 3;
3051058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf5);
30527c478bd9Sstevel@tonic-gate 	}
30537c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYMODTIME)
30547c478bd9Sstevel@tonic-gate 	{
30557c478bd9Sstevel@tonic-gate 		/*
30567c478bd9Sstevel@tonic-gate 		**  Simple sort based on modification time of queue file.
30577c478bd9Sstevel@tonic-gate 		**  This puts the oldest items first.
30587c478bd9Sstevel@tonic-gate 		*/
30597c478bd9Sstevel@tonic-gate 
3060058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf6);
30617c478bd9Sstevel@tonic-gate 	}
30627c478bd9Sstevel@tonic-gate #if _FFR_RHS
30637c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYSHUFFLE)
30647c478bd9Sstevel@tonic-gate 	{
30657c478bd9Sstevel@tonic-gate 		/*
30667c478bd9Sstevel@tonic-gate 		**  Simple sort based on shuffled host name.
30677c478bd9Sstevel@tonic-gate 		*/
30687c478bd9Sstevel@tonic-gate 
30697c478bd9Sstevel@tonic-gate 		init_shuffle_alphabet();
3070058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf7);
30717c478bd9Sstevel@tonic-gate 	}
30727c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
30737c478bd9Sstevel@tonic-gate 	else if (QueueSortOrder == QSO_BYPRIORITY)
30747c478bd9Sstevel@tonic-gate 	{
30757c478bd9Sstevel@tonic-gate 		/*
30767c478bd9Sstevel@tonic-gate 		**  Simple sort based on queue priority only.
30777c478bd9Sstevel@tonic-gate 		*/
30787c478bd9Sstevel@tonic-gate 
3079058561cbSjbeck 		qsort((char *) WorkList, wc, sizeof(*WorkList), workcmpf0);
30807c478bd9Sstevel@tonic-gate 	}
30817c478bd9Sstevel@tonic-gate 	/* else don't sort at all */
30827c478bd9Sstevel@tonic-gate 
308349218d4fSjbeck 	/* Check if the per queue group item limit will be exceeded */
308449218d4fSjbeck 	if (wc > max && max > 0)
308549218d4fSjbeck 		wc = max;
308649218d4fSjbeck 
30877c478bd9Sstevel@tonic-gate 	/*
30887c478bd9Sstevel@tonic-gate 	**  Convert the work list into canonical form.
30897c478bd9Sstevel@tonic-gate 	**	Should be turning it into a list of envelopes here perhaps.
30907c478bd9Sstevel@tonic-gate 	**  Only take the most important items up to the per queue group
30917c478bd9Sstevel@tonic-gate 	**  maximum.
30927c478bd9Sstevel@tonic-gate 	*/
30937c478bd9Sstevel@tonic-gate 
30947c478bd9Sstevel@tonic-gate 	for (i = wc; --i >= 0; )
30957c478bd9Sstevel@tonic-gate 	{
3096058561cbSjbeck 		w = (WORK *) xalloc(sizeof(*w));
30977c478bd9Sstevel@tonic-gate 		w->w_qgrp = WorkList[i].w_qgrp;
30987c478bd9Sstevel@tonic-gate 		w->w_qdir = WorkList[i].w_qdir;
30997c478bd9Sstevel@tonic-gate 		w->w_name = WorkList[i].w_name;
31007c478bd9Sstevel@tonic-gate 		w->w_host = WorkList[i].w_host;
31017c478bd9Sstevel@tonic-gate 		w->w_lock = WorkList[i].w_lock;
31027c478bd9Sstevel@tonic-gate 		w->w_tooyoung = WorkList[i].w_tooyoung;
31037c478bd9Sstevel@tonic-gate 		w->w_pri = WorkList[i].w_pri;
31047c478bd9Sstevel@tonic-gate 		w->w_ctime = WorkList[i].w_ctime;
31057c478bd9Sstevel@tonic-gate 		w->w_mtime = WorkList[i].w_mtime;
31067c478bd9Sstevel@tonic-gate 		w->w_next = WorkQ;
31077c478bd9Sstevel@tonic-gate 		WorkQ = w;
31087c478bd9Sstevel@tonic-gate 	}
31097c478bd9Sstevel@tonic-gate 
31107c478bd9Sstevel@tonic-gate 	/* free the rest of the list */
31117c478bd9Sstevel@tonic-gate 	for (i = WorkListCount; --i >= wc; )
31127c478bd9Sstevel@tonic-gate 	{
31137c478bd9Sstevel@tonic-gate 		sm_free(WorkList[i].w_name);
31147c478bd9Sstevel@tonic-gate 		if (WorkList[i].w_host != NULL)
31157c478bd9Sstevel@tonic-gate 			sm_free(WorkList[i].w_host);
31167c478bd9Sstevel@tonic-gate 	}
31177c478bd9Sstevel@tonic-gate 
31187c478bd9Sstevel@tonic-gate 	if (WorkList != NULL)
31197c478bd9Sstevel@tonic-gate 		sm_free(WorkList); /* XXX */
31207c478bd9Sstevel@tonic-gate 	WorkList = NULL;
31217c478bd9Sstevel@tonic-gate 	WorkListSize = 0;
31227c478bd9Sstevel@tonic-gate 	WorkListCount = 0;
31237c478bd9Sstevel@tonic-gate 
31247c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
31257c478bd9Sstevel@tonic-gate 	{
31267c478bd9Sstevel@tonic-gate 		for (w = WorkQ; w != NULL; w = w->w_next)
31277c478bd9Sstevel@tonic-gate 		{
31287c478bd9Sstevel@tonic-gate 			if (w->w_host != NULL)
31297c478bd9Sstevel@tonic-gate 				sm_dprintf("%22s: pri=%ld %s\n",
31307c478bd9Sstevel@tonic-gate 					w->w_name, w->w_pri, w->w_host);
31317c478bd9Sstevel@tonic-gate 			else
31327c478bd9Sstevel@tonic-gate 				sm_dprintf("%32s: pri=%ld\n",
31337c478bd9Sstevel@tonic-gate 					w->w_name, w->w_pri);
31347c478bd9Sstevel@tonic-gate 		}
31357c478bd9Sstevel@tonic-gate 	}
31367c478bd9Sstevel@tonic-gate 
31377c478bd9Sstevel@tonic-gate 	return wc; /* return number of WorkQ items */
31387c478bd9Sstevel@tonic-gate }
31397c478bd9Sstevel@tonic-gate /*
31407c478bd9Sstevel@tonic-gate **  GROW_WLIST -- make the work list larger
31417c478bd9Sstevel@tonic-gate **
31427c478bd9Sstevel@tonic-gate **	Parameters:
31437c478bd9Sstevel@tonic-gate **		qgrp -- the index for the queue group.
31447c478bd9Sstevel@tonic-gate **		qdir -- the index for the queue directory.
31457c478bd9Sstevel@tonic-gate **
31467c478bd9Sstevel@tonic-gate **	Returns:
31477c478bd9Sstevel@tonic-gate **		none.
31487c478bd9Sstevel@tonic-gate **
31497c478bd9Sstevel@tonic-gate **	Side Effects:
31507c478bd9Sstevel@tonic-gate **		Adds another QUEUESEGSIZE entries to WorkList if possible.
31517c478bd9Sstevel@tonic-gate **		It can fail if there isn't enough memory, so WorkListSize
31527c478bd9Sstevel@tonic-gate **		should be checked again upon return.
31537c478bd9Sstevel@tonic-gate */
31547c478bd9Sstevel@tonic-gate 
31557c478bd9Sstevel@tonic-gate static void
grow_wlist(qgrp,qdir)31567c478bd9Sstevel@tonic-gate grow_wlist(qgrp, qdir)
31577c478bd9Sstevel@tonic-gate 	int qgrp;
31587c478bd9Sstevel@tonic-gate 	int qdir;
31597c478bd9Sstevel@tonic-gate {
31607c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
31617c478bd9Sstevel@tonic-gate 		sm_dprintf("grow_wlist: WorkListSize=%d\n", WorkListSize);
31627c478bd9Sstevel@tonic-gate 	if (WorkList == NULL)
31637c478bd9Sstevel@tonic-gate 	{
3164058561cbSjbeck 		WorkList = (WORK *) xalloc((sizeof(*WorkList)) *
31657c478bd9Sstevel@tonic-gate 					   (QUEUESEGSIZE + 1));
31667c478bd9Sstevel@tonic-gate 		WorkListSize = QUEUESEGSIZE;
31677c478bd9Sstevel@tonic-gate 	}
31687c478bd9Sstevel@tonic-gate 	else
31697c478bd9Sstevel@tonic-gate 	{
31707c478bd9Sstevel@tonic-gate 		int newsize = WorkListSize + QUEUESEGSIZE;
31717c478bd9Sstevel@tonic-gate 		WORK *newlist = (WORK *) sm_realloc((char *) WorkList,
31727c478bd9Sstevel@tonic-gate 					  (unsigned) sizeof(WORK) * (newsize + 1));
31737c478bd9Sstevel@tonic-gate 
31747c478bd9Sstevel@tonic-gate 		if (newlist != NULL)
31757c478bd9Sstevel@tonic-gate 		{
31767c478bd9Sstevel@tonic-gate 			WorkListSize = newsize;
31777c478bd9Sstevel@tonic-gate 			WorkList = newlist;
31787c478bd9Sstevel@tonic-gate 			if (LogLevel > 1)
31797c478bd9Sstevel@tonic-gate 			{
31807c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
31817c478bd9Sstevel@tonic-gate 					  "grew WorkList for %s to %d",
31827c478bd9Sstevel@tonic-gate 					  qid_printqueue(qgrp, qdir),
31837c478bd9Sstevel@tonic-gate 					  WorkListSize);
31847c478bd9Sstevel@tonic-gate 			}
31857c478bd9Sstevel@tonic-gate 		}
31867c478bd9Sstevel@tonic-gate 		else if (LogLevel > 0)
31877c478bd9Sstevel@tonic-gate 		{
31887c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, NOQID,
31897c478bd9Sstevel@tonic-gate 				  "FAILED to grow WorkList for %s to %d",
31907c478bd9Sstevel@tonic-gate 				  qid_printqueue(qgrp, qdir), newsize);
31917c478bd9Sstevel@tonic-gate 		}
31927c478bd9Sstevel@tonic-gate 	}
31937c478bd9Sstevel@tonic-gate 	if (tTd(41, 1))
31947c478bd9Sstevel@tonic-gate 		sm_dprintf("grow_wlist: WorkListSize now %d\n", WorkListSize);
31957c478bd9Sstevel@tonic-gate }
31967c478bd9Sstevel@tonic-gate /*
31977c478bd9Sstevel@tonic-gate **  WORKCMPF0 -- simple priority-only compare function.
31987c478bd9Sstevel@tonic-gate **
31997c478bd9Sstevel@tonic-gate **	Parameters:
32007c478bd9Sstevel@tonic-gate **		a -- the first argument.
32017c478bd9Sstevel@tonic-gate **		b -- the second argument.
32027c478bd9Sstevel@tonic-gate **
32037c478bd9Sstevel@tonic-gate **	Returns:
32047c478bd9Sstevel@tonic-gate **		-1 if a < b
32057c478bd9Sstevel@tonic-gate **		 0 if a == b
32067c478bd9Sstevel@tonic-gate **		+1 if a > b
32077c478bd9Sstevel@tonic-gate **
32087c478bd9Sstevel@tonic-gate */
32097c478bd9Sstevel@tonic-gate 
32107c478bd9Sstevel@tonic-gate static int
workcmpf0(a,b)32117c478bd9Sstevel@tonic-gate workcmpf0(a, b)
32127c478bd9Sstevel@tonic-gate 	register WORK *a;
32137c478bd9Sstevel@tonic-gate 	register WORK *b;
32147c478bd9Sstevel@tonic-gate {
32157c478bd9Sstevel@tonic-gate 	long pa = a->w_pri;
32167c478bd9Sstevel@tonic-gate 	long pb = b->w_pri;
32177c478bd9Sstevel@tonic-gate 
32187c478bd9Sstevel@tonic-gate 	if (pa == pb)
32197c478bd9Sstevel@tonic-gate 		return 0;
32207c478bd9Sstevel@tonic-gate 	else if (pa > pb)
32217c478bd9Sstevel@tonic-gate 		return 1;
32227c478bd9Sstevel@tonic-gate 	else
32237c478bd9Sstevel@tonic-gate 		return -1;
32247c478bd9Sstevel@tonic-gate }
32257c478bd9Sstevel@tonic-gate /*
32267c478bd9Sstevel@tonic-gate **  WORKCMPF1 -- first compare function for ordering work based on host name.
32277c478bd9Sstevel@tonic-gate **
32287c478bd9Sstevel@tonic-gate **	Sorts on host name, lock status, and priority in that order.
32297c478bd9Sstevel@tonic-gate **
32307c478bd9Sstevel@tonic-gate **	Parameters:
32317c478bd9Sstevel@tonic-gate **		a -- the first argument.
32327c478bd9Sstevel@tonic-gate **		b -- the second argument.
32337c478bd9Sstevel@tonic-gate **
32347c478bd9Sstevel@tonic-gate **	Returns:
32357c478bd9Sstevel@tonic-gate **		<0 if a < b
32367c478bd9Sstevel@tonic-gate **		 0 if a == b
32377c478bd9Sstevel@tonic-gate **		>0 if a > b
32387c478bd9Sstevel@tonic-gate **
32397c478bd9Sstevel@tonic-gate */
32407c478bd9Sstevel@tonic-gate 
32417c478bd9Sstevel@tonic-gate static int
workcmpf1(a,b)32427c478bd9Sstevel@tonic-gate workcmpf1(a, b)
32437c478bd9Sstevel@tonic-gate 	register WORK *a;
32447c478bd9Sstevel@tonic-gate 	register WORK *b;
32457c478bd9Sstevel@tonic-gate {
32467c478bd9Sstevel@tonic-gate 	int i;
32477c478bd9Sstevel@tonic-gate 
32487c478bd9Sstevel@tonic-gate 	/* host name */
32497c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
32507c478bd9Sstevel@tonic-gate 		return 1;
32517c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
32527c478bd9Sstevel@tonic-gate 		return -1;
32537c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
32547c478bd9Sstevel@tonic-gate 	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
32557c478bd9Sstevel@tonic-gate 		return i;
32567c478bd9Sstevel@tonic-gate 
32577c478bd9Sstevel@tonic-gate 	/* lock status */
32587c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
32597c478bd9Sstevel@tonic-gate 		return b->w_lock - a->w_lock;
32607c478bd9Sstevel@tonic-gate 
32617c478bd9Sstevel@tonic-gate 	/* job priority */
32627c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
32637c478bd9Sstevel@tonic-gate }
32647c478bd9Sstevel@tonic-gate /*
32657c478bd9Sstevel@tonic-gate **  WORKCMPF2 -- second compare function for ordering work based on host name.
32667c478bd9Sstevel@tonic-gate **
32677c478bd9Sstevel@tonic-gate **	Sorts on lock status, host name, and priority in that order.
32687c478bd9Sstevel@tonic-gate **
32697c478bd9Sstevel@tonic-gate **	Parameters:
32707c478bd9Sstevel@tonic-gate **		a -- the first argument.
32717c478bd9Sstevel@tonic-gate **		b -- the second argument.
32727c478bd9Sstevel@tonic-gate **
32737c478bd9Sstevel@tonic-gate **	Returns:
32747c478bd9Sstevel@tonic-gate **		<0 if a < b
32757c478bd9Sstevel@tonic-gate **		 0 if a == b
32767c478bd9Sstevel@tonic-gate **		>0 if a > b
32777c478bd9Sstevel@tonic-gate **
32787c478bd9Sstevel@tonic-gate */
32797c478bd9Sstevel@tonic-gate 
32807c478bd9Sstevel@tonic-gate static int
workcmpf2(a,b)32817c478bd9Sstevel@tonic-gate workcmpf2(a, b)
32827c478bd9Sstevel@tonic-gate 	register WORK *a;
32837c478bd9Sstevel@tonic-gate 	register WORK *b;
32847c478bd9Sstevel@tonic-gate {
32857c478bd9Sstevel@tonic-gate 	int i;
32867c478bd9Sstevel@tonic-gate 
32877c478bd9Sstevel@tonic-gate 	/* lock status */
32887c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
32897c478bd9Sstevel@tonic-gate 		return a->w_lock - b->w_lock;
32907c478bd9Sstevel@tonic-gate 
32917c478bd9Sstevel@tonic-gate 	/* host name */
32927c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
32937c478bd9Sstevel@tonic-gate 		return 1;
32947c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
32957c478bd9Sstevel@tonic-gate 		return -1;
32967c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
32977c478bd9Sstevel@tonic-gate 	    (i = sm_strcasecmp(a->w_host, b->w_host)) != 0)
32987c478bd9Sstevel@tonic-gate 		return i;
32997c478bd9Sstevel@tonic-gate 
33007c478bd9Sstevel@tonic-gate 	/* job priority */
33017c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
33027c478bd9Sstevel@tonic-gate }
33037c478bd9Sstevel@tonic-gate /*
33047c478bd9Sstevel@tonic-gate **  WORKCMPF3 -- simple submission-time-only compare function.
33057c478bd9Sstevel@tonic-gate **
33067c478bd9Sstevel@tonic-gate **	Parameters:
33077c478bd9Sstevel@tonic-gate **		a -- the first argument.
33087c478bd9Sstevel@tonic-gate **		b -- the second argument.
33097c478bd9Sstevel@tonic-gate **
33107c478bd9Sstevel@tonic-gate **	Returns:
33117c478bd9Sstevel@tonic-gate **		-1 if a < b
33127c478bd9Sstevel@tonic-gate **		 0 if a == b
33137c478bd9Sstevel@tonic-gate **		+1 if a > b
33147c478bd9Sstevel@tonic-gate **
33157c478bd9Sstevel@tonic-gate */
33167c478bd9Sstevel@tonic-gate 
33177c478bd9Sstevel@tonic-gate static int
workcmpf3(a,b)33187c478bd9Sstevel@tonic-gate workcmpf3(a, b)
33197c478bd9Sstevel@tonic-gate 	register WORK *a;
33207c478bd9Sstevel@tonic-gate 	register WORK *b;
33217c478bd9Sstevel@tonic-gate {
33227c478bd9Sstevel@tonic-gate 	if (a->w_ctime > b->w_ctime)
33237c478bd9Sstevel@tonic-gate 		return 1;
33247c478bd9Sstevel@tonic-gate 	else if (a->w_ctime < b->w_ctime)
33257c478bd9Sstevel@tonic-gate 		return -1;
33267c478bd9Sstevel@tonic-gate 	else
33277c478bd9Sstevel@tonic-gate 		return 0;
33287c478bd9Sstevel@tonic-gate }
33297c478bd9Sstevel@tonic-gate /*
33307c478bd9Sstevel@tonic-gate **  WORKCMPF4 -- compare based on file name
33317c478bd9Sstevel@tonic-gate **
33327c478bd9Sstevel@tonic-gate **	Parameters:
33337c478bd9Sstevel@tonic-gate **		a -- the first argument.
33347c478bd9Sstevel@tonic-gate **		b -- the second argument.
33357c478bd9Sstevel@tonic-gate **
33367c478bd9Sstevel@tonic-gate **	Returns:
33377c478bd9Sstevel@tonic-gate **		-1 if a < b
33387c478bd9Sstevel@tonic-gate **		 0 if a == b
33397c478bd9Sstevel@tonic-gate **		+1 if a > b
33407c478bd9Sstevel@tonic-gate **
33417c478bd9Sstevel@tonic-gate */
33427c478bd9Sstevel@tonic-gate 
33437c478bd9Sstevel@tonic-gate static int
workcmpf4(a,b)33447c478bd9Sstevel@tonic-gate workcmpf4(a, b)
33457c478bd9Sstevel@tonic-gate 	register WORK *a;
33467c478bd9Sstevel@tonic-gate 	register WORK *b;
33477c478bd9Sstevel@tonic-gate {
33487c478bd9Sstevel@tonic-gate 	return strcmp(a->w_name, b->w_name);
33497c478bd9Sstevel@tonic-gate }
33507c478bd9Sstevel@tonic-gate /*
33517c478bd9Sstevel@tonic-gate **  WORKCMPF5 -- compare based on assigned random number
33527c478bd9Sstevel@tonic-gate **
33537c478bd9Sstevel@tonic-gate **	Parameters:
3354e9af4bc0SJohn Beck **		a -- the first argument.
3355e9af4bc0SJohn Beck **		b -- the second argument.
33567c478bd9Sstevel@tonic-gate **
33577c478bd9Sstevel@tonic-gate **	Returns:
33587c478bd9Sstevel@tonic-gate **		randomly 1/-1
33597c478bd9Sstevel@tonic-gate */
33607c478bd9Sstevel@tonic-gate 
33617c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
33627c478bd9Sstevel@tonic-gate static int
workcmpf5(a,b)33637c478bd9Sstevel@tonic-gate workcmpf5(a, b)
33647c478bd9Sstevel@tonic-gate 	register WORK *a;
33657c478bd9Sstevel@tonic-gate 	register WORK *b;
33667c478bd9Sstevel@tonic-gate {
33677c478bd9Sstevel@tonic-gate 	if (strlen(a->w_name) < randi || strlen(b->w_name) < randi)
33687c478bd9Sstevel@tonic-gate 		return -1;
33697c478bd9Sstevel@tonic-gate 	return a->w_name[randi] - b->w_name[randi];
33707c478bd9Sstevel@tonic-gate }
33717c478bd9Sstevel@tonic-gate /*
33727c478bd9Sstevel@tonic-gate **  WORKCMPF6 -- simple modification-time-only compare function.
33737c478bd9Sstevel@tonic-gate **
33747c478bd9Sstevel@tonic-gate **	Parameters:
33757c478bd9Sstevel@tonic-gate **		a -- the first argument.
33767c478bd9Sstevel@tonic-gate **		b -- the second argument.
33777c478bd9Sstevel@tonic-gate **
33787c478bd9Sstevel@tonic-gate **	Returns:
33797c478bd9Sstevel@tonic-gate **		-1 if a < b
33807c478bd9Sstevel@tonic-gate **		 0 if a == b
33817c478bd9Sstevel@tonic-gate **		+1 if a > b
33827c478bd9Sstevel@tonic-gate **
33837c478bd9Sstevel@tonic-gate */
33847c478bd9Sstevel@tonic-gate 
33857c478bd9Sstevel@tonic-gate static int
workcmpf6(a,b)33867c478bd9Sstevel@tonic-gate workcmpf6(a, b)
33877c478bd9Sstevel@tonic-gate 	register WORK *a;
33887c478bd9Sstevel@tonic-gate 	register WORK *b;
33897c478bd9Sstevel@tonic-gate {
33907c478bd9Sstevel@tonic-gate 	if (a->w_mtime > b->w_mtime)
33917c478bd9Sstevel@tonic-gate 		return 1;
33927c478bd9Sstevel@tonic-gate 	else if (a->w_mtime < b->w_mtime)
33937c478bd9Sstevel@tonic-gate 		return -1;
33947c478bd9Sstevel@tonic-gate 	else
33957c478bd9Sstevel@tonic-gate 		return 0;
33967c478bd9Sstevel@tonic-gate }
33977c478bd9Sstevel@tonic-gate #if _FFR_RHS
33987c478bd9Sstevel@tonic-gate /*
33997c478bd9Sstevel@tonic-gate **  WORKCMPF7 -- compare function for ordering work based on shuffled host name.
34007c478bd9Sstevel@tonic-gate **
34017c478bd9Sstevel@tonic-gate **	Sorts on lock status, host name, and priority in that order.
34027c478bd9Sstevel@tonic-gate **
34037c478bd9Sstevel@tonic-gate **	Parameters:
34047c478bd9Sstevel@tonic-gate **		a -- the first argument.
34057c478bd9Sstevel@tonic-gate **		b -- the second argument.
34067c478bd9Sstevel@tonic-gate **
34077c478bd9Sstevel@tonic-gate **	Returns:
34087c478bd9Sstevel@tonic-gate **		<0 if a < b
34097c478bd9Sstevel@tonic-gate **		 0 if a == b
34107c478bd9Sstevel@tonic-gate **		>0 if a > b
34117c478bd9Sstevel@tonic-gate **
34127c478bd9Sstevel@tonic-gate */
34137c478bd9Sstevel@tonic-gate 
34147c478bd9Sstevel@tonic-gate static int
workcmpf7(a,b)34157c478bd9Sstevel@tonic-gate workcmpf7(a, b)
34167c478bd9Sstevel@tonic-gate 	register WORK *a;
34177c478bd9Sstevel@tonic-gate 	register WORK *b;
34187c478bd9Sstevel@tonic-gate {
34197c478bd9Sstevel@tonic-gate 	int i;
34207c478bd9Sstevel@tonic-gate 
34217c478bd9Sstevel@tonic-gate 	/* lock status */
34227c478bd9Sstevel@tonic-gate 	if (a->w_lock != b->w_lock)
34237c478bd9Sstevel@tonic-gate 		return a->w_lock - b->w_lock;
34247c478bd9Sstevel@tonic-gate 
34257c478bd9Sstevel@tonic-gate 	/* host name */
34267c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host == NULL)
34277c478bd9Sstevel@tonic-gate 		return 1;
34287c478bd9Sstevel@tonic-gate 	else if (a->w_host == NULL && b->w_host != NULL)
34297c478bd9Sstevel@tonic-gate 		return -1;
34307c478bd9Sstevel@tonic-gate 	if (a->w_host != NULL && b->w_host != NULL &&
34317c478bd9Sstevel@tonic-gate 	    (i = sm_strshufflecmp(a->w_host, b->w_host)) != 0)
34327c478bd9Sstevel@tonic-gate 		return i;
34337c478bd9Sstevel@tonic-gate 
34347c478bd9Sstevel@tonic-gate 	/* job priority */
34357c478bd9Sstevel@tonic-gate 	return workcmpf0(a, b);
34367c478bd9Sstevel@tonic-gate }
34377c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
34387c478bd9Sstevel@tonic-gate /*
34397c478bd9Sstevel@tonic-gate **  STRREV -- reverse string
34407c478bd9Sstevel@tonic-gate **
34417c478bd9Sstevel@tonic-gate **	Returns a pointer to a new string that is the reverse of
34427c478bd9Sstevel@tonic-gate **	the string pointed to by fwd.  The space for the new
34437c478bd9Sstevel@tonic-gate **	string is obtained using xalloc().
34447c478bd9Sstevel@tonic-gate **
34457c478bd9Sstevel@tonic-gate **	Parameters:
34467c478bd9Sstevel@tonic-gate **		fwd -- the string to reverse.
34477c478bd9Sstevel@tonic-gate **
34487c478bd9Sstevel@tonic-gate **	Returns:
34497c478bd9Sstevel@tonic-gate **		the reversed string.
34507c478bd9Sstevel@tonic-gate */
34517c478bd9Sstevel@tonic-gate 
34527c478bd9Sstevel@tonic-gate static char *
strrev(fwd)34537c478bd9Sstevel@tonic-gate strrev(fwd)
34547c478bd9Sstevel@tonic-gate 	char *fwd;
34557c478bd9Sstevel@tonic-gate {
34567c478bd9Sstevel@tonic-gate 	char *rev = NULL;
34577c478bd9Sstevel@tonic-gate 	int len, cnt;
34587c478bd9Sstevel@tonic-gate 
34597c478bd9Sstevel@tonic-gate 	len = strlen(fwd);
34607c478bd9Sstevel@tonic-gate 	rev = xalloc(len + 1);
34617c478bd9Sstevel@tonic-gate 	for (cnt = 0; cnt < len; ++cnt)
34627c478bd9Sstevel@tonic-gate 		rev[cnt] = fwd[len - cnt - 1];
34637c478bd9Sstevel@tonic-gate 	rev[len] = '\0';
34647c478bd9Sstevel@tonic-gate 	return rev;
34657c478bd9Sstevel@tonic-gate }
34667c478bd9Sstevel@tonic-gate 
34677c478bd9Sstevel@tonic-gate #if _FFR_RHS
34687c478bd9Sstevel@tonic-gate 
34697c478bd9Sstevel@tonic-gate # define NASCII	128
34707c478bd9Sstevel@tonic-gate # define NCHAR	256
34717c478bd9Sstevel@tonic-gate 
34727c478bd9Sstevel@tonic-gate static unsigned char ShuffledAlphabet[NCHAR];
34737c478bd9Sstevel@tonic-gate 
34747c478bd9Sstevel@tonic-gate void
init_shuffle_alphabet()34757c478bd9Sstevel@tonic-gate init_shuffle_alphabet()
34767c478bd9Sstevel@tonic-gate {
34777c478bd9Sstevel@tonic-gate 	static bool init = false;
34787c478bd9Sstevel@tonic-gate 	int i;
34797c478bd9Sstevel@tonic-gate 
34807c478bd9Sstevel@tonic-gate 	if (init)
34817c478bd9Sstevel@tonic-gate 		return;
34827c478bd9Sstevel@tonic-gate 
34837c478bd9Sstevel@tonic-gate 	/* fill the ShuffledAlphabet */
348449218d4fSjbeck 	for (i = 0; i < NASCII; i++)
34857c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = i;
34867c478bd9Sstevel@tonic-gate 
34877c478bd9Sstevel@tonic-gate 	/* mix it */
348849218d4fSjbeck 	for (i = 1; i < NASCII; i++)
34897c478bd9Sstevel@tonic-gate 	{
349049218d4fSjbeck 		register int j = get_random() % NASCII;
34917c478bd9Sstevel@tonic-gate 		register int tmp;
34927c478bd9Sstevel@tonic-gate 
34937c478bd9Sstevel@tonic-gate 		tmp = ShuffledAlphabet[j];
34947c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[j] = ShuffledAlphabet[i];
34957c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = tmp;
34967c478bd9Sstevel@tonic-gate 	}
34977c478bd9Sstevel@tonic-gate 
34987c478bd9Sstevel@tonic-gate 	/* make it case insensitive */
34997c478bd9Sstevel@tonic-gate 	for (i = 'A'; i <= 'Z'; i++)
35007c478bd9Sstevel@tonic-gate 		ShuffledAlphabet[i] = ShuffledAlphabet[i + 'a' - 'A'];
35017c478bd9Sstevel@tonic-gate 
35027c478bd9Sstevel@tonic-gate 	/* fill the upper part */
350349218d4fSjbeck 	for (i = 0; i < NASCII; i++)
350449218d4fSjbeck 		ShuffledAlphabet[i + NASCII] = ShuffledAlphabet[i];
35057c478bd9Sstevel@tonic-gate 	init = true;
35067c478bd9Sstevel@tonic-gate }
35077c478bd9Sstevel@tonic-gate 
35087c478bd9Sstevel@tonic-gate static int
sm_strshufflecmp(a,b)35097c478bd9Sstevel@tonic-gate sm_strshufflecmp(a, b)
35107c478bd9Sstevel@tonic-gate 	char *a;
35117c478bd9Sstevel@tonic-gate 	char *b;
35127c478bd9Sstevel@tonic-gate {
35137c478bd9Sstevel@tonic-gate 	const unsigned char *us1 = (const unsigned char *) a;
35147c478bd9Sstevel@tonic-gate 	const unsigned char *us2 = (const unsigned char *) b;
35157c478bd9Sstevel@tonic-gate 
35167c478bd9Sstevel@tonic-gate 	while (ShuffledAlphabet[*us1] == ShuffledAlphabet[*us2++])
35177c478bd9Sstevel@tonic-gate 	{
35187c478bd9Sstevel@tonic-gate 		if (*us1++ == '\0')
35197c478bd9Sstevel@tonic-gate 			return 0;
35207c478bd9Sstevel@tonic-gate 	}
35217c478bd9Sstevel@tonic-gate 	return (ShuffledAlphabet[*us1] - ShuffledAlphabet[*--us2]);
35227c478bd9Sstevel@tonic-gate }
35237c478bd9Sstevel@tonic-gate #endif /* _FFR_RHS */
35247c478bd9Sstevel@tonic-gate 
35257c478bd9Sstevel@tonic-gate /*
35267c478bd9Sstevel@tonic-gate **  DOWORK -- do a work request.
35277c478bd9Sstevel@tonic-gate **
35287c478bd9Sstevel@tonic-gate **	Parameters:
35297c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group for the job.
35307c478bd9Sstevel@tonic-gate **		qdir -- the index of the queue directory for the job.
35317c478bd9Sstevel@tonic-gate **		id -- the ID of the job to run.
35327c478bd9Sstevel@tonic-gate **		forkflag -- if set, run this in background.
35337c478bd9Sstevel@tonic-gate **		requeueflag -- if set, reinstantiate the queue quickly.
35347c478bd9Sstevel@tonic-gate **			This is used when expanding aliases in the queue.
35357c478bd9Sstevel@tonic-gate **			If forkflag is also set, it doesn't wait for the
35367c478bd9Sstevel@tonic-gate **			child.
35377c478bd9Sstevel@tonic-gate **		e - the envelope in which to run it.
35387c478bd9Sstevel@tonic-gate **
35397c478bd9Sstevel@tonic-gate **	Returns:
35407c478bd9Sstevel@tonic-gate **		process id of process that is running the queue job.
35417c478bd9Sstevel@tonic-gate **
35427c478bd9Sstevel@tonic-gate **	Side Effects:
35437c478bd9Sstevel@tonic-gate **		The work request is satisfied if possible.
35447c478bd9Sstevel@tonic-gate */
35457c478bd9Sstevel@tonic-gate 
35467c478bd9Sstevel@tonic-gate pid_t
dowork(qgrp,qdir,id,forkflag,requeueflag,e)35477c478bd9Sstevel@tonic-gate dowork(qgrp, qdir, id, forkflag, requeueflag, e)
35487c478bd9Sstevel@tonic-gate 	int qgrp;
35497c478bd9Sstevel@tonic-gate 	int qdir;
35507c478bd9Sstevel@tonic-gate 	char *id;
35517c478bd9Sstevel@tonic-gate 	bool forkflag;
35527c478bd9Sstevel@tonic-gate 	bool requeueflag;
35537c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
35547c478bd9Sstevel@tonic-gate {
35557c478bd9Sstevel@tonic-gate 	register pid_t pid;
35567c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
35577c478bd9Sstevel@tonic-gate 
35587c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
35597c478bd9Sstevel@tonic-gate 		sm_dprintf("dowork(%s/%s)\n", qid_printqueue(qgrp, qdir), id);
35607c478bd9Sstevel@tonic-gate 
35617c478bd9Sstevel@tonic-gate 	/*
35627c478bd9Sstevel@tonic-gate 	**  Fork for work.
35637c478bd9Sstevel@tonic-gate 	*/
35647c478bd9Sstevel@tonic-gate 
35657c478bd9Sstevel@tonic-gate 	if (forkflag)
35667c478bd9Sstevel@tonic-gate 	{
35677c478bd9Sstevel@tonic-gate 		/*
35687c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the
35697c478bd9Sstevel@tonic-gate 		**  parent does not wait, the parent may close the
35707c478bd9Sstevel@tonic-gate 		**  maps thereby removing any shared memory used by
35717c478bd9Sstevel@tonic-gate 		**  the map.  Therefore, close the maps now so the
35727c478bd9Sstevel@tonic-gate 		**  child will dynamically open them if necessary.
35737c478bd9Sstevel@tonic-gate 		*/
35747c478bd9Sstevel@tonic-gate 
35757c478bd9Sstevel@tonic-gate 		closemaps(false);
35767c478bd9Sstevel@tonic-gate 
35777c478bd9Sstevel@tonic-gate 		pid = fork();
35787c478bd9Sstevel@tonic-gate 		if (pid < 0)
35797c478bd9Sstevel@tonic-gate 		{
35807c478bd9Sstevel@tonic-gate 			syserr("dowork: cannot fork");
35817c478bd9Sstevel@tonic-gate 			return 0;
35827c478bd9Sstevel@tonic-gate 		}
35837c478bd9Sstevel@tonic-gate 		else if (pid > 0)
35847c478bd9Sstevel@tonic-gate 		{
35857c478bd9Sstevel@tonic-gate 			/* parent -- clean out connection cache */
35867c478bd9Sstevel@tonic-gate 			mci_flush(false, NULL);
35877c478bd9Sstevel@tonic-gate 		}
35887c478bd9Sstevel@tonic-gate 		else
35897c478bd9Sstevel@tonic-gate 		{
35907c478bd9Sstevel@tonic-gate 			/*
35917c478bd9Sstevel@tonic-gate 			**  Initialize exception stack and default exception
35927c478bd9Sstevel@tonic-gate 			**  handler for child process.
35937c478bd9Sstevel@tonic-gate 			*/
35947c478bd9Sstevel@tonic-gate 
35957c478bd9Sstevel@tonic-gate 			/* Reset global flags */
35967c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
35977c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
35987c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
35997c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
36007c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
36017c478bd9Sstevel@tonic-gate 			sm_exc_newthread(fatal_error);
36027c478bd9Sstevel@tonic-gate 
36037c478bd9Sstevel@tonic-gate 			/*
36047c478bd9Sstevel@tonic-gate 			**  See note above about SMTP processes and SIGCHLD.
36057c478bd9Sstevel@tonic-gate 			*/
36067c478bd9Sstevel@tonic-gate 
36077c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP ||
36087c478bd9Sstevel@tonic-gate 			    OpMode == MD_DAEMON ||
36097c478bd9Sstevel@tonic-gate 			    MaxQueueChildren > 0)
36107c478bd9Sstevel@tonic-gate 			{
36117c478bd9Sstevel@tonic-gate 				proc_list_clear();
36127c478bd9Sstevel@tonic-gate 				sm_releasesignal(SIGCHLD);
36137c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, SIG_DFL);
36147c478bd9Sstevel@tonic-gate 			}
36157c478bd9Sstevel@tonic-gate 
36167c478bd9Sstevel@tonic-gate 			/* child -- error messages to the transcript */
36177c478bd9Sstevel@tonic-gate 			QuickAbort = OnlyOneError = false;
36187c478bd9Sstevel@tonic-gate 		}
36197c478bd9Sstevel@tonic-gate 	}
36207c478bd9Sstevel@tonic-gate 	else
36217c478bd9Sstevel@tonic-gate 	{
36227c478bd9Sstevel@tonic-gate 		pid = 0;
36237c478bd9Sstevel@tonic-gate 	}
36247c478bd9Sstevel@tonic-gate 
36257c478bd9Sstevel@tonic-gate 	if (pid == 0)
36267c478bd9Sstevel@tonic-gate 	{
36277c478bd9Sstevel@tonic-gate 		/*
36287c478bd9Sstevel@tonic-gate 		**  CHILD
36297c478bd9Sstevel@tonic-gate 		**	Lock the control file to avoid duplicate deliveries.
36307c478bd9Sstevel@tonic-gate 		**		Then run the file as though we had just read it.
36317c478bd9Sstevel@tonic-gate 		**	We save an idea of the temporary name so we
36327c478bd9Sstevel@tonic-gate 		**		can recover on interrupt.
36337c478bd9Sstevel@tonic-gate 		*/
36347c478bd9Sstevel@tonic-gate 
36357c478bd9Sstevel@tonic-gate 		if (forkflag)
36367c478bd9Sstevel@tonic-gate 		{
36377c478bd9Sstevel@tonic-gate 			/* Reset global flags */
36387c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
36397c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
36407c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
36417c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
36427c478bd9Sstevel@tonic-gate 		}
36437c478bd9Sstevel@tonic-gate 
36447c478bd9Sstevel@tonic-gate 		/* set basic modes, etc. */
36457c478bd9Sstevel@tonic-gate 		sm_clear_events();
36467c478bd9Sstevel@tonic-gate 		clearstats();
36477c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
36487c478bd9Sstevel@tonic-gate 		clearenvelope(e, false, rpool);
36497c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
36507c478bd9Sstevel@tonic-gate 		set_delivery_mode(SM_DELIVER, e);
36517c478bd9Sstevel@tonic-gate 		e->e_errormode = EM_MAIL;
36527c478bd9Sstevel@tonic-gate 		e->e_id = id;
36537c478bd9Sstevel@tonic-gate 		e->e_qgrp = qgrp;
36547c478bd9Sstevel@tonic-gate 		e->e_qdir = qdir;
36557c478bd9Sstevel@tonic-gate 		GrabTo = UseErrorsTo = false;
36567c478bd9Sstevel@tonic-gate 		ExitStat = EX_OK;
36577c478bd9Sstevel@tonic-gate 		if (forkflag)
36587c478bd9Sstevel@tonic-gate 		{
36597c478bd9Sstevel@tonic-gate 			disconnect(1, e);
36607c478bd9Sstevel@tonic-gate 			set_op_mode(MD_QUEUERUN);
36617c478bd9Sstevel@tonic-gate 		}
36627c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, e, "%s from queue", qid_printname(e));
36637c478bd9Sstevel@tonic-gate 		if (LogLevel > 76)
36647c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "dowork, pid=%d",
36657c478bd9Sstevel@tonic-gate 				  (int) CurrentPid);
36667c478bd9Sstevel@tonic-gate 
36677c478bd9Sstevel@tonic-gate 		/* don't use the headers from sendmail.cf... */
36687c478bd9Sstevel@tonic-gate 		e->e_header = NULL;
36697c478bd9Sstevel@tonic-gate 
36707c478bd9Sstevel@tonic-gate 		/* read the queue control file -- return if locked */
36717c478bd9Sstevel@tonic-gate 		if (!readqf(e, false))
36727c478bd9Sstevel@tonic-gate 		{
36737c478bd9Sstevel@tonic-gate 			if (tTd(40, 4) && e->e_id != NULL)
36747c478bd9Sstevel@tonic-gate 				sm_dprintf("readqf(%s) failed\n",
36757c478bd9Sstevel@tonic-gate 					qid_printname(e));
36767c478bd9Sstevel@tonic-gate 			e->e_id = NULL;
36777c478bd9Sstevel@tonic-gate 			if (forkflag)
36787c478bd9Sstevel@tonic-gate 				finis(false, true, EX_OK);
36797c478bd9Sstevel@tonic-gate 			else
36807c478bd9Sstevel@tonic-gate 			{
36817c478bd9Sstevel@tonic-gate 				/* adding this frees 8 bytes */
36827c478bd9Sstevel@tonic-gate 				clearenvelope(e, false, rpool);
36837c478bd9Sstevel@tonic-gate 
36847c478bd9Sstevel@tonic-gate 				/* adding this frees 12 bytes */
36857c478bd9Sstevel@tonic-gate 				sm_rpool_free(rpool);
36867c478bd9Sstevel@tonic-gate 				e->e_rpool = NULL;
36877c478bd9Sstevel@tonic-gate 				return 0;
36887c478bd9Sstevel@tonic-gate 			}
36897c478bd9Sstevel@tonic-gate 		}
36907c478bd9Sstevel@tonic-gate 
36917c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_INQUEUE;
36927c478bd9Sstevel@tonic-gate 		eatheader(e, requeueflag, true);
36937c478bd9Sstevel@tonic-gate 
36947c478bd9Sstevel@tonic-gate 		if (requeueflag)
36957c478bd9Sstevel@tonic-gate 			queueup(e, false, false);
36967c478bd9Sstevel@tonic-gate 
36977c478bd9Sstevel@tonic-gate 		/* do the delivery */
36987c478bd9Sstevel@tonic-gate 		sendall(e, SM_DELIVER);
36997c478bd9Sstevel@tonic-gate 
37007c478bd9Sstevel@tonic-gate 		/* finish up and exit */
37017c478bd9Sstevel@tonic-gate 		if (forkflag)
37027c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
37037c478bd9Sstevel@tonic-gate 		else
37047c478bd9Sstevel@tonic-gate 		{
3705e9af4bc0SJohn Beck 			(void) dropenvelope(e, true, false);
37067c478bd9Sstevel@tonic-gate 			sm_rpool_free(rpool);
37077c478bd9Sstevel@tonic-gate 			e->e_rpool = NULL;
37087c478bd9Sstevel@tonic-gate 		}
37097c478bd9Sstevel@tonic-gate 	}
37107c478bd9Sstevel@tonic-gate 	e->e_id = NULL;
37117c478bd9Sstevel@tonic-gate 	return pid;
37127c478bd9Sstevel@tonic-gate }
37137c478bd9Sstevel@tonic-gate 
37147c478bd9Sstevel@tonic-gate /*
37157c478bd9Sstevel@tonic-gate **  DOWORKLIST -- process a list of envelopes as work requests
37167c478bd9Sstevel@tonic-gate **
37177c478bd9Sstevel@tonic-gate **	Similar to dowork(), except that after forking, it processes an
37187c478bd9Sstevel@tonic-gate **	envelope and its siblings, treating each envelope as a work request.
37197c478bd9Sstevel@tonic-gate **
37207c478bd9Sstevel@tonic-gate **	Parameters:
37217c478bd9Sstevel@tonic-gate **		el -- envelope to be processed including its siblings.
37227c478bd9Sstevel@tonic-gate **		forkflag -- if set, run this in background.
37237c478bd9Sstevel@tonic-gate **		requeueflag -- if set, reinstantiate the queue quickly.
37247c478bd9Sstevel@tonic-gate **			This is used when expanding aliases in the queue.
37257c478bd9Sstevel@tonic-gate **			If forkflag is also set, it doesn't wait for the
37267c478bd9Sstevel@tonic-gate **			child.
37277c478bd9Sstevel@tonic-gate **
37287c478bd9Sstevel@tonic-gate **	Returns:
37297c478bd9Sstevel@tonic-gate **		process id of process that is running the queue job.
37307c478bd9Sstevel@tonic-gate **
37317c478bd9Sstevel@tonic-gate **	Side Effects:
37327c478bd9Sstevel@tonic-gate **		The work request is satisfied if possible.
37337c478bd9Sstevel@tonic-gate */
37347c478bd9Sstevel@tonic-gate 
37357c478bd9Sstevel@tonic-gate pid_t
doworklist(el,forkflag,requeueflag)37367c478bd9Sstevel@tonic-gate doworklist(el, forkflag, requeueflag)
37377c478bd9Sstevel@tonic-gate 	ENVELOPE *el;
37387c478bd9Sstevel@tonic-gate 	bool forkflag;
37397c478bd9Sstevel@tonic-gate 	bool requeueflag;
37407c478bd9Sstevel@tonic-gate {
37417c478bd9Sstevel@tonic-gate 	register pid_t pid;
37427c478bd9Sstevel@tonic-gate 	ENVELOPE *ei;
37437c478bd9Sstevel@tonic-gate 
37447c478bd9Sstevel@tonic-gate 	if (tTd(40, 1))
37457c478bd9Sstevel@tonic-gate 		sm_dprintf("doworklist()\n");
37467c478bd9Sstevel@tonic-gate 
37477c478bd9Sstevel@tonic-gate 	/*
37487c478bd9Sstevel@tonic-gate 	**  Fork for work.
37497c478bd9Sstevel@tonic-gate 	*/
37507c478bd9Sstevel@tonic-gate 
37517c478bd9Sstevel@tonic-gate 	if (forkflag)
37527c478bd9Sstevel@tonic-gate 	{
37537c478bd9Sstevel@tonic-gate 		/*
37547c478bd9Sstevel@tonic-gate 		**  Since the delivery may happen in a child and the
37557c478bd9Sstevel@tonic-gate 		**  parent does not wait, the parent may close the
37567c478bd9Sstevel@tonic-gate 		**  maps thereby removing any shared memory used by
37577c478bd9Sstevel@tonic-gate 		**  the map.  Therefore, close the maps now so the
37587c478bd9Sstevel@tonic-gate 		**  child will dynamically open them if necessary.
37597c478bd9Sstevel@tonic-gate 		*/
37607c478bd9Sstevel@tonic-gate 
37617c478bd9Sstevel@tonic-gate 		closemaps(false);
37627c478bd9Sstevel@tonic-gate 
37637c478bd9Sstevel@tonic-gate 		pid = fork();
37647c478bd9Sstevel@tonic-gate 		if (pid < 0)
37657c478bd9Sstevel@tonic-gate 		{
37667c478bd9Sstevel@tonic-gate 			syserr("doworklist: cannot fork");
37677c478bd9Sstevel@tonic-gate 			return 0;
37687c478bd9Sstevel@tonic-gate 		}
37697c478bd9Sstevel@tonic-gate 		else if (pid > 0)
37707c478bd9Sstevel@tonic-gate 		{
37717c478bd9Sstevel@tonic-gate 			/* parent -- clean out connection cache */
37727c478bd9Sstevel@tonic-gate 			mci_flush(false, NULL);
37737c478bd9Sstevel@tonic-gate 		}
37747c478bd9Sstevel@tonic-gate 		else
37757c478bd9Sstevel@tonic-gate 		{
37767c478bd9Sstevel@tonic-gate 			/*
37777c478bd9Sstevel@tonic-gate 			**  Initialize exception stack and default exception
37787c478bd9Sstevel@tonic-gate 			**  handler for child process.
37797c478bd9Sstevel@tonic-gate 			*/
37807c478bd9Sstevel@tonic-gate 
37817c478bd9Sstevel@tonic-gate 			/* Reset global flags */
37827c478bd9Sstevel@tonic-gate 			RestartRequest = NULL;
37837c478bd9Sstevel@tonic-gate 			RestartWorkGroup = false;
37847c478bd9Sstevel@tonic-gate 			ShutdownRequest = NULL;
37857c478bd9Sstevel@tonic-gate 			PendingSignal = 0;
37867c478bd9Sstevel@tonic-gate 			CurrentPid = getpid();
37877c478bd9Sstevel@tonic-gate 			sm_exc_newthread(fatal_error);
37887c478bd9Sstevel@tonic-gate 
37897c478bd9Sstevel@tonic-gate 			/*
37907c478bd9Sstevel@tonic-gate 			**  See note above about SMTP processes and SIGCHLD.
37917c478bd9Sstevel@tonic-gate 			*/
37927c478bd9Sstevel@tonic-gate 
37937c478bd9Sstevel@tonic-gate 			if (OpMode == MD_SMTP ||
37947c478bd9Sstevel@tonic-gate 			    OpMode == MD_DAEMON ||
37957c478bd9Sstevel@tonic-gate 			    MaxQueueChildren > 0)
37967c478bd9Sstevel@tonic-gate 			{
37977c478bd9Sstevel@tonic-gate 				proc_list_clear();
37987c478bd9Sstevel@tonic-gate 				sm_releasesignal(SIGCHLD);
37997c478bd9Sstevel@tonic-gate 				(void) sm_signal(SIGCHLD, SIG_DFL);
38007c478bd9Sstevel@tonic-gate 			}
38017c478bd9Sstevel@tonic-gate 
38027c478bd9Sstevel@tonic-gate 			/* child -- error messages to the transcript */
38037c478bd9Sstevel@tonic-gate 			QuickAbort = OnlyOneError = false;
38047c478bd9Sstevel@tonic-gate 		}
38057c478bd9Sstevel@tonic-gate 	}
38067c478bd9Sstevel@tonic-gate 	else
38077c478bd9Sstevel@tonic-gate 	{
38087c478bd9Sstevel@tonic-gate 		pid = 0;
38097c478bd9Sstevel@tonic-gate 	}
38107c478bd9Sstevel@tonic-gate 
38117c478bd9Sstevel@tonic-gate 	if (pid != 0)
38127c478bd9Sstevel@tonic-gate 		return pid;
38137c478bd9Sstevel@tonic-gate 
38147c478bd9Sstevel@tonic-gate 	/*
38157c478bd9Sstevel@tonic-gate 	**  IN CHILD
38167c478bd9Sstevel@tonic-gate 	**	Lock the control file to avoid duplicate deliveries.
38177c478bd9Sstevel@tonic-gate 	**		Then run the file as though we had just read it.
38187c478bd9Sstevel@tonic-gate 	**	We save an idea of the temporary name so we
38197c478bd9Sstevel@tonic-gate 	**		can recover on interrupt.
38207c478bd9Sstevel@tonic-gate 	*/
38217c478bd9Sstevel@tonic-gate 
38227c478bd9Sstevel@tonic-gate 	if (forkflag)
38237c478bd9Sstevel@tonic-gate 	{
38247c478bd9Sstevel@tonic-gate 		/* Reset global flags */
38257c478bd9Sstevel@tonic-gate 		RestartRequest = NULL;
38267c478bd9Sstevel@tonic-gate 		RestartWorkGroup = false;
38277c478bd9Sstevel@tonic-gate 		ShutdownRequest = NULL;
38287c478bd9Sstevel@tonic-gate 		PendingSignal = 0;
38297c478bd9Sstevel@tonic-gate 	}
38307c478bd9Sstevel@tonic-gate 
38317c478bd9Sstevel@tonic-gate 	/* set basic modes, etc. */
38327c478bd9Sstevel@tonic-gate 	sm_clear_events();
38337c478bd9Sstevel@tonic-gate 	clearstats();
38347c478bd9Sstevel@tonic-gate 	GrabTo = UseErrorsTo = false;
38357c478bd9Sstevel@tonic-gate 	ExitStat = EX_OK;
38367c478bd9Sstevel@tonic-gate 	if (forkflag)
38377c478bd9Sstevel@tonic-gate 	{
38387c478bd9Sstevel@tonic-gate 		disconnect(1, el);
38397c478bd9Sstevel@tonic-gate 		set_op_mode(MD_QUEUERUN);
38407c478bd9Sstevel@tonic-gate 	}
38417c478bd9Sstevel@tonic-gate 	if (LogLevel > 76)
38427c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, el->e_id, "doworklist, pid=%d",
38437c478bd9Sstevel@tonic-gate 			  (int) CurrentPid);
38447c478bd9Sstevel@tonic-gate 
38457c478bd9Sstevel@tonic-gate 	for (ei = el; ei != NULL; ei = ei->e_sibling)
38467c478bd9Sstevel@tonic-gate 	{
38477c478bd9Sstevel@tonic-gate 		ENVELOPE e;
38487c478bd9Sstevel@tonic-gate 		SM_RPOOL_T *rpool;
38497c478bd9Sstevel@tonic-gate 
38507c478bd9Sstevel@tonic-gate 		if (WILL_BE_QUEUED(ei->e_sendmode))
38517c478bd9Sstevel@tonic-gate 			continue;
38527c478bd9Sstevel@tonic-gate 		else if (QueueMode != QM_QUARANTINE &&
38537c478bd9Sstevel@tonic-gate 			 ei->e_quarmsg != NULL)
38547c478bd9Sstevel@tonic-gate 			continue;
38557c478bd9Sstevel@tonic-gate 
38567c478bd9Sstevel@tonic-gate 		rpool = sm_rpool_new_x(NULL);
38577c478bd9Sstevel@tonic-gate 		clearenvelope(&e, true, rpool);
38587c478bd9Sstevel@tonic-gate 		e.e_flags |= EF_QUEUERUN|EF_GLOBALERRS;
38597c478bd9Sstevel@tonic-gate 		set_delivery_mode(SM_DELIVER, &e);
38607c478bd9Sstevel@tonic-gate 		e.e_errormode = EM_MAIL;
38617c478bd9Sstevel@tonic-gate 		e.e_id = ei->e_id;
38627c478bd9Sstevel@tonic-gate 		e.e_qgrp = ei->e_qgrp;
38637c478bd9Sstevel@tonic-gate 		e.e_qdir = ei->e_qdir;
38647c478bd9Sstevel@tonic-gate 		openxscript(&e);
38657c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, &e, "%s from queue", qid_printname(&e));
38667c478bd9Sstevel@tonic-gate 
38677c478bd9Sstevel@tonic-gate 		/* don't use the headers from sendmail.cf... */
38687c478bd9Sstevel@tonic-gate 		e.e_header = NULL;
38697c478bd9Sstevel@tonic-gate 		CurEnv = &e;
38707c478bd9Sstevel@tonic-gate 
38717c478bd9Sstevel@tonic-gate 		/* read the queue control file -- return if locked */
38727c478bd9Sstevel@tonic-gate 		if (readqf(&e, false))
38737c478bd9Sstevel@tonic-gate 		{
38747c478bd9Sstevel@tonic-gate 			e.e_flags |= EF_INQUEUE;
38757c478bd9Sstevel@tonic-gate 			eatheader(&e, requeueflag, true);
38767c478bd9Sstevel@tonic-gate 
38777c478bd9Sstevel@tonic-gate 			if (requeueflag)
38787c478bd9Sstevel@tonic-gate 				queueup(&e, false, false);
38797c478bd9Sstevel@tonic-gate 
38807c478bd9Sstevel@tonic-gate 			/* do the delivery */
38817c478bd9Sstevel@tonic-gate 			sendall(&e, SM_DELIVER);
3882e9af4bc0SJohn Beck 			(void) dropenvelope(&e, true, false);
38837c478bd9Sstevel@tonic-gate 		}
38847c478bd9Sstevel@tonic-gate 		else
38857c478bd9Sstevel@tonic-gate 		{
38867c478bd9Sstevel@tonic-gate 			if (tTd(40, 4) && e.e_id != NULL)
38877c478bd9Sstevel@tonic-gate 				sm_dprintf("readqf(%s) failed\n",
38887c478bd9Sstevel@tonic-gate 					qid_printname(&e));
38897c478bd9Sstevel@tonic-gate 		}
38907c478bd9Sstevel@tonic-gate 		sm_rpool_free(rpool);
38917c478bd9Sstevel@tonic-gate 		ei->e_id = NULL;
38927c478bd9Sstevel@tonic-gate 	}
38937c478bd9Sstevel@tonic-gate 
38947c478bd9Sstevel@tonic-gate 	/* restore CurEnv */
38957c478bd9Sstevel@tonic-gate 	CurEnv = el;
38967c478bd9Sstevel@tonic-gate 
38977c478bd9Sstevel@tonic-gate 	/* finish up and exit */
38987c478bd9Sstevel@tonic-gate 	if (forkflag)
38997c478bd9Sstevel@tonic-gate 		finis(true, true, ExitStat);
39007c478bd9Sstevel@tonic-gate 	return 0;
39017c478bd9Sstevel@tonic-gate }
39027c478bd9Sstevel@tonic-gate /*
39037c478bd9Sstevel@tonic-gate **  READQF -- read queue file and set up environment.
39047c478bd9Sstevel@tonic-gate **
39057c478bd9Sstevel@tonic-gate **	Parameters:
39067c478bd9Sstevel@tonic-gate **		e -- the envelope of the job to run.
39077c478bd9Sstevel@tonic-gate **		openonly -- only open the qf (returned as e_lockfp)
39087c478bd9Sstevel@tonic-gate **
39097c478bd9Sstevel@tonic-gate **	Returns:
39107c478bd9Sstevel@tonic-gate **		true if it successfully read the queue file.
39117c478bd9Sstevel@tonic-gate **		false otherwise.
39127c478bd9Sstevel@tonic-gate **
39137c478bd9Sstevel@tonic-gate **	Side Effects:
39147c478bd9Sstevel@tonic-gate **		The queue file is returned locked.
39157c478bd9Sstevel@tonic-gate */
39167c478bd9Sstevel@tonic-gate 
39177c478bd9Sstevel@tonic-gate static bool
readqf(e,openonly)39187c478bd9Sstevel@tonic-gate readqf(e, openonly)
39197c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
39207c478bd9Sstevel@tonic-gate 	bool openonly;
39217c478bd9Sstevel@tonic-gate {
39227c478bd9Sstevel@tonic-gate 	register SM_FILE_T *qfp;
39237c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
39247c478bd9Sstevel@tonic-gate 	struct stat st, stf;
39257c478bd9Sstevel@tonic-gate 	char *bp;
39267c478bd9Sstevel@tonic-gate 	int qfver = 0;
39277c478bd9Sstevel@tonic-gate 	long hdrsize = 0;
39287c478bd9Sstevel@tonic-gate 	register char *p;
39297c478bd9Sstevel@tonic-gate 	char *frcpt = NULL;
39307c478bd9Sstevel@tonic-gate 	char *orcpt = NULL;
39317c478bd9Sstevel@tonic-gate 	bool nomore = false;
39327c478bd9Sstevel@tonic-gate 	bool bogus = false;
39337c478bd9Sstevel@tonic-gate 	MODE_T qsafe;
39347c478bd9Sstevel@tonic-gate 	char *err;
39357c478bd9Sstevel@tonic-gate 	char qf[MAXPATHLEN];
39367c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
3937058561cbSjbeck 	int bufsize;
39387c478bd9Sstevel@tonic-gate 
39397c478bd9Sstevel@tonic-gate 	/*
39407c478bd9Sstevel@tonic-gate 	**  Read and process the file.
39417c478bd9Sstevel@tonic-gate 	*/
39427c478bd9Sstevel@tonic-gate 
39434aac33d3Sjbeck 	SM_REQUIRE(e != NULL);
39443ee0e492Sjbeck 	bp = NULL;
3945058561cbSjbeck 	(void) sm_strlcpy(qf, queuename(e, ANYQFL_LETTER), sizeof(qf));
39467c478bd9Sstevel@tonic-gate 	qfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDWR_B, NULL);
39477c478bd9Sstevel@tonic-gate 	if (qfp == NULL)
39487c478bd9Sstevel@tonic-gate 	{
39497c478bd9Sstevel@tonic-gate 		int save_errno = errno;
39507c478bd9Sstevel@tonic-gate 
39517c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39527c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): sm_io_open failure (%s)\n",
39537c478bd9Sstevel@tonic-gate 				qf, sm_errstring(errno));
39547c478bd9Sstevel@tonic-gate 		errno = save_errno;
39557c478bd9Sstevel@tonic-gate 		if (errno != ENOENT
39567c478bd9Sstevel@tonic-gate 		    )
39577c478bd9Sstevel@tonic-gate 			syserr("readqf: no control file %s", qf);
39587c478bd9Sstevel@tonic-gate 		RELEASE_QUEUE;
39597c478bd9Sstevel@tonic-gate 		return false;
39607c478bd9Sstevel@tonic-gate 	}
39617c478bd9Sstevel@tonic-gate 
39627c478bd9Sstevel@tonic-gate 	if (!lockfile(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), qf, NULL,
39637c478bd9Sstevel@tonic-gate 		      LOCK_EX|LOCK_NB))
39647c478bd9Sstevel@tonic-gate 	{
39657c478bd9Sstevel@tonic-gate 		/* being processed by another queuer */
39667c478bd9Sstevel@tonic-gate 		if (Verbose)
39677c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
39687c478bd9Sstevel@tonic-gate 					     "%s: locked\n", e->e_id);
39697c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
39707c478bd9Sstevel@tonic-gate 			sm_dprintf("%s: locked\n", e->e_id);
39717c478bd9Sstevel@tonic-gate 		if (LogLevel > 19)
39727c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "locked");
39737c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
39747c478bd9Sstevel@tonic-gate 		RELEASE_QUEUE;
39757c478bd9Sstevel@tonic-gate 		return false;
39767c478bd9Sstevel@tonic-gate 	}
39777c478bd9Sstevel@tonic-gate 
39787c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
39797c478bd9Sstevel@tonic-gate 
39807c478bd9Sstevel@tonic-gate 	/*
39817c478bd9Sstevel@tonic-gate 	**  Prevent locking race condition.
39827c478bd9Sstevel@tonic-gate 	**
39837c478bd9Sstevel@tonic-gate 	**  Process A: readqf(): qfp = fopen(qffile)
39847c478bd9Sstevel@tonic-gate 	**  Process B: queueup(): rename(tf, qf)
39857c478bd9Sstevel@tonic-gate 	**  Process B: unlocks(tf)
39867c478bd9Sstevel@tonic-gate 	**  Process A: lockfile(qf);
39877c478bd9Sstevel@tonic-gate 	**
39887c478bd9Sstevel@tonic-gate 	**  Process A (us) has the old qf file (before the rename deleted
39897c478bd9Sstevel@tonic-gate 	**  the directory entry) and will be delivering based on old data.
39907c478bd9Sstevel@tonic-gate 	**  This can lead to multiple deliveries of the same recipients.
39917c478bd9Sstevel@tonic-gate 	**
39927c478bd9Sstevel@tonic-gate 	**  Catch this by checking if the underlying qf file has changed
39937c478bd9Sstevel@tonic-gate 	**  *after* acquiring our lock and if so, act as though the file
39947c478bd9Sstevel@tonic-gate 	**  was still locked (i.e., just return like the lockfile() case
39957c478bd9Sstevel@tonic-gate 	**  above.
39967c478bd9Sstevel@tonic-gate 	*/
39977c478bd9Sstevel@tonic-gate 
39987c478bd9Sstevel@tonic-gate 	if (stat(qf, &stf) < 0 ||
39997c478bd9Sstevel@tonic-gate 	    fstat(sm_io_getinfo(qfp, SM_IO_WHAT_FD, NULL), &st) < 0)
40007c478bd9Sstevel@tonic-gate 	{
40017c478bd9Sstevel@tonic-gate 		/* must have been being processed by someone else */
40027c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
40037c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): [f]stat failure (%s)\n",
40047c478bd9Sstevel@tonic-gate 				qf, sm_errstring(errno));
40057c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40067c478bd9Sstevel@tonic-gate 		return false;
40077c478bd9Sstevel@tonic-gate 	}
40087c478bd9Sstevel@tonic-gate 
40097c478bd9Sstevel@tonic-gate 	if (st.st_nlink != stf.st_nlink ||
40107c478bd9Sstevel@tonic-gate 	    st.st_dev != stf.st_dev ||
40117c478bd9Sstevel@tonic-gate 	    ST_INODE(st) != ST_INODE(stf) ||
40127c478bd9Sstevel@tonic-gate #if HAS_ST_GEN && 0		/* AFS returns garbage in st_gen */
40137c478bd9Sstevel@tonic-gate 	    st.st_gen != stf.st_gen ||
40147c478bd9Sstevel@tonic-gate #endif /* HAS_ST_GEN && 0 */
40157c478bd9Sstevel@tonic-gate 	    st.st_uid != stf.st_uid ||
40167c478bd9Sstevel@tonic-gate 	    st.st_gid != stf.st_gid ||
40177c478bd9Sstevel@tonic-gate 	    st.st_size != stf.st_size)
40187c478bd9Sstevel@tonic-gate 	{
40197c478bd9Sstevel@tonic-gate 		/* changed after opened */
40207c478bd9Sstevel@tonic-gate 		if (Verbose)
40217c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
40227c478bd9Sstevel@tonic-gate 					     "%s: changed\n", e->e_id);
40237c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
40247c478bd9Sstevel@tonic-gate 			sm_dprintf("%s: changed\n", e->e_id);
40257c478bd9Sstevel@tonic-gate 		if (LogLevel > 19)
40267c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, e->e_id, "changed");
40277c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40287c478bd9Sstevel@tonic-gate 		return false;
40297c478bd9Sstevel@tonic-gate 	}
40307c478bd9Sstevel@tonic-gate 
40317c478bd9Sstevel@tonic-gate 	/*
40327c478bd9Sstevel@tonic-gate 	**  Check the queue file for plausibility to avoid attacks.
40337c478bd9Sstevel@tonic-gate 	*/
40347c478bd9Sstevel@tonic-gate 
40357c478bd9Sstevel@tonic-gate 	qsafe = S_IWOTH|S_IWGRP;
40367c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
40377c478bd9Sstevel@tonic-gate 		qsafe &= ~S_IWGRP;
40387c478bd9Sstevel@tonic-gate 
40397c478bd9Sstevel@tonic-gate 	bogus = st.st_uid != geteuid() &&
40407c478bd9Sstevel@tonic-gate 		st.st_uid != TrustedUid &&
40417c478bd9Sstevel@tonic-gate 		geteuid() != RealUid;
40427c478bd9Sstevel@tonic-gate 
40437c478bd9Sstevel@tonic-gate 	/*
40447c478bd9Sstevel@tonic-gate 	**  If this qf file results from a set-group-ID binary, then
40457c478bd9Sstevel@tonic-gate 	**  we check whether the directory is group-writable,
40467c478bd9Sstevel@tonic-gate 	**  the queue file mode contains the group-writable bit, and
40477c478bd9Sstevel@tonic-gate 	**  the groups are the same.
40487c478bd9Sstevel@tonic-gate 	**  Notice: this requires that the set-group-ID binary is used to
40497c478bd9Sstevel@tonic-gate 	**  run the queue!
40507c478bd9Sstevel@tonic-gate 	*/
40517c478bd9Sstevel@tonic-gate 
40527c478bd9Sstevel@tonic-gate 	if (bogus && st.st_gid == getegid() && UseMSP)
40537c478bd9Sstevel@tonic-gate 	{
40547c478bd9Sstevel@tonic-gate 		char delim;
40557c478bd9Sstevel@tonic-gate 		struct stat dst;
40567c478bd9Sstevel@tonic-gate 
40577c478bd9Sstevel@tonic-gate 		bp = SM_LAST_DIR_DELIM(qf);
40587c478bd9Sstevel@tonic-gate 		if (bp == NULL)
40597c478bd9Sstevel@tonic-gate 			delim = '\0';
40607c478bd9Sstevel@tonic-gate 		else
40617c478bd9Sstevel@tonic-gate 		{
40627c478bd9Sstevel@tonic-gate 			delim = *bp;
40637c478bd9Sstevel@tonic-gate 			*bp = '\0';
40647c478bd9Sstevel@tonic-gate 		}
40657c478bd9Sstevel@tonic-gate 		if (stat(delim == '\0' ? "." : qf, &dst) < 0)
40667c478bd9Sstevel@tonic-gate 			syserr("readqf: cannot stat directory %s",
40677c478bd9Sstevel@tonic-gate 				delim == '\0' ? "." : qf);
40687c478bd9Sstevel@tonic-gate 		else
40697c478bd9Sstevel@tonic-gate 		{
40707c478bd9Sstevel@tonic-gate 			bogus = !(bitset(S_IWGRP, QueueFileMode) &&
40717c478bd9Sstevel@tonic-gate 				  bitset(S_IWGRP, dst.st_mode) &&
40727c478bd9Sstevel@tonic-gate 				  dst.st_gid == st.st_gid);
40737c478bd9Sstevel@tonic-gate 		}
40747c478bd9Sstevel@tonic-gate 		if (delim != '\0')
40757c478bd9Sstevel@tonic-gate 			*bp = delim;
40763ee0e492Sjbeck 		bp = NULL;
40777c478bd9Sstevel@tonic-gate 	}
40787c478bd9Sstevel@tonic-gate 	if (!bogus)
40797c478bd9Sstevel@tonic-gate 		bogus = bitset(qsafe, st.st_mode);
40807c478bd9Sstevel@tonic-gate 	if (bogus)
40817c478bd9Sstevel@tonic-gate 	{
40827c478bd9Sstevel@tonic-gate 		if (LogLevel > 0)
40837c478bd9Sstevel@tonic-gate 		{
40847c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, e->e_id,
40857c478bd9Sstevel@tonic-gate 				  "bogus queue file, uid=%d, gid=%d, mode=%o",
40867c478bd9Sstevel@tonic-gate 				  st.st_uid, st.st_gid, st.st_mode);
40877c478bd9Sstevel@tonic-gate 		}
40887c478bd9Sstevel@tonic-gate 		if (tTd(40, 8))
40897c478bd9Sstevel@tonic-gate 			sm_dprintf("readqf(%s): bogus file\n", qf);
40907c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_INQUEUE;
40917c478bd9Sstevel@tonic-gate 		if (!openonly)
40927c478bd9Sstevel@tonic-gate 			loseqfile(e, "bogus file uid/gid in mqueue");
40937c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
40947c478bd9Sstevel@tonic-gate 		return false;
40957c478bd9Sstevel@tonic-gate 	}
40967c478bd9Sstevel@tonic-gate 
40977c478bd9Sstevel@tonic-gate 	if (st.st_size == 0)
40987c478bd9Sstevel@tonic-gate 	{
40997c478bd9Sstevel@tonic-gate 		/* must be a bogus file -- if also old, just remove it */
41007c478bd9Sstevel@tonic-gate 		if (!openonly && st.st_ctime + 10 * 60 < curtime())
41017c478bd9Sstevel@tonic-gate 		{
41027c478bd9Sstevel@tonic-gate 			(void) xunlink(queuename(e, DATAFL_LETTER));
41037c478bd9Sstevel@tonic-gate 			(void) xunlink(queuename(e, ANYQFL_LETTER));
41047c478bd9Sstevel@tonic-gate 		}
41057c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
41067c478bd9Sstevel@tonic-gate 		return false;
41077c478bd9Sstevel@tonic-gate 	}
41087c478bd9Sstevel@tonic-gate 
41097c478bd9Sstevel@tonic-gate 	if (st.st_nlink == 0)
41107c478bd9Sstevel@tonic-gate 	{
41117c478bd9Sstevel@tonic-gate 		/*
41127c478bd9Sstevel@tonic-gate 		**  Race condition -- we got a file just as it was being
41137c478bd9Sstevel@tonic-gate 		**  unlinked.  Just assume it is zero length.
41147c478bd9Sstevel@tonic-gate 		*/
41157c478bd9Sstevel@tonic-gate 
41167c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
41177c478bd9Sstevel@tonic-gate 		return false;
41187c478bd9Sstevel@tonic-gate 	}
41197c478bd9Sstevel@tonic-gate 
41207c478bd9Sstevel@tonic-gate #if _FFR_TRUSTED_QF
41217c478bd9Sstevel@tonic-gate 	/*
41227c478bd9Sstevel@tonic-gate 	**  If we don't own the file mark it as unsafe.
41237c478bd9Sstevel@tonic-gate 	**  However, allow TrustedUser to own it as well
41247c478bd9Sstevel@tonic-gate 	**  in case TrustedUser manipulates the queue.
41257c478bd9Sstevel@tonic-gate 	*/
41267c478bd9Sstevel@tonic-gate 
41277c478bd9Sstevel@tonic-gate 	if (st.st_uid != geteuid() && st.st_uid != TrustedUid)
41287c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_UNSAFE;
41297c478bd9Sstevel@tonic-gate #else /* _FFR_TRUSTED_QF */
41307c478bd9Sstevel@tonic-gate 	/* If we don't own the file mark it as unsafe */
41317c478bd9Sstevel@tonic-gate 	if (st.st_uid != geteuid())
41327c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_UNSAFE;
41337c478bd9Sstevel@tonic-gate #endif /* _FFR_TRUSTED_QF */
41347c478bd9Sstevel@tonic-gate 
41357c478bd9Sstevel@tonic-gate 	/* good file -- save this lock */
41367c478bd9Sstevel@tonic-gate 	e->e_lockfp = qfp;
41377c478bd9Sstevel@tonic-gate 
41387c478bd9Sstevel@tonic-gate 	/* Just wanted the open file */
41397c478bd9Sstevel@tonic-gate 	if (openonly)
41407c478bd9Sstevel@tonic-gate 		return true;
41417c478bd9Sstevel@tonic-gate 
41427c478bd9Sstevel@tonic-gate 	/* do basic system initialization */
41437c478bd9Sstevel@tonic-gate 	initsys(e);
41447c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
41457c478bd9Sstevel@tonic-gate 
41467c478bd9Sstevel@tonic-gate 	LineNumber = 0;
41477c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_GLOBALERRS;
41487c478bd9Sstevel@tonic-gate 	set_op_mode(MD_QUEUERUN);
41497c478bd9Sstevel@tonic-gate 	ctladdr = NULL;
41507c478bd9Sstevel@tonic-gate 	e->e_qfletter = queue_letter(e, ANYQFL_LETTER);
41517c478bd9Sstevel@tonic-gate 	e->e_dfqgrp = e->e_qgrp;
41527c478bd9Sstevel@tonic-gate 	e->e_dfqdir = e->e_qdir;
41537c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_MACRO
41547c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_TEMP, macid("{queue}"),
41557c478bd9Sstevel@tonic-gate 		  qid_printqueue(e->e_qgrp, e->e_qdir));
41567c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_MACRO */
41577c478bd9Sstevel@tonic-gate 	e->e_dfino = -1;
41587c478bd9Sstevel@tonic-gate 	e->e_msgsize = -1;
4159058561cbSjbeck 	while (bufsize = sizeof(buf),
4160058561cbSjbeck 	       (bp = fgetfolded(buf, &bufsize, qfp)) != NULL)
41617c478bd9Sstevel@tonic-gate 	{
41627c478bd9Sstevel@tonic-gate 		unsigned long qflags;
41637c478bd9Sstevel@tonic-gate 		ADDRESS *q;
41647c478bd9Sstevel@tonic-gate 		int r;
41657c478bd9Sstevel@tonic-gate 		time_t now;
41667c478bd9Sstevel@tonic-gate 		auto char *ep;
41677c478bd9Sstevel@tonic-gate 
41687c478bd9Sstevel@tonic-gate 		if (tTd(40, 4))
41697c478bd9Sstevel@tonic-gate 			sm_dprintf("+++++ %s\n", bp);
41707c478bd9Sstevel@tonic-gate 		if (nomore)
41717c478bd9Sstevel@tonic-gate 		{
41727c478bd9Sstevel@tonic-gate 			/* hack attack */
41737c478bd9Sstevel@tonic-gate   hackattack:
41747c478bd9Sstevel@tonic-gate 			syserr("SECURITY ALERT: extra or bogus data in queue file: %s",
41757c478bd9Sstevel@tonic-gate 			       bp);
41767c478bd9Sstevel@tonic-gate 			err = "bogus queue line";
41777c478bd9Sstevel@tonic-gate 			goto fail;
41787c478bd9Sstevel@tonic-gate 		}
41797c478bd9Sstevel@tonic-gate 		switch (bp[0])
41807c478bd9Sstevel@tonic-gate 		{
41817c478bd9Sstevel@tonic-gate 		  case 'A':		/* AUTH= parameter */
41827c478bd9Sstevel@tonic-gate 			if (!xtextok(&bp[1]))
41837c478bd9Sstevel@tonic-gate 				goto hackattack;
41847c478bd9Sstevel@tonic-gate 			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
41857c478bd9Sstevel@tonic-gate 			break;
41867c478bd9Sstevel@tonic-gate 
41877c478bd9Sstevel@tonic-gate 		  case 'B':		/* body type */
41887c478bd9Sstevel@tonic-gate 			r = check_bodytype(&bp[1]);
41897c478bd9Sstevel@tonic-gate 			if (!BODYTYPE_VALID(r))
41907c478bd9Sstevel@tonic-gate 				goto hackattack;
41917c478bd9Sstevel@tonic-gate 			e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
41927c478bd9Sstevel@tonic-gate 			break;
41937c478bd9Sstevel@tonic-gate 
41947c478bd9Sstevel@tonic-gate 		  case 'C':		/* specify controlling user */
41957c478bd9Sstevel@tonic-gate 			ctladdr = setctluser(&bp[1], qfver, e);
41967c478bd9Sstevel@tonic-gate 			break;
41977c478bd9Sstevel@tonic-gate 
41987c478bd9Sstevel@tonic-gate 		  case 'D':		/* data file name */
41997c478bd9Sstevel@tonic-gate 			/* obsolete -- ignore */
42007c478bd9Sstevel@tonic-gate 			break;
42017c478bd9Sstevel@tonic-gate 
42027c478bd9Sstevel@tonic-gate 		  case 'd':		/* data file directory name */
42037c478bd9Sstevel@tonic-gate 			{
42047c478bd9Sstevel@tonic-gate 				int qgrp, qdir;
42057c478bd9Sstevel@tonic-gate 
42067c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
42077c478bd9Sstevel@tonic-gate 				/* forbid queue groups in MSP? */
42087c478bd9Sstevel@tonic-gate 				if (UseMSP)
42097c478bd9Sstevel@tonic-gate 					goto hackattack;
42107c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
42117c478bd9Sstevel@tonic-gate 				for (qgrp = 0;
42127c478bd9Sstevel@tonic-gate 				     qgrp < NumQueue && Queue[qgrp] != NULL;
42137c478bd9Sstevel@tonic-gate 				     ++qgrp)
42147c478bd9Sstevel@tonic-gate 				{
42157c478bd9Sstevel@tonic-gate 					for (qdir = 0;
42167c478bd9Sstevel@tonic-gate 					     qdir < Queue[qgrp]->qg_numqueues;
42177c478bd9Sstevel@tonic-gate 					     ++qdir)
42187c478bd9Sstevel@tonic-gate 					{
42197c478bd9Sstevel@tonic-gate 						if (strcmp(&bp[1],
42207c478bd9Sstevel@tonic-gate 							   Queue[qgrp]->qg_qpaths[qdir].qp_name)
42217c478bd9Sstevel@tonic-gate 						    == 0)
42227c478bd9Sstevel@tonic-gate 						{
42237c478bd9Sstevel@tonic-gate 							e->e_dfqgrp = qgrp;
42247c478bd9Sstevel@tonic-gate 							e->e_dfqdir = qdir;
42257c478bd9Sstevel@tonic-gate 							goto done;
42267c478bd9Sstevel@tonic-gate 						}
42277c478bd9Sstevel@tonic-gate 					}
42287c478bd9Sstevel@tonic-gate 				}
42297c478bd9Sstevel@tonic-gate 				err = "bogus queue file directory";
42307c478bd9Sstevel@tonic-gate 				goto fail;
42317c478bd9Sstevel@tonic-gate 			  done:
42327c478bd9Sstevel@tonic-gate 				break;
42337c478bd9Sstevel@tonic-gate 			}
42347c478bd9Sstevel@tonic-gate 
42357c478bd9Sstevel@tonic-gate 		  case 'E':		/* specify error recipient */
42367c478bd9Sstevel@tonic-gate 			/* no longer used */
42377c478bd9Sstevel@tonic-gate 			break;
42387c478bd9Sstevel@tonic-gate 
42397c478bd9Sstevel@tonic-gate 		  case 'F':		/* flag bits */
42407c478bd9Sstevel@tonic-gate 			if (strncmp(bp, "From ", 5) == 0)
42417c478bd9Sstevel@tonic-gate 			{
42427c478bd9Sstevel@tonic-gate 				/* we are being spoofed! */
42437c478bd9Sstevel@tonic-gate 				syserr("SECURITY ALERT: bogus qf line %s", bp);
42447c478bd9Sstevel@tonic-gate 				err = "bogus queue line";
42457c478bd9Sstevel@tonic-gate 				goto fail;
42467c478bd9Sstevel@tonic-gate 			}
42477c478bd9Sstevel@tonic-gate 			for (p = &bp[1]; *p != '\0'; p++)
42487c478bd9Sstevel@tonic-gate 			{
42497c478bd9Sstevel@tonic-gate 				switch (*p)
42507c478bd9Sstevel@tonic-gate 				{
42517c478bd9Sstevel@tonic-gate 				  case '8':	/* has 8 bit data */
42527c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_HAS8BIT;
42537c478bd9Sstevel@tonic-gate 					break;
42547c478bd9Sstevel@tonic-gate 
42557c478bd9Sstevel@tonic-gate 				  case 'b':	/* delete Bcc: header */
42567c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_DELETE_BCC;
42577c478bd9Sstevel@tonic-gate 					break;
42587c478bd9Sstevel@tonic-gate 
42597c478bd9Sstevel@tonic-gate 				  case 'd':	/* envelope has DSN RET= */
42607c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_RET_PARAM;
42617c478bd9Sstevel@tonic-gate 					break;
42627c478bd9Sstevel@tonic-gate 
42637c478bd9Sstevel@tonic-gate 				  case 'n':	/* don't return body */
42647c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_NO_BODY_RETN;
42657c478bd9Sstevel@tonic-gate 					break;
42667c478bd9Sstevel@tonic-gate 
42677c478bd9Sstevel@tonic-gate 				  case 'r':	/* response */
42687c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_RESPONSE;
42697c478bd9Sstevel@tonic-gate 					break;
42707c478bd9Sstevel@tonic-gate 
42717c478bd9Sstevel@tonic-gate 				  case 's':	/* split */
42727c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_SPLIT;
42737c478bd9Sstevel@tonic-gate 					break;
42747c478bd9Sstevel@tonic-gate 
42757c478bd9Sstevel@tonic-gate 				  case 'w':	/* warning sent */
42767c478bd9Sstevel@tonic-gate 					e->e_flags |= EF_WARNING;
42777c478bd9Sstevel@tonic-gate 					break;
42787c478bd9Sstevel@tonic-gate 				}
42797c478bd9Sstevel@tonic-gate 			}
42807c478bd9Sstevel@tonic-gate 			break;
42817c478bd9Sstevel@tonic-gate 
42827c478bd9Sstevel@tonic-gate 		  case 'q':		/* quarantine reason */
42837c478bd9Sstevel@tonic-gate 			e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
42847c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
42857c478bd9Sstevel@tonic-gate 				  macid("{quarantine}"), e->e_quarmsg);
42867c478bd9Sstevel@tonic-gate 			break;
42877c478bd9Sstevel@tonic-gate 
42887c478bd9Sstevel@tonic-gate 		  case 'H':		/* header */
42897c478bd9Sstevel@tonic-gate 
42907c478bd9Sstevel@tonic-gate 			/*
42917c478bd9Sstevel@tonic-gate 			**  count size before chompheader() destroys the line.
42927c478bd9Sstevel@tonic-gate 			**  this isn't accurate due to macro expansion, but
42937c478bd9Sstevel@tonic-gate 			**  better than before. "-3" to skip H?? at least.
42947c478bd9Sstevel@tonic-gate 			*/
42957c478bd9Sstevel@tonic-gate 
42967c478bd9Sstevel@tonic-gate 			hdrsize += strlen(bp) - 3;
42977c478bd9Sstevel@tonic-gate 			(void) chompheader(&bp[1], CHHDR_QUEUE, NULL, e);
42987c478bd9Sstevel@tonic-gate 			break;
42997c478bd9Sstevel@tonic-gate 
43007c478bd9Sstevel@tonic-gate 		  case 'I':		/* data file's inode number */
43017c478bd9Sstevel@tonic-gate 			/* regenerated below */
43027c478bd9Sstevel@tonic-gate 			break;
43037c478bd9Sstevel@tonic-gate 
43047c478bd9Sstevel@tonic-gate 		  case 'K':		/* time of last delivery attempt */
43057c478bd9Sstevel@tonic-gate 			e->e_dtime = atol(&buf[1]);
43067c478bd9Sstevel@tonic-gate 			break;
43077c478bd9Sstevel@tonic-gate 
43087c478bd9Sstevel@tonic-gate 		  case 'L':		/* Solaris Content-Length: */
43097c478bd9Sstevel@tonic-gate 		  case 'M':		/* message */
43107c478bd9Sstevel@tonic-gate 			/* ignore this; we want a new message next time */
43117c478bd9Sstevel@tonic-gate 			break;
43127c478bd9Sstevel@tonic-gate 
43137c478bd9Sstevel@tonic-gate 		  case 'N':		/* number of delivery attempts */
43147c478bd9Sstevel@tonic-gate 			e->e_ntries = atoi(&buf[1]);
43157c478bd9Sstevel@tonic-gate 
43167c478bd9Sstevel@tonic-gate 			/* if this has been tried recently, let it be */
43177c478bd9Sstevel@tonic-gate 			now = curtime();
43187c478bd9Sstevel@tonic-gate 			if (e->e_ntries > 0 && e->e_dtime <= now &&
43197c478bd9Sstevel@tonic-gate 			    now < e->e_dtime + MinQueueAge)
43207c478bd9Sstevel@tonic-gate 			{
43217c478bd9Sstevel@tonic-gate 				char *howlong;
43227c478bd9Sstevel@tonic-gate 
43237c478bd9Sstevel@tonic-gate 				howlong = pintvl(now - e->e_dtime, true);
43247c478bd9Sstevel@tonic-gate 				if (Verbose)
43257c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
43267c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
43277c478bd9Sstevel@tonic-gate 							     "%s: too young (%s)\n",
43287c478bd9Sstevel@tonic-gate 							     e->e_id, howlong);
43297c478bd9Sstevel@tonic-gate 				if (tTd(40, 8))
43307c478bd9Sstevel@tonic-gate 					sm_dprintf("%s: too young (%s)\n",
43317c478bd9Sstevel@tonic-gate 						e->e_id, howlong);
43327c478bd9Sstevel@tonic-gate 				if (LogLevel > 19)
43337c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_DEBUG, e->e_id,
43347c478bd9Sstevel@tonic-gate 						  "too young (%s)",
43357c478bd9Sstevel@tonic-gate 						  howlong);
43367c478bd9Sstevel@tonic-gate 				e->e_id = NULL;
43377c478bd9Sstevel@tonic-gate 				unlockqueue(e);
4338058561cbSjbeck 				if (bp != buf)
4339058561cbSjbeck 					sm_free(bp);
43407c478bd9Sstevel@tonic-gate 				return false;
43417c478bd9Sstevel@tonic-gate 			}
43427c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP,
43437c478bd9Sstevel@tonic-gate 				macid("{ntries}"), &buf[1]);
43447c478bd9Sstevel@tonic-gate 
43457c478bd9Sstevel@tonic-gate #if NAMED_BIND
43467c478bd9Sstevel@tonic-gate 			/* adjust BIND parameters immediately */
43477c478bd9Sstevel@tonic-gate 			if (e->e_ntries == 0)
43487c478bd9Sstevel@tonic-gate 			{
43497c478bd9Sstevel@tonic-gate 				_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
43507c478bd9Sstevel@tonic-gate 				_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
43517c478bd9Sstevel@tonic-gate 			}
43527c478bd9Sstevel@tonic-gate 			else
43537c478bd9Sstevel@tonic-gate 			{
43547c478bd9Sstevel@tonic-gate 				_res.retry = TimeOuts.res_retry[RES_TO_NORMAL];
43557c478bd9Sstevel@tonic-gate 				_res.retrans = TimeOuts.res_retrans[RES_TO_NORMAL];
43567c478bd9Sstevel@tonic-gate 			}
43577c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
43587c478bd9Sstevel@tonic-gate 			break;
43597c478bd9Sstevel@tonic-gate 
43607c478bd9Sstevel@tonic-gate 		  case 'P':		/* message priority */
43617c478bd9Sstevel@tonic-gate 			e->e_msgpriority = atol(&bp[1]) + WkTimeFact;
43627c478bd9Sstevel@tonic-gate 			break;
43637c478bd9Sstevel@tonic-gate 
43647c478bd9Sstevel@tonic-gate 		  case 'Q':		/* original recipient */
43657c478bd9Sstevel@tonic-gate 			orcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
43667c478bd9Sstevel@tonic-gate 			break;
43677c478bd9Sstevel@tonic-gate 
43687c478bd9Sstevel@tonic-gate 		  case 'r':		/* final recipient */
43697c478bd9Sstevel@tonic-gate 			frcpt = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
43707c478bd9Sstevel@tonic-gate 			break;
43717c478bd9Sstevel@tonic-gate 
43727c478bd9Sstevel@tonic-gate 		  case 'R':		/* specify recipient */
43737c478bd9Sstevel@tonic-gate 			p = bp;
43747c478bd9Sstevel@tonic-gate 			qflags = 0;
43757c478bd9Sstevel@tonic-gate 			if (qfver >= 1)
43767c478bd9Sstevel@tonic-gate 			{
43777c478bd9Sstevel@tonic-gate 				/* get flag bits */
43787c478bd9Sstevel@tonic-gate 				while (*++p != '\0' && *p != ':')
43797c478bd9Sstevel@tonic-gate 				{
43807c478bd9Sstevel@tonic-gate 					switch (*p)
43817c478bd9Sstevel@tonic-gate 					{
43827c478bd9Sstevel@tonic-gate 					  case 'N':
43837c478bd9Sstevel@tonic-gate 						qflags |= QHASNOTIFY;
43847c478bd9Sstevel@tonic-gate 						break;
43857c478bd9Sstevel@tonic-gate 
43867c478bd9Sstevel@tonic-gate 					  case 'S':
43877c478bd9Sstevel@tonic-gate 						qflags |= QPINGONSUCCESS;
43887c478bd9Sstevel@tonic-gate 						break;
43897c478bd9Sstevel@tonic-gate 
43907c478bd9Sstevel@tonic-gate 					  case 'F':
43917c478bd9Sstevel@tonic-gate 						qflags |= QPINGONFAILURE;
43927c478bd9Sstevel@tonic-gate 						break;
43937c478bd9Sstevel@tonic-gate 
43947c478bd9Sstevel@tonic-gate 					  case 'D':
43957c478bd9Sstevel@tonic-gate 						qflags |= QPINGONDELAY;
43967c478bd9Sstevel@tonic-gate 						break;
43977c478bd9Sstevel@tonic-gate 
43987c478bd9Sstevel@tonic-gate 					  case 'P':
43997c478bd9Sstevel@tonic-gate 						qflags |= QPRIMARY;
44007c478bd9Sstevel@tonic-gate 						break;
44017c478bd9Sstevel@tonic-gate 
44027c478bd9Sstevel@tonic-gate 					  case 'A':
44037c478bd9Sstevel@tonic-gate 						if (ctladdr != NULL)
44047c478bd9Sstevel@tonic-gate 							ctladdr->q_flags |= QALIAS;
44057c478bd9Sstevel@tonic-gate 						break;
44067c478bd9Sstevel@tonic-gate 
44077c478bd9Sstevel@tonic-gate 					  default: /* ignore or complain? */
44087c478bd9Sstevel@tonic-gate 						break;
44097c478bd9Sstevel@tonic-gate 					}
44107c478bd9Sstevel@tonic-gate 				}
44117c478bd9Sstevel@tonic-gate 			}
44127c478bd9Sstevel@tonic-gate 			else
44137c478bd9Sstevel@tonic-gate 				qflags |= QPRIMARY;
44147c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
44157c478bd9Sstevel@tonic-gate 				"e r");
44167c478bd9Sstevel@tonic-gate 			if (*p != '\0')
44177c478bd9Sstevel@tonic-gate 				q = parseaddr(++p, NULLADDR, RF_COPYALL, '\0',
44187c478bd9Sstevel@tonic-gate 						NULL, e, true);
44197c478bd9Sstevel@tonic-gate 			else
44207c478bd9Sstevel@tonic-gate 				q = NULL;
44217c478bd9Sstevel@tonic-gate 			if (q != NULL)
44227c478bd9Sstevel@tonic-gate 			{
44237c478bd9Sstevel@tonic-gate 				/* make sure we keep the current qgrp */
44247c478bd9Sstevel@tonic-gate 				if (ISVALIDQGRP(e->e_qgrp))
44257c478bd9Sstevel@tonic-gate 					q->q_qgrp = e->e_qgrp;
44267c478bd9Sstevel@tonic-gate 				q->q_alias = ctladdr;
44277c478bd9Sstevel@tonic-gate 				if (qfver >= 1)
44287c478bd9Sstevel@tonic-gate 					q->q_flags &= ~Q_PINGFLAGS;
44297c478bd9Sstevel@tonic-gate 				q->q_flags |= qflags;
44307c478bd9Sstevel@tonic-gate 				q->q_finalrcpt = frcpt;
44317c478bd9Sstevel@tonic-gate 				q->q_orcpt = orcpt;
44327c478bd9Sstevel@tonic-gate 				(void) recipient(q, &e->e_sendqueue, 0, e);
44337c478bd9Sstevel@tonic-gate 			}
44347c478bd9Sstevel@tonic-gate 			frcpt = NULL;
44357c478bd9Sstevel@tonic-gate 			orcpt = NULL;
44367c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{addr_type}"),
44377c478bd9Sstevel@tonic-gate 				NULL);
44387c478bd9Sstevel@tonic-gate 			break;
44397c478bd9Sstevel@tonic-gate 
44407c478bd9Sstevel@tonic-gate 		  case 'S':		/* sender */
44417c478bd9Sstevel@tonic-gate 			setsender(sm_rpool_strdup_x(e->e_rpool, &bp[1]),
44427c478bd9Sstevel@tonic-gate 				  e, NULL, '\0', true);
44437c478bd9Sstevel@tonic-gate 			break;
44447c478bd9Sstevel@tonic-gate 
44457c478bd9Sstevel@tonic-gate 		  case 'T':		/* init time */
44467c478bd9Sstevel@tonic-gate 			e->e_ctime = atol(&bp[1]);
44477c478bd9Sstevel@tonic-gate 			break;
44487c478bd9Sstevel@tonic-gate 
44497c478bd9Sstevel@tonic-gate 		  case 'V':		/* queue file version number */
44507c478bd9Sstevel@tonic-gate 			qfver = atoi(&bp[1]);
44517c478bd9Sstevel@tonic-gate 			if (qfver <= QF_VERSION)
44527c478bd9Sstevel@tonic-gate 				break;
44537c478bd9Sstevel@tonic-gate 			syserr("Version number in queue file (%d) greater than max (%d)",
44547c478bd9Sstevel@tonic-gate 				qfver, QF_VERSION);
44557c478bd9Sstevel@tonic-gate 			err = "unsupported queue file version";
44567c478bd9Sstevel@tonic-gate 			goto fail;
44577c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
44587c478bd9Sstevel@tonic-gate 			break;
44597c478bd9Sstevel@tonic-gate 
44607c478bd9Sstevel@tonic-gate 		  case 'Z':		/* original envelope id from ESMTP */
44617c478bd9Sstevel@tonic-gate 			e->e_envid = sm_rpool_strdup_x(e->e_rpool, &bp[1]);
44627c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
44637c478bd9Sstevel@tonic-gate 				macid("{dsn_envid}"), e->e_envid);
44647c478bd9Sstevel@tonic-gate 			break;
44657c478bd9Sstevel@tonic-gate 
44667c478bd9Sstevel@tonic-gate 		  case '!':		/* deliver by */
44677c478bd9Sstevel@tonic-gate 
44687c478bd9Sstevel@tonic-gate 			/* format: flag (1 char) space long-integer */
44697c478bd9Sstevel@tonic-gate 			e->e_dlvr_flag = buf[1];
44707c478bd9Sstevel@tonic-gate 			e->e_deliver_by = strtol(&buf[3], NULL, 10);
4471*fec46055SToomas Soome 			/* FALLTHROUGH */
44727c478bd9Sstevel@tonic-gate 
44737c478bd9Sstevel@tonic-gate 		  case '$':		/* define macro */
44747c478bd9Sstevel@tonic-gate 			{
44757c478bd9Sstevel@tonic-gate 				char *p;
44767c478bd9Sstevel@tonic-gate 
44777c478bd9Sstevel@tonic-gate 				/* XXX elimate p? */
44787c478bd9Sstevel@tonic-gate 				r = macid_parse(&bp[1], &ep);
44797c478bd9Sstevel@tonic-gate 				if (r == 0)
44807c478bd9Sstevel@tonic-gate 					break;
44817c478bd9Sstevel@tonic-gate 				p = sm_rpool_strdup_x(e->e_rpool, ep);
44827c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM, r, p);
44837c478bd9Sstevel@tonic-gate 			}
44847c478bd9Sstevel@tonic-gate 			break;
44857c478bd9Sstevel@tonic-gate 
44867c478bd9Sstevel@tonic-gate 		  case '.':		/* terminate file */
44877c478bd9Sstevel@tonic-gate 			nomore = true;
44887c478bd9Sstevel@tonic-gate 			break;
44897c478bd9Sstevel@tonic-gate 
44907c478bd9Sstevel@tonic-gate #if _FFR_QUEUEDELAY
44917c478bd9Sstevel@tonic-gate 		  case 'G':
44927c478bd9Sstevel@tonic-gate 		  case 'Y':
44937c478bd9Sstevel@tonic-gate 
44947c478bd9Sstevel@tonic-gate 			/*
44957c478bd9Sstevel@tonic-gate 			**  Maintain backward compatibility for
44967c478bd9Sstevel@tonic-gate 			**  users who defined _FFR_QUEUEDELAY in
44977c478bd9Sstevel@tonic-gate 			**  previous releases.  Remove this
44987c478bd9Sstevel@tonic-gate 			**  code in 8.14 or 8.15.
44997c478bd9Sstevel@tonic-gate 			*/
45007c478bd9Sstevel@tonic-gate 
45017c478bd9Sstevel@tonic-gate 			if (qfver == 5 || qfver == 7)
45027c478bd9Sstevel@tonic-gate 				break;
45037c478bd9Sstevel@tonic-gate 
45047c478bd9Sstevel@tonic-gate 			/* If not qfver 5 or 7, then 'G' or 'Y' is invalid */
45057c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
45067c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUEDELAY */
45077c478bd9Sstevel@tonic-gate 
45087c478bd9Sstevel@tonic-gate 		  default:
45097c478bd9Sstevel@tonic-gate 			syserr("readqf: %s: line %d: bad line \"%s\"",
45107c478bd9Sstevel@tonic-gate 				qf, LineNumber, shortenstring(bp, MAXSHORTSTR));
45117c478bd9Sstevel@tonic-gate 			err = "unrecognized line";
45127c478bd9Sstevel@tonic-gate 			goto fail;
45137c478bd9Sstevel@tonic-gate 		}
45147c478bd9Sstevel@tonic-gate 
45157c478bd9Sstevel@tonic-gate 		if (bp != buf)
4516058561cbSjbeck 			SM_FREE(bp);
45177c478bd9Sstevel@tonic-gate 	}
45187c478bd9Sstevel@tonic-gate 
45197c478bd9Sstevel@tonic-gate 	/*
45207c478bd9Sstevel@tonic-gate 	**  If we haven't read any lines, this queue file is empty.
45217c478bd9Sstevel@tonic-gate 	**  Arrange to remove it without referencing any null pointers.
45227c478bd9Sstevel@tonic-gate 	*/
45237c478bd9Sstevel@tonic-gate 
45247c478bd9Sstevel@tonic-gate 	if (LineNumber == 0)
45257c478bd9Sstevel@tonic-gate 	{
45267c478bd9Sstevel@tonic-gate 		errno = 0;
45277c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_CLRQUEUE|EF_FATALERRS|EF_RESPONSE;
45287c478bd9Sstevel@tonic-gate 		return true;
45297c478bd9Sstevel@tonic-gate 	}
45307c478bd9Sstevel@tonic-gate 
45317c478bd9Sstevel@tonic-gate 	/* Check to make sure we have a complete queue file read */
45327c478bd9Sstevel@tonic-gate 	if (!nomore)
45337c478bd9Sstevel@tonic-gate 	{
45347c478bd9Sstevel@tonic-gate 		syserr("readqf: %s: incomplete queue file read", qf);
45357c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
45367c478bd9Sstevel@tonic-gate 		return false;
45377c478bd9Sstevel@tonic-gate 	}
45387c478bd9Sstevel@tonic-gate 
45394aac33d3Sjbeck #if _FFR_QF_PARANOIA
45404aac33d3Sjbeck 	/* Check to make sure key fields were read */
45414aac33d3Sjbeck 	if (e->e_from.q_mailer == NULL)
45424aac33d3Sjbeck 	{
45434aac33d3Sjbeck 		syserr("readqf: %s: sender not specified in queue file", qf);
45444aac33d3Sjbeck 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
45454aac33d3Sjbeck 		return false;
45464aac33d3Sjbeck 	}
45474aac33d3Sjbeck 	/* other checks? */
45484aac33d3Sjbeck #endif /* _FFR_QF_PARANOIA */
45494aac33d3Sjbeck 
45507c478bd9Sstevel@tonic-gate 	/* possibly set ${dsn_ret} macro */
45517c478bd9Sstevel@tonic-gate 	if (bitset(EF_RET_PARAM, e->e_flags))
45527c478bd9Sstevel@tonic-gate 	{
45537c478bd9Sstevel@tonic-gate 		if (bitset(EF_NO_BODY_RETN, e->e_flags))
45547c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
45557c478bd9Sstevel@tonic-gate 				macid("{dsn_ret}"), "hdrs");
45567c478bd9Sstevel@tonic-gate 		else
45577c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
45587c478bd9Sstevel@tonic-gate 				macid("{dsn_ret}"), "full");
45597c478bd9Sstevel@tonic-gate 	}
45607c478bd9Sstevel@tonic-gate 
45617c478bd9Sstevel@tonic-gate 	/*
45627c478bd9Sstevel@tonic-gate 	**  Arrange to read the data file.
45637c478bd9Sstevel@tonic-gate 	*/
45647c478bd9Sstevel@tonic-gate 
45657c478bd9Sstevel@tonic-gate 	p = queuename(e, DATAFL_LETTER);
45667c478bd9Sstevel@tonic-gate 	e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, p, SM_IO_RDONLY_B,
45677c478bd9Sstevel@tonic-gate 			      NULL);
45687c478bd9Sstevel@tonic-gate 	if (e->e_dfp == NULL)
45697c478bd9Sstevel@tonic-gate 	{
45707c478bd9Sstevel@tonic-gate 		syserr("readqf: cannot open %s", p);
45717c478bd9Sstevel@tonic-gate 	}
45727c478bd9Sstevel@tonic-gate 	else
45737c478bd9Sstevel@tonic-gate 	{
45747c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_HAS_DF;
45757c478bd9Sstevel@tonic-gate 		if (fstat(sm_io_getinfo(e->e_dfp, SM_IO_WHAT_FD, NULL), &st)
45767c478bd9Sstevel@tonic-gate 		    >= 0)
45777c478bd9Sstevel@tonic-gate 		{
45787c478bd9Sstevel@tonic-gate 			e->e_msgsize = st.st_size + hdrsize;
45797c478bd9Sstevel@tonic-gate 			e->e_dfdev = st.st_dev;
45807c478bd9Sstevel@tonic-gate 			e->e_dfino = ST_INODE(st);
4581058561cbSjbeck 			(void) sm_snprintf(buf, sizeof(buf), "%ld",
45827c478bd9Sstevel@tonic-gate 					   e->e_msgsize);
45837c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"),
45847c478bd9Sstevel@tonic-gate 				  buf);
45857c478bd9Sstevel@tonic-gate 		}
45867c478bd9Sstevel@tonic-gate 	}
45877c478bd9Sstevel@tonic-gate 
45887c478bd9Sstevel@tonic-gate 	return true;
45897c478bd9Sstevel@tonic-gate 
45907c478bd9Sstevel@tonic-gate   fail:
45917c478bd9Sstevel@tonic-gate 	/*
45927c478bd9Sstevel@tonic-gate 	**  There was some error reading the qf file (reason is in err var.)
45937c478bd9Sstevel@tonic-gate 	**  Cleanup:
45947c478bd9Sstevel@tonic-gate 	**	close file; clear e_lockfp since it is the same as qfp,
45957c478bd9Sstevel@tonic-gate 	**	hence it is invalid (as file) after qfp is closed;
45967c478bd9Sstevel@tonic-gate 	**	the qf file is on disk, so set the flag to avoid calling
45977c478bd9Sstevel@tonic-gate 	**	queueup() with bogus data.
45987c478bd9Sstevel@tonic-gate 	*/
45997c478bd9Sstevel@tonic-gate 
4600058561cbSjbeck 	if (bp != buf)
4601058561cbSjbeck 		SM_FREE(bp);
46027c478bd9Sstevel@tonic-gate 	if (qfp != NULL)
46037c478bd9Sstevel@tonic-gate 		(void) sm_io_close(qfp, SM_TIME_DEFAULT);
46047c478bd9Sstevel@tonic-gate 	e->e_lockfp = NULL;
46057c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_INQUEUE;
46067c478bd9Sstevel@tonic-gate 	loseqfile(e, err);
46077c478bd9Sstevel@tonic-gate 	return false;
46087c478bd9Sstevel@tonic-gate }
46097c478bd9Sstevel@tonic-gate /*
46107c478bd9Sstevel@tonic-gate **  PRTSTR -- print a string, "unprintable" characters are shown as \oct
46117c478bd9Sstevel@tonic-gate **
46127c478bd9Sstevel@tonic-gate **	Parameters:
46137c478bd9Sstevel@tonic-gate **		s -- string to print
46147c478bd9Sstevel@tonic-gate **		ml -- maximum length of output
46157c478bd9Sstevel@tonic-gate **
46167c478bd9Sstevel@tonic-gate **	Returns:
46177c478bd9Sstevel@tonic-gate **		number of entries
46187c478bd9Sstevel@tonic-gate **
46197c478bd9Sstevel@tonic-gate **	Side Effects:
46207c478bd9Sstevel@tonic-gate **		Prints a string on stdout.
46217c478bd9Sstevel@tonic-gate */
46227c478bd9Sstevel@tonic-gate 
4623058561cbSjbeck static void prtstr __P((char *, int));
4624058561cbSjbeck 
46257c478bd9Sstevel@tonic-gate static void
prtstr(s,ml)46267c478bd9Sstevel@tonic-gate prtstr(s, ml)
46277c478bd9Sstevel@tonic-gate 	char *s;
46287c478bd9Sstevel@tonic-gate 	int ml;
46297c478bd9Sstevel@tonic-gate {
46307c478bd9Sstevel@tonic-gate 	int c;
46317c478bd9Sstevel@tonic-gate 
46327c478bd9Sstevel@tonic-gate 	if (s == NULL)
46337c478bd9Sstevel@tonic-gate 		return;
46347c478bd9Sstevel@tonic-gate 	while (ml-- > 0 && ((c = *s++) != '\0'))
46357c478bd9Sstevel@tonic-gate 	{
46367c478bd9Sstevel@tonic-gate 		if (c == '\\')
46377c478bd9Sstevel@tonic-gate 		{
46387c478bd9Sstevel@tonic-gate 			if (ml-- > 0)
46397c478bd9Sstevel@tonic-gate 			{
46407c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46417c478bd9Sstevel@tonic-gate 				(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46427c478bd9Sstevel@tonic-gate 			}
46437c478bd9Sstevel@tonic-gate 		}
46447c478bd9Sstevel@tonic-gate 		else if (isascii(c) && isprint(c))
46457c478bd9Sstevel@tonic-gate 			(void) sm_io_putc(smioout, SM_TIME_DEFAULT, c);
46467c478bd9Sstevel@tonic-gate 		else
46477c478bd9Sstevel@tonic-gate 		{
46487c478bd9Sstevel@tonic-gate 			if ((ml -= 3) > 0)
46497c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
46507c478bd9Sstevel@tonic-gate 						     "\\%03o", c & 0xFF);
46517c478bd9Sstevel@tonic-gate 		}
46527c478bd9Sstevel@tonic-gate 	}
46537c478bd9Sstevel@tonic-gate }
46547c478bd9Sstevel@tonic-gate /*
46557c478bd9Sstevel@tonic-gate **  PRINTNQE -- print out number of entries in the mail queue
46567c478bd9Sstevel@tonic-gate **
46577c478bd9Sstevel@tonic-gate **	Parameters:
46587c478bd9Sstevel@tonic-gate **		out -- output file pointer.
46597c478bd9Sstevel@tonic-gate **		prefix -- string to output in front of each line.
46607c478bd9Sstevel@tonic-gate **
46617c478bd9Sstevel@tonic-gate **	Returns:
46627c478bd9Sstevel@tonic-gate **		none.
46637c478bd9Sstevel@tonic-gate */
46647c478bd9Sstevel@tonic-gate 
46657c478bd9Sstevel@tonic-gate void
printnqe(out,prefix)46667c478bd9Sstevel@tonic-gate printnqe(out, prefix)
46677c478bd9Sstevel@tonic-gate 	SM_FILE_T *out;
46687c478bd9Sstevel@tonic-gate 	char *prefix;
46697c478bd9Sstevel@tonic-gate {
46707c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
46717c478bd9Sstevel@tonic-gate 	int i, k = 0, nrequests = 0;
46727c478bd9Sstevel@tonic-gate 	bool unknown = false;
46737c478bd9Sstevel@tonic-gate 
46747c478bd9Sstevel@tonic-gate 	if (ShmId == SM_SHM_NO_ID)
46757c478bd9Sstevel@tonic-gate 	{
46767c478bd9Sstevel@tonic-gate 		if (prefix == NULL)
46777c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46787c478bd9Sstevel@tonic-gate 					"Data unavailable: shared memory not updated\n");
46797c478bd9Sstevel@tonic-gate 		else
46807c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46817c478bd9Sstevel@tonic-gate 					"%sNOTCONFIGURED:-1\r\n", prefix);
46827c478bd9Sstevel@tonic-gate 		return;
46837c478bd9Sstevel@tonic-gate 	}
46847c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
46857c478bd9Sstevel@tonic-gate 	{
46867c478bd9Sstevel@tonic-gate 		int j;
46877c478bd9Sstevel@tonic-gate 
46887c478bd9Sstevel@tonic-gate 		k++;
46897c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; j++)
46907c478bd9Sstevel@tonic-gate 		{
46917c478bd9Sstevel@tonic-gate 			int n;
46927c478bd9Sstevel@tonic-gate 
46937c478bd9Sstevel@tonic-gate 			if (StopRequest)
46947c478bd9Sstevel@tonic-gate 				stop_sendmail();
46957c478bd9Sstevel@tonic-gate 
46967c478bd9Sstevel@tonic-gate 			n = QSHM_ENTRIES(Queue[i]->qg_qpaths[j].qp_idx);
46977c478bd9Sstevel@tonic-gate 			if (prefix != NULL)
46987c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
46997c478bd9Sstevel@tonic-gate 					"%s%s:%d\r\n",
47007c478bd9Sstevel@tonic-gate 					prefix, qid_printqueue(i, j), n);
47017c478bd9Sstevel@tonic-gate 			else if (n < 0)
47027c478bd9Sstevel@tonic-gate 			{
47037c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47047c478bd9Sstevel@tonic-gate 					"%s: unknown number of entries\n",
47057c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j));
47067c478bd9Sstevel@tonic-gate 				unknown = true;
47077c478bd9Sstevel@tonic-gate 			}
47087c478bd9Sstevel@tonic-gate 			else if (n == 0)
47097c478bd9Sstevel@tonic-gate 			{
47107c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47117c478bd9Sstevel@tonic-gate 					"%s is empty\n",
47127c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j));
47137c478bd9Sstevel@tonic-gate 			}
47147c478bd9Sstevel@tonic-gate 			else if (n > 0)
47157c478bd9Sstevel@tonic-gate 			{
47167c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47177c478bd9Sstevel@tonic-gate 					"%s: entries=%d\n",
47187c478bd9Sstevel@tonic-gate 					qid_printqueue(i, j), n);
47197c478bd9Sstevel@tonic-gate 				nrequests += n;
47207c478bd9Sstevel@tonic-gate 				k++;
47217c478bd9Sstevel@tonic-gate 			}
47227c478bd9Sstevel@tonic-gate 		}
47237c478bd9Sstevel@tonic-gate 	}
47247c478bd9Sstevel@tonic-gate 	if (prefix == NULL && k > 1)
47257c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47267c478bd9Sstevel@tonic-gate 				     "\t\tTotal requests: %d%s\n",
47277c478bd9Sstevel@tonic-gate 				     nrequests, unknown ? " (about)" : "");
47287c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
47297c478bd9Sstevel@tonic-gate 	if (prefix == NULL)
47307c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47317c478bd9Sstevel@tonic-gate 			     "Data unavailable without shared memory support\n");
47327c478bd9Sstevel@tonic-gate 	else
47337c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
47347c478bd9Sstevel@tonic-gate 			     "%sNOTAVAILABLE:-1\r\n", prefix);
47357c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
47367c478bd9Sstevel@tonic-gate }
47377c478bd9Sstevel@tonic-gate /*
47387c478bd9Sstevel@tonic-gate **  PRINTQUEUE -- print out a representation of the mail queue
47397c478bd9Sstevel@tonic-gate **
47407c478bd9Sstevel@tonic-gate **	Parameters:
47417c478bd9Sstevel@tonic-gate **		none.
47427c478bd9Sstevel@tonic-gate **
47437c478bd9Sstevel@tonic-gate **	Returns:
47447c478bd9Sstevel@tonic-gate **		none.
47457c478bd9Sstevel@tonic-gate **
47467c478bd9Sstevel@tonic-gate **	Side Effects:
47477c478bd9Sstevel@tonic-gate **		Prints a listing of the mail queue on the standard output.
47487c478bd9Sstevel@tonic-gate */
47497c478bd9Sstevel@tonic-gate 
47507c478bd9Sstevel@tonic-gate void
printqueue()47517c478bd9Sstevel@tonic-gate printqueue()
47527c478bd9Sstevel@tonic-gate {
47537c478bd9Sstevel@tonic-gate 	int i, k = 0, nrequests = 0;
47547c478bd9Sstevel@tonic-gate 
47557c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
47567c478bd9Sstevel@tonic-gate 	{
47577c478bd9Sstevel@tonic-gate 		int j;
47587c478bd9Sstevel@tonic-gate 
47597c478bd9Sstevel@tonic-gate 		k++;
47607c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; j++)
47617c478bd9Sstevel@tonic-gate 		{
47627c478bd9Sstevel@tonic-gate 			if (StopRequest)
47637c478bd9Sstevel@tonic-gate 				stop_sendmail();
47647c478bd9Sstevel@tonic-gate 			nrequests += print_single_queue(i, j);
47657c478bd9Sstevel@tonic-gate 			k++;
47667c478bd9Sstevel@tonic-gate 		}
47677c478bd9Sstevel@tonic-gate 	}
47687c478bd9Sstevel@tonic-gate 	if (k > 1)
47697c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
47707c478bd9Sstevel@tonic-gate 				     "\t\tTotal requests: %d\n",
47717c478bd9Sstevel@tonic-gate 				     nrequests);
47727c478bd9Sstevel@tonic-gate }
47737c478bd9Sstevel@tonic-gate /*
47747c478bd9Sstevel@tonic-gate **  PRINT_SINGLE_QUEUE -- print out a representation of a single mail queue
47757c478bd9Sstevel@tonic-gate **
47767c478bd9Sstevel@tonic-gate **	Parameters:
47777c478bd9Sstevel@tonic-gate **		qgrp -- the index of the queue group.
47787c478bd9Sstevel@tonic-gate **		qdir -- the queue directory.
47797c478bd9Sstevel@tonic-gate **
47807c478bd9Sstevel@tonic-gate **	Returns:
47817c478bd9Sstevel@tonic-gate **		number of requests in mail queue.
47827c478bd9Sstevel@tonic-gate **
47837c478bd9Sstevel@tonic-gate **	Side Effects:
47847c478bd9Sstevel@tonic-gate **		Prints a listing of the mail queue on the standard output.
47857c478bd9Sstevel@tonic-gate */
47867c478bd9Sstevel@tonic-gate 
47877c478bd9Sstevel@tonic-gate int
print_single_queue(qgrp,qdir)47887c478bd9Sstevel@tonic-gate print_single_queue(qgrp, qdir)
47897c478bd9Sstevel@tonic-gate 	int qgrp;
47907c478bd9Sstevel@tonic-gate 	int qdir;
47917c478bd9Sstevel@tonic-gate {
47927c478bd9Sstevel@tonic-gate 	register WORK *w;
47937c478bd9Sstevel@tonic-gate 	SM_FILE_T *f;
47947c478bd9Sstevel@tonic-gate 	int nrequests;
47957c478bd9Sstevel@tonic-gate 	char qd[MAXPATHLEN];
47967c478bd9Sstevel@tonic-gate 	char qddf[MAXPATHLEN];
47977c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
47987c478bd9Sstevel@tonic-gate 
47997c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
48007c478bd9Sstevel@tonic-gate 	{
4801058561cbSjbeck 		(void) sm_strlcpy(qd, ".", sizeof(qd));
4802058561cbSjbeck 		(void) sm_strlcpy(qddf, ".", sizeof(qddf));
48037c478bd9Sstevel@tonic-gate 	}
48047c478bd9Sstevel@tonic-gate 	else
48057c478bd9Sstevel@tonic-gate 	{
4806058561cbSjbeck 		(void) sm_strlcpyn(qd, sizeof(qd), 2,
48077c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
48087c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBQF,
48097c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
48107c478bd9Sstevel@tonic-gate 					? "/qf" : ""));
4811058561cbSjbeck 		(void) sm_strlcpyn(qddf, sizeof(qddf), 2,
48127c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qpaths[qdir].qp_name,
48137c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBDF,
48147c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
48157c478bd9Sstevel@tonic-gate 					? "/df" : ""));
48167c478bd9Sstevel@tonic-gate 	}
48177c478bd9Sstevel@tonic-gate 
48187c478bd9Sstevel@tonic-gate 	/*
48197c478bd9Sstevel@tonic-gate 	**  Check for permission to print the queue
48207c478bd9Sstevel@tonic-gate 	*/
48217c478bd9Sstevel@tonic-gate 
48227c478bd9Sstevel@tonic-gate 	if (bitset(PRIV_RESTRICTMAILQ, PrivacyFlags) && RealUid != 0)
48237c478bd9Sstevel@tonic-gate 	{
48247c478bd9Sstevel@tonic-gate 		struct stat st;
48257c478bd9Sstevel@tonic-gate #ifdef NGROUPS_MAX
48267c478bd9Sstevel@tonic-gate 		int n;
48277c478bd9Sstevel@tonic-gate 		extern GIDSET_T InitialGidSet[NGROUPS_MAX];
48287c478bd9Sstevel@tonic-gate #endif /* NGROUPS_MAX */
48297c478bd9Sstevel@tonic-gate 
48307c478bd9Sstevel@tonic-gate 		if (stat(qd, &st) < 0)
48317c478bd9Sstevel@tonic-gate 		{
48327c478bd9Sstevel@tonic-gate 			syserr("Cannot stat %s",
48337c478bd9Sstevel@tonic-gate 				qid_printqueue(qgrp, qdir));
48347c478bd9Sstevel@tonic-gate 			return 0;
48357c478bd9Sstevel@tonic-gate 		}
48367c478bd9Sstevel@tonic-gate #ifdef NGROUPS_MAX
48377c478bd9Sstevel@tonic-gate 		n = NGROUPS_MAX;
48387c478bd9Sstevel@tonic-gate 		while (--n >= 0)
48397c478bd9Sstevel@tonic-gate 		{
48407c478bd9Sstevel@tonic-gate 			if (InitialGidSet[n] == st.st_gid)
48417c478bd9Sstevel@tonic-gate 				break;
48427c478bd9Sstevel@tonic-gate 		}
48437c478bd9Sstevel@tonic-gate 		if (n < 0 && RealGid != st.st_gid)
48447c478bd9Sstevel@tonic-gate #else /* NGROUPS_MAX */
48457c478bd9Sstevel@tonic-gate 		if (RealGid != st.st_gid)
48467c478bd9Sstevel@tonic-gate #endif /* NGROUPS_MAX */
48477c478bd9Sstevel@tonic-gate 		{
48487c478bd9Sstevel@tonic-gate 			usrerr("510 You are not permitted to see the queue");
48497c478bd9Sstevel@tonic-gate 			setstat(EX_NOPERM);
48507c478bd9Sstevel@tonic-gate 			return 0;
48517c478bd9Sstevel@tonic-gate 		}
48527c478bd9Sstevel@tonic-gate 	}
48537c478bd9Sstevel@tonic-gate 
48547c478bd9Sstevel@tonic-gate 	/*
48557c478bd9Sstevel@tonic-gate 	**  Read and order the queue.
48567c478bd9Sstevel@tonic-gate 	*/
48577c478bd9Sstevel@tonic-gate 
4858e9af4bc0SJohn Beck 	nrequests = gatherq(qgrp, qdir, true, NULL, NULL, NULL);
48597c478bd9Sstevel@tonic-gate 	(void) sortq(Queue[qgrp]->qg_maxlist);
48607c478bd9Sstevel@tonic-gate 
48617c478bd9Sstevel@tonic-gate 	/*
48627c478bd9Sstevel@tonic-gate 	**  Print the work list that we have read.
48637c478bd9Sstevel@tonic-gate 	*/
48647c478bd9Sstevel@tonic-gate 
48657c478bd9Sstevel@tonic-gate 	/* first see if there is anything */
48667c478bd9Sstevel@tonic-gate 	if (nrequests <= 0)
48677c478bd9Sstevel@tonic-gate 	{
48687c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s is empty\n",
48697c478bd9Sstevel@tonic-gate 				     qid_printqueue(qgrp, qdir));
48707c478bd9Sstevel@tonic-gate 		return 0;
48717c478bd9Sstevel@tonic-gate 	}
48727c478bd9Sstevel@tonic-gate 
48737c478bd9Sstevel@tonic-gate 	sm_getla();	/* get load average */
48747c478bd9Sstevel@tonic-gate 
48757c478bd9Sstevel@tonic-gate 	(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\t\t%s (%d request%s",
48767c478bd9Sstevel@tonic-gate 			     qid_printqueue(qgrp, qdir),
48777c478bd9Sstevel@tonic-gate 			     nrequests, nrequests == 1 ? "" : "s");
48787c478bd9Sstevel@tonic-gate 	if (MaxQueueRun > 0 && nrequests > MaxQueueRun)
48797c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48807c478bd9Sstevel@tonic-gate 				     ", only %d printed", MaxQueueRun);
48817c478bd9Sstevel@tonic-gate 	if (Verbose)
48827c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
48837c478bd9Sstevel@tonic-gate 			")\n-----Q-ID----- --Size-- -Priority- ---Q-Time--- --------Sender/Recipient--------\n");
48847c478bd9Sstevel@tonic-gate 	else
48857c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout,  SM_TIME_DEFAULT,
48867c478bd9Sstevel@tonic-gate 			")\n-----Q-ID----- --Size-- -----Q-Time----- ------------Sender/Recipient-----------\n");
48877c478bd9Sstevel@tonic-gate 	for (w = WorkQ; w != NULL; w = w->w_next)
48887c478bd9Sstevel@tonic-gate 	{
48897c478bd9Sstevel@tonic-gate 		struct stat st;
48907c478bd9Sstevel@tonic-gate 		auto time_t submittime = 0;
48917c478bd9Sstevel@tonic-gate 		long dfsize;
48927c478bd9Sstevel@tonic-gate 		int flags = 0;
48937c478bd9Sstevel@tonic-gate 		int qfver;
48947c478bd9Sstevel@tonic-gate 		char quarmsg[MAXLINE];
48957c478bd9Sstevel@tonic-gate 		char statmsg[MAXLINE];
48967c478bd9Sstevel@tonic-gate 		char bodytype[MAXNAME + 1];
48977c478bd9Sstevel@tonic-gate 		char qf[MAXPATHLEN];
48987c478bd9Sstevel@tonic-gate 
48997c478bd9Sstevel@tonic-gate 		if (StopRequest)
49007c478bd9Sstevel@tonic-gate 			stop_sendmail();
49017c478bd9Sstevel@tonic-gate 
49027c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%13s",
49037c478bd9Sstevel@tonic-gate 				     w->w_name + 2);
4904058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qd, "/", w->w_name);
49057c478bd9Sstevel@tonic-gate 		f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, qf, SM_IO_RDONLY_B,
49067c478bd9Sstevel@tonic-gate 			       NULL);
49077c478bd9Sstevel@tonic-gate 		if (f == NULL)
49087c478bd9Sstevel@tonic-gate 		{
49097c478bd9Sstevel@tonic-gate 			if (errno == EPERM)
49107c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
49117c478bd9Sstevel@tonic-gate 						     " (permission denied)\n");
49127c478bd9Sstevel@tonic-gate 			else if (errno == ENOENT)
49137c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
49147c478bd9Sstevel@tonic-gate 						     " (job completed)\n");
49157c478bd9Sstevel@tonic-gate 			else
49167c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
49177c478bd9Sstevel@tonic-gate 						     " (%s)\n",
49187c478bd9Sstevel@tonic-gate 						     sm_errstring(errno));
49197c478bd9Sstevel@tonic-gate 			errno = 0;
49207c478bd9Sstevel@tonic-gate 			continue;
49217c478bd9Sstevel@tonic-gate 		}
49227c478bd9Sstevel@tonic-gate 		w->w_name[0] = DATAFL_LETTER;
4923058561cbSjbeck 		(void) sm_strlcpyn(qf, sizeof(qf), 3, qddf, "/", w->w_name);
49247c478bd9Sstevel@tonic-gate 		if (stat(qf, &st) >= 0)
49257c478bd9Sstevel@tonic-gate 			dfsize = st.st_size;
49267c478bd9Sstevel@tonic-gate 		else
49277c478bd9Sstevel@tonic-gate 		{
49287c478bd9Sstevel@tonic-gate 			ENVELOPE e;
49297c478bd9Sstevel@tonic-gate 
49307c478bd9Sstevel@tonic-gate 			/*
49317c478bd9Sstevel@tonic-gate 			**  Maybe the df file can't be statted because
49327c478bd9Sstevel@tonic-gate 			**  it is in a different directory than the qf file.
49337c478bd9Sstevel@tonic-gate 			**  In order to find out, we must read the qf file.
49347c478bd9Sstevel@tonic-gate 			*/
49357c478bd9Sstevel@tonic-gate 
49367c478bd9Sstevel@tonic-gate 			newenvelope(&e, &BlankEnvelope, sm_rpool_new_x(NULL));
49377c478bd9Sstevel@tonic-gate 			e.e_id = w->w_name + 2;
49387c478bd9Sstevel@tonic-gate 			e.e_qgrp = qgrp;
49397c478bd9Sstevel@tonic-gate 			e.e_qdir = qdir;
49407c478bd9Sstevel@tonic-gate 			dfsize = -1;
49417c478bd9Sstevel@tonic-gate 			if (readqf(&e, false))
49427c478bd9Sstevel@tonic-gate 			{
49437c478bd9Sstevel@tonic-gate 				char *df = queuename(&e, DATAFL_LETTER);
49447c478bd9Sstevel@tonic-gate 				if (stat(df, &st) >= 0)
49457c478bd9Sstevel@tonic-gate 					dfsize = st.st_size;
49467c478bd9Sstevel@tonic-gate 			}
49477c478bd9Sstevel@tonic-gate 			if (e.e_lockfp != NULL)
49487c478bd9Sstevel@tonic-gate 			{
49497c478bd9Sstevel@tonic-gate 				(void) sm_io_close(e.e_lockfp, SM_TIME_DEFAULT);
49507c478bd9Sstevel@tonic-gate 				e.e_lockfp = NULL;
49517c478bd9Sstevel@tonic-gate 			}
49527c478bd9Sstevel@tonic-gate 			clearenvelope(&e, false, e.e_rpool);
49537c478bd9Sstevel@tonic-gate 			sm_rpool_free(e.e_rpool);
49547c478bd9Sstevel@tonic-gate 		}
49557c478bd9Sstevel@tonic-gate 		if (w->w_lock)
49567c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "*");
49577c478bd9Sstevel@tonic-gate 		else if (QueueMode == QM_LOST)
49587c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "?");
49597c478bd9Sstevel@tonic-gate 		else if (w->w_tooyoung)
49607c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "-");
49617c478bd9Sstevel@tonic-gate 		else if (shouldqueue(w->w_pri, w->w_ctime))
49627c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "X");
49637c478bd9Sstevel@tonic-gate 		else
49647c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " ");
49657c478bd9Sstevel@tonic-gate 
49667c478bd9Sstevel@tonic-gate 		errno = 0;
49677c478bd9Sstevel@tonic-gate 
49687c478bd9Sstevel@tonic-gate 		quarmsg[0] = '\0';
49697c478bd9Sstevel@tonic-gate 		statmsg[0] = bodytype[0] = '\0';
49707c478bd9Sstevel@tonic-gate 		qfver = 0;
4971058561cbSjbeck 		while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
49727c478bd9Sstevel@tonic-gate 		{
49737c478bd9Sstevel@tonic-gate 			register int i;
49747c478bd9Sstevel@tonic-gate 			register char *p;
49757c478bd9Sstevel@tonic-gate 
49767c478bd9Sstevel@tonic-gate 			if (StopRequest)
49777c478bd9Sstevel@tonic-gate 				stop_sendmail();
49787c478bd9Sstevel@tonic-gate 
49797c478bd9Sstevel@tonic-gate 			fixcrlf(buf, true);
49807c478bd9Sstevel@tonic-gate 			switch (buf[0])
49817c478bd9Sstevel@tonic-gate 			{
49827c478bd9Sstevel@tonic-gate 			  case 'V':	/* queue file version */
49837c478bd9Sstevel@tonic-gate 				qfver = atoi(&buf[1]);
49847c478bd9Sstevel@tonic-gate 				break;
49857c478bd9Sstevel@tonic-gate 
49867c478bd9Sstevel@tonic-gate 			  case 'M':	/* error message */
4987058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(statmsg))
4988058561cbSjbeck 					i = sizeof(statmsg) - 1;
49897c478bd9Sstevel@tonic-gate 				memmove(statmsg, &buf[1], i);
49907c478bd9Sstevel@tonic-gate 				statmsg[i] = '\0';
49917c478bd9Sstevel@tonic-gate 				break;
49927c478bd9Sstevel@tonic-gate 
49937c478bd9Sstevel@tonic-gate 			  case 'q':	/* quarantine reason */
4994058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(quarmsg))
4995058561cbSjbeck 					i = sizeof(quarmsg) - 1;
49967c478bd9Sstevel@tonic-gate 				memmove(quarmsg, &buf[1], i);
49977c478bd9Sstevel@tonic-gate 				quarmsg[i] = '\0';
49987c478bd9Sstevel@tonic-gate 				break;
49997c478bd9Sstevel@tonic-gate 
50007c478bd9Sstevel@tonic-gate 			  case 'B':	/* body type */
5001058561cbSjbeck 				if ((i = strlen(&buf[1])) >= sizeof(bodytype))
5002058561cbSjbeck 					i = sizeof(bodytype) - 1;
50037c478bd9Sstevel@tonic-gate 				memmove(bodytype, &buf[1], i);
50047c478bd9Sstevel@tonic-gate 				bodytype[i] = '\0';
50057c478bd9Sstevel@tonic-gate 				break;
50067c478bd9Sstevel@tonic-gate 
50077c478bd9Sstevel@tonic-gate 			  case 'S':	/* sender name */
50087c478bd9Sstevel@tonic-gate 				if (Verbose)
50097c478bd9Sstevel@tonic-gate 				{
50107c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50117c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50127c478bd9Sstevel@tonic-gate 						"%8ld %10ld%c%.12s ",
50137c478bd9Sstevel@tonic-gate 						dfsize,
50147c478bd9Sstevel@tonic-gate 						w->w_pri,
50157c478bd9Sstevel@tonic-gate 						bitset(EF_WARNING, flags)
50167c478bd9Sstevel@tonic-gate 							? '+' : ' ',
50177c478bd9Sstevel@tonic-gate 						ctime(&submittime) + 4);
50187c478bd9Sstevel@tonic-gate 					prtstr(&buf[1], 78);
50197c478bd9Sstevel@tonic-gate 				}
50207c478bd9Sstevel@tonic-gate 				else
50217c478bd9Sstevel@tonic-gate 				{
50227c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50237c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50247c478bd9Sstevel@tonic-gate 						"%8ld %.16s ",
50257c478bd9Sstevel@tonic-gate 						dfsize,
50267c478bd9Sstevel@tonic-gate 						ctime(&submittime));
50277c478bd9Sstevel@tonic-gate 					prtstr(&buf[1], 39);
50287c478bd9Sstevel@tonic-gate 				}
50297c478bd9Sstevel@tonic-gate 
50307c478bd9Sstevel@tonic-gate 				if (quarmsg[0] != '\0')
50317c478bd9Sstevel@tonic-gate 				{
50327c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50337c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
50347c478bd9Sstevel@tonic-gate 							     "\n     QUARANTINE: %.*s",
50357c478bd9Sstevel@tonic-gate 							     Verbose ? 100 : 60,
50367c478bd9Sstevel@tonic-gate 							     quarmsg);
50377c478bd9Sstevel@tonic-gate 					quarmsg[0] = '\0';
50387c478bd9Sstevel@tonic-gate 				}
50397c478bd9Sstevel@tonic-gate 
50407c478bd9Sstevel@tonic-gate 				if (statmsg[0] != '\0' || bodytype[0] != '\0')
50417c478bd9Sstevel@tonic-gate 				{
50427c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50437c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50447c478bd9Sstevel@tonic-gate 						"\n    %10.10s",
50457c478bd9Sstevel@tonic-gate 						bodytype);
50467c478bd9Sstevel@tonic-gate 					if (statmsg[0] != '\0')
50477c478bd9Sstevel@tonic-gate 						(void) sm_io_fprintf(smioout,
50487c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50497c478bd9Sstevel@tonic-gate 							"   (%.*s)",
50507c478bd9Sstevel@tonic-gate 							Verbose ? 100 : 60,
50517c478bd9Sstevel@tonic-gate 							statmsg);
50527c478bd9Sstevel@tonic-gate 					statmsg[0] = '\0';
50537c478bd9Sstevel@tonic-gate 				}
50547c478bd9Sstevel@tonic-gate 				break;
50557c478bd9Sstevel@tonic-gate 
50567c478bd9Sstevel@tonic-gate 			  case 'C':	/* controlling user */
50577c478bd9Sstevel@tonic-gate 				if (Verbose)
50587c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50597c478bd9Sstevel@tonic-gate 						SM_TIME_DEFAULT,
50607c478bd9Sstevel@tonic-gate 						"\n\t\t\t\t\t\t(---%.64s---)",
50617c478bd9Sstevel@tonic-gate 						&buf[1]);
50627c478bd9Sstevel@tonic-gate 				break;
50637c478bd9Sstevel@tonic-gate 
50647c478bd9Sstevel@tonic-gate 			  case 'R':	/* recipient name */
50657c478bd9Sstevel@tonic-gate 				p = &buf[1];
50667c478bd9Sstevel@tonic-gate 				if (qfver >= 1)
50677c478bd9Sstevel@tonic-gate 				{
50687c478bd9Sstevel@tonic-gate 					p = strchr(p, ':');
50697c478bd9Sstevel@tonic-gate 					if (p == NULL)
50707c478bd9Sstevel@tonic-gate 						break;
50717c478bd9Sstevel@tonic-gate 					p++;
50727c478bd9Sstevel@tonic-gate 				}
50737c478bd9Sstevel@tonic-gate 				if (Verbose)
50747c478bd9Sstevel@tonic-gate 				{
50757c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50767c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50777c478bd9Sstevel@tonic-gate 							"\n\t\t\t\t\t\t");
50787c478bd9Sstevel@tonic-gate 					prtstr(p, 71);
50797c478bd9Sstevel@tonic-gate 				}
50807c478bd9Sstevel@tonic-gate 				else
50817c478bd9Sstevel@tonic-gate 				{
50827c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50837c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50847c478bd9Sstevel@tonic-gate 							"\n\t\t\t\t\t ");
50857c478bd9Sstevel@tonic-gate 					prtstr(p, 38);
50867c478bd9Sstevel@tonic-gate 				}
50877c478bd9Sstevel@tonic-gate 				if (Verbose && statmsg[0] != '\0')
50887c478bd9Sstevel@tonic-gate 				{
50897c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
50907c478bd9Sstevel@tonic-gate 							SM_TIME_DEFAULT,
50917c478bd9Sstevel@tonic-gate 							"\n\t\t (%.100s)",
50927c478bd9Sstevel@tonic-gate 							statmsg);
50937c478bd9Sstevel@tonic-gate 					statmsg[0] = '\0';
50947c478bd9Sstevel@tonic-gate 				}
50957c478bd9Sstevel@tonic-gate 				break;
50967c478bd9Sstevel@tonic-gate 
50977c478bd9Sstevel@tonic-gate 			  case 'T':	/* creation time */
50987c478bd9Sstevel@tonic-gate 				submittime = atol(&buf[1]);
50997c478bd9Sstevel@tonic-gate 				break;
51007c478bd9Sstevel@tonic-gate 
51017c478bd9Sstevel@tonic-gate 			  case 'F':	/* flag bits */
51027c478bd9Sstevel@tonic-gate 				for (p = &buf[1]; *p != '\0'; p++)
51037c478bd9Sstevel@tonic-gate 				{
51047c478bd9Sstevel@tonic-gate 					switch (*p)
51057c478bd9Sstevel@tonic-gate 					{
51067c478bd9Sstevel@tonic-gate 					  case 'w':
51077c478bd9Sstevel@tonic-gate 						flags |= EF_WARNING;
51087c478bd9Sstevel@tonic-gate 						break;
51097c478bd9Sstevel@tonic-gate 					}
51107c478bd9Sstevel@tonic-gate 				}
51117c478bd9Sstevel@tonic-gate 			}
51127c478bd9Sstevel@tonic-gate 		}
51137c478bd9Sstevel@tonic-gate 		if (submittime == (time_t) 0)
51147c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
51157c478bd9Sstevel@tonic-gate 					     " (no control file)");
51167c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n");
51177c478bd9Sstevel@tonic-gate 		(void) sm_io_close(f, SM_TIME_DEFAULT);
51187c478bd9Sstevel@tonic-gate 	}
51197c478bd9Sstevel@tonic-gate 	return nrequests;
51207c478bd9Sstevel@tonic-gate }
51217c478bd9Sstevel@tonic-gate 
51227c478bd9Sstevel@tonic-gate /*
51237c478bd9Sstevel@tonic-gate **  QUEUE_LETTER -- get the proper queue letter for the current QueueMode.
51247c478bd9Sstevel@tonic-gate **
51257c478bd9Sstevel@tonic-gate **	Parameters:
51267c478bd9Sstevel@tonic-gate **		e -- envelope to build it in/from.
51277c478bd9Sstevel@tonic-gate **		type -- the file type, used as the first character
51287c478bd9Sstevel@tonic-gate **			of the file name.
51297c478bd9Sstevel@tonic-gate **
51307c478bd9Sstevel@tonic-gate **	Returns:
51317c478bd9Sstevel@tonic-gate **		the letter to use
51327c478bd9Sstevel@tonic-gate */
51337c478bd9Sstevel@tonic-gate 
51347c478bd9Sstevel@tonic-gate static char
queue_letter(e,type)51357c478bd9Sstevel@tonic-gate queue_letter(e, type)
51367c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
51377c478bd9Sstevel@tonic-gate 	int type;
51387c478bd9Sstevel@tonic-gate {
51397c478bd9Sstevel@tonic-gate 	/* Change type according to QueueMode */
51407c478bd9Sstevel@tonic-gate 	if (type == ANYQFL_LETTER)
51417c478bd9Sstevel@tonic-gate 	{
51427c478bd9Sstevel@tonic-gate 		if (e->e_quarmsg != NULL)
51437c478bd9Sstevel@tonic-gate 			type = QUARQF_LETTER;
51447c478bd9Sstevel@tonic-gate 		else
51457c478bd9Sstevel@tonic-gate 		{
51467c478bd9Sstevel@tonic-gate 			switch (QueueMode)
51477c478bd9Sstevel@tonic-gate 			{
51487c478bd9Sstevel@tonic-gate 			  case QM_NORMAL:
51497c478bd9Sstevel@tonic-gate 				type = NORMQF_LETTER;
51507c478bd9Sstevel@tonic-gate 				break;
51517c478bd9Sstevel@tonic-gate 
51527c478bd9Sstevel@tonic-gate 			  case QM_QUARANTINE:
51537c478bd9Sstevel@tonic-gate 				type = QUARQF_LETTER;
51547c478bd9Sstevel@tonic-gate 				break;
51557c478bd9Sstevel@tonic-gate 
51567c478bd9Sstevel@tonic-gate 			  case QM_LOST:
51577c478bd9Sstevel@tonic-gate 				type = LOSEQF_LETTER;
51587c478bd9Sstevel@tonic-gate 				break;
51597c478bd9Sstevel@tonic-gate 
51607c478bd9Sstevel@tonic-gate 			  default:
51617c478bd9Sstevel@tonic-gate 				/* should never happen */
51627c478bd9Sstevel@tonic-gate 				abort();
51637c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
51647c478bd9Sstevel@tonic-gate 			}
51657c478bd9Sstevel@tonic-gate 		}
51667c478bd9Sstevel@tonic-gate 	}
51677c478bd9Sstevel@tonic-gate 	return type;
51687c478bd9Sstevel@tonic-gate }
51697c478bd9Sstevel@tonic-gate 
51707c478bd9Sstevel@tonic-gate /*
51717c478bd9Sstevel@tonic-gate **  QUEUENAME -- build a file name in the queue directory for this envelope.
51727c478bd9Sstevel@tonic-gate **
51737c478bd9Sstevel@tonic-gate **	Parameters:
51747c478bd9Sstevel@tonic-gate **		e -- envelope to build it in/from.
51757c478bd9Sstevel@tonic-gate **		type -- the file type, used as the first character
51767c478bd9Sstevel@tonic-gate **			of the file name.
51777c478bd9Sstevel@tonic-gate **
51787c478bd9Sstevel@tonic-gate **	Returns:
51797c478bd9Sstevel@tonic-gate **		a pointer to the queue name (in a static buffer).
51807c478bd9Sstevel@tonic-gate **
51817c478bd9Sstevel@tonic-gate **	Side Effects:
51827c478bd9Sstevel@tonic-gate **		If no id code is already assigned, queuename() will
51837c478bd9Sstevel@tonic-gate **		assign an id code with assign_queueid().  If no queue
51847c478bd9Sstevel@tonic-gate **		directory is assigned, one will be set with setnewqueue().
51857c478bd9Sstevel@tonic-gate */
51867c478bd9Sstevel@tonic-gate 
51877c478bd9Sstevel@tonic-gate char *
queuename(e,type)51887c478bd9Sstevel@tonic-gate queuename(e, type)
51897c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
51907c478bd9Sstevel@tonic-gate 	int type;
51917c478bd9Sstevel@tonic-gate {
51927c478bd9Sstevel@tonic-gate 	int qd, qg;
51937c478bd9Sstevel@tonic-gate 	char *sub = "/";
51947c478bd9Sstevel@tonic-gate 	char pref[3];
51957c478bd9Sstevel@tonic-gate 	static char buf[MAXPATHLEN];
51967c478bd9Sstevel@tonic-gate 
51977c478bd9Sstevel@tonic-gate 	/* Assign an ID if needed */
51987c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
51997c478bd9Sstevel@tonic-gate 		assign_queueid(e);
52007c478bd9Sstevel@tonic-gate 	type = queue_letter(e, type);
52017c478bd9Sstevel@tonic-gate 
52027c478bd9Sstevel@tonic-gate 	/* begin of filename */
52037c478bd9Sstevel@tonic-gate 	pref[0] = (char) type;
52047c478bd9Sstevel@tonic-gate 	pref[1] = 'f';
52057c478bd9Sstevel@tonic-gate 	pref[2] = '\0';
52067c478bd9Sstevel@tonic-gate 
52077c478bd9Sstevel@tonic-gate 	/* Assign a queue group/directory if needed */
52087c478bd9Sstevel@tonic-gate 	if (type == XSCRPT_LETTER)
52097c478bd9Sstevel@tonic-gate 	{
52107c478bd9Sstevel@tonic-gate 		/*
52117c478bd9Sstevel@tonic-gate 		**  We don't want to call setnewqueue() if we are fetching
52127c478bd9Sstevel@tonic-gate 		**  the pathname of the transcript file, because setnewqueue
52137c478bd9Sstevel@tonic-gate 		**  chooses a queue, and sometimes we need to write to the
52147c478bd9Sstevel@tonic-gate 		**  transcript file before we have gathered enough information
52157c478bd9Sstevel@tonic-gate 		**  to choose a queue.
52167c478bd9Sstevel@tonic-gate 		*/
52177c478bd9Sstevel@tonic-gate 
52187c478bd9Sstevel@tonic-gate 		if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
52197c478bd9Sstevel@tonic-gate 		{
52207c478bd9Sstevel@tonic-gate 			if (e->e_qgrp != NOQGRP && e->e_qdir != NOQDIR)
52217c478bd9Sstevel@tonic-gate 			{
52227c478bd9Sstevel@tonic-gate 				e->e_xfqgrp = e->e_qgrp;
52237c478bd9Sstevel@tonic-gate 				e->e_xfqdir = e->e_qdir;
52247c478bd9Sstevel@tonic-gate 			}
52257c478bd9Sstevel@tonic-gate 			else
52267c478bd9Sstevel@tonic-gate 			{
52277c478bd9Sstevel@tonic-gate 				e->e_xfqgrp = 0;
52287c478bd9Sstevel@tonic-gate 				if (Queue[e->e_xfqgrp]->qg_numqueues <= 1)
52297c478bd9Sstevel@tonic-gate 					e->e_xfqdir = 0;
52307c478bd9Sstevel@tonic-gate 				else
52317c478bd9Sstevel@tonic-gate 				{
52327c478bd9Sstevel@tonic-gate 					e->e_xfqdir = get_rand_mod(
52337c478bd9Sstevel@tonic-gate 					      Queue[e->e_xfqgrp]->qg_numqueues);
52347c478bd9Sstevel@tonic-gate 				}
52357c478bd9Sstevel@tonic-gate 			}
52367c478bd9Sstevel@tonic-gate 		}
52377c478bd9Sstevel@tonic-gate 		qd = e->e_xfqdir;
52387c478bd9Sstevel@tonic-gate 		qg = e->e_xfqgrp;
52397c478bd9Sstevel@tonic-gate 	}
52407c478bd9Sstevel@tonic-gate 	else
52417c478bd9Sstevel@tonic-gate 	{
52427c478bd9Sstevel@tonic-gate 		if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
52433ee0e492Sjbeck 			(void) setnewqueue(e);
52447c478bd9Sstevel@tonic-gate 		if (type ==  DATAFL_LETTER)
52457c478bd9Sstevel@tonic-gate 		{
52467c478bd9Sstevel@tonic-gate 			qd = e->e_dfqdir;
52477c478bd9Sstevel@tonic-gate 			qg = e->e_dfqgrp;
52487c478bd9Sstevel@tonic-gate 		}
52497c478bd9Sstevel@tonic-gate 		else
52507c478bd9Sstevel@tonic-gate 		{
52517c478bd9Sstevel@tonic-gate 			qd = e->e_qdir;
52527c478bd9Sstevel@tonic-gate 			qg = e->e_qgrp;
52537c478bd9Sstevel@tonic-gate 		}
52547c478bd9Sstevel@tonic-gate 	}
52557c478bd9Sstevel@tonic-gate 
52567c478bd9Sstevel@tonic-gate 	/* xf files always have a valid qd and qg picked above */
52573ee0e492Sjbeck 	if ((qd == NOQDIR || qg == NOQGRP) && type != XSCRPT_LETTER)
5258058561cbSjbeck 		(void) sm_strlcpyn(buf, sizeof(buf), 2, pref, e->e_id);
52597c478bd9Sstevel@tonic-gate 	else
52607c478bd9Sstevel@tonic-gate 	{
52617c478bd9Sstevel@tonic-gate 		switch (type)
52627c478bd9Sstevel@tonic-gate 		{
52637c478bd9Sstevel@tonic-gate 		  case DATAFL_LETTER:
52647c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBDF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52657c478bd9Sstevel@tonic-gate 				sub = "/df/";
52667c478bd9Sstevel@tonic-gate 			break;
52677c478bd9Sstevel@tonic-gate 
52687c478bd9Sstevel@tonic-gate 		  case QUARQF_LETTER:
52697c478bd9Sstevel@tonic-gate 		  case TEMPQF_LETTER:
52707c478bd9Sstevel@tonic-gate 		  case NEWQFL_LETTER:
52717c478bd9Sstevel@tonic-gate 		  case LOSEQF_LETTER:
52727c478bd9Sstevel@tonic-gate 		  case NORMQF_LETTER:
52737c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBQF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52747c478bd9Sstevel@tonic-gate 				sub = "/qf/";
52757c478bd9Sstevel@tonic-gate 			break;
52767c478bd9Sstevel@tonic-gate 
52777c478bd9Sstevel@tonic-gate 		  case XSCRPT_LETTER:
52787c478bd9Sstevel@tonic-gate 			if (bitset(QP_SUBXF, Queue[qg]->qg_qpaths[qd].qp_subdirs))
52797c478bd9Sstevel@tonic-gate 				sub = "/xf/";
52807c478bd9Sstevel@tonic-gate 			break;
52817c478bd9Sstevel@tonic-gate 
52827c478bd9Sstevel@tonic-gate 		  default:
52837c478bd9Sstevel@tonic-gate 			sm_abort("queuename: bad queue file type %d", type);
52847c478bd9Sstevel@tonic-gate 		}
52857c478bd9Sstevel@tonic-gate 
5286058561cbSjbeck 		(void) sm_strlcpyn(buf, sizeof(buf), 4,
52877c478bd9Sstevel@tonic-gate 				Queue[qg]->qg_qpaths[qd].qp_name,
52887c478bd9Sstevel@tonic-gate 				sub, pref, e->e_id);
52897c478bd9Sstevel@tonic-gate 	}
52907c478bd9Sstevel@tonic-gate 
52917c478bd9Sstevel@tonic-gate 	if (tTd(7, 2))
52927c478bd9Sstevel@tonic-gate 		sm_dprintf("queuename: %s\n", buf);
52937c478bd9Sstevel@tonic-gate 	return buf;
52947c478bd9Sstevel@tonic-gate }
52957c478bd9Sstevel@tonic-gate 
52967c478bd9Sstevel@tonic-gate /*
52977c478bd9Sstevel@tonic-gate **  INIT_QID_ALG -- Initialize the (static) parameters that are used to
52987c478bd9Sstevel@tonic-gate **	generate a queue ID.
52997c478bd9Sstevel@tonic-gate **
53007c478bd9Sstevel@tonic-gate **	This function is called by the daemon to reset
53017c478bd9Sstevel@tonic-gate **	LastQueueTime and LastQueuePid which are used by assign_queueid().
53027c478bd9Sstevel@tonic-gate **	Otherwise the algorithm may cause problems because
53037c478bd9Sstevel@tonic-gate **	LastQueueTime and LastQueuePid are set indirectly by main()
53047c478bd9Sstevel@tonic-gate **	before the daemon process is started, hence LastQueuePid is not
53057c478bd9Sstevel@tonic-gate **	the pid of the daemon and therefore a child of the daemon can
53067c478bd9Sstevel@tonic-gate **	actually have the same pid as LastQueuePid which means the section
53077c478bd9Sstevel@tonic-gate **	in  assign_queueid():
53087c478bd9Sstevel@tonic-gate **	* see if we need to get a new base time/pid *
53097c478bd9Sstevel@tonic-gate **	is NOT triggered which will cause the same queue id to be generated.
53107c478bd9Sstevel@tonic-gate **
53117c478bd9Sstevel@tonic-gate **	Parameters:
53127c478bd9Sstevel@tonic-gate **		none
53137c478bd9Sstevel@tonic-gate **
53147c478bd9Sstevel@tonic-gate **	Returns:
53157c478bd9Sstevel@tonic-gate **		none.
53167c478bd9Sstevel@tonic-gate */
53177c478bd9Sstevel@tonic-gate 
53187c478bd9Sstevel@tonic-gate void
init_qid_alg()53197c478bd9Sstevel@tonic-gate init_qid_alg()
53207c478bd9Sstevel@tonic-gate {
53217c478bd9Sstevel@tonic-gate 	LastQueueTime = 0;
53227c478bd9Sstevel@tonic-gate 	LastQueuePid = -1;
53237c478bd9Sstevel@tonic-gate }
53247c478bd9Sstevel@tonic-gate 
53257c478bd9Sstevel@tonic-gate /*
53267c478bd9Sstevel@tonic-gate **  ASSIGN_QUEUEID -- assign a queue ID for this envelope.
53277c478bd9Sstevel@tonic-gate **
53287c478bd9Sstevel@tonic-gate **	Assigns an id code if one does not already exist.
53297c478bd9Sstevel@tonic-gate **	This code assumes that nothing will remain in the queue for
53307c478bd9Sstevel@tonic-gate **	longer than 60 years.  It is critical that files with the given
53317c478bd9Sstevel@tonic-gate **	name do not already exist in the queue.
53327c478bd9Sstevel@tonic-gate **	[No longer initializes e_qdir to NOQDIR.]
53337c478bd9Sstevel@tonic-gate **
53347c478bd9Sstevel@tonic-gate **	Parameters:
53357c478bd9Sstevel@tonic-gate **		e -- envelope to set it in.
53367c478bd9Sstevel@tonic-gate **
53377c478bd9Sstevel@tonic-gate **	Returns:
53387c478bd9Sstevel@tonic-gate **		none.
53397c478bd9Sstevel@tonic-gate */
53407c478bd9Sstevel@tonic-gate 
53417c478bd9Sstevel@tonic-gate static const char QueueIdChars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
53427c478bd9Sstevel@tonic-gate # define QIC_LEN	60
53437c478bd9Sstevel@tonic-gate # define QIC_LEN_R	62
53447c478bd9Sstevel@tonic-gate 
53457c478bd9Sstevel@tonic-gate /*
53467c478bd9Sstevel@tonic-gate **  Note: the length is "officially" 60 because minutes and seconds are
53477c478bd9Sstevel@tonic-gate **	usually only 0-59.  However (Linux):
53487c478bd9Sstevel@tonic-gate **       tm_sec The number of seconds after the minute, normally in
53497c478bd9Sstevel@tonic-gate **		the range 0 to 59, but can be up to 61 to allow for
53507c478bd9Sstevel@tonic-gate **		leap seconds.
53517c478bd9Sstevel@tonic-gate **	Hence the real length of the string is 62 to take this into account.
53527c478bd9Sstevel@tonic-gate **	Alternatively % QIC_LEN can (should) be used for access everywhere.
53537c478bd9Sstevel@tonic-gate */
53547c478bd9Sstevel@tonic-gate 
53557c478bd9Sstevel@tonic-gate # define queuenextid() CurrentPid
5356e9af4bc0SJohn Beck #define QIC_LEN_SQR	(QIC_LEN * QIC_LEN)
53577c478bd9Sstevel@tonic-gate 
53587c478bd9Sstevel@tonic-gate void
assign_queueid(e)53597c478bd9Sstevel@tonic-gate assign_queueid(e)
53607c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
53617c478bd9Sstevel@tonic-gate {
53627c478bd9Sstevel@tonic-gate 	pid_t pid = queuenextid();
5363e9af4bc0SJohn Beck 	static unsigned int cX = 0;
5364e9af4bc0SJohn Beck 	static unsigned int random_offset;
53657c478bd9Sstevel@tonic-gate 	struct tm *tm;
53667c478bd9Sstevel@tonic-gate 	char idbuf[MAXQFNAME - 2];
5367e9af4bc0SJohn Beck 	unsigned int seq;
53687c478bd9Sstevel@tonic-gate 
53697c478bd9Sstevel@tonic-gate 	if (e->e_id != NULL)
53707c478bd9Sstevel@tonic-gate 		return;
53717c478bd9Sstevel@tonic-gate 
53727c478bd9Sstevel@tonic-gate 	/* see if we need to get a new base time/pid */
5373e9af4bc0SJohn Beck 	if (cX >= QIC_LEN_SQR || LastQueueTime == 0 || LastQueuePid != pid)
53747c478bd9Sstevel@tonic-gate 	{
53757c478bd9Sstevel@tonic-gate 		time_t then = LastQueueTime;
53767c478bd9Sstevel@tonic-gate 
53777c478bd9Sstevel@tonic-gate 		/* if the first time through, pick a random offset */
53787c478bd9Sstevel@tonic-gate 		if (LastQueueTime == 0)
5379e9af4bc0SJohn Beck 			random_offset = ((unsigned int)get_random())
5380e9af4bc0SJohn Beck 					% QIC_LEN_SQR;
53817c478bd9Sstevel@tonic-gate 
53827c478bd9Sstevel@tonic-gate 		while ((LastQueueTime = curtime()) == then &&
53837c478bd9Sstevel@tonic-gate 		       LastQueuePid == pid)
53847c478bd9Sstevel@tonic-gate 		{
53857c478bd9Sstevel@tonic-gate 			(void) sleep(1);
53867c478bd9Sstevel@tonic-gate 		}
53877c478bd9Sstevel@tonic-gate 		LastQueuePid = queuenextid();
53887c478bd9Sstevel@tonic-gate 		cX = 0;
53897c478bd9Sstevel@tonic-gate 	}
53907c478bd9Sstevel@tonic-gate 
53917c478bd9Sstevel@tonic-gate 	/*
5392e9af4bc0SJohn Beck 	**  Generate a new sequence number between 0 and QIC_LEN_SQR-1.
5393e9af4bc0SJohn Beck 	**  This lets us generate up to QIC_LEN_SQR unique queue ids
53947c478bd9Sstevel@tonic-gate 	**  per second, per process.  With envelope splitting,
53957c478bd9Sstevel@tonic-gate 	**  a single message can consume many queue ids.
53967c478bd9Sstevel@tonic-gate 	*/
53977c478bd9Sstevel@tonic-gate 
5398e9af4bc0SJohn Beck 	seq = (cX + random_offset) % QIC_LEN_SQR;
53997c478bd9Sstevel@tonic-gate 	++cX;
54007c478bd9Sstevel@tonic-gate 	if (tTd(7, 50))
5401e9af4bc0SJohn Beck 		sm_dprintf("assign_queueid: random_offset=%u (%u)\n",
54027c478bd9Sstevel@tonic-gate 			random_offset, seq);
54037c478bd9Sstevel@tonic-gate 
54047c478bd9Sstevel@tonic-gate 	tm = gmtime(&LastQueueTime);
54057c478bd9Sstevel@tonic-gate 	idbuf[0] = QueueIdChars[tm->tm_year % QIC_LEN];
54067c478bd9Sstevel@tonic-gate 	idbuf[1] = QueueIdChars[tm->tm_mon];
54077c478bd9Sstevel@tonic-gate 	idbuf[2] = QueueIdChars[tm->tm_mday];
54087c478bd9Sstevel@tonic-gate 	idbuf[3] = QueueIdChars[tm->tm_hour];
54097c478bd9Sstevel@tonic-gate 	idbuf[4] = QueueIdChars[tm->tm_min % QIC_LEN_R];
54107c478bd9Sstevel@tonic-gate 	idbuf[5] = QueueIdChars[tm->tm_sec % QIC_LEN_R];
54117c478bd9Sstevel@tonic-gate 	idbuf[6] = QueueIdChars[seq / QIC_LEN];
54127c478bd9Sstevel@tonic-gate 	idbuf[7] = QueueIdChars[seq % QIC_LEN];
5413058561cbSjbeck 	(void) sm_snprintf(&idbuf[8], sizeof(idbuf) - 8, "%06d",
54147c478bd9Sstevel@tonic-gate 			   (int) LastQueuePid);
54157c478bd9Sstevel@tonic-gate 	e->e_id = sm_rpool_strdup_x(e->e_rpool, idbuf);
54167c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'i', e->e_id);
54177c478bd9Sstevel@tonic-gate #if 0
54187c478bd9Sstevel@tonic-gate 	/* XXX: inherited from MainEnvelope */
54197c478bd9Sstevel@tonic-gate 	e->e_qgrp = NOQGRP;  /* too early to do anything else */
54207c478bd9Sstevel@tonic-gate 	e->e_qdir = NOQDIR;
54217c478bd9Sstevel@tonic-gate 	e->e_xfqgrp = NOQGRP;
54227c478bd9Sstevel@tonic-gate #endif /* 0 */
54237c478bd9Sstevel@tonic-gate 
54247c478bd9Sstevel@tonic-gate 	/* New ID means it's not on disk yet */
54257c478bd9Sstevel@tonic-gate 	e->e_qfletter = '\0';
54267c478bd9Sstevel@tonic-gate 
54277c478bd9Sstevel@tonic-gate 	if (tTd(7, 1))
54287c478bd9Sstevel@tonic-gate 		sm_dprintf("assign_queueid: assigned id %s, e=%p\n",
54297c478bd9Sstevel@tonic-gate 			e->e_id, e);
54307c478bd9Sstevel@tonic-gate 	if (LogLevel > 93)
54317c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id, "assigned id");
54327c478bd9Sstevel@tonic-gate }
54337c478bd9Sstevel@tonic-gate /*
54347c478bd9Sstevel@tonic-gate **  SYNC_QUEUE_TIME -- Assure exclusive PID in any given second
54357c478bd9Sstevel@tonic-gate **
54367c478bd9Sstevel@tonic-gate **	Make sure one PID can't be used by two processes in any one second.
54377c478bd9Sstevel@tonic-gate **
54387c478bd9Sstevel@tonic-gate **		If the system rotates PIDs fast enough, may get the
54397c478bd9Sstevel@tonic-gate **		same pid in the same second for two distinct processes.
54407c478bd9Sstevel@tonic-gate **		This will interfere with the queue file naming system.
54417c478bd9Sstevel@tonic-gate **
54427c478bd9Sstevel@tonic-gate **	Parameters:
54437c478bd9Sstevel@tonic-gate **		none
54447c478bd9Sstevel@tonic-gate **
54457c478bd9Sstevel@tonic-gate **	Returns:
54467c478bd9Sstevel@tonic-gate **		none
54477c478bd9Sstevel@tonic-gate */
54487c478bd9Sstevel@tonic-gate 
54497c478bd9Sstevel@tonic-gate void
sync_queue_time()54507c478bd9Sstevel@tonic-gate sync_queue_time()
54517c478bd9Sstevel@tonic-gate {
54527c478bd9Sstevel@tonic-gate #if FAST_PID_RECYCLE
54537c478bd9Sstevel@tonic-gate 	if (OpMode != MD_TEST &&
5454e9af4bc0SJohn Beck 	    OpMode != MD_CHECKCONFIG &&
54557c478bd9Sstevel@tonic-gate 	    OpMode != MD_VERIFY &&
54567c478bd9Sstevel@tonic-gate 	    LastQueueTime > 0 &&
54577c478bd9Sstevel@tonic-gate 	    LastQueuePid == CurrentPid &&
54587c478bd9Sstevel@tonic-gate 	    curtime() == LastQueueTime)
54597c478bd9Sstevel@tonic-gate 		(void) sleep(1);
54607c478bd9Sstevel@tonic-gate #endif /* FAST_PID_RECYCLE */
54617c478bd9Sstevel@tonic-gate }
54627c478bd9Sstevel@tonic-gate /*
54637c478bd9Sstevel@tonic-gate **  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
54647c478bd9Sstevel@tonic-gate **
54657c478bd9Sstevel@tonic-gate **	Parameters:
54667c478bd9Sstevel@tonic-gate **		e -- the envelope to unlock.
54677c478bd9Sstevel@tonic-gate **
54687c478bd9Sstevel@tonic-gate **	Returns:
54697c478bd9Sstevel@tonic-gate **		none
54707c478bd9Sstevel@tonic-gate **
54717c478bd9Sstevel@tonic-gate **	Side Effects:
54727c478bd9Sstevel@tonic-gate **		unlocks the queue for `e'.
54737c478bd9Sstevel@tonic-gate */
54747c478bd9Sstevel@tonic-gate 
54757c478bd9Sstevel@tonic-gate void
unlockqueue(e)54767c478bd9Sstevel@tonic-gate unlockqueue(e)
54777c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
54787c478bd9Sstevel@tonic-gate {
54797c478bd9Sstevel@tonic-gate 	if (tTd(51, 4))
54807c478bd9Sstevel@tonic-gate 		sm_dprintf("unlockqueue(%s)\n",
54817c478bd9Sstevel@tonic-gate 			e->e_id == NULL ? "NOQUEUE" : e->e_id);
54827c478bd9Sstevel@tonic-gate 
54837c478bd9Sstevel@tonic-gate 
54847c478bd9Sstevel@tonic-gate 	/* if there is a lock file in the envelope, close it */
54857c478bd9Sstevel@tonic-gate 	if (e->e_lockfp != NULL)
54867c478bd9Sstevel@tonic-gate 		(void) sm_io_close(e->e_lockfp, SM_TIME_DEFAULT);
54877c478bd9Sstevel@tonic-gate 	e->e_lockfp = NULL;
54887c478bd9Sstevel@tonic-gate 
54897c478bd9Sstevel@tonic-gate 	/* don't create a queue id if we don't already have one */
54907c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
54917c478bd9Sstevel@tonic-gate 		return;
54927c478bd9Sstevel@tonic-gate 
54937c478bd9Sstevel@tonic-gate 	/* remove the transcript */
54947c478bd9Sstevel@tonic-gate 	if (LogLevel > 87)
54957c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id, "unlock");
54967c478bd9Sstevel@tonic-gate 	if (!tTd(51, 104))
54977c478bd9Sstevel@tonic-gate 		(void) xunlink(queuename(e, XSCRPT_LETTER));
54987c478bd9Sstevel@tonic-gate }
54997c478bd9Sstevel@tonic-gate /*
55007c478bd9Sstevel@tonic-gate **  SETCTLUSER -- create a controlling address
55017c478bd9Sstevel@tonic-gate **
55027c478bd9Sstevel@tonic-gate **	Create a fake "address" given only a local login name; this is
55037c478bd9Sstevel@tonic-gate **	used as a "controlling user" for future recipient addresses.
55047c478bd9Sstevel@tonic-gate **
55057c478bd9Sstevel@tonic-gate **	Parameters:
55067c478bd9Sstevel@tonic-gate **		user -- the user name of the controlling user.
55077c478bd9Sstevel@tonic-gate **		qfver -- the version stamp of this queue file.
55087c478bd9Sstevel@tonic-gate **		e -- envelope
55097c478bd9Sstevel@tonic-gate **
55107c478bd9Sstevel@tonic-gate **	Returns:
55117c478bd9Sstevel@tonic-gate **		An address descriptor for the controlling user,
55127c478bd9Sstevel@tonic-gate **		using storage allocated from e->e_rpool.
55137c478bd9Sstevel@tonic-gate **
55147c478bd9Sstevel@tonic-gate */
55157c478bd9Sstevel@tonic-gate 
55167c478bd9Sstevel@tonic-gate static ADDRESS *
setctluser(user,qfver,e)55177c478bd9Sstevel@tonic-gate setctluser(user, qfver, e)
55187c478bd9Sstevel@tonic-gate 	char *user;
55197c478bd9Sstevel@tonic-gate 	int qfver;
55207c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
55217c478bd9Sstevel@tonic-gate {
55227c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
55237c478bd9Sstevel@tonic-gate 	struct passwd *pw;
55247c478bd9Sstevel@tonic-gate 	char *p;
55257c478bd9Sstevel@tonic-gate 
55267c478bd9Sstevel@tonic-gate 	/*
55277c478bd9Sstevel@tonic-gate 	**  See if this clears our concept of controlling user.
55287c478bd9Sstevel@tonic-gate 	*/
55297c478bd9Sstevel@tonic-gate 
55307c478bd9Sstevel@tonic-gate 	if (user == NULL || *user == '\0')
55317c478bd9Sstevel@tonic-gate 		return NULL;
55327c478bd9Sstevel@tonic-gate 
55337c478bd9Sstevel@tonic-gate 	/*
55347c478bd9Sstevel@tonic-gate 	**  Set up addr fields for controlling user.
55357c478bd9Sstevel@tonic-gate 	*/
55367c478bd9Sstevel@tonic-gate 
5537058561cbSjbeck 	a = (ADDRESS *) sm_rpool_malloc_x(e->e_rpool, sizeof(*a));
5538058561cbSjbeck 	memset((char *) a, '\0', sizeof(*a));
55397c478bd9Sstevel@tonic-gate 
55407c478bd9Sstevel@tonic-gate 	if (*user == ':')
55417c478bd9Sstevel@tonic-gate 	{
55427c478bd9Sstevel@tonic-gate 		p = &user[1];
55437c478bd9Sstevel@tonic-gate 		a->q_user = sm_rpool_strdup_x(e->e_rpool, p);
55447c478bd9Sstevel@tonic-gate 	}
55457c478bd9Sstevel@tonic-gate 	else
55467c478bd9Sstevel@tonic-gate 	{
55477c478bd9Sstevel@tonic-gate 		p = strtok(user, ":");
55487c478bd9Sstevel@tonic-gate 		a->q_user = sm_rpool_strdup_x(e->e_rpool, user);
55497c478bd9Sstevel@tonic-gate 		if (qfver >= 2)
55507c478bd9Sstevel@tonic-gate 		{
55517c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55527c478bd9Sstevel@tonic-gate 				a->q_uid = atoi(p);
55537c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55547c478bd9Sstevel@tonic-gate 				a->q_gid = atoi(p);
55557c478bd9Sstevel@tonic-gate 			if ((p = strtok(NULL, ":")) != NULL)
55567c478bd9Sstevel@tonic-gate 			{
55577c478bd9Sstevel@tonic-gate 				char *o;
55587c478bd9Sstevel@tonic-gate 
55597c478bd9Sstevel@tonic-gate 				a->q_flags |= QGOODUID;
55607c478bd9Sstevel@tonic-gate 
55617c478bd9Sstevel@tonic-gate 				/* if there is another ':': restore it */
55627c478bd9Sstevel@tonic-gate 				if ((o = strtok(NULL, ":")) != NULL && o > p)
55637c478bd9Sstevel@tonic-gate 					o[-1] = ':';
55647c478bd9Sstevel@tonic-gate 			}
55657c478bd9Sstevel@tonic-gate 		}
55667c478bd9Sstevel@tonic-gate 		else if ((pw = sm_getpwnam(user)) != NULL)
55677c478bd9Sstevel@tonic-gate 		{
55687c478bd9Sstevel@tonic-gate 			if (*pw->pw_dir == '\0')
55697c478bd9Sstevel@tonic-gate 				a->q_home = NULL;
55707c478bd9Sstevel@tonic-gate 			else if (strcmp(pw->pw_dir, "/") == 0)
55717c478bd9Sstevel@tonic-gate 				a->q_home = "";
55727c478bd9Sstevel@tonic-gate 			else
55737c478bd9Sstevel@tonic-gate 				a->q_home = sm_rpool_strdup_x(e->e_rpool, pw->pw_dir);
55747c478bd9Sstevel@tonic-gate 			a->q_uid = pw->pw_uid;
55757c478bd9Sstevel@tonic-gate 			a->q_gid = pw->pw_gid;
55767c478bd9Sstevel@tonic-gate 			a->q_flags |= QGOODUID;
55777c478bd9Sstevel@tonic-gate 		}
55787c478bd9Sstevel@tonic-gate 	}
55797c478bd9Sstevel@tonic-gate 
55807c478bd9Sstevel@tonic-gate 	a->q_flags |= QPRIMARY;		/* flag as a "ctladdr" */
55817c478bd9Sstevel@tonic-gate 	a->q_mailer = LocalMailer;
55827c478bd9Sstevel@tonic-gate 	if (p == NULL)
55837c478bd9Sstevel@tonic-gate 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, a->q_user);
55847c478bd9Sstevel@tonic-gate 	else
55857c478bd9Sstevel@tonic-gate 		a->q_paddr = sm_rpool_strdup_x(e->e_rpool, p);
55867c478bd9Sstevel@tonic-gate 	return a;
55877c478bd9Sstevel@tonic-gate }
55887c478bd9Sstevel@tonic-gate /*
55897c478bd9Sstevel@tonic-gate **  LOSEQFILE -- rename queue file with LOSEQF_LETTER & try to let someone know
55907c478bd9Sstevel@tonic-gate **
55917c478bd9Sstevel@tonic-gate **	Parameters:
55927c478bd9Sstevel@tonic-gate **		e -- the envelope (e->e_id will be used).
55937c478bd9Sstevel@tonic-gate **		why -- reported to whomever can hear.
55947c478bd9Sstevel@tonic-gate **
55957c478bd9Sstevel@tonic-gate **	Returns:
55967c478bd9Sstevel@tonic-gate **		none.
55977c478bd9Sstevel@tonic-gate */
55987c478bd9Sstevel@tonic-gate 
55997c478bd9Sstevel@tonic-gate void
loseqfile(e,why)56007c478bd9Sstevel@tonic-gate loseqfile(e, why)
56017c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
56027c478bd9Sstevel@tonic-gate 	char *why;
56037c478bd9Sstevel@tonic-gate {
56047c478bd9Sstevel@tonic-gate 	bool loseit = true;
56057c478bd9Sstevel@tonic-gate 	char *p;
56067c478bd9Sstevel@tonic-gate 	char buf[MAXPATHLEN];
56077c478bd9Sstevel@tonic-gate 
56087c478bd9Sstevel@tonic-gate 	if (e == NULL || e->e_id == NULL)
56097c478bd9Sstevel@tonic-gate 		return;
56107c478bd9Sstevel@tonic-gate 	p = queuename(e, ANYQFL_LETTER);
5611058561cbSjbeck 	if (sm_strlcpy(buf, p, sizeof(buf)) >= sizeof(buf))
56127c478bd9Sstevel@tonic-gate 		return;
56137c478bd9Sstevel@tonic-gate 	if (!bitset(EF_INQUEUE, e->e_flags))
56147c478bd9Sstevel@tonic-gate 		queueup(e, false, true);
56157c478bd9Sstevel@tonic-gate 	else if (QueueMode == QM_LOST)
56167c478bd9Sstevel@tonic-gate 		loseit = false;
56177c478bd9Sstevel@tonic-gate 
56187c478bd9Sstevel@tonic-gate 	/* if already lost, no need to re-lose */
56197c478bd9Sstevel@tonic-gate 	if (loseit)
56207c478bd9Sstevel@tonic-gate 	{
56217c478bd9Sstevel@tonic-gate 		p = queuename(e, LOSEQF_LETTER);
56227c478bd9Sstevel@tonic-gate 		if (rename(buf, p) < 0)
56237c478bd9Sstevel@tonic-gate 			syserr("cannot rename(%s, %s), uid=%d",
56247c478bd9Sstevel@tonic-gate 			       buf, p, (int) geteuid());
56257c478bd9Sstevel@tonic-gate 		else if (LogLevel > 0)
56267c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ALERT, e->e_id,
56277c478bd9Sstevel@tonic-gate 				  "Losing %s: %s", buf, why);
56287c478bd9Sstevel@tonic-gate 	}
56297c478bd9Sstevel@tonic-gate 	if (e->e_dfp != NULL)
56307c478bd9Sstevel@tonic-gate 	{
56317c478bd9Sstevel@tonic-gate 		(void) sm_io_close(e->e_dfp, SM_TIME_DEFAULT);
56327c478bd9Sstevel@tonic-gate 		e->e_dfp = NULL;
56337c478bd9Sstevel@tonic-gate 	}
56347c478bd9Sstevel@tonic-gate 	e->e_flags &= ~EF_HAS_DF;
56357c478bd9Sstevel@tonic-gate }
56367c478bd9Sstevel@tonic-gate /*
56377c478bd9Sstevel@tonic-gate **  NAME2QID -- translate a queue group name to a queue group id
56387c478bd9Sstevel@tonic-gate **
56397c478bd9Sstevel@tonic-gate **	Parameters:
56407c478bd9Sstevel@tonic-gate **		queuename -- name of queue group.
56417c478bd9Sstevel@tonic-gate **
56427c478bd9Sstevel@tonic-gate **	Returns:
56437c478bd9Sstevel@tonic-gate **		queue group id if found.
56447c478bd9Sstevel@tonic-gate **		NOQGRP otherwise.
56457c478bd9Sstevel@tonic-gate */
56467c478bd9Sstevel@tonic-gate 
56477c478bd9Sstevel@tonic-gate int
name2qid(queuename)56487c478bd9Sstevel@tonic-gate name2qid(queuename)
56497c478bd9Sstevel@tonic-gate 	char *queuename;
56507c478bd9Sstevel@tonic-gate {
56517c478bd9Sstevel@tonic-gate 	register STAB *s;
56527c478bd9Sstevel@tonic-gate 
56537c478bd9Sstevel@tonic-gate 	s = stab(queuename, ST_QUEUE, ST_FIND);
56547c478bd9Sstevel@tonic-gate 	if (s == NULL)
56557c478bd9Sstevel@tonic-gate 		return NOQGRP;
56567c478bd9Sstevel@tonic-gate 	return s->s_quegrp->qg_index;
56577c478bd9Sstevel@tonic-gate }
56587c478bd9Sstevel@tonic-gate /*
56597c478bd9Sstevel@tonic-gate **  QID_PRINTNAME -- create externally printable version of queue id
56607c478bd9Sstevel@tonic-gate **
56617c478bd9Sstevel@tonic-gate **	Parameters:
56627c478bd9Sstevel@tonic-gate **		e -- the envelope.
56637c478bd9Sstevel@tonic-gate **
56647c478bd9Sstevel@tonic-gate **	Returns:
56657c478bd9Sstevel@tonic-gate **		a printable version
56667c478bd9Sstevel@tonic-gate */
56677c478bd9Sstevel@tonic-gate 
56687c478bd9Sstevel@tonic-gate char *
qid_printname(e)56697c478bd9Sstevel@tonic-gate qid_printname(e)
56707c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
56717c478bd9Sstevel@tonic-gate {
56727c478bd9Sstevel@tonic-gate 	char *id;
56737c478bd9Sstevel@tonic-gate 	static char idbuf[MAXQFNAME + 34];
56747c478bd9Sstevel@tonic-gate 
56757c478bd9Sstevel@tonic-gate 	if (e == NULL)
56767c478bd9Sstevel@tonic-gate 		return "";
56777c478bd9Sstevel@tonic-gate 
56787c478bd9Sstevel@tonic-gate 	if (e->e_id == NULL)
56797c478bd9Sstevel@tonic-gate 		id = "";
56807c478bd9Sstevel@tonic-gate 	else
56817c478bd9Sstevel@tonic-gate 		id = e->e_id;
56827c478bd9Sstevel@tonic-gate 
56837c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR)
56847c478bd9Sstevel@tonic-gate 		return id;
56857c478bd9Sstevel@tonic-gate 
5686058561cbSjbeck 	(void) sm_snprintf(idbuf, sizeof(idbuf), "%.32s/%s",
56877c478bd9Sstevel@tonic-gate 			   Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_name,
56887c478bd9Sstevel@tonic-gate 			   id);
56897c478bd9Sstevel@tonic-gate 	return idbuf;
56907c478bd9Sstevel@tonic-gate }
56917c478bd9Sstevel@tonic-gate /*
56927c478bd9Sstevel@tonic-gate **  QID_PRINTQUEUE -- create full version of queue directory for data files
56937c478bd9Sstevel@tonic-gate **
56947c478bd9Sstevel@tonic-gate **	Parameters:
56957c478bd9Sstevel@tonic-gate **		qgrp -- index in queue group.
56967c478bd9Sstevel@tonic-gate **		qdir -- the short version of the queue directory
56977c478bd9Sstevel@tonic-gate **
56987c478bd9Sstevel@tonic-gate **	Returns:
56997c478bd9Sstevel@tonic-gate **		the full pathname to the queue (might point to a static var)
57007c478bd9Sstevel@tonic-gate */
57017c478bd9Sstevel@tonic-gate 
57027c478bd9Sstevel@tonic-gate char *
qid_printqueue(qgrp,qdir)57037c478bd9Sstevel@tonic-gate qid_printqueue(qgrp, qdir)
57047c478bd9Sstevel@tonic-gate 	int qgrp;
57057c478bd9Sstevel@tonic-gate 	int qdir;
57067c478bd9Sstevel@tonic-gate {
57077c478bd9Sstevel@tonic-gate 	char *subdir;
57087c478bd9Sstevel@tonic-gate 	static char dir[MAXPATHLEN];
57097c478bd9Sstevel@tonic-gate 
57107c478bd9Sstevel@tonic-gate 	if (qdir == NOQDIR)
57117c478bd9Sstevel@tonic-gate 		return Queue[qgrp]->qg_qdir;
57127c478bd9Sstevel@tonic-gate 
57137c478bd9Sstevel@tonic-gate 	if (strcmp(Queue[qgrp]->qg_qpaths[qdir].qp_name, ".") == 0)
57147c478bd9Sstevel@tonic-gate 		subdir = NULL;
57157c478bd9Sstevel@tonic-gate 	else
57167c478bd9Sstevel@tonic-gate 		subdir = Queue[qgrp]->qg_qpaths[qdir].qp_name;
57177c478bd9Sstevel@tonic-gate 
5718058561cbSjbeck 	(void) sm_strlcpyn(dir, sizeof(dir), 4,
57197c478bd9Sstevel@tonic-gate 			Queue[qgrp]->qg_qdir,
57207c478bd9Sstevel@tonic-gate 			subdir == NULL ? "" : "/",
57217c478bd9Sstevel@tonic-gate 			subdir == NULL ? "" : subdir,
57227c478bd9Sstevel@tonic-gate 			(bitset(QP_SUBDF,
57237c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_qpaths[qdir].qp_subdirs)
57247c478bd9Sstevel@tonic-gate 					? "/df" : ""));
57257c478bd9Sstevel@tonic-gate 	return dir;
57267c478bd9Sstevel@tonic-gate }
57277c478bd9Sstevel@tonic-gate 
57287c478bd9Sstevel@tonic-gate /*
57297c478bd9Sstevel@tonic-gate **  PICKQDIR -- Pick a queue directory from a queue group
57307c478bd9Sstevel@tonic-gate **
57317c478bd9Sstevel@tonic-gate **	Parameters:
57327c478bd9Sstevel@tonic-gate **		qg -- queue group
57337c478bd9Sstevel@tonic-gate **		fsize -- file size in bytes
57347c478bd9Sstevel@tonic-gate **		e -- envelope, or NULL
57357c478bd9Sstevel@tonic-gate **
57367c478bd9Sstevel@tonic-gate **	Result:
57377c478bd9Sstevel@tonic-gate **		NOQDIR if no queue directory in qg has enough free space to
57387c478bd9Sstevel@tonic-gate **		hold a file of size 'fsize', otherwise the index of
57397c478bd9Sstevel@tonic-gate **		a randomly selected queue directory which resides on a
57407c478bd9Sstevel@tonic-gate **		file system with enough disk space.
57417c478bd9Sstevel@tonic-gate **		XXX This could be extended to select a queuedir with
57427c478bd9Sstevel@tonic-gate **			a few (the fewest?) number of entries. That data
57437c478bd9Sstevel@tonic-gate **			is available if shared memory is used.
57447c478bd9Sstevel@tonic-gate **
57457c478bd9Sstevel@tonic-gate **	Side Effects:
57467c478bd9Sstevel@tonic-gate **		If the request fails and e != NULL then sm_syslog is called.
57477c478bd9Sstevel@tonic-gate */
57487c478bd9Sstevel@tonic-gate 
57497c478bd9Sstevel@tonic-gate int
pickqdir(qg,fsize,e)57507c478bd9Sstevel@tonic-gate pickqdir(qg, fsize, e)
57517c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
57527c478bd9Sstevel@tonic-gate 	long fsize;
57537c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
57547c478bd9Sstevel@tonic-gate {
57557c478bd9Sstevel@tonic-gate 	int qdir;
57567c478bd9Sstevel@tonic-gate 	int i;
57577c478bd9Sstevel@tonic-gate 	long avail = 0;
57587c478bd9Sstevel@tonic-gate 
57597c478bd9Sstevel@tonic-gate 	/* Pick a random directory, as a starting point. */
57607c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues <= 1)
57617c478bd9Sstevel@tonic-gate 		qdir = 0;
57627c478bd9Sstevel@tonic-gate 	else
57637c478bd9Sstevel@tonic-gate 		qdir = get_rand_mod(qg->qg_numqueues);
57647c478bd9Sstevel@tonic-gate 
5765e9af4bc0SJohn Beck #if _FFR_TESTS
5766e9af4bc0SJohn Beck 	if (tTd(4, 101))
5767e9af4bc0SJohn Beck 		return NOQDIR;
5768e9af4bc0SJohn Beck #endif /* _FFR_TESTS */
57697c478bd9Sstevel@tonic-gate 	if (MinBlocksFree <= 0 && fsize <= 0)
57707c478bd9Sstevel@tonic-gate 		return qdir;
57717c478bd9Sstevel@tonic-gate 
57727c478bd9Sstevel@tonic-gate 	/*
57737c478bd9Sstevel@tonic-gate 	**  Now iterate over the queue directories,
57747c478bd9Sstevel@tonic-gate 	**  looking for a directory with enough space for this message.
57757c478bd9Sstevel@tonic-gate 	*/
57767c478bd9Sstevel@tonic-gate 
57777c478bd9Sstevel@tonic-gate 	i = qdir;
57787c478bd9Sstevel@tonic-gate 	do
57797c478bd9Sstevel@tonic-gate 	{
57807c478bd9Sstevel@tonic-gate 		QPATHS *qp = &qg->qg_qpaths[i];
57817c478bd9Sstevel@tonic-gate 		long needed = 0;
57827c478bd9Sstevel@tonic-gate 		long fsavail = 0;
57837c478bd9Sstevel@tonic-gate 
57847c478bd9Sstevel@tonic-gate 		if (fsize > 0)
57857c478bd9Sstevel@tonic-gate 			needed += fsize / FILE_SYS_BLKSIZE(qp->qp_fsysidx)
57867c478bd9Sstevel@tonic-gate 				  + ((fsize % FILE_SYS_BLKSIZE(qp->qp_fsysidx)
57877c478bd9Sstevel@tonic-gate 				      > 0) ? 1 : 0);
57887c478bd9Sstevel@tonic-gate 		if (MinBlocksFree > 0)
57897c478bd9Sstevel@tonic-gate 			needed += MinBlocksFree;
57907c478bd9Sstevel@tonic-gate 		fsavail = FILE_SYS_AVAIL(qp->qp_fsysidx);
57917c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
57927c478bd9Sstevel@tonic-gate 		if (fsavail <= 0)
57937c478bd9Sstevel@tonic-gate 		{
57947c478bd9Sstevel@tonic-gate 			long blksize;
57957c478bd9Sstevel@tonic-gate 
57967c478bd9Sstevel@tonic-gate 			/*
57977c478bd9Sstevel@tonic-gate 			**  might be not correctly updated,
57987c478bd9Sstevel@tonic-gate 			**  let's try to get the info directly.
57997c478bd9Sstevel@tonic-gate 			*/
58007c478bd9Sstevel@tonic-gate 
58017c478bd9Sstevel@tonic-gate 			fsavail = freediskspace(FILE_SYS_NAME(qp->qp_fsysidx),
58027c478bd9Sstevel@tonic-gate 						&blksize);
58037c478bd9Sstevel@tonic-gate 			if (fsavail < 0)
58047c478bd9Sstevel@tonic-gate 				fsavail = 0;
58057c478bd9Sstevel@tonic-gate 		}
58067c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
58077c478bd9Sstevel@tonic-gate 		if (needed <= fsavail)
58087c478bd9Sstevel@tonic-gate 			return i;
58097c478bd9Sstevel@tonic-gate 		if (avail < fsavail)
58107c478bd9Sstevel@tonic-gate 			avail = fsavail;
58117c478bd9Sstevel@tonic-gate 
58127c478bd9Sstevel@tonic-gate 		if (qg->qg_numqueues > 0)
58137c478bd9Sstevel@tonic-gate 			i = (i + 1) % qg->qg_numqueues;
58147c478bd9Sstevel@tonic-gate 	} while (i != qdir);
58157c478bd9Sstevel@tonic-gate 
58167c478bd9Sstevel@tonic-gate 	if (e != NULL && LogLevel > 0)
58177c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ALERT, e->e_id,
58187c478bd9Sstevel@tonic-gate 			"low on space (%s needs %ld bytes + %ld blocks in %s), max avail: %ld",
58197c478bd9Sstevel@tonic-gate 			CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
58207c478bd9Sstevel@tonic-gate 			fsize, MinBlocksFree,
58217c478bd9Sstevel@tonic-gate 			qg->qg_qdir, avail);
58227c478bd9Sstevel@tonic-gate 	return NOQDIR;
58237c478bd9Sstevel@tonic-gate }
58247c478bd9Sstevel@tonic-gate /*
58257c478bd9Sstevel@tonic-gate **  SETNEWQUEUE -- Sets a new queue group and directory
58267c478bd9Sstevel@tonic-gate **
58277c478bd9Sstevel@tonic-gate **	Assign a queue group and directory to an envelope and store the
58287c478bd9Sstevel@tonic-gate **	directory in e->e_qdir.
58297c478bd9Sstevel@tonic-gate **
58307c478bd9Sstevel@tonic-gate **	Parameters:
58317c478bd9Sstevel@tonic-gate **		e -- envelope to assign a queue for.
58327c478bd9Sstevel@tonic-gate **
58337c478bd9Sstevel@tonic-gate **	Returns:
58347c478bd9Sstevel@tonic-gate **		true if successful
58357c478bd9Sstevel@tonic-gate **		false otherwise
58367c478bd9Sstevel@tonic-gate **
58377c478bd9Sstevel@tonic-gate **	Side Effects:
58387c478bd9Sstevel@tonic-gate **		On success, e->e_qgrp and e->e_qdir are non-negative.
58397c478bd9Sstevel@tonic-gate **		On failure (not enough disk space),
58407c478bd9Sstevel@tonic-gate **		e->qgrp = NOQGRP, e->e_qdir = NOQDIR
58417c478bd9Sstevel@tonic-gate **		and usrerr() is invoked (which could raise an exception).
58427c478bd9Sstevel@tonic-gate */
58437c478bd9Sstevel@tonic-gate 
58447c478bd9Sstevel@tonic-gate bool
setnewqueue(e)58457c478bd9Sstevel@tonic-gate setnewqueue(e)
58467c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
58477c478bd9Sstevel@tonic-gate {
58487c478bd9Sstevel@tonic-gate 	if (tTd(41, 20))
58497c478bd9Sstevel@tonic-gate 		sm_dprintf("setnewqueue: called\n");
58507c478bd9Sstevel@tonic-gate 
58517c478bd9Sstevel@tonic-gate 	/* not set somewhere else */
58527c478bd9Sstevel@tonic-gate 	if (e->e_qgrp == NOQGRP)
58537c478bd9Sstevel@tonic-gate 	{
58547c478bd9Sstevel@tonic-gate 		ADDRESS *q;
58557c478bd9Sstevel@tonic-gate 
58567c478bd9Sstevel@tonic-gate 		/*
58577c478bd9Sstevel@tonic-gate 		**  Use the queue group of the "first" recipient, as set by
58587c478bd9Sstevel@tonic-gate 		**  the "queuegroup" rule set.  If that is not defined, then
58597c478bd9Sstevel@tonic-gate 		**  use the queue group of the mailer of the first recipient.
58607c478bd9Sstevel@tonic-gate 		**  If that is not defined either, then use the default
58617c478bd9Sstevel@tonic-gate 		**  queue group.
58627c478bd9Sstevel@tonic-gate 		**  Notice: "first" depends on the sorting of sendqueue
58637c478bd9Sstevel@tonic-gate 		**  in recipient().
58647c478bd9Sstevel@tonic-gate 		**  To avoid problems with "bad" recipients look
58657c478bd9Sstevel@tonic-gate 		**  for a valid address first.
58667c478bd9Sstevel@tonic-gate 		*/
58677c478bd9Sstevel@tonic-gate 
58687c478bd9Sstevel@tonic-gate 		q = e->e_sendqueue;
58697c478bd9Sstevel@tonic-gate 		while (q != NULL &&
58707c478bd9Sstevel@tonic-gate 		       (QS_IS_BADADDR(q->q_state) || QS_IS_DEAD(q->q_state)))
58717c478bd9Sstevel@tonic-gate 		{
58727c478bd9Sstevel@tonic-gate 			q = q->q_next;
58737c478bd9Sstevel@tonic-gate 		}
58747c478bd9Sstevel@tonic-gate 		if (q == NULL)
58757c478bd9Sstevel@tonic-gate 			e->e_qgrp = 0;
58767c478bd9Sstevel@tonic-gate 		else if (q->q_qgrp >= 0)
58777c478bd9Sstevel@tonic-gate 			e->e_qgrp = q->q_qgrp;
58787c478bd9Sstevel@tonic-gate 		else if (q->q_mailer != NULL &&
58797c478bd9Sstevel@tonic-gate 			 ISVALIDQGRP(q->q_mailer->m_qgrp))
58807c478bd9Sstevel@tonic-gate 			e->e_qgrp = q->q_mailer->m_qgrp;
58817c478bd9Sstevel@tonic-gate 		else
58827c478bd9Sstevel@tonic-gate 			e->e_qgrp = 0;
58837c478bd9Sstevel@tonic-gate 		e->e_dfqgrp = e->e_qgrp;
58847c478bd9Sstevel@tonic-gate 	}
58857c478bd9Sstevel@tonic-gate 
58867c478bd9Sstevel@tonic-gate 	if (ISVALIDQDIR(e->e_qdir) && ISVALIDQDIR(e->e_dfqdir))
58877c478bd9Sstevel@tonic-gate 	{
58887c478bd9Sstevel@tonic-gate 		if (tTd(41, 20))
58897c478bd9Sstevel@tonic-gate 			sm_dprintf("setnewqueue: e_qdir already assigned (%s)\n",
58907c478bd9Sstevel@tonic-gate 				qid_printqueue(e->e_qgrp, e->e_qdir));
58917c478bd9Sstevel@tonic-gate 		return true;
58927c478bd9Sstevel@tonic-gate 	}
58937c478bd9Sstevel@tonic-gate 
58947c478bd9Sstevel@tonic-gate 	filesys_update();
58957c478bd9Sstevel@tonic-gate 	e->e_qdir = pickqdir(Queue[e->e_qgrp], e->e_msgsize, e);
58967c478bd9Sstevel@tonic-gate 	if (e->e_qdir == NOQDIR)
58977c478bd9Sstevel@tonic-gate 	{
58987c478bd9Sstevel@tonic-gate 		e->e_qgrp = NOQGRP;
58997c478bd9Sstevel@tonic-gate 		if (!bitset(EF_FATALERRS, e->e_flags))
59007c478bd9Sstevel@tonic-gate 			usrerr("452 4.4.5 Insufficient disk space; try again later");
59017c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_FATALERRS;
59027c478bd9Sstevel@tonic-gate 		return false;
59037c478bd9Sstevel@tonic-gate 	}
59047c478bd9Sstevel@tonic-gate 
59057c478bd9Sstevel@tonic-gate 	if (tTd(41, 3))
59067c478bd9Sstevel@tonic-gate 		sm_dprintf("setnewqueue: Assigned queue directory %s\n",
59077c478bd9Sstevel@tonic-gate 			qid_printqueue(e->e_qgrp, e->e_qdir));
59087c478bd9Sstevel@tonic-gate 
59097c478bd9Sstevel@tonic-gate 	if (e->e_xfqgrp == NOQGRP || e->e_xfqdir == NOQDIR)
59107c478bd9Sstevel@tonic-gate 	{
59117c478bd9Sstevel@tonic-gate 		e->e_xfqgrp = e->e_qgrp;
59127c478bd9Sstevel@tonic-gate 		e->e_xfqdir = e->e_qdir;
59137c478bd9Sstevel@tonic-gate 	}
59147c478bd9Sstevel@tonic-gate 	e->e_dfqdir = e->e_qdir;
59157c478bd9Sstevel@tonic-gate 	return true;
59167c478bd9Sstevel@tonic-gate }
59177c478bd9Sstevel@tonic-gate /*
59187c478bd9Sstevel@tonic-gate **  CHKQDIR -- check a queue directory
59197c478bd9Sstevel@tonic-gate **
59207c478bd9Sstevel@tonic-gate **	Parameters:
59217c478bd9Sstevel@tonic-gate **		name -- name of queue directory
59227c478bd9Sstevel@tonic-gate **		sff -- flags for safefile()
59237c478bd9Sstevel@tonic-gate **
59247c478bd9Sstevel@tonic-gate **	Returns:
59257c478bd9Sstevel@tonic-gate **		is it a queue directory?
59267c478bd9Sstevel@tonic-gate */
59277c478bd9Sstevel@tonic-gate 
5928058561cbSjbeck static bool chkqdir __P((char *, long));
5929058561cbSjbeck 
59307c478bd9Sstevel@tonic-gate static bool
chkqdir(name,sff)59317c478bd9Sstevel@tonic-gate chkqdir(name, sff)
59327c478bd9Sstevel@tonic-gate 	char *name;
59337c478bd9Sstevel@tonic-gate 	long sff;
59347c478bd9Sstevel@tonic-gate {
59357c478bd9Sstevel@tonic-gate 	struct stat statb;
59367c478bd9Sstevel@tonic-gate 	int i;
59377c478bd9Sstevel@tonic-gate 
59387c478bd9Sstevel@tonic-gate 	/* skip over . and .. directories */
59397c478bd9Sstevel@tonic-gate 	if (name[0] == '.' &&
59407c478bd9Sstevel@tonic-gate 	    (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
59417c478bd9Sstevel@tonic-gate 		return false;
59427c478bd9Sstevel@tonic-gate #if HASLSTAT
59437c478bd9Sstevel@tonic-gate 	if (lstat(name, &statb) < 0)
59447c478bd9Sstevel@tonic-gate #else /* HASLSTAT */
59457c478bd9Sstevel@tonic-gate 	if (stat(name, &statb) < 0)
59467c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
59477c478bd9Sstevel@tonic-gate 	{
59487c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59497c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
59507c478bd9Sstevel@tonic-gate 				   name, sm_errstring(errno));
59517c478bd9Sstevel@tonic-gate 		return false;
59527c478bd9Sstevel@tonic-gate 	}
59537c478bd9Sstevel@tonic-gate #if HASLSTAT
59547c478bd9Sstevel@tonic-gate 	if (S_ISLNK(statb.st_mode))
59557c478bd9Sstevel@tonic-gate 	{
59567c478bd9Sstevel@tonic-gate 		/*
59577c478bd9Sstevel@tonic-gate 		**  For a symlink we need to make sure the
59587c478bd9Sstevel@tonic-gate 		**  target is a directory
59597c478bd9Sstevel@tonic-gate 		*/
59607c478bd9Sstevel@tonic-gate 
59617c478bd9Sstevel@tonic-gate 		if (stat(name, &statb) < 0)
59627c478bd9Sstevel@tonic-gate 		{
59637c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
59647c478bd9Sstevel@tonic-gate 				sm_dprintf("chkqdir: stat(\"%s\"): %s\n",
59657c478bd9Sstevel@tonic-gate 					   name, sm_errstring(errno));
59667c478bd9Sstevel@tonic-gate 			return false;
59677c478bd9Sstevel@tonic-gate 		}
59687c478bd9Sstevel@tonic-gate 	}
59697c478bd9Sstevel@tonic-gate #endif /* HASLSTAT */
59707c478bd9Sstevel@tonic-gate 
59717c478bd9Sstevel@tonic-gate 	if (!S_ISDIR(statb.st_mode))
59727c478bd9Sstevel@tonic-gate 	{
59737c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59747c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: \"%s\": Not a directory\n",
59757c478bd9Sstevel@tonic-gate 				name);
59767c478bd9Sstevel@tonic-gate 		return false;
59777c478bd9Sstevel@tonic-gate 	}
59787c478bd9Sstevel@tonic-gate 
59797c478bd9Sstevel@tonic-gate 	/* Print a warning if unsafe (but still use it) */
59807c478bd9Sstevel@tonic-gate 	/* XXX do this only if we want the warning? */
59817c478bd9Sstevel@tonic-gate 	i = safedirpath(name, RunAsUid, RunAsGid, NULL, sff, 0, 0);
59827c478bd9Sstevel@tonic-gate 	if (i != 0)
59837c478bd9Sstevel@tonic-gate 	{
59847c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
59857c478bd9Sstevel@tonic-gate 			sm_dprintf("chkqdir: \"%s\": Not safe: %s\n",
59867c478bd9Sstevel@tonic-gate 				   name, sm_errstring(i));
59877c478bd9Sstevel@tonic-gate #if _FFR_CHK_QUEUE
59887c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
59897c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
59907c478bd9Sstevel@tonic-gate 				  "queue directory \"%s\": Not safe: %s",
59917c478bd9Sstevel@tonic-gate 				  name, sm_errstring(i));
59927c478bd9Sstevel@tonic-gate #endif /* _FFR_CHK_QUEUE */
59937c478bd9Sstevel@tonic-gate 	}
59947c478bd9Sstevel@tonic-gate 	return true;
59957c478bd9Sstevel@tonic-gate }
59967c478bd9Sstevel@tonic-gate /*
59977c478bd9Sstevel@tonic-gate **  MULTIQUEUE_CACHE -- cache a list of paths to queues.
59987c478bd9Sstevel@tonic-gate **
59997c478bd9Sstevel@tonic-gate **	Each potential queue is checked as the cache is built.
60007c478bd9Sstevel@tonic-gate **	Thereafter, each is blindly trusted.
60017c478bd9Sstevel@tonic-gate **	Note that we can be called again after a timeout to rebuild
60027c478bd9Sstevel@tonic-gate **	(although code for that is not ready yet).
60037c478bd9Sstevel@tonic-gate **
60047c478bd9Sstevel@tonic-gate **	Parameters:
60057c478bd9Sstevel@tonic-gate **		basedir -- base of all queue directories.
60067c478bd9Sstevel@tonic-gate **		blen -- strlen(basedir).
60077c478bd9Sstevel@tonic-gate **		qg -- queue group.
60087c478bd9Sstevel@tonic-gate **		qn -- number of queue directories already cached.
60097c478bd9Sstevel@tonic-gate **		phash -- pointer to hash value over queue dirs.
60107c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
60117c478bd9Sstevel@tonic-gate **			only used if shared memory is active.
60127c478bd9Sstevel@tonic-gate #endif * SM_CONF_SHM *
60137c478bd9Sstevel@tonic-gate **
60147c478bd9Sstevel@tonic-gate **	Returns:
60157c478bd9Sstevel@tonic-gate **		new number of queue directories.
60167c478bd9Sstevel@tonic-gate */
60177c478bd9Sstevel@tonic-gate 
60187c478bd9Sstevel@tonic-gate #define INITIAL_SLOTS	20
60197c478bd9Sstevel@tonic-gate #define ADD_SLOTS	10
60207c478bd9Sstevel@tonic-gate 
60217c478bd9Sstevel@tonic-gate static int
multiqueue_cache(basedir,blen,qg,qn,phash)60227c478bd9Sstevel@tonic-gate multiqueue_cache(basedir, blen, qg, qn, phash)
60237c478bd9Sstevel@tonic-gate 	char *basedir;
60247c478bd9Sstevel@tonic-gate 	int blen;
60257c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
60267c478bd9Sstevel@tonic-gate 	int qn;
60277c478bd9Sstevel@tonic-gate 	unsigned int *phash;
60287c478bd9Sstevel@tonic-gate {
60297c478bd9Sstevel@tonic-gate 	char *cp;
60307c478bd9Sstevel@tonic-gate 	int i, len;
60317c478bd9Sstevel@tonic-gate 	int slotsleft = 0;
60327c478bd9Sstevel@tonic-gate 	long sff = SFF_ANYFILE;
60337c478bd9Sstevel@tonic-gate 	char qpath[MAXPATHLEN];
60347c478bd9Sstevel@tonic-gate 	char subdir[MAXPATHLEN];
60357c478bd9Sstevel@tonic-gate 	char prefix[MAXPATHLEN];	/* dir relative to basedir */
60367c478bd9Sstevel@tonic-gate 
60377c478bd9Sstevel@tonic-gate 	if (tTd(41, 20))
60387c478bd9Sstevel@tonic-gate 		sm_dprintf("multiqueue_cache: called\n");
60397c478bd9Sstevel@tonic-gate 
60407c478bd9Sstevel@tonic-gate 	/* Initialize to current directory */
60417c478bd9Sstevel@tonic-gate 	prefix[0] = '.';
60427c478bd9Sstevel@tonic-gate 	prefix[1] = '\0';
60437c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues != 0 && qg->qg_qpaths != NULL)
60447c478bd9Sstevel@tonic-gate 	{
60457c478bd9Sstevel@tonic-gate 		for (i = 0; i < qg->qg_numqueues; i++)
60467c478bd9Sstevel@tonic-gate 		{
60477c478bd9Sstevel@tonic-gate 			if (qg->qg_qpaths[i].qp_name != NULL)
60487c478bd9Sstevel@tonic-gate 				(void) sm_free(qg->qg_qpaths[i].qp_name); /* XXX */
60497c478bd9Sstevel@tonic-gate 		}
60507c478bd9Sstevel@tonic-gate 		(void) sm_free((char *) qg->qg_qpaths); /* XXX */
60517c478bd9Sstevel@tonic-gate 		qg->qg_qpaths = NULL;
60527c478bd9Sstevel@tonic-gate 		qg->qg_numqueues = 0;
60537c478bd9Sstevel@tonic-gate 	}
60547c478bd9Sstevel@tonic-gate 
60557c478bd9Sstevel@tonic-gate 	/* If running as root, allow safedirpath() checks to use privs */
60567c478bd9Sstevel@tonic-gate 	if (RunAsUid == 0)
60577c478bd9Sstevel@tonic-gate 		sff |= SFF_ROOTOK;
60587c478bd9Sstevel@tonic-gate #if _FFR_CHK_QUEUE
60597c478bd9Sstevel@tonic-gate 	sff |= SFF_SAFEDIRPATH|SFF_NOWWFILES;
60607c478bd9Sstevel@tonic-gate 	if (!UseMSP)
60617c478bd9Sstevel@tonic-gate 		sff |= SFF_NOGWFILES;
60627c478bd9Sstevel@tonic-gate #endif /* _FFR_CHK_QUEUE */
60637c478bd9Sstevel@tonic-gate 
60647c478bd9Sstevel@tonic-gate 	if (!SM_IS_DIR_START(qg->qg_qdir))
60657c478bd9Sstevel@tonic-gate 	{
60667c478bd9Sstevel@tonic-gate 		/*
60677c478bd9Sstevel@tonic-gate 		**  XXX we could add basedir, but then we have to realloc()
60687c478bd9Sstevel@tonic-gate 		**  the string... Maybe another time.
60697c478bd9Sstevel@tonic-gate 		*/
60707c478bd9Sstevel@tonic-gate 
60717c478bd9Sstevel@tonic-gate 		syserr("QueuePath %s not absolute", qg->qg_qdir);
60727c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60737c478bd9Sstevel@tonic-gate 		return qn;
60747c478bd9Sstevel@tonic-gate 	}
60757c478bd9Sstevel@tonic-gate 
60767c478bd9Sstevel@tonic-gate 	/* qpath: directory of current workgroup */
6077058561cbSjbeck 	len = sm_strlcpy(qpath, qg->qg_qdir, sizeof(qpath));
6078058561cbSjbeck 	if (len >= sizeof(qpath))
60797c478bd9Sstevel@tonic-gate 	{
60807c478bd9Sstevel@tonic-gate 		syserr("QueuePath %.256s too long (%d max)",
6081058561cbSjbeck 		       qg->qg_qdir, (int) sizeof(qpath));
60827c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60837c478bd9Sstevel@tonic-gate 		return qn;
60847c478bd9Sstevel@tonic-gate 	}
60857c478bd9Sstevel@tonic-gate 
60867c478bd9Sstevel@tonic-gate 	/* begin of qpath must be same as basedir */
60877c478bd9Sstevel@tonic-gate 	if (strncmp(basedir, qpath, blen) != 0 &&
60887c478bd9Sstevel@tonic-gate 	    (strncmp(basedir, qpath, blen - 1) != 0 || len != blen - 1))
60897c478bd9Sstevel@tonic-gate 	{
60907c478bd9Sstevel@tonic-gate 		syserr("QueuePath %s not subpath of QueueDirectory %s",
60917c478bd9Sstevel@tonic-gate 			qpath, basedir);
60927c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
60937c478bd9Sstevel@tonic-gate 		return qn;
60947c478bd9Sstevel@tonic-gate 	}
60957c478bd9Sstevel@tonic-gate 
60967c478bd9Sstevel@tonic-gate 	/* Do we have a nested subdirectory? */
60977c478bd9Sstevel@tonic-gate 	if (blen < len && SM_FIRST_DIR_DELIM(qg->qg_qdir + blen) != NULL)
60987c478bd9Sstevel@tonic-gate 	{
60997c478bd9Sstevel@tonic-gate 
61007c478bd9Sstevel@tonic-gate 		/* Copy subdirectory into prefix for later use */
6101058561cbSjbeck 		if (sm_strlcpy(prefix, qg->qg_qdir + blen, sizeof(prefix)) >=
6102058561cbSjbeck 		    sizeof(prefix))
61037c478bd9Sstevel@tonic-gate 		{
61047c478bd9Sstevel@tonic-gate 			syserr("QueuePath %.256s too long (%d max)",
6105058561cbSjbeck 				qg->qg_qdir, (int) sizeof(qpath));
61067c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
61077c478bd9Sstevel@tonic-gate 			return qn;
61087c478bd9Sstevel@tonic-gate 		}
61097c478bd9Sstevel@tonic-gate 		cp = SM_LAST_DIR_DELIM(prefix);
61107c478bd9Sstevel@tonic-gate 		SM_ASSERT(cp != NULL);
61117c478bd9Sstevel@tonic-gate 		*cp = '\0';	/* cut off trailing / */
61127c478bd9Sstevel@tonic-gate 	}
61137c478bd9Sstevel@tonic-gate 
61147c478bd9Sstevel@tonic-gate 	/* This is guaranteed by the basedir check above */
61157c478bd9Sstevel@tonic-gate 	SM_ASSERT(len >= blen - 1);
61167c478bd9Sstevel@tonic-gate 	cp = &qpath[len - 1];
61177c478bd9Sstevel@tonic-gate 	if (*cp == '*')
61187c478bd9Sstevel@tonic-gate 	{
61197c478bd9Sstevel@tonic-gate 		register DIR *dp;
61207c478bd9Sstevel@tonic-gate 		register struct dirent *d;
61217c478bd9Sstevel@tonic-gate 		int off;
61227c478bd9Sstevel@tonic-gate 		char *delim;
61237c478bd9Sstevel@tonic-gate 		char relpath[MAXPATHLEN];
61247c478bd9Sstevel@tonic-gate 
61257c478bd9Sstevel@tonic-gate 		*cp = '\0';	/* Overwrite wildcard */
61267c478bd9Sstevel@tonic-gate 		if ((cp = SM_LAST_DIR_DELIM(qpath)) == NULL)
61277c478bd9Sstevel@tonic-gate 		{
61287c478bd9Sstevel@tonic-gate 			syserr("QueueDirectory: can not wildcard relative path");
61297c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
61307c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: \"%s*\": Can not wildcard relative path.\n",
61317c478bd9Sstevel@tonic-gate 					qpath);
61327c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
61337c478bd9Sstevel@tonic-gate 			return qn;
61347c478bd9Sstevel@tonic-gate 		}
61357c478bd9Sstevel@tonic-gate 		if (cp == qpath)
61367c478bd9Sstevel@tonic-gate 		{
61377c478bd9Sstevel@tonic-gate 			/*
61387c478bd9Sstevel@tonic-gate 			**  Special case of top level wildcard, like /foo*
61397c478bd9Sstevel@tonic-gate 			**	Change to //foo*
61407c478bd9Sstevel@tonic-gate 			*/
61417c478bd9Sstevel@tonic-gate 
6142058561cbSjbeck 			(void) sm_strlcpy(qpath + 1, qpath, sizeof(qpath) - 1);
61437c478bd9Sstevel@tonic-gate 			++cp;
61447c478bd9Sstevel@tonic-gate 		}
61457c478bd9Sstevel@tonic-gate 		delim = cp;
61467c478bd9Sstevel@tonic-gate 		*(cp++) = '\0';		/* Replace / with \0 */
61477c478bd9Sstevel@tonic-gate 		len = strlen(cp);	/* Last component of queue directory */
61487c478bd9Sstevel@tonic-gate 
61497c478bd9Sstevel@tonic-gate 		/*
61507c478bd9Sstevel@tonic-gate 		**  Path relative to basedir, with trailing /
61517c478bd9Sstevel@tonic-gate 		**  It will be modified below to specify the subdirectories
61527c478bd9Sstevel@tonic-gate 		**  so they can be opened without chdir().
61537c478bd9Sstevel@tonic-gate 		*/
61547c478bd9Sstevel@tonic-gate 
6155058561cbSjbeck 		off = sm_strlcpyn(relpath, sizeof(relpath), 2, prefix, "/");
6156058561cbSjbeck 		SM_ASSERT(off < sizeof(relpath));
61577c478bd9Sstevel@tonic-gate 
61587c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
61597c478bd9Sstevel@tonic-gate 			sm_dprintf("multiqueue_cache: prefix=\"%s%s\"\n",
61607c478bd9Sstevel@tonic-gate 				   relpath, cp);
61617c478bd9Sstevel@tonic-gate 
61627c478bd9Sstevel@tonic-gate 		/* It is always basedir: we don't need to store it per group */
61637c478bd9Sstevel@tonic-gate 		/* XXX: optimize this! -> one more global? */
61647c478bd9Sstevel@tonic-gate 		qg->qg_qdir = newstr(basedir);
61657c478bd9Sstevel@tonic-gate 		qg->qg_qdir[blen - 1] = '\0';	/* cut off trailing / */
61667c478bd9Sstevel@tonic-gate 
61677c478bd9Sstevel@tonic-gate 		/*
61687c478bd9Sstevel@tonic-gate 		**  XXX Should probably wrap this whole loop in a timeout
61697c478bd9Sstevel@tonic-gate 		**  in case some wag decides to NFS mount the queues.
61707c478bd9Sstevel@tonic-gate 		*/
61717c478bd9Sstevel@tonic-gate 
61727c478bd9Sstevel@tonic-gate 		/* Test path to get warning messages. */
61737c478bd9Sstevel@tonic-gate 		if (qn == 0)
61747c478bd9Sstevel@tonic-gate 		{
61757c478bd9Sstevel@tonic-gate 			/*  XXX qg_runasuid and qg_runasgid for specials? */
61767c478bd9Sstevel@tonic-gate 			i = safedirpath(basedir, RunAsUid, RunAsGid, NULL,
61777c478bd9Sstevel@tonic-gate 					sff, 0, 0);
61787c478bd9Sstevel@tonic-gate 			if (i != 0 && tTd(41, 2))
61797c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: \"%s\": Not safe: %s\n",
61807c478bd9Sstevel@tonic-gate 					   basedir, sm_errstring(i));
61817c478bd9Sstevel@tonic-gate 		}
61827c478bd9Sstevel@tonic-gate 
61837c478bd9Sstevel@tonic-gate 		if ((dp = opendir(prefix)) == NULL)
61847c478bd9Sstevel@tonic-gate 		{
61857c478bd9Sstevel@tonic-gate 			syserr("can not opendir(%s/%s)", qg->qg_qdir, prefix);
61867c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
61877c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: opendir(\"%s/%s\"): %s\n",
61887c478bd9Sstevel@tonic-gate 					   qg->qg_qdir, prefix,
61897c478bd9Sstevel@tonic-gate 					   sm_errstring(errno));
61907c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
61917c478bd9Sstevel@tonic-gate 			return qn;
61927c478bd9Sstevel@tonic-gate 		}
61937c478bd9Sstevel@tonic-gate 		while ((d = readdir(dp)) != NULL)
61947c478bd9Sstevel@tonic-gate 		{
6195058561cbSjbeck 			/* Skip . and .. directories */
6196058561cbSjbeck 			if (strcmp(d->d_name, ".") == 0 ||
6197058561cbSjbeck 			    strcmp(d->d_name, "..") == 0)
6198058561cbSjbeck 				continue;
6199058561cbSjbeck 
62007c478bd9Sstevel@tonic-gate 			i = strlen(d->d_name);
62017c478bd9Sstevel@tonic-gate 			if (i < len || strncmp(d->d_name, cp, len) != 0)
62027c478bd9Sstevel@tonic-gate 			{
62037c478bd9Sstevel@tonic-gate 				if (tTd(41, 5))
62047c478bd9Sstevel@tonic-gate 					sm_dprintf("multiqueue_cache: \"%s\", skipped\n",
62057c478bd9Sstevel@tonic-gate 						d->d_name);
62067c478bd9Sstevel@tonic-gate 				continue;
62077c478bd9Sstevel@tonic-gate 			}
62087c478bd9Sstevel@tonic-gate 
62097c478bd9Sstevel@tonic-gate 			/* Create relative pathname: prefix + local directory */
62107c478bd9Sstevel@tonic-gate 			i = sizeof(relpath) - off;
62117c478bd9Sstevel@tonic-gate 			if (sm_strlcpy(relpath + off, d->d_name, i) >= i)
62127c478bd9Sstevel@tonic-gate 				continue;	/* way too long */
62137c478bd9Sstevel@tonic-gate 
62147c478bd9Sstevel@tonic-gate 			if (!chkqdir(relpath, sff))
62157c478bd9Sstevel@tonic-gate 				continue;
62167c478bd9Sstevel@tonic-gate 
62177c478bd9Sstevel@tonic-gate 			if (qg->qg_qpaths == NULL)
62187c478bd9Sstevel@tonic-gate 			{
62197c478bd9Sstevel@tonic-gate 				slotsleft = INITIAL_SLOTS;
6220058561cbSjbeck 				qg->qg_qpaths = (QPATHS *)xalloc((sizeof(*qg->qg_qpaths)) *
62217c478bd9Sstevel@tonic-gate 								slotsleft);
62227c478bd9Sstevel@tonic-gate 				qg->qg_numqueues = 0;
62237c478bd9Sstevel@tonic-gate 			}
62247c478bd9Sstevel@tonic-gate 			else if (slotsleft < 1)
62257c478bd9Sstevel@tonic-gate 			{
62267c478bd9Sstevel@tonic-gate 				qg->qg_qpaths = (QPATHS *)sm_realloc((char *)qg->qg_qpaths,
6227058561cbSjbeck 							  (sizeof(*qg->qg_qpaths)) *
62287c478bd9Sstevel@tonic-gate 							  (qg->qg_numqueues +
62297c478bd9Sstevel@tonic-gate 							   ADD_SLOTS));
62307c478bd9Sstevel@tonic-gate 				if (qg->qg_qpaths == NULL)
62317c478bd9Sstevel@tonic-gate 				{
62327c478bd9Sstevel@tonic-gate 					(void) closedir(dp);
62337c478bd9Sstevel@tonic-gate 					return qn;
62347c478bd9Sstevel@tonic-gate 				}
62357c478bd9Sstevel@tonic-gate 				slotsleft += ADD_SLOTS;
62367c478bd9Sstevel@tonic-gate 			}
62377c478bd9Sstevel@tonic-gate 
62387c478bd9Sstevel@tonic-gate 			/* check subdirs */
62397c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[qg->qg_numqueues].qp_subdirs = QP_NOSUB;
62407c478bd9Sstevel@tonic-gate 
62417c478bd9Sstevel@tonic-gate #define CHKRSUBDIR(name, flag)	\
6242058561cbSjbeck 	(void) sm_strlcpyn(subdir, sizeof(subdir), 3, relpath, "/", name); \
62437c478bd9Sstevel@tonic-gate 	if (chkqdir(subdir, sff))	\
62447c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[qg->qg_numqueues].qp_subdirs |= flag;	\
62457c478bd9Sstevel@tonic-gate 	else
62467c478bd9Sstevel@tonic-gate 
62477c478bd9Sstevel@tonic-gate 
62487c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("qf", QP_SUBQF);
62497c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("df", QP_SUBDF);
62507c478bd9Sstevel@tonic-gate 			CHKRSUBDIR("xf", QP_SUBXF);
62517c478bd9Sstevel@tonic-gate 
62527c478bd9Sstevel@tonic-gate 			/* assert(strlen(d->d_name) < MAXPATHLEN - 14) */
62537c478bd9Sstevel@tonic-gate 			/* maybe even - 17 (subdirs) */
62547c478bd9Sstevel@tonic-gate 
62557c478bd9Sstevel@tonic-gate 			if (prefix[0] != '.')
62567c478bd9Sstevel@tonic-gate 				qg->qg_qpaths[qg->qg_numqueues].qp_name =
62577c478bd9Sstevel@tonic-gate 					newstr(relpath);
62587c478bd9Sstevel@tonic-gate 			else
62597c478bd9Sstevel@tonic-gate 				qg->qg_qpaths[qg->qg_numqueues].qp_name =
62607c478bd9Sstevel@tonic-gate 					newstr(d->d_name);
62617c478bd9Sstevel@tonic-gate 
62627c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
62637c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: %d: \"%s\" cached (%x).\n",
62647c478bd9Sstevel@tonic-gate 					qg->qg_numqueues, relpath,
62657c478bd9Sstevel@tonic-gate 					qg->qg_qpaths[qg->qg_numqueues].qp_subdirs);
62667c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
62677c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[qg->qg_numqueues].qp_idx = qn;
62687c478bd9Sstevel@tonic-gate 			*phash = hash_q(relpath, *phash);
62697c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
62707c478bd9Sstevel@tonic-gate 			qg->qg_numqueues++;
62717c478bd9Sstevel@tonic-gate 			++qn;
62727c478bd9Sstevel@tonic-gate 			slotsleft--;
62737c478bd9Sstevel@tonic-gate 		}
62747c478bd9Sstevel@tonic-gate 		(void) closedir(dp);
62757c478bd9Sstevel@tonic-gate 
62767c478bd9Sstevel@tonic-gate 		/* undo damage */
62777c478bd9Sstevel@tonic-gate 		*delim = '/';
62787c478bd9Sstevel@tonic-gate 	}
62797c478bd9Sstevel@tonic-gate 	if (qg->qg_numqueues == 0)
62807c478bd9Sstevel@tonic-gate 	{
6281058561cbSjbeck 		qg->qg_qpaths = (QPATHS *) xalloc(sizeof(*qg->qg_qpaths));
62827c478bd9Sstevel@tonic-gate 
62837c478bd9Sstevel@tonic-gate 		/* test path to get warning messages */
62847c478bd9Sstevel@tonic-gate 		i = safedirpath(qpath, RunAsUid, RunAsGid, NULL, sff, 0, 0);
62857c478bd9Sstevel@tonic-gate 		if (i == ENOENT)
62867c478bd9Sstevel@tonic-gate 		{
62877c478bd9Sstevel@tonic-gate 			syserr("can not opendir(%s)", qpath);
62887c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
62897c478bd9Sstevel@tonic-gate 				sm_dprintf("multiqueue_cache: opendir(\"%s\"): %s\n",
62907c478bd9Sstevel@tonic-gate 					   qpath, sm_errstring(i));
62917c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
62927c478bd9Sstevel@tonic-gate 			return qn;
62937c478bd9Sstevel@tonic-gate 		}
62947c478bd9Sstevel@tonic-gate 
62957c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_subdirs = QP_NOSUB;
62967c478bd9Sstevel@tonic-gate 		qg->qg_numqueues = 1;
62977c478bd9Sstevel@tonic-gate 
62987c478bd9Sstevel@tonic-gate 		/* check subdirs */
62997c478bd9Sstevel@tonic-gate #define CHKSUBDIR(name, flag)	\
6300058561cbSjbeck 	(void) sm_strlcpyn(subdir, sizeof(subdir), 3, qg->qg_qdir, "/", name); \
63017c478bd9Sstevel@tonic-gate 	if (chkqdir(subdir, sff))	\
63027c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_subdirs |= flag;	\
63037c478bd9Sstevel@tonic-gate 	else
63047c478bd9Sstevel@tonic-gate 
63057c478bd9Sstevel@tonic-gate 		CHKSUBDIR("qf", QP_SUBQF);
63067c478bd9Sstevel@tonic-gate 		CHKSUBDIR("df", QP_SUBDF);
63077c478bd9Sstevel@tonic-gate 		CHKSUBDIR("xf", QP_SUBXF);
63087c478bd9Sstevel@tonic-gate 
63097c478bd9Sstevel@tonic-gate 		if (qg->qg_qdir[blen - 1] != '\0' &&
63107c478bd9Sstevel@tonic-gate 		    qg->qg_qdir[blen] != '\0')
63117c478bd9Sstevel@tonic-gate 		{
63127c478bd9Sstevel@tonic-gate 			/*
63137c478bd9Sstevel@tonic-gate 			**  Copy the last component into qpaths and
63147c478bd9Sstevel@tonic-gate 			**  cut off qdir
63157c478bd9Sstevel@tonic-gate 			*/
63167c478bd9Sstevel@tonic-gate 
63177c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[0].qp_name = newstr(qg->qg_qdir + blen);
63187c478bd9Sstevel@tonic-gate 			qg->qg_qdir[blen - 1] = '\0';
63197c478bd9Sstevel@tonic-gate 		}
63207c478bd9Sstevel@tonic-gate 		else
63217c478bd9Sstevel@tonic-gate 			qg->qg_qpaths[0].qp_name = newstr(".");
63227c478bd9Sstevel@tonic-gate 
63237c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
63247c478bd9Sstevel@tonic-gate 		qg->qg_qpaths[0].qp_idx = qn;
63257c478bd9Sstevel@tonic-gate 		*phash = hash_q(qg->qg_qpaths[0].qp_name, *phash);
63267c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
63277c478bd9Sstevel@tonic-gate 		++qn;
63287c478bd9Sstevel@tonic-gate 	}
63297c478bd9Sstevel@tonic-gate 	return qn;
63307c478bd9Sstevel@tonic-gate }
63317c478bd9Sstevel@tonic-gate 
63327c478bd9Sstevel@tonic-gate /*
63337c478bd9Sstevel@tonic-gate **  FILESYS_FIND -- find entry in FileSys table, or add new one
63347c478bd9Sstevel@tonic-gate **
63357c478bd9Sstevel@tonic-gate **	Given the pathname of a directory, determine the file system
63367c478bd9Sstevel@tonic-gate **	in which that directory resides, and return a pointer to the
63377c478bd9Sstevel@tonic-gate **	entry in the FileSys table that describes the file system.
63387c478bd9Sstevel@tonic-gate **	A new entry is added if necessary (and requested).
63397c478bd9Sstevel@tonic-gate **	If the directory does not exist, -1 is returned.
63407c478bd9Sstevel@tonic-gate **
63417c478bd9Sstevel@tonic-gate **	Parameters:
634249218d4fSjbeck **		name -- name of directory (must be persistent!)
634349218d4fSjbeck **		path -- pathname of directory (name plus maybe "/df")
63447c478bd9Sstevel@tonic-gate **		add -- add to structure if not found.
63457c478bd9Sstevel@tonic-gate **
63467c478bd9Sstevel@tonic-gate **	Returns:
63477c478bd9Sstevel@tonic-gate **		>=0: found: index in file system table
63487c478bd9Sstevel@tonic-gate **		<0: some error, i.e.,
63497c478bd9Sstevel@tonic-gate **		FSF_TOO_MANY: too many filesystems (-> syserr())
63507c478bd9Sstevel@tonic-gate **		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
63517c478bd9Sstevel@tonic-gate **		FSF_NOT_FOUND: not in list
63527c478bd9Sstevel@tonic-gate */
63537c478bd9Sstevel@tonic-gate 
6354058561cbSjbeck static short filesys_find __P((const char *, const char *, bool));
63557c478bd9Sstevel@tonic-gate 
63567c478bd9Sstevel@tonic-gate #define FSF_NOT_FOUND	(-1)
63577c478bd9Sstevel@tonic-gate #define FSF_STAT_FAIL	(-2)
63587c478bd9Sstevel@tonic-gate #define FSF_TOO_MANY	(-3)
63597c478bd9Sstevel@tonic-gate 
63607c478bd9Sstevel@tonic-gate static short
filesys_find(name,path,add)636149218d4fSjbeck filesys_find(name, path, add)
6362058561cbSjbeck 	const char *name;
6363058561cbSjbeck 	const char *path;
63647c478bd9Sstevel@tonic-gate 	bool add;
63657c478bd9Sstevel@tonic-gate {
63667c478bd9Sstevel@tonic-gate 	struct stat st;
63677c478bd9Sstevel@tonic-gate 	short i;
63687c478bd9Sstevel@tonic-gate 
63697c478bd9Sstevel@tonic-gate 	if (stat(path, &st) < 0)
63707c478bd9Sstevel@tonic-gate 	{
63717c478bd9Sstevel@tonic-gate 		syserr("cannot stat queue directory %s", path);
63727c478bd9Sstevel@tonic-gate 		return FSF_STAT_FAIL;
63737c478bd9Sstevel@tonic-gate 	}
63747c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
63757c478bd9Sstevel@tonic-gate 	{
63767c478bd9Sstevel@tonic-gate 		if (FILE_SYS_DEV(i) == st.st_dev)
63771daa5768Sjbeck 		{
63781daa5768Sjbeck 			/*
63791daa5768Sjbeck 			**  Make sure the file system (FS) name is set:
63801daa5768Sjbeck 			**  even though the source code indicates that
63811daa5768Sjbeck 			**  FILE_SYS_DEV() is only set below, it could be
63821daa5768Sjbeck 			**  set via shared memory, hence we need to perform
63831daa5768Sjbeck 			**  this check/assignment here.
63841daa5768Sjbeck 			*/
63851daa5768Sjbeck 
63861daa5768Sjbeck 			if (NULL == FILE_SYS_NAME(i))
63871daa5768Sjbeck 				FILE_SYS_NAME(i) = name;
63887c478bd9Sstevel@tonic-gate 			return i;
63897c478bd9Sstevel@tonic-gate 		}
63901daa5768Sjbeck 	}
63917c478bd9Sstevel@tonic-gate 	if (i >= MAXFILESYS)
63927c478bd9Sstevel@tonic-gate 	{
63937c478bd9Sstevel@tonic-gate 		syserr("too many queue file systems (%d max)", MAXFILESYS);
63947c478bd9Sstevel@tonic-gate 		return FSF_TOO_MANY;
63957c478bd9Sstevel@tonic-gate 	}
63967c478bd9Sstevel@tonic-gate 	if (!add)
63977c478bd9Sstevel@tonic-gate 		return FSF_NOT_FOUND;
63987c478bd9Sstevel@tonic-gate 
63997c478bd9Sstevel@tonic-gate 	++NumFileSys;
640049218d4fSjbeck 	FILE_SYS_NAME(i) = name;
64017c478bd9Sstevel@tonic-gate 	FILE_SYS_DEV(i) = st.st_dev;
64027c478bd9Sstevel@tonic-gate 	FILE_SYS_AVAIL(i) = 0;
64037c478bd9Sstevel@tonic-gate 	FILE_SYS_BLKSIZE(i) = 1024; /* avoid divide by zero */
64047c478bd9Sstevel@tonic-gate 	return i;
64057c478bd9Sstevel@tonic-gate }
64067c478bd9Sstevel@tonic-gate 
64077c478bd9Sstevel@tonic-gate /*
64087c478bd9Sstevel@tonic-gate **  FILESYS_SETUP -- set up mapping from queue directories to file systems
64097c478bd9Sstevel@tonic-gate **
64107c478bd9Sstevel@tonic-gate **	This data structure is used to efficiently check the amount of
64117c478bd9Sstevel@tonic-gate **	free space available in a set of queue directories.
64127c478bd9Sstevel@tonic-gate **
64137c478bd9Sstevel@tonic-gate **	Parameters:
64147c478bd9Sstevel@tonic-gate **		add -- initialize structure if necessary.
64157c478bd9Sstevel@tonic-gate **
64167c478bd9Sstevel@tonic-gate **	Returns:
64177c478bd9Sstevel@tonic-gate **		0: success
64187c478bd9Sstevel@tonic-gate **		<0: some error, i.e.,
64197c478bd9Sstevel@tonic-gate **		FSF_NOT_FOUND: not in list
64207c478bd9Sstevel@tonic-gate **		FSF_STAT_FAIL: can't stat() filesystem (-> syserr())
64217c478bd9Sstevel@tonic-gate **		FSF_TOO_MANY: too many filesystems (-> syserr())
64227c478bd9Sstevel@tonic-gate */
64237c478bd9Sstevel@tonic-gate 
64247c478bd9Sstevel@tonic-gate static int filesys_setup __P((bool));
64257c478bd9Sstevel@tonic-gate 
64267c478bd9Sstevel@tonic-gate static int
filesys_setup(add)64277c478bd9Sstevel@tonic-gate filesys_setup(add)
64287c478bd9Sstevel@tonic-gate 	bool add;
64297c478bd9Sstevel@tonic-gate {
64307c478bd9Sstevel@tonic-gate 	int i, j;
64317c478bd9Sstevel@tonic-gate 	short fs;
64327c478bd9Sstevel@tonic-gate 	int ret;
64337c478bd9Sstevel@tonic-gate 
64347c478bd9Sstevel@tonic-gate 	ret = 0;
64357c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
64367c478bd9Sstevel@tonic-gate 	{
64377c478bd9Sstevel@tonic-gate 		for (j = 0; j < Queue[i]->qg_numqueues; ++j)
64387c478bd9Sstevel@tonic-gate 		{
64397c478bd9Sstevel@tonic-gate 			QPATHS *qp = &Queue[i]->qg_qpaths[j];
644049218d4fSjbeck 			char qddf[MAXPATHLEN];
64417c478bd9Sstevel@tonic-gate 
6442058561cbSjbeck 			(void) sm_strlcpyn(qddf, sizeof(qddf), 2, qp->qp_name,
644349218d4fSjbeck 					(bitset(QP_SUBDF, qp->qp_subdirs)
644449218d4fSjbeck 						? "/df" : ""));
644549218d4fSjbeck 			fs = filesys_find(qp->qp_name, qddf, add);
64467c478bd9Sstevel@tonic-gate 			if (fs >= 0)
64477c478bd9Sstevel@tonic-gate 				qp->qp_fsysidx = fs;
64487c478bd9Sstevel@tonic-gate 			else
64497c478bd9Sstevel@tonic-gate 				qp->qp_fsysidx = 0;
64507c478bd9Sstevel@tonic-gate 			if (fs < ret)
64517c478bd9Sstevel@tonic-gate 				ret = fs;
64527c478bd9Sstevel@tonic-gate 		}
64537c478bd9Sstevel@tonic-gate 	}
64547c478bd9Sstevel@tonic-gate 	return ret;
64557c478bd9Sstevel@tonic-gate }
64567c478bd9Sstevel@tonic-gate 
64577c478bd9Sstevel@tonic-gate /*
64587c478bd9Sstevel@tonic-gate **  FILESYS_UPDATE -- update amount of free space on all file systems
64597c478bd9Sstevel@tonic-gate **
64607c478bd9Sstevel@tonic-gate **	The FileSys table is used to cache the amount of free space
64617c478bd9Sstevel@tonic-gate **	available on all queue directory file systems.
64627c478bd9Sstevel@tonic-gate **	This function updates the cached information if it has expired.
64637c478bd9Sstevel@tonic-gate **
64647c478bd9Sstevel@tonic-gate **	Parameters:
64657c478bd9Sstevel@tonic-gate **		none.
64667c478bd9Sstevel@tonic-gate **
64677c478bd9Sstevel@tonic-gate **	Returns:
64687c478bd9Sstevel@tonic-gate **		none.
64697c478bd9Sstevel@tonic-gate **
64707c478bd9Sstevel@tonic-gate **	Side Effects:
64717c478bd9Sstevel@tonic-gate **		Updates FileSys table.
64727c478bd9Sstevel@tonic-gate */
64737c478bd9Sstevel@tonic-gate 
64747c478bd9Sstevel@tonic-gate void
filesys_update()64757c478bd9Sstevel@tonic-gate filesys_update()
64767c478bd9Sstevel@tonic-gate {
64777c478bd9Sstevel@tonic-gate 	int i;
64787c478bd9Sstevel@tonic-gate 	long avail, blksize;
64797c478bd9Sstevel@tonic-gate 	time_t now;
64807c478bd9Sstevel@tonic-gate 	static time_t nextupdate = 0;
64817c478bd9Sstevel@tonic-gate 
64827c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
6483058561cbSjbeck 	/*
6484058561cbSjbeck 	**  Only the daemon updates the shared memory, i.e.,
6485058561cbSjbeck 	**  if shared memory is available but the pid is not the
6486058561cbSjbeck 	**  one of the daemon, then don't do anything.
6487058561cbSjbeck 	*/
6488058561cbSjbeck 
64891daa5768Sjbeck 	if (ShmId != SM_SHM_NO_ID && DaemonPid != CurrentPid)
64907c478bd9Sstevel@tonic-gate 		return;
64917c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
64927c478bd9Sstevel@tonic-gate 	now = curtime();
64937c478bd9Sstevel@tonic-gate 	if (now < nextupdate)
64947c478bd9Sstevel@tonic-gate 		return;
64957c478bd9Sstevel@tonic-gate 	nextupdate = now + FILESYS_UPDATE_INTERVAL;
64967c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
64977c478bd9Sstevel@tonic-gate 	{
64987c478bd9Sstevel@tonic-gate 		FILESYS *fs = &FILE_SYS(i);
64997c478bd9Sstevel@tonic-gate 
65007c478bd9Sstevel@tonic-gate 		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
65017c478bd9Sstevel@tonic-gate 		if (avail < 0 || blksize <= 0)
65027c478bd9Sstevel@tonic-gate 		{
65037c478bd9Sstevel@tonic-gate 			if (LogLevel > 5)
65047c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
65057c478bd9Sstevel@tonic-gate 					"filesys_update failed: %s, fs=%s, avail=%ld, blocksize=%ld",
65067c478bd9Sstevel@tonic-gate 					sm_errstring(errno),
65077c478bd9Sstevel@tonic-gate 					FILE_SYS_NAME(i), avail, blksize);
65087c478bd9Sstevel@tonic-gate 			fs->fs_avail = 0;
65097c478bd9Sstevel@tonic-gate 			fs->fs_blksize = 1024; /* avoid divide by zero */
65107c478bd9Sstevel@tonic-gate 			nextupdate = now + 2; /* let's do this soon again */
65117c478bd9Sstevel@tonic-gate 		}
65127c478bd9Sstevel@tonic-gate 		else
65137c478bd9Sstevel@tonic-gate 		{
65147c478bd9Sstevel@tonic-gate 			fs->fs_avail = avail;
65157c478bd9Sstevel@tonic-gate 			fs->fs_blksize = blksize;
65167c478bd9Sstevel@tonic-gate 		}
65177c478bd9Sstevel@tonic-gate 	}
65187c478bd9Sstevel@tonic-gate }
65197c478bd9Sstevel@tonic-gate 
65207c478bd9Sstevel@tonic-gate #if _FFR_ANY_FREE_FS
65217c478bd9Sstevel@tonic-gate /*
65227c478bd9Sstevel@tonic-gate **  FILESYS_FREE -- check whether there is at least one fs with enough space.
65237c478bd9Sstevel@tonic-gate **
65247c478bd9Sstevel@tonic-gate **	Parameters:
65257c478bd9Sstevel@tonic-gate **		fsize -- file size in bytes
65267c478bd9Sstevel@tonic-gate **
65277c478bd9Sstevel@tonic-gate **	Returns:
65287c478bd9Sstevel@tonic-gate **		true iff there is one fs with more than fsize bytes free.
65297c478bd9Sstevel@tonic-gate */
65307c478bd9Sstevel@tonic-gate 
65317c478bd9Sstevel@tonic-gate bool
filesys_free(fsize)65327c478bd9Sstevel@tonic-gate filesys_free(fsize)
65337c478bd9Sstevel@tonic-gate 	long fsize;
65347c478bd9Sstevel@tonic-gate {
65357c478bd9Sstevel@tonic-gate 	int i;
65367c478bd9Sstevel@tonic-gate 
65377c478bd9Sstevel@tonic-gate 	if (fsize <= 0)
65387c478bd9Sstevel@tonic-gate 		return true;
65397c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
65407c478bd9Sstevel@tonic-gate 	{
65417c478bd9Sstevel@tonic-gate 		long needed = 0;
65427c478bd9Sstevel@tonic-gate 
65437c478bd9Sstevel@tonic-gate 		if (FILE_SYS_AVAIL(i) < 0 || FILE_SYS_BLKSIZE(i) <= 0)
65447c478bd9Sstevel@tonic-gate 			continue;
65457c478bd9Sstevel@tonic-gate 		needed += fsize / FILE_SYS_BLKSIZE(i)
65467c478bd9Sstevel@tonic-gate 			  + ((fsize % FILE_SYS_BLKSIZE(i)
65477c478bd9Sstevel@tonic-gate 			      > 0) ? 1 : 0)
65487c478bd9Sstevel@tonic-gate 			  + MinBlocksFree;
65497c478bd9Sstevel@tonic-gate 		if (needed <= FILE_SYS_AVAIL(i))
65507c478bd9Sstevel@tonic-gate 			return true;
65517c478bd9Sstevel@tonic-gate 	}
65527c478bd9Sstevel@tonic-gate 	return false;
65537c478bd9Sstevel@tonic-gate }
65547c478bd9Sstevel@tonic-gate #endif /* _FFR_ANY_FREE_FS */
65557c478bd9Sstevel@tonic-gate 
65567c478bd9Sstevel@tonic-gate /*
65577c478bd9Sstevel@tonic-gate **  DISK_STATUS -- show amount of free space in queue directories
65587c478bd9Sstevel@tonic-gate **
65597c478bd9Sstevel@tonic-gate **	Parameters:
65607c478bd9Sstevel@tonic-gate **		out -- output file pointer.
65617c478bd9Sstevel@tonic-gate **		prefix -- string to output in front of each line.
65627c478bd9Sstevel@tonic-gate **
65637c478bd9Sstevel@tonic-gate **	Returns:
65647c478bd9Sstevel@tonic-gate **		none.
65657c478bd9Sstevel@tonic-gate */
65667c478bd9Sstevel@tonic-gate 
65677c478bd9Sstevel@tonic-gate void
disk_status(out,prefix)65687c478bd9Sstevel@tonic-gate disk_status(out, prefix)
65697c478bd9Sstevel@tonic-gate 	SM_FILE_T *out;
65707c478bd9Sstevel@tonic-gate 	char *prefix;
65717c478bd9Sstevel@tonic-gate {
65727c478bd9Sstevel@tonic-gate 	int i;
65737c478bd9Sstevel@tonic-gate 	long avail, blksize;
65747c478bd9Sstevel@tonic-gate 	long free;
65757c478bd9Sstevel@tonic-gate 
65767c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumFileSys; ++i)
65777c478bd9Sstevel@tonic-gate 	{
65787c478bd9Sstevel@tonic-gate 		avail = freediskspace(FILE_SYS_NAME(i), &blksize);
65797c478bd9Sstevel@tonic-gate 		if (avail >= 0 && blksize > 0)
65807c478bd9Sstevel@tonic-gate 		{
65817c478bd9Sstevel@tonic-gate 			free = (long)((double) avail *
65827c478bd9Sstevel@tonic-gate 				((double) blksize / 1024));
65837c478bd9Sstevel@tonic-gate 		}
65847c478bd9Sstevel@tonic-gate 		else
65857c478bd9Sstevel@tonic-gate 			free = -1;
65867c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(out, SM_TIME_DEFAULT,
65877c478bd9Sstevel@tonic-gate 				"%s%d/%s/%ld\r\n",
65887c478bd9Sstevel@tonic-gate 				prefix, i,
65897c478bd9Sstevel@tonic-gate 				FILE_SYS_NAME(i),
65907c478bd9Sstevel@tonic-gate 					free);
65917c478bd9Sstevel@tonic-gate 	}
65927c478bd9Sstevel@tonic-gate }
65937c478bd9Sstevel@tonic-gate 
65947c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
65957c478bd9Sstevel@tonic-gate 
65967c478bd9Sstevel@tonic-gate /*
65977c478bd9Sstevel@tonic-gate **  INIT_SEM -- initialize semaphore system
65987c478bd9Sstevel@tonic-gate **
65997c478bd9Sstevel@tonic-gate **	Parameters:
66007c478bd9Sstevel@tonic-gate **		owner -- is this the owner of semaphores?
66017c478bd9Sstevel@tonic-gate **
66027c478bd9Sstevel@tonic-gate **	Returns:
66037c478bd9Sstevel@tonic-gate **		none.
66047c478bd9Sstevel@tonic-gate */
66057c478bd9Sstevel@tonic-gate 
66067c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
66077c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
66087c478bd9Sstevel@tonic-gate static int SemId = -1;		/* Semaphore Id */
66097c478bd9Sstevel@tonic-gate int SemKey = SM_SEM_KEY;
66107c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
66117c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
66127c478bd9Sstevel@tonic-gate 
66137c478bd9Sstevel@tonic-gate static void init_sem __P((bool));
66147c478bd9Sstevel@tonic-gate 
66157c478bd9Sstevel@tonic-gate static void
init_sem(owner)66167c478bd9Sstevel@tonic-gate init_sem(owner)
66177c478bd9Sstevel@tonic-gate 	bool owner;
66187c478bd9Sstevel@tonic-gate {
66197c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
66207c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
66217c478bd9Sstevel@tonic-gate 	SemId = sm_sem_start(SemKey, 1, 0, owner);
66227c478bd9Sstevel@tonic-gate 	if (SemId < 0)
66237c478bd9Sstevel@tonic-gate 	{
66247c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID,
6625d4660949Sjbeck 			"func=init_sem, sem_key=%ld, sm_sem_start=%d, error=%s",
6626d4660949Sjbeck 			(long) SemKey, SemId, sm_errstring(-SemId));
66277c478bd9Sstevel@tonic-gate 		return;
66287c478bd9Sstevel@tonic-gate 	}
6629e9af4bc0SJohn Beck 	if (owner && RunAsUid != 0)
6630e9af4bc0SJohn Beck 	{
6631e9af4bc0SJohn Beck 		int r;
6632e9af4bc0SJohn Beck 
6633e9af4bc0SJohn Beck 		r = sm_semsetowner(SemId, RunAsUid, RunAsGid, 0660);
6634e9af4bc0SJohn Beck 		if (r != 0)
6635e9af4bc0SJohn Beck 			sm_syslog(LOG_ERR, NOQID,
6636e9af4bc0SJohn Beck 				"key=%ld, sm_semsetowner=%d, RunAsUid=%d, RunAsGid=%d",
6637e9af4bc0SJohn Beck 				(long) SemKey, r, RunAsUid, RunAsGid);
6638e9af4bc0SJohn Beck 	}
66397c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
66407c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
66417c478bd9Sstevel@tonic-gate 	return;
66427c478bd9Sstevel@tonic-gate }
66437c478bd9Sstevel@tonic-gate 
66447c478bd9Sstevel@tonic-gate /*
66457c478bd9Sstevel@tonic-gate **  STOP_SEM -- stop semaphore system
66467c478bd9Sstevel@tonic-gate **
66477c478bd9Sstevel@tonic-gate **	Parameters:
66487c478bd9Sstevel@tonic-gate **		owner -- is this the owner of semaphores?
66497c478bd9Sstevel@tonic-gate **
66507c478bd9Sstevel@tonic-gate **	Returns:
66517c478bd9Sstevel@tonic-gate **		none.
66527c478bd9Sstevel@tonic-gate */
66537c478bd9Sstevel@tonic-gate 
66547c478bd9Sstevel@tonic-gate static void stop_sem __P((bool));
66557c478bd9Sstevel@tonic-gate 
66567c478bd9Sstevel@tonic-gate static void
stop_sem(owner)66577c478bd9Sstevel@tonic-gate stop_sem(owner)
66587c478bd9Sstevel@tonic-gate 	bool owner;
66597c478bd9Sstevel@tonic-gate {
66607c478bd9Sstevel@tonic-gate #if _FFR_USE_SEM_LOCKING
66617c478bd9Sstevel@tonic-gate #if SM_CONF_SEM
66627c478bd9Sstevel@tonic-gate 	if (owner && SemId >= 0)
66637c478bd9Sstevel@tonic-gate 		sm_sem_stop(SemId);
66647c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SEM */
66657c478bd9Sstevel@tonic-gate #endif /* _FFR_USE_SEM_LOCKING */
66667c478bd9Sstevel@tonic-gate 	return;
66677c478bd9Sstevel@tonic-gate }
66687c478bd9Sstevel@tonic-gate 
66697c478bd9Sstevel@tonic-gate /*
66707c478bd9Sstevel@tonic-gate **  UPD_QS -- update information about queue when adding/deleting an entry
66717c478bd9Sstevel@tonic-gate **
66727c478bd9Sstevel@tonic-gate **	Parameters:
66737c478bd9Sstevel@tonic-gate **		e -- envelope.
66747c478bd9Sstevel@tonic-gate **		count -- add/remove entry (+1/0/-1: add/no change/remove)
66757c478bd9Sstevel@tonic-gate **		space -- update the space available as well.
66767c478bd9Sstevel@tonic-gate **			(>0/0/<0: add/no change/remove)
66777c478bd9Sstevel@tonic-gate **		where -- caller (for logging)
66787c478bd9Sstevel@tonic-gate **
66797c478bd9Sstevel@tonic-gate **	Returns:
66807c478bd9Sstevel@tonic-gate **		none.
66817c478bd9Sstevel@tonic-gate **
66827c478bd9Sstevel@tonic-gate **	Side Effects:
66837c478bd9Sstevel@tonic-gate **		Modifies available space in filesystem.
66847c478bd9Sstevel@tonic-gate **		Changes number of entries in queue directory.
66857c478bd9Sstevel@tonic-gate */
66867c478bd9Sstevel@tonic-gate 
66877c478bd9Sstevel@tonic-gate void
upd_qs(e,count,space,where)66887c478bd9Sstevel@tonic-gate upd_qs(e, count, space, where)
66897c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
66907c478bd9Sstevel@tonic-gate 	int count;
66917c478bd9Sstevel@tonic-gate 	int space;
66927c478bd9Sstevel@tonic-gate 	char *where;
66937c478bd9Sstevel@tonic-gate {
66947c478bd9Sstevel@tonic-gate 	short fidx;
66957c478bd9Sstevel@tonic-gate 	int idx;
66967c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
66977c478bd9Sstevel@tonic-gate 	int r;
66987c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
66997c478bd9Sstevel@tonic-gate 	long s;
67007c478bd9Sstevel@tonic-gate 
67017c478bd9Sstevel@tonic-gate 	if (ShmId == SM_SHM_NO_ID || e == NULL)
67027c478bd9Sstevel@tonic-gate 		return;
67037c478bd9Sstevel@tonic-gate 	if (e->e_qgrp == NOQGRP || e->e_qdir == NOQDIR)
67047c478bd9Sstevel@tonic-gate 		return;
67057c478bd9Sstevel@tonic-gate 	idx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_idx;
67067c478bd9Sstevel@tonic-gate 	if (tTd(73,2))
67077c478bd9Sstevel@tonic-gate 		sm_dprintf("func=upd_qs, count=%d, space=%d, where=%s, idx=%d, entries=%d\n",
67087c478bd9Sstevel@tonic-gate 			count, space, where, idx, QSHM_ENTRIES(idx));
67097c478bd9Sstevel@tonic-gate 
67107c478bd9Sstevel@tonic-gate 	/* XXX in theory this needs to be protected with a mutex */
67117c478bd9Sstevel@tonic-gate 	if (QSHM_ENTRIES(idx) >= 0 && count != 0)
67127c478bd9Sstevel@tonic-gate 	{
67137c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
67147c478bd9Sstevel@tonic-gate 		r = sm_sem_acq(SemId, 0, 1);
67157c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
67167c478bd9Sstevel@tonic-gate 		QSHM_ENTRIES(idx) += count;
67177c478bd9Sstevel@tonic-gate # if _FFR_USE_SEM_LOCKING
67187c478bd9Sstevel@tonic-gate 		if (r >= 0)
67197c478bd9Sstevel@tonic-gate 			r = sm_sem_rel(SemId, 0, 1);
67207c478bd9Sstevel@tonic-gate # endif /* _FFR_USE_SEM_LOCKING */
67217c478bd9Sstevel@tonic-gate 	}
67227c478bd9Sstevel@tonic-gate 
67237c478bd9Sstevel@tonic-gate 	fidx = Queue[e->e_qgrp]->qg_qpaths[e->e_qdir].qp_fsysidx;
67247c478bd9Sstevel@tonic-gate 	if (fidx < 0)
67257c478bd9Sstevel@tonic-gate 		return;
67267c478bd9Sstevel@tonic-gate 
67277c478bd9Sstevel@tonic-gate 	/* update available space also?  (might be loseqfile) */
67287c478bd9Sstevel@tonic-gate 	if (space == 0)
67297c478bd9Sstevel@tonic-gate 		return;
67307c478bd9Sstevel@tonic-gate 
67317c478bd9Sstevel@tonic-gate 	/* convert size to blocks; this causes rounding errors */
67327c478bd9Sstevel@tonic-gate 	s = e->e_msgsize / FILE_SYS_BLKSIZE(fidx);
67337c478bd9Sstevel@tonic-gate 	if (s == 0)
67347c478bd9Sstevel@tonic-gate 		return;
67357c478bd9Sstevel@tonic-gate 
67367c478bd9Sstevel@tonic-gate 	/* XXX in theory this needs to be protected with a mutex */
67377c478bd9Sstevel@tonic-gate 	if (space > 0)
67387c478bd9Sstevel@tonic-gate 		FILE_SYS_AVAIL(fidx) += s;
67397c478bd9Sstevel@tonic-gate 	else
67407c478bd9Sstevel@tonic-gate 		FILE_SYS_AVAIL(fidx) -= s;
67417c478bd9Sstevel@tonic-gate 
67427c478bd9Sstevel@tonic-gate }
67437c478bd9Sstevel@tonic-gate 
67447c478bd9Sstevel@tonic-gate static bool write_key_file __P((char *, long));
67457c478bd9Sstevel@tonic-gate static long read_key_file __P((char *, long));
67467c478bd9Sstevel@tonic-gate 
67477c478bd9Sstevel@tonic-gate /*
67487c478bd9Sstevel@tonic-gate **  WRITE_KEY_FILE -- record some key into a file.
67497c478bd9Sstevel@tonic-gate **
67507c478bd9Sstevel@tonic-gate **	Parameters:
67517c478bd9Sstevel@tonic-gate **		keypath -- file name.
67527c478bd9Sstevel@tonic-gate **		key -- key to write.
67537c478bd9Sstevel@tonic-gate **
67547c478bd9Sstevel@tonic-gate **	Returns:
67557c478bd9Sstevel@tonic-gate **		true iff file could be written.
67567c478bd9Sstevel@tonic-gate **
67577c478bd9Sstevel@tonic-gate **	Side Effects:
67587c478bd9Sstevel@tonic-gate **		writes file.
67597c478bd9Sstevel@tonic-gate */
67607c478bd9Sstevel@tonic-gate 
67617c478bd9Sstevel@tonic-gate static bool
write_key_file(keypath,key)67627c478bd9Sstevel@tonic-gate write_key_file(keypath, key)
67637c478bd9Sstevel@tonic-gate 	char *keypath;
67647c478bd9Sstevel@tonic-gate 	long key;
67657c478bd9Sstevel@tonic-gate {
67667c478bd9Sstevel@tonic-gate 	bool ok;
67677c478bd9Sstevel@tonic-gate 	long sff;
67687c478bd9Sstevel@tonic-gate 	SM_FILE_T *keyf;
67697c478bd9Sstevel@tonic-gate 
67707c478bd9Sstevel@tonic-gate 	ok = false;
67717c478bd9Sstevel@tonic-gate 	if (keypath == NULL || *keypath == '\0')
67727c478bd9Sstevel@tonic-gate 		return ok;
67737c478bd9Sstevel@tonic-gate 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY|SFF_CREAT;
67747c478bd9Sstevel@tonic-gate 	if (TrustedUid != 0 && RealUid == TrustedUid)
67757c478bd9Sstevel@tonic-gate 		sff |= SFF_OPENASROOT;
67767c478bd9Sstevel@tonic-gate 	keyf = safefopen(keypath, O_WRONLY|O_TRUNC, FileMode, sff);
67777c478bd9Sstevel@tonic-gate 	if (keyf == NULL)
67787c478bd9Sstevel@tonic-gate 	{
67797c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID, "unable to write %s: %s",
67807c478bd9Sstevel@tonic-gate 			  keypath, sm_errstring(errno));
67817c478bd9Sstevel@tonic-gate 	}
67827c478bd9Sstevel@tonic-gate 	else
67837c478bd9Sstevel@tonic-gate 	{
678449218d4fSjbeck 		if (geteuid() == 0 && RunAsUid != 0)
678549218d4fSjbeck 		{
678649218d4fSjbeck #  if HASFCHOWN
678749218d4fSjbeck 			int fd;
678849218d4fSjbeck 
678949218d4fSjbeck 			fd = keyf->f_file;
679049218d4fSjbeck 			if (fd >= 0 && fchown(fd, RunAsUid, -1) < 0)
679149218d4fSjbeck 			{
679249218d4fSjbeck 				int err = errno;
679349218d4fSjbeck 
679449218d4fSjbeck 				sm_syslog(LOG_ALERT, NOQID,
679549218d4fSjbeck 					  "ownership change on %s to %d failed: %s",
679649218d4fSjbeck 					  keypath, RunAsUid, sm_errstring(err));
679749218d4fSjbeck 			}
679849218d4fSjbeck #  endif /* HASFCHOWN */
679949218d4fSjbeck 		}
68007c478bd9Sstevel@tonic-gate 		ok = sm_io_fprintf(keyf, SM_TIME_DEFAULT, "%ld\n", key) !=
68017c478bd9Sstevel@tonic-gate 		     SM_IO_EOF;
68027c478bd9Sstevel@tonic-gate 		ok = (sm_io_close(keyf, SM_TIME_DEFAULT) != SM_IO_EOF) && ok;
68037c478bd9Sstevel@tonic-gate 	}
68047c478bd9Sstevel@tonic-gate 	return ok;
68057c478bd9Sstevel@tonic-gate }
68067c478bd9Sstevel@tonic-gate 
68077c478bd9Sstevel@tonic-gate /*
68087c478bd9Sstevel@tonic-gate **  READ_KEY_FILE -- read a key from a file.
68097c478bd9Sstevel@tonic-gate **
68107c478bd9Sstevel@tonic-gate **	Parameters:
68117c478bd9Sstevel@tonic-gate **		keypath -- file name.
68127c478bd9Sstevel@tonic-gate **		key -- default key.
68137c478bd9Sstevel@tonic-gate **
68147c478bd9Sstevel@tonic-gate **	Returns:
68157c478bd9Sstevel@tonic-gate **		key.
68167c478bd9Sstevel@tonic-gate */
68177c478bd9Sstevel@tonic-gate 
68187c478bd9Sstevel@tonic-gate static long
read_key_file(keypath,key)68197c478bd9Sstevel@tonic-gate read_key_file(keypath, key)
68207c478bd9Sstevel@tonic-gate 	char *keypath;
68217c478bd9Sstevel@tonic-gate 	long key;
68227c478bd9Sstevel@tonic-gate {
68237c478bd9Sstevel@tonic-gate 	int r;
68247c478bd9Sstevel@tonic-gate 	long sff, n;
68257c478bd9Sstevel@tonic-gate 	SM_FILE_T *keyf;
68267c478bd9Sstevel@tonic-gate 
68277c478bd9Sstevel@tonic-gate 	if (keypath == NULL || *keypath == '\0')
68287c478bd9Sstevel@tonic-gate 		return key;
68297c478bd9Sstevel@tonic-gate 	sff = SFF_NOLINK|SFF_ROOTOK|SFF_REGONLY;
68307c478bd9Sstevel@tonic-gate 	if (RealUid == 0 || (TrustedUid != 0 && RealUid == TrustedUid))
68317c478bd9Sstevel@tonic-gate 		sff |= SFF_OPENASROOT;
68327c478bd9Sstevel@tonic-gate 	keyf = safefopen(keypath, O_RDONLY, FileMode, sff);
68337c478bd9Sstevel@tonic-gate 	if (keyf == NULL)
68347c478bd9Sstevel@tonic-gate 	{
68357c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, NOQID, "unable to read %s: %s",
68367c478bd9Sstevel@tonic-gate 			  keypath, sm_errstring(errno));
68377c478bd9Sstevel@tonic-gate 	}
68387c478bd9Sstevel@tonic-gate 	else
68397c478bd9Sstevel@tonic-gate 	{
68407c478bd9Sstevel@tonic-gate 		r = sm_io_fscanf(keyf, SM_TIME_DEFAULT, "%ld", &n);
68417c478bd9Sstevel@tonic-gate 		if (r == 1)
68427c478bd9Sstevel@tonic-gate 			key = n;
68437c478bd9Sstevel@tonic-gate 		(void) sm_io_close(keyf, SM_TIME_DEFAULT);
68447c478bd9Sstevel@tonic-gate 	}
68457c478bd9Sstevel@tonic-gate 	return key;
68467c478bd9Sstevel@tonic-gate }
68477c478bd9Sstevel@tonic-gate 
68487c478bd9Sstevel@tonic-gate /*
68497c478bd9Sstevel@tonic-gate **  INIT_SHM -- initialize shared memory structure
68507c478bd9Sstevel@tonic-gate **
68517c478bd9Sstevel@tonic-gate **	Initialize or attach to shared memory segment.
68527c478bd9Sstevel@tonic-gate **	Currently it is not a fatal error if this doesn't work.
68537c478bd9Sstevel@tonic-gate **	However, it causes us to have a "fallback" storage location
68547c478bd9Sstevel@tonic-gate **	for everything that is supposed to be in the shared memory,
68557c478bd9Sstevel@tonic-gate **	which makes the code slightly ugly.
68567c478bd9Sstevel@tonic-gate **
68577c478bd9Sstevel@tonic-gate **	Parameters:
68587c478bd9Sstevel@tonic-gate **		qn -- number of queue directories.
68597c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory.
68607c478bd9Sstevel@tonic-gate **		hash -- identifies data that is stored in shared memory.
68617c478bd9Sstevel@tonic-gate **
68627c478bd9Sstevel@tonic-gate **	Returns:
68637c478bd9Sstevel@tonic-gate **		none.
68647c478bd9Sstevel@tonic-gate */
68657c478bd9Sstevel@tonic-gate 
68667c478bd9Sstevel@tonic-gate static void init_shm __P((int, bool, unsigned int));
68677c478bd9Sstevel@tonic-gate 
68687c478bd9Sstevel@tonic-gate static void
init_shm(qn,owner,hash)68697c478bd9Sstevel@tonic-gate init_shm(qn, owner, hash)
68707c478bd9Sstevel@tonic-gate 	int qn;
68717c478bd9Sstevel@tonic-gate 	bool owner;
68727c478bd9Sstevel@tonic-gate 	unsigned int hash;
68737c478bd9Sstevel@tonic-gate {
68747c478bd9Sstevel@tonic-gate 	int i;
68757c478bd9Sstevel@tonic-gate 	int count;
68767c478bd9Sstevel@tonic-gate 	int save_errno;
68777c478bd9Sstevel@tonic-gate 	bool keyselect;
68787c478bd9Sstevel@tonic-gate 
68797c478bd9Sstevel@tonic-gate 	PtrFileSys = &FileSys[0];
68807c478bd9Sstevel@tonic-gate 	PNumFileSys = &Numfilesys;
68817c478bd9Sstevel@tonic-gate /* if this "key" is specified: select one yourself */
68827c478bd9Sstevel@tonic-gate #define SEL_SHM_KEY	((key_t) -1)
68837c478bd9Sstevel@tonic-gate #define FIRST_SHM_KEY	25
68847c478bd9Sstevel@tonic-gate 
68857c478bd9Sstevel@tonic-gate 	/* This allows us to disable shared memory at runtime. */
68867c478bd9Sstevel@tonic-gate 	if (ShmKey == 0)
68877c478bd9Sstevel@tonic-gate 		return;
68887c478bd9Sstevel@tonic-gate 
68897c478bd9Sstevel@tonic-gate 	count = 0;
68907c478bd9Sstevel@tonic-gate 	shms = SM_T_SIZE + qn * sizeof(QUEUE_SHM_T);
68917c478bd9Sstevel@tonic-gate 	keyselect = ShmKey == SEL_SHM_KEY;
68927c478bd9Sstevel@tonic-gate 	if (keyselect)
68937c478bd9Sstevel@tonic-gate 	{
68947c478bd9Sstevel@tonic-gate 		if (owner)
68957c478bd9Sstevel@tonic-gate 			ShmKey = FIRST_SHM_KEY;
68967c478bd9Sstevel@tonic-gate 		else
68977c478bd9Sstevel@tonic-gate 		{
6898058561cbSjbeck 			errno = 0;
68997c478bd9Sstevel@tonic-gate 			ShmKey = read_key_file(ShmKeyFile, ShmKey);
69007c478bd9Sstevel@tonic-gate 			keyselect = false;
69017c478bd9Sstevel@tonic-gate 			if (ShmKey == SEL_SHM_KEY)
6902058561cbSjbeck 			{
6903058561cbSjbeck 				save_errno = (errno != 0) ? errno : EINVAL;
69047c478bd9Sstevel@tonic-gate 				goto error;
69057c478bd9Sstevel@tonic-gate 			}
69067c478bd9Sstevel@tonic-gate 		}
6907058561cbSjbeck 	}
69087c478bd9Sstevel@tonic-gate 	for (;;)
69097c478bd9Sstevel@tonic-gate 	{
69107c478bd9Sstevel@tonic-gate 		/* allow read/write access for group? */
69117c478bd9Sstevel@tonic-gate 		Pshm = sm_shmstart(ShmKey, shms,
69127c478bd9Sstevel@tonic-gate 				SHM_R|SHM_W|(SHM_R>>3)|(SHM_W>>3),
69137c478bd9Sstevel@tonic-gate 				&ShmId, owner);
69147c478bd9Sstevel@tonic-gate 		save_errno = errno;
69157c478bd9Sstevel@tonic-gate 		if (Pshm != NULL || !sm_file_exists(save_errno))
69167c478bd9Sstevel@tonic-gate 			break;
69177c478bd9Sstevel@tonic-gate 		if (++count >= 3)
69187c478bd9Sstevel@tonic-gate 		{
69197c478bd9Sstevel@tonic-gate 			if (keyselect)
69207c478bd9Sstevel@tonic-gate 			{
69217c478bd9Sstevel@tonic-gate 				++ShmKey;
69227c478bd9Sstevel@tonic-gate 
69237c478bd9Sstevel@tonic-gate 				/* back where we started? */
69247c478bd9Sstevel@tonic-gate 				if (ShmKey == SEL_SHM_KEY)
69257c478bd9Sstevel@tonic-gate 					break;
69267c478bd9Sstevel@tonic-gate 				continue;
69277c478bd9Sstevel@tonic-gate 			}
69287c478bd9Sstevel@tonic-gate 			break;
69297c478bd9Sstevel@tonic-gate 		}
6930058561cbSjbeck 
69317c478bd9Sstevel@tonic-gate 		/* only sleep if we are at the first key */
69327c478bd9Sstevel@tonic-gate 		if (!keyselect || ShmKey == SEL_SHM_KEY)
69337c478bd9Sstevel@tonic-gate 			sleep(count);
69347c478bd9Sstevel@tonic-gate 	}
69357c478bd9Sstevel@tonic-gate 	if (Pshm != NULL)
69367c478bd9Sstevel@tonic-gate 	{
69377c478bd9Sstevel@tonic-gate 		int *p;
69387c478bd9Sstevel@tonic-gate 
69397c478bd9Sstevel@tonic-gate 		if (keyselect)
69407c478bd9Sstevel@tonic-gate 			(void) write_key_file(ShmKeyFile, (long) ShmKey);
69417c478bd9Sstevel@tonic-gate 		if (owner && RunAsUid != 0)
69427c478bd9Sstevel@tonic-gate 		{
6943445f2479Sjbeck 			i = sm_shmsetowner(ShmId, RunAsUid, RunAsGid, 0660);
69447c478bd9Sstevel@tonic-gate 			if (i != 0)
69457c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
69467c478bd9Sstevel@tonic-gate 					"key=%ld, sm_shmsetowner=%d, RunAsUid=%d, RunAsGid=%d",
6947445f2479Sjbeck 					(long) ShmKey, i, RunAsUid, RunAsGid);
69487c478bd9Sstevel@tonic-gate 		}
69497c478bd9Sstevel@tonic-gate 		p = (int *) Pshm;
69507c478bd9Sstevel@tonic-gate 		if (owner)
69517c478bd9Sstevel@tonic-gate 		{
69527c478bd9Sstevel@tonic-gate 			*p = (int) shms;
69537c478bd9Sstevel@tonic-gate 			*((pid_t *) SHM_OFF_PID(Pshm)) = CurrentPid;
69547c478bd9Sstevel@tonic-gate 			p = (int *) SHM_OFF_TAG(Pshm);
69557c478bd9Sstevel@tonic-gate 			*p = hash;
69567c478bd9Sstevel@tonic-gate 		}
69577c478bd9Sstevel@tonic-gate 		else
69587c478bd9Sstevel@tonic-gate 		{
69597c478bd9Sstevel@tonic-gate 			if (*p != (int) shms)
69607c478bd9Sstevel@tonic-gate 			{
69617c478bd9Sstevel@tonic-gate 				save_errno = EINVAL;
69627c478bd9Sstevel@tonic-gate 				cleanup_shm(false);
69637c478bd9Sstevel@tonic-gate 				goto error;
69647c478bd9Sstevel@tonic-gate 			}
69657c478bd9Sstevel@tonic-gate 			p = (int *) SHM_OFF_TAG(Pshm);
69667c478bd9Sstevel@tonic-gate 			if (*p != (int) hash)
69677c478bd9Sstevel@tonic-gate 			{
69687c478bd9Sstevel@tonic-gate 				save_errno = EINVAL;
69697c478bd9Sstevel@tonic-gate 				cleanup_shm(false);
69707c478bd9Sstevel@tonic-gate 				goto error;
69717c478bd9Sstevel@tonic-gate 			}
69727c478bd9Sstevel@tonic-gate 
69737c478bd9Sstevel@tonic-gate 			/*
69747c478bd9Sstevel@tonic-gate 			**  XXX how to check the pid?
69757c478bd9Sstevel@tonic-gate 			**  Read it from the pid-file? That does
69767c478bd9Sstevel@tonic-gate 			**  not need to exist.
69777c478bd9Sstevel@tonic-gate 			**  We could disable shm if we can't confirm
69787c478bd9Sstevel@tonic-gate 			**  that it is the right one.
69797c478bd9Sstevel@tonic-gate 			*/
69807c478bd9Sstevel@tonic-gate 		}
69817c478bd9Sstevel@tonic-gate 
69827c478bd9Sstevel@tonic-gate 		PtrFileSys = (FILESYS *) OFF_FILE_SYS(Pshm);
69837c478bd9Sstevel@tonic-gate 		PNumFileSys = (int *) OFF_NUM_FILE_SYS(Pshm);
69847c478bd9Sstevel@tonic-gate 		QShm = (QUEUE_SHM_T *) OFF_QUEUE_SHM(Pshm);
69857c478bd9Sstevel@tonic-gate 		PRSATmpCnt = (int *) OFF_RSA_TMP_CNT(Pshm);
69867c478bd9Sstevel@tonic-gate 		*PRSATmpCnt = 0;
69877c478bd9Sstevel@tonic-gate 		if (owner)
69887c478bd9Sstevel@tonic-gate 		{
69897c478bd9Sstevel@tonic-gate 			/* initialize values in shared memory */
69907c478bd9Sstevel@tonic-gate 			NumFileSys = 0;
69917c478bd9Sstevel@tonic-gate 			for (i = 0; i < qn; i++)
69927c478bd9Sstevel@tonic-gate 				QShm[i].qs_entries = -1;
69937c478bd9Sstevel@tonic-gate 		}
69947c478bd9Sstevel@tonic-gate 		init_sem(owner);
69957c478bd9Sstevel@tonic-gate 		return;
69967c478bd9Sstevel@tonic-gate 	}
69977c478bd9Sstevel@tonic-gate   error:
69987c478bd9Sstevel@tonic-gate 	if (LogLevel > (owner ? 8 : 11))
69997c478bd9Sstevel@tonic-gate 	{
70007c478bd9Sstevel@tonic-gate 		sm_syslog(owner ? LOG_ERR : LOG_NOTICE, NOQID,
70017c478bd9Sstevel@tonic-gate 			  "can't %s shared memory, key=%ld: %s",
70027c478bd9Sstevel@tonic-gate 			  owner ? "initialize" : "attach to",
70037c478bd9Sstevel@tonic-gate 			  (long) ShmKey, sm_errstring(save_errno));
70047c478bd9Sstevel@tonic-gate 	}
70057c478bd9Sstevel@tonic-gate }
70067c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
70077c478bd9Sstevel@tonic-gate 
70087c478bd9Sstevel@tonic-gate 
70097c478bd9Sstevel@tonic-gate /*
70107c478bd9Sstevel@tonic-gate **  SETUP_QUEUES -- set up all queue groups
70117c478bd9Sstevel@tonic-gate **
70127c478bd9Sstevel@tonic-gate **	Parameters:
7013058561cbSjbeck **		owner -- owner of shared memory?
70147c478bd9Sstevel@tonic-gate **
70157c478bd9Sstevel@tonic-gate **	Returns:
70167c478bd9Sstevel@tonic-gate **		none.
70177c478bd9Sstevel@tonic-gate **
70187c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
70197c478bd9Sstevel@tonic-gate **	Side Effects:
70207c478bd9Sstevel@tonic-gate **		attaches shared memory.
70217c478bd9Sstevel@tonic-gate #endif * SM_CONF_SHM *
70227c478bd9Sstevel@tonic-gate */
70237c478bd9Sstevel@tonic-gate 
70247c478bd9Sstevel@tonic-gate void
setup_queues(owner)70257c478bd9Sstevel@tonic-gate setup_queues(owner)
70267c478bd9Sstevel@tonic-gate 	bool owner;
70277c478bd9Sstevel@tonic-gate {
70287c478bd9Sstevel@tonic-gate 	int i, qn, len;
70297c478bd9Sstevel@tonic-gate 	unsigned int hashval;
70307c478bd9Sstevel@tonic-gate 	time_t now;
70317c478bd9Sstevel@tonic-gate 	char basedir[MAXPATHLEN];
70327c478bd9Sstevel@tonic-gate 	struct stat st;
70337c478bd9Sstevel@tonic-gate 
70347c478bd9Sstevel@tonic-gate 	/*
70357c478bd9Sstevel@tonic-gate 	**  Determine basedir for all queue directories.
70367c478bd9Sstevel@tonic-gate 	**  All queue directories must be (first level) subdirectories
70377c478bd9Sstevel@tonic-gate 	**  of the basedir.  The basedir is the QueueDir
70387c478bd9Sstevel@tonic-gate 	**  without wildcards, but with trailing /
70397c478bd9Sstevel@tonic-gate 	*/
70407c478bd9Sstevel@tonic-gate 
70417c478bd9Sstevel@tonic-gate 	hashval = 0;
70427c478bd9Sstevel@tonic-gate 	errno = 0;
7043058561cbSjbeck 	len = sm_strlcpy(basedir, QueueDir, sizeof(basedir));
70447c478bd9Sstevel@tonic-gate 
70457c478bd9Sstevel@tonic-gate 	/* Provide space for trailing '/' */
7046058561cbSjbeck 	if (len >= sizeof(basedir) - 1)
70477c478bd9Sstevel@tonic-gate 	{
70487c478bd9Sstevel@tonic-gate 		syserr("QueueDirectory: path too long: %d,  max %d",
7049058561cbSjbeck 			len, (int) sizeof(basedir) - 1);
70507c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
70517c478bd9Sstevel@tonic-gate 		return;
70527c478bd9Sstevel@tonic-gate 	}
70537c478bd9Sstevel@tonic-gate 	SM_ASSERT(len > 0);
70547c478bd9Sstevel@tonic-gate 	if (basedir[len - 1] == '*')
70557c478bd9Sstevel@tonic-gate 	{
70567c478bd9Sstevel@tonic-gate 		char *cp;
70577c478bd9Sstevel@tonic-gate 
70587c478bd9Sstevel@tonic-gate 		cp = SM_LAST_DIR_DELIM(basedir);
70597c478bd9Sstevel@tonic-gate 		if (cp == NULL)
70607c478bd9Sstevel@tonic-gate 		{
70617c478bd9Sstevel@tonic-gate 			syserr("QueueDirectory: can not wildcard relative path \"%s\"",
70627c478bd9Sstevel@tonic-gate 				QueueDir);
70637c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
70647c478bd9Sstevel@tonic-gate 				sm_dprintf("setup_queues: \"%s\": Can not wildcard relative path.\n",
70657c478bd9Sstevel@tonic-gate 					QueueDir);
70667c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
70677c478bd9Sstevel@tonic-gate 			return;
70687c478bd9Sstevel@tonic-gate 		}
70697c478bd9Sstevel@tonic-gate 
70707c478bd9Sstevel@tonic-gate 		/* cut off wildcard pattern */
70717c478bd9Sstevel@tonic-gate 		*++cp = '\0';
70727c478bd9Sstevel@tonic-gate 		len = cp - basedir;
70737c478bd9Sstevel@tonic-gate 	}
70747c478bd9Sstevel@tonic-gate 	else if (!SM_IS_DIR_DELIM(basedir[len - 1]))
70757c478bd9Sstevel@tonic-gate 	{
70767c478bd9Sstevel@tonic-gate 		/* append trailing slash since it is a directory */
70777c478bd9Sstevel@tonic-gate 		basedir[len] = '/';
70787c478bd9Sstevel@tonic-gate 		basedir[++len] = '\0';
70797c478bd9Sstevel@tonic-gate 	}
70807c478bd9Sstevel@tonic-gate 
70817c478bd9Sstevel@tonic-gate 	/* len counts up to the last directory delimiter */
70827c478bd9Sstevel@tonic-gate 	SM_ASSERT(basedir[len - 1] == '/');
70837c478bd9Sstevel@tonic-gate 
70847c478bd9Sstevel@tonic-gate 	if (chdir(basedir) < 0)
70857c478bd9Sstevel@tonic-gate 	{
70867c478bd9Sstevel@tonic-gate 		int save_errno = errno;
70877c478bd9Sstevel@tonic-gate 
70887c478bd9Sstevel@tonic-gate 		syserr("can not chdir(%s)", basedir);
70897c478bd9Sstevel@tonic-gate 		if (save_errno == EACCES)
70907c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
70917c478bd9Sstevel@tonic-gate 				"Program mode requires special privileges, e.g., root or TrustedUser.\n");
70927c478bd9Sstevel@tonic-gate 		if (tTd(41, 2))
70937c478bd9Sstevel@tonic-gate 			sm_dprintf("setup_queues: \"%s\": %s\n",
70947c478bd9Sstevel@tonic-gate 				   basedir, sm_errstring(errno));
70957c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
70967c478bd9Sstevel@tonic-gate 		return;
70977c478bd9Sstevel@tonic-gate 	}
70987c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
70997c478bd9Sstevel@tonic-gate 	hashval = hash_q(basedir, hashval);
71007c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
71017c478bd9Sstevel@tonic-gate 
71027c478bd9Sstevel@tonic-gate 	/* initialize for queue runs */
71037c478bd9Sstevel@tonic-gate 	DoQueueRun = false;
71047c478bd9Sstevel@tonic-gate 	now = curtime();
71057c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
71067c478bd9Sstevel@tonic-gate 		Queue[i]->qg_nextrun = now;
71077c478bd9Sstevel@tonic-gate 
71087c478bd9Sstevel@tonic-gate 
71097c478bd9Sstevel@tonic-gate 	if (UseMSP && OpMode != MD_TEST)
71107c478bd9Sstevel@tonic-gate 	{
71117c478bd9Sstevel@tonic-gate 		long sff = SFF_CREAT;
71127c478bd9Sstevel@tonic-gate 
71137c478bd9Sstevel@tonic-gate 		if (stat(".", &st) < 0)
71147c478bd9Sstevel@tonic-gate 		{
71157c478bd9Sstevel@tonic-gate 			syserr("can not stat(%s)", basedir);
71167c478bd9Sstevel@tonic-gate 			if (tTd(41, 2))
71177c478bd9Sstevel@tonic-gate 				sm_dprintf("setup_queues: \"%s\": %s\n",
71187c478bd9Sstevel@tonic-gate 					   basedir, sm_errstring(errno));
71197c478bd9Sstevel@tonic-gate 			ExitStat = EX_CONFIG;
71207c478bd9Sstevel@tonic-gate 			return;
71217c478bd9Sstevel@tonic-gate 		}
71227c478bd9Sstevel@tonic-gate 		if (RunAsUid == 0)
71237c478bd9Sstevel@tonic-gate 			sff |= SFF_ROOTOK;
71247c478bd9Sstevel@tonic-gate 
71257c478bd9Sstevel@tonic-gate 		/*
71267c478bd9Sstevel@tonic-gate 		**  Check queue directory permissions.
71277c478bd9Sstevel@tonic-gate 		**	Can we write to a group writable queue directory?
71287c478bd9Sstevel@tonic-gate 		*/
71297c478bd9Sstevel@tonic-gate 
71307c478bd9Sstevel@tonic-gate 		if (bitset(S_IWGRP, QueueFileMode) &&
71317c478bd9Sstevel@tonic-gate 		    bitset(S_IWGRP, st.st_mode) &&
71327c478bd9Sstevel@tonic-gate 		    safefile(" ", RunAsUid, RunAsGid, RunAsUserName, sff,
71337c478bd9Sstevel@tonic-gate 			     QueueFileMode, NULL) != 0)
71347c478bd9Sstevel@tonic-gate 		{
71357c478bd9Sstevel@tonic-gate 			syserr("can not write to queue directory %s (RunAsGid=%d, required=%d)",
71367c478bd9Sstevel@tonic-gate 				basedir, (int) RunAsGid, (int) st.st_gid);
71377c478bd9Sstevel@tonic-gate 		}
71387c478bd9Sstevel@tonic-gate 		if (bitset(S_IWOTH|S_IXOTH, st.st_mode))
71397c478bd9Sstevel@tonic-gate 		{
71407c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
71417c478bd9Sstevel@tonic-gate 			syserr("dangerous permissions=%o on queue directory %s",
71427c478bd9Sstevel@tonic-gate 				(int) st.st_mode, basedir);
71437c478bd9Sstevel@tonic-gate #else /* _FFR_MSP_PARANOIA */
71447c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
71457c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
71467c478bd9Sstevel@tonic-gate 					  "dangerous permissions=%o on queue directory %s",
71477c478bd9Sstevel@tonic-gate 					  (int) st.st_mode, basedir);
71487c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
71497c478bd9Sstevel@tonic-gate 		}
71507c478bd9Sstevel@tonic-gate #if _FFR_MSP_PARANOIA
71517c478bd9Sstevel@tonic-gate 		if (NumQueue > 1)
71527c478bd9Sstevel@tonic-gate 			syserr("can not use multiple queues for MSP");
71537c478bd9Sstevel@tonic-gate #endif /* _FFR_MSP_PARANOIA */
71547c478bd9Sstevel@tonic-gate 	}
71557c478bd9Sstevel@tonic-gate 
71567c478bd9Sstevel@tonic-gate 	/* initial number of queue directories */
71577c478bd9Sstevel@tonic-gate 	qn = 0;
71587c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue && Queue[i] != NULL; i++)
71597c478bd9Sstevel@tonic-gate 		qn = multiqueue_cache(basedir, len, Queue[i], qn, &hashval);
71607c478bd9Sstevel@tonic-gate 
71617c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
71627c478bd9Sstevel@tonic-gate 	init_shm(qn, owner, hashval);
71637c478bd9Sstevel@tonic-gate 	i = filesys_setup(owner || ShmId == SM_SHM_NO_ID);
71647c478bd9Sstevel@tonic-gate 	if (i == FSF_NOT_FOUND)
71657c478bd9Sstevel@tonic-gate 	{
71667c478bd9Sstevel@tonic-gate 		/*
71677c478bd9Sstevel@tonic-gate 		**  We didn't get the right filesystem data
71687c478bd9Sstevel@tonic-gate 		**  This may happen if we don't have the right shared memory.
71697c478bd9Sstevel@tonic-gate 		**  So let's do this without shared memory.
71707c478bd9Sstevel@tonic-gate 		*/
71717c478bd9Sstevel@tonic-gate 
71727c478bd9Sstevel@tonic-gate 		SM_ASSERT(!owner);
71737c478bd9Sstevel@tonic-gate 		cleanup_shm(false);	/* release shared memory */
71747c478bd9Sstevel@tonic-gate 		i = filesys_setup(false);
71757c478bd9Sstevel@tonic-gate 		if (i < 0)
71767c478bd9Sstevel@tonic-gate 			syserr("filesys_setup failed twice, result=%d", i);
71777c478bd9Sstevel@tonic-gate 		else if (LogLevel > 8)
71787c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
71797c478bd9Sstevel@tonic-gate 				  "shared memory does not contain expected data, ignored");
71807c478bd9Sstevel@tonic-gate 	}
71817c478bd9Sstevel@tonic-gate #else /* SM_CONF_SHM */
71827c478bd9Sstevel@tonic-gate 	i = filesys_setup(true);
71837c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
71847c478bd9Sstevel@tonic-gate 	if (i < 0)
71857c478bd9Sstevel@tonic-gate 		ExitStat = EX_CONFIG;
71867c478bd9Sstevel@tonic-gate }
71877c478bd9Sstevel@tonic-gate 
71887c478bd9Sstevel@tonic-gate #if SM_CONF_SHM
71897c478bd9Sstevel@tonic-gate /*
71907c478bd9Sstevel@tonic-gate **  CLEANUP_SHM -- do some cleanup work for shared memory etc
71917c478bd9Sstevel@tonic-gate **
71927c478bd9Sstevel@tonic-gate **	Parameters:
71937c478bd9Sstevel@tonic-gate **		owner -- owner of shared memory?
71947c478bd9Sstevel@tonic-gate **
71957c478bd9Sstevel@tonic-gate **	Returns:
71967c478bd9Sstevel@tonic-gate **		none.
71977c478bd9Sstevel@tonic-gate **
71987c478bd9Sstevel@tonic-gate **	Side Effects:
71997c478bd9Sstevel@tonic-gate **		detaches shared memory.
72007c478bd9Sstevel@tonic-gate */
72017c478bd9Sstevel@tonic-gate 
72027c478bd9Sstevel@tonic-gate void
cleanup_shm(owner)72037c478bd9Sstevel@tonic-gate cleanup_shm(owner)
72047c478bd9Sstevel@tonic-gate 	bool owner;
72057c478bd9Sstevel@tonic-gate {
72067c478bd9Sstevel@tonic-gate 	if (ShmId != SM_SHM_NO_ID)
72077c478bd9Sstevel@tonic-gate 	{
72087c478bd9Sstevel@tonic-gate 		if (sm_shmstop(Pshm, ShmId, owner) < 0 && LogLevel > 8)
72097c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID, "sm_shmstop failed=%s",
72107c478bd9Sstevel@tonic-gate 				  sm_errstring(errno));
72117c478bd9Sstevel@tonic-gate 		Pshm = NULL;
72127c478bd9Sstevel@tonic-gate 		ShmId = SM_SHM_NO_ID;
72137c478bd9Sstevel@tonic-gate 	}
72147c478bd9Sstevel@tonic-gate 	stop_sem(owner);
72157c478bd9Sstevel@tonic-gate }
72167c478bd9Sstevel@tonic-gate #endif /* SM_CONF_SHM */
72177c478bd9Sstevel@tonic-gate 
72187c478bd9Sstevel@tonic-gate /*
72197c478bd9Sstevel@tonic-gate **  CLEANUP_QUEUES -- do some cleanup work for queues
72207c478bd9Sstevel@tonic-gate **
72217c478bd9Sstevel@tonic-gate **	Parameters:
72227c478bd9Sstevel@tonic-gate **		none.
72237c478bd9Sstevel@tonic-gate **
72247c478bd9Sstevel@tonic-gate **	Returns:
72257c478bd9Sstevel@tonic-gate **		none.
72267c478bd9Sstevel@tonic-gate **
72277c478bd9Sstevel@tonic-gate */
72287c478bd9Sstevel@tonic-gate 
72297c478bd9Sstevel@tonic-gate void
cleanup_queues()72307c478bd9Sstevel@tonic-gate cleanup_queues()
72317c478bd9Sstevel@tonic-gate {
72327c478bd9Sstevel@tonic-gate 	sync_queue_time();
72337c478bd9Sstevel@tonic-gate }
72347c478bd9Sstevel@tonic-gate /*
72357c478bd9Sstevel@tonic-gate **  SET_DEF_QUEUEVAL -- set default values for a queue group.
72367c478bd9Sstevel@tonic-gate **
72377c478bd9Sstevel@tonic-gate **	Parameters:
72387c478bd9Sstevel@tonic-gate **		qg -- queue group
72397c478bd9Sstevel@tonic-gate **		all -- set all values (true for default group)?
72407c478bd9Sstevel@tonic-gate **
72417c478bd9Sstevel@tonic-gate **	Returns:
72427c478bd9Sstevel@tonic-gate **		none.
72437c478bd9Sstevel@tonic-gate **
72447c478bd9Sstevel@tonic-gate **	Side Effects:
72457c478bd9Sstevel@tonic-gate **		sets default values for the queue group.
72467c478bd9Sstevel@tonic-gate */
72477c478bd9Sstevel@tonic-gate 
72487c478bd9Sstevel@tonic-gate void
set_def_queueval(qg,all)72497c478bd9Sstevel@tonic-gate set_def_queueval(qg, all)
72507c478bd9Sstevel@tonic-gate 	QUEUEGRP *qg;
72517c478bd9Sstevel@tonic-gate 	bool all;
72527c478bd9Sstevel@tonic-gate {
72537c478bd9Sstevel@tonic-gate 	if (bitnset(QD_DEFINED, qg->qg_flags))
72547c478bd9Sstevel@tonic-gate 		return;
72557c478bd9Sstevel@tonic-gate 	if (all)
72567c478bd9Sstevel@tonic-gate 		qg->qg_qdir = QueueDir;
72577c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_GROUP_SORTORDER
72587c478bd9Sstevel@tonic-gate 	qg->qg_sortorder = QueueSortOrder;
72597c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_GROUP_SORTORDER */
72607c478bd9Sstevel@tonic-gate 	qg->qg_maxqrun = all ? MaxRunnersPerQueue : -1;
72617c478bd9Sstevel@tonic-gate 	qg->qg_nice = NiceQueueRun;
72627c478bd9Sstevel@tonic-gate }
72637c478bd9Sstevel@tonic-gate /*
72647c478bd9Sstevel@tonic-gate **  MAKEQUEUE -- define a new queue.
72657c478bd9Sstevel@tonic-gate **
72667c478bd9Sstevel@tonic-gate **	Parameters:
72677c478bd9Sstevel@tonic-gate **		line -- description of queue.  This is in labeled fields.
72687c478bd9Sstevel@tonic-gate **			The fields are:
72697c478bd9Sstevel@tonic-gate **			   F -- the flags associated with the queue
72707c478bd9Sstevel@tonic-gate **			   I -- the interval between running the queue
72717c478bd9Sstevel@tonic-gate **			   J -- the maximum # of jobs in work list
72727c478bd9Sstevel@tonic-gate **			   [M -- the maximum # of jobs in a queue run]
72737c478bd9Sstevel@tonic-gate **			   N -- the niceness at which to run
72747c478bd9Sstevel@tonic-gate **			   P -- the path to the queue
72757c478bd9Sstevel@tonic-gate **			   S -- the queue sorting order
72767c478bd9Sstevel@tonic-gate **			   R -- number of parallel queue runners
72777c478bd9Sstevel@tonic-gate **			   r -- max recipients per envelope
72787c478bd9Sstevel@tonic-gate **			The first word is the canonical name of the queue.
72797c478bd9Sstevel@tonic-gate **		qdef -- this is a 'Q' definition from .cf
72807c478bd9Sstevel@tonic-gate **
72817c478bd9Sstevel@tonic-gate **	Returns:
72827c478bd9Sstevel@tonic-gate **		none.
72837c478bd9Sstevel@tonic-gate **
72847c478bd9Sstevel@tonic-gate **	Side Effects:
72857c478bd9Sstevel@tonic-gate **		enters the queue into the queue table.
72867c478bd9Sstevel@tonic-gate */
72877c478bd9Sstevel@tonic-gate 
72887c478bd9Sstevel@tonic-gate void
makequeue(line,qdef)72897c478bd9Sstevel@tonic-gate makequeue(line, qdef)
72907c478bd9Sstevel@tonic-gate 	char *line;
72917c478bd9Sstevel@tonic-gate 	bool qdef;
72927c478bd9Sstevel@tonic-gate {
72937c478bd9Sstevel@tonic-gate 	register char *p;
72947c478bd9Sstevel@tonic-gate 	register QUEUEGRP *qg;
72957c478bd9Sstevel@tonic-gate 	register STAB *s;
72967c478bd9Sstevel@tonic-gate 	int i;
72977c478bd9Sstevel@tonic-gate 	char fcode;
72987c478bd9Sstevel@tonic-gate 
72997c478bd9Sstevel@tonic-gate 	/* allocate a queue and set up defaults */
7300058561cbSjbeck 	qg = (QUEUEGRP *) xalloc(sizeof(*qg));
7301058561cbSjbeck 	memset((char *) qg, '\0', sizeof(*qg));
73027c478bd9Sstevel@tonic-gate 
73037c478bd9Sstevel@tonic-gate 	if (line[0] == '\0')
73047c478bd9Sstevel@tonic-gate 	{
73057c478bd9Sstevel@tonic-gate 		syserr("name required for queue");
73067c478bd9Sstevel@tonic-gate 		return;
73077c478bd9Sstevel@tonic-gate 	}
73087c478bd9Sstevel@tonic-gate 
73097c478bd9Sstevel@tonic-gate 	/* collect the queue name */
73107c478bd9Sstevel@tonic-gate 	for (p = line;
73117c478bd9Sstevel@tonic-gate 	     *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p));
73127c478bd9Sstevel@tonic-gate 	     p++)
73137c478bd9Sstevel@tonic-gate 		continue;
73147c478bd9Sstevel@tonic-gate 	if (*p != '\0')
73157c478bd9Sstevel@tonic-gate 		*p++ = '\0';
73167c478bd9Sstevel@tonic-gate 	qg->qg_name = newstr(line);
73177c478bd9Sstevel@tonic-gate 
73187c478bd9Sstevel@tonic-gate 	/* set default values, can be overridden below */
73197c478bd9Sstevel@tonic-gate 	set_def_queueval(qg, false);
73207c478bd9Sstevel@tonic-gate 
73217c478bd9Sstevel@tonic-gate 	/* now scan through and assign info from the fields */
73227c478bd9Sstevel@tonic-gate 	while (*p != '\0')
73237c478bd9Sstevel@tonic-gate 	{
73247c478bd9Sstevel@tonic-gate 		auto char *delimptr;
73257c478bd9Sstevel@tonic-gate 
73267c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
73277c478bd9Sstevel@tonic-gate 		       (*p == ',' || (isascii(*p) && isspace(*p))))
73287c478bd9Sstevel@tonic-gate 			p++;
73297c478bd9Sstevel@tonic-gate 
73307c478bd9Sstevel@tonic-gate 		/* p now points to field code */
73317c478bd9Sstevel@tonic-gate 		fcode = *p;
73327c478bd9Sstevel@tonic-gate 		while (*p != '\0' && *p != '=' && *p != ',')
73337c478bd9Sstevel@tonic-gate 			p++;
73347c478bd9Sstevel@tonic-gate 		if (*p++ != '=')
73357c478bd9Sstevel@tonic-gate 		{
73367c478bd9Sstevel@tonic-gate 			syserr("queue %s: `=' expected", qg->qg_name);
73377c478bd9Sstevel@tonic-gate 			return;
73387c478bd9Sstevel@tonic-gate 		}
73397c478bd9Sstevel@tonic-gate 		while (isascii(*p) && isspace(*p))
73407c478bd9Sstevel@tonic-gate 			p++;
73417c478bd9Sstevel@tonic-gate 
73427c478bd9Sstevel@tonic-gate 		/* p now points to the field body */
73437c478bd9Sstevel@tonic-gate 		p = munchstring(p, &delimptr, ',');
73447c478bd9Sstevel@tonic-gate 
73457c478bd9Sstevel@tonic-gate 		/* install the field into the queue struct */
73467c478bd9Sstevel@tonic-gate 		switch (fcode)
73477c478bd9Sstevel@tonic-gate 		{
73487c478bd9Sstevel@tonic-gate 		  case 'P':		/* pathname */
73497c478bd9Sstevel@tonic-gate 			if (*p == '\0')
73507c478bd9Sstevel@tonic-gate 				syserr("queue %s: empty path name",
73517c478bd9Sstevel@tonic-gate 					qg->qg_name);
73527c478bd9Sstevel@tonic-gate 			else
73537c478bd9Sstevel@tonic-gate 				qg->qg_qdir = newstr(p);
73547c478bd9Sstevel@tonic-gate 			break;
73557c478bd9Sstevel@tonic-gate 
73567c478bd9Sstevel@tonic-gate 		  case 'F':		/* flags */
73577c478bd9Sstevel@tonic-gate 			for (; *p != '\0'; p++)
73587c478bd9Sstevel@tonic-gate 				if (!(isascii(*p) && isspace(*p)))
73597c478bd9Sstevel@tonic-gate 					setbitn(*p, qg->qg_flags);
73607c478bd9Sstevel@tonic-gate 			break;
73617c478bd9Sstevel@tonic-gate 
73627c478bd9Sstevel@tonic-gate 			/*
73637c478bd9Sstevel@tonic-gate 			**  Do we need two intervals here:
73647c478bd9Sstevel@tonic-gate 			**  One for persistent queue runners,
73657c478bd9Sstevel@tonic-gate 			**  one for "normal" queue runs?
73667c478bd9Sstevel@tonic-gate 			*/
73677c478bd9Sstevel@tonic-gate 
73687c478bd9Sstevel@tonic-gate 		  case 'I':	/* interval between running the queue */
73697c478bd9Sstevel@tonic-gate 			qg->qg_queueintvl = convtime(p, 'm');
73707c478bd9Sstevel@tonic-gate 			break;
73717c478bd9Sstevel@tonic-gate 
73727c478bd9Sstevel@tonic-gate 		  case 'N':		/* run niceness */
73737c478bd9Sstevel@tonic-gate 			qg->qg_nice = atoi(p);
73747c478bd9Sstevel@tonic-gate 			break;
73757c478bd9Sstevel@tonic-gate 
73767c478bd9Sstevel@tonic-gate 		  case 'R':		/* maximum # of runners for the group */
73777c478bd9Sstevel@tonic-gate 			i = atoi(p);
73787c478bd9Sstevel@tonic-gate 
73797c478bd9Sstevel@tonic-gate 			/* can't have more runners than allowed total */
73807c478bd9Sstevel@tonic-gate 			if (MaxQueueChildren > 0 && i > MaxQueueChildren)
73817c478bd9Sstevel@tonic-gate 			{
73827c478bd9Sstevel@tonic-gate 				qg->qg_maxqrun = MaxQueueChildren;
73837c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
73847c478bd9Sstevel@tonic-gate 						     "Q=%s: R=%d exceeds MaxQueueChildren=%d, set to MaxQueueChildren\n",
73857c478bd9Sstevel@tonic-gate 						     qg->qg_name, i,
73867c478bd9Sstevel@tonic-gate 						     MaxQueueChildren);
73877c478bd9Sstevel@tonic-gate 			}
73887c478bd9Sstevel@tonic-gate 			else
73897c478bd9Sstevel@tonic-gate 				qg->qg_maxqrun = i;
73907c478bd9Sstevel@tonic-gate 			break;
73917c478bd9Sstevel@tonic-gate 
73927c478bd9Sstevel@tonic-gate 		  case 'J':		/* maximum # of jobs in work list */
73937c478bd9Sstevel@tonic-gate 			qg->qg_maxlist = atoi(p);
73947c478bd9Sstevel@tonic-gate 			break;
73957c478bd9Sstevel@tonic-gate 
73967c478bd9Sstevel@tonic-gate 		  case 'r':		/* max recipients per envelope */
73977c478bd9Sstevel@tonic-gate 			qg->qg_maxrcpt = atoi(p);
73987c478bd9Sstevel@tonic-gate 			break;
73997c478bd9Sstevel@tonic-gate 
74007c478bd9Sstevel@tonic-gate #if _FFR_QUEUE_GROUP_SORTORDER
74017c478bd9Sstevel@tonic-gate 		  case 'S':		/* queue sorting order */
74027c478bd9Sstevel@tonic-gate 			switch (*p)
74037c478bd9Sstevel@tonic-gate 			{
74047c478bd9Sstevel@tonic-gate 			  case 'h':	/* Host first */
74057c478bd9Sstevel@tonic-gate 			  case 'H':
74067c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYHOST;
74077c478bd9Sstevel@tonic-gate 				break;
74087c478bd9Sstevel@tonic-gate 
74097c478bd9Sstevel@tonic-gate 			  case 'p':	/* Priority order */
74107c478bd9Sstevel@tonic-gate 			  case 'P':
74117c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYPRIORITY;
74127c478bd9Sstevel@tonic-gate 				break;
74137c478bd9Sstevel@tonic-gate 
74147c478bd9Sstevel@tonic-gate 			  case 't':	/* Submission time */
74157c478bd9Sstevel@tonic-gate 			  case 'T':
74167c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYTIME;
74177c478bd9Sstevel@tonic-gate 				break;
74187c478bd9Sstevel@tonic-gate 
74197c478bd9Sstevel@tonic-gate 			  case 'f':	/* File name */
74207c478bd9Sstevel@tonic-gate 			  case 'F':
74217c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYFILENAME;
74227c478bd9Sstevel@tonic-gate 				break;
74237c478bd9Sstevel@tonic-gate 
74247c478bd9Sstevel@tonic-gate 			  case 'm':	/* Modification time */
74257c478bd9Sstevel@tonic-gate 			  case 'M':
74267c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYMODTIME;
74277c478bd9Sstevel@tonic-gate 				break;
74287c478bd9Sstevel@tonic-gate 
74297c478bd9Sstevel@tonic-gate 			  case 'r':	/* Random */
74307c478bd9Sstevel@tonic-gate 			  case 'R':
74317c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_RANDOM;
74327c478bd9Sstevel@tonic-gate 				break;
74337c478bd9Sstevel@tonic-gate 
74347c478bd9Sstevel@tonic-gate # if _FFR_RHS
74357c478bd9Sstevel@tonic-gate 			  case 's':	/* Shuffled host name */
74367c478bd9Sstevel@tonic-gate 			  case 'S':
74377c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_BYSHUFFLE;
74387c478bd9Sstevel@tonic-gate 				break;
74397c478bd9Sstevel@tonic-gate # endif /* _FFR_RHS */
74407c478bd9Sstevel@tonic-gate 
74417c478bd9Sstevel@tonic-gate 			  case 'n':	/* none */
74427c478bd9Sstevel@tonic-gate 			  case 'N':
74437c478bd9Sstevel@tonic-gate 				qg->qg_sortorder = QSO_NONE;
74447c478bd9Sstevel@tonic-gate 				break;
74457c478bd9Sstevel@tonic-gate 
74467c478bd9Sstevel@tonic-gate 			  default:
74477c478bd9Sstevel@tonic-gate 				syserr("Invalid queue sort order \"%s\"", p);
74487c478bd9Sstevel@tonic-gate 			}
74497c478bd9Sstevel@tonic-gate 			break;
74507c478bd9Sstevel@tonic-gate #endif /* _FFR_QUEUE_GROUP_SORTORDER */
74517c478bd9Sstevel@tonic-gate 
74527c478bd9Sstevel@tonic-gate 		  default:
74537c478bd9Sstevel@tonic-gate 			syserr("Q%s: unknown queue equate %c=",
74547c478bd9Sstevel@tonic-gate 			       qg->qg_name, fcode);
74557c478bd9Sstevel@tonic-gate 			break;
74567c478bd9Sstevel@tonic-gate 		}
74577c478bd9Sstevel@tonic-gate 
74587c478bd9Sstevel@tonic-gate 		p = delimptr;
74597c478bd9Sstevel@tonic-gate 	}
74607c478bd9Sstevel@tonic-gate 
74617c478bd9Sstevel@tonic-gate #if !HASNICE
74627c478bd9Sstevel@tonic-gate 	if (qg->qg_nice != NiceQueueRun)
74637c478bd9Sstevel@tonic-gate 	{
74647c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
74657c478bd9Sstevel@tonic-gate 				     "Q%s: Warning: N= set on system that doesn't support nice()\n",
74667c478bd9Sstevel@tonic-gate 				     qg->qg_name);
74677c478bd9Sstevel@tonic-gate 	}
74687c478bd9Sstevel@tonic-gate #endif /* !HASNICE */
74697c478bd9Sstevel@tonic-gate 
74707c478bd9Sstevel@tonic-gate 	/* do some rationality checking */
74717c478bd9Sstevel@tonic-gate 	if (NumQueue >= MAXQUEUEGROUPS)
74727c478bd9Sstevel@tonic-gate 	{
74737c478bd9Sstevel@tonic-gate 		syserr("too many queue groups defined (%d max)",
74747c478bd9Sstevel@tonic-gate 			MAXQUEUEGROUPS);
74757c478bd9Sstevel@tonic-gate 		return;
74767c478bd9Sstevel@tonic-gate 	}
74777c478bd9Sstevel@tonic-gate 
74787c478bd9Sstevel@tonic-gate 	if (qg->qg_qdir == NULL)
74797c478bd9Sstevel@tonic-gate 	{
74807c478bd9Sstevel@tonic-gate 		if (QueueDir == NULL || *QueueDir == '\0')
74817c478bd9Sstevel@tonic-gate 		{
74827c478bd9Sstevel@tonic-gate 			syserr("QueueDir must be defined before queue groups");
74837c478bd9Sstevel@tonic-gate 			return;
74847c478bd9Sstevel@tonic-gate 		}
74857c478bd9Sstevel@tonic-gate 		qg->qg_qdir = newstr(QueueDir);
74867c478bd9Sstevel@tonic-gate 	}
74877c478bd9Sstevel@tonic-gate 
74887c478bd9Sstevel@tonic-gate 	if (qg->qg_maxqrun > 1 && !bitnset(QD_FORK, qg->qg_flags))
74897c478bd9Sstevel@tonic-gate 	{
74907c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
74917c478bd9Sstevel@tonic-gate 				     "Warning: Q=%s: R=%d: multiple queue runners specified\n\tbut flag '%c' is not set\n",
74927c478bd9Sstevel@tonic-gate 				     qg->qg_name, qg->qg_maxqrun, QD_FORK);
74937c478bd9Sstevel@tonic-gate 	}
74947c478bd9Sstevel@tonic-gate 
74957c478bd9Sstevel@tonic-gate 	/* enter the queue into the symbol table */
74967c478bd9Sstevel@tonic-gate 	if (tTd(37, 8))
74977c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
74987c478bd9Sstevel@tonic-gate 			  "Adding %s to stab, path: %s", qg->qg_name,
74997c478bd9Sstevel@tonic-gate 			  qg->qg_qdir);
75007c478bd9Sstevel@tonic-gate 	s = stab(qg->qg_name, ST_QUEUE, ST_ENTER);
75017c478bd9Sstevel@tonic-gate 	if (s->s_quegrp != NULL)
75027c478bd9Sstevel@tonic-gate 	{
75037c478bd9Sstevel@tonic-gate 		i = s->s_quegrp->qg_index;
75047c478bd9Sstevel@tonic-gate 
75057c478bd9Sstevel@tonic-gate 		/* XXX what about the pointers inside this struct? */
75067c478bd9Sstevel@tonic-gate 		sm_free(s->s_quegrp); /* XXX */
75077c478bd9Sstevel@tonic-gate 	}
75087c478bd9Sstevel@tonic-gate 	else
75097c478bd9Sstevel@tonic-gate 		i = NumQueue++;
75107c478bd9Sstevel@tonic-gate 	Queue[i] = s->s_quegrp = qg;
75117c478bd9Sstevel@tonic-gate 	qg->qg_index = i;
75127c478bd9Sstevel@tonic-gate 
75137c478bd9Sstevel@tonic-gate 	/* set default value for max queue runners */
75147c478bd9Sstevel@tonic-gate 	if (qg->qg_maxqrun < 0)
75157c478bd9Sstevel@tonic-gate 	{
75167c478bd9Sstevel@tonic-gate 		if (MaxRunnersPerQueue > 0)
75177c478bd9Sstevel@tonic-gate 			qg->qg_maxqrun = MaxRunnersPerQueue;
75187c478bd9Sstevel@tonic-gate 		else
75197c478bd9Sstevel@tonic-gate 			qg->qg_maxqrun = 1;
75207c478bd9Sstevel@tonic-gate 	}
75217c478bd9Sstevel@tonic-gate 	if (qdef)
75227c478bd9Sstevel@tonic-gate 		setbitn(QD_DEFINED, qg->qg_flags);
75237c478bd9Sstevel@tonic-gate }
75247c478bd9Sstevel@tonic-gate #if 0
75257c478bd9Sstevel@tonic-gate /*
75267c478bd9Sstevel@tonic-gate **  HASHFQN -- calculate a hash value for a fully qualified host name
75277c478bd9Sstevel@tonic-gate **
75287c478bd9Sstevel@tonic-gate **	Arguments:
75297c478bd9Sstevel@tonic-gate **		fqn -- an all lower-case host.domain string
75307c478bd9Sstevel@tonic-gate **		buckets -- the number of buckets (queue directories)
75317c478bd9Sstevel@tonic-gate **
75327c478bd9Sstevel@tonic-gate **	Returns:
75337c478bd9Sstevel@tonic-gate **		a bucket number (signed integer)
75347c478bd9Sstevel@tonic-gate **		-1 on error
75357c478bd9Sstevel@tonic-gate **
75367c478bd9Sstevel@tonic-gate **	Contributed by Exactis.com, Inc.
75377c478bd9Sstevel@tonic-gate */
75387c478bd9Sstevel@tonic-gate 
75397c478bd9Sstevel@tonic-gate int
75407c478bd9Sstevel@tonic-gate hashfqn(fqn, buckets)
75417c478bd9Sstevel@tonic-gate 	register char *fqn;
75427c478bd9Sstevel@tonic-gate 	int buckets;
75437c478bd9Sstevel@tonic-gate {
75447c478bd9Sstevel@tonic-gate 	register char *p;
75457c478bd9Sstevel@tonic-gate 	register int h = 0, hash, cnt;
75467c478bd9Sstevel@tonic-gate 
75477c478bd9Sstevel@tonic-gate 	if (fqn == NULL)
75487c478bd9Sstevel@tonic-gate 		return -1;
75497c478bd9Sstevel@tonic-gate 
75507c478bd9Sstevel@tonic-gate 	/*
75517c478bd9Sstevel@tonic-gate 	**  A variation on the gdb hash
75527c478bd9Sstevel@tonic-gate 	**  This is the best as of Feb 19, 1996 --bcx
75537c478bd9Sstevel@tonic-gate 	*/
75547c478bd9Sstevel@tonic-gate 
75557c478bd9Sstevel@tonic-gate 	p = fqn;
75567c478bd9Sstevel@tonic-gate 	h = 0x238F13AF * strlen(p);
75577c478bd9Sstevel@tonic-gate 	for (cnt = 0; *p != 0; ++p, cnt++)
75587c478bd9Sstevel@tonic-gate 	{
75597c478bd9Sstevel@tonic-gate 		h = (h + (*p << (cnt * 5 % 24))) & 0x7FFFFFFF;
75607c478bd9Sstevel@tonic-gate 	}
75617c478bd9Sstevel@tonic-gate 	h = (1103515243 * h + 12345) & 0x7FFFFFFF;
75627c478bd9Sstevel@tonic-gate 	if (buckets < 2)
75637c478bd9Sstevel@tonic-gate 		hash = 0;
75647c478bd9Sstevel@tonic-gate 	else
75657c478bd9Sstevel@tonic-gate 		hash = (h % buckets);
75667c478bd9Sstevel@tonic-gate 
75677c478bd9Sstevel@tonic-gate 	return hash;
75687c478bd9Sstevel@tonic-gate }
75697c478bd9Sstevel@tonic-gate #endif /* 0 */
75707c478bd9Sstevel@tonic-gate 
75717c478bd9Sstevel@tonic-gate /*
75727c478bd9Sstevel@tonic-gate **  A structure for sorting Queue according to maxqrun without
75737c478bd9Sstevel@tonic-gate **	screwing up Queue itself.
75747c478bd9Sstevel@tonic-gate */
75757c478bd9Sstevel@tonic-gate 
75767c478bd9Sstevel@tonic-gate struct sortqgrp
75777c478bd9Sstevel@tonic-gate {
75787c478bd9Sstevel@tonic-gate 	int sg_idx;		/* original index */
75797c478bd9Sstevel@tonic-gate 	int sg_maxqrun;		/* max queue runners */
75807c478bd9Sstevel@tonic-gate };
75817c478bd9Sstevel@tonic-gate typedef struct sortqgrp	SORTQGRP_T;
75827c478bd9Sstevel@tonic-gate static int cmpidx __P((const void *, const void *));
75837c478bd9Sstevel@tonic-gate 
75847c478bd9Sstevel@tonic-gate static int
cmpidx(a,b)75857c478bd9Sstevel@tonic-gate cmpidx(a, b)
75867c478bd9Sstevel@tonic-gate 	const void *a;
75877c478bd9Sstevel@tonic-gate 	const void *b;
75887c478bd9Sstevel@tonic-gate {
75897c478bd9Sstevel@tonic-gate 	/* The sort is highest to lowest, so the comparison is reversed */
75907c478bd9Sstevel@tonic-gate 	if (((SORTQGRP_T *)a)->sg_maxqrun < ((SORTQGRP_T *)b)->sg_maxqrun)
75917c478bd9Sstevel@tonic-gate 		return 1;
75927c478bd9Sstevel@tonic-gate 	else if (((SORTQGRP_T *)a)->sg_maxqrun > ((SORTQGRP_T *)b)->sg_maxqrun)
75937c478bd9Sstevel@tonic-gate 		return -1;
75947c478bd9Sstevel@tonic-gate 	else
75957c478bd9Sstevel@tonic-gate 		return 0;
75967c478bd9Sstevel@tonic-gate }
75977c478bd9Sstevel@tonic-gate 
75987c478bd9Sstevel@tonic-gate /*
75997c478bd9Sstevel@tonic-gate **  MAKEWORKGROUP -- balance queue groups into work groups per MaxQueueChildren
76007c478bd9Sstevel@tonic-gate **
76017c478bd9Sstevel@tonic-gate **  Take the now defined queue groups and assign them to work groups.
76027c478bd9Sstevel@tonic-gate **  This is done to balance out the number of concurrently active
76037c478bd9Sstevel@tonic-gate **  queue runners such that MaxQueueChildren is not exceeded. This may
76047c478bd9Sstevel@tonic-gate **  result in more than one queue group per work group. In such a case
76057c478bd9Sstevel@tonic-gate **  the number of running queue groups in that work group will have no
76067c478bd9Sstevel@tonic-gate **  more than the work group maximum number of runners (a "fair" portion
76077c478bd9Sstevel@tonic-gate **  of MaxQueueRunners). All queue groups within a work group will get a
76087c478bd9Sstevel@tonic-gate **  chance at running.
76097c478bd9Sstevel@tonic-gate **
76107c478bd9Sstevel@tonic-gate **	Parameters:
76117c478bd9Sstevel@tonic-gate **		none.
76127c478bd9Sstevel@tonic-gate **
76137c478bd9Sstevel@tonic-gate **	Returns:
76147c478bd9Sstevel@tonic-gate **		nothing.
76157c478bd9Sstevel@tonic-gate **
76167c478bd9Sstevel@tonic-gate **	Side Effects:
76177c478bd9Sstevel@tonic-gate **		Sets up WorkGrp structure.
76187c478bd9Sstevel@tonic-gate */
76197c478bd9Sstevel@tonic-gate 
76207c478bd9Sstevel@tonic-gate void
makeworkgroups()76217c478bd9Sstevel@tonic-gate makeworkgroups()
76227c478bd9Sstevel@tonic-gate {
76237c478bd9Sstevel@tonic-gate 	int i, j, total_runners, dir, h;
76247c478bd9Sstevel@tonic-gate 	SORTQGRP_T si[MAXQUEUEGROUPS + 1];
76257c478bd9Sstevel@tonic-gate 
76267c478bd9Sstevel@tonic-gate 	total_runners = 0;
76277c478bd9Sstevel@tonic-gate 	if (NumQueue == 1 && strcmp(Queue[0]->qg_name, "mqueue") == 0)
76287c478bd9Sstevel@tonic-gate 	{
76297c478bd9Sstevel@tonic-gate 		/*
76307c478bd9Sstevel@tonic-gate 		**  There is only the "mqueue" queue group (a default)
76317c478bd9Sstevel@tonic-gate 		**  containing all of the queues. We want to provide to
76327c478bd9Sstevel@tonic-gate 		**  this queue group the maximum allowable queue runners.
76337c478bd9Sstevel@tonic-gate 		**  To match older behavior (8.10/8.11) we'll try for
76347c478bd9Sstevel@tonic-gate 		**  1 runner per queue capping it at MaxQueueChildren.
76357c478bd9Sstevel@tonic-gate 		**  So if there are N queues, then there will be N runners
76367c478bd9Sstevel@tonic-gate 		**  for the "mqueue" queue group (where N is kept less than
76377c478bd9Sstevel@tonic-gate 		**  MaxQueueChildren).
76387c478bd9Sstevel@tonic-gate 		*/
76397c478bd9Sstevel@tonic-gate 
76407c478bd9Sstevel@tonic-gate 		NumWorkGroups = 1;
76417c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_numqgrp = 1;
76427c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_qgs = (QUEUEGRP **) xalloc(sizeof(QUEUEGRP *));
76437c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_qgs[0] = Queue[0];
76447c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
76457c478bd9Sstevel@tonic-gate 		    Queue[0]->qg_numqueues > MaxQueueChildren)
76467c478bd9Sstevel@tonic-gate 			WorkGrp[0].wg_runners = MaxQueueChildren;
76477c478bd9Sstevel@tonic-gate 		else
76487c478bd9Sstevel@tonic-gate 			WorkGrp[0].wg_runners = Queue[0]->qg_numqueues;
76497c478bd9Sstevel@tonic-gate 
76507c478bd9Sstevel@tonic-gate 		Queue[0]->qg_wgrp = 0;
76517c478bd9Sstevel@tonic-gate 
76527c478bd9Sstevel@tonic-gate 		/* can't have more runners than allowed total */
76537c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren > 0 &&
76547c478bd9Sstevel@tonic-gate 		    Queue[0]->qg_maxqrun > MaxQueueChildren)
76557c478bd9Sstevel@tonic-gate 			Queue[0]->qg_maxqrun = MaxQueueChildren;
76567c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_maxact = Queue[0]->qg_maxqrun;
76577c478bd9Sstevel@tonic-gate 		WorkGrp[0].wg_lowqintvl = Queue[0]->qg_queueintvl;
76587c478bd9Sstevel@tonic-gate 		return;
76597c478bd9Sstevel@tonic-gate 	}
76607c478bd9Sstevel@tonic-gate 
76617c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76627c478bd9Sstevel@tonic-gate 	{
76637c478bd9Sstevel@tonic-gate 		si[i].sg_maxqrun = Queue[i]->qg_maxqrun;
76647c478bd9Sstevel@tonic-gate 		si[i].sg_idx = i;
76657c478bd9Sstevel@tonic-gate 	}
76667c478bd9Sstevel@tonic-gate 	qsort(si, NumQueue, sizeof(si[0]), cmpidx);
76677c478bd9Sstevel@tonic-gate 
76687c478bd9Sstevel@tonic-gate 	NumWorkGroups = 0;
76697c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76707c478bd9Sstevel@tonic-gate 	{
76717c478bd9Sstevel@tonic-gate 		total_runners += si[i].sg_maxqrun;
76727c478bd9Sstevel@tonic-gate 		if (MaxQueueChildren <= 0 || total_runners <= MaxQueueChildren)
76737c478bd9Sstevel@tonic-gate 			NumWorkGroups++;
76747c478bd9Sstevel@tonic-gate 		else
76757c478bd9Sstevel@tonic-gate 			break;
76767c478bd9Sstevel@tonic-gate 	}
76777c478bd9Sstevel@tonic-gate 
76787c478bd9Sstevel@tonic-gate 	if (NumWorkGroups < 1)
76797c478bd9Sstevel@tonic-gate 		NumWorkGroups = 1; /* gotta have one at least */
76807c478bd9Sstevel@tonic-gate 	else if (NumWorkGroups > MAXWORKGROUPS)
76817c478bd9Sstevel@tonic-gate 		NumWorkGroups = MAXWORKGROUPS; /* the limit */
76827c478bd9Sstevel@tonic-gate 
76837c478bd9Sstevel@tonic-gate 	/*
76847c478bd9Sstevel@tonic-gate 	**  We now know the number of work groups to pack the queue groups
76857c478bd9Sstevel@tonic-gate 	**  into. The queue groups in 'Queue' are sorted from highest
76867c478bd9Sstevel@tonic-gate 	**  to lowest for the number of runners per queue group.
76877c478bd9Sstevel@tonic-gate 	**  We put the queue groups with the largest number of runners
76887c478bd9Sstevel@tonic-gate 	**  into work groups first. Then the smaller ones are fitted in
76897c478bd9Sstevel@tonic-gate 	**  where it looks best.
76907c478bd9Sstevel@tonic-gate 	*/
76917c478bd9Sstevel@tonic-gate 
76927c478bd9Sstevel@tonic-gate 	j = 0;
76937c478bd9Sstevel@tonic-gate 	dir = 1;
76947c478bd9Sstevel@tonic-gate 	for (i = 0; i < NumQueue; i++)
76957c478bd9Sstevel@tonic-gate 	{
76967c478bd9Sstevel@tonic-gate 		/* a to-and-fro packing scheme, continue from last position */
76977c478bd9Sstevel@tonic-gate 		if (j >= NumWorkGroups)
76987c478bd9Sstevel@tonic-gate 		{
76997c478bd9Sstevel@tonic-gate 			dir = -1;
77007c478bd9Sstevel@tonic-gate 			j = NumWorkGroups - 1;
77017c478bd9Sstevel@tonic-gate 		}
77027c478bd9Sstevel@tonic-gate 		else if (j < 0)
77037c478bd9Sstevel@tonic-gate 		{
77047c478bd9Sstevel@tonic-gate 			j = 0;
77057c478bd9Sstevel@tonic-gate 			dir = 1;
77067c478bd9Sstevel@tonic-gate 		}
77077c478bd9Sstevel@tonic-gate 
77087c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_qgs == NULL)
77097c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_malloc(sizeof(QUEUEGRP *) *
77107c478bd9Sstevel@tonic-gate 							(WorkGrp[j].wg_numqgrp + 1));
77117c478bd9Sstevel@tonic-gate 		else
77127c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_qgs = (QUEUEGRP **)sm_realloc(WorkGrp[j].wg_qgs,
77137c478bd9Sstevel@tonic-gate 							sizeof(QUEUEGRP *) *
77147c478bd9Sstevel@tonic-gate 							(WorkGrp[j].wg_numqgrp + 1));
77157c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_qgs == NULL)
77167c478bd9Sstevel@tonic-gate 		{
77177c478bd9Sstevel@tonic-gate 			syserr("!cannot allocate memory for work queues, need %d bytes",
77187c478bd9Sstevel@tonic-gate 			       (int) (sizeof(QUEUEGRP *) *
77197c478bd9Sstevel@tonic-gate 				      (WorkGrp[j].wg_numqgrp + 1)));
77207c478bd9Sstevel@tonic-gate 		}
77217c478bd9Sstevel@tonic-gate 
77227c478bd9Sstevel@tonic-gate 		h = si[i].sg_idx;
77237c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_qgs[WorkGrp[j].wg_numqgrp] = Queue[h];
77247c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_numqgrp++;
77257c478bd9Sstevel@tonic-gate 		WorkGrp[j].wg_runners += Queue[h]->qg_maxqrun;
77267c478bd9Sstevel@tonic-gate 		Queue[h]->qg_wgrp = j;
77277c478bd9Sstevel@tonic-gate 
77287c478bd9Sstevel@tonic-gate 		if (WorkGrp[j].wg_maxact == 0)
77297c478bd9Sstevel@tonic-gate 		{
77307c478bd9Sstevel@tonic-gate 			/* can't have more runners than allowed total */
77317c478bd9Sstevel@tonic-gate 			if (MaxQueueChildren > 0 &&
77327c478bd9Sstevel@tonic-gate 			    Queue[h]->qg_maxqrun > MaxQueueChildren)
77337c478bd9Sstevel@tonic-gate 				Queue[h]->qg_maxqrun = MaxQueueChildren;
77347c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_maxact = Queue[h]->qg_maxqrun;
77357c478bd9Sstevel@tonic-gate 		}
77367c478bd9Sstevel@tonic-gate 
77377c478bd9Sstevel@tonic-gate 		/*
77387c478bd9Sstevel@tonic-gate 		**  XXX: must wg_lowqintvl be the GCD?
77397c478bd9Sstevel@tonic-gate 		**  qg1: 2m, qg2: 3m, minimum: 2m, when do queue runs for
77407c478bd9Sstevel@tonic-gate 		**  qg2 occur?
77417c478bd9Sstevel@tonic-gate 		*/
77427c478bd9Sstevel@tonic-gate 
77437c478bd9Sstevel@tonic-gate 		/* keep track of the lowest interval for a persistent runner */
77447c478bd9Sstevel@tonic-gate 		if (Queue[h]->qg_queueintvl > 0 &&
77457c478bd9Sstevel@tonic-gate 		    WorkGrp[j].wg_lowqintvl < Queue[h]->qg_queueintvl)
77467c478bd9Sstevel@tonic-gate 			WorkGrp[j].wg_lowqintvl = Queue[h]->qg_queueintvl;
77477c478bd9Sstevel@tonic-gate 		j += dir;
77487c478bd9Sstevel@tonic-gate 	}
77497c478bd9Sstevel@tonic-gate 	if (tTd(41, 9))
77507c478bd9Sstevel@tonic-gate 	{
77517c478bd9Sstevel@tonic-gate 		for (i = 0; i < NumWorkGroups; i++)
77527c478bd9Sstevel@tonic-gate 		{
77537c478bd9Sstevel@tonic-gate 			sm_dprintf("Workgroup[%d]=", i);
77547c478bd9Sstevel@tonic-gate 			for (j = 0; j < WorkGrp[i].wg_numqgrp; j++)
77557c478bd9Sstevel@tonic-gate 			{
77567c478bd9Sstevel@tonic-gate 				sm_dprintf("%s, ",
77577c478bd9Sstevel@tonic-gate 					WorkGrp[i].wg_qgs[j]->qg_name);
77587c478bd9Sstevel@tonic-gate 			}
77597c478bd9Sstevel@tonic-gate 			sm_dprintf("\n");
77607c478bd9Sstevel@tonic-gate 		}
77617c478bd9Sstevel@tonic-gate 	}
77627c478bd9Sstevel@tonic-gate }
77637c478bd9Sstevel@tonic-gate 
77647c478bd9Sstevel@tonic-gate /*
77657c478bd9Sstevel@tonic-gate **  DUP_DF -- duplicate envelope data file
77667c478bd9Sstevel@tonic-gate **
77677c478bd9Sstevel@tonic-gate **	Copy the data file from the 'old' envelope to the 'new' envelope
77687c478bd9Sstevel@tonic-gate **	in the most efficient way possible.
77697c478bd9Sstevel@tonic-gate **
77707c478bd9Sstevel@tonic-gate **	Create a hard link from the 'old' data file to the 'new' data file.
77717c478bd9Sstevel@tonic-gate **	If the old and new queue directories are on different file systems,
77727c478bd9Sstevel@tonic-gate **	then the new data file link is created in the old queue directory,
77737c478bd9Sstevel@tonic-gate **	and the new queue file will contain a 'd' record pointing to the
77747c478bd9Sstevel@tonic-gate **	directory containing the new data file.
77757c478bd9Sstevel@tonic-gate **
77767c478bd9Sstevel@tonic-gate **	Parameters:
77777c478bd9Sstevel@tonic-gate **		old -- old envelope.
77787c478bd9Sstevel@tonic-gate **		new -- new envelope.
77797c478bd9Sstevel@tonic-gate **
77807c478bd9Sstevel@tonic-gate **	Results:
77817c478bd9Sstevel@tonic-gate **		Returns true on success, false on failure.
77827c478bd9Sstevel@tonic-gate **
77837c478bd9Sstevel@tonic-gate **	Side Effects:
77847c478bd9Sstevel@tonic-gate **		On success, the new data file is created.
77857c478bd9Sstevel@tonic-gate **		On fatal failure, EF_FATALERRS is set in old->e_flags.
77867c478bd9Sstevel@tonic-gate */
77877c478bd9Sstevel@tonic-gate 
77887c478bd9Sstevel@tonic-gate static bool	dup_df __P((ENVELOPE *, ENVELOPE *));
77897c478bd9Sstevel@tonic-gate 
77907c478bd9Sstevel@tonic-gate static bool
dup_df(old,new)77917c478bd9Sstevel@tonic-gate dup_df(old, new)
77927c478bd9Sstevel@tonic-gate 	ENVELOPE *old;
77937c478bd9Sstevel@tonic-gate 	ENVELOPE *new;
77947c478bd9Sstevel@tonic-gate {
77957c478bd9Sstevel@tonic-gate 	int ofs, nfs, r;
77967c478bd9Sstevel@tonic-gate 	char opath[MAXPATHLEN];
77977c478bd9Sstevel@tonic-gate 	char npath[MAXPATHLEN];
77987c478bd9Sstevel@tonic-gate 
77997c478bd9Sstevel@tonic-gate 	if (!bitset(EF_HAS_DF, old->e_flags))
78007c478bd9Sstevel@tonic-gate 	{
78017c478bd9Sstevel@tonic-gate 		/*
78027c478bd9Sstevel@tonic-gate 		**  this can happen if: SuperSafe != True
78037c478bd9Sstevel@tonic-gate 		**  and a bounce mail is sent that is split.
78047c478bd9Sstevel@tonic-gate 		*/
78057c478bd9Sstevel@tonic-gate 
78067c478bd9Sstevel@tonic-gate 		queueup(old, false, true);
78077c478bd9Sstevel@tonic-gate 	}
78087c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(old->e_qgrp) && ISVALIDQDIR(old->e_qdir));
78097c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(new->e_qgrp) && ISVALIDQDIR(new->e_qdir));
78107c478bd9Sstevel@tonic-gate 
7811058561cbSjbeck 	(void) sm_strlcpy(opath, queuename(old, DATAFL_LETTER), sizeof(opath));
7812058561cbSjbeck 	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
78137c478bd9Sstevel@tonic-gate 
78147c478bd9Sstevel@tonic-gate 	if (old->e_dfp != NULL)
78157c478bd9Sstevel@tonic-gate 	{
78167c478bd9Sstevel@tonic-gate 		r = sm_io_setinfo(old->e_dfp, SM_BF_COMMIT, NULL);
78177c478bd9Sstevel@tonic-gate 		if (r < 0 && errno != EINVAL)
78187c478bd9Sstevel@tonic-gate 		{
78197c478bd9Sstevel@tonic-gate 			syserr("@can't commit %s", opath);
78207c478bd9Sstevel@tonic-gate 			old->e_flags |= EF_FATALERRS;
78217c478bd9Sstevel@tonic-gate 			return false;
78227c478bd9Sstevel@tonic-gate 		}
78237c478bd9Sstevel@tonic-gate 	}
78247c478bd9Sstevel@tonic-gate 
78257c478bd9Sstevel@tonic-gate 	/*
78267c478bd9Sstevel@tonic-gate 	**  Attempt to create a hard link, if we think both old and new
78277c478bd9Sstevel@tonic-gate 	**  are on the same file system, otherwise copy the file.
78287c478bd9Sstevel@tonic-gate 	**
78297c478bd9Sstevel@tonic-gate 	**  Don't waste time attempting a hard link unless old and new
78307c478bd9Sstevel@tonic-gate 	**  are on the same file system.
78317c478bd9Sstevel@tonic-gate 	*/
78327c478bd9Sstevel@tonic-gate 
783349218d4fSjbeck 	SM_REQUIRE(ISVALIDQGRP(old->e_dfqgrp) && ISVALIDQDIR(old->e_dfqdir));
783449218d4fSjbeck 	SM_REQUIRE(ISVALIDQGRP(new->e_dfqgrp) && ISVALIDQDIR(new->e_dfqdir));
783549218d4fSjbeck 
783649218d4fSjbeck 	ofs = Queue[old->e_dfqgrp]->qg_qpaths[old->e_dfqdir].qp_fsysidx;
783749218d4fSjbeck 	nfs = Queue[new->e_dfqgrp]->qg_qpaths[new->e_dfqdir].qp_fsysidx;
78387c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(ofs) == FILE_SYS_DEV(nfs))
78397c478bd9Sstevel@tonic-gate 	{
78407c478bd9Sstevel@tonic-gate 		if (link(opath, npath) == 0)
78417c478bd9Sstevel@tonic-gate 		{
78427c478bd9Sstevel@tonic-gate 			new->e_flags |= EF_HAS_DF;
78437c478bd9Sstevel@tonic-gate 			SYNC_DIR(npath, true);
78447c478bd9Sstevel@tonic-gate 			return true;
78457c478bd9Sstevel@tonic-gate 		}
78467c478bd9Sstevel@tonic-gate 		goto error;
78477c478bd9Sstevel@tonic-gate 	}
78487c478bd9Sstevel@tonic-gate 
78497c478bd9Sstevel@tonic-gate 	/*
78507c478bd9Sstevel@tonic-gate 	**  Can't link across queue directories, so try to create a hard
78517c478bd9Sstevel@tonic-gate 	**  link in the same queue directory as the old df file.
78527c478bd9Sstevel@tonic-gate 	**  The qf file will refer to the new df file using a 'd' record.
78537c478bd9Sstevel@tonic-gate 	*/
78547c478bd9Sstevel@tonic-gate 
78557c478bd9Sstevel@tonic-gate 	new->e_dfqgrp = old->e_dfqgrp;
78567c478bd9Sstevel@tonic-gate 	new->e_dfqdir = old->e_dfqdir;
7857058561cbSjbeck 	(void) sm_strlcpy(npath, queuename(new, DATAFL_LETTER), sizeof(npath));
78587c478bd9Sstevel@tonic-gate 	if (link(opath, npath) == 0)
78597c478bd9Sstevel@tonic-gate 	{
78607c478bd9Sstevel@tonic-gate 		new->e_flags |= EF_HAS_DF;
78617c478bd9Sstevel@tonic-gate 		SYNC_DIR(npath, true);
78627c478bd9Sstevel@tonic-gate 		return true;
78637c478bd9Sstevel@tonic-gate 	}
78647c478bd9Sstevel@tonic-gate 
78657c478bd9Sstevel@tonic-gate   error:
78667c478bd9Sstevel@tonic-gate 	if (LogLevel > 0)
78677c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, old->e_id,
78687c478bd9Sstevel@tonic-gate 			  "dup_df: can't link %s to %s, error=%s, envelope splitting failed",
78697c478bd9Sstevel@tonic-gate 			  opath, npath, sm_errstring(errno));
78707c478bd9Sstevel@tonic-gate 	return false;
78717c478bd9Sstevel@tonic-gate }
78727c478bd9Sstevel@tonic-gate 
78737c478bd9Sstevel@tonic-gate /*
78747c478bd9Sstevel@tonic-gate **  SPLIT_ENV -- Allocate a new envelope based on a given envelope.
78757c478bd9Sstevel@tonic-gate **
78767c478bd9Sstevel@tonic-gate **	Parameters:
78777c478bd9Sstevel@tonic-gate **		e -- envelope.
78787c478bd9Sstevel@tonic-gate **		sendqueue -- sendqueue for new envelope.
78797c478bd9Sstevel@tonic-gate **		qgrp -- index of queue group.
78807c478bd9Sstevel@tonic-gate **		qdir -- queue directory.
78817c478bd9Sstevel@tonic-gate **
78827c478bd9Sstevel@tonic-gate **	Results:
78837c478bd9Sstevel@tonic-gate **		new envelope.
78847c478bd9Sstevel@tonic-gate **
78857c478bd9Sstevel@tonic-gate */
78867c478bd9Sstevel@tonic-gate 
78877c478bd9Sstevel@tonic-gate static ENVELOPE	*split_env __P((ENVELOPE *, ADDRESS *, int, int));
78887c478bd9Sstevel@tonic-gate 
78897c478bd9Sstevel@tonic-gate static ENVELOPE *
split_env(e,sendqueue,qgrp,qdir)78907c478bd9Sstevel@tonic-gate split_env(e, sendqueue, qgrp, qdir)
78917c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
78927c478bd9Sstevel@tonic-gate 	ADDRESS *sendqueue;
78937c478bd9Sstevel@tonic-gate 	int qgrp;
78947c478bd9Sstevel@tonic-gate 	int qdir;
78957c478bd9Sstevel@tonic-gate {
78967c478bd9Sstevel@tonic-gate 	ENVELOPE *ee;
78977c478bd9Sstevel@tonic-gate 
7898058561cbSjbeck 	ee = (ENVELOPE *) sm_rpool_malloc_x(e->e_rpool, sizeof(*ee));
78997c478bd9Sstevel@tonic-gate 	STRUCTCOPY(*e, *ee);
79007c478bd9Sstevel@tonic-gate 	ee->e_message = NULL;	/* XXX use original message? */
79017c478bd9Sstevel@tonic-gate 	ee->e_id = NULL;
79027c478bd9Sstevel@tonic-gate 	assign_queueid(ee);
79037c478bd9Sstevel@tonic-gate 	ee->e_sendqueue = sendqueue;
79047c478bd9Sstevel@tonic-gate 	ee->e_flags &= ~(EF_INQUEUE|EF_CLRQUEUE|EF_FATALERRS
79057c478bd9Sstevel@tonic-gate 			 |EF_SENDRECEIPT|EF_RET_PARAM|EF_HAS_DF);
79067c478bd9Sstevel@tonic-gate 	ee->e_flags |= EF_NORECEIPT;	/* XXX really? */
79077c478bd9Sstevel@tonic-gate 	ee->e_from.q_state = QS_SENDER;
79087c478bd9Sstevel@tonic-gate 	ee->e_dfp = NULL;
79097c478bd9Sstevel@tonic-gate 	ee->e_lockfp = NULL;
79107c478bd9Sstevel@tonic-gate 	if (e->e_xfp != NULL)
79117c478bd9Sstevel@tonic-gate 		ee->e_xfp = sm_io_dup(e->e_xfp);
79127c478bd9Sstevel@tonic-gate 
79137c478bd9Sstevel@tonic-gate 	/* failed to dup e->e_xfp, start a new transcript */
79147c478bd9Sstevel@tonic-gate 	if (ee->e_xfp == NULL)
79157c478bd9Sstevel@tonic-gate 		openxscript(ee);
79167c478bd9Sstevel@tonic-gate 
79177c478bd9Sstevel@tonic-gate 	ee->e_qgrp = ee->e_dfqgrp = qgrp;
79187c478bd9Sstevel@tonic-gate 	ee->e_qdir = ee->e_dfqdir = qdir;
79197c478bd9Sstevel@tonic-gate 	ee->e_errormode = EM_MAIL;
79207c478bd9Sstevel@tonic-gate 	ee->e_statmsg = NULL;
79217c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg != NULL)
79227c478bd9Sstevel@tonic-gate 		ee->e_quarmsg = sm_rpool_strdup_x(ee->e_rpool,
79237c478bd9Sstevel@tonic-gate 						  e->e_quarmsg);
79247c478bd9Sstevel@tonic-gate 
79257c478bd9Sstevel@tonic-gate 	/*
79267c478bd9Sstevel@tonic-gate 	**  XXX Not sure if this copying is necessary.
79277c478bd9Sstevel@tonic-gate 	**  sendall() does this copying, but I (dm) don't know if that is
79287c478bd9Sstevel@tonic-gate 	**  because of the storage management discipline we were using
79297c478bd9Sstevel@tonic-gate 	**  before rpools were introduced, or if it is because these lists
79307c478bd9Sstevel@tonic-gate 	**  can be modified later.
79317c478bd9Sstevel@tonic-gate 	*/
79327c478bd9Sstevel@tonic-gate 
79337c478bd9Sstevel@tonic-gate 	ee->e_header = copyheader(e->e_header, ee->e_rpool);
79347c478bd9Sstevel@tonic-gate 	ee->e_errorqueue = copyqueue(e->e_errorqueue, ee->e_rpool);
79357c478bd9Sstevel@tonic-gate 
79367c478bd9Sstevel@tonic-gate 	return ee;
79377c478bd9Sstevel@tonic-gate }
79387c478bd9Sstevel@tonic-gate 
79397c478bd9Sstevel@tonic-gate /* return values from split functions, check also below! */
79407c478bd9Sstevel@tonic-gate #define SM_SPLIT_FAIL	(0)
79417c478bd9Sstevel@tonic-gate #define SM_SPLIT_NONE	(1)
79427c478bd9Sstevel@tonic-gate #define SM_SPLIT_NEW(n)	(1 + (n))
79437c478bd9Sstevel@tonic-gate 
79447c478bd9Sstevel@tonic-gate /*
79457c478bd9Sstevel@tonic-gate **  SPLIT_ACROSS_QUEUE_GROUPS
79467c478bd9Sstevel@tonic-gate **
79477c478bd9Sstevel@tonic-gate **	This function splits an envelope across multiple queue groups
79487c478bd9Sstevel@tonic-gate **	based on the queue group of each recipient.
79497c478bd9Sstevel@tonic-gate **
79507c478bd9Sstevel@tonic-gate **	Parameters:
79517c478bd9Sstevel@tonic-gate **		e -- envelope.
79527c478bd9Sstevel@tonic-gate **
79537c478bd9Sstevel@tonic-gate **	Results:
79547c478bd9Sstevel@tonic-gate **		SM_SPLIT_FAIL on failure
79557c478bd9Sstevel@tonic-gate **		SM_SPLIT_NONE if no splitting occurred,
79567c478bd9Sstevel@tonic-gate **		or 1 + the number of additional envelopes created.
79577c478bd9Sstevel@tonic-gate **
79587c478bd9Sstevel@tonic-gate **	Side Effects:
79597c478bd9Sstevel@tonic-gate **		On success, e->e_sibling points to a list of zero or more
79607c478bd9Sstevel@tonic-gate **		additional envelopes, and the associated data files exist
79617c478bd9Sstevel@tonic-gate **		on disk.  But the queue files are not created.
79627c478bd9Sstevel@tonic-gate **
79637c478bd9Sstevel@tonic-gate **		On failure, e->e_sibling is not changed.
79647c478bd9Sstevel@tonic-gate **		The order of recipients in e->e_sendqueue is permuted.
79657c478bd9Sstevel@tonic-gate **		Abandoned data files for additional envelopes that failed
79667c478bd9Sstevel@tonic-gate **		to be created may exist on disk.
79677c478bd9Sstevel@tonic-gate */
79687c478bd9Sstevel@tonic-gate 
79697c478bd9Sstevel@tonic-gate static int	q_qgrp_compare __P((const void *, const void *));
79707c478bd9Sstevel@tonic-gate static int	e_filesys_compare __P((const void *, const void *));
79717c478bd9Sstevel@tonic-gate 
79727c478bd9Sstevel@tonic-gate static int
q_qgrp_compare(p1,p2)79737c478bd9Sstevel@tonic-gate q_qgrp_compare(p1, p2)
79747c478bd9Sstevel@tonic-gate 	const void *p1;
79757c478bd9Sstevel@tonic-gate 	const void *p2;
79767c478bd9Sstevel@tonic-gate {
79777c478bd9Sstevel@tonic-gate 	ADDRESS **pq1 = (ADDRESS **) p1;
79787c478bd9Sstevel@tonic-gate 	ADDRESS **pq2 = (ADDRESS **) p2;
79797c478bd9Sstevel@tonic-gate 
79807c478bd9Sstevel@tonic-gate 	return (*pq1)->q_qgrp - (*pq2)->q_qgrp;
79817c478bd9Sstevel@tonic-gate }
79827c478bd9Sstevel@tonic-gate 
79837c478bd9Sstevel@tonic-gate static int
e_filesys_compare(p1,p2)79847c478bd9Sstevel@tonic-gate e_filesys_compare(p1, p2)
79857c478bd9Sstevel@tonic-gate 	const void *p1;
79867c478bd9Sstevel@tonic-gate 	const void *p2;
79877c478bd9Sstevel@tonic-gate {
79887c478bd9Sstevel@tonic-gate 	ENVELOPE **pe1 = (ENVELOPE **) p1;
79897c478bd9Sstevel@tonic-gate 	ENVELOPE **pe2 = (ENVELOPE **) p2;
79907c478bd9Sstevel@tonic-gate 	int fs1, fs2;
79917c478bd9Sstevel@tonic-gate 
79927c478bd9Sstevel@tonic-gate 	fs1 = Queue[(*pe1)->e_qgrp]->qg_qpaths[(*pe1)->e_qdir].qp_fsysidx;
79937c478bd9Sstevel@tonic-gate 	fs2 = Queue[(*pe2)->e_qgrp]->qg_qpaths[(*pe2)->e_qdir].qp_fsysidx;
79947c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(fs1) < FILE_SYS_DEV(fs2))
79957c478bd9Sstevel@tonic-gate 		return -1;
79967c478bd9Sstevel@tonic-gate 	if (FILE_SYS_DEV(fs1) > FILE_SYS_DEV(fs2))
79977c478bd9Sstevel@tonic-gate 		return 1;
79987c478bd9Sstevel@tonic-gate 	return 0;
79997c478bd9Sstevel@tonic-gate }
80007c478bd9Sstevel@tonic-gate 
8001058561cbSjbeck static int split_across_queue_groups __P((ENVELOPE *));
80027c478bd9Sstevel@tonic-gate static int
split_across_queue_groups(e)80037c478bd9Sstevel@tonic-gate split_across_queue_groups(e)
80047c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
80057c478bd9Sstevel@tonic-gate {
80067c478bd9Sstevel@tonic-gate 	int naddrs, nsplits, i;
80077c478bd9Sstevel@tonic-gate 	bool changed;
80087c478bd9Sstevel@tonic-gate 	char **pvp;
80097c478bd9Sstevel@tonic-gate 	ADDRESS *q, **addrs;
80107c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *es;
80117c478bd9Sstevel@tonic-gate 	ENVELOPE *splits[MAXQUEUEGROUPS];
80127c478bd9Sstevel@tonic-gate 	char pvpbuf[PSBUFSIZE];
80137c478bd9Sstevel@tonic-gate 
80147c478bd9Sstevel@tonic-gate 	SM_REQUIRE(ISVALIDQGRP(e->e_qgrp));
80157c478bd9Sstevel@tonic-gate 
80167c478bd9Sstevel@tonic-gate 	/* Count addresses and assign queue groups. */
80177c478bd9Sstevel@tonic-gate 	naddrs = 0;
80187c478bd9Sstevel@tonic-gate 	changed = false;
80197c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
80207c478bd9Sstevel@tonic-gate 	{
80217c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
80227c478bd9Sstevel@tonic-gate 			continue;
80237c478bd9Sstevel@tonic-gate 		++naddrs;
80247c478bd9Sstevel@tonic-gate 
80257c478bd9Sstevel@tonic-gate 		/* bad addresses and those already sent stay put */
80267c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(q->q_state) ||
80277c478bd9Sstevel@tonic-gate 		    QS_IS_SENT(q->q_state))
80287c478bd9Sstevel@tonic-gate 			q->q_qgrp = e->e_qgrp;
80297c478bd9Sstevel@tonic-gate 		else if (!ISVALIDQGRP(q->q_qgrp))
80307c478bd9Sstevel@tonic-gate 		{
80317c478bd9Sstevel@tonic-gate 			/* call ruleset which should return a queue group */
80327c478bd9Sstevel@tonic-gate 			i = rscap(RS_QUEUEGROUP, q->q_user, NULL, e, &pvp,
80337c478bd9Sstevel@tonic-gate 				  pvpbuf, sizeof(pvpbuf));
80347c478bd9Sstevel@tonic-gate 			if (i == EX_OK &&
80357c478bd9Sstevel@tonic-gate 			    pvp != NULL && pvp[0] != NULL &&
80367c478bd9Sstevel@tonic-gate 			    (pvp[0][0] & 0377) == CANONNET &&
80377c478bd9Sstevel@tonic-gate 			    pvp[1] != NULL && pvp[1][0] != '\0')
80387c478bd9Sstevel@tonic-gate 			{
80397c478bd9Sstevel@tonic-gate 				i = name2qid(pvp[1]);
80407c478bd9Sstevel@tonic-gate 				if (ISVALIDQGRP(i))
80417c478bd9Sstevel@tonic-gate 				{
80427c478bd9Sstevel@tonic-gate 					q->q_qgrp = i;
80437c478bd9Sstevel@tonic-gate 					changed = true;
80447c478bd9Sstevel@tonic-gate 					if (tTd(20, 4))
80457c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_INFO, NOQID,
80467c478bd9Sstevel@tonic-gate 							"queue group name %s -> %d",
80477c478bd9Sstevel@tonic-gate 							pvp[1], i);
80487c478bd9Sstevel@tonic-gate 					continue;
80497c478bd9Sstevel@tonic-gate 				}
80507c478bd9Sstevel@tonic-gate 				else if (LogLevel > 10)
80517c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
80527c478bd9Sstevel@tonic-gate 						"can't find queue group name %s, selection ignored",
80537c478bd9Sstevel@tonic-gate 						pvp[1]);
80547c478bd9Sstevel@tonic-gate 			}
80557c478bd9Sstevel@tonic-gate 			if (q->q_mailer != NULL &&
80567c478bd9Sstevel@tonic-gate 			    ISVALIDQGRP(q->q_mailer->m_qgrp))
80577c478bd9Sstevel@tonic-gate 			{
80587c478bd9Sstevel@tonic-gate 				changed = true;
80597c478bd9Sstevel@tonic-gate 				q->q_qgrp = q->q_mailer->m_qgrp;
80607c478bd9Sstevel@tonic-gate 			}
80617c478bd9Sstevel@tonic-gate 			else if (ISVALIDQGRP(e->e_qgrp))
80627c478bd9Sstevel@tonic-gate 				q->q_qgrp = e->e_qgrp;
80637c478bd9Sstevel@tonic-gate 			else
80647c478bd9Sstevel@tonic-gate 				q->q_qgrp = 0;
80657c478bd9Sstevel@tonic-gate 		}
80667c478bd9Sstevel@tonic-gate 	}
80677c478bd9Sstevel@tonic-gate 
80687c478bd9Sstevel@tonic-gate 	/* only one address? nothing to split. */
80697c478bd9Sstevel@tonic-gate 	if (naddrs <= 1 && !changed)
80707c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
80717c478bd9Sstevel@tonic-gate 
80727c478bd9Sstevel@tonic-gate 	/* sort the addresses by queue group */
80737c478bd9Sstevel@tonic-gate 	addrs = sm_rpool_malloc_x(e->e_rpool, naddrs * sizeof(ADDRESS *));
80747c478bd9Sstevel@tonic-gate 	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
80757c478bd9Sstevel@tonic-gate 	{
80767c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
80777c478bd9Sstevel@tonic-gate 			continue;
80787c478bd9Sstevel@tonic-gate 		addrs[i++] = q;
80797c478bd9Sstevel@tonic-gate 	}
80807c478bd9Sstevel@tonic-gate 	qsort(addrs, naddrs, sizeof(ADDRESS *), q_qgrp_compare);
80817c478bd9Sstevel@tonic-gate 
80827c478bd9Sstevel@tonic-gate 	/* split into multiple envelopes, by queue group */
80837c478bd9Sstevel@tonic-gate 	nsplits = 0;
80847c478bd9Sstevel@tonic-gate 	es = NULL;
80857c478bd9Sstevel@tonic-gate 	e->e_sendqueue = NULL;
80867c478bd9Sstevel@tonic-gate 	for (i = 0; i < naddrs; ++i)
80877c478bd9Sstevel@tonic-gate 	{
80887c478bd9Sstevel@tonic-gate 		if (i == naddrs - 1 || addrs[i]->q_qgrp != addrs[i + 1]->q_qgrp)
80897c478bd9Sstevel@tonic-gate 			addrs[i]->q_next = NULL;
80907c478bd9Sstevel@tonic-gate 		else
80917c478bd9Sstevel@tonic-gate 			addrs[i]->q_next = addrs[i + 1];
80927c478bd9Sstevel@tonic-gate 
80937c478bd9Sstevel@tonic-gate 		/* same queue group as original envelope? */
80947c478bd9Sstevel@tonic-gate 		if (addrs[i]->q_qgrp == e->e_qgrp)
80957c478bd9Sstevel@tonic-gate 		{
80967c478bd9Sstevel@tonic-gate 			if (e->e_sendqueue == NULL)
80977c478bd9Sstevel@tonic-gate 				e->e_sendqueue = addrs[i];
80987c478bd9Sstevel@tonic-gate 			continue;
80997c478bd9Sstevel@tonic-gate 		}
81007c478bd9Sstevel@tonic-gate 
81017c478bd9Sstevel@tonic-gate 		/* different queue group than original envelope */
81027c478bd9Sstevel@tonic-gate 		if (es == NULL || addrs[i]->q_qgrp != es->e_qgrp)
81037c478bd9Sstevel@tonic-gate 		{
81047c478bd9Sstevel@tonic-gate 			ee = split_env(e, addrs[i], addrs[i]->q_qgrp, NOQDIR);
81057c478bd9Sstevel@tonic-gate 			es = ee;
81067c478bd9Sstevel@tonic-gate 			splits[nsplits++] = ee;
81077c478bd9Sstevel@tonic-gate 		}
81087c478bd9Sstevel@tonic-gate 	}
81097c478bd9Sstevel@tonic-gate 
81107c478bd9Sstevel@tonic-gate 	/* no splits? return right now. */
81117c478bd9Sstevel@tonic-gate 	if (nsplits <= 0)
81127c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81137c478bd9Sstevel@tonic-gate 
81147c478bd9Sstevel@tonic-gate 	/* assign a queue directory to each additional envelope */
81157c478bd9Sstevel@tonic-gate 	for (i = 0; i < nsplits; ++i)
81167c478bd9Sstevel@tonic-gate 	{
81177c478bd9Sstevel@tonic-gate 		es = splits[i];
81187c478bd9Sstevel@tonic-gate #if 0
81197c478bd9Sstevel@tonic-gate 		es->e_qdir = pickqdir(Queue[es->e_qgrp], es->e_msgsize, es);
81207c478bd9Sstevel@tonic-gate #endif /* 0 */
81217c478bd9Sstevel@tonic-gate 		if (!setnewqueue(es))
81227c478bd9Sstevel@tonic-gate 			goto failure;
81237c478bd9Sstevel@tonic-gate 	}
81247c478bd9Sstevel@tonic-gate 
81257c478bd9Sstevel@tonic-gate 	/* sort the additional envelopes by queue file system */
81267c478bd9Sstevel@tonic-gate 	qsort(splits, nsplits, sizeof(ENVELOPE *), e_filesys_compare);
81277c478bd9Sstevel@tonic-gate 
81287c478bd9Sstevel@tonic-gate 	/* create data files for each additional envelope */
81297c478bd9Sstevel@tonic-gate 	if (!dup_df(e, splits[0]))
81307c478bd9Sstevel@tonic-gate 	{
81317c478bd9Sstevel@tonic-gate 		i = 0;
81327c478bd9Sstevel@tonic-gate 		goto failure;
81337c478bd9Sstevel@tonic-gate 	}
81347c478bd9Sstevel@tonic-gate 	for (i = 1; i < nsplits; ++i)
81357c478bd9Sstevel@tonic-gate 	{
81367c478bd9Sstevel@tonic-gate 		/* copy or link to the previous data file */
81377c478bd9Sstevel@tonic-gate 		if (!dup_df(splits[i - 1], splits[i]))
81387c478bd9Sstevel@tonic-gate 			goto failure;
81397c478bd9Sstevel@tonic-gate 	}
81407c478bd9Sstevel@tonic-gate 
81417c478bd9Sstevel@tonic-gate 	/* success: prepend the new envelopes to the e->e_sibling list */
81427c478bd9Sstevel@tonic-gate 	for (i = 0; i < nsplits; ++i)
81437c478bd9Sstevel@tonic-gate 	{
81447c478bd9Sstevel@tonic-gate 		es = splits[i];
81457c478bd9Sstevel@tonic-gate 		es->e_sibling = e->e_sibling;
81467c478bd9Sstevel@tonic-gate 		e->e_sibling = es;
81477c478bd9Sstevel@tonic-gate 	}
81487c478bd9Sstevel@tonic-gate 	return SM_SPLIT_NEW(nsplits);
81497c478bd9Sstevel@tonic-gate 
81507c478bd9Sstevel@tonic-gate 	/* failure: clean up */
81517c478bd9Sstevel@tonic-gate   failure:
81527c478bd9Sstevel@tonic-gate 	if (i > 0)
81537c478bd9Sstevel@tonic-gate 	{
81547c478bd9Sstevel@tonic-gate 		int j;
81557c478bd9Sstevel@tonic-gate 
81567c478bd9Sstevel@tonic-gate 		for (j = 0; j < i; j++)
81577c478bd9Sstevel@tonic-gate 			(void) unlink(queuename(splits[j], DATAFL_LETTER));
81587c478bd9Sstevel@tonic-gate 	}
81597c478bd9Sstevel@tonic-gate 	e->e_sendqueue = addrs[0];
81607c478bd9Sstevel@tonic-gate 	for (i = 0; i < naddrs - 1; ++i)
81617c478bd9Sstevel@tonic-gate 		addrs[i]->q_next = addrs[i + 1];
81627c478bd9Sstevel@tonic-gate 	addrs[naddrs - 1]->q_next = NULL;
81637c478bd9Sstevel@tonic-gate 	return SM_SPLIT_FAIL;
81647c478bd9Sstevel@tonic-gate }
81657c478bd9Sstevel@tonic-gate 
81667c478bd9Sstevel@tonic-gate /*
81677c478bd9Sstevel@tonic-gate **  SPLIT_WITHIN_QUEUE
81687c478bd9Sstevel@tonic-gate **
81697c478bd9Sstevel@tonic-gate **	Split an envelope with multiple recipients into several
81707c478bd9Sstevel@tonic-gate **	envelopes within the same queue directory, if the number of
81717c478bd9Sstevel@tonic-gate **	recipients exceeds the limit for the queue group.
81727c478bd9Sstevel@tonic-gate **
81737c478bd9Sstevel@tonic-gate **	Parameters:
81747c478bd9Sstevel@tonic-gate **		e -- envelope.
81757c478bd9Sstevel@tonic-gate **
81767c478bd9Sstevel@tonic-gate **	Results:
81777c478bd9Sstevel@tonic-gate **		SM_SPLIT_FAIL on failure
81787c478bd9Sstevel@tonic-gate **		SM_SPLIT_NONE if no splitting occurred,
81797c478bd9Sstevel@tonic-gate **		or 1 + the number of additional envelopes created.
81807c478bd9Sstevel@tonic-gate */
81817c478bd9Sstevel@tonic-gate 
81827c478bd9Sstevel@tonic-gate #define SPLIT_LOG_LEVEL	8
81837c478bd9Sstevel@tonic-gate 
81847c478bd9Sstevel@tonic-gate static int	split_within_queue __P((ENVELOPE *));
81857c478bd9Sstevel@tonic-gate 
81867c478bd9Sstevel@tonic-gate static int
split_within_queue(e)81877c478bd9Sstevel@tonic-gate split_within_queue(e)
81887c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
81897c478bd9Sstevel@tonic-gate {
81907c478bd9Sstevel@tonic-gate 	int maxrcpt, nrcpt, ndead, nsplit, i;
81917c478bd9Sstevel@tonic-gate 	int j, l;
81927c478bd9Sstevel@tonic-gate 	char *lsplits;
81937c478bd9Sstevel@tonic-gate 	ADDRESS *q, **addrs;
81947c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *firstsibling;
81957c478bd9Sstevel@tonic-gate 
81967c478bd9Sstevel@tonic-gate 	if (!ISVALIDQGRP(e->e_qgrp) || bitset(EF_SPLIT, e->e_flags))
81977c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
81987c478bd9Sstevel@tonic-gate 
81997c478bd9Sstevel@tonic-gate 	/* don't bother if there is no recipient limit */
82007c478bd9Sstevel@tonic-gate 	maxrcpt = Queue[e->e_qgrp]->qg_maxrcpt;
82017c478bd9Sstevel@tonic-gate 	if (maxrcpt <= 0)
82027c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
82037c478bd9Sstevel@tonic-gate 
82047c478bd9Sstevel@tonic-gate 	/* count recipients */
82057c478bd9Sstevel@tonic-gate 	nrcpt = 0;
82067c478bd9Sstevel@tonic-gate 	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
82077c478bd9Sstevel@tonic-gate 	{
82087c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
82097c478bd9Sstevel@tonic-gate 			continue;
82107c478bd9Sstevel@tonic-gate 		++nrcpt;
82117c478bd9Sstevel@tonic-gate 	}
82127c478bd9Sstevel@tonic-gate 	if (nrcpt <= maxrcpt)
82137c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
82147c478bd9Sstevel@tonic-gate 
82157c478bd9Sstevel@tonic-gate 	/*
82167c478bd9Sstevel@tonic-gate 	**  Preserve the recipient list
82177c478bd9Sstevel@tonic-gate 	**  so that we can restore it in case of error.
82187c478bd9Sstevel@tonic-gate 	**  (But we discard dead addresses.)
82197c478bd9Sstevel@tonic-gate 	*/
82207c478bd9Sstevel@tonic-gate 
82217c478bd9Sstevel@tonic-gate 	addrs = sm_rpool_malloc_x(e->e_rpool, nrcpt * sizeof(ADDRESS *));
82227c478bd9Sstevel@tonic-gate 	for (i = 0, q = e->e_sendqueue; q != NULL; q = q->q_next)
82237c478bd9Sstevel@tonic-gate 	{
82247c478bd9Sstevel@tonic-gate 		if (QS_IS_DEAD(q->q_state))
82257c478bd9Sstevel@tonic-gate 			continue;
82267c478bd9Sstevel@tonic-gate 		addrs[i++] = q;
82277c478bd9Sstevel@tonic-gate 	}
82287c478bd9Sstevel@tonic-gate 
82297c478bd9Sstevel@tonic-gate 	/*
82307c478bd9Sstevel@tonic-gate 	**  Partition the recipient list so that bad and sent addresses
82317c478bd9Sstevel@tonic-gate 	**  come first. These will go with the original envelope, and
82327c478bd9Sstevel@tonic-gate 	**  do not count towards the maxrcpt limit.
82337c478bd9Sstevel@tonic-gate 	**  addrs[] does not contain QS_IS_DEAD() addresses.
82347c478bd9Sstevel@tonic-gate 	*/
82357c478bd9Sstevel@tonic-gate 
82367c478bd9Sstevel@tonic-gate 	ndead = 0;
82377c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrcpt; ++i)
82387c478bd9Sstevel@tonic-gate 	{
82397c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(addrs[i]->q_state) ||
82407c478bd9Sstevel@tonic-gate 		    QS_IS_SENT(addrs[i]->q_state) ||
82417c478bd9Sstevel@tonic-gate 		    QS_IS_DEAD(addrs[i]->q_state)) /* for paranoia's sake */
82427c478bd9Sstevel@tonic-gate 		{
82437c478bd9Sstevel@tonic-gate 			if (i > ndead)
82447c478bd9Sstevel@tonic-gate 			{
82457c478bd9Sstevel@tonic-gate 				ADDRESS *tmp = addrs[i];
82467c478bd9Sstevel@tonic-gate 
82477c478bd9Sstevel@tonic-gate 				addrs[i] = addrs[ndead];
82487c478bd9Sstevel@tonic-gate 				addrs[ndead] = tmp;
82497c478bd9Sstevel@tonic-gate 			}
82507c478bd9Sstevel@tonic-gate 			++ndead;
82517c478bd9Sstevel@tonic-gate 		}
82527c478bd9Sstevel@tonic-gate 	}
82537c478bd9Sstevel@tonic-gate 
82547c478bd9Sstevel@tonic-gate 	/* Check if no splitting required. */
82557c478bd9Sstevel@tonic-gate 	if (nrcpt - ndead <= maxrcpt)
82567c478bd9Sstevel@tonic-gate 		return SM_SPLIT_NONE;
82577c478bd9Sstevel@tonic-gate 
82587c478bd9Sstevel@tonic-gate 	/* fix links */
82597c478bd9Sstevel@tonic-gate 	for (i = 0; i < nrcpt - 1; ++i)
82607c478bd9Sstevel@tonic-gate 		addrs[i]->q_next = addrs[i + 1];
82617c478bd9Sstevel@tonic-gate 	addrs[nrcpt - 1]->q_next = NULL;
82627c478bd9Sstevel@tonic-gate 	e->e_sendqueue = addrs[0];
82637c478bd9Sstevel@tonic-gate 
82647c478bd9Sstevel@tonic-gate 	/* prepare buffer for logging */
82657c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL)
82667c478bd9Sstevel@tonic-gate 	{
82677c478bd9Sstevel@tonic-gate 		l = MAXLINE;
82687c478bd9Sstevel@tonic-gate 		lsplits = sm_malloc(l);
82697c478bd9Sstevel@tonic-gate 		if (lsplits != NULL)
82707c478bd9Sstevel@tonic-gate 			*lsplits = '\0';
82717c478bd9Sstevel@tonic-gate 		j = 0;
82727c478bd9Sstevel@tonic-gate 	}
82737c478bd9Sstevel@tonic-gate 	else
82747c478bd9Sstevel@tonic-gate 	{
82757c478bd9Sstevel@tonic-gate 		/* get rid of stupid compiler warnings */
82767c478bd9Sstevel@tonic-gate 		lsplits = NULL;
82777c478bd9Sstevel@tonic-gate 		j = l = 0;
82787c478bd9Sstevel@tonic-gate 	}
82797c478bd9Sstevel@tonic-gate 
82807c478bd9Sstevel@tonic-gate 	/* split the envelope */
82817c478bd9Sstevel@tonic-gate 	firstsibling = e->e_sibling;
82827c478bd9Sstevel@tonic-gate 	i = maxrcpt + ndead;
82837c478bd9Sstevel@tonic-gate 	nsplit = 0;
82847c478bd9Sstevel@tonic-gate 	for (;;)
82857c478bd9Sstevel@tonic-gate 	{
82867c478bd9Sstevel@tonic-gate 		addrs[i - 1]->q_next = NULL;
82877c478bd9Sstevel@tonic-gate 		ee = split_env(e, addrs[i], e->e_qgrp, e->e_qdir);
82887c478bd9Sstevel@tonic-gate 		if (!dup_df(e, ee))
82897c478bd9Sstevel@tonic-gate 		{
82907c478bd9Sstevel@tonic-gate 
82917c478bd9Sstevel@tonic-gate 			ee = firstsibling;
82927c478bd9Sstevel@tonic-gate 			while (ee != NULL)
82937c478bd9Sstevel@tonic-gate 			{
82947c478bd9Sstevel@tonic-gate 				(void) unlink(queuename(ee, DATAFL_LETTER));
82957c478bd9Sstevel@tonic-gate 				ee = ee->e_sibling;
82967c478bd9Sstevel@tonic-gate 			}
82977c478bd9Sstevel@tonic-gate 
82987c478bd9Sstevel@tonic-gate 			/* Error.  Restore e's sibling & recipient lists. */
82997c478bd9Sstevel@tonic-gate 			e->e_sibling = firstsibling;
83007c478bd9Sstevel@tonic-gate 			for (i = 0; i < nrcpt - 1; ++i)
83017c478bd9Sstevel@tonic-gate 				addrs[i]->q_next = addrs[i + 1];
83027c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
83037c478bd9Sstevel@tonic-gate 				sm_free(lsplits);
83047c478bd9Sstevel@tonic-gate 			return SM_SPLIT_FAIL;
83057c478bd9Sstevel@tonic-gate 		}
83067c478bd9Sstevel@tonic-gate 
83077c478bd9Sstevel@tonic-gate 		/* prepend the new envelope to e->e_sibling */
83087c478bd9Sstevel@tonic-gate 		ee->e_sibling = e->e_sibling;
83097c478bd9Sstevel@tonic-gate 		e->e_sibling = ee;
83107c478bd9Sstevel@tonic-gate 		++nsplit;
83117c478bd9Sstevel@tonic-gate 		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
83127c478bd9Sstevel@tonic-gate 		{
83137c478bd9Sstevel@tonic-gate 			if (j >= l - strlen(ee->e_id) - 3)
83147c478bd9Sstevel@tonic-gate 			{
83157c478bd9Sstevel@tonic-gate 				char *p;
83167c478bd9Sstevel@tonic-gate 
83177c478bd9Sstevel@tonic-gate 				l += MAXLINE;
83187c478bd9Sstevel@tonic-gate 				p = sm_realloc(lsplits, l);
83197c478bd9Sstevel@tonic-gate 				if (p == NULL)
83207c478bd9Sstevel@tonic-gate 				{
83217c478bd9Sstevel@tonic-gate 					/* let's try to get this done */
83227c478bd9Sstevel@tonic-gate 					sm_free(lsplits);
83237c478bd9Sstevel@tonic-gate 					lsplits = NULL;
83247c478bd9Sstevel@tonic-gate 				}
83257c478bd9Sstevel@tonic-gate 				else
83267c478bd9Sstevel@tonic-gate 					lsplits = p;
83277c478bd9Sstevel@tonic-gate 			}
83287c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
83297c478bd9Sstevel@tonic-gate 			{
83307c478bd9Sstevel@tonic-gate 				if (j == 0)
83317c478bd9Sstevel@tonic-gate 					j += sm_strlcat(lsplits + j,
83327c478bd9Sstevel@tonic-gate 							ee->e_id,
83337c478bd9Sstevel@tonic-gate 							l - j);
83347c478bd9Sstevel@tonic-gate 				else
83357c478bd9Sstevel@tonic-gate 					j += sm_strlcat2(lsplits + j,
83367c478bd9Sstevel@tonic-gate 							 "; ",
83377c478bd9Sstevel@tonic-gate 							 ee->e_id,
83387c478bd9Sstevel@tonic-gate 							 l - j);
83397c478bd9Sstevel@tonic-gate 				SM_ASSERT(j < l);
83407c478bd9Sstevel@tonic-gate 			}
83417c478bd9Sstevel@tonic-gate 		}
83427c478bd9Sstevel@tonic-gate 		if (nrcpt - i <= maxrcpt)
83437c478bd9Sstevel@tonic-gate 			break;
83447c478bd9Sstevel@tonic-gate 		i += maxrcpt;
83457c478bd9Sstevel@tonic-gate 	}
83467c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
83477c478bd9Sstevel@tonic-gate 	{
83487c478bd9Sstevel@tonic-gate 		if (nsplit > 0)
83497c478bd9Sstevel@tonic-gate 		{
83507c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_NOTICE, e->e_id,
83517c478bd9Sstevel@tonic-gate 				  "split: maxrcpts=%d, rcpts=%d, count=%d, id%s=%s",
83527c478bd9Sstevel@tonic-gate 				  maxrcpt, nrcpt - ndead, nsplit,
83537c478bd9Sstevel@tonic-gate 				  nsplit > 1 ? "s" : "", lsplits);
83547c478bd9Sstevel@tonic-gate 		}
83557c478bd9Sstevel@tonic-gate 		sm_free(lsplits);
83567c478bd9Sstevel@tonic-gate 	}
83577c478bd9Sstevel@tonic-gate 	return SM_SPLIT_NEW(nsplit);
83587c478bd9Sstevel@tonic-gate }
83597c478bd9Sstevel@tonic-gate /*
83607c478bd9Sstevel@tonic-gate **  SPLIT_BY_RECIPIENT
83617c478bd9Sstevel@tonic-gate **
83627c478bd9Sstevel@tonic-gate **	Split an envelope with multiple recipients into multiple
83637c478bd9Sstevel@tonic-gate **	envelopes as required by the sendmail configuration.
83647c478bd9Sstevel@tonic-gate **
83657c478bd9Sstevel@tonic-gate **	Parameters:
83667c478bd9Sstevel@tonic-gate **		e -- envelope.
83677c478bd9Sstevel@tonic-gate **
83687c478bd9Sstevel@tonic-gate **	Results:
83697c478bd9Sstevel@tonic-gate **		Returns true on success, false on failure.
83707c478bd9Sstevel@tonic-gate **
83717c478bd9Sstevel@tonic-gate **	Side Effects:
83727c478bd9Sstevel@tonic-gate **		see split_across_queue_groups(), split_within_queue(e)
83737c478bd9Sstevel@tonic-gate */
83747c478bd9Sstevel@tonic-gate 
83757c478bd9Sstevel@tonic-gate bool
split_by_recipient(e)83767c478bd9Sstevel@tonic-gate split_by_recipient(e)
83777c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
83787c478bd9Sstevel@tonic-gate {
83797c478bd9Sstevel@tonic-gate 	int split, n, i, j, l;
83807c478bd9Sstevel@tonic-gate 	char *lsplits;
83817c478bd9Sstevel@tonic-gate 	ENVELOPE *ee, *next, *firstsibling;
83827c478bd9Sstevel@tonic-gate 
83837c478bd9Sstevel@tonic-gate 	if (OpMode == SM_VERIFY || !ISVALIDQGRP(e->e_qgrp) ||
83847c478bd9Sstevel@tonic-gate 	    bitset(EF_SPLIT, e->e_flags))
83857c478bd9Sstevel@tonic-gate 		return true;
83867c478bd9Sstevel@tonic-gate 	n = split_across_queue_groups(e);
83877c478bd9Sstevel@tonic-gate 	if (n == SM_SPLIT_FAIL)
83887c478bd9Sstevel@tonic-gate 		return false;
83897c478bd9Sstevel@tonic-gate 	firstsibling = ee = e->e_sibling;
83907c478bd9Sstevel@tonic-gate 	if (n > 1 && LogLevel > SPLIT_LOG_LEVEL)
83917c478bd9Sstevel@tonic-gate 	{
83927c478bd9Sstevel@tonic-gate 		l = MAXLINE;
83937c478bd9Sstevel@tonic-gate 		lsplits = sm_malloc(l);
83947c478bd9Sstevel@tonic-gate 		if (lsplits != NULL)
83957c478bd9Sstevel@tonic-gate 			*lsplits = '\0';
83967c478bd9Sstevel@tonic-gate 		j = 0;
83977c478bd9Sstevel@tonic-gate 	}
83987c478bd9Sstevel@tonic-gate 	else
83997c478bd9Sstevel@tonic-gate 	{
84007c478bd9Sstevel@tonic-gate 		/* get rid of stupid compiler warnings */
84017c478bd9Sstevel@tonic-gate 		lsplits = NULL;
84027c478bd9Sstevel@tonic-gate 		j = l = 0;
84037c478bd9Sstevel@tonic-gate 	}
84047c478bd9Sstevel@tonic-gate 	for (i = 1; i < n; ++i)
84057c478bd9Sstevel@tonic-gate 	{
84067c478bd9Sstevel@tonic-gate 		next = ee->e_sibling;
84077c478bd9Sstevel@tonic-gate 		if (split_within_queue(ee) == SM_SPLIT_FAIL)
84087c478bd9Sstevel@tonic-gate 		{
84097c478bd9Sstevel@tonic-gate 			e->e_sibling = firstsibling;
84107c478bd9Sstevel@tonic-gate 			return false;
84117c478bd9Sstevel@tonic-gate 		}
84127c478bd9Sstevel@tonic-gate 		ee->e_flags |= EF_SPLIT;
84137c478bd9Sstevel@tonic-gate 		if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL)
84147c478bd9Sstevel@tonic-gate 		{
84157c478bd9Sstevel@tonic-gate 			if (j >= l - strlen(ee->e_id) - 3)
84167c478bd9Sstevel@tonic-gate 			{
84177c478bd9Sstevel@tonic-gate 				char *p;
84187c478bd9Sstevel@tonic-gate 
84197c478bd9Sstevel@tonic-gate 				l += MAXLINE;
84207c478bd9Sstevel@tonic-gate 				p = sm_realloc(lsplits, l);
84217c478bd9Sstevel@tonic-gate 				if (p == NULL)
84227c478bd9Sstevel@tonic-gate 				{
84237c478bd9Sstevel@tonic-gate 					/* let's try to get this done */
84247c478bd9Sstevel@tonic-gate 					sm_free(lsplits);
84257c478bd9Sstevel@tonic-gate 					lsplits = NULL;
84267c478bd9Sstevel@tonic-gate 				}
84277c478bd9Sstevel@tonic-gate 				else
84287c478bd9Sstevel@tonic-gate 					lsplits = p;
84297c478bd9Sstevel@tonic-gate 			}
84307c478bd9Sstevel@tonic-gate 			if (lsplits != NULL)
84317c478bd9Sstevel@tonic-gate 			{
84327c478bd9Sstevel@tonic-gate 				if (j == 0)
84337c478bd9Sstevel@tonic-gate 					j += sm_strlcat(lsplits + j,
84347c478bd9Sstevel@tonic-gate 							ee->e_id, l - j);
84357c478bd9Sstevel@tonic-gate 				else
84367c478bd9Sstevel@tonic-gate 					j += sm_strlcat2(lsplits + j, "; ",
84377c478bd9Sstevel@tonic-gate 							 ee->e_id, l - j);
84387c478bd9Sstevel@tonic-gate 				SM_ASSERT(j < l);
84397c478bd9Sstevel@tonic-gate 			}
84407c478bd9Sstevel@tonic-gate 		}
84417c478bd9Sstevel@tonic-gate 		ee = next;
84427c478bd9Sstevel@tonic-gate 	}
84437c478bd9Sstevel@tonic-gate 	if (LogLevel > SPLIT_LOG_LEVEL && lsplits != NULL && n > 1)
84447c478bd9Sstevel@tonic-gate 	{
84457c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_NOTICE, e->e_id, "split: count=%d, id%s=%s",
84467c478bd9Sstevel@tonic-gate 			  n - 1, n > 2 ? "s" : "", lsplits);
84477c478bd9Sstevel@tonic-gate 		sm_free(lsplits);
84487c478bd9Sstevel@tonic-gate 	}
84497c478bd9Sstevel@tonic-gate 	split = split_within_queue(e) != SM_SPLIT_FAIL;
84507c478bd9Sstevel@tonic-gate 	if (split)
84517c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_SPLIT;
84527c478bd9Sstevel@tonic-gate 	return split;
84537c478bd9Sstevel@tonic-gate }
84547c478bd9Sstevel@tonic-gate 
84557c478bd9Sstevel@tonic-gate /*
84567c478bd9Sstevel@tonic-gate **  QUARANTINE_QUEUE_ITEM -- {un,}quarantine a single envelope
84577c478bd9Sstevel@tonic-gate **
84587c478bd9Sstevel@tonic-gate **	Add/remove quarantine reason and requeue appropriately.
84597c478bd9Sstevel@tonic-gate **
84607c478bd9Sstevel@tonic-gate **	Parameters:
84617c478bd9Sstevel@tonic-gate **		qgrp -- queue group for the item
84627c478bd9Sstevel@tonic-gate **		qdir -- queue directory in the given queue group
84637c478bd9Sstevel@tonic-gate **		e -- envelope information for the item
84647c478bd9Sstevel@tonic-gate **		reason -- quarantine reason, NULL means unquarantine.
84657c478bd9Sstevel@tonic-gate **
84667c478bd9Sstevel@tonic-gate **	Results:
84677c478bd9Sstevel@tonic-gate **		true if item changed, false otherwise
84687c478bd9Sstevel@tonic-gate **
84697c478bd9Sstevel@tonic-gate **	Side Effects:
84707c478bd9Sstevel@tonic-gate **		Changes quarantine tag in queue file and renames it.
84717c478bd9Sstevel@tonic-gate */
84727c478bd9Sstevel@tonic-gate 
84737c478bd9Sstevel@tonic-gate static bool
quarantine_queue_item(qgrp,qdir,e,reason)84747c478bd9Sstevel@tonic-gate quarantine_queue_item(qgrp, qdir, e, reason)
84757c478bd9Sstevel@tonic-gate 	int qgrp;
84767c478bd9Sstevel@tonic-gate 	int qdir;
84777c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
84787c478bd9Sstevel@tonic-gate 	char *reason;
84797c478bd9Sstevel@tonic-gate {
84807c478bd9Sstevel@tonic-gate 	bool dirty = false;
84817c478bd9Sstevel@tonic-gate 	bool failing = false;
84827c478bd9Sstevel@tonic-gate 	bool foundq = false;
84837c478bd9Sstevel@tonic-gate 	bool finished = false;
84847c478bd9Sstevel@tonic-gate 	int fd;
84857c478bd9Sstevel@tonic-gate 	int flags;
84867c478bd9Sstevel@tonic-gate 	int oldtype;
84877c478bd9Sstevel@tonic-gate 	int newtype;
84887c478bd9Sstevel@tonic-gate 	int save_errno;
84897c478bd9Sstevel@tonic-gate 	MODE_T oldumask = 0;
84907c478bd9Sstevel@tonic-gate 	SM_FILE_T *oldqfp, *tempqfp;
84917c478bd9Sstevel@tonic-gate 	char *bp;
8492058561cbSjbeck 	int bufsize;
84937c478bd9Sstevel@tonic-gate 	char oldqf[MAXPATHLEN];
84947c478bd9Sstevel@tonic-gate 	char tempqf[MAXPATHLEN];
84957c478bd9Sstevel@tonic-gate 	char newqf[MAXPATHLEN];
84967c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
84977c478bd9Sstevel@tonic-gate 
84987c478bd9Sstevel@tonic-gate 	oldtype = queue_letter(e, ANYQFL_LETTER);
8499058561cbSjbeck 	(void) sm_strlcpy(oldqf, queuename(e, ANYQFL_LETTER), sizeof(oldqf));
8500058561cbSjbeck 	(void) sm_strlcpy(tempqf, queuename(e, NEWQFL_LETTER), sizeof(tempqf));
85017c478bd9Sstevel@tonic-gate 
85027c478bd9Sstevel@tonic-gate 	/*
85037c478bd9Sstevel@tonic-gate 	**  Instead of duplicating all the open
85047c478bd9Sstevel@tonic-gate 	**  and lock code here, tell readqf() to
85057c478bd9Sstevel@tonic-gate 	**  do that work and return the open
85067c478bd9Sstevel@tonic-gate 	**  file pointer in e_lockfp.  Note that
85077c478bd9Sstevel@tonic-gate 	**  we must release the locks properly when
85087c478bd9Sstevel@tonic-gate 	**  we are done.
85097c478bd9Sstevel@tonic-gate 	*/
85107c478bd9Sstevel@tonic-gate 
85117c478bd9Sstevel@tonic-gate 	if (!readqf(e, true))
85127c478bd9Sstevel@tonic-gate 	{
85137c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
85147c478bd9Sstevel@tonic-gate 				     "Skipping %s\n", qid_printname(e));
85157c478bd9Sstevel@tonic-gate 		return false;
85167c478bd9Sstevel@tonic-gate 	}
85177c478bd9Sstevel@tonic-gate 	oldqfp = e->e_lockfp;
85187c478bd9Sstevel@tonic-gate 
85197c478bd9Sstevel@tonic-gate 	/* open the new queue file */
85207c478bd9Sstevel@tonic-gate 	flags = O_CREAT|O_WRONLY|O_EXCL;
85217c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
85227c478bd9Sstevel@tonic-gate 		oldumask = umask(002);
85237c478bd9Sstevel@tonic-gate 	fd = open(tempqf, flags, QueueFileMode);
85247c478bd9Sstevel@tonic-gate 	if (bitset(S_IWGRP, QueueFileMode))
85257c478bd9Sstevel@tonic-gate 		(void) umask(oldumask);
85267c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
85277c478bd9Sstevel@tonic-gate 
85287c478bd9Sstevel@tonic-gate 	if (fd < 0)
85297c478bd9Sstevel@tonic-gate 	{
85307c478bd9Sstevel@tonic-gate 		save_errno = errno;
85317c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
85327c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not open %s: %s\n",
85337c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf,
85347c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
85357c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
85367c478bd9Sstevel@tonic-gate 		return false;
85377c478bd9Sstevel@tonic-gate 	}
85387c478bd9Sstevel@tonic-gate 	if (!lockfile(fd, tempqf, NULL, LOCK_EX|LOCK_NB))
85397c478bd9Sstevel@tonic-gate 	{
85407c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
85417c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not lock %s\n",
85427c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf);
85437c478bd9Sstevel@tonic-gate 		(void) close(fd);
85447c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
85457c478bd9Sstevel@tonic-gate 		return false;
85467c478bd9Sstevel@tonic-gate 	}
85477c478bd9Sstevel@tonic-gate 
85487c478bd9Sstevel@tonic-gate 	tempqfp = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &fd,
85497c478bd9Sstevel@tonic-gate 			     SM_IO_WRONLY_B, NULL);
85507c478bd9Sstevel@tonic-gate 	if (tempqfp == NULL)
85517c478bd9Sstevel@tonic-gate 	{
85527c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
85537c478bd9Sstevel@tonic-gate 				     "Skipping %s: Could not lock %s\n",
85547c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf);
85557c478bd9Sstevel@tonic-gate 		(void) close(fd);
85567c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
85577c478bd9Sstevel@tonic-gate 		return false;
85587c478bd9Sstevel@tonic-gate 	}
85597c478bd9Sstevel@tonic-gate 
85607c478bd9Sstevel@tonic-gate 	/* Copy the data over, changing the quarantine reason */
8561058561cbSjbeck 	while (bufsize = sizeof(buf),
8562058561cbSjbeck 	       (bp = fgetfolded(buf, &bufsize, oldqfp)) != NULL)
85637c478bd9Sstevel@tonic-gate 	{
85647c478bd9Sstevel@tonic-gate 		if (tTd(40, 4))
85657c478bd9Sstevel@tonic-gate 			sm_dprintf("+++++ %s\n", bp);
85667c478bd9Sstevel@tonic-gate 		switch (bp[0])
85677c478bd9Sstevel@tonic-gate 		{
85687c478bd9Sstevel@tonic-gate 		  case 'q':		/* quarantine reason */
85697c478bd9Sstevel@tonic-gate 			foundq = true;
85707c478bd9Sstevel@tonic-gate 			if (reason == NULL)
85717c478bd9Sstevel@tonic-gate 			{
85727c478bd9Sstevel@tonic-gate 				if (Verbose)
85737c478bd9Sstevel@tonic-gate 				{
85747c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85757c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85767c478bd9Sstevel@tonic-gate 							     "%s: Removed quarantine of \"%s\"\n",
85777c478bd9Sstevel@tonic-gate 							     e->e_id, &bp[1]);
85787c478bd9Sstevel@tonic-gate 				}
85797c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "unquarantine");
85807c478bd9Sstevel@tonic-gate 				dirty = true;
85817c478bd9Sstevel@tonic-gate 			}
85827c478bd9Sstevel@tonic-gate 			else if (strcmp(reason, &bp[1]) == 0)
85837c478bd9Sstevel@tonic-gate 			{
85847c478bd9Sstevel@tonic-gate 				if (Verbose)
85857c478bd9Sstevel@tonic-gate 				{
85867c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85877c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
85887c478bd9Sstevel@tonic-gate 							     "%s: Already quarantined with \"%s\"\n",
85897c478bd9Sstevel@tonic-gate 							     e->e_id, reason);
85907c478bd9Sstevel@tonic-gate 				}
85917c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
85927c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
85937c478bd9Sstevel@tonic-gate 			}
85947c478bd9Sstevel@tonic-gate 			else
85957c478bd9Sstevel@tonic-gate 			{
85967c478bd9Sstevel@tonic-gate 				if (Verbose)
85977c478bd9Sstevel@tonic-gate 				{
85987c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
85997c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
86007c478bd9Sstevel@tonic-gate 							     "%s: Quarantine changed from \"%s\" to \"%s\"\n",
86017c478bd9Sstevel@tonic-gate 							     e->e_id, &bp[1],
86027c478bd9Sstevel@tonic-gate 							     reason);
86037c478bd9Sstevel@tonic-gate 				}
86047c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
86057c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
86067c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
86077c478bd9Sstevel@tonic-gate 					  reason);
86087c478bd9Sstevel@tonic-gate 				dirty = true;
86097c478bd9Sstevel@tonic-gate 			}
86107c478bd9Sstevel@tonic-gate 			break;
86117c478bd9Sstevel@tonic-gate 
86127c478bd9Sstevel@tonic-gate 		  case 'S':
86137c478bd9Sstevel@tonic-gate 			/*
86147c478bd9Sstevel@tonic-gate 			**  If we are quarantining an unquarantined item,
86157c478bd9Sstevel@tonic-gate 			**  need to put in a new 'q' line before it's
86167c478bd9Sstevel@tonic-gate 			**  too late.
86177c478bd9Sstevel@tonic-gate 			*/
86187c478bd9Sstevel@tonic-gate 
86197c478bd9Sstevel@tonic-gate 			if (!foundq && reason != NULL)
86207c478bd9Sstevel@tonic-gate 			{
86217c478bd9Sstevel@tonic-gate 				if (Verbose)
86227c478bd9Sstevel@tonic-gate 				{
86237c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
86247c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT,
86257c478bd9Sstevel@tonic-gate 							     "%s: Quarantined with \"%s\"\n",
86267c478bd9Sstevel@tonic-gate 							     e->e_id, reason);
86277c478bd9Sstevel@tonic-gate 				}
86287c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
86297c478bd9Sstevel@tonic-gate 						     "q%s\n", reason);
86307c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "quarantine=%s",
86317c478bd9Sstevel@tonic-gate 					  reason);
86327c478bd9Sstevel@tonic-gate 				foundq = true;
86337c478bd9Sstevel@tonic-gate 				dirty = true;
86347c478bd9Sstevel@tonic-gate 			}
86357c478bd9Sstevel@tonic-gate 
86367c478bd9Sstevel@tonic-gate 			/* Copy the line to the new file */
86377c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
86387c478bd9Sstevel@tonic-gate 					     "%s\n", bp);
86397c478bd9Sstevel@tonic-gate 			break;
86407c478bd9Sstevel@tonic-gate 
86417c478bd9Sstevel@tonic-gate 		  case '.':
86427c478bd9Sstevel@tonic-gate 			finished = true;
86437c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
86447c478bd9Sstevel@tonic-gate 
86457c478bd9Sstevel@tonic-gate 		  default:
86467c478bd9Sstevel@tonic-gate 			/* Copy the line to the new file */
86477c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(tempqfp, SM_TIME_DEFAULT,
86487c478bd9Sstevel@tonic-gate 					     "%s\n", bp);
86497c478bd9Sstevel@tonic-gate 			break;
86507c478bd9Sstevel@tonic-gate 		}
8651058561cbSjbeck 		if (bp != buf)
8652058561cbSjbeck 			sm_free(bp);
86537c478bd9Sstevel@tonic-gate 	}
86547c478bd9Sstevel@tonic-gate 
86557c478bd9Sstevel@tonic-gate 	/* Make sure we read the whole old file */
86567c478bd9Sstevel@tonic-gate 	errno = sm_io_error(tempqfp);
86577c478bd9Sstevel@tonic-gate 	if (errno != 0 && errno != SM_IO_EOF)
86587c478bd9Sstevel@tonic-gate 	{
86597c478bd9Sstevel@tonic-gate 		save_errno = errno;
86607c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86617c478bd9Sstevel@tonic-gate 				     "Skipping %s: Error reading %s: %s\n",
86627c478bd9Sstevel@tonic-gate 				     qid_printname(e), oldqf,
86637c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86647c478bd9Sstevel@tonic-gate 		failing = true;
86657c478bd9Sstevel@tonic-gate 	}
86667c478bd9Sstevel@tonic-gate 
86677c478bd9Sstevel@tonic-gate 	if (!failing && !finished)
86687c478bd9Sstevel@tonic-gate 	{
86697c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86707c478bd9Sstevel@tonic-gate 				     "Skipping %s: Incomplete file: %s\n",
86717c478bd9Sstevel@tonic-gate 				     qid_printname(e), oldqf);
86727c478bd9Sstevel@tonic-gate 		failing = true;
86737c478bd9Sstevel@tonic-gate 	}
86747c478bd9Sstevel@tonic-gate 
86757c478bd9Sstevel@tonic-gate 	/* Check if we actually changed anything or we can just bail now */
86767c478bd9Sstevel@tonic-gate 	if (!dirty)
86777c478bd9Sstevel@tonic-gate 	{
86787c478bd9Sstevel@tonic-gate 		/* pretend we failed, even though we technically didn't */
86797c478bd9Sstevel@tonic-gate 		failing = true;
86807c478bd9Sstevel@tonic-gate 	}
86817c478bd9Sstevel@tonic-gate 
86827c478bd9Sstevel@tonic-gate 	/* Make sure we wrote things out safely */
86837c478bd9Sstevel@tonic-gate 	if (!failing &&
86847c478bd9Sstevel@tonic-gate 	    (sm_io_flush(tempqfp, SM_TIME_DEFAULT) != 0 ||
86857c478bd9Sstevel@tonic-gate 	     ((SuperSafe == SAFE_REALLY ||
86867c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_REALLY_POSTMILTER ||
86877c478bd9Sstevel@tonic-gate 	       SuperSafe == SAFE_INTERACTIVE) &&
86887c478bd9Sstevel@tonic-gate 	      fsync(sm_io_getinfo(tempqfp, SM_IO_WHAT_FD, NULL)) < 0) ||
86897c478bd9Sstevel@tonic-gate 	     ((errno = sm_io_error(tempqfp)) != 0)))
86907c478bd9Sstevel@tonic-gate 	{
86917c478bd9Sstevel@tonic-gate 		save_errno = errno;
86927c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
86937c478bd9Sstevel@tonic-gate 				     "Skipping %s: Error writing %s: %s\n",
86947c478bd9Sstevel@tonic-gate 				     qid_printname(e), tempqf,
86957c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
86967c478bd9Sstevel@tonic-gate 		failing = true;
86977c478bd9Sstevel@tonic-gate 	}
86987c478bd9Sstevel@tonic-gate 
86997c478bd9Sstevel@tonic-gate 
87007c478bd9Sstevel@tonic-gate 	/* Figure out the new filename */
87017c478bd9Sstevel@tonic-gate 	newtype = (reason == NULL ? NORMQF_LETTER : QUARQF_LETTER);
87027c478bd9Sstevel@tonic-gate 	if (oldtype == newtype)
87037c478bd9Sstevel@tonic-gate 	{
87047c478bd9Sstevel@tonic-gate 		/* going to rename tempqf to oldqf */
8705058561cbSjbeck 		(void) sm_strlcpy(newqf, oldqf, sizeof(newqf));
87067c478bd9Sstevel@tonic-gate 	}
87077c478bd9Sstevel@tonic-gate 	else
87087c478bd9Sstevel@tonic-gate 	{
87097c478bd9Sstevel@tonic-gate 		/* going to rename tempqf to new name based on newtype */
8710058561cbSjbeck 		(void) sm_strlcpy(newqf, queuename(e, newtype), sizeof(newqf));
87117c478bd9Sstevel@tonic-gate 	}
87127c478bd9Sstevel@tonic-gate 
87137c478bd9Sstevel@tonic-gate 	save_errno = 0;
87147c478bd9Sstevel@tonic-gate 
87157c478bd9Sstevel@tonic-gate 	/* rename tempqf to newqf */
87167c478bd9Sstevel@tonic-gate 	if (!failing &&
87177c478bd9Sstevel@tonic-gate 	    rename(tempqf, newqf) < 0)
87187c478bd9Sstevel@tonic-gate 		save_errno = (errno == 0) ? EINVAL : errno;
87197c478bd9Sstevel@tonic-gate 
87207c478bd9Sstevel@tonic-gate 	/* Check rename() success */
87217c478bd9Sstevel@tonic-gate 	if (!failing && save_errno != 0)
87227c478bd9Sstevel@tonic-gate 	{
87237c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_DEBUG, e->e_id,
87247c478bd9Sstevel@tonic-gate 			  "quarantine_queue_item: rename(%s, %s): %s",
87257c478bd9Sstevel@tonic-gate 			  tempqf, newqf, sm_errstring(save_errno));
87267c478bd9Sstevel@tonic-gate 
87277c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
87287c478bd9Sstevel@tonic-gate 				     "Error renaming %s to %s: %s\n",
87297c478bd9Sstevel@tonic-gate 				     tempqf, newqf,
87307c478bd9Sstevel@tonic-gate 				     sm_errstring(save_errno));
87317c478bd9Sstevel@tonic-gate 		if (oldtype == newtype)
87327c478bd9Sstevel@tonic-gate 		{
87337c478bd9Sstevel@tonic-gate 			/*
87347c478bd9Sstevel@tonic-gate 			**  Bail here since we don't know the state of
87357c478bd9Sstevel@tonic-gate 			**  the filesystem and may need to keep tempqf
87367c478bd9Sstevel@tonic-gate 			**  for the user to rescue us.
87377c478bd9Sstevel@tonic-gate 			*/
87387c478bd9Sstevel@tonic-gate 
87397c478bd9Sstevel@tonic-gate 			RELEASE_QUEUE;
87407c478bd9Sstevel@tonic-gate 			errno = save_errno;
87417c478bd9Sstevel@tonic-gate 			syserr("!452 Error renaming control file %s", tempqf);
87427c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
87437c478bd9Sstevel@tonic-gate 		}
87447c478bd9Sstevel@tonic-gate 		else
87457c478bd9Sstevel@tonic-gate 		{
87467c478bd9Sstevel@tonic-gate 			/* remove new file (if rename() half completed) */
87477c478bd9Sstevel@tonic-gate 			if (xunlink(newqf) < 0)
87487c478bd9Sstevel@tonic-gate 			{
87497c478bd9Sstevel@tonic-gate 				save_errno = errno;
87507c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
87517c478bd9Sstevel@tonic-gate 						     "Error removing %s: %s\n",
87527c478bd9Sstevel@tonic-gate 						     newqf,
87537c478bd9Sstevel@tonic-gate 						     sm_errstring(save_errno));
87547c478bd9Sstevel@tonic-gate 			}
87557c478bd9Sstevel@tonic-gate 
87567c478bd9Sstevel@tonic-gate 			/* tempqf removed below */
87577c478bd9Sstevel@tonic-gate 			failing = true;
87587c478bd9Sstevel@tonic-gate 		}
87597c478bd9Sstevel@tonic-gate 
87607c478bd9Sstevel@tonic-gate 	}
87617c478bd9Sstevel@tonic-gate 
87627c478bd9Sstevel@tonic-gate 	/* If changing file types, need to remove old type */
87637c478bd9Sstevel@tonic-gate 	if (!failing && oldtype != newtype)
87647c478bd9Sstevel@tonic-gate 	{
87657c478bd9Sstevel@tonic-gate 		if (xunlink(oldqf) < 0)
87667c478bd9Sstevel@tonic-gate 		{
87677c478bd9Sstevel@tonic-gate 			save_errno = errno;
87687c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
87697c478bd9Sstevel@tonic-gate 					     "Error removing %s: %s\n",
87707c478bd9Sstevel@tonic-gate 					     oldqf, sm_errstring(save_errno));
87717c478bd9Sstevel@tonic-gate 		}
87727c478bd9Sstevel@tonic-gate 	}
87737c478bd9Sstevel@tonic-gate 
87747c478bd9Sstevel@tonic-gate 	/* see if anything above failed */
87757c478bd9Sstevel@tonic-gate 	if (failing)
87767c478bd9Sstevel@tonic-gate 	{
87777c478bd9Sstevel@tonic-gate 		/* Something failed: remove new file, old file still there */
87787c478bd9Sstevel@tonic-gate 		(void) xunlink(tempqf);
87797c478bd9Sstevel@tonic-gate 	}
87807c478bd9Sstevel@tonic-gate 
87817c478bd9Sstevel@tonic-gate 	/*
87827c478bd9Sstevel@tonic-gate 	**  fsync() after file operations to make sure metadata is
87837c478bd9Sstevel@tonic-gate 	**  written to disk on filesystems in which renames are
87847c478bd9Sstevel@tonic-gate 	**  not guaranteed.  It's ok if they fail, mail won't be lost.
87857c478bd9Sstevel@tonic-gate 	*/
87867c478bd9Sstevel@tonic-gate 
87877c478bd9Sstevel@tonic-gate 	if (SuperSafe != SAFE_NO)
87887c478bd9Sstevel@tonic-gate 	{
87897c478bd9Sstevel@tonic-gate 		/* for soft-updates */
87907c478bd9Sstevel@tonic-gate 		(void) fsync(sm_io_getinfo(tempqfp,
87917c478bd9Sstevel@tonic-gate 					   SM_IO_WHAT_FD, NULL));
87927c478bd9Sstevel@tonic-gate 
87937c478bd9Sstevel@tonic-gate 		if (!failing)
87947c478bd9Sstevel@tonic-gate 		{
87957c478bd9Sstevel@tonic-gate 			/* for soft-updates */
87967c478bd9Sstevel@tonic-gate 			(void) fsync(sm_io_getinfo(oldqfp,
87977c478bd9Sstevel@tonic-gate 						   SM_IO_WHAT_FD, NULL));
87987c478bd9Sstevel@tonic-gate 		}
87997c478bd9Sstevel@tonic-gate 
88007c478bd9Sstevel@tonic-gate 		/* for other odd filesystems */
88017c478bd9Sstevel@tonic-gate 		SYNC_DIR(tempqf, false);
88027c478bd9Sstevel@tonic-gate 	}
88037c478bd9Sstevel@tonic-gate 
88047c478bd9Sstevel@tonic-gate 	/* Close up shop */
88057c478bd9Sstevel@tonic-gate 	RELEASE_QUEUE;
88067c478bd9Sstevel@tonic-gate 	if (tempqfp != NULL)
88077c478bd9Sstevel@tonic-gate 		(void) sm_io_close(tempqfp, SM_TIME_DEFAULT);
88087c478bd9Sstevel@tonic-gate 	if (oldqfp != NULL)
88097c478bd9Sstevel@tonic-gate 		(void) sm_io_close(oldqfp, SM_TIME_DEFAULT);
88107c478bd9Sstevel@tonic-gate 
88117c478bd9Sstevel@tonic-gate 	/* All went well */
88127c478bd9Sstevel@tonic-gate 	return !failing;
88137c478bd9Sstevel@tonic-gate }
88147c478bd9Sstevel@tonic-gate 
88157c478bd9Sstevel@tonic-gate /*
88167c478bd9Sstevel@tonic-gate **  QUARANTINE_QUEUE -- {un,}quarantine matching items in the queue
88177c478bd9Sstevel@tonic-gate **
88187c478bd9Sstevel@tonic-gate **	Read all matching queue items, add/remove quarantine
88197c478bd9Sstevel@tonic-gate **	reason, and requeue appropriately.
88207c478bd9Sstevel@tonic-gate **
88217c478bd9Sstevel@tonic-gate **	Parameters:
88227c478bd9Sstevel@tonic-gate **		reason -- quarantine reason, "." means unquarantine.
88237c478bd9Sstevel@tonic-gate **		qgrplimit -- limit to single queue group unless NOQGRP
88247c478bd9Sstevel@tonic-gate **
88257c478bd9Sstevel@tonic-gate **	Results:
88267c478bd9Sstevel@tonic-gate **		none.
88277c478bd9Sstevel@tonic-gate **
88287c478bd9Sstevel@tonic-gate **	Side Effects:
88297c478bd9Sstevel@tonic-gate **		Lots of changes to the queue.
88307c478bd9Sstevel@tonic-gate */
88317c478bd9Sstevel@tonic-gate 
88327c478bd9Sstevel@tonic-gate void
quarantine_queue(reason,qgrplimit)88337c478bd9Sstevel@tonic-gate quarantine_queue(reason, qgrplimit)
88347c478bd9Sstevel@tonic-gate 	char *reason;
88357c478bd9Sstevel@tonic-gate 	int qgrplimit;
88367c478bd9Sstevel@tonic-gate {
88377c478bd9Sstevel@tonic-gate 	int changed = 0;
88387c478bd9Sstevel@tonic-gate 	int qgrp;
88397c478bd9Sstevel@tonic-gate 
88407c478bd9Sstevel@tonic-gate 	/* Convert internal representation of unquarantine */
88417c478bd9Sstevel@tonic-gate 	if (reason != NULL && reason[0] == '.' && reason[1] == '\0')
88427c478bd9Sstevel@tonic-gate 		reason = NULL;
88437c478bd9Sstevel@tonic-gate 
88447c478bd9Sstevel@tonic-gate 	if (reason != NULL)
88457c478bd9Sstevel@tonic-gate 	{
88467c478bd9Sstevel@tonic-gate 		/* clean it */
88477c478bd9Sstevel@tonic-gate 		reason = newstr(denlstring(reason, true, true));
88487c478bd9Sstevel@tonic-gate 	}
88497c478bd9Sstevel@tonic-gate 
88507c478bd9Sstevel@tonic-gate 	for (qgrp = 0; qgrp < NumQueue && Queue[qgrp] != NULL; qgrp++)
88517c478bd9Sstevel@tonic-gate 	{
88527c478bd9Sstevel@tonic-gate 		int qdir;
88537c478bd9Sstevel@tonic-gate 
88547c478bd9Sstevel@tonic-gate 		if (qgrplimit != NOQGRP && qgrplimit != qgrp)
88557c478bd9Sstevel@tonic-gate 			continue;
88567c478bd9Sstevel@tonic-gate 
88577c478bd9Sstevel@tonic-gate 		for (qdir = 0; qdir < Queue[qgrp]->qg_numqueues; qdir++)
88587c478bd9Sstevel@tonic-gate 		{
88597c478bd9Sstevel@tonic-gate 			int i;
88607c478bd9Sstevel@tonic-gate 			int nrequests;
88617c478bd9Sstevel@tonic-gate 
88627c478bd9Sstevel@tonic-gate 			if (StopRequest)
88637c478bd9Sstevel@tonic-gate 				stop_sendmail();
88647c478bd9Sstevel@tonic-gate 
8865e9af4bc0SJohn Beck 			nrequests = gatherq(qgrp, qdir, true, NULL, NULL, NULL);
88667c478bd9Sstevel@tonic-gate 
88677c478bd9Sstevel@tonic-gate 			/* first see if there is anything */
88687c478bd9Sstevel@tonic-gate 			if (nrequests <= 0)
88697c478bd9Sstevel@tonic-gate 			{
88707c478bd9Sstevel@tonic-gate 				if (Verbose)
88717c478bd9Sstevel@tonic-gate 				{
88727c478bd9Sstevel@tonic-gate 					(void) sm_io_fprintf(smioout,
88737c478bd9Sstevel@tonic-gate 							     SM_TIME_DEFAULT, "%s: no matches\n",
88747c478bd9Sstevel@tonic-gate 							     qid_printqueue(qgrp, qdir));
88757c478bd9Sstevel@tonic-gate 				}
88767c478bd9Sstevel@tonic-gate 				continue;
88777c478bd9Sstevel@tonic-gate 			}
88787c478bd9Sstevel@tonic-gate 
88797c478bd9Sstevel@tonic-gate 			if (Verbose)
88807c478bd9Sstevel@tonic-gate 			{
88817c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(smioout,
88827c478bd9Sstevel@tonic-gate 						     SM_TIME_DEFAULT, "Processing %s:\n",
88837c478bd9Sstevel@tonic-gate 						     qid_printqueue(qgrp, qdir));
88847c478bd9Sstevel@tonic-gate 			}
88857c478bd9Sstevel@tonic-gate 
88867c478bd9Sstevel@tonic-gate 			for (i = 0; i < WorkListCount; i++)
88877c478bd9Sstevel@tonic-gate 			{
88887c478bd9Sstevel@tonic-gate 				ENVELOPE e;
88897c478bd9Sstevel@tonic-gate 
88907c478bd9Sstevel@tonic-gate 				if (StopRequest)
88917c478bd9Sstevel@tonic-gate 					stop_sendmail();
88927c478bd9Sstevel@tonic-gate 
88937c478bd9Sstevel@tonic-gate 				/* setup envelope */
88947c478bd9Sstevel@tonic-gate 				clearenvelope(&e, true, sm_rpool_new_x(NULL));
88957c478bd9Sstevel@tonic-gate 				e.e_id = WorkList[i].w_name + 2;
88967c478bd9Sstevel@tonic-gate 				e.e_qgrp = qgrp;
88977c478bd9Sstevel@tonic-gate 				e.e_qdir = qdir;
88987c478bd9Sstevel@tonic-gate 
88997c478bd9Sstevel@tonic-gate 				if (tTd(70, 101))
89007c478bd9Sstevel@tonic-gate 				{
89017c478bd9Sstevel@tonic-gate 					sm_io_fprintf(smioout, SM_TIME_DEFAULT,
89027c478bd9Sstevel@tonic-gate 						      "Would do %s\n", e.e_id);
89037c478bd9Sstevel@tonic-gate 					changed++;
89047c478bd9Sstevel@tonic-gate 				}
89057c478bd9Sstevel@tonic-gate 				else if (quarantine_queue_item(qgrp, qdir,
89067c478bd9Sstevel@tonic-gate 							       &e, reason))
89077c478bd9Sstevel@tonic-gate 					changed++;
89087c478bd9Sstevel@tonic-gate 
89097c478bd9Sstevel@tonic-gate 				/* clean up */
89107c478bd9Sstevel@tonic-gate 				sm_rpool_free(e.e_rpool);
89117c478bd9Sstevel@tonic-gate 				e.e_rpool = NULL;
89127c478bd9Sstevel@tonic-gate 			}
89137c478bd9Sstevel@tonic-gate 			if (WorkList != NULL)
89147c478bd9Sstevel@tonic-gate 				sm_free(WorkList); /* XXX */
89157c478bd9Sstevel@tonic-gate 			WorkList = NULL;
89167c478bd9Sstevel@tonic-gate 			WorkListSize = 0;
89177c478bd9Sstevel@tonic-gate 			WorkListCount = 0;
89187c478bd9Sstevel@tonic-gate 		}
89197c478bd9Sstevel@tonic-gate 	}
89207c478bd9Sstevel@tonic-gate 	if (Verbose)
89217c478bd9Sstevel@tonic-gate 	{
89227c478bd9Sstevel@tonic-gate 		if (changed == 0)
89237c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
89247c478bd9Sstevel@tonic-gate 					     "No changes\n");
89257c478bd9Sstevel@tonic-gate 		else
89267c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
89277c478bd9Sstevel@tonic-gate 					     "%d change%s\n",
89287c478bd9Sstevel@tonic-gate 					     changed,
89297c478bd9Sstevel@tonic-gate 					     changed == 1 ? "" : "s");
89307c478bd9Sstevel@tonic-gate 	}
89317c478bd9Sstevel@tonic-gate }
8932