main.c revision 98121
138032Speter/*
294334Sgshapiro * Copyright (c) 1998-2002 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[]) =
2173188Sgshapiro"@(#) Copyright (c) 1998-2001 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
2898121SgshapiroSM_RCSID("@(#)$Id: main.c,v 8.882 2002/05/10 16:20:55 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).
8038032Speter**		The support of the 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 &&		\
13190792Sgshapiro	    OpMode != MD_VERIFY && OpMode != MD_TEST)			\
13290792Sgshapiro	{								\
13390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
13490792Sgshapiro				     "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
13590792Sgshapiro		       (cmd));						\
13690792Sgshapiro		break;							\
13790792Sgshapiro	}								\
13890792Sgshapiro	if (extraprivs && queuerun)					\
13990792Sgshapiro	{								\
14090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
14190792Sgshapiro				     "WARNING: Ignoring submission mode -%c option with -q\n", \
14290792Sgshapiro		       (cmd));						\
14390792Sgshapiro		break;							\
14490792Sgshapiro	}								\
14590792Sgshapiro}
14690792Sgshapiro
14738032Speterint
14838032Spetermain(argc, argv, envp)
14938032Speter	int argc;
15038032Speter	char **argv;
15138032Speter	char **envp;
15238032Speter{
15338032Speter	register char *p;
15438032Speter	char **av;
15538032Speter	extern char Version[];
15638032Speter	char *ep, *from;
15738032Speter	STAB *st;
15838032Speter	register int i;
15938032Speter	int j;
16064562Sgshapiro	int dp;
16190792Sgshapiro	int fill_errno;
16290792Sgshapiro	int qgrp = NOQGRP;		/* queue group to process */
16390792Sgshapiro	bool safecf = true;
16464562Sgshapiro	BITMAP256 *p_flags = NULL;	/* daemon flags */
16590792Sgshapiro	bool warn_C_flag = false;
16690792Sgshapiro	bool auth = true;		/* whether to set e_auth_param */
16738032Speter	char warn_f_flag = '\0';
16890792Sgshapiro	bool run_in_foreground = false;	/* -bD mode */
16990792Sgshapiro	bool queuerun = false, debug = false;
17038032Speter	struct passwd *pw;
17138032Speter	struct hostent *hp;
17238032Speter	char *nullserver = NULL;
17364562Sgshapiro	char *authinfo = NULL;
17464562Sgshapiro	char *sysloglabel = NULL;	/* label for syslog */
17590792Sgshapiro	char *conffile = NULL;		/* name of .cf file */
17690792Sgshapiro	char *queuegroup = NULL;	/* queue group to process */
17790792Sgshapiro#if _FFR_QUARANTINE
17890792Sgshapiro	char *quarantining = NULL;	/* quarantine queue items? */
17990792Sgshapiro#endif /* _FFR_QUARANTINE */
18090792Sgshapiro	bool extraprivs;
18190792Sgshapiro	bool forged, negate;
18290792Sgshapiro	bool queuepersistent = false;	/* queue runner process runs forever */
18390792Sgshapiro	bool foregroundqueue = false;	/* queue run in foreground */
18490792Sgshapiro	bool save_val;			/* to save some bool var. */
18590792Sgshapiro	int cftype;			/* which cf file to use? */
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
21638032Speter	/*
21738032Speter	**  Check to see if we reentered.
21838032Speter	**	This would normally happen if e_putheader or e_putbody
21938032Speter	**	were NULL when invoked.
22038032Speter	*/
22138032Speter
22290792Sgshapiro	if (starttime != 0)
22338032Speter	{
22438032Speter		syserr("main: reentered!");
22538032Speter		abort();
22638032Speter	}
22790792Sgshapiro	starttime = curtime();
22838032Speter
22938032Speter	/* avoid null pointer dereferences */
23038032Speter	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
23138032Speter
23290792Sgshapiro	RealUid = getuid();
23390792Sgshapiro	RealGid = getgid();
23477349Sgshapiro
23590792Sgshapiro	/* Check if sendmail is running with extra privs */
23690792Sgshapiro	extraprivs = (RealUid != 0 &&
23790792Sgshapiro		      (geteuid() != getuid() || getegid() != getgid()));
23877349Sgshapiro
23990792Sgshapiro	CurrentPid = getpid();
24038032Speter
24190792Sgshapiro	/* get whatever .cf file is right for the opmode */
24290792Sgshapiro	cftype = SM_GET_RIGHT_CF;
24364562Sgshapiro
24438032Speter	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
24538032Speter	DtableSize = getdtsize();
24638032Speter	if (DtableSize > 256)
24738032Speter		DtableSize = 256;
24838032Speter
24938032Speter	/*
25038032Speter	**  Be sure we have enough file descriptors.
25138032Speter	**	But also be sure that 0, 1, & 2 are open.
25238032Speter	*/
25338032Speter
25490792Sgshapiro	/* reset errno and fill_errno; the latter is used way down below */
25590792Sgshapiro	errno = fill_errno = 0;
25638032Speter	fill_fd(STDIN_FILENO, NULL);
25790792Sgshapiro	if (errno != 0)
25890792Sgshapiro		fill_errno = errno;
25938032Speter	fill_fd(STDOUT_FILENO, NULL);
26090792Sgshapiro	if (errno != 0)
26190792Sgshapiro		fill_errno = errno;
26238032Speter	fill_fd(STDERR_FILENO, NULL);
26390792Sgshapiro	if (errno != 0)
26490792Sgshapiro		fill_errno = errno;
26538032Speter
26638032Speter	i = DtableSize;
26738032Speter	while (--i > 0)
26838032Speter	{
26990792Sgshapiro		if (i != STDIN_FILENO && i != STDOUT_FILENO &&
27090792Sgshapiro		    i != STDERR_FILENO)
27138032Speter			(void) close(i);
27238032Speter	}
27338032Speter	errno = 0;
27438032Speter
27538032Speter#if LOG
27690792Sgshapiro# ifndef SM_LOG_STR
27790792Sgshapiro#  define SM_LOG_STR	"sendmail"
27890792Sgshapiro# endif /* ! SM_LOG_STR */
27964562Sgshapiro#  ifdef LOG_MAIL
28090792Sgshapiro	openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
28164562Sgshapiro#  else /* LOG_MAIL */
28290792Sgshapiro	openlog(SM_LOG_STR, LOG_PID);
28364562Sgshapiro#  endif /* LOG_MAIL */
28464562Sgshapiro#endif /* LOG */
28538032Speter
28690792Sgshapiro	/*
28790792Sgshapiro	**  Seed the random number generator.
28890792Sgshapiro	**  Used for queue file names, picking a queue directory, and
28990792Sgshapiro	**  MX randomization.
29090792Sgshapiro	*/
29138032Speter
29290792Sgshapiro	seed_random();
29338032Speter
29490792Sgshapiro	/* do machine-dependent initializations */
29590792Sgshapiro	init_md(argc, argv);
29690792Sgshapiro
29790792Sgshapiro
29890792Sgshapiro	SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
29990792Sgshapiro
30038032Speter	/* reset status from syserr() calls for missing file descriptors */
30138032Speter	Errors = 0;
30238032Speter	ExitStat = EX_OK;
30338032Speter
30464562Sgshapiro	SubmitMode = SUBMIT_UNKNOWN;
30538032Speter#if XDEBUG
30638032Speter	checkfd012("after openlog");
30764562Sgshapiro#endif /* XDEBUG */
30838032Speter
30990792Sgshapiro	tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1");
31038032Speter
31138032Speter#ifdef NGROUPS_MAX
31238032Speter	/* save initial group set for future checks */
31338032Speter	i = getgroups(NGROUPS_MAX, InitialGidSet);
31490792Sgshapiro	if (i <= 0)
31590792Sgshapiro	{
31638032Speter		InitialGidSet[0] = (GID_T) -1;
31790792Sgshapiro		i = 0;
31890792Sgshapiro	}
31938032Speter	while (i < NGROUPS_MAX)
32038032Speter		InitialGidSet[i++] = InitialGidSet[0];
32164562Sgshapiro#endif /* NGROUPS_MAX */
32238032Speter
32338032Speter	/* drop group id privileges (RunAsUser not yet set) */
32490792Sgshapiro	dp = drop_privileges(false);
32564562Sgshapiro	setstat(dp);
32638032Speter
32790792Sgshapiro#ifdef SIGUSR1
32877349Sgshapiro	/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
32994334Sgshapiro	if (!extraprivs)
33077349Sgshapiro	{
33177349Sgshapiro		/* arrange to dump state on user-1 signal */
33290792Sgshapiro		(void) sm_signal(SIGUSR1, sigusr1);
33377349Sgshapiro	}
33494334Sgshapiro	else
33594334Sgshapiro	{
33694334Sgshapiro		/* ignore user-1 signal */
33794334Sgshapiro		(void) sm_signal(SIGUSR1, SIG_IGN);
33894334Sgshapiro	}
33990792Sgshapiro#endif /* SIGUSR1 */
34038032Speter
34138032Speter	/* initialize for setproctitle */
34238032Speter	initsetproctitle(argc, argv, envp);
34338032Speter
34438032Speter	/* Handle any non-getoptable constructions. */
34538032Speter	obsolete(argv);
34638032Speter
34738032Speter	/*
34838032Speter	**  Do a quick prescan of the argument list.
34938032Speter	*/
35038032Speter
35164562Sgshapiro
35290792Sgshapiro	/* find initial opMode */
35390792Sgshapiro	OpMode = MD_DELIVER;
35490792Sgshapiro	av = argv;
35590792Sgshapiro	p = strrchr(*av, '/');
35690792Sgshapiro	if (p++ == NULL)
35790792Sgshapiro		p = *av;
35890792Sgshapiro	if (strcmp(p, "newaliases") == 0)
35990792Sgshapiro		OpMode = MD_INITALIAS;
36090792Sgshapiro	else if (strcmp(p, "mailq") == 0)
36190792Sgshapiro		OpMode = MD_PRINT;
36290792Sgshapiro	else if (strcmp(p, "smtpd") == 0)
36390792Sgshapiro		OpMode = MD_DAEMON;
36490792Sgshapiro	else if (strcmp(p, "hoststat") == 0)
36590792Sgshapiro		OpMode = MD_HOSTSTAT;
36690792Sgshapiro	else if (strcmp(p, "purgestat") == 0)
36790792Sgshapiro		OpMode = MD_PURGESTAT;
36890792Sgshapiro
36990792Sgshapiro#if _FFR_QUARANTINE
37090792Sgshapiro# if defined(__osf__) || defined(_AIX3)
37190792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:xQ:"
37290792Sgshapiro# endif /* defined(__osf__) || defined(_AIX3) */
37390792Sgshapiro# if defined(sony_news)
37490792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
37590792Sgshapiro# endif /* defined(sony_news) */
37690792Sgshapiro# ifndef OPTIONS
37790792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
37890792Sgshapiro# endif /* ! OPTIONS */
37990792Sgshapiro#else /* _FFR_QUARANTINE */
38090792Sgshapiro# if defined(__osf__) || defined(_AIX3)
38190792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:x"
38290792Sgshapiro# endif /* defined(__osf__) || defined(_AIX3) */
38390792Sgshapiro# if defined(sony_news)
38490792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:"
38590792Sgshapiro# endif /* defined(sony_news) */
38690792Sgshapiro# ifndef OPTIONS
38790792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:"
38890792Sgshapiro# endif /* ! OPTIONS */
38990792Sgshapiro#endif /* _FFR_QUARANTINE */
39090792Sgshapiro
39138032Speter	opterr = 0;
39238032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
39338032Speter	{
39438032Speter		switch (j)
39538032Speter		{
39690792Sgshapiro		  case 'b':	/* operations mode */
39794334Sgshapiro			j = (optarg == NULL) ? ' ' : *optarg;
39894334Sgshapiro			switch (j)
39938032Speter			{
40090792Sgshapiro			  case MD_DAEMON:
40190792Sgshapiro			  case MD_FGDAEMON:
40290792Sgshapiro			  case MD_SMTP:
40390792Sgshapiro			  case MD_INITALIAS:
40490792Sgshapiro			  case MD_DELIVER:
40590792Sgshapiro			  case MD_VERIFY:
40690792Sgshapiro			  case MD_TEST:
40790792Sgshapiro			  case MD_PRINT:
40890792Sgshapiro			  case MD_PRINTNQE:
40990792Sgshapiro			  case MD_HOSTSTAT:
41090792Sgshapiro			  case MD_PURGESTAT:
41190792Sgshapiro			  case MD_ARPAFTP:
41290792Sgshapiro				OpMode = j;
41338032Speter				break;
41490792Sgshapiro
41590792Sgshapiro			  case MD_FREEZE:
41690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
41790792Sgshapiro						     "Frozen configurations unsupported\n");
41890792Sgshapiro				return EX_USAGE;
41990792Sgshapiro
42090792Sgshapiro			  default:
42190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
42290792Sgshapiro						     "Invalid operation mode %c\n",
42390792Sgshapiro						     j);
42490792Sgshapiro				return EX_USAGE;
42538032Speter			}
42690792Sgshapiro			break;
42790792Sgshapiro
42890792Sgshapiro		  case 'd':
42990792Sgshapiro			debug = true;
43038032Speter			tTflag(optarg);
43190792Sgshapiro			(void) sm_io_setvbuf(smioout, SM_TIME_DEFAULT,
43290792Sgshapiro					     (char *) NULL, SM_IO_NBF,
43390792Sgshapiro					     SM_IO_BUFSIZ);
43438032Speter			break;
43564562Sgshapiro
43664562Sgshapiro		  case 'G':	/* relay (gateway) submission */
43790792Sgshapiro			SubmitMode = SUBMIT_MTA;
43864562Sgshapiro			break;
43964562Sgshapiro
44064562Sgshapiro		  case 'L':
44190792Sgshapiro			j = SM_MIN(strlen(optarg), 24) + 1;
44264562Sgshapiro			sysloglabel = xalloc(j);
44390792Sgshapiro			(void) sm_strlcpy(sysloglabel, optarg, j);
44490792Sgshapiro			SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
44590792Sgshapiro					  SL_FUDGE + j;
44664562Sgshapiro			break;
44764562Sgshapiro
44890792Sgshapiro#if _FFR_QUARANTINE
44990792Sgshapiro		  case 'Q':
45090792Sgshapiro#endif /* _FFR_QUARANTINE */
45190792Sgshapiro		  case 'q':
45290792Sgshapiro			/* just check if it is there */
45390792Sgshapiro			queuerun = true;
45464562Sgshapiro			break;
45538032Speter		}
45638032Speter	}
45738032Speter	opterr = 1;
45838032Speter
45990792Sgshapiro	/* Don't leak queue information via debug flags */
46090792Sgshapiro	if (extraprivs && queuerun && debug)
46190792Sgshapiro	{
46290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
46390792Sgshapiro				     "WARNING: Can not use -d with -q.  Disabling debugging.\n");
46490792Sgshapiro		sm_debug_setfile(NULL);
46590792Sgshapiro		(void) memset(tTdvect, '\0', sizeof tTdvect);
46690792Sgshapiro	}
46790792Sgshapiro
46877349Sgshapiro#if LOG
46964562Sgshapiro	if (sysloglabel != NULL)
47064562Sgshapiro	{
47177349Sgshapiro		/* Sanitize the string */
47277349Sgshapiro		for (p = sysloglabel; *p != '\0'; p++)
47377349Sgshapiro		{
47477349Sgshapiro			if (!isascii(*p) || !isprint(*p) || *p == '%')
47577349Sgshapiro				*p = '*';
47677349Sgshapiro		}
47764562Sgshapiro		closelog();
47864562Sgshapiro#  ifdef LOG_MAIL
47964562Sgshapiro		openlog(sysloglabel, LOG_PID, LOG_MAIL);
48064562Sgshapiro#  else /* LOG_MAIL */
48164562Sgshapiro		openlog(sysloglabel, LOG_PID);
48264562Sgshapiro#  endif /* LOG_MAIL */
48377349Sgshapiro	}
48464562Sgshapiro#endif /* LOG */
48564562Sgshapiro
48638032Speter	/* set up the blank envelope */
48738032Speter	BlankEnvelope.e_puthdr = putheader;
48838032Speter	BlankEnvelope.e_putbody = putbody;
48938032Speter	BlankEnvelope.e_xfp = NULL;
49038032Speter	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
49138032Speter	CurEnv = &BlankEnvelope;
49238032Speter	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
49338032Speter
49438032Speter	/*
49538032Speter	**  Set default values for variables.
49638032Speter	**	These cannot be in initialized data space.
49738032Speter	*/
49838032Speter
49938032Speter	setdefaults(&BlankEnvelope);
50090792Sgshapiro	initmacros(&BlankEnvelope);
50138032Speter
50290792Sgshapiro	/* reset macro */
50390792Sgshapiro	set_op_mode(OpMode);
50438032Speter
50538032Speter	pw = sm_getpwuid(RealUid);
50638032Speter	if (pw != NULL)
50790792Sgshapiro		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
50838032Speter	else
50990792Sgshapiro		(void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
51090792Sgshapiro				   (int) RealUid);
51164562Sgshapiro
51238032Speter	RealUserName = rnamebuf;
51338032Speter
51438032Speter	if (tTd(0, 101))
51538032Speter	{
51690792Sgshapiro		sm_dprintf("Version %s\n", Version);
51790792Sgshapiro		finis(false, true, EX_OK);
51890792Sgshapiro		/* NOTREACHED */
51938032Speter	}
52038032Speter
52138032Speter	/*
52290792Sgshapiro	**  if running non-set-user-ID binary as non-root, pretend
52338032Speter	**  we are the RunAsUid
52438032Speter	*/
52577349Sgshapiro
52638032Speter	if (RealUid != 0 && geteuid() == RealUid)
52738032Speter	{
52838032Speter		if (tTd(47, 1))
52990792Sgshapiro			sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
53090792Sgshapiro				   (int) RealUid);
53138032Speter		RunAsUid = RealUid;
53238032Speter	}
53338032Speter	else if (geteuid() != 0)
53438032Speter		RunAsUid = geteuid();
53538032Speter
53690792Sgshapiro	EffGid = getegid();
53790792Sgshapiro	if (RealUid != 0 && EffGid == RealGid)
53838032Speter		RunAsGid = RealGid;
53938032Speter
54038032Speter	if (tTd(47, 5))
54138032Speter	{
54290792Sgshapiro		sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
54390792Sgshapiro			   (int) geteuid(), (int) getuid(),
54490792Sgshapiro			   (int) getegid(), (int) getgid());
54590792Sgshapiro		sm_dprintf("main: RunAsUser = %d:%d\n",
54690792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
54738032Speter	}
54838032Speter
54938032Speter	/* save command line arguments */
55064562Sgshapiro	j = 0;
55138032Speter	for (av = argv; *av != NULL; )
55264562Sgshapiro		j += strlen(*av++) + 1;
55338032Speter	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
55464562Sgshapiro	CommandLineArgs = xalloc(j);
55538032Speter	p = CommandLineArgs;
55638032Speter	for (av = argv, i = 0; *av != NULL; )
55738032Speter	{
55864562Sgshapiro		int h;
55964562Sgshapiro
56038032Speter		SaveArgv[i++] = newstr(*av);
56138032Speter		if (av != argv)
56238032Speter			*p++ = ' ';
56390792Sgshapiro		(void) sm_strlcpy(p, *av++, j);
56464562Sgshapiro		h = strlen(p);
56564562Sgshapiro		p += h;
56664562Sgshapiro		j -= h + 1;
56738032Speter	}
56838032Speter	SaveArgv[i] = NULL;
56938032Speter
57038032Speter	if (tTd(0, 1))
57138032Speter	{
57238032Speter		extern char *CompileOptions[];
57338032Speter
57490792Sgshapiro		sm_dprintf("Version %s\n Compiled with:", Version);
57590792Sgshapiro		sm_printoptions(CompileOptions);
57638032Speter	}
57738032Speter	if (tTd(0, 10))
57838032Speter	{
57938032Speter		extern char *OsCompileOptions[];
58038032Speter
58190792Sgshapiro		sm_dprintf("    OS Defines:");
58290792Sgshapiro		sm_printoptions(OsCompileOptions);
58338032Speter#ifdef _PATH_UNIX
58490792Sgshapiro		sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
58564562Sgshapiro#endif /* _PATH_UNIX */
58690792Sgshapiro
58790792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MSP)\n",
58890792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
58990792Sgshapiro				     conffile));
59090792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MTA)\n",
59190792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
59290792Sgshapiro				     conffile));
59390792Sgshapiro		sm_dprintf("      Pid file:\t%s (default)\n", PidFile);
59438032Speter	}
59538032Speter
59690792Sgshapiro	if (tTd(0, 12))
59790792Sgshapiro	{
59890792Sgshapiro		extern char *SmCompileOptions[];
59938032Speter
60090792Sgshapiro		sm_dprintf(" libsm Defines:");
60190792Sgshapiro		sm_printoptions(SmCompileOptions);
60290792Sgshapiro	}
60390792Sgshapiro
60490792Sgshapiro	if (tTd(0, 13))
60590792Sgshapiro	{
60690792Sgshapiro		extern char *FFRCompileOptions[];
60790792Sgshapiro
60890792Sgshapiro		sm_dprintf("   FFR Defines:");
60990792Sgshapiro		sm_printoptions(FFRCompileOptions);
61090792Sgshapiro	}
61190792Sgshapiro
61290792Sgshapiro	InChannel = smioin;
61390792Sgshapiro	OutChannel = smioout;
61490792Sgshapiro
61538032Speter	/* clear sendmail's environment */
61638032Speter	ExternalEnviron = environ;
61738032Speter	emptyenviron[0] = NULL;
61838032Speter	environ = emptyenviron;
61938032Speter
62038032Speter	/*
62142575Speter	**  restore any original TZ setting until TimeZoneSpec has been
62242575Speter	**  determined - or early log messages may get bogus time stamps
62338032Speter	*/
62490792Sgshapiro
62538032Speter	if ((p = getextenv("TZ")) != NULL)
62638032Speter	{
62738032Speter		char *tz;
62838032Speter		int tzlen;
62938032Speter
63090792Sgshapiro		/* XXX check for reasonable length? */
63138032Speter		tzlen = strlen(p) + 4;
63238032Speter		tz = xalloc(tzlen);
63390792Sgshapiro		(void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
63490792Sgshapiro
63590792Sgshapiro		/* XXX check return code? */
63664562Sgshapiro		(void) putenv(tz);
63738032Speter	}
63838032Speter
63938032Speter	/* prime the child environment */
64038032Speter	setuserenv("AGENT", "sendmail");
64138032Speter
64290792Sgshapiro	(void) sm_signal(SIGPIPE, SIG_IGN);
64338032Speter	OldUmask = umask(022);
64438032Speter	FullName = getextenv("NAME");
64595154Sgshapiro	if (FullName != NULL)
64695154Sgshapiro		FullName = newstr(FullName);
64738032Speter
64838032Speter	/*
64938032Speter	**  Initialize name server if it is going to be used.
65038032Speter	*/
65138032Speter
65238032Speter#if NAMED_BIND
65338032Speter	if (!bitset(RES_INIT, _res.options))
65464562Sgshapiro		(void) res_init();
65538032Speter	if (tTd(8, 8))
65638032Speter		_res.options |= RES_DEBUG;
65738032Speter	else
65838032Speter		_res.options &= ~RES_DEBUG;
65938032Speter# ifdef RES_NOALIASES
66090792Sgshapiro	if (bitset(RES_NOALIASES, _res.options))
66190792Sgshapiro		ResNoAliases = true;
66238032Speter	_res.options |= RES_NOALIASES;
66364562Sgshapiro# endif /* RES_NOALIASES */
66464562Sgshapiro	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
66564562Sgshapiro	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
66664562Sgshapiro	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
66764562Sgshapiro	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
66864562Sgshapiro	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
66964562Sgshapiro	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
67064562Sgshapiro#endif /* NAMED_BIND */
67138032Speter
67238032Speter	errno = 0;
67338032Speter	from = NULL;
67438032Speter
67538032Speter	/* initialize some macros, etc. */
67690792Sgshapiro	init_vendor_macros(&BlankEnvelope);
67738032Speter
67838032Speter	/* version */
67990792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
68038032Speter
68138032Speter	/* hostname */
68238032Speter	hp = myhostname(jbuf, sizeof jbuf);
68338032Speter	if (jbuf[0] != '\0')
68438032Speter	{
68590792Sgshapiro		struct utsname utsname;
68638032Speter
68738032Speter		if (tTd(0, 4))
68890792Sgshapiro			sm_dprintf("Canonical name: %s\n", jbuf);
68990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
69090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
69138032Speter		setclass('w', jbuf);
69238032Speter
69338032Speter		p = strchr(jbuf, '.');
69438032Speter		if (p != NULL)
69538032Speter		{
69638032Speter			if (p[1] != '\0')
69738032Speter			{
69890792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm',
69990792Sgshapiro					  &p[1]);
70038032Speter			}
70138032Speter			while (p != NULL && strchr(&p[1], '.') != NULL)
70238032Speter			{
70338032Speter				*p = '\0';
70438032Speter				if (tTd(0, 4))
70590792Sgshapiro					sm_dprintf("\ta.k.a.: %s\n", jbuf);
70638032Speter				setclass('w', jbuf);
70738032Speter				*p++ = '.';
70838032Speter				p = strchr(p, '.');
70938032Speter			}
71038032Speter		}
71138032Speter
71238032Speter		if (uname(&utsname) >= 0)
71338032Speter			p = utsname.nodename;
71438032Speter		else
71538032Speter		{
71638032Speter			if (tTd(0, 22))
71790792Sgshapiro				sm_dprintf("uname failed (%s)\n",
71890792Sgshapiro					   sm_errstring(errno));
71938032Speter			makelower(jbuf);
72038032Speter			p = jbuf;
72138032Speter		}
72238032Speter		if (tTd(0, 4))
72390792Sgshapiro			sm_dprintf(" UUCP nodename: %s\n", p);
72490792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
72538032Speter		setclass('k', p);
72638032Speter		setclass('w', p);
72738032Speter	}
72838032Speter	if (hp != NULL)
72938032Speter	{
73038032Speter		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
73138032Speter		{
73238032Speter			if (tTd(0, 4))
73390792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", *av);
73438032Speter			setclass('w', *av);
73538032Speter		}
73664562Sgshapiro#if NETINET || NETINET6
73790792Sgshapiro		for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
73838032Speter		{
73964562Sgshapiro# if NETINET6
74064562Sgshapiro			char *addr;
74164562Sgshapiro			char buf6[INET6_ADDRSTRLEN];
74264562Sgshapiro			struct in6_addr ia6;
74364562Sgshapiro# endif /* NETINET6 */
74464562Sgshapiro# if NETINET
74564562Sgshapiro			struct in_addr ia;
74664562Sgshapiro# endif /* NETINET */
74764562Sgshapiro			char ipbuf[103];
74864562Sgshapiro
74964562Sgshapiro			ipbuf[0] = '\0';
75064562Sgshapiro			switch (hp->h_addrtype)
75138032Speter			{
75264562Sgshapiro# if NETINET
75364562Sgshapiro			  case AF_INET:
75464562Sgshapiro				if (hp->h_length != INADDRSZ)
75564562Sgshapiro					break;
75638032Speter
75764562Sgshapiro				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
75890792Sgshapiro				(void) sm_snprintf(ipbuf, sizeof ipbuf,
75990792Sgshapiro						   "[%.100s]", inet_ntoa(ia));
76064562Sgshapiro				break;
76164562Sgshapiro# endif /* NETINET */
76264562Sgshapiro
76364562Sgshapiro# if NETINET6
76464562Sgshapiro			  case AF_INET6:
76564562Sgshapiro				if (hp->h_length != IN6ADDRSZ)
76664562Sgshapiro					break;
76764562Sgshapiro
76864562Sgshapiro				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
76964562Sgshapiro				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
77064562Sgshapiro				if (addr != NULL)
77190792Sgshapiro					(void) sm_snprintf(ipbuf, sizeof ipbuf,
77290792Sgshapiro							   "[%.100s]", addr);
77364562Sgshapiro				break;
77464562Sgshapiro# endif /* NETINET6 */
77538032Speter			}
77664562Sgshapiro			if (ipbuf[0] == '\0')
77764562Sgshapiro				break;
77864562Sgshapiro
77964562Sgshapiro			if (tTd(0, 4))
78090792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", ipbuf);
78164562Sgshapiro			setclass('w', ipbuf);
78238032Speter		}
78364562Sgshapiro#endif /* NETINET || NETINET6 */
78490792Sgshapiro#if NETINET6
78571345Sgshapiro		freehostent(hp);
78671345Sgshapiro		hp = NULL;
78790792Sgshapiro#endif /* NETINET6 */
78838032Speter	}
78938032Speter
79038032Speter	/* current time */
79190792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
79290792Sgshapiro
79364562Sgshapiro	/* current load average */
79490792Sgshapiro	sm_getla();
79538032Speter
79638032Speter	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
79738032Speter	QueueLimitSender = (QUEUE_CHAR *) NULL;
79838032Speter	QueueLimitId = (QUEUE_CHAR *) NULL;
79990792Sgshapiro#if _FFR_QUARANTINE
80090792Sgshapiro	QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
80190792Sgshapiro#endif /* _FFR_QUARANTINE */
80238032Speter
80338032Speter	/*
80442575Speter	**  Crack argv.
80538032Speter	*/
80638032Speter
80738032Speter	optind = 1;
80838032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
80938032Speter	{
81038032Speter		switch (j)
81138032Speter		{
81238032Speter		  case 'b':	/* operations mode */
81390792Sgshapiro			/* already done */
81490792Sgshapiro			break;
81538032Speter
81690792Sgshapiro		  case 'A':	/* use Alternate sendmail/submit.cf */
81790792Sgshapiro			cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
81890792Sgshapiro						  : SM_GET_SENDMAIL_CF;
81938032Speter			break;
82038032Speter
82138032Speter		  case 'B':	/* body type */
82290792Sgshapiro			CHECK_AGAINST_OPMODE(j);
82390792Sgshapiro			BlankEnvelope.e_bodytype = newstr(optarg);
82438032Speter			break;
82538032Speter
82638032Speter		  case 'C':	/* select configuration file (already done) */
82738032Speter			if (RealUid != 0)
82890792Sgshapiro				warn_C_flag = true;
82990792Sgshapiro			conffile = newstr(optarg);
83090792Sgshapiro			dp = drop_privileges(true);
83164562Sgshapiro			setstat(dp);
83290792Sgshapiro			safecf = false;
83338032Speter			break;
83438032Speter
83590792Sgshapiro		  case 'd':	/* debugging */
83690792Sgshapiro			/* already done */
83738032Speter			break;
83838032Speter
83938032Speter		  case 'f':	/* from address */
84038032Speter		  case 'r':	/* obsolete -f flag */
84190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
84238032Speter			if (from != NULL)
84338032Speter			{
84438032Speter				usrerr("More than one \"from\" person");
84538032Speter				ExitStat = EX_USAGE;
84638032Speter				break;
84738032Speter			}
84890792Sgshapiro			from = newstr(denlstring(optarg, true, true));
84938032Speter			if (strcmp(RealUserName, from) != 0)
85038032Speter				warn_f_flag = j;
85138032Speter			break;
85238032Speter
85338032Speter		  case 'F':	/* set full name */
85490792Sgshapiro			CHECK_AGAINST_OPMODE(j);
85538032Speter			FullName = newstr(optarg);
85638032Speter			break;
85738032Speter
85864562Sgshapiro		  case 'G':	/* relay (gateway) submission */
85964562Sgshapiro			/* already set */
86090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
86164562Sgshapiro			break;
86264562Sgshapiro
86338032Speter		  case 'h':	/* hop count */
86490792Sgshapiro			CHECK_AGAINST_OPMODE(j);
86590792Sgshapiro			BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
86690792Sgshapiro								  10);
86790792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
86890792Sgshapiro					   BlankEnvelope.e_hopcount);
86990792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
87090792Sgshapiro
87138032Speter			if (*ep)
87238032Speter			{
87338032Speter				usrerr("Bad hop count (%s)", optarg);
87438032Speter				ExitStat = EX_USAGE;
87538032Speter			}
87638032Speter			break;
87764562Sgshapiro
87864562Sgshapiro		  case 'L':	/* program label */
87964562Sgshapiro			/* already set */
88064562Sgshapiro			break;
88164562Sgshapiro
88238032Speter		  case 'n':	/* don't alias */
88390792Sgshapiro			CHECK_AGAINST_OPMODE(j);
88490792Sgshapiro			NoAlias = true;
88538032Speter			break;
88638032Speter
88738032Speter		  case 'N':	/* delivery status notifications */
88890792Sgshapiro			CHECK_AGAINST_OPMODE(j);
88938032Speter			DefaultNotify |= QHASNOTIFY;
89090792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
89190792Sgshapiro				macid("{dsn_notify}"), optarg);
89290792Sgshapiro			if (sm_strcasecmp(optarg, "never") == 0)
89338032Speter				break;
89438032Speter			for (p = optarg; p != NULL; optarg = p)
89538032Speter			{
89638032Speter				p = strchr(p, ',');
89738032Speter				if (p != NULL)
89838032Speter					*p++ = '\0';
89990792Sgshapiro				if (sm_strcasecmp(optarg, "success") == 0)
90038032Speter					DefaultNotify |= QPINGONSUCCESS;
90190792Sgshapiro				else if (sm_strcasecmp(optarg, "failure") == 0)
90238032Speter					DefaultNotify |= QPINGONFAILURE;
90390792Sgshapiro				else if (sm_strcasecmp(optarg, "delay") == 0)
90438032Speter					DefaultNotify |= QPINGONDELAY;
90538032Speter				else
90638032Speter				{
90738032Speter					usrerr("Invalid -N argument");
90838032Speter					ExitStat = EX_USAGE;
90938032Speter				}
91038032Speter			}
91138032Speter			break;
91238032Speter
91338032Speter		  case 'o':	/* set option */
91490792Sgshapiro			setoption(*optarg, optarg + 1, false, true,
91590792Sgshapiro				  &BlankEnvelope);
91638032Speter			break;
91738032Speter
91838032Speter		  case 'O':	/* set option (long form) */
91990792Sgshapiro			setoption(' ', optarg, false, true, &BlankEnvelope);
92038032Speter			break;
92138032Speter
92238032Speter		  case 'p':	/* set protocol */
92390792Sgshapiro			CHECK_AGAINST_OPMODE(j);
92438032Speter			p = strchr(optarg, ':');
92538032Speter			if (p != NULL)
92638032Speter			{
92738032Speter				*p++ = '\0';
92838032Speter				if (*p != '\0')
92938032Speter				{
93090792Sgshapiro					ep = sm_malloc_x(strlen(p) + 1);
93138032Speter					cleanstrcpy(ep, p, MAXNAME);
93290792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
93390792Sgshapiro						  A_HEAP, 's', ep);
93438032Speter				}
93538032Speter			}
93638032Speter			if (*optarg != '\0')
93738032Speter			{
93890792Sgshapiro				ep = sm_malloc_x(strlen(optarg) + 1);
93938032Speter				cleanstrcpy(ep, optarg, MAXNAME);
94090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_HEAP,
94190792Sgshapiro					  'r', ep);
94238032Speter			}
94338032Speter			break;
94438032Speter
94590792Sgshapiro#if _FFR_QUARANTINE
94690792Sgshapiro		  case 'Q':	/* change quarantining on queued items */
94790792Sgshapiro			/* sanity check */
94890792Sgshapiro			if (OpMode != MD_DELIVER &&
94990792Sgshapiro			    OpMode != MD_QUEUERUN)
95090792Sgshapiro			{
95190792Sgshapiro				usrerr("Can not use -Q with -b%c", OpMode);
95290792Sgshapiro				ExitStat = EX_USAGE;
95390792Sgshapiro				break;
95490792Sgshapiro			}
95590792Sgshapiro
95690792Sgshapiro			if (OpMode == MD_DELIVER)
95790792Sgshapiro				set_op_mode(MD_QUEUERUN);
95890792Sgshapiro
95990792Sgshapiro			FullName = NULL;
96090792Sgshapiro
96190792Sgshapiro			quarantining = newstr(optarg);
96290792Sgshapiro			break;
96390792Sgshapiro#endif /* _FFR_QUARANTINE */
96490792Sgshapiro
96538032Speter		  case 'q':	/* run queue files at intervals */
96664562Sgshapiro			/* sanity check */
96764562Sgshapiro			if (OpMode != MD_DELIVER &&
96864562Sgshapiro			    OpMode != MD_DAEMON &&
96964562Sgshapiro			    OpMode != MD_FGDAEMON &&
97064562Sgshapiro			    OpMode != MD_PRINT &&
97190792Sgshapiro			    OpMode != MD_PRINTNQE &&
97264562Sgshapiro			    OpMode != MD_QUEUERUN)
97364562Sgshapiro			{
97464562Sgshapiro				usrerr("Can not use -q with -b%c", OpMode);
97564562Sgshapiro				ExitStat = EX_USAGE;
97664562Sgshapiro				break;
97764562Sgshapiro			}
97864562Sgshapiro
97964562Sgshapiro			/* don't override -bd, -bD or -bp */
98064562Sgshapiro			if (OpMode == MD_DELIVER)
98190792Sgshapiro				set_op_mode(MD_QUEUERUN);
98264562Sgshapiro
98338032Speter			FullName = NULL;
98490792Sgshapiro			negate = optarg[0] == '!';
98590792Sgshapiro			if (negate)
98690792Sgshapiro			{
98790792Sgshapiro				/* negate meaning of pattern match */
98890792Sgshapiro				optarg++; /* skip '!' for next switch */
98990792Sgshapiro			}
99064562Sgshapiro
99138032Speter			switch (optarg[0])
99238032Speter			{
99390792Sgshapiro			  case 'G': /* Limit by queue group name */
99490792Sgshapiro				if (negate)
99590792Sgshapiro				{
99690792Sgshapiro					usrerr("Can not use -q!G");
99790792Sgshapiro					ExitStat = EX_USAGE;
99890792Sgshapiro					break;
99990792Sgshapiro				}
100090792Sgshapiro				if (queuegroup != NULL)
100190792Sgshapiro				{
100290792Sgshapiro					usrerr("Can not use multiple -qG options");
100390792Sgshapiro					ExitStat = EX_USAGE;
100490792Sgshapiro					break;
100590792Sgshapiro				}
100690792Sgshapiro				queuegroup = newstr(&optarg[1]);
100790792Sgshapiro				break;
100890792Sgshapiro
100990792Sgshapiro			  case 'I': /* Limit by ID */
101064562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
101138032Speter				new->queue_match = newstr(&optarg[1]);
101290792Sgshapiro				new->queue_negate = negate;
101338032Speter				new->queue_next = QueueLimitId;
101438032Speter				QueueLimitId = new;
101538032Speter				break;
101638032Speter
101790792Sgshapiro			  case 'R': /* Limit by recipient */
101864562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
101938032Speter				new->queue_match = newstr(&optarg[1]);
102090792Sgshapiro				new->queue_negate = negate;
102138032Speter				new->queue_next = QueueLimitRecipient;
102238032Speter				QueueLimitRecipient = new;
102338032Speter				break;
102438032Speter
102590792Sgshapiro			  case 'S': /* Limit by sender */
102664562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
102738032Speter				new->queue_match = newstr(&optarg[1]);
102890792Sgshapiro				new->queue_negate = negate;
102938032Speter				new->queue_next = QueueLimitSender;
103038032Speter				QueueLimitSender = new;
103138032Speter				break;
103238032Speter
103390792Sgshapiro			  case 'f': /* foreground queue run */
103490792Sgshapiro				foregroundqueue  = true;
103590792Sgshapiro				break;
103690792Sgshapiro
103790792Sgshapiro#if _FFR_QUARANTINE
103890792Sgshapiro			  case 'Q': /* Limit by quarantine message */
103990792Sgshapiro				if (optarg[1] != '\0')
104090792Sgshapiro				{
104190792Sgshapiro					new = (QUEUE_CHAR *) xalloc(sizeof *new);
104290792Sgshapiro					new->queue_match = newstr(&optarg[1]);
104390792Sgshapiro					new->queue_negate = negate;
104490792Sgshapiro					new->queue_next = QueueLimitQuarantine;
104590792Sgshapiro					QueueLimitQuarantine = new;
104690792Sgshapiro				}
104790792Sgshapiro				QueueMode = QM_QUARANTINE;
104890792Sgshapiro				break;
104990792Sgshapiro
105090792Sgshapiro			  case 'L': /* act on lost items */
105190792Sgshapiro				QueueMode = QM_LOST;
105290792Sgshapiro				break;
105390792Sgshapiro#endif /* _FFR_QUARANTINE */
105490792Sgshapiro
105590792Sgshapiro			  case 'p': /* Persistent queue */
105690792Sgshapiro				queuepersistent = true;
105790792Sgshapiro				if (QueueIntvl == 0)
105890792Sgshapiro					QueueIntvl = 1;
105990792Sgshapiro				if (optarg[1] == '\0')
106090792Sgshapiro					break;
106190792Sgshapiro				++optarg;
106290792Sgshapiro				/* FALLTHROUGH */
106390792Sgshapiro
106438032Speter			  default:
106564562Sgshapiro				i = Errors;
106638032Speter				QueueIntvl = convtime(optarg, 'm');
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	{
119190792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
119290792Sgshapiro			  macid("{daemon_flags}"), "CC f");
119364562Sgshapiro	}
119490792Sgshapiro	else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
119564562Sgshapiro	{
119690792Sgshapiro		SubmitMode = SUBMIT_MSA;
119790792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
119890792Sgshapiro			  macid("{daemon_flags}"), "c u");
119964562Sgshapiro	}
120064562Sgshapiro
120138032Speter	/*
120238032Speter	**  Do basic initialization.
120338032Speter	**	Read system control file.
120438032Speter	**	Extract special fields for local use.
120538032Speter	*/
120638032Speter
120738032Speter#if XDEBUG
120838032Speter	checkfd012("before readcf");
120964562Sgshapiro#endif /* XDEBUG */
121090792Sgshapiro	vendor_pre_defaults(&BlankEnvelope);
121164562Sgshapiro
121290792Sgshapiro	readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
121390792Sgshapiro			 safecf, &BlankEnvelope);
121490792Sgshapiro#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
121590792Sgshapiro	ConfigFileRead = true;
121690792Sgshapiro#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
121790792Sgshapiro	vendor_post_defaults(&BlankEnvelope);
121838032Speter
121990792Sgshapiro	/* now we can complain about missing fds */
122090792Sgshapiro	if (MissingFds != 0 && LogLevel > 8)
122190792Sgshapiro	{
122290792Sgshapiro		char mbuf[MAXLINE];
122390792Sgshapiro
122490792Sgshapiro		mbuf[0] = '\0';
122590792Sgshapiro		if (bitset(1 << STDIN_FILENO, MissingFds))
122690792Sgshapiro			(void) sm_strlcat(mbuf, ", stdin", sizeof mbuf);
122790792Sgshapiro		if (bitset(1 << STDOUT_FILENO, MissingFds))
122890792Sgshapiro			(void) sm_strlcat(mbuf, ", stdout", sizeof mbuf);
122990792Sgshapiro		if (bitset(1 << STDERR_FILENO, MissingFds))
123090792Sgshapiro			(void) sm_strlcat(mbuf, ", stderr", sizeof mbuf);
123190792Sgshapiro
123290792Sgshapiro		/* Notice: fill_errno is from high above: fill_fd() */
123390792Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
123490792Sgshapiro			  "File descriptors missing on startup: %s; %s",
123590792Sgshapiro			  &mbuf[2], sm_errstring(fill_errno));
123690792Sgshapiro	}
123790792Sgshapiro
123877349Sgshapiro	/* Remove the ability for a normal user to send signals */
123990792Sgshapiro	if (RealUid != 0 && RealUid != geteuid())
124077349Sgshapiro	{
124177349Sgshapiro		uid_t new_uid = geteuid();
124277349Sgshapiro
124377349Sgshapiro#if HASSETREUID
124477349Sgshapiro		/*
124577349Sgshapiro		**  Since we can differentiate between uid and euid,
124677349Sgshapiro		**  make the uid a different user so the real user
124777349Sgshapiro		**  can't send signals.  However, it doesn't need to be
124877349Sgshapiro		**  root (euid has root).
124977349Sgshapiro		*/
125077349Sgshapiro
125177349Sgshapiro		if (new_uid == 0)
125277349Sgshapiro			new_uid = DefUid;
125377349Sgshapiro		if (tTd(47, 5))
125490792Sgshapiro			sm_dprintf("Changing real uid to %d\n", (int) new_uid);
125577349Sgshapiro		if (setreuid(new_uid, geteuid()) < 0)
125677349Sgshapiro		{
125777349Sgshapiro			syserr("main: setreuid(%d, %d) failed",
125877349Sgshapiro			       (int) new_uid, (int) geteuid());
125990792Sgshapiro			finis(false, true, EX_OSERR);
126077349Sgshapiro			/* NOTREACHED */
126177349Sgshapiro		}
126277349Sgshapiro		if (tTd(47, 10))
126390792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
126490792Sgshapiro				   (int) geteuid(), (int) getuid());
126577349Sgshapiro#else /* HASSETREUID */
126677349Sgshapiro		/*
126777349Sgshapiro		**  Have to change both effective and real so need to
126877349Sgshapiro		**  change them both to effective to keep privs.
126977349Sgshapiro		*/
127077349Sgshapiro
127177349Sgshapiro		if (tTd(47, 5))
127290792Sgshapiro			sm_dprintf("Changing uid to %d\n", (int) new_uid);
127377349Sgshapiro		if (setuid(new_uid) < 0)
127477349Sgshapiro		{
127577349Sgshapiro			syserr("main: setuid(%d) failed", (int) new_uid);
127690792Sgshapiro			finis(false, true, EX_OSERR);
127777349Sgshapiro			/* NOTREACHED */
127877349Sgshapiro		}
127977349Sgshapiro		if (tTd(47, 10))
128090792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
128190792Sgshapiro				   (int) geteuid(), (int) getuid());
128277349Sgshapiro#endif /* HASSETREUID */
128377349Sgshapiro	}
128477349Sgshapiro
128590792Sgshapiro#if NAMED_BIND
128690792Sgshapiro	if (FallBackMX != NULL)
128790792Sgshapiro		(void) getfallbackmxrr(FallBackMX);
128890792Sgshapiro#endif /* NAMED_BIND */
128990792Sgshapiro
129090792Sgshapiro	if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER)
129190792Sgshapiro	{
129290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
129390792Sgshapiro				     "WARNING: SuperSafe=interactive should only be used with\n         DeliveryMode=interactive\n");
129490792Sgshapiro	}
129590792Sgshapiro
129690792Sgshapiro	if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
129790792Sgshapiro	{
129890792Sgshapiro		usrerr("Mail submission program cannot be used as daemon");
129990792Sgshapiro		finis(false, true, EX_USAGE);
130090792Sgshapiro	}
130190792Sgshapiro
130290792Sgshapiro	if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
130390792Sgshapiro	    OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
130490792Sgshapiro	    OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
130590792Sgshapiro		makeworkgroups();
130690792Sgshapiro
130777349Sgshapiro	/* set up the basic signal handlers */
130890792Sgshapiro	if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
130990792Sgshapiro		(void) sm_signal(SIGINT, intsig);
131090792Sgshapiro	(void) sm_signal(SIGTERM, intsig);
131177349Sgshapiro
131238032Speter	/* Enforce use of local time (null string overrides this) */
131338032Speter	if (TimeZoneSpec == NULL)
131438032Speter		unsetenv("TZ");
131538032Speter	else if (TimeZoneSpec[0] != '\0')
131638032Speter		setuserenv("TZ", TimeZoneSpec);
131738032Speter	else
131838032Speter		setuserenv("TZ", NULL);
131938032Speter	tzset();
132038032Speter
132190792Sgshapiro	/* initialize mailbox database */
132290792Sgshapiro	i = sm_mbdb_initialize(Mbdb);
132390792Sgshapiro	if (i != EX_OK)
132490792Sgshapiro	{
132590792Sgshapiro		usrerr("Can't initialize mailbox database \"%s\": %s",
132690792Sgshapiro		       Mbdb, sm_strexit(i));
132790792Sgshapiro		ExitStat = i;
132890792Sgshapiro	}
132990792Sgshapiro
133038032Speter	/* avoid denial-of-service attacks */
133138032Speter	resetlimits();
133238032Speter
133390792Sgshapiro	if (OpMode == MD_TEST)
133438032Speter	{
133590792Sgshapiro		/* can't be done after readcf if RunAs* is used */
133690792Sgshapiro		dp = drop_privileges(true);
133790792Sgshapiro		if (dp != EX_OK)
133890792Sgshapiro		{
133990792Sgshapiro			finis(false, true, dp);
134090792Sgshapiro			/* NOTREACHED */
134190792Sgshapiro		}
134290792Sgshapiro	}
134390792Sgshapiro	else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
134490792Sgshapiro	{
134538032Speter		/* drop privileges -- daemon mode done after socket/bind */
134690792Sgshapiro		dp = drop_privileges(false);
134764562Sgshapiro		setstat(dp);
134890792Sgshapiro		if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
134990792Sgshapiro		{
135090792Sgshapiro			usrerr("Mail submission program must have RunAsUser set to non root user");
135190792Sgshapiro			finis(false, true, EX_CONFIG);
135290792Sgshapiro			/* NOTREACHED */
135390792Sgshapiro		}
135438032Speter	}
135538032Speter
135664562Sgshapiro#if NAMED_BIND
135764562Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
135864562Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
135964562Sgshapiro#endif /* NAMED_BIND */
136064562Sgshapiro
136138032Speter	/*
136238032Speter	**  Find our real host name for future logging.
136338032Speter	*/
136438032Speter
136564562Sgshapiro	authinfo = getauthinfo(STDIN_FILENO, &forged);
136690792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
136738032Speter
136838032Speter	/* suppress error printing if errors mailed back or whatever */
136990792Sgshapiro	if (BlankEnvelope.e_errormode != EM_PRINT)
137090792Sgshapiro		HoldErrs = true;
137138032Speter
137238032Speter	/* set up the $=m class now, after .cf has a chance to redefine $m */
137390792Sgshapiro	expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope);
137473188Sgshapiro	if (jbuf[0] != '\0')
137573188Sgshapiro		setclass('m', jbuf);
137638032Speter
137738032Speter	/* probe interfaces and locate any additional names */
137890792Sgshapiro	if (DontProbeInterfaces != DPI_PROBENONE)
137938032Speter		load_if_names();
138038032Speter
138190792Sgshapiro	if (tTd(0, 10))
138290792Sgshapiro	{
138390792Sgshapiro		/* Now we know which .cf file we use */
138490792Sgshapiro		sm_dprintf("     Conf file:\t%s (selected)\n",
138590792Sgshapiro			   getcfname(OpMode, SubmitMode, cftype, conffile));
138690792Sgshapiro		sm_dprintf("      Pid file:\t%s (selected)\n", PidFile);
138790792Sgshapiro	}
138890792Sgshapiro
138938032Speter	if (tTd(0, 1))
139038032Speter	{
139190792Sgshapiro		sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
139290792Sgshapiro		sm_dprintf("\n      (short domain name) $w = ");
139390792Sgshapiro		xputs(macvalue('w', &BlankEnvelope));
139490792Sgshapiro		sm_dprintf("\n  (canonical domain name) $j = ");
139590792Sgshapiro		xputs(macvalue('j', &BlankEnvelope));
139690792Sgshapiro		sm_dprintf("\n         (subdomain name) $m = ");
139790792Sgshapiro		xputs(macvalue('m', &BlankEnvelope));
139890792Sgshapiro		sm_dprintf("\n              (node name) $k = ");
139990792Sgshapiro		xputs(macvalue('k', &BlankEnvelope));
140090792Sgshapiro		sm_dprintf("\n========================================================\n\n");
140138032Speter	}
140238032Speter
140338032Speter	/*
140438032Speter	**  Do more command line checking -- these are things that
140538032Speter	**  have to modify the results of reading the config file.
140638032Speter	*/
140738032Speter
140838032Speter	/* process authorization warnings from command line */
140938032Speter	if (warn_C_flag)
141090792Sgshapiro		auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
141190792Sgshapiro			     RealUserName, conffile);
141264562Sgshapiro	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
141390792Sgshapiro		auth_warning(&BlankEnvelope, "Processed from queue %s",
141490792Sgshapiro			     QueueDir);
141590792Sgshapiro	if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
141690792Sgshapiro	    RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
141790792Sgshapiro		sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
141890792Sgshapiro			  (int) RealUid);
141938032Speter
142038032Speter	/* check body type for legality */
142190792Sgshapiro	i = check_bodytype(BlankEnvelope.e_bodytype);
142290792Sgshapiro	if (i == BODYTYPE_ILLEGAL)
142338032Speter	{
142490792Sgshapiro		usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
142590792Sgshapiro		BlankEnvelope.e_bodytype = NULL;
142638032Speter	}
142790792Sgshapiro	else if (i != BODYTYPE_NONE)
142890792Sgshapiro		SevenBitInput = (i == BODYTYPE_7BIT);
142938032Speter
143038032Speter	/* tweak default DSN notifications */
143138032Speter	if (DefaultNotify == 0)
143238032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
143338032Speter
143438032Speter	/* be sure we don't pick up bogus HOSTALIASES environment variable */
143564562Sgshapiro	if (OpMode == MD_QUEUERUN && RealUid != 0)
143638032Speter		(void) unsetenv("HOSTALIASES");
143738032Speter
143838032Speter	/* check for sane configuration level */
143938032Speter	if (ConfigLevel > MAXCONFIGLEVEL)
144038032Speter	{
144138032Speter		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
144290792Sgshapiro		       ConfigLevel, Version, MAXCONFIGLEVEL);
144338032Speter	}
144438032Speter
144538032Speter	/* need MCI cache to have persistence */
144638032Speter	if (HostStatDir != NULL && MaxMciCache == 0)
144738032Speter	{
144838032Speter		HostStatDir = NULL;
144990792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
145090792Sgshapiro				     "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
145138032Speter	}
145238032Speter
145338032Speter	/* need HostStatusDir in order to have SingleThreadDelivery */
145438032Speter	if (SingleThreadDelivery && HostStatDir == NULL)
145538032Speter	{
145690792Sgshapiro		SingleThreadDelivery = false;
145790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
145890792Sgshapiro				     "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
145938032Speter	}
146038032Speter
146138032Speter	/* check for permissions */
146290792Sgshapiro	if (RealUid != 0 &&
146342575Speter	    RealUid != TrustedUid)
146438032Speter	{
146590792Sgshapiro		char *action = NULL;
146690792Sgshapiro
146790792Sgshapiro		switch (OpMode)
146890792Sgshapiro		{
146990792Sgshapiro		  case MD_QUEUERUN:
147090792Sgshapiro#if _FFR_QUARANTINE
147190792Sgshapiro			if (quarantining != NULL)
147290792Sgshapiro				action = "quarantine jobs";
147390792Sgshapiro			else
147490792Sgshapiro#endif /* _FFR_QUARANTINE */
147590792Sgshapiro			/* Normal users can do a single queue run */
147690792Sgshapiro			if (QueueIntvl == 0)
147790792Sgshapiro				break;
147890792Sgshapiro
147990792Sgshapiro			/* but not persistent queue runners */
148090792Sgshapiro			if (action == NULL)
148190792Sgshapiro				action = "start a queue runner daemon";
148290792Sgshapiro			/* FALLTHROUGH */
148390792Sgshapiro
148490792Sgshapiro		  case MD_PURGESTAT:
148590792Sgshapiro			if (action == NULL)
148690792Sgshapiro				action = "purge host status";
148790792Sgshapiro			/* FALLTHROUGH */
148890792Sgshapiro
148990792Sgshapiro		  case MD_DAEMON:
149090792Sgshapiro		  case MD_FGDAEMON:
149190792Sgshapiro			if (action == NULL)
149290792Sgshapiro				action = "run daemon";
149390792Sgshapiro
149490792Sgshapiro			if (tTd(65, 1))
149590792Sgshapiro				sm_dprintf("Deny user %d attempt to %s\n",
149690792Sgshapiro					   (int) RealUid, action);
149790792Sgshapiro
149890792Sgshapiro			if (LogLevel > 1)
149990792Sgshapiro				sm_syslog(LOG_ALERT, NOQID,
150090792Sgshapiro					  "user %d attempted to %s",
150190792Sgshapiro					  (int) RealUid, action);
150290792Sgshapiro			HoldErrs = false;
150390792Sgshapiro			usrerr("Permission denied (real uid not trusted)");
150490792Sgshapiro			finis(false, true, EX_USAGE);
150590792Sgshapiro			/* NOTREACHED */
150690792Sgshapiro			break;
150790792Sgshapiro
150890792Sgshapiro		  case MD_VERIFY:
150990792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
151090792Sgshapiro			{
151190792Sgshapiro				/*
151290792Sgshapiro				**  If -bv and RestrictExpand,
151390792Sgshapiro				**  drop privs to prevent normal
151490792Sgshapiro				**  users from reading private
151590792Sgshapiro				**  aliases/forwards/:include:s
151690792Sgshapiro				*/
151790792Sgshapiro
151890792Sgshapiro				if (tTd(65, 1))
151990792Sgshapiro					sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
152090792Sgshapiro						   (int) RealUid);
152190792Sgshapiro
152290792Sgshapiro				dp = drop_privileges(true);
152390792Sgshapiro
152490792Sgshapiro				/* Fake address safety */
152590792Sgshapiro				if (tTd(65, 1))
152690792Sgshapiro					sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
152790792Sgshapiro				setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
152890792Sgshapiro
152990792Sgshapiro				if (dp != EX_OK)
153090792Sgshapiro				{
153190792Sgshapiro					if (tTd(65, 1))
153290792Sgshapiro						sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
153390792Sgshapiro							   (int) RealUid);
153490792Sgshapiro					CurEnv->e_id = NULL;
153590792Sgshapiro					finis(true, true, dp);
153690792Sgshapiro					/* NOTREACHED */
153790792Sgshapiro				}
153890792Sgshapiro			}
153990792Sgshapiro			break;
154090792Sgshapiro
154190792Sgshapiro		  case MD_TEST:
154290792Sgshapiro		  case MD_PRINT:
154390792Sgshapiro		  case MD_PRINTNQE:
154490792Sgshapiro		  case MD_FREEZE:
154590792Sgshapiro		  case MD_HOSTSTAT:
154690792Sgshapiro			/* Nothing special to check */
154790792Sgshapiro			break;
154890792Sgshapiro
154990792Sgshapiro		  case MD_INITALIAS:
155090792Sgshapiro			if (!wordinclass(RealUserName, 't'))
155190792Sgshapiro			{
155290792Sgshapiro				if (tTd(65, 1))
155390792Sgshapiro					sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
155490792Sgshapiro						   (int) RealUid);
155590792Sgshapiro				if (LogLevel > 1)
155690792Sgshapiro					sm_syslog(LOG_ALERT, NOQID,
155790792Sgshapiro						  "user %d attempted to rebuild the alias map",
155890792Sgshapiro						  (int) RealUid);
155990792Sgshapiro				HoldErrs = false;
156090792Sgshapiro				usrerr("Permission denied (real uid not trusted)");
156190792Sgshapiro				finis(false, true, EX_USAGE);
156290792Sgshapiro				/* NOTREACHED */
156390792Sgshapiro			}
156490792Sgshapiro			if (UseMSP)
156590792Sgshapiro			{
156690792Sgshapiro				HoldErrs = false;
156790792Sgshapiro				usrerr("User %d cannot rebuild aliases in mail submission program",
156890792Sgshapiro				       (int) RealUid);
156990792Sgshapiro				finis(false, true, EX_USAGE);
157090792Sgshapiro				/* NOTREACHED */
157190792Sgshapiro			}
157290792Sgshapiro			/* FALLTHROUGH */
157390792Sgshapiro
157490792Sgshapiro		  default:
157590792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
157690792Sgshapiro			    Verbose != 0)
157790792Sgshapiro			{
157890792Sgshapiro				/*
157990792Sgshapiro				**  If -v and RestrictExpand, reset
158090792Sgshapiro				**  Verbose to prevent normal users
158190792Sgshapiro				**  from seeing the expansion of
158290792Sgshapiro				**  aliases/forwards/:include:s
158390792Sgshapiro				*/
158490792Sgshapiro
158590792Sgshapiro				if (tTd(65, 1))
158690792Sgshapiro					sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
158790792Sgshapiro						   (int) RealUid);
158890792Sgshapiro				Verbose = 0;
158990792Sgshapiro			}
159090792Sgshapiro			break;
159190792Sgshapiro		}
159238032Speter	}
159338032Speter
159438032Speter	if (MeToo)
159538032Speter		BlankEnvelope.e_flags |= EF_METOO;
159638032Speter
159738032Speter	switch (OpMode)
159838032Speter	{
159938032Speter	  case MD_TEST:
160038032Speter		/* don't have persistent host status in test mode */
160138032Speter		HostStatDir = NULL;
160238032Speter		if (Verbose == 0)
160338032Speter			Verbose = 2;
160490792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
160590792Sgshapiro		HoldErrs = false;
160638032Speter		break;
160738032Speter
160838032Speter	  case MD_VERIFY:
160990792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
161090792Sgshapiro		HoldErrs = false;
161138032Speter		/* arrange to exit cleanly on hangup signal */
161290792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
161390792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
161490792Sgshapiro		if (geteuid() != 0)
161590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
161690792Sgshapiro					     "Notice: -bv may give misleading output for non-privileged user\n");
161738032Speter		break;
161838032Speter
161938032Speter	  case MD_FGDAEMON:
162090792Sgshapiro		run_in_foreground = true;
162190792Sgshapiro		set_op_mode(MD_DAEMON);
162264562Sgshapiro		/* FALLTHROUGH */
162338032Speter
162438032Speter	  case MD_DAEMON:
162590792Sgshapiro		vendor_daemon_setup(&BlankEnvelope);
162638032Speter
162738032Speter		/* remove things that don't make sense in daemon mode */
162838032Speter		FullName = NULL;
162990792Sgshapiro		GrabTo = false;
163038032Speter
163138032Speter		/* arrange to restart on hangup signal */
163238032Speter		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
163338032Speter			sm_syslog(LOG_WARNING, NOQID,
163464562Sgshapiro				  "daemon invoked without full pathname; kill -1 won't work");
163538032Speter		break;
163638032Speter
163738032Speter	  case MD_INITALIAS:
163838032Speter		Verbose = 2;
163990792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
164090792Sgshapiro		HoldErrs = false;
164164562Sgshapiro		/* FALLTHROUGH */
164238032Speter
164338032Speter	  default:
164438032Speter		/* arrange to exit cleanly on hangup signal */
164590792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
164690792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
164738032Speter		break;
164838032Speter	}
164938032Speter
165038032Speter	/* special considerations for FullName */
165138032Speter	if (FullName != NULL)
165238032Speter	{
165338032Speter		char *full = NULL;
165438032Speter
165538032Speter		/* full names can't have newlines */
165664562Sgshapiro		if (strchr(FullName, '\n') != NULL)
165738032Speter		{
165890792Sgshapiro			full = newstr(denlstring(FullName, true, true));
165973188Sgshapiro			FullName = full;
166038032Speter		}
166173188Sgshapiro
166238032Speter		/* check for characters that may have to be quoted */
166338032Speter		if (!rfc822_string(FullName))
166438032Speter		{
166538032Speter			/*
166638032Speter			**  Quote a full name with special characters
166738032Speter			**  as a comment so crackaddr() doesn't destroy
166838032Speter			**  the name portion of the address.
166938032Speter			*/
167073188Sgshapiro
167190792Sgshapiro			FullName = addquotes(FullName, NULL);
167238032Speter			if (full != NULL)
167390792Sgshapiro				sm_free(full);  /* XXX */
167438032Speter		}
167538032Speter	}
167638032Speter
167738032Speter	/* do heuristic mode adjustment */
167838032Speter	if (Verbose)
167938032Speter	{
168038032Speter		/* turn off noconnect option */
168190792Sgshapiro		setoption('c', "F", true, false, &BlankEnvelope);
168238032Speter
168338032Speter		/* turn on interactive delivery */
168490792Sgshapiro		setoption('d', "", true, false, &BlankEnvelope);
168538032Speter	}
168638032Speter
168742575Speter#ifdef VENDOR_CODE
168842575Speter	/* check for vendor mismatch */
168942575Speter	if (VendorCode != VENDOR_CODE)
169042575Speter	{
169142575Speter		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
169242575Speter			getvendor(VENDOR_CODE), getvendor(VendorCode));
169342575Speter	}
169464562Sgshapiro#endif /* VENDOR_CODE */
169564562Sgshapiro
169638032Speter	/* check for out of date configuration level */
169738032Speter	if (ConfigLevel < MAXCONFIGLEVEL)
169838032Speter	{
169938032Speter		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
170038032Speter			Version, MAXCONFIGLEVEL, ConfigLevel);
170138032Speter	}
170238032Speter
170338032Speter	if (ConfigLevel < 3)
170490792Sgshapiro		UseErrorsTo = true;
170538032Speter
170638032Speter	/* set options that were previous macros */
170738032Speter	if (SmtpGreeting == NULL)
170838032Speter	{
170990792Sgshapiro		if (ConfigLevel < 7 &&
171090792Sgshapiro		    (p = macvalue('e', &BlankEnvelope)) != NULL)
171138032Speter			SmtpGreeting = newstr(p);
171238032Speter		else
171338032Speter			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
171438032Speter	}
171538032Speter	if (UnixFromLine == NULL)
171638032Speter	{
171790792Sgshapiro		if (ConfigLevel < 7 &&
171890792Sgshapiro		    (p = macvalue('l', &BlankEnvelope)) != NULL)
171938032Speter			UnixFromLine = newstr(p);
172038032Speter		else
172138032Speter			UnixFromLine = "From \201g  \201d";
172238032Speter	}
172364562Sgshapiro	SmtpError[0] = '\0';
172438032Speter
172538032Speter	/* our name for SMTP codes */
172690792Sgshapiro	expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
172773188Sgshapiro	if (jbuf[0] == '\0')
172890792Sgshapiro		PSTRSET(MyHostName, "localhost");
172973188Sgshapiro	else
173090792Sgshapiro		PSTRSET(MyHostName, jbuf);
173173188Sgshapiro	if (strchr(MyHostName, '.') == NULL)
173238032Speter		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
173373188Sgshapiro			MyHostName);
173438032Speter
173538032Speter	/* make certain that this name is part of the $=w class */
173638032Speter	setclass('w', MyHostName);
173738032Speter
173890792Sgshapiro	/* fill in the structure of the *default* queue */
173990792Sgshapiro	st = stab("mqueue", ST_QUEUE, ST_FIND);
174090792Sgshapiro	if (st == NULL)
174190792Sgshapiro		syserr("No default queue (mqueue) defined");
174290792Sgshapiro	else
174390792Sgshapiro		set_def_queueval(st->s_quegrp, true);
174490792Sgshapiro
174538032Speter	/* the indices of built-in mailers */
174638032Speter	st = stab("local", ST_MAILER, ST_FIND);
174738032Speter	if (st != NULL)
174838032Speter		LocalMailer = st->s_mailer;
174938032Speter	else if (OpMode != MD_TEST || !warn_C_flag)
175038032Speter		syserr("No local mailer defined");
175138032Speter
175238032Speter	st = stab("prog", ST_MAILER, ST_FIND);
175338032Speter	if (st == NULL)
175438032Speter		syserr("No prog mailer defined");
175538032Speter	else
175638032Speter	{
175738032Speter		ProgMailer = st->s_mailer;
175838032Speter		clrbitn(M_MUSER, ProgMailer->m_flags);
175938032Speter	}
176038032Speter
176138032Speter	st = stab("*file*", ST_MAILER, ST_FIND);
176238032Speter	if (st == NULL)
176338032Speter		syserr("No *file* mailer defined");
176438032Speter	else
176538032Speter	{
176638032Speter		FileMailer = st->s_mailer;
176738032Speter		clrbitn(M_MUSER, FileMailer->m_flags);
176838032Speter	}
176938032Speter
177038032Speter	st = stab("*include*", ST_MAILER, ST_FIND);
177138032Speter	if (st == NULL)
177238032Speter		syserr("No *include* mailer defined");
177338032Speter	else
177438032Speter		InclMailer = st->s_mailer;
177538032Speter
177638032Speter	if (ConfigLevel < 6)
177738032Speter	{
177838032Speter		/* heuristic tweaking of local mailer for back compat */
177938032Speter		if (LocalMailer != NULL)
178038032Speter		{
178138032Speter			setbitn(M_ALIASABLE, LocalMailer->m_flags);
178238032Speter			setbitn(M_HASPWENT, LocalMailer->m_flags);
178338032Speter			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
178438032Speter			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
178538032Speter			setbitn(M_CHECKPROG, LocalMailer->m_flags);
178638032Speter			setbitn(M_CHECKFILE, LocalMailer->m_flags);
178738032Speter			setbitn(M_CHECKUDB, LocalMailer->m_flags);
178838032Speter		}
178938032Speter		if (ProgMailer != NULL)
179038032Speter			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
179138032Speter		if (FileMailer != NULL)
179238032Speter			setbitn(M_RUNASRCPT, FileMailer->m_flags);
179338032Speter	}
179438032Speter	if (ConfigLevel < 7)
179538032Speter	{
179638032Speter		if (LocalMailer != NULL)
179738032Speter			setbitn(M_VRFY250, LocalMailer->m_flags);
179838032Speter		if (ProgMailer != NULL)
179938032Speter			setbitn(M_VRFY250, ProgMailer->m_flags);
180038032Speter		if (FileMailer != NULL)
180138032Speter			setbitn(M_VRFY250, FileMailer->m_flags);
180238032Speter	}
180338032Speter
180438032Speter	/* MIME Content-Types that cannot be transfer encoded */
180538032Speter	setclass('n', "multipart/signed");
180638032Speter
180738032Speter	/* MIME message/xxx subtypes that can be treated as messages */
180838032Speter	setclass('s', "rfc822");
180938032Speter
181038032Speter	/* MIME Content-Transfer-Encodings that can be encoded */
181138032Speter	setclass('e', "7bit");
181238032Speter	setclass('e', "8bit");
181338032Speter	setclass('e', "binary");
181438032Speter
181538032Speter#ifdef USE_B_CLASS
181638032Speter	/* MIME Content-Types that should be treated as binary */
181738032Speter	setclass('b', "image");
181838032Speter	setclass('b', "audio");
181938032Speter	setclass('b', "video");
182038032Speter	setclass('b', "application/octet-stream");
182164562Sgshapiro#endif /* USE_B_CLASS */
182238032Speter
182342575Speter	/* MIME headers which have fields to check for overflow */
182490792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
182590792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
182642575Speter
182742575Speter	/* MIME headers to check for length overflow */
182890792Sgshapiro	setclass(macid("{checkMIMETextHeaders}"), "content-description");
182942575Speter
183042575Speter	/* MIME headers to check for overflow and rebalance */
183190792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-disposition");
183290792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-id");
183390792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
183490792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-type");
183590792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "mime-version");
183642575Speter
183790792Sgshapiro	/* Macros to save in the queue file -- don't remove any */
183890792Sgshapiro	setclass(macid("{persistentMacros}"), "r");
183990792Sgshapiro	setclass(macid("{persistentMacros}"), "s");
184090792Sgshapiro	setclass(macid("{persistentMacros}"), "_");
184190792Sgshapiro	setclass(macid("{persistentMacros}"), "{if_addr}");
184290792Sgshapiro	setclass(macid("{persistentMacros}"), "{daemon_flags}");
184364562Sgshapiro
184438032Speter	/* operate in queue directory */
184590792Sgshapiro	if (QueueDir == NULL || *QueueDir == '\0')
184638032Speter	{
184738032Speter		if (OpMode != MD_TEST)
184838032Speter		{
184938032Speter			syserr("QueueDirectory (Q) option must be set");
185038032Speter			ExitStat = EX_CONFIG;
185138032Speter		}
185238032Speter	}
185338032Speter	else
185438032Speter	{
185564562Sgshapiro		if (OpMode != MD_TEST)
185690792Sgshapiro			setup_queues(OpMode == MD_DAEMON);
185738032Speter	}
185838032Speter
185938032Speter	/* check host status directory for validity */
186090792Sgshapiro	if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
186138032Speter	{
186238032Speter		/* cannot use this value */
186390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
186490792Sgshapiro				     "Warning: Cannot use HostStatusDirectory = %s: %s\n",
186590792Sgshapiro				     HostStatDir, sm_errstring(errno));
186638032Speter		HostStatDir = NULL;
186738032Speter	}
186838032Speter
186990792Sgshapiro	if (OpMode == MD_QUEUERUN &&
187090792Sgshapiro	    RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
187138032Speter	{
187238032Speter		struct stat stbuf;
187338032Speter
187438032Speter		/* check to see if we own the queue directory */
187538032Speter		if (stat(".", &stbuf) < 0)
187638032Speter			syserr("main: cannot stat %s", QueueDir);
187738032Speter		if (stbuf.st_uid != RealUid)
187838032Speter		{
187938032Speter			/* nope, really a botch */
188090792Sgshapiro			HoldErrs = false;
188138032Speter			usrerr("You do not have permission to process the queue");
188290792Sgshapiro			finis(false, true, EX_NOPERM);
188390792Sgshapiro			/* NOTREACHED */
188438032Speter		}
188538032Speter	}
188638032Speter
188790792Sgshapiro#if MILTER
188864562Sgshapiro	/* sanity checks on milter filters */
188964562Sgshapiro	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
189090792Sgshapiro	{
189190792Sgshapiro		milter_config(InputFilterList, InputFilters, MAXFILTERS);
189290792Sgshapiro# if _FFR_MILTER_PERDAEMON
189390792Sgshapiro		setup_daemon_milters();
189490792Sgshapiro# endif /* _FFR_MILTER_PERDAEMON */
189590792Sgshapiro	}
189690792Sgshapiro#endif /* MILTER */
189764562Sgshapiro
189890792Sgshapiro	/* Convert queuegroup string to qgrp number */
189990792Sgshapiro	if (queuegroup != NULL)
190090792Sgshapiro	{
190190792Sgshapiro		qgrp = name2qid(queuegroup);
190290792Sgshapiro		if (qgrp == NOQGRP)
190390792Sgshapiro		{
190490792Sgshapiro			HoldErrs = false;
190590792Sgshapiro			usrerr("Queue group %s unknown", queuegroup);
190690792Sgshapiro			finis(false, true, ExitStat);
190790792Sgshapiro			/* NOTREACHED */
190890792Sgshapiro		}
190990792Sgshapiro	}
191066494Sgshapiro
191138032Speter	/* if we've had errors so far, exit now */
191238032Speter	if (ExitStat != EX_OK && OpMode != MD_TEST)
191390792Sgshapiro	{
191490792Sgshapiro		finis(false, true, ExitStat);
191590792Sgshapiro		/* NOTREACHED */
191690792Sgshapiro	}
191738032Speter
191890792Sgshapiro#if SASL
191990792Sgshapiro	/* sendmail specific SASL initialization */
192090792Sgshapiro	sm_sasl_init();
192190792Sgshapiro#endif /* SASL */
192290792Sgshapiro
192338032Speter#if XDEBUG
192438032Speter	checkfd012("before main() initmaps");
192564562Sgshapiro#endif /* XDEBUG */
192638032Speter
192738032Speter	/*
192838032Speter	**  Do operation-mode-dependent initialization.
192938032Speter	*/
193038032Speter
193138032Speter	switch (OpMode)
193238032Speter	{
193338032Speter	  case MD_PRINT:
193438032Speter		/* print the queue */
193590792Sgshapiro		HoldErrs = false;
193690792Sgshapiro		dropenvelope(&BlankEnvelope, true, false);
193790792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
193890792Sgshapiro		if (qgrp != NOQGRP)
193990792Sgshapiro		{
194090792Sgshapiro			int j;
194190792Sgshapiro
194290792Sgshapiro			/* Selecting a particular queue group to run */
194390792Sgshapiro			for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
194490792Sgshapiro			{
194590792Sgshapiro				if (StopRequest)
194690792Sgshapiro					stop_sendmail();
194790792Sgshapiro				(void) print_single_queue(qgrp, j);
194890792Sgshapiro			}
194990792Sgshapiro			finis(false, true, EX_OK);
195090792Sgshapiro			/* NOTREACHED */
195190792Sgshapiro		}
195238032Speter		printqueue();
195390792Sgshapiro		finis(false, true, EX_OK);
195490792Sgshapiro		/* NOTREACHED */
195542575Speter		break;
195638032Speter
195790792Sgshapiro	  case MD_PRINTNQE:
195890792Sgshapiro		/* print number of entries in queue */
195990792Sgshapiro		dropenvelope(&BlankEnvelope, true, false);
196090792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
196190792Sgshapiro		printnqe(smioout, NULL);
196290792Sgshapiro		finis(false, true, EX_OK);
196390792Sgshapiro		/* NOTREACHED */
196490792Sgshapiro		break;
196590792Sgshapiro
196690792Sgshapiro#if _FFR_QUARANTINE
196790792Sgshapiro	  case MD_QUEUERUN:
196890792Sgshapiro		/* only handle quarantining here */
196990792Sgshapiro		if (quarantining == NULL)
197090792Sgshapiro			break;
197190792Sgshapiro
197290792Sgshapiro		if (QueueMode != QM_QUARANTINE &&
197390792Sgshapiro		    QueueMode != QM_NORMAL)
197490792Sgshapiro		{
197590792Sgshapiro			HoldErrs = false;
197690792Sgshapiro			usrerr("Can not use -Q with -q%c", QueueMode);
197790792Sgshapiro			ExitStat = EX_USAGE;
197890792Sgshapiro			finis(false, true, ExitStat);
197990792Sgshapiro			/* NOTREACHED */
198090792Sgshapiro		}
198190792Sgshapiro		quarantine_queue(quarantining, qgrp);
198290792Sgshapiro		finis(false, true, EX_OK);
198390792Sgshapiro		break;
198490792Sgshapiro#endif /* _FFR_QUARANTINE */
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)
203138032Speter				printmailer(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			{
217790792Sgshapiro				/*
217890792Sgshapiro				**  To run a specific queue group mark it to
217990792Sgshapiro				**  be run, select the work group it's in and
218090792Sgshapiro				**  increment the work counter.
218190792Sgshapiro				*/
218290792Sgshapiro
218394334Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
218494334Sgshapiro				     i++)
218594334Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
218694334Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
218790792Sgshapiro				(void) run_work_group(Queue[qgrp]->qg_wgrp,
218890792Sgshapiro						      false, Verbose,
218990792Sgshapiro						      queuepersistent, false);
219090792Sgshapiro			}
219190792Sgshapiro			else
219290792Sgshapiro				(void) runqueue(false, Verbose,
219390792Sgshapiro						queuepersistent, true);
219490792Sgshapiro
219590792Sgshapiro			/* set the title to make it easier to find */
219690792Sgshapiro			sm_setproctitle(true, CurEnv, "Queue control");
219790792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
219890792Sgshapiro			while (CurChildren > 0)
219990792Sgshapiro			{
220090792Sgshapiro				int status;
220190792Sgshapiro				pid_t ret;
220290792Sgshapiro
220390792Sgshapiro				while ((ret = sm_wait(&status)) <= 0)
220490792Sgshapiro					continue;
220590792Sgshapiro
220690792Sgshapiro				/* Only drop when a child gives status */
220790792Sgshapiro				if (WIFSTOPPED(status))
220890792Sgshapiro					continue;
220990792Sgshapiro
221090792Sgshapiro				proc_list_drop(ret, status, NULL);
221190792Sgshapiro			}
221264562Sgshapiro		}
221390792Sgshapiro		finis(true, true, ExitStat);
221490792Sgshapiro		/* NOTREACHED */
221538032Speter	}
221638032Speter
221771345Sgshapiro# if SASL
221871345Sgshapiro	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
221971345Sgshapiro	{
222090792Sgshapiro		/* check whether AUTH is turned off for the server */
222190792Sgshapiro		if (!chkdaemonmodifiers(D_NOAUTH) &&
222290792Sgshapiro		    (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
222371345Sgshapiro			syserr("!sasl_server_init failed! [%s]",
222490792Sgshapiro				sasl_errstring(i, NULL, NULL));
222571345Sgshapiro	}
222671345Sgshapiro# endif /* SASL */
222771345Sgshapiro
222890792Sgshapiro	if (OpMode == MD_SMTP)
222990792Sgshapiro	{
223090792Sgshapiro		proc_list_add(CurrentPid, "Sendmail SMTP Agent",
223190792Sgshapiro			      PROC_DAEMON, 0, -1);
223290792Sgshapiro
223390792Sgshapiro		/* clean up background delivery children */
223490792Sgshapiro		(void) sm_signal(SIGCHLD, reapchild);
223590792Sgshapiro	}
223690792Sgshapiro
223738032Speter	/*
223838032Speter	**  If a daemon, wait for a request.
223938032Speter	**	getrequests will always return in a child.
224038032Speter	**	If we should also be processing the queue, start
224138032Speter	**		doing it in background.
224238032Speter	**	We check for any errors that might have happened
224338032Speter	**		during startup.
224438032Speter	*/
224538032Speter
224638032Speter	if (OpMode == MD_DAEMON || QueueIntvl != 0)
224738032Speter	{
224838032Speter		char dtype[200];
224938032Speter
225038032Speter		if (!run_in_foreground && !tTd(99, 100))
225138032Speter		{
225238032Speter			/* put us in background */
225338032Speter			i = fork();
225438032Speter			if (i < 0)
225538032Speter				syserr("daemon: cannot fork");
225638032Speter			if (i != 0)
225790792Sgshapiro			{
225890792Sgshapiro				finis(false, true, EX_OK);
225990792Sgshapiro				/* NOTREACHED */
226090792Sgshapiro			}
226138032Speter
226290792Sgshapiro			/*
226390792Sgshapiro			**  Initialize exception stack and default exception
226490792Sgshapiro			**  handler for child process.
226590792Sgshapiro			*/
226690792Sgshapiro
226790792Sgshapiro			/* Reset global flags */
226890792Sgshapiro			RestartRequest = NULL;
226990792Sgshapiro			RestartWorkGroup = false;
227090792Sgshapiro			ShutdownRequest = NULL;
227190792Sgshapiro			PendingSignal = 0;
227290792Sgshapiro			CurrentPid = getpid();
227390792Sgshapiro
227490792Sgshapiro			sm_exc_newthread(fatal_error);
227590792Sgshapiro
227638032Speter			/* disconnect from our controlling tty */
227790792Sgshapiro			disconnect(2, &MainEnvelope);
227838032Speter		}
227938032Speter
228038032Speter		dtype[0] = '\0';
228138032Speter		if (OpMode == MD_DAEMON)
228290792Sgshapiro		{
228390792Sgshapiro			(void) sm_strlcat(dtype, "+SMTP", sizeof dtype);
228490792Sgshapiro			DaemonPid = CurrentPid;
228590792Sgshapiro		}
228638032Speter		if (QueueIntvl != 0)
228738032Speter		{
228890792Sgshapiro			(void) sm_strlcat2(dtype,
228990792Sgshapiro					   queuepersistent
229090792Sgshapiro					   ? "+persistent-queueing@"
229190792Sgshapiro					   : "+queueing@",
229290792Sgshapiro					   pintvl(QueueIntvl, true),
229390792Sgshapiro					   sizeof dtype);
229438032Speter		}
229538032Speter		if (tTd(0, 1))
229690792Sgshapiro			(void) sm_strlcat(dtype, "+debugging", sizeof dtype);
229738032Speter
229838032Speter		sm_syslog(LOG_INFO, NOQID,
229964562Sgshapiro			  "starting daemon (%s): %s", Version, dtype + 1);
230090792Sgshapiro#if XLA
230138032Speter		xla_create_file();
230264562Sgshapiro#endif /* XLA */
230338032Speter
230464562Sgshapiro		/* save daemon type in a macro for possible PidFile use */
230590792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
230690792Sgshapiro			macid("{daemon_info}"), dtype + 1);
230764562Sgshapiro
230864562Sgshapiro		/* save queue interval in a macro for possible PidFile use */
230990792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
231090792Sgshapiro			macid("{queue_interval}"), pintvl(QueueIntvl, true));
231164562Sgshapiro
231290792Sgshapiro		/* workaround: can't seem to release the signal in the parent */
231390792Sgshapiro		(void) sm_signal(SIGHUP, sighup);
231490792Sgshapiro		(void) sm_releasesignal(SIGHUP);
231590792Sgshapiro		(void) sm_signal(SIGTERM, sigterm);
231690792Sgshapiro
231764562Sgshapiro		if (QueueIntvl != 0)
231838032Speter		{
231990792Sgshapiro			(void) runqueue(true, false, queuepersistent, true);
232090792Sgshapiro
232190792Sgshapiro			/*
232290792Sgshapiro			**  If queuepersistent but not in daemon mode then
232390792Sgshapiro			**  we're going to do the queue runner monitoring here.
232490792Sgshapiro			**  If in daemon mode then the monitoring will happen
232590792Sgshapiro			**  elsewhere.
232690792Sgshapiro			*/
232790792Sgshapiro
232890792Sgshapiro			if (OpMode != MD_DAEMON && queuepersistent)
232990792Sgshapiro			{
233090792Sgshapiro				/* set the title to make it easier to find */
233190792Sgshapiro				sm_setproctitle(true, CurEnv, "Queue control");
233290792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
233390792Sgshapiro				while (CurChildren > 0)
233490792Sgshapiro				{
233590792Sgshapiro					int status;
233690792Sgshapiro					pid_t ret;
233790792Sgshapiro					int group;
233890792Sgshapiro
233990792Sgshapiro					if (ShutdownRequest != NULL)
234090792Sgshapiro						shutdown_daemon();
234190792Sgshapiro					else if (RestartRequest != NULL)
234290792Sgshapiro						restart_daemon();
234390792Sgshapiro					else if (RestartWorkGroup)
234490792Sgshapiro						restart_marked_work_groups();
234590792Sgshapiro
234690792Sgshapiro					while ((ret = sm_wait(&status)) <= 0)
234790792Sgshapiro						continue;
234890792Sgshapiro
234990792Sgshapiro					if (WIFSTOPPED(status))
235090792Sgshapiro						continue;
235190792Sgshapiro
235290792Sgshapiro					/* Probe only on a child status */
235390792Sgshapiro					proc_list_drop(ret, status, &group);
235490792Sgshapiro
235590792Sgshapiro					if (WIFSIGNALED(status))
235690792Sgshapiro					{
235790792Sgshapiro						if (WCOREDUMP(status))
235890792Sgshapiro						{
235990792Sgshapiro							sm_syslog(LOG_ERR, NOQID,
236090792Sgshapiro								  "persistent queue runner=%d core dumped, signal=%d",
236190792Sgshapiro								  group, WTERMSIG(status));
236290792Sgshapiro
236390792Sgshapiro							/* don't restart this one */
236490792Sgshapiro							mark_work_group_restart(group, -1);
236590792Sgshapiro							continue;
236690792Sgshapiro						}
236790792Sgshapiro
236890792Sgshapiro						sm_syslog(LOG_ERR, NOQID,
236990792Sgshapiro							  "persistent queue runner=%d died, signal=%d",
237090792Sgshapiro							  group, WTERMSIG(status));
237190792Sgshapiro					}
237290792Sgshapiro
237390792Sgshapiro					/*
237490792Sgshapiro					**  When debugging active, don't
237590792Sgshapiro					**  restart the persistent queues.
237690792Sgshapiro					**  But do log this as info.
237790792Sgshapiro					*/
237890792Sgshapiro
237990792Sgshapiro					if (sm_debug_active(&DebugNoPRestart,
238090792Sgshapiro							    1))
238190792Sgshapiro					{
238290792Sgshapiro						sm_syslog(LOG_DEBUG, NOQID,
238390792Sgshapiro							  "persistent queue runner=%d, exited",
238490792Sgshapiro							  group);
238590792Sgshapiro						mark_work_group_restart(group, -1);
238690792Sgshapiro					}
238790792Sgshapiro				}
238890792Sgshapiro				finis(true, true, ExitStat);
238990792Sgshapiro				/* NOTREACHED */
239090792Sgshapiro			}
239190792Sgshapiro
239238032Speter			if (OpMode != MD_DAEMON)
239338032Speter			{
239490792Sgshapiro				char qtype[200];
239590792Sgshapiro
239690792Sgshapiro				/*
239790792Sgshapiro				**  Write the pid to file
239890792Sgshapiro				**  XXX Overwrites sendmail.pid
239990792Sgshapiro				*/
240090792Sgshapiro
240190792Sgshapiro				log_sendmail_pid(&MainEnvelope);
240290792Sgshapiro
240390792Sgshapiro				/* set the title to make it easier to find */
240490792Sgshapiro				qtype[0] = '\0';
240590792Sgshapiro				(void) sm_strlcpyn(qtype, sizeof qtype, 4,
240690792Sgshapiro						   "Queue runner@",
240790792Sgshapiro						   pintvl(QueueIntvl, true),
240890792Sgshapiro						   " for ",
240990792Sgshapiro						   QueueDir);
241090792Sgshapiro				sm_setproctitle(true, CurEnv, qtype);
241138032Speter				for (;;)
241238032Speter				{
241364562Sgshapiro					(void) pause();
241477349Sgshapiro					if (ShutdownRequest != NULL)
241577349Sgshapiro						shutdown_daemon();
241690792Sgshapiro					else if (RestartRequest != NULL)
241790792Sgshapiro						restart_daemon();
241890792Sgshapiro					else if (RestartWorkGroup)
241990792Sgshapiro						restart_marked_work_groups();
242090792Sgshapiro
242190792Sgshapiro					if (doqueuerun())
242290792Sgshapiro						(void) runqueue(true, false,
242390792Sgshapiro								false, false);
242438032Speter				}
242538032Speter			}
242638032Speter		}
242790792Sgshapiro		dropenvelope(&MainEnvelope, true, false);
242838032Speter
242990792Sgshapiro#if STARTTLS
243064562Sgshapiro		/* init TLS for server, ignore result for now */
243190792Sgshapiro		(void) initsrvtls(tls_ok);
243290792Sgshapiro#endif /* STARTTLS */
243390792Sgshapiro#if PROFILING
243490792Sgshapiro	nextreq:
243590792Sgshapiro#endif /* PROFILING */
243690792Sgshapiro		p_flags = getrequests(&MainEnvelope);
243738032Speter
243838032Speter		/* drop privileges */
243990792Sgshapiro		(void) drop_privileges(false);
244038032Speter
244138032Speter		/*
244238032Speter		**  Get authentication data
244390792Sgshapiro		**  Set _ macro in BlankEnvelope before calling newenvelope().
244438032Speter		*/
244538032Speter
244690792Sgshapiro		authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
244790792Sgshapiro						     NULL), &forged);
244890792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
244990792Sgshapiro
245090792Sgshapiro		/* at this point we are in a child: reset state */
245190792Sgshapiro		sm_rpool_free(MainEnvelope.e_rpool);
245290792Sgshapiro		(void) newenvelope(&MainEnvelope, &MainEnvelope,
245390792Sgshapiro				   sm_rpool_new_x(NULL));
245438032Speter	}
245538032Speter
245664562Sgshapiro	if (LogLevel > 9)
245764562Sgshapiro	{
245864562Sgshapiro		/* log connection information */
245964562Sgshapiro		sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo);
246064562Sgshapiro	}
246164562Sgshapiro
246238032Speter	/*
246338032Speter	**  If running SMTP protocol, start collecting and executing
246438032Speter	**  commands.  This will never return.
246538032Speter	*/
246638032Speter
246738032Speter	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
246838032Speter	{
246938032Speter		char pbuf[20];
247038032Speter
247138032Speter		/*
247238032Speter		**  Save some macros for check_* rulesets.
247338032Speter		*/
247438032Speter
247538032Speter		if (forged)
247638032Speter		{
247738032Speter			char ipbuf[103];
247838032Speter
247990792Sgshapiro			(void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
248090792Sgshapiro					   anynet_ntoa(&RealHostAddr));
248190792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
248290792Sgshapiro				  macid("{client_name}"), ipbuf);
248338032Speter		}
248438032Speter		else
248590792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
248690792Sgshapiro				  macid("{client_name}"), RealHostName);
248790792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
248890792Sgshapiro			  macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
248990792Sgshapiro		sm_getla();
249038032Speter
249190792Sgshapiro		switch (RealHostAddr.sa.sa_family)
249264562Sgshapiro		{
249390792Sgshapiro#if NETINET
249464562Sgshapiro		  case AF_INET:
249590792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "%d",
249690792Sgshapiro					   RealHostAddr.sin.sin_port);
249764562Sgshapiro			break;
249890792Sgshapiro#endif /* NETINET */
249990792Sgshapiro#if NETINET6
250064562Sgshapiro		  case AF_INET6:
250190792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "%d",
250290792Sgshapiro					   RealHostAddr.sin6.sin6_port);
250364562Sgshapiro			break;
250490792Sgshapiro#endif /* NETINET6 */
250564562Sgshapiro		  default:
250690792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "0");
250764562Sgshapiro			break;
250864562Sgshapiro		}
250990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
251090792Sgshapiro			macid("{client_port}"), pbuf);
251142575Speter
251238032Speter		if (OpMode == MD_DAEMON)
251338032Speter		{
251438032Speter			/* validate the connection */
251590792Sgshapiro			HoldErrs = true;
251638032Speter			nullserver = validate_connection(&RealHostAddr,
251790792Sgshapiro							 RealHostName,
251890792Sgshapiro							 &MainEnvelope);
251990792Sgshapiro			HoldErrs = false;
252038032Speter		}
252164562Sgshapiro		else if (p_flags == NULL)
252264562Sgshapiro		{
252364562Sgshapiro			p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
252464562Sgshapiro			clrbitmap(p_flags);
252564562Sgshapiro		}
252690792Sgshapiro#if STARTTLS
252764562Sgshapiro		if (OpMode == MD_SMTP)
252890792Sgshapiro			(void) initsrvtls(tls_ok);
252990792Sgshapiro#endif /* STARTTLS */
253071345Sgshapiro
253190792Sgshapiro		/* turn off profiling */
253290792Sgshapiro		SM_PROF(1);
253390792Sgshapiro		smtp(nullserver, *p_flags, &MainEnvelope);
253490792Sgshapiro#if PROFILING
253590792Sgshapiro		/* turn off profiling */
253690792Sgshapiro		SM_PROF(0);
253790792Sgshapiro		if (OpMode == MD_DAEMON)
253890792Sgshapiro			goto nextreq;
253990792Sgshapiro#endif /* PROFILING */
254038032Speter	}
254138032Speter
254290792Sgshapiro	sm_rpool_free(MainEnvelope.e_rpool);
254390792Sgshapiro	clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
254438032Speter	if (OpMode == MD_VERIFY)
254538032Speter	{
254690792Sgshapiro		set_delivery_mode(SM_VERIFY, &MainEnvelope);
254738032Speter		PostMasterCopy = NULL;
254838032Speter	}
254938032Speter	else
255038032Speter	{
255138032Speter		/* interactive -- all errors are global */
255290792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
255338032Speter	}
255438032Speter
255538032Speter	/*
255638032Speter	**  Do basic system initialization and set the sender
255738032Speter	*/
255838032Speter
255990792Sgshapiro	initsys(&MainEnvelope);
256090792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
256190792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
256290792Sgshapiro	setsender(from, &MainEnvelope, NULL, '\0', false);
256364562Sgshapiro	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
256490792Sgshapiro	    (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
256590792Sgshapiro	     strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
256664562Sgshapiro	{
256790792Sgshapiro		auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
256890792Sgshapiro			     RealUserName, from, warn_f_flag);
256964562Sgshapiro#if SASL
257090792Sgshapiro		auth = false;
257164562Sgshapiro#endif /* SASL */
257264562Sgshapiro	}
257364562Sgshapiro	if (auth)
257464562Sgshapiro	{
257564562Sgshapiro		char *fv;
257664562Sgshapiro
257764562Sgshapiro		/* set the initial sender for AUTH= to $f@$j */
257890792Sgshapiro		fv = macvalue('f', &MainEnvelope);
257964562Sgshapiro		if (fv == NULL || *fv == '\0')
258090792Sgshapiro			MainEnvelope.e_auth_param = NULL;
258164562Sgshapiro		else
258264562Sgshapiro		{
258364562Sgshapiro			if (strchr(fv, '@') == NULL)
258464562Sgshapiro			{
258590792Sgshapiro				i = strlen(fv) + strlen(macvalue('j',
258690792Sgshapiro							&MainEnvelope)) + 2;
258790792Sgshapiro				p = sm_malloc_x(i);
258890792Sgshapiro				(void) sm_strlcpyn(p, i, 3, fv, "@",
258990792Sgshapiro						   macvalue('j',
259090792Sgshapiro							    &MainEnvelope));
259164562Sgshapiro			}
259264562Sgshapiro			else
259390792Sgshapiro				p = sm_strdup_x(fv);
259490792Sgshapiro			MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
259590792Sgshapiro								      xtextify(p, "="));
259690792Sgshapiro			sm_free(p);  /* XXX */
259764562Sgshapiro		}
259864562Sgshapiro	}
259990792Sgshapiro	if (macvalue('s', &MainEnvelope) == NULL)
260090792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
260138032Speter
260290792Sgshapiro	av = argv + optind;
260338032Speter	if (*av == NULL && !GrabTo)
260438032Speter	{
260590792Sgshapiro		MainEnvelope.e_to = NULL;
260690792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
260790792Sgshapiro		HoldErrs = false;
260890792Sgshapiro		SuperSafe = SAFE_NO;
260938032Speter		usrerr("Recipient names must be specified");
261038032Speter
261138032Speter		/* collect body for UUCP return */
261238032Speter		if (OpMode != MD_VERIFY)
261390792Sgshapiro			collect(InChannel, false, NULL, &MainEnvelope);
261490792Sgshapiro		finis(true, true, EX_USAGE);
261590792Sgshapiro		/* NOTREACHED */
261638032Speter	}
261738032Speter
261838032Speter	/*
261938032Speter	**  Scan argv and deliver the message to everyone.
262038032Speter	*/
262138032Speter
262290792Sgshapiro	save_val = LogUsrErrs;
262390792Sgshapiro	LogUsrErrs = true;
262490792Sgshapiro	sendtoargv(av, &MainEnvelope);
262590792Sgshapiro	LogUsrErrs = save_val;
262638032Speter
262738032Speter	/* if we have had errors sofar, arrange a meaningful exit stat */
262838032Speter	if (Errors > 0 && ExitStat == EX_OK)
262938032Speter		ExitStat = EX_USAGE;
263038032Speter
263138032Speter#if _FFR_FIX_DASHT
263238032Speter	/*
263338032Speter	**  If using -t, force not sending to argv recipients, even
263438032Speter	**  if they are mentioned in the headers.
263538032Speter	*/
263638032Speter
263738032Speter	if (GrabTo)
263838032Speter	{
263938032Speter		ADDRESS *q;
264064562Sgshapiro
264190792Sgshapiro		for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
264264562Sgshapiro			q->q_state = QS_REMOVED;
264338032Speter	}
264464562Sgshapiro#endif /* _FFR_FIX_DASHT */
264538032Speter
264638032Speter	/*
264738032Speter	**  Read the input mail.
264838032Speter	*/
264938032Speter
265090792Sgshapiro	MainEnvelope.e_to = NULL;
265138032Speter	if (OpMode != MD_VERIFY || GrabTo)
265238032Speter	{
265390792Sgshapiro		int savederrors;
265490792Sgshapiro		unsigned long savedflags;
265538032Speter
265690792Sgshapiro		/*
265790792Sgshapiro		**  workaround for compiler warning on Irix:
265890792Sgshapiro		**  do not initialize variable in the definition, but
265990792Sgshapiro		**  later on:
266090792Sgshapiro		**  warning(1548): transfer of control bypasses
266190792Sgshapiro		**  initialization of:
266290792Sgshapiro		**  variable "savederrors" (declared at line 2570)
266390792Sgshapiro		**  variable "savedflags" (declared at line 2571)
266490792Sgshapiro		**  goto giveup;
266590792Sgshapiro		*/
266690792Sgshapiro
266790792Sgshapiro		savederrors = Errors;
266890792Sgshapiro		savedflags = MainEnvelope.e_flags & EF_FATALERRS;
266990792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
267090792Sgshapiro		MainEnvelope.e_flags &= ~EF_FATALERRS;
267164562Sgshapiro		Errors = 0;
267264562Sgshapiro		buffer_errors();
267390792Sgshapiro		collect(InChannel, false, NULL, &MainEnvelope);
267438032Speter
267564562Sgshapiro		/* header checks failed */
267664562Sgshapiro		if (Errors > 0)
267764562Sgshapiro		{
267890792Sgshapiro  giveup:
267990792Sgshapiro			if (!GrabTo)
268064562Sgshapiro			{
268190792Sgshapiro				/* Log who the mail would have gone to */
268290792Sgshapiro				logundelrcpts(&MainEnvelope,
268390792Sgshapiro					      MainEnvelope.e_message,
268490792Sgshapiro					      8, false);
268564562Sgshapiro			}
268690792Sgshapiro			flush_errors(true);
268790792Sgshapiro			finis(true, true, ExitStat);
268864562Sgshapiro			/* NOTREACHED */
268964562Sgshapiro			return -1;
269064562Sgshapiro		}
269164562Sgshapiro
269238032Speter		/* bail out if message too large */
269390792Sgshapiro		if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
269438032Speter		{
269590792Sgshapiro			finis(true, true, ExitStat != EX_OK ? ExitStat
269690792Sgshapiro							    : EX_DATAERR);
269764562Sgshapiro			/* NOTREACHED */
269838032Speter			return -1;
269938032Speter		}
270098121Sgshapiro
270198121Sgshapiro		/* set message size */
270298121Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%ld",
270398121Sgshapiro				   MainEnvelope.e_msgsize);
270498121Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
270598121Sgshapiro			  macid("{msg_size}"), buf);
270698121Sgshapiro
270764562Sgshapiro		Errors = savederrors;
270890792Sgshapiro		MainEnvelope.e_flags |= savedflags;
270938032Speter	}
271038032Speter	errno = 0;
271138032Speter
271238032Speter	if (tTd(1, 1))
271390792Sgshapiro		sm_dprintf("From person = \"%s\"\n",
271490792Sgshapiro			   MainEnvelope.e_from.q_paddr);
271538032Speter
271690792Sgshapiro#if _FFR_QUARANTINE
271790792Sgshapiro	/* Check if quarantining stats should be updated */
271890792Sgshapiro	if (MainEnvelope.e_quarmsg != NULL)
271990792Sgshapiro		markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
272090792Sgshapiro#endif /* _FFR_QUARANTINE */
272190792Sgshapiro
272238032Speter	/*
272338032Speter	**  Actually send everything.
272438032Speter	**	If verifying, just ack.
272538032Speter	*/
272638032Speter
272790792Sgshapiro	if (Errors == 0)
272838032Speter	{
272990792Sgshapiro		if (!split_by_recipient(&MainEnvelope) &&
273090792Sgshapiro		    bitset(EF_FATALERRS, MainEnvelope.e_flags))
273190792Sgshapiro			goto giveup;
273238032Speter	}
273390792Sgshapiro
273490792Sgshapiro	/* make sure we deliver at least the first envelope */
273590792Sgshapiro	i = FastSplit > 0 ? 0 : -1;
273690792Sgshapiro	for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
273790792Sgshapiro	{
273890792Sgshapiro		ENVELOPE *next;
273990792Sgshapiro
274090792Sgshapiro		e->e_from.q_state = QS_SENDER;
274190792Sgshapiro		if (tTd(1, 5))
274290792Sgshapiro		{
274390792Sgshapiro			sm_dprintf("main[%d]: QS_SENDER ", i);
274490792Sgshapiro			printaddr(&e->e_from, false);
274590792Sgshapiro		}
274690792Sgshapiro		e->e_to = NULL;
274790792Sgshapiro		sm_getla();
274890792Sgshapiro		GrabTo = false;
274964562Sgshapiro#if NAMED_BIND
275090792Sgshapiro		_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
275190792Sgshapiro		_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
275264562Sgshapiro#endif /* NAMED_BIND */
275390792Sgshapiro		next = e->e_sibling;
275490792Sgshapiro		e->e_sibling = NULL;
275538032Speter
275690792Sgshapiro		/* after FastSplit envelopes: queue up */
275790792Sgshapiro		sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
275890792Sgshapiro		e->e_sibling = next;
275990792Sgshapiro	}
276090792Sgshapiro
276138032Speter	/*
276238032Speter	**  All done.
276338032Speter	**	Don't send return error message if in VERIFY mode.
276438032Speter	*/
276538032Speter
276690792Sgshapiro	finis(true, true, ExitStat);
276764562Sgshapiro	/* NOTREACHED */
276864562Sgshapiro	return ExitStat;
276938032Speter}
277090792Sgshapiro/*
277177349Sgshapiro**  STOP_SENDMAIL -- Stop the running program
277277349Sgshapiro**
277377349Sgshapiro**	Parameters:
277477349Sgshapiro**		none.
277577349Sgshapiro**
277677349Sgshapiro**	Returns:
277777349Sgshapiro**		none.
277877349Sgshapiro**
277977349Sgshapiro**	Side Effects:
278077349Sgshapiro**		exits.
278177349Sgshapiro*/
278238032Speter
278377349Sgshapirovoid
278477349Sgshapirostop_sendmail()
278577349Sgshapiro{
278677349Sgshapiro	/* reset uid for process accounting */
278777349Sgshapiro	endpwent();
278877349Sgshapiro	(void) setuid(RealUid);
278977349Sgshapiro	exit(EX_OK);
279077349Sgshapiro}
279190792Sgshapiro/*
279238032Speter**  FINIS -- Clean up and exit.
279338032Speter**
279438032Speter**	Parameters:
279542575Speter**		drop -- whether or not to drop CurEnv envelope
279690792Sgshapiro**		cleanup -- call exit() or _exit()?
279742575Speter**		exitstat -- exit status to use for exit() call
279838032Speter**
279938032Speter**	Returns:
280038032Speter**		never
280138032Speter**
280238032Speter**	Side Effects:
280338032Speter**		exits sendmail
280438032Speter*/
280538032Speter
280638032Spetervoid
280790792Sgshapirofinis(drop, cleanup, exitstat)
280842575Speter	bool drop;
280990792Sgshapiro	bool cleanup;
281042575Speter	volatile int exitstat;
281138032Speter{
281298121Sgshapiro
281377349Sgshapiro	/* Still want to process new timeouts added below */
281490792Sgshapiro	sm_clear_events();
281590792Sgshapiro	(void) sm_releasesignal(SIGALRM);
281642575Speter
281738032Speter	if (tTd(2, 1))
281838032Speter	{
281990792Sgshapiro		sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
282090792Sgshapiro			   exitstat,
282190792Sgshapiro			   CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
282238032Speter		printenvflags(CurEnv);
282338032Speter	}
282438032Speter	if (tTd(2, 9))
282590792Sgshapiro		printopenfds(false);
282638032Speter
282790792Sgshapiro	SM_TRY
282890792Sgshapiro		/*
282990792Sgshapiro		**  Clean up.  This might raise E:mta.quickabort
283090792Sgshapiro		*/
283138032Speter
283290792Sgshapiro		/* clean up temp files */
283390792Sgshapiro		CurEnv->e_to = NULL;
283490792Sgshapiro		if (drop)
283590792Sgshapiro		{
283690792Sgshapiro			if (CurEnv->e_id != NULL)
283790792Sgshapiro			{
283890792Sgshapiro				dropenvelope(CurEnv, true, false);
283990792Sgshapiro				sm_rpool_free(CurEnv->e_rpool);
284090792Sgshapiro				CurEnv->e_rpool = NULL;
284190792Sgshapiro			}
284290792Sgshapiro			else
284390792Sgshapiro				poststats(StatFile);
284490792Sgshapiro		}
284538032Speter
284690792Sgshapiro		/* flush any cached connections */
284790792Sgshapiro		mci_flush(true, NULL);
284838032Speter
284990792Sgshapiro		/* close maps belonging to this pid */
285090792Sgshapiro		closemaps(false);
285142575Speter
285264562Sgshapiro#if USERDB
285390792Sgshapiro		/* close UserDatabase */
285490792Sgshapiro		_udbx_close();
285564562Sgshapiro#endif /* USERDB */
285642575Speter
285790792Sgshapiro#if SASL
285890792Sgshapiro		stop_sasl_client();
285990792Sgshapiro#endif /* SASL */
286090792Sgshapiro
286190792Sgshapiro#if XLA
286290792Sgshapiro		/* clean up extended load average stuff */
286390792Sgshapiro		xla_all_end();
286464562Sgshapiro#endif /* XLA */
286538032Speter
286690792Sgshapiro	SM_FINALLY
286790792Sgshapiro		/*
286890792Sgshapiro		**  And exit.
286990792Sgshapiro		*/
287038032Speter
287190792Sgshapiro		if (LogLevel > 78)
287290792Sgshapiro			sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
287390792Sgshapiro				  (int) CurrentPid);
287490792Sgshapiro		if (exitstat == EX_TEMPFAIL ||
287590792Sgshapiro		    CurEnv->e_errormode == EM_BERKNET)
287690792Sgshapiro			exitstat = EX_OK;
287764562Sgshapiro
287890792Sgshapiro		/* XXX clean up queues and related data structures */
287990792Sgshapiro		cleanup_queues();
288090792Sgshapiro#if SM_CONF_SHM
288190792Sgshapiro		cleanup_shm(DaemonPid == getpid());
288290792Sgshapiro#endif /* SM_CONF_SHM */
288390792Sgshapiro
288490792Sgshapiro		/* reset uid for process accounting */
288590792Sgshapiro		endpwent();
288690792Sgshapiro		sm_mbdb_terminate();
288790792Sgshapiro		(void) setuid(RealUid);
288890792Sgshapiro#if SM_HEAP_CHECK
288990792Sgshapiro		/* dump the heap, if we are checking for memory leaks */
289090792Sgshapiro		if (sm_debug_active(&SmHeapCheck, 2))
289190792Sgshapiro			sm_heap_report(smioout,
289290792Sgshapiro				       sm_debug_level(&SmHeapCheck) - 1);
289390792Sgshapiro#endif /* SM_HEAP_CHECK */
289490792Sgshapiro		if (sm_debug_active(&SmXtrapReport, 1))
289590792Sgshapiro			sm_dprintf("xtrap count = %d\n", SmXtrapCount);
289690792Sgshapiro		if (cleanup)
289790792Sgshapiro			exit(exitstat);
289890792Sgshapiro		else
289990792Sgshapiro			_exit(exitstat);
290090792Sgshapiro	SM_END_TRY
290138032Speter}
290290792Sgshapiro/*
290390792Sgshapiro**  INTINDEBUG -- signal handler for SIGINT in -bt mode
290477349Sgshapiro**
290577349Sgshapiro**	Parameters:
290690792Sgshapiro**		sig -- incoming signal.
290790792Sgshapiro**
290890792Sgshapiro**	Returns:
290990792Sgshapiro**		none.
291090792Sgshapiro**
291190792Sgshapiro**	Side Effects:
291290792Sgshapiro**		longjmps back to test mode loop.
291390792Sgshapiro**
291490792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
291590792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
291690792Sgshapiro**		DOING.
291790792Sgshapiro*/
291890792Sgshapiro
291990792Sgshapiro/* Type of an exception generated on SIGINT during address test mode.  */
292090792Sgshapirostatic const SM_EXC_TYPE_T EtypeInterrupt =
292190792Sgshapiro{
292290792Sgshapiro	SmExcTypeMagic,
292390792Sgshapiro	"S:mta.interrupt",
292490792Sgshapiro	"",
292590792Sgshapiro	sm_etype_printf,
292690792Sgshapiro	"interrupt",
292790792Sgshapiro};
292890792Sgshapiro
292990792Sgshapiro/* ARGSUSED */
293090792Sgshapirostatic SIGFUNC_DECL
293190792Sgshapirointindebug(sig)
293290792Sgshapiro	int sig;
293390792Sgshapiro{
293490792Sgshapiro	int save_errno = errno;
293590792Sgshapiro
293690792Sgshapiro	FIX_SYSV_SIGNAL(sig, intindebug);
293790792Sgshapiro	errno = save_errno;
293890792Sgshapiro	CHECK_CRITICAL(sig);
293990792Sgshapiro	errno = save_errno;
294090792Sgshapiro	sm_exc_raisenew_x(&EtypeInterrupt);
294190792Sgshapiro	errno = save_errno;
294290792Sgshapiro	return SIGFUNC_RETURN;
294390792Sgshapiro}
294490792Sgshapiro/*
294590792Sgshapiro**  SIGTERM -- SIGTERM handler for the daemon
294690792Sgshapiro**
294790792Sgshapiro**	Parameters:
294877349Sgshapiro**		sig -- signal number.
294977349Sgshapiro**
295077349Sgshapiro**	Returns:
295177349Sgshapiro**		none.
295277349Sgshapiro**
295377349Sgshapiro**	Side Effects:
295477349Sgshapiro**		Sets ShutdownRequest which will hopefully trigger
295577349Sgshapiro**		the daemon to exit.
295677349Sgshapiro**
295777349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
295877349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
295977349Sgshapiro**		DOING.
296077349Sgshapiro*/
296177349Sgshapiro
296277349Sgshapiro/* ARGSUSED */
296377349Sgshapirostatic SIGFUNC_DECL
296490792Sgshapirosigterm(sig)
296577349Sgshapiro	int sig;
296677349Sgshapiro{
296777349Sgshapiro	int save_errno = errno;
296877349Sgshapiro
296990792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigterm);
297077349Sgshapiro	ShutdownRequest = "signal";
297177349Sgshapiro	errno = save_errno;
297277349Sgshapiro	return SIGFUNC_RETURN;
297377349Sgshapiro}
297490792Sgshapiro/*
297590792Sgshapiro**  SIGHUP -- handle a SIGHUP signal
297677349Sgshapiro**
297777349Sgshapiro**	Parameters:
297890792Sgshapiro**		sig -- incoming signal.
297977349Sgshapiro**
298077349Sgshapiro**	Returns:
298177349Sgshapiro**		none.
298277349Sgshapiro**
298377349Sgshapiro**	Side Effects:
298490792Sgshapiro**		Sets RestartRequest which should cause the daemon
298590792Sgshapiro**		to restart.
298690792Sgshapiro**
298790792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
298890792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
298990792Sgshapiro**		DOING.
299077349Sgshapiro*/
299177349Sgshapiro
299290792Sgshapiro/* ARGSUSED */
299390792Sgshapirostatic SIGFUNC_DECL
299490792Sgshapirosighup(sig)
299590792Sgshapiro	int sig;
299677349Sgshapiro{
299790792Sgshapiro	int save_errno = errno;
299877349Sgshapiro
299990792Sgshapiro	FIX_SYSV_SIGNAL(sig, sighup);
300090792Sgshapiro	RestartRequest = "signal";
300190792Sgshapiro	errno = save_errno;
300290792Sgshapiro	return SIGFUNC_RETURN;
300390792Sgshapiro}
300490792Sgshapiro/*
300590792Sgshapiro**  SIGPIPE -- signal handler for SIGPIPE
300690792Sgshapiro**
300790792Sgshapiro**	Parameters:
300890792Sgshapiro**		sig -- incoming signal.
300990792Sgshapiro**
301090792Sgshapiro**	Returns:
301190792Sgshapiro**		none.
301290792Sgshapiro**
301390792Sgshapiro**	Side Effects:
301490792Sgshapiro**		Sets StopRequest which should cause the mailq/hoststatus
301590792Sgshapiro**		display to stop.
301690792Sgshapiro**
301790792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
301890792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
301990792Sgshapiro**		DOING.
302090792Sgshapiro*/
302177349Sgshapiro
302290792Sgshapiro/* ARGSUSED */
302390792Sgshapirostatic SIGFUNC_DECL
302490792Sgshapirosigpipe(sig)
302590792Sgshapiro	int sig;
302690792Sgshapiro{
302790792Sgshapiro	int save_errno = errno;
302877349Sgshapiro
302990792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigpipe);
303090792Sgshapiro	StopRequest = true;
303190792Sgshapiro	errno = save_errno;
303290792Sgshapiro	return SIGFUNC_RETURN;
303377349Sgshapiro}
303490792Sgshapiro/*
303538032Speter**  INTSIG -- clean up on interrupt
303638032Speter**
303764562Sgshapiro**	This just arranges to exit.  It pessimizes in that it
303838032Speter**	may resend a message.
303938032Speter**
304038032Speter**	Parameters:
304138032Speter**		none.
304238032Speter**
304338032Speter**	Returns:
304438032Speter**		none.
304538032Speter**
304638032Speter**	Side Effects:
304738032Speter**		Unlocks the current job.
304877349Sgshapiro**
304977349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
305077349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
305177349Sgshapiro**		DOING.
305277349Sgshapiro**
305377349Sgshapiro**		XXX: More work is needed for this signal handler.
305438032Speter*/
305538032Speter
305638032Speter/* ARGSUSED */
305738032SpeterSIGFUNC_DECL
305838032Speterintsig(sig)
305938032Speter	int sig;
306038032Speter{
306190792Sgshapiro	bool drop = false;
306277349Sgshapiro	int save_errno = errno;
306364562Sgshapiro
306477349Sgshapiro	FIX_SYSV_SIGNAL(sig, intsig);
306577349Sgshapiro	errno = save_errno;
306677349Sgshapiro	CHECK_CRITICAL(sig);
306790792Sgshapiro	sm_allsignals(true);
306890792Sgshapiro
306964562Sgshapiro	if (sig != 0 && LogLevel > 79)
307038032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
307138032Speter	FileName = NULL;
307264562Sgshapiro
307364562Sgshapiro	/* Clean-up on aborted stdin message submission */
307464562Sgshapiro	if (CurEnv->e_id != NULL &&
307564562Sgshapiro	    (OpMode == MD_SMTP ||
307664562Sgshapiro	     OpMode == MD_DELIVER ||
307764562Sgshapiro	     OpMode == MD_ARPAFTP))
307864562Sgshapiro	{
307964562Sgshapiro		register ADDRESS *q;
308064562Sgshapiro
308164562Sgshapiro		/* don't return an error indication */
308264562Sgshapiro		CurEnv->e_to = NULL;
308364562Sgshapiro		CurEnv->e_flags &= ~EF_FATALERRS;
308464562Sgshapiro		CurEnv->e_flags |= EF_CLRQUEUE;
308564562Sgshapiro
308664562Sgshapiro		/*
308764562Sgshapiro		**  Spin through the addresses and
308864562Sgshapiro		**  mark them dead to prevent bounces
308964562Sgshapiro		*/
309064562Sgshapiro
309164562Sgshapiro		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
309264562Sgshapiro			q->q_state = QS_DONTSEND;
309364562Sgshapiro
309490792Sgshapiro		drop = true;
309564562Sgshapiro	}
309680785Sgshapiro	else if (OpMode != MD_TEST)
309790792Sgshapiro	{
309864562Sgshapiro		unlockqueue(CurEnv);
309938032Speter	}
310038032Speter
310190792Sgshapiro	finis(drop, false, EX_OK);
310290792Sgshapiro	/* NOTREACHED */
310338032Speter}
310490792Sgshapiro/*
310538032Speter**  DISCONNECT -- remove our connection with any foreground process
310638032Speter**
310738032Speter**	Parameters:
310838032Speter**		droplev -- how "deeply" we should drop the line.
310938032Speter**			0 -- ignore signals, mail back errors, make sure
311038032Speter**			     output goes to stdout.
311164562Sgshapiro**			1 -- also, make stdout go to /dev/null.
311238032Speter**			2 -- also, disconnect from controlling terminal
311338032Speter**			     (only for daemon mode).
311438032Speter**		e -- the current envelope.
311538032Speter**
311638032Speter**	Returns:
311738032Speter**		none
311838032Speter**
311938032Speter**	Side Effects:
312038032Speter**		Trys to insure that we are immune to vagaries of
312138032Speter**		the controlling tty.
312238032Speter*/
312338032Speter
312438032Spetervoid
312538032Speterdisconnect(droplev, e)
312638032Speter	int droplev;
312738032Speter	register ENVELOPE *e;
312838032Speter{
312938032Speter	int fd;
313038032Speter
313138032Speter	if (tTd(52, 1))
313290792Sgshapiro		sm_dprintf("disconnect: In %d Out %d, e=%p\n",
313390792Sgshapiro			   sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
313490792Sgshapiro			   sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
313538032Speter	if (tTd(52, 100))
313638032Speter	{
313790792Sgshapiro		sm_dprintf("don't\n");
313838032Speter		return;
313938032Speter	}
314038032Speter	if (LogLevel > 93)
314138032Speter		sm_syslog(LOG_DEBUG, e->e_id,
314264562Sgshapiro			  "disconnect level %d",
314364562Sgshapiro			  droplev);
314438032Speter
314538032Speter	/* be sure we don't get nasty signals */
314690792Sgshapiro	(void) sm_signal(SIGINT, SIG_IGN);
314790792Sgshapiro	(void) sm_signal(SIGQUIT, SIG_IGN);
314838032Speter
314938032Speter	/* we can't communicate with our caller, so.... */
315090792Sgshapiro	HoldErrs = true;
315138032Speter	CurEnv->e_errormode = EM_MAIL;
315238032Speter	Verbose = 0;
315390792Sgshapiro	DisConnected = true;
315438032Speter
315538032Speter	/* all input from /dev/null */
315690792Sgshapiro	if (InChannel != smioin)
315738032Speter	{
315890792Sgshapiro		(void) sm_io_close(InChannel, SM_TIME_DEFAULT);
315990792Sgshapiro		InChannel = smioin;
316038032Speter	}
316190792Sgshapiro	if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
316290792Sgshapiro			 SM_IO_RDONLY, NULL, smioin) == NULL)
316338032Speter		sm_syslog(LOG_ERR, e->e_id,
316490792Sgshapiro			  "disconnect: sm_io_reopen(\"%s\") failed: %s",
316590792Sgshapiro			  SM_PATH_DEVNULL, sm_errstring(errno));
316638032Speter
316790792Sgshapiro	/*
316890792Sgshapiro	**  output to the transcript
316990792Sgshapiro	**	We also compare the fd numbers here since OutChannel
317090792Sgshapiro	**	might be a layer on top of smioout due to encryption
317190792Sgshapiro	**	(see sfsasl.c).
317290792Sgshapiro	*/
317390792Sgshapiro
317490792Sgshapiro	if (OutChannel != smioout &&
317590792Sgshapiro	    sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
317690792Sgshapiro	    sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
317738032Speter	{
317890792Sgshapiro		(void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
317990792Sgshapiro		OutChannel = smioout;
318090792Sgshapiro
318190792Sgshapiro#if 0
318290792Sgshapiro		/*
318390792Sgshapiro		**  Has smioout been closed? Reopen it.
318490792Sgshapiro		**	This shouldn't happen anymore, the code is here
318590792Sgshapiro		**	just as a reminder.
318690792Sgshapiro		*/
318790792Sgshapiro
318890792Sgshapiro		if (smioout->sm_magic == NULL &&
318990792Sgshapiro		    sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
319090792Sgshapiro				 SM_IO_WRONLY, NULL, smioout) == NULL)
319190792Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
319290792Sgshapiro				  "disconnect: sm_io_reopen(\"%s\") failed: %s",
319390792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
319490792Sgshapiro#endif /* 0 */
319538032Speter	}
319638032Speter	if (droplev > 0)
319738032Speter	{
319890792Sgshapiro		fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
319964562Sgshapiro		if (fd == -1)
320064562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
320190792Sgshapiro				  "disconnect: open(\"%s\") failed: %s",
320290792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
320390792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
320464562Sgshapiro		(void) dup2(fd, STDOUT_FILENO);
320564562Sgshapiro		(void) dup2(fd, STDERR_FILENO);
320664562Sgshapiro		(void) close(fd);
320738032Speter	}
320838032Speter
320938032Speter	/* drop our controlling TTY completely if possible */
321038032Speter	if (droplev > 1)
321138032Speter	{
321238032Speter		(void) setsid();
321338032Speter		errno = 0;
321438032Speter	}
321538032Speter
321638032Speter#if XDEBUG
321738032Speter	checkfd012("disconnect");
321864562Sgshapiro#endif /* XDEBUG */
321938032Speter
322038032Speter	if (LogLevel > 71)
322190792Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
322290792Sgshapiro			  (int) CurrentPid);
322338032Speter
322438032Speter	errno = 0;
322538032Speter}
322638032Speter
322738032Speterstatic void
322838032Speterobsolete(argv)
322938032Speter	char *argv[];
323038032Speter{
323138032Speter	register char *ap;
323238032Speter	register char *op;
323338032Speter
323438032Speter	while ((ap = *++argv) != NULL)
323538032Speter	{
323638032Speter		/* Return if "--" or not an option of any form. */
323738032Speter		if (ap[0] != '-' || ap[1] == '-')
323838032Speter			return;
323938032Speter
324090792Sgshapiro#if _FFR_QUARANTINE
324190792Sgshapiro		/* Don't allow users to use "-Q." or "-Q ." */
324290792Sgshapiro		if ((ap[1] == 'Q' && ap[2] == '.') ||
324390792Sgshapiro		    (ap[1] == 'Q' && argv[1] != NULL &&
324490792Sgshapiro		     argv[1][0] == '.' && argv[1][1] == '\0'))
324590792Sgshapiro		{
324690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
324790792Sgshapiro					     "Can not use -Q.\n");
324890792Sgshapiro			exit(EX_USAGE);
324990792Sgshapiro		}
325090792Sgshapiro#endif /* _FFR_QUARANTINE */
325190792Sgshapiro
325238032Speter		/* skip over options that do have a value */
325338032Speter		op = strchr(OPTIONS, ap[1]);
325438032Speter		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
325538032Speter		    ap[1] != 'd' &&
325638032Speter#if defined(sony_news)
325738032Speter		    ap[1] != 'E' && ap[1] != 'J' &&
325864562Sgshapiro#endif /* defined(sony_news) */
325938032Speter		    argv[1] != NULL && argv[1][0] != '-')
326038032Speter		{
326138032Speter			argv++;
326238032Speter			continue;
326338032Speter		}
326438032Speter
326538032Speter		/* If -C doesn't have an argument, use sendmail.cf. */
326690792Sgshapiro#define __DEFPATH	"sendmail.cf"
326738032Speter		if (ap[1] == 'C' && ap[2] == '\0')
326838032Speter		{
326938032Speter			*argv = xalloc(sizeof(__DEFPATH) + 2);
327090792Sgshapiro			(void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
327190792Sgshapiro					   "-C", __DEFPATH);
327238032Speter		}
327338032Speter
327438032Speter		/* If -q doesn't have an argument, run it once. */
327538032Speter		if (ap[1] == 'q' && ap[2] == '\0')
327638032Speter			*argv = "-q0";
327738032Speter
327890792Sgshapiro#if _FFR_QUARANTINE
327990792Sgshapiro		/* If -Q doesn't have an argument, disable quarantining */
328090792Sgshapiro		if (ap[1] == 'Q' && ap[2] == '\0')
328190792Sgshapiro			*argv = "-Q.";
328290792Sgshapiro#endif /* _FFR_QUARANTINE */
328390792Sgshapiro
328438032Speter		/* if -d doesn't have an argument, use 0-99.1 */
328538032Speter		if (ap[1] == 'd' && ap[2] == '\0')
328638032Speter			*argv = "-d0-99.1";
328738032Speter
328864562Sgshapiro#if defined(sony_news)
328938032Speter		/* if -E doesn't have an argument, use -EC */
329038032Speter		if (ap[1] == 'E' && ap[2] == '\0')
329138032Speter			*argv = "-EC";
329238032Speter
329338032Speter		/* if -J doesn't have an argument, use -JJ */
329438032Speter		if (ap[1] == 'J' && ap[2] == '\0')
329538032Speter			*argv = "-JJ";
329664562Sgshapiro#endif /* defined(sony_news) */
329738032Speter	}
329838032Speter}
329990792Sgshapiro/*
330038032Speter**  AUTH_WARNING -- specify authorization warning
330138032Speter**
330238032Speter**	Parameters:
330338032Speter**		e -- the current envelope.
330438032Speter**		msg -- the text of the message.
330538032Speter**		args -- arguments to the message.
330638032Speter**
330738032Speter**	Returns:
330838032Speter**		none.
330938032Speter*/
331038032Speter
331138032Spetervoid
331238032Speter#ifdef __STDC__
331338032Speterauth_warning(register ENVELOPE *e, const char *msg, ...)
331464562Sgshapiro#else /* __STDC__ */
331538032Speterauth_warning(e, msg, va_alist)
331638032Speter	register ENVELOPE *e;
331738032Speter	const char *msg;
331838032Speter	va_dcl
331964562Sgshapiro#endif /* __STDC__ */
332038032Speter{
332138032Speter	char buf[MAXLINE];
332290792Sgshapiro	SM_VA_LOCAL_DECL
332338032Speter
332438032Speter	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
332538032Speter	{
332638032Speter		register char *p;
332738032Speter		static char hostbuf[48];
332838032Speter
332938032Speter		if (hostbuf[0] == '\0')
333071345Sgshapiro		{
333171345Sgshapiro			struct hostent *hp;
333238032Speter
333371345Sgshapiro			hp = myhostname(hostbuf, sizeof hostbuf);
333490792Sgshapiro#if NETINET6
333571345Sgshapiro			if (hp != NULL)
333671345Sgshapiro			{
333771345Sgshapiro				freehostent(hp);
333871345Sgshapiro				hp = NULL;
333971345Sgshapiro			}
334090792Sgshapiro#endif /* NETINET6 */
334171345Sgshapiro		}
334271345Sgshapiro
334390792Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": ");
334438032Speter		p = &buf[strlen(buf)];
334590792Sgshapiro		SM_VA_START(ap, msg);
334690792Sgshapiro		(void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
334790792Sgshapiro		SM_VA_END(ap);
334890792Sgshapiro		addheader("X-Authentication-Warning", buf, 0, e);
334938032Speter		if (LogLevel > 3)
335038032Speter			sm_syslog(LOG_INFO, e->e_id,
335164562Sgshapiro				  "Authentication-Warning: %.400s",
335264562Sgshapiro				  buf);
335338032Speter	}
335438032Speter}
335590792Sgshapiro/*
335638032Speter**  GETEXTENV -- get from external environment
335738032Speter**
335838032Speter**	Parameters:
335938032Speter**		envar -- the name of the variable to retrieve
336038032Speter**
336138032Speter**	Returns:
336238032Speter**		The value, if any.
336338032Speter*/
336438032Speter
336590792Sgshapirostatic char *
336638032Spetergetextenv(envar)
336738032Speter	const char *envar;
336838032Speter{
336938032Speter	char **envp;
337038032Speter	int l;
337138032Speter
337238032Speter	l = strlen(envar);
337338032Speter	for (envp = ExternalEnviron; *envp != NULL; envp++)
337438032Speter	{
337538032Speter		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
337638032Speter			return &(*envp)[l + 1];
337738032Speter	}
337838032Speter	return NULL;
337938032Speter}
338090792Sgshapiro/*
338190792Sgshapiro**  SETUSERENV -- set an environment in the propagated environment
338238032Speter**
338338032Speter**	Parameters:
338438032Speter**		envar -- the name of the environment variable.
338538032Speter**		value -- the value to which it should be set.  If
338638032Speter**			null, this is extracted from the incoming
338738032Speter**			environment.  If that is not set, the call
338838032Speter**			to setuserenv is ignored.
338938032Speter**
339038032Speter**	Returns:
339138032Speter**		none.
339238032Speter*/
339338032Speter
339438032Spetervoid
339538032Spetersetuserenv(envar, value)
339638032Speter	const char *envar;
339738032Speter	const char *value;
339838032Speter{
339964562Sgshapiro	int i, l;
340038032Speter	char **evp = UserEnviron;
340138032Speter	char *p;
340238032Speter
340338032Speter	if (value == NULL)
340438032Speter	{
340538032Speter		value = getextenv(envar);
340638032Speter		if (value == NULL)
340738032Speter			return;
340838032Speter	}
340938032Speter
341090792Sgshapiro	/* XXX enforce reasonable size? */
341164562Sgshapiro	i = strlen(envar) + 1;
341264562Sgshapiro	l = strlen(value) + i + 1;
341364562Sgshapiro	p = (char *) xalloc(l);
341490792Sgshapiro	(void) sm_strlcpyn(p, l, 3, envar, "=", value);
341538032Speter
341638032Speter	while (*evp != NULL && strncmp(*evp, p, i) != 0)
341738032Speter		evp++;
341838032Speter	if (*evp != NULL)
341938032Speter	{
342038032Speter		*evp++ = p;
342138032Speter	}
342238032Speter	else if (evp < &UserEnviron[MAXUSERENVIRON])
342338032Speter	{
342438032Speter		*evp++ = p;
342538032Speter		*evp = NULL;
342638032Speter	}
342738032Speter
342838032Speter	/* make sure it is in our environment as well */
342938032Speter	if (putenv(p) < 0)
343038032Speter		syserr("setuserenv: putenv(%s) failed", p);
343138032Speter}
343290792Sgshapiro/*
343338032Speter**  DUMPSTATE -- dump state
343438032Speter**
343538032Speter**	For debugging.
343638032Speter*/
343738032Speter
343838032Spetervoid
343938032Speterdumpstate(when)
344038032Speter	char *when;
344138032Speter{
344238032Speter	register char *j = macvalue('j', CurEnv);
344338032Speter	int rs;
344464562Sgshapiro	extern int NextMacroId;
344538032Speter
344638032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id,
344764562Sgshapiro		  "--- dumping state on %s: $j = %s ---",
344864562Sgshapiro		  when,
344964562Sgshapiro		  j == NULL ? "<NULL>" : j);
345038032Speter	if (j != NULL)
345138032Speter	{
345238032Speter		if (!wordinclass(j, 'w'))
345338032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
345464562Sgshapiro				  "*** $j not in $=w ***");
345538032Speter	}
345638032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
345790792Sgshapiro	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
345864562Sgshapiro		  NextMacroId, MAXMACROID);
345938032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
346090792Sgshapiro	printopenfds(true);
346138032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
346290792Sgshapiro	mci_dump_all(true);
346338032Speter	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
346438032Speter	if (rs > 0)
346538032Speter	{
346664562Sgshapiro		int status;
346738032Speter		register char **pvp;
346838032Speter		char *pv[MAXATOM + 1];
346938032Speter
347038032Speter		pv[0] = NULL;
347190792Sgshapiro		status = REWRITE(pv, rs, CurEnv);
347238032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
347364562Sgshapiro			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
347464562Sgshapiro			  status);
347538032Speter		for (pvp = pv; *pvp != NULL; pvp++)
347638032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
347738032Speter	}
347838032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
347938032Speter}
348090792Sgshapiro
348180785Sgshapiro#ifdef SIGUSR1
348290792Sgshapiro/*
348377349Sgshapiro**  SIGUSR1 -- Signal a request to dump state.
348477349Sgshapiro**
348577349Sgshapiro**	Parameters:
348677349Sgshapiro**		sig -- calling signal.
348777349Sgshapiro**
348877349Sgshapiro**	Returns:
348977349Sgshapiro**		none.
349077349Sgshapiro**
349177349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
349277349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
349377349Sgshapiro**		DOING.
349477349Sgshapiro**
349577349Sgshapiro**		XXX: More work is needed for this signal handler.
349677349Sgshapiro*/
349738032Speter
349838032Speter/* ARGSUSED */
349977349Sgshapirostatic SIGFUNC_DECL
350038032Spetersigusr1(sig)
350138032Speter	int sig;
350238032Speter{
350377349Sgshapiro	int save_errno = errno;
350490792Sgshapiro# if SM_HEAP_CHECK
350590792Sgshapiro	extern void dumpstab __P((void));
350690792Sgshapiro# endif /* SM_HEAP_CHECK */
350777349Sgshapiro
350877349Sgshapiro	FIX_SYSV_SIGNAL(sig, sigusr1);
350977349Sgshapiro	errno = save_errno;
351077349Sgshapiro	CHECK_CRITICAL(sig);
351138032Speter	dumpstate("user signal");
351290792Sgshapiro# if SM_HEAP_CHECK
351390792Sgshapiro	dumpstab();
351490792Sgshapiro# endif /* SM_HEAP_CHECK */
351577349Sgshapiro	errno = save_errno;
351638032Speter	return SIGFUNC_RETURN;
351738032Speter}
351890792Sgshapiro#endif /* SIGUSR1 */
351990792Sgshapiro
352090792Sgshapiro/*
352138032Speter**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
352238032Speter**
352338032Speter**	Parameters:
352438032Speter**		to_real_uid -- if set, drop to the real uid instead
352538032Speter**			of the RunAsUser.
352638032Speter**
352738032Speter**	Returns:
352838032Speter**		EX_OSERR if the setuid failed.
352938032Speter**		EX_OK otherwise.
353038032Speter*/
353138032Speter
353238032Speterint
353338032Speterdrop_privileges(to_real_uid)
353438032Speter	bool to_real_uid;
353538032Speter{
353638032Speter	int rval = EX_OK;
353738032Speter	GIDSET_T emptygidset[1];
353838032Speter
353938032Speter	if (tTd(47, 1))
354090792Sgshapiro		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",
354190792Sgshapiro			   (int) to_real_uid,
354290792Sgshapiro			   (int) RealUid, (int) RealGid,
354390792Sgshapiro			   (int) getuid(), (int) getgid(),
354490792Sgshapiro			   (int) geteuid(), (int) getegid(),
354590792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
354638032Speter
354738032Speter	if (to_real_uid)
354838032Speter	{
354938032Speter		RunAsUserName = RealUserName;
355038032Speter		RunAsUid = RealUid;
355138032Speter		RunAsGid = RealGid;
355294334Sgshapiro		EffGid = RunAsGid;
355338032Speter	}
355438032Speter
355538032Speter	/* make sure no one can grab open descriptors for secret files */
355638032Speter	endpwent();
355790792Sgshapiro	sm_mbdb_terminate();
355838032Speter
355938032Speter	/* reset group permissions; these can be set later */
356038032Speter	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
356190792Sgshapiro
356290792Sgshapiro	/*
356390792Sgshapiro	**  Notice:  on some OS (Linux...) the setgroups() call causes
356490792Sgshapiro	**	a logfile entry if sendmail is not run by root.
356590792Sgshapiro	**	However, it is unclear (no POSIX standard) whether
356690792Sgshapiro	**	setgroups() can only succeed if executed by root.
356790792Sgshapiro	**	So for now we keep it as it is; if you want to change it, use
356890792Sgshapiro	**  if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
356990792Sgshapiro	*/
357090792Sgshapiro
357138032Speter	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
357264562Sgshapiro	{
357364562Sgshapiro		syserr("drop_privileges: setgroups(1, %d) failed",
357490792Sgshapiro		       (int) emptygidset[0]);
357538032Speter		rval = EX_OSERR;
357664562Sgshapiro	}
357738032Speter
357890792Sgshapiro	/* reset primary group id */
357990792Sgshapiro	if (to_real_uid)
358064562Sgshapiro	{
358190792Sgshapiro		/*
358290792Sgshapiro		**  Drop gid to real gid.
358390792Sgshapiro		**  On some OS we must reset the effective[/real[/saved]] gid,
358490792Sgshapiro		**  and then use setgid() to finally drop all group privileges.
358590792Sgshapiro		**  Later on we check whether we can get back the
358690792Sgshapiro		**  effective gid.
358790792Sgshapiro		*/
358890792Sgshapiro
358990792Sgshapiro#if HASSETEGID
359090792Sgshapiro		if (setegid(RunAsGid) < 0)
359190792Sgshapiro		{
359290792Sgshapiro			syserr("drop_privileges: setegid(%d) failed",
359390792Sgshapiro			       (int) RunAsGid);
359490792Sgshapiro			rval = EX_OSERR;
359590792Sgshapiro		}
359690792Sgshapiro#else /* HASSETEGID */
359790792Sgshapiro# if HASSETREGID
359890792Sgshapiro		if (setregid(RunAsGid, RunAsGid) < 0)
359990792Sgshapiro		{
360090792Sgshapiro			syserr("drop_privileges: setregid(%d, %d) failed",
360190792Sgshapiro			       (int) RunAsGid, (int) RunAsGid);
360290792Sgshapiro			rval = EX_OSERR;
360390792Sgshapiro		}
360490792Sgshapiro# else /* HASSETREGID */
360590792Sgshapiro#  if HASSETRESGID
360690792Sgshapiro		if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
360790792Sgshapiro		{
360890792Sgshapiro			syserr("drop_privileges: setresgid(%d, %d, %d) failed",
360990792Sgshapiro			       (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
361090792Sgshapiro			rval = EX_OSERR;
361190792Sgshapiro		}
361290792Sgshapiro#  endif /* HASSETRESGID */
361390792Sgshapiro# endif /* HASSETREGID */
361490792Sgshapiro#endif /* HASSETEGID */
361564562Sgshapiro	}
361690792Sgshapiro	if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
361790792Sgshapiro	{
361890792Sgshapiro		if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
361990792Sgshapiro		{
362090792Sgshapiro			syserr("drop_privileges: setgid(%d) failed",
362190792Sgshapiro			       (int) RunAsGid);
362290792Sgshapiro			rval = EX_OSERR;
362390792Sgshapiro		}
362490792Sgshapiro		errno = 0;
362590792Sgshapiro		if (rval == EX_OK && getegid() != RunAsGid)
362690792Sgshapiro		{
362790792Sgshapiro			syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
362890792Sgshapiro			       (int) getegid(), (int) RunAsGid);
362990792Sgshapiro			rval = EX_OSERR;
363090792Sgshapiro		}
363190792Sgshapiro	}
363290792Sgshapiro
363390792Sgshapiro	/* fiddle with uid */
363464562Sgshapiro	if (to_real_uid || RunAsUid != 0)
363564562Sgshapiro	{
363694334Sgshapiro		uid_t euid;
363764562Sgshapiro
363890792Sgshapiro		/*
363990792Sgshapiro		**  Try to setuid(RunAsUid).
364090792Sgshapiro		**  euid must be RunAsUid,
364194334Sgshapiro		**  ruid must be RunAsUid unless (e|r)uid wasn't 0
364294334Sgshapiro		**	and we didn't have to drop privileges to the real uid.
364390792Sgshapiro		*/
364490792Sgshapiro
364590792Sgshapiro		if (setuid(RunAsUid) < 0 ||
364694334Sgshapiro		    geteuid() != RunAsUid ||
364794334Sgshapiro		    (getuid() != RunAsUid &&
364894334Sgshapiro		     (to_real_uid || geteuid() == 0 || getuid() == 0)))
364964562Sgshapiro		{
365090792Sgshapiro#if HASSETREUID
365190792Sgshapiro			/*
365290792Sgshapiro			**  if ruid != RunAsUid, euid == RunAsUid, then
365390792Sgshapiro			**  try resetting just the real uid, then using
365490792Sgshapiro			**  setuid() to drop the saved-uid as well.
365590792Sgshapiro			*/
365690792Sgshapiro
365794334Sgshapiro			if (geteuid() == RunAsUid)
365890792Sgshapiro			{
365990792Sgshapiro				if (setreuid(RunAsUid, -1) < 0)
366090792Sgshapiro				{
366190792Sgshapiro					syserr("drop_privileges: setreuid(%d, -1) failed",
366290792Sgshapiro					       (int) RunAsUid);
366390792Sgshapiro					rval = EX_OSERR;
366490792Sgshapiro				}
366590792Sgshapiro				if (setuid(RunAsUid) < 0)
366690792Sgshapiro				{
366790792Sgshapiro					syserr("drop_privileges: second setuid(%d) attempt failed",
366890792Sgshapiro					       (int) RunAsUid);
366990792Sgshapiro					rval = EX_OSERR;
367090792Sgshapiro				}
367190792Sgshapiro			}
367290792Sgshapiro			else
367390792Sgshapiro#endif /* HASSETREUID */
367490792Sgshapiro			{
367590792Sgshapiro				syserr("drop_privileges: setuid(%d) failed",
367690792Sgshapiro				       (int) RunAsUid);
367790792Sgshapiro				rval = EX_OSERR;
367890792Sgshapiro			}
367964562Sgshapiro		}
368094334Sgshapiro		euid = geteuid();
368190792Sgshapiro		if (RunAsUid != 0 && setuid(0) == 0)
368264562Sgshapiro		{
368364562Sgshapiro			/*
368464562Sgshapiro			**  Believe it or not, the Linux capability model
368564562Sgshapiro			**  allows a non-root process to override setuid()
368664562Sgshapiro			**  on a process running as root and prevent that
368764562Sgshapiro			**  process from dropping privileges.
368864562Sgshapiro			*/
368964562Sgshapiro
369064562Sgshapiro			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
369164562Sgshapiro			rval = EX_OSERR;
369264562Sgshapiro		}
369364562Sgshapiro		else if (RunAsUid != euid && setuid(euid) == 0)
369464562Sgshapiro		{
369564562Sgshapiro			/*
369664562Sgshapiro			**  Some operating systems will keep the saved-uid
369764562Sgshapiro			**  if a non-root effective-uid calls setuid(real-uid)
369864562Sgshapiro			**  making it possible to set it back again later.
369964562Sgshapiro			*/
370064562Sgshapiro
370190792Sgshapiro			syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
370264562Sgshapiro			rval = EX_OSERR;
370364562Sgshapiro		}
370464562Sgshapiro	}
370590792Sgshapiro
370690792Sgshapiro	if ((to_real_uid || RunAsGid != 0) &&
370790792Sgshapiro	    rval == EX_OK && RunAsGid != EffGid &&
370890792Sgshapiro	    getuid() != 0 && geteuid() != 0)
370990792Sgshapiro	{
371090792Sgshapiro		errno = 0;
371190792Sgshapiro		if (setgid(EffGid) == 0)
371290792Sgshapiro		{
371390792Sgshapiro			syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
371490792Sgshapiro			       (int) EffGid);
371590792Sgshapiro			rval = EX_OSERR;
371690792Sgshapiro		}
371790792Sgshapiro	}
371890792Sgshapiro
371938032Speter	if (tTd(47, 5))
372038032Speter	{
372190792Sgshapiro		sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
372290792Sgshapiro			   (int) geteuid(), (int) getuid(),
372390792Sgshapiro			   (int) getegid(), (int) getgid());
372490792Sgshapiro		sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
372590792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
372664562Sgshapiro		if (tTd(47, 10))
372790792Sgshapiro			sm_dprintf("drop_privileges: rval = %d\n", rval);
372838032Speter	}
372938032Speter	return rval;
373038032Speter}
373190792Sgshapiro/*
373238032Speter**  FILL_FD -- make sure a file descriptor has been properly allocated
373338032Speter**
373438032Speter**	Used to make sure that stdin/out/err are allocated on startup
373538032Speter**
373638032Speter**	Parameters:
373738032Speter**		fd -- the file descriptor to be filled.
373838032Speter**		where -- a string used for logging.  If NULL, this is
373938032Speter**			being called on startup, and logging should
374038032Speter**			not be done.
374138032Speter**
374238032Speter**	Returns:
374338032Speter**		none
374490792Sgshapiro**
374590792Sgshapiro**	Side Effects:
374690792Sgshapiro**		possibly changes MissingFds
374738032Speter*/
374838032Speter
374938032Spetervoid
375038032Speterfill_fd(fd, where)
375138032Speter	int fd;
375238032Speter	char *where;
375338032Speter{
375438032Speter	int i;
375538032Speter	struct stat stbuf;
375638032Speter
375738032Speter	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
375838032Speter		return;
375938032Speter
376038032Speter	if (where != NULL)
376138032Speter		syserr("fill_fd: %s: fd %d not open", where, fd);
376238032Speter	else
376338032Speter		MissingFds |= 1 << fd;
376490792Sgshapiro	i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
376538032Speter	if (i < 0)
376638032Speter	{
376790792Sgshapiro		syserr("!fill_fd: %s: cannot open %s",
376890792Sgshapiro		       where == NULL ? "startup" : where, SM_PATH_DEVNULL);
376938032Speter	}
377038032Speter	if (fd != i)
377138032Speter	{
377238032Speter		(void) dup2(i, fd);
377338032Speter		(void) close(i);
377438032Speter	}
377538032Speter}
377690792Sgshapiro/*
377790792Sgshapiro**  SM_PRINTOPTIONS -- print options
377890792Sgshapiro**
377990792Sgshapiro**	Parameters:
378090792Sgshapiro**		options -- array of options.
378190792Sgshapiro**
378290792Sgshapiro**	Returns:
378390792Sgshapiro**		none.
378490792Sgshapiro*/
378590792Sgshapiro
378690792Sgshapirostatic void
378790792Sgshapirosm_printoptions(options)
378890792Sgshapiro	char **options;
378990792Sgshapiro{
379090792Sgshapiro	int ll;
379190792Sgshapiro	char **av;
379290792Sgshapiro
379390792Sgshapiro	av = options;
379490792Sgshapiro	ll = 7;
379590792Sgshapiro	while (*av != NULL)
379690792Sgshapiro	{
379790792Sgshapiro		if (ll + strlen(*av) > 63)
379890792Sgshapiro		{
379990792Sgshapiro			sm_dprintf("\n");
380090792Sgshapiro			ll = 0;
380190792Sgshapiro		}
380290792Sgshapiro		if (ll == 0)
380390792Sgshapiro			sm_dprintf("\t\t");
380490792Sgshapiro		else
380590792Sgshapiro			sm_dprintf(" ");
380690792Sgshapiro		sm_dprintf("%s", *av);
380790792Sgshapiro		ll += strlen(*av++) + 1;
380890792Sgshapiro	}
380990792Sgshapiro	sm_dprintf("\n");
381090792Sgshapiro}
381190792Sgshapiro/*
381238032Speter**  TESTMODELINE -- process a test mode input line
381338032Speter**
381438032Speter**	Parameters:
381538032Speter**		line -- the input line.
381638032Speter**		e -- the current environment.
381738032Speter**	Syntax:
381838032Speter**		#  a comment
381938032Speter**		.X process X as a configuration line
382038032Speter**		=X dump a configuration item (such as mailers)
382138032Speter**		$X dump a macro or class
382238032Speter**		/X try an activity
382338032Speter**		X  normal process through rule set X
382438032Speter*/
382538032Speter
382664562Sgshapirostatic void
382738032Spetertestmodeline(line, e)
382838032Speter	char *line;
382938032Speter	ENVELOPE *e;
383038032Speter{
383138032Speter	register char *p;
383238032Speter	char *q;
383338032Speter	auto char *delimptr;
383438032Speter	int mid;
383538032Speter	int i, rs;
383638032Speter	STAB *map;
383738032Speter	char **s;
383838032Speter	struct rewrite *rw;
383938032Speter	ADDRESS a;
384038032Speter	static int tryflags = RF_COPYNONE;
384138032Speter	char exbuf[MAXLINE];
384290792Sgshapiro	extern unsigned char TokTypeNoC[];
384338032Speter
384466494Sgshapiro	/* skip leading spaces */
384566494Sgshapiro	while (*line == ' ')
384666494Sgshapiro		line++;
384766494Sgshapiro
384838032Speter	switch (line[0])
384938032Speter	{
385038032Speter	  case '#':
385164562Sgshapiro	  case '\0':
385238032Speter		return;
385338032Speter
385438032Speter	  case '?':
385564562Sgshapiro		help("-bt", e);
385638032Speter		return;
385738032Speter
385838032Speter	  case '.':		/* config-style settings */
385938032Speter		switch (line[1])
386038032Speter		{
386138032Speter		  case 'D':
386290792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
386371345Sgshapiro			if (mid == 0)
386438032Speter				return;
386538032Speter			translate_dollars(delimptr);
386690792Sgshapiro			macdefine(&e->e_macro, A_TEMP, mid, delimptr);
386738032Speter			break;
386838032Speter
386938032Speter		  case 'C':
387038032Speter			if (line[2] == '\0')	/* not to call syserr() */
387138032Speter				return;
387238032Speter
387390792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
387471345Sgshapiro			if (mid == 0)
387538032Speter				return;
387638032Speter			translate_dollars(delimptr);
387738032Speter			expand(delimptr, exbuf, sizeof exbuf, e);
387838032Speter			p = exbuf;
387938032Speter			while (*p != '\0')
388038032Speter			{
388138032Speter				register char *wd;
388238032Speter				char delim;
388338032Speter
388438032Speter				while (*p != '\0' && isascii(*p) && isspace(*p))
388538032Speter					p++;
388638032Speter				wd = p;
388738032Speter				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
388838032Speter					p++;
388938032Speter				delim = *p;
389038032Speter				*p = '\0';
389138032Speter				if (wd[0] != '\0')
389238032Speter					setclass(mid, wd);
389338032Speter				*p = delim;
389438032Speter			}
389538032Speter			break;
389638032Speter
389738032Speter		  case '\0':
389890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
389990792Sgshapiro					     "Usage: .[DC]macro value(s)\n");
390038032Speter			break;
390138032Speter
390238032Speter		  default:
390390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
390490792Sgshapiro					     "Unknown \".\" command %s\n", line);
390538032Speter			break;
390638032Speter		}
390738032Speter		return;
390838032Speter
390938032Speter	  case '=':		/* config-style settings */
391038032Speter		switch (line[1])
391138032Speter		{
391238032Speter		  case 'S':		/* dump rule set */
391338032Speter			rs = strtorwset(&line[2], NULL, ST_FIND);
391438032Speter			if (rs < 0)
391538032Speter			{
391690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
391790792Sgshapiro						     "Undefined ruleset %s\n", &line[2]);
391838032Speter				return;
391938032Speter			}
392038032Speter			rw = RewriteRules[rs];
392138032Speter			if (rw == NULL)
392238032Speter				return;
392338032Speter			do
392438032Speter			{
392590792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
392690792Sgshapiro						  'R');
392738032Speter				s = rw->r_lhs;
392838032Speter				while (*s != NULL)
392938032Speter				{
393038032Speter					xputs(*s++);
393190792Sgshapiro					(void) sm_io_putc(smioout,
393290792Sgshapiro							  SM_TIME_DEFAULT, ' ');
393338032Speter				}
393490792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
393590792Sgshapiro						  '\t');
393690792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
393790792Sgshapiro						  '\t');
393838032Speter				s = rw->r_rhs;
393938032Speter				while (*s != NULL)
394038032Speter				{
394138032Speter					xputs(*s++);
394290792Sgshapiro					(void) sm_io_putc(smioout,
394390792Sgshapiro							  SM_TIME_DEFAULT, ' ');
394438032Speter				}
394590792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
394690792Sgshapiro						  '\n');
394738032Speter			} while ((rw = rw->r_next) != NULL);
394838032Speter			break;
394938032Speter
395038032Speter		  case 'M':
395138032Speter			for (i = 0; i < MAXMAILERS; i++)
395238032Speter			{
395338032Speter				if (Mailer[i] != NULL)
395438032Speter					printmailer(Mailer[i]);
395538032Speter			}
395638032Speter			break;
395738032Speter
395838032Speter		  case '\0':
395990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
396090792Sgshapiro					     "Usage: =Sruleset or =M\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 '-':		/* set command-line-like opts */
397138032Speter		switch (line[1])
397238032Speter		{
397338032Speter		  case 'd':
397438032Speter			tTflag(&line[2]);
397538032Speter			break;
397638032Speter
397738032Speter		  case '\0':
397890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
397990792Sgshapiro					     "Usage: -d{debug arguments}\n");
398038032Speter			break;
398138032Speter
398238032Speter		  default:
398390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
398490792Sgshapiro					     "Unknown \"-\" command %s\n", line);
398538032Speter			break;
398638032Speter		}
398738032Speter		return;
398838032Speter
398938032Speter	  case '$':
399038032Speter		if (line[1] == '=')
399138032Speter		{
399290792Sgshapiro			mid = macid(&line[2]);
399371345Sgshapiro			if (mid != 0)
399438032Speter				stabapply(dump_class, mid);
399538032Speter			return;
399638032Speter		}
399790792Sgshapiro		mid = macid(&line[1]);
399871345Sgshapiro		if (mid == 0)
399938032Speter			return;
400038032Speter		p = macvalue(mid, e);
400138032Speter		if (p == NULL)
400290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
400390792Sgshapiro					     "Undefined\n");
400438032Speter		else
400538032Speter		{
400638032Speter			xputs(p);
400790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
400890792Sgshapiro					     "\n");
400938032Speter		}
401038032Speter		return;
401138032Speter
401238032Speter	  case '/':		/* miscellaneous commands */
401338032Speter		p = &line[strlen(line)];
401438032Speter		while (--p >= line && isascii(*p) && isspace(*p))
401538032Speter			*p = '\0';
401638032Speter		p = strpbrk(line, " \t");
401738032Speter		if (p != NULL)
401838032Speter		{
401938032Speter			while (isascii(*p) && isspace(*p))
402038032Speter				*p++ = '\0';
402138032Speter		}
402238032Speter		else
402338032Speter			p = "";
402438032Speter		if (line[1] == '\0')
402538032Speter		{
402690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
402790792Sgshapiro					     "Usage: /[canon|map|mx|parse|try|tryflags]\n");
402838032Speter			return;
402938032Speter		}
403090792Sgshapiro		if (sm_strcasecmp(&line[1], "quit") == 0)
403164562Sgshapiro		{
403264562Sgshapiro			CurEnv->e_id = NULL;
403390792Sgshapiro			finis(true, true, ExitStat);
403490792Sgshapiro			/* NOTREACHED */
403564562Sgshapiro		}
403690792Sgshapiro		if (sm_strcasecmp(&line[1], "mx") == 0)
403738032Speter		{
403838032Speter#if NAMED_BIND
403938032Speter			/* look up MX records */
404038032Speter			int nmx;
404138032Speter			auto int rcode;
404238032Speter			char *mxhosts[MAXMXHOSTS + 1];
404338032Speter
404438032Speter			if (*p == '\0')
404538032Speter			{
404690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
404790792Sgshapiro						     "Usage: /mx address\n");
404838032Speter				return;
404938032Speter			}
405090792Sgshapiro			nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
405190792Sgshapiro				      NULL);
405290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
405390792Sgshapiro					     "getmxrr(%s) returns %d value(s):\n",
405490792Sgshapiro				p, nmx);
405538032Speter			for (i = 0; i < nmx; i++)
405690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
405790792Sgshapiro						     "\t%s\n", mxhosts[i]);
405864562Sgshapiro#else /* NAMED_BIND */
405990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
406090792Sgshapiro					     "No MX code compiled in\n");
406164562Sgshapiro#endif /* NAMED_BIND */
406238032Speter		}
406390792Sgshapiro		else if (sm_strcasecmp(&line[1], "canon") == 0)
406438032Speter		{
406538032Speter			char host[MAXHOSTNAMELEN];
406638032Speter
406738032Speter			if (*p == '\0')
406838032Speter			{
406990792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
407090792Sgshapiro						     "Usage: /canon address\n");
407138032Speter				return;
407238032Speter			}
407390792Sgshapiro			else if (sm_strlcpy(host, p, sizeof host) >= sizeof host)
407438032Speter			{
407590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
407690792Sgshapiro						     "Name too long\n");
407738032Speter				return;
407838032Speter			}
407990792Sgshapiro			(void) getcanonname(host, sizeof host, HasWildcardMX,
408090792Sgshapiro					    NULL);
408190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
408290792Sgshapiro					     "getcanonname(%s) returns %s\n",
408390792Sgshapiro					     p, host);
408438032Speter		}
408590792Sgshapiro		else if (sm_strcasecmp(&line[1], "map") == 0)
408638032Speter		{
408738032Speter			auto int rcode = EX_OK;
408838032Speter			char *av[2];
408938032Speter
409038032Speter			if (*p == '\0')
409138032Speter			{
409290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
409390792Sgshapiro						     "Usage: /map mapname key\n");
409438032Speter				return;
409538032Speter			}
409690792Sgshapiro			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q));			     q++)
409738032Speter				continue;
409838032Speter			if (*q == '\0')
409938032Speter			{
410090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
410190792Sgshapiro						     "No key specified\n");
410238032Speter				return;
410338032Speter			}
410438032Speter			*q++ = '\0';
410538032Speter			map = stab(p, ST_MAP, ST_FIND);
410638032Speter			if (map == NULL)
410738032Speter			{
410890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
410990792Sgshapiro						     "Map named \"%s\" not found\n", p);
411038032Speter				return;
411138032Speter			}
411264562Sgshapiro			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
411364562Sgshapiro			    !openmap(&(map->s_map)))
411438032Speter			{
411590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
411690792Sgshapiro						     "Map named \"%s\" not open\n", p);
411738032Speter				return;
411838032Speter			}
411990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
412090792Sgshapiro					     "map_lookup: %s (%s) ", p, q);
412138032Speter			av[0] = q;
412238032Speter			av[1] = NULL;
412338032Speter			p = (*map->s_map.map_class->map_lookup)
412438032Speter					(&map->s_map, q, av, &rcode);
412538032Speter			if (p == NULL)
412690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
412790792Sgshapiro						     "no match (%d)\n",
412890792Sgshapiro						     rcode);
412938032Speter			else
413090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
413190792Sgshapiro						     "returns %s (%d)\n", p,
413290792Sgshapiro						     rcode);
413338032Speter		}
413490792Sgshapiro		else if (sm_strcasecmp(&line[1], "try") == 0)
413538032Speter		{
413638032Speter			MAILER *m;
413764562Sgshapiro			STAB *st;
413838032Speter			auto int rcode = EX_OK;
413938032Speter
414038032Speter			q = strpbrk(p, " \t");
414138032Speter			if (q != NULL)
414238032Speter			{
414338032Speter				while (isascii(*q) && isspace(*q))
414438032Speter					*q++ = '\0';
414538032Speter			}
414638032Speter			if (q == NULL || *q == '\0')
414738032Speter			{
414890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
414990792Sgshapiro						     "Usage: /try mailer address\n");
415038032Speter				return;
415138032Speter			}
415264562Sgshapiro			st = stab(p, ST_MAILER, ST_FIND);
415364562Sgshapiro			if (st == NULL)
415438032Speter			{
415590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
415690792Sgshapiro						     "Unknown mailer %s\n", p);
415738032Speter				return;
415838032Speter			}
415964562Sgshapiro			m = st->s_mailer;
416090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
416190792Sgshapiro					     "Trying %s %s address %s for mailer %s\n",
416290792Sgshapiro				     bitset(RF_HEADERADDR, tryflags) ? "header"
416390792Sgshapiro							: "envelope",
416490792Sgshapiro				     bitset(RF_SENDERADDR, tryflags) ? "sender"
416590792Sgshapiro							: "recipient", q, p);
416638032Speter			p = remotename(q, m, tryflags, &rcode, CurEnv);
416790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
416890792Sgshapiro					     "Rcode = %d, addr = %s\n",
416990792Sgshapiro					     rcode, p == NULL ? "<NULL>" : p);
417038032Speter			e->e_to = NULL;
417138032Speter		}
417290792Sgshapiro		else if (sm_strcasecmp(&line[1], "tryflags") == 0)
417338032Speter		{
417438032Speter			if (*p == '\0')
417538032Speter			{
417690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
417790792Sgshapiro						     "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
417838032Speter				return;
417938032Speter			}
418038032Speter			for (; *p != '\0'; p++)
418138032Speter			{
418238032Speter				switch (*p)
418338032Speter				{
418438032Speter				  case 'H':
418538032Speter				  case 'h':
418638032Speter					tryflags |= RF_HEADERADDR;
418738032Speter					break;
418838032Speter
418938032Speter				  case 'E':
419038032Speter				  case 'e':
419138032Speter					tryflags &= ~RF_HEADERADDR;
419238032Speter					break;
419338032Speter
419438032Speter				  case 'S':
419538032Speter				  case 's':
419638032Speter					tryflags |= RF_SENDERADDR;
419738032Speter					break;
419838032Speter
419938032Speter				  case 'R':
420038032Speter				  case 'r':
420138032Speter					tryflags &= ~RF_SENDERADDR;
420238032Speter					break;
420338032Speter				}
420438032Speter			}
420564562Sgshapiro			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
420664562Sgshapiro			exbuf[1] = ' ';
420764562Sgshapiro			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
420864562Sgshapiro			exbuf[3] = '\0';
420990792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
421090792Sgshapiro				macid("{addr_type}"), exbuf);
421138032Speter		}
421290792Sgshapiro		else if (sm_strcasecmp(&line[1], "parse") == 0)
421338032Speter		{
421438032Speter			if (*p == '\0')
421538032Speter			{
421690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
421790792Sgshapiro						     "Usage: /parse address\n");
421838032Speter				return;
421938032Speter			}
422038032Speter			q = crackaddr(p);
422190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
422290792Sgshapiro					     "Cracked address = ");
422338032Speter			xputs(q);
422490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
422590792Sgshapiro					     "\nParsing %s %s address\n",
422690792Sgshapiro					     bitset(RF_HEADERADDR, tryflags) ?
422790792Sgshapiro							"header" : "envelope",
422890792Sgshapiro					     bitset(RF_SENDERADDR, tryflags) ?
422990792Sgshapiro							"sender" : "recipient");
423090792Sgshapiro			if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
423190792Sgshapiro			    == NULL)
423290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
423390792Sgshapiro						     "Cannot parse\n");
423438032Speter			else if (a.q_host != NULL && a.q_host[0] != '\0')
423590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
423690792Sgshapiro						     "mailer %s, host %s, user %s\n",
423790792Sgshapiro						     a.q_mailer->m_name,
423890792Sgshapiro						     a.q_host,
423990792Sgshapiro						     a.q_user);
424038032Speter			else
424190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
424290792Sgshapiro						     "mailer %s, user %s\n",
424390792Sgshapiro						     a.q_mailer->m_name,
424490792Sgshapiro						     a.q_user);
424538032Speter			e->e_to = NULL;
424638032Speter		}
424738032Speter		else
424838032Speter		{
424990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
425090792Sgshapiro					     "Unknown \"/\" command %s\n",
425190792Sgshapiro					     line);
425238032Speter		}
425338032Speter		return;
425438032Speter	}
425538032Speter
425638032Speter	for (p = line; isascii(*p) && isspace(*p); p++)
425738032Speter		continue;
425838032Speter	q = p;
425938032Speter	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
426038032Speter		p++;
426138032Speter	if (*p == '\0')
426238032Speter	{
426390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
426490792Sgshapiro				     "No address!\n");
426538032Speter		return;
426638032Speter	}
426738032Speter	*p = '\0';
426890792Sgshapiro	if (invalidaddr(p + 1, NULL, true))
426938032Speter		return;
427038032Speter	do
427138032Speter	{
427238032Speter		register char **pvp;
427338032Speter		char pvpbuf[PSBUFSIZE];
427438032Speter
427538032Speter		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
427664562Sgshapiro			      &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
427738032Speter		if (pvp == NULL)
427838032Speter			continue;
427938032Speter		p = q;
428038032Speter		while (*p != '\0')
428138032Speter		{
428264562Sgshapiro			int status;
428338032Speter
428438032Speter			rs = strtorwset(p, NULL, ST_FIND);
428538032Speter			if (rs < 0)
428638032Speter			{
428790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
428890792Sgshapiro						     "Undefined ruleset %s\n",
428990792Sgshapiro						     p);
429038032Speter				break;
429138032Speter			}
429290792Sgshapiro			status = REWRITE(pvp, rs, e);
429364562Sgshapiro			if (status != EX_OK)
429490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
429590792Sgshapiro						     "== Ruleset %s (%d) status %d\n",
429690792Sgshapiro						     p, rs, status);
429738032Speter			while (*p != '\0' && *p++ != ',')
429838032Speter				continue;
429938032Speter		}
430038032Speter	} while (*(p = delimptr) != '\0');
430138032Speter}
430238032Speter
430364562Sgshapirostatic void
430438032Speterdump_class(s, id)
430538032Speter	register STAB *s;
430638032Speter	int id;
430738032Speter{
430890792Sgshapiro	if (s->s_symtype != ST_CLASS)
430938032Speter		return;
431071345Sgshapiro	if (bitnset(bitidx(id), s->s_class))
431190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
431290792Sgshapiro				     "%s\n", s->s_name);
431338032Speter}
431490792Sgshapiro
431590792Sgshapiro/*
431690792Sgshapiro**  An exception type used to create QuickAbort exceptions.
431790792Sgshapiro**  This is my first cut at converting QuickAbort from longjmp to exceptions.
431890792Sgshapiro**  These exceptions have a single integer argument, which is the argument
431990792Sgshapiro**  to longjmp in the original code (either 1 or 2).  I don't know the
432090792Sgshapiro**  significance of 1 vs 2: the calls to setjmp don't care.
432190792Sgshapiro*/
432290792Sgshapiro
432390792Sgshapiroconst SM_EXC_TYPE_T EtypeQuickAbort =
432490792Sgshapiro{
432590792Sgshapiro	SmExcTypeMagic,
432690792Sgshapiro	"E:mta.quickabort",
432790792Sgshapiro	"i",
432890792Sgshapiro	sm_etype_printf,
432990792Sgshapiro	"quick abort %0",
433090792Sgshapiro};
4331