main.c revision 249729
138032Speter/*
2223067Sgshapiro * Copyright (c) 1998-2006, 2008, 2009, 2011 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>
16168515Sgshapiro#include <sm/sendmail.h>
1790792Sgshapiro#include <sm/xtrap.h>
1890792Sgshapiro#include <sm/signal.h>
1990792Sgshapiro
2038032Speter#ifndef lint
2190792SgshapiroSM_UNUSED(static char copyright[]) =
22132943Sgshapiro"@(#) Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.\n\
2364562Sgshapiro	All rights reserved.\n\
2438032Speter     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
2538032Speter     Copyright (c) 1988, 1993\n\
2638032Speter	The Regents of the University of California.  All rights reserved.\n";
2764562Sgshapiro#endif /* ! lint */
2838032Speter
29249729SgshapiroSM_RCSID("@(#)$Id: main.c,v 8.983 2013/03/12 15:24:52 ca Exp $")
3038032Speter
3138032Speter
3264562Sgshapiro#if NETINET || NETINET6
3364562Sgshapiro# include <arpa/inet.h>
3464562Sgshapiro#endif /* NETINET || NETINET6 */
3564562Sgshapiro
3690792Sgshapiro/* for getcfname() */
3790792Sgshapiro#include <sendmail/pathnames.h>
3890792Sgshapiro
3990792Sgshapirostatic SM_DEBUG_T
4090792SgshapiroDebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
4190792Sgshapiro	"@(#)$Debug: no_persistent_restart - don't restart, log only $");
4290792Sgshapiro
4390792Sgshapirostatic void	dump_class __P((STAB *, int));
4490792Sgshapirostatic void	obsolete __P((char **));
4590792Sgshapirostatic void	testmodeline __P((char *, ENVELOPE *));
4690792Sgshapirostatic char	*getextenv __P((const char *));
4790792Sgshapirostatic void	sm_printoptions __P((char **));
4877349Sgshapirostatic SIGFUNC_DECL	intindebug __P((int));
4990792Sgshapirostatic SIGFUNC_DECL	sighup __P((int));
5090792Sgshapirostatic SIGFUNC_DECL	sigpipe __P((int));
5190792Sgshapirostatic SIGFUNC_DECL	sigterm __P((int));
5280785Sgshapiro#ifdef SIGUSR1
5377349Sgshapirostatic SIGFUNC_DECL	sigusr1 __P((int));
5490792Sgshapiro#endif /* SIGUSR1 */
5564562Sgshapiro
5638032Speter/*
5738032Speter**  SENDMAIL -- Post mail to a set of destinations.
5838032Speter**
5938032Speter**	This is the basic mail router.  All user mail programs should
6038032Speter**	call this routine to actually deliver mail.  Sendmail in
6138032Speter**	turn calls a bunch of mail servers that do the real work of
6238032Speter**	delivering the mail.
6338032Speter**
6464562Sgshapiro**	Sendmail is driven by settings read in from /etc/mail/sendmail.cf
6538032Speter**	(read by readcf.c).
6638032Speter**
6738032Speter**	Usage:
6838032Speter**		/usr/lib/sendmail [flags] addr ...
6938032Speter**
7038032Speter**		See the associated documentation for details.
7138032Speter**
7290792Sgshapiro**	Authors:
7338032Speter**		Eric Allman, UCB/INGRES (until 10/81).
7438032Speter**			     Britton-Lee, Inc., purveyors of fine
7538032Speter**				database computers (11/81 - 10/88).
7638032Speter**			     International Computer Science Institute
7738032Speter**				(11/88 - 9/89).
7838032Speter**			     UCB/Mammoth Project (10/89 - 7/95).
7938032Speter**			     InReference, Inc. (8/95 - 1/97).
8038032Speter**			     Sendmail, Inc. (1/98 - present).
81111823Sgshapiro**		The support of my employers is gratefully acknowledged.
8238032Speter**			Few of them (Britton-Lee in particular) have had
8338032Speter**			anything to gain from my involvement in this project.
8490792Sgshapiro**
8590792Sgshapiro**		Gregory Neil Shapiro,
8690792Sgshapiro**			Worcester Polytechnic Institute	(until 3/98).
8790792Sgshapiro**			Sendmail, Inc. (3/98 - present).
8890792Sgshapiro**
8990792Sgshapiro**		Claus Assmann,
9090792Sgshapiro**			Sendmail, Inc. (12/98 - present).
9138032Speter*/
9238032Speter
9338032Speterchar		*FullName;	/* sender's full name */
9438032SpeterENVELOPE	BlankEnvelope;	/* a "blank" envelope */
9564562Sgshapirostatic ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
9638032SpeterADDRESS		NullAddress =	/* a null address */
9738032Speter		{ "", "", NULL, "" };
9838032Speterchar		*CommandLineArgs;	/* command line args for pid file */
9990792Sgshapirobool		Warn_Q_option = false;	/* warn about Q option use */
10064562Sgshapirostatic int	MissingFds = 0;	/* bit map of fds missing on startup */
10190792Sgshapirochar		*Mbdb = "pw";	/* mailbox database defaults to /etc/passwd */
10238032Speter
10338032Speter#ifdef NGROUPS_MAX
10438032SpeterGIDSET_T	InitialGidSet[NGROUPS_MAX];
10564562Sgshapiro#endif /* NGROUPS_MAX */
10638032Speter
10790792Sgshapiro#define MAXCONFIGLEVEL	10	/* highest config version level known */
10838032Speter
10964562Sgshapiro#if SASL
11064562Sgshapirostatic sasl_callback_t srvcallbacks[] =
11164562Sgshapiro{
112225906Sume	{	SASL_CB_VERIFYFILE,	(sasl_callback_ft)&safesaslfile,	NULL	},
113225906Sume	{	SASL_CB_PROXY_POLICY,	(sasl_callback_ft)&proxy_policy,	NULL	},
11464562Sgshapiro	{	SASL_CB_LIST_END,	NULL,		NULL	}
11564562Sgshapiro};
11664562Sgshapiro#endif /* SASL */
11764562Sgshapiro
11890792Sgshapirounsigned int	SubmitMode;
11990792Sgshapiroint		SyslogPrefixLen; /* estimated length of syslog prefix */
12090792Sgshapiro#define PIDLEN		6	/* pid length for computing SyslogPrefixLen */
12190792Sgshapiro#ifndef SL_FUDGE
12290792Sgshapiro# define SL_FUDGE	10	/* fudge offset for SyslogPrefixLen */
12390792Sgshapiro#endif /* ! SL_FUDGE */
12490792Sgshapiro#define SLDLL		8	/* est. length of default syslog label */
12564562Sgshapiro
12690792Sgshapiro
12790792Sgshapiro/* Some options are dangerous to allow users to use in non-submit mode */
12890792Sgshapiro#define CHECK_AGAINST_OPMODE(cmd)					\
12990792Sgshapiro{									\
13090792Sgshapiro	if (extraprivs &&						\
13190792Sgshapiro	    OpMode != MD_DELIVER && OpMode != MD_SMTP &&		\
132203004Sgshapiro	    OpMode != MD_ARPAFTP && OpMode != MD_CHECKCONFIG &&		\
13390792Sgshapiro	    OpMode != MD_VERIFY && OpMode != MD_TEST)			\
13490792Sgshapiro	{								\
13590792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
13690792Sgshapiro				     "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
13790792Sgshapiro		       (cmd));						\
13890792Sgshapiro		break;							\
13990792Sgshapiro	}								\
14090792Sgshapiro	if (extraprivs && queuerun)					\
14190792Sgshapiro	{								\
14290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
14390792Sgshapiro				     "WARNING: Ignoring submission mode -%c option with -q\n", \
14490792Sgshapiro		       (cmd));						\
14590792Sgshapiro		break;							\
14690792Sgshapiro	}								\
14790792Sgshapiro}
14890792Sgshapiro
14938032Speterint
15038032Spetermain(argc, argv, envp)
15138032Speter	int argc;
15238032Speter	char **argv;
15338032Speter	char **envp;
15438032Speter{
15538032Speter	register char *p;
15638032Speter	char **av;
15738032Speter	extern char Version[];
15838032Speter	char *ep, *from;
15938032Speter	STAB *st;
16038032Speter	register int i;
16138032Speter	int j;
16264562Sgshapiro	int dp;
16390792Sgshapiro	int fill_errno;
16490792Sgshapiro	int qgrp = NOQGRP;		/* queue group to process */
16590792Sgshapiro	bool safecf = true;
16664562Sgshapiro	BITMAP256 *p_flags = NULL;	/* daemon flags */
16790792Sgshapiro	bool warn_C_flag = false;
16890792Sgshapiro	bool auth = true;		/* whether to set e_auth_param */
16938032Speter	char warn_f_flag = '\0';
17090792Sgshapiro	bool run_in_foreground = false;	/* -bD mode */
17190792Sgshapiro	bool queuerun = false, debug = false;
17238032Speter	struct passwd *pw;
17338032Speter	struct hostent *hp;
17438032Speter	char *nullserver = NULL;
17564562Sgshapiro	char *authinfo = NULL;
17664562Sgshapiro	char *sysloglabel = NULL;	/* label for syslog */
17790792Sgshapiro	char *conffile = NULL;		/* name of .cf file */
17890792Sgshapiro	char *queuegroup = NULL;	/* queue group to process */
17990792Sgshapiro	char *quarantining = NULL;	/* quarantine queue items? */
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? */
186132943Sgshapiro	SM_FILE_T *smdebug;
18790792Sgshapiro	static time_t starttime = 0;	/* when was process started */
18864562Sgshapiro	struct stat traf_st;		/* for TrafficLog FIFO check */
18990792Sgshapiro	char buf[MAXLINE];
19038032Speter	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
19138032Speter	static char rnamebuf[MAXNAME];	/* holds RealUserName */
19238032Speter	char *emptyenviron[1];
19390792Sgshapiro#if STARTTLS
19466494Sgshapiro	bool tls_ok;
19590792Sgshapiro#endif /* STARTTLS */
19638032Speter	QUEUE_CHAR *new;
19790792Sgshapiro	ENVELOPE *e;
19838032Speter	extern int DtableSize;
19938032Speter	extern int optind;
20038032Speter	extern int opterr;
20138032Speter	extern char *optarg;
20238032Speter	extern char **environ;
20390792Sgshapiro#if SASL
20490792Sgshapiro	extern void sm_sasl_init __P((void));
20590792Sgshapiro#endif /* SASL */
20638032Speter
20790792Sgshapiro#if USE_ENVIRON
20890792Sgshapiro	envp = environ;
20990792Sgshapiro#endif /* USE_ENVIRON */
21090792Sgshapiro
21190792Sgshapiro	/* turn off profiling */
21290792Sgshapiro	SM_PROF(0);
21390792Sgshapiro
21490792Sgshapiro	/* install default exception handler */
21590792Sgshapiro	sm_exc_newthread(fatal_error);
21690792Sgshapiro
217110560Sgshapiro	/* set the default in/out channel so errors reported to screen */
218110560Sgshapiro	InChannel = smioin;
219110560Sgshapiro	OutChannel = smioout;
220110560Sgshapiro
22138032Speter	/*
22238032Speter	**  Check to see if we reentered.
22338032Speter	**	This would normally happen if e_putheader or e_putbody
22438032Speter	**	were NULL when invoked.
22538032Speter	*/
22638032Speter
22790792Sgshapiro	if (starttime != 0)
22838032Speter	{
22938032Speter		syserr("main: reentered!");
23038032Speter		abort();
23138032Speter	}
23290792Sgshapiro	starttime = curtime();
23338032Speter
23438032Speter	/* avoid null pointer dereferences */
235168515Sgshapiro	TermEscape.te_rv_on = TermEscape.te_under_on = TermEscape.te_normal = "";
23638032Speter
23790792Sgshapiro	RealUid = getuid();
23890792Sgshapiro	RealGid = getgid();
23977349Sgshapiro
24090792Sgshapiro	/* Check if sendmail is running with extra privs */
24190792Sgshapiro	extraprivs = (RealUid != 0 &&
24290792Sgshapiro		      (geteuid() != getuid() || getegid() != getgid()));
24377349Sgshapiro
24490792Sgshapiro	CurrentPid = getpid();
24538032Speter
24690792Sgshapiro	/* get whatever .cf file is right for the opmode */
24790792Sgshapiro	cftype = SM_GET_RIGHT_CF;
24864562Sgshapiro
24938032Speter	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
25038032Speter	DtableSize = getdtsize();
25138032Speter	if (DtableSize > 256)
25238032Speter		DtableSize = 256;
25338032Speter
25438032Speter	/*
25538032Speter	**  Be sure we have enough file descriptors.
25638032Speter	**	But also be sure that 0, 1, & 2 are open.
25738032Speter	*/
25838032Speter
25990792Sgshapiro	/* reset errno and fill_errno; the latter is used way down below */
26090792Sgshapiro	errno = fill_errno = 0;
26138032Speter	fill_fd(STDIN_FILENO, NULL);
26290792Sgshapiro	if (errno != 0)
26390792Sgshapiro		fill_errno = errno;
26438032Speter	fill_fd(STDOUT_FILENO, NULL);
26590792Sgshapiro	if (errno != 0)
26690792Sgshapiro		fill_errno = errno;
26738032Speter	fill_fd(STDERR_FILENO, NULL);
26890792Sgshapiro	if (errno != 0)
26990792Sgshapiro		fill_errno = errno;
27038032Speter
271132943Sgshapiro	sm_closefrom(STDERR_FILENO + 1, DtableSize);
27238032Speter	errno = 0;
273132943Sgshapiro	smdebug = NULL;
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;
305182352Sgshapiro#if _FFR_LOCAL_DAEMON
306182352Sgshapiro	LocalDaemon = false;
307223067Sgshapiro# if NETINET6
308223067Sgshapiro	V6LoopbackAddrFound = false;
309223067Sgshapiro# endif /* NETINET6 */
310182352Sgshapiro#endif /* _FFR_LOCAL_DAEMON */
31138032Speter#if XDEBUG
31238032Speter	checkfd012("after openlog");
31364562Sgshapiro#endif /* XDEBUG */
31438032Speter
315168515Sgshapiro	tTsetup(tTdvect, sizeof(tTdvect), "0-99.1,*_trace_*.1");
31638032Speter
31738032Speter#ifdef NGROUPS_MAX
31838032Speter	/* save initial group set for future checks */
31938032Speter	i = getgroups(NGROUPS_MAX, InitialGidSet);
32090792Sgshapiro	if (i <= 0)
32190792Sgshapiro	{
32238032Speter		InitialGidSet[0] = (GID_T) -1;
32390792Sgshapiro		i = 0;
32490792Sgshapiro	}
32538032Speter	while (i < NGROUPS_MAX)
32638032Speter		InitialGidSet[i++] = InitialGidSet[0];
32764562Sgshapiro#endif /* NGROUPS_MAX */
32838032Speter
32938032Speter	/* drop group id privileges (RunAsUser not yet set) */
33090792Sgshapiro	dp = drop_privileges(false);
33164562Sgshapiro	setstat(dp);
33238032Speter
33390792Sgshapiro#ifdef SIGUSR1
33477349Sgshapiro	/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
33594334Sgshapiro	if (!extraprivs)
33677349Sgshapiro	{
33777349Sgshapiro		/* arrange to dump state on user-1 signal */
33890792Sgshapiro		(void) sm_signal(SIGUSR1, sigusr1);
33977349Sgshapiro	}
34094334Sgshapiro	else
34194334Sgshapiro	{
34294334Sgshapiro		/* ignore user-1 signal */
34394334Sgshapiro		(void) sm_signal(SIGUSR1, SIG_IGN);
34494334Sgshapiro	}
34590792Sgshapiro#endif /* SIGUSR1 */
34638032Speter
34738032Speter	/* initialize for setproctitle */
34838032Speter	initsetproctitle(argc, argv, envp);
34938032Speter
35038032Speter	/* Handle any non-getoptable constructions. */
35138032Speter	obsolete(argv);
35238032Speter
35338032Speter	/*
35438032Speter	**  Do a quick prescan of the argument list.
35538032Speter	*/
35638032Speter
35764562Sgshapiro
35890792Sgshapiro	/* find initial opMode */
35990792Sgshapiro	OpMode = MD_DELIVER;
36090792Sgshapiro	av = argv;
36190792Sgshapiro	p = strrchr(*av, '/');
36290792Sgshapiro	if (p++ == NULL)
36390792Sgshapiro		p = *av;
36490792Sgshapiro	if (strcmp(p, "newaliases") == 0)
36590792Sgshapiro		OpMode = MD_INITALIAS;
36690792Sgshapiro	else if (strcmp(p, "mailq") == 0)
36790792Sgshapiro		OpMode = MD_PRINT;
36890792Sgshapiro	else if (strcmp(p, "smtpd") == 0)
36990792Sgshapiro		OpMode = MD_DAEMON;
37090792Sgshapiro	else if (strcmp(p, "hoststat") == 0)
37190792Sgshapiro		OpMode = MD_HOSTSTAT;
37290792Sgshapiro	else if (strcmp(p, "purgestat") == 0)
37390792Sgshapiro		OpMode = MD_PURGESTAT;
37490792Sgshapiro
375132943Sgshapiro#if defined(__osf__) || defined(_AIX3)
376132943Sgshapiro# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:x"
377132943Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */
378132943Sgshapiro#if defined(sony_news)
379132943Sgshapiro# define OPTIONS	"A:B:b:C:cD:d:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
380132943Sgshapiro#endif /* defined(sony_news) */
381132943Sgshapiro#ifndef OPTIONS
382132943Sgshapiro# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
383132943Sgshapiro#endif /* ! OPTIONS */
38490792Sgshapiro
385112810Sgshapiro	/* Set to 0 to allow -b; need to check optarg before using it! */
38638032Speter	opterr = 0;
38738032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
38838032Speter	{
38938032Speter		switch (j)
39038032Speter		{
39190792Sgshapiro		  case 'b':	/* operations mode */
39294334Sgshapiro			j = (optarg == NULL) ? ' ' : *optarg;
39394334Sgshapiro			switch (j)
39438032Speter			{
39590792Sgshapiro			  case MD_DAEMON:
39690792Sgshapiro			  case MD_FGDAEMON:
39790792Sgshapiro			  case MD_SMTP:
39890792Sgshapiro			  case MD_INITALIAS:
39990792Sgshapiro			  case MD_DELIVER:
40090792Sgshapiro			  case MD_VERIFY:
40190792Sgshapiro			  case MD_TEST:
40290792Sgshapiro			  case MD_PRINT:
40390792Sgshapiro			  case MD_PRINTNQE:
40490792Sgshapiro			  case MD_HOSTSTAT:
40590792Sgshapiro			  case MD_PURGESTAT:
40690792Sgshapiro			  case MD_ARPAFTP:
407203004Sgshapiro#if _FFR_CHECKCONFIG
408203004Sgshapiro			  case MD_CHECKCONFIG:
409203004Sgshapiro#endif /* _FFR_CHECKCONFIG */
41090792Sgshapiro				OpMode = j;
41138032Speter				break;
41290792Sgshapiro
413182352Sgshapiro#if _FFR_LOCAL_DAEMON
414182352Sgshapiro			  case MD_LOCAL:
415182352Sgshapiro				OpMode = MD_DAEMON;
416182352Sgshapiro				LocalDaemon = true;
417182352Sgshapiro				break;
418182352Sgshapiro#endif /* _FFR_LOCAL_DAEMON */
419182352Sgshapiro
42090792Sgshapiro			  case MD_FREEZE:
42190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
42290792Sgshapiro						     "Frozen configurations unsupported\n");
42390792Sgshapiro				return EX_USAGE;
42490792Sgshapiro
42590792Sgshapiro			  default:
42690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
42790792Sgshapiro						     "Invalid operation mode %c\n",
42890792Sgshapiro						     j);
42990792Sgshapiro				return EX_USAGE;
43038032Speter			}
43190792Sgshapiro			break;
43290792Sgshapiro
433132943Sgshapiro		  case 'D':
434132943Sgshapiro			if (debug)
435132943Sgshapiro			{
436132943Sgshapiro				errno = 0;
437132943Sgshapiro				syserr("-D file must be before -d");
438132943Sgshapiro				ExitStat = EX_USAGE;
439132943Sgshapiro				break;
440132943Sgshapiro			}
441132943Sgshapiro			dp = drop_privileges(true);
442132943Sgshapiro			setstat(dp);
443132943Sgshapiro			smdebug = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
444132943Sgshapiro					    optarg, SM_IO_APPEND, NULL);
445132943Sgshapiro			if (smdebug == NULL)
446132943Sgshapiro			{
447132943Sgshapiro				syserr("cannot open %s", optarg);
448132943Sgshapiro				ExitStat = EX_CANTCREAT;
449132943Sgshapiro				break;
450132943Sgshapiro			}
451132943Sgshapiro			sm_debug_setfile(smdebug);
452132943Sgshapiro			break;
453132943Sgshapiro
45490792Sgshapiro		  case 'd':
45590792Sgshapiro			debug = true;
45638032Speter			tTflag(optarg);
457132943Sgshapiro			(void) sm_io_setvbuf(sm_debug_file(), SM_TIME_DEFAULT,
45890792Sgshapiro					     (char *) NULL, SM_IO_NBF,
45990792Sgshapiro					     SM_IO_BUFSIZ);
46038032Speter			break;
46164562Sgshapiro
46264562Sgshapiro		  case 'G':	/* relay (gateway) submission */
46390792Sgshapiro			SubmitMode = SUBMIT_MTA;
46464562Sgshapiro			break;
46564562Sgshapiro
46664562Sgshapiro		  case 'L':
467112810Sgshapiro			if (optarg == NULL)
468112810Sgshapiro			{
469112810Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
470112810Sgshapiro						     "option requires an argument -- '%c'",
471112810Sgshapiro						     (char) j);
472112810Sgshapiro				return EX_USAGE;
473112810Sgshapiro			}
474132943Sgshapiro			j = SM_MIN(strlen(optarg), 32) + 1;
47564562Sgshapiro			sysloglabel = xalloc(j);
47690792Sgshapiro			(void) sm_strlcpy(sysloglabel, optarg, j);
47790792Sgshapiro			SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
47890792Sgshapiro					  SL_FUDGE + j;
47964562Sgshapiro			break;
48064562Sgshapiro
48190792Sgshapiro		  case 'Q':
48290792Sgshapiro		  case 'q':
48390792Sgshapiro			/* just check if it is there */
48490792Sgshapiro			queuerun = true;
48564562Sgshapiro			break;
48638032Speter		}
48738032Speter	}
48838032Speter	opterr = 1;
48938032Speter
49090792Sgshapiro	/* Don't leak queue information via debug flags */
49190792Sgshapiro	if (extraprivs && queuerun && debug)
49290792Sgshapiro	{
49390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
49490792Sgshapiro				     "WARNING: Can not use -d with -q.  Disabling debugging.\n");
495132943Sgshapiro		sm_debug_close();
49690792Sgshapiro		sm_debug_setfile(NULL);
497168515Sgshapiro		(void) memset(tTdvect, '\0', sizeof(tTdvect));
49890792Sgshapiro	}
49990792Sgshapiro
50077349Sgshapiro#if LOG
50164562Sgshapiro	if (sysloglabel != NULL)
50264562Sgshapiro	{
50377349Sgshapiro		/* Sanitize the string */
50477349Sgshapiro		for (p = sysloglabel; *p != '\0'; p++)
50577349Sgshapiro		{
50677349Sgshapiro			if (!isascii(*p) || !isprint(*p) || *p == '%')
50777349Sgshapiro				*p = '*';
50877349Sgshapiro		}
50964562Sgshapiro		closelog();
51064562Sgshapiro#  ifdef LOG_MAIL
51164562Sgshapiro		openlog(sysloglabel, LOG_PID, LOG_MAIL);
51264562Sgshapiro#  else /* LOG_MAIL */
51364562Sgshapiro		openlog(sysloglabel, LOG_PID);
51464562Sgshapiro#  endif /* LOG_MAIL */
51577349Sgshapiro	}
51664562Sgshapiro#endif /* LOG */
51764562Sgshapiro
51838032Speter	/* set up the blank envelope */
51938032Speter	BlankEnvelope.e_puthdr = putheader;
52038032Speter	BlankEnvelope.e_putbody = putbody;
52138032Speter	BlankEnvelope.e_xfp = NULL;
52238032Speter	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
52338032Speter	CurEnv = &BlankEnvelope;
52438032Speter	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
52538032Speter
52638032Speter	/*
52738032Speter	**  Set default values for variables.
52838032Speter	**	These cannot be in initialized data space.
52938032Speter	*/
53038032Speter
53138032Speter	setdefaults(&BlankEnvelope);
53290792Sgshapiro	initmacros(&BlankEnvelope);
53338032Speter
53490792Sgshapiro	/* reset macro */
53590792Sgshapiro	set_op_mode(OpMode);
536159609Sgshapiro	if (OpMode == MD_DAEMON)
537159609Sgshapiro		DaemonPid = CurrentPid;	/* needed for finis() to work */
53838032Speter
53938032Speter	pw = sm_getpwuid(RealUid);
54038032Speter	if (pw != NULL)
541168515Sgshapiro		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof(rnamebuf));
54238032Speter	else
543168515Sgshapiro		(void) sm_snprintf(rnamebuf, sizeof(rnamebuf), "Unknown UID %d",
54490792Sgshapiro				   (int) RealUid);
54564562Sgshapiro
54638032Speter	RealUserName = rnamebuf;
54738032Speter
54838032Speter	if (tTd(0, 101))
54938032Speter	{
55090792Sgshapiro		sm_dprintf("Version %s\n", Version);
55190792Sgshapiro		finis(false, true, EX_OK);
55290792Sgshapiro		/* NOTREACHED */
55338032Speter	}
55438032Speter
55538032Speter	/*
55690792Sgshapiro	**  if running non-set-user-ID binary as non-root, pretend
55738032Speter	**  we are the RunAsUid
55838032Speter	*/
55977349Sgshapiro
56038032Speter	if (RealUid != 0 && geteuid() == RealUid)
56138032Speter	{
56238032Speter		if (tTd(47, 1))
56390792Sgshapiro			sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
56490792Sgshapiro				   (int) RealUid);
56538032Speter		RunAsUid = RealUid;
56638032Speter	}
56738032Speter	else if (geteuid() != 0)
56838032Speter		RunAsUid = geteuid();
56938032Speter
57090792Sgshapiro	EffGid = getegid();
57190792Sgshapiro	if (RealUid != 0 && EffGid == RealGid)
57238032Speter		RunAsGid = RealGid;
57338032Speter
57438032Speter	if (tTd(47, 5))
57538032Speter	{
57690792Sgshapiro		sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
57790792Sgshapiro			   (int) geteuid(), (int) getuid(),
57890792Sgshapiro			   (int) getegid(), (int) getgid());
57990792Sgshapiro		sm_dprintf("main: RunAsUser = %d:%d\n",
58090792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
58138032Speter	}
58238032Speter
58338032Speter	/* save command line arguments */
58464562Sgshapiro	j = 0;
58538032Speter	for (av = argv; *av != NULL; )
58664562Sgshapiro		j += strlen(*av++) + 1;
587168515Sgshapiro	SaveArgv = (char **) xalloc(sizeof(char *) * (argc + 1));
58864562Sgshapiro	CommandLineArgs = xalloc(j);
58938032Speter	p = CommandLineArgs;
59038032Speter	for (av = argv, i = 0; *av != NULL; )
59138032Speter	{
59264562Sgshapiro		int h;
59364562Sgshapiro
59438032Speter		SaveArgv[i++] = newstr(*av);
59538032Speter		if (av != argv)
59638032Speter			*p++ = ' ';
59790792Sgshapiro		(void) sm_strlcpy(p, *av++, j);
59864562Sgshapiro		h = strlen(p);
59964562Sgshapiro		p += h;
60064562Sgshapiro		j -= h + 1;
60138032Speter	}
60238032Speter	SaveArgv[i] = NULL;
60338032Speter
60438032Speter	if (tTd(0, 1))
60538032Speter	{
60638032Speter		extern char *CompileOptions[];
60738032Speter
60890792Sgshapiro		sm_dprintf("Version %s\n Compiled with:", Version);
60990792Sgshapiro		sm_printoptions(CompileOptions);
61038032Speter	}
61138032Speter	if (tTd(0, 10))
61238032Speter	{
61338032Speter		extern char *OsCompileOptions[];
61438032Speter
61590792Sgshapiro		sm_dprintf("    OS Defines:");
61690792Sgshapiro		sm_printoptions(OsCompileOptions);
61738032Speter#ifdef _PATH_UNIX
61890792Sgshapiro		sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
61964562Sgshapiro#endif /* _PATH_UNIX */
62090792Sgshapiro
62190792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MSP)\n",
62290792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
62390792Sgshapiro				     conffile));
62490792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MTA)\n",
62590792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
62690792Sgshapiro				     conffile));
62790792Sgshapiro		sm_dprintf("      Pid file:\t%s (default)\n", PidFile);
62838032Speter	}
62938032Speter
63090792Sgshapiro	if (tTd(0, 12))
63190792Sgshapiro	{
63290792Sgshapiro		extern char *SmCompileOptions[];
63338032Speter
63490792Sgshapiro		sm_dprintf(" libsm Defines:");
63590792Sgshapiro		sm_printoptions(SmCompileOptions);
63690792Sgshapiro	}
63790792Sgshapiro
63890792Sgshapiro	if (tTd(0, 13))
63990792Sgshapiro	{
64090792Sgshapiro		extern char *FFRCompileOptions[];
64190792Sgshapiro
64290792Sgshapiro		sm_dprintf("   FFR Defines:");
64390792Sgshapiro		sm_printoptions(FFRCompileOptions);
64490792Sgshapiro	}
64590792Sgshapiro
64638032Speter	/* clear sendmail's environment */
64738032Speter	ExternalEnviron = environ;
64838032Speter	emptyenviron[0] = NULL;
64938032Speter	environ = emptyenviron;
65038032Speter
65138032Speter	/*
65242575Speter	**  restore any original TZ setting until TimeZoneSpec has been
65342575Speter	**  determined - or early log messages may get bogus time stamps
65438032Speter	*/
65590792Sgshapiro
65638032Speter	if ((p = getextenv("TZ")) != NULL)
65738032Speter	{
65838032Speter		char *tz;
65938032Speter		int tzlen;
66038032Speter
66190792Sgshapiro		/* XXX check for reasonable length? */
66238032Speter		tzlen = strlen(p) + 4;
66338032Speter		tz = xalloc(tzlen);
66490792Sgshapiro		(void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
66590792Sgshapiro
66690792Sgshapiro		/* XXX check return code? */
66764562Sgshapiro		(void) putenv(tz);
66838032Speter	}
66938032Speter
67038032Speter	/* prime the child environment */
671157001Sgshapiro	sm_setuserenv("AGENT", "sendmail");
67238032Speter
67390792Sgshapiro	(void) sm_signal(SIGPIPE, SIG_IGN);
67438032Speter	OldUmask = umask(022);
67538032Speter	FullName = getextenv("NAME");
67695154Sgshapiro	if (FullName != NULL)
67795154Sgshapiro		FullName = newstr(FullName);
67838032Speter
67938032Speter	/*
68038032Speter	**  Initialize name server if it is going to be used.
68138032Speter	*/
68238032Speter
68338032Speter#if NAMED_BIND
68438032Speter	if (!bitset(RES_INIT, _res.options))
68564562Sgshapiro		(void) res_init();
68638032Speter	if (tTd(8, 8))
68738032Speter		_res.options |= RES_DEBUG;
68838032Speter	else
68938032Speter		_res.options &= ~RES_DEBUG;
69038032Speter# ifdef RES_NOALIASES
69138032Speter	_res.options |= RES_NOALIASES;
69264562Sgshapiro# endif /* RES_NOALIASES */
69364562Sgshapiro	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
69464562Sgshapiro	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
69564562Sgshapiro	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
69664562Sgshapiro	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
69764562Sgshapiro	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
69864562Sgshapiro	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
69964562Sgshapiro#endif /* NAMED_BIND */
70038032Speter
70138032Speter	errno = 0;
70238032Speter	from = NULL;
70338032Speter
70438032Speter	/* initialize some macros, etc. */
70590792Sgshapiro	init_vendor_macros(&BlankEnvelope);
70638032Speter
70738032Speter	/* version */
70890792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
70938032Speter
71038032Speter	/* hostname */
711168515Sgshapiro	hp = myhostname(jbuf, sizeof(jbuf));
71238032Speter	if (jbuf[0] != '\0')
71338032Speter	{
71490792Sgshapiro		struct utsname utsname;
71538032Speter
71638032Speter		if (tTd(0, 4))
71790792Sgshapiro			sm_dprintf("Canonical name: %s\n", jbuf);
71890792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
71990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
72038032Speter		setclass('w', jbuf);
72138032Speter
72238032Speter		p = strchr(jbuf, '.');
723132943Sgshapiro		if (p != NULL && p[1] != '\0')
724132943Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', &p[1]);
72538032Speter
72638032Speter		if (uname(&utsname) >= 0)
72738032Speter			p = utsname.nodename;
72838032Speter		else
72938032Speter		{
73038032Speter			if (tTd(0, 22))
73190792Sgshapiro				sm_dprintf("uname failed (%s)\n",
73290792Sgshapiro					   sm_errstring(errno));
73338032Speter			makelower(jbuf);
73438032Speter			p = jbuf;
73538032Speter		}
73638032Speter		if (tTd(0, 4))
73790792Sgshapiro			sm_dprintf(" UUCP nodename: %s\n", p);
73890792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
73938032Speter		setclass('k', p);
74038032Speter		setclass('w', p);
74138032Speter	}
74238032Speter	if (hp != NULL)
74338032Speter	{
74438032Speter		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
74538032Speter		{
74638032Speter			if (tTd(0, 4))
74790792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", *av);
74838032Speter			setclass('w', *av);
74938032Speter		}
75064562Sgshapiro#if NETINET || NETINET6
75190792Sgshapiro		for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
75238032Speter		{
75364562Sgshapiro# if NETINET6
75464562Sgshapiro			char *addr;
75564562Sgshapiro			char buf6[INET6_ADDRSTRLEN];
75664562Sgshapiro			struct in6_addr ia6;
75764562Sgshapiro# endif /* NETINET6 */
75864562Sgshapiro# if NETINET
75964562Sgshapiro			struct in_addr ia;
76064562Sgshapiro# endif /* NETINET */
76164562Sgshapiro			char ipbuf[103];
76264562Sgshapiro
76364562Sgshapiro			ipbuf[0] = '\0';
76464562Sgshapiro			switch (hp->h_addrtype)
76538032Speter			{
76664562Sgshapiro# if NETINET
76764562Sgshapiro			  case AF_INET:
76864562Sgshapiro				if (hp->h_length != INADDRSZ)
76964562Sgshapiro					break;
77038032Speter
77164562Sgshapiro				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
772168515Sgshapiro				(void) sm_snprintf(ipbuf, sizeof(ipbuf),
77390792Sgshapiro						   "[%.100s]", inet_ntoa(ia));
77464562Sgshapiro				break;
77564562Sgshapiro# endif /* NETINET */
77664562Sgshapiro
77764562Sgshapiro# if NETINET6
77864562Sgshapiro			  case AF_INET6:
77964562Sgshapiro				if (hp->h_length != IN6ADDRSZ)
78064562Sgshapiro					break;
78164562Sgshapiro
78264562Sgshapiro				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
783168515Sgshapiro				addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
78464562Sgshapiro				if (addr != NULL)
785168515Sgshapiro					(void) sm_snprintf(ipbuf, sizeof(ipbuf),
78690792Sgshapiro							   "[%.100s]", addr);
78764562Sgshapiro				break;
78864562Sgshapiro# endif /* NETINET6 */
78938032Speter			}
79064562Sgshapiro			if (ipbuf[0] == '\0')
79164562Sgshapiro				break;
79264562Sgshapiro
79364562Sgshapiro			if (tTd(0, 4))
79490792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", ipbuf);
79564562Sgshapiro			setclass('w', ipbuf);
79638032Speter		}
79764562Sgshapiro#endif /* NETINET || NETINET6 */
79890792Sgshapiro#if NETINET6
79971345Sgshapiro		freehostent(hp);
80071345Sgshapiro		hp = NULL;
80190792Sgshapiro#endif /* NETINET6 */
80238032Speter	}
80338032Speter
80438032Speter	/* current time */
80590792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
80690792Sgshapiro
80764562Sgshapiro	/* current load average */
80890792Sgshapiro	sm_getla();
80938032Speter
81038032Speter	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
81138032Speter	QueueLimitSender = (QUEUE_CHAR *) NULL;
81238032Speter	QueueLimitId = (QUEUE_CHAR *) NULL;
81390792Sgshapiro	QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
81438032Speter
81538032Speter	/*
81642575Speter	**  Crack argv.
81738032Speter	*/
81838032Speter
81938032Speter	optind = 1;
82038032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
82138032Speter	{
82238032Speter		switch (j)
82338032Speter		{
82438032Speter		  case 'b':	/* operations mode */
82590792Sgshapiro			/* already done */
82690792Sgshapiro			break;
82738032Speter
82890792Sgshapiro		  case 'A':	/* use Alternate sendmail/submit.cf */
82990792Sgshapiro			cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
83090792Sgshapiro						  : SM_GET_SENDMAIL_CF;
83138032Speter			break;
83238032Speter
83338032Speter		  case 'B':	/* body type */
83490792Sgshapiro			CHECK_AGAINST_OPMODE(j);
83590792Sgshapiro			BlankEnvelope.e_bodytype = newstr(optarg);
83638032Speter			break;
83738032Speter
83838032Speter		  case 'C':	/* select configuration file (already done) */
83938032Speter			if (RealUid != 0)
84090792Sgshapiro				warn_C_flag = true;
84190792Sgshapiro			conffile = newstr(optarg);
84290792Sgshapiro			dp = drop_privileges(true);
84364562Sgshapiro			setstat(dp);
84490792Sgshapiro			safecf = false;
84538032Speter			break;
84638032Speter
847132943Sgshapiro		  case 'D':
84890792Sgshapiro		  case 'd':	/* debugging */
84990792Sgshapiro			/* already done */
85038032Speter			break;
85138032Speter
85238032Speter		  case 'f':	/* from address */
85338032Speter		  case 'r':	/* obsolete -f flag */
85490792Sgshapiro			CHECK_AGAINST_OPMODE(j);
85538032Speter			if (from != NULL)
85638032Speter			{
85738032Speter				usrerr("More than one \"from\" person");
85838032Speter				ExitStat = EX_USAGE;
85938032Speter				break;
86038032Speter			}
861110560Sgshapiro			if (optarg[0] == '\0')
862110560Sgshapiro				from = newstr("<>");
863110560Sgshapiro			else
864110560Sgshapiro				from = newstr(denlstring(optarg, true, true));
86538032Speter			if (strcmp(RealUserName, from) != 0)
86638032Speter				warn_f_flag = j;
86738032Speter			break;
86838032Speter
86938032Speter		  case 'F':	/* set full name */
87090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
87138032Speter			FullName = newstr(optarg);
87238032Speter			break;
87338032Speter
87464562Sgshapiro		  case 'G':	/* relay (gateway) submission */
87564562Sgshapiro			/* already set */
87690792Sgshapiro			CHECK_AGAINST_OPMODE(j);
87764562Sgshapiro			break;
87864562Sgshapiro
87938032Speter		  case 'h':	/* hop count */
88090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
88190792Sgshapiro			BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
88290792Sgshapiro								  10);
883168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "%d",
88490792Sgshapiro					   BlankEnvelope.e_hopcount);
88590792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
88690792Sgshapiro
88738032Speter			if (*ep)
88838032Speter			{
88938032Speter				usrerr("Bad hop count (%s)", optarg);
89038032Speter				ExitStat = EX_USAGE;
89138032Speter			}
89238032Speter			break;
89364562Sgshapiro
89464562Sgshapiro		  case 'L':	/* program label */
89564562Sgshapiro			/* already set */
89664562Sgshapiro			break;
89764562Sgshapiro
89838032Speter		  case 'n':	/* don't alias */
89990792Sgshapiro			CHECK_AGAINST_OPMODE(j);
90090792Sgshapiro			NoAlias = true;
90138032Speter			break;
90238032Speter
90338032Speter		  case 'N':	/* delivery status notifications */
90490792Sgshapiro			CHECK_AGAINST_OPMODE(j);
90538032Speter			DefaultNotify |= QHASNOTIFY;
90690792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
90790792Sgshapiro				macid("{dsn_notify}"), optarg);
90890792Sgshapiro			if (sm_strcasecmp(optarg, "never") == 0)
90938032Speter				break;
91038032Speter			for (p = optarg; p != NULL; optarg = p)
91138032Speter			{
91238032Speter				p = strchr(p, ',');
91338032Speter				if (p != NULL)
91438032Speter					*p++ = '\0';
91590792Sgshapiro				if (sm_strcasecmp(optarg, "success") == 0)
91638032Speter					DefaultNotify |= QPINGONSUCCESS;
91790792Sgshapiro				else if (sm_strcasecmp(optarg, "failure") == 0)
91838032Speter					DefaultNotify |= QPINGONFAILURE;
91990792Sgshapiro				else if (sm_strcasecmp(optarg, "delay") == 0)
92038032Speter					DefaultNotify |= QPINGONDELAY;
92138032Speter				else
92238032Speter				{
92338032Speter					usrerr("Invalid -N argument");
92438032Speter					ExitStat = EX_USAGE;
92538032Speter				}
92638032Speter			}
92738032Speter			break;
92838032Speter
92938032Speter		  case 'o':	/* set option */
93090792Sgshapiro			setoption(*optarg, optarg + 1, false, true,
93190792Sgshapiro				  &BlankEnvelope);
93238032Speter			break;
93338032Speter
93438032Speter		  case 'O':	/* set option (long form) */
93590792Sgshapiro			setoption(' ', optarg, false, true, &BlankEnvelope);
93638032Speter			break;
93738032Speter
93838032Speter		  case 'p':	/* set protocol */
93990792Sgshapiro			CHECK_AGAINST_OPMODE(j);
94038032Speter			p = strchr(optarg, ':');
94138032Speter			if (p != NULL)
94238032Speter			{
94338032Speter				*p++ = '\0';
94438032Speter				if (*p != '\0')
94538032Speter				{
946120256Sgshapiro					i = strlen(p) + 1;
947120256Sgshapiro					ep = sm_malloc_x(i);
948120256Sgshapiro					cleanstrcpy(ep, p, i);
94990792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
95090792Sgshapiro						  A_HEAP, 's', ep);
95138032Speter				}
95238032Speter			}
95338032Speter			if (*optarg != '\0')
95438032Speter			{
955120256Sgshapiro				i = strlen(optarg) + 1;
956120256Sgshapiro				ep = sm_malloc_x(i);
957120256Sgshapiro				cleanstrcpy(ep, optarg, i);
95890792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_HEAP,
95990792Sgshapiro					  'r', ep);
96038032Speter			}
96138032Speter			break;
96238032Speter
96390792Sgshapiro		  case 'Q':	/* change quarantining on queued items */
96490792Sgshapiro			/* sanity check */
96590792Sgshapiro			if (OpMode != MD_DELIVER &&
96690792Sgshapiro			    OpMode != MD_QUEUERUN)
96790792Sgshapiro			{
96890792Sgshapiro				usrerr("Can not use -Q with -b%c", OpMode);
96990792Sgshapiro				ExitStat = EX_USAGE;
97090792Sgshapiro				break;
97190792Sgshapiro			}
97290792Sgshapiro
97390792Sgshapiro			if (OpMode == MD_DELIVER)
97490792Sgshapiro				set_op_mode(MD_QUEUERUN);
97590792Sgshapiro
97690792Sgshapiro			FullName = NULL;
97790792Sgshapiro
97890792Sgshapiro			quarantining = newstr(optarg);
97990792Sgshapiro			break;
98090792Sgshapiro
98138032Speter		  case 'q':	/* run queue files at intervals */
98264562Sgshapiro			/* sanity check */
98364562Sgshapiro			if (OpMode != MD_DELIVER &&
98464562Sgshapiro			    OpMode != MD_DAEMON &&
98564562Sgshapiro			    OpMode != MD_FGDAEMON &&
98664562Sgshapiro			    OpMode != MD_PRINT &&
98790792Sgshapiro			    OpMode != MD_PRINTNQE &&
98864562Sgshapiro			    OpMode != MD_QUEUERUN)
98964562Sgshapiro			{
99064562Sgshapiro				usrerr("Can not use -q with -b%c", OpMode);
99164562Sgshapiro				ExitStat = EX_USAGE;
99264562Sgshapiro				break;
99364562Sgshapiro			}
99464562Sgshapiro
99564562Sgshapiro			/* don't override -bd, -bD or -bp */
99664562Sgshapiro			if (OpMode == MD_DELIVER)
99790792Sgshapiro				set_op_mode(MD_QUEUERUN);
99864562Sgshapiro
99938032Speter			FullName = NULL;
100090792Sgshapiro			negate = optarg[0] == '!';
100190792Sgshapiro			if (negate)
100290792Sgshapiro			{
100390792Sgshapiro				/* negate meaning of pattern match */
100490792Sgshapiro				optarg++; /* skip '!' for next switch */
100590792Sgshapiro			}
100664562Sgshapiro
100738032Speter			switch (optarg[0])
100838032Speter			{
100990792Sgshapiro			  case 'G': /* Limit by queue group name */
101090792Sgshapiro				if (negate)
101190792Sgshapiro				{
101290792Sgshapiro					usrerr("Can not use -q!G");
101390792Sgshapiro					ExitStat = EX_USAGE;
101490792Sgshapiro					break;
101590792Sgshapiro				}
101690792Sgshapiro				if (queuegroup != NULL)
101790792Sgshapiro				{
101890792Sgshapiro					usrerr("Can not use multiple -qG options");
101990792Sgshapiro					ExitStat = EX_USAGE;
102090792Sgshapiro					break;
102190792Sgshapiro				}
102290792Sgshapiro				queuegroup = newstr(&optarg[1]);
102390792Sgshapiro				break;
102490792Sgshapiro
102590792Sgshapiro			  case 'I': /* Limit by ID */
1026168515Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof(*new));
102738032Speter				new->queue_match = newstr(&optarg[1]);
102890792Sgshapiro				new->queue_negate = negate;
102938032Speter				new->queue_next = QueueLimitId;
103038032Speter				QueueLimitId = new;
103138032Speter				break;
103238032Speter
103390792Sgshapiro			  case 'R': /* Limit by recipient */
1034168515Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof(*new));
103538032Speter				new->queue_match = newstr(&optarg[1]);
103690792Sgshapiro				new->queue_negate = negate;
103738032Speter				new->queue_next = QueueLimitRecipient;
103838032Speter				QueueLimitRecipient = new;
103938032Speter				break;
104038032Speter
104190792Sgshapiro			  case 'S': /* Limit by sender */
1042168515Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof(*new));
104338032Speter				new->queue_match = newstr(&optarg[1]);
104490792Sgshapiro				new->queue_negate = negate;
104538032Speter				new->queue_next = QueueLimitSender;
104638032Speter				QueueLimitSender = new;
104738032Speter				break;
104838032Speter
104990792Sgshapiro			  case 'f': /* foreground queue run */
105090792Sgshapiro				foregroundqueue  = true;
105190792Sgshapiro				break;
105290792Sgshapiro
105390792Sgshapiro			  case 'Q': /* Limit by quarantine message */
105490792Sgshapiro				if (optarg[1] != '\0')
105590792Sgshapiro				{
1056168515Sgshapiro					new = (QUEUE_CHAR *) xalloc(sizeof(*new));
105790792Sgshapiro					new->queue_match = newstr(&optarg[1]);
105890792Sgshapiro					new->queue_negate = negate;
105990792Sgshapiro					new->queue_next = QueueLimitQuarantine;
106090792Sgshapiro					QueueLimitQuarantine = new;
106190792Sgshapiro				}
106290792Sgshapiro				QueueMode = QM_QUARANTINE;
106390792Sgshapiro				break;
106490792Sgshapiro
106590792Sgshapiro			  case 'L': /* act on lost items */
106690792Sgshapiro				QueueMode = QM_LOST;
106790792Sgshapiro				break;
106890792Sgshapiro
106990792Sgshapiro			  case 'p': /* Persistent queue */
107090792Sgshapiro				queuepersistent = true;
107190792Sgshapiro				if (QueueIntvl == 0)
107290792Sgshapiro					QueueIntvl = 1;
107390792Sgshapiro				if (optarg[1] == '\0')
107490792Sgshapiro					break;
107590792Sgshapiro				++optarg;
107690792Sgshapiro				/* FALLTHROUGH */
107790792Sgshapiro
107838032Speter			  default:
107964562Sgshapiro				i = Errors;
108038032Speter				QueueIntvl = convtime(optarg, 'm');
108198841Sgshapiro				if (QueueIntvl < 0)
108298841Sgshapiro				{
108398841Sgshapiro					usrerr("Invalid -q value");
108498841Sgshapiro					ExitStat = EX_USAGE;
108598841Sgshapiro				}
108664562Sgshapiro
108764562Sgshapiro				/* check for bad conversion */
108864562Sgshapiro				if (i < Errors)
108964562Sgshapiro					ExitStat = EX_USAGE;
109038032Speter				break;
109138032Speter			}
109238032Speter			break;
109338032Speter
109438032Speter		  case 'R':	/* DSN RET: what to return */
109590792Sgshapiro			CHECK_AGAINST_OPMODE(j);
109690792Sgshapiro			if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
109738032Speter			{
109838032Speter				usrerr("Duplicate -R flag");
109938032Speter				ExitStat = EX_USAGE;
110038032Speter				break;
110138032Speter			}
110290792Sgshapiro			BlankEnvelope.e_flags |= EF_RET_PARAM;
110390792Sgshapiro			if (sm_strcasecmp(optarg, "hdrs") == 0)
110490792Sgshapiro				BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
110590792Sgshapiro			else if (sm_strcasecmp(optarg, "full") != 0)
110638032Speter			{
110738032Speter				usrerr("Invalid -R value");
110838032Speter				ExitStat = EX_USAGE;
110938032Speter			}
111090792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
111190792Sgshapiro				  macid("{dsn_ret}"), optarg);
111238032Speter			break;
111338032Speter
111438032Speter		  case 't':	/* read recipients from message */
111590792Sgshapiro			CHECK_AGAINST_OPMODE(j);
111690792Sgshapiro			GrabTo = true;
111738032Speter			break;
111838032Speter
111938032Speter		  case 'V':	/* DSN ENVID: set "original" envelope id */
112090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
112138032Speter			if (!xtextok(optarg))
112238032Speter			{
112338032Speter				usrerr("Invalid syntax in -V flag");
112438032Speter				ExitStat = EX_USAGE;
112538032Speter			}
112638032Speter			else
112764562Sgshapiro			{
112890792Sgshapiro				BlankEnvelope.e_envid = newstr(optarg);
112990792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
113090792Sgshapiro					  macid("{dsn_envid}"), optarg);
113164562Sgshapiro			}
113238032Speter			break;
113338032Speter
113438032Speter		  case 'X':	/* traffic log file */
113590792Sgshapiro			dp = drop_privileges(true);
113664562Sgshapiro			setstat(dp);
113764562Sgshapiro			if (stat(optarg, &traf_st) == 0 &&
113864562Sgshapiro			    S_ISFIFO(traf_st.st_mode))
113990792Sgshapiro				TrafficLogFile = sm_io_open(SmFtStdio,
114090792Sgshapiro							    SM_TIME_DEFAULT,
114190792Sgshapiro							    optarg,
114290792Sgshapiro							    SM_IO_WRONLY, NULL);
114364562Sgshapiro			else
114490792Sgshapiro				TrafficLogFile = sm_io_open(SmFtStdio,
114590792Sgshapiro							    SM_TIME_DEFAULT,
114690792Sgshapiro							    optarg,
114790792Sgshapiro							    SM_IO_APPEND, NULL);
114838032Speter			if (TrafficLogFile == NULL)
114938032Speter			{
115038032Speter				syserr("cannot open %s", optarg);
115138032Speter				ExitStat = EX_CANTCREAT;
115238032Speter				break;
115338032Speter			}
115490792Sgshapiro			(void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
115590792Sgshapiro					     NULL, SM_IO_LBF, 0);
115638032Speter			break;
115738032Speter
115838032Speter			/* compatibility flags */
115938032Speter		  case 'c':	/* connect to non-local mailers */
116038032Speter		  case 'i':	/* don't let dot stop me */
116138032Speter		  case 'm':	/* send to me too */
116238032Speter		  case 'T':	/* set timeout interval */
116338032Speter		  case 'v':	/* give blow-by-blow description */
116490792Sgshapiro			setoption(j, "T", false, true, &BlankEnvelope);
116538032Speter			break;
116638032Speter
116738032Speter		  case 'e':	/* error message disposition */
116838032Speter		  case 'M':	/* define macro */
116990792Sgshapiro			setoption(j, optarg, false, true, &BlankEnvelope);
117038032Speter			break;
117138032Speter
117238032Speter		  case 's':	/* save From lines in headers */
117390792Sgshapiro			setoption('f', "T", false, true, &BlankEnvelope);
117438032Speter			break;
117538032Speter
117664562Sgshapiro#ifdef DBM
117738032Speter		  case 'I':	/* initialize alias DBM file */
117890792Sgshapiro			set_op_mode(MD_INITALIAS);
117938032Speter			break;
118064562Sgshapiro#endif /* DBM */
118138032Speter
118264562Sgshapiro#if defined(__osf__) || defined(_AIX3)
118338032Speter		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
118438032Speter			break;
118564562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */
118664562Sgshapiro#if defined(sony_news)
118738032Speter		  case 'E':
118838032Speter		  case 'J':	/* ignore flags for Japanese code conversion
118964562Sgshapiro				   implemented on Sony NEWS */
119038032Speter			break;
119164562Sgshapiro#endif /* defined(sony_news) */
119238032Speter
119338032Speter		  default:
119490792Sgshapiro			finis(true, true, EX_USAGE);
119590792Sgshapiro			/* NOTREACHED */
119638032Speter			break;
119738032Speter		}
119838032Speter	}
119938032Speter
120090792Sgshapiro	/* if we've had errors so far, exit now */
1201203004Sgshapiro	if ((ExitStat != EX_OK && OpMode != MD_TEST && OpMode != MD_CHECKCONFIG) ||
120290792Sgshapiro	    ExitStat == EX_OSERR)
120364562Sgshapiro	{
120490792Sgshapiro		finis(false, true, ExitStat);
120590792Sgshapiro		/* NOTREACHED */
120664562Sgshapiro	}
120790792Sgshapiro
120890792Sgshapiro	if (bitset(SUBMIT_MTA, SubmitMode))
120964562Sgshapiro	{
121098841Sgshapiro		/* If set daemon_flags on command line, don't reset it */
121198841Sgshapiro		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
121298841Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
121398841Sgshapiro				  macid("{daemon_flags}"), "CC f");
121464562Sgshapiro	}
121590792Sgshapiro	else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
121664562Sgshapiro	{
121790792Sgshapiro		SubmitMode = SUBMIT_MSA;
121898841Sgshapiro
121998841Sgshapiro		/* If set daemon_flags on command line, don't reset it */
122098841Sgshapiro		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
122198841Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
122298841Sgshapiro				  macid("{daemon_flags}"), "c u");
122364562Sgshapiro	}
122464562Sgshapiro
122538032Speter	/*
122638032Speter	**  Do basic initialization.
122738032Speter	**	Read system control file.
122838032Speter	**	Extract special fields for local use.
122938032Speter	*/
123038032Speter
123138032Speter#if XDEBUG
123238032Speter	checkfd012("before readcf");
123364562Sgshapiro#endif /* XDEBUG */
123490792Sgshapiro	vendor_pre_defaults(&BlankEnvelope);
123564562Sgshapiro
123690792Sgshapiro	readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
123790792Sgshapiro			 safecf, &BlankEnvelope);
123890792Sgshapiro#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
123990792Sgshapiro	ConfigFileRead = true;
124090792Sgshapiro#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
124190792Sgshapiro	vendor_post_defaults(&BlankEnvelope);
124238032Speter
124390792Sgshapiro	/* now we can complain about missing fds */
124490792Sgshapiro	if (MissingFds != 0 && LogLevel > 8)
124590792Sgshapiro	{
124690792Sgshapiro		char mbuf[MAXLINE];
124790792Sgshapiro
124890792Sgshapiro		mbuf[0] = '\0';
124990792Sgshapiro		if (bitset(1 << STDIN_FILENO, MissingFds))
1250168515Sgshapiro			(void) sm_strlcat(mbuf, ", stdin", sizeof(mbuf));
125190792Sgshapiro		if (bitset(1 << STDOUT_FILENO, MissingFds))
1252168515Sgshapiro			(void) sm_strlcat(mbuf, ", stdout", sizeof(mbuf));
125390792Sgshapiro		if (bitset(1 << STDERR_FILENO, MissingFds))
1254168515Sgshapiro			(void) sm_strlcat(mbuf, ", stderr", sizeof(mbuf));
125590792Sgshapiro
125690792Sgshapiro		/* Notice: fill_errno is from high above: fill_fd() */
125790792Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
125890792Sgshapiro			  "File descriptors missing on startup: %s; %s",
125990792Sgshapiro			  &mbuf[2], sm_errstring(fill_errno));
126090792Sgshapiro	}
126190792Sgshapiro
126277349Sgshapiro	/* Remove the ability for a normal user to send signals */
126390792Sgshapiro	if (RealUid != 0 && RealUid != geteuid())
126477349Sgshapiro	{
126577349Sgshapiro		uid_t new_uid = geteuid();
126677349Sgshapiro
126777349Sgshapiro#if HASSETREUID
126877349Sgshapiro		/*
126977349Sgshapiro		**  Since we can differentiate between uid and euid,
127077349Sgshapiro		**  make the uid a different user so the real user
127177349Sgshapiro		**  can't send signals.  However, it doesn't need to be
127277349Sgshapiro		**  root (euid has root).
127377349Sgshapiro		*/
127477349Sgshapiro
127577349Sgshapiro		if (new_uid == 0)
127677349Sgshapiro			new_uid = DefUid;
127777349Sgshapiro		if (tTd(47, 5))
127890792Sgshapiro			sm_dprintf("Changing real uid to %d\n", (int) new_uid);
127977349Sgshapiro		if (setreuid(new_uid, geteuid()) < 0)
128077349Sgshapiro		{
128177349Sgshapiro			syserr("main: setreuid(%d, %d) failed",
128277349Sgshapiro			       (int) new_uid, (int) geteuid());
128390792Sgshapiro			finis(false, true, EX_OSERR);
128477349Sgshapiro			/* NOTREACHED */
128577349Sgshapiro		}
128677349Sgshapiro		if (tTd(47, 10))
128790792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
128890792Sgshapiro				   (int) geteuid(), (int) getuid());
128977349Sgshapiro#else /* HASSETREUID */
129077349Sgshapiro		/*
129177349Sgshapiro		**  Have to change both effective and real so need to
129277349Sgshapiro		**  change them both to effective to keep privs.
129377349Sgshapiro		*/
129477349Sgshapiro
129577349Sgshapiro		if (tTd(47, 5))
129690792Sgshapiro			sm_dprintf("Changing uid to %d\n", (int) new_uid);
129777349Sgshapiro		if (setuid(new_uid) < 0)
129877349Sgshapiro		{
129977349Sgshapiro			syserr("main: setuid(%d) failed", (int) new_uid);
130090792Sgshapiro			finis(false, true, EX_OSERR);
130177349Sgshapiro			/* NOTREACHED */
130277349Sgshapiro		}
130377349Sgshapiro		if (tTd(47, 10))
130490792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
130590792Sgshapiro				   (int) geteuid(), (int) getuid());
130677349Sgshapiro#endif /* HASSETREUID */
130777349Sgshapiro	}
130877349Sgshapiro
130990792Sgshapiro#if NAMED_BIND
1310132943Sgshapiro	if (FallbackMX != NULL)
1311132943Sgshapiro		(void) getfallbackmxrr(FallbackMX);
131290792Sgshapiro#endif /* NAMED_BIND */
131390792Sgshapiro
1314223067Sgshapiro	if (SuperSafe == SAFE_INTERACTIVE && !SM_IS_INTERACTIVE(CurEnv->e_sendmode))
131590792Sgshapiro	{
131690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
131790792Sgshapiro				     "WARNING: SuperSafe=interactive should only be used with\n         DeliveryMode=interactive\n");
131890792Sgshapiro	}
131990792Sgshapiro
132090792Sgshapiro	if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
132190792Sgshapiro	{
132290792Sgshapiro		usrerr("Mail submission program cannot be used as daemon");
132390792Sgshapiro		finis(false, true, EX_USAGE);
132490792Sgshapiro	}
132590792Sgshapiro
132690792Sgshapiro	if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
132790792Sgshapiro	    OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
132890792Sgshapiro	    OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
132990792Sgshapiro		makeworkgroups();
133090792Sgshapiro
133177349Sgshapiro	/* set up the basic signal handlers */
133290792Sgshapiro	if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
133390792Sgshapiro		(void) sm_signal(SIGINT, intsig);
133490792Sgshapiro	(void) sm_signal(SIGTERM, intsig);
133577349Sgshapiro
133638032Speter	/* Enforce use of local time (null string overrides this) */
133738032Speter	if (TimeZoneSpec == NULL)
133838032Speter		unsetenv("TZ");
133938032Speter	else if (TimeZoneSpec[0] != '\0')
1340157001Sgshapiro		sm_setuserenv("TZ", TimeZoneSpec);
134138032Speter	else
1342157001Sgshapiro		sm_setuserenv("TZ", NULL);
134338032Speter	tzset();
134438032Speter
134590792Sgshapiro	/* initialize mailbox database */
134690792Sgshapiro	i = sm_mbdb_initialize(Mbdb);
134790792Sgshapiro	if (i != EX_OK)
134890792Sgshapiro	{
134990792Sgshapiro		usrerr("Can't initialize mailbox database \"%s\": %s",
135090792Sgshapiro		       Mbdb, sm_strexit(i));
135190792Sgshapiro		ExitStat = i;
135290792Sgshapiro	}
135390792Sgshapiro
135438032Speter	/* avoid denial-of-service attacks */
135538032Speter	resetlimits();
135638032Speter
135790792Sgshapiro	if (OpMode == MD_TEST)
135838032Speter	{
135990792Sgshapiro		/* can't be done after readcf if RunAs* is used */
136090792Sgshapiro		dp = drop_privileges(true);
136190792Sgshapiro		if (dp != EX_OK)
136290792Sgshapiro		{
136390792Sgshapiro			finis(false, true, dp);
136490792Sgshapiro			/* NOTREACHED */
136590792Sgshapiro		}
136690792Sgshapiro	}
136790792Sgshapiro	else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
136890792Sgshapiro	{
136938032Speter		/* drop privileges -- daemon mode done after socket/bind */
137090792Sgshapiro		dp = drop_privileges(false);
137164562Sgshapiro		setstat(dp);
137290792Sgshapiro		if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
137390792Sgshapiro		{
137490792Sgshapiro			usrerr("Mail submission program must have RunAsUser set to non root user");
137590792Sgshapiro			finis(false, true, EX_CONFIG);
137690792Sgshapiro			/* NOTREACHED */
137790792Sgshapiro		}
137838032Speter	}
137938032Speter
138064562Sgshapiro#if NAMED_BIND
138164562Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
138264562Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
138364562Sgshapiro#endif /* NAMED_BIND */
138464562Sgshapiro
138538032Speter	/*
138638032Speter	**  Find our real host name for future logging.
138738032Speter	*/
138838032Speter
138964562Sgshapiro	authinfo = getauthinfo(STDIN_FILENO, &forged);
139090792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
139138032Speter
139238032Speter	/* suppress error printing if errors mailed back or whatever */
139390792Sgshapiro	if (BlankEnvelope.e_errormode != EM_PRINT)
139490792Sgshapiro		HoldErrs = true;
139538032Speter
139638032Speter	/* set up the $=m class now, after .cf has a chance to redefine $m */
1397168515Sgshapiro	expand("\201m", jbuf, sizeof(jbuf), &BlankEnvelope);
139873188Sgshapiro	if (jbuf[0] != '\0')
139973188Sgshapiro		setclass('m', jbuf);
140038032Speter
140138032Speter	/* probe interfaces and locate any additional names */
140290792Sgshapiro	if (DontProbeInterfaces != DPI_PROBENONE)
140338032Speter		load_if_names();
140438032Speter
140590792Sgshapiro	if (tTd(0, 10))
140690792Sgshapiro	{
1407110560Sgshapiro		char pidpath[MAXPATHLEN];
1408110560Sgshapiro
140990792Sgshapiro		/* Now we know which .cf file we use */
141090792Sgshapiro		sm_dprintf("     Conf file:\t%s (selected)\n",
141190792Sgshapiro			   getcfname(OpMode, SubmitMode, cftype, conffile));
1412168515Sgshapiro		expand(PidFile, pidpath, sizeof(pidpath), &BlankEnvelope);
1413110560Sgshapiro		sm_dprintf("      Pid file:\t%s (selected)\n", pidpath);
141490792Sgshapiro	}
141590792Sgshapiro
141638032Speter	if (tTd(0, 1))
141738032Speter	{
141890792Sgshapiro		sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
141990792Sgshapiro		sm_dprintf("\n      (short domain name) $w = ");
1420132943Sgshapiro		xputs(sm_debug_file(), macvalue('w', &BlankEnvelope));
142190792Sgshapiro		sm_dprintf("\n  (canonical domain name) $j = ");
1422132943Sgshapiro		xputs(sm_debug_file(), macvalue('j', &BlankEnvelope));
142390792Sgshapiro		sm_dprintf("\n         (subdomain name) $m = ");
1424132943Sgshapiro		xputs(sm_debug_file(), macvalue('m', &BlankEnvelope));
142590792Sgshapiro		sm_dprintf("\n              (node name) $k = ");
1426132943Sgshapiro		xputs(sm_debug_file(), macvalue('k', &BlankEnvelope));
142790792Sgshapiro		sm_dprintf("\n========================================================\n\n");
142838032Speter	}
142938032Speter
143038032Speter	/*
143138032Speter	**  Do more command line checking -- these are things that
143238032Speter	**  have to modify the results of reading the config file.
143338032Speter	*/
143438032Speter
143538032Speter	/* process authorization warnings from command line */
143638032Speter	if (warn_C_flag)
143790792Sgshapiro		auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
143890792Sgshapiro			     RealUserName, conffile);
143964562Sgshapiro	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
144090792Sgshapiro		auth_warning(&BlankEnvelope, "Processed from queue %s",
144190792Sgshapiro			     QueueDir);
144290792Sgshapiro	if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
144390792Sgshapiro	    RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
144490792Sgshapiro		sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
144590792Sgshapiro			  (int) RealUid);
144638032Speter
144738032Speter	/* check body type for legality */
144890792Sgshapiro	i = check_bodytype(BlankEnvelope.e_bodytype);
144990792Sgshapiro	if (i == BODYTYPE_ILLEGAL)
145038032Speter	{
145190792Sgshapiro		usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
145290792Sgshapiro		BlankEnvelope.e_bodytype = NULL;
145338032Speter	}
145490792Sgshapiro	else if (i != BODYTYPE_NONE)
145590792Sgshapiro		SevenBitInput = (i == BODYTYPE_7BIT);
145638032Speter
145738032Speter	/* tweak default DSN notifications */
145838032Speter	if (DefaultNotify == 0)
145938032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
146038032Speter
146138032Speter	/* check for sane configuration level */
146238032Speter	if (ConfigLevel > MAXCONFIGLEVEL)
146338032Speter	{
146438032Speter		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
146590792Sgshapiro		       ConfigLevel, Version, MAXCONFIGLEVEL);
146638032Speter	}
146738032Speter
146838032Speter	/* need MCI cache to have persistence */
146938032Speter	if (HostStatDir != NULL && MaxMciCache == 0)
147038032Speter	{
147138032Speter		HostStatDir = NULL;
147290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
147390792Sgshapiro				     "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
147438032Speter	}
147538032Speter
147638032Speter	/* need HostStatusDir in order to have SingleThreadDelivery */
147738032Speter	if (SingleThreadDelivery && HostStatDir == NULL)
147838032Speter	{
147990792Sgshapiro		SingleThreadDelivery = false;
148090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
148190792Sgshapiro				     "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
148238032Speter	}
148338032Speter
1484157001Sgshapiro#if _FFR_MEMSTAT
1485157001Sgshapiro	j = sm_memstat_open();
1486157001Sgshapiro	if (j < 0 && (RefuseLowMem > 0 || QueueLowMem > 0) && LogLevel > 4)
1487157001Sgshapiro	{
1488157001Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
1489157001Sgshapiro			  "cannot get memory statistics, settings ignored, error=%d"
1490157001Sgshapiro			  , j);
1491157001Sgshapiro	}
1492157001Sgshapiro#endif /* _FFR_MEMSTAT */
1493157001Sgshapiro
149438032Speter	/* check for permissions */
149590792Sgshapiro	if (RealUid != 0 &&
149642575Speter	    RealUid != TrustedUid)
149738032Speter	{
149890792Sgshapiro		char *action = NULL;
149990792Sgshapiro
150090792Sgshapiro		switch (OpMode)
150190792Sgshapiro		{
150290792Sgshapiro		  case MD_QUEUERUN:
150390792Sgshapiro			if (quarantining != NULL)
150490792Sgshapiro				action = "quarantine jobs";
150590792Sgshapiro			else
1506132943Sgshapiro			{
1507132943Sgshapiro				/* Normal users can do a single queue run */
1508132943Sgshapiro				if (QueueIntvl == 0)
1509132943Sgshapiro					break;
1510132943Sgshapiro			}
151190792Sgshapiro
151290792Sgshapiro			/* but not persistent queue runners */
151390792Sgshapiro			if (action == NULL)
151490792Sgshapiro				action = "start a queue runner daemon";
151590792Sgshapiro			/* FALLTHROUGH */
151690792Sgshapiro
151790792Sgshapiro		  case MD_PURGESTAT:
151890792Sgshapiro			if (action == NULL)
151990792Sgshapiro				action = "purge host status";
152090792Sgshapiro			/* FALLTHROUGH */
152190792Sgshapiro
152290792Sgshapiro		  case MD_DAEMON:
152390792Sgshapiro		  case MD_FGDAEMON:
152490792Sgshapiro			if (action == NULL)
152590792Sgshapiro				action = "run daemon";
152690792Sgshapiro
152790792Sgshapiro			if (tTd(65, 1))
152890792Sgshapiro				sm_dprintf("Deny user %d attempt to %s\n",
152990792Sgshapiro					   (int) RealUid, action);
153090792Sgshapiro
153190792Sgshapiro			if (LogLevel > 1)
153290792Sgshapiro				sm_syslog(LOG_ALERT, NOQID,
153390792Sgshapiro					  "user %d attempted to %s",
153490792Sgshapiro					  (int) RealUid, action);
153590792Sgshapiro			HoldErrs = false;
153690792Sgshapiro			usrerr("Permission denied (real uid not trusted)");
153790792Sgshapiro			finis(false, true, EX_USAGE);
153890792Sgshapiro			/* NOTREACHED */
153990792Sgshapiro			break;
154090792Sgshapiro
154190792Sgshapiro		  case MD_VERIFY:
154290792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
154390792Sgshapiro			{
154490792Sgshapiro				/*
154590792Sgshapiro				**  If -bv and RestrictExpand,
154690792Sgshapiro				**  drop privs to prevent normal
154790792Sgshapiro				**  users from reading private
154890792Sgshapiro				**  aliases/forwards/:include:s
154990792Sgshapiro				*/
155090792Sgshapiro
155190792Sgshapiro				if (tTd(65, 1))
155290792Sgshapiro					sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
155390792Sgshapiro						   (int) RealUid);
155490792Sgshapiro
155590792Sgshapiro				dp = drop_privileges(true);
155690792Sgshapiro
155790792Sgshapiro				/* Fake address safety */
155890792Sgshapiro				if (tTd(65, 1))
155990792Sgshapiro					sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
156090792Sgshapiro				setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
156190792Sgshapiro
156290792Sgshapiro				if (dp != EX_OK)
156390792Sgshapiro				{
156490792Sgshapiro					if (tTd(65, 1))
156590792Sgshapiro						sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
156690792Sgshapiro							   (int) RealUid);
156790792Sgshapiro					CurEnv->e_id = NULL;
156890792Sgshapiro					finis(true, true, dp);
156990792Sgshapiro					/* NOTREACHED */
157090792Sgshapiro				}
157190792Sgshapiro			}
157290792Sgshapiro			break;
157390792Sgshapiro
157490792Sgshapiro		  case MD_TEST:
1575203004Sgshapiro		  case MD_CHECKCONFIG:
157690792Sgshapiro		  case MD_PRINT:
157790792Sgshapiro		  case MD_PRINTNQE:
157890792Sgshapiro		  case MD_FREEZE:
157990792Sgshapiro		  case MD_HOSTSTAT:
158090792Sgshapiro			/* Nothing special to check */
158190792Sgshapiro			break;
158290792Sgshapiro
158390792Sgshapiro		  case MD_INITALIAS:
158490792Sgshapiro			if (!wordinclass(RealUserName, 't'))
158590792Sgshapiro			{
158690792Sgshapiro				if (tTd(65, 1))
158790792Sgshapiro					sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
158890792Sgshapiro						   (int) RealUid);
158990792Sgshapiro				if (LogLevel > 1)
159090792Sgshapiro					sm_syslog(LOG_ALERT, NOQID,
159190792Sgshapiro						  "user %d attempted to rebuild the alias map",
159290792Sgshapiro						  (int) RealUid);
159390792Sgshapiro				HoldErrs = false;
159490792Sgshapiro				usrerr("Permission denied (real uid not trusted)");
159590792Sgshapiro				finis(false, true, EX_USAGE);
159690792Sgshapiro				/* NOTREACHED */
159790792Sgshapiro			}
159890792Sgshapiro			if (UseMSP)
159990792Sgshapiro			{
160090792Sgshapiro				HoldErrs = false;
160190792Sgshapiro				usrerr("User %d cannot rebuild aliases in mail submission program",
160290792Sgshapiro				       (int) RealUid);
160390792Sgshapiro				finis(false, true, EX_USAGE);
160490792Sgshapiro				/* NOTREACHED */
160590792Sgshapiro			}
160690792Sgshapiro			/* FALLTHROUGH */
160790792Sgshapiro
160890792Sgshapiro		  default:
160990792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
161090792Sgshapiro			    Verbose != 0)
161190792Sgshapiro			{
161290792Sgshapiro				/*
161390792Sgshapiro				**  If -v and RestrictExpand, reset
161490792Sgshapiro				**  Verbose to prevent normal users
161590792Sgshapiro				**  from seeing the expansion of
161690792Sgshapiro				**  aliases/forwards/:include:s
161790792Sgshapiro				*/
161890792Sgshapiro
161990792Sgshapiro				if (tTd(65, 1))
162090792Sgshapiro					sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
162190792Sgshapiro						   (int) RealUid);
162290792Sgshapiro				Verbose = 0;
162390792Sgshapiro			}
162490792Sgshapiro			break;
162590792Sgshapiro		}
162638032Speter	}
162738032Speter
162838032Speter	if (MeToo)
162938032Speter		BlankEnvelope.e_flags |= EF_METOO;
163038032Speter
163138032Speter	switch (OpMode)
163238032Speter	{
163338032Speter	  case MD_TEST:
163438032Speter		/* don't have persistent host status in test mode */
163538032Speter		HostStatDir = NULL;
1636203004Sgshapiro		/* FALLTHROUGH */
1637203004Sgshapiro
1638203004Sgshapiro	  case MD_CHECKCONFIG:
163938032Speter		if (Verbose == 0)
164038032Speter			Verbose = 2;
164190792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
164290792Sgshapiro		HoldErrs = false;
164338032Speter		break;
164438032Speter
164538032Speter	  case MD_VERIFY:
164690792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
164790792Sgshapiro		HoldErrs = false;
164838032Speter		/* arrange to exit cleanly on hangup signal */
164990792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
165090792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
165190792Sgshapiro		if (geteuid() != 0)
165290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
165390792Sgshapiro					     "Notice: -bv may give misleading output for non-privileged user\n");
165438032Speter		break;
165538032Speter
165638032Speter	  case MD_FGDAEMON:
165790792Sgshapiro		run_in_foreground = true;
165890792Sgshapiro		set_op_mode(MD_DAEMON);
165964562Sgshapiro		/* FALLTHROUGH */
166038032Speter
166138032Speter	  case MD_DAEMON:
166290792Sgshapiro		vendor_daemon_setup(&BlankEnvelope);
166338032Speter
166438032Speter		/* remove things that don't make sense in daemon mode */
166538032Speter		FullName = NULL;
166690792Sgshapiro		GrabTo = false;
166738032Speter
166838032Speter		/* arrange to restart on hangup signal */
166938032Speter		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
167038032Speter			sm_syslog(LOG_WARNING, NOQID,
167164562Sgshapiro				  "daemon invoked without full pathname; kill -1 won't work");
167238032Speter		break;
167338032Speter
167438032Speter	  case MD_INITALIAS:
167538032Speter		Verbose = 2;
167690792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
167790792Sgshapiro		HoldErrs = false;
167864562Sgshapiro		/* FALLTHROUGH */
167938032Speter
168038032Speter	  default:
168138032Speter		/* arrange to exit cleanly on hangup signal */
168290792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
168390792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
168438032Speter		break;
168538032Speter	}
168638032Speter
168738032Speter	/* special considerations for FullName */
168838032Speter	if (FullName != NULL)
168938032Speter	{
169038032Speter		char *full = NULL;
169138032Speter
169238032Speter		/* full names can't have newlines */
169364562Sgshapiro		if (strchr(FullName, '\n') != NULL)
169438032Speter		{
169590792Sgshapiro			full = newstr(denlstring(FullName, true, true));
169673188Sgshapiro			FullName = full;
169738032Speter		}
169873188Sgshapiro
169938032Speter		/* check for characters that may have to be quoted */
170038032Speter		if (!rfc822_string(FullName))
170138032Speter		{
170238032Speter			/*
170338032Speter			**  Quote a full name with special characters
170438032Speter			**  as a comment so crackaddr() doesn't destroy
170538032Speter			**  the name portion of the address.
170638032Speter			*/
170773188Sgshapiro
170890792Sgshapiro			FullName = addquotes(FullName, NULL);
170938032Speter			if (full != NULL)
171090792Sgshapiro				sm_free(full);  /* XXX */
171138032Speter		}
171238032Speter	}
171338032Speter
171438032Speter	/* do heuristic mode adjustment */
171538032Speter	if (Verbose)
171638032Speter	{
171738032Speter		/* turn off noconnect option */
171890792Sgshapiro		setoption('c', "F", true, false, &BlankEnvelope);
171938032Speter
172038032Speter		/* turn on interactive delivery */
172190792Sgshapiro		setoption('d', "", true, false, &BlankEnvelope);
172238032Speter	}
172338032Speter
172442575Speter#ifdef VENDOR_CODE
172542575Speter	/* check for vendor mismatch */
172642575Speter	if (VendorCode != VENDOR_CODE)
172742575Speter	{
172842575Speter		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
172942575Speter			getvendor(VENDOR_CODE), getvendor(VendorCode));
173042575Speter	}
173164562Sgshapiro#endif /* VENDOR_CODE */
173264562Sgshapiro
173338032Speter	/* check for out of date configuration level */
173438032Speter	if (ConfigLevel < MAXCONFIGLEVEL)
173538032Speter	{
173638032Speter		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
173738032Speter			Version, MAXCONFIGLEVEL, ConfigLevel);
173838032Speter	}
173938032Speter
174038032Speter	if (ConfigLevel < 3)
174190792Sgshapiro		UseErrorsTo = true;
174238032Speter
174338032Speter	/* set options that were previous macros */
174438032Speter	if (SmtpGreeting == NULL)
174538032Speter	{
174690792Sgshapiro		if (ConfigLevel < 7 &&
174790792Sgshapiro		    (p = macvalue('e', &BlankEnvelope)) != NULL)
174838032Speter			SmtpGreeting = newstr(p);
174938032Speter		else
175038032Speter			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
175138032Speter	}
175238032Speter	if (UnixFromLine == NULL)
175338032Speter	{
175490792Sgshapiro		if (ConfigLevel < 7 &&
175590792Sgshapiro		    (p = macvalue('l', &BlankEnvelope)) != NULL)
175638032Speter			UnixFromLine = newstr(p);
175738032Speter		else
175838032Speter			UnixFromLine = "From \201g  \201d";
175938032Speter	}
176064562Sgshapiro	SmtpError[0] = '\0';
176138032Speter
176238032Speter	/* our name for SMTP codes */
1763168515Sgshapiro	expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
176473188Sgshapiro	if (jbuf[0] == '\0')
176590792Sgshapiro		PSTRSET(MyHostName, "localhost");
176673188Sgshapiro	else
176790792Sgshapiro		PSTRSET(MyHostName, jbuf);
176873188Sgshapiro	if (strchr(MyHostName, '.') == NULL)
1769132943Sgshapiro		message("WARNING: local host name (%s) is not qualified; see cf/README: WHO AM I?",
177073188Sgshapiro			MyHostName);
177138032Speter
177238032Speter	/* make certain that this name is part of the $=w class */
177338032Speter	setclass('w', MyHostName);
177438032Speter
177590792Sgshapiro	/* fill in the structure of the *default* queue */
177690792Sgshapiro	st = stab("mqueue", ST_QUEUE, ST_FIND);
177790792Sgshapiro	if (st == NULL)
177890792Sgshapiro		syserr("No default queue (mqueue) defined");
177990792Sgshapiro	else
178090792Sgshapiro		set_def_queueval(st->s_quegrp, true);
178190792Sgshapiro
178238032Speter	/* the indices of built-in mailers */
178338032Speter	st = stab("local", ST_MAILER, ST_FIND);
178438032Speter	if (st != NULL)
178538032Speter		LocalMailer = st->s_mailer;
178638032Speter	else if (OpMode != MD_TEST || !warn_C_flag)
178738032Speter		syserr("No local mailer defined");
178838032Speter
178938032Speter	st = stab("prog", ST_MAILER, ST_FIND);
179038032Speter	if (st == NULL)
179138032Speter		syserr("No prog mailer defined");
179238032Speter	else
179338032Speter	{
179438032Speter		ProgMailer = st->s_mailer;
179538032Speter		clrbitn(M_MUSER, ProgMailer->m_flags);
179638032Speter	}
179738032Speter
179838032Speter	st = stab("*file*", ST_MAILER, ST_FIND);
179938032Speter	if (st == NULL)
180038032Speter		syserr("No *file* mailer defined");
180138032Speter	else
180238032Speter	{
180338032Speter		FileMailer = st->s_mailer;
180438032Speter		clrbitn(M_MUSER, FileMailer->m_flags);
180538032Speter	}
180638032Speter
180738032Speter	st = stab("*include*", ST_MAILER, ST_FIND);
180838032Speter	if (st == NULL)
180938032Speter		syserr("No *include* mailer defined");
181038032Speter	else
181138032Speter		InclMailer = st->s_mailer;
181238032Speter
181338032Speter	if (ConfigLevel < 6)
181438032Speter	{
181538032Speter		/* heuristic tweaking of local mailer for back compat */
181638032Speter		if (LocalMailer != NULL)
181738032Speter		{
181838032Speter			setbitn(M_ALIASABLE, LocalMailer->m_flags);
181938032Speter			setbitn(M_HASPWENT, LocalMailer->m_flags);
182038032Speter			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
182138032Speter			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
182238032Speter			setbitn(M_CHECKPROG, LocalMailer->m_flags);
182338032Speter			setbitn(M_CHECKFILE, LocalMailer->m_flags);
182438032Speter			setbitn(M_CHECKUDB, LocalMailer->m_flags);
182538032Speter		}
182638032Speter		if (ProgMailer != NULL)
182738032Speter			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
182838032Speter		if (FileMailer != NULL)
182938032Speter			setbitn(M_RUNASRCPT, FileMailer->m_flags);
183038032Speter	}
183138032Speter	if (ConfigLevel < 7)
183238032Speter	{
183338032Speter		if (LocalMailer != NULL)
183438032Speter			setbitn(M_VRFY250, LocalMailer->m_flags);
183538032Speter		if (ProgMailer != NULL)
183638032Speter			setbitn(M_VRFY250, ProgMailer->m_flags);
183738032Speter		if (FileMailer != NULL)
183838032Speter			setbitn(M_VRFY250, FileMailer->m_flags);
183938032Speter	}
184038032Speter
184138032Speter	/* MIME Content-Types that cannot be transfer encoded */
184238032Speter	setclass('n', "multipart/signed");
184338032Speter
184438032Speter	/* MIME message/xxx subtypes that can be treated as messages */
184538032Speter	setclass('s', "rfc822");
184638032Speter
184738032Speter	/* MIME Content-Transfer-Encodings that can be encoded */
184838032Speter	setclass('e', "7bit");
184938032Speter	setclass('e', "8bit");
185038032Speter	setclass('e', "binary");
185138032Speter
185238032Speter#ifdef USE_B_CLASS
185338032Speter	/* MIME Content-Types that should be treated as binary */
185438032Speter	setclass('b', "image");
185538032Speter	setclass('b', "audio");
185638032Speter	setclass('b', "video");
185738032Speter	setclass('b', "application/octet-stream");
185864562Sgshapiro#endif /* USE_B_CLASS */
185938032Speter
186042575Speter	/* MIME headers which have fields to check for overflow */
186190792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
186290792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
186342575Speter
186442575Speter	/* MIME headers to check for length overflow */
186590792Sgshapiro	setclass(macid("{checkMIMETextHeaders}"), "content-description");
186642575Speter
186742575Speter	/* MIME headers to check for overflow and rebalance */
186890792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-disposition");
186990792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-id");
187090792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
187190792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-type");
187290792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "mime-version");
187342575Speter
187490792Sgshapiro	/* Macros to save in the queue file -- don't remove any */
187590792Sgshapiro	setclass(macid("{persistentMacros}"), "r");
187690792Sgshapiro	setclass(macid("{persistentMacros}"), "s");
187790792Sgshapiro	setclass(macid("{persistentMacros}"), "_");
187890792Sgshapiro	setclass(macid("{persistentMacros}"), "{if_addr}");
187990792Sgshapiro	setclass(macid("{persistentMacros}"), "{daemon_flags}");
188064562Sgshapiro
188138032Speter	/* operate in queue directory */
188290792Sgshapiro	if (QueueDir == NULL || *QueueDir == '\0')
188338032Speter	{
188438032Speter		if (OpMode != MD_TEST)
188538032Speter		{
188638032Speter			syserr("QueueDirectory (Q) option must be set");
188738032Speter			ExitStat = EX_CONFIG;
188838032Speter		}
188938032Speter	}
189038032Speter	else
189138032Speter	{
189264562Sgshapiro		if (OpMode != MD_TEST)
189390792Sgshapiro			setup_queues(OpMode == MD_DAEMON);
189438032Speter	}
189538032Speter
189638032Speter	/* check host status directory for validity */
189790792Sgshapiro	if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
189838032Speter	{
189938032Speter		/* cannot use this value */
190090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
190190792Sgshapiro				     "Warning: Cannot use HostStatusDirectory = %s: %s\n",
190290792Sgshapiro				     HostStatDir, sm_errstring(errno));
190338032Speter		HostStatDir = NULL;
190438032Speter	}
190538032Speter
190690792Sgshapiro	if (OpMode == MD_QUEUERUN &&
190790792Sgshapiro	    RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
190838032Speter	{
190938032Speter		struct stat stbuf;
191038032Speter
191138032Speter		/* check to see if we own the queue directory */
191238032Speter		if (stat(".", &stbuf) < 0)
191338032Speter			syserr("main: cannot stat %s", QueueDir);
191438032Speter		if (stbuf.st_uid != RealUid)
191538032Speter		{
191638032Speter			/* nope, really a botch */
191790792Sgshapiro			HoldErrs = false;
191838032Speter			usrerr("You do not have permission to process the queue");
191990792Sgshapiro			finis(false, true, EX_NOPERM);
192090792Sgshapiro			/* NOTREACHED */
192138032Speter		}
192238032Speter	}
192338032Speter
192490792Sgshapiro#if MILTER
192564562Sgshapiro	/* sanity checks on milter filters */
192664562Sgshapiro	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
192790792Sgshapiro	{
192890792Sgshapiro		milter_config(InputFilterList, InputFilters, MAXFILTERS);
192990792Sgshapiro		setup_daemon_milters();
193090792Sgshapiro	}
193190792Sgshapiro#endif /* MILTER */
193264562Sgshapiro
193390792Sgshapiro	/* Convert queuegroup string to qgrp number */
193490792Sgshapiro	if (queuegroup != NULL)
193590792Sgshapiro	{
193690792Sgshapiro		qgrp = name2qid(queuegroup);
193790792Sgshapiro		if (qgrp == NOQGRP)
193890792Sgshapiro		{
193990792Sgshapiro			HoldErrs = false;
194090792Sgshapiro			usrerr("Queue group %s unknown", queuegroup);
194190792Sgshapiro			finis(false, true, ExitStat);
194290792Sgshapiro			/* NOTREACHED */
194390792Sgshapiro		}
194490792Sgshapiro	}
194566494Sgshapiro
1946203004Sgshapiro	/* if checking config or have had errors so far, exit now */
1947203004Sgshapiro	if (OpMode == MD_CHECKCONFIG || (ExitStat != EX_OK && OpMode != MD_TEST))
194890792Sgshapiro	{
194990792Sgshapiro		finis(false, true, ExitStat);
195090792Sgshapiro		/* NOTREACHED */
195190792Sgshapiro	}
195238032Speter
195390792Sgshapiro#if SASL
195490792Sgshapiro	/* sendmail specific SASL initialization */
195590792Sgshapiro	sm_sasl_init();
195690792Sgshapiro#endif /* SASL */
195790792Sgshapiro
195838032Speter#if XDEBUG
195938032Speter	checkfd012("before main() initmaps");
196064562Sgshapiro#endif /* XDEBUG */
196138032Speter
196238032Speter	/*
196338032Speter	**  Do operation-mode-dependent initialization.
196438032Speter	*/
196538032Speter
196638032Speter	switch (OpMode)
196738032Speter	{
196838032Speter	  case MD_PRINT:
196938032Speter		/* print the queue */
197090792Sgshapiro		HoldErrs = false;
1971203004Sgshapiro		(void) dropenvelope(&BlankEnvelope, true, false);
197290792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
197390792Sgshapiro		if (qgrp != NOQGRP)
197490792Sgshapiro		{
197590792Sgshapiro			int j;
197690792Sgshapiro
197790792Sgshapiro			/* Selecting a particular queue group to run */
197890792Sgshapiro			for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
197990792Sgshapiro			{
198090792Sgshapiro				if (StopRequest)
198190792Sgshapiro					stop_sendmail();
198290792Sgshapiro				(void) print_single_queue(qgrp, j);
198390792Sgshapiro			}
198490792Sgshapiro			finis(false, true, EX_OK);
198590792Sgshapiro			/* NOTREACHED */
198690792Sgshapiro		}
198738032Speter		printqueue();
198890792Sgshapiro		finis(false, true, EX_OK);
198990792Sgshapiro		/* NOTREACHED */
199042575Speter		break;
199138032Speter
199290792Sgshapiro	  case MD_PRINTNQE:
199390792Sgshapiro		/* print number of entries in queue */
1994203004Sgshapiro		(void) dropenvelope(&BlankEnvelope, true, false);
199590792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
199690792Sgshapiro		printnqe(smioout, NULL);
199790792Sgshapiro		finis(false, true, EX_OK);
199890792Sgshapiro		/* NOTREACHED */
199990792Sgshapiro		break;
200090792Sgshapiro
200190792Sgshapiro	  case MD_QUEUERUN:
200290792Sgshapiro		/* only handle quarantining here */
200390792Sgshapiro		if (quarantining == NULL)
200490792Sgshapiro			break;
200590792Sgshapiro
200690792Sgshapiro		if (QueueMode != QM_QUARANTINE &&
200790792Sgshapiro		    QueueMode != QM_NORMAL)
200890792Sgshapiro		{
200990792Sgshapiro			HoldErrs = false;
201090792Sgshapiro			usrerr("Can not use -Q with -q%c", QueueMode);
201190792Sgshapiro			ExitStat = EX_USAGE;
201290792Sgshapiro			finis(false, true, ExitStat);
201390792Sgshapiro			/* NOTREACHED */
201490792Sgshapiro		}
201590792Sgshapiro		quarantine_queue(quarantining, qgrp);
201690792Sgshapiro		finis(false, true, EX_OK);
201790792Sgshapiro		break;
201890792Sgshapiro
201938032Speter	  case MD_HOSTSTAT:
202090792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
202164562Sgshapiro		(void) mci_traverse_persistent(mci_print_persistent, NULL);
202290792Sgshapiro		finis(false, true, EX_OK);
202390792Sgshapiro		/* NOTREACHED */
202464562Sgshapiro		break;
202538032Speter
202638032Speter	  case MD_PURGESTAT:
202764562Sgshapiro		(void) mci_traverse_persistent(mci_purge_persistent, NULL);
202890792Sgshapiro		finis(false, true, EX_OK);
202990792Sgshapiro		/* NOTREACHED */
203064562Sgshapiro		break;
203138032Speter
203238032Speter	  case MD_INITALIAS:
203342575Speter		/* initialize maps */
203464562Sgshapiro		initmaps();
203590792Sgshapiro		finis(false, true, ExitStat);
203690792Sgshapiro		/* NOTREACHED */
203742575Speter		break;
203838032Speter
203938032Speter	  case MD_SMTP:
204038032Speter	  case MD_DAEMON:
204138032Speter		/* reset DSN parameters */
204238032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
204390792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
204490792Sgshapiro			  macid("{dsn_notify}"), NULL);
204590792Sgshapiro		BlankEnvelope.e_envid = NULL;
204690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
204790792Sgshapiro			  macid("{dsn_envid}"), NULL);
204890792Sgshapiro		BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
204990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
205090792Sgshapiro			  macid("{dsn_ret}"), NULL);
205138032Speter
205242575Speter		/* don't open maps for daemon -- done below in child */
205338032Speter		break;
205438032Speter	}
205538032Speter
205638032Speter	if (tTd(0, 15))
205738032Speter	{
205838032Speter		/* print configuration table (or at least part of it) */
205938032Speter		if (tTd(0, 90))
206038032Speter			printrules();
206138032Speter		for (i = 0; i < MAXMAILERS; i++)
206238032Speter		{
206338032Speter			if (Mailer[i] != NULL)
2064132943Sgshapiro				printmailer(sm_debug_file(), Mailer[i]);
206538032Speter		}
206638032Speter	}
206738032Speter
206838032Speter	/*
206938032Speter	**  Switch to the main envelope.
207038032Speter	*/
207138032Speter
207290792Sgshapiro	CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
207390792Sgshapiro			     sm_rpool_new_x(NULL));
207438032Speter	MainEnvelope.e_flags = BlankEnvelope.e_flags;
207538032Speter
207638032Speter	/*
207738032Speter	**  If test mode, read addresses from stdin and process.
207838032Speter	*/
207938032Speter
208038032Speter	if (OpMode == MD_TEST)
208138032Speter	{
208290792Sgshapiro		if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
208338032Speter			Verbose = 2;
208438032Speter
208538032Speter		if (Verbose)
208638032Speter		{
208790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
208890792Sgshapiro				     "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
208990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
209090792Sgshapiro				     "Enter <ruleset> <address>\n");
209138032Speter		}
209290792Sgshapiro		macdefine(&(MainEnvelope.e_macro), A_PERM,
209390792Sgshapiro			  macid("{addr_type}"), "e r");
209438032Speter		for (;;)
209538032Speter		{
209690792Sgshapiro			SM_TRY
209790792Sgshapiro			{
209890792Sgshapiro				(void) sm_signal(SIGINT, intindebug);
209990792Sgshapiro				(void) sm_releasesignal(SIGINT);
210090792Sgshapiro				if (Verbose == 2)
210190792Sgshapiro					(void) sm_io_fprintf(smioout,
210290792Sgshapiro							     SM_TIME_DEFAULT,
210390792Sgshapiro							     "> ");
210490792Sgshapiro				(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
210590792Sgshapiro				if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
2106249729Sgshapiro						sizeof(buf)) < 0)
210790792Sgshapiro					testmodeline("/quit", &MainEnvelope);
210890792Sgshapiro				p = strchr(buf, '\n');
210990792Sgshapiro				if (p != NULL)
211090792Sgshapiro					*p = '\0';
211190792Sgshapiro				if (Verbose < 2)
211290792Sgshapiro					(void) sm_io_fprintf(smioout,
211390792Sgshapiro							     SM_TIME_DEFAULT,
211490792Sgshapiro							     "> %s\n", buf);
211590792Sgshapiro				testmodeline(buf, &MainEnvelope);
211690792Sgshapiro			}
211790792Sgshapiro			SM_EXCEPT(exc, "[!F]*")
211890792Sgshapiro			{
211990792Sgshapiro				/*
212090792Sgshapiro				**  8.10 just prints \n on interrupt.
212190792Sgshapiro				**  I'm printing the exception here in case
212290792Sgshapiro				**  sendmail is extended to raise additional
212390792Sgshapiro				**  exceptions in this context.
212490792Sgshapiro				*/
212590792Sgshapiro
212690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
212790792Sgshapiro						     "\n");
212890792Sgshapiro				sm_exc_print(exc, smioout);
212990792Sgshapiro			}
213090792Sgshapiro			SM_END_TRY
213138032Speter		}
213238032Speter	}
213338032Speter
213490792Sgshapiro#if STARTTLS
213590792Sgshapiro	tls_ok = true;
2136168515Sgshapiro	if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER ||
2137168515Sgshapiro	    OpMode == MD_ARPAFTP)
213890792Sgshapiro	{
213990792Sgshapiro		/* check whether STARTTLS is turned off for the client */
214090792Sgshapiro		if (chkclientmodifiers(D_NOTLS))
214190792Sgshapiro			tls_ok = false;
214290792Sgshapiro	}
214390792Sgshapiro	else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
214490792Sgshapiro		 OpMode == MD_SMTP)
214590792Sgshapiro	{
2146203004Sgshapiro		/* check whether STARTTLS is turned off */
2147203004Sgshapiro		if (chkdaemonmodifiers(D_NOTLS) && chkclientmodifiers(D_NOTLS))
214890792Sgshapiro			tls_ok = false;
214990792Sgshapiro	}
215090792Sgshapiro	else	/* other modes don't need STARTTLS */
215190792Sgshapiro		tls_ok = false;
215264562Sgshapiro
215390792Sgshapiro	if (tls_ok)
215490792Sgshapiro	{
215590792Sgshapiro		/* basic TLS initialization */
2156249729Sgshapiro		tls_ok = init_tls_library(FipsMode);
2157249729Sgshapiro		if (!tls_ok && FipsMode)
2158249729Sgshapiro		{
2159249729Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2160249729Sgshapiro				     "ERROR: FIPSMode failed to initialize\n");
2161249729Sgshapiro			exit(EX_USAGE);
2162249729Sgshapiro		}
216390792Sgshapiro	}
216490792Sgshapiro
216590792Sgshapiro	if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
216690792Sgshapiro	{
216790792Sgshapiro		/* disable TLS for client */
216890792Sgshapiro		setclttls(false);
216990792Sgshapiro	}
217090792Sgshapiro#endif /* STARTTLS */
217190792Sgshapiro
217264562Sgshapiro	/*
217338032Speter	**  If collecting stuff from the queue, go start doing that.
217438032Speter	*/
217538032Speter
217664562Sgshapiro	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
217738032Speter	{
217890792Sgshapiro		pid_t pid = -1;
217990792Sgshapiro
218090792Sgshapiro#if STARTTLS
218190792Sgshapiro		/* init TLS for client, ignore result for now */
218290792Sgshapiro		(void) initclttls(tls_ok);
218390792Sgshapiro#endif /* STARTTLS */
218490792Sgshapiro
218590792Sgshapiro		/*
218690792Sgshapiro		**  The parent process of the caller of runqueue() needs
218790792Sgshapiro		**  to stay around for a possible SIGTERM. The SIGTERM will
218890792Sgshapiro		**  tell this process that all of the queue runners children
218990792Sgshapiro		**  need to be sent SIGTERM as well. At the same time, we
219090792Sgshapiro		**  want to return control to the command line. So we do an
219190792Sgshapiro		**  extra fork().
219290792Sgshapiro		*/
219390792Sgshapiro
219490792Sgshapiro		if (Verbose || foregroundqueue || (pid = fork()) <= 0)
219564562Sgshapiro		{
219690792Sgshapiro			/*
219790792Sgshapiro			**  If the fork() failed we should still try to do
219890792Sgshapiro			**  the queue run. If it succeeded then the child
219990792Sgshapiro			**  is going to start the run and wait for all
220090792Sgshapiro			**  of the children to finish.
220190792Sgshapiro			*/
220290792Sgshapiro
220390792Sgshapiro			if (pid == 0)
220490792Sgshapiro			{
220590792Sgshapiro				/* Reset global flags */
220690792Sgshapiro				RestartRequest = NULL;
220790792Sgshapiro				ShutdownRequest = NULL;
220890792Sgshapiro				PendingSignal = 0;
220990792Sgshapiro
221090792Sgshapiro				/* disconnect from terminal */
221190792Sgshapiro				disconnect(2, CurEnv);
221290792Sgshapiro			}
221390792Sgshapiro
221490792Sgshapiro			CurrentPid = getpid();
221590792Sgshapiro			if (qgrp != NOQGRP)
221690792Sgshapiro			{
2217110560Sgshapiro				int rwgflags = RWG_NONE;
2218110560Sgshapiro
221990792Sgshapiro				/*
222090792Sgshapiro				**  To run a specific queue group mark it to
222190792Sgshapiro				**  be run, select the work group it's in and
222290792Sgshapiro				**  increment the work counter.
222390792Sgshapiro				*/
222490792Sgshapiro
222594334Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
222694334Sgshapiro				     i++)
222794334Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
222894334Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
2229110560Sgshapiro				if (Verbose)
2230110560Sgshapiro					rwgflags |= RWG_VERBOSE;
2231110560Sgshapiro				if (queuepersistent)
2232110560Sgshapiro					rwgflags |= RWG_PERSISTENT;
2233110560Sgshapiro				rwgflags |= RWG_FORCE;
223490792Sgshapiro				(void) run_work_group(Queue[qgrp]->qg_wgrp,
2235110560Sgshapiro						      rwgflags);
223690792Sgshapiro			}
223790792Sgshapiro			else
223890792Sgshapiro				(void) runqueue(false, Verbose,
223990792Sgshapiro						queuepersistent, true);
224090792Sgshapiro
224190792Sgshapiro			/* set the title to make it easier to find */
224290792Sgshapiro			sm_setproctitle(true, CurEnv, "Queue control");
224390792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
224490792Sgshapiro			while (CurChildren > 0)
224590792Sgshapiro			{
224690792Sgshapiro				int status;
224790792Sgshapiro				pid_t ret;
224890792Sgshapiro
2249125820Sgshapiro				errno = 0;
225090792Sgshapiro				while ((ret = sm_wait(&status)) <= 0)
2251125820Sgshapiro				{
2252125820Sgshapiro					if (errno == ECHILD)
2253125820Sgshapiro					{
2254125820Sgshapiro						/*
2255125820Sgshapiro						**  Oops... something got messed
2256125820Sgshapiro						**  up really bad. Waiting for
2257125820Sgshapiro						**  non-existent children
2258125820Sgshapiro						**  shouldn't happen. Let's get
2259125820Sgshapiro						**  out of here.
2260125820Sgshapiro						*/
2261125820Sgshapiro
2262125820Sgshapiro						CurChildren = 0;
2263125820Sgshapiro						break;
2264125820Sgshapiro					}
226590792Sgshapiro					continue;
2266125820Sgshapiro				}
226790792Sgshapiro
2268125820Sgshapiro				/* something is really really wrong */
2269125820Sgshapiro				if (errno == ECHILD)
2270125820Sgshapiro				{
2271125820Sgshapiro					sm_syslog(LOG_ERR, NOQID,
2272125820Sgshapiro						  "queue control process: lost all children: wait returned ECHILD");
2273125820Sgshapiro					break;
2274125820Sgshapiro				}
2275125820Sgshapiro
227690792Sgshapiro				/* Only drop when a child gives status */
227790792Sgshapiro				if (WIFSTOPPED(status))
227890792Sgshapiro					continue;
227990792Sgshapiro
228090792Sgshapiro				proc_list_drop(ret, status, NULL);
228190792Sgshapiro			}
228264562Sgshapiro		}
228390792Sgshapiro		finis(true, true, ExitStat);
228490792Sgshapiro		/* NOTREACHED */
228538032Speter	}
228638032Speter
228771345Sgshapiro# if SASL
228871345Sgshapiro	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
228971345Sgshapiro	{
229090792Sgshapiro		/* check whether AUTH is turned off for the server */
229190792Sgshapiro		if (!chkdaemonmodifiers(D_NOAUTH) &&
229290792Sgshapiro		    (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
229371345Sgshapiro			syserr("!sasl_server_init failed! [%s]",
229490792Sgshapiro				sasl_errstring(i, NULL, NULL));
229571345Sgshapiro	}
229671345Sgshapiro# endif /* SASL */
229771345Sgshapiro
229890792Sgshapiro	if (OpMode == MD_SMTP)
229990792Sgshapiro	{
230090792Sgshapiro		proc_list_add(CurrentPid, "Sendmail SMTP Agent",
2301132943Sgshapiro			      PROC_DAEMON, 0, -1, NULL);
230290792Sgshapiro
230390792Sgshapiro		/* clean up background delivery children */
230490792Sgshapiro		(void) sm_signal(SIGCHLD, reapchild);
230590792Sgshapiro	}
230690792Sgshapiro
230738032Speter	/*
230838032Speter	**  If a daemon, wait for a request.
230938032Speter	**	getrequests will always return in a child.
231038032Speter	**	If we should also be processing the queue, start
231138032Speter	**		doing it in background.
231238032Speter	**	We check for any errors that might have happened
231338032Speter	**		during startup.
231438032Speter	*/
231538032Speter
231698841Sgshapiro	if (OpMode == MD_DAEMON || QueueIntvl > 0)
231738032Speter	{
231838032Speter		char dtype[200];
231938032Speter
2320161389Sgshapiro		/* avoid cleanup in finis(), DaemonPid will be set below */
2321161389Sgshapiro		DaemonPid = 0;
232238032Speter		if (!run_in_foreground && !tTd(99, 100))
232338032Speter		{
232438032Speter			/* put us in background */
232538032Speter			i = fork();
232638032Speter			if (i < 0)
232738032Speter				syserr("daemon: cannot fork");
232838032Speter			if (i != 0)
232990792Sgshapiro			{
233090792Sgshapiro				finis(false, true, EX_OK);
233190792Sgshapiro				/* NOTREACHED */
233290792Sgshapiro			}
233338032Speter
233490792Sgshapiro			/*
233590792Sgshapiro			**  Initialize exception stack and default exception
233690792Sgshapiro			**  handler for child process.
233790792Sgshapiro			*/
233890792Sgshapiro
233990792Sgshapiro			/* Reset global flags */
234090792Sgshapiro			RestartRequest = NULL;
234190792Sgshapiro			RestartWorkGroup = false;
234290792Sgshapiro			ShutdownRequest = NULL;
234390792Sgshapiro			PendingSignal = 0;
234490792Sgshapiro			CurrentPid = getpid();
234590792Sgshapiro
234690792Sgshapiro			sm_exc_newthread(fatal_error);
234790792Sgshapiro
234838032Speter			/* disconnect from our controlling tty */
234990792Sgshapiro			disconnect(2, &MainEnvelope);
235038032Speter		}
235138032Speter
235238032Speter		dtype[0] = '\0';
235338032Speter		if (OpMode == MD_DAEMON)
2354161389Sgshapiro		{
2355168515Sgshapiro			(void) sm_strlcat(dtype, "+SMTP", sizeof(dtype));
2356161389Sgshapiro			DaemonPid = CurrentPid;
2357161389Sgshapiro		}
235898841Sgshapiro		if (QueueIntvl > 0)
235938032Speter		{
236090792Sgshapiro			(void) sm_strlcat2(dtype,
236190792Sgshapiro					   queuepersistent
236290792Sgshapiro					   ? "+persistent-queueing@"
236390792Sgshapiro					   : "+queueing@",
236490792Sgshapiro					   pintvl(QueueIntvl, true),
2365168515Sgshapiro					   sizeof(dtype));
236638032Speter		}
236738032Speter		if (tTd(0, 1))
2368168515Sgshapiro			(void) sm_strlcat(dtype, "+debugging", sizeof(dtype));
236938032Speter
237038032Speter		sm_syslog(LOG_INFO, NOQID,
237164562Sgshapiro			  "starting daemon (%s): %s", Version, dtype + 1);
237290792Sgshapiro#if XLA
237338032Speter		xla_create_file();
237464562Sgshapiro#endif /* XLA */
237538032Speter
237664562Sgshapiro		/* save daemon type in a macro for possible PidFile use */
237790792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
237890792Sgshapiro			macid("{daemon_info}"), dtype + 1);
237964562Sgshapiro
238064562Sgshapiro		/* save queue interval in a macro for possible PidFile use */
238190792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
238290792Sgshapiro			macid("{queue_interval}"), pintvl(QueueIntvl, true));
238364562Sgshapiro
238490792Sgshapiro		/* workaround: can't seem to release the signal in the parent */
238590792Sgshapiro		(void) sm_signal(SIGHUP, sighup);
238690792Sgshapiro		(void) sm_releasesignal(SIGHUP);
238790792Sgshapiro		(void) sm_signal(SIGTERM, sigterm);
238890792Sgshapiro
238998841Sgshapiro		if (QueueIntvl > 0)
239038032Speter		{
2391173340Sgshapiro#if _FFR_RUNPQG
2392173340Sgshapiro			if (qgrp != NOQGRP)
2393173340Sgshapiro			{
2394173340Sgshapiro				int rwgflags = RWG_NONE;
239590792Sgshapiro
2396173340Sgshapiro				/*
2397173340Sgshapiro				**  To run a specific queue group mark it to
2398173340Sgshapiro				**  be run, select the work group it's in and
2399173340Sgshapiro				**  increment the work counter.
2400173340Sgshapiro				*/
2401173340Sgshapiro
2402173340Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
2403173340Sgshapiro				     i++)
2404173340Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
2405173340Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
2406173340Sgshapiro				if (Verbose)
2407173340Sgshapiro					rwgflags |= RWG_VERBOSE;
2408173340Sgshapiro				if (queuepersistent)
2409173340Sgshapiro					rwgflags |= RWG_PERSISTENT;
2410173340Sgshapiro				rwgflags |= RWG_FORCE;
2411173340Sgshapiro				(void) run_work_group(Queue[qgrp]->qg_wgrp,
2412173340Sgshapiro						      rwgflags);
2413173340Sgshapiro			}
2414173340Sgshapiro			else
2415173340Sgshapiro#endif /* _FFR_RUNPQG */
2416173340Sgshapiro				(void) runqueue(true, false, queuepersistent,
2417173340Sgshapiro						true);
2418173340Sgshapiro
241990792Sgshapiro			/*
242090792Sgshapiro			**  If queuepersistent but not in daemon mode then
242190792Sgshapiro			**  we're going to do the queue runner monitoring here.
242290792Sgshapiro			**  If in daemon mode then the monitoring will happen
242390792Sgshapiro			**  elsewhere.
242490792Sgshapiro			*/
242590792Sgshapiro
242690792Sgshapiro			if (OpMode != MD_DAEMON && queuepersistent)
242790792Sgshapiro			{
2428132943Sgshapiro				/*
2429132943Sgshapiro				**  Write the pid to file
2430132943Sgshapiro				**  XXX Overwrites sendmail.pid
2431132943Sgshapiro				*/
2432132943Sgshapiro
2433132943Sgshapiro				log_sendmail_pid(&MainEnvelope);
2434132943Sgshapiro
243590792Sgshapiro				/* set the title to make it easier to find */
243690792Sgshapiro				sm_setproctitle(true, CurEnv, "Queue control");
243790792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
243890792Sgshapiro				while (CurChildren > 0)
243990792Sgshapiro				{
244090792Sgshapiro					int status;
244190792Sgshapiro					pid_t ret;
244290792Sgshapiro					int group;
244390792Sgshapiro
2444120256Sgshapiro					CHECK_RESTART;
2445125820Sgshapiro					errno = 0;
244690792Sgshapiro					while ((ret = sm_wait(&status)) <= 0)
2447125820Sgshapiro					{
2448125820Sgshapiro						/*
2449125820Sgshapiro						**  Waiting for non-existent
2450125820Sgshapiro						**  children shouldn't happen.
2451125820Sgshapiro						**  Let's get out of here if
2452125820Sgshapiro						**  it occurs.
2453125820Sgshapiro						*/
2454125820Sgshapiro
2455125820Sgshapiro						if (errno == ECHILD)
2456125820Sgshapiro						{
2457125820Sgshapiro							CurChildren = 0;
2458125820Sgshapiro							break;
2459125820Sgshapiro						}
246090792Sgshapiro						continue;
2461125820Sgshapiro					}
246290792Sgshapiro
2463125820Sgshapiro					/* something is really really wrong */
2464125820Sgshapiro					if (errno == ECHILD)
2465125820Sgshapiro					{
2466125820Sgshapiro						sm_syslog(LOG_ERR, NOQID,
2467125820Sgshapiro							  "persistent queue runner control process: lost all children: wait returned ECHILD");
2468125820Sgshapiro						break;
2469125820Sgshapiro					}
2470125820Sgshapiro
247190792Sgshapiro					if (WIFSTOPPED(status))
247290792Sgshapiro						continue;
247390792Sgshapiro
247490792Sgshapiro					/* Probe only on a child status */
247590792Sgshapiro					proc_list_drop(ret, status, &group);
247690792Sgshapiro
247790792Sgshapiro					if (WIFSIGNALED(status))
247890792Sgshapiro					{
247990792Sgshapiro						if (WCOREDUMP(status))
248090792Sgshapiro						{
248190792Sgshapiro							sm_syslog(LOG_ERR, NOQID,
248290792Sgshapiro								  "persistent queue runner=%d core dumped, signal=%d",
248390792Sgshapiro								  group, WTERMSIG(status));
248490792Sgshapiro
2485120256Sgshapiro							/* don't restart this */
2486120256Sgshapiro							mark_work_group_restart(
2487120256Sgshapiro								group, -1);
248890792Sgshapiro							continue;
248990792Sgshapiro						}
249090792Sgshapiro
249190792Sgshapiro						sm_syslog(LOG_ERR, NOQID,
2492168515Sgshapiro							  "persistent queue runner=%d died, pid=%ld, signal=%d",
2493168515Sgshapiro							  group, (long) ret,
2494168515Sgshapiro							  WTERMSIG(status));
249590792Sgshapiro					}
249690792Sgshapiro
249790792Sgshapiro					/*
249890792Sgshapiro					**  When debugging active, don't
249990792Sgshapiro					**  restart the persistent queues.
250090792Sgshapiro					**  But do log this as info.
250190792Sgshapiro					*/
250290792Sgshapiro
250390792Sgshapiro					if (sm_debug_active(&DebugNoPRestart,
250490792Sgshapiro							    1))
250590792Sgshapiro					{
250690792Sgshapiro						sm_syslog(LOG_DEBUG, NOQID,
250790792Sgshapiro							  "persistent queue runner=%d, exited",
250890792Sgshapiro							  group);
2509120256Sgshapiro						mark_work_group_restart(group,
2510120256Sgshapiro									-1);
251190792Sgshapiro					}
2512168515Sgshapiro					CHECK_RESTART;
251390792Sgshapiro				}
251490792Sgshapiro				finis(true, true, ExitStat);
251590792Sgshapiro				/* NOTREACHED */
251690792Sgshapiro			}
251790792Sgshapiro
251838032Speter			if (OpMode != MD_DAEMON)
251938032Speter			{
252090792Sgshapiro				char qtype[200];
252190792Sgshapiro
252290792Sgshapiro				/*
252390792Sgshapiro				**  Write the pid to file
252490792Sgshapiro				**  XXX Overwrites sendmail.pid
252590792Sgshapiro				*/
252690792Sgshapiro
252790792Sgshapiro				log_sendmail_pid(&MainEnvelope);
252890792Sgshapiro
252990792Sgshapiro				/* set the title to make it easier to find */
253090792Sgshapiro				qtype[0] = '\0';
2531168515Sgshapiro				(void) sm_strlcpyn(qtype, sizeof(qtype), 4,
253290792Sgshapiro						   "Queue runner@",
253390792Sgshapiro						   pintvl(QueueIntvl, true),
253490792Sgshapiro						   " for ",
253590792Sgshapiro						   QueueDir);
253690792Sgshapiro				sm_setproctitle(true, CurEnv, qtype);
253738032Speter				for (;;)
253838032Speter				{
253964562Sgshapiro					(void) pause();
2540132943Sgshapiro
2541120256Sgshapiro					CHECK_RESTART;
2542132943Sgshapiro
254390792Sgshapiro					if (doqueuerun())
254490792Sgshapiro						(void) runqueue(true, false,
254590792Sgshapiro								false, false);
254638032Speter				}
254738032Speter			}
254838032Speter		}
2549203004Sgshapiro		(void) dropenvelope(&MainEnvelope, true, false);
255038032Speter
255190792Sgshapiro#if STARTTLS
255264562Sgshapiro		/* init TLS for server, ignore result for now */
255390792Sgshapiro		(void) initsrvtls(tls_ok);
255490792Sgshapiro#endif /* STARTTLS */
2555110560Sgshapiro
255690792Sgshapiro	nextreq:
255790792Sgshapiro		p_flags = getrequests(&MainEnvelope);
255838032Speter
255938032Speter		/* drop privileges */
256090792Sgshapiro		(void) drop_privileges(false);
256138032Speter
256238032Speter		/*
256338032Speter		**  Get authentication data
256490792Sgshapiro		**  Set _ macro in BlankEnvelope before calling newenvelope().
256538032Speter		*/
256638032Speter
256790792Sgshapiro		authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
256890792Sgshapiro						     NULL), &forged);
256990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
2570244833Sgshapiro		if (tTd(75, 9))
2571244833Sgshapiro			sm_syslog(LOG_INFO, NOQID,
2572244833Sgshapiro				"main: where=after_getauthinfo, RealHostAddr=%s",
2573244833Sgshapiro				anynet_ntoa(&RealHostAddr));
257490792Sgshapiro
257590792Sgshapiro		/* at this point we are in a child: reset state */
257690792Sgshapiro		sm_rpool_free(MainEnvelope.e_rpool);
257790792Sgshapiro		(void) newenvelope(&MainEnvelope, &MainEnvelope,
257890792Sgshapiro				   sm_rpool_new_x(NULL));
257938032Speter	}
258038032Speter
258164562Sgshapiro	if (LogLevel > 9)
258264562Sgshapiro	{
258364562Sgshapiro		/* log connection information */
2584110560Sgshapiro		sm_syslog(LOG_INFO, NULL, "connect from %s", authinfo);
258564562Sgshapiro	}
258664562Sgshapiro
258738032Speter	/*
258838032Speter	**  If running SMTP protocol, start collecting and executing
258938032Speter	**  commands.  This will never return.
259038032Speter	*/
259138032Speter
259238032Speter	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
259338032Speter	{
259438032Speter		char pbuf[20];
259538032Speter
259638032Speter		/*
259738032Speter		**  Save some macros for check_* rulesets.
259838032Speter		*/
259938032Speter
260038032Speter		if (forged)
260138032Speter		{
260238032Speter			char ipbuf[103];
260338032Speter
2604168515Sgshapiro			(void) sm_snprintf(ipbuf, sizeof(ipbuf), "[%.100s]",
260590792Sgshapiro					   anynet_ntoa(&RealHostAddr));
260690792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
260790792Sgshapiro				  macid("{client_name}"), ipbuf);
260838032Speter		}
260938032Speter		else
261090792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
261190792Sgshapiro				  macid("{client_name}"), RealHostName);
2612132943Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
2613132943Sgshapiro			  macid("{client_ptr}"), RealHostName);
261490792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
261590792Sgshapiro			  macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
261690792Sgshapiro		sm_getla();
261738032Speter
261890792Sgshapiro		switch (RealHostAddr.sa.sa_family)
261964562Sgshapiro		{
262090792Sgshapiro#if NETINET
262164562Sgshapiro		  case AF_INET:
2622168515Sgshapiro			(void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
262390792Sgshapiro					   RealHostAddr.sin.sin_port);
262464562Sgshapiro			break;
262590792Sgshapiro#endif /* NETINET */
262690792Sgshapiro#if NETINET6
262764562Sgshapiro		  case AF_INET6:
2628168515Sgshapiro			(void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
262990792Sgshapiro					   RealHostAddr.sin6.sin6_port);
263064562Sgshapiro			break;
263190792Sgshapiro#endif /* NETINET6 */
263264562Sgshapiro		  default:
2633168515Sgshapiro			(void) sm_snprintf(pbuf, sizeof(pbuf), "0");
263464562Sgshapiro			break;
263564562Sgshapiro		}
263690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
263790792Sgshapiro			macid("{client_port}"), pbuf);
263842575Speter
263938032Speter		if (OpMode == MD_DAEMON)
264038032Speter		{
2641168515Sgshapiro			ENVELOPE *saved_env;
2642168515Sgshapiro
264338032Speter			/* validate the connection */
264490792Sgshapiro			HoldErrs = true;
2645168515Sgshapiro			saved_env = CurEnv;
2646168515Sgshapiro			CurEnv = &BlankEnvelope;
264738032Speter			nullserver = validate_connection(&RealHostAddr,
2648132943Sgshapiro						macvalue(macid("{client_name}"),
2649168515Sgshapiro							&BlankEnvelope),
2650168515Sgshapiro						&BlankEnvelope);
2651168515Sgshapiro			if (bitset(EF_DISCARD, BlankEnvelope.e_flags))
2652168515Sgshapiro				MainEnvelope.e_flags |= EF_DISCARD;
2653168515Sgshapiro			CurEnv = saved_env;
265490792Sgshapiro			HoldErrs = false;
265538032Speter		}
265664562Sgshapiro		else if (p_flags == NULL)
265764562Sgshapiro		{
2658168515Sgshapiro			p_flags = (BITMAP256 *) xalloc(sizeof(*p_flags));
265964562Sgshapiro			clrbitmap(p_flags);
266064562Sgshapiro		}
266190792Sgshapiro#if STARTTLS
266264562Sgshapiro		if (OpMode == MD_SMTP)
266390792Sgshapiro			(void) initsrvtls(tls_ok);
266490792Sgshapiro#endif /* STARTTLS */
266571345Sgshapiro
266690792Sgshapiro		/* turn off profiling */
266790792Sgshapiro		SM_PROF(1);
266890792Sgshapiro		smtp(nullserver, *p_flags, &MainEnvelope);
2669110560Sgshapiro
2670110560Sgshapiro		if (tTd(93, 100))
2671110560Sgshapiro		{
2672110560Sgshapiro			/* turn off profiling */
2673110560Sgshapiro			SM_PROF(0);
2674110560Sgshapiro			if (OpMode == MD_DAEMON)
2675110560Sgshapiro				goto nextreq;
2676110560Sgshapiro		}
267738032Speter	}
267838032Speter
267990792Sgshapiro	sm_rpool_free(MainEnvelope.e_rpool);
268090792Sgshapiro	clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
268138032Speter	if (OpMode == MD_VERIFY)
268238032Speter	{
268390792Sgshapiro		set_delivery_mode(SM_VERIFY, &MainEnvelope);
268438032Speter		PostMasterCopy = NULL;
268538032Speter	}
268638032Speter	else
268738032Speter	{
268838032Speter		/* interactive -- all errors are global */
268990792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
269038032Speter	}
269138032Speter
269238032Speter	/*
269338032Speter	**  Do basic system initialization and set the sender
269438032Speter	*/
269538032Speter
269690792Sgshapiro	initsys(&MainEnvelope);
269790792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
269890792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
269990792Sgshapiro	setsender(from, &MainEnvelope, NULL, '\0', false);
270064562Sgshapiro	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
270190792Sgshapiro	    (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
270290792Sgshapiro	     strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
270364562Sgshapiro	{
270490792Sgshapiro		auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
270590792Sgshapiro			     RealUserName, from, warn_f_flag);
270664562Sgshapiro#if SASL
270790792Sgshapiro		auth = false;
270864562Sgshapiro#endif /* SASL */
270964562Sgshapiro	}
271064562Sgshapiro	if (auth)
271164562Sgshapiro	{
271264562Sgshapiro		char *fv;
271364562Sgshapiro
271464562Sgshapiro		/* set the initial sender for AUTH= to $f@$j */
271590792Sgshapiro		fv = macvalue('f', &MainEnvelope);
271664562Sgshapiro		if (fv == NULL || *fv == '\0')
271790792Sgshapiro			MainEnvelope.e_auth_param = NULL;
271864562Sgshapiro		else
271964562Sgshapiro		{
272064562Sgshapiro			if (strchr(fv, '@') == NULL)
272164562Sgshapiro			{
272290792Sgshapiro				i = strlen(fv) + strlen(macvalue('j',
272390792Sgshapiro							&MainEnvelope)) + 2;
272490792Sgshapiro				p = sm_malloc_x(i);
272590792Sgshapiro				(void) sm_strlcpyn(p, i, 3, fv, "@",
272690792Sgshapiro						   macvalue('j',
272790792Sgshapiro							    &MainEnvelope));
272864562Sgshapiro			}
272964562Sgshapiro			else
273090792Sgshapiro				p = sm_strdup_x(fv);
273190792Sgshapiro			MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
273290792Sgshapiro								      xtextify(p, "="));
273390792Sgshapiro			sm_free(p);  /* XXX */
273464562Sgshapiro		}
273564562Sgshapiro	}
273690792Sgshapiro	if (macvalue('s', &MainEnvelope) == NULL)
273790792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
273838032Speter
273990792Sgshapiro	av = argv + optind;
274038032Speter	if (*av == NULL && !GrabTo)
274138032Speter	{
274290792Sgshapiro		MainEnvelope.e_to = NULL;
274390792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
274490792Sgshapiro		HoldErrs = false;
274590792Sgshapiro		SuperSafe = SAFE_NO;
274638032Speter		usrerr("Recipient names must be specified");
274738032Speter
274838032Speter		/* collect body for UUCP return */
274938032Speter		if (OpMode != MD_VERIFY)
2750120256Sgshapiro			collect(InChannel, false, NULL, &MainEnvelope, true);
275190792Sgshapiro		finis(true, true, EX_USAGE);
275290792Sgshapiro		/* NOTREACHED */
275338032Speter	}
275438032Speter
275538032Speter	/*
275638032Speter	**  Scan argv and deliver the message to everyone.
275738032Speter	*/
275838032Speter
275990792Sgshapiro	save_val = LogUsrErrs;
276090792Sgshapiro	LogUsrErrs = true;
276190792Sgshapiro	sendtoargv(av, &MainEnvelope);
276290792Sgshapiro	LogUsrErrs = save_val;
276338032Speter
276438032Speter	/* if we have had errors sofar, arrange a meaningful exit stat */
276538032Speter	if (Errors > 0 && ExitStat == EX_OK)
276638032Speter		ExitStat = EX_USAGE;
276738032Speter
276838032Speter#if _FFR_FIX_DASHT
276938032Speter	/*
277038032Speter	**  If using -t, force not sending to argv recipients, even
277138032Speter	**  if they are mentioned in the headers.
277238032Speter	*/
277338032Speter
277438032Speter	if (GrabTo)
277538032Speter	{
277638032Speter		ADDRESS *q;
277764562Sgshapiro
277890792Sgshapiro		for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
277964562Sgshapiro			q->q_state = QS_REMOVED;
278038032Speter	}
278164562Sgshapiro#endif /* _FFR_FIX_DASHT */
278238032Speter
278338032Speter	/*
278438032Speter	**  Read the input mail.
278538032Speter	*/
278638032Speter
278790792Sgshapiro	MainEnvelope.e_to = NULL;
278838032Speter	if (OpMode != MD_VERIFY || GrabTo)
278938032Speter	{
279090792Sgshapiro		int savederrors;
279190792Sgshapiro		unsigned long savedflags;
279238032Speter
279390792Sgshapiro		/*
279490792Sgshapiro		**  workaround for compiler warning on Irix:
279590792Sgshapiro		**  do not initialize variable in the definition, but
279690792Sgshapiro		**  later on:
279790792Sgshapiro		**  warning(1548): transfer of control bypasses
279890792Sgshapiro		**  initialization of:
279990792Sgshapiro		**  variable "savederrors" (declared at line 2570)
280090792Sgshapiro		**  variable "savedflags" (declared at line 2571)
280190792Sgshapiro		**  goto giveup;
280290792Sgshapiro		*/
280390792Sgshapiro
280490792Sgshapiro		savederrors = Errors;
280590792Sgshapiro		savedflags = MainEnvelope.e_flags & EF_FATALERRS;
280690792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
280790792Sgshapiro		MainEnvelope.e_flags &= ~EF_FATALERRS;
280864562Sgshapiro		Errors = 0;
280964562Sgshapiro		buffer_errors();
2810120256Sgshapiro		collect(InChannel, false, NULL, &MainEnvelope, true);
281138032Speter
281264562Sgshapiro		/* header checks failed */
281364562Sgshapiro		if (Errors > 0)
281464562Sgshapiro		{
281590792Sgshapiro  giveup:
281690792Sgshapiro			if (!GrabTo)
281764562Sgshapiro			{
281890792Sgshapiro				/* Log who the mail would have gone to */
281990792Sgshapiro				logundelrcpts(&MainEnvelope,
282090792Sgshapiro					      MainEnvelope.e_message,
282190792Sgshapiro					      8, false);
282264562Sgshapiro			}
282390792Sgshapiro			flush_errors(true);
282490792Sgshapiro			finis(true, true, ExitStat);
282564562Sgshapiro			/* NOTREACHED */
282664562Sgshapiro			return -1;
282764562Sgshapiro		}
282864562Sgshapiro
282938032Speter		/* bail out if message too large */
283090792Sgshapiro		if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
283138032Speter		{
283290792Sgshapiro			finis(true, true, ExitStat != EX_OK ? ExitStat
283390792Sgshapiro							    : EX_DATAERR);
283464562Sgshapiro			/* NOTREACHED */
283538032Speter			return -1;
283638032Speter		}
283798121Sgshapiro
283898121Sgshapiro		/* set message size */
2839168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%ld",
2840244833Sgshapiro				   PRT_NONNEGL(MainEnvelope.e_msgsize));
284198121Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
284298121Sgshapiro			  macid("{msg_size}"), buf);
284398121Sgshapiro
284464562Sgshapiro		Errors = savederrors;
284590792Sgshapiro		MainEnvelope.e_flags |= savedflags;
284638032Speter	}
284738032Speter	errno = 0;
284838032Speter
284938032Speter	if (tTd(1, 1))
285090792Sgshapiro		sm_dprintf("From person = \"%s\"\n",
285190792Sgshapiro			   MainEnvelope.e_from.q_paddr);
285238032Speter
285390792Sgshapiro	/* Check if quarantining stats should be updated */
285490792Sgshapiro	if (MainEnvelope.e_quarmsg != NULL)
285590792Sgshapiro		markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
285690792Sgshapiro
285738032Speter	/*
285838032Speter	**  Actually send everything.
285938032Speter	**	If verifying, just ack.
286038032Speter	*/
286138032Speter
286290792Sgshapiro	if (Errors == 0)
286338032Speter	{
286490792Sgshapiro		if (!split_by_recipient(&MainEnvelope) &&
286590792Sgshapiro		    bitset(EF_FATALERRS, MainEnvelope.e_flags))
286690792Sgshapiro			goto giveup;
286738032Speter	}
286890792Sgshapiro
286990792Sgshapiro	/* make sure we deliver at least the first envelope */
287090792Sgshapiro	i = FastSplit > 0 ? 0 : -1;
287190792Sgshapiro	for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
287290792Sgshapiro	{
287390792Sgshapiro		ENVELOPE *next;
287490792Sgshapiro
287590792Sgshapiro		e->e_from.q_state = QS_SENDER;
287690792Sgshapiro		if (tTd(1, 5))
287790792Sgshapiro		{
287890792Sgshapiro			sm_dprintf("main[%d]: QS_SENDER ", i);
2879132943Sgshapiro			printaddr(sm_debug_file(), &e->e_from, false);
288090792Sgshapiro		}
288190792Sgshapiro		e->e_to = NULL;
288290792Sgshapiro		sm_getla();
288390792Sgshapiro		GrabTo = false;
288464562Sgshapiro#if NAMED_BIND
288590792Sgshapiro		_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
288690792Sgshapiro		_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
288764562Sgshapiro#endif /* NAMED_BIND */
288890792Sgshapiro		next = e->e_sibling;
288990792Sgshapiro		e->e_sibling = NULL;
289038032Speter
289190792Sgshapiro		/* after FastSplit envelopes: queue up */
289290792Sgshapiro		sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
289390792Sgshapiro		e->e_sibling = next;
289490792Sgshapiro	}
289590792Sgshapiro
289638032Speter	/*
289738032Speter	**  All done.
289838032Speter	**	Don't send return error message if in VERIFY mode.
289938032Speter	*/
290038032Speter
290190792Sgshapiro	finis(true, true, ExitStat);
290264562Sgshapiro	/* NOTREACHED */
290364562Sgshapiro	return ExitStat;
290438032Speter}
290590792Sgshapiro/*
290677349Sgshapiro**  STOP_SENDMAIL -- Stop the running program
290777349Sgshapiro**
290877349Sgshapiro**	Parameters:
290977349Sgshapiro**		none.
291077349Sgshapiro**
291177349Sgshapiro**	Returns:
291277349Sgshapiro**		none.
291377349Sgshapiro**
291477349Sgshapiro**	Side Effects:
291577349Sgshapiro**		exits.
291677349Sgshapiro*/
291738032Speter
291877349Sgshapirovoid
291977349Sgshapirostop_sendmail()
292077349Sgshapiro{
292177349Sgshapiro	/* reset uid for process accounting */
292277349Sgshapiro	endpwent();
292377349Sgshapiro	(void) setuid(RealUid);
292477349Sgshapiro	exit(EX_OK);
292577349Sgshapiro}
292690792Sgshapiro/*
292738032Speter**  FINIS -- Clean up and exit.
292838032Speter**
292938032Speter**	Parameters:
293042575Speter**		drop -- whether or not to drop CurEnv envelope
293190792Sgshapiro**		cleanup -- call exit() or _exit()?
293242575Speter**		exitstat -- exit status to use for exit() call
293338032Speter**
293438032Speter**	Returns:
293538032Speter**		never
293638032Speter**
293738032Speter**	Side Effects:
293838032Speter**		exits sendmail
293938032Speter*/
294038032Speter
294138032Spetervoid
294290792Sgshapirofinis(drop, cleanup, exitstat)
294342575Speter	bool drop;
294490792Sgshapiro	bool cleanup;
294542575Speter	volatile int exitstat;
294638032Speter{
2947132943Sgshapiro	char pidpath[MAXPATHLEN];
2948159609Sgshapiro	pid_t pid;
294998121Sgshapiro
295077349Sgshapiro	/* Still want to process new timeouts added below */
295190792Sgshapiro	sm_clear_events();
295290792Sgshapiro	(void) sm_releasesignal(SIGALRM);
295342575Speter
295438032Speter	if (tTd(2, 1))
295538032Speter	{
295690792Sgshapiro		sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
295790792Sgshapiro			   exitstat,
295890792Sgshapiro			   CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
295938032Speter		printenvflags(CurEnv);
296038032Speter	}
296138032Speter	if (tTd(2, 9))
296290792Sgshapiro		printopenfds(false);
296338032Speter
296490792Sgshapiro	SM_TRY
296590792Sgshapiro		/*
296690792Sgshapiro		**  Clean up.  This might raise E:mta.quickabort
296790792Sgshapiro		*/
296838032Speter
296990792Sgshapiro		/* clean up temp files */
297090792Sgshapiro		CurEnv->e_to = NULL;
297190792Sgshapiro		if (drop)
297290792Sgshapiro		{
297390792Sgshapiro			if (CurEnv->e_id != NULL)
297490792Sgshapiro			{
2975203004Sgshapiro				int r;
2976203004Sgshapiro
2977203004Sgshapiro				r = dropenvelope(CurEnv, true, false);
2978203004Sgshapiro				if (exitstat == EX_OK)
2979203004Sgshapiro					exitstat = r;
298090792Sgshapiro				sm_rpool_free(CurEnv->e_rpool);
298190792Sgshapiro				CurEnv->e_rpool = NULL;
2982161389Sgshapiro
2983168515Sgshapiro				/* these may have pointed to the rpool */
2984161389Sgshapiro				CurEnv->e_to = NULL;
2985168515Sgshapiro				CurEnv->e_message = NULL;
2986168515Sgshapiro				CurEnv->e_statmsg = NULL;
2987168515Sgshapiro				CurEnv->e_quarmsg = NULL;
2988168515Sgshapiro				CurEnv->e_bodytype = NULL;
2989168515Sgshapiro				CurEnv->e_id = NULL;
2990168515Sgshapiro				CurEnv->e_envid = NULL;
2991168515Sgshapiro				CurEnv->e_auth_param = NULL;
299290792Sgshapiro			}
299390792Sgshapiro			else
299490792Sgshapiro				poststats(StatFile);
299590792Sgshapiro		}
299638032Speter
299790792Sgshapiro		/* flush any cached connections */
299890792Sgshapiro		mci_flush(true, NULL);
299938032Speter
300090792Sgshapiro		/* close maps belonging to this pid */
300190792Sgshapiro		closemaps(false);
300242575Speter
300364562Sgshapiro#if USERDB
300490792Sgshapiro		/* close UserDatabase */
300590792Sgshapiro		_udbx_close();
300664562Sgshapiro#endif /* USERDB */
300742575Speter
300890792Sgshapiro#if SASL
300990792Sgshapiro		stop_sasl_client();
301090792Sgshapiro#endif /* SASL */
301190792Sgshapiro
301290792Sgshapiro#if XLA
301390792Sgshapiro		/* clean up extended load average stuff */
301490792Sgshapiro		xla_all_end();
301564562Sgshapiro#endif /* XLA */
301638032Speter
301790792Sgshapiro	SM_FINALLY
301890792Sgshapiro		/*
301990792Sgshapiro		**  And exit.
302090792Sgshapiro		*/
302138032Speter
302290792Sgshapiro		if (LogLevel > 78)
302390792Sgshapiro			sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
302490792Sgshapiro				  (int) CurrentPid);
302590792Sgshapiro		if (exitstat == EX_TEMPFAIL ||
302690792Sgshapiro		    CurEnv->e_errormode == EM_BERKNET)
302790792Sgshapiro			exitstat = EX_OK;
302864562Sgshapiro
302990792Sgshapiro		/* XXX clean up queues and related data structures */
303090792Sgshapiro		cleanup_queues();
3031159609Sgshapiro		pid = getpid();
303290792Sgshapiro#if SM_CONF_SHM
3033159609Sgshapiro		cleanup_shm(DaemonPid == pid);
303490792Sgshapiro#endif /* SM_CONF_SHM */
303590792Sgshapiro
3036132943Sgshapiro		/* close locked pid file */
3037132943Sgshapiro		close_sendmail_pid();
3038132943Sgshapiro
3039159609Sgshapiro		if (DaemonPid == pid || PidFilePid == pid)
3040132943Sgshapiro		{
3041132943Sgshapiro			/* blow away the pid file */
3042168515Sgshapiro			expand(PidFile, pidpath, sizeof(pidpath), CurEnv);
3043132943Sgshapiro			(void) unlink(pidpath);
3044132943Sgshapiro		}
3045132943Sgshapiro
304690792Sgshapiro		/* reset uid for process accounting */
304790792Sgshapiro		endpwent();
304890792Sgshapiro		sm_mbdb_terminate();
3049157001Sgshapiro#if _FFR_MEMSTAT
3050157001Sgshapiro		(void) sm_memstat_close();
3051157001Sgshapiro#endif /* _FFR_MEMSTAT */
305290792Sgshapiro		(void) setuid(RealUid);
305390792Sgshapiro#if SM_HEAP_CHECK
305490792Sgshapiro		/* dump the heap, if we are checking for memory leaks */
305590792Sgshapiro		if (sm_debug_active(&SmHeapCheck, 2))
305690792Sgshapiro			sm_heap_report(smioout,
305790792Sgshapiro				       sm_debug_level(&SmHeapCheck) - 1);
305890792Sgshapiro#endif /* SM_HEAP_CHECK */
305990792Sgshapiro		if (sm_debug_active(&SmXtrapReport, 1))
306090792Sgshapiro			sm_dprintf("xtrap count = %d\n", SmXtrapCount);
306190792Sgshapiro		if (cleanup)
306290792Sgshapiro			exit(exitstat);
306390792Sgshapiro		else
306490792Sgshapiro			_exit(exitstat);
306590792Sgshapiro	SM_END_TRY
306638032Speter}
306790792Sgshapiro/*
306890792Sgshapiro**  INTINDEBUG -- signal handler for SIGINT in -bt mode
306977349Sgshapiro**
307077349Sgshapiro**	Parameters:
307190792Sgshapiro**		sig -- incoming signal.
307290792Sgshapiro**
307390792Sgshapiro**	Returns:
307490792Sgshapiro**		none.
307590792Sgshapiro**
307690792Sgshapiro**	Side Effects:
307790792Sgshapiro**		longjmps back to test mode loop.
307890792Sgshapiro**
307990792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
308090792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
308190792Sgshapiro**		DOING.
308290792Sgshapiro*/
308390792Sgshapiro
308490792Sgshapiro/* Type of an exception generated on SIGINT during address test mode.  */
308590792Sgshapirostatic const SM_EXC_TYPE_T EtypeInterrupt =
308690792Sgshapiro{
308790792Sgshapiro	SmExcTypeMagic,
308890792Sgshapiro	"S:mta.interrupt",
308990792Sgshapiro	"",
309090792Sgshapiro	sm_etype_printf,
309190792Sgshapiro	"interrupt",
309290792Sgshapiro};
309390792Sgshapiro
309490792Sgshapiro/* ARGSUSED */
309590792Sgshapirostatic SIGFUNC_DECL
309690792Sgshapirointindebug(sig)
309790792Sgshapiro	int sig;
309890792Sgshapiro{
309990792Sgshapiro	int save_errno = errno;
310090792Sgshapiro
310190792Sgshapiro	FIX_SYSV_SIGNAL(sig, intindebug);
310290792Sgshapiro	errno = save_errno;
310390792Sgshapiro	CHECK_CRITICAL(sig);
310490792Sgshapiro	errno = save_errno;
310590792Sgshapiro	sm_exc_raisenew_x(&EtypeInterrupt);
310690792Sgshapiro	errno = save_errno;
310790792Sgshapiro	return SIGFUNC_RETURN;
310890792Sgshapiro}
310990792Sgshapiro/*
311090792Sgshapiro**  SIGTERM -- SIGTERM handler for the daemon
311190792Sgshapiro**
311290792Sgshapiro**	Parameters:
311377349Sgshapiro**		sig -- signal number.
311477349Sgshapiro**
311577349Sgshapiro**	Returns:
311677349Sgshapiro**		none.
311777349Sgshapiro**
311877349Sgshapiro**	Side Effects:
311977349Sgshapiro**		Sets ShutdownRequest which will hopefully trigger
312077349Sgshapiro**		the daemon to exit.
312177349Sgshapiro**
312277349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
312377349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
312477349Sgshapiro**		DOING.
312577349Sgshapiro*/
312677349Sgshapiro
312777349Sgshapiro/* ARGSUSED */
312877349Sgshapirostatic SIGFUNC_DECL
312990792Sgshapirosigterm(sig)
313077349Sgshapiro	int sig;
313177349Sgshapiro{
313277349Sgshapiro	int save_errno = errno;
313377349Sgshapiro
313490792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigterm);
313577349Sgshapiro	ShutdownRequest = "signal";
313677349Sgshapiro	errno = save_errno;
313777349Sgshapiro	return SIGFUNC_RETURN;
313877349Sgshapiro}
313990792Sgshapiro/*
314090792Sgshapiro**  SIGHUP -- handle a SIGHUP signal
314177349Sgshapiro**
314277349Sgshapiro**	Parameters:
314390792Sgshapiro**		sig -- incoming signal.
314477349Sgshapiro**
314577349Sgshapiro**	Returns:
314677349Sgshapiro**		none.
314777349Sgshapiro**
314877349Sgshapiro**	Side Effects:
314990792Sgshapiro**		Sets RestartRequest which should cause the daemon
315090792Sgshapiro**		to restart.
315190792Sgshapiro**
315290792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
315390792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
315490792Sgshapiro**		DOING.
315577349Sgshapiro*/
315677349Sgshapiro
315790792Sgshapiro/* ARGSUSED */
315890792Sgshapirostatic SIGFUNC_DECL
315990792Sgshapirosighup(sig)
316090792Sgshapiro	int sig;
316177349Sgshapiro{
316290792Sgshapiro	int save_errno = errno;
316377349Sgshapiro
316490792Sgshapiro	FIX_SYSV_SIGNAL(sig, sighup);
316590792Sgshapiro	RestartRequest = "signal";
316690792Sgshapiro	errno = save_errno;
316790792Sgshapiro	return SIGFUNC_RETURN;
316890792Sgshapiro}
316990792Sgshapiro/*
317090792Sgshapiro**  SIGPIPE -- signal handler for SIGPIPE
317190792Sgshapiro**
317290792Sgshapiro**	Parameters:
317390792Sgshapiro**		sig -- incoming signal.
317490792Sgshapiro**
317590792Sgshapiro**	Returns:
317690792Sgshapiro**		none.
317790792Sgshapiro**
317890792Sgshapiro**	Side Effects:
317990792Sgshapiro**		Sets StopRequest which should cause the mailq/hoststatus
318090792Sgshapiro**		display to stop.
318190792Sgshapiro**
318290792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
318390792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
318490792Sgshapiro**		DOING.
318590792Sgshapiro*/
318677349Sgshapiro
318790792Sgshapiro/* ARGSUSED */
318890792Sgshapirostatic SIGFUNC_DECL
318990792Sgshapirosigpipe(sig)
319090792Sgshapiro	int sig;
319190792Sgshapiro{
319290792Sgshapiro	int save_errno = errno;
319377349Sgshapiro
319490792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigpipe);
319590792Sgshapiro	StopRequest = true;
319690792Sgshapiro	errno = save_errno;
319790792Sgshapiro	return SIGFUNC_RETURN;
319877349Sgshapiro}
319990792Sgshapiro/*
320038032Speter**  INTSIG -- clean up on interrupt
320138032Speter**
320264562Sgshapiro**	This just arranges to exit.  It pessimizes in that it
320338032Speter**	may resend a message.
320438032Speter**
320538032Speter**	Parameters:
3206223067Sgshapiro**		sig -- incoming signal.
320738032Speter**
320838032Speter**	Returns:
320938032Speter**		none.
321038032Speter**
321138032Speter**	Side Effects:
321238032Speter**		Unlocks the current job.
321377349Sgshapiro**
321477349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
321577349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
321677349Sgshapiro**		DOING.
321738032Speter*/
321838032Speter
321938032Speter/* ARGSUSED */
322038032SpeterSIGFUNC_DECL
322138032Speterintsig(sig)
322238032Speter	int sig;
322338032Speter{
322490792Sgshapiro	bool drop = false;
322577349Sgshapiro	int save_errno = errno;
322664562Sgshapiro
322777349Sgshapiro	FIX_SYSV_SIGNAL(sig, intsig);
322877349Sgshapiro	errno = save_errno;
322977349Sgshapiro	CHECK_CRITICAL(sig);
323090792Sgshapiro	sm_allsignals(true);
3231223067Sgshapiro	IntSig = true;
323290792Sgshapiro
323338032Speter	FileName = NULL;
323464562Sgshapiro
323564562Sgshapiro	/* Clean-up on aborted stdin message submission */
3236223067Sgshapiro	if  (OpMode == MD_SMTP ||
323764562Sgshapiro	     OpMode == MD_DELIVER ||
3238223067Sgshapiro	     OpMode == MD_ARPAFTP)
323964562Sgshapiro	{
3240223067Sgshapiro		if (CurEnv->e_id != NULL)
3241223067Sgshapiro		{
3242223067Sgshapiro			char *fn;
324364562Sgshapiro
3244223067Sgshapiro			fn = queuename(CurEnv, DATAFL_LETTER);
3245223067Sgshapiro			if (fn != NULL)
3246223067Sgshapiro				(void) unlink(fn);
3247223067Sgshapiro			fn = queuename(CurEnv, ANYQFL_LETTER);
3248223067Sgshapiro			if (fn != NULL)
3249223067Sgshapiro				(void) unlink(fn);
3250223067Sgshapiro		}
3251223067Sgshapiro		_exit(EX_OK);
3252223067Sgshapiro		/* NOTREACHED */
3253223067Sgshapiro	}
325464562Sgshapiro
3255223067Sgshapiro	if (sig != 0 && LogLevel > 79)
3256223067Sgshapiro		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
3257223067Sgshapiro	if (OpMode != MD_TEST)
325864562Sgshapiro		unlockqueue(CurEnv);
325938032Speter
326090792Sgshapiro	finis(drop, false, EX_OK);
326190792Sgshapiro	/* NOTREACHED */
326238032Speter}
326390792Sgshapiro/*
326438032Speter**  DISCONNECT -- remove our connection with any foreground process
326538032Speter**
326638032Speter**	Parameters:
326738032Speter**		droplev -- how "deeply" we should drop the line.
326838032Speter**			0 -- ignore signals, mail back errors, make sure
326938032Speter**			     output goes to stdout.
327064562Sgshapiro**			1 -- also, make stdout go to /dev/null.
327138032Speter**			2 -- also, disconnect from controlling terminal
327238032Speter**			     (only for daemon mode).
327338032Speter**		e -- the current envelope.
327438032Speter**
327538032Speter**	Returns:
327638032Speter**		none
327738032Speter**
327838032Speter**	Side Effects:
327938032Speter**		Trys to insure that we are immune to vagaries of
328038032Speter**		the controlling tty.
328138032Speter*/
328238032Speter
328338032Spetervoid
328438032Speterdisconnect(droplev, e)
328538032Speter	int droplev;
328638032Speter	register ENVELOPE *e;
328738032Speter{
328838032Speter	int fd;
328938032Speter
329038032Speter	if (tTd(52, 1))
329190792Sgshapiro		sm_dprintf("disconnect: In %d Out %d, e=%p\n",
329290792Sgshapiro			   sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
329390792Sgshapiro			   sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
329438032Speter	if (tTd(52, 100))
329538032Speter	{
329690792Sgshapiro		sm_dprintf("don't\n");
329738032Speter		return;
329838032Speter	}
329938032Speter	if (LogLevel > 93)
330038032Speter		sm_syslog(LOG_DEBUG, e->e_id,
330164562Sgshapiro			  "disconnect level %d",
330264562Sgshapiro			  droplev);
330338032Speter
330438032Speter	/* be sure we don't get nasty signals */
330590792Sgshapiro	(void) sm_signal(SIGINT, SIG_IGN);
330690792Sgshapiro	(void) sm_signal(SIGQUIT, SIG_IGN);
330738032Speter
330838032Speter	/* we can't communicate with our caller, so.... */
330990792Sgshapiro	HoldErrs = true;
331038032Speter	CurEnv->e_errormode = EM_MAIL;
331138032Speter	Verbose = 0;
331290792Sgshapiro	DisConnected = true;
331338032Speter
331438032Speter	/* all input from /dev/null */
331590792Sgshapiro	if (InChannel != smioin)
331638032Speter	{
331790792Sgshapiro		(void) sm_io_close(InChannel, SM_TIME_DEFAULT);
331890792Sgshapiro		InChannel = smioin;
331938032Speter	}
332090792Sgshapiro	if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
332190792Sgshapiro			 SM_IO_RDONLY, NULL, smioin) == NULL)
332238032Speter		sm_syslog(LOG_ERR, e->e_id,
332390792Sgshapiro			  "disconnect: sm_io_reopen(\"%s\") failed: %s",
332490792Sgshapiro			  SM_PATH_DEVNULL, sm_errstring(errno));
332538032Speter
332690792Sgshapiro	/*
332790792Sgshapiro	**  output to the transcript
332890792Sgshapiro	**	We also compare the fd numbers here since OutChannel
332990792Sgshapiro	**	might be a layer on top of smioout due to encryption
333090792Sgshapiro	**	(see sfsasl.c).
333190792Sgshapiro	*/
333290792Sgshapiro
333390792Sgshapiro	if (OutChannel != smioout &&
333490792Sgshapiro	    sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
333590792Sgshapiro	    sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
333638032Speter	{
333790792Sgshapiro		(void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
333890792Sgshapiro		OutChannel = smioout;
333990792Sgshapiro
334090792Sgshapiro#if 0
334190792Sgshapiro		/*
334290792Sgshapiro		**  Has smioout been closed? Reopen it.
334390792Sgshapiro		**	This shouldn't happen anymore, the code is here
334490792Sgshapiro		**	just as a reminder.
334590792Sgshapiro		*/
334690792Sgshapiro
334790792Sgshapiro		if (smioout->sm_magic == NULL &&
334890792Sgshapiro		    sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
334990792Sgshapiro				 SM_IO_WRONLY, NULL, smioout) == NULL)
335090792Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
335190792Sgshapiro				  "disconnect: sm_io_reopen(\"%s\") failed: %s",
335290792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
335390792Sgshapiro#endif /* 0 */
335438032Speter	}
335538032Speter	if (droplev > 0)
335638032Speter	{
335790792Sgshapiro		fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
335864562Sgshapiro		if (fd == -1)
3359159609Sgshapiro		{
336064562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
336190792Sgshapiro				  "disconnect: open(\"%s\") failed: %s",
336290792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
3363159609Sgshapiro		}
336490792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
3365159609Sgshapiro		if (fd >= 0)
3366159609Sgshapiro		{
3367159609Sgshapiro			(void) dup2(fd, STDOUT_FILENO);
3368159609Sgshapiro			(void) dup2(fd, STDERR_FILENO);
3369159609Sgshapiro			(void) close(fd);
3370159609Sgshapiro		}
337138032Speter	}
337238032Speter
337338032Speter	/* drop our controlling TTY completely if possible */
337438032Speter	if (droplev > 1)
337538032Speter	{
337638032Speter		(void) setsid();
337738032Speter		errno = 0;
337838032Speter	}
337938032Speter
338038032Speter#if XDEBUG
338138032Speter	checkfd012("disconnect");
338264562Sgshapiro#endif /* XDEBUG */
338338032Speter
338438032Speter	if (LogLevel > 71)
338590792Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
338690792Sgshapiro			  (int) CurrentPid);
338738032Speter
338838032Speter	errno = 0;
338938032Speter}
339038032Speter
339138032Speterstatic void
339238032Speterobsolete(argv)
339338032Speter	char *argv[];
339438032Speter{
339538032Speter	register char *ap;
339638032Speter	register char *op;
339738032Speter
339838032Speter	while ((ap = *++argv) != NULL)
339938032Speter	{
340038032Speter		/* Return if "--" or not an option of any form. */
340138032Speter		if (ap[0] != '-' || ap[1] == '-')
340238032Speter			return;
340338032Speter
340490792Sgshapiro		/* Don't allow users to use "-Q." or "-Q ." */
340590792Sgshapiro		if ((ap[1] == 'Q' && ap[2] == '.') ||
340690792Sgshapiro		    (ap[1] == 'Q' && argv[1] != NULL &&
340790792Sgshapiro		     argv[1][0] == '.' && argv[1][1] == '\0'))
340890792Sgshapiro		{
340990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
341090792Sgshapiro					     "Can not use -Q.\n");
341190792Sgshapiro			exit(EX_USAGE);
341290792Sgshapiro		}
341390792Sgshapiro
341438032Speter		/* skip over options that do have a value */
341538032Speter		op = strchr(OPTIONS, ap[1]);
341638032Speter		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
341738032Speter		    ap[1] != 'd' &&
341838032Speter#if defined(sony_news)
341938032Speter		    ap[1] != 'E' && ap[1] != 'J' &&
342064562Sgshapiro#endif /* defined(sony_news) */
342138032Speter		    argv[1] != NULL && argv[1][0] != '-')
342238032Speter		{
342338032Speter			argv++;
342438032Speter			continue;
342538032Speter		}
342638032Speter
342738032Speter		/* If -C doesn't have an argument, use sendmail.cf. */
342890792Sgshapiro#define __DEFPATH	"sendmail.cf"
342938032Speter		if (ap[1] == 'C' && ap[2] == '\0')
343038032Speter		{
343138032Speter			*argv = xalloc(sizeof(__DEFPATH) + 2);
343290792Sgshapiro			(void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
343390792Sgshapiro					   "-C", __DEFPATH);
343438032Speter		}
343538032Speter
343638032Speter		/* If -q doesn't have an argument, run it once. */
343738032Speter		if (ap[1] == 'q' && ap[2] == '\0')
343838032Speter			*argv = "-q0";
343938032Speter
344090792Sgshapiro		/* If -Q doesn't have an argument, disable quarantining */
344190792Sgshapiro		if (ap[1] == 'Q' && ap[2] == '\0')
344290792Sgshapiro			*argv = "-Q.";
344390792Sgshapiro
344438032Speter		/* if -d doesn't have an argument, use 0-99.1 */
344538032Speter		if (ap[1] == 'd' && ap[2] == '\0')
344638032Speter			*argv = "-d0-99.1";
344738032Speter
344864562Sgshapiro#if defined(sony_news)
344938032Speter		/* if -E doesn't have an argument, use -EC */
345038032Speter		if (ap[1] == 'E' && ap[2] == '\0')
345138032Speter			*argv = "-EC";
345238032Speter
345338032Speter		/* if -J doesn't have an argument, use -JJ */
345438032Speter		if (ap[1] == 'J' && ap[2] == '\0')
345538032Speter			*argv = "-JJ";
345664562Sgshapiro#endif /* defined(sony_news) */
345738032Speter	}
345838032Speter}
345990792Sgshapiro/*
346038032Speter**  AUTH_WARNING -- specify authorization warning
346138032Speter**
346238032Speter**	Parameters:
346338032Speter**		e -- the current envelope.
346438032Speter**		msg -- the text of the message.
346538032Speter**		args -- arguments to the message.
346638032Speter**
346738032Speter**	Returns:
346838032Speter**		none.
346938032Speter*/
347038032Speter
347138032Spetervoid
347238032Speter#ifdef __STDC__
347338032Speterauth_warning(register ENVELOPE *e, const char *msg, ...)
347464562Sgshapiro#else /* __STDC__ */
347538032Speterauth_warning(e, msg, va_alist)
347638032Speter	register ENVELOPE *e;
347738032Speter	const char *msg;
347838032Speter	va_dcl
347964562Sgshapiro#endif /* __STDC__ */
348038032Speter{
348138032Speter	char buf[MAXLINE];
348290792Sgshapiro	SM_VA_LOCAL_DECL
348338032Speter
348438032Speter	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
348538032Speter	{
348638032Speter		register char *p;
348738032Speter		static char hostbuf[48];
348838032Speter
348938032Speter		if (hostbuf[0] == '\0')
349071345Sgshapiro		{
349171345Sgshapiro			struct hostent *hp;
349238032Speter
3493168515Sgshapiro			hp = myhostname(hostbuf, sizeof(hostbuf));
349490792Sgshapiro#if NETINET6
349571345Sgshapiro			if (hp != NULL)
349671345Sgshapiro			{
349771345Sgshapiro				freehostent(hp);
349871345Sgshapiro				hp = NULL;
349971345Sgshapiro			}
350090792Sgshapiro#endif /* NETINET6 */
350171345Sgshapiro		}
350271345Sgshapiro
3503168515Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, hostbuf, ": ");
350438032Speter		p = &buf[strlen(buf)];
350590792Sgshapiro		SM_VA_START(ap, msg);
350690792Sgshapiro		(void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
350790792Sgshapiro		SM_VA_END(ap);
3508168515Sgshapiro		addheader("X-Authentication-Warning", buf, 0, e, true);
350938032Speter		if (LogLevel > 3)
351038032Speter			sm_syslog(LOG_INFO, e->e_id,
351164562Sgshapiro				  "Authentication-Warning: %.400s",
351264562Sgshapiro				  buf);
351338032Speter	}
351438032Speter}
351590792Sgshapiro/*
351638032Speter**  GETEXTENV -- get from external environment
351738032Speter**
351838032Speter**	Parameters:
351938032Speter**		envar -- the name of the variable to retrieve
352038032Speter**
352138032Speter**	Returns:
352238032Speter**		The value, if any.
352338032Speter*/
352438032Speter
352590792Sgshapirostatic char *
352638032Spetergetextenv(envar)
352738032Speter	const char *envar;
352838032Speter{
352938032Speter	char **envp;
353038032Speter	int l;
353138032Speter
353238032Speter	l = strlen(envar);
3533102528Sgshapiro	for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
353438032Speter	{
353538032Speter		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
353638032Speter			return &(*envp)[l + 1];
353738032Speter	}
353838032Speter	return NULL;
353938032Speter}
354090792Sgshapiro/*
3541157001Sgshapiro**  SM_SETUSERENV -- set an environment variable in the propagated environment
354238032Speter**
354338032Speter**	Parameters:
354438032Speter**		envar -- the name of the environment variable.
354538032Speter**		value -- the value to which it should be set.  If
354638032Speter**			null, this is extracted from the incoming
354738032Speter**			environment.  If that is not set, the call
3548157001Sgshapiro**			to sm_setuserenv is ignored.
354938032Speter**
355038032Speter**	Returns:
355138032Speter**		none.
355238032Speter*/
355338032Speter
355438032Spetervoid
3555157001Sgshapirosm_setuserenv(envar, value)
355638032Speter	const char *envar;
355738032Speter	const char *value;
355838032Speter{
355964562Sgshapiro	int i, l;
356038032Speter	char **evp = UserEnviron;
356138032Speter	char *p;
356238032Speter
356338032Speter	if (value == NULL)
356438032Speter	{
356538032Speter		value = getextenv(envar);
356638032Speter		if (value == NULL)
356738032Speter			return;
356838032Speter	}
356938032Speter
357090792Sgshapiro	/* XXX enforce reasonable size? */
357164562Sgshapiro	i = strlen(envar) + 1;
357264562Sgshapiro	l = strlen(value) + i + 1;
357364562Sgshapiro	p = (char *) xalloc(l);
357490792Sgshapiro	(void) sm_strlcpyn(p, l, 3, envar, "=", value);
357538032Speter
357638032Speter	while (*evp != NULL && strncmp(*evp, p, i) != 0)
357738032Speter		evp++;
357838032Speter	if (*evp != NULL)
357938032Speter	{
358038032Speter		*evp++ = p;
358138032Speter	}
358238032Speter	else if (evp < &UserEnviron[MAXUSERENVIRON])
358338032Speter	{
358438032Speter		*evp++ = p;
358538032Speter		*evp = NULL;
358638032Speter	}
358738032Speter
358838032Speter	/* make sure it is in our environment as well */
358938032Speter	if (putenv(p) < 0)
3590157001Sgshapiro		syserr("sm_setuserenv: putenv(%s) failed", p);
359138032Speter}
359290792Sgshapiro/*
359338032Speter**  DUMPSTATE -- dump state
359438032Speter**
359538032Speter**	For debugging.
359638032Speter*/
359738032Speter
359838032Spetervoid
359938032Speterdumpstate(when)
360038032Speter	char *when;
360138032Speter{
360238032Speter	register char *j = macvalue('j', CurEnv);
360338032Speter	int rs;
360464562Sgshapiro	extern int NextMacroId;
360538032Speter
360638032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id,
360764562Sgshapiro		  "--- dumping state on %s: $j = %s ---",
360864562Sgshapiro		  when,
360964562Sgshapiro		  j == NULL ? "<NULL>" : j);
361038032Speter	if (j != NULL)
361138032Speter	{
361238032Speter		if (!wordinclass(j, 'w'))
361338032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
361464562Sgshapiro				  "*** $j not in $=w ***");
361538032Speter	}
361638032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
361790792Sgshapiro	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
361864562Sgshapiro		  NextMacroId, MAXMACROID);
361938032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
362090792Sgshapiro	printopenfds(true);
362138032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
3622132943Sgshapiro	mci_dump_all(smioout, true);
362338032Speter	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
362438032Speter	if (rs > 0)
362538032Speter	{
362664562Sgshapiro		int status;
362738032Speter		register char **pvp;
362838032Speter		char *pv[MAXATOM + 1];
362938032Speter
363038032Speter		pv[0] = NULL;
363190792Sgshapiro		status = REWRITE(pv, rs, CurEnv);
363238032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
363364562Sgshapiro			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
363464562Sgshapiro			  status);
363538032Speter		for (pvp = pv; *pvp != NULL; pvp++)
363638032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
363738032Speter	}
363838032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
363938032Speter}
364090792Sgshapiro
364180785Sgshapiro#ifdef SIGUSR1
364290792Sgshapiro/*
364377349Sgshapiro**  SIGUSR1 -- Signal a request to dump state.
364477349Sgshapiro**
364577349Sgshapiro**	Parameters:
364677349Sgshapiro**		sig -- calling signal.
364777349Sgshapiro**
364877349Sgshapiro**	Returns:
364977349Sgshapiro**		none.
365077349Sgshapiro**
365177349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
365277349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
365377349Sgshapiro**		DOING.
365477349Sgshapiro**
365577349Sgshapiro**		XXX: More work is needed for this signal handler.
365677349Sgshapiro*/
365738032Speter
365838032Speter/* ARGSUSED */
365977349Sgshapirostatic SIGFUNC_DECL
366038032Spetersigusr1(sig)
366138032Speter	int sig;
366238032Speter{
366377349Sgshapiro	int save_errno = errno;
366477349Sgshapiro
366577349Sgshapiro	FIX_SYSV_SIGNAL(sig, sigusr1);
366677349Sgshapiro	errno = save_errno;
366777349Sgshapiro	CHECK_CRITICAL(sig);
366838032Speter	dumpstate("user signal");
366990792Sgshapiro# if SM_HEAP_CHECK
367090792Sgshapiro	dumpstab();
367190792Sgshapiro# endif /* SM_HEAP_CHECK */
367277349Sgshapiro	errno = save_errno;
367338032Speter	return SIGFUNC_RETURN;
367438032Speter}
367590792Sgshapiro#endif /* SIGUSR1 */
367690792Sgshapiro
367790792Sgshapiro/*
367838032Speter**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
367938032Speter**
368038032Speter**	Parameters:
368138032Speter**		to_real_uid -- if set, drop to the real uid instead
368238032Speter**			of the RunAsUser.
368338032Speter**
368438032Speter**	Returns:
368538032Speter**		EX_OSERR if the setuid failed.
368638032Speter**		EX_OK otherwise.
368738032Speter*/
368838032Speter
368938032Speterint
369038032Speterdrop_privileges(to_real_uid)
369138032Speter	bool to_real_uid;
369238032Speter{
369338032Speter	int rval = EX_OK;
369438032Speter	GIDSET_T emptygidset[1];
369538032Speter
369638032Speter	if (tTd(47, 1))
369790792Sgshapiro		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",
369890792Sgshapiro			   (int) to_real_uid,
369990792Sgshapiro			   (int) RealUid, (int) RealGid,
370090792Sgshapiro			   (int) getuid(), (int) getgid(),
370190792Sgshapiro			   (int) geteuid(), (int) getegid(),
370290792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
370338032Speter
370438032Speter	if (to_real_uid)
370538032Speter	{
370638032Speter		RunAsUserName = RealUserName;
370738032Speter		RunAsUid = RealUid;
370838032Speter		RunAsGid = RealGid;
370994334Sgshapiro		EffGid = RunAsGid;
371038032Speter	}
371138032Speter
371238032Speter	/* make sure no one can grab open descriptors for secret files */
371338032Speter	endpwent();
371490792Sgshapiro	sm_mbdb_terminate();
371538032Speter
371638032Speter	/* reset group permissions; these can be set later */
371738032Speter	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
371890792Sgshapiro
371990792Sgshapiro	/*
372090792Sgshapiro	**  Notice:  on some OS (Linux...) the setgroups() call causes
372190792Sgshapiro	**	a logfile entry if sendmail is not run by root.
372290792Sgshapiro	**	However, it is unclear (no POSIX standard) whether
372390792Sgshapiro	**	setgroups() can only succeed if executed by root.
372490792Sgshapiro	**	So for now we keep it as it is; if you want to change it, use
372590792Sgshapiro	**  if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
372690792Sgshapiro	*/
372790792Sgshapiro
372838032Speter	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
372964562Sgshapiro	{
373064562Sgshapiro		syserr("drop_privileges: setgroups(1, %d) failed",
373190792Sgshapiro		       (int) emptygidset[0]);
373238032Speter		rval = EX_OSERR;
373364562Sgshapiro	}
373438032Speter
373590792Sgshapiro	/* reset primary group id */
373690792Sgshapiro	if (to_real_uid)
373764562Sgshapiro	{
373890792Sgshapiro		/*
373990792Sgshapiro		**  Drop gid to real gid.
374090792Sgshapiro		**  On some OS we must reset the effective[/real[/saved]] gid,
374190792Sgshapiro		**  and then use setgid() to finally drop all group privileges.
374290792Sgshapiro		**  Later on we check whether we can get back the
374390792Sgshapiro		**  effective gid.
374490792Sgshapiro		*/
374590792Sgshapiro
374690792Sgshapiro#if HASSETEGID
374790792Sgshapiro		if (setegid(RunAsGid) < 0)
374890792Sgshapiro		{
374990792Sgshapiro			syserr("drop_privileges: setegid(%d) failed",
375090792Sgshapiro			       (int) RunAsGid);
375190792Sgshapiro			rval = EX_OSERR;
375290792Sgshapiro		}
375390792Sgshapiro#else /* HASSETEGID */
375490792Sgshapiro# if HASSETREGID
375590792Sgshapiro		if (setregid(RunAsGid, RunAsGid) < 0)
375690792Sgshapiro		{
375790792Sgshapiro			syserr("drop_privileges: setregid(%d, %d) failed",
375890792Sgshapiro			       (int) RunAsGid, (int) RunAsGid);
375990792Sgshapiro			rval = EX_OSERR;
376090792Sgshapiro		}
376190792Sgshapiro# else /* HASSETREGID */
376290792Sgshapiro#  if HASSETRESGID
376390792Sgshapiro		if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
376490792Sgshapiro		{
376590792Sgshapiro			syserr("drop_privileges: setresgid(%d, %d, %d) failed",
376690792Sgshapiro			       (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
376790792Sgshapiro			rval = EX_OSERR;
376890792Sgshapiro		}
376990792Sgshapiro#  endif /* HASSETRESGID */
377090792Sgshapiro# endif /* HASSETREGID */
377190792Sgshapiro#endif /* HASSETEGID */
377264562Sgshapiro	}
377390792Sgshapiro	if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
377490792Sgshapiro	{
377590792Sgshapiro		if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
377690792Sgshapiro		{
377790792Sgshapiro			syserr("drop_privileges: setgid(%d) failed",
377890792Sgshapiro			       (int) RunAsGid);
377990792Sgshapiro			rval = EX_OSERR;
378090792Sgshapiro		}
378190792Sgshapiro		errno = 0;
378290792Sgshapiro		if (rval == EX_OK && getegid() != RunAsGid)
378390792Sgshapiro		{
378490792Sgshapiro			syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
378590792Sgshapiro			       (int) getegid(), (int) RunAsGid);
378690792Sgshapiro			rval = EX_OSERR;
378790792Sgshapiro		}
378890792Sgshapiro	}
378990792Sgshapiro
379090792Sgshapiro	/* fiddle with uid */
379164562Sgshapiro	if (to_real_uid || RunAsUid != 0)
379264562Sgshapiro	{
379394334Sgshapiro		uid_t euid;
379464562Sgshapiro
379590792Sgshapiro		/*
379690792Sgshapiro		**  Try to setuid(RunAsUid).
379790792Sgshapiro		**  euid must be RunAsUid,
379894334Sgshapiro		**  ruid must be RunAsUid unless (e|r)uid wasn't 0
379994334Sgshapiro		**	and we didn't have to drop privileges to the real uid.
380090792Sgshapiro		*/
380190792Sgshapiro
380290792Sgshapiro		if (setuid(RunAsUid) < 0 ||
380394334Sgshapiro		    geteuid() != RunAsUid ||
380494334Sgshapiro		    (getuid() != RunAsUid &&
380594334Sgshapiro		     (to_real_uid || geteuid() == 0 || getuid() == 0)))
380664562Sgshapiro		{
380790792Sgshapiro#if HASSETREUID
380890792Sgshapiro			/*
380990792Sgshapiro			**  if ruid != RunAsUid, euid == RunAsUid, then
381090792Sgshapiro			**  try resetting just the real uid, then using
381190792Sgshapiro			**  setuid() to drop the saved-uid as well.
381290792Sgshapiro			*/
381390792Sgshapiro
381494334Sgshapiro			if (geteuid() == RunAsUid)
381590792Sgshapiro			{
381690792Sgshapiro				if (setreuid(RunAsUid, -1) < 0)
381790792Sgshapiro				{
381890792Sgshapiro					syserr("drop_privileges: setreuid(%d, -1) failed",
381990792Sgshapiro					       (int) RunAsUid);
382090792Sgshapiro					rval = EX_OSERR;
382190792Sgshapiro				}
382290792Sgshapiro				if (setuid(RunAsUid) < 0)
382390792Sgshapiro				{
382490792Sgshapiro					syserr("drop_privileges: second setuid(%d) attempt failed",
382590792Sgshapiro					       (int) RunAsUid);
382690792Sgshapiro					rval = EX_OSERR;
382790792Sgshapiro				}
382890792Sgshapiro			}
382990792Sgshapiro			else
383090792Sgshapiro#endif /* HASSETREUID */
383190792Sgshapiro			{
383290792Sgshapiro				syserr("drop_privileges: setuid(%d) failed",
383390792Sgshapiro				       (int) RunAsUid);
383490792Sgshapiro				rval = EX_OSERR;
383590792Sgshapiro			}
383664562Sgshapiro		}
383794334Sgshapiro		euid = geteuid();
383890792Sgshapiro		if (RunAsUid != 0 && setuid(0) == 0)
383964562Sgshapiro		{
384064562Sgshapiro			/*
384164562Sgshapiro			**  Believe it or not, the Linux capability model
384264562Sgshapiro			**  allows a non-root process to override setuid()
384364562Sgshapiro			**  on a process running as root and prevent that
384464562Sgshapiro			**  process from dropping privileges.
384564562Sgshapiro			*/
384664562Sgshapiro
384764562Sgshapiro			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
384864562Sgshapiro			rval = EX_OSERR;
384964562Sgshapiro		}
385064562Sgshapiro		else if (RunAsUid != euid && setuid(euid) == 0)
385164562Sgshapiro		{
385264562Sgshapiro			/*
385364562Sgshapiro			**  Some operating systems will keep the saved-uid
385464562Sgshapiro			**  if a non-root effective-uid calls setuid(real-uid)
385564562Sgshapiro			**  making it possible to set it back again later.
385664562Sgshapiro			*/
385764562Sgshapiro
385890792Sgshapiro			syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
385964562Sgshapiro			rval = EX_OSERR;
386064562Sgshapiro		}
386164562Sgshapiro	}
386290792Sgshapiro
386390792Sgshapiro	if ((to_real_uid || RunAsGid != 0) &&
386490792Sgshapiro	    rval == EX_OK && RunAsGid != EffGid &&
386590792Sgshapiro	    getuid() != 0 && geteuid() != 0)
386690792Sgshapiro	{
386790792Sgshapiro		errno = 0;
386890792Sgshapiro		if (setgid(EffGid) == 0)
386990792Sgshapiro		{
387090792Sgshapiro			syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
387190792Sgshapiro			       (int) EffGid);
387290792Sgshapiro			rval = EX_OSERR;
387390792Sgshapiro		}
387490792Sgshapiro	}
387590792Sgshapiro
387638032Speter	if (tTd(47, 5))
387738032Speter	{
387890792Sgshapiro		sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
387990792Sgshapiro			   (int) geteuid(), (int) getuid(),
388090792Sgshapiro			   (int) getegid(), (int) getgid());
388190792Sgshapiro		sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
388290792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
388364562Sgshapiro		if (tTd(47, 10))
388490792Sgshapiro			sm_dprintf("drop_privileges: rval = %d\n", rval);
388538032Speter	}
388638032Speter	return rval;
388738032Speter}
388890792Sgshapiro/*
388938032Speter**  FILL_FD -- make sure a file descriptor has been properly allocated
389038032Speter**
389138032Speter**	Used to make sure that stdin/out/err are allocated on startup
389238032Speter**
389338032Speter**	Parameters:
389438032Speter**		fd -- the file descriptor to be filled.
389538032Speter**		where -- a string used for logging.  If NULL, this is
389638032Speter**			being called on startup, and logging should
389738032Speter**			not be done.
389838032Speter**
389938032Speter**	Returns:
390038032Speter**		none
390190792Sgshapiro**
390290792Sgshapiro**	Side Effects:
390390792Sgshapiro**		possibly changes MissingFds
390438032Speter*/
390538032Speter
390638032Spetervoid
390738032Speterfill_fd(fd, where)
390838032Speter	int fd;
390938032Speter	char *where;
391038032Speter{
391138032Speter	int i;
391238032Speter	struct stat stbuf;
391338032Speter
391438032Speter	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
391538032Speter		return;
391638032Speter
391738032Speter	if (where != NULL)
391838032Speter		syserr("fill_fd: %s: fd %d not open", where, fd);
391938032Speter	else
392038032Speter		MissingFds |= 1 << fd;
392190792Sgshapiro	i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
392238032Speter	if (i < 0)
392338032Speter	{
392490792Sgshapiro		syserr("!fill_fd: %s: cannot open %s",
392590792Sgshapiro		       where == NULL ? "startup" : where, SM_PATH_DEVNULL);
392638032Speter	}
392738032Speter	if (fd != i)
392838032Speter	{
392938032Speter		(void) dup2(i, fd);
393038032Speter		(void) close(i);
393138032Speter	}
393238032Speter}
393390792Sgshapiro/*
393490792Sgshapiro**  SM_PRINTOPTIONS -- print options
393590792Sgshapiro**
393690792Sgshapiro**	Parameters:
393790792Sgshapiro**		options -- array of options.
393890792Sgshapiro**
393990792Sgshapiro**	Returns:
394090792Sgshapiro**		none.
394190792Sgshapiro*/
394290792Sgshapiro
394390792Sgshapirostatic void
394490792Sgshapirosm_printoptions(options)
394590792Sgshapiro	char **options;
394690792Sgshapiro{
394790792Sgshapiro	int ll;
394890792Sgshapiro	char **av;
394990792Sgshapiro
395090792Sgshapiro	av = options;
395190792Sgshapiro	ll = 7;
395290792Sgshapiro	while (*av != NULL)
395390792Sgshapiro	{
395490792Sgshapiro		if (ll + strlen(*av) > 63)
395590792Sgshapiro		{
395690792Sgshapiro			sm_dprintf("\n");
395790792Sgshapiro			ll = 0;
395890792Sgshapiro		}
395990792Sgshapiro		if (ll == 0)
396090792Sgshapiro			sm_dprintf("\t\t");
396190792Sgshapiro		else
396290792Sgshapiro			sm_dprintf(" ");
396390792Sgshapiro		sm_dprintf("%s", *av);
396490792Sgshapiro		ll += strlen(*av++) + 1;
396590792Sgshapiro	}
396690792Sgshapiro	sm_dprintf("\n");
396790792Sgshapiro}
3968168515Sgshapiro
396990792Sgshapiro/*
3970168515Sgshapiro**  TO8BIT -- convert \octal sequences in a test mode input line
3971168515Sgshapiro**
3972168515Sgshapiro**	Parameters:
3973168515Sgshapiro**		str -- the input line.
3974168515Sgshapiro**
3975168515Sgshapiro**	Returns:
3976168515Sgshapiro**		none.
3977168515Sgshapiro**
3978168515Sgshapiro**	Side Effects:
3979168515Sgshapiro**		replaces \0octal in str with octal value.
3980168515Sgshapiro*/
3981168515Sgshapiro
3982168515Sgshapirostatic bool to8bit __P((char *));
3983168515Sgshapiro
3984168515Sgshapirostatic bool
3985168515Sgshapiroto8bit(str)
3986168515Sgshapiro	char *str;
3987168515Sgshapiro{
3988168515Sgshapiro	int c, len;
3989168515Sgshapiro	char *out, *in;
3990168515Sgshapiro	bool changed;
3991168515Sgshapiro
3992168515Sgshapiro	if (str == NULL)
3993168515Sgshapiro		return false;
3994168515Sgshapiro	in = out = str;
3995168515Sgshapiro	changed = false;
3996168515Sgshapiro	len = 0;
3997168515Sgshapiro	while ((c = (*str++ & 0377)) != '\0')
3998168515Sgshapiro	{
3999168515Sgshapiro		int oct, nxtc;
4000168515Sgshapiro
4001168515Sgshapiro		++len;
4002168515Sgshapiro		if (c == '\\' &&
4003168515Sgshapiro		    (nxtc = (*str & 0377)) == '0')
4004168515Sgshapiro		{
4005168515Sgshapiro			oct = 0;
4006168515Sgshapiro			while ((nxtc = (*str & 0377)) != '\0' &&
4007168515Sgshapiro				isascii(nxtc) && isdigit(nxtc))
4008168515Sgshapiro			{
4009168515Sgshapiro				oct <<= 3;
4010168515Sgshapiro				oct += nxtc - '0';
4011168515Sgshapiro				++str;
4012168515Sgshapiro				++len;
4013168515Sgshapiro			}
4014168515Sgshapiro			changed = true;
4015168515Sgshapiro			c = oct;
4016168515Sgshapiro		}
4017168515Sgshapiro		*out++ = c;
4018168515Sgshapiro	}
4019168515Sgshapiro	*out++ = c;
4020168515Sgshapiro	if (changed)
4021168515Sgshapiro	{
4022168515Sgshapiro		char *q;
4023168515Sgshapiro
4024168515Sgshapiro		q = quote_internal_chars(in, in, &len);
4025168515Sgshapiro		if (q != in)
4026168515Sgshapiro			sm_strlcpy(in, q, len);
4027168515Sgshapiro	}
4028168515Sgshapiro	return changed;
4029168515Sgshapiro}
4030168515Sgshapiro
4031168515Sgshapiro/*
403238032Speter**  TESTMODELINE -- process a test mode input line
403338032Speter**
403438032Speter**	Parameters:
403538032Speter**		line -- the input line.
403638032Speter**		e -- the current environment.
403738032Speter**	Syntax:
403838032Speter**		#  a comment
403938032Speter**		.X process X as a configuration line
404038032Speter**		=X dump a configuration item (such as mailers)
404138032Speter**		$X dump a macro or class
404238032Speter**		/X try an activity
404338032Speter**		X  normal process through rule set X
404438032Speter*/
404538032Speter
404664562Sgshapirostatic void
404738032Spetertestmodeline(line, e)
404838032Speter	char *line;
404938032Speter	ENVELOPE *e;
405038032Speter{
405138032Speter	register char *p;
405238032Speter	char *q;
405338032Speter	auto char *delimptr;
405438032Speter	int mid;
405538032Speter	int i, rs;
405638032Speter	STAB *map;
405738032Speter	char **s;
405838032Speter	struct rewrite *rw;
405938032Speter	ADDRESS a;
4060168515Sgshapiro	char *lbp;
4061168515Sgshapiro	auto int lbs;
406238032Speter	static int tryflags = RF_COPYNONE;
406338032Speter	char exbuf[MAXLINE];
4064168515Sgshapiro	char lbuf[MAXLINE];
406590792Sgshapiro	extern unsigned char TokTypeNoC[];
4066168515Sgshapiro	bool eightbit;
406738032Speter
406866494Sgshapiro	/* skip leading spaces */
406966494Sgshapiro	while (*line == ' ')
407066494Sgshapiro		line++;
407166494Sgshapiro
4072168515Sgshapiro	lbp = NULL;
4073168515Sgshapiro	eightbit = false;
407438032Speter	switch (line[0])
407538032Speter	{
407638032Speter	  case '#':
407764562Sgshapiro	  case '\0':
407838032Speter		return;
407938032Speter
408038032Speter	  case '?':
408164562Sgshapiro		help("-bt", e);
408238032Speter		return;
408338032Speter
408438032Speter	  case '.':		/* config-style settings */
408538032Speter		switch (line[1])
408638032Speter		{
408738032Speter		  case 'D':
408890792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
408971345Sgshapiro			if (mid == 0)
409038032Speter				return;
4091168515Sgshapiro			lbs = sizeof(lbuf);
4092168515Sgshapiro			lbp = translate_dollars(delimptr, lbuf, &lbs);
4093168515Sgshapiro			macdefine(&e->e_macro, A_TEMP, mid, lbp);
4094168515Sgshapiro			if (lbp != lbuf)
4095168515Sgshapiro				SM_FREE(lbp);
409638032Speter			break;
409738032Speter
409838032Speter		  case 'C':
409938032Speter			if (line[2] == '\0')	/* not to call syserr() */
410038032Speter				return;
410138032Speter
410290792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
410371345Sgshapiro			if (mid == 0)
410438032Speter				return;
4105168515Sgshapiro			lbs = sizeof(lbuf);
4106168515Sgshapiro			lbp = translate_dollars(delimptr, lbuf, &lbs);
4107168515Sgshapiro			expand(lbp, exbuf, sizeof(exbuf), e);
4108168515Sgshapiro			if (lbp != lbuf)
4109168515Sgshapiro				SM_FREE(lbp);
411038032Speter			p = exbuf;
411138032Speter			while (*p != '\0')
411238032Speter			{
411338032Speter				register char *wd;
411438032Speter				char delim;
411538032Speter
411638032Speter				while (*p != '\0' && isascii(*p) && isspace(*p))
411738032Speter					p++;
411838032Speter				wd = p;
411938032Speter				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
412038032Speter					p++;
412138032Speter				delim = *p;
412238032Speter				*p = '\0';
412338032Speter				if (wd[0] != '\0')
412438032Speter					setclass(mid, wd);
412538032Speter				*p = delim;
412638032Speter			}
412738032Speter			break;
412838032Speter
412938032Speter		  case '\0':
413090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
413190792Sgshapiro					     "Usage: .[DC]macro value(s)\n");
413238032Speter			break;
413338032Speter
413438032Speter		  default:
413590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
413690792Sgshapiro					     "Unknown \".\" command %s\n", line);
413738032Speter			break;
413838032Speter		}
413938032Speter		return;
414038032Speter
414138032Speter	  case '=':		/* config-style settings */
414238032Speter		switch (line[1])
414338032Speter		{
414438032Speter		  case 'S':		/* dump rule set */
414538032Speter			rs = strtorwset(&line[2], NULL, ST_FIND);
414638032Speter			if (rs < 0)
414738032Speter			{
414890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
414990792Sgshapiro						     "Undefined ruleset %s\n", &line[2]);
415038032Speter				return;
415138032Speter			}
415238032Speter			rw = RewriteRules[rs];
415338032Speter			if (rw == NULL)
415438032Speter				return;
415538032Speter			do
415638032Speter			{
415790792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
415890792Sgshapiro						  'R');
415938032Speter				s = rw->r_lhs;
416038032Speter				while (*s != NULL)
416138032Speter				{
4162132943Sgshapiro					xputs(smioout, *s++);
416390792Sgshapiro					(void) sm_io_putc(smioout,
416490792Sgshapiro							  SM_TIME_DEFAULT, ' ');
416538032Speter				}
416690792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
416790792Sgshapiro						  '\t');
416890792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
416990792Sgshapiro						  '\t');
417038032Speter				s = rw->r_rhs;
417138032Speter				while (*s != NULL)
417238032Speter				{
4173132943Sgshapiro					xputs(smioout, *s++);
417490792Sgshapiro					(void) sm_io_putc(smioout,
417590792Sgshapiro							  SM_TIME_DEFAULT, ' ');
417638032Speter				}
417790792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
417890792Sgshapiro						  '\n');
417938032Speter			} while ((rw = rw->r_next) != NULL);
418038032Speter			break;
418138032Speter
418238032Speter		  case 'M':
418338032Speter			for (i = 0; i < MAXMAILERS; i++)
418438032Speter			{
418538032Speter				if (Mailer[i] != NULL)
4186132943Sgshapiro					printmailer(smioout, Mailer[i]);
418738032Speter			}
418838032Speter			break;
418938032Speter
419038032Speter		  case '\0':
419190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
419290792Sgshapiro					     "Usage: =Sruleset or =M\n");
419338032Speter			break;
419438032Speter
419538032Speter		  default:
419690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
419790792Sgshapiro					     "Unknown \"=\" command %s\n", line);
419838032Speter			break;
419938032Speter		}
420038032Speter		return;
420138032Speter
420238032Speter	  case '-':		/* set command-line-like opts */
420338032Speter		switch (line[1])
420438032Speter		{
420538032Speter		  case 'd':
420638032Speter			tTflag(&line[2]);
420738032Speter			break;
420838032Speter
420938032Speter		  case '\0':
421090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
421190792Sgshapiro					     "Usage: -d{debug arguments}\n");
421238032Speter			break;
421338032Speter
421438032Speter		  default:
421590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
421690792Sgshapiro					     "Unknown \"-\" command %s\n", line);
421738032Speter			break;
421838032Speter		}
421938032Speter		return;
422038032Speter
422138032Speter	  case '$':
422238032Speter		if (line[1] == '=')
422338032Speter		{
422490792Sgshapiro			mid = macid(&line[2]);
422571345Sgshapiro			if (mid != 0)
422638032Speter				stabapply(dump_class, mid);
422738032Speter			return;
422838032Speter		}
422990792Sgshapiro		mid = macid(&line[1]);
423071345Sgshapiro		if (mid == 0)
423138032Speter			return;
423238032Speter		p = macvalue(mid, e);
423338032Speter		if (p == NULL)
423490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
423590792Sgshapiro					     "Undefined\n");
423638032Speter		else
423738032Speter		{
4238132943Sgshapiro			xputs(smioout, p);
423990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
424090792Sgshapiro					     "\n");
424138032Speter		}
424238032Speter		return;
424338032Speter
424438032Speter	  case '/':		/* miscellaneous commands */
424538032Speter		p = &line[strlen(line)];
424638032Speter		while (--p >= line && isascii(*p) && isspace(*p))
424738032Speter			*p = '\0';
424838032Speter		p = strpbrk(line, " \t");
424938032Speter		if (p != NULL)
425038032Speter		{
425138032Speter			while (isascii(*p) && isspace(*p))
425238032Speter				*p++ = '\0';
425338032Speter		}
425438032Speter		else
425538032Speter			p = "";
425638032Speter		if (line[1] == '\0')
425738032Speter		{
425890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
425990792Sgshapiro					     "Usage: /[canon|map|mx|parse|try|tryflags]\n");
426038032Speter			return;
426138032Speter		}
426290792Sgshapiro		if (sm_strcasecmp(&line[1], "quit") == 0)
426364562Sgshapiro		{
426464562Sgshapiro			CurEnv->e_id = NULL;
426590792Sgshapiro			finis(true, true, ExitStat);
426690792Sgshapiro			/* NOTREACHED */
426764562Sgshapiro		}
426890792Sgshapiro		if (sm_strcasecmp(&line[1], "mx") == 0)
426938032Speter		{
427038032Speter#if NAMED_BIND
427138032Speter			/* look up MX records */
427238032Speter			int nmx;
427338032Speter			auto int rcode;
427438032Speter			char *mxhosts[MAXMXHOSTS + 1];
427538032Speter
427638032Speter			if (*p == '\0')
427738032Speter			{
427890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
427990792Sgshapiro						     "Usage: /mx address\n");
428038032Speter				return;
428138032Speter			}
428290792Sgshapiro			nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
428390792Sgshapiro				      NULL);
428490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
428590792Sgshapiro					     "getmxrr(%s) returns %d value(s):\n",
428690792Sgshapiro				p, nmx);
428738032Speter			for (i = 0; i < nmx; i++)
428890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
428990792Sgshapiro						     "\t%s\n", mxhosts[i]);
429064562Sgshapiro#else /* NAMED_BIND */
429190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
429290792Sgshapiro					     "No MX code compiled in\n");
429364562Sgshapiro#endif /* NAMED_BIND */
429438032Speter		}
429590792Sgshapiro		else if (sm_strcasecmp(&line[1], "canon") == 0)
429638032Speter		{
429738032Speter			char host[MAXHOSTNAMELEN];
429838032Speter
429938032Speter			if (*p == '\0')
430038032Speter			{
430190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
430290792Sgshapiro						     "Usage: /canon address\n");
430338032Speter				return;
430438032Speter			}
4305168515Sgshapiro			else if (sm_strlcpy(host, p, sizeof(host)) >= sizeof(host))
430638032Speter			{
430790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
430890792Sgshapiro						     "Name too long\n");
430938032Speter				return;
431038032Speter			}
4311168515Sgshapiro			(void) getcanonname(host, sizeof(host), !HasWildcardMX,
431290792Sgshapiro					    NULL);
431390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
431490792Sgshapiro					     "getcanonname(%s) returns %s\n",
431590792Sgshapiro					     p, host);
431638032Speter		}
431790792Sgshapiro		else if (sm_strcasecmp(&line[1], "map") == 0)
431838032Speter		{
431938032Speter			auto int rcode = EX_OK;
432038032Speter			char *av[2];
432138032Speter
432238032Speter			if (*p == '\0')
432338032Speter			{
432490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
432590792Sgshapiro						     "Usage: /map mapname key\n");
432638032Speter				return;
432738032Speter			}
432890792Sgshapiro			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q));			     q++)
432938032Speter				continue;
433038032Speter			if (*q == '\0')
433138032Speter			{
433290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
433390792Sgshapiro						     "No key specified\n");
433438032Speter				return;
433538032Speter			}
433638032Speter			*q++ = '\0';
433738032Speter			map = stab(p, ST_MAP, ST_FIND);
433838032Speter			if (map == NULL)
433938032Speter			{
434090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
434190792Sgshapiro						     "Map named \"%s\" not found\n", p);
434238032Speter				return;
434338032Speter			}
434464562Sgshapiro			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
434564562Sgshapiro			    !openmap(&(map->s_map)))
434638032Speter			{
434790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
434890792Sgshapiro						     "Map named \"%s\" not open\n", p);
434938032Speter				return;
435038032Speter			}
435190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
435290792Sgshapiro					     "map_lookup: %s (%s) ", p, q);
435338032Speter			av[0] = q;
435438032Speter			av[1] = NULL;
435538032Speter			p = (*map->s_map.map_class->map_lookup)
435638032Speter					(&map->s_map, q, av, &rcode);
435738032Speter			if (p == NULL)
435890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
435990792Sgshapiro						     "no match (%d)\n",
436090792Sgshapiro						     rcode);
436138032Speter			else
436290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
436390792Sgshapiro						     "returns %s (%d)\n", p,
436490792Sgshapiro						     rcode);
436538032Speter		}
436690792Sgshapiro		else if (sm_strcasecmp(&line[1], "try") == 0)
436738032Speter		{
436838032Speter			MAILER *m;
436964562Sgshapiro			STAB *st;
437038032Speter			auto int rcode = EX_OK;
437138032Speter
437238032Speter			q = strpbrk(p, " \t");
437338032Speter			if (q != NULL)
437438032Speter			{
437538032Speter				while (isascii(*q) && isspace(*q))
437638032Speter					*q++ = '\0';
437738032Speter			}
437838032Speter			if (q == NULL || *q == '\0')
437938032Speter			{
438090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
438190792Sgshapiro						     "Usage: /try mailer address\n");
438238032Speter				return;
438338032Speter			}
438464562Sgshapiro			st = stab(p, ST_MAILER, ST_FIND);
438564562Sgshapiro			if (st == NULL)
438638032Speter			{
438790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
438890792Sgshapiro						     "Unknown mailer %s\n", p);
438938032Speter				return;
439038032Speter			}
439164562Sgshapiro			m = st->s_mailer;
439290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
439390792Sgshapiro					     "Trying %s %s address %s for mailer %s\n",
439490792Sgshapiro				     bitset(RF_HEADERADDR, tryflags) ? "header"
439590792Sgshapiro							: "envelope",
439690792Sgshapiro				     bitset(RF_SENDERADDR, tryflags) ? "sender"
439790792Sgshapiro							: "recipient", q, p);
439838032Speter			p = remotename(q, m, tryflags, &rcode, CurEnv);
439990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
440090792Sgshapiro					     "Rcode = %d, addr = %s\n",
440190792Sgshapiro					     rcode, p == NULL ? "<NULL>" : p);
440238032Speter			e->e_to = NULL;
440338032Speter		}
440490792Sgshapiro		else if (sm_strcasecmp(&line[1], "tryflags") == 0)
440538032Speter		{
440638032Speter			if (*p == '\0')
440738032Speter			{
440890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
440990792Sgshapiro						     "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
441038032Speter				return;
441138032Speter			}
441238032Speter			for (; *p != '\0'; p++)
441338032Speter			{
441438032Speter				switch (*p)
441538032Speter				{
441638032Speter				  case 'H':
441738032Speter				  case 'h':
441838032Speter					tryflags |= RF_HEADERADDR;
441938032Speter					break;
442038032Speter
442138032Speter				  case 'E':
442238032Speter				  case 'e':
442338032Speter					tryflags &= ~RF_HEADERADDR;
442438032Speter					break;
442538032Speter
442638032Speter				  case 'S':
442738032Speter				  case 's':
442838032Speter					tryflags |= RF_SENDERADDR;
442938032Speter					break;
443038032Speter
443138032Speter				  case 'R':
443238032Speter				  case 'r':
443338032Speter					tryflags &= ~RF_SENDERADDR;
443438032Speter					break;
443538032Speter				}
443638032Speter			}
443764562Sgshapiro			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
443864562Sgshapiro			exbuf[1] = ' ';
443964562Sgshapiro			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
444064562Sgshapiro			exbuf[3] = '\0';
444190792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
444290792Sgshapiro				macid("{addr_type}"), exbuf);
444338032Speter		}
444490792Sgshapiro		else if (sm_strcasecmp(&line[1], "parse") == 0)
444538032Speter		{
444638032Speter			if (*p == '\0')
444738032Speter			{
444890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
444990792Sgshapiro						     "Usage: /parse address\n");
445038032Speter				return;
445138032Speter			}
4452111823Sgshapiro			q = crackaddr(p, e);
445390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
445490792Sgshapiro					     "Cracked address = ");
4455132943Sgshapiro			xputs(smioout, q);
445690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
445790792Sgshapiro					     "\nParsing %s %s address\n",
445890792Sgshapiro					     bitset(RF_HEADERADDR, tryflags) ?
445990792Sgshapiro							"header" : "envelope",
446090792Sgshapiro					     bitset(RF_SENDERADDR, tryflags) ?
446190792Sgshapiro							"sender" : "recipient");
446290792Sgshapiro			if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
446390792Sgshapiro			    == NULL)
446490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
446590792Sgshapiro						     "Cannot parse\n");
446638032Speter			else if (a.q_host != NULL && a.q_host[0] != '\0')
446790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
446890792Sgshapiro						     "mailer %s, host %s, user %s\n",
446990792Sgshapiro						     a.q_mailer->m_name,
447090792Sgshapiro						     a.q_host,
447190792Sgshapiro						     a.q_user);
447238032Speter			else
447390792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
447490792Sgshapiro						     "mailer %s, user %s\n",
447590792Sgshapiro						     a.q_mailer->m_name,
447690792Sgshapiro						     a.q_user);
447738032Speter			e->e_to = NULL;
447838032Speter		}
4479168515Sgshapiro		else if (sm_strcasecmp(&line[1], "header") == 0)
4480168515Sgshapiro		{
4481168515Sgshapiro			unsigned long ul;
4482168515Sgshapiro
4483168515Sgshapiro			ul = chompheader(p, CHHDR_CHECK|CHHDR_USER, NULL, e);
4484168515Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4485168515Sgshapiro					     "ul = %lu\n", ul);
4486168515Sgshapiro		}
448738032Speter		else
448838032Speter		{
448990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
449090792Sgshapiro					     "Unknown \"/\" command %s\n",
449190792Sgshapiro					     line);
449238032Speter		}
4493168515Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
449438032Speter		return;
449538032Speter	}
449638032Speter
449738032Speter	for (p = line; isascii(*p) && isspace(*p); p++)
449838032Speter		continue;
449938032Speter	q = p;
450038032Speter	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
450138032Speter		p++;
450238032Speter	if (*p == '\0')
450338032Speter	{
450490792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
450590792Sgshapiro				     "No address!\n");
450638032Speter		return;
450738032Speter	}
450838032Speter	*p = '\0';
4509168515Sgshapiro	if (tTd(23, 101))
4510168515Sgshapiro		eightbit = to8bit(p + 1);
451190792Sgshapiro	if (invalidaddr(p + 1, NULL, true))
451238032Speter		return;
451338032Speter	do
451438032Speter	{
451538032Speter		register char **pvp;
451638032Speter		char pvpbuf[PSBUFSIZE];
451738032Speter
4518168515Sgshapiro		pvp = prescan(++p, ',', pvpbuf, sizeof(pvpbuf), &delimptr,
4519168515Sgshapiro			      ConfigLevel >= 9 ? TokTypeNoC : ExtTokenTab, false);
452038032Speter		if (pvp == NULL)
452138032Speter			continue;
452238032Speter		p = q;
452338032Speter		while (*p != '\0')
452438032Speter		{
452564562Sgshapiro			int status;
452638032Speter
452738032Speter			rs = strtorwset(p, NULL, ST_FIND);
452838032Speter			if (rs < 0)
452938032Speter			{
453090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
453190792Sgshapiro						     "Undefined ruleset %s\n",
453290792Sgshapiro						     p);
453338032Speter				break;
453438032Speter			}
453590792Sgshapiro			status = REWRITE(pvp, rs, e);
453664562Sgshapiro			if (status != EX_OK)
453790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
453890792Sgshapiro						     "== Ruleset %s (%d) status %d\n",
453990792Sgshapiro						     p, rs, status);
4540168515Sgshapiro			else if (eightbit)
4541168515Sgshapiro			{
4542168515Sgshapiro				cataddr(pvp, NULL, exbuf, sizeof(exbuf), '\0',
4543168515Sgshapiro					true);
4544168515Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4545168515Sgshapiro						     "cataddr: %s\n",
4546168515Sgshapiro						     str2prt(exbuf));
4547168515Sgshapiro			}
454838032Speter			while (*p != '\0' && *p++ != ',')
454938032Speter				continue;
455038032Speter		}
455138032Speter	} while (*(p = delimptr) != '\0');
4552168515Sgshapiro	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
455338032Speter}
455438032Speter
455564562Sgshapirostatic void
455638032Speterdump_class(s, id)
455738032Speter	register STAB *s;
455838032Speter	int id;
455938032Speter{
456090792Sgshapiro	if (s->s_symtype != ST_CLASS)
456138032Speter		return;
456271345Sgshapiro	if (bitnset(bitidx(id), s->s_class))
456390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
456490792Sgshapiro				     "%s\n", s->s_name);
456538032Speter}
456690792Sgshapiro
456790792Sgshapiro/*
456890792Sgshapiro**  An exception type used to create QuickAbort exceptions.
456990792Sgshapiro**  This is my first cut at converting QuickAbort from longjmp to exceptions.
457090792Sgshapiro**  These exceptions have a single integer argument, which is the argument
457190792Sgshapiro**  to longjmp in the original code (either 1 or 2).  I don't know the
457290792Sgshapiro**  significance of 1 vs 2: the calls to setjmp don't care.
457390792Sgshapiro*/
457490792Sgshapiro
457590792Sgshapiroconst SM_EXC_TYPE_T EtypeQuickAbort =
457690792Sgshapiro{
457790792Sgshapiro	SmExcTypeMagic,
457890792Sgshapiro	"E:mta.quickabort",
457990792Sgshapiro	"i",
458090792Sgshapiro	sm_etype_printf,
458190792Sgshapiro	"quick abort %0",
458290792Sgshapiro};
4583