138032Speter/*
2261194Sgshapiro * Copyright (c) 1998-2006, 2008, 2009, 2011 Proofpoint, 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>
19363466Sgshapiro#include <tls.h>
2090792Sgshapiro
2138032Speter#ifndef lint
2290792SgshapiroSM_UNUSED(static char copyright[]) =
23261194Sgshapiro"@(#) Copyright (c) 1998-2013 Proofpoint, Inc. and its suppliers.\n\
2464562Sgshapiro	All rights reserved.\n\
2538032Speter     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
2638032Speter     Copyright (c) 1988, 1993\n\
2738032Speter	The Regents of the University of California.  All rights reserved.\n";
2864562Sgshapiro#endif /* ! lint */
2938032Speter
30266527SgshapiroSM_RCSID("@(#)$Id: main.c,v 8.988 2013-11-23 02:52:37 gshapiro Exp $")
3138032Speter
3238032Speter
3364562Sgshapiro#if NETINET || NETINET6
3464562Sgshapiro# include <arpa/inet.h>
35363466Sgshapiro# if DANE
36363466Sgshapiro#  include "sm_resolve.h"
37363466Sgshapiro# endif
38363466Sgshapiro#endif
3964562Sgshapiro
4090792Sgshapiro/* for getcfname() */
4190792Sgshapiro#include <sendmail/pathnames.h>
42363466Sgshapiro#include <ratectrl.h>
4390792Sgshapiro
4490792Sgshapirostatic SM_DEBUG_T
4590792SgshapiroDebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
4690792Sgshapiro	"@(#)$Debug: no_persistent_restart - don't restart, log only $");
4790792Sgshapiro
4890792Sgshapirostatic void	dump_class __P((STAB *, int));
4990792Sgshapirostatic void	obsolete __P((char **));
5090792Sgshapirostatic void	testmodeline __P((char *, ENVELOPE *));
5190792Sgshapirostatic char	*getextenv __P((const char *));
5290792Sgshapirostatic void	sm_printoptions __P((char **));
5377349Sgshapirostatic SIGFUNC_DECL	intindebug __P((int));
5490792Sgshapirostatic SIGFUNC_DECL	sighup __P((int));
5590792Sgshapirostatic SIGFUNC_DECL	sigpipe __P((int));
5690792Sgshapirostatic SIGFUNC_DECL	sigterm __P((int));
5780785Sgshapiro#ifdef SIGUSR1
5877349Sgshapirostatic SIGFUNC_DECL	sigusr1 __P((int));
59363466Sgshapiro#endif
6064562Sgshapiro
6138032Speter/*
6238032Speter**  SENDMAIL -- Post mail to a set of destinations.
6338032Speter**
6438032Speter**	This is the basic mail router.  All user mail programs should
6538032Speter**	call this routine to actually deliver mail.  Sendmail in
6638032Speter**	turn calls a bunch of mail servers that do the real work of
6738032Speter**	delivering the mail.
6838032Speter**
6964562Sgshapiro**	Sendmail is driven by settings read in from /etc/mail/sendmail.cf
7038032Speter**	(read by readcf.c).
7138032Speter**
7238032Speter**	Usage:
7338032Speter**		/usr/lib/sendmail [flags] addr ...
7438032Speter**
7538032Speter**		See the associated documentation for details.
7638032Speter**
7790792Sgshapiro**	Authors:
7838032Speter**		Eric Allman, UCB/INGRES (until 10/81).
7938032Speter**			     Britton-Lee, Inc., purveyors of fine
8038032Speter**				database computers (11/81 - 10/88).
8138032Speter**			     International Computer Science Institute
8238032Speter**				(11/88 - 9/89).
8338032Speter**			     UCB/Mammoth Project (10/89 - 7/95).
8438032Speter**			     InReference, Inc. (8/95 - 1/97).
85261194Sgshapiro**			     Sendmail, Inc. (1/98 - 9/13).
86111823Sgshapiro**		The support of my employers is gratefully acknowledged.
8738032Speter**			Few of them (Britton-Lee in particular) have had
8838032Speter**			anything to gain from my involvement in this project.
8990792Sgshapiro**
9090792Sgshapiro**		Gregory Neil Shapiro,
9190792Sgshapiro**			Worcester Polytechnic Institute	(until 3/98).
92261194Sgshapiro**			Sendmail, Inc. (3/98 - 10/13).
93261194Sgshapiro**			Proofpoint, Inc. (10/13 - present).
9490792Sgshapiro**
9590792Sgshapiro**		Claus Assmann,
96261194Sgshapiro**			Sendmail, Inc. (12/98 - 10/13).
97261194Sgshapiro**			Proofpoint, Inc. (10/13 - present).
9838032Speter*/
9938032Speter
10038032Speterchar		*FullName;	/* sender's full name */
10138032SpeterENVELOPE	BlankEnvelope;	/* a "blank" envelope */
10264562Sgshapirostatic ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
10338032SpeterADDRESS		NullAddress =	/* a null address */
10438032Speter		{ "", "", NULL, "" };
10538032Speterchar		*CommandLineArgs;	/* command line args for pid file */
10690792Sgshapirobool		Warn_Q_option = false;	/* warn about Q option use */
10764562Sgshapirostatic int	MissingFds = 0;	/* bit map of fds missing on startup */
10890792Sgshapirochar		*Mbdb = "pw";	/* mailbox database defaults to /etc/passwd */
10938032Speter
11038032Speter#ifdef NGROUPS_MAX
11138032SpeterGIDSET_T	InitialGidSet[NGROUPS_MAX];
112363466Sgshapiro#endif
11338032Speter
11490792Sgshapiro#define MAXCONFIGLEVEL	10	/* highest config version level known */
11538032Speter
11664562Sgshapiro#if SASL
11764562Sgshapirostatic sasl_callback_t srvcallbacks[] =
11864562Sgshapiro{
119225906Sume	{	SASL_CB_VERIFYFILE,	(sasl_callback_ft)&safesaslfile,	NULL	},
120225906Sume	{	SASL_CB_PROXY_POLICY,	(sasl_callback_ft)&proxy_policy,	NULL	},
12164562Sgshapiro	{	SASL_CB_LIST_END,	NULL,		NULL	}
12264562Sgshapiro};
12364562Sgshapiro#endif /* SASL */
12464562Sgshapiro
12590792Sgshapirounsigned int	SubmitMode;
12690792Sgshapiroint		SyslogPrefixLen; /* estimated length of syslog prefix */
12790792Sgshapiro#define PIDLEN		6	/* pid length for computing SyslogPrefixLen */
12890792Sgshapiro#ifndef SL_FUDGE
12990792Sgshapiro# define SL_FUDGE	10	/* fudge offset for SyslogPrefixLen */
130363466Sgshapiro#endif
13190792Sgshapiro#define SLDLL		8	/* est. length of default syslog label */
13264562Sgshapiro
13390792Sgshapiro
13490792Sgshapiro/* Some options are dangerous to allow users to use in non-submit mode */
13590792Sgshapiro#define CHECK_AGAINST_OPMODE(cmd)					\
13690792Sgshapiro{									\
13790792Sgshapiro	if (extraprivs &&						\
13890792Sgshapiro	    OpMode != MD_DELIVER && OpMode != MD_SMTP &&		\
139203004Sgshapiro	    OpMode != MD_ARPAFTP && OpMode != MD_CHECKCONFIG &&		\
14090792Sgshapiro	    OpMode != MD_VERIFY && OpMode != MD_TEST)			\
14190792Sgshapiro	{								\
14290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
14390792Sgshapiro				     "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
14490792Sgshapiro		       (cmd));						\
14590792Sgshapiro		break;							\
14690792Sgshapiro	}								\
14790792Sgshapiro	if (extraprivs && queuerun)					\
14890792Sgshapiro	{								\
14990792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
15090792Sgshapiro				     "WARNING: Ignoring submission mode -%c option with -q\n", \
15190792Sgshapiro		       (cmd));						\
15290792Sgshapiro		break;							\
15390792Sgshapiro	}								\
15490792Sgshapiro}
15590792Sgshapiro
15638032Speterint
15738032Spetermain(argc, argv, envp)
15838032Speter	int argc;
15938032Speter	char **argv;
16038032Speter	char **envp;
16138032Speter{
16238032Speter	register char *p;
16338032Speter	char **av;
16438032Speter	extern char Version[];
16538032Speter	char *ep, *from;
16638032Speter	STAB *st;
16738032Speter	register int i;
16838032Speter	int j;
16964562Sgshapiro	int dp;
17090792Sgshapiro	int fill_errno;
17190792Sgshapiro	int qgrp = NOQGRP;		/* queue group to process */
17290792Sgshapiro	bool safecf = true;
17364562Sgshapiro	BITMAP256 *p_flags = NULL;	/* daemon flags */
17490792Sgshapiro	bool warn_C_flag = false;
17590792Sgshapiro	bool auth = true;		/* whether to set e_auth_param */
17638032Speter	char warn_f_flag = '\0';
17790792Sgshapiro	bool run_in_foreground = false;	/* -bD mode */
17890792Sgshapiro	bool queuerun = false, debug = false;
17938032Speter	struct passwd *pw;
18038032Speter	struct hostent *hp;
18138032Speter	char *nullserver = NULL;
18264562Sgshapiro	char *authinfo = NULL;
18364562Sgshapiro	char *sysloglabel = NULL;	/* label for syslog */
18490792Sgshapiro	char *conffile = NULL;		/* name of .cf file */
18590792Sgshapiro	char *queuegroup = NULL;	/* queue group to process */
18690792Sgshapiro	char *quarantining = NULL;	/* quarantine queue items? */
18790792Sgshapiro	bool extraprivs;
18890792Sgshapiro	bool forged, negate;
18990792Sgshapiro	bool queuepersistent = false;	/* queue runner process runs forever */
19090792Sgshapiro	bool foregroundqueue = false;	/* queue run in foreground */
19190792Sgshapiro	bool save_val;			/* to save some bool var. */
19290792Sgshapiro	int cftype;			/* which cf file to use? */
193132943Sgshapiro	SM_FILE_T *smdebug;
19490792Sgshapiro	static time_t starttime = 0;	/* when was process started */
19564562Sgshapiro	struct stat traf_st;		/* for TrafficLog FIFO check */
19690792Sgshapiro	char buf[MAXLINE];
19738032Speter	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
19838032Speter	static char rnamebuf[MAXNAME];	/* holds RealUserName */
19938032Speter	char *emptyenviron[1];
20090792Sgshapiro#if STARTTLS
20166494Sgshapiro	bool tls_ok;
202363466Sgshapiro#endif
20338032Speter	QUEUE_CHAR *new;
20490792Sgshapiro	ENVELOPE *e;
20538032Speter	extern int DtableSize;
20638032Speter	extern int optind;
20738032Speter	extern int opterr;
20838032Speter	extern char *optarg;
20938032Speter	extern char **environ;
21090792Sgshapiro#if SASL
21190792Sgshapiro	extern void sm_sasl_init __P((void));
212363466Sgshapiro#endif
21338032Speter
21490792Sgshapiro#if USE_ENVIRON
21590792Sgshapiro	envp = environ;
216363466Sgshapiro#endif
21790792Sgshapiro
21890792Sgshapiro	/* turn off profiling */
21990792Sgshapiro	SM_PROF(0);
22090792Sgshapiro
22190792Sgshapiro	/* install default exception handler */
22290792Sgshapiro	sm_exc_newthread(fatal_error);
22390792Sgshapiro
224110560Sgshapiro	/* set the default in/out channel so errors reported to screen */
225110560Sgshapiro	InChannel = smioin;
226110560Sgshapiro	OutChannel = smioout;
227110560Sgshapiro
22838032Speter	/*
22938032Speter	**  Check to see if we reentered.
23038032Speter	**	This would normally happen if e_putheader or e_putbody
23138032Speter	**	were NULL when invoked.
23238032Speter	*/
23338032Speter
23490792Sgshapiro	if (starttime != 0)
23538032Speter	{
23638032Speter		syserr("main: reentered!");
23738032Speter		abort();
23838032Speter	}
23990792Sgshapiro	starttime = curtime();
24038032Speter
24138032Speter	/* avoid null pointer dereferences */
242168515Sgshapiro	TermEscape.te_rv_on = TermEscape.te_under_on = TermEscape.te_normal = "";
24338032Speter
24490792Sgshapiro	RealUid = getuid();
24590792Sgshapiro	RealGid = getgid();
24677349Sgshapiro
24790792Sgshapiro	/* Check if sendmail is running with extra privs */
24890792Sgshapiro	extraprivs = (RealUid != 0 &&
24990792Sgshapiro		      (geteuid() != getuid() || getegid() != getgid()));
25077349Sgshapiro
25190792Sgshapiro	CurrentPid = getpid();
25238032Speter
25390792Sgshapiro	/* get whatever .cf file is right for the opmode */
25490792Sgshapiro	cftype = SM_GET_RIGHT_CF;
25564562Sgshapiro
25638032Speter	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
25738032Speter	DtableSize = getdtsize();
25838032Speter	if (DtableSize > 256)
25938032Speter		DtableSize = 256;
26038032Speter
26138032Speter	/*
26238032Speter	**  Be sure we have enough file descriptors.
26338032Speter	**	But also be sure that 0, 1, & 2 are open.
26438032Speter	*/
26538032Speter
26690792Sgshapiro	/* reset errno and fill_errno; the latter is used way down below */
26790792Sgshapiro	errno = fill_errno = 0;
26838032Speter	fill_fd(STDIN_FILENO, NULL);
26990792Sgshapiro	if (errno != 0)
27090792Sgshapiro		fill_errno = errno;
27138032Speter	fill_fd(STDOUT_FILENO, NULL);
27290792Sgshapiro	if (errno != 0)
27390792Sgshapiro		fill_errno = errno;
27438032Speter	fill_fd(STDERR_FILENO, NULL);
27590792Sgshapiro	if (errno != 0)
27690792Sgshapiro		fill_errno = errno;
27738032Speter
278132943Sgshapiro	sm_closefrom(STDERR_FILENO + 1, DtableSize);
27938032Speter	errno = 0;
280132943Sgshapiro	smdebug = NULL;
28138032Speter
28238032Speter#if LOG
28390792Sgshapiro# ifndef SM_LOG_STR
28490792Sgshapiro#  define SM_LOG_STR	"sendmail"
285363466Sgshapiro# endif
286363466Sgshapiro# ifdef LOG_MAIL
28790792Sgshapiro	openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
288363466Sgshapiro# else
28990792Sgshapiro	openlog(SM_LOG_STR, LOG_PID);
290363466Sgshapiro# endif
29164562Sgshapiro#endif /* LOG */
29238032Speter
29390792Sgshapiro	/*
29490792Sgshapiro	**  Seed the random number generator.
29590792Sgshapiro	**  Used for queue file names, picking a queue directory, and
29690792Sgshapiro	**  MX randomization.
29790792Sgshapiro	*/
29838032Speter
29990792Sgshapiro	seed_random();
30038032Speter
30190792Sgshapiro	/* do machine-dependent initializations */
30290792Sgshapiro	init_md(argc, argv);
30390792Sgshapiro
30490792Sgshapiro
30590792Sgshapiro	SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
30690792Sgshapiro
30738032Speter	/* reset status from syserr() calls for missing file descriptors */
30838032Speter	Errors = 0;
30938032Speter	ExitStat = EX_OK;
31038032Speter
31164562Sgshapiro	SubmitMode = SUBMIT_UNKNOWN;
312182352Sgshapiro#if _FFR_LOCAL_DAEMON
313182352Sgshapiro	LocalDaemon = false;
314223067Sgshapiro# if NETINET6
315223067Sgshapiro	V6LoopbackAddrFound = false;
316363466Sgshapiro# endif
317363466Sgshapiro#endif
31838032Speter#if XDEBUG
31938032Speter	checkfd012("after openlog");
320363466Sgshapiro#endif
32138032Speter
322168515Sgshapiro	tTsetup(tTdvect, sizeof(tTdvect), "0-99.1,*_trace_*.1");
32338032Speter
32438032Speter#ifdef NGROUPS_MAX
32538032Speter	/* save initial group set for future checks */
32638032Speter	i = getgroups(NGROUPS_MAX, InitialGidSet);
32790792Sgshapiro	if (i <= 0)
32890792Sgshapiro	{
32938032Speter		InitialGidSet[0] = (GID_T) -1;
33090792Sgshapiro		i = 0;
33190792Sgshapiro	}
33238032Speter	while (i < NGROUPS_MAX)
33338032Speter		InitialGidSet[i++] = InitialGidSet[0];
33464562Sgshapiro#endif /* NGROUPS_MAX */
33538032Speter
33638032Speter	/* drop group id privileges (RunAsUser not yet set) */
33790792Sgshapiro	dp = drop_privileges(false);
33864562Sgshapiro	setstat(dp);
33938032Speter
34090792Sgshapiro#ifdef SIGUSR1
34177349Sgshapiro	/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
34294334Sgshapiro	if (!extraprivs)
34377349Sgshapiro	{
34477349Sgshapiro		/* arrange to dump state on user-1 signal */
34590792Sgshapiro		(void) sm_signal(SIGUSR1, sigusr1);
34677349Sgshapiro	}
34794334Sgshapiro	else
34894334Sgshapiro	{
34994334Sgshapiro		/* ignore user-1 signal */
35094334Sgshapiro		(void) sm_signal(SIGUSR1, SIG_IGN);
35194334Sgshapiro	}
35290792Sgshapiro#endif /* SIGUSR1 */
35338032Speter
35438032Speter	/* initialize for setproctitle */
35538032Speter	initsetproctitle(argc, argv, envp);
35638032Speter
35738032Speter	/* Handle any non-getoptable constructions. */
35838032Speter	obsolete(argv);
35938032Speter
36038032Speter	/*
36138032Speter	**  Do a quick prescan of the argument list.
36238032Speter	*/
36338032Speter
36464562Sgshapiro
36590792Sgshapiro	/* find initial opMode */
36690792Sgshapiro	OpMode = MD_DELIVER;
36790792Sgshapiro	av = argv;
36890792Sgshapiro	p = strrchr(*av, '/');
36990792Sgshapiro	if (p++ == NULL)
37090792Sgshapiro		p = *av;
37190792Sgshapiro	if (strcmp(p, "newaliases") == 0)
37290792Sgshapiro		OpMode = MD_INITALIAS;
37390792Sgshapiro	else if (strcmp(p, "mailq") == 0)
37490792Sgshapiro		OpMode = MD_PRINT;
37590792Sgshapiro	else if (strcmp(p, "smtpd") == 0)
37690792Sgshapiro		OpMode = MD_DAEMON;
37790792Sgshapiro	else if (strcmp(p, "hoststat") == 0)
37890792Sgshapiro		OpMode = MD_HOSTSTAT;
37990792Sgshapiro	else if (strcmp(p, "purgestat") == 0)
38090792Sgshapiro		OpMode = MD_PURGESTAT;
38190792Sgshapiro
382132943Sgshapiro#if defined(__osf__) || defined(_AIX3)
383132943Sgshapiro# 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"
384132943Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */
385132943Sgshapiro#if defined(sony_news)
386132943Sgshapiro# 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:"
387132943Sgshapiro#endif /* defined(sony_news) */
388132943Sgshapiro#ifndef OPTIONS
389132943Sgshapiro# define OPTIONS	"A:B:b:C:cD:d:e:F:f:Gh:IiL:M:mN:nO:o:p:Q:q:R:r:sTtV:vX:"
390363466Sgshapiro#endif
39190792Sgshapiro
392112810Sgshapiro	/* Set to 0 to allow -b; need to check optarg before using it! */
39338032Speter	opterr = 0;
39438032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
39538032Speter	{
39638032Speter		switch (j)
39738032Speter		{
39890792Sgshapiro		  case 'b':	/* operations mode */
39994334Sgshapiro			j = (optarg == NULL) ? ' ' : *optarg;
40094334Sgshapiro			switch (j)
40138032Speter			{
40290792Sgshapiro			  case MD_DAEMON:
40390792Sgshapiro			  case MD_FGDAEMON:
40490792Sgshapiro			  case MD_SMTP:
40590792Sgshapiro			  case MD_INITALIAS:
40690792Sgshapiro			  case MD_DELIVER:
40790792Sgshapiro			  case MD_VERIFY:
40890792Sgshapiro			  case MD_TEST:
40990792Sgshapiro			  case MD_PRINT:
41090792Sgshapiro			  case MD_PRINTNQE:
41190792Sgshapiro			  case MD_HOSTSTAT:
41290792Sgshapiro			  case MD_PURGESTAT:
41390792Sgshapiro			  case MD_ARPAFTP:
414203004Sgshapiro			  case MD_CHECKCONFIG:
41590792Sgshapiro				OpMode = j;
41638032Speter				break;
41790792Sgshapiro
418182352Sgshapiro#if _FFR_LOCAL_DAEMON
419182352Sgshapiro			  case MD_LOCAL:
420182352Sgshapiro				OpMode = MD_DAEMON;
421182352Sgshapiro				LocalDaemon = true;
422182352Sgshapiro				break;
423182352Sgshapiro#endif /* _FFR_LOCAL_DAEMON */
424182352Sgshapiro
42590792Sgshapiro			  case MD_FREEZE:
42690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
42790792Sgshapiro						     "Frozen configurations unsupported\n");
42890792Sgshapiro				return EX_USAGE;
42990792Sgshapiro
43090792Sgshapiro			  default:
43190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
43290792Sgshapiro						     "Invalid operation mode %c\n",
43390792Sgshapiro						     j);
43490792Sgshapiro				return EX_USAGE;
43538032Speter			}
43690792Sgshapiro			break;
43790792Sgshapiro
438132943Sgshapiro		  case 'D':
439132943Sgshapiro			if (debug)
440132943Sgshapiro			{
441132943Sgshapiro				errno = 0;
442132943Sgshapiro				syserr("-D file must be before -d");
443132943Sgshapiro				ExitStat = EX_USAGE;
444132943Sgshapiro				break;
445132943Sgshapiro			}
446132943Sgshapiro			dp = drop_privileges(true);
447132943Sgshapiro			setstat(dp);
448132943Sgshapiro			smdebug = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
449132943Sgshapiro					    optarg, SM_IO_APPEND, NULL);
450132943Sgshapiro			if (smdebug == NULL)
451132943Sgshapiro			{
452132943Sgshapiro				syserr("cannot open %s", optarg);
453132943Sgshapiro				ExitStat = EX_CANTCREAT;
454132943Sgshapiro				break;
455132943Sgshapiro			}
456132943Sgshapiro			sm_debug_setfile(smdebug);
457132943Sgshapiro			break;
458132943Sgshapiro
45990792Sgshapiro		  case 'd':
46090792Sgshapiro			debug = true;
46138032Speter			tTflag(optarg);
462132943Sgshapiro			(void) sm_io_setvbuf(sm_debug_file(), SM_TIME_DEFAULT,
46390792Sgshapiro					     (char *) NULL, SM_IO_NBF,
46490792Sgshapiro					     SM_IO_BUFSIZ);
46538032Speter			break;
46664562Sgshapiro
46764562Sgshapiro		  case 'G':	/* relay (gateway) submission */
46890792Sgshapiro			SubmitMode = SUBMIT_MTA;
46964562Sgshapiro			break;
47064562Sgshapiro
47164562Sgshapiro		  case 'L':
472112810Sgshapiro			if (optarg == NULL)
473112810Sgshapiro			{
474112810Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
475112810Sgshapiro						     "option requires an argument -- '%c'",
476112810Sgshapiro						     (char) j);
477112810Sgshapiro				return EX_USAGE;
478112810Sgshapiro			}
479132943Sgshapiro			j = SM_MIN(strlen(optarg), 32) + 1;
48064562Sgshapiro			sysloglabel = xalloc(j);
48190792Sgshapiro			(void) sm_strlcpy(sysloglabel, optarg, j);
48290792Sgshapiro			SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
48390792Sgshapiro					  SL_FUDGE + j;
48464562Sgshapiro			break;
48564562Sgshapiro
48690792Sgshapiro		  case 'Q':
48790792Sgshapiro		  case 'q':
48890792Sgshapiro			/* just check if it is there */
48990792Sgshapiro			queuerun = true;
49064562Sgshapiro			break;
49138032Speter		}
49238032Speter	}
49338032Speter	opterr = 1;
49438032Speter
49590792Sgshapiro	/* Don't leak queue information via debug flags */
49690792Sgshapiro	if (extraprivs && queuerun && debug)
49790792Sgshapiro	{
49890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
49990792Sgshapiro				     "WARNING: Can not use -d with -q.  Disabling debugging.\n");
500132943Sgshapiro		sm_debug_close();
50190792Sgshapiro		sm_debug_setfile(NULL);
502168515Sgshapiro		(void) memset(tTdvect, '\0', sizeof(tTdvect));
50390792Sgshapiro	}
50490792Sgshapiro
50577349Sgshapiro#if LOG
50664562Sgshapiro	if (sysloglabel != NULL)
50764562Sgshapiro	{
50877349Sgshapiro		/* Sanitize the string */
50977349Sgshapiro		for (p = sysloglabel; *p != '\0'; p++)
51077349Sgshapiro		{
51177349Sgshapiro			if (!isascii(*p) || !isprint(*p) || *p == '%')
51277349Sgshapiro				*p = '*';
51377349Sgshapiro		}
51464562Sgshapiro		closelog();
515363466Sgshapiro# ifdef LOG_MAIL
51664562Sgshapiro		openlog(sysloglabel, LOG_PID, LOG_MAIL);
517363466Sgshapiro# else
51864562Sgshapiro		openlog(sysloglabel, LOG_PID);
519363466Sgshapiro# endif
52077349Sgshapiro	}
52164562Sgshapiro#endif /* LOG */
52264562Sgshapiro
52338032Speter	/* set up the blank envelope */
52438032Speter	BlankEnvelope.e_puthdr = putheader;
52538032Speter	BlankEnvelope.e_putbody = putbody;
52638032Speter	BlankEnvelope.e_xfp = NULL;
52738032Speter	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
52838032Speter	CurEnv = &BlankEnvelope;
52938032Speter	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
53038032Speter
53138032Speter	/*
53238032Speter	**  Set default values for variables.
53338032Speter	**	These cannot be in initialized data space.
53438032Speter	*/
53538032Speter
53638032Speter	setdefaults(&BlankEnvelope);
53790792Sgshapiro	initmacros(&BlankEnvelope);
53838032Speter
53990792Sgshapiro	/* reset macro */
54090792Sgshapiro	set_op_mode(OpMode);
541159609Sgshapiro	if (OpMode == MD_DAEMON)
542159609Sgshapiro		DaemonPid = CurrentPid;	/* needed for finis() to work */
54338032Speter
54438032Speter	pw = sm_getpwuid(RealUid);
54538032Speter	if (pw != NULL)
546168515Sgshapiro		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof(rnamebuf));
54738032Speter	else
548168515Sgshapiro		(void) sm_snprintf(rnamebuf, sizeof(rnamebuf), "Unknown UID %d",
54990792Sgshapiro				   (int) RealUid);
55064562Sgshapiro
55138032Speter	RealUserName = rnamebuf;
55238032Speter
55338032Speter	if (tTd(0, 101))
55438032Speter	{
55590792Sgshapiro		sm_dprintf("Version %s\n", Version);
55690792Sgshapiro		finis(false, true, EX_OK);
55790792Sgshapiro		/* NOTREACHED */
55838032Speter	}
55938032Speter
56038032Speter	/*
56190792Sgshapiro	**  if running non-set-user-ID binary as non-root, pretend
56238032Speter	**  we are the RunAsUid
56338032Speter	*/
56477349Sgshapiro
56538032Speter	if (RealUid != 0 && geteuid() == RealUid)
56638032Speter	{
56738032Speter		if (tTd(47, 1))
56890792Sgshapiro			sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
56990792Sgshapiro				   (int) RealUid);
57038032Speter		RunAsUid = RealUid;
57138032Speter	}
57238032Speter	else if (geteuid() != 0)
57338032Speter		RunAsUid = geteuid();
57438032Speter
57590792Sgshapiro	EffGid = getegid();
57690792Sgshapiro	if (RealUid != 0 && EffGid == RealGid)
57738032Speter		RunAsGid = RealGid;
57838032Speter
57938032Speter	if (tTd(47, 5))
58038032Speter	{
58190792Sgshapiro		sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
58290792Sgshapiro			   (int) geteuid(), (int) getuid(),
58390792Sgshapiro			   (int) getegid(), (int) getgid());
58490792Sgshapiro		sm_dprintf("main: RunAsUser = %d:%d\n",
58590792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
58638032Speter	}
58738032Speter
58838032Speter	/* save command line arguments */
58964562Sgshapiro	j = 0;
59038032Speter	for (av = argv; *av != NULL; )
59164562Sgshapiro		j += strlen(*av++) + 1;
592168515Sgshapiro	SaveArgv = (char **) xalloc(sizeof(char *) * (argc + 1));
59364562Sgshapiro	CommandLineArgs = xalloc(j);
59438032Speter	p = CommandLineArgs;
59538032Speter	for (av = argv, i = 0; *av != NULL; )
59638032Speter	{
59764562Sgshapiro		int h;
59864562Sgshapiro
59938032Speter		SaveArgv[i++] = newstr(*av);
60038032Speter		if (av != argv)
60138032Speter			*p++ = ' ';
60290792Sgshapiro		(void) sm_strlcpy(p, *av++, j);
60364562Sgshapiro		h = strlen(p);
60464562Sgshapiro		p += h;
60564562Sgshapiro		j -= h + 1;
60638032Speter	}
60738032Speter	SaveArgv[i] = NULL;
60838032Speter
60938032Speter	if (tTd(0, 1))
61038032Speter	{
61138032Speter		extern char *CompileOptions[];
61238032Speter
61390792Sgshapiro		sm_dprintf("Version %s\n Compiled with:", Version);
61490792Sgshapiro		sm_printoptions(CompileOptions);
61538032Speter	}
61638032Speter	if (tTd(0, 10))
61738032Speter	{
61838032Speter		extern char *OsCompileOptions[];
61938032Speter
62090792Sgshapiro		sm_dprintf("    OS Defines:");
62190792Sgshapiro		sm_printoptions(OsCompileOptions);
62238032Speter#ifdef _PATH_UNIX
62390792Sgshapiro		sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
624363466Sgshapiro#endif
62590792Sgshapiro
62690792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MSP)\n",
62790792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
62890792Sgshapiro				     conffile));
62990792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MTA)\n",
63090792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
63190792Sgshapiro				     conffile));
63290792Sgshapiro		sm_dprintf("      Pid file:\t%s (default)\n", PidFile);
63338032Speter	}
63438032Speter
63590792Sgshapiro	if (tTd(0, 12))
63690792Sgshapiro	{
63790792Sgshapiro		extern char *SmCompileOptions[];
63838032Speter
63990792Sgshapiro		sm_dprintf(" libsm Defines:");
64090792Sgshapiro		sm_printoptions(SmCompileOptions);
64190792Sgshapiro	}
64290792Sgshapiro
64390792Sgshapiro	if (tTd(0, 13))
64490792Sgshapiro	{
64590792Sgshapiro		extern char *FFRCompileOptions[];
64690792Sgshapiro
64790792Sgshapiro		sm_dprintf("   FFR Defines:");
64890792Sgshapiro		sm_printoptions(FFRCompileOptions);
64990792Sgshapiro	}
65090792Sgshapiro
651285229Sgshapiro#if STARTTLS
652285229Sgshapiro	if (tTd(0, 14))
653285229Sgshapiro	{
654285229Sgshapiro		/* exit(EX_CONFIG) if different? */
655285229Sgshapiro		sm_dprintf("       OpenSSL: compiled 0x%08x\n",
656285229Sgshapiro			   (uint) OPENSSL_VERSION_NUMBER);
657285229Sgshapiro		sm_dprintf("       OpenSSL: linked   0x%08x\n",
658363466Sgshapiro			   (uint) TLS_version_num());
659285229Sgshapiro	}
660285229Sgshapiro#endif /* STARTTLS */
661285229Sgshapiro
66238032Speter	/* clear sendmail's environment */
66338032Speter	ExternalEnviron = environ;
66438032Speter	emptyenviron[0] = NULL;
66538032Speter	environ = emptyenviron;
66638032Speter
66738032Speter	/*
66842575Speter	**  restore any original TZ setting until TimeZoneSpec has been
66942575Speter	**  determined - or early log messages may get bogus time stamps
67038032Speter	*/
67190792Sgshapiro
67238032Speter	if ((p = getextenv("TZ")) != NULL)
67338032Speter	{
67438032Speter		char *tz;
67538032Speter		int tzlen;
67638032Speter
67790792Sgshapiro		/* XXX check for reasonable length? */
67838032Speter		tzlen = strlen(p) + 4;
67938032Speter		tz = xalloc(tzlen);
68090792Sgshapiro		(void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
68190792Sgshapiro
68290792Sgshapiro		/* XXX check return code? */
68364562Sgshapiro		(void) putenv(tz);
68438032Speter	}
68538032Speter
68638032Speter	/* prime the child environment */
687157001Sgshapiro	sm_setuserenv("AGENT", "sendmail");
68838032Speter
68990792Sgshapiro	(void) sm_signal(SIGPIPE, SIG_IGN);
69038032Speter	OldUmask = umask(022);
69138032Speter	FullName = getextenv("NAME");
69295154Sgshapiro	if (FullName != NULL)
69395154Sgshapiro		FullName = newstr(FullName);
69438032Speter
69538032Speter	/*
69638032Speter	**  Initialize name server if it is going to be used.
69738032Speter	*/
69838032Speter
69938032Speter#if NAMED_BIND
70038032Speter	if (!bitset(RES_INIT, _res.options))
70164562Sgshapiro		(void) res_init();
70238032Speter	if (tTd(8, 8))
70338032Speter		_res.options |= RES_DEBUG;
70438032Speter	else
70538032Speter		_res.options &= ~RES_DEBUG;
70638032Speter# ifdef RES_NOALIASES
70738032Speter	_res.options |= RES_NOALIASES;
708363466Sgshapiro# endif
70964562Sgshapiro	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
71064562Sgshapiro	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
71164562Sgshapiro	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
71264562Sgshapiro	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
71364562Sgshapiro	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
71464562Sgshapiro	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
71564562Sgshapiro#endif /* NAMED_BIND */
71638032Speter
71738032Speter	errno = 0;
71838032Speter	from = NULL;
71938032Speter
72038032Speter	/* initialize some macros, etc. */
72190792Sgshapiro	init_vendor_macros(&BlankEnvelope);
72238032Speter
72338032Speter	/* version */
72490792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
72538032Speter
72638032Speter	/* hostname */
727168515Sgshapiro	hp = myhostname(jbuf, sizeof(jbuf));
72838032Speter	if (jbuf[0] != '\0')
72938032Speter	{
73090792Sgshapiro		struct utsname utsname;
73138032Speter
73238032Speter		if (tTd(0, 4))
73390792Sgshapiro			sm_dprintf("Canonical name: %s\n", jbuf);
73490792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
73590792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
73638032Speter		setclass('w', jbuf);
73738032Speter
73838032Speter		p = strchr(jbuf, '.');
739132943Sgshapiro		if (p != NULL && p[1] != '\0')
740132943Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm', &p[1]);
74138032Speter
74238032Speter		if (uname(&utsname) >= 0)
74338032Speter			p = utsname.nodename;
74438032Speter		else
74538032Speter		{
74638032Speter			if (tTd(0, 22))
74790792Sgshapiro				sm_dprintf("uname failed (%s)\n",
74890792Sgshapiro					   sm_errstring(errno));
74938032Speter			makelower(jbuf);
75038032Speter			p = jbuf;
75138032Speter		}
75238032Speter		if (tTd(0, 4))
75390792Sgshapiro			sm_dprintf(" UUCP nodename: %s\n", p);
75490792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
75538032Speter		setclass('k', p);
75638032Speter		setclass('w', p);
75738032Speter	}
75838032Speter	if (hp != NULL)
75938032Speter	{
76038032Speter		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
76138032Speter		{
76238032Speter			if (tTd(0, 4))
76390792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", *av);
76438032Speter			setclass('w', *av);
76538032Speter		}
76664562Sgshapiro#if NETINET || NETINET6
76790792Sgshapiro		for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
76838032Speter		{
76964562Sgshapiro# if NETINET6
77064562Sgshapiro			char *addr;
77164562Sgshapiro			char buf6[INET6_ADDRSTRLEN];
77264562Sgshapiro			struct in6_addr ia6;
77364562Sgshapiro# endif /* NETINET6 */
77464562Sgshapiro# if NETINET
77564562Sgshapiro			struct in_addr ia;
776363466Sgshapiro# endif
77764562Sgshapiro			char ipbuf[103];
77864562Sgshapiro
77964562Sgshapiro			ipbuf[0] = '\0';
78064562Sgshapiro			switch (hp->h_addrtype)
78138032Speter			{
78264562Sgshapiro# if NETINET
78364562Sgshapiro			  case AF_INET:
78464562Sgshapiro				if (hp->h_length != INADDRSZ)
78564562Sgshapiro					break;
78638032Speter
78764562Sgshapiro				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
788168515Sgshapiro				(void) sm_snprintf(ipbuf, sizeof(ipbuf),
78990792Sgshapiro						   "[%.100s]", inet_ntoa(ia));
79064562Sgshapiro				break;
79164562Sgshapiro# endif /* NETINET */
79264562Sgshapiro
79364562Sgshapiro# if NETINET6
79464562Sgshapiro			  case AF_INET6:
79564562Sgshapiro				if (hp->h_length != IN6ADDRSZ)
79664562Sgshapiro					break;
79764562Sgshapiro
79864562Sgshapiro				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
799168515Sgshapiro				addr = anynet_ntop(&ia6, buf6, sizeof(buf6));
80064562Sgshapiro				if (addr != NULL)
801168515Sgshapiro					(void) sm_snprintf(ipbuf, sizeof(ipbuf),
80290792Sgshapiro							   "[%.100s]", addr);
80364562Sgshapiro				break;
80464562Sgshapiro# endif /* NETINET6 */
80538032Speter			}
80664562Sgshapiro			if (ipbuf[0] == '\0')
80764562Sgshapiro				break;
80864562Sgshapiro
80964562Sgshapiro			if (tTd(0, 4))
81090792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", ipbuf);
81164562Sgshapiro			setclass('w', ipbuf);
81238032Speter		}
81364562Sgshapiro#endif /* NETINET || NETINET6 */
81490792Sgshapiro#if NETINET6
81571345Sgshapiro		freehostent(hp);
81671345Sgshapiro		hp = NULL;
817363466Sgshapiro#endif
81838032Speter	}
81938032Speter
82038032Speter	/* current time */
82190792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
82290792Sgshapiro
82364562Sgshapiro	/* current load average */
82490792Sgshapiro	sm_getla();
82538032Speter
82638032Speter	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
82738032Speter	QueueLimitSender = (QUEUE_CHAR *) NULL;
82838032Speter	QueueLimitId = (QUEUE_CHAR *) NULL;
82990792Sgshapiro	QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
83038032Speter
83138032Speter	/*
83242575Speter	**  Crack argv.
83338032Speter	*/
83438032Speter
83538032Speter	optind = 1;
83638032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
83738032Speter	{
83838032Speter		switch (j)
83938032Speter		{
84038032Speter		  case 'b':	/* operations mode */
84190792Sgshapiro			/* already done */
84290792Sgshapiro			break;
84338032Speter
84490792Sgshapiro		  case 'A':	/* use Alternate sendmail/submit.cf */
84590792Sgshapiro			cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
84690792Sgshapiro						  : SM_GET_SENDMAIL_CF;
84738032Speter			break;
84838032Speter
84938032Speter		  case 'B':	/* body type */
85090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
85190792Sgshapiro			BlankEnvelope.e_bodytype = newstr(optarg);
85238032Speter			break;
85338032Speter
85438032Speter		  case 'C':	/* select configuration file (already done) */
85538032Speter			if (RealUid != 0)
85690792Sgshapiro				warn_C_flag = true;
85790792Sgshapiro			conffile = newstr(optarg);
85890792Sgshapiro			dp = drop_privileges(true);
85964562Sgshapiro			setstat(dp);
86090792Sgshapiro			safecf = false;
86138032Speter			break;
86238032Speter
863132943Sgshapiro		  case 'D':
86490792Sgshapiro		  case 'd':	/* debugging */
86590792Sgshapiro			/* already done */
86638032Speter			break;
86738032Speter
86838032Speter		  case 'f':	/* from address */
86938032Speter		  case 'r':	/* obsolete -f flag */
87090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
87138032Speter			if (from != NULL)
87238032Speter			{
87338032Speter				usrerr("More than one \"from\" person");
87438032Speter				ExitStat = EX_USAGE;
87538032Speter				break;
87638032Speter			}
877110560Sgshapiro			if (optarg[0] == '\0')
878110560Sgshapiro				from = newstr("<>");
879110560Sgshapiro			else
880110560Sgshapiro				from = newstr(denlstring(optarg, true, true));
88138032Speter			if (strcmp(RealUserName, from) != 0)
88238032Speter				warn_f_flag = j;
88338032Speter			break;
88438032Speter
88538032Speter		  case 'F':	/* set full name */
88690792Sgshapiro			CHECK_AGAINST_OPMODE(j);
88738032Speter			FullName = newstr(optarg);
88838032Speter			break;
88938032Speter
89064562Sgshapiro		  case 'G':	/* relay (gateway) submission */
89164562Sgshapiro			/* already set */
89290792Sgshapiro			CHECK_AGAINST_OPMODE(j);
89364562Sgshapiro			break;
89464562Sgshapiro
89538032Speter		  case 'h':	/* hop count */
89690792Sgshapiro			CHECK_AGAINST_OPMODE(j);
89790792Sgshapiro			BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
89890792Sgshapiro								  10);
899168515Sgshapiro			(void) sm_snprintf(buf, sizeof(buf), "%d",
90090792Sgshapiro					   BlankEnvelope.e_hopcount);
90190792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
90290792Sgshapiro
90338032Speter			if (*ep)
90438032Speter			{
90538032Speter				usrerr("Bad hop count (%s)", optarg);
90638032Speter				ExitStat = EX_USAGE;
90738032Speter			}
90838032Speter			break;
90964562Sgshapiro
91064562Sgshapiro		  case 'L':	/* program label */
91164562Sgshapiro			/* already set */
91264562Sgshapiro			break;
91364562Sgshapiro
91438032Speter		  case 'n':	/* don't alias */
91590792Sgshapiro			CHECK_AGAINST_OPMODE(j);
91690792Sgshapiro			NoAlias = true;
91738032Speter			break;
91838032Speter
91938032Speter		  case 'N':	/* delivery status notifications */
92090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
92138032Speter			DefaultNotify |= QHASNOTIFY;
92290792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
92390792Sgshapiro				macid("{dsn_notify}"), optarg);
92490792Sgshapiro			if (sm_strcasecmp(optarg, "never") == 0)
92538032Speter				break;
92638032Speter			for (p = optarg; p != NULL; optarg = p)
92738032Speter			{
92838032Speter				p = strchr(p, ',');
92938032Speter				if (p != NULL)
93038032Speter					*p++ = '\0';
93190792Sgshapiro				if (sm_strcasecmp(optarg, "success") == 0)
93238032Speter					DefaultNotify |= QPINGONSUCCESS;
93390792Sgshapiro				else if (sm_strcasecmp(optarg, "failure") == 0)
93438032Speter					DefaultNotify |= QPINGONFAILURE;
93590792Sgshapiro				else if (sm_strcasecmp(optarg, "delay") == 0)
93638032Speter					DefaultNotify |= QPINGONDELAY;
93738032Speter				else
93838032Speter				{
93938032Speter					usrerr("Invalid -N argument");
94038032Speter					ExitStat = EX_USAGE;
94138032Speter				}
94238032Speter			}
94338032Speter			break;
94438032Speter
94538032Speter		  case 'o':	/* set option */
94690792Sgshapiro			setoption(*optarg, optarg + 1, false, true,
94790792Sgshapiro				  &BlankEnvelope);
94838032Speter			break;
94938032Speter
95038032Speter		  case 'O':	/* set option (long form) */
95190792Sgshapiro			setoption(' ', optarg, false, true, &BlankEnvelope);
95238032Speter			break;
95338032Speter
95438032Speter		  case 'p':	/* set protocol */
95590792Sgshapiro			CHECK_AGAINST_OPMODE(j);
95638032Speter			p = strchr(optarg, ':');
95738032Speter			if (p != NULL)
95838032Speter			{
95938032Speter				*p++ = '\0';
96038032Speter				if (*p != '\0')
96138032Speter				{
962120256Sgshapiro					i = strlen(p) + 1;
963120256Sgshapiro					ep = sm_malloc_x(i);
964120256Sgshapiro					cleanstrcpy(ep, p, i);
96590792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
96690792Sgshapiro						  A_HEAP, 's', ep);
96738032Speter				}
96838032Speter			}
96938032Speter			if (*optarg != '\0')
97038032Speter			{
971120256Sgshapiro				i = strlen(optarg) + 1;
972120256Sgshapiro				ep = sm_malloc_x(i);
973120256Sgshapiro				cleanstrcpy(ep, optarg, i);
97490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_HEAP,
97590792Sgshapiro					  'r', ep);
97638032Speter			}
97738032Speter			break;
97838032Speter
97990792Sgshapiro		  case 'Q':	/* change quarantining on queued items */
98090792Sgshapiro			/* sanity check */
98190792Sgshapiro			if (OpMode != MD_DELIVER &&
98290792Sgshapiro			    OpMode != MD_QUEUERUN)
98390792Sgshapiro			{
98490792Sgshapiro				usrerr("Can not use -Q with -b%c", OpMode);
98590792Sgshapiro				ExitStat = EX_USAGE;
98690792Sgshapiro				break;
98790792Sgshapiro			}
98890792Sgshapiro
98990792Sgshapiro			if (OpMode == MD_DELIVER)
99090792Sgshapiro				set_op_mode(MD_QUEUERUN);
99190792Sgshapiro
99290792Sgshapiro			FullName = NULL;
99390792Sgshapiro
99490792Sgshapiro			quarantining = newstr(optarg);
99590792Sgshapiro			break;
99690792Sgshapiro
99738032Speter		  case 'q':	/* run queue files at intervals */
99864562Sgshapiro			/* sanity check */
99964562Sgshapiro			if (OpMode != MD_DELIVER &&
100064562Sgshapiro			    OpMode != MD_DAEMON &&
100164562Sgshapiro			    OpMode != MD_FGDAEMON &&
100264562Sgshapiro			    OpMode != MD_PRINT &&
100390792Sgshapiro			    OpMode != MD_PRINTNQE &&
100464562Sgshapiro			    OpMode != MD_QUEUERUN)
100564562Sgshapiro			{
100664562Sgshapiro				usrerr("Can not use -q with -b%c", OpMode);
100764562Sgshapiro				ExitStat = EX_USAGE;
100864562Sgshapiro				break;
100964562Sgshapiro			}
101064562Sgshapiro
101164562Sgshapiro			/* don't override -bd, -bD or -bp */
101264562Sgshapiro			if (OpMode == MD_DELIVER)
101390792Sgshapiro				set_op_mode(MD_QUEUERUN);
101464562Sgshapiro
101538032Speter			FullName = NULL;
101690792Sgshapiro			negate = optarg[0] == '!';
101790792Sgshapiro			if (negate)
101890792Sgshapiro			{
101990792Sgshapiro				/* negate meaning of pattern match */
102090792Sgshapiro				optarg++; /* skip '!' for next switch */
102190792Sgshapiro			}
102264562Sgshapiro
102338032Speter			switch (optarg[0])
102438032Speter			{
102590792Sgshapiro			  case 'G': /* Limit by queue group name */
102690792Sgshapiro				if (negate)
102790792Sgshapiro				{
102890792Sgshapiro					usrerr("Can not use -q!G");
102990792Sgshapiro					ExitStat = EX_USAGE;
103090792Sgshapiro					break;
103190792Sgshapiro				}
103290792Sgshapiro				if (queuegroup != NULL)
103390792Sgshapiro				{
103490792Sgshapiro					usrerr("Can not use multiple -qG options");
103590792Sgshapiro					ExitStat = EX_USAGE;
103690792Sgshapiro					break;
103790792Sgshapiro				}
103890792Sgshapiro				queuegroup = newstr(&optarg[1]);
103990792Sgshapiro				break;
104090792Sgshapiro
104190792Sgshapiro			  case 'I': /* Limit by ID */
1042168515Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof(*new));
104338032Speter				new->queue_match = newstr(&optarg[1]);
104490792Sgshapiro				new->queue_negate = negate;
104538032Speter				new->queue_next = QueueLimitId;
104638032Speter				QueueLimitId = new;
104738032Speter				break;
104838032Speter
104990792Sgshapiro			  case 'R': /* Limit by recipient */
1050168515Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof(*new));
105138032Speter				new->queue_match = newstr(&optarg[1]);
105290792Sgshapiro				new->queue_negate = negate;
105338032Speter				new->queue_next = QueueLimitRecipient;
105438032Speter				QueueLimitRecipient = new;
105538032Speter				break;
105638032Speter
105790792Sgshapiro			  case 'S': /* Limit by sender */
1058168515Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof(*new));
105938032Speter				new->queue_match = newstr(&optarg[1]);
106090792Sgshapiro				new->queue_negate = negate;
106138032Speter				new->queue_next = QueueLimitSender;
106238032Speter				QueueLimitSender = new;
106338032Speter				break;
106438032Speter
106590792Sgshapiro			  case 'f': /* foreground queue run */
106690792Sgshapiro				foregroundqueue  = true;
106790792Sgshapiro				break;
106890792Sgshapiro
106990792Sgshapiro			  case 'Q': /* Limit by quarantine message */
107090792Sgshapiro				if (optarg[1] != '\0')
107190792Sgshapiro				{
1072168515Sgshapiro					new = (QUEUE_CHAR *) xalloc(sizeof(*new));
107390792Sgshapiro					new->queue_match = newstr(&optarg[1]);
107490792Sgshapiro					new->queue_negate = negate;
107590792Sgshapiro					new->queue_next = QueueLimitQuarantine;
107690792Sgshapiro					QueueLimitQuarantine = new;
107790792Sgshapiro				}
107890792Sgshapiro				QueueMode = QM_QUARANTINE;
107990792Sgshapiro				break;
108090792Sgshapiro
108190792Sgshapiro			  case 'L': /* act on lost items */
108290792Sgshapiro				QueueMode = QM_LOST;
108390792Sgshapiro				break;
108490792Sgshapiro
108590792Sgshapiro			  case 'p': /* Persistent queue */
108690792Sgshapiro				queuepersistent = true;
108790792Sgshapiro				if (QueueIntvl == 0)
108890792Sgshapiro					QueueIntvl = 1;
108990792Sgshapiro				if (optarg[1] == '\0')
109090792Sgshapiro					break;
109190792Sgshapiro				++optarg;
109290792Sgshapiro				/* FALLTHROUGH */
109390792Sgshapiro
109438032Speter			  default:
109564562Sgshapiro				i = Errors;
109638032Speter				QueueIntvl = convtime(optarg, 'm');
109798841Sgshapiro				if (QueueIntvl < 0)
109898841Sgshapiro				{
109998841Sgshapiro					usrerr("Invalid -q value");
110098841Sgshapiro					ExitStat = EX_USAGE;
110198841Sgshapiro				}
110264562Sgshapiro
110364562Sgshapiro				/* check for bad conversion */
110464562Sgshapiro				if (i < Errors)
110564562Sgshapiro					ExitStat = EX_USAGE;
110638032Speter				break;
110738032Speter			}
110838032Speter			break;
110938032Speter
111038032Speter		  case 'R':	/* DSN RET: what to return */
111190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
111290792Sgshapiro			if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
111338032Speter			{
111438032Speter				usrerr("Duplicate -R flag");
111538032Speter				ExitStat = EX_USAGE;
111638032Speter				break;
111738032Speter			}
111890792Sgshapiro			BlankEnvelope.e_flags |= EF_RET_PARAM;
111990792Sgshapiro			if (sm_strcasecmp(optarg, "hdrs") == 0)
112090792Sgshapiro				BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
112190792Sgshapiro			else if (sm_strcasecmp(optarg, "full") != 0)
112238032Speter			{
112338032Speter				usrerr("Invalid -R value");
112438032Speter				ExitStat = EX_USAGE;
112538032Speter			}
112690792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
112790792Sgshapiro				  macid("{dsn_ret}"), optarg);
112838032Speter			break;
112938032Speter
113038032Speter		  case 't':	/* read recipients from message */
113190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
113290792Sgshapiro			GrabTo = true;
113338032Speter			break;
113438032Speter
113538032Speter		  case 'V':	/* DSN ENVID: set "original" envelope id */
113690792Sgshapiro			CHECK_AGAINST_OPMODE(j);
113738032Speter			if (!xtextok(optarg))
113838032Speter			{
113938032Speter				usrerr("Invalid syntax in -V flag");
114038032Speter				ExitStat = EX_USAGE;
114138032Speter			}
114238032Speter			else
114364562Sgshapiro			{
114490792Sgshapiro				BlankEnvelope.e_envid = newstr(optarg);
114590792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
114690792Sgshapiro					  macid("{dsn_envid}"), optarg);
114764562Sgshapiro			}
114838032Speter			break;
114938032Speter
115038032Speter		  case 'X':	/* traffic log file */
115190792Sgshapiro			dp = drop_privileges(true);
115264562Sgshapiro			setstat(dp);
115364562Sgshapiro			if (stat(optarg, &traf_st) == 0 &&
115464562Sgshapiro			    S_ISFIFO(traf_st.st_mode))
115590792Sgshapiro				TrafficLogFile = sm_io_open(SmFtStdio,
115690792Sgshapiro							    SM_TIME_DEFAULT,
115790792Sgshapiro							    optarg,
115890792Sgshapiro							    SM_IO_WRONLY, NULL);
115964562Sgshapiro			else
116090792Sgshapiro				TrafficLogFile = sm_io_open(SmFtStdio,
116190792Sgshapiro							    SM_TIME_DEFAULT,
116290792Sgshapiro							    optarg,
116390792Sgshapiro							    SM_IO_APPEND, NULL);
116438032Speter			if (TrafficLogFile == NULL)
116538032Speter			{
116638032Speter				syserr("cannot open %s", optarg);
116738032Speter				ExitStat = EX_CANTCREAT;
116838032Speter				break;
116938032Speter			}
117090792Sgshapiro			(void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
117190792Sgshapiro					     NULL, SM_IO_LBF, 0);
117238032Speter			break;
117338032Speter
117438032Speter			/* compatibility flags */
117538032Speter		  case 'c':	/* connect to non-local mailers */
117638032Speter		  case 'i':	/* don't let dot stop me */
117738032Speter		  case 'm':	/* send to me too */
117838032Speter		  case 'T':	/* set timeout interval */
117938032Speter		  case 'v':	/* give blow-by-blow description */
118090792Sgshapiro			setoption(j, "T", false, true, &BlankEnvelope);
118138032Speter			break;
118238032Speter
118338032Speter		  case 'e':	/* error message disposition */
118438032Speter		  case 'M':	/* define macro */
118590792Sgshapiro			setoption(j, optarg, false, true, &BlankEnvelope);
118638032Speter			break;
118738032Speter
118838032Speter		  case 's':	/* save From lines in headers */
118990792Sgshapiro			setoption('f', "T", false, true, &BlankEnvelope);
119038032Speter			break;
119138032Speter
119264562Sgshapiro#ifdef DBM
119338032Speter		  case 'I':	/* initialize alias DBM file */
119490792Sgshapiro			set_op_mode(MD_INITALIAS);
119538032Speter			break;
119664562Sgshapiro#endif /* DBM */
119738032Speter
119864562Sgshapiro#if defined(__osf__) || defined(_AIX3)
119938032Speter		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
120038032Speter			break;
1201363466Sgshapiro#endif
120264562Sgshapiro#if defined(sony_news)
120338032Speter		  case 'E':
120438032Speter		  case 'J':	/* ignore flags for Japanese code conversion
120564562Sgshapiro				   implemented on Sony NEWS */
120638032Speter			break;
120764562Sgshapiro#endif /* defined(sony_news) */
120838032Speter
120938032Speter		  default:
121090792Sgshapiro			finis(true, true, EX_USAGE);
121190792Sgshapiro			/* NOTREACHED */
121238032Speter			break;
121338032Speter		}
121438032Speter	}
121538032Speter
121690792Sgshapiro	/* if we've had errors so far, exit now */
1217203004Sgshapiro	if ((ExitStat != EX_OK && OpMode != MD_TEST && OpMode != MD_CHECKCONFIG) ||
121890792Sgshapiro	    ExitStat == EX_OSERR)
121964562Sgshapiro	{
122090792Sgshapiro		finis(false, true, ExitStat);
122190792Sgshapiro		/* NOTREACHED */
122264562Sgshapiro	}
122390792Sgshapiro
122490792Sgshapiro	if (bitset(SUBMIT_MTA, SubmitMode))
122564562Sgshapiro	{
122698841Sgshapiro		/* If set daemon_flags on command line, don't reset it */
122798841Sgshapiro		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
122898841Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
122998841Sgshapiro				  macid("{daemon_flags}"), "CC f");
123064562Sgshapiro	}
123190792Sgshapiro	else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
123264562Sgshapiro	{
123390792Sgshapiro		SubmitMode = SUBMIT_MSA;
123498841Sgshapiro
123598841Sgshapiro		/* If set daemon_flags on command line, don't reset it */
123698841Sgshapiro		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
123798841Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
123898841Sgshapiro				  macid("{daemon_flags}"), "c u");
123964562Sgshapiro	}
124064562Sgshapiro
124138032Speter	/*
124238032Speter	**  Do basic initialization.
124338032Speter	**	Read system control file.
124438032Speter	**	Extract special fields for local use.
124538032Speter	*/
124638032Speter
124738032Speter#if XDEBUG
124838032Speter	checkfd012("before readcf");
1249363466Sgshapiro#endif
125090792Sgshapiro	vendor_pre_defaults(&BlankEnvelope);
125164562Sgshapiro
125290792Sgshapiro	readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
125390792Sgshapiro			 safecf, &BlankEnvelope);
125490792Sgshapiro#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
125590792Sgshapiro	ConfigFileRead = true;
1256363466Sgshapiro#endif
125790792Sgshapiro	vendor_post_defaults(&BlankEnvelope);
125838032Speter
125990792Sgshapiro	/* now we can complain about missing fds */
126090792Sgshapiro	if (MissingFds != 0 && LogLevel > 8)
126190792Sgshapiro	{
126290792Sgshapiro		char mbuf[MAXLINE];
126390792Sgshapiro
126490792Sgshapiro		mbuf[0] = '\0';
126590792Sgshapiro		if (bitset(1 << STDIN_FILENO, MissingFds))
1266168515Sgshapiro			(void) sm_strlcat(mbuf, ", stdin", sizeof(mbuf));
126790792Sgshapiro		if (bitset(1 << STDOUT_FILENO, MissingFds))
1268168515Sgshapiro			(void) sm_strlcat(mbuf, ", stdout", sizeof(mbuf));
126990792Sgshapiro		if (bitset(1 << STDERR_FILENO, MissingFds))
1270168515Sgshapiro			(void) sm_strlcat(mbuf, ", stderr", sizeof(mbuf));
127190792Sgshapiro
127290792Sgshapiro		/* Notice: fill_errno is from high above: fill_fd() */
127390792Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
127490792Sgshapiro			  "File descriptors missing on startup: %s; %s",
127590792Sgshapiro			  &mbuf[2], sm_errstring(fill_errno));
127690792Sgshapiro	}
127790792Sgshapiro
127877349Sgshapiro	/* Remove the ability for a normal user to send signals */
127990792Sgshapiro	if (RealUid != 0 && RealUid != geteuid())
128077349Sgshapiro	{
128177349Sgshapiro		uid_t new_uid = geteuid();
128277349Sgshapiro
128377349Sgshapiro#if HASSETREUID
128477349Sgshapiro		/*
128577349Sgshapiro		**  Since we can differentiate between uid and euid,
128677349Sgshapiro		**  make the uid a different user so the real user
128777349Sgshapiro		**  can't send signals.  However, it doesn't need to be
128877349Sgshapiro		**  root (euid has root).
128977349Sgshapiro		*/
129077349Sgshapiro
129177349Sgshapiro		if (new_uid == 0)
129277349Sgshapiro			new_uid = DefUid;
129377349Sgshapiro		if (tTd(47, 5))
129490792Sgshapiro			sm_dprintf("Changing real uid to %d\n", (int) new_uid);
129577349Sgshapiro		if (setreuid(new_uid, geteuid()) < 0)
129677349Sgshapiro		{
129777349Sgshapiro			syserr("main: setreuid(%d, %d) failed",
129877349Sgshapiro			       (int) new_uid, (int) geteuid());
129990792Sgshapiro			finis(false, true, EX_OSERR);
130077349Sgshapiro			/* NOTREACHED */
130177349Sgshapiro		}
130277349Sgshapiro		if (tTd(47, 10))
130390792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
130490792Sgshapiro				   (int) geteuid(), (int) getuid());
130577349Sgshapiro#else /* HASSETREUID */
130677349Sgshapiro		/*
130777349Sgshapiro		**  Have to change both effective and real so need to
130877349Sgshapiro		**  change them both to effective to keep privs.
130977349Sgshapiro		*/
131077349Sgshapiro
131177349Sgshapiro		if (tTd(47, 5))
131290792Sgshapiro			sm_dprintf("Changing uid to %d\n", (int) new_uid);
131377349Sgshapiro		if (setuid(new_uid) < 0)
131477349Sgshapiro		{
131577349Sgshapiro			syserr("main: setuid(%d) failed", (int) new_uid);
131690792Sgshapiro			finis(false, true, EX_OSERR);
131777349Sgshapiro			/* NOTREACHED */
131877349Sgshapiro		}
131977349Sgshapiro		if (tTd(47, 10))
132090792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
132190792Sgshapiro				   (int) geteuid(), (int) getuid());
132277349Sgshapiro#endif /* HASSETREUID */
132377349Sgshapiro	}
132477349Sgshapiro
132590792Sgshapiro#if NAMED_BIND
1326132943Sgshapiro	if (FallbackMX != NULL)
1327132943Sgshapiro		(void) getfallbackmxrr(FallbackMX);
1328363466Sgshapiro#endif
132990792Sgshapiro
1330223067Sgshapiro	if (SuperSafe == SAFE_INTERACTIVE && !SM_IS_INTERACTIVE(CurEnv->e_sendmode))
133190792Sgshapiro	{
133290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
133390792Sgshapiro				     "WARNING: SuperSafe=interactive should only be used with\n         DeliveryMode=interactive\n");
133490792Sgshapiro	}
133590792Sgshapiro
133690792Sgshapiro	if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
133790792Sgshapiro	{
133890792Sgshapiro		usrerr("Mail submission program cannot be used as daemon");
133990792Sgshapiro		finis(false, true, EX_USAGE);
134090792Sgshapiro	}
134190792Sgshapiro
134290792Sgshapiro	if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
134390792Sgshapiro	    OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
134490792Sgshapiro	    OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
134590792Sgshapiro		makeworkgroups();
134690792Sgshapiro
134777349Sgshapiro	/* set up the basic signal handlers */
134890792Sgshapiro	if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
134990792Sgshapiro		(void) sm_signal(SIGINT, intsig);
135090792Sgshapiro	(void) sm_signal(SIGTERM, intsig);
135177349Sgshapiro
135238032Speter	/* Enforce use of local time (null string overrides this) */
135338032Speter	if (TimeZoneSpec == NULL)
135438032Speter		unsetenv("TZ");
135538032Speter	else if (TimeZoneSpec[0] != '\0')
1356157001Sgshapiro		sm_setuserenv("TZ", TimeZoneSpec);
135738032Speter	else
1358157001Sgshapiro		sm_setuserenv("TZ", NULL);
135938032Speter	tzset();
136038032Speter
136190792Sgshapiro	/* initialize mailbox database */
136290792Sgshapiro	i = sm_mbdb_initialize(Mbdb);
136390792Sgshapiro	if (i != EX_OK)
136490792Sgshapiro	{
136590792Sgshapiro		usrerr("Can't initialize mailbox database \"%s\": %s",
136690792Sgshapiro		       Mbdb, sm_strexit(i));
136790792Sgshapiro		ExitStat = i;
136890792Sgshapiro	}
136990792Sgshapiro
137038032Speter	/* avoid denial-of-service attacks */
137138032Speter	resetlimits();
137238032Speter
137390792Sgshapiro	if (OpMode == MD_TEST)
137438032Speter	{
137590792Sgshapiro		/* can't be done after readcf if RunAs* is used */
137690792Sgshapiro		dp = drop_privileges(true);
137790792Sgshapiro		if (dp != EX_OK)
137890792Sgshapiro		{
137990792Sgshapiro			finis(false, true, dp);
138090792Sgshapiro			/* NOTREACHED */
138190792Sgshapiro		}
138290792Sgshapiro	}
138390792Sgshapiro	else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
138490792Sgshapiro	{
138538032Speter		/* drop privileges -- daemon mode done after socket/bind */
138690792Sgshapiro		dp = drop_privileges(false);
138764562Sgshapiro		setstat(dp);
138890792Sgshapiro		if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
138990792Sgshapiro		{
139090792Sgshapiro			usrerr("Mail submission program must have RunAsUser set to non root user");
139190792Sgshapiro			finis(false, true, EX_CONFIG);
139290792Sgshapiro			/* NOTREACHED */
139390792Sgshapiro		}
139438032Speter	}
139538032Speter
139664562Sgshapiro#if NAMED_BIND
139764562Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
139864562Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
1399363466Sgshapiro#endif
140064562Sgshapiro
140138032Speter	/*
140238032Speter	**  Find our real host name for future logging.
140338032Speter	*/
140438032Speter
140564562Sgshapiro	authinfo = getauthinfo(STDIN_FILENO, &forged);
140690792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
140738032Speter
140838032Speter	/* suppress error printing if errors mailed back or whatever */
140990792Sgshapiro	if (BlankEnvelope.e_errormode != EM_PRINT)
141090792Sgshapiro		HoldErrs = true;
141138032Speter
141238032Speter	/* set up the $=m class now, after .cf has a chance to redefine $m */
1413168515Sgshapiro	expand("\201m", jbuf, sizeof(jbuf), &BlankEnvelope);
141473188Sgshapiro	if (jbuf[0] != '\0')
141573188Sgshapiro		setclass('m', jbuf);
141638032Speter
141738032Speter	/* probe interfaces and locate any additional names */
141890792Sgshapiro	if (DontProbeInterfaces != DPI_PROBENONE)
141938032Speter		load_if_names();
142038032Speter
142190792Sgshapiro	if (tTd(0, 10))
142290792Sgshapiro	{
1423110560Sgshapiro		char pidpath[MAXPATHLEN];
1424110560Sgshapiro
142590792Sgshapiro		/* Now we know which .cf file we use */
142690792Sgshapiro		sm_dprintf("     Conf file:\t%s (selected)\n",
142790792Sgshapiro			   getcfname(OpMode, SubmitMode, cftype, conffile));
1428168515Sgshapiro		expand(PidFile, pidpath, sizeof(pidpath), &BlankEnvelope);
1429110560Sgshapiro		sm_dprintf("      Pid file:\t%s (selected)\n", pidpath);
143090792Sgshapiro	}
143190792Sgshapiro
143238032Speter	if (tTd(0, 1))
143338032Speter	{
143490792Sgshapiro		sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
143590792Sgshapiro		sm_dprintf("\n      (short domain name) $w = ");
1436132943Sgshapiro		xputs(sm_debug_file(), macvalue('w', &BlankEnvelope));
143790792Sgshapiro		sm_dprintf("\n  (canonical domain name) $j = ");
1438132943Sgshapiro		xputs(sm_debug_file(), macvalue('j', &BlankEnvelope));
143990792Sgshapiro		sm_dprintf("\n         (subdomain name) $m = ");
1440132943Sgshapiro		xputs(sm_debug_file(), macvalue('m', &BlankEnvelope));
144190792Sgshapiro		sm_dprintf("\n              (node name) $k = ");
1442132943Sgshapiro		xputs(sm_debug_file(), macvalue('k', &BlankEnvelope));
144390792Sgshapiro		sm_dprintf("\n========================================================\n\n");
144438032Speter	}
144538032Speter
144638032Speter	/*
144738032Speter	**  Do more command line checking -- these are things that
144838032Speter	**  have to modify the results of reading the config file.
144938032Speter	*/
145038032Speter
145138032Speter	/* process authorization warnings from command line */
145238032Speter	if (warn_C_flag)
145390792Sgshapiro		auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
145490792Sgshapiro			     RealUserName, conffile);
145564562Sgshapiro	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
145690792Sgshapiro		auth_warning(&BlankEnvelope, "Processed from queue %s",
145790792Sgshapiro			     QueueDir);
145890792Sgshapiro	if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
145990792Sgshapiro	    RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
146090792Sgshapiro		sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
146190792Sgshapiro			  (int) RealUid);
146238032Speter
146338032Speter	/* check body type for legality */
146490792Sgshapiro	i = check_bodytype(BlankEnvelope.e_bodytype);
146590792Sgshapiro	if (i == BODYTYPE_ILLEGAL)
146638032Speter	{
146790792Sgshapiro		usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
146890792Sgshapiro		BlankEnvelope.e_bodytype = NULL;
146938032Speter	}
147090792Sgshapiro	else if (i != BODYTYPE_NONE)
147190792Sgshapiro		SevenBitInput = (i == BODYTYPE_7BIT);
147238032Speter
147338032Speter	/* tweak default DSN notifications */
147438032Speter	if (DefaultNotify == 0)
147538032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
147638032Speter
147738032Speter	/* check for sane configuration level */
147838032Speter	if (ConfigLevel > MAXCONFIGLEVEL)
147938032Speter	{
148038032Speter		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
148190792Sgshapiro		       ConfigLevel, Version, MAXCONFIGLEVEL);
148238032Speter	}
148338032Speter
148438032Speter	/* need MCI cache to have persistence */
148538032Speter	if (HostStatDir != NULL && MaxMciCache == 0)
148638032Speter	{
148738032Speter		HostStatDir = NULL;
148890792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
148990792Sgshapiro				     "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
149038032Speter	}
149138032Speter
149238032Speter	/* need HostStatusDir in order to have SingleThreadDelivery */
149338032Speter	if (SingleThreadDelivery && HostStatDir == NULL)
149438032Speter	{
149590792Sgshapiro		SingleThreadDelivery = false;
149690792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
149790792Sgshapiro				     "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
149838032Speter	}
149938032Speter
1500157001Sgshapiro#if _FFR_MEMSTAT
1501157001Sgshapiro	j = sm_memstat_open();
1502157001Sgshapiro	if (j < 0 && (RefuseLowMem > 0 || QueueLowMem > 0) && LogLevel > 4)
1503157001Sgshapiro	{
1504157001Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
1505157001Sgshapiro			  "cannot get memory statistics, settings ignored, error=%d"
1506157001Sgshapiro			  , j);
1507157001Sgshapiro	}
1508157001Sgshapiro#endif /* _FFR_MEMSTAT */
1509157001Sgshapiro
151038032Speter	/* check for permissions */
151190792Sgshapiro	if (RealUid != 0 &&
151242575Speter	    RealUid != TrustedUid)
151338032Speter	{
151490792Sgshapiro		char *action = NULL;
151590792Sgshapiro
151690792Sgshapiro		switch (OpMode)
151790792Sgshapiro		{
151890792Sgshapiro		  case MD_QUEUERUN:
151990792Sgshapiro			if (quarantining != NULL)
152090792Sgshapiro				action = "quarantine jobs";
152190792Sgshapiro			else
1522132943Sgshapiro			{
1523132943Sgshapiro				/* Normal users can do a single queue run */
1524132943Sgshapiro				if (QueueIntvl == 0)
1525132943Sgshapiro					break;
1526132943Sgshapiro			}
152790792Sgshapiro
152890792Sgshapiro			/* but not persistent queue runners */
152990792Sgshapiro			if (action == NULL)
153090792Sgshapiro				action = "start a queue runner daemon";
153190792Sgshapiro			/* FALLTHROUGH */
153290792Sgshapiro
153390792Sgshapiro		  case MD_PURGESTAT:
153490792Sgshapiro			if (action == NULL)
153590792Sgshapiro				action = "purge host status";
153690792Sgshapiro			/* FALLTHROUGH */
153790792Sgshapiro
153890792Sgshapiro		  case MD_DAEMON:
153990792Sgshapiro		  case MD_FGDAEMON:
154090792Sgshapiro			if (action == NULL)
154190792Sgshapiro				action = "run daemon";
154290792Sgshapiro
154390792Sgshapiro			if (tTd(65, 1))
154490792Sgshapiro				sm_dprintf("Deny user %d attempt to %s\n",
154590792Sgshapiro					   (int) RealUid, action);
154690792Sgshapiro
154790792Sgshapiro			if (LogLevel > 1)
154890792Sgshapiro				sm_syslog(LOG_ALERT, NOQID,
154990792Sgshapiro					  "user %d attempted to %s",
155090792Sgshapiro					  (int) RealUid, action);
155190792Sgshapiro			HoldErrs = false;
155290792Sgshapiro			usrerr("Permission denied (real uid not trusted)");
155390792Sgshapiro			finis(false, true, EX_USAGE);
155490792Sgshapiro			/* NOTREACHED */
155590792Sgshapiro			break;
155690792Sgshapiro
155790792Sgshapiro		  case MD_VERIFY:
155890792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
155990792Sgshapiro			{
156090792Sgshapiro				/*
156190792Sgshapiro				**  If -bv and RestrictExpand,
156290792Sgshapiro				**  drop privs to prevent normal
156390792Sgshapiro				**  users from reading private
156490792Sgshapiro				**  aliases/forwards/:include:s
156590792Sgshapiro				*/
156690792Sgshapiro
156790792Sgshapiro				if (tTd(65, 1))
156890792Sgshapiro					sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
156990792Sgshapiro						   (int) RealUid);
157090792Sgshapiro
157190792Sgshapiro				dp = drop_privileges(true);
157290792Sgshapiro
157390792Sgshapiro				/* Fake address safety */
157490792Sgshapiro				if (tTd(65, 1))
157590792Sgshapiro					sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
157690792Sgshapiro				setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
157790792Sgshapiro
157890792Sgshapiro				if (dp != EX_OK)
157990792Sgshapiro				{
158090792Sgshapiro					if (tTd(65, 1))
158190792Sgshapiro						sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
158290792Sgshapiro							   (int) RealUid);
158390792Sgshapiro					CurEnv->e_id = NULL;
158490792Sgshapiro					finis(true, true, dp);
158590792Sgshapiro					/* NOTREACHED */
158690792Sgshapiro				}
158790792Sgshapiro			}
158890792Sgshapiro			break;
158990792Sgshapiro
159090792Sgshapiro		  case MD_TEST:
1591203004Sgshapiro		  case MD_CHECKCONFIG:
159290792Sgshapiro		  case MD_PRINT:
159390792Sgshapiro		  case MD_PRINTNQE:
159490792Sgshapiro		  case MD_FREEZE:
159590792Sgshapiro		  case MD_HOSTSTAT:
159690792Sgshapiro			/* Nothing special to check */
159790792Sgshapiro			break;
159890792Sgshapiro
159990792Sgshapiro		  case MD_INITALIAS:
160090792Sgshapiro			if (!wordinclass(RealUserName, 't'))
160190792Sgshapiro			{
160290792Sgshapiro				if (tTd(65, 1))
160390792Sgshapiro					sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
160490792Sgshapiro						   (int) RealUid);
160590792Sgshapiro				if (LogLevel > 1)
160690792Sgshapiro					sm_syslog(LOG_ALERT, NOQID,
160790792Sgshapiro						  "user %d attempted to rebuild the alias map",
160890792Sgshapiro						  (int) RealUid);
160990792Sgshapiro				HoldErrs = false;
161090792Sgshapiro				usrerr("Permission denied (real uid not trusted)");
161190792Sgshapiro				finis(false, true, EX_USAGE);
161290792Sgshapiro				/* NOTREACHED */
161390792Sgshapiro			}
161490792Sgshapiro			if (UseMSP)
161590792Sgshapiro			{
161690792Sgshapiro				HoldErrs = false;
161790792Sgshapiro				usrerr("User %d cannot rebuild aliases in mail submission program",
161890792Sgshapiro				       (int) RealUid);
161990792Sgshapiro				finis(false, true, EX_USAGE);
162090792Sgshapiro				/* NOTREACHED */
162190792Sgshapiro			}
162290792Sgshapiro			/* FALLTHROUGH */
162390792Sgshapiro
162490792Sgshapiro		  default:
162590792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
162690792Sgshapiro			    Verbose != 0)
162790792Sgshapiro			{
162890792Sgshapiro				/*
162990792Sgshapiro				**  If -v and RestrictExpand, reset
163090792Sgshapiro				**  Verbose to prevent normal users
163190792Sgshapiro				**  from seeing the expansion of
163290792Sgshapiro				**  aliases/forwards/:include:s
163390792Sgshapiro				*/
163490792Sgshapiro
163590792Sgshapiro				if (tTd(65, 1))
163690792Sgshapiro					sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
163790792Sgshapiro						   (int) RealUid);
163890792Sgshapiro				Verbose = 0;
163990792Sgshapiro			}
164090792Sgshapiro			break;
164190792Sgshapiro		}
164238032Speter	}
164338032Speter
164438032Speter	if (MeToo)
164538032Speter		BlankEnvelope.e_flags |= EF_METOO;
164638032Speter
164738032Speter	switch (OpMode)
164838032Speter	{
164938032Speter	  case MD_TEST:
165038032Speter		/* don't have persistent host status in test mode */
165138032Speter		HostStatDir = NULL;
1652203004Sgshapiro		/* FALLTHROUGH */
1653203004Sgshapiro
1654203004Sgshapiro	  case MD_CHECKCONFIG:
165538032Speter		if (Verbose == 0)
165638032Speter			Verbose = 2;
165790792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
165890792Sgshapiro		HoldErrs = false;
165938032Speter		break;
166038032Speter
166138032Speter	  case MD_VERIFY:
166290792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
166390792Sgshapiro		HoldErrs = false;
166438032Speter		/* arrange to exit cleanly on hangup signal */
166590792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
166690792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
166790792Sgshapiro		if (geteuid() != 0)
166890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
166990792Sgshapiro					     "Notice: -bv may give misleading output for non-privileged user\n");
167038032Speter		break;
167138032Speter
167238032Speter	  case MD_FGDAEMON:
167390792Sgshapiro		run_in_foreground = true;
167490792Sgshapiro		set_op_mode(MD_DAEMON);
167564562Sgshapiro		/* FALLTHROUGH */
167638032Speter
167738032Speter	  case MD_DAEMON:
167890792Sgshapiro		vendor_daemon_setup(&BlankEnvelope);
167938032Speter
168038032Speter		/* remove things that don't make sense in daemon mode */
168138032Speter		FullName = NULL;
168290792Sgshapiro		GrabTo = false;
168338032Speter
168438032Speter		/* arrange to restart on hangup signal */
168538032Speter		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
168638032Speter			sm_syslog(LOG_WARNING, NOQID,
168764562Sgshapiro				  "daemon invoked without full pathname; kill -1 won't work");
168838032Speter		break;
168938032Speter
169038032Speter	  case MD_INITALIAS:
169138032Speter		Verbose = 2;
169290792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
169390792Sgshapiro		HoldErrs = false;
169464562Sgshapiro		/* FALLTHROUGH */
169538032Speter
169638032Speter	  default:
169738032Speter		/* arrange to exit cleanly on hangup signal */
169890792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
169990792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
170038032Speter		break;
170138032Speter	}
170238032Speter
170338032Speter	/* special considerations for FullName */
170438032Speter	if (FullName != NULL)
170538032Speter	{
170638032Speter		char *full = NULL;
170738032Speter
170838032Speter		/* full names can't have newlines */
170964562Sgshapiro		if (strchr(FullName, '\n') != NULL)
171038032Speter		{
171190792Sgshapiro			full = newstr(denlstring(FullName, true, true));
171273188Sgshapiro			FullName = full;
171338032Speter		}
171473188Sgshapiro
171538032Speter		/* check for characters that may have to be quoted */
171638032Speter		if (!rfc822_string(FullName))
171738032Speter		{
171838032Speter			/*
171938032Speter			**  Quote a full name with special characters
172038032Speter			**  as a comment so crackaddr() doesn't destroy
172138032Speter			**  the name portion of the address.
172238032Speter			*/
172373188Sgshapiro
172490792Sgshapiro			FullName = addquotes(FullName, NULL);
172538032Speter			if (full != NULL)
172690792Sgshapiro				sm_free(full);  /* XXX */
172738032Speter		}
172838032Speter	}
172938032Speter
173038032Speter	/* do heuristic mode adjustment */
173138032Speter	if (Verbose)
173238032Speter	{
173338032Speter		/* turn off noconnect option */
173490792Sgshapiro		setoption('c', "F", true, false, &BlankEnvelope);
173538032Speter
173638032Speter		/* turn on interactive delivery */
173790792Sgshapiro		setoption('d', "", true, false, &BlankEnvelope);
173838032Speter	}
173938032Speter
174042575Speter#ifdef VENDOR_CODE
174142575Speter	/* check for vendor mismatch */
174242575Speter	if (VendorCode != VENDOR_CODE)
174342575Speter	{
174442575Speter		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
174542575Speter			getvendor(VENDOR_CODE), getvendor(VendorCode));
174642575Speter	}
174764562Sgshapiro#endif /* VENDOR_CODE */
174864562Sgshapiro
174938032Speter	/* check for out of date configuration level */
175038032Speter	if (ConfigLevel < MAXCONFIGLEVEL)
175138032Speter	{
175238032Speter		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
175338032Speter			Version, MAXCONFIGLEVEL, ConfigLevel);
175438032Speter	}
175538032Speter
175638032Speter	if (ConfigLevel < 3)
175790792Sgshapiro		UseErrorsTo = true;
175838032Speter
175938032Speter	/* set options that were previous macros */
176038032Speter	if (SmtpGreeting == NULL)
176138032Speter	{
176290792Sgshapiro		if (ConfigLevel < 7 &&
176390792Sgshapiro		    (p = macvalue('e', &BlankEnvelope)) != NULL)
176438032Speter			SmtpGreeting = newstr(p);
176538032Speter		else
176638032Speter			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
176738032Speter	}
176838032Speter	if (UnixFromLine == NULL)
176938032Speter	{
177090792Sgshapiro		if (ConfigLevel < 7 &&
177190792Sgshapiro		    (p = macvalue('l', &BlankEnvelope)) != NULL)
177238032Speter			UnixFromLine = newstr(p);
177338032Speter		else
177438032Speter			UnixFromLine = "From \201g  \201d";
177538032Speter	}
177664562Sgshapiro	SmtpError[0] = '\0';
177738032Speter
177838032Speter	/* our name for SMTP codes */
1779168515Sgshapiro	expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
178073188Sgshapiro	if (jbuf[0] == '\0')
178190792Sgshapiro		PSTRSET(MyHostName, "localhost");
178273188Sgshapiro	else
178390792Sgshapiro		PSTRSET(MyHostName, jbuf);
178473188Sgshapiro	if (strchr(MyHostName, '.') == NULL)
1785132943Sgshapiro		message("WARNING: local host name (%s) is not qualified; see cf/README: WHO AM I?",
178673188Sgshapiro			MyHostName);
178738032Speter
178838032Speter	/* make certain that this name is part of the $=w class */
178938032Speter	setclass('w', MyHostName);
179038032Speter
179190792Sgshapiro	/* fill in the structure of the *default* queue */
179290792Sgshapiro	st = stab("mqueue", ST_QUEUE, ST_FIND);
179390792Sgshapiro	if (st == NULL)
179490792Sgshapiro		syserr("No default queue (mqueue) defined");
179590792Sgshapiro	else
179690792Sgshapiro		set_def_queueval(st->s_quegrp, true);
179790792Sgshapiro
179838032Speter	/* the indices of built-in mailers */
179938032Speter	st = stab("local", ST_MAILER, ST_FIND);
180038032Speter	if (st != NULL)
180138032Speter		LocalMailer = st->s_mailer;
180238032Speter	else if (OpMode != MD_TEST || !warn_C_flag)
180338032Speter		syserr("No local mailer defined");
180438032Speter
180538032Speter	st = stab("prog", ST_MAILER, ST_FIND);
180638032Speter	if (st == NULL)
180738032Speter		syserr("No prog mailer defined");
180838032Speter	else
180938032Speter	{
181038032Speter		ProgMailer = st->s_mailer;
181138032Speter		clrbitn(M_MUSER, ProgMailer->m_flags);
181238032Speter	}
181338032Speter
181438032Speter	st = stab("*file*", ST_MAILER, ST_FIND);
181538032Speter	if (st == NULL)
181638032Speter		syserr("No *file* mailer defined");
181738032Speter	else
181838032Speter	{
181938032Speter		FileMailer = st->s_mailer;
182038032Speter		clrbitn(M_MUSER, FileMailer->m_flags);
182138032Speter	}
182238032Speter
182338032Speter	st = stab("*include*", ST_MAILER, ST_FIND);
182438032Speter	if (st == NULL)
182538032Speter		syserr("No *include* mailer defined");
182638032Speter	else
182738032Speter		InclMailer = st->s_mailer;
182838032Speter
182938032Speter	if (ConfigLevel < 6)
183038032Speter	{
183138032Speter		/* heuristic tweaking of local mailer for back compat */
183238032Speter		if (LocalMailer != NULL)
183338032Speter		{
183438032Speter			setbitn(M_ALIASABLE, LocalMailer->m_flags);
183538032Speter			setbitn(M_HASPWENT, LocalMailer->m_flags);
183638032Speter			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
183738032Speter			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
183838032Speter			setbitn(M_CHECKPROG, LocalMailer->m_flags);
183938032Speter			setbitn(M_CHECKFILE, LocalMailer->m_flags);
184038032Speter			setbitn(M_CHECKUDB, LocalMailer->m_flags);
184138032Speter		}
184238032Speter		if (ProgMailer != NULL)
184338032Speter			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
184438032Speter		if (FileMailer != NULL)
184538032Speter			setbitn(M_RUNASRCPT, FileMailer->m_flags);
184638032Speter	}
184738032Speter	if (ConfigLevel < 7)
184838032Speter	{
184938032Speter		if (LocalMailer != NULL)
185038032Speter			setbitn(M_VRFY250, LocalMailer->m_flags);
185138032Speter		if (ProgMailer != NULL)
185238032Speter			setbitn(M_VRFY250, ProgMailer->m_flags);
185338032Speter		if (FileMailer != NULL)
185438032Speter			setbitn(M_VRFY250, FileMailer->m_flags);
185538032Speter	}
185638032Speter
185738032Speter	/* MIME Content-Types that cannot be transfer encoded */
185838032Speter	setclass('n', "multipart/signed");
185938032Speter
186038032Speter	/* MIME message/xxx subtypes that can be treated as messages */
186138032Speter	setclass('s', "rfc822");
1862363466Sgshapiro#if _FFR_EAI
1863363466Sgshapiro	setclass('s', "global");
1864363466Sgshapiro#endif
186538032Speter
186638032Speter	/* MIME Content-Transfer-Encodings that can be encoded */
186738032Speter	setclass('e', "7bit");
186838032Speter	setclass('e', "8bit");
186938032Speter	setclass('e', "binary");
187038032Speter
187138032Speter#ifdef USE_B_CLASS
187238032Speter	/* MIME Content-Types that should be treated as binary */
187338032Speter	setclass('b', "image");
187438032Speter	setclass('b', "audio");
187538032Speter	setclass('b', "video");
187638032Speter	setclass('b', "application/octet-stream");
187764562Sgshapiro#endif /* USE_B_CLASS */
187838032Speter
187942575Speter	/* MIME headers which have fields to check for overflow */
188090792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
188190792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
188242575Speter
188342575Speter	/* MIME headers to check for length overflow */
188490792Sgshapiro	setclass(macid("{checkMIMETextHeaders}"), "content-description");
188542575Speter
188642575Speter	/* MIME headers to check for overflow and rebalance */
188790792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-disposition");
188890792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-id");
188990792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
189090792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-type");
189190792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "mime-version");
189242575Speter
189390792Sgshapiro	/* Macros to save in the queue file -- don't remove any */
189490792Sgshapiro	setclass(macid("{persistentMacros}"), "r");
189590792Sgshapiro	setclass(macid("{persistentMacros}"), "s");
189690792Sgshapiro	setclass(macid("{persistentMacros}"), "_");
189790792Sgshapiro	setclass(macid("{persistentMacros}"), "{if_addr}");
189890792Sgshapiro	setclass(macid("{persistentMacros}"), "{daemon_flags}");
189964562Sgshapiro
190038032Speter	/* operate in queue directory */
190190792Sgshapiro	if (QueueDir == NULL || *QueueDir == '\0')
190238032Speter	{
190338032Speter		if (OpMode != MD_TEST)
190438032Speter		{
190538032Speter			syserr("QueueDirectory (Q) option must be set");
190638032Speter			ExitStat = EX_CONFIG;
190738032Speter		}
190838032Speter	}
190938032Speter	else
191038032Speter	{
191164562Sgshapiro		if (OpMode != MD_TEST)
191290792Sgshapiro			setup_queues(OpMode == MD_DAEMON);
191338032Speter	}
191438032Speter
191538032Speter	/* check host status directory for validity */
191690792Sgshapiro	if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
191738032Speter	{
191838032Speter		/* cannot use this value */
191990792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
192090792Sgshapiro				     "Warning: Cannot use HostStatusDirectory = %s: %s\n",
192190792Sgshapiro				     HostStatDir, sm_errstring(errno));
192238032Speter		HostStatDir = NULL;
192338032Speter	}
192438032Speter
192590792Sgshapiro	if (OpMode == MD_QUEUERUN &&
192690792Sgshapiro	    RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
192738032Speter	{
192838032Speter		struct stat stbuf;
192938032Speter
193038032Speter		/* check to see if we own the queue directory */
193138032Speter		if (stat(".", &stbuf) < 0)
193238032Speter			syserr("main: cannot stat %s", QueueDir);
193338032Speter		if (stbuf.st_uid != RealUid)
193438032Speter		{
193538032Speter			/* nope, really a botch */
193690792Sgshapiro			HoldErrs = false;
193738032Speter			usrerr("You do not have permission to process the queue");
193890792Sgshapiro			finis(false, true, EX_NOPERM);
193990792Sgshapiro			/* NOTREACHED */
194038032Speter		}
194138032Speter	}
194238032Speter
194390792Sgshapiro#if MILTER
194464562Sgshapiro	/* sanity checks on milter filters */
194564562Sgshapiro	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
194690792Sgshapiro	{
194790792Sgshapiro		milter_config(InputFilterList, InputFilters, MAXFILTERS);
194890792Sgshapiro		setup_daemon_milters();
194990792Sgshapiro	}
195090792Sgshapiro#endif /* MILTER */
195164562Sgshapiro
195290792Sgshapiro	/* Convert queuegroup string to qgrp number */
195390792Sgshapiro	if (queuegroup != NULL)
195490792Sgshapiro	{
195590792Sgshapiro		qgrp = name2qid(queuegroup);
195690792Sgshapiro		if (qgrp == NOQGRP)
195790792Sgshapiro		{
195890792Sgshapiro			HoldErrs = false;
195990792Sgshapiro			usrerr("Queue group %s unknown", queuegroup);
196090792Sgshapiro			finis(false, true, ExitStat);
196190792Sgshapiro			/* NOTREACHED */
196290792Sgshapiro		}
196390792Sgshapiro	}
196466494Sgshapiro
1965203004Sgshapiro	/* if checking config or have had errors so far, exit now */
1966203004Sgshapiro	if (OpMode == MD_CHECKCONFIG || (ExitStat != EX_OK && OpMode != MD_TEST))
196790792Sgshapiro	{
196890792Sgshapiro		finis(false, true, ExitStat);
196990792Sgshapiro		/* NOTREACHED */
197090792Sgshapiro	}
197138032Speter
197290792Sgshapiro#if SASL
197390792Sgshapiro	/* sendmail specific SASL initialization */
197490792Sgshapiro	sm_sasl_init();
1975363466Sgshapiro#endif
197690792Sgshapiro
197738032Speter#if XDEBUG
197838032Speter	checkfd012("before main() initmaps");
1979363466Sgshapiro#endif
198038032Speter
198138032Speter	/*
198238032Speter	**  Do operation-mode-dependent initialization.
198338032Speter	*/
198438032Speter
198538032Speter	switch (OpMode)
198638032Speter	{
198738032Speter	  case MD_PRINT:
198838032Speter		/* print the queue */
198990792Sgshapiro		HoldErrs = false;
1990203004Sgshapiro		(void) dropenvelope(&BlankEnvelope, true, false);
199190792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
199290792Sgshapiro		if (qgrp != NOQGRP)
199390792Sgshapiro		{
199490792Sgshapiro			/* Selecting a particular queue group to run */
199590792Sgshapiro			for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
199690792Sgshapiro			{
199790792Sgshapiro				if (StopRequest)
199890792Sgshapiro					stop_sendmail();
199990792Sgshapiro				(void) print_single_queue(qgrp, j);
200090792Sgshapiro			}
200190792Sgshapiro			finis(false, true, EX_OK);
200290792Sgshapiro			/* NOTREACHED */
200390792Sgshapiro		}
200438032Speter		printqueue();
200590792Sgshapiro		finis(false, true, EX_OK);
200690792Sgshapiro		/* NOTREACHED */
200742575Speter		break;
200838032Speter
200990792Sgshapiro	  case MD_PRINTNQE:
201090792Sgshapiro		/* print number of entries in queue */
2011203004Sgshapiro		(void) dropenvelope(&BlankEnvelope, true, false);
201290792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
201390792Sgshapiro		printnqe(smioout, NULL);
201490792Sgshapiro		finis(false, true, EX_OK);
201590792Sgshapiro		/* NOTREACHED */
201690792Sgshapiro		break;
201790792Sgshapiro
201890792Sgshapiro	  case MD_QUEUERUN:
201990792Sgshapiro		/* only handle quarantining here */
202090792Sgshapiro		if (quarantining == NULL)
202190792Sgshapiro			break;
202290792Sgshapiro
202390792Sgshapiro		if (QueueMode != QM_QUARANTINE &&
202490792Sgshapiro		    QueueMode != QM_NORMAL)
202590792Sgshapiro		{
202690792Sgshapiro			HoldErrs = false;
202790792Sgshapiro			usrerr("Can not use -Q with -q%c", QueueMode);
202890792Sgshapiro			ExitStat = EX_USAGE;
202990792Sgshapiro			finis(false, true, ExitStat);
203090792Sgshapiro			/* NOTREACHED */
203190792Sgshapiro		}
203290792Sgshapiro		quarantine_queue(quarantining, qgrp);
203390792Sgshapiro		finis(false, true, EX_OK);
203490792Sgshapiro		break;
203590792Sgshapiro
203638032Speter	  case MD_HOSTSTAT:
203790792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
203864562Sgshapiro		(void) mci_traverse_persistent(mci_print_persistent, NULL);
203990792Sgshapiro		finis(false, true, EX_OK);
204090792Sgshapiro		/* NOTREACHED */
204164562Sgshapiro		break;
204238032Speter
204338032Speter	  case MD_PURGESTAT:
204464562Sgshapiro		(void) mci_traverse_persistent(mci_purge_persistent, NULL);
204590792Sgshapiro		finis(false, true, EX_OK);
204690792Sgshapiro		/* NOTREACHED */
204764562Sgshapiro		break;
204838032Speter
204938032Speter	  case MD_INITALIAS:
205042575Speter		/* initialize maps */
205164562Sgshapiro		initmaps();
205290792Sgshapiro		finis(false, true, ExitStat);
205390792Sgshapiro		/* NOTREACHED */
205442575Speter		break;
205538032Speter
205638032Speter	  case MD_SMTP:
205738032Speter	  case MD_DAEMON:
205838032Speter		/* reset DSN parameters */
205938032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
206090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
206190792Sgshapiro			  macid("{dsn_notify}"), NULL);
206290792Sgshapiro		BlankEnvelope.e_envid = NULL;
206390792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
206490792Sgshapiro			  macid("{dsn_envid}"), NULL);
206590792Sgshapiro		BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
206690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
206790792Sgshapiro			  macid("{dsn_ret}"), NULL);
206838032Speter
206942575Speter		/* don't open maps for daemon -- done below in child */
207038032Speter		break;
207138032Speter	}
207238032Speter
207338032Speter	if (tTd(0, 15))
207438032Speter	{
207538032Speter		/* print configuration table (or at least part of it) */
207638032Speter		if (tTd(0, 90))
207738032Speter			printrules();
207838032Speter		for (i = 0; i < MAXMAILERS; i++)
207938032Speter		{
208038032Speter			if (Mailer[i] != NULL)
2081132943Sgshapiro				printmailer(sm_debug_file(), Mailer[i]);
208238032Speter		}
208338032Speter	}
208438032Speter
208538032Speter	/*
208638032Speter	**  Switch to the main envelope.
208738032Speter	*/
208838032Speter
208990792Sgshapiro	CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
209090792Sgshapiro			     sm_rpool_new_x(NULL));
209138032Speter	MainEnvelope.e_flags = BlankEnvelope.e_flags;
209238032Speter
209338032Speter	/*
209438032Speter	**  If test mode, read addresses from stdin and process.
209538032Speter	*/
209638032Speter
209738032Speter	if (OpMode == MD_TEST)
209838032Speter	{
209990792Sgshapiro		if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
210038032Speter			Verbose = 2;
210138032Speter
210238032Speter		if (Verbose)
210338032Speter		{
210490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
210590792Sgshapiro				     "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
210690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
210790792Sgshapiro				     "Enter <ruleset> <address>\n");
210838032Speter		}
210990792Sgshapiro		macdefine(&(MainEnvelope.e_macro), A_PERM,
211090792Sgshapiro			  macid("{addr_type}"), "e r");
211138032Speter		for (;;)
211238032Speter		{
211390792Sgshapiro			SM_TRY
211490792Sgshapiro			{
211590792Sgshapiro				(void) sm_signal(SIGINT, intindebug);
211690792Sgshapiro				(void) sm_releasesignal(SIGINT);
211790792Sgshapiro				if (Verbose == 2)
211890792Sgshapiro					(void) sm_io_fprintf(smioout,
211990792Sgshapiro							     SM_TIME_DEFAULT,
212090792Sgshapiro							     "> ");
212190792Sgshapiro				(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
212290792Sgshapiro				if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
2123249729Sgshapiro						sizeof(buf)) < 0)
212490792Sgshapiro					testmodeline("/quit", &MainEnvelope);
212590792Sgshapiro				p = strchr(buf, '\n');
212690792Sgshapiro				if (p != NULL)
212790792Sgshapiro					*p = '\0';
212890792Sgshapiro				if (Verbose < 2)
212990792Sgshapiro					(void) sm_io_fprintf(smioout,
213090792Sgshapiro							     SM_TIME_DEFAULT,
213190792Sgshapiro							     "> %s\n", buf);
213290792Sgshapiro				testmodeline(buf, &MainEnvelope);
213390792Sgshapiro			}
213490792Sgshapiro			SM_EXCEPT(exc, "[!F]*")
213590792Sgshapiro			{
213690792Sgshapiro				/*
213790792Sgshapiro				**  8.10 just prints \n on interrupt.
213890792Sgshapiro				**  I'm printing the exception here in case
213990792Sgshapiro				**  sendmail is extended to raise additional
214090792Sgshapiro				**  exceptions in this context.
214190792Sgshapiro				*/
214290792Sgshapiro
214390792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
214490792Sgshapiro						     "\n");
214590792Sgshapiro				sm_exc_print(exc, smioout);
214690792Sgshapiro			}
214790792Sgshapiro			SM_END_TRY
214838032Speter		}
214938032Speter	}
215038032Speter
215190792Sgshapiro#if STARTTLS
215290792Sgshapiro	tls_ok = true;
2153168515Sgshapiro	if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER ||
2154168515Sgshapiro	    OpMode == MD_ARPAFTP)
215590792Sgshapiro	{
215690792Sgshapiro		/* check whether STARTTLS is turned off for the client */
215790792Sgshapiro		if (chkclientmodifiers(D_NOTLS))
215890792Sgshapiro			tls_ok = false;
215990792Sgshapiro	}
216090792Sgshapiro	else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
216190792Sgshapiro		 OpMode == MD_SMTP)
216290792Sgshapiro	{
2163203004Sgshapiro		/* check whether STARTTLS is turned off */
2164203004Sgshapiro		if (chkdaemonmodifiers(D_NOTLS) && chkclientmodifiers(D_NOTLS))
216590792Sgshapiro			tls_ok = false;
216690792Sgshapiro	}
216790792Sgshapiro	else	/* other modes don't need STARTTLS */
216890792Sgshapiro		tls_ok = false;
216964562Sgshapiro
217090792Sgshapiro	if (tls_ok)
217190792Sgshapiro	{
217290792Sgshapiro		/* basic TLS initialization */
2173363466Sgshapiro		j = init_tls_library(FipsMode);
2174363466Sgshapiro		if (j < 0)
2175249729Sgshapiro		{
2176249729Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2177363466Sgshapiro				     "ERROR: TLS failed to initialize\n");
2178249729Sgshapiro			exit(EX_USAGE);
2179249729Sgshapiro		}
2180363466Sgshapiro		if (j > 0)
2181363466Sgshapiro			tls_ok = false;
218290792Sgshapiro	}
218390792Sgshapiro
218490792Sgshapiro	if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
218590792Sgshapiro	{
218690792Sgshapiro		/* disable TLS for client */
218790792Sgshapiro		setclttls(false);
218890792Sgshapiro	}
218990792Sgshapiro#endif /* STARTTLS */
219090792Sgshapiro
219164562Sgshapiro	/*
219238032Speter	**  If collecting stuff from the queue, go start doing that.
219338032Speter	*/
219438032Speter
219564562Sgshapiro	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
219638032Speter	{
219790792Sgshapiro		pid_t pid = -1;
219890792Sgshapiro
219990792Sgshapiro#if STARTTLS
220090792Sgshapiro		/* init TLS for client, ignore result for now */
220190792Sgshapiro		(void) initclttls(tls_ok);
2202363466Sgshapiro#endif
220390792Sgshapiro
220490792Sgshapiro		/*
220590792Sgshapiro		**  The parent process of the caller of runqueue() needs
220690792Sgshapiro		**  to stay around for a possible SIGTERM. The SIGTERM will
220790792Sgshapiro		**  tell this process that all of the queue runners children
220890792Sgshapiro		**  need to be sent SIGTERM as well. At the same time, we
220990792Sgshapiro		**  want to return control to the command line. So we do an
221090792Sgshapiro		**  extra fork().
221190792Sgshapiro		*/
221290792Sgshapiro
221390792Sgshapiro		if (Verbose || foregroundqueue || (pid = fork()) <= 0)
221464562Sgshapiro		{
221590792Sgshapiro			/*
221690792Sgshapiro			**  If the fork() failed we should still try to do
221790792Sgshapiro			**  the queue run. If it succeeded then the child
221890792Sgshapiro			**  is going to start the run and wait for all
221990792Sgshapiro			**  of the children to finish.
222090792Sgshapiro			*/
222190792Sgshapiro
222290792Sgshapiro			if (pid == 0)
222390792Sgshapiro			{
222490792Sgshapiro				/* Reset global flags */
222590792Sgshapiro				RestartRequest = NULL;
222690792Sgshapiro				ShutdownRequest = NULL;
222790792Sgshapiro				PendingSignal = 0;
222890792Sgshapiro
222990792Sgshapiro				/* disconnect from terminal */
223090792Sgshapiro				disconnect(2, CurEnv);
223190792Sgshapiro			}
223290792Sgshapiro
223390792Sgshapiro			CurrentPid = getpid();
223490792Sgshapiro			if (qgrp != NOQGRP)
223590792Sgshapiro			{
2236110560Sgshapiro				int rwgflags = RWG_NONE;
2237110560Sgshapiro
223890792Sgshapiro				/*
223990792Sgshapiro				**  To run a specific queue group mark it to
224090792Sgshapiro				**  be run, select the work group it's in and
224190792Sgshapiro				**  increment the work counter.
224290792Sgshapiro				*/
224390792Sgshapiro
224494334Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
224594334Sgshapiro				     i++)
224694334Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
224794334Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
2248110560Sgshapiro				if (Verbose)
2249110560Sgshapiro					rwgflags |= RWG_VERBOSE;
2250110560Sgshapiro				if (queuepersistent)
2251110560Sgshapiro					rwgflags |= RWG_PERSISTENT;
2252110560Sgshapiro				rwgflags |= RWG_FORCE;
225390792Sgshapiro				(void) run_work_group(Queue[qgrp]->qg_wgrp,
2254110560Sgshapiro						      rwgflags);
225590792Sgshapiro			}
225690792Sgshapiro			else
225790792Sgshapiro				(void) runqueue(false, Verbose,
225890792Sgshapiro						queuepersistent, true);
225990792Sgshapiro
226090792Sgshapiro			/* set the title to make it easier to find */
226190792Sgshapiro			sm_setproctitle(true, CurEnv, "Queue control");
226290792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
226390792Sgshapiro			while (CurChildren > 0)
226490792Sgshapiro			{
226590792Sgshapiro				int status;
226690792Sgshapiro				pid_t ret;
226790792Sgshapiro
2268125820Sgshapiro				errno = 0;
226990792Sgshapiro				while ((ret = sm_wait(&status)) <= 0)
2270125820Sgshapiro				{
2271125820Sgshapiro					if (errno == ECHILD)
2272125820Sgshapiro					{
2273125820Sgshapiro						/*
2274125820Sgshapiro						**  Oops... something got messed
2275125820Sgshapiro						**  up really bad. Waiting for
2276125820Sgshapiro						**  non-existent children
2277125820Sgshapiro						**  shouldn't happen. Let's get
2278125820Sgshapiro						**  out of here.
2279125820Sgshapiro						*/
2280125820Sgshapiro
2281125820Sgshapiro						CurChildren = 0;
2282125820Sgshapiro						break;
2283125820Sgshapiro					}
228490792Sgshapiro					continue;
2285125820Sgshapiro				}
228690792Sgshapiro
2287125820Sgshapiro				/* something is really really wrong */
2288125820Sgshapiro				if (errno == ECHILD)
2289125820Sgshapiro				{
2290125820Sgshapiro					sm_syslog(LOG_ERR, NOQID,
2291125820Sgshapiro						  "queue control process: lost all children: wait returned ECHILD");
2292125820Sgshapiro					break;
2293125820Sgshapiro				}
2294125820Sgshapiro
229590792Sgshapiro				/* Only drop when a child gives status */
229690792Sgshapiro				if (WIFSTOPPED(status))
229790792Sgshapiro					continue;
229890792Sgshapiro
229990792Sgshapiro				proc_list_drop(ret, status, NULL);
230090792Sgshapiro			}
230164562Sgshapiro		}
230290792Sgshapiro		finis(true, true, ExitStat);
230390792Sgshapiro		/* NOTREACHED */
230438032Speter	}
230538032Speter
2306363466Sgshapiro#if SASL
230771345Sgshapiro	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
230871345Sgshapiro	{
230990792Sgshapiro		/* check whether AUTH is turned off for the server */
231090792Sgshapiro		if (!chkdaemonmodifiers(D_NOAUTH) &&
231190792Sgshapiro		    (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
231271345Sgshapiro			syserr("!sasl_server_init failed! [%s]",
231390792Sgshapiro				sasl_errstring(i, NULL, NULL));
231471345Sgshapiro	}
2315363466Sgshapiro#endif /* SASL */
231671345Sgshapiro
231790792Sgshapiro	if (OpMode == MD_SMTP)
231890792Sgshapiro	{
231990792Sgshapiro		proc_list_add(CurrentPid, "Sendmail SMTP Agent",
2320132943Sgshapiro			      PROC_DAEMON, 0, -1, NULL);
232190792Sgshapiro
232290792Sgshapiro		/* clean up background delivery children */
232390792Sgshapiro		(void) sm_signal(SIGCHLD, reapchild);
232490792Sgshapiro	}
232590792Sgshapiro
232638032Speter	/*
232738032Speter	**  If a daemon, wait for a request.
232838032Speter	**	getrequests will always return in a child.
232938032Speter	**	If we should also be processing the queue, start
233038032Speter	**		doing it in background.
233138032Speter	**	We check for any errors that might have happened
233238032Speter	**		during startup.
233338032Speter	*/
233438032Speter
233598841Sgshapiro	if (OpMode == MD_DAEMON || QueueIntvl > 0)
233638032Speter	{
233738032Speter		char dtype[200];
233838032Speter
2339161389Sgshapiro		/* avoid cleanup in finis(), DaemonPid will be set below */
2340161389Sgshapiro		DaemonPid = 0;
234138032Speter		if (!run_in_foreground && !tTd(99, 100))
234238032Speter		{
234338032Speter			/* put us in background */
234438032Speter			i = fork();
234538032Speter			if (i < 0)
234638032Speter				syserr("daemon: cannot fork");
234738032Speter			if (i != 0)
234890792Sgshapiro			{
234990792Sgshapiro				finis(false, true, EX_OK);
235090792Sgshapiro				/* NOTREACHED */
235190792Sgshapiro			}
235238032Speter
235390792Sgshapiro			/*
235490792Sgshapiro			**  Initialize exception stack and default exception
235590792Sgshapiro			**  handler for child process.
235690792Sgshapiro			*/
235790792Sgshapiro
235890792Sgshapiro			/* Reset global flags */
235990792Sgshapiro			RestartRequest = NULL;
236090792Sgshapiro			RestartWorkGroup = false;
236190792Sgshapiro			ShutdownRequest = NULL;
236290792Sgshapiro			PendingSignal = 0;
236390792Sgshapiro			CurrentPid = getpid();
236490792Sgshapiro
236590792Sgshapiro			sm_exc_newthread(fatal_error);
236690792Sgshapiro
236738032Speter			/* disconnect from our controlling tty */
236890792Sgshapiro			disconnect(2, &MainEnvelope);
236938032Speter		}
237038032Speter
237138032Speter		dtype[0] = '\0';
237238032Speter		if (OpMode == MD_DAEMON)
2373161389Sgshapiro		{
2374168515Sgshapiro			(void) sm_strlcat(dtype, "+SMTP", sizeof(dtype));
2375161389Sgshapiro			DaemonPid = CurrentPid;
2376161389Sgshapiro		}
237798841Sgshapiro		if (QueueIntvl > 0)
237838032Speter		{
237990792Sgshapiro			(void) sm_strlcat2(dtype,
238090792Sgshapiro					   queuepersistent
238190792Sgshapiro					   ? "+persistent-queueing@"
238290792Sgshapiro					   : "+queueing@",
238390792Sgshapiro					   pintvl(QueueIntvl, true),
2384168515Sgshapiro					   sizeof(dtype));
238538032Speter		}
238638032Speter		if (tTd(0, 1))
2387168515Sgshapiro			(void) sm_strlcat(dtype, "+debugging", sizeof(dtype));
238838032Speter
238938032Speter		sm_syslog(LOG_INFO, NOQID,
239064562Sgshapiro			  "starting daemon (%s): %s", Version, dtype + 1);
239190792Sgshapiro#if XLA
239238032Speter		xla_create_file();
2393363466Sgshapiro#endif
239438032Speter
239564562Sgshapiro		/* save daemon type in a macro for possible PidFile use */
239690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
239790792Sgshapiro			macid("{daemon_info}"), dtype + 1);
239864562Sgshapiro
239964562Sgshapiro		/* save queue interval in a macro for possible PidFile use */
240090792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
240190792Sgshapiro			macid("{queue_interval}"), pintvl(QueueIntvl, true));
240264562Sgshapiro
240390792Sgshapiro		/* workaround: can't seem to release the signal in the parent */
240490792Sgshapiro		(void) sm_signal(SIGHUP, sighup);
240590792Sgshapiro		(void) sm_releasesignal(SIGHUP);
240690792Sgshapiro		(void) sm_signal(SIGTERM, sigterm);
240790792Sgshapiro
240898841Sgshapiro		if (QueueIntvl > 0)
240938032Speter		{
2410173340Sgshapiro#if _FFR_RUNPQG
2411173340Sgshapiro			if (qgrp != NOQGRP)
2412173340Sgshapiro			{
2413173340Sgshapiro				int rwgflags = RWG_NONE;
241490792Sgshapiro
2415173340Sgshapiro				/*
2416173340Sgshapiro				**  To run a specific queue group mark it to
2417173340Sgshapiro				**  be run, select the work group it's in and
2418173340Sgshapiro				**  increment the work counter.
2419173340Sgshapiro				*/
2420173340Sgshapiro
2421173340Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
2422173340Sgshapiro				     i++)
2423173340Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
2424173340Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
2425173340Sgshapiro				if (Verbose)
2426173340Sgshapiro					rwgflags |= RWG_VERBOSE;
2427173340Sgshapiro				if (queuepersistent)
2428173340Sgshapiro					rwgflags |= RWG_PERSISTENT;
2429173340Sgshapiro				rwgflags |= RWG_FORCE;
2430173340Sgshapiro				(void) run_work_group(Queue[qgrp]->qg_wgrp,
2431173340Sgshapiro						      rwgflags);
2432173340Sgshapiro			}
2433173340Sgshapiro			else
2434173340Sgshapiro#endif /* _FFR_RUNPQG */
2435173340Sgshapiro				(void) runqueue(true, false, queuepersistent,
2436173340Sgshapiro						true);
2437173340Sgshapiro
243890792Sgshapiro			/*
243990792Sgshapiro			**  If queuepersistent but not in daemon mode then
244090792Sgshapiro			**  we're going to do the queue runner monitoring here.
244190792Sgshapiro			**  If in daemon mode then the monitoring will happen
244290792Sgshapiro			**  elsewhere.
244390792Sgshapiro			*/
244490792Sgshapiro
244590792Sgshapiro			if (OpMode != MD_DAEMON && queuepersistent)
244690792Sgshapiro			{
2447132943Sgshapiro				/*
2448132943Sgshapiro				**  Write the pid to file
2449132943Sgshapiro				**  XXX Overwrites sendmail.pid
2450132943Sgshapiro				*/
2451132943Sgshapiro
2452132943Sgshapiro				log_sendmail_pid(&MainEnvelope);
2453132943Sgshapiro
245490792Sgshapiro				/* set the title to make it easier to find */
245590792Sgshapiro				sm_setproctitle(true, CurEnv, "Queue control");
245690792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
245790792Sgshapiro				while (CurChildren > 0)
245890792Sgshapiro				{
245990792Sgshapiro					int status;
246090792Sgshapiro					pid_t ret;
246190792Sgshapiro					int group;
246290792Sgshapiro
2463120256Sgshapiro					CHECK_RESTART;
2464125820Sgshapiro					errno = 0;
246590792Sgshapiro					while ((ret = sm_wait(&status)) <= 0)
2466125820Sgshapiro					{
2467125820Sgshapiro						/*
2468125820Sgshapiro						**  Waiting for non-existent
2469125820Sgshapiro						**  children shouldn't happen.
2470125820Sgshapiro						**  Let's get out of here if
2471125820Sgshapiro						**  it occurs.
2472125820Sgshapiro						*/
2473125820Sgshapiro
2474125820Sgshapiro						if (errno == ECHILD)
2475125820Sgshapiro						{
2476125820Sgshapiro							CurChildren = 0;
2477125820Sgshapiro							break;
2478125820Sgshapiro						}
247990792Sgshapiro						continue;
2480125820Sgshapiro					}
248190792Sgshapiro
2482125820Sgshapiro					/* something is really really wrong */
2483125820Sgshapiro					if (errno == ECHILD)
2484125820Sgshapiro					{
2485125820Sgshapiro						sm_syslog(LOG_ERR, NOQID,
2486125820Sgshapiro							  "persistent queue runner control process: lost all children: wait returned ECHILD");
2487125820Sgshapiro						break;
2488125820Sgshapiro					}
2489125820Sgshapiro
249090792Sgshapiro					if (WIFSTOPPED(status))
249190792Sgshapiro						continue;
249290792Sgshapiro
249390792Sgshapiro					/* Probe only on a child status */
249490792Sgshapiro					proc_list_drop(ret, status, &group);
249590792Sgshapiro
249690792Sgshapiro					if (WIFSIGNALED(status))
249790792Sgshapiro					{
249890792Sgshapiro						if (WCOREDUMP(status))
249990792Sgshapiro						{
250090792Sgshapiro							sm_syslog(LOG_ERR, NOQID,
250190792Sgshapiro								  "persistent queue runner=%d core dumped, signal=%d",
250290792Sgshapiro								  group, WTERMSIG(status));
250390792Sgshapiro
2504120256Sgshapiro							/* don't restart this */
2505120256Sgshapiro							mark_work_group_restart(
2506120256Sgshapiro								group, -1);
250790792Sgshapiro							continue;
250890792Sgshapiro						}
250990792Sgshapiro
251090792Sgshapiro						sm_syslog(LOG_ERR, NOQID,
2511168515Sgshapiro							  "persistent queue runner=%d died, pid=%ld, signal=%d",
2512168515Sgshapiro							  group, (long) ret,
2513168515Sgshapiro							  WTERMSIG(status));
251490792Sgshapiro					}
251590792Sgshapiro
251690792Sgshapiro					/*
251790792Sgshapiro					**  When debugging active, don't
251890792Sgshapiro					**  restart the persistent queues.
251990792Sgshapiro					**  But do log this as info.
252090792Sgshapiro					*/
252190792Sgshapiro
252290792Sgshapiro					if (sm_debug_active(&DebugNoPRestart,
252390792Sgshapiro							    1))
252490792Sgshapiro					{
252590792Sgshapiro						sm_syslog(LOG_DEBUG, NOQID,
252690792Sgshapiro							  "persistent queue runner=%d, exited",
252790792Sgshapiro							  group);
2528120256Sgshapiro						mark_work_group_restart(group,
2529120256Sgshapiro									-1);
253090792Sgshapiro					}
2531168515Sgshapiro					CHECK_RESTART;
253290792Sgshapiro				}
253390792Sgshapiro				finis(true, true, ExitStat);
253490792Sgshapiro				/* NOTREACHED */
253590792Sgshapiro			}
253690792Sgshapiro
253738032Speter			if (OpMode != MD_DAEMON)
253838032Speter			{
253990792Sgshapiro				char qtype[200];
254090792Sgshapiro
254190792Sgshapiro				/*
254290792Sgshapiro				**  Write the pid to file
254390792Sgshapiro				**  XXX Overwrites sendmail.pid
254490792Sgshapiro				*/
254590792Sgshapiro
254690792Sgshapiro				log_sendmail_pid(&MainEnvelope);
254790792Sgshapiro
254890792Sgshapiro				/* set the title to make it easier to find */
254990792Sgshapiro				qtype[0] = '\0';
2550168515Sgshapiro				(void) sm_strlcpyn(qtype, sizeof(qtype), 4,
255190792Sgshapiro						   "Queue runner@",
255290792Sgshapiro						   pintvl(QueueIntvl, true),
255390792Sgshapiro						   " for ",
255490792Sgshapiro						   QueueDir);
255590792Sgshapiro				sm_setproctitle(true, CurEnv, qtype);
255638032Speter				for (;;)
255738032Speter				{
255864562Sgshapiro					(void) pause();
2559132943Sgshapiro
2560120256Sgshapiro					CHECK_RESTART;
2561132943Sgshapiro
256290792Sgshapiro					if (doqueuerun())
256390792Sgshapiro						(void) runqueue(true, false,
256490792Sgshapiro								false, false);
256538032Speter				}
256638032Speter			}
256738032Speter		}
2568203004Sgshapiro		(void) dropenvelope(&MainEnvelope, true, false);
256938032Speter
257090792Sgshapiro#if STARTTLS
257164562Sgshapiro		/* init TLS for server, ignore result for now */
257290792Sgshapiro		(void) initsrvtls(tls_ok);
2573363466Sgshapiro#endif
2574110560Sgshapiro
257590792Sgshapiro	nextreq:
257690792Sgshapiro		p_flags = getrequests(&MainEnvelope);
257738032Speter
257838032Speter		/* drop privileges */
257990792Sgshapiro		(void) drop_privileges(false);
258038032Speter
258138032Speter		/*
258238032Speter		**  Get authentication data
258390792Sgshapiro		**  Set _ macro in BlankEnvelope before calling newenvelope().
258438032Speter		*/
258538032Speter
2586285229Sgshapiro#if _FFR_XCNCT
2587285229Sgshapiro		if (bitnset(D_XCNCT, *p_flags) || bitnset(D_XCNCT_M, *p_flags))
2588285229Sgshapiro		{
2589285229Sgshapiro			/* copied from getauthinfo() */
2590285229Sgshapiro			if (RealHostName == NULL)
2591285229Sgshapiro			{
2592285229Sgshapiro				RealHostName = newstr(hostnamebyanyaddr(&RealHostAddr));
2593285229Sgshapiro				if (strlen(RealHostName) > MAXNAME)
2594285229Sgshapiro					RealHostName[MAXNAME] = '\0'; /* XXX - 1 ? */
2595285229Sgshapiro			}
2596285229Sgshapiro			snprintf(buf, sizeof(buf), "%s [%s]",
2597285229Sgshapiro				RealHostName, anynet_ntoa(&RealHostAddr));
2598285229Sgshapiro
2599285229Sgshapiro			forged = bitnset(D_XCNCT_M, *p_flags);
2600285229Sgshapiro			if (forged)
2601285229Sgshapiro			{
2602285229Sgshapiro				(void) sm_strlcat(buf, " (may be forged)",
2603285229Sgshapiro						sizeof(buf));
2604285229Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_PERM,
2605285229Sgshapiro					  macid("{client_resolve}"), "FORGED");
2606285229Sgshapiro			}
2607285229Sgshapiro
2608285229Sgshapiro			/* HACK! variable used only two times right below */
2609285229Sgshapiro			authinfo = buf;
2610285229Sgshapiro			if (tTd(75, 9))
2611285229Sgshapiro				sm_syslog(LOG_INFO, NOQID,
2612285229Sgshapiro					"main: where=not_calling_getauthinfo, RealHostAddr=%s",
2613285229Sgshapiro					anynet_ntoa(&RealHostAddr));
2614285229Sgshapiro		}
2615285229Sgshapiro		else
2616285229Sgshapiro		/* WARNING: "non-braced" else */
2617285229Sgshapiro#endif /* _FFR_XCNCT */
261890792Sgshapiro		authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
261990792Sgshapiro						     NULL), &forged);
262090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
2621244833Sgshapiro		if (tTd(75, 9))
2622244833Sgshapiro			sm_syslog(LOG_INFO, NOQID,
2623244833Sgshapiro				"main: where=after_getauthinfo, RealHostAddr=%s",
2624244833Sgshapiro				anynet_ntoa(&RealHostAddr));
262590792Sgshapiro
262690792Sgshapiro		/* at this point we are in a child: reset state */
262790792Sgshapiro		sm_rpool_free(MainEnvelope.e_rpool);
262890792Sgshapiro		(void) newenvelope(&MainEnvelope, &MainEnvelope,
262990792Sgshapiro				   sm_rpool_new_x(NULL));
263038032Speter	}
263138032Speter
263264562Sgshapiro	if (LogLevel > 9)
263364562Sgshapiro	{
2634363466Sgshapiro		p = authinfo;
2635363466Sgshapiro		if (NULL == p)
2636363466Sgshapiro		{
2637363466Sgshapiro			if (NULL != RealHostName)
2638363466Sgshapiro				p = RealHostName;
2639363466Sgshapiro			else
2640363466Sgshapiro				p = anynet_ntoa(&RealHostAddr);
2641363466Sgshapiro			if (NULL == p)
2642363466Sgshapiro				p = "unknown";
2643363466Sgshapiro		}
2644363466Sgshapiro
264564562Sgshapiro		/* log connection information */
2646363466Sgshapiro		sm_syslog(LOG_INFO, NULL, "connect from %s", p);
264764562Sgshapiro	}
264864562Sgshapiro
264938032Speter	/*
265038032Speter	**  If running SMTP protocol, start collecting and executing
265138032Speter	**  commands.  This will never return.
265238032Speter	*/
265338032Speter
265438032Speter	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
265538032Speter	{
265638032Speter		char pbuf[20];
265738032Speter
265838032Speter		/*
265938032Speter		**  Save some macros for check_* rulesets.
266038032Speter		*/
266138032Speter
266238032Speter		if (forged)
266338032Speter		{
266438032Speter			char ipbuf[103];
266538032Speter
2666168515Sgshapiro			(void) sm_snprintf(ipbuf, sizeof(ipbuf), "[%.100s]",
266790792Sgshapiro					   anynet_ntoa(&RealHostAddr));
266890792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
266990792Sgshapiro				  macid("{client_name}"), ipbuf);
267038032Speter		}
267138032Speter		else
267290792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
267390792Sgshapiro				  macid("{client_name}"), RealHostName);
2674132943Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
2675132943Sgshapiro			  macid("{client_ptr}"), RealHostName);
267690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
267790792Sgshapiro			  macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
267890792Sgshapiro		sm_getla();
267938032Speter
268090792Sgshapiro		switch (RealHostAddr.sa.sa_family)
268164562Sgshapiro		{
268290792Sgshapiro#if NETINET
268364562Sgshapiro		  case AF_INET:
2684168515Sgshapiro			(void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
2685285229Sgshapiro					   ntohs(RealHostAddr.sin.sin_port));
268664562Sgshapiro			break;
268790792Sgshapiro#endif /* NETINET */
268890792Sgshapiro#if NETINET6
268964562Sgshapiro		  case AF_INET6:
2690168515Sgshapiro			(void) sm_snprintf(pbuf, sizeof(pbuf), "%d",
2691285229Sgshapiro					   ntohs(RealHostAddr.sin6.sin6_port));
269264562Sgshapiro			break;
269390792Sgshapiro#endif /* NETINET6 */
269464562Sgshapiro		  default:
2695168515Sgshapiro			(void) sm_snprintf(pbuf, sizeof(pbuf), "0");
269664562Sgshapiro			break;
269764562Sgshapiro		}
269890792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
269990792Sgshapiro			macid("{client_port}"), pbuf);
270042575Speter
270138032Speter		if (OpMode == MD_DAEMON)
270238032Speter		{
2703168515Sgshapiro			ENVELOPE *saved_env;
2704168515Sgshapiro
270538032Speter			/* validate the connection */
270690792Sgshapiro			HoldErrs = true;
2707168515Sgshapiro			saved_env = CurEnv;
2708168515Sgshapiro			CurEnv = &BlankEnvelope;
270938032Speter			nullserver = validate_connection(&RealHostAddr,
2710132943Sgshapiro						macvalue(macid("{client_name}"),
2711168515Sgshapiro							&BlankEnvelope),
2712168515Sgshapiro						&BlankEnvelope);
2713168515Sgshapiro			if (bitset(EF_DISCARD, BlankEnvelope.e_flags))
2714168515Sgshapiro				MainEnvelope.e_flags |= EF_DISCARD;
2715168515Sgshapiro			CurEnv = saved_env;
271690792Sgshapiro			HoldErrs = false;
271738032Speter		}
271864562Sgshapiro		else if (p_flags == NULL)
271964562Sgshapiro		{
2720168515Sgshapiro			p_flags = (BITMAP256 *) xalloc(sizeof(*p_flags));
272164562Sgshapiro			clrbitmap(p_flags);
272264562Sgshapiro		}
272390792Sgshapiro#if STARTTLS
272464562Sgshapiro		if (OpMode == MD_SMTP)
272590792Sgshapiro			(void) initsrvtls(tls_ok);
2726363466Sgshapiro#endif
272771345Sgshapiro
272890792Sgshapiro		/* turn off profiling */
272990792Sgshapiro		SM_PROF(1);
273090792Sgshapiro		smtp(nullserver, *p_flags, &MainEnvelope);
2731110560Sgshapiro
2732110560Sgshapiro		if (tTd(93, 100))
2733110560Sgshapiro		{
2734110560Sgshapiro			/* turn off profiling */
2735110560Sgshapiro			SM_PROF(0);
2736110560Sgshapiro			if (OpMode == MD_DAEMON)
2737110560Sgshapiro				goto nextreq;
2738110560Sgshapiro		}
273938032Speter	}
274038032Speter
274190792Sgshapiro	sm_rpool_free(MainEnvelope.e_rpool);
274290792Sgshapiro	clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
274338032Speter	if (OpMode == MD_VERIFY)
274438032Speter	{
274590792Sgshapiro		set_delivery_mode(SM_VERIFY, &MainEnvelope);
274638032Speter		PostMasterCopy = NULL;
274738032Speter	}
274838032Speter	else
274938032Speter	{
275038032Speter		/* interactive -- all errors are global */
275190792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
275238032Speter	}
275338032Speter
275438032Speter	/*
275538032Speter	**  Do basic system initialization and set the sender
275638032Speter	*/
275738032Speter
275890792Sgshapiro	initsys(&MainEnvelope);
275990792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
276090792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
276190792Sgshapiro	setsender(from, &MainEnvelope, NULL, '\0', false);
276264562Sgshapiro	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
276390792Sgshapiro	    (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
276490792Sgshapiro	     strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
276564562Sgshapiro	{
276690792Sgshapiro		auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
276790792Sgshapiro			     RealUserName, from, warn_f_flag);
276864562Sgshapiro#if SASL
276990792Sgshapiro		auth = false;
2770363466Sgshapiro#endif
277164562Sgshapiro	}
277264562Sgshapiro	if (auth)
277364562Sgshapiro	{
277464562Sgshapiro		char *fv;
277564562Sgshapiro
277664562Sgshapiro		/* set the initial sender for AUTH= to $f@$j */
277790792Sgshapiro		fv = macvalue('f', &MainEnvelope);
277864562Sgshapiro		if (fv == NULL || *fv == '\0')
277990792Sgshapiro			MainEnvelope.e_auth_param = NULL;
278064562Sgshapiro		else
278164562Sgshapiro		{
278264562Sgshapiro			if (strchr(fv, '@') == NULL)
278364562Sgshapiro			{
278490792Sgshapiro				i = strlen(fv) + strlen(macvalue('j',
278590792Sgshapiro							&MainEnvelope)) + 2;
278690792Sgshapiro				p = sm_malloc_x(i);
278790792Sgshapiro				(void) sm_strlcpyn(p, i, 3, fv, "@",
278890792Sgshapiro						   macvalue('j',
278990792Sgshapiro							    &MainEnvelope));
279064562Sgshapiro			}
279164562Sgshapiro			else
279290792Sgshapiro				p = sm_strdup_x(fv);
279390792Sgshapiro			MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
279490792Sgshapiro								      xtextify(p, "="));
279590792Sgshapiro			sm_free(p);  /* XXX */
279664562Sgshapiro		}
279764562Sgshapiro	}
279890792Sgshapiro	if (macvalue('s', &MainEnvelope) == NULL)
279990792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
280038032Speter
280190792Sgshapiro	av = argv + optind;
280238032Speter	if (*av == NULL && !GrabTo)
280338032Speter	{
280490792Sgshapiro		MainEnvelope.e_to = NULL;
280590792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
280690792Sgshapiro		HoldErrs = false;
280790792Sgshapiro		SuperSafe = SAFE_NO;
280838032Speter		usrerr("Recipient names must be specified");
280938032Speter
281038032Speter		/* collect body for UUCP return */
281138032Speter		if (OpMode != MD_VERIFY)
2812120256Sgshapiro			collect(InChannel, false, NULL, &MainEnvelope, true);
281390792Sgshapiro		finis(true, true, EX_USAGE);
281490792Sgshapiro		/* NOTREACHED */
281538032Speter	}
281638032Speter
281738032Speter	/*
281838032Speter	**  Scan argv and deliver the message to everyone.
281938032Speter	*/
282038032Speter
282190792Sgshapiro	save_val = LogUsrErrs;
282290792Sgshapiro	LogUsrErrs = true;
282390792Sgshapiro	sendtoargv(av, &MainEnvelope);
282490792Sgshapiro	LogUsrErrs = save_val;
282538032Speter
282638032Speter	/* if we have had errors sofar, arrange a meaningful exit stat */
282738032Speter	if (Errors > 0 && ExitStat == EX_OK)
282838032Speter		ExitStat = EX_USAGE;
282938032Speter
283038032Speter#if _FFR_FIX_DASHT
283138032Speter	/*
283238032Speter	**  If using -t, force not sending to argv recipients, even
283338032Speter	**  if they are mentioned in the headers.
283438032Speter	*/
283538032Speter
283638032Speter	if (GrabTo)
283738032Speter	{
283838032Speter		ADDRESS *q;
283964562Sgshapiro
284090792Sgshapiro		for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
284164562Sgshapiro			q->q_state = QS_REMOVED;
284238032Speter	}
284364562Sgshapiro#endif /* _FFR_FIX_DASHT */
284438032Speter
284538032Speter	/*
284638032Speter	**  Read the input mail.
284738032Speter	*/
284838032Speter
284990792Sgshapiro	MainEnvelope.e_to = NULL;
285038032Speter	if (OpMode != MD_VERIFY || GrabTo)
285138032Speter	{
285290792Sgshapiro		int savederrors;
285390792Sgshapiro		unsigned long savedflags;
285438032Speter
285590792Sgshapiro		/*
285690792Sgshapiro		**  workaround for compiler warning on Irix:
285790792Sgshapiro		**  do not initialize variable in the definition, but
285890792Sgshapiro		**  later on:
285990792Sgshapiro		**  warning(1548): transfer of control bypasses
286090792Sgshapiro		**  initialization of:
286190792Sgshapiro		**  variable "savederrors" (declared at line 2570)
286290792Sgshapiro		**  variable "savedflags" (declared at line 2571)
286390792Sgshapiro		**  goto giveup;
286490792Sgshapiro		*/
286590792Sgshapiro
286690792Sgshapiro		savederrors = Errors;
286790792Sgshapiro		savedflags = MainEnvelope.e_flags & EF_FATALERRS;
286890792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
286990792Sgshapiro		MainEnvelope.e_flags &= ~EF_FATALERRS;
287064562Sgshapiro		Errors = 0;
287164562Sgshapiro		buffer_errors();
2872120256Sgshapiro		collect(InChannel, false, NULL, &MainEnvelope, true);
287338032Speter
287464562Sgshapiro		/* header checks failed */
287564562Sgshapiro		if (Errors > 0)
287664562Sgshapiro		{
287790792Sgshapiro  giveup:
287890792Sgshapiro			if (!GrabTo)
287964562Sgshapiro			{
288090792Sgshapiro				/* Log who the mail would have gone to */
288190792Sgshapiro				logundelrcpts(&MainEnvelope,
288290792Sgshapiro					      MainEnvelope.e_message,
288390792Sgshapiro					      8, false);
288464562Sgshapiro			}
288590792Sgshapiro			flush_errors(true);
288690792Sgshapiro			finis(true, true, ExitStat);
288764562Sgshapiro			/* NOTREACHED */
288864562Sgshapiro			return -1;
288964562Sgshapiro		}
289064562Sgshapiro
289138032Speter		/* bail out if message too large */
289290792Sgshapiro		if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
289338032Speter		{
289490792Sgshapiro			finis(true, true, ExitStat != EX_OK ? ExitStat
289590792Sgshapiro							    : EX_DATAERR);
289664562Sgshapiro			/* NOTREACHED */
289738032Speter			return -1;
289838032Speter		}
289998121Sgshapiro
290098121Sgshapiro		/* set message size */
2901168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%ld",
2902244833Sgshapiro				   PRT_NONNEGL(MainEnvelope.e_msgsize));
290398121Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
290498121Sgshapiro			  macid("{msg_size}"), buf);
290598121Sgshapiro
290664562Sgshapiro		Errors = savederrors;
290790792Sgshapiro		MainEnvelope.e_flags |= savedflags;
290838032Speter	}
290938032Speter	errno = 0;
291038032Speter
291138032Speter	if (tTd(1, 1))
291290792Sgshapiro		sm_dprintf("From person = \"%s\"\n",
291390792Sgshapiro			   MainEnvelope.e_from.q_paddr);
291438032Speter
291590792Sgshapiro	/* Check if quarantining stats should be updated */
291690792Sgshapiro	if (MainEnvelope.e_quarmsg != NULL)
291790792Sgshapiro		markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
291890792Sgshapiro
291938032Speter	/*
292038032Speter	**  Actually send everything.
292138032Speter	**	If verifying, just ack.
292238032Speter	*/
292338032Speter
292490792Sgshapiro	if (Errors == 0)
292538032Speter	{
292690792Sgshapiro		if (!split_by_recipient(&MainEnvelope) &&
292790792Sgshapiro		    bitset(EF_FATALERRS, MainEnvelope.e_flags))
292890792Sgshapiro			goto giveup;
292938032Speter	}
293090792Sgshapiro
293190792Sgshapiro	/* make sure we deliver at least the first envelope */
293290792Sgshapiro	i = FastSplit > 0 ? 0 : -1;
293390792Sgshapiro	for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
293490792Sgshapiro	{
293590792Sgshapiro		ENVELOPE *next;
293690792Sgshapiro
293790792Sgshapiro		e->e_from.q_state = QS_SENDER;
293890792Sgshapiro		if (tTd(1, 5))
293990792Sgshapiro		{
294090792Sgshapiro			sm_dprintf("main[%d]: QS_SENDER ", i);
2941132943Sgshapiro			printaddr(sm_debug_file(), &e->e_from, false);
294290792Sgshapiro		}
294390792Sgshapiro		e->e_to = NULL;
294490792Sgshapiro		sm_getla();
294590792Sgshapiro		GrabTo = false;
294664562Sgshapiro#if NAMED_BIND
294790792Sgshapiro		_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
294890792Sgshapiro		_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
2949363466Sgshapiro#endif
295090792Sgshapiro		next = e->e_sibling;
295190792Sgshapiro		e->e_sibling = NULL;
295238032Speter
295390792Sgshapiro		/* after FastSplit envelopes: queue up */
295490792Sgshapiro		sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
295590792Sgshapiro		e->e_sibling = next;
295690792Sgshapiro	}
295790792Sgshapiro
295838032Speter	/*
295938032Speter	**  All done.
296038032Speter	**	Don't send return error message if in VERIFY mode.
296138032Speter	*/
296238032Speter
296390792Sgshapiro	finis(true, true, ExitStat);
296464562Sgshapiro	/* NOTREACHED */
296564562Sgshapiro	return ExitStat;
296638032Speter}
296790792Sgshapiro/*
296877349Sgshapiro**  STOP_SENDMAIL -- Stop the running program
296977349Sgshapiro**
297077349Sgshapiro**	Parameters:
297177349Sgshapiro**		none.
297277349Sgshapiro**
297377349Sgshapiro**	Returns:
297477349Sgshapiro**		none.
297577349Sgshapiro**
297677349Sgshapiro**	Side Effects:
297777349Sgshapiro**		exits.
297877349Sgshapiro*/
297938032Speter
298077349Sgshapirovoid
298177349Sgshapirostop_sendmail()
298277349Sgshapiro{
298377349Sgshapiro	/* reset uid for process accounting */
298477349Sgshapiro	endpwent();
298577349Sgshapiro	(void) setuid(RealUid);
298677349Sgshapiro	exit(EX_OK);
298777349Sgshapiro}
298890792Sgshapiro/*
298938032Speter**  FINIS -- Clean up and exit.
299038032Speter**
299138032Speter**	Parameters:
299242575Speter**		drop -- whether or not to drop CurEnv envelope
299390792Sgshapiro**		cleanup -- call exit() or _exit()?
299442575Speter**		exitstat -- exit status to use for exit() call
299538032Speter**
299638032Speter**	Returns:
299738032Speter**		never
299838032Speter**
299938032Speter**	Side Effects:
300038032Speter**		exits sendmail
300138032Speter*/
300238032Speter
300338032Spetervoid
300490792Sgshapirofinis(drop, cleanup, exitstat)
300542575Speter	bool drop;
300690792Sgshapiro	bool cleanup;
300742575Speter	volatile int exitstat;
300838032Speter{
3009132943Sgshapiro	char pidpath[MAXPATHLEN];
3010159609Sgshapiro	pid_t pid;
301198121Sgshapiro
301277349Sgshapiro	/* Still want to process new timeouts added below */
301390792Sgshapiro	sm_clear_events();
301490792Sgshapiro	(void) sm_releasesignal(SIGALRM);
301542575Speter
3016363466Sgshapiro#if RATECTL_DEBUG || _FFR_OCC
3017363466Sgshapiro	/* do this only in "main" process */
3018363466Sgshapiro	if (DaemonPid == getpid())
3019363466Sgshapiro	{
3020363466Sgshapiro		SM_FILE_T *fp;
3021363466Sgshapiro
3022363466Sgshapiro		fp = sm_debug_file();
3023363466Sgshapiro		if (fp != NULL)
3024363466Sgshapiro			dump_ch(fp);
3025363466Sgshapiro	}
3026363466Sgshapiro#endif
302738032Speter	if (tTd(2, 1))
302838032Speter	{
302990792Sgshapiro		sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
303090792Sgshapiro			   exitstat,
303190792Sgshapiro			   CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
303238032Speter		printenvflags(CurEnv);
303338032Speter	}
303438032Speter	if (tTd(2, 9))
303590792Sgshapiro		printopenfds(false);
303638032Speter
303790792Sgshapiro	SM_TRY
303890792Sgshapiro		/*
303990792Sgshapiro		**  Clean up.  This might raise E:mta.quickabort
304090792Sgshapiro		*/
304138032Speter
304290792Sgshapiro		/* clean up temp files */
304390792Sgshapiro		CurEnv->e_to = NULL;
304490792Sgshapiro		if (drop)
304590792Sgshapiro		{
304690792Sgshapiro			if (CurEnv->e_id != NULL)
304790792Sgshapiro			{
3048203004Sgshapiro				int r;
3049203004Sgshapiro
3050203004Sgshapiro				r = dropenvelope(CurEnv, true, false);
3051203004Sgshapiro				if (exitstat == EX_OK)
3052203004Sgshapiro					exitstat = r;
305390792Sgshapiro				sm_rpool_free(CurEnv->e_rpool);
305490792Sgshapiro				CurEnv->e_rpool = NULL;
3055161389Sgshapiro
3056168515Sgshapiro				/* these may have pointed to the rpool */
3057161389Sgshapiro				CurEnv->e_to = NULL;
3058168515Sgshapiro				CurEnv->e_message = NULL;
3059168515Sgshapiro				CurEnv->e_statmsg = NULL;
3060168515Sgshapiro				CurEnv->e_quarmsg = NULL;
3061168515Sgshapiro				CurEnv->e_bodytype = NULL;
3062168515Sgshapiro				CurEnv->e_id = NULL;
3063168515Sgshapiro				CurEnv->e_envid = NULL;
3064168515Sgshapiro				CurEnv->e_auth_param = NULL;
306590792Sgshapiro			}
306690792Sgshapiro			else
306790792Sgshapiro				poststats(StatFile);
306890792Sgshapiro		}
306938032Speter
307090792Sgshapiro		/* flush any cached connections */
307190792Sgshapiro		mci_flush(true, NULL);
307238032Speter
307390792Sgshapiro		/* close maps belonging to this pid */
307490792Sgshapiro		closemaps(false);
307542575Speter
307664562Sgshapiro#if USERDB
307790792Sgshapiro		/* close UserDatabase */
307890792Sgshapiro		_udbx_close();
3079363466Sgshapiro#endif
308042575Speter
308190792Sgshapiro#if SASL
308290792Sgshapiro		stop_sasl_client();
3083363466Sgshapiro#endif
308490792Sgshapiro
308590792Sgshapiro#if XLA
308690792Sgshapiro		/* clean up extended load average stuff */
308790792Sgshapiro		xla_all_end();
3088363466Sgshapiro#endif
308938032Speter
309090792Sgshapiro	SM_FINALLY
309190792Sgshapiro		/*
309290792Sgshapiro		**  And exit.
309390792Sgshapiro		*/
309438032Speter
309590792Sgshapiro		if (LogLevel > 78)
309690792Sgshapiro			sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
309790792Sgshapiro				  (int) CurrentPid);
309890792Sgshapiro		if (exitstat == EX_TEMPFAIL ||
309990792Sgshapiro		    CurEnv->e_errormode == EM_BERKNET)
310090792Sgshapiro			exitstat = EX_OK;
310164562Sgshapiro
310290792Sgshapiro		/* XXX clean up queues and related data structures */
310390792Sgshapiro		cleanup_queues();
3104159609Sgshapiro		pid = getpid();
310590792Sgshapiro#if SM_CONF_SHM
3106159609Sgshapiro		cleanup_shm(DaemonPid == pid);
3107363466Sgshapiro#endif
310890792Sgshapiro
3109132943Sgshapiro		/* close locked pid file */
3110132943Sgshapiro		close_sendmail_pid();
3111132943Sgshapiro
3112159609Sgshapiro		if (DaemonPid == pid || PidFilePid == pid)
3113132943Sgshapiro		{
3114132943Sgshapiro			/* blow away the pid file */
3115168515Sgshapiro			expand(PidFile, pidpath, sizeof(pidpath), CurEnv);
3116132943Sgshapiro			(void) unlink(pidpath);
3117132943Sgshapiro		}
3118132943Sgshapiro
311990792Sgshapiro		/* reset uid for process accounting */
312090792Sgshapiro		endpwent();
312190792Sgshapiro		sm_mbdb_terminate();
3122157001Sgshapiro#if _FFR_MEMSTAT
3123157001Sgshapiro		(void) sm_memstat_close();
3124363466Sgshapiro#endif
312590792Sgshapiro		(void) setuid(RealUid);
312690792Sgshapiro#if SM_HEAP_CHECK
312790792Sgshapiro		/* dump the heap, if we are checking for memory leaks */
312890792Sgshapiro		if (sm_debug_active(&SmHeapCheck, 2))
312990792Sgshapiro			sm_heap_report(smioout,
313090792Sgshapiro				       sm_debug_level(&SmHeapCheck) - 1);
3131363466Sgshapiro#endif
313290792Sgshapiro		if (sm_debug_active(&SmXtrapReport, 1))
313390792Sgshapiro			sm_dprintf("xtrap count = %d\n", SmXtrapCount);
313490792Sgshapiro		if (cleanup)
313590792Sgshapiro			exit(exitstat);
313690792Sgshapiro		else
313790792Sgshapiro			_exit(exitstat);
313890792Sgshapiro	SM_END_TRY
313938032Speter}
314090792Sgshapiro/*
314190792Sgshapiro**  INTINDEBUG -- signal handler for SIGINT in -bt mode
314277349Sgshapiro**
314377349Sgshapiro**	Parameters:
314490792Sgshapiro**		sig -- incoming signal.
314590792Sgshapiro**
314690792Sgshapiro**	Returns:
314790792Sgshapiro**		none.
314890792Sgshapiro**
314990792Sgshapiro**	Side Effects:
315090792Sgshapiro**		longjmps back to test mode loop.
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.
315590792Sgshapiro*/
315690792Sgshapiro
315790792Sgshapiro/* Type of an exception generated on SIGINT during address test mode.  */
315890792Sgshapirostatic const SM_EXC_TYPE_T EtypeInterrupt =
315990792Sgshapiro{
316090792Sgshapiro	SmExcTypeMagic,
316190792Sgshapiro	"S:mta.interrupt",
316290792Sgshapiro	"",
316390792Sgshapiro	sm_etype_printf,
316490792Sgshapiro	"interrupt",
316590792Sgshapiro};
316690792Sgshapiro
316790792Sgshapiro/* ARGSUSED */
316890792Sgshapirostatic SIGFUNC_DECL
316990792Sgshapirointindebug(sig)
317090792Sgshapiro	int sig;
317190792Sgshapiro{
317290792Sgshapiro	int save_errno = errno;
317390792Sgshapiro
317490792Sgshapiro	FIX_SYSV_SIGNAL(sig, intindebug);
317590792Sgshapiro	errno = save_errno;
317690792Sgshapiro	CHECK_CRITICAL(sig);
317790792Sgshapiro	errno = save_errno;
317890792Sgshapiro	sm_exc_raisenew_x(&EtypeInterrupt);
317990792Sgshapiro	errno = save_errno;
318090792Sgshapiro	return SIGFUNC_RETURN;
318190792Sgshapiro}
318290792Sgshapiro/*
318390792Sgshapiro**  SIGTERM -- SIGTERM handler for the daemon
318490792Sgshapiro**
318590792Sgshapiro**	Parameters:
318677349Sgshapiro**		sig -- signal number.
318777349Sgshapiro**
318877349Sgshapiro**	Returns:
318977349Sgshapiro**		none.
319077349Sgshapiro**
319177349Sgshapiro**	Side Effects:
319277349Sgshapiro**		Sets ShutdownRequest which will hopefully trigger
319377349Sgshapiro**		the daemon to exit.
319477349Sgshapiro**
319577349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
319677349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
319777349Sgshapiro**		DOING.
319877349Sgshapiro*/
319977349Sgshapiro
320077349Sgshapiro/* ARGSUSED */
320177349Sgshapirostatic SIGFUNC_DECL
320290792Sgshapirosigterm(sig)
320377349Sgshapiro	int sig;
320477349Sgshapiro{
320577349Sgshapiro	int save_errno = errno;
320677349Sgshapiro
320790792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigterm);
320877349Sgshapiro	ShutdownRequest = "signal";
320977349Sgshapiro	errno = save_errno;
321077349Sgshapiro	return SIGFUNC_RETURN;
321177349Sgshapiro}
321290792Sgshapiro/*
321390792Sgshapiro**  SIGHUP -- handle a SIGHUP signal
321477349Sgshapiro**
321577349Sgshapiro**	Parameters:
321690792Sgshapiro**		sig -- incoming signal.
321777349Sgshapiro**
321877349Sgshapiro**	Returns:
321977349Sgshapiro**		none.
322077349Sgshapiro**
322177349Sgshapiro**	Side Effects:
322290792Sgshapiro**		Sets RestartRequest which should cause the daemon
322390792Sgshapiro**		to restart.
322490792Sgshapiro**
322590792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
322690792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
322790792Sgshapiro**		DOING.
322877349Sgshapiro*/
322977349Sgshapiro
323090792Sgshapiro/* ARGSUSED */
323190792Sgshapirostatic SIGFUNC_DECL
323290792Sgshapirosighup(sig)
323390792Sgshapiro	int sig;
323477349Sgshapiro{
323590792Sgshapiro	int save_errno = errno;
323677349Sgshapiro
323790792Sgshapiro	FIX_SYSV_SIGNAL(sig, sighup);
323890792Sgshapiro	RestartRequest = "signal";
323990792Sgshapiro	errno = save_errno;
324090792Sgshapiro	return SIGFUNC_RETURN;
324190792Sgshapiro}
324290792Sgshapiro/*
324390792Sgshapiro**  SIGPIPE -- signal handler for SIGPIPE
324490792Sgshapiro**
324590792Sgshapiro**	Parameters:
324690792Sgshapiro**		sig -- incoming signal.
324790792Sgshapiro**
324890792Sgshapiro**	Returns:
324990792Sgshapiro**		none.
325090792Sgshapiro**
325190792Sgshapiro**	Side Effects:
325290792Sgshapiro**		Sets StopRequest which should cause the mailq/hoststatus
325390792Sgshapiro**		display to stop.
325490792Sgshapiro**
325590792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
325690792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
325790792Sgshapiro**		DOING.
325890792Sgshapiro*/
325977349Sgshapiro
326090792Sgshapiro/* ARGSUSED */
326190792Sgshapirostatic SIGFUNC_DECL
326290792Sgshapirosigpipe(sig)
326390792Sgshapiro	int sig;
326490792Sgshapiro{
326590792Sgshapiro	int save_errno = errno;
326677349Sgshapiro
326790792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigpipe);
326890792Sgshapiro	StopRequest = true;
326990792Sgshapiro	errno = save_errno;
327090792Sgshapiro	return SIGFUNC_RETURN;
327177349Sgshapiro}
327290792Sgshapiro/*
327338032Speter**  INTSIG -- clean up on interrupt
327438032Speter**
327564562Sgshapiro**	This just arranges to exit.  It pessimizes in that it
327638032Speter**	may resend a message.
327738032Speter**
327838032Speter**	Parameters:
3279223067Sgshapiro**		sig -- incoming signal.
328038032Speter**
328138032Speter**	Returns:
328238032Speter**		none.
328338032Speter**
328438032Speter**	Side Effects:
328538032Speter**		Unlocks the current job.
328677349Sgshapiro**
328777349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
328877349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
328977349Sgshapiro**		DOING.
329038032Speter*/
329138032Speter
329238032Speter/* ARGSUSED */
329338032SpeterSIGFUNC_DECL
329438032Speterintsig(sig)
329538032Speter	int sig;
329638032Speter{
329790792Sgshapiro	bool drop = false;
329877349Sgshapiro	int save_errno = errno;
329964562Sgshapiro
330077349Sgshapiro	FIX_SYSV_SIGNAL(sig, intsig);
330177349Sgshapiro	errno = save_errno;
330277349Sgshapiro	CHECK_CRITICAL(sig);
330390792Sgshapiro	sm_allsignals(true);
3304223067Sgshapiro	IntSig = true;
330590792Sgshapiro
330638032Speter	FileName = NULL;
330764562Sgshapiro
330864562Sgshapiro	/* Clean-up on aborted stdin message submission */
3309223067Sgshapiro	if  (OpMode == MD_SMTP ||
331064562Sgshapiro	     OpMode == MD_DELIVER ||
3311223067Sgshapiro	     OpMode == MD_ARPAFTP)
331264562Sgshapiro	{
3313223067Sgshapiro		if (CurEnv->e_id != NULL)
3314223067Sgshapiro		{
3315223067Sgshapiro			char *fn;
331664562Sgshapiro
3317223067Sgshapiro			fn = queuename(CurEnv, DATAFL_LETTER);
3318223067Sgshapiro			if (fn != NULL)
3319223067Sgshapiro				(void) unlink(fn);
3320223067Sgshapiro			fn = queuename(CurEnv, ANYQFL_LETTER);
3321223067Sgshapiro			if (fn != NULL)
3322223067Sgshapiro				(void) unlink(fn);
3323223067Sgshapiro		}
3324223067Sgshapiro		_exit(EX_OK);
3325223067Sgshapiro		/* NOTREACHED */
3326223067Sgshapiro	}
332764562Sgshapiro
3328223067Sgshapiro	if (sig != 0 && LogLevel > 79)
3329223067Sgshapiro		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
3330223067Sgshapiro	if (OpMode != MD_TEST)
333164562Sgshapiro		unlockqueue(CurEnv);
333238032Speter
333390792Sgshapiro	finis(drop, false, EX_OK);
333490792Sgshapiro	/* NOTREACHED */
333538032Speter}
333690792Sgshapiro/*
333738032Speter**  DISCONNECT -- remove our connection with any foreground process
333838032Speter**
333938032Speter**	Parameters:
334038032Speter**		droplev -- how "deeply" we should drop the line.
334138032Speter**			0 -- ignore signals, mail back errors, make sure
334238032Speter**			     output goes to stdout.
334364562Sgshapiro**			1 -- also, make stdout go to /dev/null.
334438032Speter**			2 -- also, disconnect from controlling terminal
334538032Speter**			     (only for daemon mode).
334638032Speter**		e -- the current envelope.
334738032Speter**
334838032Speter**	Returns:
334938032Speter**		none
335038032Speter**
335138032Speter**	Side Effects:
335238032Speter**		Trys to insure that we are immune to vagaries of
335338032Speter**		the controlling tty.
335438032Speter*/
335538032Speter
335638032Spetervoid
335738032Speterdisconnect(droplev, e)
335838032Speter	int droplev;
335938032Speter	register ENVELOPE *e;
336038032Speter{
336138032Speter	int fd;
336238032Speter
336338032Speter	if (tTd(52, 1))
336490792Sgshapiro		sm_dprintf("disconnect: In %d Out %d, e=%p\n",
336590792Sgshapiro			   sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
3366363466Sgshapiro			   sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL),
3367363466Sgshapiro			   (void *)e);
336838032Speter	if (tTd(52, 100))
336938032Speter	{
337090792Sgshapiro		sm_dprintf("don't\n");
337138032Speter		return;
337238032Speter	}
337338032Speter	if (LogLevel > 93)
337438032Speter		sm_syslog(LOG_DEBUG, e->e_id,
337564562Sgshapiro			  "disconnect level %d",
337664562Sgshapiro			  droplev);
337738032Speter
337838032Speter	/* be sure we don't get nasty signals */
337990792Sgshapiro	(void) sm_signal(SIGINT, SIG_IGN);
338090792Sgshapiro	(void) sm_signal(SIGQUIT, SIG_IGN);
338138032Speter
338238032Speter	/* we can't communicate with our caller, so.... */
338390792Sgshapiro	HoldErrs = true;
338438032Speter	CurEnv->e_errormode = EM_MAIL;
338538032Speter	Verbose = 0;
338690792Sgshapiro	DisConnected = true;
338738032Speter
338838032Speter	/* all input from /dev/null */
338990792Sgshapiro	if (InChannel != smioin)
339038032Speter	{
339190792Sgshapiro		(void) sm_io_close(InChannel, SM_TIME_DEFAULT);
339290792Sgshapiro		InChannel = smioin;
339338032Speter	}
339490792Sgshapiro	if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
339590792Sgshapiro			 SM_IO_RDONLY, NULL, smioin) == NULL)
339638032Speter		sm_syslog(LOG_ERR, e->e_id,
339790792Sgshapiro			  "disconnect: sm_io_reopen(\"%s\") failed: %s",
339890792Sgshapiro			  SM_PATH_DEVNULL, sm_errstring(errno));
339938032Speter
340090792Sgshapiro	/*
340190792Sgshapiro	**  output to the transcript
340290792Sgshapiro	**	We also compare the fd numbers here since OutChannel
340390792Sgshapiro	**	might be a layer on top of smioout due to encryption
340490792Sgshapiro	**	(see sfsasl.c).
340590792Sgshapiro	*/
340690792Sgshapiro
340790792Sgshapiro	if (OutChannel != smioout &&
340890792Sgshapiro	    sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
340990792Sgshapiro	    sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
341038032Speter	{
341190792Sgshapiro		(void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
341290792Sgshapiro		OutChannel = smioout;
341390792Sgshapiro
341490792Sgshapiro#if 0
341590792Sgshapiro		/*
341690792Sgshapiro		**  Has smioout been closed? Reopen it.
341790792Sgshapiro		**	This shouldn't happen anymore, the code is here
341890792Sgshapiro		**	just as a reminder.
341990792Sgshapiro		*/
342090792Sgshapiro
342190792Sgshapiro		if (smioout->sm_magic == NULL &&
342290792Sgshapiro		    sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
342390792Sgshapiro				 SM_IO_WRONLY, NULL, smioout) == NULL)
342490792Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
342590792Sgshapiro				  "disconnect: sm_io_reopen(\"%s\") failed: %s",
342690792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
342790792Sgshapiro#endif /* 0 */
342838032Speter	}
342938032Speter	if (droplev > 0)
343038032Speter	{
343190792Sgshapiro		fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
343264562Sgshapiro		if (fd == -1)
3433159609Sgshapiro		{
343464562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
343590792Sgshapiro				  "disconnect: open(\"%s\") failed: %s",
343690792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
3437159609Sgshapiro		}
343890792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
3439159609Sgshapiro		if (fd >= 0)
3440159609Sgshapiro		{
3441159609Sgshapiro			(void) dup2(fd, STDOUT_FILENO);
3442159609Sgshapiro			(void) dup2(fd, STDERR_FILENO);
3443159609Sgshapiro			(void) close(fd);
3444159609Sgshapiro		}
344538032Speter	}
344638032Speter
344738032Speter	/* drop our controlling TTY completely if possible */
344838032Speter	if (droplev > 1)
344938032Speter	{
345038032Speter		(void) setsid();
345138032Speter		errno = 0;
345238032Speter	}
345338032Speter
345438032Speter#if XDEBUG
345538032Speter	checkfd012("disconnect");
3456363466Sgshapiro#endif
345738032Speter
345838032Speter	if (LogLevel > 71)
345990792Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
346090792Sgshapiro			  (int) CurrentPid);
346138032Speter
346238032Speter	errno = 0;
346338032Speter}
346438032Speter
346538032Speterstatic void
346638032Speterobsolete(argv)
346738032Speter	char *argv[];
346838032Speter{
346938032Speter	register char *ap;
347038032Speter	register char *op;
347138032Speter
347238032Speter	while ((ap = *++argv) != NULL)
347338032Speter	{
347438032Speter		/* Return if "--" or not an option of any form. */
347538032Speter		if (ap[0] != '-' || ap[1] == '-')
347638032Speter			return;
347738032Speter
347890792Sgshapiro		/* Don't allow users to use "-Q." or "-Q ." */
347990792Sgshapiro		if ((ap[1] == 'Q' && ap[2] == '.') ||
348090792Sgshapiro		    (ap[1] == 'Q' && argv[1] != NULL &&
348190792Sgshapiro		     argv[1][0] == '.' && argv[1][1] == '\0'))
348290792Sgshapiro		{
348390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
348490792Sgshapiro					     "Can not use -Q.\n");
348590792Sgshapiro			exit(EX_USAGE);
348690792Sgshapiro		}
348790792Sgshapiro
348838032Speter		/* skip over options that do have a value */
348938032Speter		op = strchr(OPTIONS, ap[1]);
349038032Speter		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
349138032Speter		    ap[1] != 'd' &&
349238032Speter#if defined(sony_news)
349338032Speter		    ap[1] != 'E' && ap[1] != 'J' &&
3494363466Sgshapiro#endif
349538032Speter		    argv[1] != NULL && argv[1][0] != '-')
349638032Speter		{
349738032Speter			argv++;
349838032Speter			continue;
349938032Speter		}
350038032Speter
350138032Speter		/* If -C doesn't have an argument, use sendmail.cf. */
350290792Sgshapiro#define __DEFPATH	"sendmail.cf"
350338032Speter		if (ap[1] == 'C' && ap[2] == '\0')
350438032Speter		{
350538032Speter			*argv = xalloc(sizeof(__DEFPATH) + 2);
350690792Sgshapiro			(void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
350790792Sgshapiro					   "-C", __DEFPATH);
350838032Speter		}
350938032Speter
351038032Speter		/* If -q doesn't have an argument, run it once. */
351138032Speter		if (ap[1] == 'q' && ap[2] == '\0')
351238032Speter			*argv = "-q0";
351338032Speter
351490792Sgshapiro		/* If -Q doesn't have an argument, disable quarantining */
351590792Sgshapiro		if (ap[1] == 'Q' && ap[2] == '\0')
351690792Sgshapiro			*argv = "-Q.";
351790792Sgshapiro
351838032Speter		/* if -d doesn't have an argument, use 0-99.1 */
351938032Speter		if (ap[1] == 'd' && ap[2] == '\0')
352038032Speter			*argv = "-d0-99.1";
352138032Speter
352264562Sgshapiro#if defined(sony_news)
352338032Speter		/* if -E doesn't have an argument, use -EC */
352438032Speter		if (ap[1] == 'E' && ap[2] == '\0')
352538032Speter			*argv = "-EC";
352638032Speter
352738032Speter		/* if -J doesn't have an argument, use -JJ */
352838032Speter		if (ap[1] == 'J' && ap[2] == '\0')
352938032Speter			*argv = "-JJ";
353064562Sgshapiro#endif /* defined(sony_news) */
353138032Speter	}
353238032Speter}
353390792Sgshapiro/*
353438032Speter**  AUTH_WARNING -- specify authorization warning
353538032Speter**
353638032Speter**	Parameters:
353738032Speter**		e -- the current envelope.
353838032Speter**		msg -- the text of the message.
353938032Speter**		args -- arguments to the message.
354038032Speter**
354138032Speter**	Returns:
354238032Speter**		none.
354338032Speter*/
354438032Speter
354538032Spetervoid
354638032Speter#ifdef __STDC__
354738032Speterauth_warning(register ENVELOPE *e, const char *msg, ...)
354864562Sgshapiro#else /* __STDC__ */
354938032Speterauth_warning(e, msg, va_alist)
355038032Speter	register ENVELOPE *e;
355138032Speter	const char *msg;
355238032Speter	va_dcl
355364562Sgshapiro#endif /* __STDC__ */
355438032Speter{
355538032Speter	char buf[MAXLINE];
355690792Sgshapiro	SM_VA_LOCAL_DECL
355738032Speter
355838032Speter	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
355938032Speter	{
356038032Speter		register char *p;
356138032Speter		static char hostbuf[48];
356238032Speter
356338032Speter		if (hostbuf[0] == '\0')
356471345Sgshapiro		{
356571345Sgshapiro			struct hostent *hp;
356638032Speter
3567168515Sgshapiro			hp = myhostname(hostbuf, sizeof(hostbuf));
356890792Sgshapiro#if NETINET6
356971345Sgshapiro			if (hp != NULL)
357071345Sgshapiro			{
357171345Sgshapiro				freehostent(hp);
357271345Sgshapiro				hp = NULL;
357371345Sgshapiro			}
357490792Sgshapiro#endif /* NETINET6 */
357571345Sgshapiro		}
357671345Sgshapiro
3577168515Sgshapiro		(void) sm_strlcpyn(buf, sizeof(buf), 2, hostbuf, ": ");
357838032Speter		p = &buf[strlen(buf)];
357990792Sgshapiro		SM_VA_START(ap, msg);
358090792Sgshapiro		(void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
358190792Sgshapiro		SM_VA_END(ap);
3582168515Sgshapiro		addheader("X-Authentication-Warning", buf, 0, e, true);
358338032Speter		if (LogLevel > 3)
358438032Speter			sm_syslog(LOG_INFO, e->e_id,
358564562Sgshapiro				  "Authentication-Warning: %.400s",
358664562Sgshapiro				  buf);
358738032Speter	}
358838032Speter}
358990792Sgshapiro/*
359038032Speter**  GETEXTENV -- get from external environment
359138032Speter**
359238032Speter**	Parameters:
359338032Speter**		envar -- the name of the variable to retrieve
359438032Speter**
359538032Speter**	Returns:
359638032Speter**		The value, if any.
359738032Speter*/
359838032Speter
359990792Sgshapirostatic char *
360038032Spetergetextenv(envar)
360138032Speter	const char *envar;
360238032Speter{
360338032Speter	char **envp;
360438032Speter	int l;
360538032Speter
360638032Speter	l = strlen(envar);
3607102528Sgshapiro	for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
360838032Speter	{
360938032Speter		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
361038032Speter			return &(*envp)[l + 1];
361138032Speter	}
361238032Speter	return NULL;
361338032Speter}
361490792Sgshapiro/*
3615157001Sgshapiro**  SM_SETUSERENV -- set an environment variable in the propagated environment
361638032Speter**
361738032Speter**	Parameters:
361838032Speter**		envar -- the name of the environment variable.
361938032Speter**		value -- the value to which it should be set.  If
362038032Speter**			null, this is extracted from the incoming
362138032Speter**			environment.  If that is not set, the call
3622157001Sgshapiro**			to sm_setuserenv is ignored.
362338032Speter**
362438032Speter**	Returns:
362538032Speter**		none.
362638032Speter*/
362738032Speter
362838032Spetervoid
3629157001Sgshapirosm_setuserenv(envar, value)
363038032Speter	const char *envar;
363138032Speter	const char *value;
363238032Speter{
363364562Sgshapiro	int i, l;
363438032Speter	char **evp = UserEnviron;
363538032Speter	char *p;
363638032Speter
363738032Speter	if (value == NULL)
363838032Speter	{
363938032Speter		value = getextenv(envar);
364038032Speter		if (value == NULL)
364138032Speter			return;
364238032Speter	}
364338032Speter
364490792Sgshapiro	/* XXX enforce reasonable size? */
364564562Sgshapiro	i = strlen(envar) + 1;
364664562Sgshapiro	l = strlen(value) + i + 1;
364764562Sgshapiro	p = (char *) xalloc(l);
364890792Sgshapiro	(void) sm_strlcpyn(p, l, 3, envar, "=", value);
364938032Speter
365038032Speter	while (*evp != NULL && strncmp(*evp, p, i) != 0)
365138032Speter		evp++;
365238032Speter	if (*evp != NULL)
365338032Speter	{
365438032Speter		*evp++ = p;
365538032Speter	}
365638032Speter	else if (evp < &UserEnviron[MAXUSERENVIRON])
365738032Speter	{
365838032Speter		*evp++ = p;
365938032Speter		*evp = NULL;
366038032Speter	}
366138032Speter
366238032Speter	/* make sure it is in our environment as well */
366338032Speter	if (putenv(p) < 0)
3664157001Sgshapiro		syserr("sm_setuserenv: putenv(%s) failed", p);
366538032Speter}
366690792Sgshapiro/*
366738032Speter**  DUMPSTATE -- dump state
366838032Speter**
366938032Speter**	For debugging.
367038032Speter*/
367138032Speter
367238032Spetervoid
367338032Speterdumpstate(when)
367438032Speter	char *when;
367538032Speter{
367638032Speter	register char *j = macvalue('j', CurEnv);
367738032Speter	int rs;
367864562Sgshapiro	extern int NextMacroId;
367938032Speter
368038032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id,
368164562Sgshapiro		  "--- dumping state on %s: $j = %s ---",
368264562Sgshapiro		  when,
368364562Sgshapiro		  j == NULL ? "<NULL>" : j);
368438032Speter	if (j != NULL)
368538032Speter	{
368638032Speter		if (!wordinclass(j, 'w'))
368738032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
368864562Sgshapiro				  "*** $j not in $=w ***");
368938032Speter	}
369038032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
369190792Sgshapiro	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
369264562Sgshapiro		  NextMacroId, MAXMACROID);
369338032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
369490792Sgshapiro	printopenfds(true);
369538032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
3696132943Sgshapiro	mci_dump_all(smioout, true);
369738032Speter	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
369838032Speter	if (rs > 0)
369938032Speter	{
370064562Sgshapiro		int status;
370138032Speter		register char **pvp;
370238032Speter		char *pv[MAXATOM + 1];
370338032Speter
370438032Speter		pv[0] = NULL;
370590792Sgshapiro		status = REWRITE(pv, rs, CurEnv);
370638032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
370764562Sgshapiro			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
370864562Sgshapiro			  status);
370938032Speter		for (pvp = pv; *pvp != NULL; pvp++)
371038032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
371138032Speter	}
371238032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
371338032Speter}
371490792Sgshapiro
371580785Sgshapiro#ifdef SIGUSR1
371690792Sgshapiro/*
371777349Sgshapiro**  SIGUSR1 -- Signal a request to dump state.
371877349Sgshapiro**
371977349Sgshapiro**	Parameters:
372077349Sgshapiro**		sig -- calling signal.
372177349Sgshapiro**
372277349Sgshapiro**	Returns:
372377349Sgshapiro**		none.
372477349Sgshapiro**
372577349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
372677349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
372777349Sgshapiro**		DOING.
372877349Sgshapiro**
372977349Sgshapiro**		XXX: More work is needed for this signal handler.
373077349Sgshapiro*/
373138032Speter
373238032Speter/* ARGSUSED */
373377349Sgshapirostatic SIGFUNC_DECL
373438032Spetersigusr1(sig)
373538032Speter	int sig;
373638032Speter{
373777349Sgshapiro	int save_errno = errno;
373877349Sgshapiro
373977349Sgshapiro	FIX_SYSV_SIGNAL(sig, sigusr1);
374077349Sgshapiro	errno = save_errno;
374177349Sgshapiro	CHECK_CRITICAL(sig);
374238032Speter	dumpstate("user signal");
374390792Sgshapiro# if SM_HEAP_CHECK
374490792Sgshapiro	dumpstab();
3745363466Sgshapiro# endif
374677349Sgshapiro	errno = save_errno;
374738032Speter	return SIGFUNC_RETURN;
374838032Speter}
374990792Sgshapiro#endif /* SIGUSR1 */
375090792Sgshapiro
375190792Sgshapiro/*
375238032Speter**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
375338032Speter**
375438032Speter**	Parameters:
375538032Speter**		to_real_uid -- if set, drop to the real uid instead
375638032Speter**			of the RunAsUser.
375738032Speter**
375838032Speter**	Returns:
375938032Speter**		EX_OSERR if the setuid failed.
376038032Speter**		EX_OK otherwise.
376138032Speter*/
376238032Speter
376338032Speterint
376438032Speterdrop_privileges(to_real_uid)
376538032Speter	bool to_real_uid;
376638032Speter{
376738032Speter	int rval = EX_OK;
376838032Speter	GIDSET_T emptygidset[1];
376938032Speter
377038032Speter	if (tTd(47, 1))
3771285229Sgshapiro		sm_dprintf("drop_privileges(%d): Real[UG]id=%ld:%ld, get[ug]id=%ld:%ld, gete[ug]id=%ld:%ld, RunAs[UG]id=%ld:%ld\n",
377290792Sgshapiro			   (int) to_real_uid,
3773285229Sgshapiro			   (long) RealUid, (long) RealGid,
3774285229Sgshapiro			   (long) getuid(), (long) getgid(),
3775285229Sgshapiro			   (long) geteuid(), (long) getegid(),
3776285229Sgshapiro			   (long) RunAsUid, (long) RunAsGid);
377738032Speter
377838032Speter	if (to_real_uid)
377938032Speter	{
378038032Speter		RunAsUserName = RealUserName;
378138032Speter		RunAsUid = RealUid;
378238032Speter		RunAsGid = RealGid;
378394334Sgshapiro		EffGid = RunAsGid;
378438032Speter	}
378538032Speter
378638032Speter	/* make sure no one can grab open descriptors for secret files */
378738032Speter	endpwent();
378890792Sgshapiro	sm_mbdb_terminate();
378938032Speter
379038032Speter	/* reset group permissions; these can be set later */
379138032Speter	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
379290792Sgshapiro
379390792Sgshapiro	/*
379490792Sgshapiro	**  Notice:  on some OS (Linux...) the setgroups() call causes
379590792Sgshapiro	**	a logfile entry if sendmail is not run by root.
379690792Sgshapiro	**	However, it is unclear (no POSIX standard) whether
379790792Sgshapiro	**	setgroups() can only succeed if executed by root.
379890792Sgshapiro	**	So for now we keep it as it is; if you want to change it, use
379990792Sgshapiro	**  if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
380090792Sgshapiro	*/
380190792Sgshapiro
380238032Speter	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
380364562Sgshapiro	{
380464562Sgshapiro		syserr("drop_privileges: setgroups(1, %d) failed",
380590792Sgshapiro		       (int) emptygidset[0]);
380638032Speter		rval = EX_OSERR;
380764562Sgshapiro	}
380838032Speter
380990792Sgshapiro	/* reset primary group id */
381090792Sgshapiro	if (to_real_uid)
381164562Sgshapiro	{
381290792Sgshapiro		/*
381390792Sgshapiro		**  Drop gid to real gid.
381490792Sgshapiro		**  On some OS we must reset the effective[/real[/saved]] gid,
381590792Sgshapiro		**  and then use setgid() to finally drop all group privileges.
381690792Sgshapiro		**  Later on we check whether we can get back the
381790792Sgshapiro		**  effective gid.
381890792Sgshapiro		*/
381990792Sgshapiro
382090792Sgshapiro#if HASSETEGID
382190792Sgshapiro		if (setegid(RunAsGid) < 0)
382290792Sgshapiro		{
382390792Sgshapiro			syserr("drop_privileges: setegid(%d) failed",
382490792Sgshapiro			       (int) RunAsGid);
382590792Sgshapiro			rval = EX_OSERR;
382690792Sgshapiro		}
382790792Sgshapiro#else /* HASSETEGID */
382890792Sgshapiro# if HASSETREGID
382990792Sgshapiro		if (setregid(RunAsGid, RunAsGid) < 0)
383090792Sgshapiro		{
383190792Sgshapiro			syserr("drop_privileges: setregid(%d, %d) failed",
383290792Sgshapiro			       (int) RunAsGid, (int) RunAsGid);
383390792Sgshapiro			rval = EX_OSERR;
383490792Sgshapiro		}
383590792Sgshapiro# else /* HASSETREGID */
383690792Sgshapiro#  if HASSETRESGID
383790792Sgshapiro		if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
383890792Sgshapiro		{
383990792Sgshapiro			syserr("drop_privileges: setresgid(%d, %d, %d) failed",
384090792Sgshapiro			       (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
384190792Sgshapiro			rval = EX_OSERR;
384290792Sgshapiro		}
384390792Sgshapiro#  endif /* HASSETRESGID */
384490792Sgshapiro# endif /* HASSETREGID */
384590792Sgshapiro#endif /* HASSETEGID */
384664562Sgshapiro	}
384790792Sgshapiro	if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
384890792Sgshapiro	{
384990792Sgshapiro		if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
385090792Sgshapiro		{
3851285229Sgshapiro			syserr("drop_privileges: setgid(%ld) failed",
3852285229Sgshapiro			       (long) RunAsGid);
385390792Sgshapiro			rval = EX_OSERR;
385490792Sgshapiro		}
385590792Sgshapiro		errno = 0;
385690792Sgshapiro		if (rval == EX_OK && getegid() != RunAsGid)
385790792Sgshapiro		{
3858285229Sgshapiro			syserr("drop_privileges: Unable to set effective gid=%ld to RunAsGid=%ld",
3859285229Sgshapiro			       (long) getegid(), (long) RunAsGid);
386090792Sgshapiro			rval = EX_OSERR;
386190792Sgshapiro		}
386290792Sgshapiro	}
386390792Sgshapiro
386490792Sgshapiro	/* fiddle with uid */
386564562Sgshapiro	if (to_real_uid || RunAsUid != 0)
386664562Sgshapiro	{
386794334Sgshapiro		uid_t euid;
386864562Sgshapiro
386990792Sgshapiro		/*
387090792Sgshapiro		**  Try to setuid(RunAsUid).
387190792Sgshapiro		**  euid must be RunAsUid,
387294334Sgshapiro		**  ruid must be RunAsUid unless (e|r)uid wasn't 0
387394334Sgshapiro		**	and we didn't have to drop privileges to the real uid.
387490792Sgshapiro		*/
387590792Sgshapiro
387690792Sgshapiro		if (setuid(RunAsUid) < 0 ||
387794334Sgshapiro		    geteuid() != RunAsUid ||
387894334Sgshapiro		    (getuid() != RunAsUid &&
387994334Sgshapiro		     (to_real_uid || geteuid() == 0 || getuid() == 0)))
388064562Sgshapiro		{
388190792Sgshapiro#if HASSETREUID
388290792Sgshapiro			/*
388390792Sgshapiro			**  if ruid != RunAsUid, euid == RunAsUid, then
388490792Sgshapiro			**  try resetting just the real uid, then using
388590792Sgshapiro			**  setuid() to drop the saved-uid as well.
388690792Sgshapiro			*/
388790792Sgshapiro
388894334Sgshapiro			if (geteuid() == RunAsUid)
388990792Sgshapiro			{
389090792Sgshapiro				if (setreuid(RunAsUid, -1) < 0)
389190792Sgshapiro				{
389290792Sgshapiro					syserr("drop_privileges: setreuid(%d, -1) failed",
389390792Sgshapiro					       (int) RunAsUid);
389490792Sgshapiro					rval = EX_OSERR;
389590792Sgshapiro				}
389690792Sgshapiro				if (setuid(RunAsUid) < 0)
389790792Sgshapiro				{
389890792Sgshapiro					syserr("drop_privileges: second setuid(%d) attempt failed",
389990792Sgshapiro					       (int) RunAsUid);
390090792Sgshapiro					rval = EX_OSERR;
390190792Sgshapiro				}
390290792Sgshapiro			}
390390792Sgshapiro			else
390490792Sgshapiro#endif /* HASSETREUID */
390590792Sgshapiro			{
390690792Sgshapiro				syserr("drop_privileges: setuid(%d) failed",
390790792Sgshapiro				       (int) RunAsUid);
390890792Sgshapiro				rval = EX_OSERR;
390990792Sgshapiro			}
391064562Sgshapiro		}
391194334Sgshapiro		euid = geteuid();
391290792Sgshapiro		if (RunAsUid != 0 && setuid(0) == 0)
391364562Sgshapiro		{
391464562Sgshapiro			/*
391564562Sgshapiro			**  Believe it or not, the Linux capability model
391664562Sgshapiro			**  allows a non-root process to override setuid()
391764562Sgshapiro			**  on a process running as root and prevent that
391864562Sgshapiro			**  process from dropping privileges.
391964562Sgshapiro			*/
392064562Sgshapiro
392164562Sgshapiro			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
392264562Sgshapiro			rval = EX_OSERR;
392364562Sgshapiro		}
392464562Sgshapiro		else if (RunAsUid != euid && setuid(euid) == 0)
392564562Sgshapiro		{
392664562Sgshapiro			/*
392764562Sgshapiro			**  Some operating systems will keep the saved-uid
392864562Sgshapiro			**  if a non-root effective-uid calls setuid(real-uid)
392964562Sgshapiro			**  making it possible to set it back again later.
393064562Sgshapiro			*/
393164562Sgshapiro
393290792Sgshapiro			syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
393364562Sgshapiro			rval = EX_OSERR;
393464562Sgshapiro		}
393564562Sgshapiro	}
393690792Sgshapiro
393790792Sgshapiro	if ((to_real_uid || RunAsGid != 0) &&
393890792Sgshapiro	    rval == EX_OK && RunAsGid != EffGid &&
393990792Sgshapiro	    getuid() != 0 && geteuid() != 0)
394090792Sgshapiro	{
394190792Sgshapiro		errno = 0;
394290792Sgshapiro		if (setgid(EffGid) == 0)
394390792Sgshapiro		{
394490792Sgshapiro			syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
394590792Sgshapiro			       (int) EffGid);
394690792Sgshapiro			rval = EX_OSERR;
394790792Sgshapiro		}
394890792Sgshapiro	}
394990792Sgshapiro
395038032Speter	if (tTd(47, 5))
395138032Speter	{
395290792Sgshapiro		sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
395390792Sgshapiro			   (int) geteuid(), (int) getuid(),
395490792Sgshapiro			   (int) getegid(), (int) getgid());
395590792Sgshapiro		sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
395690792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
395764562Sgshapiro		if (tTd(47, 10))
395890792Sgshapiro			sm_dprintf("drop_privileges: rval = %d\n", rval);
395938032Speter	}
396038032Speter	return rval;
396138032Speter}
396290792Sgshapiro/*
396338032Speter**  FILL_FD -- make sure a file descriptor has been properly allocated
396438032Speter**
396538032Speter**	Used to make sure that stdin/out/err are allocated on startup
396638032Speter**
396738032Speter**	Parameters:
396838032Speter**		fd -- the file descriptor to be filled.
396938032Speter**		where -- a string used for logging.  If NULL, this is
397038032Speter**			being called on startup, and logging should
397138032Speter**			not be done.
397238032Speter**
397338032Speter**	Returns:
397438032Speter**		none
397590792Sgshapiro**
397690792Sgshapiro**	Side Effects:
397790792Sgshapiro**		possibly changes MissingFds
397838032Speter*/
397938032Speter
398038032Spetervoid
398138032Speterfill_fd(fd, where)
398238032Speter	int fd;
398338032Speter	char *where;
398438032Speter{
398538032Speter	int i;
398638032Speter	struct stat stbuf;
398738032Speter
398838032Speter	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
398938032Speter		return;
399038032Speter
399138032Speter	if (where != NULL)
399238032Speter		syserr("fill_fd: %s: fd %d not open", where, fd);
399338032Speter	else
399438032Speter		MissingFds |= 1 << fd;
399590792Sgshapiro	i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
399638032Speter	if (i < 0)
399738032Speter	{
399890792Sgshapiro		syserr("!fill_fd: %s: cannot open %s",
399990792Sgshapiro		       where == NULL ? "startup" : where, SM_PATH_DEVNULL);
400038032Speter	}
400138032Speter	if (fd != i)
400238032Speter	{
400338032Speter		(void) dup2(i, fd);
400438032Speter		(void) close(i);
400538032Speter	}
400638032Speter}
400790792Sgshapiro/*
400890792Sgshapiro**  SM_PRINTOPTIONS -- print options
400990792Sgshapiro**
401090792Sgshapiro**	Parameters:
401190792Sgshapiro**		options -- array of options.
401290792Sgshapiro**
401390792Sgshapiro**	Returns:
401490792Sgshapiro**		none.
401590792Sgshapiro*/
401690792Sgshapiro
401790792Sgshapirostatic void
401890792Sgshapirosm_printoptions(options)
401990792Sgshapiro	char **options;
402090792Sgshapiro{
402190792Sgshapiro	int ll;
402290792Sgshapiro	char **av;
402390792Sgshapiro
402490792Sgshapiro	av = options;
402590792Sgshapiro	ll = 7;
402690792Sgshapiro	while (*av != NULL)
402790792Sgshapiro	{
402890792Sgshapiro		if (ll + strlen(*av) > 63)
402990792Sgshapiro		{
403090792Sgshapiro			sm_dprintf("\n");
403190792Sgshapiro			ll = 0;
403290792Sgshapiro		}
403390792Sgshapiro		if (ll == 0)
403490792Sgshapiro			sm_dprintf("\t\t");
403590792Sgshapiro		else
403690792Sgshapiro			sm_dprintf(" ");
403790792Sgshapiro		sm_dprintf("%s", *av);
403890792Sgshapiro		ll += strlen(*av++) + 1;
403990792Sgshapiro	}
404090792Sgshapiro	sm_dprintf("\n");
404190792Sgshapiro}
4042168515Sgshapiro
404390792Sgshapiro/*
4044168515Sgshapiro**  TO8BIT -- convert \octal sequences in a test mode input line
4045168515Sgshapiro**
4046168515Sgshapiro**	Parameters:
4047168515Sgshapiro**		str -- the input line.
4048168515Sgshapiro**
4049168515Sgshapiro**	Returns:
4050168515Sgshapiro**		none.
4051168515Sgshapiro**
4052168515Sgshapiro**	Side Effects:
4053168515Sgshapiro**		replaces \0octal in str with octal value.
4054168515Sgshapiro*/
4055168515Sgshapiro
4056168515Sgshapirostatic bool to8bit __P((char *));
4057168515Sgshapiro
4058168515Sgshapirostatic bool
4059168515Sgshapiroto8bit(str)
4060168515Sgshapiro	char *str;
4061168515Sgshapiro{
4062168515Sgshapiro	int c, len;
4063168515Sgshapiro	char *out, *in;
4064168515Sgshapiro	bool changed;
4065168515Sgshapiro
4066168515Sgshapiro	if (str == NULL)
4067168515Sgshapiro		return false;
4068168515Sgshapiro	in = out = str;
4069168515Sgshapiro	changed = false;
4070168515Sgshapiro	len = 0;
4071168515Sgshapiro	while ((c = (*str++ & 0377)) != '\0')
4072168515Sgshapiro	{
4073168515Sgshapiro		int oct, nxtc;
4074168515Sgshapiro
4075168515Sgshapiro		++len;
4076168515Sgshapiro		if (c == '\\' &&
4077168515Sgshapiro		    (nxtc = (*str & 0377)) == '0')
4078168515Sgshapiro		{
4079168515Sgshapiro			oct = 0;
4080168515Sgshapiro			while ((nxtc = (*str & 0377)) != '\0' &&
4081168515Sgshapiro				isascii(nxtc) && isdigit(nxtc))
4082168515Sgshapiro			{
4083168515Sgshapiro				oct <<= 3;
4084168515Sgshapiro				oct += nxtc - '0';
4085168515Sgshapiro				++str;
4086168515Sgshapiro				++len;
4087168515Sgshapiro			}
4088168515Sgshapiro			changed = true;
4089168515Sgshapiro			c = oct;
4090168515Sgshapiro		}
4091168515Sgshapiro		*out++ = c;
4092168515Sgshapiro	}
4093168515Sgshapiro	*out++ = c;
4094168515Sgshapiro	if (changed)
4095168515Sgshapiro	{
4096168515Sgshapiro		char *q;
4097168515Sgshapiro
4098168515Sgshapiro		q = quote_internal_chars(in, in, &len);
4099168515Sgshapiro		if (q != in)
4100168515Sgshapiro			sm_strlcpy(in, q, len);
4101168515Sgshapiro	}
4102168515Sgshapiro	return changed;
4103168515Sgshapiro}
4104168515Sgshapiro
4105168515Sgshapiro/*
410638032Speter**  TESTMODELINE -- process a test mode input line
410738032Speter**
410838032Speter**	Parameters:
410938032Speter**		line -- the input line.
411038032Speter**		e -- the current environment.
411138032Speter**	Syntax:
411238032Speter**		#  a comment
411338032Speter**		.X process X as a configuration line
411438032Speter**		=X dump a configuration item (such as mailers)
411538032Speter**		$X dump a macro or class
411638032Speter**		/X try an activity
411738032Speter**		X  normal process through rule set X
411838032Speter*/
411938032Speter
412064562Sgshapirostatic void
412138032Spetertestmodeline(line, e)
412238032Speter	char *line;
412338032Speter	ENVELOPE *e;
412438032Speter{
412538032Speter	register char *p;
412638032Speter	char *q;
412738032Speter	auto char *delimptr;
412838032Speter	int mid;
412938032Speter	int i, rs;
413038032Speter	STAB *map;
413138032Speter	char **s;
413238032Speter	struct rewrite *rw;
413338032Speter	ADDRESS a;
4134168515Sgshapiro	char *lbp;
4135168515Sgshapiro	auto int lbs;
413638032Speter	static int tryflags = RF_COPYNONE;
413738032Speter	char exbuf[MAXLINE];
4138168515Sgshapiro	char lbuf[MAXLINE];
413990792Sgshapiro	extern unsigned char TokTypeNoC[];
4140168515Sgshapiro	bool eightbit;
414138032Speter
414266494Sgshapiro	/* skip leading spaces */
414366494Sgshapiro	while (*line == ' ')
414466494Sgshapiro		line++;
414566494Sgshapiro
4146168515Sgshapiro	lbp = NULL;
4147168515Sgshapiro	eightbit = false;
414838032Speter	switch (line[0])
414938032Speter	{
415038032Speter	  case '#':
415164562Sgshapiro	  case '\0':
415238032Speter		return;
415338032Speter
415438032Speter	  case '?':
415564562Sgshapiro		help("-bt", e);
415638032Speter		return;
415738032Speter
415838032Speter	  case '.':		/* config-style settings */
415938032Speter		switch (line[1])
416038032Speter		{
416138032Speter		  case 'D':
416290792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
416371345Sgshapiro			if (mid == 0)
416438032Speter				return;
4165168515Sgshapiro			lbs = sizeof(lbuf);
4166168515Sgshapiro			lbp = translate_dollars(delimptr, lbuf, &lbs);
4167168515Sgshapiro			macdefine(&e->e_macro, A_TEMP, mid, lbp);
4168168515Sgshapiro			if (lbp != lbuf)
4169168515Sgshapiro				SM_FREE(lbp);
417038032Speter			break;
417138032Speter
417238032Speter		  case 'C':
417338032Speter			if (line[2] == '\0')	/* not to call syserr() */
417438032Speter				return;
417538032Speter
417690792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
417771345Sgshapiro			if (mid == 0)
417838032Speter				return;
4179168515Sgshapiro			lbs = sizeof(lbuf);
4180168515Sgshapiro			lbp = translate_dollars(delimptr, lbuf, &lbs);
4181168515Sgshapiro			expand(lbp, exbuf, sizeof(exbuf), e);
4182168515Sgshapiro			if (lbp != lbuf)
4183168515Sgshapiro				SM_FREE(lbp);
418438032Speter			p = exbuf;
418538032Speter			while (*p != '\0')
418638032Speter			{
418738032Speter				register char *wd;
418838032Speter				char delim;
418938032Speter
4190363466Sgshapiro				while (*p != '\0' && SM_ISSPACE(*p))
419138032Speter					p++;
419238032Speter				wd = p;
4193363466Sgshapiro				while (*p != '\0' && !(SM_ISSPACE(*p)))
419438032Speter					p++;
419538032Speter				delim = *p;
419638032Speter				*p = '\0';
419738032Speter				if (wd[0] != '\0')
419838032Speter					setclass(mid, wd);
419938032Speter				*p = delim;
420038032Speter			}
420138032Speter			break;
420238032Speter
420338032Speter		  case '\0':
420490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
420590792Sgshapiro					     "Usage: .[DC]macro value(s)\n");
420638032Speter			break;
420738032Speter
420838032Speter		  default:
420990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
421090792Sgshapiro					     "Unknown \".\" command %s\n", line);
421138032Speter			break;
421238032Speter		}
421338032Speter		return;
421438032Speter
421538032Speter	  case '=':		/* config-style settings */
421638032Speter		switch (line[1])
421738032Speter		{
421838032Speter		  case 'S':		/* dump rule set */
421938032Speter			rs = strtorwset(&line[2], NULL, ST_FIND);
422038032Speter			if (rs < 0)
422138032Speter			{
422290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
422390792Sgshapiro						     "Undefined ruleset %s\n", &line[2]);
422438032Speter				return;
422538032Speter			}
422638032Speter			rw = RewriteRules[rs];
422738032Speter			if (rw == NULL)
422838032Speter				return;
422938032Speter			do
423038032Speter			{
423190792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
423290792Sgshapiro						  'R');
423338032Speter				s = rw->r_lhs;
423438032Speter				while (*s != NULL)
423538032Speter				{
4236132943Sgshapiro					xputs(smioout, *s++);
423790792Sgshapiro					(void) sm_io_putc(smioout,
423890792Sgshapiro							  SM_TIME_DEFAULT, ' ');
423938032Speter				}
424090792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
424190792Sgshapiro						  '\t');
424290792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
424390792Sgshapiro						  '\t');
424438032Speter				s = rw->r_rhs;
424538032Speter				while (*s != NULL)
424638032Speter				{
4247132943Sgshapiro					xputs(smioout, *s++);
424890792Sgshapiro					(void) sm_io_putc(smioout,
424990792Sgshapiro							  SM_TIME_DEFAULT, ' ');
425038032Speter				}
425190792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
425290792Sgshapiro						  '\n');
425338032Speter			} while ((rw = rw->r_next) != NULL);
425438032Speter			break;
425538032Speter
425638032Speter		  case 'M':
425738032Speter			for (i = 0; i < MAXMAILERS; i++)
425838032Speter			{
425938032Speter				if (Mailer[i] != NULL)
4260132943Sgshapiro					printmailer(smioout, Mailer[i]);
426138032Speter			}
426238032Speter			break;
426338032Speter
426438032Speter		  case '\0':
426590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
426690792Sgshapiro					     "Usage: =Sruleset or =M\n");
426738032Speter			break;
426838032Speter
426938032Speter		  default:
427090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
427190792Sgshapiro					     "Unknown \"=\" command %s\n", line);
427238032Speter			break;
427338032Speter		}
427438032Speter		return;
427538032Speter
427638032Speter	  case '-':		/* set command-line-like opts */
427738032Speter		switch (line[1])
427838032Speter		{
427938032Speter		  case 'd':
428038032Speter			tTflag(&line[2]);
428138032Speter			break;
428238032Speter
428338032Speter		  case '\0':
428490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
428590792Sgshapiro					     "Usage: -d{debug arguments}\n");
428638032Speter			break;
428738032Speter
428838032Speter		  default:
428990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
429090792Sgshapiro					     "Unknown \"-\" command %s\n", line);
429138032Speter			break;
429238032Speter		}
429338032Speter		return;
429438032Speter
429538032Speter	  case '$':
429638032Speter		if (line[1] == '=')
429738032Speter		{
429890792Sgshapiro			mid = macid(&line[2]);
429971345Sgshapiro			if (mid != 0)
430038032Speter				stabapply(dump_class, mid);
430138032Speter			return;
430238032Speter		}
430390792Sgshapiro		mid = macid(&line[1]);
430471345Sgshapiro		if (mid == 0)
430538032Speter			return;
430638032Speter		p = macvalue(mid, e);
430738032Speter		if (p == NULL)
430890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
430990792Sgshapiro					     "Undefined\n");
431038032Speter		else
431138032Speter		{
4312132943Sgshapiro			xputs(smioout, p);
431390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
431490792Sgshapiro					     "\n");
431538032Speter		}
431638032Speter		return;
431738032Speter
431838032Speter	  case '/':		/* miscellaneous commands */
431938032Speter		p = &line[strlen(line)];
4320363466Sgshapiro		while (--p >= line && SM_ISSPACE(*p))
432138032Speter			*p = '\0';
432238032Speter		p = strpbrk(line, " \t");
432338032Speter		if (p != NULL)
432438032Speter		{
4325363466Sgshapiro			while (SM_ISSPACE(*p))
432638032Speter				*p++ = '\0';
432738032Speter		}
432838032Speter		else
432938032Speter			p = "";
433038032Speter		if (line[1] == '\0')
433138032Speter		{
433290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
433390792Sgshapiro					     "Usage: /[canon|map|mx|parse|try|tryflags]\n");
433438032Speter			return;
433538032Speter		}
433690792Sgshapiro		if (sm_strcasecmp(&line[1], "quit") == 0)
433764562Sgshapiro		{
433864562Sgshapiro			CurEnv->e_id = NULL;
433990792Sgshapiro			finis(true, true, ExitStat);
434090792Sgshapiro			/* NOTREACHED */
434164562Sgshapiro		}
434290792Sgshapiro		if (sm_strcasecmp(&line[1], "mx") == 0)
434338032Speter		{
434438032Speter#if NAMED_BIND
434538032Speter			/* look up MX records */
434638032Speter			int nmx;
434738032Speter			auto int rcode;
434838032Speter			char *mxhosts[MAXMXHOSTS + 1];
434938032Speter
435038032Speter			if (*p == '\0')
435138032Speter			{
435290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
435390792Sgshapiro						     "Usage: /mx address\n");
435438032Speter				return;
435538032Speter			}
4356363466Sgshapiro			nmx = getmxrr(p, mxhosts, NULL, TRYFALLBACK, &rcode,
4357363466Sgshapiro				      NULL, -1);
4358363466Sgshapiro			if (nmx == NULLMX)
4359363466Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4360363466Sgshapiro						     "getmxrr(%s) returns null MX (See RFC7505)\n",
4361363466Sgshapiro						     p);
4362363466Sgshapiro			else
4363363466Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4364363466Sgshapiro						     "getmxrr(%s) returns %d value(s):\n",
4365363466Sgshapiro						     p, nmx);
436638032Speter			for (i = 0; i < nmx; i++)
436790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
436890792Sgshapiro						     "\t%s\n", mxhosts[i]);
436964562Sgshapiro#else /* NAMED_BIND */
437090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
437190792Sgshapiro					     "No MX code compiled in\n");
437264562Sgshapiro#endif /* NAMED_BIND */
437338032Speter		}
437490792Sgshapiro		else if (sm_strcasecmp(&line[1], "canon") == 0)
437538032Speter		{
437638032Speter			char host[MAXHOSTNAMELEN];
437738032Speter
437838032Speter			if (*p == '\0')
437938032Speter			{
438090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
438190792Sgshapiro						     "Usage: /canon address\n");
438238032Speter				return;
438338032Speter			}
4384168515Sgshapiro			else if (sm_strlcpy(host, p, sizeof(host)) >= sizeof(host))
438538032Speter			{
438690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
438790792Sgshapiro						     "Name too long\n");
438838032Speter				return;
438938032Speter			}
4390168515Sgshapiro			(void) getcanonname(host, sizeof(host), !HasWildcardMX,
439190792Sgshapiro					    NULL);
439290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
439390792Sgshapiro					     "getcanonname(%s) returns %s\n",
439490792Sgshapiro					     p, host);
439538032Speter		}
439690792Sgshapiro		else if (sm_strcasecmp(&line[1], "map") == 0)
439738032Speter		{
439838032Speter			auto int rcode = EX_OK;
439938032Speter			char *av[2];
440038032Speter
440138032Speter			if (*p == '\0')
440238032Speter			{
440390792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
440490792Sgshapiro						     "Usage: /map mapname key\n");
440538032Speter				return;
440638032Speter			}
4407363466Sgshapiro			for (q = p; *q != '\0' && !(SM_ISSPACE(*q)); q++)
440838032Speter				continue;
440938032Speter			if (*q == '\0')
441038032Speter			{
441190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
441290792Sgshapiro						     "No key specified\n");
441338032Speter				return;
441438032Speter			}
441538032Speter			*q++ = '\0';
441638032Speter			map = stab(p, ST_MAP, ST_FIND);
441738032Speter			if (map == NULL)
441838032Speter			{
441990792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
442090792Sgshapiro						     "Map named \"%s\" not found\n", p);
442138032Speter				return;
442238032Speter			}
442364562Sgshapiro			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
442464562Sgshapiro			    !openmap(&(map->s_map)))
442538032Speter			{
442690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
442790792Sgshapiro						     "Map named \"%s\" not open\n", p);
442838032Speter				return;
442938032Speter			}
443090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
443190792Sgshapiro					     "map_lookup: %s (%s) ", p, q);
443238032Speter			av[0] = q;
443338032Speter			av[1] = NULL;
443438032Speter			p = (*map->s_map.map_class->map_lookup)
443538032Speter					(&map->s_map, q, av, &rcode);
443638032Speter			if (p == NULL)
443790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
443890792Sgshapiro						     "no match (%d)\n",
443990792Sgshapiro						     rcode);
444038032Speter			else
444190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
444290792Sgshapiro						     "returns %s (%d)\n", p,
444390792Sgshapiro						     rcode);
444438032Speter		}
444590792Sgshapiro		else if (sm_strcasecmp(&line[1], "try") == 0)
444638032Speter		{
444738032Speter			MAILER *m;
444864562Sgshapiro			STAB *st;
444938032Speter			auto int rcode = EX_OK;
445038032Speter
445138032Speter			q = strpbrk(p, " \t");
445238032Speter			if (q != NULL)
445338032Speter			{
4454363466Sgshapiro				while (SM_ISSPACE(*q))
445538032Speter					*q++ = '\0';
445638032Speter			}
445738032Speter			if (q == NULL || *q == '\0')
445838032Speter			{
445990792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
446090792Sgshapiro						     "Usage: /try mailer address\n");
446138032Speter				return;
446238032Speter			}
446364562Sgshapiro			st = stab(p, ST_MAILER, ST_FIND);
446464562Sgshapiro			if (st == NULL)
446538032Speter			{
446690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
446790792Sgshapiro						     "Unknown mailer %s\n", p);
446838032Speter				return;
446938032Speter			}
447064562Sgshapiro			m = st->s_mailer;
447190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
447290792Sgshapiro					     "Trying %s %s address %s for mailer %s\n",
447390792Sgshapiro				     bitset(RF_HEADERADDR, tryflags) ? "header"
447490792Sgshapiro							: "envelope",
447590792Sgshapiro				     bitset(RF_SENDERADDR, tryflags) ? "sender"
447690792Sgshapiro							: "recipient", q, p);
447738032Speter			p = remotename(q, m, tryflags, &rcode, CurEnv);
447890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
447990792Sgshapiro					     "Rcode = %d, addr = %s\n",
448090792Sgshapiro					     rcode, p == NULL ? "<NULL>" : p);
448138032Speter			e->e_to = NULL;
448238032Speter		}
448390792Sgshapiro		else if (sm_strcasecmp(&line[1], "tryflags") == 0)
448438032Speter		{
448538032Speter			if (*p == '\0')
448638032Speter			{
448790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
448890792Sgshapiro						     "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
448938032Speter				return;
449038032Speter			}
449138032Speter			for (; *p != '\0'; p++)
449238032Speter			{
449338032Speter				switch (*p)
449438032Speter				{
449538032Speter				  case 'H':
449638032Speter				  case 'h':
449738032Speter					tryflags |= RF_HEADERADDR;
449838032Speter					break;
449938032Speter
450038032Speter				  case 'E':
450138032Speter				  case 'e':
450238032Speter					tryflags &= ~RF_HEADERADDR;
450338032Speter					break;
450438032Speter
450538032Speter				  case 'S':
450638032Speter				  case 's':
450738032Speter					tryflags |= RF_SENDERADDR;
450838032Speter					break;
450938032Speter
451038032Speter				  case 'R':
451138032Speter				  case 'r':
451238032Speter					tryflags &= ~RF_SENDERADDR;
451338032Speter					break;
451438032Speter				}
451538032Speter			}
451664562Sgshapiro			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
451764562Sgshapiro			exbuf[1] = ' ';
451864562Sgshapiro			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
451964562Sgshapiro			exbuf[3] = '\0';
452090792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
452190792Sgshapiro				macid("{addr_type}"), exbuf);
452238032Speter		}
452390792Sgshapiro		else if (sm_strcasecmp(&line[1], "parse") == 0)
452438032Speter		{
452538032Speter			if (*p == '\0')
452638032Speter			{
452790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
452890792Sgshapiro						     "Usage: /parse address\n");
452938032Speter				return;
453038032Speter			}
4531111823Sgshapiro			q = crackaddr(p, e);
453290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
453390792Sgshapiro					     "Cracked address = ");
4534132943Sgshapiro			xputs(smioout, q);
453590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
453690792Sgshapiro					     "\nParsing %s %s address\n",
453790792Sgshapiro					     bitset(RF_HEADERADDR, tryflags) ?
453890792Sgshapiro							"header" : "envelope",
453990792Sgshapiro					     bitset(RF_SENDERADDR, tryflags) ?
454090792Sgshapiro							"sender" : "recipient");
454190792Sgshapiro			if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
454290792Sgshapiro			    == NULL)
454390792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
454490792Sgshapiro						     "Cannot parse\n");
454538032Speter			else if (a.q_host != NULL && a.q_host[0] != '\0')
454690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
454790792Sgshapiro						     "mailer %s, host %s, user %s\n",
454890792Sgshapiro						     a.q_mailer->m_name,
454990792Sgshapiro						     a.q_host,
455090792Sgshapiro						     a.q_user);
455138032Speter			else
455290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
455390792Sgshapiro						     "mailer %s, user %s\n",
455490792Sgshapiro						     a.q_mailer->m_name,
455590792Sgshapiro						     a.q_user);
455638032Speter			e->e_to = NULL;
455738032Speter		}
4558168515Sgshapiro		else if (sm_strcasecmp(&line[1], "header") == 0)
4559168515Sgshapiro		{
4560168515Sgshapiro			unsigned long ul;
4561168515Sgshapiro
4562168515Sgshapiro			ul = chompheader(p, CHHDR_CHECK|CHHDR_USER, NULL, e);
4563168515Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4564168515Sgshapiro					     "ul = %lu\n", ul);
4565168515Sgshapiro		}
4566261194Sgshapiro#if NETINET || NETINET6
4567261194Sgshapiro		else if (sm_strcasecmp(&line[1], "gethostbyname") == 0)
4568261194Sgshapiro		{
4569261194Sgshapiro			int family = AF_INET;
4570261194Sgshapiro
4571261194Sgshapiro			q = strpbrk(p, " \t");
4572261194Sgshapiro			if (q != NULL)
4573261194Sgshapiro			{
4574363466Sgshapiro				while (SM_ISSPACE(*q))
4575261194Sgshapiro					*q++ = '\0';
4576261194Sgshapiro# if NETINET6
4577261194Sgshapiro				if (*q != '\0' && (strcmp(q, "inet6") == 0 ||
4578261194Sgshapiro						   strcmp(q, "AAAA") == 0))
4579261194Sgshapiro					family = AF_INET6;
4580261194Sgshapiro# endif /* NETINET6 */
4581261194Sgshapiro			}
4582261194Sgshapiro			(void) sm_gethostbyname(p, family);
4583261194Sgshapiro		}
4584261194Sgshapiro#endif /* NETINET || NETINET6 */
4585363466Sgshapiro#if DANE
4586363466Sgshapiro		else if (sm_strcasecmp(&line[1], "dnslookup") == 0)
4587363466Sgshapiro		{
4588363466Sgshapiro			DNS_REPLY_T *r;
4589363466Sgshapiro			int rr_type, family;
4590363466Sgshapiro			unsigned int flags;
4591363466Sgshapiro
4592363466Sgshapiro			rr_type = T_A;
4593363466Sgshapiro			family = AF_INET;
4594363466Sgshapiro			flags = RR_AS_TEXT;
4595363466Sgshapiro			q = strpbrk(p, " \t");
4596363466Sgshapiro			if (q != NULL)
4597363466Sgshapiro			{
4598363466Sgshapiro				char *pflags;
4599363466Sgshapiro
4600363466Sgshapiro				while (SM_ISSPACE(*q))
4601363466Sgshapiro					*q++ = '\0';
4602363466Sgshapiro				pflags = strpbrk(q, " \t");
4603363466Sgshapiro				if (pflags != NULL)
4604363466Sgshapiro				{
4605363466Sgshapiro					while (SM_ISSPACE(*pflags))
4606363466Sgshapiro						*pflags++ = '\0';
4607363466Sgshapiro				}
4608363466Sgshapiro				rr_type = dns_string_to_type(q);
4609363466Sgshapiro				if (rr_type == T_A)
4610363466Sgshapiro					family = AF_INET;
4611363466Sgshapiro# if NETINET6
4612363466Sgshapiro				if (rr_type == T_AAAA)
4613363466Sgshapiro					family = AF_INET6;
4614363466Sgshapiro# endif
4615363466Sgshapiro				while (pflags != NULL && *pflags != '\0' &&
4616363466Sgshapiro					!SM_ISSPACE(*pflags))
4617363466Sgshapiro				{
4618363466Sgshapiro					if (*pflags == 'c')
4619363466Sgshapiro						flags |= RR_NO_CNAME;
4620363466Sgshapiro					else if (*pflags == 'o')
4621363466Sgshapiro						flags |= RR_ONLY_CNAME;
4622363466Sgshapiro					else if (*pflags == 'T')
4623363466Sgshapiro						flags &= ~RR_AS_TEXT;
4624363466Sgshapiro					++pflags;
4625363466Sgshapiro				}
4626363466Sgshapiro			}
4627363466Sgshapiro			r = dns_lookup_int(p, C_IN, rr_type,
4628363466Sgshapiro					0, 0, 0, flags, NULL, NULL);
4629363466Sgshapiro			if (r != NULL && family >= 0)
4630363466Sgshapiro			{
4631363466Sgshapiro				(void) dns2he(r, family);
4632363466Sgshapiro				dns_free_data(r);
4633363466Sgshapiro				r = NULL;
4634363466Sgshapiro			}
4635363466Sgshapiro		}
4636363466Sgshapiro#endif /* DANE */
463738032Speter		else
463838032Speter		{
463990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
464090792Sgshapiro					     "Unknown \"/\" command %s\n",
464190792Sgshapiro					     line);
464238032Speter		}
4643168515Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
464438032Speter		return;
464538032Speter	}
464638032Speter
4647363466Sgshapiro	for (p = line; SM_ISSPACE(*p); p++)
464838032Speter		continue;
464938032Speter	q = p;
4650363466Sgshapiro	while (*p != '\0' && !(SM_ISSPACE(*p)))
465138032Speter		p++;
465238032Speter	if (*p == '\0')
465338032Speter	{
465490792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
465590792Sgshapiro				     "No address!\n");
465638032Speter		return;
465738032Speter	}
465838032Speter	*p = '\0';
4659168515Sgshapiro	if (tTd(23, 101))
4660168515Sgshapiro		eightbit = to8bit(p + 1);
466190792Sgshapiro	if (invalidaddr(p + 1, NULL, true))
466238032Speter		return;
466338032Speter	do
466438032Speter	{
466538032Speter		register char **pvp;
466638032Speter		char pvpbuf[PSBUFSIZE];
466738032Speter
4668168515Sgshapiro		pvp = prescan(++p, ',', pvpbuf, sizeof(pvpbuf), &delimptr,
4669168515Sgshapiro			      ConfigLevel >= 9 ? TokTypeNoC : ExtTokenTab, false);
467038032Speter		if (pvp == NULL)
467138032Speter			continue;
467238032Speter		p = q;
467338032Speter		while (*p != '\0')
467438032Speter		{
467564562Sgshapiro			int status;
467638032Speter
467738032Speter			rs = strtorwset(p, NULL, ST_FIND);
467838032Speter			if (rs < 0)
467938032Speter			{
468090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
468190792Sgshapiro						     "Undefined ruleset %s\n",
468290792Sgshapiro						     p);
468338032Speter				break;
468438032Speter			}
468590792Sgshapiro			status = REWRITE(pvp, rs, e);
468664562Sgshapiro			if (status != EX_OK)
468790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
468890792Sgshapiro						     "== Ruleset %s (%d) status %d\n",
468990792Sgshapiro						     p, rs, status);
4690168515Sgshapiro			else if (eightbit)
4691168515Sgshapiro			{
4692168515Sgshapiro				cataddr(pvp, NULL, exbuf, sizeof(exbuf), '\0',
4693168515Sgshapiro					true);
4694168515Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4695168515Sgshapiro						     "cataddr: %s\n",
4696168515Sgshapiro						     str2prt(exbuf));
4697168515Sgshapiro			}
469838032Speter			while (*p != '\0' && *p++ != ',')
469938032Speter				continue;
470038032Speter		}
470138032Speter	} while (*(p = delimptr) != '\0');
4702168515Sgshapiro	(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
470338032Speter}
470438032Speter
470564562Sgshapirostatic void
470638032Speterdump_class(s, id)
470738032Speter	register STAB *s;
470838032Speter	int id;
470938032Speter{
471090792Sgshapiro	if (s->s_symtype != ST_CLASS)
471138032Speter		return;
471271345Sgshapiro	if (bitnset(bitidx(id), s->s_class))
471390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
471490792Sgshapiro				     "%s\n", s->s_name);
471538032Speter}
471690792Sgshapiro
471790792Sgshapiro/*
471890792Sgshapiro**  An exception type used to create QuickAbort exceptions.
471990792Sgshapiro**  This is my first cut at converting QuickAbort from longjmp to exceptions.
472090792Sgshapiro**  These exceptions have a single integer argument, which is the argument
472190792Sgshapiro**  to longjmp in the original code (either 1 or 2).  I don't know the
472290792Sgshapiro**  significance of 1 vs 2: the calls to setjmp don't care.
472390792Sgshapiro*/
472490792Sgshapiro
472590792Sgshapiroconst SM_EXC_TYPE_T EtypeQuickAbort =
472690792Sgshapiro{
472790792Sgshapiro	SmExcTypeMagic,
472890792Sgshapiro	"E:mta.quickabort",
472990792Sgshapiro	"i",
473090792Sgshapiro	sm_etype_printf,
473190792Sgshapiro	"quick abort %0",
473290792Sgshapiro};
4733