main.c revision 102528
138032Speter/*
294334Sgshapiro * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1490792Sgshapiro#define _DEFINE
1590792Sgshapiro#include <sendmail.h>
1690792Sgshapiro#include <sm/xtrap.h>
1790792Sgshapiro#include <sm/signal.h>
1890792Sgshapiro
1938032Speter#ifndef lint
2090792SgshapiroSM_UNUSED(static char copyright[]) =
2173188Sgshapiro"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
2264562Sgshapiro	All rights reserved.\n\
2338032Speter     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
2438032Speter     Copyright (c) 1988, 1993\n\
2538032Speter	The Regents of the University of California.  All rights reserved.\n";
2664562Sgshapiro#endif /* ! lint */
2738032Speter
28102528SgshapiroSM_RCSID("@(#)$Id: main.c,v 8.887.2.1 2002/08/04 17:36:06 gshapiro Exp $")
2938032Speter
3038032Speter
3164562Sgshapiro#if NETINET || NETINET6
3264562Sgshapiro# include <arpa/inet.h>
3364562Sgshapiro#endif /* NETINET || NETINET6 */
3464562Sgshapiro
3590792Sgshapiro/* for getcfname() */
3690792Sgshapiro#include <sendmail/pathnames.h>
3790792Sgshapiro
3890792Sgshapirostatic SM_DEBUG_T
3990792SgshapiroDebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
4090792Sgshapiro	"@(#)$Debug: no_persistent_restart - don't restart, log only $");
4190792Sgshapiro
4290792Sgshapirostatic void	dump_class __P((STAB *, int));
4390792Sgshapirostatic void	obsolete __P((char **));
4490792Sgshapirostatic void	testmodeline __P((char *, ENVELOPE *));
4590792Sgshapirostatic char	*getextenv __P((const char *));
4690792Sgshapirostatic void	sm_printoptions __P((char **));
4777349Sgshapirostatic SIGFUNC_DECL	intindebug __P((int));
4890792Sgshapirostatic SIGFUNC_DECL	sighup __P((int));
4990792Sgshapirostatic SIGFUNC_DECL	sigpipe __P((int));
5090792Sgshapirostatic SIGFUNC_DECL	sigterm __P((int));
5180785Sgshapiro#ifdef SIGUSR1
5277349Sgshapirostatic SIGFUNC_DECL	sigusr1 __P((int));
5390792Sgshapiro#endif /* SIGUSR1 */
5464562Sgshapiro
5538032Speter/*
5638032Speter**  SENDMAIL -- Post mail to a set of destinations.
5738032Speter**
5838032Speter**	This is the basic mail router.  All user mail programs should
5938032Speter**	call this routine to actually deliver mail.  Sendmail in
6038032Speter**	turn calls a bunch of mail servers that do the real work of
6138032Speter**	delivering the mail.
6238032Speter**
6364562Sgshapiro**	Sendmail is driven by settings read in from /etc/mail/sendmail.cf
6438032Speter**	(read by readcf.c).
6538032Speter**
6638032Speter**	Usage:
6738032Speter**		/usr/lib/sendmail [flags] addr ...
6838032Speter**
6938032Speter**		See the associated documentation for details.
7038032Speter**
7190792Sgshapiro**	Authors:
7238032Speter**		Eric Allman, UCB/INGRES (until 10/81).
7338032Speter**			     Britton-Lee, Inc., purveyors of fine
7438032Speter**				database computers (11/81 - 10/88).
7538032Speter**			     International Computer Science Institute
7638032Speter**				(11/88 - 9/89).
7738032Speter**			     UCB/Mammoth Project (10/89 - 7/95).
7838032Speter**			     InReference, Inc. (8/95 - 1/97).
7938032Speter**			     Sendmail, Inc. (1/98 - present).
8038032Speter**		The support of the my employers is gratefully acknowledged.
8138032Speter**			Few of them (Britton-Lee in particular) have had
8238032Speter**			anything to gain from my involvement in this project.
8390792Sgshapiro**
8490792Sgshapiro**		Gregory Neil Shapiro,
8590792Sgshapiro**			Worcester Polytechnic Institute	(until 3/98).
8690792Sgshapiro**			Sendmail, Inc. (3/98 - present).
8790792Sgshapiro**
8890792Sgshapiro**		Claus Assmann,
8990792Sgshapiro**			Sendmail, Inc. (12/98 - present).
9038032Speter*/
9138032Speter
9238032Speterchar		*FullName;	/* sender's full name */
9338032SpeterENVELOPE	BlankEnvelope;	/* a "blank" envelope */
9464562Sgshapirostatic ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
9538032SpeterADDRESS		NullAddress =	/* a null address */
9638032Speter		{ "", "", NULL, "" };
9738032Speterchar		*CommandLineArgs;	/* command line args for pid file */
9890792Sgshapirobool		Warn_Q_option = false;	/* warn about Q option use */
9964562Sgshapirostatic int	MissingFds = 0;	/* bit map of fds missing on startup */
10090792Sgshapirochar		*Mbdb = "pw";	/* mailbox database defaults to /etc/passwd */
10138032Speter
10238032Speter#ifdef NGROUPS_MAX
10338032SpeterGIDSET_T	InitialGidSet[NGROUPS_MAX];
10464562Sgshapiro#endif /* NGROUPS_MAX */
10538032Speter
10690792Sgshapiro#define MAXCONFIGLEVEL	10	/* highest config version level known */
10738032Speter
10864562Sgshapiro#if SASL
10964562Sgshapirostatic sasl_callback_t srvcallbacks[] =
11064562Sgshapiro{
11164562Sgshapiro	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
11264562Sgshapiro	{	SASL_CB_PROXY_POLICY,	&proxy_policy,	NULL	},
11364562Sgshapiro	{	SASL_CB_LIST_END,	NULL,		NULL	}
11464562Sgshapiro};
11564562Sgshapiro#endif /* SASL */
11664562Sgshapiro
11790792Sgshapirounsigned int	SubmitMode;
11890792Sgshapiroint		SyslogPrefixLen; /* estimated length of syslog prefix */
11990792Sgshapiro#define PIDLEN		6	/* pid length for computing SyslogPrefixLen */
12090792Sgshapiro#ifndef SL_FUDGE
12190792Sgshapiro# define SL_FUDGE	10	/* fudge offset for SyslogPrefixLen */
12290792Sgshapiro#endif /* ! SL_FUDGE */
12390792Sgshapiro#define SLDLL		8	/* est. length of default syslog label */
12464562Sgshapiro
12590792Sgshapiro
12690792Sgshapiro/* Some options are dangerous to allow users to use in non-submit mode */
12790792Sgshapiro#define CHECK_AGAINST_OPMODE(cmd)					\
12890792Sgshapiro{									\
12990792Sgshapiro	if (extraprivs &&						\
13090792Sgshapiro	    OpMode != MD_DELIVER && OpMode != MD_SMTP &&		\
13190792Sgshapiro	    OpMode != MD_VERIFY && OpMode != MD_TEST)			\
13290792Sgshapiro	{								\
13390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
13490792Sgshapiro				     "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
13590792Sgshapiro		       (cmd));						\
13690792Sgshapiro		break;							\
13790792Sgshapiro	}								\
13890792Sgshapiro	if (extraprivs && queuerun)					\
13990792Sgshapiro	{								\
14090792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
14190792Sgshapiro				     "WARNING: Ignoring submission mode -%c option with -q\n", \
14290792Sgshapiro		       (cmd));						\
14390792Sgshapiro		break;							\
14490792Sgshapiro	}								\
14590792Sgshapiro}
14690792Sgshapiro
14738032Speterint
14838032Spetermain(argc, argv, envp)
14938032Speter	int argc;
15038032Speter	char **argv;
15138032Speter	char **envp;
15238032Speter{
15338032Speter	register char *p;
15438032Speter	char **av;
15538032Speter	extern char Version[];
15638032Speter	char *ep, *from;
15738032Speter	STAB *st;
15838032Speter	register int i;
15938032Speter	int j;
16064562Sgshapiro	int dp;
16190792Sgshapiro	int fill_errno;
16290792Sgshapiro	int qgrp = NOQGRP;		/* queue group to process */
16390792Sgshapiro	bool safecf = true;
16464562Sgshapiro	BITMAP256 *p_flags = NULL;	/* daemon flags */
16590792Sgshapiro	bool warn_C_flag = false;
16690792Sgshapiro	bool auth = true;		/* whether to set e_auth_param */
16738032Speter	char warn_f_flag = '\0';
16890792Sgshapiro	bool run_in_foreground = false;	/* -bD mode */
16990792Sgshapiro	bool queuerun = false, debug = false;
17038032Speter	struct passwd *pw;
17138032Speter	struct hostent *hp;
17238032Speter	char *nullserver = NULL;
17364562Sgshapiro	char *authinfo = NULL;
17464562Sgshapiro	char *sysloglabel = NULL;	/* label for syslog */
17590792Sgshapiro	char *conffile = NULL;		/* name of .cf file */
17690792Sgshapiro	char *queuegroup = NULL;	/* queue group to process */
17790792Sgshapiro#if _FFR_QUARANTINE
17890792Sgshapiro	char *quarantining = NULL;	/* quarantine queue items? */
17990792Sgshapiro#endif /* _FFR_QUARANTINE */
18090792Sgshapiro	bool extraprivs;
18190792Sgshapiro	bool forged, negate;
18290792Sgshapiro	bool queuepersistent = false;	/* queue runner process runs forever */
18390792Sgshapiro	bool foregroundqueue = false;	/* queue run in foreground */
18490792Sgshapiro	bool save_val;			/* to save some bool var. */
18590792Sgshapiro	int cftype;			/* which cf file to use? */
18690792Sgshapiro	static time_t starttime = 0;	/* when was process started */
18764562Sgshapiro	struct stat traf_st;		/* for TrafficLog FIFO check */
18890792Sgshapiro	char buf[MAXLINE];
18938032Speter	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
19038032Speter	static char rnamebuf[MAXNAME];	/* holds RealUserName */
19138032Speter	char *emptyenviron[1];
19290792Sgshapiro#if STARTTLS
19366494Sgshapiro	bool tls_ok;
19490792Sgshapiro#endif /* STARTTLS */
19538032Speter	QUEUE_CHAR *new;
19690792Sgshapiro	ENVELOPE *e;
19738032Speter	extern int DtableSize;
19838032Speter	extern int optind;
19938032Speter	extern int opterr;
20038032Speter	extern char *optarg;
20138032Speter	extern char **environ;
20290792Sgshapiro#if SASL
20390792Sgshapiro	extern void sm_sasl_init __P((void));
20490792Sgshapiro#endif /* SASL */
20538032Speter
20690792Sgshapiro#if USE_ENVIRON
20790792Sgshapiro	envp = environ;
20890792Sgshapiro#endif /* USE_ENVIRON */
20990792Sgshapiro
21090792Sgshapiro	/* turn off profiling */
21190792Sgshapiro	SM_PROF(0);
21290792Sgshapiro
21390792Sgshapiro	/* install default exception handler */
21490792Sgshapiro	sm_exc_newthread(fatal_error);
21590792Sgshapiro
21638032Speter	/*
21738032Speter	**  Check to see if we reentered.
21838032Speter	**	This would normally happen if e_putheader or e_putbody
21938032Speter	**	were NULL when invoked.
22038032Speter	*/
22138032Speter
22290792Sgshapiro	if (starttime != 0)
22338032Speter	{
22438032Speter		syserr("main: reentered!");
22538032Speter		abort();
22638032Speter	}
22790792Sgshapiro	starttime = curtime();
22838032Speter
22938032Speter	/* avoid null pointer dereferences */
23038032Speter	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
23138032Speter
23290792Sgshapiro	RealUid = getuid();
23390792Sgshapiro	RealGid = getgid();
23477349Sgshapiro
23590792Sgshapiro	/* Check if sendmail is running with extra privs */
23690792Sgshapiro	extraprivs = (RealUid != 0 &&
23790792Sgshapiro		      (geteuid() != getuid() || getegid() != getgid()));
23877349Sgshapiro
23990792Sgshapiro	CurrentPid = getpid();
24038032Speter
24190792Sgshapiro	/* get whatever .cf file is right for the opmode */
24290792Sgshapiro	cftype = SM_GET_RIGHT_CF;
24364562Sgshapiro
24438032Speter	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
24538032Speter	DtableSize = getdtsize();
24638032Speter	if (DtableSize > 256)
24738032Speter		DtableSize = 256;
24838032Speter
24938032Speter	/*
25038032Speter	**  Be sure we have enough file descriptors.
25138032Speter	**	But also be sure that 0, 1, & 2 are open.
25238032Speter	*/
25338032Speter
25490792Sgshapiro	/* reset errno and fill_errno; the latter is used way down below */
25590792Sgshapiro	errno = fill_errno = 0;
25638032Speter	fill_fd(STDIN_FILENO, NULL);
25790792Sgshapiro	if (errno != 0)
25890792Sgshapiro		fill_errno = errno;
25938032Speter	fill_fd(STDOUT_FILENO, NULL);
26090792Sgshapiro	if (errno != 0)
26190792Sgshapiro		fill_errno = errno;
26238032Speter	fill_fd(STDERR_FILENO, NULL);
26390792Sgshapiro	if (errno != 0)
26490792Sgshapiro		fill_errno = errno;
26538032Speter
26638032Speter	i = DtableSize;
26738032Speter	while (--i > 0)
26838032Speter	{
26990792Sgshapiro		if (i != STDIN_FILENO && i != STDOUT_FILENO &&
27090792Sgshapiro		    i != STDERR_FILENO)
27138032Speter			(void) close(i);
27238032Speter	}
27338032Speter	errno = 0;
27438032Speter
27538032Speter#if LOG
27690792Sgshapiro# ifndef SM_LOG_STR
27790792Sgshapiro#  define SM_LOG_STR	"sendmail"
27890792Sgshapiro# endif /* ! SM_LOG_STR */
27964562Sgshapiro#  ifdef LOG_MAIL
28090792Sgshapiro	openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
28164562Sgshapiro#  else /* LOG_MAIL */
28290792Sgshapiro	openlog(SM_LOG_STR, LOG_PID);
28364562Sgshapiro#  endif /* LOG_MAIL */
28464562Sgshapiro#endif /* LOG */
28538032Speter
28690792Sgshapiro	/*
28790792Sgshapiro	**  Seed the random number generator.
28890792Sgshapiro	**  Used for queue file names, picking a queue directory, and
28990792Sgshapiro	**  MX randomization.
29090792Sgshapiro	*/
29138032Speter
29290792Sgshapiro	seed_random();
29338032Speter
29490792Sgshapiro	/* do machine-dependent initializations */
29590792Sgshapiro	init_md(argc, argv);
29690792Sgshapiro
29790792Sgshapiro
29890792Sgshapiro	SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
29990792Sgshapiro
30038032Speter	/* reset status from syserr() calls for missing file descriptors */
30138032Speter	Errors = 0;
30238032Speter	ExitStat = EX_OK;
30338032Speter
30464562Sgshapiro	SubmitMode = SUBMIT_UNKNOWN;
30538032Speter#if XDEBUG
30638032Speter	checkfd012("after openlog");
30764562Sgshapiro#endif /* XDEBUG */
30838032Speter
30990792Sgshapiro	tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1");
31038032Speter
31138032Speter#ifdef NGROUPS_MAX
31238032Speter	/* save initial group set for future checks */
31338032Speter	i = getgroups(NGROUPS_MAX, InitialGidSet);
31490792Sgshapiro	if (i <= 0)
31590792Sgshapiro	{
31638032Speter		InitialGidSet[0] = (GID_T) -1;
31790792Sgshapiro		i = 0;
31890792Sgshapiro	}
31938032Speter	while (i < NGROUPS_MAX)
32038032Speter		InitialGidSet[i++] = InitialGidSet[0];
32164562Sgshapiro#endif /* NGROUPS_MAX */
32238032Speter
32338032Speter	/* drop group id privileges (RunAsUser not yet set) */
32490792Sgshapiro	dp = drop_privileges(false);
32564562Sgshapiro	setstat(dp);
32638032Speter
32790792Sgshapiro#ifdef SIGUSR1
32877349Sgshapiro	/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
32994334Sgshapiro	if (!extraprivs)
33077349Sgshapiro	{
33177349Sgshapiro		/* arrange to dump state on user-1 signal */
33290792Sgshapiro		(void) sm_signal(SIGUSR1, sigusr1);
33377349Sgshapiro	}
33494334Sgshapiro	else
33594334Sgshapiro	{
33694334Sgshapiro		/* ignore user-1 signal */
33794334Sgshapiro		(void) sm_signal(SIGUSR1, SIG_IGN);
33894334Sgshapiro	}
33990792Sgshapiro#endif /* SIGUSR1 */
34038032Speter
34138032Speter	/* initialize for setproctitle */
34238032Speter	initsetproctitle(argc, argv, envp);
34338032Speter
34438032Speter	/* Handle any non-getoptable constructions. */
34538032Speter	obsolete(argv);
34638032Speter
34738032Speter	/*
34838032Speter	**  Do a quick prescan of the argument list.
34938032Speter	*/
35038032Speter
35164562Sgshapiro
35290792Sgshapiro	/* find initial opMode */
35390792Sgshapiro	OpMode = MD_DELIVER;
35490792Sgshapiro	av = argv;
35590792Sgshapiro	p = strrchr(*av, '/');
35690792Sgshapiro	if (p++ == NULL)
35790792Sgshapiro		p = *av;
35890792Sgshapiro	if (strcmp(p, "newaliases") == 0)
35990792Sgshapiro		OpMode = MD_INITALIAS;
36090792Sgshapiro	else if (strcmp(p, "mailq") == 0)
36190792Sgshapiro		OpMode = MD_PRINT;
36290792Sgshapiro	else if (strcmp(p, "smtpd") == 0)
36390792Sgshapiro		OpMode = MD_DAEMON;
36490792Sgshapiro	else if (strcmp(p, "hoststat") == 0)
36590792Sgshapiro		OpMode = MD_HOSTSTAT;
36690792Sgshapiro	else if (strcmp(p, "purgestat") == 0)
36790792Sgshapiro		OpMode = MD_PURGESTAT;
36890792Sgshapiro
36990792Sgshapiro#if _FFR_QUARANTINE
37090792Sgshapiro# if defined(__osf__) || defined(_AIX3)
37190792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:xQ:"
37290792Sgshapiro# endif /* defined(__osf__) || defined(_AIX3) */
37390792Sgshapiro# if defined(sony_news)
37490792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
37590792Sgshapiro# endif /* defined(sony_news) */
37690792Sgshapiro# ifndef OPTIONS
37790792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
37890792Sgshapiro# endif /* ! OPTIONS */
37990792Sgshapiro#else /* _FFR_QUARANTINE */
38090792Sgshapiro# if defined(__osf__) || defined(_AIX3)
38190792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:x"
38290792Sgshapiro# endif /* defined(__osf__) || defined(_AIX3) */
38390792Sgshapiro# if defined(sony_news)
38490792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:"
38590792Sgshapiro# endif /* defined(sony_news) */
38690792Sgshapiro# ifndef OPTIONS
38790792Sgshapiro#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:"
38890792Sgshapiro# endif /* ! OPTIONS */
38990792Sgshapiro#endif /* _FFR_QUARANTINE */
39090792Sgshapiro
39138032Speter	opterr = 0;
39238032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
39338032Speter	{
39438032Speter		switch (j)
39538032Speter		{
39690792Sgshapiro		  case 'b':	/* operations mode */
39794334Sgshapiro			j = (optarg == NULL) ? ' ' : *optarg;
39894334Sgshapiro			switch (j)
39938032Speter			{
40090792Sgshapiro			  case MD_DAEMON:
40190792Sgshapiro			  case MD_FGDAEMON:
40290792Sgshapiro			  case MD_SMTP:
40390792Sgshapiro			  case MD_INITALIAS:
40490792Sgshapiro			  case MD_DELIVER:
40590792Sgshapiro			  case MD_VERIFY:
40690792Sgshapiro			  case MD_TEST:
40790792Sgshapiro			  case MD_PRINT:
40890792Sgshapiro			  case MD_PRINTNQE:
40990792Sgshapiro			  case MD_HOSTSTAT:
41090792Sgshapiro			  case MD_PURGESTAT:
41190792Sgshapiro			  case MD_ARPAFTP:
41290792Sgshapiro				OpMode = j;
41338032Speter				break;
41490792Sgshapiro
41590792Sgshapiro			  case MD_FREEZE:
41690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
41790792Sgshapiro						     "Frozen configurations unsupported\n");
41890792Sgshapiro				return EX_USAGE;
41990792Sgshapiro
42090792Sgshapiro			  default:
42190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
42290792Sgshapiro						     "Invalid operation mode %c\n",
42390792Sgshapiro						     j);
42490792Sgshapiro				return EX_USAGE;
42538032Speter			}
42690792Sgshapiro			break;
42790792Sgshapiro
42890792Sgshapiro		  case 'd':
42990792Sgshapiro			debug = true;
43038032Speter			tTflag(optarg);
43190792Sgshapiro			(void) sm_io_setvbuf(smioout, SM_TIME_DEFAULT,
43290792Sgshapiro					     (char *) NULL, SM_IO_NBF,
43390792Sgshapiro					     SM_IO_BUFSIZ);
43438032Speter			break;
43564562Sgshapiro
43664562Sgshapiro		  case 'G':	/* relay (gateway) submission */
43790792Sgshapiro			SubmitMode = SUBMIT_MTA;
43864562Sgshapiro			break;
43964562Sgshapiro
44064562Sgshapiro		  case 'L':
44190792Sgshapiro			j = SM_MIN(strlen(optarg), 24) + 1;
44264562Sgshapiro			sysloglabel = xalloc(j);
44390792Sgshapiro			(void) sm_strlcpy(sysloglabel, optarg, j);
44490792Sgshapiro			SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
44590792Sgshapiro					  SL_FUDGE + j;
44664562Sgshapiro			break;
44764562Sgshapiro
44890792Sgshapiro#if _FFR_QUARANTINE
44990792Sgshapiro		  case 'Q':
45090792Sgshapiro#endif /* _FFR_QUARANTINE */
45190792Sgshapiro		  case 'q':
45290792Sgshapiro			/* just check if it is there */
45390792Sgshapiro			queuerun = true;
45464562Sgshapiro			break;
45538032Speter		}
45638032Speter	}
45738032Speter	opterr = 1;
45838032Speter
45990792Sgshapiro	/* Don't leak queue information via debug flags */
46090792Sgshapiro	if (extraprivs && queuerun && debug)
46190792Sgshapiro	{
46290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
46390792Sgshapiro				     "WARNING: Can not use -d with -q.  Disabling debugging.\n");
46490792Sgshapiro		sm_debug_setfile(NULL);
46590792Sgshapiro		(void) memset(tTdvect, '\0', sizeof tTdvect);
46690792Sgshapiro	}
46790792Sgshapiro
46877349Sgshapiro#if LOG
46964562Sgshapiro	if (sysloglabel != NULL)
47064562Sgshapiro	{
47177349Sgshapiro		/* Sanitize the string */
47277349Sgshapiro		for (p = sysloglabel; *p != '\0'; p++)
47377349Sgshapiro		{
47477349Sgshapiro			if (!isascii(*p) || !isprint(*p) || *p == '%')
47577349Sgshapiro				*p = '*';
47677349Sgshapiro		}
47764562Sgshapiro		closelog();
47864562Sgshapiro#  ifdef LOG_MAIL
47964562Sgshapiro		openlog(sysloglabel, LOG_PID, LOG_MAIL);
48064562Sgshapiro#  else /* LOG_MAIL */
48164562Sgshapiro		openlog(sysloglabel, LOG_PID);
48264562Sgshapiro#  endif /* LOG_MAIL */
48377349Sgshapiro	}
48464562Sgshapiro#endif /* LOG */
48564562Sgshapiro
48638032Speter	/* set up the blank envelope */
48738032Speter	BlankEnvelope.e_puthdr = putheader;
48838032Speter	BlankEnvelope.e_putbody = putbody;
48938032Speter	BlankEnvelope.e_xfp = NULL;
49038032Speter	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
49138032Speter	CurEnv = &BlankEnvelope;
49238032Speter	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
49338032Speter
49438032Speter	/*
49538032Speter	**  Set default values for variables.
49638032Speter	**	These cannot be in initialized data space.
49738032Speter	*/
49838032Speter
49938032Speter	setdefaults(&BlankEnvelope);
50090792Sgshapiro	initmacros(&BlankEnvelope);
50138032Speter
50290792Sgshapiro	/* reset macro */
50390792Sgshapiro	set_op_mode(OpMode);
50438032Speter
50538032Speter	pw = sm_getpwuid(RealUid);
50638032Speter	if (pw != NULL)
50790792Sgshapiro		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
50838032Speter	else
50990792Sgshapiro		(void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
51090792Sgshapiro				   (int) RealUid);
51164562Sgshapiro
51238032Speter	RealUserName = rnamebuf;
51338032Speter
51438032Speter	if (tTd(0, 101))
51538032Speter	{
51690792Sgshapiro		sm_dprintf("Version %s\n", Version);
51790792Sgshapiro		finis(false, true, EX_OK);
51890792Sgshapiro		/* NOTREACHED */
51938032Speter	}
52038032Speter
52138032Speter	/*
52290792Sgshapiro	**  if running non-set-user-ID binary as non-root, pretend
52338032Speter	**  we are the RunAsUid
52438032Speter	*/
52577349Sgshapiro
52638032Speter	if (RealUid != 0 && geteuid() == RealUid)
52738032Speter	{
52838032Speter		if (tTd(47, 1))
52990792Sgshapiro			sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
53090792Sgshapiro				   (int) RealUid);
53138032Speter		RunAsUid = RealUid;
53238032Speter	}
53338032Speter	else if (geteuid() != 0)
53438032Speter		RunAsUid = geteuid();
53538032Speter
53690792Sgshapiro	EffGid = getegid();
53790792Sgshapiro	if (RealUid != 0 && EffGid == RealGid)
53838032Speter		RunAsGid = RealGid;
53938032Speter
54038032Speter	if (tTd(47, 5))
54138032Speter	{
54290792Sgshapiro		sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
54390792Sgshapiro			   (int) geteuid(), (int) getuid(),
54490792Sgshapiro			   (int) getegid(), (int) getgid());
54590792Sgshapiro		sm_dprintf("main: RunAsUser = %d:%d\n",
54690792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
54738032Speter	}
54838032Speter
54938032Speter	/* save command line arguments */
55064562Sgshapiro	j = 0;
55138032Speter	for (av = argv; *av != NULL; )
55264562Sgshapiro		j += strlen(*av++) + 1;
55338032Speter	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
55464562Sgshapiro	CommandLineArgs = xalloc(j);
55538032Speter	p = CommandLineArgs;
55638032Speter	for (av = argv, i = 0; *av != NULL; )
55738032Speter	{
55864562Sgshapiro		int h;
55964562Sgshapiro
56038032Speter		SaveArgv[i++] = newstr(*av);
56138032Speter		if (av != argv)
56238032Speter			*p++ = ' ';
56390792Sgshapiro		(void) sm_strlcpy(p, *av++, j);
56464562Sgshapiro		h = strlen(p);
56564562Sgshapiro		p += h;
56664562Sgshapiro		j -= h + 1;
56738032Speter	}
56838032Speter	SaveArgv[i] = NULL;
56938032Speter
57038032Speter	if (tTd(0, 1))
57138032Speter	{
57238032Speter		extern char *CompileOptions[];
57338032Speter
57490792Sgshapiro		sm_dprintf("Version %s\n Compiled with:", Version);
57590792Sgshapiro		sm_printoptions(CompileOptions);
57638032Speter	}
57738032Speter	if (tTd(0, 10))
57838032Speter	{
57938032Speter		extern char *OsCompileOptions[];
58038032Speter
58190792Sgshapiro		sm_dprintf("    OS Defines:");
58290792Sgshapiro		sm_printoptions(OsCompileOptions);
58338032Speter#ifdef _PATH_UNIX
58490792Sgshapiro		sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
58564562Sgshapiro#endif /* _PATH_UNIX */
58690792Sgshapiro
58790792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MSP)\n",
58890792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
58990792Sgshapiro				     conffile));
59090792Sgshapiro		sm_dprintf("     Conf file:\t%s (default for MTA)\n",
59190792Sgshapiro			   getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
59290792Sgshapiro				     conffile));
59390792Sgshapiro		sm_dprintf("      Pid file:\t%s (default)\n", PidFile);
59438032Speter	}
59538032Speter
59690792Sgshapiro	if (tTd(0, 12))
59790792Sgshapiro	{
59890792Sgshapiro		extern char *SmCompileOptions[];
59938032Speter
60090792Sgshapiro		sm_dprintf(" libsm Defines:");
60190792Sgshapiro		sm_printoptions(SmCompileOptions);
60290792Sgshapiro	}
60390792Sgshapiro
60490792Sgshapiro	if (tTd(0, 13))
60590792Sgshapiro	{
60690792Sgshapiro		extern char *FFRCompileOptions[];
60790792Sgshapiro
60890792Sgshapiro		sm_dprintf("   FFR Defines:");
60990792Sgshapiro		sm_printoptions(FFRCompileOptions);
61090792Sgshapiro	}
61190792Sgshapiro
61290792Sgshapiro	InChannel = smioin;
61390792Sgshapiro	OutChannel = smioout;
61490792Sgshapiro
61538032Speter	/* clear sendmail's environment */
61638032Speter	ExternalEnviron = environ;
61738032Speter	emptyenviron[0] = NULL;
61838032Speter	environ = emptyenviron;
61938032Speter
62038032Speter	/*
62142575Speter	**  restore any original TZ setting until TimeZoneSpec has been
62242575Speter	**  determined - or early log messages may get bogus time stamps
62338032Speter	*/
62490792Sgshapiro
62538032Speter	if ((p = getextenv("TZ")) != NULL)
62638032Speter	{
62738032Speter		char *tz;
62838032Speter		int tzlen;
62938032Speter
63090792Sgshapiro		/* XXX check for reasonable length? */
63138032Speter		tzlen = strlen(p) + 4;
63238032Speter		tz = xalloc(tzlen);
63390792Sgshapiro		(void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
63490792Sgshapiro
63590792Sgshapiro		/* XXX check return code? */
63664562Sgshapiro		(void) putenv(tz);
63738032Speter	}
63838032Speter
63938032Speter	/* prime the child environment */
64038032Speter	setuserenv("AGENT", "sendmail");
64138032Speter
64290792Sgshapiro	(void) sm_signal(SIGPIPE, SIG_IGN);
64338032Speter	OldUmask = umask(022);
64438032Speter	FullName = getextenv("NAME");
64595154Sgshapiro	if (FullName != NULL)
64695154Sgshapiro		FullName = newstr(FullName);
64738032Speter
64838032Speter	/*
64938032Speter	**  Initialize name server if it is going to be used.
65038032Speter	*/
65138032Speter
65238032Speter#if NAMED_BIND
65338032Speter	if (!bitset(RES_INIT, _res.options))
65464562Sgshapiro		(void) res_init();
65538032Speter	if (tTd(8, 8))
65638032Speter		_res.options |= RES_DEBUG;
65738032Speter	else
65838032Speter		_res.options &= ~RES_DEBUG;
65938032Speter# ifdef RES_NOALIASES
66090792Sgshapiro	if (bitset(RES_NOALIASES, _res.options))
66190792Sgshapiro		ResNoAliases = true;
66238032Speter	_res.options |= RES_NOALIASES;
66364562Sgshapiro# endif /* RES_NOALIASES */
66464562Sgshapiro	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
66564562Sgshapiro	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
66664562Sgshapiro	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
66764562Sgshapiro	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
66864562Sgshapiro	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
66964562Sgshapiro	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
67064562Sgshapiro#endif /* NAMED_BIND */
67138032Speter
67238032Speter	errno = 0;
67338032Speter	from = NULL;
67438032Speter
67538032Speter	/* initialize some macros, etc. */
67690792Sgshapiro	init_vendor_macros(&BlankEnvelope);
67738032Speter
67838032Speter	/* version */
67990792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
68038032Speter
68138032Speter	/* hostname */
68238032Speter	hp = myhostname(jbuf, sizeof jbuf);
68338032Speter	if (jbuf[0] != '\0')
68438032Speter	{
68590792Sgshapiro		struct utsname utsname;
68638032Speter
68738032Speter		if (tTd(0, 4))
68890792Sgshapiro			sm_dprintf("Canonical name: %s\n", jbuf);
68990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
69090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
69138032Speter		setclass('w', jbuf);
69238032Speter
69338032Speter		p = strchr(jbuf, '.');
69438032Speter		if (p != NULL)
69538032Speter		{
69638032Speter			if (p[1] != '\0')
69738032Speter			{
69890792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm',
69990792Sgshapiro					  &p[1]);
70038032Speter			}
70138032Speter			while (p != NULL && strchr(&p[1], '.') != NULL)
70238032Speter			{
70338032Speter				*p = '\0';
70438032Speter				if (tTd(0, 4))
70590792Sgshapiro					sm_dprintf("\ta.k.a.: %s\n", jbuf);
70638032Speter				setclass('w', jbuf);
70738032Speter				*p++ = '.';
70838032Speter				p = strchr(p, '.');
70938032Speter			}
71038032Speter		}
71138032Speter
71238032Speter		if (uname(&utsname) >= 0)
71338032Speter			p = utsname.nodename;
71438032Speter		else
71538032Speter		{
71638032Speter			if (tTd(0, 22))
71790792Sgshapiro				sm_dprintf("uname failed (%s)\n",
71890792Sgshapiro					   sm_errstring(errno));
71938032Speter			makelower(jbuf);
72038032Speter			p = jbuf;
72138032Speter		}
72238032Speter		if (tTd(0, 4))
72390792Sgshapiro			sm_dprintf(" UUCP nodename: %s\n", p);
72490792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
72538032Speter		setclass('k', p);
72638032Speter		setclass('w', p);
72738032Speter	}
72838032Speter	if (hp != NULL)
72938032Speter	{
73038032Speter		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
73138032Speter		{
73238032Speter			if (tTd(0, 4))
73390792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", *av);
73438032Speter			setclass('w', *av);
73538032Speter		}
73664562Sgshapiro#if NETINET || NETINET6
73790792Sgshapiro		for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
73838032Speter		{
73964562Sgshapiro# if NETINET6
74064562Sgshapiro			char *addr;
74164562Sgshapiro			char buf6[INET6_ADDRSTRLEN];
74264562Sgshapiro			struct in6_addr ia6;
74364562Sgshapiro# endif /* NETINET6 */
74464562Sgshapiro# if NETINET
74564562Sgshapiro			struct in_addr ia;
74664562Sgshapiro# endif /* NETINET */
74764562Sgshapiro			char ipbuf[103];
74864562Sgshapiro
74964562Sgshapiro			ipbuf[0] = '\0';
75064562Sgshapiro			switch (hp->h_addrtype)
75138032Speter			{
75264562Sgshapiro# if NETINET
75364562Sgshapiro			  case AF_INET:
75464562Sgshapiro				if (hp->h_length != INADDRSZ)
75564562Sgshapiro					break;
75638032Speter
75764562Sgshapiro				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
75890792Sgshapiro				(void) sm_snprintf(ipbuf, sizeof ipbuf,
75990792Sgshapiro						   "[%.100s]", inet_ntoa(ia));
76064562Sgshapiro				break;
76164562Sgshapiro# endif /* NETINET */
76264562Sgshapiro
76364562Sgshapiro# if NETINET6
76464562Sgshapiro			  case AF_INET6:
76564562Sgshapiro				if (hp->h_length != IN6ADDRSZ)
76664562Sgshapiro					break;
76764562Sgshapiro
76864562Sgshapiro				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
76964562Sgshapiro				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
77064562Sgshapiro				if (addr != NULL)
77190792Sgshapiro					(void) sm_snprintf(ipbuf, sizeof ipbuf,
77290792Sgshapiro							   "[%.100s]", addr);
77364562Sgshapiro				break;
77464562Sgshapiro# endif /* NETINET6 */
77538032Speter			}
77664562Sgshapiro			if (ipbuf[0] == '\0')
77764562Sgshapiro				break;
77864562Sgshapiro
77964562Sgshapiro			if (tTd(0, 4))
78090792Sgshapiro				sm_dprintf("\ta.k.a.: %s\n", ipbuf);
78164562Sgshapiro			setclass('w', ipbuf);
78238032Speter		}
78364562Sgshapiro#endif /* NETINET || NETINET6 */
78490792Sgshapiro#if NETINET6
78571345Sgshapiro		freehostent(hp);
78671345Sgshapiro		hp = NULL;
78790792Sgshapiro#endif /* NETINET6 */
78838032Speter	}
78938032Speter
79038032Speter	/* current time */
79190792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
79290792Sgshapiro
79364562Sgshapiro	/* current load average */
79490792Sgshapiro	sm_getla();
79538032Speter
79638032Speter	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
79738032Speter	QueueLimitSender = (QUEUE_CHAR *) NULL;
79838032Speter	QueueLimitId = (QUEUE_CHAR *) NULL;
79990792Sgshapiro#if _FFR_QUARANTINE
80090792Sgshapiro	QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
80190792Sgshapiro#endif /* _FFR_QUARANTINE */
80238032Speter
80338032Speter	/*
80442575Speter	**  Crack argv.
80538032Speter	*/
80638032Speter
80738032Speter	optind = 1;
80838032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
80938032Speter	{
81038032Speter		switch (j)
81138032Speter		{
81238032Speter		  case 'b':	/* operations mode */
81390792Sgshapiro			/* already done */
81490792Sgshapiro			break;
81538032Speter
81690792Sgshapiro		  case 'A':	/* use Alternate sendmail/submit.cf */
81790792Sgshapiro			cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
81890792Sgshapiro						  : SM_GET_SENDMAIL_CF;
81938032Speter			break;
82038032Speter
82138032Speter		  case 'B':	/* body type */
82290792Sgshapiro			CHECK_AGAINST_OPMODE(j);
82390792Sgshapiro			BlankEnvelope.e_bodytype = newstr(optarg);
82438032Speter			break;
82538032Speter
82638032Speter		  case 'C':	/* select configuration file (already done) */
82738032Speter			if (RealUid != 0)
82890792Sgshapiro				warn_C_flag = true;
82990792Sgshapiro			conffile = newstr(optarg);
83090792Sgshapiro			dp = drop_privileges(true);
83164562Sgshapiro			setstat(dp);
83290792Sgshapiro			safecf = false;
83338032Speter			break;
83438032Speter
83590792Sgshapiro		  case 'd':	/* debugging */
83690792Sgshapiro			/* already done */
83738032Speter			break;
83838032Speter
83938032Speter		  case 'f':	/* from address */
84038032Speter		  case 'r':	/* obsolete -f flag */
84190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
84238032Speter			if (from != NULL)
84338032Speter			{
84438032Speter				usrerr("More than one \"from\" person");
84538032Speter				ExitStat = EX_USAGE;
84638032Speter				break;
84738032Speter			}
84890792Sgshapiro			from = newstr(denlstring(optarg, true, true));
84938032Speter			if (strcmp(RealUserName, from) != 0)
85038032Speter				warn_f_flag = j;
85138032Speter			break;
85238032Speter
85338032Speter		  case 'F':	/* set full name */
85490792Sgshapiro			CHECK_AGAINST_OPMODE(j);
85538032Speter			FullName = newstr(optarg);
85638032Speter			break;
85738032Speter
85864562Sgshapiro		  case 'G':	/* relay (gateway) submission */
85964562Sgshapiro			/* already set */
86090792Sgshapiro			CHECK_AGAINST_OPMODE(j);
86164562Sgshapiro			break;
86264562Sgshapiro
86338032Speter		  case 'h':	/* hop count */
86490792Sgshapiro			CHECK_AGAINST_OPMODE(j);
86590792Sgshapiro			BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
86690792Sgshapiro								  10);
86790792Sgshapiro			(void) sm_snprintf(buf, sizeof buf, "%d",
86890792Sgshapiro					   BlankEnvelope.e_hopcount);
86990792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
87090792Sgshapiro
87138032Speter			if (*ep)
87238032Speter			{
87338032Speter				usrerr("Bad hop count (%s)", optarg);
87438032Speter				ExitStat = EX_USAGE;
87538032Speter			}
87638032Speter			break;
87764562Sgshapiro
87864562Sgshapiro		  case 'L':	/* program label */
87964562Sgshapiro			/* already set */
88064562Sgshapiro			break;
88164562Sgshapiro
88238032Speter		  case 'n':	/* don't alias */
88390792Sgshapiro			CHECK_AGAINST_OPMODE(j);
88490792Sgshapiro			NoAlias = true;
88538032Speter			break;
88638032Speter
88738032Speter		  case 'N':	/* delivery status notifications */
88890792Sgshapiro			CHECK_AGAINST_OPMODE(j);
88938032Speter			DefaultNotify |= QHASNOTIFY;
89090792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
89190792Sgshapiro				macid("{dsn_notify}"), optarg);
89290792Sgshapiro			if (sm_strcasecmp(optarg, "never") == 0)
89338032Speter				break;
89438032Speter			for (p = optarg; p != NULL; optarg = p)
89538032Speter			{
89638032Speter				p = strchr(p, ',');
89738032Speter				if (p != NULL)
89838032Speter					*p++ = '\0';
89990792Sgshapiro				if (sm_strcasecmp(optarg, "success") == 0)
90038032Speter					DefaultNotify |= QPINGONSUCCESS;
90190792Sgshapiro				else if (sm_strcasecmp(optarg, "failure") == 0)
90238032Speter					DefaultNotify |= QPINGONFAILURE;
90390792Sgshapiro				else if (sm_strcasecmp(optarg, "delay") == 0)
90438032Speter					DefaultNotify |= QPINGONDELAY;
90538032Speter				else
90638032Speter				{
90738032Speter					usrerr("Invalid -N argument");
90838032Speter					ExitStat = EX_USAGE;
90938032Speter				}
91038032Speter			}
91138032Speter			break;
91238032Speter
91338032Speter		  case 'o':	/* set option */
91490792Sgshapiro			setoption(*optarg, optarg + 1, false, true,
91590792Sgshapiro				  &BlankEnvelope);
91638032Speter			break;
91738032Speter
91838032Speter		  case 'O':	/* set option (long form) */
91990792Sgshapiro			setoption(' ', optarg, false, true, &BlankEnvelope);
92038032Speter			break;
92138032Speter
92238032Speter		  case 'p':	/* set protocol */
92390792Sgshapiro			CHECK_AGAINST_OPMODE(j);
92438032Speter			p = strchr(optarg, ':');
92538032Speter			if (p != NULL)
92638032Speter			{
92738032Speter				*p++ = '\0';
92838032Speter				if (*p != '\0')
92938032Speter				{
93090792Sgshapiro					ep = sm_malloc_x(strlen(p) + 1);
93138032Speter					cleanstrcpy(ep, p, MAXNAME);
93290792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
93390792Sgshapiro						  A_HEAP, 's', ep);
93438032Speter				}
93538032Speter			}
93638032Speter			if (*optarg != '\0')
93738032Speter			{
93890792Sgshapiro				ep = sm_malloc_x(strlen(optarg) + 1);
93938032Speter				cleanstrcpy(ep, optarg, MAXNAME);
94090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_HEAP,
94190792Sgshapiro					  'r', ep);
94238032Speter			}
94338032Speter			break;
94438032Speter
94590792Sgshapiro#if _FFR_QUARANTINE
94690792Sgshapiro		  case 'Q':	/* change quarantining on queued items */
94790792Sgshapiro			/* sanity check */
94890792Sgshapiro			if (OpMode != MD_DELIVER &&
94990792Sgshapiro			    OpMode != MD_QUEUERUN)
95090792Sgshapiro			{
95190792Sgshapiro				usrerr("Can not use -Q with -b%c", OpMode);
95290792Sgshapiro				ExitStat = EX_USAGE;
95390792Sgshapiro				break;
95490792Sgshapiro			}
95590792Sgshapiro
95690792Sgshapiro			if (OpMode == MD_DELIVER)
95790792Sgshapiro				set_op_mode(MD_QUEUERUN);
95890792Sgshapiro
95990792Sgshapiro			FullName = NULL;
96090792Sgshapiro
96190792Sgshapiro			quarantining = newstr(optarg);
96290792Sgshapiro			break;
96390792Sgshapiro#endif /* _FFR_QUARANTINE */
96490792Sgshapiro
96538032Speter		  case 'q':	/* run queue files at intervals */
96664562Sgshapiro			/* sanity check */
96764562Sgshapiro			if (OpMode != MD_DELIVER &&
96864562Sgshapiro			    OpMode != MD_DAEMON &&
96964562Sgshapiro			    OpMode != MD_FGDAEMON &&
97064562Sgshapiro			    OpMode != MD_PRINT &&
97190792Sgshapiro			    OpMode != MD_PRINTNQE &&
97264562Sgshapiro			    OpMode != MD_QUEUERUN)
97364562Sgshapiro			{
97464562Sgshapiro				usrerr("Can not use -q with -b%c", OpMode);
97564562Sgshapiro				ExitStat = EX_USAGE;
97664562Sgshapiro				break;
97764562Sgshapiro			}
97864562Sgshapiro
97964562Sgshapiro			/* don't override -bd, -bD or -bp */
98064562Sgshapiro			if (OpMode == MD_DELIVER)
98190792Sgshapiro				set_op_mode(MD_QUEUERUN);
98264562Sgshapiro
98338032Speter			FullName = NULL;
98490792Sgshapiro			negate = optarg[0] == '!';
98590792Sgshapiro			if (negate)
98690792Sgshapiro			{
98790792Sgshapiro				/* negate meaning of pattern match */
98890792Sgshapiro				optarg++; /* skip '!' for next switch */
98990792Sgshapiro			}
99064562Sgshapiro
99138032Speter			switch (optarg[0])
99238032Speter			{
99390792Sgshapiro			  case 'G': /* Limit by queue group name */
99490792Sgshapiro				if (negate)
99590792Sgshapiro				{
99690792Sgshapiro					usrerr("Can not use -q!G");
99790792Sgshapiro					ExitStat = EX_USAGE;
99890792Sgshapiro					break;
99990792Sgshapiro				}
100090792Sgshapiro				if (queuegroup != NULL)
100190792Sgshapiro				{
100290792Sgshapiro					usrerr("Can not use multiple -qG options");
100390792Sgshapiro					ExitStat = EX_USAGE;
100490792Sgshapiro					break;
100590792Sgshapiro				}
100690792Sgshapiro				queuegroup = newstr(&optarg[1]);
100790792Sgshapiro				break;
100890792Sgshapiro
100990792Sgshapiro			  case 'I': /* Limit by ID */
101064562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
101138032Speter				new->queue_match = newstr(&optarg[1]);
101290792Sgshapiro				new->queue_negate = negate;
101338032Speter				new->queue_next = QueueLimitId;
101438032Speter				QueueLimitId = new;
101538032Speter				break;
101638032Speter
101790792Sgshapiro			  case 'R': /* Limit by recipient */
101864562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
101938032Speter				new->queue_match = newstr(&optarg[1]);
102090792Sgshapiro				new->queue_negate = negate;
102138032Speter				new->queue_next = QueueLimitRecipient;
102238032Speter				QueueLimitRecipient = new;
102338032Speter				break;
102438032Speter
102590792Sgshapiro			  case 'S': /* Limit by sender */
102664562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
102738032Speter				new->queue_match = newstr(&optarg[1]);
102890792Sgshapiro				new->queue_negate = negate;
102938032Speter				new->queue_next = QueueLimitSender;
103038032Speter				QueueLimitSender = new;
103138032Speter				break;
103238032Speter
103390792Sgshapiro			  case 'f': /* foreground queue run */
103490792Sgshapiro				foregroundqueue  = true;
103590792Sgshapiro				break;
103690792Sgshapiro
103790792Sgshapiro#if _FFR_QUARANTINE
103890792Sgshapiro			  case 'Q': /* Limit by quarantine message */
103990792Sgshapiro				if (optarg[1] != '\0')
104090792Sgshapiro				{
104190792Sgshapiro					new = (QUEUE_CHAR *) xalloc(sizeof *new);
104290792Sgshapiro					new->queue_match = newstr(&optarg[1]);
104390792Sgshapiro					new->queue_negate = negate;
104490792Sgshapiro					new->queue_next = QueueLimitQuarantine;
104590792Sgshapiro					QueueLimitQuarantine = new;
104690792Sgshapiro				}
104790792Sgshapiro				QueueMode = QM_QUARANTINE;
104890792Sgshapiro				break;
104990792Sgshapiro
105090792Sgshapiro			  case 'L': /* act on lost items */
105190792Sgshapiro				QueueMode = QM_LOST;
105290792Sgshapiro				break;
105390792Sgshapiro#endif /* _FFR_QUARANTINE */
105490792Sgshapiro
105590792Sgshapiro			  case 'p': /* Persistent queue */
105690792Sgshapiro				queuepersistent = true;
105790792Sgshapiro				if (QueueIntvl == 0)
105890792Sgshapiro					QueueIntvl = 1;
105990792Sgshapiro				if (optarg[1] == '\0')
106090792Sgshapiro					break;
106190792Sgshapiro				++optarg;
106290792Sgshapiro				/* FALLTHROUGH */
106390792Sgshapiro
106438032Speter			  default:
106564562Sgshapiro				i = Errors;
106638032Speter				QueueIntvl = convtime(optarg, 'm');
106798841Sgshapiro				if (QueueIntvl < 0)
106898841Sgshapiro				{
106998841Sgshapiro					usrerr("Invalid -q value");
107098841Sgshapiro					ExitStat = EX_USAGE;
107198841Sgshapiro				}
107264562Sgshapiro
107364562Sgshapiro				/* check for bad conversion */
107464562Sgshapiro				if (i < Errors)
107564562Sgshapiro					ExitStat = EX_USAGE;
107638032Speter				break;
107738032Speter			}
107838032Speter			break;
107938032Speter
108038032Speter		  case 'R':	/* DSN RET: what to return */
108190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
108290792Sgshapiro			if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
108338032Speter			{
108438032Speter				usrerr("Duplicate -R flag");
108538032Speter				ExitStat = EX_USAGE;
108638032Speter				break;
108738032Speter			}
108890792Sgshapiro			BlankEnvelope.e_flags |= EF_RET_PARAM;
108990792Sgshapiro			if (sm_strcasecmp(optarg, "hdrs") == 0)
109090792Sgshapiro				BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
109190792Sgshapiro			else if (sm_strcasecmp(optarg, "full") != 0)
109238032Speter			{
109338032Speter				usrerr("Invalid -R value");
109438032Speter				ExitStat = EX_USAGE;
109538032Speter			}
109690792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
109790792Sgshapiro				  macid("{dsn_ret}"), optarg);
109838032Speter			break;
109938032Speter
110038032Speter		  case 't':	/* read recipients from message */
110190792Sgshapiro			CHECK_AGAINST_OPMODE(j);
110290792Sgshapiro			GrabTo = true;
110338032Speter			break;
110438032Speter
110538032Speter		  case 'V':	/* DSN ENVID: set "original" envelope id */
110690792Sgshapiro			CHECK_AGAINST_OPMODE(j);
110738032Speter			if (!xtextok(optarg))
110838032Speter			{
110938032Speter				usrerr("Invalid syntax in -V flag");
111038032Speter				ExitStat = EX_USAGE;
111138032Speter			}
111238032Speter			else
111364562Sgshapiro			{
111490792Sgshapiro				BlankEnvelope.e_envid = newstr(optarg);
111590792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
111690792Sgshapiro					  macid("{dsn_envid}"), optarg);
111764562Sgshapiro			}
111838032Speter			break;
111938032Speter
112038032Speter		  case 'X':	/* traffic log file */
112190792Sgshapiro			dp = drop_privileges(true);
112264562Sgshapiro			setstat(dp);
112364562Sgshapiro			if (stat(optarg, &traf_st) == 0 &&
112464562Sgshapiro			    S_ISFIFO(traf_st.st_mode))
112590792Sgshapiro				TrafficLogFile = sm_io_open(SmFtStdio,
112690792Sgshapiro							    SM_TIME_DEFAULT,
112790792Sgshapiro							    optarg,
112890792Sgshapiro							    SM_IO_WRONLY, NULL);
112964562Sgshapiro			else
113090792Sgshapiro				TrafficLogFile = sm_io_open(SmFtStdio,
113190792Sgshapiro							    SM_TIME_DEFAULT,
113290792Sgshapiro							    optarg,
113390792Sgshapiro							    SM_IO_APPEND, NULL);
113438032Speter			if (TrafficLogFile == NULL)
113538032Speter			{
113638032Speter				syserr("cannot open %s", optarg);
113738032Speter				ExitStat = EX_CANTCREAT;
113838032Speter				break;
113938032Speter			}
114090792Sgshapiro			(void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
114190792Sgshapiro					     NULL, SM_IO_LBF, 0);
114238032Speter			break;
114338032Speter
114438032Speter			/* compatibility flags */
114538032Speter		  case 'c':	/* connect to non-local mailers */
114638032Speter		  case 'i':	/* don't let dot stop me */
114738032Speter		  case 'm':	/* send to me too */
114838032Speter		  case 'T':	/* set timeout interval */
114938032Speter		  case 'v':	/* give blow-by-blow description */
115090792Sgshapiro			setoption(j, "T", false, true, &BlankEnvelope);
115138032Speter			break;
115238032Speter
115338032Speter		  case 'e':	/* error message disposition */
115438032Speter		  case 'M':	/* define macro */
115590792Sgshapiro			setoption(j, optarg, false, true, &BlankEnvelope);
115638032Speter			break;
115738032Speter
115838032Speter		  case 's':	/* save From lines in headers */
115990792Sgshapiro			setoption('f', "T", false, true, &BlankEnvelope);
116038032Speter			break;
116138032Speter
116264562Sgshapiro#ifdef DBM
116338032Speter		  case 'I':	/* initialize alias DBM file */
116490792Sgshapiro			set_op_mode(MD_INITALIAS);
116538032Speter			break;
116664562Sgshapiro#endif /* DBM */
116738032Speter
116864562Sgshapiro#if defined(__osf__) || defined(_AIX3)
116938032Speter		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
117038032Speter			break;
117164562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */
117264562Sgshapiro#if defined(sony_news)
117338032Speter		  case 'E':
117438032Speter		  case 'J':	/* ignore flags for Japanese code conversion
117564562Sgshapiro				   implemented on Sony NEWS */
117638032Speter			break;
117764562Sgshapiro#endif /* defined(sony_news) */
117838032Speter
117938032Speter		  default:
118090792Sgshapiro			finis(true, true, EX_USAGE);
118190792Sgshapiro			/* NOTREACHED */
118238032Speter			break;
118338032Speter		}
118438032Speter	}
118538032Speter
118690792Sgshapiro	/* if we've had errors so far, exit now */
118790792Sgshapiro	if ((ExitStat != EX_OK && OpMode != MD_TEST) ||
118890792Sgshapiro	    ExitStat == EX_OSERR)
118964562Sgshapiro	{
119090792Sgshapiro		finis(false, true, ExitStat);
119190792Sgshapiro		/* NOTREACHED */
119264562Sgshapiro	}
119390792Sgshapiro
119490792Sgshapiro	if (bitset(SUBMIT_MTA, SubmitMode))
119564562Sgshapiro	{
119698841Sgshapiro		/* If set daemon_flags on command line, don't reset it */
119798841Sgshapiro		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
119898841Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
119998841Sgshapiro				  macid("{daemon_flags}"), "CC f");
120064562Sgshapiro	}
120190792Sgshapiro	else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
120264562Sgshapiro	{
120390792Sgshapiro		SubmitMode = SUBMIT_MSA;
120498841Sgshapiro
120598841Sgshapiro		/* If set daemon_flags on command line, don't reset it */
120698841Sgshapiro		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
120798841Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
120898841Sgshapiro				  macid("{daemon_flags}"), "c u");
120964562Sgshapiro	}
121064562Sgshapiro
121138032Speter	/*
121238032Speter	**  Do basic initialization.
121338032Speter	**	Read system control file.
121438032Speter	**	Extract special fields for local use.
121538032Speter	*/
121638032Speter
121738032Speter#if XDEBUG
121838032Speter	checkfd012("before readcf");
121964562Sgshapiro#endif /* XDEBUG */
122090792Sgshapiro	vendor_pre_defaults(&BlankEnvelope);
122164562Sgshapiro
122290792Sgshapiro	readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
122390792Sgshapiro			 safecf, &BlankEnvelope);
122490792Sgshapiro#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
122590792Sgshapiro	ConfigFileRead = true;
122690792Sgshapiro#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
122790792Sgshapiro	vendor_post_defaults(&BlankEnvelope);
122838032Speter
122990792Sgshapiro	/* now we can complain about missing fds */
123090792Sgshapiro	if (MissingFds != 0 && LogLevel > 8)
123190792Sgshapiro	{
123290792Sgshapiro		char mbuf[MAXLINE];
123390792Sgshapiro
123490792Sgshapiro		mbuf[0] = '\0';
123590792Sgshapiro		if (bitset(1 << STDIN_FILENO, MissingFds))
123690792Sgshapiro			(void) sm_strlcat(mbuf, ", stdin", sizeof mbuf);
123790792Sgshapiro		if (bitset(1 << STDOUT_FILENO, MissingFds))
123890792Sgshapiro			(void) sm_strlcat(mbuf, ", stdout", sizeof mbuf);
123990792Sgshapiro		if (bitset(1 << STDERR_FILENO, MissingFds))
124090792Sgshapiro			(void) sm_strlcat(mbuf, ", stderr", sizeof mbuf);
124190792Sgshapiro
124290792Sgshapiro		/* Notice: fill_errno is from high above: fill_fd() */
124390792Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
124490792Sgshapiro			  "File descriptors missing on startup: %s; %s",
124590792Sgshapiro			  &mbuf[2], sm_errstring(fill_errno));
124690792Sgshapiro	}
124790792Sgshapiro
124877349Sgshapiro	/* Remove the ability for a normal user to send signals */
124990792Sgshapiro	if (RealUid != 0 && RealUid != geteuid())
125077349Sgshapiro	{
125177349Sgshapiro		uid_t new_uid = geteuid();
125277349Sgshapiro
125377349Sgshapiro#if HASSETREUID
125477349Sgshapiro		/*
125577349Sgshapiro		**  Since we can differentiate between uid and euid,
125677349Sgshapiro		**  make the uid a different user so the real user
125777349Sgshapiro		**  can't send signals.  However, it doesn't need to be
125877349Sgshapiro		**  root (euid has root).
125977349Sgshapiro		*/
126077349Sgshapiro
126177349Sgshapiro		if (new_uid == 0)
126277349Sgshapiro			new_uid = DefUid;
126377349Sgshapiro		if (tTd(47, 5))
126490792Sgshapiro			sm_dprintf("Changing real uid to %d\n", (int) new_uid);
126577349Sgshapiro		if (setreuid(new_uid, geteuid()) < 0)
126677349Sgshapiro		{
126777349Sgshapiro			syserr("main: setreuid(%d, %d) failed",
126877349Sgshapiro			       (int) new_uid, (int) geteuid());
126990792Sgshapiro			finis(false, true, EX_OSERR);
127077349Sgshapiro			/* NOTREACHED */
127177349Sgshapiro		}
127277349Sgshapiro		if (tTd(47, 10))
127390792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
127490792Sgshapiro				   (int) geteuid(), (int) getuid());
127577349Sgshapiro#else /* HASSETREUID */
127677349Sgshapiro		/*
127777349Sgshapiro		**  Have to change both effective and real so need to
127877349Sgshapiro		**  change them both to effective to keep privs.
127977349Sgshapiro		*/
128077349Sgshapiro
128177349Sgshapiro		if (tTd(47, 5))
128290792Sgshapiro			sm_dprintf("Changing uid to %d\n", (int) new_uid);
128377349Sgshapiro		if (setuid(new_uid) < 0)
128477349Sgshapiro		{
128577349Sgshapiro			syserr("main: setuid(%d) failed", (int) new_uid);
128690792Sgshapiro			finis(false, true, EX_OSERR);
128777349Sgshapiro			/* NOTREACHED */
128877349Sgshapiro		}
128977349Sgshapiro		if (tTd(47, 10))
129090792Sgshapiro			sm_dprintf("Now running as e/ruid %d:%d\n",
129190792Sgshapiro				   (int) geteuid(), (int) getuid());
129277349Sgshapiro#endif /* HASSETREUID */
129377349Sgshapiro	}
129477349Sgshapiro
129590792Sgshapiro#if NAMED_BIND
129690792Sgshapiro	if (FallBackMX != NULL)
129790792Sgshapiro		(void) getfallbackmxrr(FallBackMX);
129890792Sgshapiro#endif /* NAMED_BIND */
129990792Sgshapiro
130090792Sgshapiro	if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER)
130190792Sgshapiro	{
130290792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
130390792Sgshapiro				     "WARNING: SuperSafe=interactive should only be used with\n         DeliveryMode=interactive\n");
130490792Sgshapiro	}
130590792Sgshapiro
130690792Sgshapiro	if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
130790792Sgshapiro	{
130890792Sgshapiro		usrerr("Mail submission program cannot be used as daemon");
130990792Sgshapiro		finis(false, true, EX_USAGE);
131090792Sgshapiro	}
131190792Sgshapiro
131290792Sgshapiro	if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
131390792Sgshapiro	    OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
131490792Sgshapiro	    OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
131590792Sgshapiro		makeworkgroups();
131690792Sgshapiro
131777349Sgshapiro	/* set up the basic signal handlers */
131890792Sgshapiro	if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
131990792Sgshapiro		(void) sm_signal(SIGINT, intsig);
132090792Sgshapiro	(void) sm_signal(SIGTERM, intsig);
132177349Sgshapiro
132238032Speter	/* Enforce use of local time (null string overrides this) */
132338032Speter	if (TimeZoneSpec == NULL)
132438032Speter		unsetenv("TZ");
132538032Speter	else if (TimeZoneSpec[0] != '\0')
132638032Speter		setuserenv("TZ", TimeZoneSpec);
132738032Speter	else
132838032Speter		setuserenv("TZ", NULL);
132938032Speter	tzset();
133038032Speter
133190792Sgshapiro	/* initialize mailbox database */
133290792Sgshapiro	i = sm_mbdb_initialize(Mbdb);
133390792Sgshapiro	if (i != EX_OK)
133490792Sgshapiro	{
133590792Sgshapiro		usrerr("Can't initialize mailbox database \"%s\": %s",
133690792Sgshapiro		       Mbdb, sm_strexit(i));
133790792Sgshapiro		ExitStat = i;
133890792Sgshapiro	}
133990792Sgshapiro
134038032Speter	/* avoid denial-of-service attacks */
134138032Speter	resetlimits();
134238032Speter
134390792Sgshapiro	if (OpMode == MD_TEST)
134438032Speter	{
134590792Sgshapiro		/* can't be done after readcf if RunAs* is used */
134690792Sgshapiro		dp = drop_privileges(true);
134790792Sgshapiro		if (dp != EX_OK)
134890792Sgshapiro		{
134990792Sgshapiro			finis(false, true, dp);
135090792Sgshapiro			/* NOTREACHED */
135190792Sgshapiro		}
135290792Sgshapiro	}
135390792Sgshapiro	else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
135490792Sgshapiro	{
135538032Speter		/* drop privileges -- daemon mode done after socket/bind */
135690792Sgshapiro		dp = drop_privileges(false);
135764562Sgshapiro		setstat(dp);
135890792Sgshapiro		if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
135990792Sgshapiro		{
136090792Sgshapiro			usrerr("Mail submission program must have RunAsUser set to non root user");
136190792Sgshapiro			finis(false, true, EX_CONFIG);
136290792Sgshapiro			/* NOTREACHED */
136390792Sgshapiro		}
136438032Speter	}
136538032Speter
136664562Sgshapiro#if NAMED_BIND
136764562Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
136864562Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
136964562Sgshapiro#endif /* NAMED_BIND */
137064562Sgshapiro
137138032Speter	/*
137238032Speter	**  Find our real host name for future logging.
137338032Speter	*/
137438032Speter
137564562Sgshapiro	authinfo = getauthinfo(STDIN_FILENO, &forged);
137690792Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
137738032Speter
137838032Speter	/* suppress error printing if errors mailed back or whatever */
137990792Sgshapiro	if (BlankEnvelope.e_errormode != EM_PRINT)
138090792Sgshapiro		HoldErrs = true;
138138032Speter
138238032Speter	/* set up the $=m class now, after .cf has a chance to redefine $m */
138390792Sgshapiro	expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope);
138473188Sgshapiro	if (jbuf[0] != '\0')
138573188Sgshapiro		setclass('m', jbuf);
138638032Speter
138738032Speter	/* probe interfaces and locate any additional names */
138890792Sgshapiro	if (DontProbeInterfaces != DPI_PROBENONE)
138938032Speter		load_if_names();
139038032Speter
139190792Sgshapiro	if (tTd(0, 10))
139290792Sgshapiro	{
139390792Sgshapiro		/* Now we know which .cf file we use */
139490792Sgshapiro		sm_dprintf("     Conf file:\t%s (selected)\n",
139590792Sgshapiro			   getcfname(OpMode, SubmitMode, cftype, conffile));
139690792Sgshapiro		sm_dprintf("      Pid file:\t%s (selected)\n", PidFile);
139790792Sgshapiro	}
139890792Sgshapiro
139938032Speter	if (tTd(0, 1))
140038032Speter	{
140190792Sgshapiro		sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
140290792Sgshapiro		sm_dprintf("\n      (short domain name) $w = ");
140390792Sgshapiro		xputs(macvalue('w', &BlankEnvelope));
140490792Sgshapiro		sm_dprintf("\n  (canonical domain name) $j = ");
140590792Sgshapiro		xputs(macvalue('j', &BlankEnvelope));
140690792Sgshapiro		sm_dprintf("\n         (subdomain name) $m = ");
140790792Sgshapiro		xputs(macvalue('m', &BlankEnvelope));
140890792Sgshapiro		sm_dprintf("\n              (node name) $k = ");
140990792Sgshapiro		xputs(macvalue('k', &BlankEnvelope));
141090792Sgshapiro		sm_dprintf("\n========================================================\n\n");
141138032Speter	}
141238032Speter
141338032Speter	/*
141438032Speter	**  Do more command line checking -- these are things that
141538032Speter	**  have to modify the results of reading the config file.
141638032Speter	*/
141738032Speter
141838032Speter	/* process authorization warnings from command line */
141938032Speter	if (warn_C_flag)
142090792Sgshapiro		auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
142190792Sgshapiro			     RealUserName, conffile);
142264562Sgshapiro	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
142390792Sgshapiro		auth_warning(&BlankEnvelope, "Processed from queue %s",
142490792Sgshapiro			     QueueDir);
142590792Sgshapiro	if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
142690792Sgshapiro	    RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
142790792Sgshapiro		sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
142890792Sgshapiro			  (int) RealUid);
142938032Speter
143038032Speter	/* check body type for legality */
143190792Sgshapiro	i = check_bodytype(BlankEnvelope.e_bodytype);
143290792Sgshapiro	if (i == BODYTYPE_ILLEGAL)
143338032Speter	{
143490792Sgshapiro		usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
143590792Sgshapiro		BlankEnvelope.e_bodytype = NULL;
143638032Speter	}
143790792Sgshapiro	else if (i != BODYTYPE_NONE)
143890792Sgshapiro		SevenBitInput = (i == BODYTYPE_7BIT);
143938032Speter
144038032Speter	/* tweak default DSN notifications */
144138032Speter	if (DefaultNotify == 0)
144238032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
144338032Speter
144438032Speter	/* be sure we don't pick up bogus HOSTALIASES environment variable */
144564562Sgshapiro	if (OpMode == MD_QUEUERUN && RealUid != 0)
144638032Speter		(void) unsetenv("HOSTALIASES");
144738032Speter
144838032Speter	/* check for sane configuration level */
144938032Speter	if (ConfigLevel > MAXCONFIGLEVEL)
145038032Speter	{
145138032Speter		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
145290792Sgshapiro		       ConfigLevel, Version, MAXCONFIGLEVEL);
145338032Speter	}
145438032Speter
145538032Speter	/* need MCI cache to have persistence */
145638032Speter	if (HostStatDir != NULL && MaxMciCache == 0)
145738032Speter	{
145838032Speter		HostStatDir = NULL;
145990792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
146090792Sgshapiro				     "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
146138032Speter	}
146238032Speter
146338032Speter	/* need HostStatusDir in order to have SingleThreadDelivery */
146438032Speter	if (SingleThreadDelivery && HostStatDir == NULL)
146538032Speter	{
146690792Sgshapiro		SingleThreadDelivery = false;
146790792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
146890792Sgshapiro				     "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
146938032Speter	}
147038032Speter
147138032Speter	/* check for permissions */
147290792Sgshapiro	if (RealUid != 0 &&
147342575Speter	    RealUid != TrustedUid)
147438032Speter	{
147590792Sgshapiro		char *action = NULL;
147690792Sgshapiro
147790792Sgshapiro		switch (OpMode)
147890792Sgshapiro		{
147990792Sgshapiro		  case MD_QUEUERUN:
148090792Sgshapiro#if _FFR_QUARANTINE
148190792Sgshapiro			if (quarantining != NULL)
148290792Sgshapiro				action = "quarantine jobs";
148390792Sgshapiro			else
148490792Sgshapiro#endif /* _FFR_QUARANTINE */
148590792Sgshapiro			/* Normal users can do a single queue run */
148690792Sgshapiro			if (QueueIntvl == 0)
148790792Sgshapiro				break;
148890792Sgshapiro
148990792Sgshapiro			/* but not persistent queue runners */
149090792Sgshapiro			if (action == NULL)
149190792Sgshapiro				action = "start a queue runner daemon";
149290792Sgshapiro			/* FALLTHROUGH */
149390792Sgshapiro
149490792Sgshapiro		  case MD_PURGESTAT:
149590792Sgshapiro			if (action == NULL)
149690792Sgshapiro				action = "purge host status";
149790792Sgshapiro			/* FALLTHROUGH */
149890792Sgshapiro
149990792Sgshapiro		  case MD_DAEMON:
150090792Sgshapiro		  case MD_FGDAEMON:
150190792Sgshapiro			if (action == NULL)
150290792Sgshapiro				action = "run daemon";
150390792Sgshapiro
150490792Sgshapiro			if (tTd(65, 1))
150590792Sgshapiro				sm_dprintf("Deny user %d attempt to %s\n",
150690792Sgshapiro					   (int) RealUid, action);
150790792Sgshapiro
150890792Sgshapiro			if (LogLevel > 1)
150990792Sgshapiro				sm_syslog(LOG_ALERT, NOQID,
151090792Sgshapiro					  "user %d attempted to %s",
151190792Sgshapiro					  (int) RealUid, action);
151290792Sgshapiro			HoldErrs = false;
151390792Sgshapiro			usrerr("Permission denied (real uid not trusted)");
151490792Sgshapiro			finis(false, true, EX_USAGE);
151590792Sgshapiro			/* NOTREACHED */
151690792Sgshapiro			break;
151790792Sgshapiro
151890792Sgshapiro		  case MD_VERIFY:
151990792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
152090792Sgshapiro			{
152190792Sgshapiro				/*
152290792Sgshapiro				**  If -bv and RestrictExpand,
152390792Sgshapiro				**  drop privs to prevent normal
152490792Sgshapiro				**  users from reading private
152590792Sgshapiro				**  aliases/forwards/:include:s
152690792Sgshapiro				*/
152790792Sgshapiro
152890792Sgshapiro				if (tTd(65, 1))
152990792Sgshapiro					sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
153090792Sgshapiro						   (int) RealUid);
153190792Sgshapiro
153290792Sgshapiro				dp = drop_privileges(true);
153390792Sgshapiro
153490792Sgshapiro				/* Fake address safety */
153590792Sgshapiro				if (tTd(65, 1))
153690792Sgshapiro					sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
153790792Sgshapiro				setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
153890792Sgshapiro
153990792Sgshapiro				if (dp != EX_OK)
154090792Sgshapiro				{
154190792Sgshapiro					if (tTd(65, 1))
154290792Sgshapiro						sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
154390792Sgshapiro							   (int) RealUid);
154490792Sgshapiro					CurEnv->e_id = NULL;
154590792Sgshapiro					finis(true, true, dp);
154690792Sgshapiro					/* NOTREACHED */
154790792Sgshapiro				}
154890792Sgshapiro			}
154990792Sgshapiro			break;
155090792Sgshapiro
155190792Sgshapiro		  case MD_TEST:
155290792Sgshapiro		  case MD_PRINT:
155390792Sgshapiro		  case MD_PRINTNQE:
155490792Sgshapiro		  case MD_FREEZE:
155590792Sgshapiro		  case MD_HOSTSTAT:
155690792Sgshapiro			/* Nothing special to check */
155790792Sgshapiro			break;
155890792Sgshapiro
155990792Sgshapiro		  case MD_INITALIAS:
156090792Sgshapiro			if (!wordinclass(RealUserName, 't'))
156190792Sgshapiro			{
156290792Sgshapiro				if (tTd(65, 1))
156390792Sgshapiro					sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
156490792Sgshapiro						   (int) RealUid);
156590792Sgshapiro				if (LogLevel > 1)
156690792Sgshapiro					sm_syslog(LOG_ALERT, NOQID,
156790792Sgshapiro						  "user %d attempted to rebuild the alias map",
156890792Sgshapiro						  (int) RealUid);
156990792Sgshapiro				HoldErrs = false;
157090792Sgshapiro				usrerr("Permission denied (real uid not trusted)");
157190792Sgshapiro				finis(false, true, EX_USAGE);
157290792Sgshapiro				/* NOTREACHED */
157390792Sgshapiro			}
157490792Sgshapiro			if (UseMSP)
157590792Sgshapiro			{
157690792Sgshapiro				HoldErrs = false;
157790792Sgshapiro				usrerr("User %d cannot rebuild aliases in mail submission program",
157890792Sgshapiro				       (int) RealUid);
157990792Sgshapiro				finis(false, true, EX_USAGE);
158090792Sgshapiro				/* NOTREACHED */
158190792Sgshapiro			}
158290792Sgshapiro			/* FALLTHROUGH */
158390792Sgshapiro
158490792Sgshapiro		  default:
158590792Sgshapiro			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
158690792Sgshapiro			    Verbose != 0)
158790792Sgshapiro			{
158890792Sgshapiro				/*
158990792Sgshapiro				**  If -v and RestrictExpand, reset
159090792Sgshapiro				**  Verbose to prevent normal users
159190792Sgshapiro				**  from seeing the expansion of
159290792Sgshapiro				**  aliases/forwards/:include:s
159390792Sgshapiro				*/
159490792Sgshapiro
159590792Sgshapiro				if (tTd(65, 1))
159690792Sgshapiro					sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
159790792Sgshapiro						   (int) RealUid);
159890792Sgshapiro				Verbose = 0;
159990792Sgshapiro			}
160090792Sgshapiro			break;
160190792Sgshapiro		}
160238032Speter	}
160338032Speter
160438032Speter	if (MeToo)
160538032Speter		BlankEnvelope.e_flags |= EF_METOO;
160638032Speter
160738032Speter	switch (OpMode)
160838032Speter	{
160938032Speter	  case MD_TEST:
161038032Speter		/* don't have persistent host status in test mode */
161138032Speter		HostStatDir = NULL;
161238032Speter		if (Verbose == 0)
161338032Speter			Verbose = 2;
161490792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
161590792Sgshapiro		HoldErrs = false;
161638032Speter		break;
161738032Speter
161838032Speter	  case MD_VERIFY:
161990792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
162090792Sgshapiro		HoldErrs = false;
162138032Speter		/* arrange to exit cleanly on hangup signal */
162290792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
162390792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
162490792Sgshapiro		if (geteuid() != 0)
162590792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
162690792Sgshapiro					     "Notice: -bv may give misleading output for non-privileged user\n");
162738032Speter		break;
162838032Speter
162938032Speter	  case MD_FGDAEMON:
163090792Sgshapiro		run_in_foreground = true;
163190792Sgshapiro		set_op_mode(MD_DAEMON);
163264562Sgshapiro		/* FALLTHROUGH */
163338032Speter
163438032Speter	  case MD_DAEMON:
163590792Sgshapiro		vendor_daemon_setup(&BlankEnvelope);
163638032Speter
163738032Speter		/* remove things that don't make sense in daemon mode */
163838032Speter		FullName = NULL;
163990792Sgshapiro		GrabTo = false;
164038032Speter
164138032Speter		/* arrange to restart on hangup signal */
164238032Speter		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
164338032Speter			sm_syslog(LOG_WARNING, NOQID,
164464562Sgshapiro				  "daemon invoked without full pathname; kill -1 won't work");
164538032Speter		break;
164638032Speter
164738032Speter	  case MD_INITALIAS:
164838032Speter		Verbose = 2;
164990792Sgshapiro		BlankEnvelope.e_errormode = EM_PRINT;
165090792Sgshapiro		HoldErrs = false;
165164562Sgshapiro		/* FALLTHROUGH */
165238032Speter
165338032Speter	  default:
165438032Speter		/* arrange to exit cleanly on hangup signal */
165590792Sgshapiro		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
165690792Sgshapiro			(void) sm_signal(SIGHUP, intsig);
165738032Speter		break;
165838032Speter	}
165938032Speter
166038032Speter	/* special considerations for FullName */
166138032Speter	if (FullName != NULL)
166238032Speter	{
166338032Speter		char *full = NULL;
166438032Speter
166538032Speter		/* full names can't have newlines */
166664562Sgshapiro		if (strchr(FullName, '\n') != NULL)
166738032Speter		{
166890792Sgshapiro			full = newstr(denlstring(FullName, true, true));
166973188Sgshapiro			FullName = full;
167038032Speter		}
167173188Sgshapiro
167238032Speter		/* check for characters that may have to be quoted */
167338032Speter		if (!rfc822_string(FullName))
167438032Speter		{
167538032Speter			/*
167638032Speter			**  Quote a full name with special characters
167738032Speter			**  as a comment so crackaddr() doesn't destroy
167838032Speter			**  the name portion of the address.
167938032Speter			*/
168073188Sgshapiro
168190792Sgshapiro			FullName = addquotes(FullName, NULL);
168238032Speter			if (full != NULL)
168390792Sgshapiro				sm_free(full);  /* XXX */
168438032Speter		}
168538032Speter	}
168638032Speter
168738032Speter	/* do heuristic mode adjustment */
168838032Speter	if (Verbose)
168938032Speter	{
169038032Speter		/* turn off noconnect option */
169190792Sgshapiro		setoption('c', "F", true, false, &BlankEnvelope);
169238032Speter
169338032Speter		/* turn on interactive delivery */
169490792Sgshapiro		setoption('d', "", true, false, &BlankEnvelope);
169538032Speter	}
169638032Speter
169742575Speter#ifdef VENDOR_CODE
169842575Speter	/* check for vendor mismatch */
169942575Speter	if (VendorCode != VENDOR_CODE)
170042575Speter	{
170142575Speter		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
170242575Speter			getvendor(VENDOR_CODE), getvendor(VendorCode));
170342575Speter	}
170464562Sgshapiro#endif /* VENDOR_CODE */
170564562Sgshapiro
170638032Speter	/* check for out of date configuration level */
170738032Speter	if (ConfigLevel < MAXCONFIGLEVEL)
170838032Speter	{
170938032Speter		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
171038032Speter			Version, MAXCONFIGLEVEL, ConfigLevel);
171138032Speter	}
171238032Speter
171338032Speter	if (ConfigLevel < 3)
171490792Sgshapiro		UseErrorsTo = true;
171538032Speter
171638032Speter	/* set options that were previous macros */
171738032Speter	if (SmtpGreeting == NULL)
171838032Speter	{
171990792Sgshapiro		if (ConfigLevel < 7 &&
172090792Sgshapiro		    (p = macvalue('e', &BlankEnvelope)) != NULL)
172138032Speter			SmtpGreeting = newstr(p);
172238032Speter		else
172338032Speter			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
172438032Speter	}
172538032Speter	if (UnixFromLine == NULL)
172638032Speter	{
172790792Sgshapiro		if (ConfigLevel < 7 &&
172890792Sgshapiro		    (p = macvalue('l', &BlankEnvelope)) != NULL)
172938032Speter			UnixFromLine = newstr(p);
173038032Speter		else
173138032Speter			UnixFromLine = "From \201g  \201d";
173238032Speter	}
173364562Sgshapiro	SmtpError[0] = '\0';
173438032Speter
173538032Speter	/* our name for SMTP codes */
173690792Sgshapiro	expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
173773188Sgshapiro	if (jbuf[0] == '\0')
173890792Sgshapiro		PSTRSET(MyHostName, "localhost");
173973188Sgshapiro	else
174090792Sgshapiro		PSTRSET(MyHostName, jbuf);
174173188Sgshapiro	if (strchr(MyHostName, '.') == NULL)
174238032Speter		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
174373188Sgshapiro			MyHostName);
174438032Speter
174538032Speter	/* make certain that this name is part of the $=w class */
174638032Speter	setclass('w', MyHostName);
174738032Speter
174890792Sgshapiro	/* fill in the structure of the *default* queue */
174990792Sgshapiro	st = stab("mqueue", ST_QUEUE, ST_FIND);
175090792Sgshapiro	if (st == NULL)
175190792Sgshapiro		syserr("No default queue (mqueue) defined");
175290792Sgshapiro	else
175390792Sgshapiro		set_def_queueval(st->s_quegrp, true);
175490792Sgshapiro
175538032Speter	/* the indices of built-in mailers */
175638032Speter	st = stab("local", ST_MAILER, ST_FIND);
175738032Speter	if (st != NULL)
175838032Speter		LocalMailer = st->s_mailer;
175938032Speter	else if (OpMode != MD_TEST || !warn_C_flag)
176038032Speter		syserr("No local mailer defined");
176138032Speter
176238032Speter	st = stab("prog", ST_MAILER, ST_FIND);
176338032Speter	if (st == NULL)
176438032Speter		syserr("No prog mailer defined");
176538032Speter	else
176638032Speter	{
176738032Speter		ProgMailer = st->s_mailer;
176838032Speter		clrbitn(M_MUSER, ProgMailer->m_flags);
176938032Speter	}
177038032Speter
177138032Speter	st = stab("*file*", ST_MAILER, ST_FIND);
177238032Speter	if (st == NULL)
177338032Speter		syserr("No *file* mailer defined");
177438032Speter	else
177538032Speter	{
177638032Speter		FileMailer = st->s_mailer;
177738032Speter		clrbitn(M_MUSER, FileMailer->m_flags);
177838032Speter	}
177938032Speter
178038032Speter	st = stab("*include*", ST_MAILER, ST_FIND);
178138032Speter	if (st == NULL)
178238032Speter		syserr("No *include* mailer defined");
178338032Speter	else
178438032Speter		InclMailer = st->s_mailer;
178538032Speter
178638032Speter	if (ConfigLevel < 6)
178738032Speter	{
178838032Speter		/* heuristic tweaking of local mailer for back compat */
178938032Speter		if (LocalMailer != NULL)
179038032Speter		{
179138032Speter			setbitn(M_ALIASABLE, LocalMailer->m_flags);
179238032Speter			setbitn(M_HASPWENT, LocalMailer->m_flags);
179338032Speter			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
179438032Speter			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
179538032Speter			setbitn(M_CHECKPROG, LocalMailer->m_flags);
179638032Speter			setbitn(M_CHECKFILE, LocalMailer->m_flags);
179738032Speter			setbitn(M_CHECKUDB, LocalMailer->m_flags);
179838032Speter		}
179938032Speter		if (ProgMailer != NULL)
180038032Speter			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
180138032Speter		if (FileMailer != NULL)
180238032Speter			setbitn(M_RUNASRCPT, FileMailer->m_flags);
180338032Speter	}
180438032Speter	if (ConfigLevel < 7)
180538032Speter	{
180638032Speter		if (LocalMailer != NULL)
180738032Speter			setbitn(M_VRFY250, LocalMailer->m_flags);
180838032Speter		if (ProgMailer != NULL)
180938032Speter			setbitn(M_VRFY250, ProgMailer->m_flags);
181038032Speter		if (FileMailer != NULL)
181138032Speter			setbitn(M_VRFY250, FileMailer->m_flags);
181238032Speter	}
181338032Speter
181438032Speter	/* MIME Content-Types that cannot be transfer encoded */
181538032Speter	setclass('n', "multipart/signed");
181638032Speter
181738032Speter	/* MIME message/xxx subtypes that can be treated as messages */
181838032Speter	setclass('s', "rfc822");
181938032Speter
182038032Speter	/* MIME Content-Transfer-Encodings that can be encoded */
182138032Speter	setclass('e', "7bit");
182238032Speter	setclass('e', "8bit");
182338032Speter	setclass('e', "binary");
182438032Speter
182538032Speter#ifdef USE_B_CLASS
182638032Speter	/* MIME Content-Types that should be treated as binary */
182738032Speter	setclass('b', "image");
182838032Speter	setclass('b', "audio");
182938032Speter	setclass('b', "video");
183038032Speter	setclass('b', "application/octet-stream");
183164562Sgshapiro#endif /* USE_B_CLASS */
183238032Speter
183342575Speter	/* MIME headers which have fields to check for overflow */
183490792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
183590792Sgshapiro	setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
183642575Speter
183742575Speter	/* MIME headers to check for length overflow */
183890792Sgshapiro	setclass(macid("{checkMIMETextHeaders}"), "content-description");
183942575Speter
184042575Speter	/* MIME headers to check for overflow and rebalance */
184190792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-disposition");
184290792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-id");
184390792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
184490792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "content-type");
184590792Sgshapiro	setclass(macid("{checkMIMEHeaders}"), "mime-version");
184642575Speter
184790792Sgshapiro	/* Macros to save in the queue file -- don't remove any */
184890792Sgshapiro	setclass(macid("{persistentMacros}"), "r");
184990792Sgshapiro	setclass(macid("{persistentMacros}"), "s");
185090792Sgshapiro	setclass(macid("{persistentMacros}"), "_");
185190792Sgshapiro	setclass(macid("{persistentMacros}"), "{if_addr}");
185290792Sgshapiro	setclass(macid("{persistentMacros}"), "{daemon_flags}");
185364562Sgshapiro
185438032Speter	/* operate in queue directory */
185590792Sgshapiro	if (QueueDir == NULL || *QueueDir == '\0')
185638032Speter	{
185738032Speter		if (OpMode != MD_TEST)
185838032Speter		{
185938032Speter			syserr("QueueDirectory (Q) option must be set");
186038032Speter			ExitStat = EX_CONFIG;
186138032Speter		}
186238032Speter	}
186338032Speter	else
186438032Speter	{
186564562Sgshapiro		if (OpMode != MD_TEST)
186690792Sgshapiro			setup_queues(OpMode == MD_DAEMON);
186738032Speter	}
186838032Speter
186938032Speter	/* check host status directory for validity */
187090792Sgshapiro	if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
187138032Speter	{
187238032Speter		/* cannot use this value */
187390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
187490792Sgshapiro				     "Warning: Cannot use HostStatusDirectory = %s: %s\n",
187590792Sgshapiro				     HostStatDir, sm_errstring(errno));
187638032Speter		HostStatDir = NULL;
187738032Speter	}
187838032Speter
187990792Sgshapiro	if (OpMode == MD_QUEUERUN &&
188090792Sgshapiro	    RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
188138032Speter	{
188238032Speter		struct stat stbuf;
188338032Speter
188438032Speter		/* check to see if we own the queue directory */
188538032Speter		if (stat(".", &stbuf) < 0)
188638032Speter			syserr("main: cannot stat %s", QueueDir);
188738032Speter		if (stbuf.st_uid != RealUid)
188838032Speter		{
188938032Speter			/* nope, really a botch */
189090792Sgshapiro			HoldErrs = false;
189138032Speter			usrerr("You do not have permission to process the queue");
189290792Sgshapiro			finis(false, true, EX_NOPERM);
189390792Sgshapiro			/* NOTREACHED */
189438032Speter		}
189538032Speter	}
189638032Speter
189790792Sgshapiro#if MILTER
189864562Sgshapiro	/* sanity checks on milter filters */
189964562Sgshapiro	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
190090792Sgshapiro	{
190190792Sgshapiro		milter_config(InputFilterList, InputFilters, MAXFILTERS);
190290792Sgshapiro# if _FFR_MILTER_PERDAEMON
190390792Sgshapiro		setup_daemon_milters();
190490792Sgshapiro# endif /* _FFR_MILTER_PERDAEMON */
190590792Sgshapiro	}
190690792Sgshapiro#endif /* MILTER */
190764562Sgshapiro
190890792Sgshapiro	/* Convert queuegroup string to qgrp number */
190990792Sgshapiro	if (queuegroup != NULL)
191090792Sgshapiro	{
191190792Sgshapiro		qgrp = name2qid(queuegroup);
191290792Sgshapiro		if (qgrp == NOQGRP)
191390792Sgshapiro		{
191490792Sgshapiro			HoldErrs = false;
191590792Sgshapiro			usrerr("Queue group %s unknown", queuegroup);
191690792Sgshapiro			finis(false, true, ExitStat);
191790792Sgshapiro			/* NOTREACHED */
191890792Sgshapiro		}
191990792Sgshapiro	}
192066494Sgshapiro
192138032Speter	/* if we've had errors so far, exit now */
192238032Speter	if (ExitStat != EX_OK && OpMode != MD_TEST)
192390792Sgshapiro	{
192490792Sgshapiro		finis(false, true, ExitStat);
192590792Sgshapiro		/* NOTREACHED */
192690792Sgshapiro	}
192738032Speter
192890792Sgshapiro#if SASL
192990792Sgshapiro	/* sendmail specific SASL initialization */
193090792Sgshapiro	sm_sasl_init();
193190792Sgshapiro#endif /* SASL */
193290792Sgshapiro
193338032Speter#if XDEBUG
193438032Speter	checkfd012("before main() initmaps");
193564562Sgshapiro#endif /* XDEBUG */
193638032Speter
193738032Speter	/*
193838032Speter	**  Do operation-mode-dependent initialization.
193938032Speter	*/
194038032Speter
194138032Speter	switch (OpMode)
194238032Speter	{
194338032Speter	  case MD_PRINT:
194438032Speter		/* print the queue */
194590792Sgshapiro		HoldErrs = false;
194690792Sgshapiro		dropenvelope(&BlankEnvelope, true, false);
194790792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
194890792Sgshapiro		if (qgrp != NOQGRP)
194990792Sgshapiro		{
195090792Sgshapiro			int j;
195190792Sgshapiro
195290792Sgshapiro			/* Selecting a particular queue group to run */
195390792Sgshapiro			for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
195490792Sgshapiro			{
195590792Sgshapiro				if (StopRequest)
195690792Sgshapiro					stop_sendmail();
195790792Sgshapiro				(void) print_single_queue(qgrp, j);
195890792Sgshapiro			}
195990792Sgshapiro			finis(false, true, EX_OK);
196090792Sgshapiro			/* NOTREACHED */
196190792Sgshapiro		}
196238032Speter		printqueue();
196390792Sgshapiro		finis(false, true, EX_OK);
196490792Sgshapiro		/* NOTREACHED */
196542575Speter		break;
196638032Speter
196790792Sgshapiro	  case MD_PRINTNQE:
196890792Sgshapiro		/* print number of entries in queue */
196990792Sgshapiro		dropenvelope(&BlankEnvelope, true, false);
197090792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
197190792Sgshapiro		printnqe(smioout, NULL);
197290792Sgshapiro		finis(false, true, EX_OK);
197390792Sgshapiro		/* NOTREACHED */
197490792Sgshapiro		break;
197590792Sgshapiro
197690792Sgshapiro#if _FFR_QUARANTINE
197790792Sgshapiro	  case MD_QUEUERUN:
197890792Sgshapiro		/* only handle quarantining here */
197990792Sgshapiro		if (quarantining == NULL)
198090792Sgshapiro			break;
198190792Sgshapiro
198290792Sgshapiro		if (QueueMode != QM_QUARANTINE &&
198390792Sgshapiro		    QueueMode != QM_NORMAL)
198490792Sgshapiro		{
198590792Sgshapiro			HoldErrs = false;
198690792Sgshapiro			usrerr("Can not use -Q with -q%c", QueueMode);
198790792Sgshapiro			ExitStat = EX_USAGE;
198890792Sgshapiro			finis(false, true, ExitStat);
198990792Sgshapiro			/* NOTREACHED */
199090792Sgshapiro		}
199190792Sgshapiro		quarantine_queue(quarantining, qgrp);
199290792Sgshapiro		finis(false, true, EX_OK);
199390792Sgshapiro		break;
199490792Sgshapiro#endif /* _FFR_QUARANTINE */
199590792Sgshapiro
199638032Speter	  case MD_HOSTSTAT:
199790792Sgshapiro		(void) sm_signal(SIGPIPE, sigpipe);
199864562Sgshapiro		(void) mci_traverse_persistent(mci_print_persistent, NULL);
199990792Sgshapiro		finis(false, true, EX_OK);
200090792Sgshapiro		/* NOTREACHED */
200164562Sgshapiro		break;
200238032Speter
200338032Speter	  case MD_PURGESTAT:
200464562Sgshapiro		(void) mci_traverse_persistent(mci_purge_persistent, NULL);
200590792Sgshapiro		finis(false, true, EX_OK);
200690792Sgshapiro		/* NOTREACHED */
200764562Sgshapiro		break;
200838032Speter
200938032Speter	  case MD_INITALIAS:
201042575Speter		/* initialize maps */
201164562Sgshapiro		initmaps();
201290792Sgshapiro		finis(false, true, ExitStat);
201390792Sgshapiro		/* NOTREACHED */
201442575Speter		break;
201538032Speter
201638032Speter	  case MD_SMTP:
201738032Speter	  case MD_DAEMON:
201838032Speter		/* reset DSN parameters */
201938032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
202090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
202190792Sgshapiro			  macid("{dsn_notify}"), NULL);
202290792Sgshapiro		BlankEnvelope.e_envid = NULL;
202390792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
202490792Sgshapiro			  macid("{dsn_envid}"), NULL);
202590792Sgshapiro		BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
202690792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
202790792Sgshapiro			  macid("{dsn_ret}"), NULL);
202838032Speter
202942575Speter		/* don't open maps for daemon -- done below in child */
203038032Speter		break;
203138032Speter	}
203238032Speter
203338032Speter	if (tTd(0, 15))
203438032Speter	{
203538032Speter		/* print configuration table (or at least part of it) */
203638032Speter		if (tTd(0, 90))
203738032Speter			printrules();
203838032Speter		for (i = 0; i < MAXMAILERS; i++)
203938032Speter		{
204038032Speter			if (Mailer[i] != NULL)
204138032Speter				printmailer(Mailer[i]);
204238032Speter		}
204338032Speter	}
204438032Speter
204538032Speter	/*
204638032Speter	**  Switch to the main envelope.
204738032Speter	*/
204838032Speter
204990792Sgshapiro	CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
205090792Sgshapiro			     sm_rpool_new_x(NULL));
205138032Speter	MainEnvelope.e_flags = BlankEnvelope.e_flags;
205238032Speter
205338032Speter	/*
205438032Speter	**  If test mode, read addresses from stdin and process.
205538032Speter	*/
205638032Speter
205738032Speter	if (OpMode == MD_TEST)
205838032Speter	{
205990792Sgshapiro		if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
206038032Speter			Verbose = 2;
206138032Speter
206238032Speter		if (Verbose)
206338032Speter		{
206490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
206590792Sgshapiro				     "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
206690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
206790792Sgshapiro				     "Enter <ruleset> <address>\n");
206838032Speter		}
206990792Sgshapiro		macdefine(&(MainEnvelope.e_macro), A_PERM,
207090792Sgshapiro			  macid("{addr_type}"), "e r");
207138032Speter		for (;;)
207238032Speter		{
207390792Sgshapiro			SM_TRY
207490792Sgshapiro			{
207590792Sgshapiro				(void) sm_signal(SIGINT, intindebug);
207690792Sgshapiro				(void) sm_releasesignal(SIGINT);
207790792Sgshapiro				if (Verbose == 2)
207890792Sgshapiro					(void) sm_io_fprintf(smioout,
207990792Sgshapiro							     SM_TIME_DEFAULT,
208090792Sgshapiro							     "> ");
208190792Sgshapiro				(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
208290792Sgshapiro				if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
208390792Sgshapiro						sizeof buf) == NULL)
208490792Sgshapiro					testmodeline("/quit", &MainEnvelope);
208590792Sgshapiro				p = strchr(buf, '\n');
208690792Sgshapiro				if (p != NULL)
208790792Sgshapiro					*p = '\0';
208890792Sgshapiro				if (Verbose < 2)
208990792Sgshapiro					(void) sm_io_fprintf(smioout,
209090792Sgshapiro							     SM_TIME_DEFAULT,
209190792Sgshapiro							     "> %s\n", buf);
209290792Sgshapiro				testmodeline(buf, &MainEnvelope);
209390792Sgshapiro			}
209490792Sgshapiro			SM_EXCEPT(exc, "[!F]*")
209590792Sgshapiro			{
209690792Sgshapiro				/*
209790792Sgshapiro				**  8.10 just prints \n on interrupt.
209890792Sgshapiro				**  I'm printing the exception here in case
209990792Sgshapiro				**  sendmail is extended to raise additional
210090792Sgshapiro				**  exceptions in this context.
210190792Sgshapiro				*/
210290792Sgshapiro
210390792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
210490792Sgshapiro						     "\n");
210590792Sgshapiro				sm_exc_print(exc, smioout);
210690792Sgshapiro			}
210790792Sgshapiro			SM_END_TRY
210838032Speter		}
210938032Speter	}
211038032Speter
211190792Sgshapiro#if STARTTLS
211290792Sgshapiro	tls_ok = true;
211390792Sgshapiro	if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER)
211490792Sgshapiro	{
211590792Sgshapiro		/* check whether STARTTLS is turned off for the client */
211690792Sgshapiro		if (chkclientmodifiers(D_NOTLS))
211790792Sgshapiro			tls_ok = false;
211890792Sgshapiro	}
211990792Sgshapiro	else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
212090792Sgshapiro		 OpMode == MD_SMTP)
212190792Sgshapiro	{
212290792Sgshapiro		/* check whether STARTTLS is turned off for the server */
212390792Sgshapiro		if (chkdaemonmodifiers(D_NOTLS))
212490792Sgshapiro			tls_ok = false;
212590792Sgshapiro	}
212690792Sgshapiro	else	/* other modes don't need STARTTLS */
212790792Sgshapiro		tls_ok = false;
212864562Sgshapiro
212990792Sgshapiro	if (tls_ok)
213090792Sgshapiro	{
213190792Sgshapiro		/* basic TLS initialization */
213290792Sgshapiro		tls_ok = init_tls_library();
213390792Sgshapiro	}
213490792Sgshapiro
213590792Sgshapiro	if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
213690792Sgshapiro	{
213790792Sgshapiro		/* disable TLS for client */
213890792Sgshapiro		setclttls(false);
213990792Sgshapiro	}
214090792Sgshapiro#endif /* STARTTLS */
214190792Sgshapiro
214264562Sgshapiro	/*
214338032Speter	**  If collecting stuff from the queue, go start doing that.
214438032Speter	*/
214538032Speter
214664562Sgshapiro	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
214738032Speter	{
214890792Sgshapiro		pid_t pid = -1;
214990792Sgshapiro
215090792Sgshapiro#if STARTTLS
215190792Sgshapiro		/* init TLS for client, ignore result for now */
215290792Sgshapiro		(void) initclttls(tls_ok);
215390792Sgshapiro#endif /* STARTTLS */
215490792Sgshapiro
215590792Sgshapiro		/*
215690792Sgshapiro		**  The parent process of the caller of runqueue() needs
215790792Sgshapiro		**  to stay around for a possible SIGTERM. The SIGTERM will
215890792Sgshapiro		**  tell this process that all of the queue runners children
215990792Sgshapiro		**  need to be sent SIGTERM as well. At the same time, we
216090792Sgshapiro		**  want to return control to the command line. So we do an
216190792Sgshapiro		**  extra fork().
216290792Sgshapiro		*/
216390792Sgshapiro
216490792Sgshapiro		if (Verbose || foregroundqueue || (pid = fork()) <= 0)
216564562Sgshapiro		{
216690792Sgshapiro			/*
216790792Sgshapiro			**  If the fork() failed we should still try to do
216890792Sgshapiro			**  the queue run. If it succeeded then the child
216990792Sgshapiro			**  is going to start the run and wait for all
217090792Sgshapiro			**  of the children to finish.
217190792Sgshapiro			*/
217290792Sgshapiro
217390792Sgshapiro			if (pid == 0)
217490792Sgshapiro			{
217590792Sgshapiro				/* Reset global flags */
217690792Sgshapiro				RestartRequest = NULL;
217790792Sgshapiro				ShutdownRequest = NULL;
217890792Sgshapiro				PendingSignal = 0;
217990792Sgshapiro
218090792Sgshapiro				/* disconnect from terminal */
218190792Sgshapiro				disconnect(2, CurEnv);
218290792Sgshapiro			}
218390792Sgshapiro
218490792Sgshapiro			CurrentPid = getpid();
218590792Sgshapiro			if (qgrp != NOQGRP)
218690792Sgshapiro			{
218790792Sgshapiro				/*
218890792Sgshapiro				**  To run a specific queue group mark it to
218990792Sgshapiro				**  be run, select the work group it's in and
219090792Sgshapiro				**  increment the work counter.
219190792Sgshapiro				*/
219290792Sgshapiro
219394334Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
219494334Sgshapiro				     i++)
219594334Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
219694334Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
219790792Sgshapiro				(void) run_work_group(Queue[qgrp]->qg_wgrp,
219890792Sgshapiro						      false, Verbose,
219990792Sgshapiro						      queuepersistent, false);
220090792Sgshapiro			}
220190792Sgshapiro			else
220290792Sgshapiro				(void) runqueue(false, Verbose,
220390792Sgshapiro						queuepersistent, true);
220490792Sgshapiro
220590792Sgshapiro			/* set the title to make it easier to find */
220690792Sgshapiro			sm_setproctitle(true, CurEnv, "Queue control");
220790792Sgshapiro			(void) sm_signal(SIGCHLD, SIG_DFL);
220890792Sgshapiro			while (CurChildren > 0)
220990792Sgshapiro			{
221090792Sgshapiro				int status;
221190792Sgshapiro				pid_t ret;
221290792Sgshapiro
221390792Sgshapiro				while ((ret = sm_wait(&status)) <= 0)
221490792Sgshapiro					continue;
221590792Sgshapiro
221690792Sgshapiro				/* Only drop when a child gives status */
221790792Sgshapiro				if (WIFSTOPPED(status))
221890792Sgshapiro					continue;
221990792Sgshapiro
222090792Sgshapiro				proc_list_drop(ret, status, NULL);
222190792Sgshapiro			}
222264562Sgshapiro		}
222390792Sgshapiro		finis(true, true, ExitStat);
222490792Sgshapiro		/* NOTREACHED */
222538032Speter	}
222638032Speter
222771345Sgshapiro# if SASL
222871345Sgshapiro	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
222971345Sgshapiro	{
223090792Sgshapiro		/* check whether AUTH is turned off for the server */
223190792Sgshapiro		if (!chkdaemonmodifiers(D_NOAUTH) &&
223290792Sgshapiro		    (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
223371345Sgshapiro			syserr("!sasl_server_init failed! [%s]",
223490792Sgshapiro				sasl_errstring(i, NULL, NULL));
223571345Sgshapiro	}
223671345Sgshapiro# endif /* SASL */
223771345Sgshapiro
223890792Sgshapiro	if (OpMode == MD_SMTP)
223990792Sgshapiro	{
224090792Sgshapiro		proc_list_add(CurrentPid, "Sendmail SMTP Agent",
224190792Sgshapiro			      PROC_DAEMON, 0, -1);
224290792Sgshapiro
224390792Sgshapiro		/* clean up background delivery children */
224490792Sgshapiro		(void) sm_signal(SIGCHLD, reapchild);
224590792Sgshapiro	}
224690792Sgshapiro
224738032Speter	/*
224838032Speter	**  If a daemon, wait for a request.
224938032Speter	**	getrequests will always return in a child.
225038032Speter	**	If we should also be processing the queue, start
225138032Speter	**		doing it in background.
225238032Speter	**	We check for any errors that might have happened
225338032Speter	**		during startup.
225438032Speter	*/
225538032Speter
225698841Sgshapiro	if (OpMode == MD_DAEMON || QueueIntvl > 0)
225738032Speter	{
225838032Speter		char dtype[200];
225938032Speter
226038032Speter		if (!run_in_foreground && !tTd(99, 100))
226138032Speter		{
226238032Speter			/* put us in background */
226338032Speter			i = fork();
226438032Speter			if (i < 0)
226538032Speter				syserr("daemon: cannot fork");
226638032Speter			if (i != 0)
226790792Sgshapiro			{
226890792Sgshapiro				finis(false, true, EX_OK);
226990792Sgshapiro				/* NOTREACHED */
227090792Sgshapiro			}
227138032Speter
227290792Sgshapiro			/*
227390792Sgshapiro			**  Initialize exception stack and default exception
227490792Sgshapiro			**  handler for child process.
227590792Sgshapiro			*/
227690792Sgshapiro
227790792Sgshapiro			/* Reset global flags */
227890792Sgshapiro			RestartRequest = NULL;
227990792Sgshapiro			RestartWorkGroup = false;
228090792Sgshapiro			ShutdownRequest = NULL;
228190792Sgshapiro			PendingSignal = 0;
228290792Sgshapiro			CurrentPid = getpid();
228390792Sgshapiro
228490792Sgshapiro			sm_exc_newthread(fatal_error);
228590792Sgshapiro
228638032Speter			/* disconnect from our controlling tty */
228790792Sgshapiro			disconnect(2, &MainEnvelope);
228838032Speter		}
228938032Speter
229038032Speter		dtype[0] = '\0';
229138032Speter		if (OpMode == MD_DAEMON)
229290792Sgshapiro		{
229390792Sgshapiro			(void) sm_strlcat(dtype, "+SMTP", sizeof dtype);
229490792Sgshapiro			DaemonPid = CurrentPid;
229590792Sgshapiro		}
229698841Sgshapiro		if (QueueIntvl > 0)
229738032Speter		{
229890792Sgshapiro			(void) sm_strlcat2(dtype,
229990792Sgshapiro					   queuepersistent
230090792Sgshapiro					   ? "+persistent-queueing@"
230190792Sgshapiro					   : "+queueing@",
230290792Sgshapiro					   pintvl(QueueIntvl, true),
230390792Sgshapiro					   sizeof dtype);
230438032Speter		}
230538032Speter		if (tTd(0, 1))
230690792Sgshapiro			(void) sm_strlcat(dtype, "+debugging", sizeof dtype);
230738032Speter
230838032Speter		sm_syslog(LOG_INFO, NOQID,
230964562Sgshapiro			  "starting daemon (%s): %s", Version, dtype + 1);
231090792Sgshapiro#if XLA
231138032Speter		xla_create_file();
231264562Sgshapiro#endif /* XLA */
231338032Speter
231464562Sgshapiro		/* save daemon type in a macro for possible PidFile use */
231590792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
231690792Sgshapiro			macid("{daemon_info}"), dtype + 1);
231764562Sgshapiro
231864562Sgshapiro		/* save queue interval in a macro for possible PidFile use */
231990792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
232090792Sgshapiro			macid("{queue_interval}"), pintvl(QueueIntvl, true));
232164562Sgshapiro
232290792Sgshapiro		/* workaround: can't seem to release the signal in the parent */
232390792Sgshapiro		(void) sm_signal(SIGHUP, sighup);
232490792Sgshapiro		(void) sm_releasesignal(SIGHUP);
232590792Sgshapiro		(void) sm_signal(SIGTERM, sigterm);
232690792Sgshapiro
232798841Sgshapiro		if (QueueIntvl > 0)
232838032Speter		{
232990792Sgshapiro			(void) runqueue(true, false, queuepersistent, true);
233090792Sgshapiro
233190792Sgshapiro			/*
233290792Sgshapiro			**  If queuepersistent but not in daemon mode then
233390792Sgshapiro			**  we're going to do the queue runner monitoring here.
233490792Sgshapiro			**  If in daemon mode then the monitoring will happen
233590792Sgshapiro			**  elsewhere.
233690792Sgshapiro			*/
233790792Sgshapiro
233890792Sgshapiro			if (OpMode != MD_DAEMON && queuepersistent)
233990792Sgshapiro			{
234090792Sgshapiro				/* set the title to make it easier to find */
234190792Sgshapiro				sm_setproctitle(true, CurEnv, "Queue control");
234290792Sgshapiro				(void) sm_signal(SIGCHLD, SIG_DFL);
234390792Sgshapiro				while (CurChildren > 0)
234490792Sgshapiro				{
234590792Sgshapiro					int status;
234690792Sgshapiro					pid_t ret;
234790792Sgshapiro					int group;
234890792Sgshapiro
234990792Sgshapiro					if (ShutdownRequest != NULL)
235090792Sgshapiro						shutdown_daemon();
235190792Sgshapiro					else if (RestartRequest != NULL)
235290792Sgshapiro						restart_daemon();
235390792Sgshapiro					else if (RestartWorkGroup)
235490792Sgshapiro						restart_marked_work_groups();
235590792Sgshapiro
235690792Sgshapiro					while ((ret = sm_wait(&status)) <= 0)
235790792Sgshapiro						continue;
235890792Sgshapiro
235990792Sgshapiro					if (WIFSTOPPED(status))
236090792Sgshapiro						continue;
236190792Sgshapiro
236290792Sgshapiro					/* Probe only on a child status */
236390792Sgshapiro					proc_list_drop(ret, status, &group);
236490792Sgshapiro
236590792Sgshapiro					if (WIFSIGNALED(status))
236690792Sgshapiro					{
236790792Sgshapiro						if (WCOREDUMP(status))
236890792Sgshapiro						{
236990792Sgshapiro							sm_syslog(LOG_ERR, NOQID,
237090792Sgshapiro								  "persistent queue runner=%d core dumped, signal=%d",
237190792Sgshapiro								  group, WTERMSIG(status));
237290792Sgshapiro
237390792Sgshapiro							/* don't restart this one */
237490792Sgshapiro							mark_work_group_restart(group, -1);
237590792Sgshapiro							continue;
237690792Sgshapiro						}
237790792Sgshapiro
237890792Sgshapiro						sm_syslog(LOG_ERR, NOQID,
237990792Sgshapiro							  "persistent queue runner=%d died, signal=%d",
238090792Sgshapiro							  group, WTERMSIG(status));
238190792Sgshapiro					}
238290792Sgshapiro
238390792Sgshapiro					/*
238490792Sgshapiro					**  When debugging active, don't
238590792Sgshapiro					**  restart the persistent queues.
238690792Sgshapiro					**  But do log this as info.
238790792Sgshapiro					*/
238890792Sgshapiro
238990792Sgshapiro					if (sm_debug_active(&DebugNoPRestart,
239090792Sgshapiro							    1))
239190792Sgshapiro					{
239290792Sgshapiro						sm_syslog(LOG_DEBUG, NOQID,
239390792Sgshapiro							  "persistent queue runner=%d, exited",
239490792Sgshapiro							  group);
239590792Sgshapiro						mark_work_group_restart(group, -1);
239690792Sgshapiro					}
239790792Sgshapiro				}
239890792Sgshapiro				finis(true, true, ExitStat);
239990792Sgshapiro				/* NOTREACHED */
240090792Sgshapiro			}
240190792Sgshapiro
240238032Speter			if (OpMode != MD_DAEMON)
240338032Speter			{
240490792Sgshapiro				char qtype[200];
240590792Sgshapiro
240690792Sgshapiro				/*
240790792Sgshapiro				**  Write the pid to file
240890792Sgshapiro				**  XXX Overwrites sendmail.pid
240990792Sgshapiro				*/
241090792Sgshapiro
241190792Sgshapiro				log_sendmail_pid(&MainEnvelope);
241290792Sgshapiro
241390792Sgshapiro				/* set the title to make it easier to find */
241490792Sgshapiro				qtype[0] = '\0';
241590792Sgshapiro				(void) sm_strlcpyn(qtype, sizeof qtype, 4,
241690792Sgshapiro						   "Queue runner@",
241790792Sgshapiro						   pintvl(QueueIntvl, true),
241890792Sgshapiro						   " for ",
241990792Sgshapiro						   QueueDir);
242090792Sgshapiro				sm_setproctitle(true, CurEnv, qtype);
242138032Speter				for (;;)
242238032Speter				{
242364562Sgshapiro					(void) pause();
242477349Sgshapiro					if (ShutdownRequest != NULL)
242577349Sgshapiro						shutdown_daemon();
242690792Sgshapiro					else if (RestartRequest != NULL)
242790792Sgshapiro						restart_daemon();
242890792Sgshapiro					else if (RestartWorkGroup)
242990792Sgshapiro						restart_marked_work_groups();
243090792Sgshapiro
243190792Sgshapiro					if (doqueuerun())
243290792Sgshapiro						(void) runqueue(true, false,
243390792Sgshapiro								false, false);
243438032Speter				}
243538032Speter			}
243638032Speter		}
243790792Sgshapiro		dropenvelope(&MainEnvelope, true, false);
243838032Speter
243990792Sgshapiro#if STARTTLS
244064562Sgshapiro		/* init TLS for server, ignore result for now */
244190792Sgshapiro		(void) initsrvtls(tls_ok);
244290792Sgshapiro#endif /* STARTTLS */
244390792Sgshapiro#if PROFILING
244490792Sgshapiro	nextreq:
244590792Sgshapiro#endif /* PROFILING */
244690792Sgshapiro		p_flags = getrequests(&MainEnvelope);
244738032Speter
244838032Speter		/* drop privileges */
244990792Sgshapiro		(void) drop_privileges(false);
245038032Speter
245138032Speter		/*
245238032Speter		**  Get authentication data
245390792Sgshapiro		**  Set _ macro in BlankEnvelope before calling newenvelope().
245438032Speter		*/
245538032Speter
245690792Sgshapiro		authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
245790792Sgshapiro						     NULL), &forged);
245890792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
245990792Sgshapiro
246090792Sgshapiro		/* at this point we are in a child: reset state */
246190792Sgshapiro		sm_rpool_free(MainEnvelope.e_rpool);
246290792Sgshapiro		(void) newenvelope(&MainEnvelope, &MainEnvelope,
246390792Sgshapiro				   sm_rpool_new_x(NULL));
246438032Speter	}
246538032Speter
246664562Sgshapiro	if (LogLevel > 9)
246764562Sgshapiro	{
246864562Sgshapiro		/* log connection information */
246964562Sgshapiro		sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo);
247064562Sgshapiro	}
247164562Sgshapiro
247238032Speter	/*
247338032Speter	**  If running SMTP protocol, start collecting and executing
247438032Speter	**  commands.  This will never return.
247538032Speter	*/
247638032Speter
247738032Speter	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
247838032Speter	{
247938032Speter		char pbuf[20];
248038032Speter
248138032Speter		/*
248238032Speter		**  Save some macros for check_* rulesets.
248338032Speter		*/
248438032Speter
248538032Speter		if (forged)
248638032Speter		{
248738032Speter			char ipbuf[103];
248838032Speter
248990792Sgshapiro			(void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
249090792Sgshapiro					   anynet_ntoa(&RealHostAddr));
249190792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_TEMP,
249290792Sgshapiro				  macid("{client_name}"), ipbuf);
249338032Speter		}
249438032Speter		else
249590792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
249690792Sgshapiro				  macid("{client_name}"), RealHostName);
249790792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
249890792Sgshapiro			  macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
249990792Sgshapiro		sm_getla();
250038032Speter
250190792Sgshapiro		switch (RealHostAddr.sa.sa_family)
250264562Sgshapiro		{
250390792Sgshapiro#if NETINET
250464562Sgshapiro		  case AF_INET:
250590792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "%d",
250690792Sgshapiro					   RealHostAddr.sin.sin_port);
250764562Sgshapiro			break;
250890792Sgshapiro#endif /* NETINET */
250990792Sgshapiro#if NETINET6
251064562Sgshapiro		  case AF_INET6:
251190792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "%d",
251290792Sgshapiro					   RealHostAddr.sin6.sin6_port);
251364562Sgshapiro			break;
251490792Sgshapiro#endif /* NETINET6 */
251564562Sgshapiro		  default:
251690792Sgshapiro			(void) sm_snprintf(pbuf, sizeof pbuf, "0");
251764562Sgshapiro			break;
251864562Sgshapiro		}
251990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_TEMP,
252090792Sgshapiro			macid("{client_port}"), pbuf);
252142575Speter
252238032Speter		if (OpMode == MD_DAEMON)
252338032Speter		{
252438032Speter			/* validate the connection */
252590792Sgshapiro			HoldErrs = true;
252638032Speter			nullserver = validate_connection(&RealHostAddr,
252790792Sgshapiro							 RealHostName,
252890792Sgshapiro							 &MainEnvelope);
252990792Sgshapiro			HoldErrs = false;
253038032Speter		}
253164562Sgshapiro		else if (p_flags == NULL)
253264562Sgshapiro		{
253364562Sgshapiro			p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
253464562Sgshapiro			clrbitmap(p_flags);
253564562Sgshapiro		}
253690792Sgshapiro#if STARTTLS
253764562Sgshapiro		if (OpMode == MD_SMTP)
253890792Sgshapiro			(void) initsrvtls(tls_ok);
253990792Sgshapiro#endif /* STARTTLS */
254071345Sgshapiro
254190792Sgshapiro		/* turn off profiling */
254290792Sgshapiro		SM_PROF(1);
254390792Sgshapiro		smtp(nullserver, *p_flags, &MainEnvelope);
254490792Sgshapiro#if PROFILING
254590792Sgshapiro		/* turn off profiling */
254690792Sgshapiro		SM_PROF(0);
254790792Sgshapiro		if (OpMode == MD_DAEMON)
254890792Sgshapiro			goto nextreq;
254990792Sgshapiro#endif /* PROFILING */
255038032Speter	}
255138032Speter
255290792Sgshapiro	sm_rpool_free(MainEnvelope.e_rpool);
255390792Sgshapiro	clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
255438032Speter	if (OpMode == MD_VERIFY)
255538032Speter	{
255690792Sgshapiro		set_delivery_mode(SM_VERIFY, &MainEnvelope);
255738032Speter		PostMasterCopy = NULL;
255838032Speter	}
255938032Speter	else
256038032Speter	{
256138032Speter		/* interactive -- all errors are global */
256290792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
256338032Speter	}
256438032Speter
256538032Speter	/*
256638032Speter	**  Do basic system initialization and set the sender
256738032Speter	*/
256838032Speter
256990792Sgshapiro	initsys(&MainEnvelope);
257090792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
257190792Sgshapiro	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
257290792Sgshapiro	setsender(from, &MainEnvelope, NULL, '\0', false);
257364562Sgshapiro	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
257490792Sgshapiro	    (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
257590792Sgshapiro	     strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
257664562Sgshapiro	{
257790792Sgshapiro		auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
257890792Sgshapiro			     RealUserName, from, warn_f_flag);
257964562Sgshapiro#if SASL
258090792Sgshapiro		auth = false;
258164562Sgshapiro#endif /* SASL */
258264562Sgshapiro	}
258364562Sgshapiro	if (auth)
258464562Sgshapiro	{
258564562Sgshapiro		char *fv;
258664562Sgshapiro
258764562Sgshapiro		/* set the initial sender for AUTH= to $f@$j */
258890792Sgshapiro		fv = macvalue('f', &MainEnvelope);
258964562Sgshapiro		if (fv == NULL || *fv == '\0')
259090792Sgshapiro			MainEnvelope.e_auth_param = NULL;
259164562Sgshapiro		else
259264562Sgshapiro		{
259364562Sgshapiro			if (strchr(fv, '@') == NULL)
259464562Sgshapiro			{
259590792Sgshapiro				i = strlen(fv) + strlen(macvalue('j',
259690792Sgshapiro							&MainEnvelope)) + 2;
259790792Sgshapiro				p = sm_malloc_x(i);
259890792Sgshapiro				(void) sm_strlcpyn(p, i, 3, fv, "@",
259990792Sgshapiro						   macvalue('j',
260090792Sgshapiro							    &MainEnvelope));
260164562Sgshapiro			}
260264562Sgshapiro			else
260390792Sgshapiro				p = sm_strdup_x(fv);
260490792Sgshapiro			MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
260590792Sgshapiro								      xtextify(p, "="));
260690792Sgshapiro			sm_free(p);  /* XXX */
260764562Sgshapiro		}
260864562Sgshapiro	}
260990792Sgshapiro	if (macvalue('s', &MainEnvelope) == NULL)
261090792Sgshapiro		macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
261138032Speter
261290792Sgshapiro	av = argv + optind;
261338032Speter	if (*av == NULL && !GrabTo)
261438032Speter	{
261590792Sgshapiro		MainEnvelope.e_to = NULL;
261690792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
261790792Sgshapiro		HoldErrs = false;
261890792Sgshapiro		SuperSafe = SAFE_NO;
261938032Speter		usrerr("Recipient names must be specified");
262038032Speter
262138032Speter		/* collect body for UUCP return */
262238032Speter		if (OpMode != MD_VERIFY)
262390792Sgshapiro			collect(InChannel, false, NULL, &MainEnvelope);
262490792Sgshapiro		finis(true, true, EX_USAGE);
262590792Sgshapiro		/* NOTREACHED */
262638032Speter	}
262738032Speter
262838032Speter	/*
262938032Speter	**  Scan argv and deliver the message to everyone.
263038032Speter	*/
263138032Speter
263290792Sgshapiro	save_val = LogUsrErrs;
263390792Sgshapiro	LogUsrErrs = true;
263490792Sgshapiro	sendtoargv(av, &MainEnvelope);
263590792Sgshapiro	LogUsrErrs = save_val;
263638032Speter
263738032Speter	/* if we have had errors sofar, arrange a meaningful exit stat */
263838032Speter	if (Errors > 0 && ExitStat == EX_OK)
263938032Speter		ExitStat = EX_USAGE;
264038032Speter
264138032Speter#if _FFR_FIX_DASHT
264238032Speter	/*
264338032Speter	**  If using -t, force not sending to argv recipients, even
264438032Speter	**  if they are mentioned in the headers.
264538032Speter	*/
264638032Speter
264738032Speter	if (GrabTo)
264838032Speter	{
264938032Speter		ADDRESS *q;
265064562Sgshapiro
265190792Sgshapiro		for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
265264562Sgshapiro			q->q_state = QS_REMOVED;
265338032Speter	}
265464562Sgshapiro#endif /* _FFR_FIX_DASHT */
265538032Speter
265638032Speter	/*
265738032Speter	**  Read the input mail.
265838032Speter	*/
265938032Speter
266090792Sgshapiro	MainEnvelope.e_to = NULL;
266138032Speter	if (OpMode != MD_VERIFY || GrabTo)
266238032Speter	{
266390792Sgshapiro		int savederrors;
266490792Sgshapiro		unsigned long savedflags;
266538032Speter
266690792Sgshapiro		/*
266790792Sgshapiro		**  workaround for compiler warning on Irix:
266890792Sgshapiro		**  do not initialize variable in the definition, but
266990792Sgshapiro		**  later on:
267090792Sgshapiro		**  warning(1548): transfer of control bypasses
267190792Sgshapiro		**  initialization of:
267290792Sgshapiro		**  variable "savederrors" (declared at line 2570)
267390792Sgshapiro		**  variable "savedflags" (declared at line 2571)
267490792Sgshapiro		**  goto giveup;
267590792Sgshapiro		*/
267690792Sgshapiro
267790792Sgshapiro		savederrors = Errors;
267890792Sgshapiro		savedflags = MainEnvelope.e_flags & EF_FATALERRS;
267990792Sgshapiro		MainEnvelope.e_flags |= EF_GLOBALERRS;
268090792Sgshapiro		MainEnvelope.e_flags &= ~EF_FATALERRS;
268164562Sgshapiro		Errors = 0;
268264562Sgshapiro		buffer_errors();
268390792Sgshapiro		collect(InChannel, false, NULL, &MainEnvelope);
268438032Speter
268564562Sgshapiro		/* header checks failed */
268664562Sgshapiro		if (Errors > 0)
268764562Sgshapiro		{
268890792Sgshapiro  giveup:
268990792Sgshapiro			if (!GrabTo)
269064562Sgshapiro			{
269190792Sgshapiro				/* Log who the mail would have gone to */
269290792Sgshapiro				logundelrcpts(&MainEnvelope,
269390792Sgshapiro					      MainEnvelope.e_message,
269490792Sgshapiro					      8, false);
269564562Sgshapiro			}
269690792Sgshapiro			flush_errors(true);
269790792Sgshapiro			finis(true, true, ExitStat);
269864562Sgshapiro			/* NOTREACHED */
269964562Sgshapiro			return -1;
270064562Sgshapiro		}
270164562Sgshapiro
270238032Speter		/* bail out if message too large */
270390792Sgshapiro		if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
270438032Speter		{
270590792Sgshapiro			finis(true, true, ExitStat != EX_OK ? ExitStat
270690792Sgshapiro							    : EX_DATAERR);
270764562Sgshapiro			/* NOTREACHED */
270838032Speter			return -1;
270938032Speter		}
271098121Sgshapiro
271198121Sgshapiro		/* set message size */
271298121Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%ld",
271398121Sgshapiro				   MainEnvelope.e_msgsize);
271498121Sgshapiro		macdefine(&MainEnvelope.e_macro, A_TEMP,
271598121Sgshapiro			  macid("{msg_size}"), buf);
271698121Sgshapiro
271764562Sgshapiro		Errors = savederrors;
271890792Sgshapiro		MainEnvelope.e_flags |= savedflags;
271938032Speter	}
272038032Speter	errno = 0;
272138032Speter
272238032Speter	if (tTd(1, 1))
272390792Sgshapiro		sm_dprintf("From person = \"%s\"\n",
272490792Sgshapiro			   MainEnvelope.e_from.q_paddr);
272538032Speter
272690792Sgshapiro#if _FFR_QUARANTINE
272790792Sgshapiro	/* Check if quarantining stats should be updated */
272890792Sgshapiro	if (MainEnvelope.e_quarmsg != NULL)
272990792Sgshapiro		markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
273090792Sgshapiro#endif /* _FFR_QUARANTINE */
273190792Sgshapiro
273238032Speter	/*
273338032Speter	**  Actually send everything.
273438032Speter	**	If verifying, just ack.
273538032Speter	*/
273638032Speter
273790792Sgshapiro	if (Errors == 0)
273838032Speter	{
273990792Sgshapiro		if (!split_by_recipient(&MainEnvelope) &&
274090792Sgshapiro		    bitset(EF_FATALERRS, MainEnvelope.e_flags))
274190792Sgshapiro			goto giveup;
274238032Speter	}
274390792Sgshapiro
274490792Sgshapiro	/* make sure we deliver at least the first envelope */
274590792Sgshapiro	i = FastSplit > 0 ? 0 : -1;
274690792Sgshapiro	for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
274790792Sgshapiro	{
274890792Sgshapiro		ENVELOPE *next;
274990792Sgshapiro
275090792Sgshapiro		e->e_from.q_state = QS_SENDER;
275190792Sgshapiro		if (tTd(1, 5))
275290792Sgshapiro		{
275390792Sgshapiro			sm_dprintf("main[%d]: QS_SENDER ", i);
275490792Sgshapiro			printaddr(&e->e_from, false);
275590792Sgshapiro		}
275690792Sgshapiro		e->e_to = NULL;
275790792Sgshapiro		sm_getla();
275890792Sgshapiro		GrabTo = false;
275964562Sgshapiro#if NAMED_BIND
276090792Sgshapiro		_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
276190792Sgshapiro		_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
276264562Sgshapiro#endif /* NAMED_BIND */
276390792Sgshapiro		next = e->e_sibling;
276490792Sgshapiro		e->e_sibling = NULL;
276538032Speter
276690792Sgshapiro		/* after FastSplit envelopes: queue up */
276790792Sgshapiro		sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
276890792Sgshapiro		e->e_sibling = next;
276990792Sgshapiro	}
277090792Sgshapiro
277138032Speter	/*
277238032Speter	**  All done.
277338032Speter	**	Don't send return error message if in VERIFY mode.
277438032Speter	*/
277538032Speter
277690792Sgshapiro	finis(true, true, ExitStat);
277764562Sgshapiro	/* NOTREACHED */
277864562Sgshapiro	return ExitStat;
277938032Speter}
278090792Sgshapiro/*
278177349Sgshapiro**  STOP_SENDMAIL -- Stop the running program
278277349Sgshapiro**
278377349Sgshapiro**	Parameters:
278477349Sgshapiro**		none.
278577349Sgshapiro**
278677349Sgshapiro**	Returns:
278777349Sgshapiro**		none.
278877349Sgshapiro**
278977349Sgshapiro**	Side Effects:
279077349Sgshapiro**		exits.
279177349Sgshapiro*/
279238032Speter
279377349Sgshapirovoid
279477349Sgshapirostop_sendmail()
279577349Sgshapiro{
279677349Sgshapiro	/* reset uid for process accounting */
279777349Sgshapiro	endpwent();
279877349Sgshapiro	(void) setuid(RealUid);
279977349Sgshapiro	exit(EX_OK);
280077349Sgshapiro}
280190792Sgshapiro/*
280238032Speter**  FINIS -- Clean up and exit.
280338032Speter**
280438032Speter**	Parameters:
280542575Speter**		drop -- whether or not to drop CurEnv envelope
280690792Sgshapiro**		cleanup -- call exit() or _exit()?
280742575Speter**		exitstat -- exit status to use for exit() call
280838032Speter**
280938032Speter**	Returns:
281038032Speter**		never
281138032Speter**
281238032Speter**	Side Effects:
281338032Speter**		exits sendmail
281438032Speter*/
281538032Speter
281638032Spetervoid
281790792Sgshapirofinis(drop, cleanup, exitstat)
281842575Speter	bool drop;
281990792Sgshapiro	bool cleanup;
282042575Speter	volatile int exitstat;
282138032Speter{
282298121Sgshapiro
282377349Sgshapiro	/* Still want to process new timeouts added below */
282490792Sgshapiro	sm_clear_events();
282590792Sgshapiro	(void) sm_releasesignal(SIGALRM);
282642575Speter
282738032Speter	if (tTd(2, 1))
282838032Speter	{
282990792Sgshapiro		sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
283090792Sgshapiro			   exitstat,
283190792Sgshapiro			   CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
283238032Speter		printenvflags(CurEnv);
283338032Speter	}
283438032Speter	if (tTd(2, 9))
283590792Sgshapiro		printopenfds(false);
283638032Speter
283790792Sgshapiro	SM_TRY
283890792Sgshapiro		/*
283990792Sgshapiro		**  Clean up.  This might raise E:mta.quickabort
284090792Sgshapiro		*/
284138032Speter
284290792Sgshapiro		/* clean up temp files */
284390792Sgshapiro		CurEnv->e_to = NULL;
284490792Sgshapiro		if (drop)
284590792Sgshapiro		{
284690792Sgshapiro			if (CurEnv->e_id != NULL)
284790792Sgshapiro			{
284890792Sgshapiro				dropenvelope(CurEnv, true, false);
284990792Sgshapiro				sm_rpool_free(CurEnv->e_rpool);
285090792Sgshapiro				CurEnv->e_rpool = NULL;
285190792Sgshapiro			}
285290792Sgshapiro			else
285390792Sgshapiro				poststats(StatFile);
285490792Sgshapiro		}
285538032Speter
285690792Sgshapiro		/* flush any cached connections */
285790792Sgshapiro		mci_flush(true, NULL);
285838032Speter
285990792Sgshapiro		/* close maps belonging to this pid */
286090792Sgshapiro		closemaps(false);
286142575Speter
286264562Sgshapiro#if USERDB
286390792Sgshapiro		/* close UserDatabase */
286490792Sgshapiro		_udbx_close();
286564562Sgshapiro#endif /* USERDB */
286642575Speter
286790792Sgshapiro#if SASL
286890792Sgshapiro		stop_sasl_client();
286990792Sgshapiro#endif /* SASL */
287090792Sgshapiro
287190792Sgshapiro#if XLA
287290792Sgshapiro		/* clean up extended load average stuff */
287390792Sgshapiro		xla_all_end();
287464562Sgshapiro#endif /* XLA */
287538032Speter
287690792Sgshapiro	SM_FINALLY
287790792Sgshapiro		/*
287890792Sgshapiro		**  And exit.
287990792Sgshapiro		*/
288038032Speter
288190792Sgshapiro		if (LogLevel > 78)
288290792Sgshapiro			sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
288390792Sgshapiro				  (int) CurrentPid);
288490792Sgshapiro		if (exitstat == EX_TEMPFAIL ||
288590792Sgshapiro		    CurEnv->e_errormode == EM_BERKNET)
288690792Sgshapiro			exitstat = EX_OK;
288764562Sgshapiro
288890792Sgshapiro		/* XXX clean up queues and related data structures */
288990792Sgshapiro		cleanup_queues();
289090792Sgshapiro#if SM_CONF_SHM
289190792Sgshapiro		cleanup_shm(DaemonPid == getpid());
289290792Sgshapiro#endif /* SM_CONF_SHM */
289390792Sgshapiro
289490792Sgshapiro		/* reset uid for process accounting */
289590792Sgshapiro		endpwent();
289690792Sgshapiro		sm_mbdb_terminate();
289790792Sgshapiro		(void) setuid(RealUid);
289890792Sgshapiro#if SM_HEAP_CHECK
289990792Sgshapiro		/* dump the heap, if we are checking for memory leaks */
290090792Sgshapiro		if (sm_debug_active(&SmHeapCheck, 2))
290190792Sgshapiro			sm_heap_report(smioout,
290290792Sgshapiro				       sm_debug_level(&SmHeapCheck) - 1);
290390792Sgshapiro#endif /* SM_HEAP_CHECK */
290490792Sgshapiro		if (sm_debug_active(&SmXtrapReport, 1))
290590792Sgshapiro			sm_dprintf("xtrap count = %d\n", SmXtrapCount);
290690792Sgshapiro		if (cleanup)
290790792Sgshapiro			exit(exitstat);
290890792Sgshapiro		else
290990792Sgshapiro			_exit(exitstat);
291090792Sgshapiro	SM_END_TRY
291138032Speter}
291290792Sgshapiro/*
291390792Sgshapiro**  INTINDEBUG -- signal handler for SIGINT in -bt mode
291477349Sgshapiro**
291577349Sgshapiro**	Parameters:
291690792Sgshapiro**		sig -- incoming signal.
291790792Sgshapiro**
291890792Sgshapiro**	Returns:
291990792Sgshapiro**		none.
292090792Sgshapiro**
292190792Sgshapiro**	Side Effects:
292290792Sgshapiro**		longjmps back to test mode loop.
292390792Sgshapiro**
292490792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
292590792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
292690792Sgshapiro**		DOING.
292790792Sgshapiro*/
292890792Sgshapiro
292990792Sgshapiro/* Type of an exception generated on SIGINT during address test mode.  */
293090792Sgshapirostatic const SM_EXC_TYPE_T EtypeInterrupt =
293190792Sgshapiro{
293290792Sgshapiro	SmExcTypeMagic,
293390792Sgshapiro	"S:mta.interrupt",
293490792Sgshapiro	"",
293590792Sgshapiro	sm_etype_printf,
293690792Sgshapiro	"interrupt",
293790792Sgshapiro};
293890792Sgshapiro
293990792Sgshapiro/* ARGSUSED */
294090792Sgshapirostatic SIGFUNC_DECL
294190792Sgshapirointindebug(sig)
294290792Sgshapiro	int sig;
294390792Sgshapiro{
294490792Sgshapiro	int save_errno = errno;
294590792Sgshapiro
294690792Sgshapiro	FIX_SYSV_SIGNAL(sig, intindebug);
294790792Sgshapiro	errno = save_errno;
294890792Sgshapiro	CHECK_CRITICAL(sig);
294990792Sgshapiro	errno = save_errno;
295090792Sgshapiro	sm_exc_raisenew_x(&EtypeInterrupt);
295190792Sgshapiro	errno = save_errno;
295290792Sgshapiro	return SIGFUNC_RETURN;
295390792Sgshapiro}
295490792Sgshapiro/*
295590792Sgshapiro**  SIGTERM -- SIGTERM handler for the daemon
295690792Sgshapiro**
295790792Sgshapiro**	Parameters:
295877349Sgshapiro**		sig -- signal number.
295977349Sgshapiro**
296077349Sgshapiro**	Returns:
296177349Sgshapiro**		none.
296277349Sgshapiro**
296377349Sgshapiro**	Side Effects:
296477349Sgshapiro**		Sets ShutdownRequest which will hopefully trigger
296577349Sgshapiro**		the daemon to exit.
296677349Sgshapiro**
296777349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
296877349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
296977349Sgshapiro**		DOING.
297077349Sgshapiro*/
297177349Sgshapiro
297277349Sgshapiro/* ARGSUSED */
297377349Sgshapirostatic SIGFUNC_DECL
297490792Sgshapirosigterm(sig)
297577349Sgshapiro	int sig;
297677349Sgshapiro{
297777349Sgshapiro	int save_errno = errno;
297877349Sgshapiro
297990792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigterm);
298077349Sgshapiro	ShutdownRequest = "signal";
298177349Sgshapiro	errno = save_errno;
298277349Sgshapiro	return SIGFUNC_RETURN;
298377349Sgshapiro}
298490792Sgshapiro/*
298590792Sgshapiro**  SIGHUP -- handle a SIGHUP signal
298677349Sgshapiro**
298777349Sgshapiro**	Parameters:
298890792Sgshapiro**		sig -- incoming signal.
298977349Sgshapiro**
299077349Sgshapiro**	Returns:
299177349Sgshapiro**		none.
299277349Sgshapiro**
299377349Sgshapiro**	Side Effects:
299490792Sgshapiro**		Sets RestartRequest which should cause the daemon
299590792Sgshapiro**		to restart.
299690792Sgshapiro**
299790792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
299890792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
299990792Sgshapiro**		DOING.
300077349Sgshapiro*/
300177349Sgshapiro
300290792Sgshapiro/* ARGSUSED */
300390792Sgshapirostatic SIGFUNC_DECL
300490792Sgshapirosighup(sig)
300590792Sgshapiro	int sig;
300677349Sgshapiro{
300790792Sgshapiro	int save_errno = errno;
300877349Sgshapiro
300990792Sgshapiro	FIX_SYSV_SIGNAL(sig, sighup);
301090792Sgshapiro	RestartRequest = "signal";
301190792Sgshapiro	errno = save_errno;
301290792Sgshapiro	return SIGFUNC_RETURN;
301390792Sgshapiro}
301490792Sgshapiro/*
301590792Sgshapiro**  SIGPIPE -- signal handler for SIGPIPE
301690792Sgshapiro**
301790792Sgshapiro**	Parameters:
301890792Sgshapiro**		sig -- incoming signal.
301990792Sgshapiro**
302090792Sgshapiro**	Returns:
302190792Sgshapiro**		none.
302290792Sgshapiro**
302390792Sgshapiro**	Side Effects:
302490792Sgshapiro**		Sets StopRequest which should cause the mailq/hoststatus
302590792Sgshapiro**		display to stop.
302690792Sgshapiro**
302790792Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
302890792Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
302990792Sgshapiro**		DOING.
303090792Sgshapiro*/
303177349Sgshapiro
303290792Sgshapiro/* ARGSUSED */
303390792Sgshapirostatic SIGFUNC_DECL
303490792Sgshapirosigpipe(sig)
303590792Sgshapiro	int sig;
303690792Sgshapiro{
303790792Sgshapiro	int save_errno = errno;
303877349Sgshapiro
303990792Sgshapiro	FIX_SYSV_SIGNAL(sig, sigpipe);
304090792Sgshapiro	StopRequest = true;
304190792Sgshapiro	errno = save_errno;
304290792Sgshapiro	return SIGFUNC_RETURN;
304377349Sgshapiro}
304490792Sgshapiro/*
304538032Speter**  INTSIG -- clean up on interrupt
304638032Speter**
304764562Sgshapiro**	This just arranges to exit.  It pessimizes in that it
304838032Speter**	may resend a message.
304938032Speter**
305038032Speter**	Parameters:
305138032Speter**		none.
305238032Speter**
305338032Speter**	Returns:
305438032Speter**		none.
305538032Speter**
305638032Speter**	Side Effects:
305738032Speter**		Unlocks the current job.
305877349Sgshapiro**
305977349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
306077349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
306177349Sgshapiro**		DOING.
306277349Sgshapiro**
306377349Sgshapiro**		XXX: More work is needed for this signal handler.
306438032Speter*/
306538032Speter
306638032Speter/* ARGSUSED */
306738032SpeterSIGFUNC_DECL
306838032Speterintsig(sig)
306938032Speter	int sig;
307038032Speter{
307190792Sgshapiro	bool drop = false;
307277349Sgshapiro	int save_errno = errno;
307364562Sgshapiro
307477349Sgshapiro	FIX_SYSV_SIGNAL(sig, intsig);
307577349Sgshapiro	errno = save_errno;
307677349Sgshapiro	CHECK_CRITICAL(sig);
307790792Sgshapiro	sm_allsignals(true);
307890792Sgshapiro
307964562Sgshapiro	if (sig != 0 && LogLevel > 79)
308038032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
308138032Speter	FileName = NULL;
308264562Sgshapiro
308364562Sgshapiro	/* Clean-up on aborted stdin message submission */
308464562Sgshapiro	if (CurEnv->e_id != NULL &&
308564562Sgshapiro	    (OpMode == MD_SMTP ||
308664562Sgshapiro	     OpMode == MD_DELIVER ||
308764562Sgshapiro	     OpMode == MD_ARPAFTP))
308864562Sgshapiro	{
308964562Sgshapiro		register ADDRESS *q;
309064562Sgshapiro
309164562Sgshapiro		/* don't return an error indication */
309264562Sgshapiro		CurEnv->e_to = NULL;
309364562Sgshapiro		CurEnv->e_flags &= ~EF_FATALERRS;
309464562Sgshapiro		CurEnv->e_flags |= EF_CLRQUEUE;
309564562Sgshapiro
309664562Sgshapiro		/*
309764562Sgshapiro		**  Spin through the addresses and
309864562Sgshapiro		**  mark them dead to prevent bounces
309964562Sgshapiro		*/
310064562Sgshapiro
310164562Sgshapiro		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
310264562Sgshapiro			q->q_state = QS_DONTSEND;
310364562Sgshapiro
310490792Sgshapiro		drop = true;
310564562Sgshapiro	}
310680785Sgshapiro	else if (OpMode != MD_TEST)
310790792Sgshapiro	{
310864562Sgshapiro		unlockqueue(CurEnv);
310938032Speter	}
311038032Speter
311190792Sgshapiro	finis(drop, false, EX_OK);
311290792Sgshapiro	/* NOTREACHED */
311338032Speter}
311490792Sgshapiro/*
311538032Speter**  DISCONNECT -- remove our connection with any foreground process
311638032Speter**
311738032Speter**	Parameters:
311838032Speter**		droplev -- how "deeply" we should drop the line.
311938032Speter**			0 -- ignore signals, mail back errors, make sure
312038032Speter**			     output goes to stdout.
312164562Sgshapiro**			1 -- also, make stdout go to /dev/null.
312238032Speter**			2 -- also, disconnect from controlling terminal
312338032Speter**			     (only for daemon mode).
312438032Speter**		e -- the current envelope.
312538032Speter**
312638032Speter**	Returns:
312738032Speter**		none
312838032Speter**
312938032Speter**	Side Effects:
313038032Speter**		Trys to insure that we are immune to vagaries of
313138032Speter**		the controlling tty.
313238032Speter*/
313338032Speter
313438032Spetervoid
313538032Speterdisconnect(droplev, e)
313638032Speter	int droplev;
313738032Speter	register ENVELOPE *e;
313838032Speter{
313938032Speter	int fd;
314038032Speter
314138032Speter	if (tTd(52, 1))
314290792Sgshapiro		sm_dprintf("disconnect: In %d Out %d, e=%p\n",
314390792Sgshapiro			   sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
314490792Sgshapiro			   sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
314538032Speter	if (tTd(52, 100))
314638032Speter	{
314790792Sgshapiro		sm_dprintf("don't\n");
314838032Speter		return;
314938032Speter	}
315038032Speter	if (LogLevel > 93)
315138032Speter		sm_syslog(LOG_DEBUG, e->e_id,
315264562Sgshapiro			  "disconnect level %d",
315364562Sgshapiro			  droplev);
315438032Speter
315538032Speter	/* be sure we don't get nasty signals */
315690792Sgshapiro	(void) sm_signal(SIGINT, SIG_IGN);
315790792Sgshapiro	(void) sm_signal(SIGQUIT, SIG_IGN);
315838032Speter
315938032Speter	/* we can't communicate with our caller, so.... */
316090792Sgshapiro	HoldErrs = true;
316138032Speter	CurEnv->e_errormode = EM_MAIL;
316238032Speter	Verbose = 0;
316390792Sgshapiro	DisConnected = true;
316438032Speter
316538032Speter	/* all input from /dev/null */
316690792Sgshapiro	if (InChannel != smioin)
316738032Speter	{
316890792Sgshapiro		(void) sm_io_close(InChannel, SM_TIME_DEFAULT);
316990792Sgshapiro		InChannel = smioin;
317038032Speter	}
317190792Sgshapiro	if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
317290792Sgshapiro			 SM_IO_RDONLY, NULL, smioin) == NULL)
317338032Speter		sm_syslog(LOG_ERR, e->e_id,
317490792Sgshapiro			  "disconnect: sm_io_reopen(\"%s\") failed: %s",
317590792Sgshapiro			  SM_PATH_DEVNULL, sm_errstring(errno));
317638032Speter
317790792Sgshapiro	/*
317890792Sgshapiro	**  output to the transcript
317990792Sgshapiro	**	We also compare the fd numbers here since OutChannel
318090792Sgshapiro	**	might be a layer on top of smioout due to encryption
318190792Sgshapiro	**	(see sfsasl.c).
318290792Sgshapiro	*/
318390792Sgshapiro
318490792Sgshapiro	if (OutChannel != smioout &&
318590792Sgshapiro	    sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
318690792Sgshapiro	    sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
318738032Speter	{
318890792Sgshapiro		(void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
318990792Sgshapiro		OutChannel = smioout;
319090792Sgshapiro
319190792Sgshapiro#if 0
319290792Sgshapiro		/*
319390792Sgshapiro		**  Has smioout been closed? Reopen it.
319490792Sgshapiro		**	This shouldn't happen anymore, the code is here
319590792Sgshapiro		**	just as a reminder.
319690792Sgshapiro		*/
319790792Sgshapiro
319890792Sgshapiro		if (smioout->sm_magic == NULL &&
319990792Sgshapiro		    sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
320090792Sgshapiro				 SM_IO_WRONLY, NULL, smioout) == NULL)
320190792Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
320290792Sgshapiro				  "disconnect: sm_io_reopen(\"%s\") failed: %s",
320390792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
320490792Sgshapiro#endif /* 0 */
320538032Speter	}
320638032Speter	if (droplev > 0)
320738032Speter	{
320890792Sgshapiro		fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
320964562Sgshapiro		if (fd == -1)
321064562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
321190792Sgshapiro				  "disconnect: open(\"%s\") failed: %s",
321290792Sgshapiro				  SM_PATH_DEVNULL, sm_errstring(errno));
321390792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
321464562Sgshapiro		(void) dup2(fd, STDOUT_FILENO);
321564562Sgshapiro		(void) dup2(fd, STDERR_FILENO);
321664562Sgshapiro		(void) close(fd);
321738032Speter	}
321838032Speter
321938032Speter	/* drop our controlling TTY completely if possible */
322038032Speter	if (droplev > 1)
322138032Speter	{
322238032Speter		(void) setsid();
322338032Speter		errno = 0;
322438032Speter	}
322538032Speter
322638032Speter#if XDEBUG
322738032Speter	checkfd012("disconnect");
322864562Sgshapiro#endif /* XDEBUG */
322938032Speter
323038032Speter	if (LogLevel > 71)
323190792Sgshapiro		sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
323290792Sgshapiro			  (int) CurrentPid);
323338032Speter
323438032Speter	errno = 0;
323538032Speter}
323638032Speter
323738032Speterstatic void
323838032Speterobsolete(argv)
323938032Speter	char *argv[];
324038032Speter{
324138032Speter	register char *ap;
324238032Speter	register char *op;
324338032Speter
324438032Speter	while ((ap = *++argv) != NULL)
324538032Speter	{
324638032Speter		/* Return if "--" or not an option of any form. */
324738032Speter		if (ap[0] != '-' || ap[1] == '-')
324838032Speter			return;
324938032Speter
325090792Sgshapiro#if _FFR_QUARANTINE
325190792Sgshapiro		/* Don't allow users to use "-Q." or "-Q ." */
325290792Sgshapiro		if ((ap[1] == 'Q' && ap[2] == '.') ||
325390792Sgshapiro		    (ap[1] == 'Q' && argv[1] != NULL &&
325490792Sgshapiro		     argv[1][0] == '.' && argv[1][1] == '\0'))
325590792Sgshapiro		{
325690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
325790792Sgshapiro					     "Can not use -Q.\n");
325890792Sgshapiro			exit(EX_USAGE);
325990792Sgshapiro		}
326090792Sgshapiro#endif /* _FFR_QUARANTINE */
326190792Sgshapiro
326238032Speter		/* skip over options that do have a value */
326338032Speter		op = strchr(OPTIONS, ap[1]);
326438032Speter		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
326538032Speter		    ap[1] != 'd' &&
326638032Speter#if defined(sony_news)
326738032Speter		    ap[1] != 'E' && ap[1] != 'J' &&
326864562Sgshapiro#endif /* defined(sony_news) */
326938032Speter		    argv[1] != NULL && argv[1][0] != '-')
327038032Speter		{
327138032Speter			argv++;
327238032Speter			continue;
327338032Speter		}
327438032Speter
327538032Speter		/* If -C doesn't have an argument, use sendmail.cf. */
327690792Sgshapiro#define __DEFPATH	"sendmail.cf"
327738032Speter		if (ap[1] == 'C' && ap[2] == '\0')
327838032Speter		{
327938032Speter			*argv = xalloc(sizeof(__DEFPATH) + 2);
328090792Sgshapiro			(void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
328190792Sgshapiro					   "-C", __DEFPATH);
328238032Speter		}
328338032Speter
328438032Speter		/* If -q doesn't have an argument, run it once. */
328538032Speter		if (ap[1] == 'q' && ap[2] == '\0')
328638032Speter			*argv = "-q0";
328738032Speter
328890792Sgshapiro#if _FFR_QUARANTINE
328990792Sgshapiro		/* If -Q doesn't have an argument, disable quarantining */
329090792Sgshapiro		if (ap[1] == 'Q' && ap[2] == '\0')
329190792Sgshapiro			*argv = "-Q.";
329290792Sgshapiro#endif /* _FFR_QUARANTINE */
329390792Sgshapiro
329438032Speter		/* if -d doesn't have an argument, use 0-99.1 */
329538032Speter		if (ap[1] == 'd' && ap[2] == '\0')
329638032Speter			*argv = "-d0-99.1";
329738032Speter
329864562Sgshapiro#if defined(sony_news)
329938032Speter		/* if -E doesn't have an argument, use -EC */
330038032Speter		if (ap[1] == 'E' && ap[2] == '\0')
330138032Speter			*argv = "-EC";
330238032Speter
330338032Speter		/* if -J doesn't have an argument, use -JJ */
330438032Speter		if (ap[1] == 'J' && ap[2] == '\0')
330538032Speter			*argv = "-JJ";
330664562Sgshapiro#endif /* defined(sony_news) */
330738032Speter	}
330838032Speter}
330990792Sgshapiro/*
331038032Speter**  AUTH_WARNING -- specify authorization warning
331138032Speter**
331238032Speter**	Parameters:
331338032Speter**		e -- the current envelope.
331438032Speter**		msg -- the text of the message.
331538032Speter**		args -- arguments to the message.
331638032Speter**
331738032Speter**	Returns:
331838032Speter**		none.
331938032Speter*/
332038032Speter
332138032Spetervoid
332238032Speter#ifdef __STDC__
332338032Speterauth_warning(register ENVELOPE *e, const char *msg, ...)
332464562Sgshapiro#else /* __STDC__ */
332538032Speterauth_warning(e, msg, va_alist)
332638032Speter	register ENVELOPE *e;
332738032Speter	const char *msg;
332838032Speter	va_dcl
332964562Sgshapiro#endif /* __STDC__ */
333038032Speter{
333138032Speter	char buf[MAXLINE];
333290792Sgshapiro	SM_VA_LOCAL_DECL
333338032Speter
333438032Speter	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
333538032Speter	{
333638032Speter		register char *p;
333738032Speter		static char hostbuf[48];
333838032Speter
333938032Speter		if (hostbuf[0] == '\0')
334071345Sgshapiro		{
334171345Sgshapiro			struct hostent *hp;
334238032Speter
334371345Sgshapiro			hp = myhostname(hostbuf, sizeof hostbuf);
334490792Sgshapiro#if NETINET6
334571345Sgshapiro			if (hp != NULL)
334671345Sgshapiro			{
334771345Sgshapiro				freehostent(hp);
334871345Sgshapiro				hp = NULL;
334971345Sgshapiro			}
335090792Sgshapiro#endif /* NETINET6 */
335171345Sgshapiro		}
335271345Sgshapiro
335390792Sgshapiro		(void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": ");
335438032Speter		p = &buf[strlen(buf)];
335590792Sgshapiro		SM_VA_START(ap, msg);
335690792Sgshapiro		(void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
335790792Sgshapiro		SM_VA_END(ap);
335890792Sgshapiro		addheader("X-Authentication-Warning", buf, 0, e);
335938032Speter		if (LogLevel > 3)
336038032Speter			sm_syslog(LOG_INFO, e->e_id,
336164562Sgshapiro				  "Authentication-Warning: %.400s",
336264562Sgshapiro				  buf);
336338032Speter	}
336438032Speter}
336590792Sgshapiro/*
336638032Speter**  GETEXTENV -- get from external environment
336738032Speter**
336838032Speter**	Parameters:
336938032Speter**		envar -- the name of the variable to retrieve
337038032Speter**
337138032Speter**	Returns:
337238032Speter**		The value, if any.
337338032Speter*/
337438032Speter
337590792Sgshapirostatic char *
337638032Spetergetextenv(envar)
337738032Speter	const char *envar;
337838032Speter{
337938032Speter	char **envp;
338038032Speter	int l;
338138032Speter
338238032Speter	l = strlen(envar);
3383102528Sgshapiro	for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
338438032Speter	{
338538032Speter		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
338638032Speter			return &(*envp)[l + 1];
338738032Speter	}
338838032Speter	return NULL;
338938032Speter}
339090792Sgshapiro/*
339190792Sgshapiro**  SETUSERENV -- set an environment in the propagated environment
339238032Speter**
339338032Speter**	Parameters:
339438032Speter**		envar -- the name of the environment variable.
339538032Speter**		value -- the value to which it should be set.  If
339638032Speter**			null, this is extracted from the incoming
339738032Speter**			environment.  If that is not set, the call
339838032Speter**			to setuserenv is ignored.
339938032Speter**
340038032Speter**	Returns:
340138032Speter**		none.
340238032Speter*/
340338032Speter
340438032Spetervoid
340538032Spetersetuserenv(envar, value)
340638032Speter	const char *envar;
340738032Speter	const char *value;
340838032Speter{
340964562Sgshapiro	int i, l;
341038032Speter	char **evp = UserEnviron;
341138032Speter	char *p;
341238032Speter
341338032Speter	if (value == NULL)
341438032Speter	{
341538032Speter		value = getextenv(envar);
341638032Speter		if (value == NULL)
341738032Speter			return;
341838032Speter	}
341938032Speter
342090792Sgshapiro	/* XXX enforce reasonable size? */
342164562Sgshapiro	i = strlen(envar) + 1;
342264562Sgshapiro	l = strlen(value) + i + 1;
342364562Sgshapiro	p = (char *) xalloc(l);
342490792Sgshapiro	(void) sm_strlcpyn(p, l, 3, envar, "=", value);
342538032Speter
342638032Speter	while (*evp != NULL && strncmp(*evp, p, i) != 0)
342738032Speter		evp++;
342838032Speter	if (*evp != NULL)
342938032Speter	{
343038032Speter		*evp++ = p;
343138032Speter	}
343238032Speter	else if (evp < &UserEnviron[MAXUSERENVIRON])
343338032Speter	{
343438032Speter		*evp++ = p;
343538032Speter		*evp = NULL;
343638032Speter	}
343738032Speter
343838032Speter	/* make sure it is in our environment as well */
343938032Speter	if (putenv(p) < 0)
344038032Speter		syserr("setuserenv: putenv(%s) failed", p);
344138032Speter}
344290792Sgshapiro/*
344338032Speter**  DUMPSTATE -- dump state
344438032Speter**
344538032Speter**	For debugging.
344638032Speter*/
344738032Speter
344838032Spetervoid
344938032Speterdumpstate(when)
345038032Speter	char *when;
345138032Speter{
345238032Speter	register char *j = macvalue('j', CurEnv);
345338032Speter	int rs;
345464562Sgshapiro	extern int NextMacroId;
345538032Speter
345638032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id,
345764562Sgshapiro		  "--- dumping state on %s: $j = %s ---",
345864562Sgshapiro		  when,
345964562Sgshapiro		  j == NULL ? "<NULL>" : j);
346038032Speter	if (j != NULL)
346138032Speter	{
346238032Speter		if (!wordinclass(j, 'w'))
346338032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
346464562Sgshapiro				  "*** $j not in $=w ***");
346538032Speter	}
346638032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
346790792Sgshapiro	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
346864562Sgshapiro		  NextMacroId, MAXMACROID);
346938032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
347090792Sgshapiro	printopenfds(true);
347138032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
347290792Sgshapiro	mci_dump_all(true);
347338032Speter	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
347438032Speter	if (rs > 0)
347538032Speter	{
347664562Sgshapiro		int status;
347738032Speter		register char **pvp;
347838032Speter		char *pv[MAXATOM + 1];
347938032Speter
348038032Speter		pv[0] = NULL;
348190792Sgshapiro		status = REWRITE(pv, rs, CurEnv);
348238032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
348364562Sgshapiro			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
348464562Sgshapiro			  status);
348538032Speter		for (pvp = pv; *pvp != NULL; pvp++)
348638032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
348738032Speter	}
348838032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
348938032Speter}
349090792Sgshapiro
349180785Sgshapiro#ifdef SIGUSR1
349290792Sgshapiro/*
349377349Sgshapiro**  SIGUSR1 -- Signal a request to dump state.
349477349Sgshapiro**
349577349Sgshapiro**	Parameters:
349677349Sgshapiro**		sig -- calling signal.
349777349Sgshapiro**
349877349Sgshapiro**	Returns:
349977349Sgshapiro**		none.
350077349Sgshapiro**
350177349Sgshapiro**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
350277349Sgshapiro**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
350377349Sgshapiro**		DOING.
350477349Sgshapiro**
350577349Sgshapiro**		XXX: More work is needed for this signal handler.
350677349Sgshapiro*/
350738032Speter
350838032Speter/* ARGSUSED */
350977349Sgshapirostatic SIGFUNC_DECL
351038032Spetersigusr1(sig)
351138032Speter	int sig;
351238032Speter{
351377349Sgshapiro	int save_errno = errno;
351490792Sgshapiro# if SM_HEAP_CHECK
351590792Sgshapiro	extern void dumpstab __P((void));
351690792Sgshapiro# endif /* SM_HEAP_CHECK */
351777349Sgshapiro
351877349Sgshapiro	FIX_SYSV_SIGNAL(sig, sigusr1);
351977349Sgshapiro	errno = save_errno;
352077349Sgshapiro	CHECK_CRITICAL(sig);
352138032Speter	dumpstate("user signal");
352290792Sgshapiro# if SM_HEAP_CHECK
352390792Sgshapiro	dumpstab();
352490792Sgshapiro# endif /* SM_HEAP_CHECK */
352577349Sgshapiro	errno = save_errno;
352638032Speter	return SIGFUNC_RETURN;
352738032Speter}
352890792Sgshapiro#endif /* SIGUSR1 */
352990792Sgshapiro
353090792Sgshapiro/*
353138032Speter**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
353238032Speter**
353338032Speter**	Parameters:
353438032Speter**		to_real_uid -- if set, drop to the real uid instead
353538032Speter**			of the RunAsUser.
353638032Speter**
353738032Speter**	Returns:
353838032Speter**		EX_OSERR if the setuid failed.
353938032Speter**		EX_OK otherwise.
354038032Speter*/
354138032Speter
354238032Speterint
354338032Speterdrop_privileges(to_real_uid)
354438032Speter	bool to_real_uid;
354538032Speter{
354638032Speter	int rval = EX_OK;
354738032Speter	GIDSET_T emptygidset[1];
354838032Speter
354938032Speter	if (tTd(47, 1))
355090792Sgshapiro		sm_dprintf("drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n",
355190792Sgshapiro			   (int) to_real_uid,
355290792Sgshapiro			   (int) RealUid, (int) RealGid,
355390792Sgshapiro			   (int) getuid(), (int) getgid(),
355490792Sgshapiro			   (int) geteuid(), (int) getegid(),
355590792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
355638032Speter
355738032Speter	if (to_real_uid)
355838032Speter	{
355938032Speter		RunAsUserName = RealUserName;
356038032Speter		RunAsUid = RealUid;
356138032Speter		RunAsGid = RealGid;
356294334Sgshapiro		EffGid = RunAsGid;
356338032Speter	}
356438032Speter
356538032Speter	/* make sure no one can grab open descriptors for secret files */
356638032Speter	endpwent();
356790792Sgshapiro	sm_mbdb_terminate();
356838032Speter
356938032Speter	/* reset group permissions; these can be set later */
357038032Speter	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
357190792Sgshapiro
357290792Sgshapiro	/*
357390792Sgshapiro	**  Notice:  on some OS (Linux...) the setgroups() call causes
357490792Sgshapiro	**	a logfile entry if sendmail is not run by root.
357590792Sgshapiro	**	However, it is unclear (no POSIX standard) whether
357690792Sgshapiro	**	setgroups() can only succeed if executed by root.
357790792Sgshapiro	**	So for now we keep it as it is; if you want to change it, use
357890792Sgshapiro	**  if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
357990792Sgshapiro	*/
358090792Sgshapiro
358138032Speter	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
358264562Sgshapiro	{
358364562Sgshapiro		syserr("drop_privileges: setgroups(1, %d) failed",
358490792Sgshapiro		       (int) emptygidset[0]);
358538032Speter		rval = EX_OSERR;
358664562Sgshapiro	}
358738032Speter
358890792Sgshapiro	/* reset primary group id */
358990792Sgshapiro	if (to_real_uid)
359064562Sgshapiro	{
359190792Sgshapiro		/*
359290792Sgshapiro		**  Drop gid to real gid.
359390792Sgshapiro		**  On some OS we must reset the effective[/real[/saved]] gid,
359490792Sgshapiro		**  and then use setgid() to finally drop all group privileges.
359590792Sgshapiro		**  Later on we check whether we can get back the
359690792Sgshapiro		**  effective gid.
359790792Sgshapiro		*/
359890792Sgshapiro
359990792Sgshapiro#if HASSETEGID
360090792Sgshapiro		if (setegid(RunAsGid) < 0)
360190792Sgshapiro		{
360290792Sgshapiro			syserr("drop_privileges: setegid(%d) failed",
360390792Sgshapiro			       (int) RunAsGid);
360490792Sgshapiro			rval = EX_OSERR;
360590792Sgshapiro		}
360690792Sgshapiro#else /* HASSETEGID */
360790792Sgshapiro# if HASSETREGID
360890792Sgshapiro		if (setregid(RunAsGid, RunAsGid) < 0)
360990792Sgshapiro		{
361090792Sgshapiro			syserr("drop_privileges: setregid(%d, %d) failed",
361190792Sgshapiro			       (int) RunAsGid, (int) RunAsGid);
361290792Sgshapiro			rval = EX_OSERR;
361390792Sgshapiro		}
361490792Sgshapiro# else /* HASSETREGID */
361590792Sgshapiro#  if HASSETRESGID
361690792Sgshapiro		if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
361790792Sgshapiro		{
361890792Sgshapiro			syserr("drop_privileges: setresgid(%d, %d, %d) failed",
361990792Sgshapiro			       (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
362090792Sgshapiro			rval = EX_OSERR;
362190792Sgshapiro		}
362290792Sgshapiro#  endif /* HASSETRESGID */
362390792Sgshapiro# endif /* HASSETREGID */
362490792Sgshapiro#endif /* HASSETEGID */
362564562Sgshapiro	}
362690792Sgshapiro	if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
362790792Sgshapiro	{
362890792Sgshapiro		if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
362990792Sgshapiro		{
363090792Sgshapiro			syserr("drop_privileges: setgid(%d) failed",
363190792Sgshapiro			       (int) RunAsGid);
363290792Sgshapiro			rval = EX_OSERR;
363390792Sgshapiro		}
363490792Sgshapiro		errno = 0;
363590792Sgshapiro		if (rval == EX_OK && getegid() != RunAsGid)
363690792Sgshapiro		{
363790792Sgshapiro			syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
363890792Sgshapiro			       (int) getegid(), (int) RunAsGid);
363990792Sgshapiro			rval = EX_OSERR;
364090792Sgshapiro		}
364190792Sgshapiro	}
364290792Sgshapiro
364390792Sgshapiro	/* fiddle with uid */
364464562Sgshapiro	if (to_real_uid || RunAsUid != 0)
364564562Sgshapiro	{
364694334Sgshapiro		uid_t euid;
364764562Sgshapiro
364890792Sgshapiro		/*
364990792Sgshapiro		**  Try to setuid(RunAsUid).
365090792Sgshapiro		**  euid must be RunAsUid,
365194334Sgshapiro		**  ruid must be RunAsUid unless (e|r)uid wasn't 0
365294334Sgshapiro		**	and we didn't have to drop privileges to the real uid.
365390792Sgshapiro		*/
365490792Sgshapiro
365590792Sgshapiro		if (setuid(RunAsUid) < 0 ||
365694334Sgshapiro		    geteuid() != RunAsUid ||
365794334Sgshapiro		    (getuid() != RunAsUid &&
365894334Sgshapiro		     (to_real_uid || geteuid() == 0 || getuid() == 0)))
365964562Sgshapiro		{
366090792Sgshapiro#if HASSETREUID
366190792Sgshapiro			/*
366290792Sgshapiro			**  if ruid != RunAsUid, euid == RunAsUid, then
366390792Sgshapiro			**  try resetting just the real uid, then using
366490792Sgshapiro			**  setuid() to drop the saved-uid as well.
366590792Sgshapiro			*/
366690792Sgshapiro
366794334Sgshapiro			if (geteuid() == RunAsUid)
366890792Sgshapiro			{
366990792Sgshapiro				if (setreuid(RunAsUid, -1) < 0)
367090792Sgshapiro				{
367190792Sgshapiro					syserr("drop_privileges: setreuid(%d, -1) failed",
367290792Sgshapiro					       (int) RunAsUid);
367390792Sgshapiro					rval = EX_OSERR;
367490792Sgshapiro				}
367590792Sgshapiro				if (setuid(RunAsUid) < 0)
367690792Sgshapiro				{
367790792Sgshapiro					syserr("drop_privileges: second setuid(%d) attempt failed",
367890792Sgshapiro					       (int) RunAsUid);
367990792Sgshapiro					rval = EX_OSERR;
368090792Sgshapiro				}
368190792Sgshapiro			}
368290792Sgshapiro			else
368390792Sgshapiro#endif /* HASSETREUID */
368490792Sgshapiro			{
368590792Sgshapiro				syserr("drop_privileges: setuid(%d) failed",
368690792Sgshapiro				       (int) RunAsUid);
368790792Sgshapiro				rval = EX_OSERR;
368890792Sgshapiro			}
368964562Sgshapiro		}
369094334Sgshapiro		euid = geteuid();
369190792Sgshapiro		if (RunAsUid != 0 && setuid(0) == 0)
369264562Sgshapiro		{
369364562Sgshapiro			/*
369464562Sgshapiro			**  Believe it or not, the Linux capability model
369564562Sgshapiro			**  allows a non-root process to override setuid()
369664562Sgshapiro			**  on a process running as root and prevent that
369764562Sgshapiro			**  process from dropping privileges.
369864562Sgshapiro			*/
369964562Sgshapiro
370064562Sgshapiro			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
370164562Sgshapiro			rval = EX_OSERR;
370264562Sgshapiro		}
370364562Sgshapiro		else if (RunAsUid != euid && setuid(euid) == 0)
370464562Sgshapiro		{
370564562Sgshapiro			/*
370664562Sgshapiro			**  Some operating systems will keep the saved-uid
370764562Sgshapiro			**  if a non-root effective-uid calls setuid(real-uid)
370864562Sgshapiro			**  making it possible to set it back again later.
370964562Sgshapiro			*/
371064562Sgshapiro
371190792Sgshapiro			syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
371264562Sgshapiro			rval = EX_OSERR;
371364562Sgshapiro		}
371464562Sgshapiro	}
371590792Sgshapiro
371690792Sgshapiro	if ((to_real_uid || RunAsGid != 0) &&
371790792Sgshapiro	    rval == EX_OK && RunAsGid != EffGid &&
371890792Sgshapiro	    getuid() != 0 && geteuid() != 0)
371990792Sgshapiro	{
372090792Sgshapiro		errno = 0;
372190792Sgshapiro		if (setgid(EffGid) == 0)
372290792Sgshapiro		{
372390792Sgshapiro			syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
372490792Sgshapiro			       (int) EffGid);
372590792Sgshapiro			rval = EX_OSERR;
372690792Sgshapiro		}
372790792Sgshapiro	}
372890792Sgshapiro
372938032Speter	if (tTd(47, 5))
373038032Speter	{
373190792Sgshapiro		sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
373290792Sgshapiro			   (int) geteuid(), (int) getuid(),
373390792Sgshapiro			   (int) getegid(), (int) getgid());
373490792Sgshapiro		sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
373590792Sgshapiro			   (int) RunAsUid, (int) RunAsGid);
373664562Sgshapiro		if (tTd(47, 10))
373790792Sgshapiro			sm_dprintf("drop_privileges: rval = %d\n", rval);
373838032Speter	}
373938032Speter	return rval;
374038032Speter}
374190792Sgshapiro/*
374238032Speter**  FILL_FD -- make sure a file descriptor has been properly allocated
374338032Speter**
374438032Speter**	Used to make sure that stdin/out/err are allocated on startup
374538032Speter**
374638032Speter**	Parameters:
374738032Speter**		fd -- the file descriptor to be filled.
374838032Speter**		where -- a string used for logging.  If NULL, this is
374938032Speter**			being called on startup, and logging should
375038032Speter**			not be done.
375138032Speter**
375238032Speter**	Returns:
375338032Speter**		none
375490792Sgshapiro**
375590792Sgshapiro**	Side Effects:
375690792Sgshapiro**		possibly changes MissingFds
375738032Speter*/
375838032Speter
375938032Spetervoid
376038032Speterfill_fd(fd, where)
376138032Speter	int fd;
376238032Speter	char *where;
376338032Speter{
376438032Speter	int i;
376538032Speter	struct stat stbuf;
376638032Speter
376738032Speter	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
376838032Speter		return;
376938032Speter
377038032Speter	if (where != NULL)
377138032Speter		syserr("fill_fd: %s: fd %d not open", where, fd);
377238032Speter	else
377338032Speter		MissingFds |= 1 << fd;
377490792Sgshapiro	i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
377538032Speter	if (i < 0)
377638032Speter	{
377790792Sgshapiro		syserr("!fill_fd: %s: cannot open %s",
377890792Sgshapiro		       where == NULL ? "startup" : where, SM_PATH_DEVNULL);
377938032Speter	}
378038032Speter	if (fd != i)
378138032Speter	{
378238032Speter		(void) dup2(i, fd);
378338032Speter		(void) close(i);
378438032Speter	}
378538032Speter}
378690792Sgshapiro/*
378790792Sgshapiro**  SM_PRINTOPTIONS -- print options
378890792Sgshapiro**
378990792Sgshapiro**	Parameters:
379090792Sgshapiro**		options -- array of options.
379190792Sgshapiro**
379290792Sgshapiro**	Returns:
379390792Sgshapiro**		none.
379490792Sgshapiro*/
379590792Sgshapiro
379690792Sgshapirostatic void
379790792Sgshapirosm_printoptions(options)
379890792Sgshapiro	char **options;
379990792Sgshapiro{
380090792Sgshapiro	int ll;
380190792Sgshapiro	char **av;
380290792Sgshapiro
380390792Sgshapiro	av = options;
380490792Sgshapiro	ll = 7;
380590792Sgshapiro	while (*av != NULL)
380690792Sgshapiro	{
380790792Sgshapiro		if (ll + strlen(*av) > 63)
380890792Sgshapiro		{
380990792Sgshapiro			sm_dprintf("\n");
381090792Sgshapiro			ll = 0;
381190792Sgshapiro		}
381290792Sgshapiro		if (ll == 0)
381390792Sgshapiro			sm_dprintf("\t\t");
381490792Sgshapiro		else
381590792Sgshapiro			sm_dprintf(" ");
381690792Sgshapiro		sm_dprintf("%s", *av);
381790792Sgshapiro		ll += strlen(*av++) + 1;
381890792Sgshapiro	}
381990792Sgshapiro	sm_dprintf("\n");
382090792Sgshapiro}
382190792Sgshapiro/*
382238032Speter**  TESTMODELINE -- process a test mode input line
382338032Speter**
382438032Speter**	Parameters:
382538032Speter**		line -- the input line.
382638032Speter**		e -- the current environment.
382738032Speter**	Syntax:
382838032Speter**		#  a comment
382938032Speter**		.X process X as a configuration line
383038032Speter**		=X dump a configuration item (such as mailers)
383138032Speter**		$X dump a macro or class
383238032Speter**		/X try an activity
383338032Speter**		X  normal process through rule set X
383438032Speter*/
383538032Speter
383664562Sgshapirostatic void
383738032Spetertestmodeline(line, e)
383838032Speter	char *line;
383938032Speter	ENVELOPE *e;
384038032Speter{
384138032Speter	register char *p;
384238032Speter	char *q;
384338032Speter	auto char *delimptr;
384438032Speter	int mid;
384538032Speter	int i, rs;
384638032Speter	STAB *map;
384738032Speter	char **s;
384838032Speter	struct rewrite *rw;
384938032Speter	ADDRESS a;
385038032Speter	static int tryflags = RF_COPYNONE;
385138032Speter	char exbuf[MAXLINE];
385290792Sgshapiro	extern unsigned char TokTypeNoC[];
385338032Speter
385466494Sgshapiro	/* skip leading spaces */
385566494Sgshapiro	while (*line == ' ')
385666494Sgshapiro		line++;
385766494Sgshapiro
385838032Speter	switch (line[0])
385938032Speter	{
386038032Speter	  case '#':
386164562Sgshapiro	  case '\0':
386238032Speter		return;
386338032Speter
386438032Speter	  case '?':
386564562Sgshapiro		help("-bt", e);
386638032Speter		return;
386738032Speter
386838032Speter	  case '.':		/* config-style settings */
386938032Speter		switch (line[1])
387038032Speter		{
387138032Speter		  case 'D':
387290792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
387371345Sgshapiro			if (mid == 0)
387438032Speter				return;
387538032Speter			translate_dollars(delimptr);
387690792Sgshapiro			macdefine(&e->e_macro, A_TEMP, mid, delimptr);
387738032Speter			break;
387838032Speter
387938032Speter		  case 'C':
388038032Speter			if (line[2] == '\0')	/* not to call syserr() */
388138032Speter				return;
388238032Speter
388390792Sgshapiro			mid = macid_parse(&line[2], &delimptr);
388471345Sgshapiro			if (mid == 0)
388538032Speter				return;
388638032Speter			translate_dollars(delimptr);
388738032Speter			expand(delimptr, exbuf, sizeof exbuf, e);
388838032Speter			p = exbuf;
388938032Speter			while (*p != '\0')
389038032Speter			{
389138032Speter				register char *wd;
389238032Speter				char delim;
389338032Speter
389438032Speter				while (*p != '\0' && isascii(*p) && isspace(*p))
389538032Speter					p++;
389638032Speter				wd = p;
389738032Speter				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
389838032Speter					p++;
389938032Speter				delim = *p;
390038032Speter				*p = '\0';
390138032Speter				if (wd[0] != '\0')
390238032Speter					setclass(mid, wd);
390338032Speter				*p = delim;
390438032Speter			}
390538032Speter			break;
390638032Speter
390738032Speter		  case '\0':
390890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
390990792Sgshapiro					     "Usage: .[DC]macro value(s)\n");
391038032Speter			break;
391138032Speter
391238032Speter		  default:
391390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
391490792Sgshapiro					     "Unknown \".\" command %s\n", line);
391538032Speter			break;
391638032Speter		}
391738032Speter		return;
391838032Speter
391938032Speter	  case '=':		/* config-style settings */
392038032Speter		switch (line[1])
392138032Speter		{
392238032Speter		  case 'S':		/* dump rule set */
392338032Speter			rs = strtorwset(&line[2], NULL, ST_FIND);
392438032Speter			if (rs < 0)
392538032Speter			{
392690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
392790792Sgshapiro						     "Undefined ruleset %s\n", &line[2]);
392838032Speter				return;
392938032Speter			}
393038032Speter			rw = RewriteRules[rs];
393138032Speter			if (rw == NULL)
393238032Speter				return;
393338032Speter			do
393438032Speter			{
393590792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
393690792Sgshapiro						  'R');
393738032Speter				s = rw->r_lhs;
393838032Speter				while (*s != NULL)
393938032Speter				{
394038032Speter					xputs(*s++);
394190792Sgshapiro					(void) sm_io_putc(smioout,
394290792Sgshapiro							  SM_TIME_DEFAULT, ' ');
394338032Speter				}
394490792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
394590792Sgshapiro						  '\t');
394690792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
394790792Sgshapiro						  '\t');
394838032Speter				s = rw->r_rhs;
394938032Speter				while (*s != NULL)
395038032Speter				{
395138032Speter					xputs(*s++);
395290792Sgshapiro					(void) sm_io_putc(smioout,
395390792Sgshapiro							  SM_TIME_DEFAULT, ' ');
395438032Speter				}
395590792Sgshapiro				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
395690792Sgshapiro						  '\n');
395738032Speter			} while ((rw = rw->r_next) != NULL);
395838032Speter			break;
395938032Speter
396038032Speter		  case 'M':
396138032Speter			for (i = 0; i < MAXMAILERS; i++)
396238032Speter			{
396338032Speter				if (Mailer[i] != NULL)
396438032Speter					printmailer(Mailer[i]);
396538032Speter			}
396638032Speter			break;
396738032Speter
396838032Speter		  case '\0':
396990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
397090792Sgshapiro					     "Usage: =Sruleset or =M\n");
397138032Speter			break;
397238032Speter
397338032Speter		  default:
397490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
397590792Sgshapiro					     "Unknown \"=\" command %s\n", line);
397638032Speter			break;
397738032Speter		}
397838032Speter		return;
397938032Speter
398038032Speter	  case '-':		/* set command-line-like opts */
398138032Speter		switch (line[1])
398238032Speter		{
398338032Speter		  case 'd':
398438032Speter			tTflag(&line[2]);
398538032Speter			break;
398638032Speter
398738032Speter		  case '\0':
398890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
398990792Sgshapiro					     "Usage: -d{debug arguments}\n");
399038032Speter			break;
399138032Speter
399238032Speter		  default:
399390792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
399490792Sgshapiro					     "Unknown \"-\" command %s\n", line);
399538032Speter			break;
399638032Speter		}
399738032Speter		return;
399838032Speter
399938032Speter	  case '$':
400038032Speter		if (line[1] == '=')
400138032Speter		{
400290792Sgshapiro			mid = macid(&line[2]);
400371345Sgshapiro			if (mid != 0)
400438032Speter				stabapply(dump_class, mid);
400538032Speter			return;
400638032Speter		}
400790792Sgshapiro		mid = macid(&line[1]);
400871345Sgshapiro		if (mid == 0)
400938032Speter			return;
401038032Speter		p = macvalue(mid, e);
401138032Speter		if (p == NULL)
401290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
401390792Sgshapiro					     "Undefined\n");
401438032Speter		else
401538032Speter		{
401638032Speter			xputs(p);
401790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
401890792Sgshapiro					     "\n");
401938032Speter		}
402038032Speter		return;
402138032Speter
402238032Speter	  case '/':		/* miscellaneous commands */
402338032Speter		p = &line[strlen(line)];
402438032Speter		while (--p >= line && isascii(*p) && isspace(*p))
402538032Speter			*p = '\0';
402638032Speter		p = strpbrk(line, " \t");
402738032Speter		if (p != NULL)
402838032Speter		{
402938032Speter			while (isascii(*p) && isspace(*p))
403038032Speter				*p++ = '\0';
403138032Speter		}
403238032Speter		else
403338032Speter			p = "";
403438032Speter		if (line[1] == '\0')
403538032Speter		{
403690792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
403790792Sgshapiro					     "Usage: /[canon|map|mx|parse|try|tryflags]\n");
403838032Speter			return;
403938032Speter		}
404090792Sgshapiro		if (sm_strcasecmp(&line[1], "quit") == 0)
404164562Sgshapiro		{
404264562Sgshapiro			CurEnv->e_id = NULL;
404390792Sgshapiro			finis(true, true, ExitStat);
404490792Sgshapiro			/* NOTREACHED */
404564562Sgshapiro		}
404690792Sgshapiro		if (sm_strcasecmp(&line[1], "mx") == 0)
404738032Speter		{
404838032Speter#if NAMED_BIND
404938032Speter			/* look up MX records */
405038032Speter			int nmx;
405138032Speter			auto int rcode;
405238032Speter			char *mxhosts[MAXMXHOSTS + 1];
405338032Speter
405438032Speter			if (*p == '\0')
405538032Speter			{
405690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
405790792Sgshapiro						     "Usage: /mx address\n");
405838032Speter				return;
405938032Speter			}
406090792Sgshapiro			nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
406190792Sgshapiro				      NULL);
406290792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
406390792Sgshapiro					     "getmxrr(%s) returns %d value(s):\n",
406490792Sgshapiro				p, nmx);
406538032Speter			for (i = 0; i < nmx; i++)
406690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
406790792Sgshapiro						     "\t%s\n", mxhosts[i]);
406864562Sgshapiro#else /* NAMED_BIND */
406990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
407090792Sgshapiro					     "No MX code compiled in\n");
407164562Sgshapiro#endif /* NAMED_BIND */
407238032Speter		}
407390792Sgshapiro		else if (sm_strcasecmp(&line[1], "canon") == 0)
407438032Speter		{
407538032Speter			char host[MAXHOSTNAMELEN];
407638032Speter
407738032Speter			if (*p == '\0')
407838032Speter			{
407990792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
408090792Sgshapiro						     "Usage: /canon address\n");
408138032Speter				return;
408238032Speter			}
408390792Sgshapiro			else if (sm_strlcpy(host, p, sizeof host) >= sizeof host)
408438032Speter			{
408590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
408690792Sgshapiro						     "Name too long\n");
408738032Speter				return;
408838032Speter			}
408990792Sgshapiro			(void) getcanonname(host, sizeof host, HasWildcardMX,
409090792Sgshapiro					    NULL);
409190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
409290792Sgshapiro					     "getcanonname(%s) returns %s\n",
409390792Sgshapiro					     p, host);
409438032Speter		}
409590792Sgshapiro		else if (sm_strcasecmp(&line[1], "map") == 0)
409638032Speter		{
409738032Speter			auto int rcode = EX_OK;
409838032Speter			char *av[2];
409938032Speter
410038032Speter			if (*p == '\0')
410138032Speter			{
410290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
410390792Sgshapiro						     "Usage: /map mapname key\n");
410438032Speter				return;
410538032Speter			}
410690792Sgshapiro			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q));			     q++)
410738032Speter				continue;
410838032Speter			if (*q == '\0')
410938032Speter			{
411090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
411190792Sgshapiro						     "No key specified\n");
411238032Speter				return;
411338032Speter			}
411438032Speter			*q++ = '\0';
411538032Speter			map = stab(p, ST_MAP, ST_FIND);
411638032Speter			if (map == NULL)
411738032Speter			{
411890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
411990792Sgshapiro						     "Map named \"%s\" not found\n", p);
412038032Speter				return;
412138032Speter			}
412264562Sgshapiro			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
412364562Sgshapiro			    !openmap(&(map->s_map)))
412438032Speter			{
412590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
412690792Sgshapiro						     "Map named \"%s\" not open\n", p);
412738032Speter				return;
412838032Speter			}
412990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
413090792Sgshapiro					     "map_lookup: %s (%s) ", p, q);
413138032Speter			av[0] = q;
413238032Speter			av[1] = NULL;
413338032Speter			p = (*map->s_map.map_class->map_lookup)
413438032Speter					(&map->s_map, q, av, &rcode);
413538032Speter			if (p == NULL)
413690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
413790792Sgshapiro						     "no match (%d)\n",
413890792Sgshapiro						     rcode);
413938032Speter			else
414090792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
414190792Sgshapiro						     "returns %s (%d)\n", p,
414290792Sgshapiro						     rcode);
414338032Speter		}
414490792Sgshapiro		else if (sm_strcasecmp(&line[1], "try") == 0)
414538032Speter		{
414638032Speter			MAILER *m;
414764562Sgshapiro			STAB *st;
414838032Speter			auto int rcode = EX_OK;
414938032Speter
415038032Speter			q = strpbrk(p, " \t");
415138032Speter			if (q != NULL)
415238032Speter			{
415338032Speter				while (isascii(*q) && isspace(*q))
415438032Speter					*q++ = '\0';
415538032Speter			}
415638032Speter			if (q == NULL || *q == '\0')
415738032Speter			{
415890792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
415990792Sgshapiro						     "Usage: /try mailer address\n");
416038032Speter				return;
416138032Speter			}
416264562Sgshapiro			st = stab(p, ST_MAILER, ST_FIND);
416364562Sgshapiro			if (st == NULL)
416438032Speter			{
416590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
416690792Sgshapiro						     "Unknown mailer %s\n", p);
416738032Speter				return;
416838032Speter			}
416964562Sgshapiro			m = st->s_mailer;
417090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
417190792Sgshapiro					     "Trying %s %s address %s for mailer %s\n",
417290792Sgshapiro				     bitset(RF_HEADERADDR, tryflags) ? "header"
417390792Sgshapiro							: "envelope",
417490792Sgshapiro				     bitset(RF_SENDERADDR, tryflags) ? "sender"
417590792Sgshapiro							: "recipient", q, p);
417638032Speter			p = remotename(q, m, tryflags, &rcode, CurEnv);
417790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
417890792Sgshapiro					     "Rcode = %d, addr = %s\n",
417990792Sgshapiro					     rcode, p == NULL ? "<NULL>" : p);
418038032Speter			e->e_to = NULL;
418138032Speter		}
418290792Sgshapiro		else if (sm_strcasecmp(&line[1], "tryflags") == 0)
418338032Speter		{
418438032Speter			if (*p == '\0')
418538032Speter			{
418690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
418790792Sgshapiro						     "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
418838032Speter				return;
418938032Speter			}
419038032Speter			for (; *p != '\0'; p++)
419138032Speter			{
419238032Speter				switch (*p)
419338032Speter				{
419438032Speter				  case 'H':
419538032Speter				  case 'h':
419638032Speter					tryflags |= RF_HEADERADDR;
419738032Speter					break;
419838032Speter
419938032Speter				  case 'E':
420038032Speter				  case 'e':
420138032Speter					tryflags &= ~RF_HEADERADDR;
420238032Speter					break;
420338032Speter
420438032Speter				  case 'S':
420538032Speter				  case 's':
420638032Speter					tryflags |= RF_SENDERADDR;
420738032Speter					break;
420838032Speter
420938032Speter				  case 'R':
421038032Speter				  case 'r':
421138032Speter					tryflags &= ~RF_SENDERADDR;
421238032Speter					break;
421338032Speter				}
421438032Speter			}
421564562Sgshapiro			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
421664562Sgshapiro			exbuf[1] = ' ';
421764562Sgshapiro			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
421864562Sgshapiro			exbuf[3] = '\0';
421990792Sgshapiro			macdefine(&e->e_macro, A_TEMP,
422090792Sgshapiro				macid("{addr_type}"), exbuf);
422138032Speter		}
422290792Sgshapiro		else if (sm_strcasecmp(&line[1], "parse") == 0)
422338032Speter		{
422438032Speter			if (*p == '\0')
422538032Speter			{
422690792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
422790792Sgshapiro						     "Usage: /parse address\n");
422838032Speter				return;
422938032Speter			}
423038032Speter			q = crackaddr(p);
423190792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
423290792Sgshapiro					     "Cracked address = ");
423338032Speter			xputs(q);
423490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
423590792Sgshapiro					     "\nParsing %s %s address\n",
423690792Sgshapiro					     bitset(RF_HEADERADDR, tryflags) ?
423790792Sgshapiro							"header" : "envelope",
423890792Sgshapiro					     bitset(RF_SENDERADDR, tryflags) ?
423990792Sgshapiro							"sender" : "recipient");
424090792Sgshapiro			if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
424190792Sgshapiro			    == NULL)
424290792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
424390792Sgshapiro						     "Cannot parse\n");
424438032Speter			else if (a.q_host != NULL && a.q_host[0] != '\0')
424590792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
424690792Sgshapiro						     "mailer %s, host %s, user %s\n",
424790792Sgshapiro						     a.q_mailer->m_name,
424890792Sgshapiro						     a.q_host,
424990792Sgshapiro						     a.q_user);
425038032Speter			else
425190792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
425290792Sgshapiro						     "mailer %s, user %s\n",
425390792Sgshapiro						     a.q_mailer->m_name,
425490792Sgshapiro						     a.q_user);
425538032Speter			e->e_to = NULL;
425638032Speter		}
425738032Speter		else
425838032Speter		{
425990792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
426090792Sgshapiro					     "Unknown \"/\" command %s\n",
426190792Sgshapiro					     line);
426238032Speter		}
426338032Speter		return;
426438032Speter	}
426538032Speter
426638032Speter	for (p = line; isascii(*p) && isspace(*p); p++)
426738032Speter		continue;
426838032Speter	q = p;
426938032Speter	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
427038032Speter		p++;
427138032Speter	if (*p == '\0')
427238032Speter	{
427390792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
427490792Sgshapiro				     "No address!\n");
427538032Speter		return;
427638032Speter	}
427738032Speter	*p = '\0';
427890792Sgshapiro	if (invalidaddr(p + 1, NULL, true))
427938032Speter		return;
428038032Speter	do
428138032Speter	{
428238032Speter		register char **pvp;
428338032Speter		char pvpbuf[PSBUFSIZE];
428438032Speter
428538032Speter		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
428664562Sgshapiro			      &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
428738032Speter		if (pvp == NULL)
428838032Speter			continue;
428938032Speter		p = q;
429038032Speter		while (*p != '\0')
429138032Speter		{
429264562Sgshapiro			int status;
429338032Speter
429438032Speter			rs = strtorwset(p, NULL, ST_FIND);
429538032Speter			if (rs < 0)
429638032Speter			{
429790792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
429890792Sgshapiro						     "Undefined ruleset %s\n",
429990792Sgshapiro						     p);
430038032Speter				break;
430138032Speter			}
430290792Sgshapiro			status = REWRITE(pvp, rs, e);
430364562Sgshapiro			if (status != EX_OK)
430490792Sgshapiro				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
430590792Sgshapiro						     "== Ruleset %s (%d) status %d\n",
430690792Sgshapiro						     p, rs, status);
430738032Speter			while (*p != '\0' && *p++ != ',')
430838032Speter				continue;
430938032Speter		}
431038032Speter	} while (*(p = delimptr) != '\0');
431138032Speter}
431238032Speter
431364562Sgshapirostatic void
431438032Speterdump_class(s, id)
431538032Speter	register STAB *s;
431638032Speter	int id;
431738032Speter{
431890792Sgshapiro	if (s->s_symtype != ST_CLASS)
431938032Speter		return;
432071345Sgshapiro	if (bitnset(bitidx(id), s->s_class))
432190792Sgshapiro		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
432290792Sgshapiro				     "%s\n", s->s_name);
432338032Speter}
432490792Sgshapiro
432590792Sgshapiro/*
432690792Sgshapiro**  An exception type used to create QuickAbort exceptions.
432790792Sgshapiro**  This is my first cut at converting QuickAbort from longjmp to exceptions.
432890792Sgshapiro**  These exceptions have a single integer argument, which is the argument
432990792Sgshapiro**  to longjmp in the original code (either 1 or 2).  I don't know the
433090792Sgshapiro**  significance of 1 vs 2: the calls to setjmp don't care.
433190792Sgshapiro*/
433290792Sgshapiro
433390792Sgshapiroconst SM_EXC_TYPE_T EtypeQuickAbort =
433490792Sgshapiro{
433590792Sgshapiro	SmExcTypeMagic,
433690792Sgshapiro	"E:mta.quickabort",
433790792Sgshapiro	"i",
433890792Sgshapiro	sm_etype_printf,
433990792Sgshapiro	"quick abort %0",
434090792Sgshapiro};
4341