xref: /illumos-gate/usr/src/cmd/sendmail/src/queue.c (revision fec46055)
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 		{
834058561cbSjbeck 			(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 		{
855058561cbSjbeck 			(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