main.c revision 132943
138032Speter/*
2132943Sgshapiro * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1490792Sgshapiro#define _DEFINE
1590792Sgshapiro#include <sendmail.h>
1690792Sgshapiro#include <sm/xtrap.h>
1790792Sgshapiro#include <sm/signal.h>
1890792Sgshapiro
1938032Speter#ifndef lint
2090792SgshapiroSM_UNUSED(static char copyright[]) =
21132943Sgshapiro"@(#) Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.\n\
2264562Sgshapiro	All rights reserved.\n\
2338032Speter     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
2438032Speter     Copyright (c) 1988, 1993\n\
2538032Speter	The Regents of the University of California.  All rights reserved.\n";
2664562Sgshapiro#endif /* ! lint */
2738032Speter
28132943SgshapiroSM_RCSID("@(#)$Id: main.c,v 8.939 2004/06/17 16:39:21 ca Exp $")
2938032Speter
3038032Speter
3164562Sgshapiro#if NETINET || NETINET6
3264562Sgshapiro# include <arpa/inet.h>
3364562Sgshapiro#endif /* NETINET || NETINET6 */
3464562Sgshapiro
3590792Sgshapiro/* for getcfname() */
3690792Sgshapiro#include <sendmail/pathnames.h>
3790792Sgshapiro
3890792Sgshapirostatic SM_DEBUG_T
3990792SgshapiroDebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
4090792Sgshapiro	"@(#)$Debug: no_persistent_restart - don't restart, log only $");
4190792Sgshapiro
4290792Sgshapirostatic void	dump_class __P((STAB *, int));
4390792Sgshapirostatic void	obsolete __P((char **));
4490792Sgshapirostatic void	testmodeline __P((char *, ENVELOPE *));
4590792Sgshapirostatic char	*getextenv __P((const char *));
4690792Sgshapirostatic void	sm_printoptions __P((char **));
4777349Sgshapirostatic SIGFUNC_DECL	intindebug __P((int));
4890792Sgshapirostatic SIGFUNC_DECL	sighup __P((int));
4990792Sgshapirostatic SIGFUNC_DECL	sigpipe __P((int));
5090792Sgshapirostatic SIGFUNC_DECL	sigterm __P((int));
5180785Sgshapiro#ifdef SIGUSR1
5277349Sgshapirostatic SIGFUNC_DECL	sigusr1 __P((int));
5390792Sgshapiro#endif /* SIGUSR1 */
5464562Sgshapiro
5538032Speter/*
5638032Speter**  SENDMAIL -- Post mail to a set of destinations.
5738032Speter**
5838032Speter**	This is the basic mail router.  All user mail programs should
5938032Speter**	call this routine to actually deliver mail.  Sendmail in
6038032Speter**	turn calls a bunch of mail servers that do the real work of
6138032Speter**	delivering the mail.
6238032Speter**
6364562Sgshapiro**	Sendmail is driven by settings read in from /etc/mail/sendmail.cf
6438032Speter**	(read by readcf.c).
6538032Speter**
6638032Speter**	Usage:
6738032Speter**		/usr/lib/sendmail [flags] addr ...
6838032Speter**
6938032Speter**		See the associated documentation for details.
7038032Speter**
7190792Sgshapiro**	Authors:
7238032Speter**		Eric Allman, UCB/INGRES (until 10/81).
7338032Speter**			     Britton-Lee, Inc., purveyors of fine
7438032Speter**				database computers (11/81 - 10/88).
7538032Speter**			     International Computer Science Institute
7638032Speter**				(11/88 - 9/89).
7738032Speter**			     UCB/Mammoth Project (10/89 - 7/95).
7838032Speter**			     InReference, Inc. (8/95 - 1/97).
7938032Speter**			     Sendmail, Inc. (1/98 - present).
80111823Sgshapiro**		The support of my employers is gratefully acknowledged.
8138032Speter**			Few of them (Britton-Lee in particular) have had
8238032Speter**			anything to gain from my involvement in this project.
8390792Sgshapiro**
8490792Sgshapiro**		Gregory Neil Shapiro,
8590792Sgshapiro**			Worcester Polytechnic Institute	(until 3/98).
8690792Sgshapiro**			Sendmail, Inc. (3/98 - present).
8790792Sgshapiro**
8890792Sgshapiro**		Claus Assmann,
8990792Sgshapiro**			Sendmail, Inc. (12/98 - present).
9038032Speter*/
9138032Speter
9238032Speterchar		*FullName;	/* sender's full name */
9338032SpeterENVELOPE	BlankEnvelope;	/* a "blank" envelope */
9464562Sgshapirostatic ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
9538032SpeterADDRESS		NullAddress =	/* a null address */
9638032Speter		{ "", "", NULL, "" };
9738032Speterchar		*CommandLineArgs;	/* command line args for pid file */
9890792Sgshapirobool		Warn_Q_option = false;	/* warn about Q option use */
9964562Sgshapirostatic int	MissingFds = 0;	/* bit map of fds missing on startup */
10090792Sgshapirochar		*Mbdb = "pw";	/* mailbox database defaults to /etc/passwd */
10138032Speter
10238032Speter#ifdef NGROUPS_MAX
10338032SpeterGIDSET_T	InitialGidSet[NGROUPS_MAX];
10464562Sgshapiro#endif /* NGROUPS_MAX */
10538032Speter
10690792Sgshapiro#define MAXCONFIGLEVEL	10	/* highest config version level known */
10738032Speter
10864562Sgshapiro#if SASL
10964562Sgshapirostatic sasl_callback_t srvcallbacks[] =
11064562Sgshapiro{
11164562Sgshapiro	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
11264562Sgshapiro	{	SASL_CB_PROXY_POLICY,	&proxy_policy,	NULL	},
11364562Sgshapiro	{	SASL_CB_LIST_END,	NULL,		NULL	}
11464562Sgshapiro};
11564562Sgshapiro#endif /* SASL */
11664562Sgshapiro
11790792Sgshapirounsigned int	SubmitMode;
11890792Sgshapiroint		SyslogPrefixLen; /* estimated length of syslog prefix */
11990792Sgshapiro#define PIDLEN		6	/* pid length for computing SyslogPrefixLen */
12090792Sgshapiro#ifndef SL_FUDGE
12190792Sgshapiro# define SL_FUDGE	10	/* fudge offset for SyslogPrefixLen */
12290792Sgshapiro#endif /* ! SL_FUDGE */
12390792Sgshapiro#define SLDLL		8	/* est. length of default syslog label */
12464562Sgshapiro
12590792Sgshapiro
12690792Sgshapiro/* Some options are dangerous to allow users to use in non-submit mode */
12790792Sgshapiro#define CHECK_AGAINST_OPMODE(cmd)					\
12890792Sgshapiro{									\
12990792Sgshapiro	if (extraprivs &&						\
13090792Sgshapiro	    OpMode != MD_DELIVER && OpMode != MD_SMTP &&		\
131112810Sgshapiro	    OpMode != MD_ARPAFTP &&					\
13290792Sgshapiro	    OpMode != MD_VERIFY && OpMode != MD_TEST)			\
13390792Sgshapiro	{								\
13490792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
13590792Sgshapiro				     "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
13690792Sgshapiro		       (cmd));						\
13790792Sgshapiro		break;							\
13890792Sgshapiro	}								\
13990792Sgshapiro	if (extraprivs && queuerun)					\
14090792Sgshapiro	{								\
14190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
14290792Sgshapiro				     "WARNING: Ignoring submission mode -%c option with -q\n", \
14390792Sgshapiro		       (cmd));						\
14490792Sgshapiro		break;							\
14590792Sgshapiro	}								\
14690792Sgshapiro}
14790792Sgshapiro
14838032Speterint
14938032Spetermain(argc, argv, envp)
15038032Speter	int argc;
15138032Speter	char **argv;
15238032Speter	char **envp;
15338032Speter{
15438032Speter	register char *p;
15538032Speter	char **av;
15638032Speter	extern char Version[];
15738032Speter	char *ep, *from;
15838032Speter	STAB *st;
15938032Speter	register int i;
16038032Speter	int j;
16164562Sgshapiro	int dp;
16290792Sgshapiro	int fill_errno;
16390792Sgshapiro	int qgrp = NOQGRP;		/* queue group to process */
16490792Sgshapiro	bool safecf = true;
16564562Sgshapiro	BITMAP256 *p_flags = NULL;	/* daemon flags */
16690792Sgshapiro	bool warn_C_flag = false;
16790792Sgshapiro	bool auth = true;		/* whether to set e_auth_param */
16838032Speter	char warn_f_flag = '\0';
16990792Sgshapiro	bool run_in_foreground = false;	/* -bD mode */
17090792Sgshapiro	bool queuerun = false, debug = false;
17138032Speter	struct passwd *pw;
17238032Speter	struct hostent *hp;
17338032Speter	char *nullserver = NULL;
17464562Sgshapiro	char *authinfo = NULL;
17564562Sgshapiro	char *sysloglabel = NULL;	/* label for syslog */
17690792Sgshapiro	char *conffile = NULL;		/* name of .cf file */
17790792Sgshapiro	char *queuegroup = NULL;	/* queue group to process */
17890792Sgshapiro	char *quarantining = NULL;	/* quarantine queue items? */
17990792Sgshapiro	bool extraprivs;
18090792Sgshapiro	bool forged, negate;
18190792Sgshapiro	bool queuepersistent = false;	/* queue runner process runs forever */
18290792Sgshapiro	bool foregroundqueue = false;	/* queue run in foreground */
18390792Sgshapiro	bool save_val;			/* to save some bool var. */
18490792Sgshapiro	int cftype;			/* which cf file to use? */
185132943Sgshapiro	SM_FILE_T *smdebug;
18690792Sgshapiro	static time_t starttime = 0;	/* when was process started */
18764562Sgshapiro	struct stat traf_st;		/* for TrafficLog FIFO check */
18890792Sgshapiro	char buf[MAXLINE];
18938032Speter	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
19038032Speter	static char rnamebuf[MAXNAME];	/* holds RealUserName */
19138032Speter	char *emptyenviron[1];
19290792Sgshapiro#if STARTTLS
19366494Sgshapiro	bool tls_ok;
19490792Sgshapiro#endif /* STARTTLS */
19538032Speter	QUEUE_CHAR *new;
19690792Sgshapiro	ENVELOPE *e;
19738032Speter	extern int DtableSize;
19838032Speter	extern int optind;
19938032Speter	extern int opterr;
20038032Speter	extern char *optarg;
20138032Speter	extern char **environ;
20290792Sgshapiro#if SASL
20390792Sgshapiro	extern void sm_sasl_init __P((void));
20490792Sgshapiro#endif /* SASL */
20538032Speter
20690792Sgshapiro#if USE_ENVIRON
20790792Sgshapiro	envp = environ;
20890792Sgshapiro#endif /* USE_ENVIRON */
20990792Sgshapiro
21090792Sgshapiro	/* turn off profiling */
21190792Sgshapiro	SM_PROF(0);
21290792Sgshapiro
21390792Sgshapiro	/* install default exception handler */
21490792Sgshapiro	sm_exc_newthread(fatal_error);
21590792Sgshapiro
216110560Sgshapiro	/* set the default in/out channel so errors reported to screen */
217110560Sgshapiro	InChannel = smioin;
218110560Sgshapiro	OutChannel = smioout;
219110560Sgshapiro
22038032Speter	/*
22138032Speter	**  Check to see if we reentered.
22238032Speter	**	This would normally happen if e_putheader or e_putbody
22338032Speter	**	were NULL when invoked.
22438032Speter	*/
22538032Speter
22690792Sgshapiro	if (starttime != 0)
22738032Speter	{
22838032Speter		syserr("main: reentered!");
22938032Speter		abort();
23038032Speter	}
23190792Sgshapiro	starttime = curtime();
23238032Speter
23338032Speter	/* avoid null pointer dereferences */
23438032Speter	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
23538032Speter
23690792Sgshapiro	RealUid = getuid();
23790792Sgshapiro	RealGid = getgid();
23877349Sgshapiro
23990792Sgshapiro	/* Check if sendmail is running with extra privs */
24090792Sgshapiro	extraprivs = (RealUid != 0 &&
24190792Sgshapiro		      (geteuid() != getuid() || getegid() != getgid()));
24277349Sgshapiro
24390792Sgshapiro	CurrentPid = getpid();
24438032Speter
24590792Sgshapiro	/* get whatever .cf file is right for the opmode */
24690792Sgshapiro	cftype = SM_GET_RIGHT_CF;
24764562Sgshapiro
24838032Speter	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
24938032Speter	DtableSize = getdtsize();
25038032Speter	if (DtableSize > 256)
25138032Speter		DtableSize = 256;
25238032Speter
25338032Speter	/*
25438032Speter	**  Be sure we have enough file descriptors.
25538032Speter	**	But also be sure that 0, 1, & 2 are open.
25638032Speter	*/
25738032Speter
25890792Sgshapiro	/* reset errno and fill_errno; the latter is used way down below */
25990792Sgshapiro	errno = fill_errno = 0;
26038032Speter	fill_fd(STDIN_FILENO, NULL);
26190792Sgshapiro	if (errno != 0)
26290792Sgshapiro		fill_errno = errno;
26338032Speter	fill_fd(STDOUT_FILENO, NULL);
26490792Sgshapiro	if (errno != 0)
26590792Sgshapiro		fill_errno = errno;
26638032Speter	fill_fd(STDERR_FILENO, NULL);
26790792Sgshapiro	if (errno != 0)
26890792Sgshapiro		fill_errno = errno;
26938032Speter
270132943Sgshapiro	sm_closefrom(STDERR_FILENO + 1, DtableSize);
27138032Speter	errno = 0;
272132943Sgshapiro	smdebug = NULL;
27338032Speter
27438032Speter#if LOG
27590792Sgshapiro# ifndef SM_LOG_STR
27690792Sgshapiro#  define SM_LOG_STR	"sendmail"
27790792Sgshapiro# endif /* ! SM_LOG_STR */
27864562Sgshapiro#  ifdef LOG_MAIL
27990792Sgshapiro	openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
28064562Sgshapiro#  else /* LOG_MAIL */
28190792Sgshapiro	openlog(SM_LOG_STR, LOG_PID);
28264562Sgshapiro#  endif /* LOG_MAIL */
28364562Sgshapiro#endif /* LOG */
28438032Speter
28590792Sgshapiro	/*
28690792Sgshapiro	**  Seed the random number generator.
28790792Sgshapiro	**  Used for queue file names, picking a queue directory, and
28890792Sgshapiro	**  MX randomization.
28990792Sgshapiro	*/
29038032Speter
29190792Sgshapiro	seed_random();
29238032Speter
29390792Sgshapiro	/* do machine-dependent initializations */
29490792Sgshapiro	init_md(argc, argv);
29590792Sgshapiro
29690792Sgshapiro
29790792Sgshapiro	SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
29890792Sgshapiro
29938032Speter	/* reset status from syserr() calls for missing file descriptors */
30038032Speter	Errors = 0;
30138032Speter	ExitStat = EX_OK;
30238032Speter
30364562Sgshapiro	SubmitMode = SUBMIT_UNKNOWN;
30438032Speter#if XDEBUG
30538032Speter	checkfd012("after openlog");
30664562Sgshapiro#endif /* XDEBUG */
30738032Speter
30890792Sgshapiro	tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1");
30938032Speter
31038032Speter#ifdef NGROUPS_MAX
31138032Speter	/* save initial group set for future checks */
31238032Speter	i = getgroups(NGROUPS_MAX, InitialGidSet);
31390792Sgshapiro	if (i <= 0)
31490792Sgshapiro	{
31538032Speter		InitialGidSet[0] = (GID_T) -1;
31690792Sgshapiro		i = 0;
31790792Sgshapiro	}
31838032Speter	while (i < NGROUPS_MAX)
31938032Speter		InitialGidSet[i++] = InitialGidSet[0];
32064562Sgshapiro#endif /* NGROUPS_MAX */
32138032Speter
32238032Speter	/* drop group id privileges (RunAsUser not yet set) */
32390792Sgshapiro	dp = drop_privileges(false);
32464562Sgshapiro	setstat(dp);
32538032Speter
32690792Sgshapiro#ifdef SIGUSR1
32777349Sgshapiro	/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
32894334Sgshapiro	if (!extraprivs)
32977349Sgshapiro	{
33077349Sgshapiro		/* arrange to dump state on user-1 signal */
33190792Sgshapiro		(void) sm_signal(SIGUSR1, sigusr1);
33277349Sgshapiro	}
33394334Sgshapiro	else
33494334Sgshapiro	{
33594334Sgshapiro		/* ignore user-1 signal */
33694334Sgshapiro		(void) sm_signal(SIGUSR1, SIG_IGN);
33794334Sgshapiro	}
33890792Sgshapiro#endif /* SIGUSR1 */
33938032Speter
34038032Speter	/* initialize for setproctitle */
34138032Speter	initsetproctitle(argc, argv, envp);
34238032Speter
34338032Speter	/* Handle any non-getoptable constructions. */
34438032Speter	obsolete(argv);
34538032Speter
34638032Speter	/*
34738032Speter	**  Do a quick prescan of the argument list.
34838032Speter	*/
34938032Speter
35064562Sgshapiro
35190792Sgshapiro	/* find initial opMode */
35290792Sgshapiro	OpMode = MD_DELIVER;
35390792Sgshapiro	av = argv;
35490792Sgshapiro	p = strrchr(*av, '/');
35590792Sgshapiro	if (p++ == NULL)
35690792Sgshapiro		p = *av;
35790792Sgshapiro	if (strcmp(p, "newaliases") == 0)
35890792Sgshapiro		OpMode = MD_INITALIAS;
35990792Sgshapiro	else if (strcmp(p, "mailq") == 0)
36090792Sgshapiro		OpMode = MD_PRINT;
36190792Sgshapiro	else if (strcmp(p, "smtpd") == 0)
36290792Sgshapiro		OpMode = MD_DAEMON;
36390792Sgshapiro	else if (strcmp(p, "hoststat") == 0)
36490792Sgshapiro		OpMode = MD_HOSTSTAT;
36590792Sgshapiro	else if (strcmp(p, "purgestat") == 0)
36690792Sgshapiro		OpMode = MD_PURGESTAT;
36790792Sgshapiro
368132943Sgshapiro#if defined(__osf__) || defined(_AIX3)
369132943Sgshapiro# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:x"
370132943Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */
371132943Sgshapiro#if defined(sony_news)
372132943Sgshapiro# define OPTIONS	"A:B:b:C:cD:d:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
373132943Sgshapiro#endif /* defined(sony_news) */
374132943Sgshapiro#ifndef OPTIONS
375132943Sgshapiro# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
376132943Sgshapiro#endif /* ! OPTIONS */
37790792Sgshapiro
378112810Sgshapiro	/* Set to 0 to allow -b; need to check optarg before using it! */
37938032Speter	opterr = 0;
38038032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
38138032Speter	{
38238032Speter		switch (j)
38338032Speter		{
38490792Sgshapiro		  case 'b':	/* operations mode */
38594334Sgshapiro			j = (optarg == NULL) ? ' ' : *optarg;
38694334Sgshapiro			switch (j)
38738032Speter			{
38890792Sgshapiro			  case MD_DAEMON:
38990792Sgshapiro			  case MD_FGDAEMON:
39090792Sgshapiro			  case MD_SMTP:
39190792Sgshapiro			  case MD_INITALIAS:
39290792Sgshapiro			  case MD_DELIVER:
39390792Sgshapiro			  case MD_VERIFY:
39490792Sgshapiro			  case MD_TEST:
39590792Sgshapiro			  case MD_PRINT:
39690792Sgshapiro			  case MD_PRINTNQE:
39790792Sgshapiro			  case MD_HOSTSTAT:
39890792Sgshapiro			  case MD_PURGESTAT:
39990792Sgshapiro			  case MD_ARPAFTP:
40090792Sgshapiro				OpMode = j;
40138032Speter				break;
40290792Sgshapiro
40390792Sgshapiro			  case MD_FREEZE:
40490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
40590792Sgshapiro						     "Frozen configurations unsupported\n");
40690792Sgshapiro				return EX_USAGE;
40790792Sgshapiro
40890792Sgshapiro			  default:
40990792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
41090792Sgshapiro						     "Invalid operation mode %c\n",
41190792Sgshapiro						     j);
41290792Sgshapiro				return EX_USAGE;
41338032Speter			}
41490792Sgshapiro			break;
41590792Sgshapiro
416132943Sgshapiro		  case 'D':
417132943Sgshapiro			if (debug)
418132943Sgshapiro			{
419132943Sgshapiro				errno = 0;
420132943Sgshapiro				syserr("-D file must be before -d");
421132943Sgshapiro				ExitStat = EX_USAGE;
422132943Sgshapiro				break;
423132943Sgshapiro			}
424132943Sgshapiro			dp = drop_privileges(true);
425132943Sgshapiro			setstat(dp);
426132943Sgshapiro			smdebug = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
427132943Sgshapiro					    optarg, SM_IO_APPEND, NULL);
428132943Sgshapiro			if (smdebug == NULL)
429132943Sgshapiro			{
430132943Sgshapiro				syserr("cannot open %s", optarg);
431132943Sgshapiro				ExitStat = EX_CANTCREAT;
432132943Sgshapiro				break;
433132943Sgshapiro			}
434132943Sgshapiro			sm_debug_setfile(smdebug);
435132943Sgshapiro			break;
436132943Sgshapiro
43790792Sgshapiro		  case 'd':
43890792Sgshapiro			debug = true;
43938032Speter			tTflag(optarg);
440132943Sgshapiro			(void) sm_io_setvbuf(sm_debug_file(), SM_TIME_DEFAULT,
44190792Sgshapiro					     (char *) NULL, SM_IO_NBF,
44290792Sgshapiro					     SM_IO_BUFSIZ);
44338032Speter			break;
44464562Sgshapiro
44564562Sgshapiro		  case 'G':	/* relay (gateway) submission */
44690792Sgshapiro			SubmitMode = SUBMIT_MTA;
44764562Sgshapiro			break;
44864562Sgshapiro
44964562Sgshapiro		  case 'L':
450112810Sgshapiro			if (optarg == NULL)
451112810Sgshapiro			{
452112810Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
453112810Sgshapiro						     "option requires an argument -- '%c'",
454112810Sgshapiro						     (char) j);
455112810Sgshapiro				return EX_USAGE;
456112810Sgshapiro			}
457132943Sgshapiro			j = SM_MIN(strlen(optarg), 32) + 1;
45864562Sgshapiro			sysloglabel = xalloc(j);
45990792Sgshapiro			(void) sm_strlcpy(sysloglabel, optarg, j);
46090792Sgshapiro			SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
46190792Sgshapiro					  SL_FUDGE + j;
46264562Sgshapiro			break;
46364562Sgshapiro
46490792Sgshapiro		  case 'Q':
46590792Sgshapiro		  case 'q':
46690792Sgshapiro			/* just check if it is there */
46790792Sgshapiro			queuerun = true;
46864562Sgshapiro			break;
46938032Speter		}
47038032Speter	}
47138032Speter	opterr = 1;
47238032Speter
47390792Sgshapiro	/* Don't leak queue information via debug flags */
47490792Sgshapiro	if (extraprivs && queuerun && debug)
47590792Sgshapiro	{
47690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
47790792Sgshapiro				     "WARNING: Can not use -d with -q.  Disabling debugging.\n");
478132943Sgshapiro		sm_debug_close();
47990792Sgshapiro		sm_debug_setfile(NULL);
48090792Sgshapiro		(void) memset(tTdvect, '\0', sizeof tTdvect);
48190792Sgshapiro	}
48290792Sgshapiro
48377349Sgshapiro#if LOG
48464562Sgshapiro	if (sysloglabel != NULL)
48564562Sgshapiro	{
48677349Sgshapiro		/* Sanitize the string */
48777349Sgshapiro		for (p = sysloglabel; *p != '\0'; p++)
48877349Sgshapiro		{
48977349Sgshapiro			if (!isascii(*p) || !isprint(*p) || *p == '%')
49077349Sgshapiro				*p = '*';
49177349Sgshapiro		}
49264562Sgshapiro		closelog();
49364562Sgshapiro#  ifdef LOG_MAIL
49464562Sgshapiro		openlog(sysloglabel, LOG_PID, LOG_MAIL);
49564562Sgshapiro#  else /* LOG_MAIL */
49664562Sgshapiro		openlog(sysloglabel, LOG_PID);
49764562Sgshapiro#  endif /* LOG_MAIL */
49877349Sgshapiro	}
49964562Sgshapiro#endif /* LOG */
50064562Sgshapiro
50138032Speter	/* set up the blank envelope */
50238032Speter	BlankEnvelope.e_puthdr = putheader;
50338032Speter	BlankEnvelope.e_putbody = putbody;
50438032Speter	BlankEnvelope.e_xfp = NULL;
50538032Speter	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
50638032Speter	CurEnv = &BlankEnvelope;
50738032Speter	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
50838032Speter
50938032Speter	/*
51038032Speter	**  Set default values for variables.
51138032Speter	**	These cannot be in initialized data space.
51238032Speter	*/
51338032Speter
51438032Speter	setdefaults(&BlankEnvelope);
51590792Sgshapiro	initmacros(&BlankEnvelope);
51638032Speter
51790792Sgshapiro	/* reset macro */
51890792Sgshapiro	set_op_mode(OpMode);
51938032Speter
52038032Speter	pw = sm_getpwuid(RealUid);
52138032Speter	if (pw != NULL)
52290792Sgshapiro		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
52338032Speter	else
52490792Sgshapiro		(void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
52590792Sgshapiro				   (int) RealUid);
52664562Sgshapiro
52738032Speter	RealUserName = rnamebuf;
52838032Speter
52938032Speter	if (tTd(0, 101))
53038032Speter	{
53190792Sgshapiro		sm_dprintf("Version %s\n", Version);
53290792Sgshapiro		finis(false, true, EX_OK);
53390792Sgshapiro		/* NOTREACHED */
53438032Speter	}
53538032Speter
53638032Speter	/*
53790792Sgshapiro	**  if running non-set-user-ID binary as non-root, pretend
53838032Speter	**  we are the RunAsUid
53938032Speter	*/
54077349Sgshapiro
54138032Speter	if (RealUid != 0 && geteuid() == RealUid)
54238032Speter	{
54338032Speter		if (tTd(47, 1))
54490792Sgshapiro			sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
54590792Sgshapiro				   (int) RealUid);
54638032Speter		RunAsUid = RealUid;
54738032Speter	}
54838032Speter	else if (geteuid() != 0)
54938032Speter		RunAsUid = geteuid();
55038032Speter
55190792Sgshapiro	EffGid = getegid();
55290792Sgshapiro	if (RealUid != 0 && EffGid == RealGid)
55338032Speter		RunAsGid = RealGid;
55438032Speter
55538032Speter	if (tTd(47, 5))
55638032Speter	{
55790792Sgshapiro		sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
55890792Sgshapiro			   (int) geteuid(), (int) getuid(),
55990792Sgshapiro			   (int) getegid(), (int) getgid());
56090792Sgshapiro		sm_dprintf("main: RunAsUser = %d:%d\n",
56190792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
56238032Speter	}
56338032Speter
56438032Speter	/* save command line arguments */
56564562Sgshapiro	j = 0;
56638032Speter	for (av = argv; *av != NULL; )
56764562Sgshapiro		j += strlen(*av++) + 1;
56838032Speter	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
56964562Sgshapiro	CommandLineArgs = xalloc(j);
57038032Speter	p = CommandLineArgs;
57138032Speter	for (av = argv, i = 0; *av != NULL; )
57238032Speter	{
57364562Sgshapiro		int h;
57464562Sgshapiro
57538032Speter		SaveArgv[i++] = newstr(*av);
57638032Speter		if (av != argv)
57738032Speter			*p++ = ' ';
57890792Sgshapiro		(void) sm_strlcpy(p, *av++, j);
57964562Sgshapiro		h = strlen(p);
58064562Sgshapiro		p += h;
58164562Sgshapiro		j -= h + 1;
58238032Speter	}
58338032Speter	SaveArgv[i] = NULL;
58438032Speter
58538032Speter	if (tTd(0, 1))
58638032Speter	{
58738032Speter		extern char *CompileOptions[];
58838032Speter
58990792Sgshapiro		sm_dprintf("Version %s\n Compiled with:", Version);
59090792Sgshapiro		sm_printoptions(CompileOptions);
59138032Speter	}
59238032Speter	if (tTd(0, 10))
59338032Speter	{
59438032Speter		extern char *OsCompileOptions[];
59538032Speter
59690792Sgshapiro		sm_dprintf("    OS Defines:");
59790792Sgshapiro		sm_printoptions(OsCompileOptions);
59838032Speter#ifdef _PATH_UNIX
59990792Sgshapiro		sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
60064562Sgshapiro#endif /* _PATH_UNIX */
60190792Sgshapiro
60290792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MSP)\n",
60390792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
60490792Sgshapiro				     conffile));
60590792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MTA)\n",
60690792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
60790792Sgshapiro				     conffile));
60890792Sgshapiro		sm_dprintf("      Pid file:\t%s (default)\n", PidFile);
60938032Speter	}
61038032Speter
61190792Sgshapiro	if (tTd(0, 12))
61290792Sgshapiro	{
61390792Sgshapiro		extern char *SmCompileOptions[];
61438032Speter
61590792Sgshapiro		sm_dprintf(" libsm Defines:");
61690792Sgshapiro		sm_printoptions(SmCompileOptions);
61790792Sgshapiro	}
61890792Sgshapiro
61990792Sgshapiro	if (tTd(0, 13))
62090792Sgshapiro	{
62190792Sgshapiro		extern char *FFRCompileOptions[];
62290792Sgshapiro
62390792Sgshapiro		sm_dprintf("   FFR Defines:");
62490792Sgshapiro		sm_printoptions(FFRCompileOptions);
62590792Sgshapiro	}
62690792Sgshapiro
62738032Speter	/* clear sendmail's environment */
62838032Speter	ExternalEnviron = environ;
62938032Speter	emptyenviron[0] = NULL;
63038032Speter	environ = emptyenviron;
63138032Speter
63238032Speter	/*
63342575Speter	**  restore any original TZ setting until TimeZoneSpec has been
63442575Speter	**  determined - or early log messages may get bogus time stamps
63538032Speter	*/
63690792Sgshapiro
63738032Speter	if ((p = getextenv("TZ")) != NULL)
63838032Speter	{
63938032Speter		char *tz;
64038032Speter		int tzlen;
64138032Speter
64290792Sgshapiro		/* XXX check for reasonable length? */
64338032Speter		tzlen = strlen(p) + 4;
64438032Speter		tz = xalloc(tzlen);
64590792Sgshapiro		(void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
64690792Sgshapiro
64790792Sgshapiro		/* XXX check return code? */
64864562Sgshapiro		(void) putenv(tz);
64938032Speter	}
65038032Speter
65138032Speter	/* prime the child environment */
65238032Speter	setuserenv("AGENT", "sendmail");
65338032Speter
65490792Sgshapiro	(void) sm_signal(SIGPIPE, SIG_IGN);
65538032Speter	OldUmask = umask(022);
65638032Speter	FullName = getextenv("NAME");
65795154Sgshapiro	if (FullName != NULL)
65895154Sgshapiro		FullName = newstr(FullName);
65938032Speter
66038032Speter	/*
66138032Speter	**  Initialize name server if it is going to be used.
66238032Speter	*/
66338032Speter
66438032Speter#if NAMED_BIND
66538032Speter	if (!bitset(RES_INIT, _res.options))
66664562Sgshapiro		(void) res_init();
66738032Speter	if (tTd(8, 8))
66838032Speter		_res.options |= RES_DEBUG;
66938032Speter	else
67038032Speter		_res.options &= ~RES_DEBUG;
67138032Speter# ifdef RES_NOALIASES
67238032Speter	_res.options |= RES_NOALIASES;
67364562Sgshapiro# endif /* RES_NOALIASES */
67464562Sgshapiro	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
67564562Sgshapiro	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
67664562Sgshapiro	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
67764562Sgshapiro	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
67864562Sgshapiro	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
67964562Sgshapiro	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
68064562Sgshapiro#endif /* NAMED_BIND */
68138032Speter
68238032Speter	errno = 0;
68338032Speter	from = NULL;
68438032Speter
68538032Speter	/* initialize some macros, etc. */
68690792Sgshapiro	init_vendor_macros(&BlankEnvelope);
68738032Speter
68838032Speter	/* version */
68990792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
69038032Speter
69138032Speter	/* hostname */
69238032Speter	hp = myhostname(jbuf, sizeof jbuf);
69338032Speter	if (jbuf[0] != '\0')
69438032Speter	{
69590792Sgshapiro		struct utsname utsname;
69638032Speter
69738032Speter		if (tTd(0, 4))
69890792Sgshapiro			sm_dprintf("Canonical name: %s\n", jbuf);
69990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
70090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
70138032Speter		setclass('w', jbuf);
70238032Speter
70338032Speter		p = strchr(jbuf, '.');
704132943Sgshapiro		if (p != NULL && p[1] != '\0')
705132943Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', &p[1]);
70638032Speter
70738032Speter		if (uname(&utsname) >= 0)
70838032Speter			p = utsname.nodename;
70938032Speter		else
71038032Speter		{
71138032Speter			if (tTd(0, 22))
71290792Sgshapiro				sm_dprintf("uname failed (%s)\n",
71390792Sgshapiro					   sm_errstring(errno));
71438032Speter			makelower(jbuf);
71538032Speter			p = jbuf;
71638032Speter		}
71738032Speter		if (tTd(0, 4))
71890792Sgshapiro			sm_dprintf(" UUCP nodename: %s\n", p);
71990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
72038032Speter		setclass('k', p);
72138032Speter		setclass('w', p);
72238032Speter	}
72338032Speter	if (hp != NULL)
72438032Speter	{
72538032Speter		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
72638032Speter		{
72738032Speter			if (tTd(0, 4))
72890792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", *av);
72938032Speter			setclass('w', *av);
73038032Speter		}
73164562Sgshapiro#if NETINET || NETINET6
73290792Sgshapiro		for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
73338032Speter		{
73464562Sgshapiro# if NETINET6
73564562Sgshapiro			char *addr;
73664562Sgshapiro			char buf6[INET6_ADDRSTRLEN];
73764562Sgshapiro			struct in6_addr ia6;
73864562Sgshapiro# endif /* NETINET6 */
73964562Sgshapiro# if NETINET
74064562Sgshapiro			struct in_addr ia;
74164562Sgshapiro# endif /* NETINET */
74264562Sgshapiro			char ipbuf[103];
74364562Sgshapiro
74464562Sgshapiro			ipbuf[0] = '\0';
74564562Sgshapiro			switch (hp->h_addrtype)
74638032Speter			{
74764562Sgshapiro# if NETINET
74864562Sgshapiro			  case AF_INET:
74964562Sgshapiro				if (hp->h_length != INADDRSZ)
75064562Sgshapiro					break;
75138032Speter
75264562Sgshapiro				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
75390792Sgshapiro				(void) sm_snprintf(ipbuf, sizeof ipbuf,
75490792Sgshapiro						   "[%.100s]", inet_ntoa(ia));
75564562Sgshapiro				break;
75664562Sgshapiro# endif /* NETINET */
75764562Sgshapiro
75864562Sgshapiro# if NETINET6
75964562Sgshapiro			  case AF_INET6:
76064562Sgshapiro				if (hp->h_length != IN6ADDRSZ)
76164562Sgshapiro					break;
76264562Sgshapiro
76364562Sgshapiro				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
76464562Sgshapiro				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
76564562Sgshapiro				if (addr != NULL)
76690792Sgshapiro					(void) sm_snprintf(ipbuf, sizeof ipbuf,
76790792Sgshapiro							   "[%.100s]", addr);
76864562Sgshapiro				break;
76964562Sgshapiro# endif /* NETINET6 */
77038032Speter			}
77164562Sgshapiro			if (ipbuf[0] == '\0')
77264562Sgshapiro				break;
77364562Sgshapiro
77464562Sgshapiro			if (tTd(0, 4))
77590792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", ipbuf);
77664562Sgshapiro			setclass('w', ipbuf);
77738032Speter		}
77864562Sgshapiro#endif /* NETINET || NETINET6 */
77990792Sgshapiro#if NETINET6
78071345Sgshapiro		freehostent(hp);
78171345Sgshapiro		hp = NULL;
78290792Sgshapiro#endif /* NETINET6 */
78338032Speter	}
78438032Speter
78538032Speter	/* current time */
78690792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
78790792Sgshapiro
78864562Sgshapiro	/* current load average */
78990792Sgshapiro	sm_getla();
79038032Speter
79138032Speter	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
79238032Speter	QueueLimitSender = (QUEUE_CHAR *) NULL;
79338032Speter	QueueLimitId = (QUEUE_CHAR *) NULL;
79490792Sgshapiro	QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
79538032Speter
79638032Speter	/*
79742575Speter	**  Crack argv.
79838032Speter	*/
79938032Speter
80038032Speter	optind = 1;
80138032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
80238032Speter	{
80338032Speter		switch (j)
80438032Speter		{
80538032Speter		  case 'b':	/* operations mode */
80690792Sgshapiro			/* already done */
80790792Sgshapiro			break;
80838032Speter
80990792Sgshapiro		  case 'A':	/* use Alternate sendmail/submit.cf */
81090792Sgshapiro			cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
81190792Sgshapiro						  : SM_GET_SENDMAIL_CF;
81238032Speter			break;
81338032Speter
81438032Speter		  case 'B':	/* body type */
81590792Sgshapiro			CHECK_AGAINST_OPMODE(j);
81690792Sgshapiro			BlankEnvelope.e_bodytype = newstr(optarg);
81738032Speter			break;
81838032Speter
81938032Speter		  case 'C':	/* select configuration file (already done) */
82038032Speter			if (RealUid != 0)
82190792Sgshapiro				warn_C_flag = true;
82290792Sgshapiro			conffile = newstr(optarg);
82390792Sgshapiro			dp = drop_privileges(true);
82464562Sgshapiro			setstat(dp);
82590792Sgshapiro			safecf = false;
82638032Speter			break;
82738032Speter
828132943Sgshapiro		  case 'D':
82990792Sgshapiro		  case 'd':	/* debugging */
83090792Sgshapiro			/* already done */
83138032Speter			break;
83238032Speter
83338032Speter		  case 'f':	/* from address */
83438032Speter		  case 'r':	/* obsolete -f flag */
83590792Sgshapiro			CHECK_AGAINST_OPMODE(j);
83638032Speter			if (from != NULL)
83738032Speter			{
83838032Speter				usrerr("More than one \"from\" person");
83938032Speter				ExitStat = EX_USAGE;
84038032Speter				break;
84138032Speter			}
842110560Sgshapiro			if (optarg[0] == '\0')
843110560Sgshapiro				from = newstr("<>");
844110560Sgshapiro			else
845110560Sgshapiro				from = newstr(denlstring(optarg, true, true));
84638032Speter			if (strcmp(RealUserName, from) != 0)
84738032Speter				warn_f_flag = j;
84838032Speter			break;
84938032Speter
85038032Speter		  case 'F':	/* set full name */
85190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
85238032Speter			FullName = newstr(optarg);
85338032Speter			break;
85438032Speter
85564562Sgshapiro		  case 'G':	/* relay (gateway) submission */
85664562Sgshapiro			/* already set */
85790792Sgshapiro			CHECK_AGAINST_OPMODE(j);
85864562Sgshapiro			break;
85964562Sgshapiro
86038032Speter		  case 'h':	/* hop count */
86190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
86290792Sgshapiro			BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
86390792Sgshapiro								  10);
86490792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
86590792Sgshapiro					   BlankEnvelope.e_hopcount);
86690792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
86790792Sgshapiro
86838032Speter			if (*ep)
86938032Speter			{
87038032Speter				usrerr("Bad hop count (%s)", optarg);
87138032Speter				ExitStat = EX_USAGE;
87238032Speter			}
87338032Speter			break;
87464562Sgshapiro
87564562Sgshapiro		  case 'L':	/* program label */
87664562Sgshapiro			/* already set */
87764562Sgshapiro			break;
87864562Sgshapiro
87938032Speter		  case 'n':	/* don't alias */
88090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
88190792Sgshapiro			NoAlias = true;
88238032Speter			break;
88338032Speter
88438032Speter		  case 'N':	/* delivery status notifications */
88590792Sgshapiro			CHECK_AGAINST_OPMODE(j);
88638032Speter			DefaultNotify |= QHASNOTIFY;
88790792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
88890792Sgshapiro				macid("{dsn_notify}"), optarg);
88990792Sgshapiro			if (sm_strcasecmp(optarg, "never") == 0)
89038032Speter				break;
89138032Speter			for (p = optarg; p != NULL; optarg = p)
89238032Speter			{
89338032Speter				p = strchr(p, ',');
89438032Speter				if (p != NULL)
89538032Speter					*p++ = '\0';
89690792Sgshapiro				if (sm_strcasecmp(optarg, "success") == 0)
89738032Speter					DefaultNotify |= QPINGONSUCCESS;
89890792Sgshapiro				else if (sm_strcasecmp(optarg, "failure") == 0)
89938032Speter					DefaultNotify |= QPINGONFAILURE;
90090792Sgshapiro				else if (sm_strcasecmp(optarg, "delay") == 0)
90138032Speter					DefaultNotify |= QPINGONDELAY;
90238032Speter				else
90338032Speter				{
90438032Speter					usrerr("Invalid -N argument");
90538032Speter					ExitStat = EX_USAGE;
90638032Speter				}
90738032Speter			}
90838032Speter			break;
90938032Speter
91038032Speter		  case 'o':	/* set option */
91190792Sgshapiro			setoption(*optarg, optarg + 1, false, true,
91290792Sgshapiro				  &BlankEnvelope);
91338032Speter			break;
91438032Speter
91538032Speter		  case 'O':	/* set option (long form) */
91690792Sgshapiro			setoption(' ', optarg, false, true, &BlankEnvelope);
91738032Speter			break;
91838032Speter
91938032Speter		  case 'p':	/* set protocol */
92090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
92138032Speter			p = strchr(optarg, ':');
92238032Speter			if (p != NULL)
92338032Speter			{
92438032Speter				*p++ = '\0';
92538032Speter				if (*p != '\0')
92638032Speter				{
927120256Sgshapiro					i = strlen(p) + 1;
928120256Sgshapiro					ep = sm_malloc_x(i);
929120256Sgshapiro					cleanstrcpy(ep, p, i);
93090792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
93190792Sgshapiro						  A_HEAP, 's', ep);
93238032Speter				}
93338032Speter			}
93438032Speter			if (*optarg != '\0')
93538032Speter			{
936120256Sgshapiro				i = strlen(optarg) + 1;
937120256Sgshapiro				ep = sm_malloc_x(i);
938120256Sgshapiro				cleanstrcpy(ep, optarg, i);
93990792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_HEAP,
94090792Sgshapiro					  'r', ep);
94138032Speter			}
94238032Speter			break;
94338032Speter
94490792Sgshapiro		  case 'Q':	/* change quarantining on queued items */
94590792Sgshapiro			/* sanity check */
94690792Sgshapiro			if (OpMode != MD_DELIVER &&
94790792Sgshapiro			    OpMode != MD_QUEUERUN)
94890792Sgshapiro			{
94990792Sgshapiro				usrerr("Can not use -Q with -b%c", OpMode);
95090792Sgshapiro				ExitStat = EX_USAGE;
95190792Sgshapiro				break;
95290792Sgshapiro			}
95390792Sgshapiro
95490792Sgshapiro			if (OpMode == MD_DELIVER)
95590792Sgshapiro				set_op_mode(MD_QUEUERUN);
95690792Sgshapiro
95790792Sgshapiro			FullName = NULL;
95890792Sgshapiro
95990792Sgshapiro			quarantining = newstr(optarg);
96090792Sgshapiro			break;
96190792Sgshapiro
96238032Speter		  case 'q':	/* run queue files at intervals */
96364562Sgshapiro			/* sanity check */
96464562Sgshapiro			if (OpMode != MD_DELIVER &&
96564562Sgshapiro			    OpMode != MD_DAEMON &&
96664562Sgshapiro			    OpMode != MD_FGDAEMON &&
96764562Sgshapiro			    OpMode != MD_PRINT &&
96890792Sgshapiro			    OpMode != MD_PRINTNQE &&
96964562Sgshapiro			    OpMode != MD_QUEUERUN)
97064562Sgshapiro			{
97164562Sgshapiro				usrerr("Can not use -q with -b%c", OpMode);
97264562Sgshapiro				ExitStat = EX_USAGE;
97364562Sgshapiro				break;
97464562Sgshapiro			}
97564562Sgshapiro
97664562Sgshapiro			/* don't override -bd, -bD or -bp */
97764562Sgshapiro			if (OpMode == MD_DELIVER)
97890792Sgshapiro				set_op_mode(MD_QUEUERUN);
97964562Sgshapiro
98038032Speter			FullName = NULL;
98190792Sgshapiro			negate = optarg[0] == '!';
98290792Sgshapiro			if (negate)
98390792Sgshapiro			{
98490792Sgshapiro				/* negate meaning of pattern match */
98590792Sgshapiro				optarg++; /* skip '!' for next switch */
98690792Sgshapiro			}
98764562Sgshapiro
98838032Speter			switch (optarg[0])
98938032Speter			{
99090792Sgshapiro			  case 'G': /* Limit by queue group name */
99190792Sgshapiro				if (negate)
99290792Sgshapiro				{
99390792Sgshapiro					usrerr("Can not use -q!G");
99490792Sgshapiro					ExitStat = EX_USAGE;
99590792Sgshapiro					break;
99690792Sgshapiro				}
99790792Sgshapiro				if (queuegroup != NULL)
99890792Sgshapiro				{
99990792Sgshapiro					usrerr("Can not use multiple -qG options");
100090792Sgshapiro					ExitStat = EX_USAGE;
100190792Sgshapiro					break;
100290792Sgshapiro				}
100390792Sgshapiro				queuegroup = newstr(&optarg[1]);
100490792Sgshapiro				break;
100590792Sgshapiro
100690792Sgshapiro			  case 'I': /* Limit by ID */
100764562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
100838032Speter				new->queue_match = newstr(&optarg[1]);
100990792Sgshapiro				new->queue_negate = negate;
101038032Speter				new->queue_next = QueueLimitId;
101138032Speter				QueueLimitId = new;
101238032Speter				break;
101338032Speter
101490792Sgshapiro			  case 'R': /* Limit by recipient */
101564562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
101638032Speter				new->queue_match = newstr(&optarg[1]);
101790792Sgshapiro				new->queue_negate = negate;
101838032Speter				new->queue_next = QueueLimitRecipient;
101938032Speter				QueueLimitRecipient = new;
102038032Speter				break;
102138032Speter
102290792Sgshapiro			  case 'S': /* Limit by sender */
102364562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
102438032Speter				new->queue_match = newstr(&optarg[1]);
102590792Sgshapiro				new->queue_negate = negate;
102638032Speter				new->queue_next = QueueLimitSender;
102738032Speter				QueueLimitSender = new;
102838032Speter				break;
102938032Speter
103090792Sgshapiro			  case 'f': /* foreground queue run */
103190792Sgshapiro				foregroundqueue  = true;
103290792Sgshapiro				break;
103390792Sgshapiro
103490792Sgshapiro			  case 'Q': /* Limit by quarantine message */
103590792Sgshapiro				if (optarg[1] != '\0')
103690792Sgshapiro				{
103790792Sgshapiro					new = (QUEUE_CHAR *) xalloc(sizeof *new);
103890792Sgshapiro					new->queue_match = newstr(&optarg[1]);
103990792Sgshapiro					new->queue_negate = negate;
104090792Sgshapiro					new->queue_next = QueueLimitQuarantine;
104190792Sgshapiro					QueueLimitQuarantine = new;
104290792Sgshapiro				}
104390792Sgshapiro				QueueMode = QM_QUARANTINE;
104490792Sgshapiro				break;
104590792Sgshapiro
104690792Sgshapiro			  case 'L': /* act on lost items */
104790792Sgshapiro				QueueMode = QM_LOST;
104890792Sgshapiro				break;
104990792Sgshapiro
105090792Sgshapiro			  case 'p': /* Persistent queue */
105190792Sgshapiro				queuepersistent = true;
105290792Sgshapiro				if (QueueIntvl == 0)
105390792Sgshapiro					QueueIntvl = 1;
105490792Sgshapiro				if (optarg[1] == '\0')
105590792Sgshapiro					break;
105690792Sgshapiro				++optarg;
105790792Sgshapiro				/* FALLTHROUGH */
105890792Sgshapiro
105938032Speter			  default:
106064562Sgshapiro				i = Errors;
106138032Speter				QueueIntvl = convtime(optarg, 'm');
106298841Sgshapiro				if (QueueIntvl < 0)
106398841Sgshapiro				{
106498841Sgshapiro					usrerr("Invalid -q value");
106598841Sgshapiro					ExitStat = EX_USAGE;
106698841Sgshapiro				}
106764562Sgshapiro
106864562Sgshapiro				/* check for bad conversion */
106964562Sgshapiro				if (i < Errors)
107064562Sgshapiro					ExitStat = EX_USAGE;
107138032Speter				break;
107238032Speter			}
107338032Speter			break;
107438032Speter
107538032Speter		  case 'R':	/* DSN RET: what to return */
107690792Sgshapiro			CHECK_AGAINST_OPMODE(j);
107790792Sgshapiro			if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
107838032Speter			{
107938032Speter				usrerr("Duplicate -R flag");
108038032Speter				ExitStat = EX_USAGE;
108138032Speter				break;
108238032Speter			}
108390792Sgshapiro			BlankEnvelope.e_flags |= EF_RET_PARAM;
108490792Sgshapiro			if (sm_strcasecmp(optarg, "hdrs") == 0)
108590792Sgshapiro				BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
108690792Sgshapiro			else if (sm_strcasecmp(optarg, "full") != 0)
108738032Speter			{
108838032Speter				usrerr("Invalid -R value");
108938032Speter				ExitStat = EX_USAGE;
109038032Speter			}
109190792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
109290792Sgshapiro				  macid("{dsn_ret}"), optarg);
109338032Speter			break;
109438032Speter
109538032Speter		  case 't':	/* read recipients from message */
109690792Sgshapiro			CHECK_AGAINST_OPMODE(j);
109790792Sgshapiro			GrabTo = true;
109838032Speter			break;
109938032Speter
110038032Speter		  case 'V':	/* DSN ENVID: set "original" envelope id */
110190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
110238032Speter			if (!xtextok(optarg))
110338032Speter			{
110438032Speter				usrerr("Invalid syntax in -V flag");
110538032Speter				ExitStat = EX_USAGE;
110638032Speter			}
110738032Speter			else
110864562Sgshapiro			{
110990792Sgshapiro				BlankEnvelope.e_envid = newstr(optarg);
111090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
111190792Sgshapiro					  macid("{dsn_envid}"), optarg);
111264562Sgshapiro			}
111338032Speter			break;
111438032Speter
111538032Speter		  case 'X':	/* traffic log file */
111690792Sgshapiro			dp = drop_privileges(true);
111764562Sgshapiro			setstat(dp);
111864562Sgshapiro			if (stat(optarg, &traf_st) == 0 &&
111964562Sgshapiro			    S_ISFIFO(traf_st.st_mode))
112090792Sgshapiro				TrafficLogFile = sm_io_open(SmFtStdio,
112190792Sgshapiro							    SM_TIME_DEFAULT,
112290792Sgshapiro							    optarg,
112390792Sgshapiro							    SM_IO_WRONLY, NULL);
112464562Sgshapiro			else
112590792Sgshapiro				TrafficLogFile = sm_io_open(SmFtStdio,
112690792Sgshapiro							    SM_TIME_DEFAULT,
112790792Sgshapiro							    optarg,
112890792Sgshapiro							    SM_IO_APPEND, NULL);
112938032Speter			if (TrafficLogFile == NULL)
113038032Speter			{
113138032Speter				syserr("cannot open %s", optarg);
113238032Speter				ExitStat = EX_CANTCREAT;
113338032Speter				break;
113438032Speter			}
113590792Sgshapiro			(void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
113690792Sgshapiro					     NULL, SM_IO_LBF, 0);
113738032Speter			break;
113838032Speter
113938032Speter			/* compatibility flags */
114038032Speter		  case 'c':	/* connect to non-local mailers */
114138032Speter		  case 'i':	/* don't let dot stop me */
114238032Speter		  case 'm':	/* send to me too */
114338032Speter		  case 'T':	/* set timeout interval */
114438032Speter		  case 'v':	/* give blow-by-blow description */
114590792Sgshapiro			setoption(j, "T", false, true, &BlankEnvelope);
114638032Speter			break;
114738032Speter
114838032Speter		  case 'e':	/* error message disposition */
114938032Speter		  case 'M':	/* define macro */
115090792Sgshapiro			setoption(j, optarg, false, true, &BlankEnvelope);
115138032Speter			break;
115238032Speter
115338032Speter		  case 's':	/* save From lines in headers */
115490792Sgshapiro			setoption('f', "T", false, true, &BlankEnvelope);
115538032Speter			break;
115638032Speter
115764562Sgshapiro#ifdef DBM
115838032Speter		  case 'I':	/* initialize alias DBM file */
115990792Sgshapiro			set_op_mode(MD_INITALIAS);
116038032Speter			break;
116164562Sgshapiro#endif /* DBM */
116238032Speter
116364562Sgshapiro#if defined(__osf__) || defined(_AIX3)
116438032Speter		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
116538032Speter			break;
116664562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */
116764562Sgshapiro#if defined(sony_news)
116838032Speter		  case 'E':
116938032Speter		  case 'J':	/* ignore flags for Japanese code conversion
117064562Sgshapiro				   implemented on Sony NEWS */
117138032Speter			break;
117264562Sgshapiro#endif /* defined(sony_news) */
117338032Speter
117438032Speter		  default:
117590792Sgshapiro			finis(true, true, EX_USAGE);
117690792Sgshapiro			/* NOTREACHED */
117738032Speter			break;
117838032Speter		}
117938032Speter	}
118038032Speter
118190792Sgshapiro	/* if we've had errors so far, exit now */
118290792Sgshapiro	if ((ExitStat != EX_OK && OpMode != MD_TEST) ||
118390792Sgshapiro	    ExitStat == EX_OSERR)
118464562Sgshapiro	{
118590792Sgshapiro		finis(false, true, ExitStat);
118690792Sgshapiro		/* NOTREACHED */
118764562Sgshapiro	}
118890792Sgshapiro
118990792Sgshapiro	if (bitset(SUBMIT_MTA, SubmitMode))
119064562Sgshapiro	{
119198841Sgshapiro		/* If set daemon_flags on command line, don't reset it */
119298841Sgshapiro		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
119398841Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
119498841Sgshapiro				  macid("{daemon_flags}"), "CC f");
119564562Sgshapiro	}
119690792Sgshapiro	else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
119764562Sgshapiro	{
119890792Sgshapiro		SubmitMode = SUBMIT_MSA;
119998841Sgshapiro
120098841Sgshapiro		/* If set daemon_flags on command line, don't reset it */
120198841Sgshapiro		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
120298841Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
120398841Sgshapiro				  macid("{daemon_flags}"), "c u");
120464562Sgshapiro	}
120564562Sgshapiro
120638032Speter	/*
120738032Speter	**  Do basic initialization.
120838032Speter	**	Read system control file.
120938032Speter	**	Extract special fields for local use.
121038032Speter	*/
121138032Speter
121238032Speter#if XDEBUG
121338032Speter	checkfd012("before readcf");
121464562Sgshapiro#endif /* XDEBUG */
121590792Sgshapiro	vendor_pre_defaults(&BlankEnvelope);
121664562Sgshapiro
121790792Sgshapiro	readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
121890792Sgshapiro			 safecf, &BlankEnvelope);
121990792Sgshapiro#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
122090792Sgshapiro	ConfigFileRead = true;
122190792Sgshapiro#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
122290792Sgshapiro	vendor_post_defaults(&BlankEnvelope);
122338032Speter
122490792Sgshapiro	/* now we can complain about missing fds */
122590792Sgshapiro	if (MissingFds != 0 && LogLevel > 8)
122690792Sgshapiro	{
122790792Sgshapiro		char mbuf[MAXLINE];
122890792Sgshapiro
122990792Sgshapiro		mbuf[0] = '\0';
123090792Sgshapiro		if (bitset(1 << STDIN_FILENO, MissingFds))
123190792Sgshapiro			(void) sm_strlcat(mbuf, ", stdin", sizeof mbuf);
123290792Sgshapiro		if (bitset(1 << STDOUT_FILENO, MissingFds))
123390792Sgshapiro			(void) sm_strlcat(mbuf, ", stdout", sizeof mbuf);
123490792Sgshapiro		if (bitset(1 << STDERR_FILENO, MissingFds))
123590792Sgshapiro			(void) sm_strlcat(mbuf, ", stderr", sizeof mbuf);
123690792Sgshapiro
123790792Sgshapiro		/* Notice: fill_errno is from high above: fill_fd() */
123890792Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
123990792Sgshapiro			  "File descriptors missing on startup: %s; %s",
124090792Sgshapiro			  &mbuf[2], sm_errstring(fill_errno));
124190792Sgshapiro	}
124290792Sgshapiro
124377349Sgshapiro	/* Remove the ability for a normal user to send signals */
124490792Sgshapiro	if (RealUid != 0 && RealUid != geteuid())
124577349Sgshapiro	{
124677349Sgshapiro		uid_t new_uid = geteuid();
124777349Sgshapiro
124877349Sgshapiro#if HASSETREUID
124977349Sgshapiro		/*
125077349Sgshapiro		**  Since we can differentiate between uid and euid,
125177349Sgshapiro		**  make the uid a different user so the real user
125277349Sgshapiro		**  can't send signals.  However, it doesn't need to be
125377349Sgshapiro		**  root (euid has root).
125477349Sgshapiro		*/
125577349Sgshapiro
125677349Sgshapiro		if (new_uid == 0)
125777349Sgshapiro			new_uid = DefUid;
125877349Sgshapiro		if (tTd(47, 5))
125990792Sgshapiro			sm_dprintf("Changing real uid to %d\n", (int) new_uid);
126077349Sgshapiro		if (setreuid(new_uid, geteuid()) < 0)
126177349Sgshapiro		{
126277349Sgshapiro			syserr("main: setreuid(%d, %d) failed",
126377349Sgshapiro			       (int) new_uid, (int) geteuid());
126490792Sgshapiro			finis(false, true, EX_OSERR);
126577349Sgshapiro			/* NOTREACHED */
126677349Sgshapiro		}
126777349Sgshapiro		if (tTd(47, 10))
126890792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
126990792Sgshapiro				   (int) geteuid(), (int) getuid());
127077349Sgshapiro#else /* HASSETREUID */
127177349Sgshapiro		/*
127277349Sgshapiro		**  Have to change both effective and real so need to
127377349Sgshapiro		**  change them both to effective to keep privs.
127477349Sgshapiro		*/
127577349Sgshapiro
127677349Sgshapiro		if (tTd(47, 5))
127790792Sgshapiro			sm_dprintf("Changing uid to %d\n", (int) new_uid);
127877349Sgshapiro		if (setuid(new_uid) < 0)
127977349Sgshapiro		{
128077349Sgshapiro			syserr("main: setuid(%d) failed", (int) new_uid);
128190792Sgshapiro			finis(false, true, EX_OSERR);
128277349Sgshapiro			/* NOTREACHED */
128377349Sgshapiro		}
128477349Sgshapiro		if (tTd(47, 10))
128590792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
128690792Sgshapiro				   (int) geteuid(), (int) getuid());
128777349Sgshapiro#endif /* HASSETREUID */
128877349Sgshapiro	}
128977349Sgshapiro
129090792Sgshapiro#if NAMED_BIND
1291132943Sgshapiro	if (FallbackMX != NULL)
1292132943Sgshapiro		(void) getfallbackmxrr(FallbackMX);
129390792Sgshapiro#endif /* NAMED_BIND */
129490792Sgshapiro
129590792Sgshapiro	if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER)
129690792Sgshapiro	{
129790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
129890792Sgshapiro				     "WARNING: SuperSafe=interactive should only be used with\n         DeliveryMode=interactive\n");
129990792Sgshapiro	}
130090792Sgshapiro
130190792Sgshapiro	if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
130290792Sgshapiro	{
130390792Sgshapiro		usrerr("Mail submission program cannot be used as daemon");
130490792Sgshapiro		finis(false, true, EX_USAGE);
130590792Sgshapiro	}
130690792Sgshapiro
130790792Sgshapiro	if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
130890792Sgshapiro	    OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
130990792Sgshapiro	    OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
131090792Sgshapiro		makeworkgroups();
131190792Sgshapiro
131277349Sgshapiro	/* set up the basic signal handlers */
131390792Sgshapiro	if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
131490792Sgshapiro		(void) sm_signal(SIGINT, intsig);
131590792Sgshapiro	(void) sm_signal(SIGTERM, intsig);
131677349Sgshapiro
131738032Speter	/* Enforce use of local time (null string overrides this) */
131838032Speter	if (TimeZoneSpec == NULL)
131938032Speter		unsetenv("TZ");
132038032Speter	else if (TimeZoneSpec[0] != '\0')
132138032Speter		setuserenv("TZ", TimeZoneSpec);
132238032Speter	else
132338032Speter		setuserenv("TZ", NULL);
132438032Speter	tzset();
132538032Speter
132690792Sgshapiro	/* initialize mailbox database */
132790792Sgshapiro	i = sm_mbdb_initialize(Mbdb);
132890792Sgshapiro	if (i != EX_OK)
132990792Sgshapiro	{
133090792Sgshapiro		usrerr("Can't initialize mailbox database \"%s\": %s",
133190792Sgshapiro		       Mbdb, sm_strexit(i));
133290792Sgshapiro		ExitStat = i;
133390792Sgshapiro	}
133490792Sgshapiro
133538032Speter	/* avoid denial-of-service attacks */
133638032Speter	resetlimits();
133738032Speter
133890792Sgshapiro	if (OpMode == MD_TEST)
133938032Speter	{
134090792Sgshapiro		/* can't be done after readcf if RunAs* is used */
134190792Sgshapiro		dp = drop_privileges(true);
134290792Sgshapiro		if (dp != EX_OK)
134390792Sgshapiro		{
134490792Sgshapiro			finis(false, true, dp);
134590792Sgshapiro			/* NOTREACHED */
134690792Sgshapiro		}
134790792Sgshapiro	}
134890792Sgshapiro	else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
134990792Sgshapiro	{
135038032Speter		/* drop privileges -- daemon mode done after socket/bind */
135190792Sgshapiro		dp = drop_privileges(false);
135264562Sgshapiro		setstat(dp);
135390792Sgshapiro		if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
135490792Sgshapiro		{
135590792Sgshapiro			usrerr("Mail submission program must have RunAsUser set to non root user");
135690792Sgshapiro			finis(false, true, EX_CONFIG);
135790792Sgshapiro			/* NOTREACHED */
135890792Sgshapiro		}
135938032Speter	}
136038032Speter
136164562Sgshapiro#if NAMED_BIND
136264562Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
136364562Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
136464562Sgshapiro#endif /* NAMED_BIND */
136564562Sgshapiro
136638032Speter	/*
136738032Speter	**  Find our real host name for future logging.
136838032Speter	*/
136938032Speter
137064562Sgshapiro	authinfo = getauthinfo(STDIN_FILENO, &forged);
137190792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
137238032Speter
137338032Speter	/* suppress error printing if errors mailed back or whatever */
137490792Sgshapiro	if (BlankEnvelope.e_errormode != EM_PRINT)
137590792Sgshapiro		HoldErrs = true;
137638032Speter
137738032Speter	/* set up the $=m class now, after .cf has a chance to redefine $m */
137890792Sgshapiro	expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope);
137973188Sgshapiro	if (jbuf[0] != '\0')
138073188Sgshapiro		setclass('m', jbuf);
138138032Speter
138238032Speter	/* probe interfaces and locate any additional names */
138390792Sgshapiro	if (DontProbeInterfaces != DPI_PROBENONE)
138438032Speter		load_if_names();
138538032Speter
138690792Sgshapiro	if (tTd(0, 10))
138790792Sgshapiro	{
1388110560Sgshapiro		char pidpath[MAXPATHLEN];
1389110560Sgshapiro
139090792Sgshapiro		/* Now we know which .cf file we use */
139190792Sgshapiro		sm_dprintf("     Conf file:\t%s (selected)\n",
139290792Sgshapiro			   getcfname(OpMode, SubmitMode, cftype, conffile));
1393110560Sgshapiro		expand(PidFile, pidpath, sizeof pidpath, &BlankEnvelope);
1394110560Sgshapiro		sm_dprintf("      Pid file:\t%s (selected)\n", pidpath);
139590792Sgshapiro	}
139690792Sgshapiro
139738032Speter	if (tTd(0, 1))
139838032Speter	{
139990792Sgshapiro		sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
140090792Sgshapiro		sm_dprintf("\n      (short domain name) $w = ");
1401132943Sgshapiro		xputs(sm_debug_file(), macvalue('w', &BlankEnvelope));
140290792Sgshapiro		sm_dprintf("\n  (canonical domain name) $j = ");
1403132943Sgshapiro		xputs(sm_debug_file(), macvalue('j', &BlankEnvelope));
140490792Sgshapiro		sm_dprintf("\n         (subdomain name) $m = ");
1405132943Sgshapiro		xputs(sm_debug_file(), macvalue('m', &BlankEnvelope));
140690792Sgshapiro		sm_dprintf("\n              (node name) $k = ");
1407132943Sgshapiro		xputs(sm_debug_file(), macvalue('k', &BlankEnvelope));
140890792Sgshapiro		sm_dprintf("\n========================================================\n\n");
140938032Speter	}
141038032Speter
141138032Speter	/*
141238032Speter	**  Do more command line checking -- these are things that
141338032Speter	**  have to modify the results of reading the config file.
141438032Speter	*/
141538032Speter
141638032Speter	/* process authorization warnings from command line */
141738032Speter	if (warn_C_flag)
141890792Sgshapiro		auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
141990792Sgshapiro			     RealUserName, conffile);
142064562Sgshapiro	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
142190792Sgshapiro		auth_warning(&BlankEnvelope, "Processed from queue %s",
142290792Sgshapiro			     QueueDir);
142390792Sgshapiro	if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
142490792Sgshapiro	    RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
142590792Sgshapiro		sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
142690792Sgshapiro			  (int) RealUid);
142738032Speter
142838032Speter	/* check body type for legality */
142990792Sgshapiro	i = check_bodytype(BlankEnvelope.e_bodytype);
143090792Sgshapiro	if (i == BODYTYPE_ILLEGAL)
143138032Speter	{
143290792Sgshapiro		usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
143390792Sgshapiro		BlankEnvelope.e_bodytype = NULL;
143438032Speter	}
143590792Sgshapiro	else if (i != BODYTYPE_NONE)
143690792Sgshapiro		SevenBitInput = (i == BODYTYPE_7BIT);
143738032Speter
143838032Speter	/* tweak default DSN notifications */
143938032Speter	if (DefaultNotify == 0)
144038032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
144138032Speter
144238032Speter	/* check for sane configuration level */
144338032Speter	if (ConfigLevel > MAXCONFIGLEVEL)
144438032Speter	{
144538032Speter		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
144690792Sgshapiro		       ConfigLevel, Version, MAXCONFIGLEVEL);
144738032Speter	}
144838032Speter
144938032Speter	/* need MCI cache to have persistence */
145038032Speter	if (HostStatDir != NULL && MaxMciCache == 0)
145138032Speter	{
145238032Speter		HostStatDir = NULL;
145390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
145490792Sgshapiro				     "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
145538032Speter	}
145638032Speter
145738032Speter	/* need HostStatusDir in order to have SingleThreadDelivery */
145838032Speter	if (SingleThreadDelivery && HostStatDir == NULL)
145938032Speter	{
146090792Sgshapiro		SingleThreadDelivery = false;
146190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
146290792Sgshapiro				     "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
146338032Speter	}
146438032Speter
146538032Speter	/* check for permissions */
146690792Sgshapiro	if (RealUid != 0 &&
146742575Speter	    RealUid != TrustedUid)
146838032Speter	{
146990792Sgshapiro		char *action = NULL;
147090792Sgshapiro
147190792Sgshapiro		switch (OpMode)
147290792Sgshapiro		{
147390792Sgshapiro		  case MD_QUEUERUN:
147490792Sgshapiro			if (quarantining != NULL)
147590792Sgshapiro				action = "quarantine jobs";
147690792Sgshapiro			else
1477132943Sgshapiro			{
1478132943Sgshapiro				/* Normal users can do a single queue run */
1479132943Sgshapiro				if (QueueIntvl == 0)
1480132943Sgshapiro					break;
1481132943Sgshapiro			}
148290792Sgshapiro
148390792Sgshapiro			/* but not persistent queue runners */
148490792Sgshapiro			if (action == NULL)
148590792Sgshapiro				action = "start a queue runner daemon";
148690792Sgshapiro			/* FALLTHROUGH */
148790792Sgshapiro
148890792Sgshapiro		  case MD_PURGESTAT:
148990792Sgshapiro			if (action == NULL)
149090792Sgshapiro				action = "purge host status";
149190792Sgshapiro			/* FALLTHROUGH */
149290792Sgshapiro
149390792Sgshapiro		  case MD_DAEMON:
149490792Sgshapiro		  case MD_FGDAEMON:
149590792Sgshapiro			if (action == NULL)
149690792Sgshapiro				action = "run daemon";
149790792Sgshapiro
149890792Sgshapiro			if (tTd(65, 1))
149990792Sgshapiro				sm_dprintf("Deny user %d attempt to %s\n",
150090792Sgshapiro					   (int) RealUid, action);
150190792Sgshapiro
150290792Sgshapiro			if (LogLevel > 1)
150390792Sgshapiro				sm_syslog(LOG_ALERT, NOQID,
150490792Sgshapiro					  "user %d attempted to %s",
150590792Sgshapiro					  (int) RealUid, action);
150690792Sgshapiro			HoldErrs = false;
150790792Sgshapiro			usrerr("Permission denied (real uid not trusted)");
150890792Sgshapiro			finis(false, true, EX_USAGE);
150990792Sgshapiro			/* NOTREACHED */
151090792Sgshapiro			break;
151190792Sgshapiro
151290792Sgshapiro		  case MD_VERIFY:
151390792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
151490792Sgshapiro			{
151590792Sgshapiro				/*
151690792Sgshapiro				**  If -bv and RestrictExpand,
151790792Sgshapiro				**  drop privs to prevent normal
151890792Sgshapiro				**  users from reading private
151990792Sgshapiro				**  aliases/forwards/:include:s
152090792Sgshapiro				*/
152190792Sgshapiro
152290792Sgshapiro				if (tTd(65, 1))
152390792Sgshapiro					sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
152490792Sgshapiro						   (int) RealUid);
152590792Sgshapiro
152690792Sgshapiro				dp = drop_privileges(true);
152790792Sgshapiro
152890792Sgshapiro				/* Fake address safety */
152990792Sgshapiro				if (tTd(65, 1))
153090792Sgshapiro					sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
153190792Sgshapiro				setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
153290792Sgshapiro
153390792Sgshapiro				if (dp != EX_OK)
153490792Sgshapiro				{
153590792Sgshapiro					if (tTd(65, 1))
153690792Sgshapiro						sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
153790792Sgshapiro							   (int) RealUid);
153890792Sgshapiro					CurEnv->e_id = NULL;
153990792Sgshapiro					finis(true, true, dp);
154090792Sgshapiro					/* NOTREACHED */
154190792Sgshapiro				}
154290792Sgshapiro			}
154390792Sgshapiro			break;
154490792Sgshapiro
154590792Sgshapiro		  case MD_TEST:
154690792Sgshapiro		  case MD_PRINT:
154790792Sgshapiro		  case MD_PRINTNQE:
154890792Sgshapiro		  case MD_FREEZE:
154990792Sgshapiro		  case MD_HOSTSTAT:
155090792Sgshapiro			/* Nothing special to check */
155190792Sgshapiro			break;
155290792Sgshapiro
155390792Sgshapiro		  case MD_INITALIAS:
155490792Sgshapiro			if (!wordinclass(RealUserName, 't'))
155590792Sgshapiro			{
155690792Sgshapiro				if (tTd(65, 1))
155790792Sgshapiro					sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
155890792Sgshapiro						   (int) RealUid);
155990792Sgshapiro				if (LogLevel > 1)
156090792Sgshapiro					sm_syslog(LOG_ALERT, NOQID,
156190792Sgshapiro						  "user %d attempted to rebuild the alias map",
156290792Sgshapiro						  (int) RealUid);
156390792Sgshapiro				HoldErrs = false;
156490792Sgshapiro				usrerr("Permission denied (real uid not trusted)");
156590792Sgshapiro				finis(false, true, EX_USAGE);
156690792Sgshapiro				/* NOTREACHED */
156790792Sgshapiro			}
156890792Sgshapiro			if (UseMSP)
156990792Sgshapiro			{
157090792Sgshapiro				HoldErrs = false;
157190792Sgshapiro				usrerr("User %d cannot rebuild aliases in mail submission program",
157290792Sgshapiro				       (int) RealUid);
157390792Sgshapiro				finis(false, true, EX_USAGE);
157490792Sgshapiro				/* NOTREACHED */
157590792Sgshapiro			}
157690792Sgshapiro			/* FALLTHROUGH */
157790792Sgshapiro
157890792Sgshapiro		  default:
157990792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
158090792Sgshapiro			    Verbose != 0)
158190792Sgshapiro			{
158290792Sgshapiro				/*
158390792Sgshapiro				**  If -v and RestrictExpand, reset
158490792Sgshapiro				**  Verbose to prevent normal users
158590792Sgshapiro				**  from seeing the expansion of
158690792Sgshapiro				**  aliases/forwards/:include:s
158790792Sgshapiro				*/
158890792Sgshapiro
158990792Sgshapiro				if (tTd(65, 1))
159090792Sgshapiro					sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
159190792Sgshapiro						   (int) RealUid);
159290792Sgshapiro				Verbose = 0;
159390792Sgshapiro			}
159490792Sgshapiro			break;
159590792Sgshapiro		}
159638032Speter	}
159738032Speter
159838032Speter	if (MeToo)
159938032Speter		BlankEnvelope.e_flags |= EF_METOO;
160038032Speter
160138032Speter	switch (OpMode)
160238032Speter	{
160338032Speter	  case MD_TEST:
160438032Speter		/* don't have persistent host status in test mode */
160538032Speter		HostStatDir = NULL;
160638032Speter		if (Verbose == 0)
160738032Speter			Verbose = 2;
160890792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
160990792Sgshapiro		HoldErrs = false;
161038032Speter		break;
161138032Speter
161238032Speter	  case MD_VERIFY:
161390792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
161490792Sgshapiro		HoldErrs = false;
161538032Speter		/* arrange to exit cleanly on hangup signal */
161690792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
161790792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
161890792Sgshapiro		if (geteuid() != 0)
161990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
162090792Sgshapiro					     "Notice: -bv may give misleading output for non-privileged user\n");
162138032Speter		break;
162238032Speter
162338032Speter	  case MD_FGDAEMON:
162490792Sgshapiro		run_in_foreground = true;
162590792Sgshapiro		set_op_mode(MD_DAEMON);
162664562Sgshapiro		/* FALLTHROUGH */
162738032Speter
162838032Speter	  case MD_DAEMON:
162990792Sgshapiro		vendor_daemon_setup(&BlankEnvelope);
163038032Speter
163138032Speter		/* remove things that don't make sense in daemon mode */
163238032Speter		FullName = NULL;
163390792Sgshapiro		GrabTo = false;
163438032Speter
163538032Speter		/* arrange to restart on hangup signal */
163638032Speter		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
163738032Speter			sm_syslog(LOG_WARNING, NOQID,
163864562Sgshapiro				  "daemon invoked without full pathname; kill -1 won't work");
163938032Speter		break;
164038032Speter
164138032Speter	  case MD_INITALIAS:
164238032Speter		Verbose = 2;
164390792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
164490792Sgshapiro		HoldErrs = false;
164564562Sgshapiro		/* FALLTHROUGH */
164638032Speter
164738032Speter	  default:
164838032Speter		/* arrange to exit cleanly on hangup signal */
164990792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
165090792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
165138032Speter		break;
165238032Speter	}
165338032Speter
165438032Speter	/* special considerations for FullName */
165538032Speter	if (FullName != NULL)
165638032Speter	{
165738032Speter		char *full = NULL;
165838032Speter
165938032Speter		/* full names can't have newlines */
166064562Sgshapiro		if (strchr(FullName, '\n') != NULL)
166138032Speter		{
166290792Sgshapiro			full = newstr(denlstring(FullName, true, true));
166373188Sgshapiro			FullName = full;
166438032Speter		}
166573188Sgshapiro
166638032Speter		/* check for characters that may have to be quoted */
166738032Speter		if (!rfc822_string(FullName))
166838032Speter		{
166938032Speter			/*
167038032Speter			**  Quote a full name with special characters
167138032Speter			**  as a comment so crackaddr() doesn't destroy
167238032Speter			**  the name portion of the address.
167338032Speter			*/
167473188Sgshapiro
167590792Sgshapiro			FullName = addquotes(FullName, NULL);
167638032Speter			if (full != NULL)
167790792Sgshapiro				sm_free(full);  /* XXX */
167838032Speter		}
167938032Speter	}
168038032Speter
168138032Speter	/* do heuristic mode adjustment */
168238032Speter	if (Verbose)
168338032Speter	{
168438032Speter		/* turn off noconnect option */
168590792Sgshapiro		setoption('c', "F", true, false, &BlankEnvelope);
168638032Speter
168738032Speter		/* turn on interactive delivery */
168890792Sgshapiro		setoption('d', "", true, false, &BlankEnvelope);
168938032Speter	}
169038032Speter
169142575Speter#ifdef VENDOR_CODE
169242575Speter	/* check for vendor mismatch */
169342575Speter	if (VendorCode != VENDOR_CODE)
169442575Speter	{
169542575Speter		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
169642575Speter			getvendor(VENDOR_CODE), getvendor(VendorCode));
169742575Speter	}
169864562Sgshapiro#endif /* VENDOR_CODE */
169964562Sgshapiro
170038032Speter	/* check for out of date configuration level */
170138032Speter	if (ConfigLevel < MAXCONFIGLEVEL)
170238032Speter	{
170338032Speter		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
170438032Speter			Version, MAXCONFIGLEVEL, ConfigLevel);
170538032Speter	}
170638032Speter
170738032Speter	if (ConfigLevel < 3)
170890792Sgshapiro		UseErrorsTo = true;
170938032Speter
171038032Speter	/* set options that were previous macros */
171138032Speter	if (SmtpGreeting == NULL)
171238032Speter	{
171390792Sgshapiro		if (ConfigLevel < 7 &&
171490792Sgshapiro		    (p = macvalue('e', &BlankEnvelope)) != NULL)
171538032Speter			SmtpGreeting = newstr(p);
171638032Speter		else
171738032Speter			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
171838032Speter	}
171938032Speter	if (UnixFromLine == NULL)
172038032Speter	{
172190792Sgshapiro		if (ConfigLevel < 7 &&
172290792Sgshapiro		    (p = macvalue('l', &BlankEnvelope)) != NULL)
172338032Speter			UnixFromLine = newstr(p);
172438032Speter		else
172538032Speter			UnixFromLine = "From \201g  \201d";
172638032Speter	}
172764562Sgshapiro	SmtpError[0] = '\0';
172838032Speter
172938032Speter	/* our name for SMTP codes */
173090792Sgshapiro	expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
173173188Sgshapiro	if (jbuf[0] == '\0')
173290792Sgshapiro		PSTRSET(MyHostName, "localhost");
173373188Sgshapiro	else
173490792Sgshapiro		PSTRSET(MyHostName, jbuf);
173573188Sgshapiro	if (strchr(MyHostName, '.') == NULL)
1736132943Sgshapiro		message("WARNING: local host name (%s) is not qualified; see cf/README: WHO AM I?",
173773188Sgshapiro			MyHostName);
173838032Speter
173938032Speter	/* make certain that this name is part of the $=w class */
174038032Speter	setclass('w', MyHostName);
174138032Speter
174290792Sgshapiro	/* fill in the structure of the *default* queue */
174390792Sgshapiro	st = stab("mqueue", ST_QUEUE, ST_FIND);
174490792Sgshapiro	if (st == NULL)
174590792Sgshapiro		syserr("No default queue (mqueue) defined");
174690792Sgshapiro	else
174790792Sgshapiro		set_def_queueval(st->s_quegrp, true);
174890792Sgshapiro
174938032Speter	/* the indices of built-in mailers */
175038032Speter	st = stab("local", ST_MAILER, ST_FIND);
175138032Speter	if (st != NULL)
175238032Speter		LocalMailer = st->s_mailer;
175338032Speter	else if (OpMode != MD_TEST || !warn_C_flag)
175438032Speter		syserr("No local mailer defined");
175538032Speter
175638032Speter	st = stab("prog", ST_MAILER, ST_FIND);
175738032Speter	if (st == NULL)
175838032Speter		syserr("No prog mailer defined");
175938032Speter	else
176038032Speter	{
176138032Speter		ProgMailer = st->s_mailer;
176238032Speter		clrbitn(M_MUSER, ProgMailer->m_flags);
176338032Speter	}
176438032Speter
176538032Speter	st = stab("*file*", ST_MAILER, ST_FIND);
176638032Speter	if (st == NULL)
176738032Speter		syserr("No *file* mailer defined");
176838032Speter	else
176938032Speter	{
177038032Speter		FileMailer = st->s_mailer;
177138032Speter		clrbitn(M_MUSER, FileMailer->m_flags);
177238032Speter	}
177338032Speter
177438032Speter	st = stab("*include*", ST_MAILER, ST_FIND);
177538032Speter	if (st == NULL)
177638032Speter		syserr("No *include* mailer defined");
177738032Speter	else
177838032Speter		InclMailer = st->s_mailer;
177938032Speter
178038032Speter	if (ConfigLevel < 6)
178138032Speter	{
178238032Speter		/* heuristic tweaking of local mailer for back compat */
178338032Speter		if (LocalMailer != NULL)
178438032Speter		{
178538032Speter			setbitn(M_ALIASABLE, LocalMailer->m_flags);
178638032Speter			setbitn(M_HASPWENT, LocalMailer->m_flags);
178738032Speter			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
178838032Speter			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
178938032Speter			setbitn(M_CHECKPROG, LocalMailer->m_flags);
179038032Speter			setbitn(M_CHECKFILE, LocalMailer->m_flags);
179138032Speter			setbitn(M_CHECKUDB, LocalMailer->m_flags);
179238032Speter		}
179338032Speter		if (ProgMailer != NULL)
179438032Speter			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
179538032Speter		if (FileMailer != NULL)
179638032Speter			setbitn(M_RUNASRCPT, FileMailer->m_flags);
179738032Speter	}
179838032Speter	if (ConfigLevel < 7)
179938032Speter	{
180038032Speter		if (LocalMailer != NULL)
180138032Speter			setbitn(M_VRFY250, LocalMailer->m_flags);
180238032Speter		if (ProgMailer != NULL)
180338032Speter			setbitn(M_VRFY250, ProgMailer->m_flags);
180438032Speter		if (FileMailer != NULL)
180538032Speter			setbitn(M_VRFY250, FileMailer->m_flags);
180638032Speter	}
180738032Speter
180838032Speter	/* MIME Content-Types that cannot be transfer encoded */
180938032Speter	setclass('n', "multipart/signed");
181038032Speter
181138032Speter	/* MIME message/xxx subtypes that can be treated as messages */
181238032Speter	setclass('s', "rfc822");
181338032Speter
181438032Speter	/* MIME Content-Transfer-Encodings that can be encoded */
181538032Speter	setclass('e', "7bit");
181638032Speter	setclass('e', "8bit");
181738032Speter	setclass('e', "binary");
181838032Speter
181938032Speter#ifdef USE_B_CLASS
182038032Speter	/* MIME Content-Types that should be treated as binary */
182138032Speter	setclass('b', "image");
182238032Speter	setclass('b', "audio");
182338032Speter	setclass('b', "video");
182438032Speter	setclass('b', "application/octet-stream");
182564562Sgshapiro#endif /* USE_B_CLASS */
182638032Speter
182742575Speter	/* MIME headers which have fields to check for overflow */
182890792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
182990792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
183042575Speter
183142575Speter	/* MIME headers to check for length overflow */
183290792Sgshapiro	setclass(macid("{checkMIMETextHeaders}"), "content-description");
183342575Speter
183442575Speter	/* MIME headers to check for overflow and rebalance */
183590792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-disposition");
183690792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-id");
183790792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
183890792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-type");
183990792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "mime-version");
184042575Speter
184190792Sgshapiro	/* Macros to save in the queue file -- don't remove any */
184290792Sgshapiro	setclass(macid("{persistentMacros}"), "r");
184390792Sgshapiro	setclass(macid("{persistentMacros}"), "s");
184490792Sgshapiro	setclass(macid("{persistentMacros}"), "_");
184590792Sgshapiro	setclass(macid("{persistentMacros}"), "{if_addr}");
184690792Sgshapiro	setclass(macid("{persistentMacros}"), "{daemon_flags}");
184764562Sgshapiro
184838032Speter	/* operate in queue directory */
184990792Sgshapiro	if (QueueDir == NULL || *QueueDir == '\0')
185038032Speter	{
185138032Speter		if (OpMode != MD_TEST)
185238032Speter		{
185338032Speter			syserr("QueueDirectory (Q) option must be set");
185438032Speter			ExitStat = EX_CONFIG;
185538032Speter		}
185638032Speter	}
185738032Speter	else
185838032Speter	{
185964562Sgshapiro		if (OpMode != MD_TEST)
186090792Sgshapiro			setup_queues(OpMode == MD_DAEMON);
186138032Speter	}
186238032Speter
186338032Speter	/* check host status directory for validity */
186490792Sgshapiro	if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
186538032Speter	{
186638032Speter		/* cannot use this value */
186790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
186890792Sgshapiro				     "Warning: Cannot use HostStatusDirectory = %s: %s\n",
186990792Sgshapiro				     HostStatDir, sm_errstring(errno));
187038032Speter		HostStatDir = NULL;
187138032Speter	}
187238032Speter
187390792Sgshapiro	if (OpMode == MD_QUEUERUN &&
187490792Sgshapiro	    RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
187538032Speter	{
187638032Speter		struct stat stbuf;
187738032Speter
187838032Speter		/* check to see if we own the queue directory */
187938032Speter		if (stat(".", &stbuf) < 0)
188038032Speter			syserr("main: cannot stat %s", QueueDir);
188138032Speter		if (stbuf.st_uid != RealUid)
188238032Speter		{
188338032Speter			/* nope, really a botch */
188490792Sgshapiro			HoldErrs = false;
188538032Speter			usrerr("You do not have permission to process the queue");
188690792Sgshapiro			finis(false, true, EX_NOPERM);
188790792Sgshapiro			/* NOTREACHED */
188838032Speter		}
188938032Speter	}
189038032Speter
189190792Sgshapiro#if MILTER
189264562Sgshapiro	/* sanity checks on milter filters */
189364562Sgshapiro	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
189490792Sgshapiro	{
189590792Sgshapiro		milter_config(InputFilterList, InputFilters, MAXFILTERS);
189690792Sgshapiro		setup_daemon_milters();
189790792Sgshapiro	}
189890792Sgshapiro#endif /* MILTER */
189964562Sgshapiro
190090792Sgshapiro	/* Convert queuegroup string to qgrp number */
190190792Sgshapiro	if (queuegroup != NULL)
190290792Sgshapiro	{
190390792Sgshapiro		qgrp = name2qid(queuegroup);
190490792Sgshapiro		if (qgrp == NOQGRP)
190590792Sgshapiro		{
190690792Sgshapiro			HoldErrs = false;
190790792Sgshapiro			usrerr("Queue group %s unknown", queuegroup);
190890792Sgshapiro			finis(false, true, ExitStat);
190990792Sgshapiro			/* NOTREACHED */
191090792Sgshapiro		}
191190792Sgshapiro	}
191266494Sgshapiro
191338032Speter	/* if we've had errors so far, exit now */
191438032Speter	if (ExitStat != EX_OK && OpMode != MD_TEST)
191590792Sgshapiro	{
191690792Sgshapiro		finis(false, true, ExitStat);
191790792Sgshapiro		/* NOTREACHED */
191890792Sgshapiro	}
191938032Speter
192090792Sgshapiro#if SASL
192190792Sgshapiro	/* sendmail specific SASL initialization */
192290792Sgshapiro	sm_sasl_init();
192390792Sgshapiro#endif /* SASL */
192490792Sgshapiro
192538032Speter#if XDEBUG
192638032Speter	checkfd012("before main() initmaps");
192764562Sgshapiro#endif /* XDEBUG */
192838032Speter
192938032Speter	/*
193038032Speter	**  Do operation-mode-dependent initialization.
193138032Speter	*/
193238032Speter
193338032Speter	switch (OpMode)
193438032Speter	{
193538032Speter	  case MD_PRINT:
193638032Speter		/* print the queue */
193790792Sgshapiro		HoldErrs = false;
193890792Sgshapiro		dropenvelope(&BlankEnvelope, true, false);
193990792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
194090792Sgshapiro		if (qgrp != NOQGRP)
194190792Sgshapiro		{
194290792Sgshapiro			int j;
194390792Sgshapiro
194490792Sgshapiro			/* Selecting a particular queue group to run */
194590792Sgshapiro			for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
194690792Sgshapiro			{
194790792Sgshapiro				if (StopRequest)
194890792Sgshapiro					stop_sendmail();
194990792Sgshapiro				(void) print_single_queue(qgrp, j);
195090792Sgshapiro			}
195190792Sgshapiro			finis(false, true, EX_OK);
195290792Sgshapiro			/* NOTREACHED */
195390792Sgshapiro		}
195438032Speter		printqueue();
195590792Sgshapiro		finis(false, true, EX_OK);
195690792Sgshapiro		/* NOTREACHED */
195742575Speter		break;
195838032Speter
195990792Sgshapiro	  case MD_PRINTNQE:
196090792Sgshapiro		/* print number of entries in queue */
196190792Sgshapiro		dropenvelope(&BlankEnvelope, true, false);
196290792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
196390792Sgshapiro		printnqe(smioout, NULL);
196490792Sgshapiro		finis(false, true, EX_OK);
196590792Sgshapiro		/* NOTREACHED */
196690792Sgshapiro		break;
196790792Sgshapiro
196890792Sgshapiro	  case MD_QUEUERUN:
196990792Sgshapiro		/* only handle quarantining here */
197090792Sgshapiro		if (quarantining == NULL)
197190792Sgshapiro			break;
197290792Sgshapiro
197390792Sgshapiro		if (QueueMode != QM_QUARANTINE &&
197490792Sgshapiro		    QueueMode != QM_NORMAL)
197590792Sgshapiro		{
197690792Sgshapiro			HoldErrs = false;
197790792Sgshapiro			usrerr("Can not use -Q with -q%c", QueueMode);
197890792Sgshapiro			ExitStat = EX_USAGE;
197990792Sgshapiro			finis(false, true, ExitStat);
198090792Sgshapiro			/* NOTREACHED */
198190792Sgshapiro		}
198290792Sgshapiro		quarantine_queue(quarantining, qgrp);
198390792Sgshapiro		finis(false, true, EX_OK);
198490792Sgshapiro		break;
198590792Sgshapiro
198638032Speter	  case MD_HOSTSTAT:
198790792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
198864562Sgshapiro		(void) mci_traverse_persistent(mci_print_persistent, NULL);
198990792Sgshapiro		finis(false, true, EX_OK);
199090792Sgshapiro		/* NOTREACHED */
199164562Sgshapiro		break;
199238032Speter
199338032Speter	  case MD_PURGESTAT:
199464562Sgshapiro		(void) mci_traverse_persistent(mci_purge_persistent, NULL);
199590792Sgshapiro		finis(false, true, EX_OK);
199690792Sgshapiro		/* NOTREACHED */
199764562Sgshapiro		break;
199838032Speter
199938032Speter	  case MD_INITALIAS:
200042575Speter		/* initialize maps */
200164562Sgshapiro		initmaps();
200290792Sgshapiro		finis(false, true, ExitStat);
200390792Sgshapiro		/* NOTREACHED */
200442575Speter		break;
200538032Speter
200638032Speter	  case MD_SMTP:
200738032Speter	  case MD_DAEMON:
200838032Speter		/* reset DSN parameters */
200938032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
201090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
201190792Sgshapiro			  macid("{dsn_notify}"), NULL);
201290792Sgshapiro		BlankEnvelope.e_envid = NULL;
201390792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
201490792Sgshapiro			  macid("{dsn_envid}"), NULL);
201590792Sgshapiro		BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
201690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
201790792Sgshapiro			  macid("{dsn_ret}"), NULL);
201838032Speter
201942575Speter		/* don't open maps for daemon -- done below in child */
202038032Speter		break;
202138032Speter	}
202238032Speter
202338032Speter	if (tTd(0, 15))
202438032Speter	{
202538032Speter		/* print configuration table (or at least part of it) */
202638032Speter		if (tTd(0, 90))
202738032Speter			printrules();
202838032Speter		for (i = 0; i < MAXMAILERS; i++)
202938032Speter		{
203038032Speter			if (Mailer[i] != NULL)
2031132943Sgshapiro				printmailer(sm_debug_file(), Mailer[i]);
203238032Speter		}
203338032Speter	}
203438032Speter
203538032Speter	/*
203638032Speter	**  Switch to the main envelope.
203738032Speter	*/
203838032Speter
203990792Sgshapiro	CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
204090792Sgshapiro			     sm_rpool_new_x(NULL));
204138032Speter	MainEnvelope.e_flags = BlankEnvelope.e_flags;
204238032Speter
204338032Speter	/*
204438032Speter	**  If test mode, read addresses from stdin and process.
204538032Speter	*/
204638032Speter
204738032Speter	if (OpMode == MD_TEST)
204838032Speter	{
204990792Sgshapiro		if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
205038032Speter			Verbose = 2;
205138032Speter
205238032Speter		if (Verbose)
205338032Speter		{
205490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
205590792Sgshapiro				     "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
205690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
205790792Sgshapiro				     "Enter <ruleset> <address>\n");
205838032Speter		}
205990792Sgshapiro		macdefine(&(MainEnvelope.e_macro), A_PERM,
206090792Sgshapiro			  macid("{addr_type}"), "e r");
206138032Speter		for (;;)
206238032Speter		{
206390792Sgshapiro			SM_TRY
206490792Sgshapiro			{
206590792Sgshapiro				(void) sm_signal(SIGINT, intindebug);
206690792Sgshapiro				(void) sm_releasesignal(SIGINT);
206790792Sgshapiro				if (Verbose == 2)
206890792Sgshapiro					(void) sm_io_fprintf(smioout,
206990792Sgshapiro							     SM_TIME_DEFAULT,
207090792Sgshapiro							     "> ");
207190792Sgshapiro				(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
207290792Sgshapiro				if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
207390792Sgshapiro						sizeof buf) == NULL)
207490792Sgshapiro					testmodeline("/quit", &MainEnvelope);
207590792Sgshapiro				p = strchr(buf, '\n');
207690792Sgshapiro				if (p != NULL)
207790792Sgshapiro					*p = '\0';
207890792Sgshapiro				if (Verbose < 2)
207990792Sgshapiro					(void) sm_io_fprintf(smioout,
208090792Sgshapiro							     SM_TIME_DEFAULT,
208190792Sgshapiro							     "> %s\n", buf);
208290792Sgshapiro				testmodeline(buf, &MainEnvelope);
208390792Sgshapiro			}
208490792Sgshapiro			SM_EXCEPT(exc, "[!F]*")
208590792Sgshapiro			{
208690792Sgshapiro				/*
208790792Sgshapiro				**  8.10 just prints \n on interrupt.
208890792Sgshapiro				**  I'm printing the exception here in case
208990792Sgshapiro				**  sendmail is extended to raise additional
209090792Sgshapiro				**  exceptions in this context.
209190792Sgshapiro				*/
209290792Sgshapiro
209390792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
209490792Sgshapiro						     "\n");
209590792Sgshapiro				sm_exc_print(exc, smioout);
209690792Sgshapiro			}
209790792Sgshapiro			SM_END_TRY
209838032Speter		}
209938032Speter	}
210038032Speter
210190792Sgshapiro#if STARTTLS
210290792Sgshapiro	tls_ok = true;
210390792Sgshapiro	if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER)
210490792Sgshapiro	{
210590792Sgshapiro		/* check whether STARTTLS is turned off for the client */
210690792Sgshapiro		if (chkclientmodifiers(D_NOTLS))
210790792Sgshapiro			tls_ok = false;
210890792Sgshapiro	}
210990792Sgshapiro	else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
211090792Sgshapiro		 OpMode == MD_SMTP)
211190792Sgshapiro	{
211290792Sgshapiro		/* check whether STARTTLS is turned off for the server */
211390792Sgshapiro		if (chkdaemonmodifiers(D_NOTLS))
211490792Sgshapiro			tls_ok = false;
211590792Sgshapiro	}
211690792Sgshapiro	else	/* other modes don't need STARTTLS */
211790792Sgshapiro		tls_ok = false;
211864562Sgshapiro
211990792Sgshapiro	if (tls_ok)
212090792Sgshapiro	{
212190792Sgshapiro		/* basic TLS initialization */
212290792Sgshapiro		tls_ok = init_tls_library();
212390792Sgshapiro	}
212490792Sgshapiro
212590792Sgshapiro	if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
212690792Sgshapiro	{
212790792Sgshapiro		/* disable TLS for client */
212890792Sgshapiro		setclttls(false);
212990792Sgshapiro	}
213090792Sgshapiro#endif /* STARTTLS */
213190792Sgshapiro
213264562Sgshapiro	/*
213338032Speter	**  If collecting stuff from the queue, go start doing that.
213438032Speter	*/
213538032Speter
213664562Sgshapiro	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
213738032Speter	{
213890792Sgshapiro		pid_t pid = -1;
213990792Sgshapiro
214090792Sgshapiro#if STARTTLS
214190792Sgshapiro		/* init TLS for client, ignore result for now */
214290792Sgshapiro		(void) initclttls(tls_ok);
214390792Sgshapiro#endif /* STARTTLS */
214490792Sgshapiro
214590792Sgshapiro		/*
214690792Sgshapiro		**  The parent process of the caller of runqueue() needs
214790792Sgshapiro		**  to stay around for a possible SIGTERM. The SIGTERM will
214890792Sgshapiro		**  tell this process that all of the queue runners children
214990792Sgshapiro		**  need to be sent SIGTERM as well. At the same time, we
215090792Sgshapiro		**  want to return control to the command line. So we do an
215190792Sgshapiro		**  extra fork().
215290792Sgshapiro		*/
215390792Sgshapiro
215490792Sgshapiro		if (Verbose || foregroundqueue || (pid = fork()) <= 0)
215564562Sgshapiro		{
215690792Sgshapiro			/*
215790792Sgshapiro			**  If the fork() failed we should still try to do
215890792Sgshapiro			**  the queue run. If it succeeded then the child
215990792Sgshapiro			**  is going to start the run and wait for all
216090792Sgshapiro			**  of the children to finish.
216190792Sgshapiro			*/
216290792Sgshapiro
216390792Sgshapiro			if (pid == 0)
216490792Sgshapiro			{
216590792Sgshapiro				/* Reset global flags */
216690792Sgshapiro				RestartRequest = NULL;
216790792Sgshapiro				ShutdownRequest = NULL;
216890792Sgshapiro				PendingSignal = 0;
216990792Sgshapiro
217090792Sgshapiro				/* disconnect from terminal */
217190792Sgshapiro				disconnect(2, CurEnv);
217290792Sgshapiro			}
217390792Sgshapiro
217490792Sgshapiro			CurrentPid = getpid();
217590792Sgshapiro			if (qgrp != NOQGRP)
217690792Sgshapiro			{
2177110560Sgshapiro				int rwgflags = RWG_NONE;
2178110560Sgshapiro
217990792Sgshapiro				/*
218090792Sgshapiro				**  To run a specific queue group mark it to
218190792Sgshapiro				**  be run, select the work group it's in and
218290792Sgshapiro				**  increment the work counter.
218390792Sgshapiro				*/
218490792Sgshapiro
218594334Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
218694334Sgshapiro				     i++)
218794334Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
218894334Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
2189110560Sgshapiro				if (Verbose)
2190110560Sgshapiro					rwgflags |= RWG_VERBOSE;
2191110560Sgshapiro				if (queuepersistent)
2192110560Sgshapiro					rwgflags |= RWG_PERSISTENT;
2193110560Sgshapiro				rwgflags |= RWG_FORCE;
219490792Sgshapiro				(void) run_work_group(Queue[qgrp]->qg_wgrp,
2195110560Sgshapiro						      rwgflags);
219690792Sgshapiro			}
219790792Sgshapiro			else
219890792Sgshapiro				(void) runqueue(false, Verbose,
219990792Sgshapiro						queuepersistent, true);
220090792Sgshapiro
220190792Sgshapiro			/* set the title to make it easier to find */
220290792Sgshapiro			sm_setproctitle(true, CurEnv, "Queue control");
220390792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
220490792Sgshapiro			while (CurChildren > 0)
220590792Sgshapiro			{
220690792Sgshapiro				int status;
220790792Sgshapiro				pid_t ret;
220890792Sgshapiro
2209125820Sgshapiro				errno = 0;
221090792Sgshapiro				while ((ret = sm_wait(&status)) <= 0)
2211125820Sgshapiro				{
2212125820Sgshapiro					if (errno == ECHILD)
2213125820Sgshapiro					{
2214125820Sgshapiro						/*
2215125820Sgshapiro						**  Oops... something got messed
2216125820Sgshapiro						**  up really bad. Waiting for
2217125820Sgshapiro						**  non-existent children
2218125820Sgshapiro						**  shouldn't happen. Let's get
2219125820Sgshapiro						**  out of here.
2220125820Sgshapiro						*/
2221125820Sgshapiro
2222125820Sgshapiro						CurChildren = 0;
2223125820Sgshapiro						break;
2224125820Sgshapiro					}
222590792Sgshapiro					continue;
2226125820Sgshapiro				}
222790792Sgshapiro
2228125820Sgshapiro				/* something is really really wrong */
2229125820Sgshapiro				if (errno == ECHILD)
2230125820Sgshapiro				{
2231125820Sgshapiro					sm_syslog(LOG_ERR, NOQID,
2232125820Sgshapiro						  "queue control process: lost all children: wait returned ECHILD");
2233125820Sgshapiro					break;
2234125820Sgshapiro				}
2235125820Sgshapiro
223690792Sgshapiro				/* Only drop when a child gives status */
223790792Sgshapiro				if (WIFSTOPPED(status))
223890792Sgshapiro					continue;
223990792Sgshapiro
224090792Sgshapiro				proc_list_drop(ret, status, NULL);
224190792Sgshapiro			}
224264562Sgshapiro		}
224390792Sgshapiro		finis(true, true, ExitStat);
224490792Sgshapiro		/* NOTREACHED */
224538032Speter	}
224638032Speter
224771345Sgshapiro# if SASL
224871345Sgshapiro	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
224971345Sgshapiro	{
225090792Sgshapiro		/* check whether AUTH is turned off for the server */
225190792Sgshapiro		if (!chkdaemonmodifiers(D_NOAUTH) &&
225290792Sgshapiro		    (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
225371345Sgshapiro			syserr("!sasl_server_init failed! [%s]",
225490792Sgshapiro				sasl_errstring(i, NULL, NULL));
225571345Sgshapiro	}
225671345Sgshapiro# endif /* SASL */
225771345Sgshapiro
225890792Sgshapiro	if (OpMode == MD_SMTP)
225990792Sgshapiro	{
226090792Sgshapiro		proc_list_add(CurrentPid, "Sendmail SMTP Agent",
2261132943Sgshapiro			      PROC_DAEMON, 0, -1, NULL);
226290792Sgshapiro
226390792Sgshapiro		/* clean up background delivery children */
226490792Sgshapiro		(void) sm_signal(SIGCHLD, reapchild);
226590792Sgshapiro	}
226690792Sgshapiro
226738032Speter	/*
226838032Speter	**  If a daemon, wait for a request.
226938032Speter	**	getrequests will always return in a child.
227038032Speter	**	If we should also be processing the queue, start
227138032Speter	**		doing it in background.
227238032Speter	**	We check for any errors that might have happened
227338032Speter	**		during startup.
227438032Speter	*/
227538032Speter
227698841Sgshapiro	if (OpMode == MD_DAEMON || QueueIntvl > 0)
227738032Speter	{
227838032Speter		char dtype[200];
227938032Speter
228038032Speter		if (!run_in_foreground && !tTd(99, 100))
228138032Speter		{
228238032Speter			/* put us in background */
228338032Speter			i = fork();
228438032Speter			if (i < 0)
228538032Speter				syserr("daemon: cannot fork");
228638032Speter			if (i != 0)
228790792Sgshapiro			{
228890792Sgshapiro				finis(false, true, EX_OK);
228990792Sgshapiro				/* NOTREACHED */
229090792Sgshapiro			}
229138032Speter
229290792Sgshapiro			/*
229390792Sgshapiro			**  Initialize exception stack and default exception
229490792Sgshapiro			**  handler for child process.
229590792Sgshapiro			*/
229690792Sgshapiro
229790792Sgshapiro			/* Reset global flags */
229890792Sgshapiro			RestartRequest = NULL;
229990792Sgshapiro			RestartWorkGroup = false;
230090792Sgshapiro			ShutdownRequest = NULL;
230190792Sgshapiro			PendingSignal = 0;
230290792Sgshapiro			CurrentPid = getpid();
230390792Sgshapiro
230490792Sgshapiro			sm_exc_newthread(fatal_error);
230590792Sgshapiro
230638032Speter			/* disconnect from our controlling tty */
230790792Sgshapiro			disconnect(2, &MainEnvelope);
230838032Speter		}
230938032Speter
231038032Speter		dtype[0] = '\0';
231138032Speter		if (OpMode == MD_DAEMON)
231290792Sgshapiro		{
231390792Sgshapiro			(void) sm_strlcat(dtype, "+SMTP", sizeof dtype);
231490792Sgshapiro			DaemonPid = CurrentPid;
231590792Sgshapiro		}
231698841Sgshapiro		if (QueueIntvl > 0)
231738032Speter		{
231890792Sgshapiro			(void) sm_strlcat2(dtype,
231990792Sgshapiro					   queuepersistent
232090792Sgshapiro					   ? "+persistent-queueing@"
232190792Sgshapiro					   : "+queueing@",
232290792Sgshapiro					   pintvl(QueueIntvl, true),
232390792Sgshapiro					   sizeof dtype);
232438032Speter		}
232538032Speter		if (tTd(0, 1))
232690792Sgshapiro			(void) sm_strlcat(dtype, "+debugging", sizeof dtype);
232738032Speter
232838032Speter		sm_syslog(LOG_INFO, NOQID,
232964562Sgshapiro			  "starting daemon (%s): %s", Version, dtype + 1);
233090792Sgshapiro#if XLA
233138032Speter		xla_create_file();
233264562Sgshapiro#endif /* XLA */
233338032Speter
233464562Sgshapiro		/* save daemon type in a macro for possible PidFile use */
233590792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
233690792Sgshapiro			macid("{daemon_info}"), dtype + 1);
233764562Sgshapiro
233864562Sgshapiro		/* save queue interval in a macro for possible PidFile use */
233990792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
234090792Sgshapiro			macid("{queue_interval}"), pintvl(QueueIntvl, true));
234164562Sgshapiro
234290792Sgshapiro		/* workaround: can't seem to release the signal in the parent */
234390792Sgshapiro		(void) sm_signal(SIGHUP, sighup);
234490792Sgshapiro		(void) sm_releasesignal(SIGHUP);
234590792Sgshapiro		(void) sm_signal(SIGTERM, sigterm);
234690792Sgshapiro
234798841Sgshapiro		if (QueueIntvl > 0)
234838032Speter		{
234990792Sgshapiro			(void) runqueue(true, false, queuepersistent, true);
235090792Sgshapiro
235190792Sgshapiro			/*
235290792Sgshapiro			**  If queuepersistent but not in daemon mode then
235390792Sgshapiro			**  we're going to do the queue runner monitoring here.
235490792Sgshapiro			**  If in daemon mode then the monitoring will happen
235590792Sgshapiro			**  elsewhere.
235690792Sgshapiro			*/
235790792Sgshapiro
235890792Sgshapiro			if (OpMode != MD_DAEMON && queuepersistent)
235990792Sgshapiro			{
2360132943Sgshapiro				/*
2361132943Sgshapiro				**  Write the pid to file
2362132943Sgshapiro				**  XXX Overwrites sendmail.pid
2363132943Sgshapiro				*/
2364132943Sgshapiro
2365132943Sgshapiro				log_sendmail_pid(&MainEnvelope);
2366132943Sgshapiro
236790792Sgshapiro				/* set the title to make it easier to find */
236890792Sgshapiro				sm_setproctitle(true, CurEnv, "Queue control");
236990792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
237090792Sgshapiro				while (CurChildren > 0)
237190792Sgshapiro				{
237290792Sgshapiro					int status;
237390792Sgshapiro					pid_t ret;
237490792Sgshapiro					int group;
237590792Sgshapiro
2376120256Sgshapiro					CHECK_RESTART;
2377125820Sgshapiro					errno = 0;
237890792Sgshapiro					while ((ret = sm_wait(&status)) <= 0)
2379125820Sgshapiro					{
2380125820Sgshapiro						/*
2381125820Sgshapiro						**  Waiting for non-existent
2382125820Sgshapiro						**  children shouldn't happen.
2383125820Sgshapiro						**  Let's get out of here if
2384125820Sgshapiro						**  it occurs.
2385125820Sgshapiro						*/
2386125820Sgshapiro
2387125820Sgshapiro						if (errno == ECHILD)
2388125820Sgshapiro						{
2389125820Sgshapiro							CurChildren = 0;
2390125820Sgshapiro							break;
2391125820Sgshapiro						}
239290792Sgshapiro						continue;
2393125820Sgshapiro					}
239490792Sgshapiro
2395125820Sgshapiro					/* something is really really wrong */
2396125820Sgshapiro					if (errno == ECHILD)
2397125820Sgshapiro					{
2398125820Sgshapiro						sm_syslog(LOG_ERR, NOQID,
2399125820Sgshapiro							  "persistent queue runner control process: lost all children: wait returned ECHILD");
2400125820Sgshapiro						break;
2401125820Sgshapiro					}
2402125820Sgshapiro
240390792Sgshapiro					if (WIFSTOPPED(status))
240490792Sgshapiro						continue;
240590792Sgshapiro
240690792Sgshapiro					/* Probe only on a child status */
240790792Sgshapiro					proc_list_drop(ret, status, &group);
240890792Sgshapiro
240990792Sgshapiro					if (WIFSIGNALED(status))
241090792Sgshapiro					{
241190792Sgshapiro						if (WCOREDUMP(status))
241290792Sgshapiro						{
241390792Sgshapiro							sm_syslog(LOG_ERR, NOQID,
241490792Sgshapiro								  "persistent queue runner=%d core dumped, signal=%d",
241590792Sgshapiro								  group, WTERMSIG(status));
241690792Sgshapiro
2417120256Sgshapiro							/* don't restart this */
2418120256Sgshapiro							mark_work_group_restart(
2419120256Sgshapiro								group, -1);
242090792Sgshapiro							continue;
242190792Sgshapiro						}
242290792Sgshapiro
242390792Sgshapiro						sm_syslog(LOG_ERR, NOQID,
242490792Sgshapiro							  "persistent queue runner=%d died, signal=%d",
242590792Sgshapiro							  group, WTERMSIG(status));
242690792Sgshapiro					}
242790792Sgshapiro
242890792Sgshapiro					/*
242990792Sgshapiro					**  When debugging active, don't
243090792Sgshapiro					**  restart the persistent queues.
243190792Sgshapiro					**  But do log this as info.
243290792Sgshapiro					*/
243390792Sgshapiro
243490792Sgshapiro					if (sm_debug_active(&DebugNoPRestart,
243590792Sgshapiro							    1))
243690792Sgshapiro					{
243790792Sgshapiro						sm_syslog(LOG_DEBUG, NOQID,
243890792Sgshapiro							  "persistent queue runner=%d, exited",
243990792Sgshapiro							  group);
2440120256Sgshapiro						mark_work_group_restart(group,
2441120256Sgshapiro									-1);
244290792Sgshapiro					}
244390792Sgshapiro				}
244490792Sgshapiro				finis(true, true, ExitStat);
244590792Sgshapiro				/* NOTREACHED */
244690792Sgshapiro			}
244790792Sgshapiro
244838032Speter			if (OpMode != MD_DAEMON)
244938032Speter			{
245090792Sgshapiro				char qtype[200];
245190792Sgshapiro
245290792Sgshapiro				/*
245390792Sgshapiro				**  Write the pid to file
245490792Sgshapiro				**  XXX Overwrites sendmail.pid
245590792Sgshapiro				*/
245690792Sgshapiro
245790792Sgshapiro				log_sendmail_pid(&MainEnvelope);
245890792Sgshapiro
245990792Sgshapiro				/* set the title to make it easier to find */
246090792Sgshapiro				qtype[0] = '\0';
246190792Sgshapiro				(void) sm_strlcpyn(qtype, sizeof qtype, 4,
246290792Sgshapiro						   "Queue runner@",
246390792Sgshapiro						   pintvl(QueueIntvl, true),
246490792Sgshapiro						   " for ",
246590792Sgshapiro						   QueueDir);
246690792Sgshapiro				sm_setproctitle(true, CurEnv, qtype);
246738032Speter				for (;;)
246838032Speter				{
246964562Sgshapiro					(void) pause();
2470132943Sgshapiro
2471120256Sgshapiro					CHECK_RESTART;
2472132943Sgshapiro
247390792Sgshapiro					if (doqueuerun())
247490792Sgshapiro						(void) runqueue(true, false,
247590792Sgshapiro								false, false);
247638032Speter				}
247738032Speter			}
247838032Speter		}
247990792Sgshapiro		dropenvelope(&MainEnvelope, true, false);
248038032Speter
248190792Sgshapiro#if STARTTLS
248264562Sgshapiro		/* init TLS for server, ignore result for now */
248390792Sgshapiro		(void) initsrvtls(tls_ok);
248490792Sgshapiro#endif /* STARTTLS */
2485110560Sgshapiro
248690792Sgshapiro	nextreq:
248790792Sgshapiro		p_flags = getrequests(&MainEnvelope);
248838032Speter
248938032Speter		/* drop privileges */
249090792Sgshapiro		(void) drop_privileges(false);
249138032Speter
249238032Speter		/*
249338032Speter		**  Get authentication data
249490792Sgshapiro		**  Set _ macro in BlankEnvelope before calling newenvelope().
249538032Speter		*/
249638032Speter
249790792Sgshapiro		authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
249890792Sgshapiro						     NULL), &forged);
249990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
250090792Sgshapiro
250190792Sgshapiro		/* at this point we are in a child: reset state */
250290792Sgshapiro		sm_rpool_free(MainEnvelope.e_rpool);
250390792Sgshapiro		(void) newenvelope(&MainEnvelope, &MainEnvelope,
250490792Sgshapiro				   sm_rpool_new_x(NULL));
250538032Speter	}
250638032Speter
250764562Sgshapiro	if (LogLevel > 9)
250864562Sgshapiro	{
250964562Sgshapiro		/* log connection information */
2510110560Sgshapiro		sm_syslog(LOG_INFO, NULL, "connect from %s", authinfo);
251164562Sgshapiro	}
251264562Sgshapiro
251338032Speter	/*
251438032Speter	**  If running SMTP protocol, start collecting and executing
251538032Speter	**  commands.  This will never return.
251638032Speter	*/
251738032Speter
251838032Speter	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
251938032Speter	{
252038032Speter		char pbuf[20];
252138032Speter
252238032Speter		/*
252338032Speter		**  Save some macros for check_* rulesets.
252438032Speter		*/
252538032Speter
252638032Speter		if (forged)
252738032Speter		{
252838032Speter			char ipbuf[103];
252938032Speter
253090792Sgshapiro			(void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
253190792Sgshapiro					   anynet_ntoa(&RealHostAddr));
253290792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
253390792Sgshapiro				  macid("{client_name}"), ipbuf);
253438032Speter		}
253538032Speter		else
253690792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
253790792Sgshapiro				  macid("{client_name}"), RealHostName);
2538132943Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
2539132943Sgshapiro			  macid("{client_ptr}"), RealHostName);
254090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
254190792Sgshapiro			  macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
254290792Sgshapiro		sm_getla();
254338032Speter
254490792Sgshapiro		switch (RealHostAddr.sa.sa_family)
254564562Sgshapiro		{
254690792Sgshapiro#if NETINET
254764562Sgshapiro		  case AF_INET:
254890792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "%d",
254990792Sgshapiro					   RealHostAddr.sin.sin_port);
255064562Sgshapiro			break;
255190792Sgshapiro#endif /* NETINET */
255290792Sgshapiro#if NETINET6
255364562Sgshapiro		  case AF_INET6:
255490792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "%d",
255590792Sgshapiro					   RealHostAddr.sin6.sin6_port);
255664562Sgshapiro			break;
255790792Sgshapiro#endif /* NETINET6 */
255864562Sgshapiro		  default:
255990792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "0");
256064562Sgshapiro			break;
256164562Sgshapiro		}
256290792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
256390792Sgshapiro			macid("{client_port}"), pbuf);
256442575Speter
256538032Speter		if (OpMode == MD_DAEMON)
256638032Speter		{
256738032Speter			/* validate the connection */
256890792Sgshapiro			HoldErrs = true;
256938032Speter			nullserver = validate_connection(&RealHostAddr,
2570132943Sgshapiro						macvalue(macid("{client_name}"),
2571132943Sgshapiro							&MainEnvelope),
2572132943Sgshapiro						&MainEnvelope);
257390792Sgshapiro			HoldErrs = false;
257438032Speter		}
257564562Sgshapiro		else if (p_flags == NULL)
257664562Sgshapiro		{
257764562Sgshapiro			p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
257864562Sgshapiro			clrbitmap(p_flags);
257964562Sgshapiro		}
258090792Sgshapiro#if STARTTLS
258164562Sgshapiro		if (OpMode == MD_SMTP)
258290792Sgshapiro			(void) initsrvtls(tls_ok);
258390792Sgshapiro#endif /* STARTTLS */
258471345Sgshapiro
258590792Sgshapiro		/* turn off profiling */
258690792Sgshapiro		SM_PROF(1);
258790792Sgshapiro		smtp(nullserver, *p_flags, &MainEnvelope);
2588110560Sgshapiro
2589110560Sgshapiro		if (tTd(93, 100))
2590110560Sgshapiro		{
2591110560Sgshapiro			/* turn off profiling */
2592110560Sgshapiro			SM_PROF(0);
2593110560Sgshapiro			if (OpMode == MD_DAEMON)
2594110560Sgshapiro				goto nextreq;
2595110560Sgshapiro		}
259638032Speter	}
259738032Speter
259890792Sgshapiro	sm_rpool_free(MainEnvelope.e_rpool);
259990792Sgshapiro	clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
260038032Speter	if (OpMode == MD_VERIFY)
260138032Speter	{
260290792Sgshapiro		set_delivery_mode(SM_VERIFY, &MainEnvelope);
260338032Speter		PostMasterCopy = NULL;
260438032Speter	}
260538032Speter	else
260638032Speter	{
260738032Speter		/* interactive -- all errors are global */
260890792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
260938032Speter	}
261038032Speter
261138032Speter	/*
261238032Speter	**  Do basic system initialization and set the sender
261338032Speter	*/
261438032Speter
261590792Sgshapiro	initsys(&MainEnvelope);
261690792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
261790792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
261890792Sgshapiro	setsender(from, &MainEnvelope, NULL, '\0', false);
261964562Sgshapiro	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
262090792Sgshapiro	    (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
262190792Sgshapiro	     strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
262264562Sgshapiro	{
262390792Sgshapiro		auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
262490792Sgshapiro			     RealUserName, from, warn_f_flag);
262564562Sgshapiro#if SASL
262690792Sgshapiro		auth = false;
262764562Sgshapiro#endif /* SASL */
262864562Sgshapiro	}
262964562Sgshapiro	if (auth)
263064562Sgshapiro	{
263164562Sgshapiro		char *fv;
263264562Sgshapiro
263364562Sgshapiro		/* set the initial sender for AUTH= to $f@$j */
263490792Sgshapiro		fv = macvalue('f', &MainEnvelope);
263564562Sgshapiro		if (fv == NULL || *fv == '\0')
263690792Sgshapiro			MainEnvelope.e_auth_param = NULL;
263764562Sgshapiro		else
263864562Sgshapiro		{
263964562Sgshapiro			if (strchr(fv, '@') == NULL)
264064562Sgshapiro			{
264190792Sgshapiro				i = strlen(fv) + strlen(macvalue('j',
264290792Sgshapiro							&MainEnvelope)) + 2;
264390792Sgshapiro				p = sm_malloc_x(i);
264490792Sgshapiro				(void) sm_strlcpyn(p, i, 3, fv, "@",
264590792Sgshapiro						   macvalue('j',
264690792Sgshapiro							    &MainEnvelope));
264764562Sgshapiro			}
264864562Sgshapiro			else
264990792Sgshapiro				p = sm_strdup_x(fv);
265090792Sgshapiro			MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
265190792Sgshapiro								      xtextify(p, "="));
265290792Sgshapiro			sm_free(p);  /* XXX */
265364562Sgshapiro		}
265464562Sgshapiro	}
265590792Sgshapiro	if (macvalue('s', &MainEnvelope) == NULL)
265690792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
265738032Speter
265890792Sgshapiro	av = argv + optind;
265938032Speter	if (*av == NULL && !GrabTo)
266038032Speter	{
266190792Sgshapiro		MainEnvelope.e_to = NULL;
266290792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
266390792Sgshapiro		HoldErrs = false;
266490792Sgshapiro		SuperSafe = SAFE_NO;
266538032Speter		usrerr("Recipient names must be specified");
266638032Speter
266738032Speter		/* collect body for UUCP return */
266838032Speter		if (OpMode != MD_VERIFY)
2669120256Sgshapiro			collect(InChannel, false, NULL, &MainEnvelope, true);
267090792Sgshapiro		finis(true, true, EX_USAGE);
267190792Sgshapiro		/* NOTREACHED */
267238032Speter	}
267338032Speter
267438032Speter	/*
267538032Speter	**  Scan argv and deliver the message to everyone.
267638032Speter	*/
267738032Speter
267890792Sgshapiro	save_val = LogUsrErrs;
267990792Sgshapiro	LogUsrErrs = true;
268090792Sgshapiro	sendtoargv(av, &MainEnvelope);
268190792Sgshapiro	LogUsrErrs = save_val;
268238032Speter
268338032Speter	/* if we have had errors sofar, arrange a meaningful exit stat */
268438032Speter	if (Errors > 0 && ExitStat == EX_OK)
268538032Speter		ExitStat = EX_USAGE;
268638032Speter
268738032Speter#if _FFR_FIX_DASHT
268838032Speter	/*
268938032Speter	**  If using -t, force not sending to argv recipients, even
269038032Speter	**  if they are mentioned in the headers.
269138032Speter	*/
269238032Speter
269338032Speter	if (GrabTo)
269438032Speter	{
269538032Speter		ADDRESS *q;
269664562Sgshapiro
269790792Sgshapiro		for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
269864562Sgshapiro			q->q_state = QS_REMOVED;
269938032Speter	}
270064562Sgshapiro#endif /* _FFR_FIX_DASHT */
270138032Speter
270238032Speter	/*
270338032Speter	**  Read the input mail.
270438032Speter	*/
270538032Speter
270690792Sgshapiro	MainEnvelope.e_to = NULL;
270738032Speter	if (OpMode != MD_VERIFY || GrabTo)
270838032Speter	{
270990792Sgshapiro		int savederrors;
271090792Sgshapiro		unsigned long savedflags;
271138032Speter
271290792Sgshapiro		/*
271390792Sgshapiro		**  workaround for compiler warning on Irix:
271490792Sgshapiro		**  do not initialize variable in the definition, but
271590792Sgshapiro		**  later on:
271690792Sgshapiro		**  warning(1548): transfer of control bypasses
271790792Sgshapiro		**  initialization of:
271890792Sgshapiro		**  variable "savederrors" (declared at line 2570)
271990792Sgshapiro		**  variable "savedflags" (declared at line 2571)
272090792Sgshapiro		**  goto giveup;
272190792Sgshapiro		*/
272290792Sgshapiro
272390792Sgshapiro		savederrors = Errors;
272490792Sgshapiro		savedflags = MainEnvelope.e_flags & EF_FATALERRS;
272590792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
272690792Sgshapiro		MainEnvelope.e_flags &= ~EF_FATALERRS;
272764562Sgshapiro		Errors = 0;
272864562Sgshapiro		buffer_errors();
2729120256Sgshapiro		collect(InChannel, false, NULL, &MainEnvelope, true);
273038032Speter
273164562Sgshapiro		/* header checks failed */
273264562Sgshapiro		if (Errors > 0)
273364562Sgshapiro		{
273490792Sgshapiro  giveup:
273590792Sgshapiro			if (!GrabTo)
273664562Sgshapiro			{
273790792Sgshapiro				/* Log who the mail would have gone to */
273890792Sgshapiro				logundelrcpts(&MainEnvelope,
273990792Sgshapiro					      MainEnvelope.e_message,
274090792Sgshapiro					      8, false);
274164562Sgshapiro			}
274290792Sgshapiro			flush_errors(true);
274390792Sgshapiro			finis(true, true, ExitStat);
274464562Sgshapiro			/* NOTREACHED */
274564562Sgshapiro			return -1;
274664562Sgshapiro		}
274764562Sgshapiro
274838032Speter		/* bail out if message too large */
274990792Sgshapiro		if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
275038032Speter		{
275190792Sgshapiro			finis(true, true, ExitStat != EX_OK ? ExitStat
275290792Sgshapiro							    : EX_DATAERR);
275364562Sgshapiro			/* NOTREACHED */
275438032Speter			return -1;
275538032Speter		}
275698121Sgshapiro
275798121Sgshapiro		/* set message size */
275898121Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%ld",
275998121Sgshapiro				   MainEnvelope.e_msgsize);
276098121Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
276198121Sgshapiro			  macid("{msg_size}"), buf);
276298121Sgshapiro
276364562Sgshapiro		Errors = savederrors;
276490792Sgshapiro		MainEnvelope.e_flags |= savedflags;
276538032Speter	}
276638032Speter	errno = 0;
276738032Speter
276838032Speter	if (tTd(1, 1))
276990792Sgshapiro		sm_dprintf("From person = \"%s\"\n",
277090792Sgshapiro			   MainEnvelope.e_from.q_paddr);
277138032Speter
277290792Sgshapiro	/* Check if quarantining stats should be updated */
277390792Sgshapiro	if (MainEnvelope.e_quarmsg != NULL)
277490792Sgshapiro		markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
277590792Sgshapiro
277638032Speter	/*
277738032Speter	**  Actually send everything.
277838032Speter	**	If verifying, just ack.
277938032Speter	*/
278038032Speter
278190792Sgshapiro	if (Errors == 0)
278238032Speter	{
278390792Sgshapiro		if (!split_by_recipient(&MainEnvelope) &&
278490792Sgshapiro		    bitset(EF_FATALERRS, MainEnvelope.e_flags))
278590792Sgshapiro			goto giveup;
278638032Speter	}
278790792Sgshapiro
278890792Sgshapiro	/* make sure we deliver at least the first envelope */
278990792Sgshapiro	i = FastSplit > 0 ? 0 : -1;
279090792Sgshapiro	for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
279190792Sgshapiro	{
279290792Sgshapiro		ENVELOPE *next;
279390792Sgshapiro
279490792Sgshapiro		e->e_from.q_state = QS_SENDER;
279590792Sgshapiro		if (tTd(1, 5))
279690792Sgshapiro		{
279790792Sgshapiro			sm_dprintf("main[%d]: QS_SENDER ", i);
2798132943Sgshapiro			printaddr(sm_debug_file(), &e->e_from, false);
279990792Sgshapiro		}
280090792Sgshapiro		e->e_to = NULL;
280190792Sgshapiro		sm_getla();
280290792Sgshapiro		GrabTo = false;
280364562Sgshapiro#if NAMED_BIND
280490792Sgshapiro		_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
280590792Sgshapiro		_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
280664562Sgshapiro#endif /* NAMED_BIND */
280790792Sgshapiro		next = e->e_sibling;
280890792Sgshapiro		e->e_sibling = NULL;
280938032Speter
281090792Sgshapiro		/* after FastSplit envelopes: queue up */
281190792Sgshapiro		sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
281290792Sgshapiro		e->e_sibling = next;
281390792Sgshapiro	}
281490792Sgshapiro
281538032Speter	/*
281638032Speter	**  All done.
281738032Speter	**	Don't send return error message if in VERIFY mode.
281838032Speter	*/
281938032Speter
282090792Sgshapiro	finis(true, true, ExitStat);
282164562Sgshapiro	/* NOTREACHED */
282264562Sgshapiro	return ExitStat;
282338032Speter}
282490792Sgshapiro/*
282577349Sgshapiro**  STOP_SENDMAIL -- Stop the running program
282677349Sgshapiro**
282777349Sgshapiro**	Parameters:
282877349Sgshapiro**		none.
282977349Sgshapiro**
283077349Sgshapiro**	Returns:
283177349Sgshapiro**		none.
283277349Sgshapiro**
283377349Sgshapiro**	Side Effects:
283477349Sgshapiro**		exits.
283577349Sgshapiro*/
283638032Speter
283777349Sgshapirovoid
283877349Sgshapirostop_sendmail()
283977349Sgshapiro{
284077349Sgshapiro	/* reset uid for process accounting */
284177349Sgshapiro	endpwent();
284277349Sgshapiro	(void) setuid(RealUid);
284377349Sgshapiro	exit(EX_OK);
284477349Sgshapiro}
284590792Sgshapiro/*
284638032Speter**  FINIS -- Clean up and exit.
284738032Speter**
284838032Speter**	Parameters:
284942575Speter**		drop -- whether or not to drop CurEnv envelope
285090792Sgshapiro**		cleanup -- call exit() or _exit()?
285142575Speter**		exitstat -- exit status to use for exit() call
285238032Speter**
285338032Speter**	Returns:
285438032Speter**		never
285538032Speter**
285638032Speter**	Side Effects:
285738032Speter**		exits sendmail
285838032Speter*/
285938032Speter
286038032Spetervoid
286190792Sgshapirofinis(drop, cleanup, exitstat)
286242575Speter	bool drop;
286390792Sgshapiro	bool cleanup;
286442575Speter	volatile int exitstat;
286538032Speter{
2866132943Sgshapiro	char pidpath[MAXPATHLEN];
286798121Sgshapiro
286877349Sgshapiro	/* Still want to process new timeouts added below */
286990792Sgshapiro	sm_clear_events();
287090792Sgshapiro	(void) sm_releasesignal(SIGALRM);
287142575Speter
287238032Speter	if (tTd(2, 1))
287338032Speter	{
287490792Sgshapiro		sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
287590792Sgshapiro			   exitstat,
287690792Sgshapiro			   CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
287738032Speter		printenvflags(CurEnv);
287838032Speter	}
287938032Speter	if (tTd(2, 9))
288090792Sgshapiro		printopenfds(false);
288138032Speter
288290792Sgshapiro	SM_TRY
288390792Sgshapiro		/*
288490792Sgshapiro		**  Clean up.  This might raise E:mta.quickabort
288590792Sgshapiro		*/
288638032Speter
288790792Sgshapiro		/* clean up temp files */
288890792Sgshapiro		CurEnv->e_to = NULL;
288990792Sgshapiro		if (drop)
289090792Sgshapiro		{
289190792Sgshapiro			if (CurEnv->e_id != NULL)
289290792Sgshapiro			{
289390792Sgshapiro				dropenvelope(CurEnv, true, false);
289490792Sgshapiro				sm_rpool_free(CurEnv->e_rpool);
289590792Sgshapiro				CurEnv->e_rpool = NULL;
289690792Sgshapiro			}
289790792Sgshapiro			else
289890792Sgshapiro				poststats(StatFile);
289990792Sgshapiro		}
290038032Speter
290190792Sgshapiro		/* flush any cached connections */
290290792Sgshapiro		mci_flush(true, NULL);
290338032Speter
290490792Sgshapiro		/* close maps belonging to this pid */
290590792Sgshapiro		closemaps(false);
290642575Speter
290764562Sgshapiro#if USERDB
290890792Sgshapiro		/* close UserDatabase */
290990792Sgshapiro		_udbx_close();
291064562Sgshapiro#endif /* USERDB */
291142575Speter
291290792Sgshapiro#if SASL
291390792Sgshapiro		stop_sasl_client();
291490792Sgshapiro#endif /* SASL */
291590792Sgshapiro
291690792Sgshapiro#if XLA
291790792Sgshapiro		/* clean up extended load average stuff */
291890792Sgshapiro		xla_all_end();
291964562Sgshapiro#endif /* XLA */
292038032Speter
292190792Sgshapiro	SM_FINALLY
292290792Sgshapiro		/*
292390792Sgshapiro		**  And exit.
292490792Sgshapiro		*/
292538032Speter
292690792Sgshapiro		if (LogLevel > 78)
292790792Sgshapiro			sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
292890792Sgshapiro				  (int) CurrentPid);
292990792Sgshapiro		if (exitstat == EX_TEMPFAIL ||
293090792Sgshapiro		    CurEnv->e_errormode == EM_BERKNET)
293190792Sgshapiro			exitstat = EX_OK;
293264562Sgshapiro
293390792Sgshapiro		/* XXX clean up queues and related data structures */
293490792Sgshapiro		cleanup_queues();
293590792Sgshapiro#if SM_CONF_SHM
293690792Sgshapiro		cleanup_shm(DaemonPid == getpid());
293790792Sgshapiro#endif /* SM_CONF_SHM */
293890792Sgshapiro
2939132943Sgshapiro		/* close locked pid file */
2940132943Sgshapiro		close_sendmail_pid();
2941132943Sgshapiro
2942132943Sgshapiro		if (DaemonPid == getpid() || PidFilePid == getpid())
2943132943Sgshapiro		{
2944132943Sgshapiro			/* blow away the pid file */
2945132943Sgshapiro			expand(PidFile, pidpath, sizeof pidpath, CurEnv);
2946132943Sgshapiro			(void) unlink(pidpath);
2947132943Sgshapiro		}
2948132943Sgshapiro
294990792Sgshapiro		/* reset uid for process accounting */
295090792Sgshapiro		endpwent();
295190792Sgshapiro		sm_mbdb_terminate();
295290792Sgshapiro		(void) setuid(RealUid);
295390792Sgshapiro#if SM_HEAP_CHECK
295490792Sgshapiro		/* dump the heap, if we are checking for memory leaks */
295590792Sgshapiro		if (sm_debug_active(&SmHeapCheck, 2))
295690792Sgshapiro			sm_heap_report(smioout,
295790792Sgshapiro				       sm_debug_level(&SmHeapCheck) - 1);
295890792Sgshapiro#endif /* SM_HEAP_CHECK */
295990792Sgshapiro		if (sm_debug_active(&SmXtrapReport, 1))
296090792Sgshapiro			sm_dprintf("xtrap count = %d\n", SmXtrapCount);
296190792Sgshapiro		if (cleanup)
296290792Sgshapiro			exit(exitstat);
296390792Sgshapiro		else
296490792Sgshapiro			_exit(exitstat);
296590792Sgshapiro	SM_END_TRY
296638032Speter}
296790792Sgshapiro/*
296890792Sgshapiro**  INTINDEBUG -- signal handler for SIGINT in -bt mode
296977349Sgshapiro**
297077349Sgshapiro**	Parameters:
297190792Sgshapiro**		sig -- incoming signal.
297290792Sgshapiro**
297390792Sgshapiro**	Returns:
297490792Sgshapiro**		none.
297590792Sgshapiro**
297690792Sgshapiro**	Side Effects:
297790792Sgshapiro**		longjmps back to test mode loop.
297890792Sgshapiro**
297990792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
298090792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
298190792Sgshapiro**		DOING.
298290792Sgshapiro*/
298390792Sgshapiro
298490792Sgshapiro/* Type of an exception generated on SIGINT during address test mode.  */
298590792Sgshapirostatic const SM_EXC_TYPE_T EtypeInterrupt =
298690792Sgshapiro{
298790792Sgshapiro	SmExcTypeMagic,
298890792Sgshapiro	"S:mta.interrupt",
298990792Sgshapiro	"",
299090792Sgshapiro	sm_etype_printf,
299190792Sgshapiro	"interrupt",
299290792Sgshapiro};
299390792Sgshapiro
299490792Sgshapiro/* ARGSUSED */
299590792Sgshapirostatic SIGFUNC_DECL
299690792Sgshapirointindebug(sig)
299790792Sgshapiro	int sig;
299890792Sgshapiro{
299990792Sgshapiro	int save_errno = errno;
300090792Sgshapiro
300190792Sgshapiro	FIX_SYSV_SIGNAL(sig, intindebug);
300290792Sgshapiro	errno = save_errno;
300390792Sgshapiro	CHECK_CRITICAL(sig);
300490792Sgshapiro	errno = save_errno;
300590792Sgshapiro	sm_exc_raisenew_x(&EtypeInterrupt);
300690792Sgshapiro	errno = save_errno;
300790792Sgshapiro	return SIGFUNC_RETURN;
300890792Sgshapiro}
300990792Sgshapiro/*
301090792Sgshapiro**  SIGTERM -- SIGTERM handler for the daemon
301190792Sgshapiro**
301290792Sgshapiro**	Parameters:
301377349Sgshapiro**		sig -- signal number.
301477349Sgshapiro**
301577349Sgshapiro**	Returns:
301677349Sgshapiro**		none.
301777349Sgshapiro**
301877349Sgshapiro**	Side Effects:
301977349Sgshapiro**		Sets ShutdownRequest which will hopefully trigger
302077349Sgshapiro**		the daemon to exit.
302177349Sgshapiro**
302277349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
302377349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
302477349Sgshapiro**		DOING.
302577349Sgshapiro*/
302677349Sgshapiro
302777349Sgshapiro/* ARGSUSED */
302877349Sgshapirostatic SIGFUNC_DECL
302990792Sgshapirosigterm(sig)
303077349Sgshapiro	int sig;
303177349Sgshapiro{
303277349Sgshapiro	int save_errno = errno;
303377349Sgshapiro
303490792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigterm);
303577349Sgshapiro	ShutdownRequest = "signal";
303677349Sgshapiro	errno = save_errno;
303777349Sgshapiro	return SIGFUNC_RETURN;
303877349Sgshapiro}
303990792Sgshapiro/*
304090792Sgshapiro**  SIGHUP -- handle a SIGHUP signal
304177349Sgshapiro**
304277349Sgshapiro**	Parameters:
304390792Sgshapiro**		sig -- incoming signal.
304477349Sgshapiro**
304577349Sgshapiro**	Returns:
304677349Sgshapiro**		none.
304777349Sgshapiro**
304877349Sgshapiro**	Side Effects:
304990792Sgshapiro**		Sets RestartRequest which should cause the daemon
305090792Sgshapiro**		to restart.
305190792Sgshapiro**
305290792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
305390792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
305490792Sgshapiro**		DOING.
305577349Sgshapiro*/
305677349Sgshapiro
305790792Sgshapiro/* ARGSUSED */
305890792Sgshapirostatic SIGFUNC_DECL
305990792Sgshapirosighup(sig)
306090792Sgshapiro	int sig;
306177349Sgshapiro{
306290792Sgshapiro	int save_errno = errno;
306377349Sgshapiro
306490792Sgshapiro	FIX_SYSV_SIGNAL(sig, sighup);
306590792Sgshapiro	RestartRequest = "signal";
306690792Sgshapiro	errno = save_errno;
306790792Sgshapiro	return SIGFUNC_RETURN;
306890792Sgshapiro}
306990792Sgshapiro/*
307090792Sgshapiro**  SIGPIPE -- signal handler for SIGPIPE
307190792Sgshapiro**
307290792Sgshapiro**	Parameters:
307390792Sgshapiro**		sig -- incoming signal.
307490792Sgshapiro**
307590792Sgshapiro**	Returns:
307690792Sgshapiro**		none.
307790792Sgshapiro**
307890792Sgshapiro**	Side Effects:
307990792Sgshapiro**		Sets StopRequest which should cause the mailq/hoststatus
308090792Sgshapiro**		display to stop.
308190792Sgshapiro**
308290792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
308390792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
308490792Sgshapiro**		DOING.
308590792Sgshapiro*/
308677349Sgshapiro
308790792Sgshapiro/* ARGSUSED */
308890792Sgshapirostatic SIGFUNC_DECL
308990792Sgshapirosigpipe(sig)
309090792Sgshapiro	int sig;
309190792Sgshapiro{
309290792Sgshapiro	int save_errno = errno;
309377349Sgshapiro
309490792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigpipe);
309590792Sgshapiro	StopRequest = true;
309690792Sgshapiro	errno = save_errno;
309790792Sgshapiro	return SIGFUNC_RETURN;
309877349Sgshapiro}
309990792Sgshapiro/*
310038032Speter**  INTSIG -- clean up on interrupt
310138032Speter**
310264562Sgshapiro**	This just arranges to exit.  It pessimizes in that it
310338032Speter**	may resend a message.
310438032Speter**
310538032Speter**	Parameters:
310638032Speter**		none.
310738032Speter**
310838032Speter**	Returns:
310938032Speter**		none.
311038032Speter**
311138032Speter**	Side Effects:
311238032Speter**		Unlocks the current job.
311377349Sgshapiro**
311477349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
311577349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
311677349Sgshapiro**		DOING.
311777349Sgshapiro**
311877349Sgshapiro**		XXX: More work is needed for this signal handler.
311938032Speter*/
312038032Speter
312138032Speter/* ARGSUSED */
312238032SpeterSIGFUNC_DECL
312338032Speterintsig(sig)
312438032Speter	int sig;
312538032Speter{
312690792Sgshapiro	bool drop = false;
312777349Sgshapiro	int save_errno = errno;
312864562Sgshapiro
312977349Sgshapiro	FIX_SYSV_SIGNAL(sig, intsig);
313077349Sgshapiro	errno = save_errno;
313177349Sgshapiro	CHECK_CRITICAL(sig);
313290792Sgshapiro	sm_allsignals(true);
313390792Sgshapiro
313464562Sgshapiro	if (sig != 0 && LogLevel > 79)
313538032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
313638032Speter	FileName = NULL;
313764562Sgshapiro
313864562Sgshapiro	/* Clean-up on aborted stdin message submission */
313964562Sgshapiro	if (CurEnv->e_id != NULL &&
314064562Sgshapiro	    (OpMode == MD_SMTP ||
314164562Sgshapiro	     OpMode == MD_DELIVER ||
314264562Sgshapiro	     OpMode == MD_ARPAFTP))
314364562Sgshapiro	{
314464562Sgshapiro		register ADDRESS *q;
314564562Sgshapiro
314664562Sgshapiro		/* don't return an error indication */
314764562Sgshapiro		CurEnv->e_to = NULL;
314864562Sgshapiro		CurEnv->e_flags &= ~EF_FATALERRS;
314964562Sgshapiro		CurEnv->e_flags |= EF_CLRQUEUE;
315064562Sgshapiro
315164562Sgshapiro		/*
315264562Sgshapiro		**  Spin through the addresses and
315364562Sgshapiro		**  mark them dead to prevent bounces
315464562Sgshapiro		*/
315564562Sgshapiro
315664562Sgshapiro		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
315764562Sgshapiro			q->q_state = QS_DONTSEND;
315864562Sgshapiro
315990792Sgshapiro		drop = true;
316064562Sgshapiro	}
316180785Sgshapiro	else if (OpMode != MD_TEST)
316290792Sgshapiro	{
316364562Sgshapiro		unlockqueue(CurEnv);
316438032Speter	}
316538032Speter
316690792Sgshapiro	finis(drop, false, EX_OK);
316790792Sgshapiro	/* NOTREACHED */
316838032Speter}
316990792Sgshapiro/*
317038032Speter**  DISCONNECT -- remove our connection with any foreground process
317138032Speter**
317238032Speter**	Parameters:
317338032Speter**		droplev -- how "deeply" we should drop the line.
317438032Speter**			0 -- ignore signals, mail back errors, make sure
317538032Speter**			     output goes to stdout.
317664562Sgshapiro**			1 -- also, make stdout go to /dev/null.
317738032Speter**			2 -- also, disconnect from controlling terminal
317838032Speter**			     (only for daemon mode).
317938032Speter**		e -- the current envelope.
318038032Speter**
318138032Speter**	Returns:
318238032Speter**		none
318338032Speter**
318438032Speter**	Side Effects:
318538032Speter**		Trys to insure that we are immune to vagaries of
318638032Speter**		the controlling tty.
318738032Speter*/
318838032Speter
318938032Spetervoid
319038032Speterdisconnect(droplev, e)
319138032Speter	int droplev;
319238032Speter	register ENVELOPE *e;
319338032Speter{
319438032Speter	int fd;
319538032Speter
319638032Speter	if (tTd(52, 1))
319790792Sgshapiro		sm_dprintf("disconnect: In %d Out %d, e=%p\n",
319890792Sgshapiro			   sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
319990792Sgshapiro			   sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
320038032Speter	if (tTd(52, 100))
320138032Speter	{
320290792Sgshapiro		sm_dprintf("don't\n");
320338032Speter		return;
320438032Speter	}
320538032Speter	if (LogLevel > 93)
320638032Speter		sm_syslog(LOG_DEBUG, e->e_id,
320764562Sgshapiro			  "disconnect level %d",
320864562Sgshapiro			  droplev);
320938032Speter
321038032Speter	/* be sure we don't get nasty signals */
321190792Sgshapiro	(void) sm_signal(SIGINT, SIG_IGN);
321290792Sgshapiro	(void) sm_signal(SIGQUIT, SIG_IGN);
321338032Speter
321438032Speter	/* we can't communicate with our caller, so.... */
321590792Sgshapiro	HoldErrs = true;
321638032Speter	CurEnv->e_errormode = EM_MAIL;
321738032Speter	Verbose = 0;
321890792Sgshapiro	DisConnected = true;
321938032Speter
322038032Speter	/* all input from /dev/null */
322190792Sgshapiro	if (InChannel != smioin)
322238032Speter	{
322390792Sgshapiro		(void) sm_io_close(InChannel, SM_TIME_DEFAULT);
322490792Sgshapiro		InChannel = smioin;
322538032Speter	}
322690792Sgshapiro	if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
322790792Sgshapiro			 SM_IO_RDONLY, NULL, smioin) == NULL)
322838032Speter		sm_syslog(LOG_ERR, e->e_id,
322990792Sgshapiro			  "disconnect: sm_io_reopen(\"%s\") failed: %s",
323090792Sgshapiro			  SM_PATH_DEVNULL, sm_errstring(errno));
323138032Speter
323290792Sgshapiro	/*
323390792Sgshapiro	**  output to the transcript
323490792Sgshapiro	**	We also compare the fd numbers here since OutChannel
323590792Sgshapiro	**	might be a layer on top of smioout due to encryption
323690792Sgshapiro	**	(see sfsasl.c).
323790792Sgshapiro	*/
323890792Sgshapiro
323990792Sgshapiro	if (OutChannel != smioout &&
324090792Sgshapiro	    sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
324190792Sgshapiro	    sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
324238032Speter	{
324390792Sgshapiro		(void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
324490792Sgshapiro		OutChannel = smioout;
324590792Sgshapiro
324690792Sgshapiro#if 0
324790792Sgshapiro		/*
324890792Sgshapiro		**  Has smioout been closed? Reopen it.
324990792Sgshapiro		**	This shouldn't happen anymore, the code is here
325090792Sgshapiro		**	just as a reminder.
325190792Sgshapiro		*/
325290792Sgshapiro
325390792Sgshapiro		if (smioout->sm_magic == NULL &&
325490792Sgshapiro		    sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
325590792Sgshapiro				 SM_IO_WRONLY, NULL, smioout) == NULL)
325690792Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
325790792Sgshapiro				  "disconnect: sm_io_reopen(\"%s\") failed: %s",
325890792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
325990792Sgshapiro#endif /* 0 */
326038032Speter	}
326138032Speter	if (droplev > 0)
326238032Speter	{
326390792Sgshapiro		fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
326464562Sgshapiro		if (fd == -1)
326564562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
326690792Sgshapiro				  "disconnect: open(\"%s\") failed: %s",
326790792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
326890792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
326964562Sgshapiro		(void) dup2(fd, STDOUT_FILENO);
327064562Sgshapiro		(void) dup2(fd, STDERR_FILENO);
327164562Sgshapiro		(void) close(fd);
327238032Speter	}
327338032Speter
327438032Speter	/* drop our controlling TTY completely if possible */
327538032Speter	if (droplev > 1)
327638032Speter	{
327738032Speter		(void) setsid();
327838032Speter		errno = 0;
327938032Speter	}
328038032Speter
328138032Speter#if XDEBUG
328238032Speter	checkfd012("disconnect");
328364562Sgshapiro#endif /* XDEBUG */
328438032Speter
328538032Speter	if (LogLevel > 71)
328690792Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
328790792Sgshapiro			  (int) CurrentPid);
328838032Speter
328938032Speter	errno = 0;
329038032Speter}
329138032Speter
329238032Speterstatic void
329338032Speterobsolete(argv)
329438032Speter	char *argv[];
329538032Speter{
329638032Speter	register char *ap;
329738032Speter	register char *op;
329838032Speter
329938032Speter	while ((ap = *++argv) != NULL)
330038032Speter	{
330138032Speter		/* Return if "--" or not an option of any form. */
330238032Speter		if (ap[0] != '-' || ap[1] == '-')
330338032Speter			return;
330438032Speter
330590792Sgshapiro		/* Don't allow users to use "-Q." or "-Q ." */
330690792Sgshapiro		if ((ap[1] == 'Q' && ap[2] == '.') ||
330790792Sgshapiro		    (ap[1] == 'Q' && argv[1] != NULL &&
330890792Sgshapiro		     argv[1][0] == '.' && argv[1][1] == '\0'))
330990792Sgshapiro		{
331090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
331190792Sgshapiro					     "Can not use -Q.\n");
331290792Sgshapiro			exit(EX_USAGE);
331390792Sgshapiro		}
331490792Sgshapiro
331538032Speter		/* skip over options that do have a value */
331638032Speter		op = strchr(OPTIONS, ap[1]);
331738032Speter		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
331838032Speter		    ap[1] != 'd' &&
331938032Speter#if defined(sony_news)
332038032Speter		    ap[1] != 'E' && ap[1] != 'J' &&
332164562Sgshapiro#endif /* defined(sony_news) */
332238032Speter		    argv[1] != NULL && argv[1][0] != '-')
332338032Speter		{
332438032Speter			argv++;
332538032Speter			continue;
332638032Speter		}
332738032Speter
332838032Speter		/* If -C doesn't have an argument, use sendmail.cf. */
332990792Sgshapiro#define __DEFPATH	"sendmail.cf"
333038032Speter		if (ap[1] == 'C' && ap[2] == '\0')
333138032Speter		{
333238032Speter			*argv = xalloc(sizeof(__DEFPATH) + 2);
333390792Sgshapiro			(void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
333490792Sgshapiro					   "-C", __DEFPATH);
333538032Speter		}
333638032Speter
333738032Speter		/* If -q doesn't have an argument, run it once. */
333838032Speter		if (ap[1] == 'q' && ap[2] == '\0')
333938032Speter			*argv = "-q0";
334038032Speter
334190792Sgshapiro		/* If -Q doesn't have an argument, disable quarantining */
334290792Sgshapiro		if (ap[1] == 'Q' && ap[2] == '\0')
334390792Sgshapiro			*argv = "-Q.";
334490792Sgshapiro
334538032Speter		/* if -d doesn't have an argument, use 0-99.1 */
334638032Speter		if (ap[1] == 'd' && ap[2] == '\0')
334738032Speter			*argv = "-d0-99.1";
334838032Speter
334964562Sgshapiro#if defined(sony_news)
335038032Speter		/* if -E doesn't have an argument, use -EC */
335138032Speter		if (ap[1] == 'E' && ap[2] == '\0')
335238032Speter			*argv = "-EC";
335338032Speter
335438032Speter		/* if -J doesn't have an argument, use -JJ */
335538032Speter		if (ap[1] == 'J' && ap[2] == '\0')
335638032Speter			*argv = "-JJ";
335764562Sgshapiro#endif /* defined(sony_news) */
335838032Speter	}
335938032Speter}
336090792Sgshapiro/*
336138032Speter**  AUTH_WARNING -- specify authorization warning
336238032Speter**
336338032Speter**	Parameters:
336438032Speter**		e -- the current envelope.
336538032Speter**		msg -- the text of the message.
336638032Speter**		args -- arguments to the message.
336738032Speter**
336838032Speter**	Returns:
336938032Speter**		none.
337038032Speter*/
337138032Speter
337238032Spetervoid
337338032Speter#ifdef __STDC__
337438032Speterauth_warning(register ENVELOPE *e, const char *msg, ...)
337564562Sgshapiro#else /* __STDC__ */
337638032Speterauth_warning(e, msg, va_alist)
337738032Speter	register ENVELOPE *e;
337838032Speter	const char *msg;
337938032Speter	va_dcl
338064562Sgshapiro#endif /* __STDC__ */
338138032Speter{
338238032Speter	char buf[MAXLINE];
338390792Sgshapiro	SM_VA_LOCAL_DECL
338438032Speter
338538032Speter	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
338638032Speter	{
338738032Speter		register char *p;
338838032Speter		static char hostbuf[48];
338938032Speter
339038032Speter		if (hostbuf[0] == '\0')
339171345Sgshapiro		{
339271345Sgshapiro			struct hostent *hp;
339338032Speter
339471345Sgshapiro			hp = myhostname(hostbuf, sizeof hostbuf);
339590792Sgshapiro#if NETINET6
339671345Sgshapiro			if (hp != NULL)
339771345Sgshapiro			{
339871345Sgshapiro				freehostent(hp);
339971345Sgshapiro				hp = NULL;
340071345Sgshapiro			}
340190792Sgshapiro#endif /* NETINET6 */
340271345Sgshapiro		}
340371345Sgshapiro
340490792Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": ");
340538032Speter		p = &buf[strlen(buf)];
340690792Sgshapiro		SM_VA_START(ap, msg);
340790792Sgshapiro		(void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
340890792Sgshapiro		SM_VA_END(ap);
340990792Sgshapiro		addheader("X-Authentication-Warning", buf, 0, e);
341038032Speter		if (LogLevel > 3)
341138032Speter			sm_syslog(LOG_INFO, e->e_id,
341264562Sgshapiro				  "Authentication-Warning: %.400s",
341364562Sgshapiro				  buf);
341438032Speter	}
341538032Speter}
341690792Sgshapiro/*
341738032Speter**  GETEXTENV -- get from external environment
341838032Speter**
341938032Speter**	Parameters:
342038032Speter**		envar -- the name of the variable to retrieve
342138032Speter**
342238032Speter**	Returns:
342338032Speter**		The value, if any.
342438032Speter*/
342538032Speter
342690792Sgshapirostatic char *
342738032Spetergetextenv(envar)
342838032Speter	const char *envar;
342938032Speter{
343038032Speter	char **envp;
343138032Speter	int l;
343238032Speter
343338032Speter	l = strlen(envar);
3434102528Sgshapiro	for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
343538032Speter	{
343638032Speter		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
343738032Speter			return &(*envp)[l + 1];
343838032Speter	}
343938032Speter	return NULL;
344038032Speter}
344190792Sgshapiro/*
344290792Sgshapiro**  SETUSERENV -- set an environment in the propagated environment
344338032Speter**
344438032Speter**	Parameters:
344538032Speter**		envar -- the name of the environment variable.
344638032Speter**		value -- the value to which it should be set.  If
344738032Speter**			null, this is extracted from the incoming
344838032Speter**			environment.  If that is not set, the call
344938032Speter**			to setuserenv is ignored.
345038032Speter**
345138032Speter**	Returns:
345238032Speter**		none.
345338032Speter*/
345438032Speter
345538032Spetervoid
345638032Spetersetuserenv(envar, value)
345738032Speter	const char *envar;
345838032Speter	const char *value;
345938032Speter{
346064562Sgshapiro	int i, l;
346138032Speter	char **evp = UserEnviron;
346238032Speter	char *p;
346338032Speter
346438032Speter	if (value == NULL)
346538032Speter	{
346638032Speter		value = getextenv(envar);
346738032Speter		if (value == NULL)
346838032Speter			return;
346938032Speter	}
347038032Speter
347190792Sgshapiro	/* XXX enforce reasonable size? */
347264562Sgshapiro	i = strlen(envar) + 1;
347364562Sgshapiro	l = strlen(value) + i + 1;
347464562Sgshapiro	p = (char *) xalloc(l);
347590792Sgshapiro	(void) sm_strlcpyn(p, l, 3, envar, "=", value);
347638032Speter
347738032Speter	while (*evp != NULL && strncmp(*evp, p, i) != 0)
347838032Speter		evp++;
347938032Speter	if (*evp != NULL)
348038032Speter	{
348138032Speter		*evp++ = p;
348238032Speter	}
348338032Speter	else if (evp < &UserEnviron[MAXUSERENVIRON])
348438032Speter	{
348538032Speter		*evp++ = p;
348638032Speter		*evp = NULL;
348738032Speter	}
348838032Speter
348938032Speter	/* make sure it is in our environment as well */
349038032Speter	if (putenv(p) < 0)
349138032Speter		syserr("setuserenv: putenv(%s) failed", p);
349238032Speter}
349390792Sgshapiro/*
349438032Speter**  DUMPSTATE -- dump state
349538032Speter**
349638032Speter**	For debugging.
349738032Speter*/
349838032Speter
349938032Spetervoid
350038032Speterdumpstate(when)
350138032Speter	char *when;
350238032Speter{
350338032Speter	register char *j = macvalue('j', CurEnv);
350438032Speter	int rs;
350564562Sgshapiro	extern int NextMacroId;
350638032Speter
350738032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id,
350864562Sgshapiro		  "--- dumping state on %s: $j = %s ---",
350964562Sgshapiro		  when,
351064562Sgshapiro		  j == NULL ? "<NULL>" : j);
351138032Speter	if (j != NULL)
351238032Speter	{
351338032Speter		if (!wordinclass(j, 'w'))
351438032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
351564562Sgshapiro				  "*** $j not in $=w ***");
351638032Speter	}
351738032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
351890792Sgshapiro	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
351964562Sgshapiro		  NextMacroId, MAXMACROID);
352038032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
352190792Sgshapiro	printopenfds(true);
352238032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
3523132943Sgshapiro	mci_dump_all(smioout, true);
352438032Speter	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
352538032Speter	if (rs > 0)
352638032Speter	{
352764562Sgshapiro		int status;
352838032Speter		register char **pvp;
352938032Speter		char *pv[MAXATOM + 1];
353038032Speter
353138032Speter		pv[0] = NULL;
353290792Sgshapiro		status = REWRITE(pv, rs, CurEnv);
353338032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
353464562Sgshapiro			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
353564562Sgshapiro			  status);
353638032Speter		for (pvp = pv; *pvp != NULL; pvp++)
353738032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
353838032Speter	}
353938032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
354038032Speter}
354190792Sgshapiro
354280785Sgshapiro#ifdef SIGUSR1
354390792Sgshapiro/*
354477349Sgshapiro**  SIGUSR1 -- Signal a request to dump state.
354577349Sgshapiro**
354677349Sgshapiro**	Parameters:
354777349Sgshapiro**		sig -- calling signal.
354877349Sgshapiro**
354977349Sgshapiro**	Returns:
355077349Sgshapiro**		none.
355177349Sgshapiro**
355277349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
355377349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
355477349Sgshapiro**		DOING.
355577349Sgshapiro**
355677349Sgshapiro**		XXX: More work is needed for this signal handler.
355777349Sgshapiro*/
355838032Speter
355938032Speter/* ARGSUSED */
356077349Sgshapirostatic SIGFUNC_DECL
356138032Spetersigusr1(sig)
356238032Speter	int sig;
356338032Speter{
356477349Sgshapiro	int save_errno = errno;
356590792Sgshapiro# if SM_HEAP_CHECK
356690792Sgshapiro	extern void dumpstab __P((void));
356790792Sgshapiro# endif /* SM_HEAP_CHECK */
356877349Sgshapiro
356977349Sgshapiro	FIX_SYSV_SIGNAL(sig, sigusr1);
357077349Sgshapiro	errno = save_errno;
357177349Sgshapiro	CHECK_CRITICAL(sig);
357238032Speter	dumpstate("user signal");
357390792Sgshapiro# if SM_HEAP_CHECK
357490792Sgshapiro	dumpstab();
357590792Sgshapiro# endif /* SM_HEAP_CHECK */
357677349Sgshapiro	errno = save_errno;
357738032Speter	return SIGFUNC_RETURN;
357838032Speter}
357990792Sgshapiro#endif /* SIGUSR1 */
358090792Sgshapiro
358190792Sgshapiro/*
358238032Speter**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
358338032Speter**
358438032Speter**	Parameters:
358538032Speter**		to_real_uid -- if set, drop to the real uid instead
358638032Speter**			of the RunAsUser.
358738032Speter**
358838032Speter**	Returns:
358938032Speter**		EX_OSERR if the setuid failed.
359038032Speter**		EX_OK otherwise.
359138032Speter*/
359238032Speter
359338032Speterint
359438032Speterdrop_privileges(to_real_uid)
359538032Speter	bool to_real_uid;
359638032Speter{
359738032Speter	int rval = EX_OK;
359838032Speter	GIDSET_T emptygidset[1];
359938032Speter
360038032Speter	if (tTd(47, 1))
360190792Sgshapiro		sm_dprintf("drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n",
360290792Sgshapiro			   (int) to_real_uid,
360390792Sgshapiro			   (int) RealUid, (int) RealGid,
360490792Sgshapiro			   (int) getuid(), (int) getgid(),
360590792Sgshapiro			   (int) geteuid(), (int) getegid(),
360690792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
360738032Speter
360838032Speter	if (to_real_uid)
360938032Speter	{
361038032Speter		RunAsUserName = RealUserName;
361138032Speter		RunAsUid = RealUid;
361238032Speter		RunAsGid = RealGid;
361394334Sgshapiro		EffGid = RunAsGid;
361438032Speter	}
361538032Speter
361638032Speter	/* make sure no one can grab open descriptors for secret files */
361738032Speter	endpwent();
361890792Sgshapiro	sm_mbdb_terminate();
361938032Speter
362038032Speter	/* reset group permissions; these can be set later */
362138032Speter	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
362290792Sgshapiro
362390792Sgshapiro	/*
362490792Sgshapiro	**  Notice:  on some OS (Linux...) the setgroups() call causes
362590792Sgshapiro	**	a logfile entry if sendmail is not run by root.
362690792Sgshapiro	**	However, it is unclear (no POSIX standard) whether
362790792Sgshapiro	**	setgroups() can only succeed if executed by root.
362890792Sgshapiro	**	So for now we keep it as it is; if you want to change it, use
362990792Sgshapiro	**  if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
363090792Sgshapiro	*/
363190792Sgshapiro
363238032Speter	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
363364562Sgshapiro	{
363464562Sgshapiro		syserr("drop_privileges: setgroups(1, %d) failed",
363590792Sgshapiro		       (int) emptygidset[0]);
363638032Speter		rval = EX_OSERR;
363764562Sgshapiro	}
363838032Speter
363990792Sgshapiro	/* reset primary group id */
364090792Sgshapiro	if (to_real_uid)
364164562Sgshapiro	{
364290792Sgshapiro		/*
364390792Sgshapiro		**  Drop gid to real gid.
364490792Sgshapiro		**  On some OS we must reset the effective[/real[/saved]] gid,
364590792Sgshapiro		**  and then use setgid() to finally drop all group privileges.
364690792Sgshapiro		**  Later on we check whether we can get back the
364790792Sgshapiro		**  effective gid.
364890792Sgshapiro		*/
364990792Sgshapiro
365090792Sgshapiro#if HASSETEGID
365190792Sgshapiro		if (setegid(RunAsGid) < 0)
365290792Sgshapiro		{
365390792Sgshapiro			syserr("drop_privileges: setegid(%d) failed",
365490792Sgshapiro			       (int) RunAsGid);
365590792Sgshapiro			rval = EX_OSERR;
365690792Sgshapiro		}
365790792Sgshapiro#else /* HASSETEGID */
365890792Sgshapiro# if HASSETREGID
365990792Sgshapiro		if (setregid(RunAsGid, RunAsGid) < 0)
366090792Sgshapiro		{
366190792Sgshapiro			syserr("drop_privileges: setregid(%d, %d) failed",
366290792Sgshapiro			       (int) RunAsGid, (int) RunAsGid);
366390792Sgshapiro			rval = EX_OSERR;
366490792Sgshapiro		}
366590792Sgshapiro# else /* HASSETREGID */
366690792Sgshapiro#  if HASSETRESGID
366790792Sgshapiro		if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
366890792Sgshapiro		{
366990792Sgshapiro			syserr("drop_privileges: setresgid(%d, %d, %d) failed",
367090792Sgshapiro			       (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
367190792Sgshapiro			rval = EX_OSERR;
367290792Sgshapiro		}
367390792Sgshapiro#  endif /* HASSETRESGID */
367490792Sgshapiro# endif /* HASSETREGID */
367590792Sgshapiro#endif /* HASSETEGID */
367664562Sgshapiro	}
367790792Sgshapiro	if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
367890792Sgshapiro	{
367990792Sgshapiro		if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
368090792Sgshapiro		{
368190792Sgshapiro			syserr("drop_privileges: setgid(%d) failed",
368290792Sgshapiro			       (int) RunAsGid);
368390792Sgshapiro			rval = EX_OSERR;
368490792Sgshapiro		}
368590792Sgshapiro		errno = 0;
368690792Sgshapiro		if (rval == EX_OK && getegid() != RunAsGid)
368790792Sgshapiro		{
368890792Sgshapiro			syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
368990792Sgshapiro			       (int) getegid(), (int) RunAsGid);
369090792Sgshapiro			rval = EX_OSERR;
369190792Sgshapiro		}
369290792Sgshapiro	}
369390792Sgshapiro
369490792Sgshapiro	/* fiddle with uid */
369564562Sgshapiro	if (to_real_uid || RunAsUid != 0)
369664562Sgshapiro	{
369794334Sgshapiro		uid_t euid;
369864562Sgshapiro
369990792Sgshapiro		/*
370090792Sgshapiro		**  Try to setuid(RunAsUid).
370190792Sgshapiro		**  euid must be RunAsUid,
370294334Sgshapiro		**  ruid must be RunAsUid unless (e|r)uid wasn't 0
370394334Sgshapiro		**	and we didn't have to drop privileges to the real uid.
370490792Sgshapiro		*/
370590792Sgshapiro
370690792Sgshapiro		if (setuid(RunAsUid) < 0 ||
370794334Sgshapiro		    geteuid() != RunAsUid ||
370894334Sgshapiro		    (getuid() != RunAsUid &&
370994334Sgshapiro		     (to_real_uid || geteuid() == 0 || getuid() == 0)))
371064562Sgshapiro		{
371190792Sgshapiro#if HASSETREUID
371290792Sgshapiro			/*
371390792Sgshapiro			**  if ruid != RunAsUid, euid == RunAsUid, then
371490792Sgshapiro			**  try resetting just the real uid, then using
371590792Sgshapiro			**  setuid() to drop the saved-uid as well.
371690792Sgshapiro			*/
371790792Sgshapiro
371894334Sgshapiro			if (geteuid() == RunAsUid)
371990792Sgshapiro			{
372090792Sgshapiro				if (setreuid(RunAsUid, -1) < 0)
372190792Sgshapiro				{
372290792Sgshapiro					syserr("drop_privileges: setreuid(%d, -1) failed",
372390792Sgshapiro					       (int) RunAsUid);
372490792Sgshapiro					rval = EX_OSERR;
372590792Sgshapiro				}
372690792Sgshapiro				if (setuid(RunAsUid) < 0)
372790792Sgshapiro				{
372890792Sgshapiro					syserr("drop_privileges: second setuid(%d) attempt failed",
372990792Sgshapiro					       (int) RunAsUid);
373090792Sgshapiro					rval = EX_OSERR;
373190792Sgshapiro				}
373290792Sgshapiro			}
373390792Sgshapiro			else
373490792Sgshapiro#endif /* HASSETREUID */
373590792Sgshapiro			{
373690792Sgshapiro				syserr("drop_privileges: setuid(%d) failed",
373790792Sgshapiro				       (int) RunAsUid);
373890792Sgshapiro				rval = EX_OSERR;
373990792Sgshapiro			}
374064562Sgshapiro		}
374194334Sgshapiro		euid = geteuid();
374290792Sgshapiro		if (RunAsUid != 0 && setuid(0) == 0)
374364562Sgshapiro		{
374464562Sgshapiro			/*
374564562Sgshapiro			**  Believe it or not, the Linux capability model
374664562Sgshapiro			**  allows a non-root process to override setuid()
374764562Sgshapiro			**  on a process running as root and prevent that
374864562Sgshapiro			**  process from dropping privileges.
374964562Sgshapiro			*/
375064562Sgshapiro
375164562Sgshapiro			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
375264562Sgshapiro			rval = EX_OSERR;
375364562Sgshapiro		}
375464562Sgshapiro		else if (RunAsUid != euid && setuid(euid) == 0)
375564562Sgshapiro		{
375664562Sgshapiro			/*
375764562Sgshapiro			**  Some operating systems will keep the saved-uid
375864562Sgshapiro			**  if a non-root effective-uid calls setuid(real-uid)
375964562Sgshapiro			**  making it possible to set it back again later.
376064562Sgshapiro			*/
376164562Sgshapiro
376290792Sgshapiro			syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
376364562Sgshapiro			rval = EX_OSERR;
376464562Sgshapiro		}
376564562Sgshapiro	}
376690792Sgshapiro
376790792Sgshapiro	if ((to_real_uid || RunAsGid != 0) &&
376890792Sgshapiro	    rval == EX_OK && RunAsGid != EffGid &&
376990792Sgshapiro	    getuid() != 0 && geteuid() != 0)
377090792Sgshapiro	{
377190792Sgshapiro		errno = 0;
377290792Sgshapiro		if (setgid(EffGid) == 0)
377390792Sgshapiro		{
377490792Sgshapiro			syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
377590792Sgshapiro			       (int) EffGid);
377690792Sgshapiro			rval = EX_OSERR;
377790792Sgshapiro		}
377890792Sgshapiro	}
377990792Sgshapiro
378038032Speter	if (tTd(47, 5))
378138032Speter	{
378290792Sgshapiro		sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
378390792Sgshapiro			   (int) geteuid(), (int) getuid(),
378490792Sgshapiro			   (int) getegid(), (int) getgid());
378590792Sgshapiro		sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
378690792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
378764562Sgshapiro		if (tTd(47, 10))
378890792Sgshapiro			sm_dprintf("drop_privileges: rval = %d\n", rval);
378938032Speter	}
379038032Speter	return rval;
379138032Speter}
379290792Sgshapiro/*
379338032Speter**  FILL_FD -- make sure a file descriptor has been properly allocated
379438032Speter**
379538032Speter**	Used to make sure that stdin/out/err are allocated on startup
379638032Speter**
379738032Speter**	Parameters:
379838032Speter**		fd -- the file descriptor to be filled.
379938032Speter**		where -- a string used for logging.  If NULL, this is
380038032Speter**			being called on startup, and logging should
380138032Speter**			not be done.
380238032Speter**
380338032Speter**	Returns:
380438032Speter**		none
380590792Sgshapiro**
380690792Sgshapiro**	Side Effects:
380790792Sgshapiro**		possibly changes MissingFds
380838032Speter*/
380938032Speter
381038032Spetervoid
381138032Speterfill_fd(fd, where)
381238032Speter	int fd;
381338032Speter	char *where;
381438032Speter{
381538032Speter	int i;
381638032Speter	struct stat stbuf;
381738032Speter
381838032Speter	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
381938032Speter		return;
382038032Speter
382138032Speter	if (where != NULL)
382238032Speter		syserr("fill_fd: %s: fd %d not open", where, fd);
382338032Speter	else
382438032Speter		MissingFds |= 1 << fd;
382590792Sgshapiro	i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
382638032Speter	if (i < 0)
382738032Speter	{
382890792Sgshapiro		syserr("!fill_fd: %s: cannot open %s",
382990792Sgshapiro		       where == NULL ? "startup" : where, SM_PATH_DEVNULL);
383038032Speter	}
383138032Speter	if (fd != i)
383238032Speter	{
383338032Speter		(void) dup2(i, fd);
383438032Speter		(void) close(i);
383538032Speter	}
383638032Speter}
383790792Sgshapiro/*
383890792Sgshapiro**  SM_PRINTOPTIONS -- print options
383990792Sgshapiro**
384090792Sgshapiro**	Parameters:
384190792Sgshapiro**		options -- array of options.
384290792Sgshapiro**
384390792Sgshapiro**	Returns:
384490792Sgshapiro**		none.
384590792Sgshapiro*/
384690792Sgshapiro
384790792Sgshapirostatic void
384890792Sgshapirosm_printoptions(options)
384990792Sgshapiro	char **options;
385090792Sgshapiro{
385190792Sgshapiro	int ll;
385290792Sgshapiro	char **av;
385390792Sgshapiro
385490792Sgshapiro	av = options;
385590792Sgshapiro	ll = 7;
385690792Sgshapiro	while (*av != NULL)
385790792Sgshapiro	{
385890792Sgshapiro		if (ll + strlen(*av) > 63)
385990792Sgshapiro		{
386090792Sgshapiro			sm_dprintf("\n");
386190792Sgshapiro			ll = 0;
386290792Sgshapiro		}
386390792Sgshapiro		if (ll == 0)
386490792Sgshapiro			sm_dprintf("\t\t");
386590792Sgshapiro		else
386690792Sgshapiro			sm_dprintf(" ");
386790792Sgshapiro		sm_dprintf("%s", *av);
386890792Sgshapiro		ll += strlen(*av++) + 1;
386990792Sgshapiro	}
387090792Sgshapiro	sm_dprintf("\n");
387190792Sgshapiro}
387290792Sgshapiro/*
387338032Speter**  TESTMODELINE -- process a test mode input line
387438032Speter**
387538032Speter**	Parameters:
387638032Speter**		line -- the input line.
387738032Speter**		e -- the current environment.
387838032Speter**	Syntax:
387938032Speter**		#  a comment
388038032Speter**		.X process X as a configuration line
388138032Speter**		=X dump a configuration item (such as mailers)
388238032Speter**		$X dump a macro or class
388338032Speter**		/X try an activity
388438032Speter**		X  normal process through rule set X
388538032Speter*/
388638032Speter
388764562Sgshapirostatic void
388838032Spetertestmodeline(line, e)
388938032Speter	char *line;
389038032Speter	ENVELOPE *e;
389138032Speter{
389238032Speter	register char *p;
389338032Speter	char *q;
389438032Speter	auto char *delimptr;
389538032Speter	int mid;
389638032Speter	int i, rs;
389738032Speter	STAB *map;
389838032Speter	char **s;
389938032Speter	struct rewrite *rw;
390038032Speter	ADDRESS a;
390138032Speter	static int tryflags = RF_COPYNONE;
390238032Speter	char exbuf[MAXLINE];
390390792Sgshapiro	extern unsigned char TokTypeNoC[];
390438032Speter
390566494Sgshapiro	/* skip leading spaces */
390666494Sgshapiro	while (*line == ' ')
390766494Sgshapiro		line++;
390866494Sgshapiro
390938032Speter	switch (line[0])
391038032Speter	{
391138032Speter	  case '#':
391264562Sgshapiro	  case '\0':
391338032Speter		return;
391438032Speter
391538032Speter	  case '?':
391664562Sgshapiro		help("-bt", e);
391738032Speter		return;
391838032Speter
391938032Speter	  case '.':		/* config-style settings */
392038032Speter		switch (line[1])
392138032Speter		{
392238032Speter		  case 'D':
392390792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
392471345Sgshapiro			if (mid == 0)
392538032Speter				return;
392638032Speter			translate_dollars(delimptr);
392790792Sgshapiro			macdefine(&e->e_macro, A_TEMP, mid, delimptr);
392838032Speter			break;
392938032Speter
393038032Speter		  case 'C':
393138032Speter			if (line[2] == '\0')	/* not to call syserr() */
393238032Speter				return;
393338032Speter
393490792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
393571345Sgshapiro			if (mid == 0)
393638032Speter				return;
393738032Speter			translate_dollars(delimptr);
393838032Speter			expand(delimptr, exbuf, sizeof exbuf, e);
393938032Speter			p = exbuf;
394038032Speter			while (*p != '\0')
394138032Speter			{
394238032Speter				register char *wd;
394338032Speter				char delim;
394438032Speter
394538032Speter				while (*p != '\0' && isascii(*p) && isspace(*p))
394638032Speter					p++;
394738032Speter				wd = p;
394838032Speter				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
394938032Speter					p++;
395038032Speter				delim = *p;
395138032Speter				*p = '\0';
395238032Speter				if (wd[0] != '\0')
395338032Speter					setclass(mid, wd);
395438032Speter				*p = delim;
395538032Speter			}
395638032Speter			break;
395738032Speter
395838032Speter		  case '\0':
395990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
396090792Sgshapiro					     "Usage: .[DC]macro value(s)\n");
396138032Speter			break;
396238032Speter
396338032Speter		  default:
396490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
396590792Sgshapiro					     "Unknown \".\" command %s\n", line);
396638032Speter			break;
396738032Speter		}
396838032Speter		return;
396938032Speter
397038032Speter	  case '=':		/* config-style settings */
397138032Speter		switch (line[1])
397238032Speter		{
397338032Speter		  case 'S':		/* dump rule set */
397438032Speter			rs = strtorwset(&line[2], NULL, ST_FIND);
397538032Speter			if (rs < 0)
397638032Speter			{
397790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
397890792Sgshapiro						     "Undefined ruleset %s\n", &line[2]);
397938032Speter				return;
398038032Speter			}
398138032Speter			rw = RewriteRules[rs];
398238032Speter			if (rw == NULL)
398338032Speter				return;
398438032Speter			do
398538032Speter			{
398690792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
398790792Sgshapiro						  'R');
398838032Speter				s = rw->r_lhs;
398938032Speter				while (*s != NULL)
399038032Speter				{
3991132943Sgshapiro					xputs(smioout, *s++);
399290792Sgshapiro					(void) sm_io_putc(smioout,
399390792Sgshapiro							  SM_TIME_DEFAULT, ' ');
399438032Speter				}
399590792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
399690792Sgshapiro						  '\t');
399790792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
399890792Sgshapiro						  '\t');
399938032Speter				s = rw->r_rhs;
400038032Speter				while (*s != NULL)
400138032Speter				{
4002132943Sgshapiro					xputs(smioout, *s++);
400390792Sgshapiro					(void) sm_io_putc(smioout,
400490792Sgshapiro							  SM_TIME_DEFAULT, ' ');
400538032Speter				}
400690792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
400790792Sgshapiro						  '\n');
400838032Speter			} while ((rw = rw->r_next) != NULL);
400938032Speter			break;
401038032Speter
401138032Speter		  case 'M':
401238032Speter			for (i = 0; i < MAXMAILERS; i++)
401338032Speter			{
401438032Speter				if (Mailer[i] != NULL)
4015132943Sgshapiro					printmailer(smioout, Mailer[i]);
401638032Speter			}
401738032Speter			break;
401838032Speter
401938032Speter		  case '\0':
402090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
402190792Sgshapiro					     "Usage: =Sruleset or =M\n");
402238032Speter			break;
402338032Speter
402438032Speter		  default:
402590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
402690792Sgshapiro					     "Unknown \"=\" command %s\n", line);
402738032Speter			break;
402838032Speter		}
402938032Speter		return;
403038032Speter
403138032Speter	  case '-':		/* set command-line-like opts */
403238032Speter		switch (line[1])
403338032Speter		{
403438032Speter		  case 'd':
403538032Speter			tTflag(&line[2]);
403638032Speter			break;
403738032Speter
403838032Speter		  case '\0':
403990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
404090792Sgshapiro					     "Usage: -d{debug arguments}\n");
404138032Speter			break;
404238032Speter
404338032Speter		  default:
404490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
404590792Sgshapiro					     "Unknown \"-\" command %s\n", line);
404638032Speter			break;
404738032Speter		}
404838032Speter		return;
404938032Speter
405038032Speter	  case '$':
405138032Speter		if (line[1] == '=')
405238032Speter		{
405390792Sgshapiro			mid = macid(&line[2]);
405471345Sgshapiro			if (mid != 0)
405538032Speter				stabapply(dump_class, mid);
405638032Speter			return;
405738032Speter		}
405890792Sgshapiro		mid = macid(&line[1]);
405971345Sgshapiro		if (mid == 0)
406038032Speter			return;
406138032Speter		p = macvalue(mid, e);
406238032Speter		if (p == NULL)
406390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
406490792Sgshapiro					     "Undefined\n");
406538032Speter		else
406638032Speter		{
4067132943Sgshapiro			xputs(smioout, p);
406890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
406990792Sgshapiro					     "\n");
407038032Speter		}
407138032Speter		return;
407238032Speter
407338032Speter	  case '/':		/* miscellaneous commands */
407438032Speter		p = &line[strlen(line)];
407538032Speter		while (--p >= line && isascii(*p) && isspace(*p))
407638032Speter			*p = '\0';
407738032Speter		p = strpbrk(line, " \t");
407838032Speter		if (p != NULL)
407938032Speter		{
408038032Speter			while (isascii(*p) && isspace(*p))
408138032Speter				*p++ = '\0';
408238032Speter		}
408338032Speter		else
408438032Speter			p = "";
408538032Speter		if (line[1] == '\0')
408638032Speter		{
408790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
408890792Sgshapiro					     "Usage: /[canon|map|mx|parse|try|tryflags]\n");
408938032Speter			return;
409038032Speter		}
409190792Sgshapiro		if (sm_strcasecmp(&line[1], "quit") == 0)
409264562Sgshapiro		{
409364562Sgshapiro			CurEnv->e_id = NULL;
409490792Sgshapiro			finis(true, true, ExitStat);
409590792Sgshapiro			/* NOTREACHED */
409664562Sgshapiro		}
409790792Sgshapiro		if (sm_strcasecmp(&line[1], "mx") == 0)
409838032Speter		{
409938032Speter#if NAMED_BIND
410038032Speter			/* look up MX records */
410138032Speter			int nmx;
410238032Speter			auto int rcode;
410338032Speter			char *mxhosts[MAXMXHOSTS + 1];
410438032Speter
410538032Speter			if (*p == '\0')
410638032Speter			{
410790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
410890792Sgshapiro						     "Usage: /mx address\n");
410938032Speter				return;
411038032Speter			}
411190792Sgshapiro			nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
411290792Sgshapiro				      NULL);
411390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
411490792Sgshapiro					     "getmxrr(%s) returns %d value(s):\n",
411590792Sgshapiro				p, nmx);
411638032Speter			for (i = 0; i < nmx; i++)
411790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
411890792Sgshapiro						     "\t%s\n", mxhosts[i]);
411964562Sgshapiro#else /* NAMED_BIND */
412090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
412190792Sgshapiro					     "No MX code compiled in\n");
412264562Sgshapiro#endif /* NAMED_BIND */
412338032Speter		}
412490792Sgshapiro		else if (sm_strcasecmp(&line[1], "canon") == 0)
412538032Speter		{
412638032Speter			char host[MAXHOSTNAMELEN];
412738032Speter
412838032Speter			if (*p == '\0')
412938032Speter			{
413090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
413190792Sgshapiro						     "Usage: /canon address\n");
413238032Speter				return;
413338032Speter			}
413490792Sgshapiro			else if (sm_strlcpy(host, p, sizeof host) >= sizeof host)
413538032Speter			{
413690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
413790792Sgshapiro						     "Name too long\n");
413838032Speter				return;
413938032Speter			}
4140110560Sgshapiro			(void) getcanonname(host, sizeof host, !HasWildcardMX,
414190792Sgshapiro					    NULL);
414290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
414390792Sgshapiro					     "getcanonname(%s) returns %s\n",
414490792Sgshapiro					     p, host);
414538032Speter		}
414690792Sgshapiro		else if (sm_strcasecmp(&line[1], "map") == 0)
414738032Speter		{
414838032Speter			auto int rcode = EX_OK;
414938032Speter			char *av[2];
415038032Speter
415138032Speter			if (*p == '\0')
415238032Speter			{
415390792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
415490792Sgshapiro						     "Usage: /map mapname key\n");
415538032Speter				return;
415638032Speter			}
415790792Sgshapiro			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q));			     q++)
415838032Speter				continue;
415938032Speter			if (*q == '\0')
416038032Speter			{
416190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
416290792Sgshapiro						     "No key specified\n");
416338032Speter				return;
416438032Speter			}
416538032Speter			*q++ = '\0';
416638032Speter			map = stab(p, ST_MAP, ST_FIND);
416738032Speter			if (map == NULL)
416838032Speter			{
416990792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
417090792Sgshapiro						     "Map named \"%s\" not found\n", p);
417138032Speter				return;
417238032Speter			}
417364562Sgshapiro			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
417464562Sgshapiro			    !openmap(&(map->s_map)))
417538032Speter			{
417690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
417790792Sgshapiro						     "Map named \"%s\" not open\n", p);
417838032Speter				return;
417938032Speter			}
418090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
418190792Sgshapiro					     "map_lookup: %s (%s) ", p, q);
418238032Speter			av[0] = q;
418338032Speter			av[1] = NULL;
418438032Speter			p = (*map->s_map.map_class->map_lookup)
418538032Speter					(&map->s_map, q, av, &rcode);
418638032Speter			if (p == NULL)
418790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
418890792Sgshapiro						     "no match (%d)\n",
418990792Sgshapiro						     rcode);
419038032Speter			else
419190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
419290792Sgshapiro						     "returns %s (%d)\n", p,
419390792Sgshapiro						     rcode);
419438032Speter		}
419590792Sgshapiro		else if (sm_strcasecmp(&line[1], "try") == 0)
419638032Speter		{
419738032Speter			MAILER *m;
419864562Sgshapiro			STAB *st;
419938032Speter			auto int rcode = EX_OK;
420038032Speter
420138032Speter			q = strpbrk(p, " \t");
420238032Speter			if (q != NULL)
420338032Speter			{
420438032Speter				while (isascii(*q) && isspace(*q))
420538032Speter					*q++ = '\0';
420638032Speter			}
420738032Speter			if (q == NULL || *q == '\0')
420838032Speter			{
420990792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
421090792Sgshapiro						     "Usage: /try mailer address\n");
421138032Speter				return;
421238032Speter			}
421364562Sgshapiro			st = stab(p, ST_MAILER, ST_FIND);
421464562Sgshapiro			if (st == NULL)
421538032Speter			{
421690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
421790792Sgshapiro						     "Unknown mailer %s\n", p);
421838032Speter				return;
421938032Speter			}
422064562Sgshapiro			m = st->s_mailer;
422190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
422290792Sgshapiro					     "Trying %s %s address %s for mailer %s\n",
422390792Sgshapiro				     bitset(RF_HEADERADDR, tryflags) ? "header"
422490792Sgshapiro							: "envelope",
422590792Sgshapiro				     bitset(RF_SENDERADDR, tryflags) ? "sender"
422690792Sgshapiro							: "recipient", q, p);
422738032Speter			p = remotename(q, m, tryflags, &rcode, CurEnv);
422890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
422990792Sgshapiro					     "Rcode = %d, addr = %s\n",
423090792Sgshapiro					     rcode, p == NULL ? "<NULL>" : p);
423138032Speter			e->e_to = NULL;
423238032Speter		}
423390792Sgshapiro		else if (sm_strcasecmp(&line[1], "tryflags") == 0)
423438032Speter		{
423538032Speter			if (*p == '\0')
423638032Speter			{
423790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
423890792Sgshapiro						     "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
423938032Speter				return;
424038032Speter			}
424138032Speter			for (; *p != '\0'; p++)
424238032Speter			{
424338032Speter				switch (*p)
424438032Speter				{
424538032Speter				  case 'H':
424638032Speter				  case 'h':
424738032Speter					tryflags |= RF_HEADERADDR;
424838032Speter					break;
424938032Speter
425038032Speter				  case 'E':
425138032Speter				  case 'e':
425238032Speter					tryflags &= ~RF_HEADERADDR;
425338032Speter					break;
425438032Speter
425538032Speter				  case 'S':
425638032Speter				  case 's':
425738032Speter					tryflags |= RF_SENDERADDR;
425838032Speter					break;
425938032Speter
426038032Speter				  case 'R':
426138032Speter				  case 'r':
426238032Speter					tryflags &= ~RF_SENDERADDR;
426338032Speter					break;
426438032Speter				}
426538032Speter			}
426664562Sgshapiro			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
426764562Sgshapiro			exbuf[1] = ' ';
426864562Sgshapiro			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
426964562Sgshapiro			exbuf[3] = '\0';
427090792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
427190792Sgshapiro				macid("{addr_type}"), exbuf);
427238032Speter		}
427390792Sgshapiro		else if (sm_strcasecmp(&line[1], "parse") == 0)
427438032Speter		{
427538032Speter			if (*p == '\0')
427638032Speter			{
427790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
427890792Sgshapiro						     "Usage: /parse address\n");
427938032Speter				return;
428038032Speter			}
4281111823Sgshapiro			q = crackaddr(p, e);
428290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
428390792Sgshapiro					     "Cracked address = ");
4284132943Sgshapiro			xputs(smioout, q);
428590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
428690792Sgshapiro					     "\nParsing %s %s address\n",
428790792Sgshapiro					     bitset(RF_HEADERADDR, tryflags) ?
428890792Sgshapiro							"header" : "envelope",
428990792Sgshapiro					     bitset(RF_SENDERADDR, tryflags) ?
429090792Sgshapiro							"sender" : "recipient");
429190792Sgshapiro			if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
429290792Sgshapiro			    == NULL)
429390792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
429490792Sgshapiro						     "Cannot parse\n");
429538032Speter			else if (a.q_host != NULL && a.q_host[0] != '\0')
429690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
429790792Sgshapiro						     "mailer %s, host %s, user %s\n",
429890792Sgshapiro						     a.q_mailer->m_name,
429990792Sgshapiro						     a.q_host,
430090792Sgshapiro						     a.q_user);
430138032Speter			else
430290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
430390792Sgshapiro						     "mailer %s, user %s\n",
430490792Sgshapiro						     a.q_mailer->m_name,
430590792Sgshapiro						     a.q_user);
430638032Speter			e->e_to = NULL;
430738032Speter		}
430838032Speter		else
430938032Speter		{
431090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
431190792Sgshapiro					     "Unknown \"/\" command %s\n",
431290792Sgshapiro					     line);
431338032Speter		}
431438032Speter		return;
431538032Speter	}
431638032Speter
431738032Speter	for (p = line; isascii(*p) && isspace(*p); p++)
431838032Speter		continue;
431938032Speter	q = p;
432038032Speter	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
432138032Speter		p++;
432238032Speter	if (*p == '\0')
432338032Speter	{
432490792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
432590792Sgshapiro				     "No address!\n");
432638032Speter		return;
432738032Speter	}
432838032Speter	*p = '\0';
432990792Sgshapiro	if (invalidaddr(p + 1, NULL, true))
433038032Speter		return;
433138032Speter	do
433238032Speter	{
433338032Speter		register char **pvp;
433438032Speter		char pvpbuf[PSBUFSIZE];
433538032Speter
4336132943Sgshapiro		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf, &delimptr,
4337132943Sgshapiro			      ConfigLevel >= 9 ? TokTypeNoC : NULL, false);
433838032Speter		if (pvp == NULL)
433938032Speter			continue;
434038032Speter		p = q;
434138032Speter		while (*p != '\0')
434238032Speter		{
434364562Sgshapiro			int status;
434438032Speter
434538032Speter			rs = strtorwset(p, NULL, ST_FIND);
434638032Speter			if (rs < 0)
434738032Speter			{
434890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
434990792Sgshapiro						     "Undefined ruleset %s\n",
435090792Sgshapiro						     p);
435138032Speter				break;
435238032Speter			}
435390792Sgshapiro			status = REWRITE(pvp, rs, e);
435464562Sgshapiro			if (status != EX_OK)
435590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
435690792Sgshapiro						     "== Ruleset %s (%d) status %d\n",
435790792Sgshapiro						     p, rs, status);
435838032Speter			while (*p != '\0' && *p++ != ',')
435938032Speter				continue;
436038032Speter		}
436138032Speter	} while (*(p = delimptr) != '\0');
436238032Speter}
436338032Speter
436464562Sgshapirostatic void
436538032Speterdump_class(s, id)
436638032Speter	register STAB *s;
436738032Speter	int id;
436838032Speter{
436990792Sgshapiro	if (s->s_symtype != ST_CLASS)
437038032Speter		return;
437171345Sgshapiro	if (bitnset(bitidx(id), s->s_class))
437290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
437390792Sgshapiro				     "%s\n", s->s_name);
437438032Speter}
437590792Sgshapiro
437690792Sgshapiro/*
437790792Sgshapiro**  An exception type used to create QuickAbort exceptions.
437890792Sgshapiro**  This is my first cut at converting QuickAbort from longjmp to exceptions.
437990792Sgshapiro**  These exceptions have a single integer argument, which is the argument
438090792Sgshapiro**  to longjmp in the original code (either 1 or 2).  I don't know the
438190792Sgshapiro**  significance of 1 vs 2: the calls to setjmp don't care.
438290792Sgshapiro*/
438390792Sgshapiro
438490792Sgshapiroconst SM_EXC_TYPE_T EtypeQuickAbort =
438590792Sgshapiro{
438690792Sgshapiro	SmExcTypeMagic,
438790792Sgshapiro	"E:mta.quickabort",
438890792Sgshapiro	"i",
438990792Sgshapiro	sm_etype_printf,
439090792Sgshapiro	"quick abort %0",
439190792Sgshapiro};
4392