xref: /freebsd/contrib/sendmail/src/main.c (revision 380a989b3223d455375b4fae70fd0b9bdd43bafb)
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12 
13 #ifndef lint
14 static char copyright[] =
15 "@(#) Copyright (c) 1998 Sendmail, Inc.  All rights reserved.\n\
16      Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
17      Copyright (c) 1988, 1993\n\
18 	The Regents of the University of California.  All rights reserved.\n";
19 #endif /* not lint */
20 
21 #ifndef lint
22 static char sccsid[] = "@(#)main.c	8.302 (Berkeley) 6/4/98";
23 #endif /* not lint */
24 
25 #define	_DEFINE
26 
27 #include "sendmail.h"
28 #include <arpa/inet.h>
29 #include <grp.h>
30 #if NAMED_BIND
31 #include <resolv.h>
32 #endif
33 
34 /*
35 **  SENDMAIL -- Post mail to a set of destinations.
36 **
37 **	This is the basic mail router.  All user mail programs should
38 **	call this routine to actually deliver mail.  Sendmail in
39 **	turn calls a bunch of mail servers that do the real work of
40 **	delivering the mail.
41 **
42 **	Sendmail is driven by settings read in from /etc/sendmail.cf
43 **	(read by readcf.c).
44 **
45 **	Usage:
46 **		/usr/lib/sendmail [flags] addr ...
47 **
48 **		See the associated documentation for details.
49 **
50 **	Author:
51 **		Eric Allman, UCB/INGRES (until 10/81).
52 **			     Britton-Lee, Inc., purveyors of fine
53 **				database computers (11/81 - 10/88).
54 **			     International Computer Science Institute
55 **				(11/88 - 9/89).
56 **			     UCB/Mammoth Project (10/89 - 7/95).
57 **			     InReference, Inc. (8/95 - 1/97).
58 **			     Sendmail, Inc. (1/98 - present).
59 **		The support of the my employers is gratefully acknowledged.
60 **			Few of them (Britton-Lee in particular) have had
61 **			anything to gain from my involvement in this project.
62 */
63 
64 
65 int		NextMailer;	/* "free" index into Mailer struct */
66 char		*FullName;	/* sender's full name */
67 ENVELOPE	BlankEnvelope;	/* a "blank" envelope */
68 ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
69 ADDRESS		NullAddress =	/* a null address */
70 		{ "", "", NULL, "" };
71 char		*CommandLineArgs;	/* command line args for pid file */
72 bool		Warn_Q_option = FALSE;	/* warn about Q option use */
73 char		**SaveArgv;	/* argument vector for re-execing */
74 int		MissingFds = 0;	/* bit map of fds missing on startup */
75 
76 #ifdef NGROUPS_MAX
77 GIDSET_T	InitialGidSet[NGROUPS_MAX];
78 #endif
79 
80 static void	obsolete __P((char **));
81 extern void	printmailer __P((MAILER *));
82 extern void	tTflag __P((char *));
83 
84 #if DAEMON && !SMTP
85 ERROR %%%%   Cannot have DAEMON mode without SMTP   %%%% ERROR
86 #endif /* DAEMON && !SMTP */
87 #if SMTP && !QUEUE
88 ERROR %%%%   Cannot have SMTP mode without QUEUE   %%%% ERROR
89 #endif /* DAEMON && !SMTP */
90 
91 #define MAXCONFIGLEVEL	8	/* highest config version level known */
92 
93 int
94 main(argc, argv, envp)
95 	int argc;
96 	char **argv;
97 	char **envp;
98 {
99 	register char *p;
100 	char **av;
101 	extern char Version[];
102 	char *ep, *from;
103 	STAB *st;
104 	register int i;
105 	int j;
106 	bool queuemode = FALSE;		/* process queue requests */
107 	bool safecf = TRUE;
108 	bool warn_C_flag = FALSE;
109 	char warn_f_flag = '\0';
110 	bool run_in_foreground = FALSE;	/* -bD mode */
111 	static bool reenter = FALSE;
112 	struct passwd *pw;
113 	struct hostent *hp;
114 	char *nullserver = NULL;
115 	bool forged;
116 	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
117 	static char rnamebuf[MAXNAME];	/* holds RealUserName */
118 	char *emptyenviron[1];
119 	QUEUE_CHAR *new;
120 	extern int DtableSize;
121 	extern int optind;
122 	extern int opterr;
123 	extern char *optarg;
124 	extern char **environ;
125 	extern time_t convtime __P((char *, char));
126 	extern SIGFUNC_DECL intsig __P((int));
127 	extern struct hostent *myhostname __P((char *, int));
128 	extern char *getauthinfo __P((int, bool *));
129 	extern char *getcfname __P((void));
130 	extern SIGFUNC_DECL sigusr1 __P((int));
131 	extern SIGFUNC_DECL sighup __P((int));
132 	extern void initmacros __P((ENVELOPE *));
133 	extern void init_md __P((int, char **));
134 	extern int getdtsize __P((void));
135 	extern void tTsetup __P((u_char *, int, char *));
136 	extern void setdefaults __P((ENVELOPE *));
137 	extern void initsetproctitle __P((int, char **, char **));
138 	extern void init_vendor_macros __P((ENVELOPE *));
139 	extern void load_if_names __P((void));
140 	extern void vendor_pre_defaults __P((ENVELOPE *));
141 	extern void vendor_post_defaults __P((ENVELOPE *));
142 	extern void readcf __P((char *, bool, ENVELOPE *));
143 	extern void printqueue __P((void));
144 	extern void sendtoargv __P((char **, ENVELOPE *));
145 	extern void resetlimits __P((void));
146 #ifndef HASUNSETENV
147 	extern void unsetenv __P((char *));
148 #endif
149 
150 	/*
151 	**  Check to see if we reentered.
152 	**	This would normally happen if e_putheader or e_putbody
153 	**	were NULL when invoked.
154 	*/
155 
156 	if (reenter)
157 	{
158 		syserr("main: reentered!");
159 		abort();
160 	}
161 	reenter = TRUE;
162 
163 	/* avoid null pointer dereferences */
164 	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
165 
166 	/* do machine-dependent initializations */
167 	init_md(argc, argv);
168 
169 	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
170 	DtableSize = getdtsize();
171 	if (DtableSize > 256)
172 		DtableSize = 256;
173 
174 	/*
175 	**  Be sure we have enough file descriptors.
176 	**	But also be sure that 0, 1, & 2 are open.
177 	*/
178 
179 	fill_fd(STDIN_FILENO, NULL);
180 	fill_fd(STDOUT_FILENO, NULL);
181 	fill_fd(STDERR_FILENO, NULL);
182 
183 	i = DtableSize;
184 	while (--i > 0)
185 	{
186 		if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
187 			(void) close(i);
188 	}
189 	errno = 0;
190 
191 #if LOG
192 # ifdef LOG_MAIL
193 	openlog("sendmail", LOG_PID, LOG_MAIL);
194 # else
195 	openlog("sendmail", LOG_PID);
196 # endif
197 #endif
198 
199 	if (MissingFds != 0)
200 	{
201 		char mbuf[MAXLINE];
202 
203 		mbuf[0] = '\0';
204 		if (bitset(1 << STDIN_FILENO, MissingFds))
205 			strcat(mbuf, ", stdin");
206 		if (bitset(1 << STDOUT_FILENO, MissingFds))
207 			strcat(mbuf, ", stdout");
208 		if (bitset(1 << STDERR_FILENO, MissingFds))
209 			strcat(mbuf, ", stderr");
210 		syserr("File descriptors missing on startup: %s", &mbuf[2]);
211 	}
212 
213 	/* reset status from syserr() calls for missing file descriptors */
214 	Errors = 0;
215 	ExitStat = EX_OK;
216 
217 #if XDEBUG
218 	checkfd012("after openlog");
219 #endif
220 
221 	tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
222 
223 #ifdef NGROUPS_MAX
224 	/* save initial group set for future checks */
225 	i = getgroups(NGROUPS_MAX, InitialGidSet);
226 	if (i == 0)
227 		InitialGidSet[0] = (GID_T) -1;
228 	while (i < NGROUPS_MAX)
229 		InitialGidSet[i++] = InitialGidSet[0];
230 #endif
231 
232 	/* drop group id privileges (RunAsUser not yet set) */
233 	(void) drop_privileges(FALSE);
234 
235 #ifdef SIGUSR1
236 	/* arrange to dump state on user-1 signal */
237 	setsignal(SIGUSR1, sigusr1);
238 #endif
239 
240 	/* initialize for setproctitle */
241 	initsetproctitle(argc, argv, envp);
242 
243 	/* Handle any non-getoptable constructions. */
244 	obsolete(argv);
245 
246 	/*
247 	**  Do a quick prescan of the argument list.
248 	*/
249 
250 #if defined(__osf__) || defined(_AIX3)
251 # define OPTIONS	"B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x"
252 #endif
253 #if defined(sony_news)
254 # define OPTIONS	"B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
255 #endif
256 #ifndef OPTIONS
257 # define OPTIONS	"B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:"
258 #endif
259 	opterr = 0;
260 	while ((j = getopt(argc, argv, OPTIONS)) != -1)
261 	{
262 		switch (j)
263 		{
264 		  case 'd':
265 			/* hack attack -- see if should use ANSI mode */
266 			if (strcmp(optarg, "ANSI") == 0)
267 			{
268 				TermEscape.te_rv_on = "\033[7m";
269 				TermEscape.te_rv_off = "\033[0m";
270 				break;
271 			}
272 			tTflag(optarg);
273 			setbuf(stdout, (char *) NULL);
274 			break;
275 		}
276 	}
277 	opterr = 1;
278 
279 	/* set up the blank envelope */
280 	BlankEnvelope.e_puthdr = putheader;
281 	BlankEnvelope.e_putbody = putbody;
282 	BlankEnvelope.e_xfp = NULL;
283 	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
284 	CurEnv = &BlankEnvelope;
285 	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
286 
287 	/*
288 	**  Set default values for variables.
289 	**	These cannot be in initialized data space.
290 	*/
291 
292 	setdefaults(&BlankEnvelope);
293 
294 	RealUid = getuid();
295 	RealGid = getgid();
296 
297 	pw = sm_getpwuid(RealUid);
298 	if (pw != NULL)
299 		(void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
300 	else
301 		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid);
302 	RealUserName = rnamebuf;
303 
304 	if (tTd(0, 101))
305 	{
306 		printf("Version %s\n", Version);
307 		endpwent();
308 		setuid(RealUid);
309 		exit(EX_OK);
310 	}
311 
312 	/*
313 	**  if running non-setuid binary as non-root, pretend
314 	**  we are the RunAsUid
315 	*/
316 	if (RealUid != 0 && geteuid() == RealUid)
317 	{
318 		if (tTd(47, 1))
319 			printf("Non-setuid binary: RunAsUid = RealUid = %d\n",
320 				(int)RealUid);
321 		RunAsUid = RealUid;
322 	}
323 	else if (geteuid() != 0)
324 		RunAsUid = geteuid();
325 
326 	if (RealUid != 0 && getegid() == RealGid)
327 		RunAsGid = RealGid;
328 
329 	if (tTd(47, 5))
330 	{
331 		printf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
332 			(int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
333 		printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
334 	}
335 
336 	/* save command line arguments */
337 	i = 0;
338 	for (av = argv; *av != NULL; )
339 		i += strlen(*av++) + 1;
340 	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
341 	CommandLineArgs = xalloc(i);
342 	p = CommandLineArgs;
343 	for (av = argv, i = 0; *av != NULL; )
344 	{
345 		SaveArgv[i++] = newstr(*av);
346 		if (av != argv)
347 			*p++ = ' ';
348 		strcpy(p, *av++);
349 		p += strlen(p);
350 	}
351 	SaveArgv[i] = NULL;
352 
353 	if (tTd(0, 1))
354 	{
355 		int ll;
356 		extern char *CompileOptions[];
357 
358 		printf("Version %s\n Compiled with:", Version);
359 		av = CompileOptions;
360 		ll = 7;
361 		while (*av != NULL)
362 		{
363 			if (ll + strlen(*av) > 63)
364 			{
365 				putchar('\n');
366 				ll = 0;
367 			}
368 			if (ll == 0)
369 			{
370 				putchar('\t');
371 				putchar('\t');
372 			}
373 			else
374 				putchar(' ');
375 			printf("%s", *av);
376 			ll += strlen(*av++) + 1;
377 		}
378 		putchar('\n');
379 	}
380 	if (tTd(0, 10))
381 	{
382 		int ll;
383 		extern char *OsCompileOptions[];
384 
385 		printf("    OS Defines:");
386 		av = OsCompileOptions;
387 		ll = 7;
388 		while (*av != NULL)
389 		{
390 			if (ll + strlen(*av) > 63)
391 			{
392 				putchar('\n');
393 				ll = 0;
394 			}
395 			if (ll == 0)
396 			{
397 				putchar('\t');
398 				putchar('\t');
399 			}
400 			else
401 				putchar(' ');
402 			printf("%s", *av);
403 			ll += strlen(*av++) + 1;
404 		}
405 		putchar('\n');
406 #ifdef _PATH_UNIX
407 		printf("Kernel symbols:\t%s\n", _PATH_UNIX);
408 #endif
409 		printf(" Def Conf file:\t%s\n", getcfname());
410 		printf("      Pid file:\t%s\n", PidFile);
411 	}
412 
413 	InChannel = stdin;
414 	OutChannel = stdout;
415 
416 	/* clear sendmail's environment */
417 	ExternalEnviron = environ;
418 	emptyenviron[0] = NULL;
419 	environ = emptyenviron;
420 
421 	/*
422 	** restore any original TZ setting until TimeZoneSpec has been
423 	** determined - or early log messages may get bogus time stamps
424 	*/
425 	if ((p = getextenv("TZ")) != NULL)
426 	{
427 		char *tz;
428 		int tzlen;
429 
430 		tzlen = strlen(p) + 4;
431 		tz = xalloc(tzlen);
432 		snprintf(tz, tzlen, "TZ=%s", p);
433 		putenv(tz);
434 	}
435 
436 	/* prime the child environment */
437 	setuserenv("AGENT", "sendmail");
438 
439 	if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
440 		(void) setsignal(SIGINT, intsig);
441 	(void) setsignal(SIGTERM, intsig);
442 	(void) setsignal(SIGPIPE, SIG_IGN);
443 	OldUmask = umask(022);
444 	OpMode = MD_DELIVER;
445 	FullName = getextenv("NAME");
446 
447 	/*
448 	**  Initialize name server if it is going to be used.
449 	*/
450 
451 #if NAMED_BIND
452 	if (!bitset(RES_INIT, _res.options))
453 		res_init();
454 	if (tTd(8, 8))
455 		_res.options |= RES_DEBUG;
456 	else
457 		_res.options &= ~RES_DEBUG;
458 # ifdef RES_NOALIASES
459 	_res.options |= RES_NOALIASES;
460 # endif
461 #endif
462 
463 	errno = 0;
464 	from = NULL;
465 
466 	/* initialize some macros, etc. */
467 	initmacros(CurEnv);
468 	init_vendor_macros(CurEnv);
469 
470 	/* version */
471 	define('v', Version, CurEnv);
472 
473 	/* hostname */
474 	hp = myhostname(jbuf, sizeof jbuf);
475 	if (jbuf[0] != '\0')
476 	{
477 		struct	utsname	utsname;
478 
479 		if (tTd(0, 4))
480 			printf("canonical name: %s\n", jbuf);
481 		define('w', newstr(jbuf), CurEnv);	/* must be new string */
482 		define('j', newstr(jbuf), CurEnv);
483 		setclass('w', jbuf);
484 
485 		p = strchr(jbuf, '.');
486 		if (p != NULL)
487 		{
488 			if (p[1] != '\0')
489 			{
490 				define('m', newstr(&p[1]), CurEnv);
491 			}
492 			while (p != NULL && strchr(&p[1], '.') != NULL)
493 			{
494 				*p = '\0';
495 				if (tTd(0, 4))
496 					printf("\ta.k.a.: %s\n", jbuf);
497 				setclass('w', jbuf);
498 				*p++ = '.';
499 				p = strchr(p, '.');
500 			}
501 		}
502 
503 		if (uname(&utsname) >= 0)
504 			p = utsname.nodename;
505 		else
506 		{
507 			if (tTd(0, 22))
508 				printf("uname failed (%s)\n", errstring(errno));
509 			makelower(jbuf);
510 			p = jbuf;
511 		}
512 		if (tTd(0, 4))
513 			printf(" UUCP nodename: %s\n", p);
514 		p = newstr(p);
515 		define('k', p, CurEnv);
516 		setclass('k', p);
517 		setclass('w', p);
518 	}
519 	if (hp != NULL)
520 	{
521 		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
522 		{
523 			if (tTd(0, 4))
524 				printf("\ta.k.a.: %s\n", *av);
525 			setclass('w', *av);
526 		}
527 #if NETINET
528 		if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
529 		{
530 			for (i = 0; hp->h_addr_list[i] != NULL; i++)
531 			{
532 				char ipbuf[103];
533 
534 				snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
535 					inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
536 				if (tTd(0, 4))
537 					printf("\ta.k.a.: %s\n", ipbuf);
538 				setclass('w', ipbuf);
539 			}
540 		}
541 #endif
542 	}
543 
544 	/* current time */
545 	define('b', arpadate((char *) NULL), CurEnv);
546 
547 	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
548 	QueueLimitSender = (QUEUE_CHAR *) NULL;
549 	QueueLimitId = (QUEUE_CHAR *) NULL;
550 
551 	/*
552 	** Crack argv.
553 	*/
554 
555 	av = argv;
556 	p = strrchr(*av, '/');
557 	if (p++ == NULL)
558 		p = *av;
559 	if (strcmp(p, "newaliases") == 0)
560 		OpMode = MD_INITALIAS;
561 	else if (strcmp(p, "mailq") == 0)
562 		OpMode = MD_PRINT;
563 	else if (strcmp(p, "smtpd") == 0)
564 		OpMode = MD_DAEMON;
565 	else if (strcmp(p, "hoststat") == 0)
566 		OpMode = MD_HOSTSTAT;
567 	else if (strcmp(p, "purgestat") == 0)
568 		OpMode = MD_PURGESTAT;
569 
570 	optind = 1;
571 	while ((j = getopt(argc, argv, OPTIONS)) != -1)
572 	{
573 		switch (j)
574 		{
575 		  case 'b':	/* operations mode */
576 			switch (j = *optarg)
577 			{
578 			  case MD_DAEMON:
579 			  case MD_FGDAEMON:
580 # if !DAEMON
581 				usrerr("Daemon mode not implemented");
582 				ExitStat = EX_USAGE;
583 				break;
584 # endif /* DAEMON */
585 			  case MD_SMTP:
586 # if !SMTP
587 				usrerr("I don't speak SMTP");
588 				ExitStat = EX_USAGE;
589 				break;
590 # endif /* SMTP */
591 
592 			  case MD_INITALIAS:
593 			  case MD_DELIVER:
594 			  case MD_VERIFY:
595 			  case MD_TEST:
596 			  case MD_PRINT:
597 			  case MD_HOSTSTAT:
598 			  case MD_PURGESTAT:
599 			  case MD_ARPAFTP:
600 				OpMode = j;
601 				break;
602 
603 			  case MD_FREEZE:
604 				usrerr("Frozen configurations unsupported");
605 				ExitStat = EX_USAGE;
606 				break;
607 
608 			  default:
609 				usrerr("Invalid operation mode %c", j);
610 				ExitStat = EX_USAGE;
611 				break;
612 			}
613 			break;
614 
615 		  case 'B':	/* body type */
616 			CurEnv->e_bodytype = optarg;
617 			break;
618 
619 		  case 'C':	/* select configuration file (already done) */
620 			if (RealUid != 0)
621 				warn_C_flag = TRUE;
622 			ConfFile = optarg;
623 			(void) drop_privileges(TRUE);
624 			safecf = FALSE;
625 			break;
626 
627 		  case 'd':	/* debugging -- already done */
628 			break;
629 
630 		  case 'f':	/* from address */
631 		  case 'r':	/* obsolete -f flag */
632 			if (from != NULL)
633 			{
634 				usrerr("More than one \"from\" person");
635 				ExitStat = EX_USAGE;
636 				break;
637 			}
638 			from = newstr(denlstring(optarg, TRUE, TRUE));
639 			if (strcmp(RealUserName, from) != 0)
640 				warn_f_flag = j;
641 			break;
642 
643 		  case 'F':	/* set full name */
644 			FullName = newstr(optarg);
645 			break;
646 
647 		  case 'h':	/* hop count */
648 			CurEnv->e_hopcount = strtol(optarg, &ep, 10);
649 			if (*ep)
650 			{
651 				usrerr("Bad hop count (%s)", optarg);
652 				ExitStat = EX_USAGE;
653 			}
654 			break;
655 
656 		  case 'n':	/* don't alias */
657 			NoAlias = TRUE;
658 			break;
659 
660 		  case 'N':	/* delivery status notifications */
661 			DefaultNotify |= QHASNOTIFY;
662 			if (strcasecmp(optarg, "never") == 0)
663 				break;
664 			for (p = optarg; p != NULL; optarg = p)
665 			{
666 				p = strchr(p, ',');
667 				if (p != NULL)
668 					*p++ = '\0';
669 				if (strcasecmp(optarg, "success") == 0)
670 					DefaultNotify |= QPINGONSUCCESS;
671 				else if (strcasecmp(optarg, "failure") == 0)
672 					DefaultNotify |= QPINGONFAILURE;
673 				else if (strcasecmp(optarg, "delay") == 0)
674 					DefaultNotify |= QPINGONDELAY;
675 				else
676 				{
677 					usrerr("Invalid -N argument");
678 					ExitStat = EX_USAGE;
679 				}
680 			}
681 			break;
682 
683 		  case 'o':	/* set option */
684 			setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
685 			break;
686 
687 		  case 'O':	/* set option (long form) */
688 			setoption(' ', optarg, FALSE, TRUE, CurEnv);
689 			break;
690 
691 		  case 'p':	/* set protocol */
692 			p = strchr(optarg, ':');
693 			if (p != NULL)
694 			{
695 				*p++ = '\0';
696 				if (*p != '\0')
697 				{
698 					ep = xalloc(strlen(p) + 1);
699 					cleanstrcpy(ep, p, MAXNAME);
700 					define('s', ep, CurEnv);
701 				}
702 			}
703 			if (*optarg != '\0')
704 			{
705 				ep = xalloc(strlen(optarg) + 1);
706 				cleanstrcpy(ep, optarg, MAXNAME);
707 				define('r', ep, CurEnv);
708 			}
709 			break;
710 
711 		  case 'q':	/* run queue files at intervals */
712 # if QUEUE
713 			FullName = NULL;
714 			queuemode = TRUE;
715 			switch (optarg[0])
716 			{
717 			  case 'I':
718 				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
719 					syserr("!Out of memory!!");
720 				new->queue_match = newstr(&optarg[1]);
721 				new->queue_next = QueueLimitId;
722 				QueueLimitId = new;
723 				break;
724 
725 			  case 'R':
726 				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
727 					syserr("!Out of memory!!");
728 				new->queue_match = newstr(&optarg[1]);
729 				new->queue_next = QueueLimitRecipient;
730 				QueueLimitRecipient = new;
731 				break;
732 
733 			  case 'S':
734 				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
735 					syserr("!Out of memory!!");
736 				new->queue_match = newstr(&optarg[1]);
737 				new->queue_next = QueueLimitSender;
738 				QueueLimitSender = new;
739 				break;
740 
741 			  default:
742 				QueueIntvl = convtime(optarg, 'm');
743 				break;
744 			}
745 # else /* QUEUE */
746 			usrerr("I don't know about queues");
747 			ExitStat = EX_USAGE;
748 # endif /* QUEUE */
749 			break;
750 
751 		  case 'R':	/* DSN RET: what to return */
752 			if (bitset(EF_RET_PARAM, CurEnv->e_flags))
753 			{
754 				usrerr("Duplicate -R flag");
755 				ExitStat = EX_USAGE;
756 				break;
757 			}
758 			CurEnv->e_flags |= EF_RET_PARAM;
759 			if (strcasecmp(optarg, "hdrs") == 0)
760 				CurEnv->e_flags |= EF_NO_BODY_RETN;
761 			else if (strcasecmp(optarg, "full") != 0)
762 			{
763 				usrerr("Invalid -R value");
764 				ExitStat = EX_USAGE;
765 			}
766 			break;
767 
768 		  case 't':	/* read recipients from message */
769 			GrabTo = TRUE;
770 			break;
771 
772 		  case 'U':	/* initial (user) submission */
773 			UserSubmission = TRUE;
774 			break;
775 
776 		  case 'V':	/* DSN ENVID: set "original" envelope id */
777 			if (!xtextok(optarg))
778 			{
779 				usrerr("Invalid syntax in -V flag");
780 				ExitStat = EX_USAGE;
781 			}
782 			else
783 				CurEnv->e_envid = newstr(optarg);
784 			break;
785 
786 		  case 'X':	/* traffic log file */
787 			(void) drop_privileges(TRUE);
788 			TrafficLogFile = fopen(optarg, "a");
789 			if (TrafficLogFile == NULL)
790 			{
791 				syserr("cannot open %s", optarg);
792 				ExitStat = EX_CANTCREAT;
793 				break;
794 			}
795 #ifdef HASSETVBUF
796 			setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
797 #else
798 			setlinebuf(TrafficLogFile);
799 #endif
800 			break;
801 
802 			/* compatibility flags */
803 		  case 'c':	/* connect to non-local mailers */
804 		  case 'i':	/* don't let dot stop me */
805 		  case 'm':	/* send to me too */
806 		  case 'T':	/* set timeout interval */
807 		  case 'v':	/* give blow-by-blow description */
808 			setoption(j, "T", FALSE, TRUE, CurEnv);
809 			break;
810 
811 		  case 'e':	/* error message disposition */
812 		  case 'M':	/* define macro */
813 			setoption(j, optarg, FALSE, TRUE, CurEnv);
814 			break;
815 
816 		  case 's':	/* save From lines in headers */
817 			setoption('f', "T", FALSE, TRUE, CurEnv);
818 			break;
819 
820 # ifdef DBM
821 		  case 'I':	/* initialize alias DBM file */
822 			OpMode = MD_INITALIAS;
823 			break;
824 # endif /* DBM */
825 
826 # if defined(__osf__) || defined(_AIX3)
827 		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
828 			break;
829 # endif
830 # if defined(sony_news)
831 		  case 'E':
832 		  case 'J':	/* ignore flags for Japanese code conversion
833 				   impremented on Sony NEWS */
834 			break;
835 # endif
836 
837 		  default:
838 			ExitStat = EX_USAGE;
839 			finis();
840 			break;
841 		}
842 	}
843 	av += optind;
844 
845 	/*
846 	**  Do basic initialization.
847 	**	Read system control file.
848 	**	Extract special fields for local use.
849 	*/
850 
851 	/* set up ${opMode} for use in config file */
852 	{
853 		char mbuf[2];
854 
855 		mbuf[0] = OpMode;
856 		mbuf[1] = '\0';
857 		define(MID_OPMODE, newstr(mbuf), CurEnv);
858 	}
859 
860 #if XDEBUG
861 	checkfd012("before readcf");
862 #endif
863 	vendor_pre_defaults(CurEnv);
864 	readcf(getcfname(), safecf, CurEnv);
865 	ConfigFileRead = TRUE;
866 	vendor_post_defaults(CurEnv);
867 
868 	/* Enforce use of local time (null string overrides this) */
869 	if (TimeZoneSpec == NULL)
870 		unsetenv("TZ");
871 	else if (TimeZoneSpec[0] != '\0')
872 		setuserenv("TZ", TimeZoneSpec);
873 	else
874 		setuserenv("TZ", NULL);
875 	tzset();
876 
877 	/* avoid denial-of-service attacks */
878 	resetlimits();
879 
880 	if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
881 	{
882 		/* drop privileges -- daemon mode done after socket/bind */
883 		(void) drop_privileges(FALSE);
884 	}
885 
886 	/*
887 	**  Find our real host name for future logging.
888 	*/
889 
890 	p = getauthinfo(STDIN_FILENO, &forged);
891 	define('_', p, CurEnv);
892 
893 	/* suppress error printing if errors mailed back or whatever */
894 	if (CurEnv->e_errormode != EM_PRINT)
895 		HoldErrs = TRUE;
896 
897 	/* set up the $=m class now, after .cf has a chance to redefine $m */
898 	expand("\201m", jbuf, sizeof jbuf, CurEnv);
899 	setclass('m', jbuf);
900 
901 	/* probe interfaces and locate any additional names */
902 	if (!DontProbeInterfaces)
903 		load_if_names();
904 
905 	if (tTd(0, 1))
906 	{
907 		printf("\n============ SYSTEM IDENTITY (after readcf) ============");
908 		printf("\n      (short domain name) $w = ");
909 		xputs(macvalue('w', CurEnv));
910 		printf("\n  (canonical domain name) $j = ");
911 		xputs(macvalue('j', CurEnv));
912 		printf("\n         (subdomain name) $m = ");
913 		xputs(macvalue('m', CurEnv));
914 		printf("\n              (node name) $k = ");
915 		xputs(macvalue('k', CurEnv));
916 		printf("\n========================================================\n\n");
917 	}
918 
919 	/*
920 	**  Do more command line checking -- these are things that
921 	**  have to modify the results of reading the config file.
922 	*/
923 
924 	/* process authorization warnings from command line */
925 	if (warn_C_flag)
926 		auth_warning(CurEnv, "Processed by %s with -C %s",
927 			RealUserName, ConfFile);
928 	if (Warn_Q_option)
929 		auth_warning(CurEnv, "Processed from queue %s", QueueDir);
930 
931 	/* check body type for legality */
932 	if (CurEnv->e_bodytype == NULL)
933 		/* nothing */ ;
934 	else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
935 		SevenBitInput = TRUE;
936 	else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
937 		SevenBitInput = FALSE;
938 	else
939 	{
940 		usrerr("Illegal body type %s", CurEnv->e_bodytype);
941 		CurEnv->e_bodytype = NULL;
942 	}
943 
944 	/* tweak default DSN notifications */
945 	if (DefaultNotify == 0)
946 		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
947 
948 	/* be sure we don't pick up bogus HOSTALIASES environment variable */
949 	if (queuemode && RealUid != 0)
950 		(void) unsetenv("HOSTALIASES");
951 
952 	/* check for sane configuration level */
953 	if (ConfigLevel > MAXCONFIGLEVEL)
954 	{
955 		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
956 			ConfigLevel, Version, MAXCONFIGLEVEL);
957 	}
958 
959 	/* need MCI cache to have persistence */
960 	if (HostStatDir != NULL && MaxMciCache == 0)
961 	{
962 		HostStatDir = NULL;
963 		printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
964 	}
965 
966 	/* need HostStatusDir in order to have SingleThreadDelivery */
967 	if (SingleThreadDelivery && HostStatDir == NULL)
968 	{
969 		SingleThreadDelivery = FALSE;
970 		printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
971 	}
972 
973 	/* check for permissions */
974 	if ((OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
975 	     OpMode == MD_PURGESTAT) && RealUid != 0)
976 	{
977 		if (LogLevel > 1)
978 			sm_syslog(LOG_ALERT, NOQID,
979 				"user %d attempted to %s",
980 				RealUid,
981 				OpMode != MD_PURGESTAT ? "run daemon"
982 						       : "purge host status");
983 		usrerr("Permission denied");
984 		exit(EX_USAGE);
985 	}
986 
987 	if (MeToo)
988 		BlankEnvelope.e_flags |= EF_METOO;
989 
990 	switch (OpMode)
991 	{
992 	  case MD_TEST:
993 		/* don't have persistent host status in test mode */
994 		HostStatDir = NULL;
995 		if (Verbose == 0)
996 			Verbose = 2;
997 		CurEnv->e_errormode = EM_PRINT;
998 		HoldErrs = FALSE;
999 		break;
1000 
1001 	  case MD_VERIFY:
1002 		CurEnv->e_errormode = EM_PRINT;
1003 		HoldErrs = FALSE;
1004 		/* arrange to exit cleanly on hangup signal */
1005 		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1006 			setsignal(SIGHUP, intsig);
1007 		break;
1008 
1009 	  case MD_FGDAEMON:
1010 		run_in_foreground = TRUE;
1011 		OpMode = MD_DAEMON;
1012 		/* fall through ... */
1013 
1014 	  case MD_DAEMON:
1015 		vendor_daemon_setup(CurEnv);
1016 
1017 		/* remove things that don't make sense in daemon mode */
1018 		FullName = NULL;
1019 		GrabTo = FALSE;
1020 
1021 		/* arrange to restart on hangup signal */
1022 		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1023 			sm_syslog(LOG_WARNING, NOQID,
1024 				"daemon invoked without full pathname; kill -1 won't work");
1025 		setsignal(SIGHUP, sighup);
1026 
1027 		/* workaround: can't seem to release the signal in the parent */
1028 		releasesignal(SIGHUP);
1029 		break;
1030 
1031 	  case MD_INITALIAS:
1032 		Verbose = 2;
1033 		CurEnv->e_errormode = EM_PRINT;
1034 		HoldErrs = FALSE;
1035 		/* fall through... */
1036 
1037 	  case MD_PRINT:
1038 		/* to handle sendmail -bp -qSfoobar properly */
1039 		queuemode = FALSE;
1040 		/* fall through... */
1041 
1042 	  default:
1043 		/* arrange to exit cleanly on hangup signal */
1044 		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1045 			setsignal(SIGHUP, intsig);
1046 		break;
1047 	}
1048 
1049 	/* special considerations for FullName */
1050 	if (FullName != NULL)
1051 	{
1052 		char *full = NULL;
1053 		extern bool rfc822_string __P((char *));
1054 
1055 		/* full names can't have newlines */
1056 		if (strchr(FullName, '\n') != NULL)
1057 		{
1058 			FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
1059 		}
1060 		/* check for characters that may have to be quoted */
1061 		if (!rfc822_string(FullName))
1062 		{
1063 			extern char *addquotes __P((char *));
1064 
1065 			/*
1066 			**  Quote a full name with special characters
1067 			**  as a comment so crackaddr() doesn't destroy
1068 			**  the name portion of the address.
1069 			*/
1070 			FullName = addquotes(FullName);
1071 			if (full != NULL)
1072 				free(full);
1073 		}
1074 	}
1075 
1076 	/* do heuristic mode adjustment */
1077 	if (Verbose)
1078 	{
1079 		/* turn off noconnect option */
1080 		setoption('c', "F", TRUE, FALSE, CurEnv);
1081 
1082 		/* turn on interactive delivery */
1083 		setoption('d', "", TRUE, FALSE, CurEnv);
1084 	}
1085 
1086 	/* check for out of date configuration level */
1087 	if (ConfigLevel < MAXCONFIGLEVEL)
1088 	{
1089 		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1090 			Version, MAXCONFIGLEVEL, ConfigLevel);
1091 	}
1092 
1093 	if (ConfigLevel < 3)
1094 	{
1095 		UseErrorsTo = TRUE;
1096 	}
1097 
1098 	/* set options that were previous macros */
1099 	if (SmtpGreeting == NULL)
1100 	{
1101 		if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
1102 			SmtpGreeting = newstr(p);
1103 		else
1104 			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1105 	}
1106 	if (UnixFromLine == NULL)
1107 	{
1108 		if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
1109 			UnixFromLine = newstr(p);
1110 		else
1111 			UnixFromLine = "From \201g  \201d";
1112 	}
1113 
1114 	/* our name for SMTP codes */
1115 	expand("\201j", jbuf, sizeof jbuf, CurEnv);
1116 	MyHostName = jbuf;
1117 	if (strchr(jbuf, '.') == NULL)
1118 		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1119 			jbuf);
1120 
1121 	/* make certain that this name is part of the $=w class */
1122 	setclass('w', MyHostName);
1123 
1124 	/* the indices of built-in mailers */
1125 	st = stab("local", ST_MAILER, ST_FIND);
1126 	if (st != NULL)
1127 		LocalMailer = st->s_mailer;
1128 	else if (OpMode != MD_TEST || !warn_C_flag)
1129 		syserr("No local mailer defined");
1130 
1131 	st = stab("prog", ST_MAILER, ST_FIND);
1132 	if (st == NULL)
1133 		syserr("No prog mailer defined");
1134 	else
1135 	{
1136 		ProgMailer = st->s_mailer;
1137 		clrbitn(M_MUSER, ProgMailer->m_flags);
1138 	}
1139 
1140 	st = stab("*file*", ST_MAILER, ST_FIND);
1141 	if (st == NULL)
1142 		syserr("No *file* mailer defined");
1143 	else
1144 	{
1145 		FileMailer = st->s_mailer;
1146 		clrbitn(M_MUSER, FileMailer->m_flags);
1147 	}
1148 
1149 	st = stab("*include*", ST_MAILER, ST_FIND);
1150 	if (st == NULL)
1151 		syserr("No *include* mailer defined");
1152 	else
1153 		InclMailer = st->s_mailer;
1154 
1155 	if (ConfigLevel < 6)
1156 	{
1157 		/* heuristic tweaking of local mailer for back compat */
1158 		if (LocalMailer != NULL)
1159 		{
1160 			setbitn(M_ALIASABLE, LocalMailer->m_flags);
1161 			setbitn(M_HASPWENT, LocalMailer->m_flags);
1162 			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1163 			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1164 			setbitn(M_CHECKPROG, LocalMailer->m_flags);
1165 			setbitn(M_CHECKFILE, LocalMailer->m_flags);
1166 			setbitn(M_CHECKUDB, LocalMailer->m_flags);
1167 		}
1168 		if (ProgMailer != NULL)
1169 			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1170 		if (FileMailer != NULL)
1171 			setbitn(M_RUNASRCPT, FileMailer->m_flags);
1172 	}
1173 	if (ConfigLevel < 7)
1174 	{
1175 		if (LocalMailer != NULL)
1176 			setbitn(M_VRFY250, LocalMailer->m_flags);
1177 		if (ProgMailer != NULL)
1178 			setbitn(M_VRFY250, ProgMailer->m_flags);
1179 		if (FileMailer != NULL)
1180 			setbitn(M_VRFY250, FileMailer->m_flags);
1181 	}
1182 
1183 	/* MIME Content-Types that cannot be transfer encoded */
1184 	setclass('n', "multipart/signed");
1185 
1186 	/* MIME message/xxx subtypes that can be treated as messages */
1187 	setclass('s', "rfc822");
1188 
1189 	/* MIME Content-Transfer-Encodings that can be encoded */
1190 	setclass('e', "7bit");
1191 	setclass('e', "8bit");
1192 	setclass('e', "binary");
1193 
1194 #ifdef USE_B_CLASS
1195 	/* MIME Content-Types that should be treated as binary */
1196 	setclass('b', "image");
1197 	setclass('b', "audio");
1198 	setclass('b', "video");
1199 	setclass('b', "application/octet-stream");
1200 #endif
1201 
1202 	/* operate in queue directory */
1203 	if (QueueDir == NULL)
1204 	{
1205 		if (OpMode != MD_TEST)
1206 		{
1207 			syserr("QueueDirectory (Q) option must be set");
1208 			ExitStat = EX_CONFIG;
1209 		}
1210 	}
1211 	else
1212 	{
1213 		/* test path to get warning messages */
1214 		(void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
1215 		if (OpMode != MD_TEST && chdir(QueueDir) < 0)
1216 		{
1217 			syserr("cannot chdir(%s)", QueueDir);
1218 			ExitStat = EX_CONFIG;
1219 		}
1220 	}
1221 
1222 	/* check host status directory for validity */
1223 	if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
1224 	{
1225 		/* cannot use this value */
1226 		if (tTd(0, 2))
1227 			printf("Cannot use HostStatusDirectory = %s: %s\n",
1228 				HostStatDir, errstring(errno));
1229 		HostStatDir = NULL;
1230 	}
1231 
1232 # if QUEUE
1233 	if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1234 	{
1235 		struct stat stbuf;
1236 
1237 		/* check to see if we own the queue directory */
1238 		if (stat(".", &stbuf) < 0)
1239 			syserr("main: cannot stat %s", QueueDir);
1240 		if (stbuf.st_uid != RealUid)
1241 		{
1242 			/* nope, really a botch */
1243 			usrerr("You do not have permission to process the queue");
1244 			exit (EX_NOPERM);
1245 		}
1246 	}
1247 # endif /* QUEUE */
1248 
1249 	/* if we've had errors so far, exit now */
1250 	if (ExitStat != EX_OK && OpMode != MD_TEST)
1251 	{
1252 		endpwent();
1253 		setuid(RealUid);
1254 		exit(ExitStat);
1255 	}
1256 
1257 #if XDEBUG
1258 	checkfd012("before main() initmaps");
1259 #endif
1260 
1261 	/*
1262 	**  Do operation-mode-dependent initialization.
1263 	*/
1264 
1265 	switch (OpMode)
1266 	{
1267 	  case MD_PRINT:
1268 		/* print the queue */
1269 #if QUEUE
1270 		dropenvelope(CurEnv, TRUE);
1271 		printqueue();
1272 		endpwent();
1273 		setuid(RealUid);
1274 		exit(EX_OK);
1275 #else /* QUEUE */
1276 		usrerr("No queue to print");
1277 		finis();
1278 #endif /* QUEUE */
1279 
1280 	  case MD_HOSTSTAT:
1281 		mci_traverse_persistent(mci_print_persistent, NULL);
1282 		exit(EX_OK);
1283 	    	break;
1284 
1285 	  case MD_PURGESTAT:
1286 		mci_traverse_persistent(mci_purge_persistent, NULL);
1287 		exit(EX_OK);
1288 	    	break;
1289 
1290 	  case MD_INITALIAS:
1291 		/* initialize alias database */
1292 		initmaps(TRUE, CurEnv);
1293 		endpwent();
1294 		setuid(RealUid);
1295 		exit(ExitStat);
1296 
1297 	  case MD_SMTP:
1298 	  case MD_DAEMON:
1299 		/* reset DSN parameters */
1300 		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1301 		CurEnv->e_envid = NULL;
1302 		CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
1303 
1304 		/* don't open alias database -- done in srvrsmtp */
1305 		break;
1306 
1307 	  default:
1308 		/* open the alias database */
1309 		initmaps(FALSE, CurEnv);
1310 		break;
1311 	}
1312 
1313 	if (tTd(0, 15))
1314 	{
1315 		extern void printrules __P((void));
1316 
1317 		/* print configuration table (or at least part of it) */
1318 		if (tTd(0, 90))
1319 			printrules();
1320 		for (i = 0; i < MAXMAILERS; i++)
1321 		{
1322 			if (Mailer[i] != NULL)
1323 				printmailer(Mailer[i]);
1324 		}
1325 	}
1326 
1327 	/*
1328 	**  Switch to the main envelope.
1329 	*/
1330 
1331 	CurEnv = newenvelope(&MainEnvelope, CurEnv);
1332 	MainEnvelope.e_flags = BlankEnvelope.e_flags;
1333 
1334 	/*
1335 	**  If test mode, read addresses from stdin and process.
1336 	*/
1337 
1338 	if (OpMode == MD_TEST)
1339 	{
1340 		char buf[MAXLINE];
1341 		SIGFUNC_DECL intindebug __P((int));
1342 
1343 		if (isatty(fileno(stdin)))
1344 			Verbose = 2;
1345 
1346 		if (Verbose)
1347 		{
1348 			printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
1349 			printf("Enter <ruleset> <address>\n");
1350 		}
1351 		if (setjmp(TopFrame) > 0)
1352 			printf("\n");
1353 		(void) setsignal(SIGINT, intindebug);
1354 		for (;;)
1355 		{
1356 			extern void testmodeline __P((char *, ENVELOPE *));
1357 
1358 			if (Verbose == 2)
1359 				printf("> ");
1360 			(void) fflush(stdout);
1361 			if (fgets(buf, sizeof buf, stdin) == NULL)
1362 				finis();
1363 			p = strchr(buf, '\n');
1364 			if (p != NULL)
1365 				*p = '\0';
1366 			if (Verbose < 2)
1367 				printf("> %s\n", buf);
1368 			testmodeline(buf, CurEnv);
1369 		}
1370 	}
1371 
1372 # if QUEUE
1373 	/*
1374 	**  If collecting stuff from the queue, go start doing that.
1375 	*/
1376 
1377 	if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
1378 	{
1379 		(void) runqueue(FALSE, Verbose);
1380 		finis();
1381 	}
1382 # endif /* QUEUE */
1383 
1384 	/*
1385 	**  If a daemon, wait for a request.
1386 	**	getrequests will always return in a child.
1387 	**	If we should also be processing the queue, start
1388 	**		doing it in background.
1389 	**	We check for any errors that might have happened
1390 	**		during startup.
1391 	*/
1392 
1393 	if (OpMode == MD_DAEMON || QueueIntvl != 0)
1394 	{
1395 		char dtype[200];
1396 		extern void getrequests __P((ENVELOPE *));
1397 
1398 		if (!run_in_foreground && !tTd(99, 100))
1399 		{
1400 			/* put us in background */
1401 			i = fork();
1402 			if (i < 0)
1403 				syserr("daemon: cannot fork");
1404 			if (i != 0)
1405 				exit(EX_OK);
1406 
1407 			/* disconnect from our controlling tty */
1408 			disconnect(2, CurEnv);
1409 		}
1410 
1411 		dtype[0] = '\0';
1412 		if (OpMode == MD_DAEMON)
1413 			strcat(dtype, "+SMTP");
1414 		if (QueueIntvl != 0)
1415 		{
1416 			strcat(dtype, "+queueing@");
1417 			strcat(dtype, pintvl(QueueIntvl, TRUE));
1418 		}
1419 		if (tTd(0, 1))
1420 			strcat(dtype, "+debugging");
1421 
1422 		sm_syslog(LOG_INFO, NOQID,
1423 			"starting daemon (%s): %s", Version, dtype + 1);
1424 #ifdef XLA
1425 		xla_create_file();
1426 #endif
1427 
1428 # if QUEUE
1429 		if (queuemode)
1430 		{
1431 			(void) runqueue(TRUE, FALSE);
1432 			if (OpMode != MD_DAEMON)
1433 			{
1434 				for (;;)
1435 				{
1436 					pause();
1437 					if (DoQueueRun)
1438 						(void) runqueue(TRUE, FALSE);
1439 				}
1440 			}
1441 		}
1442 # endif /* QUEUE */
1443 		dropenvelope(CurEnv, TRUE);
1444 
1445 #if DAEMON
1446 		getrequests(CurEnv);
1447 
1448 		/* drop privileges */
1449 		(void) drop_privileges(FALSE);
1450 
1451 		/* at this point we are in a child: reset state */
1452 		(void) newenvelope(CurEnv, CurEnv);
1453 
1454 		/*
1455 		**  Get authentication data
1456 		*/
1457 
1458 		p = getauthinfo(fileno(InChannel), &forged);
1459 		define('_', p, &BlankEnvelope);
1460 #endif /* DAEMON */
1461 	}
1462 
1463 # if SMTP
1464 	/*
1465 	**  If running SMTP protocol, start collecting and executing
1466 	**  commands.  This will never return.
1467 	*/
1468 
1469 	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1470 	{
1471 		char pbuf[20];
1472 		extern void smtp __P((char *, ENVELOPE *));
1473 
1474 		/*
1475 		**  Save some macros for check_* rulesets.
1476 		*/
1477 
1478 		if (forged)
1479 		{
1480 			char ipbuf[103];
1481 
1482 			snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
1483 				 inet_ntoa(RealHostAddr.sin.sin_addr));
1484 
1485 			define(macid("{client_name}", NULL),
1486 			       newstr(ipbuf), &BlankEnvelope);
1487 		}
1488 		else
1489 			define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
1490 		define(macid("{client_addr}", NULL),
1491 		       newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
1492 		if (RealHostAddr.sa.sa_family == AF_INET)
1493 			snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
1494 		else
1495 			snprintf(pbuf, sizeof pbuf, "0");
1496 		define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
1497 
1498 		if (OpMode == MD_DAEMON)
1499 		{
1500 			/* validate the connection */
1501 			HoldErrs = TRUE;
1502 			nullserver = validate_connection(&RealHostAddr,
1503 							 RealHostName, CurEnv);
1504 			HoldErrs = FALSE;
1505 		}
1506 		smtp(nullserver, CurEnv);
1507 	}
1508 # endif /* SMTP */
1509 
1510 	clearenvelope(CurEnv, FALSE);
1511 	if (OpMode == MD_VERIFY)
1512 	{
1513 		CurEnv->e_sendmode = SM_VERIFY;
1514 		PostMasterCopy = NULL;
1515 	}
1516 	else
1517 	{
1518 		/* interactive -- all errors are global */
1519 		CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
1520 	}
1521 
1522 	/*
1523 	**  Do basic system initialization and set the sender
1524 	*/
1525 
1526 	initsys(CurEnv);
1527 	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
1528 		auth_warning(CurEnv, "%s set sender to %s using -%c",
1529 			RealUserName, from, warn_f_flag);
1530 	setsender(from, CurEnv, NULL, '\0', FALSE);
1531 	if (macvalue('s', CurEnv) == NULL)
1532 		define('s', RealHostName, CurEnv);
1533 
1534 	if (*av == NULL && !GrabTo)
1535 	{
1536 		CurEnv->e_flags |= EF_GLOBALERRS;
1537 		usrerr("Recipient names must be specified");
1538 
1539 		/* collect body for UUCP return */
1540 		if (OpMode != MD_VERIFY)
1541 			collect(InChannel, FALSE, NULL, CurEnv);
1542 		finis();
1543 	}
1544 
1545 	/*
1546 	**  Scan argv and deliver the message to everyone.
1547 	*/
1548 
1549 	sendtoargv(av, CurEnv);
1550 
1551 	/* if we have had errors sofar, arrange a meaningful exit stat */
1552 	if (Errors > 0 && ExitStat == EX_OK)
1553 		ExitStat = EX_USAGE;
1554 
1555 #if _FFR_FIX_DASHT
1556 	/*
1557 	**  If using -t, force not sending to argv recipients, even
1558 	**  if they are mentioned in the headers.
1559 	*/
1560 
1561 	if (GrabTo)
1562 	{
1563 		ADDRESS *q;
1564 
1565 		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
1566 			q->q_flags |= QDONTSEND;
1567 	}
1568 #endif
1569 
1570 	/*
1571 	**  Read the input mail.
1572 	*/
1573 
1574 	CurEnv->e_to = NULL;
1575 	if (OpMode != MD_VERIFY || GrabTo)
1576 	{
1577 		long savedflags = CurEnv->e_flags & EF_FATALERRS;
1578 
1579 		CurEnv->e_flags |= EF_GLOBALERRS;
1580 		CurEnv->e_flags &= ~EF_FATALERRS;
1581 		collect(InChannel, FALSE, NULL, CurEnv);
1582 
1583 		/* bail out if message too large */
1584 		if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
1585 		{
1586 			finis();
1587 			/*NOTREACHED*/
1588 			return -1;
1589 		}
1590 		CurEnv->e_flags |= savedflags;
1591 	}
1592 	errno = 0;
1593 
1594 	if (tTd(1, 1))
1595 		printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
1596 
1597 	/*
1598 	**  Actually send everything.
1599 	**	If verifying, just ack.
1600 	*/
1601 
1602 	CurEnv->e_from.q_flags |= QDONTSEND;
1603 	if (tTd(1, 5))
1604 	{
1605 		printf("main: QDONTSEND ");
1606 		printaddr(&CurEnv->e_from, FALSE);
1607 	}
1608 	CurEnv->e_to = NULL;
1609 	CurrentLA = getla();
1610 	GrabTo = FALSE;
1611 	sendall(CurEnv, SM_DEFAULT);
1612 
1613 	/*
1614 	**  All done.
1615 	**	Don't send return error message if in VERIFY mode.
1616 	*/
1617 
1618 	finis();
1619 	/*NOTREACHED*/
1620 	return -1;
1621 }
1622 
1623 
1624 /* ARGSUSED */
1625 SIGFUNC_DECL
1626 intindebug(sig)
1627 	int sig;
1628 {
1629 	longjmp(TopFrame, 1);
1630 	return SIGFUNC_RETURN;
1631 }
1632 
1633 
1634 /*
1635 **  FINIS -- Clean up and exit.
1636 **
1637 **	Parameters:
1638 **		none
1639 **
1640 **	Returns:
1641 **		never
1642 **
1643 **	Side Effects:
1644 **		exits sendmail
1645 */
1646 
1647 void
1648 finis()
1649 {
1650 	if (tTd(2, 1))
1651 	{
1652 		extern void printenvflags __P((ENVELOPE *));
1653 
1654 		printf("\n====finis: stat %d e_id=%s e_flags=",
1655 			ExitStat,
1656 			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
1657 		printenvflags(CurEnv);
1658 	}
1659 	if (tTd(2, 9))
1660 		printopenfds(FALSE);
1661 
1662 	/* if we fail in finis(), just exit */
1663 	if (setjmp(TopFrame) != 0)
1664 	{
1665 		/* failed -- just give it up */
1666 		goto forceexit;
1667 	}
1668 
1669 	/* clean up temp files */
1670 	CurEnv->e_to = NULL;
1671 	if (CurEnv->e_id != NULL)
1672 		dropenvelope(CurEnv, TRUE);
1673 
1674 	/* flush any cached connections */
1675 	mci_flush(TRUE, NULL);
1676 
1677 # ifdef XLA
1678 	/* clean up extended load average stuff */
1679 	xla_all_end();
1680 # endif
1681 
1682 	/* and exit */
1683   forceexit:
1684 	if (LogLevel > 78)
1685 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
1686 			"finis, pid=%d",
1687 			getpid());
1688 	if (ExitStat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
1689 		ExitStat = EX_OK;
1690 
1691 	/* reset uid for process accounting */
1692 	endpwent();
1693 	setuid(RealUid);
1694 
1695 	exit(ExitStat);
1696 }
1697 /*
1698 **  INTSIG -- clean up on interrupt
1699 **
1700 **	This just arranges to exit.  It pessimises in that it
1701 **	may resend a message.
1702 **
1703 **	Parameters:
1704 **		none.
1705 **
1706 **	Returns:
1707 **		none.
1708 **
1709 **	Side Effects:
1710 **		Unlocks the current job.
1711 */
1712 
1713 /* ARGSUSED */
1714 SIGFUNC_DECL
1715 intsig(sig)
1716 	int sig;
1717 {
1718 	if (LogLevel > 79)
1719 		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
1720 	FileName = NULL;
1721 	unlockqueue(CurEnv);
1722 #ifdef XLA
1723 	xla_all_end();
1724 #endif
1725 
1726 	/* reset uid for process accounting */
1727 	endpwent();
1728 	setuid(RealUid);
1729 
1730 	exit(EX_OK);
1731 }
1732 /*
1733 **  INITMACROS -- initialize the macro system
1734 **
1735 **	This just involves defining some macros that are actually
1736 **	used internally as metasymbols to be themselves.
1737 **
1738 **	Parameters:
1739 **		none.
1740 **
1741 **	Returns:
1742 **		none.
1743 **
1744 **	Side Effects:
1745 **		initializes several macros to be themselves.
1746 */
1747 
1748 struct metamac	MetaMacros[] =
1749 {
1750 	/* LHS pattern matching characters */
1751 	{ '*', MATCHZANY },	{ '+', MATCHANY },	{ '-', MATCHONE },
1752 	{ '=', MATCHCLASS },	{ '~', MATCHNCLASS },
1753 
1754 	/* these are RHS metasymbols */
1755 	{ '#', CANONNET },	{ '@', CANONHOST },	{ ':', CANONUSER },
1756 	{ '>', CALLSUBR },
1757 
1758 	/* the conditional operations */
1759 	{ '?', CONDIF },	{ '|', CONDELSE },	{ '.', CONDFI },
1760 
1761 	/* the hostname lookup characters */
1762 	{ '[', HOSTBEGIN },	{ ']', HOSTEND },
1763 	{ '(', LOOKUPBEGIN },	{ ')', LOOKUPEND },
1764 
1765 	/* miscellaneous control characters */
1766 	{ '&', MACRODEXPAND },
1767 
1768 	{ '\0' }
1769 };
1770 
1771 #define MACBINDING(name, mid) \
1772 		stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
1773 		MacroName[mid] = name;
1774 
1775 void
1776 initmacros(e)
1777 	register ENVELOPE *e;
1778 {
1779 	register struct metamac *m;
1780 	register int c;
1781 	char buf[5];
1782 	extern char *MacroName[256];
1783 
1784 	for (m = MetaMacros; m->metaname != '\0'; m++)
1785 	{
1786 		buf[0] = m->metaval;
1787 		buf[1] = '\0';
1788 		define(m->metaname, newstr(buf), e);
1789 	}
1790 	buf[0] = MATCHREPL;
1791 	buf[2] = '\0';
1792 	for (c = '0'; c <= '9'; c++)
1793 	{
1794 		buf[1] = c;
1795 		define(c, newstr(buf), e);
1796 	}
1797 
1798 	/* set defaults for some macros sendmail will use later */
1799 	define('n', "MAILER-DAEMON", e);
1800 
1801 	/* set up external names for some internal macros */
1802 	MACBINDING("opMode", MID_OPMODE);
1803 	/*XXX should probably add equivalents for all short macros here XXX*/
1804 }
1805 /*
1806 **  DISCONNECT -- remove our connection with any foreground process
1807 **
1808 **	Parameters:
1809 **		droplev -- how "deeply" we should drop the line.
1810 **			0 -- ignore signals, mail back errors, make sure
1811 **			     output goes to stdout.
1812 **			1 -- also, make stdout go to transcript.
1813 **			2 -- also, disconnect from controlling terminal
1814 **			     (only for daemon mode).
1815 **		e -- the current envelope.
1816 **
1817 **	Returns:
1818 **		none
1819 **
1820 **	Side Effects:
1821 **		Trys to insure that we are immune to vagaries of
1822 **		the controlling tty.
1823 */
1824 
1825 void
1826 disconnect(droplev, e)
1827 	int droplev;
1828 	register ENVELOPE *e;
1829 {
1830 	int fd;
1831 
1832 	if (tTd(52, 1))
1833 		printf("disconnect: In %d Out %d, e=%lx\n",
1834 			fileno(InChannel), fileno(OutChannel), (u_long) e);
1835 	if (tTd(52, 100))
1836 	{
1837 		printf("don't\n");
1838 		return;
1839 	}
1840 	if (LogLevel > 93)
1841 		sm_syslog(LOG_DEBUG, e->e_id,
1842 			"disconnect level %d",
1843 			droplev);
1844 
1845 	/* be sure we don't get nasty signals */
1846 	(void) setsignal(SIGINT, SIG_IGN);
1847 	(void) setsignal(SIGQUIT, SIG_IGN);
1848 
1849 	/* we can't communicate with our caller, so.... */
1850 	HoldErrs = TRUE;
1851 	CurEnv->e_errormode = EM_MAIL;
1852 	Verbose = 0;
1853 	DisConnected = TRUE;
1854 
1855 	/* all input from /dev/null */
1856 	if (InChannel != stdin)
1857 	{
1858 		(void) fclose(InChannel);
1859 		InChannel = stdin;
1860 	}
1861 	if (freopen("/dev/null", "r", stdin) == NULL)
1862 		sm_syslog(LOG_ERR, e->e_id,
1863 			  "disconnect: freopen(\"/dev/null\") failed: %s",
1864 			  errstring(errno));
1865 
1866 	/* output to the transcript */
1867 	if (OutChannel != stdout)
1868 	{
1869 		(void) fclose(OutChannel);
1870 		OutChannel = stdout;
1871 	}
1872 	if (droplev > 0)
1873 	{
1874 		if (e->e_xfp == NULL)
1875 		{
1876 			fd = open("/dev/null", O_WRONLY, 0666);
1877 			if (fd == -1)
1878 				sm_syslog(LOG_ERR, e->e_id,
1879 					  "disconnect: open(\"/dev/null\") failed: %s",
1880 					  errstring(errno));
1881 		}
1882 		else
1883 		{
1884 			fd = fileno(e->e_xfp);
1885 			if (fd == -1)
1886 				sm_syslog(LOG_ERR, e->e_id,
1887 					  "disconnect: fileno(e->e_xfp) failed: %s",
1888 					  errstring(errno));
1889 		}
1890 		(void) fflush(stdout);
1891 		dup2(fd, STDOUT_FILENO);
1892 		dup2(fd, STDERR_FILENO);
1893 		if (e->e_xfp == NULL)
1894 			close(fd);
1895 	}
1896 
1897 	/* drop our controlling TTY completely if possible */
1898 	if (droplev > 1)
1899 	{
1900 		(void) setsid();
1901 		errno = 0;
1902 	}
1903 
1904 #if XDEBUG
1905 	checkfd012("disconnect");
1906 #endif
1907 
1908 	if (LogLevel > 71)
1909 		sm_syslog(LOG_DEBUG, e->e_id,
1910 			"in background, pid=%d",
1911 			getpid());
1912 
1913 	errno = 0;
1914 }
1915 
1916 static void
1917 obsolete(argv)
1918 	char *argv[];
1919 {
1920 	register char *ap;
1921 	register char *op;
1922 
1923 	while ((ap = *++argv) != NULL)
1924 	{
1925 		/* Return if "--" or not an option of any form. */
1926 		if (ap[0] != '-' || ap[1] == '-')
1927 			return;
1928 
1929 		/* skip over options that do have a value */
1930 		op = strchr(OPTIONS, ap[1]);
1931 		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
1932 		    ap[1] != 'd' &&
1933 #if defined(sony_news)
1934 		    ap[1] != 'E' && ap[1] != 'J' &&
1935 #endif
1936 		    argv[1] != NULL && argv[1][0] != '-')
1937 		{
1938 			argv++;
1939 			continue;
1940 		}
1941 
1942 		/* If -C doesn't have an argument, use sendmail.cf. */
1943 #define	__DEFPATH	"sendmail.cf"
1944 		if (ap[1] == 'C' && ap[2] == '\0')
1945 		{
1946 			*argv = xalloc(sizeof(__DEFPATH) + 2);
1947 			argv[0][0] = '-';
1948 			argv[0][1] = 'C';
1949 			(void)strcpy(&argv[0][2], __DEFPATH);
1950 		}
1951 
1952 		/* If -q doesn't have an argument, run it once. */
1953 		if (ap[1] == 'q' && ap[2] == '\0')
1954 			*argv = "-q0";
1955 
1956 		/* if -d doesn't have an argument, use 0-99.1 */
1957 		if (ap[1] == 'd' && ap[2] == '\0')
1958 			*argv = "-d0-99.1";
1959 
1960 # if defined(sony_news)
1961 		/* if -E doesn't have an argument, use -EC */
1962 		if (ap[1] == 'E' && ap[2] == '\0')
1963 			*argv = "-EC";
1964 
1965 		/* if -J doesn't have an argument, use -JJ */
1966 		if (ap[1] == 'J' && ap[2] == '\0')
1967 			*argv = "-JJ";
1968 # endif
1969 	}
1970 }
1971 /*
1972 **  AUTH_WARNING -- specify authorization warning
1973 **
1974 **	Parameters:
1975 **		e -- the current envelope.
1976 **		msg -- the text of the message.
1977 **		args -- arguments to the message.
1978 **
1979 **	Returns:
1980 **		none.
1981 */
1982 
1983 void
1984 #ifdef __STDC__
1985 auth_warning(register ENVELOPE *e, const char *msg, ...)
1986 #else
1987 auth_warning(e, msg, va_alist)
1988 	register ENVELOPE *e;
1989 	const char *msg;
1990 	va_dcl
1991 #endif
1992 {
1993 	char buf[MAXLINE];
1994 	VA_LOCAL_DECL
1995 
1996 	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
1997 	{
1998 		register char *p;
1999 		static char hostbuf[48];
2000 		extern struct hostent *myhostname __P((char *, int));
2001 
2002 		if (hostbuf[0] == '\0')
2003 			(void) myhostname(hostbuf, sizeof hostbuf);
2004 
2005 		(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
2006 		p = &buf[strlen(buf)];
2007 		VA_START(msg);
2008 		vsnprintf(p, SPACELEFT(buf, p), msg, ap);
2009 		VA_END;
2010 		addheader("X-Authentication-Warning", buf, &e->e_header);
2011 		if (LogLevel > 3)
2012 			sm_syslog(LOG_INFO, e->e_id,
2013 				"Authentication-Warning: %.400s",
2014 				buf);
2015 	}
2016 }
2017 /*
2018 **  GETEXTENV -- get from external environment
2019 **
2020 **	Parameters:
2021 **		envar -- the name of the variable to retrieve
2022 **
2023 **	Returns:
2024 **		The value, if any.
2025 */
2026 
2027 char *
2028 getextenv(envar)
2029 	const char *envar;
2030 {
2031 	char **envp;
2032 	int l;
2033 
2034 	l = strlen(envar);
2035 	for (envp = ExternalEnviron; *envp != NULL; envp++)
2036 	{
2037 		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
2038 			return &(*envp)[l + 1];
2039 	}
2040 	return NULL;
2041 }
2042 /*
2043 **  SETUSERENV -- set an environment in the propogated environment
2044 **
2045 **	Parameters:
2046 **		envar -- the name of the environment variable.
2047 **		value -- the value to which it should be set.  If
2048 **			null, this is extracted from the incoming
2049 **			environment.  If that is not set, the call
2050 **			to setuserenv is ignored.
2051 **
2052 **	Returns:
2053 **		none.
2054 */
2055 
2056 void
2057 setuserenv(envar, value)
2058 	const char *envar;
2059 	const char *value;
2060 {
2061 	int i;
2062 	char **evp = UserEnviron;
2063 	char *p;
2064 
2065 	if (value == NULL)
2066 	{
2067 		value = getextenv(envar);
2068 		if (value == NULL)
2069 			return;
2070 	}
2071 
2072 	i = strlen(envar);
2073 	p = (char *) xalloc(strlen(value) + i + 2);
2074 	strcpy(p, envar);
2075 	p[i++] = '=';
2076 	strcpy(&p[i], value);
2077 
2078 	while (*evp != NULL && strncmp(*evp, p, i) != 0)
2079 		evp++;
2080 	if (*evp != NULL)
2081 	{
2082 		*evp++ = p;
2083 	}
2084 	else if (evp < &UserEnviron[MAXUSERENVIRON])
2085 	{
2086 		*evp++ = p;
2087 		*evp = NULL;
2088 	}
2089 
2090 	/* make sure it is in our environment as well */
2091 	if (putenv(p) < 0)
2092 		syserr("setuserenv: putenv(%s) failed", p);
2093 }
2094 /*
2095 **  DUMPSTATE -- dump state
2096 **
2097 **	For debugging.
2098 */
2099 
2100 void
2101 dumpstate(when)
2102 	char *when;
2103 {
2104 	register char *j = macvalue('j', CurEnv);
2105 	int rs;
2106 
2107 	sm_syslog(LOG_DEBUG, CurEnv->e_id,
2108 		"--- dumping state on %s: $j = %s ---",
2109 		when,
2110 		j == NULL ? "<NULL>" : j);
2111 	if (j != NULL)
2112 	{
2113 		if (!wordinclass(j, 'w'))
2114 			sm_syslog(LOG_DEBUG, CurEnv->e_id,
2115 				"*** $j not in $=w ***");
2116 	}
2117 	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
2118 	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
2119 	printopenfds(TRUE);
2120 	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
2121 	mci_dump_all(TRUE);
2122 	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
2123 	if (rs > 0)
2124 	{
2125 		int stat;
2126 		register char **pvp;
2127 		char *pv[MAXATOM + 1];
2128 
2129 		pv[0] = NULL;
2130 		stat = rewrite(pv, rs, 0, CurEnv);
2131 		sm_syslog(LOG_DEBUG, CurEnv->e_id,
2132 		       "--- ruleset debug_dumpstate returns stat %d, pv: ---",
2133 		       stat);
2134 		for (pvp = pv; *pvp != NULL; pvp++)
2135 			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
2136 	}
2137 	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
2138 }
2139 
2140 
2141 /* ARGSUSED */
2142 SIGFUNC_DECL
2143 sigusr1(sig)
2144 	int sig;
2145 {
2146 	dumpstate("user signal");
2147 	return SIGFUNC_RETURN;
2148 }
2149 
2150 
2151 /* ARGSUSED */
2152 SIGFUNC_DECL
2153 sighup(sig)
2154 	int sig;
2155 {
2156 	if (SaveArgv[0][0] != '/')
2157 	{
2158 		if (LogLevel > 3)
2159 			sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
2160 		exit(EX_OSFILE);
2161 	}
2162 	if (LogLevel > 3)
2163 		sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
2164 	alarm(0);
2165 	releasesignal(SIGHUP);
2166 	if (drop_privileges(TRUE) != EX_OK)
2167 	{
2168 		if (LogLevel > 0)
2169 			sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
2170 				RunAsUid, RunAsGid);
2171 		exit(EX_OSERR);
2172 	}
2173 	execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
2174 	if (LogLevel > 0)
2175 		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
2176 	exit(EX_OSFILE);
2177 }
2178 /*
2179 **  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
2180 **
2181 **	Parameters:
2182 **		to_real_uid -- if set, drop to the real uid instead
2183 **			of the RunAsUser.
2184 **
2185 **	Returns:
2186 **		EX_OSERR if the setuid failed.
2187 **		EX_OK otherwise.
2188 */
2189 
2190 int
2191 drop_privileges(to_real_uid)
2192 	bool to_real_uid;
2193 {
2194 	int rval = EX_OK;
2195 	GIDSET_T emptygidset[1];
2196 
2197 	if (tTd(47, 1))
2198 		printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
2199 			(int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid);
2200 
2201 	if (to_real_uid)
2202 	{
2203 		RunAsUserName = RealUserName;
2204 		RunAsUid = RealUid;
2205 		RunAsGid = RealGid;
2206 	}
2207 
2208 	/* make sure no one can grab open descriptors for secret files */
2209 	endpwent();
2210 
2211 	/* reset group permissions; these can be set later */
2212 	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
2213 	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
2214 		rval = EX_OSERR;
2215 
2216 	/* reset primary group and user id */
2217 	if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
2218 		rval = EX_OSERR;
2219 	if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
2220 		rval = EX_OSERR;
2221 	if (tTd(47, 5))
2222 	{
2223 		printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
2224 			(int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
2225 		printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
2226 	}
2227 	return rval;
2228 }
2229 /*
2230 **  FILL_FD -- make sure a file descriptor has been properly allocated
2231 **
2232 **	Used to make sure that stdin/out/err are allocated on startup
2233 **
2234 **	Parameters:
2235 **		fd -- the file descriptor to be filled.
2236 **		where -- a string used for logging.  If NULL, this is
2237 **			being called on startup, and logging should
2238 **			not be done.
2239 **
2240 **	Returns:
2241 **		none
2242 */
2243 
2244 void
2245 fill_fd(fd, where)
2246 	int fd;
2247 	char *where;
2248 {
2249 	int i;
2250 	struct stat stbuf;
2251 
2252 	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
2253 		return;
2254 
2255 	if (where != NULL)
2256 		syserr("fill_fd: %s: fd %d not open", where, fd);
2257 	else
2258 		MissingFds |= 1 << fd;
2259 	i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
2260 	if (i < 0)
2261 	{
2262 		syserr("!fill_fd: %s: cannot open /dev/null",
2263 			where == NULL ? "startup" : where);
2264 	}
2265 	if (fd != i)
2266 	{
2267 		(void) dup2(i, fd);
2268 		(void) close(i);
2269 	}
2270 }
2271 /*
2272 **  TESTMODELINE -- process a test mode input line
2273 **
2274 **	Parameters:
2275 **		line -- the input line.
2276 **		e -- the current environment.
2277 **	Syntax:
2278 **		#  a comment
2279 **		.X process X as a configuration line
2280 **		=X dump a configuration item (such as mailers)
2281 **		$X dump a macro or class
2282 **		/X try an activity
2283 **		X  normal process through rule set X
2284 */
2285 
2286 void
2287 testmodeline(line, e)
2288 	char *line;
2289 	ENVELOPE *e;
2290 {
2291 	register char *p;
2292 	char *q;
2293 	auto char *delimptr;
2294 	int mid;
2295 	int i, rs;
2296 	STAB *map;
2297 	char **s;
2298 	struct rewrite *rw;
2299 	ADDRESS a;
2300 	static int tryflags = RF_COPYNONE;
2301 	char exbuf[MAXLINE];
2302 	extern bool invalidaddr __P((char *, char *));
2303 	extern char *crackaddr __P((char *));
2304 	extern void dump_class __P((STAB *, int));
2305 	extern void translate_dollars __P((char *));
2306 	extern void help __P((char *));
2307 
2308 	switch (line[0])
2309 	{
2310 	  case '#':
2311 	  case 0:
2312 		return;
2313 
2314 	  case '?':
2315 		help("-bt");
2316 		return;
2317 
2318 	  case '.':		/* config-style settings */
2319 		switch (line[1])
2320 		{
2321 		  case 'D':
2322 			mid = macid(&line[2], &delimptr);
2323 			if (mid == '\0')
2324 				return;
2325 			translate_dollars(delimptr);
2326 			define(mid, newstr(delimptr), e);
2327 			break;
2328 
2329 		  case 'C':
2330 			if (line[2] == '\0')	/* not to call syserr() */
2331 				return;
2332 
2333 			mid = macid(&line[2], &delimptr);
2334 			if (mid == '\0')
2335 				return;
2336 			translate_dollars(delimptr);
2337 			expand(delimptr, exbuf, sizeof exbuf, e);
2338 			p = exbuf;
2339 			while (*p != '\0')
2340 			{
2341 				register char *wd;
2342 				char delim;
2343 
2344 				while (*p != '\0' && isascii(*p) && isspace(*p))
2345 					p++;
2346 				wd = p;
2347 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2348 					p++;
2349 				delim = *p;
2350 				*p = '\0';
2351 				if (wd[0] != '\0')
2352 					setclass(mid, wd);
2353 				*p = delim;
2354 			}
2355 			break;
2356 
2357 		  case '\0':
2358 			printf("Usage: .[DC]macro value(s)\n");
2359 			break;
2360 
2361 		  default:
2362 			printf("Unknown \".\" command %s\n", line);
2363 			break;
2364 		}
2365 		return;
2366 
2367 	  case '=':		/* config-style settings */
2368 		switch (line[1])
2369 		{
2370 		  case 'S':		/* dump rule set */
2371 			rs = strtorwset(&line[2], NULL, ST_FIND);
2372 			if (rs < 0)
2373 			{
2374 				printf("Undefined ruleset %s\n", &line[2]);
2375 				return;
2376 			}
2377 			rw = RewriteRules[rs];
2378 			if (rw == NULL)
2379 				return;
2380 			do
2381 			{
2382 				putchar('R');
2383 				s = rw->r_lhs;
2384 				while (*s != NULL)
2385 				{
2386 					xputs(*s++);
2387 					putchar(' ');
2388 				}
2389 				putchar('\t');
2390 				putchar('\t');
2391 				s = rw->r_rhs;
2392 				while (*s != NULL)
2393 				{
2394 					xputs(*s++);
2395 					putchar(' ');
2396 				}
2397 				putchar('\n');
2398 			} while ((rw = rw->r_next) != NULL);
2399 			break;
2400 
2401 		  case 'M':
2402 			for (i = 0; i < MAXMAILERS; i++)
2403 			{
2404 				if (Mailer[i] != NULL)
2405 					printmailer(Mailer[i]);
2406 			}
2407 			break;
2408 
2409 		  case '\0':
2410 			printf("Usage: =Sruleset or =M\n");
2411 			break;
2412 
2413 		  default:
2414 			printf("Unknown \"=\" command %s\n", line);
2415 			break;
2416 		}
2417 		return;
2418 
2419 	  case '-':		/* set command-line-like opts */
2420 		switch (line[1])
2421 		{
2422 		  case 'd':
2423 			tTflag(&line[2]);
2424 			break;
2425 
2426 		  case '\0':
2427 			printf("Usage: -d{debug arguments}\n");
2428 			break;
2429 
2430 		  default:
2431 			printf("Unknown \"-\" command %s\n", line);
2432 			break;
2433 		}
2434 		return;
2435 
2436 	  case '$':
2437 		if (line[1] == '=')
2438 		{
2439 			mid = macid(&line[2], NULL);
2440 			if (mid != '\0')
2441 				stabapply(dump_class, mid);
2442 			return;
2443 		}
2444 		mid = macid(&line[1], NULL);
2445 		if (mid == '\0')
2446 			return;
2447 		p = macvalue(mid, e);
2448 		if (p == NULL)
2449 			printf("Undefined\n");
2450 		else
2451 		{
2452 			xputs(p);
2453 			printf("\n");
2454 		}
2455 		return;
2456 
2457 	  case '/':		/* miscellaneous commands */
2458 		p = &line[strlen(line)];
2459 		while (--p >= line && isascii(*p) && isspace(*p))
2460 			*p = '\0';
2461 		p = strpbrk(line, " \t");
2462 		if (p != NULL)
2463 		{
2464 			while (isascii(*p) && isspace(*p))
2465 				*p++ = '\0';
2466 		}
2467 		else
2468 			p = "";
2469 		if (line[1] == '\0')
2470 		{
2471 			printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
2472 			return;
2473 		}
2474 		if (strcasecmp(&line[1], "mx") == 0)
2475 		{
2476 #if NAMED_BIND
2477 			/* look up MX records */
2478 			int nmx;
2479 			auto int rcode;
2480 			char *mxhosts[MAXMXHOSTS + 1];
2481 
2482 			if (*p == '\0')
2483 			{
2484 				printf("Usage: /mx address\n");
2485 				return;
2486 			}
2487 			nmx = getmxrr(p, mxhosts, FALSE, &rcode);
2488 			printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
2489 			for (i = 0; i < nmx; i++)
2490 				printf("\t%s\n", mxhosts[i]);
2491 #else
2492 			printf("No MX code compiled in\n");
2493 #endif
2494 		}
2495 		else if (strcasecmp(&line[1], "canon") == 0)
2496 		{
2497 			char host[MAXHOSTNAMELEN];
2498 
2499 			if (*p == '\0')
2500 			{
2501 				printf("Usage: /canon address\n");
2502 				return;
2503 			}
2504 			else if (strlen(p) >= sizeof host)
2505 			{
2506 				printf("Name too long\n");
2507 				return;
2508 			}
2509 			strcpy(host, p);
2510 			(void) getcanonname(host, sizeof(host), HasWildcardMX);
2511 			printf("getcanonname(%s) returns %s\n", p, host);
2512 		}
2513 		else if (strcasecmp(&line[1], "map") == 0)
2514 		{
2515 			auto int rcode = EX_OK;
2516 			char *av[2];
2517 
2518 			if (*p == '\0')
2519 			{
2520 				printf("Usage: /map mapname key\n");
2521 				return;
2522 			}
2523 			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
2524 				continue;
2525 			if (*q == '\0')
2526 			{
2527 				printf("No key specified\n");
2528 				return;
2529 			}
2530 			*q++ = '\0';
2531 			map = stab(p, ST_MAP, ST_FIND);
2532 			if (map == NULL)
2533 			{
2534 				printf("Map named \"%s\" not found\n", p);
2535 				return;
2536 			}
2537 			if (!bitset(MF_OPEN, map->s_map.map_mflags))
2538 			{
2539 				printf("Map named \"%s\" not open\n", p);
2540 				return;
2541 			}
2542 			printf("map_lookup: %s (%s) ", p, q);
2543 			av[0] = q;
2544 			av[1] = NULL;
2545 			p = (*map->s_map.map_class->map_lookup)
2546 					(&map->s_map, q, av, &rcode);
2547 			if (p == NULL)
2548 				printf("no match (%d)\n", rcode);
2549 			else
2550 				printf("returns %s (%d)\n", p, rcode);
2551 		}
2552 		else if (strcasecmp(&line[1], "try") == 0)
2553 		{
2554 			MAILER *m;
2555 			STAB *s;
2556 			auto int rcode = EX_OK;
2557 
2558 			q = strpbrk(p, " \t");
2559 			if (q != NULL)
2560 			{
2561 				while (isascii(*q) && isspace(*q))
2562 					*q++ = '\0';
2563 			}
2564 			if (q == NULL || *q == '\0')
2565 			{
2566 				printf("Usage: /try mailer address\n");
2567 				return;
2568 			}
2569 			s = stab(p, ST_MAILER, ST_FIND);
2570 			if (s == NULL)
2571 			{
2572 				printf("Unknown mailer %s\n", p);
2573 				return;
2574 			}
2575 			m = s->s_mailer;
2576 			printf("Trying %s %s address %s for mailer %s\n",
2577 				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2578 				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
2579 				q, p);
2580 			p = remotename(q, m, tryflags, &rcode, CurEnv);
2581 			printf("Rcode = %d, addr = %s\n",
2582 				rcode, p == NULL ? "<NULL>" : p);
2583 			e->e_to = NULL;
2584 		}
2585 		else if (strcasecmp(&line[1], "tryflags") == 0)
2586 		{
2587 			if (*p == '\0')
2588 			{
2589 				printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
2590 				return;
2591 			}
2592 			for (; *p != '\0'; p++)
2593 			{
2594 				switch (*p)
2595 				{
2596 				  case 'H':
2597 				  case 'h':
2598 					tryflags |= RF_HEADERADDR;
2599 					break;
2600 
2601 				  case 'E':
2602 				  case 'e':
2603 					tryflags &= ~RF_HEADERADDR;
2604 					break;
2605 
2606 				  case 'S':
2607 				  case 's':
2608 					tryflags |= RF_SENDERADDR;
2609 					break;
2610 
2611 				  case 'R':
2612 				  case 'r':
2613 					tryflags &= ~RF_SENDERADDR;
2614 					break;
2615 				}
2616 			}
2617 		}
2618 		else if (strcasecmp(&line[1], "parse") == 0)
2619 		{
2620 			if (*p == '\0')
2621 			{
2622 				printf("Usage: /parse address\n");
2623 				return;
2624 			}
2625 			q = crackaddr(p);
2626 			printf("Cracked address = ");
2627 			xputs(q);
2628 			printf("\nParsing %s %s address\n",
2629 				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2630 				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
2631 			if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
2632 				printf("Cannot parse\n");
2633 			else if (a.q_host != NULL && a.q_host[0] != '\0')
2634 				printf("mailer %s, host %s, user %s\n",
2635 					a.q_mailer->m_name, a.q_host, a.q_user);
2636 			else
2637 				printf("mailer %s, user %s\n",
2638 					a.q_mailer->m_name, a.q_user);
2639 			e->e_to = NULL;
2640 		}
2641 		else
2642 		{
2643 			printf("Unknown \"/\" command %s\n", line);
2644 		}
2645 		return;
2646 	}
2647 
2648 	for (p = line; isascii(*p) && isspace(*p); p++)
2649 		continue;
2650 	q = p;
2651 	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2652 		p++;
2653 	if (*p == '\0')
2654 	{
2655 		printf("No address!\n");
2656 		return;
2657 	}
2658 	*p = '\0';
2659 	if (invalidaddr(p + 1, NULL))
2660 		return;
2661 	do
2662 	{
2663 		register char **pvp;
2664 		char pvpbuf[PSBUFSIZE];
2665 
2666 		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
2667 			      &delimptr, NULL);
2668 		if (pvp == NULL)
2669 			continue;
2670 		p = q;
2671 		while (*p != '\0')
2672 		{
2673 			int stat;
2674 
2675 			rs = strtorwset(p, NULL, ST_FIND);
2676 			if (rs < 0)
2677 			{
2678 				printf("Undefined ruleset %s\n", p);
2679 				break;
2680 			}
2681 			stat = rewrite(pvp, rs, 0, e);
2682 			if (stat != EX_OK)
2683 				printf("== Ruleset %s (%d) status %d\n",
2684 					p, rs, stat);
2685 			while (*p != '\0' && *p++ != ',')
2686 				continue;
2687 		}
2688 	} while (*(p = delimptr) != '\0');
2689 }
2690 
2691 
2692 void
2693 dump_class(s, id)
2694 	register STAB *s;
2695 	int id;
2696 {
2697 	if (s->s_type != ST_CLASS)
2698 		return;
2699 	if (bitnset(id & 0xff, s->s_class))
2700 		printf("%s\n", s->s_name);
2701 }
2702