conf.c revision 43151
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
14static char sccsid[] = "@(#)conf.c	8.450 (Berkeley) 12/17/1998";
15#endif /* not lint */
16
17# include "sendmail.h"
18# include "pathnames.h"
19# include <sys/ioctl.h>
20# include <sys/param.h>
21# include <limits.h>
22
23/*
24**  CONF.C -- Sendmail Configuration Tables.
25**
26**	Defines the configuration of this installation.
27**
28**	Configuration Variables:
29**		HdrInfo -- a table describing well-known header fields.
30**			Each entry has the field name and some flags,
31**			which are described in sendmail.h.
32**
33**	Notes:
34**		I have tried to put almost all the reasonable
35**		configuration information into the configuration
36**		file read at runtime.  My intent is that anything
37**		here is a function of the version of UNIX you
38**		are running, or is really static -- for example
39**		the headers are a superset of widely used
40**		protocols.  If you find yourself playing with
41**		this file too much, you may be making a mistake!
42*/
43
44
45/*
46**  Header info table
47**	Final (null) entry contains the flags used for any other field.
48**
49**	Not all of these are actually handled specially by sendmail
50**	at this time.  They are included as placeholders, to let
51**	you know that "someday" I intend to have sendmail do
52**	something with them.
53*/
54
55struct hdrinfo	HdrInfo[] =
56{
57		/* originator fields, most to least significant  */
58	{ "resent-sender",		H_FROM|H_RESENT			},
59	{ "resent-from",		H_FROM|H_RESENT			},
60	{ "resent-reply-to",		H_FROM|H_RESENT			},
61	{ "sender",			H_FROM				},
62	{ "from",			H_FROM				},
63	{ "reply-to",			H_FROM				},
64	{ "errors-to",			H_FROM|H_ERRORSTO		},
65	{ "full-name",			H_ACHECK			},
66	{ "return-receipt-to",		H_RECEIPTTO			},
67
68		/* destination fields */
69	{ "to",				H_RCPT				},
70	{ "resent-to",			H_RCPT|H_RESENT			},
71	{ "cc",				H_RCPT				},
72	{ "resent-cc",			H_RCPT|H_RESENT			},
73	{ "bcc",			H_RCPT|H_BCC			},
74	{ "resent-bcc",			H_RCPT|H_BCC|H_RESENT		},
75	{ "apparently-to",		H_RCPT				},
76
77		/* message identification and control */
78	{ "message-id",			0				},
79	{ "resent-message-id",		H_RESENT			},
80	{ "message",			H_EOH				},
81	{ "text",			H_EOH				},
82
83		/* date fields */
84	{ "date",			0				},
85	{ "resent-date",		H_RESENT			},
86
87		/* trace fields */
88	{ "received",			H_TRACE|H_FORCE			},
89	{ "x400-received",		H_TRACE|H_FORCE			},
90	{ "via",			H_TRACE|H_FORCE			},
91	{ "mail-from",			H_TRACE|H_FORCE			},
92
93		/* miscellaneous fields */
94	{ "comments",			H_FORCE|H_ENCODABLE		},
95	{ "return-path",		H_FORCE|H_ACHECK		},
96	{ "content-transfer-encoding",	H_CTE				},
97	{ "content-type",		H_CTYPE				},
98	{ "content-length",		H_ACHECK			},
99	{ "subject",			H_ENCODABLE			},
100
101	{ NULL,				0				}
102};
103
104
105
106/*
107**  Privacy values
108*/
109
110struct prival PrivacyValues[] =
111{
112	{ "public",		PRIV_PUBLIC		},
113	{ "needmailhelo",	PRIV_NEEDMAILHELO	},
114	{ "needexpnhelo",	PRIV_NEEDEXPNHELO	},
115	{ "needvrfyhelo",	PRIV_NEEDVRFYHELO	},
116	{ "noexpn",		PRIV_NOEXPN		},
117	{ "novrfy",		PRIV_NOVRFY		},
118	{ "restrictmailq",	PRIV_RESTRICTMAILQ	},
119	{ "restrictqrun",	PRIV_RESTRICTQRUN	},
120	{ "noetrn",		PRIV_NOETRN		},
121	{ "noverb",		PRIV_NOVERB		},
122	{ "authwarnings",	PRIV_AUTHWARNINGS	},
123	{ "noreceipts",		PRIV_NORECEIPTS		},
124	{ "goaway",		PRIV_GOAWAY		},
125	{ NULL,			0			}
126};
127
128/*
129**  DontBlameSendmail values
130*/
131struct dbsval DontBlameSendmailValues[] =
132{
133	{ "safe",			DBS_SAFE			},
134	{ "assumesafechown",		DBS_ASSUMESAFECHOWN		},
135	{ "groupwritabledirpathsafe",	DBS_GROUPWRITABLEDIRPATHSAFE	},
136	{ "groupwritableforwardfilesafe",
137					DBS_GROUPWRITABLEFORWARDFILESAFE },
138	{ "groupwritableincludefilesafe",
139					DBS_GROUPWRITABLEINCLUDEFILESAFE },
140	{ "groupwritablealiasfile",	DBS_GROUPWRITABLEALIASFILE	},
141	{ "worldwritablealiasfile",	DBS_WORLDWRITABLEALIASFILE	},
142	{ "forwardfileinunsafedirpath",	DBS_FORWARDFILEINUNSAFEDIRPATH	},
143	{ "includefileinunsafedirpath",	DBS_INCLUDEFILEINUNSAFEDIRPATH	},
144	{ "mapinunsafedirpath",		DBS_MAPINUNSAFEDIRPATH	},
145	{ "linkedaliasfileinwritabledir",
146					DBS_LINKEDALIASFILEINWRITABLEDIR },
147	{ "linkedclassfileinwritabledir",
148					DBS_LINKEDCLASSFILEINWRITABLEDIR },
149	{ "linkedforwardfileinwritabledir",
150					DBS_LINKEDFORWARDFILEINWRITABLEDIR },
151	{ "linkedincludefileinwritabledir",
152					DBS_LINKEDINCLUDEFILEINWRITABLEDIR },
153	{ "linkedmapinwritabledir",	DBS_LINKEDMAPINWRITABLEDIR	},
154	{ "linkedserviceswitchfileinwritabledir",
155					DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR },
156	{ "filedeliverytohardlink",	DBS_FILEDELIVERYTOHARDLINK	},
157	{ "filedeliverytosymlink",	DBS_FILEDELIVERYTOSYMLINK	},
158	{ "writemaptohardlink",		DBS_WRITEMAPTOHARDLINK		},
159	{ "writemaptosymlink",		DBS_WRITEMAPTOSYMLINK		},
160	{ "writestatstohardlink",	DBS_WRITESTATSTOHARDLINK	},
161	{ "writestatstosymlink",	DBS_WRITESTATSTOSYMLINK		},
162	{ "forwardfileingroupwritabledirpath",
163					DBS_FORWARDFILEINGROUPWRITABLEDIRPATH },
164	{ "includefileingroupwritabledirpath",
165					DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH },
166	{ "classfileinunsafedirpath",	DBS_CLASSFILEINUNSAFEDIRPATH	},
167	{ "errorheaderinunsafedirpath",	DBS_ERRORHEADERINUNSAFEDIRPATH	},
168	{ "helpfileinunsafedirpath",	DBS_HELPFILEINUNSAFEDIRPATH	},
169	{ "forwardfileinunsafedirpathsafe",
170					DBS_FORWARDFILEINUNSAFEDIRPATHSAFE },
171	{ "includefileinunsafedirpathsafe",
172					DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE },
173	{ "runprograminunsafedirpath",	DBS_RUNPROGRAMINUNSAFEDIRPATH	},
174	{ "runwritableprogram",		DBS_RUNWRITABLEPROGRAM		},
175	{ NULL,				0				}
176};
177
178
179/*
180**  Miscellaneous stuff.
181*/
182
183int	DtableSize =	50;		/* max open files; reset in 4.2bsd */
184/*
185**  SETDEFAULTS -- set default values
186**
187**	Because of the way freezing is done, these must be initialized
188**	using direct code.
189**
190**	Parameters:
191**		e -- the default envelope.
192**
193**	Returns:
194**		none.
195**
196**	Side Effects:
197**		Initializes a bunch of global variables to their
198**		default values.
199*/
200
201#define MINUTES		* 60
202#define HOURS		* 60 MINUTES
203#define DAYS		* 24 HOURS
204
205#ifndef _PATH_VARTMP
206# define _PATH_VARTMP	"/usr/tmp/"
207#endif
208
209#ifndef MAXRULERECURSION
210# define MAXRULERECURSION	50	/* max ruleset recursion depth */
211#endif
212
213void
214setdefaults(e)
215	register ENVELOPE *e;
216{
217	int i;
218	struct passwd *pw;
219	char buf[MAXNAME];
220	extern void setdefuser __P((void));
221	extern void setupmaps __P((void));
222	extern void setupmailers __P((void));
223	extern void setupheaders __P((void));
224
225	SpaceSub = ' ';				/* option B */
226	QueueLA = 8;				/* option x */
227	RefuseLA = 12;				/* option X */
228	WkRecipFact = 30000L;			/* option y */
229	WkClassFact = 1800L;			/* option z */
230	WkTimeFact = 90000L;			/* option Z */
231	QueueFactor = WkRecipFact * 20;		/* option q */
232	FileMode = (RealUid != geteuid()) ? 0644 : 0600;
233						/* option F */
234
235	if (((pw = getpwnam("mailnull")) != NULL && pw->pw_uid != 0) ||
236	    ((pw = getpwnam("sendmail")) != NULL && pw->pw_uid != 0) ||
237	    ((pw = getpwnam("daemon")) != NULL && pw->pw_uid != 0))
238	{
239		DefUid = pw->pw_uid;		/* option u */
240		DefGid = pw->pw_gid;		/* option g */
241		DefUser = newstr(pw->pw_name);
242	}
243	else
244	{
245		DefUid = 1;			/* option u */
246		DefGid = 1;			/* option g */
247		setdefuser();
248	}
249	TrustedUid = 0;
250	if (tTd(37, 4))
251		printf("setdefaults: DefUser=%s, DefUid=%d, DefGid=%d\n",
252		       DefUser != NULL ? DefUser : "<1:1>",
253		       (int) DefUid, (int) DefGid);
254	CheckpointInterval = 10;		/* option C */
255	MaxHopCount = 25;			/* option h */
256	e->e_sendmode = SM_FORK;		/* option d */
257	e->e_errormode = EM_PRINT;		/* option e */
258	SevenBitInput = FALSE;			/* option 7 */
259	MaxMciCache = 1;			/* option k */
260	MciCacheTimeout = 5 MINUTES;		/* option K */
261	LogLevel = 9;				/* option L */
262	inittimeouts(NULL);			/* option r */
263	PrivacyFlags = PRIV_PUBLIC;		/* option p */
264	DontBlameSendmail = DBS_SAFE;		/* DontBlameSendmail option */
265#if MIME8TO7
266	MimeMode = MM_CVTMIME|MM_PASS8BIT;	/* option 8 */
267#else
268	MimeMode = MM_PASS8BIT;
269#endif
270	for (i = 0; i < MAXTOCLASS; i++)
271	{
272		TimeOuts.to_q_return[i] = 5 DAYS;	/* option T */
273		TimeOuts.to_q_warning[i] = 0;		/* option T */
274	}
275	ServiceSwitchFile = "/etc/service.switch";
276	ServiceCacheMaxAge = (time_t) 10;
277	HostsFile = _PATH_HOSTS;
278	PidFile = newstr(_PATH_SENDMAILPID);
279	MustQuoteChars = "@,;:\\()[].'";
280	MciInfoTimeout = 30 MINUTES;
281	MaxRuleRecursion = MAXRULERECURSION;
282	MaxAliasRecursion = 10;
283	MaxMacroRecursion = 10;
284	ColonOkInAddr = TRUE;
285	DontLockReadFiles = TRUE;
286	DoubleBounceAddr = "postmaster";
287	MaxHeaderLines = MAXHDRLINES;
288	MaxHeaderLineLength = MAXHDRLINELEN;
289	snprintf(buf, sizeof buf, "%s%sdead.letter",
290		_PATH_VARTMP,
291		_PATH_VARTMP[sizeof _PATH_VARTMP - 2] == '/' ? "" : "/");
292	DeadLetterDrop = newstr(buf);
293#ifdef HESIOD_INIT
294	HesiodContext = NULL;
295#endif
296	ControlSocketName = NULL;
297	setupmaps();
298	setupmailers();
299	setupheaders();
300}
301
302
303/*
304**  SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
305*/
306
307void
308setdefuser()
309{
310	struct passwd *defpwent;
311	static char defuserbuf[40];
312
313	DefUser = defuserbuf;
314	defpwent = sm_getpwuid(DefUid);
315	snprintf(defuserbuf, sizeof defuserbuf, "%s",
316		defpwent == NULL ? "nobody" : defpwent->pw_name);
317	if (tTd(37, 4))
318		printf("setdefuser: DefUid=%d, DefUser=%s\n",
319		       (int) DefUid, DefUser);
320}
321/*
322**  SETUPMAILERS -- initialize default mailers
323*/
324
325void
326setupmailers()
327{
328	char buf[100];
329
330	strcpy(buf, "prog, P=/bin/sh, F=lsoDq9, T=DNS/RFC822/X-Unix, A=sh -c \201u");
331	makemailer(buf);
332
333	strcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=DNS/RFC822/X-Unix, A=FILE \201u");
334	makemailer(buf);
335
336	strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u");
337	makemailer(buf);
338}
339/*
340**  SETUPMAPS -- set up map classes
341*/
342
343#define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \
344	{ \
345		extern bool parse __P((MAP *, char *)); \
346		extern bool open __P((MAP *, int)); \
347		extern void close __P((MAP *)); \
348		extern char *lookup __P((MAP *, char *, char **, int *)); \
349		extern void store __P((MAP *, char *, char *)); \
350		s = stab(name, ST_MAPCLASS, ST_ENTER); \
351		s->s_mapclass.map_cname = name; \
352		s->s_mapclass.map_ext = ext; \
353		s->s_mapclass.map_cflags = flags; \
354		s->s_mapclass.map_parse = parse; \
355		s->s_mapclass.map_open = open; \
356		s->s_mapclass.map_close = close; \
357		s->s_mapclass.map_lookup = lookup; \
358		s->s_mapclass.map_store = store; \
359	}
360
361void
362setupmaps()
363{
364	register STAB *s;
365
366#ifdef NEWDB
367	MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
368		map_parseargs, hash_map_open, db_map_close,
369		db_map_lookup, db_map_store);
370
371	MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
372		map_parseargs, bt_map_open, db_map_close,
373		db_map_lookup, db_map_store);
374#endif
375
376#ifdef NDBM
377	MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE,
378		map_parseargs, ndbm_map_open, ndbm_map_close,
379		ndbm_map_lookup, ndbm_map_store);
380#endif
381
382#ifdef NIS
383	MAPDEF("nis", NULL, MCF_ALIASOK,
384		map_parseargs, nis_map_open, null_map_close,
385		nis_map_lookup, null_map_store);
386#endif
387
388#ifdef NISPLUS
389	MAPDEF("nisplus", NULL, MCF_ALIASOK,
390		map_parseargs, nisplus_map_open, null_map_close,
391		nisplus_map_lookup, null_map_store);
392#endif
393#ifdef LDAPMAP
394	MAPDEF("ldapx", NULL, 0,
395		ldap_map_parseargs, ldap_map_open, ldap_map_close,
396		ldap_map_lookup, null_map_store);
397#endif
398
399#ifdef HESIOD
400	MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY,
401		map_parseargs, hes_map_open, null_map_close,
402		hes_map_lookup, null_map_store);
403#endif
404
405#if NETINFO
406	MAPDEF("netinfo", NULL, MCF_ALIASOK,
407		map_parseargs, ni_map_open, null_map_close,
408		ni_map_lookup, null_map_store);
409#endif
410
411#if 0
412	MAPDEF("dns", NULL, 0,
413		dns_map_init, null_map_open, null_map_close,
414		dns_map_lookup, null_map_store);
415#endif
416
417#if NAMED_BIND
418	/* best MX DNS lookup */
419	MAPDEF("bestmx", NULL, MCF_OPTFILE,
420		map_parseargs, null_map_open, null_map_close,
421		bestmx_map_lookup, null_map_store);
422#endif
423
424	MAPDEF("host", NULL, 0,
425		host_map_init, null_map_open, null_map_close,
426		host_map_lookup, null_map_store);
427
428	MAPDEF("text", NULL, MCF_ALIASOK,
429		map_parseargs, text_map_open, null_map_close,
430		text_map_lookup, null_map_store);
431
432	MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY,
433		map_parseargs, stab_map_open, null_map_close,
434		stab_map_lookup, stab_map_store);
435
436	MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE,
437		map_parseargs, impl_map_open, impl_map_close,
438		impl_map_lookup, impl_map_store);
439
440	/* access to system passwd file */
441	MAPDEF("user", NULL, MCF_OPTFILE,
442		map_parseargs, user_map_open, null_map_close,
443		user_map_lookup, null_map_store);
444
445	/* dequote map */
446	MAPDEF("dequote", NULL, 0,
447		dequote_init, null_map_open, null_map_close,
448		dequote_map, null_map_store);
449
450#ifdef MAP_REGEX
451	MAPDEF("regex", NULL, 0,
452		regex_map_init, null_map_open, null_map_close,
453		regex_map_lookup, null_map_store);
454#endif
455
456#if USERDB
457	/* user database */
458	MAPDEF("userdb", ".db", 0,
459		map_parseargs, null_map_open, null_map_close,
460		udb_map_lookup, null_map_store);
461#endif
462
463	/* arbitrary programs */
464	MAPDEF("program", NULL, MCF_ALIASOK,
465		map_parseargs, null_map_open, null_map_close,
466		prog_map_lookup, null_map_store);
467
468	/* sequenced maps */
469	MAPDEF("sequence", NULL, MCF_ALIASOK,
470		seq_map_parse, null_map_open, null_map_close,
471		seq_map_lookup, seq_map_store);
472
473	/* switched interface to sequenced maps */
474	MAPDEF("switch", NULL, MCF_ALIASOK,
475		map_parseargs, switch_map_open, null_map_close,
476		seq_map_lookup, seq_map_store);
477
478	/* null map lookup -- really for internal use only */
479	MAPDEF("null", NULL, MCF_ALIASOK|MCF_OPTFILE,
480		map_parseargs, null_map_open, null_map_close,
481		null_map_lookup, null_map_store);
482
483#if _FFR_MAP_SYSLOG
484	/* syslog map -- logs information to syslog */
485	MAPDEF("syslog", NULL, 0,
486	       syslog_map_parseargs, null_map_open, null_map_close,
487	       syslog_map_lookup, null_map_store);
488#endif
489}
490
491#undef MAPDEF
492/*
493**  INITHOSTMAPS -- initial host-dependent maps
494**
495**	This should act as an interface to any local service switch
496**	provided by the host operating system.
497**
498**	Parameters:
499**		none
500**
501**	Returns:
502**		none
503**
504**	Side Effects:
505**		Should define maps "host" and "users" as necessary
506**		for this OS.  If they are not defined, they will get
507**		a default value later.  It should check to make sure
508**		they are not defined first, since it's possible that
509**		the config file has provided an override.
510*/
511
512void
513inithostmaps()
514{
515	register int i;
516	int nmaps;
517	char *maptype[MAXMAPSTACK];
518	short mapreturn[MAXMAPACTIONS];
519	char buf[MAXLINE];
520
521	/*
522	**  Set up default hosts maps.
523	*/
524
525#if 0
526	nmaps = switch_map_find("hosts", maptype, mapreturn);
527	for (i = 0; i < nmaps; i++)
528	{
529		if (strcmp(maptype[i], "files") == 0 &&
530		    stab("hosts.files", ST_MAP, ST_FIND) == NULL)
531		{
532			strcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts");
533			(void) makemapentry(buf);
534		}
535#if NAMED_BIND
536		else if (strcmp(maptype[i], "dns") == 0 &&
537		    stab("hosts.dns", ST_MAP, ST_FIND) == NULL)
538		{
539			strcpy(buf, "hosts.dns dns A");
540			(void) makemapentry(buf);
541		}
542#endif
543#ifdef NISPLUS
544		else if (strcmp(maptype[i], "nisplus") == 0 &&
545		    stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL)
546		{
547			strcpy(buf, "hosts.nisplus nisplus -k name -v address -d hosts.org_dir");
548			(void) makemapentry(buf);
549		}
550#endif
551#ifdef NIS
552		else if (strcmp(maptype[i], "nis") == 0 &&
553		    stab("hosts.nis", ST_MAP, ST_FIND) == NULL)
554		{
555			strcpy(buf, "hosts.nis nis -d -k 0 -v 1 hosts.byname");
556			(void) makemapentry(buf);
557		}
558#endif
559#if NETINFO
560		else if (strcmp(maptype[i], "netinfo") == 0) &&
561		    stab("hosts.netinfo", ST_MAP, ST_FIND) == NULL)
562		{
563			strcpy(buf, "hosts.netinfo netinfo -v name /machines");
564			(void) makemapentry(buf);
565		}
566#endif
567	}
568#endif
569
570	/*
571	**  Make sure we have a host map.
572	*/
573
574	if (stab("host", ST_MAP, ST_FIND) == NULL)
575	{
576		/* user didn't initialize: set up host map */
577		strcpy(buf, "host host");
578#if NAMED_BIND
579		if (ConfigLevel >= 2)
580			strcat(buf, " -a.");
581#endif
582		(void) makemapentry(buf);
583	}
584
585	/*
586	**  Set up default aliases maps
587	*/
588
589	nmaps = switch_map_find("aliases", maptype, mapreturn);
590	for (i = 0; i < nmaps; i++)
591	{
592		if (strcmp(maptype[i], "files") == 0 &&
593		    stab("aliases.files", ST_MAP, ST_FIND) == NULL)
594		{
595			strcpy(buf, "aliases.files null");
596			(void) makemapentry(buf);
597		}
598#ifdef NISPLUS
599		else if (strcmp(maptype[i], "nisplus") == 0 &&
600		    stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL)
601		{
602			strcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion -d mail_aliases.org_dir");
603			(void) makemapentry(buf);
604		}
605#endif
606#ifdef NIS
607		else if (strcmp(maptype[i], "nis") == 0 &&
608		    stab("aliases.nis", ST_MAP, ST_FIND) == NULL)
609		{
610			strcpy(buf, "aliases.nis nis -d mail.aliases");
611			(void) makemapentry(buf);
612		}
613#endif
614#ifdef NETINFO
615		else if (strcmp(maptype[i], "netinfo") == 0 &&
616		    stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL)
617		{
618			strcpy(buf, "aliases.netinfo netinfo -z, /aliases");
619			(void) makemapentry(buf);
620		}
621#endif
622#ifdef HESIOD
623		else if (strcmp(maptype[i], "hesiod") == 0 &&
624		    stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL)
625		{
626			strcpy(buf, "aliases.hesiod hesiod aliases");
627			(void) makemapentry(buf);
628		}
629#endif
630	}
631	if (stab("aliases", ST_MAP, ST_FIND) == NULL)
632	{
633		strcpy(buf, "aliases switch aliases");
634		(void) makemapentry(buf);
635	}
636
637#if 0		/* "user" map class is a better choice */
638	/*
639	**  Set up default users maps.
640	*/
641
642	nmaps = switch_map_find("passwd", maptype, mapreturn);
643	for (i = 0; i < nmaps; i++)
644	{
645		if (strcmp(maptype[i], "files") == 0 &&
646		    stab("users.files", ST_MAP, ST_FIND) == NULL)
647		{
648			strcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd");
649			(void) makemapentry(buf);
650		}
651#ifdef NISPLUS
652		else if (strcmp(maptype[i], "nisplus") == 0 &&
653		    stab("users.nisplus", ST_MAP, ST_FIND) == NULL)
654		{
655			strcpy(buf, "users.nisplus nisplus -m -kname -vhome -d passwd.org_dir");
656			(void) makemapentry(buf);
657		}
658#endif
659#ifdef NIS
660		else if (strcmp(maptype[i], "nis") == 0 &&
661		    stab("users.nis", ST_MAP, ST_FIND) == NULL)
662		{
663			strcpy(buf, "users.nis nis -m -d passwd.byname");
664			(void) makemapentry(buf);
665		}
666#endif
667#ifdef HESIOD
668		else if (strcmp(maptype[i], "hesiod") == 0) &&
669		    stab("users.hesiod", ST_MAP, ST_FIND) == NULL)
670		{
671			strcpy(buf, "users.hesiod hesiod");
672			(void) makemapentry(buf);
673		}
674#endif
675	}
676	if (stab("users", ST_MAP, ST_FIND) == NULL)
677	{
678		strcpy(buf, "users switch -m passwd");
679		(void) makemapentry(buf);
680	}
681#endif
682}
683/*
684**  SWITCH_MAP_FIND -- find the list of types associated with a map
685**
686**	This is the system-dependent interface to the service switch.
687**
688**	Parameters:
689**		service -- the name of the service of interest.
690**		maptype -- an out-array of strings containing the types
691**			of access to use for this service.  There can
692**			be at most MAXMAPSTACK types for a single service.
693**		mapreturn -- an out-array of return information bitmaps
694**			for the map.
695**
696**	Returns:
697**		The number of map types filled in, or -1 for failure.
698*/
699
700#if defined(SOLARIS) || (defined(sony_news) && defined(__svr4))
701# define _USE_SUN_NSSWITCH_
702#endif
703
704#ifdef _USE_SUN_NSSWITCH_
705# include <nsswitch.h>
706#endif
707
708#if defined(ultrix) || (defined(__osf__) && defined(__alpha))
709# define _USE_DEC_SVC_CONF_
710#endif
711
712#ifdef _USE_DEC_SVC_CONF_
713# include <sys/svcinfo.h>
714#endif
715
716int
717switch_map_find(service, maptype, mapreturn)
718	char *service;
719	char *maptype[MAXMAPSTACK];
720	short mapreturn[MAXMAPACTIONS];
721{
722	int svcno;
723
724#ifdef _USE_SUN_NSSWITCH_
725	struct __nsw_switchconfig *nsw_conf;
726	enum __nsw_parse_err pserr;
727	struct __nsw_lookup *lk;
728	static struct __nsw_lookup lkp0 =
729		{ "files", {1, 0, 0, 0}, NULL, NULL };
730	static struct __nsw_switchconfig lkp_default =
731		{ 0, "sendmail", 3, &lkp0 };
732
733	for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
734		mapreturn[svcno] = 0;
735
736	if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL)
737		lk = lkp_default.lookups;
738	else
739		lk = nsw_conf->lookups;
740	svcno = 0;
741	while (lk != NULL)
742	{
743		maptype[svcno] = lk->service_name;
744		if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN)
745			mapreturn[MA_NOTFOUND] |= 1 << svcno;
746		if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN)
747			mapreturn[MA_TRYAGAIN] |= 1 << svcno;
748		if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN)
749			mapreturn[MA_TRYAGAIN] |= 1 << svcno;
750		svcno++;
751		lk = lk->next;
752	}
753	return svcno;
754#endif
755
756#ifdef _USE_DEC_SVC_CONF_
757	struct svcinfo *svcinfo;
758	int svc;
759
760	for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
761		mapreturn[svcno] = 0;
762
763	svcinfo = getsvc();
764	if (svcinfo == NULL)
765		goto punt;
766	if (strcmp(service, "hosts") == 0)
767		svc = SVC_HOSTS;
768	else if (strcmp(service, "aliases") == 0)
769		svc = SVC_ALIASES;
770	else if (strcmp(service, "passwd") == 0)
771		svc = SVC_PASSWD;
772	else
773		return -1;
774	for (svcno = 0; svcno < SVC_PATHSIZE; svcno++)
775	{
776		switch (svcinfo->svcpath[svc][svcno])
777		{
778		  case SVC_LOCAL:
779			maptype[svcno] = "files";
780			break;
781
782		  case SVC_YP:
783			maptype[svcno] = "nis";
784			break;
785
786		  case SVC_BIND:
787			maptype[svcno] = "dns";
788			break;
789
790#ifdef SVC_HESIOD
791		  case SVC_HESIOD:
792			maptype[svcno] = "hesiod";
793			break;
794#endif
795
796		  case SVC_LAST:
797			return svcno;
798		}
799	}
800	return svcno;
801#endif
802
803#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
804	/*
805	**  Fall-back mechanism.
806	*/
807
808	STAB *st;
809	time_t now = curtime();
810
811	for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
812		mapreturn[svcno] = 0;
813
814	if ((now - ServiceCacheTime) > (time_t) ServiceCacheMaxAge)
815	{
816		/* (re)read service switch */
817		register FILE *fp;
818		int sff = SFF_REGONLY|SFF_OPENASROOT|SFF_NOLOCK;
819
820		if (!bitset(DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR, DontBlameSendmail))
821			sff |= SFF_NOWLINK;
822
823		if (ConfigFileRead)
824			ServiceCacheTime = now;
825		fp = safefopen(ServiceSwitchFile, O_RDONLY, 0, sff);
826		if (fp != NULL)
827		{
828			char buf[MAXLINE];
829
830			while (fgets(buf, sizeof buf, fp) != NULL)
831			{
832				register char *p;
833
834				p = strpbrk(buf, "#\n");
835				if (p != NULL)
836					*p = '\0';
837				p = strpbrk(buf, " \t");
838				if (p != NULL)
839					*p++ = '\0';
840				if (buf[0] == '\0')
841					continue;
842				if (p == NULL)
843				{
844					sm_syslog(LOG_ERR, NOQID,
845						  "Bad line on %.100s: %.100s",
846						  ServiceSwitchFile,
847						  buf);
848					continue;
849				}
850				while (isspace(*p))
851					p++;
852				if (*p == '\0')
853					continue;
854
855				/*
856				**  Find/allocate space for this service entry.
857				**	Space for all of the service strings
858				**	are allocated at once.  This means
859				**	that we only have to free the first
860				**	one to free all of them.
861				*/
862
863				st = stab(buf, ST_SERVICE, ST_ENTER);
864				if (st->s_service[0] != NULL)
865					free((void *) st->s_service[0]);
866				p = newstr(p);
867				for (svcno = 0; svcno < MAXMAPSTACK; )
868				{
869					if (*p == '\0')
870						break;
871					st->s_service[svcno++] = p;
872					p = strpbrk(p, " \t");
873					if (p == NULL)
874						break;
875					*p++ = '\0';
876					while (isspace(*p))
877						p++;
878				}
879				if (svcno < MAXMAPSTACK)
880					st->s_service[svcno] = NULL;
881			}
882			fclose(fp);
883		}
884	}
885
886	/* look up entry in cache */
887	st = stab(service, ST_SERVICE, ST_FIND);
888	if (st != NULL && st->s_service[0] != NULL)
889	{
890		/* extract data */
891		svcno = 0;
892		while (svcno < MAXMAPSTACK)
893		{
894			maptype[svcno] = st->s_service[svcno];
895			if (maptype[svcno++] == NULL)
896				break;
897		}
898		return --svcno;
899	}
900#endif
901
902#if !defined(_USE_SUN_NSSWITCH_)
903	/* if the service file doesn't work, use an absolute fallback */
904# ifdef _USE_DEC_SVC_CONF_
905  punt:
906# endif
907	for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
908		mapreturn[svcno] = 0;
909	svcno = 0;
910	if (strcmp(service, "aliases") == 0)
911	{
912		maptype[svcno++] = "files";
913# ifdef AUTO_NIS_ALIASES
914#  ifdef NISPLUS
915		maptype[svcno++] = "nisplus";
916#  endif
917#  ifdef NIS
918		maptype[svcno++] = "nis";
919#  endif
920# endif
921		return svcno;
922	}
923	if (strcmp(service, "hosts") == 0)
924	{
925#  if NAMED_BIND
926		maptype[svcno++] = "dns";
927#  else
928#   if defined(sun) && !defined(BSD)
929		/* SunOS */
930		maptype[svcno++] = "nis";
931#   endif
932#  endif
933		maptype[svcno++] = "files";
934		return svcno;
935	}
936	return -1;
937#endif
938}
939/*
940**  USERNAME -- return the user id of the logged in user.
941**
942**	Parameters:
943**		none.
944**
945**	Returns:
946**		The login name of the logged in user.
947**
948**	Side Effects:
949**		none.
950**
951**	Notes:
952**		The return value is statically allocated.
953*/
954
955char *
956username()
957{
958	static char *myname = NULL;
959	extern char *getlogin();
960	register struct passwd *pw;
961
962	/* cache the result */
963	if (myname == NULL)
964	{
965		myname = getlogin();
966		if (myname == NULL || myname[0] == '\0')
967		{
968			pw = sm_getpwuid(RealUid);
969			if (pw != NULL)
970				myname = newstr(pw->pw_name);
971		}
972		else
973		{
974			uid_t uid = RealUid;
975
976			myname = newstr(myname);
977			if ((pw = sm_getpwnam(myname)) == NULL ||
978			      (uid != 0 && uid != pw->pw_uid))
979			{
980				pw = sm_getpwuid(uid);
981				if (pw != NULL)
982					myname = newstr(pw->pw_name);
983			}
984		}
985		if (myname == NULL || myname[0] == '\0')
986		{
987			syserr("554 Who are you?");
988			myname = "postmaster";
989		}
990	}
991
992	return (myname);
993}
994/*
995**  TTYPATH -- Get the path of the user's tty
996**
997**	Returns the pathname of the user's tty.  Returns NULL if
998**	the user is not logged in or if s/he has write permission
999**	denied.
1000**
1001**	Parameters:
1002**		none
1003**
1004**	Returns:
1005**		pathname of the user's tty.
1006**		NULL if not logged in or write permission denied.
1007**
1008**	Side Effects:
1009**		none.
1010**
1011**	WARNING:
1012**		Return value is in a local buffer.
1013**
1014**	Called By:
1015**		savemail
1016*/
1017
1018char *
1019ttypath()
1020{
1021	struct stat stbuf;
1022	register char *pathn;
1023	extern char *ttyname();
1024	extern char *getlogin();
1025
1026	/* compute the pathname of the controlling tty */
1027	if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
1028	    (pathn = ttyname(0)) == NULL)
1029	{
1030		errno = 0;
1031		return (NULL);
1032	}
1033
1034	/* see if we have write permission */
1035	if (stat(pathn, &stbuf) < 0 || !bitset(S_IWOTH, stbuf.st_mode))
1036	{
1037		errno = 0;
1038		return (NULL);
1039	}
1040
1041	/* see if the user is logged in */
1042	if (getlogin() == NULL)
1043		return (NULL);
1044
1045	/* looks good */
1046	return (pathn);
1047}
1048/*
1049**  CHECKCOMPAT -- check for From and To person compatible.
1050**
1051**	This routine can be supplied on a per-installation basis
1052**	to determine whether a person is allowed to send a message.
1053**	This allows restriction of certain types of internet
1054**	forwarding or registration of users.
1055**
1056**	If the hosts are found to be incompatible, an error
1057**	message should be given using "usrerr" and an EX_ code
1058**	should be returned.  You can also set to->q_status to
1059**	a DSN-style status code.
1060**
1061**	EF_NO_BODY_RETN can be set in e->e_flags to suppress the
1062**	body during the return-to-sender function; this should be done
1063**	on huge messages.  This bit may already be set by the ESMTP
1064**	protocol.
1065**
1066**	Parameters:
1067**		to -- the person being sent to.
1068**
1069**	Returns:
1070**		an exit status
1071**
1072**	Side Effects:
1073**		none (unless you include the usrerr stuff)
1074*/
1075
1076int
1077checkcompat(to, e)
1078	register ADDRESS *to;
1079	register ENVELOPE *e;
1080{
1081# ifdef lint
1082	if (to == NULL)
1083		to++;
1084# endif /* lint */
1085
1086	if (tTd(49, 1))
1087		printf("checkcompat(to=%s, from=%s)\n",
1088			to->q_paddr, e->e_from.q_paddr);
1089
1090# ifdef EXAMPLE_CODE
1091	/* this code is intended as an example only */
1092	register STAB *s;
1093
1094	s = stab("arpa", ST_MAILER, ST_FIND);
1095	if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 &&
1096	    to->q_mailer == s->s_mailer)
1097	{
1098		usrerr("553 No ARPA mail through this machine: see your system administration");
1099		/* e->e_flags |= EF_NO_BODY_RETN; to supress body on return */
1100		to->q_status = "5.7.1";
1101		return (EX_UNAVAILABLE);
1102	}
1103# endif /* EXAMPLE_CODE */
1104	return (EX_OK);
1105}
1106/*
1107**  SETSIGNAL -- set a signal handler
1108**
1109**	This is essentially old BSD "signal(3)".
1110*/
1111
1112sigfunc_t
1113setsignal(sig, handler)
1114	int sig;
1115	sigfunc_t handler;
1116{
1117#if defined(SYS5SIGNALS) || defined(BSD4_3)
1118# ifdef BSD4_3
1119	return signal(sig, handler);
1120# else
1121	return sigset(sig, handler);
1122# endif
1123#else
1124	struct sigaction n, o;
1125
1126	bzero(&n, sizeof n);
1127# if USE_SA_SIGACTION
1128	n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler;
1129	n.sa_flags = SA_RESTART|SA_SIGINFO;
1130# else
1131	n.sa_handler = handler;
1132#  ifdef SA_RESTART
1133	n.sa_flags = SA_RESTART;
1134#  endif
1135# endif
1136	if (sigaction(sig, &n, &o) < 0)
1137		return SIG_ERR;
1138	return o.sa_handler;
1139#endif
1140}
1141/*
1142**  BLOCKSIGNAL -- hold a signal to prevent delivery
1143**
1144**	Parameters:
1145**		sig -- the signal to block.
1146**
1147**	Returns:
1148**		1 signal was previously blocked
1149**		0 signal was not previously blocked
1150**		-1 on failure.
1151*/
1152
1153int
1154blocksignal(sig)
1155	int sig;
1156{
1157#ifdef BSD4_3
1158# ifndef sigmask
1159#  define sigmask(s)	(1 << ((s) - 1))
1160# endif
1161	return (sigblock(sigmask(sig)) & sigmask(sig)) != 0;
1162#else
1163# ifdef ALTOS_SYSTEM_V
1164	sigfunc_t handler;
1165
1166	handler = sigset(sig, SIG_HOLD);
1167	if (handler == SIG_ERR)
1168		return -1;
1169	else
1170		return handler == SIG_HOLD;
1171# else
1172	sigset_t sset, oset;
1173
1174	sigemptyset(&sset);
1175	sigaddset(&sset, sig);
1176	if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0)
1177		return -1;
1178	else
1179		return sigismember(&oset, sig);
1180# endif
1181#endif
1182}
1183/*
1184**  RELEASESIGNAL -- release a held signal
1185**
1186**	Parameters:
1187**		sig -- the signal to release.
1188**
1189**	Returns:
1190**		1 signal was previously blocked
1191**		0 signal was not previously blocked
1192**		-1 on failure.
1193*/
1194
1195int
1196releasesignal(sig)
1197	int sig;
1198{
1199#ifdef BSD4_3
1200	return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0;
1201#else
1202# ifdef ALTOS_SYSTEM_V
1203	sigfunc_t handler;
1204
1205	handler = sigset(sig, SIG_HOLD);
1206	if (sigrelse(sig) < 0)
1207		return -1;
1208	else
1209		return handler == SIG_HOLD;
1210# else
1211	sigset_t sset, oset;
1212
1213	sigemptyset(&sset);
1214	sigaddset(&sset, sig);
1215	if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0)
1216		return -1;
1217	else
1218		return sigismember(&oset, sig);
1219# endif
1220#endif
1221}
1222/*
1223**  HOLDSIGS -- arrange to hold all signals
1224**
1225**	Parameters:
1226**		none.
1227**
1228**	Returns:
1229**		none.
1230**
1231**	Side Effects:
1232**		Arranges that signals are held.
1233*/
1234
1235void
1236holdsigs()
1237{
1238}
1239/*
1240**  RLSESIGS -- arrange to release all signals
1241**
1242**	This undoes the effect of holdsigs.
1243**
1244**	Parameters:
1245**		none.
1246**
1247**	Returns:
1248**		none.
1249**
1250**	Side Effects:
1251**		Arranges that signals are released.
1252*/
1253
1254void
1255rlsesigs()
1256{
1257}
1258/*
1259**  INIT_MD -- do machine dependent initializations
1260**
1261**	Systems that have global modes that should be set should do
1262**	them here rather than in main.
1263*/
1264
1265#ifdef _AUX_SOURCE
1266# include <compat.h>
1267#endif
1268
1269#if SHARE_V1
1270# include <shares.h>
1271#endif
1272
1273void
1274init_md(argc, argv)
1275	int argc;
1276	char **argv;
1277{
1278#ifdef _AUX_SOURCE
1279	setcompat(getcompat() | COMPAT_BSDPROT);
1280#endif
1281
1282#ifdef SUN_EXTENSIONS
1283	init_md_sun();
1284#endif
1285
1286#if _CONVEX_SOURCE
1287	/* keep gethostby*() from stripping the local domain name */
1288	set_domain_trim_off();
1289#endif
1290#ifdef __QNX__
1291	/*
1292	**  Due to QNX's network distributed nature, you can target a tcpip
1293	**  stack on a different node in the qnx network; this patch lets
1294	**  this feature work.  The __sock_locate() must be done before the
1295	**  environment is clear.
1296	*/
1297	__sock_locate();
1298#endif
1299#if SECUREWARE || defined(_SCO_unix_)
1300	set_auth_parameters(argc, argv);
1301
1302# ifdef _SCO_unix_
1303	/*
1304	**  This is required for highest security levels (the kernel
1305	**  won't let it call set*uid() or run setuid binaries without
1306	**  it).  It may be necessary on other SECUREWARE systems.
1307	*/
1308
1309	if (getluid() == -1)
1310		setluid(0);
1311# endif
1312#endif
1313
1314#ifdef VENDOR_DEFAULT
1315	VendorCode = VENDOR_DEFAULT;
1316#else
1317	VendorCode = VENDOR_BERKELEY;
1318#endif
1319}
1320/*
1321**  INIT_VENDOR_MACROS -- vendor-dependent macro initializations
1322**
1323**	Called once, on startup.
1324**
1325**	Parameters:
1326**		e -- the global envelope.
1327**
1328**	Returns:
1329**		none.
1330**
1331**	Side Effects:
1332**		vendor-dependent.
1333*/
1334
1335void
1336init_vendor_macros(e)
1337	register ENVELOPE *e;
1338{
1339}
1340/*
1341**  GETLA -- get the current load average
1342**
1343**	This code stolen from la.c.
1344**
1345**	Parameters:
1346**		none.
1347**
1348**	Returns:
1349**		The current load average as an integer.
1350**
1351**	Side Effects:
1352**		none.
1353*/
1354
1355/* try to guess what style of load average we have */
1356#define LA_ZERO		1	/* always return load average as zero */
1357#define LA_INT		2	/* read kmem for avenrun; interpret as long */
1358#define LA_FLOAT	3	/* read kmem for avenrun; interpret as float */
1359#define LA_SUBR		4	/* call getloadavg */
1360#define LA_MACH		5	/* MACH load averages (as on NeXT boxes) */
1361#define LA_SHORT	6	/* read kmem for avenrun; interpret as short */
1362#define LA_PROCSTR	7	/* read string ("1.17") from /proc/loadavg */
1363#define LA_READKSYM	8	/* SVR4: use MIOC_READKSYM ioctl call */
1364#define LA_DGUX		9	/* special DGUX implementation */
1365#define LA_HPUX		10	/* special HPUX implementation */
1366#define LA_IRIX6	11	/* special IRIX 6.2 implementation */
1367#define LA_KSTAT	12	/* special Solaris kstat(3k) implementation */
1368#define LA_DEVSHORT	13	/* read short from a device */
1369#define LA_ALPHAOSF	14	/* Digital UNIX (OSF/1 on Alpha) table() call */
1370
1371/* do guesses based on general OS type */
1372#ifndef LA_TYPE
1373# define LA_TYPE	LA_ZERO
1374#endif
1375
1376#ifndef FSHIFT
1377# if defined(unixpc)
1378#  define FSHIFT	5
1379# endif
1380
1381# if defined(__alpha) || defined(IRIX)
1382#  define FSHIFT	10
1383# endif
1384
1385#endif
1386
1387#ifndef FSHIFT
1388# define FSHIFT		8
1389#endif
1390
1391#ifndef FSCALE
1392# define FSCALE		(1 << FSHIFT)
1393#endif
1394
1395#ifndef LA_AVENRUN
1396# ifdef SYSTEM5
1397#  define LA_AVENRUN	"avenrun"
1398# else
1399#  define LA_AVENRUN	"_avenrun"
1400# endif
1401#endif
1402
1403/* _PATH_KMEM should be defined in <paths.h> */
1404#ifndef _PATH_KMEM
1405# define _PATH_KMEM	"/dev/kmem"
1406#endif
1407
1408#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT)
1409
1410#include <nlist.h>
1411
1412/* _PATH_UNIX should be defined in <paths.h> */
1413#ifndef _PATH_UNIX
1414# if defined(SYSTEM5)
1415#  define _PATH_UNIX	"/unix"
1416# else
1417#  define _PATH_UNIX	"/vmunix"
1418# endif
1419#endif
1420
1421#ifdef _AUX_SOURCE
1422struct nlist	Nl[2];
1423#else
1424struct nlist	Nl[] =
1425{
1426	{ LA_AVENRUN },
1427	{ 0 },
1428};
1429#endif
1430#define	X_AVENRUN	0
1431
1432int
1433getla()
1434{
1435	static int kmem = -1;
1436#if LA_TYPE == LA_INT
1437	long avenrun[3];
1438#else
1439# if LA_TYPE == LA_SHORT
1440	short avenrun[3];
1441# else
1442	double avenrun[3];
1443# endif
1444#endif
1445	extern int errno;
1446	extern off_t lseek();
1447
1448	if (kmem < 0)
1449	{
1450#ifdef _AUX_SOURCE
1451		strcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN);
1452		Nl[1].n_name[0] = '\0';
1453#endif
1454
1455#if defined(_AIX3) || defined(_AIX4)
1456		if (knlist(Nl, 1, sizeof Nl[0]) < 0)
1457#else
1458		if (nlist(_PATH_UNIX, Nl) < 0)
1459#endif
1460		{
1461			if (tTd(3, 1))
1462				printf("getla: nlist(%s): %s\n", _PATH_UNIX,
1463					errstring(errno));
1464			return (-1);
1465		}
1466		if (Nl[X_AVENRUN].n_value == 0)
1467		{
1468			if (tTd(3, 1))
1469				printf("getla: nlist(%s, %s) ==> 0\n",
1470					_PATH_UNIX, LA_AVENRUN);
1471			return (-1);
1472		}
1473#ifdef NAMELISTMASK
1474		Nl[X_AVENRUN].n_value &= NAMELISTMASK;
1475#endif
1476
1477		kmem = open(_PATH_KMEM, 0, 0);
1478		if (kmem < 0)
1479		{
1480			if (tTd(3, 1))
1481				printf("getla: open(/dev/kmem): %s\n",
1482					errstring(errno));
1483			return (-1);
1484		}
1485		(void) fcntl(kmem, F_SETFD, 1);
1486	}
1487	if (tTd(3, 20))
1488		printf("getla: symbol address = %#lx\n",
1489			(u_long) Nl[X_AVENRUN].n_value);
1490	if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 ||
1491	    read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
1492	{
1493		/* thank you Ian */
1494		if (tTd(3, 1))
1495			printf("getla: lseek or read: %s\n", errstring(errno));
1496		return (-1);
1497	}
1498# if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)
1499	if (tTd(3, 5))
1500	{
1501#  if LA_TYPE == LA_SHORT
1502		printf("getla: avenrun = %d", avenrun[0]);
1503		if (tTd(3, 15))
1504			printf(", %d, %d", avenrun[1], avenrun[2]);
1505#  else
1506		printf("getla: avenrun = %ld", avenrun[0]);
1507		if (tTd(3, 15))
1508			printf(", %ld, %ld", avenrun[1], avenrun[2]);
1509#  endif
1510		printf("\n");
1511	}
1512	if (tTd(3, 1))
1513		printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1514	return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1515# else /* LA_TYPE == LA_FLOAT */
1516	if (tTd(3, 5))
1517	{
1518		printf("getla: avenrun = %g", avenrun[0]);
1519		if (tTd(3, 15))
1520			printf(", %g, %g", avenrun[1], avenrun[2]);
1521		printf("\n");
1522	}
1523	if (tTd(3, 1))
1524		printf("getla: %d\n", (int) (avenrun[0] +0.5));
1525	return ((int) (avenrun[0] + 0.5));
1526# endif
1527}
1528
1529#endif /* LA_TYPE == LA_INT or LA_SHORT or LA_FLOAT */
1530
1531#if LA_TYPE == LA_READKSYM
1532
1533# include <sys/ksym.h>
1534
1535getla()
1536{
1537	static int kmem = -1;
1538	long avenrun[3];
1539	extern int errno;
1540	struct mioc_rksym mirk;
1541
1542	if (kmem < 0)
1543	{
1544		kmem = open("/dev/kmem", 0, 0);
1545		if (kmem < 0)
1546		{
1547			if (tTd(3, 1))
1548				printf("getla: open(/dev/kmem): %s\n",
1549					errstring(errno));
1550			return (-1);
1551		}
1552		(void) fcntl(kmem, F_SETFD, 1);
1553	}
1554	mirk.mirk_symname = LA_AVENRUN;
1555	mirk.mirk_buf = avenrun;
1556	mirk.mirk_buflen = sizeof(avenrun);
1557	if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0)
1558	{
1559		if (tTd(3, 1))
1560			printf("getla: ioctl(MIOC_READKSYM) failed: %s\n",
1561				errstring(errno));
1562		return -1;
1563	}
1564	if (tTd(3, 5))
1565	{
1566		printf("getla: avenrun = %d", avenrun[0]);
1567		if (tTd(3, 15))
1568			printf(", %d, %d", avenrun[1], avenrun[2]);
1569		printf("\n");
1570	}
1571	if (tTd(3, 1))
1572		printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1573	return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1574}
1575
1576#endif /* LA_TYPE == LA_READKSYM */
1577
1578#if LA_TYPE == LA_DGUX
1579
1580# include <sys/dg_sys_info.h>
1581
1582int
1583getla()
1584{
1585	struct dg_sys_info_load_info load_info;
1586
1587	dg_sys_info((long *)&load_info,
1588		DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0);
1589
1590	if (tTd(3, 1))
1591		printf("getla: %d\n", (int) (load_info.one_minute + 0.5));
1592
1593	return((int) (load_info.one_minute + 0.5));
1594}
1595
1596#endif /* LA_TYPE == LA_DGUX */
1597
1598#if LA_TYPE == LA_HPUX
1599
1600/* forward declarations to keep gcc from complaining */
1601struct pst_dynamic;
1602struct pst_status;
1603struct pst_static;
1604struct pst_vminfo;
1605struct pst_diskinfo;
1606struct pst_processor;
1607struct pst_lv;
1608struct pst_swapinfo;
1609
1610# include <sys/param.h>
1611# include <sys/pstat.h>
1612
1613int
1614getla()
1615{
1616	struct pst_dynamic pstd;
1617
1618	if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic),
1619			     (size_t) 1, 0) == -1)
1620		return 0;
1621
1622	if (tTd(3, 1))
1623		printf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5));
1624
1625	return (int) (pstd.psd_avg_1_min + 0.5);
1626}
1627
1628#endif /* LA_TYPE == LA_HPUX */
1629
1630#if LA_TYPE == LA_SUBR
1631
1632int
1633getla()
1634{
1635	double avenrun[3];
1636
1637	if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0)
1638	{
1639		if (tTd(3, 1))
1640			perror("getla: getloadavg failed:");
1641		return (-1);
1642	}
1643	if (tTd(3, 1))
1644		printf("getla: %d\n", (int) (avenrun[0] +0.5));
1645	return ((int) (avenrun[0] + 0.5));
1646}
1647
1648#endif /* LA_TYPE == LA_SUBR */
1649
1650#if LA_TYPE == LA_MACH
1651
1652/*
1653**  This has been tested on NEXTSTEP release 2.1/3.X.
1654*/
1655
1656#if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0
1657# include <mach/mach.h>
1658#else
1659# include <mach.h>
1660#endif
1661
1662int
1663getla()
1664{
1665	processor_set_t default_set;
1666	kern_return_t error;
1667	unsigned int info_count;
1668	struct processor_set_basic_info info;
1669	host_t host;
1670
1671	error = processor_set_default(host_self(), &default_set);
1672	if (error != KERN_SUCCESS)
1673	{
1674		if (tTd(3, 1))
1675			perror("getla: processor_set_default failed:");
1676		return -1;
1677	}
1678	info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
1679	if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
1680			       &host, (processor_set_info_t)&info,
1681			       &info_count) != KERN_SUCCESS)
1682	{
1683		if (tTd(3, 1))
1684			perror("getla: processor_set_info failed:");
1685		return -1;
1686	}
1687	if (tTd(3, 1))
1688		printf("getla: %d\n", (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE);
1689	return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE;
1690}
1691
1692#endif /* LA_TYPE == LA_MACH */
1693
1694#if LA_TYPE == LA_PROCSTR
1695
1696/*
1697**  Read /proc/loadavg for the load average.  This is assumed to be
1698**  in a format like "0.15 0.12 0.06".
1699**
1700**	Initially intended for Linux.  This has been in the kernel
1701**	since at least 0.99.15.
1702*/
1703
1704# ifndef _PATH_LOADAVG
1705#  define _PATH_LOADAVG	"/proc/loadavg"
1706# endif
1707
1708int
1709getla()
1710{
1711	double avenrun;
1712	register int result;
1713	FILE *fp;
1714
1715	fp = fopen(_PATH_LOADAVG, "r");
1716	if (fp == NULL)
1717	{
1718		if (tTd(3, 1))
1719			printf("getla: fopen(%s): %s\n",
1720				_PATH_LOADAVG, errstring(errno));
1721		return -1;
1722	}
1723	result = fscanf(fp, "%lf", &avenrun);
1724	fclose(fp);
1725	if (result != 1)
1726	{
1727		if (tTd(3, 1))
1728			printf("getla: fscanf() = %d: %s\n",
1729				result, errstring(errno));
1730		return -1;
1731	}
1732
1733	if (tTd(3, 1))
1734		printf("getla(): %.2f\n", avenrun);
1735
1736	return ((int) (avenrun + 0.5));
1737}
1738
1739#endif /* LA_TYPE == LA_PROCSTR */
1740
1741#if LA_TYPE == LA_IRIX6
1742#include <sys/sysmp.h>
1743
1744int getla(void)
1745{
1746	static int kmem = -1;
1747	int avenrun[3];
1748
1749	if (kmem < 0)
1750	{
1751		kmem = open(_PATH_KMEM, 0, 0);
1752		if (kmem < 0)
1753		{
1754			if (tTd(3, 1))
1755				printf("getla: open(%s): %s\n", _PATH_KMEM,
1756					errstring(errno));
1757			return -1;
1758		}
1759		(void) fcntl(kmem, F_SETFD, 1);
1760	}
1761
1762	if (lseek(kmem, (sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff), SEEK_SET) == -1 ||
1763	    read(kmem, (char *)avenrun, sizeof(avenrun)) < sizeof(avenrun))
1764	{
1765		if (tTd(3, 1))
1766			printf("getla: lseek or read: %s\n",
1767			       errstring(errno));
1768		return -1;
1769	}
1770	if (tTd(3, 5))
1771	{
1772		printf("getla: avenrun = %ld", (long int) avenrun[0]);
1773		if (tTd(3, 15))
1774			printf(", %ld, %ld",
1775			       (long int) avenrun[1], (long int) avenrun[2]);
1776		printf("\n");
1777	}
1778
1779	if (tTd(3, 1))
1780		printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1781	return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1782
1783}
1784#endif
1785
1786#if LA_TYPE == LA_KSTAT
1787
1788#include <kstat.h>
1789
1790int
1791getla()
1792{
1793	static kstat_ctl_t *kc = NULL;
1794	static kstat_t *ksp = NULL;
1795	kstat_named_t *ksn;
1796	int la;
1797
1798	if (kc == NULL)		/* if not initialized before */
1799		kc = kstat_open();
1800	if (kc == NULL)
1801	{
1802		if (tTd(3, 1))
1803			printf("getla: kstat_open(): %s\n",
1804				errstring(errno));
1805		return -1;
1806	}
1807	if (ksp == NULL)
1808		ksp = kstat_lookup(kc, "unix", 0, "system_misc");
1809	if (ksp == NULL)
1810	{
1811		if (tTd(3, 1))
1812			printf("getla: kstat_lookup(): %s\n",
1813				errstring(errno));
1814		return -1;
1815	}
1816	if (kstat_read(kc, ksp, NULL) < 0)
1817	{
1818		if (tTd(3, 1))
1819			printf("getla: kstat_read(): %s\n",
1820				errstring(errno));
1821		return -1;
1822	}
1823	ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min");
1824	la = ((double)ksn->value.ul + FSCALE/2) / FSCALE;
1825	/* kstat_close(kc); /o do not close for fast access */
1826	return la;
1827}
1828
1829#endif /* LA_TYPE == LA_KSTAT */
1830
1831#if LA_TYPE == LA_DEVSHORT
1832
1833/*
1834**  Read /dev/table/avenrun for the load average.  This should contain
1835**  three shorts for the 1, 5, and 15 minute loads.  We only read the
1836**  first, since that's all we care about.
1837**
1838**	Intended for SCO OpenServer 5.
1839*/
1840
1841# ifndef _PATH_AVENRUN
1842#  define _PATH_AVENRUN	"/dev/table/avenrun"
1843# endif
1844
1845int
1846getla()
1847{
1848	static int afd = -1;
1849	short avenrun;
1850	int loadav;
1851	int r;
1852
1853	errno = EBADF;
1854
1855	if (afd == -1 || lseek(afd, 0L, SEEK_SET) == -1)
1856	{
1857		if (errno != EBADF)
1858			return -1;
1859		afd = open(_PATH_AVENRUN, O_RDONLY|O_SYNC);
1860		if (afd < 0)
1861		{
1862			sm_syslog(LOG_ERR, NOQID,
1863				"can't open %s: %m",
1864				_PATH_AVENRUN);
1865			return -1;
1866		}
1867	}
1868
1869	r = read(afd, &avenrun, sizeof avenrun);
1870
1871	if (tTd(3, 5))
1872		printf("getla: avenrun = %d\n", avenrun);
1873	loadav = (int) (avenrun + FSCALE/2) >> FSHIFT;
1874	if (tTd(3, 1))
1875		printf("getla: %d\n", loadav);
1876	return loadav;
1877}
1878
1879#endif /* LA_TYPE == LA_DEVSHORT */
1880
1881#if LA_TYPE == LA_ALPHAOSF
1882struct rtentry;
1883struct mbuf;
1884# include <sys/table.h>
1885
1886int getla()
1887{
1888	int ave = 0;
1889	struct tbl_loadavg tab;
1890
1891	if (table(TBL_LOADAVG, 0, &tab, 1, sizeof(tab)) == -1)
1892	{
1893		if (tTd(3, 1))
1894			printf("getla: table %s\n", errstring(errno));
1895		return (-1);
1896	}
1897
1898	if (tTd(3, 1))
1899		printf("getla: scale = %d\n", tab.tl_lscale);
1900
1901	if (tab.tl_lscale)
1902		ave = (tab.tl_avenrun.l[0] + (tab.tl_lscale/2)) / tab.tl_lscale;
1903	else
1904		ave = (int) (tab.tl_avenrun.d[0] + 0.5);
1905
1906	if (tTd(3, 1))
1907		printf("getla: %d\n", ave);
1908
1909	return ave;
1910}
1911
1912#endif
1913
1914#if LA_TYPE == LA_ZERO
1915
1916int
1917getla()
1918{
1919	if (tTd(3, 1))
1920		printf("getla: ZERO\n");
1921	return (0);
1922}
1923
1924#endif /* LA_TYPE == LA_ZERO */
1925
1926/*
1927 * Copyright 1989 Massachusetts Institute of Technology
1928 *
1929 * Permission to use, copy, modify, distribute, and sell this software and its
1930 * documentation for any purpose is hereby granted without fee, provided that
1931 * the above copyright notice appear in all copies and that both that
1932 * copyright notice and this permission notice appear in supporting
1933 * documentation, and that the name of M.I.T. not be used in advertising or
1934 * publicity pertaining to distribution of the software without specific,
1935 * written prior permission.  M.I.T. makes no representations about the
1936 * suitability of this software for any purpose.  It is provided "as is"
1937 * without express or implied warranty.
1938 *
1939 * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
1940 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
1941 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1942 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
1943 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1944 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1945 *
1946 * Authors:  Many and varied...
1947 */
1948
1949/* Non Apollo stuff removed by Don Lewis 11/15/93 */
1950#ifndef lint
1951static char  rcsid[] = "@(#)$Id: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $";
1952#endif /* !lint */
1953
1954#ifdef apollo
1955# undef volatile
1956#    include <apollo/base.h>
1957
1958/* ARGSUSED */
1959int getloadavg( call_data )
1960     caddr_t	call_data;	/* pointer to (double) return value */
1961{
1962     double *avenrun = (double *) call_data;
1963     int i;
1964     status_$t      st;
1965     long loadav[3];
1966     proc1_$get_loadav(loadav, &st);
1967     *avenrun = loadav[0] / (double) (1 << 16);
1968     return(0);
1969}
1970#   endif /* apollo */
1971/*
1972**  SHOULDQUEUE -- should this message be queued or sent?
1973**
1974**	Compares the message cost to the load average to decide.
1975**
1976**	Parameters:
1977**		pri -- the priority of the message in question.
1978**		ctime -- the message creation time.
1979**
1980**	Returns:
1981**		TRUE -- if this message should be queued up for the
1982**			time being.
1983**		FALSE -- if the load is low enough to send this message.
1984**
1985**	Side Effects:
1986**		none.
1987*/
1988
1989extern int	get_num_procs_online __P((void));
1990
1991bool
1992shouldqueue(pri, ctime)
1993	long pri;
1994	time_t ctime;
1995{
1996	bool rval;
1997	int queuela = QueueLA * get_num_procs_online();
1998
1999	if (tTd(3, 30))
2000		printf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri);
2001	if (CurrentLA < queuela)
2002	{
2003		if (tTd(3, 30))
2004			printf("FALSE (CurrentLA < QueueLA)\n");
2005		return (FALSE);
2006	}
2007#if 0	/* this code is reported to cause oscillation around RefuseLA */
2008	if (CurrentLA >= RefuseLA && QueueLA < RefuseLA)
2009	{
2010		if (tTd(3, 30))
2011			printf("TRUE (CurrentLA >= RefuseLA)\n");
2012		return (TRUE);
2013	}
2014#endif
2015	rval = pri > (QueueFactor / (CurrentLA - queuela + 1));
2016	if (tTd(3, 30))
2017		printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE");
2018	return rval;
2019}
2020/*
2021**  REFUSECONNECTIONS -- decide if connections should be refused
2022**
2023**	Parameters:
2024**		port -- port number (for error messages only)
2025**
2026**	Returns:
2027**		TRUE if incoming SMTP connections should be refused
2028**			(for now).
2029**		FALSE if we should accept new work.
2030**
2031**	Side Effects:
2032**		Sets process title when it is rejecting connections.
2033*/
2034
2035bool
2036refuseconnections(port)
2037	int port;
2038{
2039	int refusela = RefuseLA * get_num_procs_online();
2040	time_t now;
2041	static time_t lastconn = (time_t) 0;
2042	static int conncnt = 0;
2043	extern bool enoughdiskspace __P((long));
2044
2045#ifdef XLA
2046	if (!xla_smtp_ok())
2047		return TRUE;
2048#endif
2049
2050	now = curtime();
2051	if (now != lastconn)
2052	{
2053		lastconn = now;
2054		conncnt = 0;
2055	}
2056	else if (conncnt++ > ConnRateThrottle && ConnRateThrottle > 0)
2057	{
2058		/* sleep to flatten out connection load */
2059		sm_setproctitle(TRUE, "deferring connections on port %d: %d per second",
2060			port, ConnRateThrottle);
2061		if (LogLevel >= 14)
2062			sm_syslog(LOG_INFO, NOQID,
2063				"deferring connections on port %d: %d per second",
2064				port, ConnRateThrottle);
2065		sleep(1);
2066	}
2067
2068	CurrentLA = getla();
2069	if (CurrentLA >= refusela)
2070	{
2071		sm_setproctitle(TRUE, "rejecting connections on port %d: load average: %d",
2072			port, CurrentLA);
2073		if (LogLevel >= 14)
2074			sm_syslog(LOG_INFO, NOQID,
2075				"rejecting connections on port %d: load average: %d",
2076				port, CurrentLA);
2077		return TRUE;
2078	}
2079
2080	if (!enoughdiskspace(MinBlocksFree + 1))
2081	{
2082		sm_setproctitle(TRUE, "rejecting connections on port %d: min free: %d",
2083			port, MinBlocksFree);
2084		if (LogLevel >= 14)
2085			sm_syslog(LOG_INFO, NOQID,
2086				"rejecting connections on port %d: min free: %d",
2087				port, MinBlocksFree);
2088		return TRUE;
2089	}
2090
2091	if (MaxChildren > 0 && CurChildren >= MaxChildren)
2092	{
2093		proc_list_probe();
2094		if (CurChildren >= MaxChildren)
2095		{
2096			sm_setproctitle(TRUE, "rejecting connections on port %d: %d children, max %d",
2097				port, CurChildren, MaxChildren);
2098			if (LogLevel >= 14)
2099				sm_syslog(LOG_INFO, NOQID,
2100					"rejecting connections on port %d: %d children, max %d",
2101					port, CurChildren, MaxChildren);
2102			return TRUE;
2103		}
2104	}
2105
2106	return FALSE;
2107}
2108/*
2109**  SETPROCTITLE -- set process title for ps
2110**
2111**	Parameters:
2112**		fmt -- a printf style format string.
2113**		a, b, c -- possible parameters to fmt.
2114**
2115**	Returns:
2116**		none.
2117**
2118**	Side Effects:
2119**		Clobbers argv of our main procedure so ps(1) will
2120**		display the title.
2121*/
2122
2123#define SPT_NONE	0	/* don't use it at all */
2124#define SPT_REUSEARGV	1	/* cover argv with title information */
2125#define SPT_BUILTIN	2	/* use libc builtin */
2126#define SPT_PSTAT	3	/* use pstat(PSTAT_SETCMD, ...) */
2127#define SPT_PSSTRINGS	4	/* use PS_STRINGS->... */
2128#define SPT_SYSMIPS	5	/* use sysmips() supported by NEWS-OS 6 */
2129#define SPT_SCO		6	/* write kernel u. area */
2130#define SPT_CHANGEARGV	7	/* write our own strings into argv[] */
2131
2132#ifndef SPT_TYPE
2133# define SPT_TYPE	SPT_REUSEARGV
2134#endif
2135
2136#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
2137
2138# if SPT_TYPE == SPT_PSTAT
2139#  include <sys/pstat.h>
2140# endif
2141# if SPT_TYPE == SPT_PSSTRINGS
2142#  include <machine/vmparam.h>
2143#  include <sys/exec.h>
2144#  ifndef PS_STRINGS	/* hmmmm....  apparently not available after all */
2145#   undef SPT_TYPE
2146#   define SPT_TYPE	SPT_REUSEARGV
2147#  else
2148#   ifndef NKPDE			/* FreeBSD 2.0 */
2149#    define NKPDE 63
2150typedef unsigned int	*pt_entry_t;
2151#   endif
2152#  endif
2153# endif
2154
2155# if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
2156#  define SETPROC_STATIC	static
2157# else
2158#  define SETPROC_STATIC
2159# endif
2160
2161# if SPT_TYPE == SPT_SYSMIPS
2162#  include <sys/sysmips.h>
2163#  include <sys/sysnews.h>
2164# endif
2165
2166# if SPT_TYPE == SPT_SCO
2167#  include <sys/immu.h>
2168#  include <sys/dir.h>
2169#  include <sys/user.h>
2170#  include <sys/fs/s5param.h>
2171#  if PSARGSZ > MAXLINE
2172#   define SPT_BUFSIZE	PSARGSZ
2173#  endif
2174# endif
2175
2176# ifndef SPT_PADCHAR
2177#  define SPT_PADCHAR	' '
2178# endif
2179
2180#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
2181
2182# ifndef SPT_BUFSIZE
2183#  define SPT_BUFSIZE	MAXLINE
2184# endif
2185
2186/*
2187**  Pointers for setproctitle.
2188**	This allows "ps" listings to give more useful information.
2189*/
2190
2191char		**Argv = NULL;		/* pointer to argument vector */
2192char		*LastArgv = NULL;	/* end of argv */
2193
2194void
2195initsetproctitle(argc, argv, envp)
2196	int argc;
2197	char **argv;
2198	char **envp;
2199{
2200	register int i, envpsize = 0;
2201	extern char **environ;
2202
2203	/*
2204	**  Move the environment so setproctitle can use the space at
2205	**  the top of memory.
2206	*/
2207
2208	for (i = 0; envp[i] != NULL; i++)
2209		envpsize += strlen(envp[i]) + 1;
2210	environ = (char **) xalloc(sizeof (char *) * (i + 1));
2211	for (i = 0; envp[i] != NULL; i++)
2212		environ[i] = newstr(envp[i]);
2213	environ[i] = NULL;
2214
2215	/*
2216	**  Save start and extent of argv for setproctitle.
2217	*/
2218
2219	Argv = argv;
2220
2221	/*
2222	**  Determine how much space we can use for setproctitle.
2223	**  Use all contiguous argv and envp pointers starting at argv[0]
2224 	*/
2225	for (i = 0; i < argc; i++)
2226	{
2227		if (i==0 || LastArgv + 1 == argv[i])
2228			LastArgv = argv[i] + strlen(argv[i]);
2229		else
2230			continue;
2231	}
2232	for (i=0; envp[i] != NULL; i++)
2233	{
2234		if (LastArgv + 1 == envp[i])
2235			LastArgv = envp[i] + strlen(envp[i]);
2236		else
2237			continue;
2238	}
2239}
2240
2241#if SPT_TYPE != SPT_BUILTIN
2242
2243
2244/*VARARGS1*/
2245void
2246# ifdef __STDC__
2247setproctitle(const char *fmt, ...)
2248# else
2249setproctitle(fmt, va_alist)
2250	const char *fmt;
2251	va_dcl
2252# endif
2253{
2254# if SPT_TYPE != SPT_NONE
2255	register char *p;
2256	register int i;
2257	SETPROC_STATIC char buf[SPT_BUFSIZE];
2258	VA_LOCAL_DECL
2259#  if SPT_TYPE == SPT_PSTAT
2260	union pstun pst;
2261#  endif
2262#  if SPT_TYPE == SPT_SCO
2263	off_t seek_off;
2264	static int kmem = -1;
2265	static int kmempid = -1;
2266	struct user u;
2267#  endif
2268
2269	p = buf;
2270
2271	/* print sendmail: heading for grep */
2272	(void) strcpy(p, "sendmail: ");
2273	p += strlen(p);
2274
2275	/* print the argument string */
2276	VA_START(fmt);
2277	(void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
2278	VA_END;
2279
2280	i = strlen(buf);
2281
2282#  if SPT_TYPE == SPT_PSTAT
2283	pst.pst_command = buf;
2284	pstat(PSTAT_SETCMD, pst, i, 0, 0);
2285#  endif
2286#  if SPT_TYPE == SPT_PSSTRINGS
2287	PS_STRINGS->ps_nargvstr = 1;
2288	PS_STRINGS->ps_argvstr = buf;
2289#  endif
2290#  if SPT_TYPE == SPT_SYSMIPS
2291	sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
2292#  endif
2293#  if SPT_TYPE == SPT_SCO
2294	if (kmem < 0 || kmempid != getpid())
2295	{
2296		if (kmem >= 0)
2297			close(kmem);
2298		kmem = open(_PATH_KMEM, O_RDWR, 0);
2299		if (kmem < 0)
2300			return;
2301		(void) fcntl(kmem, F_SETFD, 1);
2302		kmempid = getpid();
2303	}
2304	buf[PSARGSZ - 1] = '\0';
2305	seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u;
2306	if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off)
2307		(void) write(kmem, buf, PSARGSZ);
2308#  endif
2309#  if SPT_TYPE == SPT_REUSEARGV
2310	if (i > LastArgv - Argv[0] - 2)
2311	{
2312		i = LastArgv - Argv[0] - 2;
2313		buf[i] = '\0';
2314	}
2315	(void) strcpy(Argv[0], buf);
2316	p = &Argv[0][i];
2317	while (p < LastArgv)
2318		*p++ = SPT_PADCHAR;
2319	Argv[1] = NULL;
2320#  endif
2321#  if SPT_TYPE == SPT_CHANGEARGV
2322	Argv[0] = buf;
2323	Argv[1] = 0;
2324#  endif
2325# endif /* SPT_TYPE != SPT_NONE */
2326}
2327
2328#endif /* SPT_TYPE != SPT_BUILTIN */
2329/*
2330**  SM_SETPROCTITLE -- set process task and set process title for ps
2331**
2332**	Possibly set process status and call setproctitle() to
2333**	change the ps display.
2334**
2335**	Parameters:
2336**		status -- whether or not to store as process status
2337**		fmt -- a printf style format string.
2338**		a, b, c -- possible parameters to fmt.
2339**
2340**	Returns:
2341**		none.
2342*/
2343
2344/*VARARGS2*/
2345void
2346# ifdef __STDC__
2347sm_setproctitle(bool status, const char *fmt, ...)
2348# else
2349sm_setproctitle(status, fmt, va_alist)
2350	bool status;
2351	const char *fmt;
2352	va_dcl
2353#endif
2354{
2355	char buf[SPT_BUFSIZE];
2356
2357	VA_LOCAL_DECL
2358	/* print the argument string */
2359	VA_START(fmt);
2360	(void) vsnprintf(buf, SPT_BUFSIZE, fmt, ap);
2361	VA_END;
2362
2363	if (status)
2364		proc_list_set(getpid(), buf);
2365	setproctitle("%s", buf);
2366}
2367/*
2368**  WAITFOR -- wait for a particular process id.
2369**
2370**	Parameters:
2371**		pid -- process id to wait for.
2372**
2373**	Returns:
2374**		status of pid.
2375**		-1 if pid never shows up.
2376**
2377**	Side Effects:
2378**		none.
2379*/
2380
2381int
2382waitfor(pid)
2383	pid_t pid;
2384{
2385#ifdef WAITUNION
2386	union wait st;
2387#else
2388	auto int st;
2389#endif
2390	pid_t i;
2391#if defined(ISC_UNIX) || defined(_SCO_unix_)
2392	int savesig;
2393#endif
2394
2395	do
2396	{
2397		errno = 0;
2398#if defined(ISC_UNIX) || defined(_SCO_unix_)
2399		savesig = releasesignal(SIGCHLD);
2400#endif
2401		i = wait(&st);
2402#if defined(ISC_UNIX) || defined(_SCO_unix_)
2403		if (savesig > 0)
2404			blocksignal(SIGCHLD);
2405#endif
2406		if (i > 0)
2407			proc_list_drop(i);
2408	} while ((i >= 0 || errno == EINTR) && i != pid);
2409	if (i < 0)
2410		return -1;
2411#ifdef WAITUNION
2412	return st.w_status;
2413#else
2414	return st;
2415#endif
2416}
2417/*
2418**  REAPCHILD -- pick up the body of my child, lest it become a zombie
2419**
2420**	Parameters:
2421**		sig -- the signal that got us here (unused).
2422**
2423**	Returns:
2424**		none.
2425**
2426**	Side Effects:
2427**		Picks up extant zombies.
2428*/
2429
2430SIGFUNC_DECL
2431reapchild(sig)
2432	int sig;
2433{
2434	int olderrno = errno;
2435	pid_t pid;
2436# ifdef HASWAITPID
2437	auto int status;
2438	int count;
2439
2440	count = 0;
2441	while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
2442	{
2443		if (count++ > 1000)
2444		{
2445			if (LogLevel > 0)
2446				sm_syslog(LOG_ALERT, NOQID,
2447					"reapchild: waitpid loop: pid=%d, status=%x",
2448					pid, status);
2449			break;
2450		}
2451		proc_list_drop(pid);
2452	}
2453# else
2454# ifdef WNOHANG
2455	union wait status;
2456
2457	while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0)
2458		proc_list_drop(pid);
2459# else /* WNOHANG */
2460	auto int status;
2461
2462	/*
2463	**  Catch one zombie -- we will be re-invoked (we hope) if there
2464	**  are more.  Unreliable signals probably break this, but this
2465	**  is the "old system" situation -- waitpid or wait3 are to be
2466	**  strongly preferred.
2467	*/
2468
2469	if ((pid = wait(&status)) > 0)
2470		proc_list_drop(pid);
2471# endif /* WNOHANG */
2472# endif
2473# ifdef SYS5SIGNALS
2474	(void) setsignal(SIGCHLD, reapchild);
2475# endif
2476	errno = olderrno;
2477	return SIGFUNC_RETURN;
2478}
2479/*
2480**  PUTENV -- emulation of putenv() in terms of setenv()
2481**
2482**	Not needed on Posix-compliant systems.
2483**	This doesn't have full Posix semantics, but it's good enough
2484**		for sendmail.
2485**
2486**	Parameter:
2487**		env -- the environment to put.
2488**
2489**	Returns:
2490**		none.
2491*/
2492
2493#ifdef NEEDPUTENV
2494
2495# if NEEDPUTENV == 2		/* no setenv(3) call available */
2496
2497int
2498putenv(str)
2499	char *str;
2500{
2501	char **current;
2502	int matchlen, envlen=0;
2503	char *tmp;
2504	char **newenv;
2505	static int first=1;
2506	extern char **environ;
2507
2508	/*
2509	 * find out how much of str to match when searching
2510	 * for a string to replace.
2511	 */
2512	if ((tmp = strchr(str, '=')) == NULL || tmp == str)
2513		matchlen = strlen(str);
2514	else
2515		matchlen = (int) (tmp - str);
2516	++matchlen;
2517
2518	/*
2519	 * Search for an existing string in the environment and find the
2520	 * length of environ.  If found, replace and exit.
2521	 */
2522	for (current=environ; *current; current++) {
2523		++envlen;
2524
2525		if (strncmp(str, *current, matchlen) == 0) {
2526			/* found it, now insert the new version */
2527			*current = (char *)str;
2528			return(0);
2529		}
2530	}
2531
2532	/*
2533	 * There wasn't already a slot so add space for a new slot.
2534	 * If this is our first time through, use malloc(), else realloc().
2535	 */
2536	if (first) {
2537		newenv = (char **) malloc(sizeof(char *) * (envlen + 2));
2538		if (newenv == NULL)
2539			return(-1);
2540
2541		first=0;
2542		(void) memcpy(newenv, environ, sizeof(char *) * envlen);
2543	} else {
2544		newenv = (char **) realloc((char *)environ, sizeof(char *) * (envlen + 2));
2545		if (newenv == NULL)
2546			return(-1);
2547	}
2548
2549	/* actually add in the new entry */
2550	environ = newenv;
2551	environ[envlen] = (char *)str;
2552	environ[envlen+1] = NULL;
2553
2554	return(0);
2555}
2556
2557#else			/* implement putenv() in terms of setenv() */
2558
2559int
2560putenv(env)
2561	char *env;
2562{
2563	char *p;
2564	int l;
2565	char nbuf[100];
2566
2567	p = strchr(env, '=');
2568	if (p == NULL)
2569		return 0;
2570	l = p - env;
2571	if (l > sizeof nbuf - 1)
2572		l = sizeof nbuf - 1;
2573	bcopy(env, nbuf, l);
2574	nbuf[l] = '\0';
2575	return setenv(nbuf, ++p, 1);
2576}
2577
2578# endif
2579#endif
2580/*
2581**  UNSETENV -- remove a variable from the environment
2582**
2583**	Not needed on newer systems.
2584**
2585**	Parameters:
2586**		name -- the string name of the environment variable to be
2587**			deleted from the current environment.
2588**
2589**	Returns:
2590**		none.
2591**
2592**	Globals:
2593**		environ -- a pointer to the current environment.
2594**
2595**	Side Effects:
2596**		Modifies environ.
2597*/
2598
2599#ifndef HASUNSETENV
2600
2601void
2602unsetenv(name)
2603	char *name;
2604{
2605	extern char **environ;
2606	register char **pp;
2607	int len = strlen(name);
2608
2609	for (pp = environ; *pp != NULL; pp++)
2610	{
2611		if (strncmp(name, *pp, len) == 0 &&
2612		    ((*pp)[len] == '=' || (*pp)[len] == '\0'))
2613			break;
2614	}
2615
2616	for (; *pp != NULL; pp++)
2617		*pp = pp[1];
2618}
2619
2620#endif
2621/*
2622**  GETDTABLESIZE -- return number of file descriptors
2623**
2624**	Only on non-BSD systems
2625**
2626**	Parameters:
2627**		none
2628**
2629**	Returns:
2630**		size of file descriptor table
2631**
2632**	Side Effects:
2633**		none
2634*/
2635
2636#ifdef SOLARIS
2637# include <sys/resource.h>
2638#endif
2639
2640int
2641getdtsize()
2642{
2643#ifdef RLIMIT_NOFILE
2644	struct rlimit rl;
2645
2646	if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
2647		return rl.rlim_cur;
2648#endif
2649
2650# ifdef HASGETDTABLESIZE
2651	return getdtablesize();
2652# else
2653#  ifdef _SC_OPEN_MAX
2654	return sysconf(_SC_OPEN_MAX);
2655#  else
2656	return NOFILE;
2657#  endif
2658# endif
2659}
2660/*
2661**  UNAME -- get the UUCP name of this system.
2662*/
2663
2664#ifndef HASUNAME
2665
2666int
2667uname(name)
2668	struct utsname *name;
2669{
2670	FILE *file;
2671	char *n;
2672
2673	name->nodename[0] = '\0';
2674
2675	/* try /etc/whoami -- one line with the node name */
2676	if ((file = fopen("/etc/whoami", "r")) != NULL)
2677	{
2678		(void) fgets(name->nodename, NODE_LENGTH + 1, file);
2679		(void) fclose(file);
2680		n = strchr(name->nodename, '\n');
2681		if (n != NULL)
2682			*n = '\0';
2683		if (name->nodename[0] != '\0')
2684			return (0);
2685	}
2686
2687	/* try /usr/include/whoami.h -- has a #define somewhere */
2688	if ((file = fopen("/usr/include/whoami.h", "r")) != NULL)
2689	{
2690		char buf[MAXLINE];
2691
2692		while (fgets(buf, MAXLINE, file) != NULL)
2693			if (sscanf(buf, "#define sysname \"%*[^\"]\"",
2694					NODE_LENGTH, name->nodename) > 0)
2695				break;
2696		(void) fclose(file);
2697		if (name->nodename[0] != '\0')
2698			return (0);
2699	}
2700
2701#ifdef TRUST_POPEN
2702	/*
2703	**  Popen is known to have security holes.
2704	*/
2705
2706	/* try uuname -l to return local name */
2707	if ((file = popen("uuname -l", "r")) != NULL)
2708	{
2709		(void) fgets(name, NODE_LENGTH + 1, file);
2710		(void) pclose(file);
2711		n = strchr(name, '\n');
2712		if (n != NULL)
2713			*n = '\0';
2714		if (name->nodename[0] != '\0')
2715			return (0);
2716	}
2717#endif
2718
2719	return (-1);
2720}
2721#endif /* HASUNAME */
2722/*
2723**  INITGROUPS -- initialize groups
2724**
2725**	Stub implementation for System V style systems
2726*/
2727
2728#ifndef HASINITGROUPS
2729
2730initgroups(name, basegid)
2731	char *name;
2732	int basegid;
2733{
2734	return 0;
2735}
2736
2737#endif
2738/*
2739**  SETGROUPS -- set group list
2740**
2741**	Stub implementation for systems that don't have group lists
2742*/
2743
2744#ifndef NGROUPS_MAX
2745
2746int
2747setgroups(ngroups, grouplist)
2748	int ngroups;
2749	GIDSET_T grouplist[];
2750{
2751	return 0;
2752}
2753
2754#endif
2755/*
2756**  SETSID -- set session id (for non-POSIX systems)
2757*/
2758
2759#ifndef HASSETSID
2760
2761pid_t
2762setsid __P ((void))
2763{
2764#ifdef TIOCNOTTY
2765	int fd;
2766
2767	fd = open("/dev/tty", O_RDWR, 0);
2768	if (fd >= 0)
2769	{
2770		(void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
2771		(void) close(fd);
2772	}
2773#endif /* TIOCNOTTY */
2774# ifdef SYS5SETPGRP
2775	return setpgrp();
2776# else
2777	return setpgid(0, getpid());
2778# endif
2779}
2780
2781#endif
2782/*
2783**  FSYNC -- dummy fsync
2784*/
2785
2786#ifdef NEEDFSYNC
2787
2788fsync(fd)
2789	int fd;
2790{
2791# ifdef O_SYNC
2792	return fcntl(fd, F_SETFL, O_SYNC);
2793# else
2794	/* nothing we can do */
2795	return 0;
2796# endif
2797}
2798
2799#endif
2800/*
2801**  DGUX_INET_ADDR -- inet_addr for DG/UX
2802**
2803**	Data General DG/UX version of inet_addr returns a struct in_addr
2804**	instead of a long.  This patches things.  Only needed on versions
2805**	prior to 5.4.3.
2806*/
2807
2808#ifdef DGUX_5_4_2
2809
2810#undef inet_addr
2811
2812long
2813dgux_inet_addr(host)
2814	char *host;
2815{
2816	struct in_addr haddr;
2817
2818	haddr = inet_addr(host);
2819	return haddr.s_addr;
2820}
2821
2822#endif
2823/*
2824**  GETOPT -- for old systems or systems with bogus implementations
2825*/
2826
2827#ifdef NEEDGETOPT
2828
2829/*
2830 * Copyright (c) 1985 Regents of the University of California.
2831 * All rights reserved.  The Berkeley software License Agreement
2832 * specifies the terms and conditions for redistribution.
2833 */
2834
2835
2836/*
2837**  this version hacked to add `atend' flag to allow state machine
2838**  to reset if invoked by the program to scan args for a 2nd time
2839*/
2840
2841#if defined(LIBC_SCCS) && !defined(lint)
2842static char sccsid[] = "@(#)getopt.c	4.3 (Berkeley) 3/9/86";
2843#endif /* LIBC_SCCS and not lint */
2844
2845#include <stdio.h>
2846
2847/*
2848 * get option letter from argument vector
2849 */
2850#ifdef _CONVEX_SOURCE
2851extern int	optind, opterr, optopt;
2852extern char	*optarg;
2853#else
2854int	opterr = 1;		/* if error message should be printed */
2855int	optind = 1;		/* index into parent argv vector */
2856int	optopt = 0;		/* character checked for validity */
2857char	*optarg = NULL;		/* argument associated with option */
2858#endif
2859
2860#define BADCH	(int)'?'
2861#define EMSG	""
2862#define tell(s)	if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \
2863		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);}
2864
2865int
2866getopt(nargc,nargv,ostr)
2867	int		nargc;
2868	char *const	*nargv;
2869	const char	*ostr;
2870{
2871	static char	*place = EMSG;	/* option letter processing */
2872	static char	atend = 0;
2873	register char	*oli = NULL;	/* option letter list index */
2874
2875	if (atend) {
2876		atend = 0;
2877		place = EMSG;
2878	}
2879	if(!*place) {			/* update scanning pointer */
2880		if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) {
2881			atend++;
2882			return -1;
2883		}
2884		if (*place == '-') {	/* found "--" */
2885			++optind;
2886			atend++;
2887			return -1;
2888		}
2889	}				/* option letter okay? */
2890	if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) {
2891		if (!*place) ++optind;
2892		tell(": illegal option -- ");
2893	}
2894	if (oli && *++oli != ':') {		/* don't need argument */
2895		optarg = NULL;
2896		if (!*place) ++optind;
2897	}
2898	else {				/* need an argument */
2899		if (*place) optarg = place;	/* no white space */
2900		else if (nargc <= ++optind) {	/* no arg */
2901			place = EMSG;
2902			tell(": option requires an argument -- ");
2903		}
2904	 	else optarg = nargv[optind];	/* white space */
2905		place = EMSG;
2906		++optind;
2907	}
2908	return(optopt);			/* dump back option letter */
2909}
2910
2911#endif
2912/*
2913**  VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version
2914*/
2915
2916#ifdef NEEDVPRINTF
2917
2918#define MAXARG	16
2919
2920vfprintf(fp, fmt, ap)
2921	FILE *fp;
2922	char *fmt;
2923	char **ap;
2924{
2925	char *bp[MAXARG];
2926	int i = 0;
2927
2928	while (*ap && i < MAXARG)
2929		bp[i++] = *ap++;
2930	fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3],
2931			 bp[4], bp[5], bp[6], bp[7],
2932			 bp[8], bp[9], bp[10], bp[11],
2933			 bp[12], bp[13], bp[14], bp[15]);
2934}
2935
2936vsprintf(s, fmt, ap)
2937	char *s;
2938	char *fmt;
2939	char **ap;
2940{
2941	char *bp[MAXARG];
2942	int i = 0;
2943
2944	while (*ap && i < MAXARG)
2945		bp[i++] = *ap++;
2946	sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3],
2947			bp[4], bp[5], bp[6], bp[7],
2948			bp[8], bp[9], bp[10], bp[11],
2949			bp[12], bp[13], bp[14], bp[15]);
2950}
2951
2952#endif
2953/*
2954**  USERSHELLOK -- tell if a user's shell is ok for unrestricted use
2955**
2956**	Parameters:
2957**		user -- the name of the user we are checking.
2958**		shell -- the user's shell from /etc/passwd
2959**
2960**	Returns:
2961**		TRUE -- if it is ok to use this for unrestricted access.
2962**		FALSE -- if the shell is restricted.
2963*/
2964
2965#if !HASGETUSERSHELL
2966
2967# ifndef _PATH_SHELLS
2968#  define _PATH_SHELLS	"/etc/shells"
2969# endif
2970
2971# if defined(_AIX3) || defined(_AIX4)
2972#  include <userconf.h>
2973#  if _AIX4 >= 40200
2974#   include <userpw.h>
2975#  endif
2976#  include <usersec.h>
2977# endif
2978
2979char	*DefaultUserShells[] =
2980{
2981	"/bin/sh",		/* standard shell */
2982	"/usr/bin/sh",
2983	"/bin/csh",		/* C shell */
2984	"/usr/bin/csh",
2985#ifdef __hpux
2986# ifdef V4FS
2987	"/usr/bin/rsh",		/* restricted Bourne shell */
2988	"/usr/bin/ksh",		/* Korn shell */
2989	"/usr/bin/rksh",	/* restricted Korn shell */
2990	"/usr/bin/pam",
2991	"/usr/bin/keysh",	/* key shell (extended Korn shell) */
2992	"/usr/bin/posix/sh",
2993# else
2994	"/bin/rsh",		/* restricted Bourne shell */
2995	"/bin/ksh",		/* Korn shell */
2996	"/bin/rksh",		/* restricted Korn shell */
2997	"/bin/pam",
2998	"/usr/bin/keysh",	/* key shell (extended Korn shell) */
2999	"/bin/posix/sh",
3000# endif
3001#endif
3002#if defined(_AIX3) || defined(_AIX4)
3003	"/bin/ksh",		/* Korn shell */
3004	"/usr/bin/ksh",
3005	"/bin/tsh",		/* trusted shell */
3006	"/usr/bin/tsh",
3007	"/bin/bsh",		/* Bourne shell */
3008	"/usr/bin/bsh",
3009#endif
3010#if defined(__svr4__) || defined(__svr5__)
3011	"/bin/ksh",		/* Korn shell */
3012	"/usr/bin/ksh",
3013#endif
3014#ifdef sgi
3015	"/sbin/sh",		/* SGI's shells really live in /sbin */
3016	"/sbin/csh",
3017	"/bin/ksh",		/* Korn shell */
3018	"/sbin/ksh",
3019	"/usr/bin/ksh",
3020	"/bin/tcsh",		/* Extended csh */
3021	"/usr/bin/tcsh",
3022#endif
3023	NULL
3024};
3025
3026#endif
3027
3028#define WILDCARD_SHELL	"/SENDMAIL/ANY/SHELL/"
3029
3030bool
3031usershellok(user, shell)
3032	char *user;
3033	char *shell;
3034{
3035#if HASGETUSERSHELL
3036	register char *p;
3037	extern char *getusershell();
3038
3039	if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
3040	    ConfigLevel <= 1)
3041		return TRUE;
3042
3043	setusershell();
3044	while ((p = getusershell()) != NULL)
3045		if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0)
3046			break;
3047	endusershell();
3048	return p != NULL;
3049#else
3050# if USEGETCONFATTR
3051	auto char *v;
3052# endif
3053	register FILE *shellf;
3054	char buf[MAXLINE];
3055
3056	if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
3057	    ConfigLevel <= 1)
3058		return TRUE;
3059
3060# if USEGETCONFATTR
3061	/*
3062	**  Naturally IBM has a "better" idea.....
3063	**
3064	**	What a crock.  This interface isn't documented, it is
3065	**	considered part of the security library (-ls), and it
3066	**	only works if you are running as root (since the list
3067	**	of valid shells is obviously a source of great concern).
3068	**	I recommend that you do NOT define USEGETCONFATTR,
3069	**	especially since you are going to have to set up an
3070	**	/etc/shells anyhow to handle the cases where getconfattr
3071	**	fails.
3072	*/
3073
3074	if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL)
3075	{
3076		while (*v != '\0')
3077		{
3078			if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0)
3079				return TRUE;
3080			v += strlen(v) + 1;
3081		}
3082		return FALSE;
3083	}
3084# endif
3085
3086	shellf = fopen(_PATH_SHELLS, "r");
3087	if (shellf == NULL)
3088	{
3089		/* no /etc/shells; see if it is one of the std shells */
3090		char **d;
3091
3092		if (errno != ENOENT && LogLevel > 3)
3093			sm_syslog(LOG_ERR, NOQID,
3094				  "usershellok: cannot open %s: %s",
3095				  _PATH_SHELLS, errstring(errno));
3096
3097		for (d = DefaultUserShells; *d != NULL; d++)
3098		{
3099			if (strcmp(shell, *d) == 0)
3100				return TRUE;
3101		}
3102		return FALSE;
3103	}
3104
3105	while (fgets(buf, sizeof buf, shellf) != NULL)
3106	{
3107		register char *p, *q;
3108
3109		p = buf;
3110		while (*p != '\0' && *p != '#' && *p != '/')
3111			p++;
3112		if (*p == '#' || *p == '\0')
3113			continue;
3114		q = p;
3115		while (*p != '\0' && *p != '#' && !(isascii(*p) && isspace(*p)))
3116			p++;
3117		*p = '\0';
3118		if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0)
3119		{
3120			fclose(shellf);
3121			return TRUE;
3122		}
3123	}
3124	fclose(shellf);
3125	return FALSE;
3126#endif
3127}
3128/*
3129**  FREEDISKSPACE -- see how much free space is on the queue filesystem
3130**
3131**	Only implemented if you have statfs.
3132**
3133**	Parameters:
3134**		dir -- the directory in question.
3135**		bsize -- a variable into which the filesystem
3136**			block size is stored.
3137**
3138**	Returns:
3139**		The number of bytes free on the queue filesystem.
3140**		-1 if the statfs call fails.
3141**
3142**	Side effects:
3143**		Puts the filesystem block size into bsize.
3144*/
3145
3146/* statfs types */
3147#define SFS_NONE	0	/* no statfs implementation */
3148#define SFS_USTAT	1	/* use ustat */
3149#define SFS_4ARGS	2	/* use four-argument statfs call */
3150#define SFS_VFS		3	/* use <sys/vfs.h> implementation */
3151#define SFS_MOUNT	4	/* use <sys/mount.h> implementation */
3152#define SFS_STATFS	5	/* use <sys/statfs.h> implementation */
3153#define SFS_STATVFS	6	/* use <sys/statvfs.h> implementation */
3154
3155#ifndef SFS_TYPE
3156# define SFS_TYPE	SFS_NONE
3157#endif
3158
3159#if SFS_TYPE == SFS_USTAT
3160# include <ustat.h>
3161#endif
3162#if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
3163# include <sys/statfs.h>
3164#endif
3165#if SFS_TYPE == SFS_VFS
3166# include <sys/vfs.h>
3167#endif
3168#if SFS_TYPE == SFS_MOUNT
3169# include <sys/mount.h>
3170#endif
3171#if SFS_TYPE == SFS_STATVFS
3172# include <sys/statvfs.h>
3173#endif
3174
3175long
3176freediskspace(dir, bsize)
3177	char *dir;
3178	long *bsize;
3179{
3180#if SFS_TYPE != SFS_NONE
3181# if SFS_TYPE == SFS_USTAT
3182	struct ustat fs;
3183	struct stat statbuf;
3184#  define FSBLOCKSIZE	DEV_BSIZE
3185#  define SFS_BAVAIL	f_tfree
3186# else
3187#  if defined(ultrix)
3188	struct fs_data fs;
3189#   define SFS_BAVAIL	fd_bfreen
3190#   define FSBLOCKSIZE	1024L
3191#  else
3192#   if SFS_TYPE == SFS_STATVFS
3193	struct statvfs fs;
3194#    define FSBLOCKSIZE	fs.f_frsize
3195#   else
3196	struct statfs fs;
3197#    define FSBLOCKSIZE	fs.f_bsize
3198#   endif
3199#  endif
3200# endif
3201# ifndef SFS_BAVAIL
3202#  define SFS_BAVAIL f_bavail
3203# endif
3204
3205# if SFS_TYPE == SFS_USTAT
3206	if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
3207# else
3208#  if SFS_TYPE == SFS_4ARGS
3209	if (statfs(dir, &fs, sizeof fs, 0) == 0)
3210#  else
3211#   if SFS_TYPE == SFS_STATVFS
3212	if (statvfs(dir, &fs) == 0)
3213#   else
3214#    if defined(ultrix)
3215	if (statfs(dir, &fs) > 0)
3216#    else
3217	if (statfs(dir, &fs) == 0)
3218#    endif
3219#   endif
3220#  endif
3221# endif
3222	{
3223		if (bsize != NULL)
3224			*bsize = FSBLOCKSIZE;
3225		if (fs.SFS_BAVAIL <= 0)
3226			return 0;
3227		else if (fs.SFS_BAVAIL > LONG_MAX)
3228			return LONG_MAX;
3229		else
3230			return (long) fs.SFS_BAVAIL;
3231	}
3232#endif
3233	return (-1);
3234}
3235/*
3236**  ENOUGHDISKSPACE -- is there enough free space on the queue fs?
3237**
3238**	Only implemented if you have statfs.
3239**
3240**	Parameters:
3241**		msize -- the size to check against.  If zero, we don't yet
3242**		know how big the message will be, so just check for
3243**		a "reasonable" amount.
3244**
3245**	Returns:
3246**		TRUE if there is enough space.
3247**		FALSE otherwise.
3248*/
3249
3250bool
3251enoughdiskspace(msize)
3252	long msize;
3253{
3254	long bfree, bsize;
3255
3256	if (MinBlocksFree <= 0 && msize <= 0)
3257	{
3258		if (tTd(4, 80))
3259			printf("enoughdiskspace: no threshold\n");
3260		return TRUE;
3261	}
3262
3263	if ((bfree = freediskspace(QueueDir, &bsize)) >= 0)
3264	{
3265		if (tTd(4, 80))
3266			printf("enoughdiskspace: bavail=%ld, need=%ld\n",
3267				bfree, msize);
3268
3269		/* convert msize to block count */
3270		msize = msize / bsize + 1;
3271		if (MinBlocksFree >= 0)
3272			msize += MinBlocksFree;
3273
3274		if (bfree < msize)
3275		{
3276			if (LogLevel > 0)
3277				sm_syslog(LOG_ALERT, CurEnv->e_id,
3278					"low on space (have %ld, %s needs %ld in %s)",
3279					bfree,
3280					CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
3281					msize, QueueDir);
3282			return FALSE;
3283		}
3284	}
3285	else if (tTd(4, 80))
3286		printf("enoughdiskspace failure: min=%ld, need=%ld: %s\n",
3287			MinBlocksFree, msize, errstring(errno));
3288	return TRUE;
3289}
3290/*
3291**  TRANSIENTERROR -- tell if an error code indicates a transient failure
3292**
3293**	This looks at an errno value and tells if this is likely to
3294**	go away if retried later.
3295**
3296**	Parameters:
3297**		err -- the errno code to classify.
3298**
3299**	Returns:
3300**		TRUE if this is probably transient.
3301**		FALSE otherwise.
3302*/
3303
3304bool
3305transienterror(err)
3306	int err;
3307{
3308	switch (err)
3309	{
3310	  case EIO:			/* I/O error */
3311	  case ENXIO:			/* Device not configured */
3312	  case EAGAIN:			/* Resource temporarily unavailable */
3313	  case ENOMEM:			/* Cannot allocate memory */
3314	  case ENODEV:			/* Operation not supported by device */
3315	  case ENFILE:			/* Too many open files in system */
3316	  case EMFILE:			/* Too many open files */
3317	  case ENOSPC:			/* No space left on device */
3318#ifdef ETIMEDOUT
3319	  case ETIMEDOUT:		/* Connection timed out */
3320#endif
3321#ifdef ESTALE
3322	  case ESTALE:			/* Stale NFS file handle */
3323#endif
3324#ifdef ENETDOWN
3325	  case ENETDOWN:		/* Network is down */
3326#endif
3327#ifdef ENETUNREACH
3328	  case ENETUNREACH:		/* Network is unreachable */
3329#endif
3330#ifdef ENETRESET
3331	  case ENETRESET:		/* Network dropped connection on reset */
3332#endif
3333#ifdef ECONNABORTED
3334	  case ECONNABORTED:		/* Software caused connection abort */
3335#endif
3336#ifdef ECONNRESET
3337	  case ECONNRESET:		/* Connection reset by peer */
3338#endif
3339#ifdef ENOBUFS
3340	  case ENOBUFS:			/* No buffer space available */
3341#endif
3342#ifdef ESHUTDOWN
3343	  case ESHUTDOWN:		/* Can't send after socket shutdown */
3344#endif
3345#ifdef ECONNREFUSED
3346	  case ECONNREFUSED:		/* Connection refused */
3347#endif
3348#ifdef EHOSTDOWN
3349	  case EHOSTDOWN:		/* Host is down */
3350#endif
3351#ifdef EHOSTUNREACH
3352	  case EHOSTUNREACH:		/* No route to host */
3353#endif
3354#ifdef EDQUOT
3355	  case EDQUOT:			/* Disc quota exceeded */
3356#endif
3357#ifdef EPROCLIM
3358	  case EPROCLIM:		/* Too many processes */
3359#endif
3360#ifdef EUSERS
3361	  case EUSERS:			/* Too many users */
3362#endif
3363#ifdef EDEADLK
3364	  case EDEADLK:			/* Resource deadlock avoided */
3365#endif
3366#ifdef EISCONN
3367	  case EISCONN:			/* Socket already connected */
3368#endif
3369#ifdef EINPROGRESS
3370	  case EINPROGRESS:		/* Operation now in progress */
3371#endif
3372#ifdef EALREADY
3373	  case EALREADY:		/* Operation already in progress */
3374#endif
3375#ifdef EADDRINUSE
3376	  case EADDRINUSE:		/* Address already in use */
3377#endif
3378#ifdef EADDRNOTAVAIL
3379	  case EADDRNOTAVAIL:		/* Can't assign requested address */
3380#endif
3381#ifdef ETXTBSY
3382	  case ETXTBSY:			/* (Apollo) file locked */
3383#endif
3384#if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
3385	  case ENOSR:			/* Out of streams resources */
3386#endif
3387	  case E_SM_OPENTIMEOUT:	/* PSEUDO: open timed out */
3388		return TRUE;
3389	}
3390
3391	/* nope, must be permanent */
3392	return FALSE;
3393}
3394/*
3395**  LOCKFILE -- lock a file using flock or (shudder) fcntl locking
3396**
3397**	Parameters:
3398**		fd -- the file descriptor of the file.
3399**		filename -- the file name (for error messages).
3400**		ext -- the filename extension.
3401**		type -- type of the lock.  Bits can be:
3402**			LOCK_EX -- exclusive lock.
3403**			LOCK_NB -- non-blocking.
3404**
3405**	Returns:
3406**		TRUE if the lock was acquired.
3407**		FALSE otherwise.
3408*/
3409
3410bool
3411lockfile(fd, filename, ext, type)
3412	int fd;
3413	char *filename;
3414	char *ext;
3415	int type;
3416{
3417	int i;
3418	int save_errno;
3419# if !HASFLOCK
3420	int action;
3421	struct flock lfd;
3422
3423	if (ext == NULL)
3424		ext = "";
3425
3426	bzero(&lfd, sizeof lfd);
3427	if (bitset(LOCK_UN, type))
3428		lfd.l_type = F_UNLCK;
3429	else if (bitset(LOCK_EX, type))
3430		lfd.l_type = F_WRLCK;
3431	else
3432		lfd.l_type = F_RDLCK;
3433
3434	if (bitset(LOCK_NB, type))
3435		action = F_SETLK;
3436	else
3437		action = F_SETLKW;
3438
3439	if (tTd(55, 60))
3440		printf("lockfile(%s%s, action=%d, type=%d): ",
3441			filename, ext, action, lfd.l_type);
3442
3443	while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
3444		continue;
3445	if (i >= 0)
3446	{
3447		if (tTd(55, 60))
3448			printf("SUCCESS\n");
3449		return TRUE;
3450	}
3451	save_errno = errno;
3452
3453	if (tTd(55, 60))
3454		printf("(%s) ", errstring(save_errno));
3455
3456	/*
3457	**  On SunOS, if you are testing using -oQ/tmp/mqueue or
3458	**  -oA/tmp/aliases or anything like that, and /tmp is mounted
3459	**  as type "tmp" (that is, served from swap space), the
3460	**  previous fcntl will fail with "Invalid argument" errors.
3461	**  Since this is fairly common during testing, we will assume
3462	**  that this indicates that the lock is successfully grabbed.
3463	*/
3464
3465	if (save_errno == EINVAL)
3466	{
3467		if (tTd(55, 60))
3468			printf("SUCCESS\n");
3469		return TRUE;
3470	}
3471
3472	if (!bitset(LOCK_NB, type) || (save_errno != EACCES && save_errno != EAGAIN))
3473	{
3474		int omode = -1;
3475#  ifdef F_GETFL
3476		(void) fcntl(fd, F_GETFL, &omode);
3477		errno = save_errno;
3478#  endif
3479		syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
3480			filename, ext, fd, type, omode, geteuid());
3481		dumpfd(fd, TRUE, TRUE);
3482	}
3483# else
3484	if (ext == NULL)
3485		ext = "";
3486
3487	if (tTd(55, 60))
3488		printf("lockfile(%s%s, type=%o): ", filename, ext, type);
3489
3490	while ((i = flock(fd, type)) < 0 && errno == EINTR)
3491		continue;
3492	if (i >= 0)
3493	{
3494		if (tTd(55, 60))
3495			printf("SUCCESS\n");
3496		return TRUE;
3497	}
3498	save_errno = errno;
3499
3500	if (tTd(55, 60))
3501		printf("(%s) ", errstring(save_errno));
3502
3503	if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
3504	{
3505		int omode = -1;
3506#  ifdef F_GETFL
3507		(void) fcntl(fd, F_GETFL, &omode);
3508		errno = save_errno;
3509#  endif
3510		syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
3511			filename, ext, fd, type, omode, geteuid());
3512		dumpfd(fd, TRUE, TRUE);
3513	}
3514# endif
3515	if (tTd(55, 60))
3516		printf("FAIL\n");
3517	errno = save_errno;
3518	return FALSE;
3519}
3520/*
3521**  CHOWNSAFE -- tell if chown is "safe" (executable only by root)
3522**
3523**	Unfortunately, given that we can't predict other systems on which
3524**	a remote mounted (NFS) filesystem will be mounted, the answer is
3525**	almost always that this is unsafe.
3526**
3527**	Note also that many operating systems have non-compliant
3528**	implementations of the _POSIX_CHOWN_RESTRICTED variable and the
3529**	fpathconf() routine.  According to IEEE 1003.1-1990, if
3530**	_POSIX_CHOWN_RESTRICTED is defined and not equal to -1, then
3531**	no non-root process can give away the file.  However, vendors
3532**	don't take NFS into account, so a comfortable value of
3533**	_POSIX_CHOWN_RESTRICTED tells us nothing.
3534**
3535**	Also, some systems (e.g., IRIX 6.2) return 1 from fpathconf()
3536**	even on files where chown is not restricted.  Many systems get
3537**	this wrong on NFS-based filesystems (that is, they say that chown
3538**	is restricted [safe] on NFS filesystems where it may not be, since
3539**	other systems can access the same filesystem and do file giveaway;
3540**	only the NFS server knows for sure!)  Hence, it is important to
3541**	get the value of SAFENFSPATHCONF correct -- it should be defined
3542**	_only_ after testing (see test/t_pathconf.c) a system on an unsafe
3543**	NFS-based filesystem to ensure that you can get meaningful results.
3544**	If in doubt, assume unsafe!
3545**
3546**	You may also need to tweak IS_SAFE_CHOWN -- it should be a
3547**	condition indicating whether the return from pathconf indicates
3548**	that chown is safe (typically either > 0 or >= 0 -- there isn't
3549**	even any agreement about whether a zero return means that a file
3550**	is or is not safe).  It defaults to "> 0".
3551**
3552**	If the parent directory is safe (writable only by owner back
3553**	to the root) then we can relax slightly and trust fpathconf
3554**	in more circumstances.  This is really a crock -- if this is an
3555**	NFS mounted filesystem then we really know nothing about the
3556**	underlying implementation.  However, most systems pessimize and
3557**	return an error (EINVAL or EOPNOTSUPP) on NFS filesystems, which
3558**	we interpret as unsafe, as we should.  Thus, this heuristic gets
3559**	us into a possible problem only on systems that have a broken
3560**	pathconf implementation and which are also poorly configured
3561**	(have :include: files in group- or world-writable directories).
3562**
3563**	Parameters:
3564**		fd -- the file descriptor to check.
3565**		safedir -- set if the parent directory is safe.
3566**
3567**	Returns:
3568**		TRUE -- if the chown(2) operation is "safe" -- that is,
3569**			only root can chown the file to an arbitrary user.
3570**		FALSE -- if an arbitrary user can give away a file.
3571*/
3572
3573#ifndef IS_SAFE_CHOWN
3574# define IS_SAFE_CHOWN	> 0
3575#endif
3576
3577bool
3578chownsafe(fd, safedir)
3579	int fd;
3580	bool safedir;
3581{
3582#if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
3583    (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
3584	int rval;
3585
3586	/* give the system administrator a chance to override */
3587	if (bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail))
3588		return TRUE;
3589
3590	/*
3591	**  Some systems (e.g., SunOS) seem to have the call and the
3592	**  #define _PC_CHOWN_RESTRICTED, but don't actually implement
3593	**  the call.  This heuristic checks for that.
3594	*/
3595
3596	errno = 0;
3597	rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
3598# if SAFENFSPATHCONF
3599	return errno == 0 && rval IS_SAFE_CHOWN;
3600# else
3601	return safedir && errno == 0 && rval IS_SAFE_CHOWN;
3602# endif
3603#else
3604	return bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail);
3605#endif
3606}
3607/*
3608**  RESETLIMITS -- reset system controlled resource limits
3609**
3610**	This is to avoid denial-of-service attacks
3611**
3612**	Parameters:
3613**		none
3614**
3615**	Returns:
3616**		none
3617*/
3618
3619#if HASSETRLIMIT
3620# ifdef RLIMIT_NEEDS_SYS_TIME_H
3621#  include <sys/time.h>
3622# endif
3623# include <sys/resource.h>
3624#endif
3625#ifndef FD_SETSIZE
3626# define FD_SETSIZE	256
3627#endif
3628
3629void
3630resetlimits()
3631{
3632#if HASSETRLIMIT
3633	struct rlimit lim;
3634
3635	lim.rlim_cur = lim.rlim_max = RLIM_INFINITY;
3636	(void) setrlimit(RLIMIT_CPU, &lim);
3637	(void) setrlimit(RLIMIT_FSIZE, &lim);
3638# ifdef RLIMIT_NOFILE
3639	lim.rlim_cur = lim.rlim_max = FD_SETSIZE;
3640	(void) setrlimit(RLIMIT_NOFILE, &lim);
3641# endif
3642#else
3643# if HASULIMIT
3644	(void) ulimit(2, 0x3fffff);
3645	(void) ulimit(4, FD_SETSIZE);
3646# endif
3647#endif
3648	errno = 0;
3649}
3650/*
3651**  GETCFNAME -- return the name of the .cf file.
3652**
3653**	Some systems (e.g., NeXT) determine this dynamically.
3654*/
3655
3656char *
3657getcfname()
3658{
3659
3660	if (ConfFile != NULL)
3661		return ConfFile;
3662#if NETINFO
3663	{
3664		extern char *ni_propval __P((char *, char *, char *, char *, int));
3665		char *cflocation;
3666
3667		cflocation = ni_propval("/locations", NULL, "sendmail",
3668					"sendmail.cf", '\0');
3669		if (cflocation != NULL)
3670			return cflocation;
3671	}
3672#endif
3673
3674	return _PATH_SENDMAILCF;
3675}
3676/*
3677**  SETVENDOR -- process vendor code from V configuration line
3678**
3679**	Parameters:
3680**		vendor -- string representation of vendor.
3681**
3682**	Returns:
3683**		TRUE -- if ok.
3684**		FALSE -- if vendor code could not be processed.
3685**
3686**	Side Effects:
3687**		It is reasonable to set mode flags here to tweak
3688**		processing in other parts of the code if necessary.
3689**		For example, if you are a vendor that uses $%y to
3690**		indicate YP lookups, you could enable that here.
3691*/
3692
3693bool
3694setvendor(vendor)
3695	char *vendor;
3696{
3697	if (strcasecmp(vendor, "Berkeley") == 0)
3698	{
3699		VendorCode = VENDOR_BERKELEY;
3700		return TRUE;
3701	}
3702
3703	/* add vendor extensions here */
3704
3705#ifdef SUN_EXTENSIONS
3706	if (strcasecmp(vendor, "Sun") == 0)
3707	{
3708		VendorCode = VENDOR_SUN;
3709		return TRUE;
3710	}
3711#endif
3712
3713#if defined(VENDOR_NAME) && defined(VENDOR_CODE)
3714	if (strcasecmp(vendor, VENDOR_NAME) == 0)
3715	{
3716		VendorCode = VENDOR_CODE;
3717		return TRUE;
3718	}
3719#endif
3720
3721	return FALSE;
3722}
3723/*
3724**  GETVENDOR -- return vendor name based on vendor code
3725**
3726**	Parameters:
3727**		vendorcode -- numeric representation of vendor.
3728**
3729**	Returns:
3730**		string containing vendor name.
3731*/
3732
3733char *
3734getvendor(vendorcode)
3735	int vendorcode;
3736{
3737#if defined(VENDOR_NAME) && defined(VENDOR_CODE)
3738	/*
3739	**  Can't have the same switch case twice so need to
3740	**  handle VENDOR_CODE outside of switch.  It might
3741	**  match one of the existing VENDOR_* codes.
3742	*/
3743
3744	if (vendorcode == VENDOR_CODE)
3745		return VENDOR_NAME;
3746#endif
3747
3748	switch (vendorcode)
3749	{
3750		case VENDOR_BERKELEY:
3751			return "Berkeley";
3752
3753		case VENDOR_SUN:
3754			return "Sun";
3755
3756		case VENDOR_HP:
3757			return "HP";
3758
3759		case VENDOR_IBM:
3760			return "IBM";
3761
3762		case VENDOR_SENDMAIL:
3763			return "Sendmail";
3764
3765		default:
3766			return "Unknown";
3767	}
3768}
3769/*
3770**  VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults
3771**
3772**	Vendor_pre_defaults is called before reading the configuration
3773**	file; vendor_post_defaults is called immediately after.
3774**
3775**	Parameters:
3776**		e -- the global environment to initialize.
3777**
3778**	Returns:
3779**		none.
3780*/
3781
3782#if SHARE_V1
3783int	DefShareUid;	/* default share uid to run as -- unused??? */
3784#endif
3785
3786void
3787vendor_pre_defaults(e)
3788	ENVELOPE *e;
3789{
3790#if SHARE_V1
3791	/* OTHERUID is defined in shares.h, do not be alarmed */
3792	DefShareUid = OTHERUID;
3793#endif
3794#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
3795	sun_pre_defaults(e);
3796#endif
3797#ifdef apollo
3798	/* stupid domain/os can't even open /etc/sendmail.cf without this */
3799	setuserenv("ISP", NULL);
3800	setuserenv("SYSTYPE", NULL);
3801#endif
3802}
3803
3804
3805void
3806vendor_post_defaults(e)
3807	ENVELOPE *e;
3808{
3809#ifdef __QNX__
3810	char *p;
3811
3812	/* Makes sure the SOCK environment variable remains */
3813	if (p = getextenv("SOCK"))
3814		setuserenv("SOCK", p);
3815#endif
3816#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
3817	sun_post_defaults(e);
3818#endif
3819}
3820/*
3821**  VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode
3822*/
3823
3824void
3825vendor_daemon_setup(e)
3826	ENVELOPE *e;
3827{
3828#if SECUREWARE
3829	if (getluid() != -1)
3830	{
3831		usrerr("Daemon cannot have LUID");
3832		finis(FALSE, EX_USAGE);
3833	}
3834#endif /* SECUREWARE */
3835}
3836/*
3837**  VENDOR_SET_UID -- do setup for setting a user id
3838**
3839**	This is called when we are still root.
3840**
3841**	Parameters:
3842**		uid -- the uid we are about to become.
3843**
3844**	Returns:
3845**		none.
3846*/
3847
3848void
3849vendor_set_uid(uid)
3850	UID_T uid;
3851{
3852	/*
3853	**  We need to setup the share groups (lnodes)
3854	**  and and auditing inforation (luid's)
3855	**  before we loose our ``root''ness.
3856	*/
3857#if SHARE_V1
3858	if (setupshares(uid, syserr) != 0)
3859		syserr("Unable to set up shares");
3860#endif
3861#if SECUREWARE
3862	(void) setup_secure(uid);
3863#endif
3864}
3865/*
3866**  VALIDATE_CONNECTION -- check connection for rationality
3867**
3868**	If the connection is rejected, this routine should log an
3869**	appropriate message -- but should never issue any SMTP protocol.
3870**
3871**	Parameters:
3872**		sap -- a pointer to a SOCKADDR naming the peer.
3873**		hostname -- the name corresponding to sap.
3874**		e -- the current envelope.
3875**
3876**	Returns:
3877**		error message from rejection.
3878**		NULL if not rejected.
3879*/
3880
3881#if TCPWRAPPERS
3882# include <tcpd.h>
3883
3884/* tcpwrappers does no logging, but you still have to declare these -- ugh */
3885int	allow_severity	= LOG_INFO;
3886int	deny_severity	= LOG_NOTICE;
3887#endif
3888
3889#if DAEMON
3890char *
3891validate_connection(sap, hostname, e)
3892	SOCKADDR *sap;
3893	char *hostname;
3894	ENVELOPE *e;
3895{
3896#if TCPWRAPPERS
3897	char *host;
3898#endif
3899
3900	if (tTd(48, 3))
3901		printf("validate_connection(%s, %s)\n",
3902			hostname, anynet_ntoa(sap));
3903
3904	if (rscheck("check_relay", hostname, anynet_ntoa(sap), e) != EX_OK)
3905	{
3906		static char reject[BUFSIZ*2];
3907		extern char MsgBuf[];
3908
3909		if (tTd(48, 4))
3910			printf("  ... validate_connection: BAD (rscheck)\n");
3911
3912		if (strlen(MsgBuf) > 5)
3913		{
3914			if (isascii(MsgBuf[0]) && isdigit(MsgBuf[0]) &&
3915			    isascii(MsgBuf[1]) && isdigit(MsgBuf[1]) &&
3916			    isascii(MsgBuf[2]) && isdigit(MsgBuf[2]))
3917				strcpy(reject, &MsgBuf[4]);
3918			else
3919				strcpy(reject, MsgBuf);
3920		}
3921		else
3922			strcpy(reject, "Access denied");
3923
3924		return reject;
3925	}
3926
3927#if TCPWRAPPERS
3928	if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']')
3929		host = "unknown";
3930	else
3931		host = hostname;
3932	if (!hosts_ctl("sendmail", host, anynet_ntoa(sap), STRING_UNKNOWN))
3933	{
3934		if (tTd(48, 4))
3935			printf("  ... validate_connection: BAD (tcpwrappers)\n");
3936		if (LogLevel >= 4)
3937			sm_syslog(LOG_NOTICE, NOQID,
3938				"tcpwrappers (%s, %s) rejection",
3939				host, anynet_ntoa(sap));
3940		return "Access denied";
3941	}
3942#endif
3943	if (tTd(48, 4))
3944		printf("  ... validate_connection: OK\n");
3945	return NULL;
3946}
3947
3948#endif
3949/*
3950**  STRTOL -- convert string to long integer
3951**
3952**	For systems that don't have it in the C library.
3953**
3954**	This is taken verbatim from the 4.4-Lite C library.
3955*/
3956
3957#ifdef NEEDSTRTOL
3958
3959#if defined(LIBC_SCCS) && !defined(lint)
3960static char sccsid[] = "@(#)strtol.c	8.1 (Berkeley) 6/4/93";
3961#endif /* LIBC_SCCS and not lint */
3962
3963/*
3964 * Convert a string to a long integer.
3965 *
3966 * Ignores `locale' stuff.  Assumes that the upper and lower case
3967 * alphabets and digits are each contiguous.
3968 */
3969
3970long
3971strtol(nptr, endptr, base)
3972	const char *nptr;
3973	char **endptr;
3974	register int base;
3975{
3976	register const char *s = nptr;
3977	register unsigned long acc;
3978	register int c;
3979	register unsigned long cutoff;
3980	register int neg = 0, any, cutlim;
3981
3982	/*
3983	 * Skip white space and pick up leading +/- sign if any.
3984	 * If base is 0, allow 0x for hex and 0 for octal, else
3985	 * assume decimal; if base is already 16, allow 0x.
3986	 */
3987	do {
3988		c = *s++;
3989	} while (isspace(c));
3990	if (c == '-') {
3991		neg = 1;
3992		c = *s++;
3993	} else if (c == '+')
3994		c = *s++;
3995	if ((base == 0 || base == 16) &&
3996	    c == '0' && (*s == 'x' || *s == 'X')) {
3997		c = s[1];
3998		s += 2;
3999		base = 16;
4000	}
4001	if (base == 0)
4002		base = c == '0' ? 8 : 10;
4003
4004	/*
4005	 * Compute the cutoff value between legal numbers and illegal
4006	 * numbers.  That is the largest legal value, divided by the
4007	 * base.  An input number that is greater than this value, if
4008	 * followed by a legal input character, is too big.  One that
4009	 * is equal to this value may be valid or not; the limit
4010	 * between valid and invalid numbers is then based on the last
4011	 * digit.  For instance, if the range for longs is
4012	 * [-2147483648..2147483647] and the input base is 10,
4013	 * cutoff will be set to 214748364 and cutlim to either
4014	 * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
4015	 * a value > 214748364, or equal but the next digit is > 7 (or 8),
4016	 * the number is too big, and we will return a range error.
4017	 *
4018	 * Set any if any `digits' consumed; make it negative to indicate
4019	 * overflow.
4020	 */
4021	cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
4022	cutlim = cutoff % (unsigned long)base;
4023	cutoff /= (unsigned long)base;
4024	for (acc = 0, any = 0;; c = *s++) {
4025		if (isdigit(c))
4026			c -= '0';
4027		else if (isalpha(c))
4028			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
4029		else
4030			break;
4031		if (c >= base)
4032			break;
4033		if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
4034			any = -1;
4035		else {
4036			any = 1;
4037			acc *= base;
4038			acc += c;
4039		}
4040	}
4041	if (any < 0) {
4042		acc = neg ? LONG_MIN : LONG_MAX;
4043		errno = ERANGE;
4044	} else if (neg)
4045		acc = -acc;
4046	if (endptr != 0)
4047		*endptr = (char *)(any ? s - 1 : nptr);
4048	return (acc);
4049}
4050
4051#endif
4052/*
4053**  STRSTR -- find first substring in string
4054**
4055**	Parameters:
4056**		big -- the big (full) string.
4057**		little -- the little (sub) string.
4058**
4059**	Returns:
4060**		A pointer to the first instance of little in big.
4061**		big if little is the null string.
4062**		NULL if little is not contained in big.
4063*/
4064
4065#ifdef NEEDSTRSTR
4066
4067char *
4068strstr(big, little)
4069	char *big;
4070	char *little;
4071{
4072	register char *p = big;
4073	int l;
4074
4075	if (*little == '\0')
4076		return big;
4077	l = strlen(little);
4078
4079	while ((p = strchr(p, *little)) != NULL)
4080	{
4081		if (strncmp(p, little, l) == 0)
4082			return p;
4083		p++;
4084	}
4085	return NULL;
4086}
4087
4088#endif
4089/*
4090**  SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
4091**
4092**	Some operating systems have wierd problems with the gethostbyXXX
4093**	routines.  For example, Solaris versions at least through 2.3
4094**	don't properly deliver a canonical h_name field.  This tries to
4095**	work around these problems.
4096*/
4097
4098struct hostent *
4099sm_gethostbyname(name)
4100	char *name;
4101{
4102	struct hostent *h;
4103#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
4104# if SOLARIS == 20300 || SOLARIS == 203
4105	static struct hostent hp;
4106	static char buf[1000];
4107	extern struct hostent *_switch_gethostbyname_r();
4108
4109	if (tTd(61, 10))
4110		printf("_switch_gethostbyname_r(%s)... ", name);
4111	h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
4112# else
4113	extern struct hostent *__switch_gethostbyname();
4114
4115	if (tTd(61, 10))
4116		printf("__switch_gethostbyname(%s)... ", name);
4117	h = __switch_gethostbyname(name);
4118# endif
4119#else
4120	int nmaps;
4121	char *maptype[MAXMAPSTACK];
4122	short mapreturn[MAXMAPACTIONS];
4123	char hbuf[MAXNAME];
4124
4125	if (tTd(61, 10))
4126		printf("gethostbyname(%s)... ", name);
4127	h = gethostbyname(name);
4128	if (h == NULL)
4129	{
4130		if (tTd(61, 10))
4131			printf("failure\n");
4132
4133		nmaps = switch_map_find("hosts", maptype, mapreturn);
4134		while (--nmaps >= 0)
4135			if (strcmp(maptype[nmaps], "nis") == 0 ||
4136			    strcmp(maptype[nmaps], "files") == 0)
4137				break;
4138		if (nmaps >= 0)
4139		{
4140			/* try short name */
4141			if (strlen(name) > (SIZE_T) sizeof hbuf - 1)
4142				return NULL;
4143			strcpy(hbuf, name);
4144			shorten_hostname(hbuf);
4145
4146			/* if it hasn't been shortened, there's no point */
4147			if (strcmp(hbuf, name) != 0)
4148			{
4149				if (tTd(61, 10))
4150					printf("gethostbyname(%s)... ", hbuf);
4151				h = gethostbyname(hbuf);
4152			}
4153		}
4154	}
4155#endif
4156	if (tTd(61, 10))
4157	{
4158		if (h == NULL)
4159			printf("failure\n");
4160		else
4161			printf("%s\n", h->h_name);
4162	}
4163	return h;
4164}
4165
4166struct hostent *
4167sm_gethostbyaddr(addr, len, type)
4168	char *addr;
4169	int len;
4170	int type;
4171{
4172#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204)
4173# if SOLARIS == 20300 || SOLARIS == 203
4174	static struct hostent hp;
4175	static char buf[1000];
4176	extern struct hostent *_switch_gethostbyaddr_r();
4177
4178	return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno);
4179# else
4180	extern struct hostent *__switch_gethostbyaddr();
4181
4182	return __switch_gethostbyaddr(addr, len, type);
4183# endif
4184#else
4185	return gethostbyaddr(addr, len, type);
4186#endif
4187}
4188/*
4189**  SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
4190*/
4191
4192struct passwd *
4193sm_getpwnam(user)
4194	char *user;
4195{
4196#ifdef _AIX4
4197	extern struct passwd *_getpwnam_shadow(const char *, const int);
4198
4199	return _getpwnam_shadow(user, 0);
4200#else
4201	return getpwnam(user);
4202#endif
4203}
4204
4205struct passwd *
4206sm_getpwuid(uid)
4207	UID_T uid;
4208{
4209#if defined(_AIX4) && 0
4210	extern struct passwd *_getpwuid_shadow(const int, const int);
4211
4212	return _getpwuid_shadow(uid,0);
4213#else
4214	return getpwuid(uid);
4215#endif
4216}
4217/*
4218**  SECUREWARE_SETUP_SECURE -- Convex SecureWare setup
4219**
4220**	Set up the trusted computing environment for C2 level security
4221**	under SecureWare.
4222**
4223**	Parameters:
4224**		uid -- uid of the user to initialize in the TCB
4225**
4226**	Returns:
4227**		none
4228**
4229**	Side Effects:
4230**		Initialized the user in the trusted computing base
4231*/
4232
4233#if SECUREWARE
4234
4235# include <sys/security.h>
4236# include <prot.h>
4237
4238void
4239secureware_setup_secure(uid)
4240	UID_T uid;
4241{
4242	int rc;
4243
4244	if (getluid() != -1)
4245		return;
4246
4247	if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN)
4248	{
4249		switch (rc)
4250		{
4251		  case SSI_NO_PRPW_ENTRY:
4252			syserr("No protected passwd entry, uid = %d", uid);
4253			break;
4254
4255		  case SSI_LOCKED:
4256			syserr("Account has been disabled, uid = %d", uid);
4257			break;
4258
4259		  case SSI_RETIRED:
4260			syserr("Account has been retired, uid = %d", uid);
4261			break;
4262
4263		  case SSI_BAD_SET_LUID:
4264			syserr("Could not set LUID, uid = %d", uid);
4265			break;
4266
4267		  case SSI_BAD_SET_PRIVS:
4268			syserr("Could not set kernel privs, uid = %d", uid);
4269
4270		  default:
4271			syserr("Unknown return code (%d) from set_secure_info(%d)",
4272				rc, uid);
4273			break;
4274		}
4275		finis(FALSE, EX_NOPERM);
4276	}
4277}
4278#endif /* SECUREWARE */
4279/*
4280**  ADD_LOCAL_HOST_NAMES -- Add a hostname to class 'w' based on IP address
4281**
4282**	Add hostnames to class 'w' based on the IP address read from
4283**	the network interface.
4284**
4285**	Parameters:
4286**		sa -- a pointer to a SOCKADDR containing the address
4287**
4288**	Returns:
4289**		0 if successful, -1 if host lookup fails.
4290*/
4291
4292int
4293add_hostnames(sa)
4294	SOCKADDR *sa;
4295{
4296	struct hostent *hp;
4297
4298	/* lookup name with IP address */
4299	switch (sa->sa.sa_family)
4300	{
4301		case AF_INET:
4302			hp = sm_gethostbyaddr((char *) &sa->sin.sin_addr,
4303				sizeof(sa->sin.sin_addr), sa->sa.sa_family);
4304			break;
4305
4306		default:
4307#if _FFR_LOG_UNSUPPORTED_FAMILIES
4308			/* XXX: Give warning about unsupported family */
4309			if (LogLevel > 3)
4310				sm_syslog(LOG_WARNING, NOQID,
4311					  "Unsupported address family %d: %.100s",
4312					  sa->sa.sa_family, anynet_ntoa(sa));
4313#endif
4314			return -1;
4315	}
4316
4317	if (hp == NULL)
4318	{
4319		int save_errno = errno;
4320
4321		if (LogLevel > 3)
4322			sm_syslog(LOG_WARNING, NOQID,
4323				"gethostbyaddr(%.100s) failed: %d\n",
4324				anynet_ntoa(sa),
4325#if NAMED_BIND
4326				h_errno
4327#else
4328				-1
4329#endif
4330				);
4331		errno = save_errno;
4332		return -1;
4333	}
4334
4335	/* save its cname */
4336	if (!wordinclass((char *) hp->h_name, 'w'))
4337	{
4338		setclass('w', (char *) hp->h_name);
4339		if (tTd(0, 4))
4340			printf("\ta.k.a.: %s\n", hp->h_name);
4341	}
4342
4343	/* save all it aliases name */
4344	while (*hp->h_aliases)
4345	{
4346		if (!wordinclass(*hp->h_aliases, 'w'))
4347		{
4348			setclass('w', *hp->h_aliases);
4349			if (tTd(0, 4))
4350				printf("\ta.k.a.: %s\n", *hp->h_aliases);
4351		}
4352		hp->h_aliases++;
4353	}
4354	return 0;
4355}
4356/*
4357**  LOAD_IF_NAMES -- load interface-specific names into $=w
4358**
4359**	Parameters:
4360**		none.
4361**
4362**	Returns:
4363**		none.
4364**
4365**	Side Effects:
4366**		Loads $=w with the names of all the interfaces.
4367*/
4368
4369#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
4370struct rtentry;
4371struct mbuf;
4372# include <arpa/inet.h>
4373# ifndef SUNOS403
4374#  include <sys/time.h>
4375# endif
4376# if _AIX4 >= 40300
4377#  undef __P
4378# endif
4379# include <net/if.h>
4380#endif
4381
4382void
4383load_if_names()
4384{
4385#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
4386	int s;
4387	int i;
4388	struct ifconf ifc;
4389	int numifs;
4390
4391	s = socket(AF_INET, SOCK_DGRAM, 0);
4392	if (s == -1)
4393		return;
4394
4395	/* get the list of known IP address from the kernel */
4396# if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN
4397	if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0)
4398	{
4399		/* can't get number of interfaces -- fall back */
4400		if (tTd(0, 4))
4401			printf("SIOCGIFNUM failed: %s\n", errstring(errno));
4402		numifs = -1;
4403	}
4404	else if (tTd(0, 42))
4405		printf("system has %d interfaces\n", numifs);
4406	if (numifs < 0)
4407# endif
4408		numifs = 512;
4409
4410	if (numifs <= 0)
4411	{
4412		close(s);
4413		return;
4414	}
4415	ifc.ifc_len = numifs * sizeof (struct ifreq);
4416	ifc.ifc_buf = xalloc(ifc.ifc_len);
4417	if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
4418	{
4419		if (tTd(0, 4))
4420			printf("SIOGIFCONF failed: %s\n", errstring(errno));
4421		close(s);
4422		return;
4423	}
4424
4425	/* scan the list of IP address */
4426	if (tTd(0, 40))
4427		printf("scanning for interface specific names, ifc_len=%d\n",
4428			ifc.ifc_len);
4429
4430	for (i = 0; i < ifc.ifc_len; )
4431	{
4432		struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
4433		SOCKADDR *sa = (SOCKADDR *) &ifr->ifr_addr;
4434		struct in_addr ia;
4435#ifdef SIOCGIFFLAGS
4436		struct ifreq ifrf;
4437#endif
4438		char ip_addr[256];
4439		extern char *inet_ntoa();
4440
4441#ifdef BSD4_4_SOCKADDR
4442		if (sa->sa.sa_len > sizeof ifr->ifr_addr)
4443			i += sizeof ifr->ifr_name + sa->sa.sa_len;
4444		else
4445#endif
4446			i += sizeof *ifr;
4447
4448		if (tTd(0, 20))
4449			printf("%s\n", anynet_ntoa(sa));
4450
4451		if (ifr->ifr_addr.sa_family != AF_INET)
4452			continue;
4453
4454#ifdef SIOCGIFFLAGS
4455		bzero(&ifrf, sizeof(struct ifreq));
4456		strncpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name));
4457		ioctl(s, SIOCGIFFLAGS, (char *) &ifrf);
4458		if (tTd(0, 41))
4459			printf("\tflags: %x\n", ifrf.ifr_flags);
4460# define IFRFREF ifrf
4461#else
4462# define IFRFREF (*ifr)
4463#endif
4464		if (!bitset(IFF_UP, IFRFREF.ifr_flags))
4465			continue;
4466
4467		/* extract IP address from the list*/
4468		ia = sa->sin.sin_addr;
4469		if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE)
4470		{
4471			message("WARNING: interface %s is UP with %s address",
4472				ifr->ifr_name, inet_ntoa(ia));
4473			continue;
4474		}
4475
4476		/* save IP address in text from */
4477		(void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
4478			(int)sizeof ip_addr - 3,
4479			inet_ntoa(ia));
4480		if (!wordinclass(ip_addr, 'w'))
4481		{
4482			setclass('w', ip_addr);
4483			if (tTd(0, 4))
4484				printf("\ta.k.a.: %s\n", ip_addr);
4485		}
4486
4487		/* skip "loopback" interface "lo" */
4488		if (bitset(IFF_LOOPBACK, IFRFREF.ifr_flags))
4489			continue;
4490
4491		(void) add_hostnames(sa);
4492	}
4493	free(ifc.ifc_buf);
4494	close(s);
4495# undef IFRFREF
4496#endif
4497}
4498/*
4499**  GET_NUM_PROCS_ONLINE -- return the number of processors currently online
4500**
4501**	Parameters:
4502**		none.
4503**
4504**	Returns:
4505**		The number of processors online.
4506*/
4507
4508int
4509get_num_procs_online()
4510{
4511	int nproc = 0;
4512
4513#if _FFR_SCALE_LA_BY_NUM_PROCS
4514#ifdef _SC_NPROCESSORS_ONLN
4515	nproc = (int) sysconf(_SC_NPROCESSORS_ONLN);
4516#endif
4517#endif
4518	if (nproc <= 0)
4519		nproc = 1;
4520	return nproc;
4521}
4522/*
4523**  SM_SYSLOG -- syslog wrapper to keep messages under SYSLOG_BUFSIZE
4524**
4525**	Parameters:
4526**		level -- syslog level
4527**		id -- envelope ID or NULL (NOQUEUE)
4528**		fmt -- format string
4529**		arg... -- arguments as implied by fmt.
4530**
4531**	Returns:
4532**		none
4533*/
4534
4535/* VARARGS3 */
4536void
4537# ifdef __STDC__
4538sm_syslog(int level, const char *id, const char *fmt, ...)
4539# else
4540sm_syslog(level, id, fmt, va_alist)
4541	int level;
4542	const char *id;
4543	const char *fmt;
4544	va_dcl
4545#endif
4546{
4547	static char *buf = NULL;
4548	static size_t bufsize = MAXLINE;
4549	char *begin, *end;
4550	int seq = 1;
4551	int idlen;
4552	extern int SnprfOverflow;
4553	extern int SyslogErrno;
4554	extern char *DoprEnd;
4555	VA_LOCAL_DECL
4556	extern void sm_dopr __P((char *, const char *, va_list));
4557
4558	SyslogErrno = errno;
4559	if (id == NULL)
4560	{
4561		id = "NOQUEUE";
4562		idlen = 9;
4563	}
4564	else if (strcmp(id, NOQID) == 0)
4565	{
4566		id = "";
4567		idlen = 0;
4568	}
4569	else
4570		idlen = strlen(id + 2);
4571bufalloc:
4572	if (buf == NULL)
4573		buf = (char *) xalloc(sizeof(char) * bufsize);
4574
4575	/* do a virtual vsnprintf into buf */
4576	VA_START(fmt);
4577	buf[0] = 0;
4578	DoprEnd = buf + bufsize - 1;
4579	SnprfOverflow = 0;
4580	sm_dopr(buf, fmt, ap);
4581	*DoprEnd = '\0';
4582	VA_END;
4583	/* end of virtual vsnprintf */
4584
4585	if (SnprfOverflow)
4586	{
4587		/* String too small, redo with correct size */
4588		bufsize += SnprfOverflow + 1;
4589		free(buf);
4590		buf = NULL;
4591		goto bufalloc;
4592	}
4593	if ((strlen(buf) + idlen + 1) < SYSLOG_BUFSIZE)
4594	{
4595#if LOG
4596		if (*id == '\0')
4597			syslog(level, "%s", buf);
4598		else
4599			syslog(level, "%s: %s", id, buf);
4600#else
4601		/*XXX should do something more sensible */
4602		if (*id == '\0')
4603			fprintf(stderr, "%s\n", buf);
4604		else
4605			fprintf(stderr, "%s: %s\n", id, buf);
4606#endif
4607		return;
4608	}
4609
4610	begin = buf;
4611	while (*begin != '\0' &&
4612	       (strlen(begin) + idlen + 5) > SYSLOG_BUFSIZE)
4613	{
4614		char save;
4615
4616		if (seq == 999)
4617		{
4618			/* Too many messages */
4619			break;
4620		}
4621		end = begin + SYSLOG_BUFSIZE - idlen - 12;
4622		while (end > begin)
4623		{
4624			/* Break on comma or space */
4625			if (*end == ',' || *end == ' ')
4626			{
4627				end++;	  /* Include separator */
4628				break;
4629			}
4630			end--;
4631		}
4632		/* No separator, break midstring... */
4633		if (end == begin)
4634			end = begin + SYSLOG_BUFSIZE - idlen - 12;
4635		save = *end;
4636		*end = 0;
4637#if LOG
4638		syslog(level, "%s[%d]: %s ...", id, seq++, begin);
4639#else
4640		fprintf(stderr, "%s[%d]: %s ...\n", id, seq++, begin);
4641#endif
4642		*end = save;
4643		begin = end;
4644	}
4645	if (seq == 999)
4646#if LOG
4647		syslog(level, "%s[%d]: log terminated, too many parts", id, seq);
4648#else
4649		fprintf(stderr, "%s[%d]: log terminated, too many parts\n", id, seq);
4650#endif
4651	else if (*begin != '\0')
4652#if LOG
4653		syslog(level, "%s[%d]: %s", id, seq, begin);
4654#else
4655		fprintf(stderr, "%s[%d]: %s\n", id, seq, begin);
4656#endif
4657}
4658/*
4659**  HARD_SYSLOG -- call syslog repeatedly until it works
4660**
4661**	Needed on HP-UX, which apparently doesn't guarantee that
4662**	syslog succeeds during interrupt handlers.
4663*/
4664
4665#if defined(__hpux) && !defined(HPUX11)
4666
4667# define MAXSYSLOGTRIES	100
4668# undef syslog
4669# ifdef V4FS
4670#  define XCNST	const
4671#  define CAST	(const char *)
4672# else
4673#  define XCNST
4674#  define CAST
4675# endif
4676
4677void
4678# ifdef __STDC__
4679hard_syslog(int pri, XCNST char *msg, ...)
4680# else
4681hard_syslog(pri, msg, va_alist)
4682	int pri;
4683	XCNST char *msg;
4684	va_dcl
4685# endif
4686{
4687	int i;
4688	char buf[SYSLOG_BUFSIZE];
4689	VA_LOCAL_DECL;
4690
4691	VA_START(msg);
4692	vsnprintf(buf, sizeof buf, msg, ap);
4693	VA_END;
4694
4695	for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; )
4696		continue;
4697}
4698
4699# undef CAST
4700#endif
4701/*
4702**  LOCAL_HOSTNAME_LENGTH
4703**
4704**	This is required to get sendmail to compile against BIND 4.9.x
4705**	on Ultrix.
4706*/
4707
4708#if defined(ultrix) && NAMED_BIND
4709
4710# include <resolv.h>
4711# if __RES >= 19931104 && __RES < 19950621
4712
4713int
4714local_hostname_length(hostname)
4715	char *hostname;
4716{
4717	int len_host, len_domain;
4718
4719	if (!*_res.defdname)
4720		res_init();
4721	len_host = strlen(hostname);
4722	len_domain = strlen(_res.defdname);
4723	if (len_host > len_domain &&
4724	    (strcasecmp(hostname + len_host - len_domain,_res.defdname) == 0) &&
4725	    hostname[len_host - len_domain - 1] == '.')
4726		return len_host - len_domain - 1;
4727	else
4728		return 0;
4729}
4730
4731# endif
4732#endif
4733/*
4734**  Compile-Time options
4735*/
4736
4737char	*CompileOptions[] =
4738{
4739#ifdef HESIOD
4740	"HESIOD",
4741#endif
4742#if HES_GETMAILHOST
4743	"HES_GETMAILHOST",
4744#endif
4745#ifdef LDAPMAP
4746	"LDAPMAP",
4747#endif
4748#ifdef MAP_REGEX
4749	"MAP_REGEX",
4750#endif
4751#if LOG
4752	"LOG",
4753#endif
4754#if MATCHGECOS
4755	"MATCHGECOS",
4756#endif
4757#if MIME7TO8
4758	"MIME7TO8",
4759#endif
4760#if MIME8TO7
4761	"MIME8TO7",
4762#endif
4763#if NAMED_BIND
4764	"NAMED_BIND",
4765#endif
4766#ifdef NDBM
4767	"NDBM",
4768#endif
4769#if NETINET
4770	"NETINET",
4771#endif
4772#if NETINFO
4773	"NETINFO",
4774#endif
4775#if NETISO
4776	"NETISO",
4777#endif
4778#if NETNS
4779	"NETNS",
4780#endif
4781#if NETUNIX
4782	"NETUNIX",
4783#endif
4784#if NETX25
4785	"NETX25",
4786#endif
4787#ifdef NEWDB
4788	"NEWDB",
4789#endif
4790#ifdef NIS
4791	"NIS",
4792#endif
4793#ifdef NISPLUS
4794	"NISPLUS",
4795#endif
4796#if QUEUE
4797	"QUEUE",
4798#endif
4799#if SCANF
4800	"SCANF",
4801#endif
4802#if SMTP
4803	"SMTP",
4804#endif
4805#if SMTPDEBUG
4806	"SMTPDEBUG",
4807#endif
4808#ifdef SUID_ROOT_FILES_OK
4809	"SUID_ROOT_FILES_OK",
4810#endif
4811#if TCPWRAPPERS
4812	"TCPWRAPPERS",
4813#endif
4814#if USERDB
4815	"USERDB",
4816#endif
4817#if XDEBUG
4818	"XDEBUG",
4819#endif
4820#ifdef XLA
4821	"XLA",
4822#endif
4823	NULL
4824};
4825
4826
4827/*
4828**  OS compile options.
4829*/
4830
4831char	*OsCompileOptions[] =
4832{
4833#if BOGUS_O_EXCL
4834	"BOGUS_O_EXCL",
4835#endif
4836#if HASFCHMOD
4837	"HASFCHMOD",
4838#endif
4839#if HASFLOCK
4840	"HASFLOCK",
4841#endif
4842#if HASGETDTABLESIZE
4843	"HASGETDTABLESIZE",
4844#endif
4845#if HASGETUSERSHELL
4846	"HASGETUSERSHELL",
4847#endif
4848#if HASINITGROUPS
4849	"HASINITGROUPS",
4850#endif
4851#if HASLSTAT
4852	"HASLSTAT",
4853#endif
4854#if HASSETREUID
4855	"HASSETREUID",
4856#endif
4857#if HASSETRLIMIT
4858	"HASSETRLIMIT",
4859#endif
4860#if HASSETSID
4861	"HASSETSID",
4862#endif
4863#if HASSETUSERCONTEXT
4864	"HASSETUSERCONTEXT",
4865#endif
4866#if HASSETVBUF
4867	"HASSETVBUF",
4868#endif
4869#if HASSNPRINTF
4870	"HASSNPRINTF",
4871#endif
4872#if HAS_ST_GEN
4873	"HAS_ST_GEN",
4874#endif
4875#if HASSTRERROR
4876	"HASSTRERROR",
4877#endif
4878#if HASULIMIT
4879	"HASULIMIT",
4880#endif
4881#if HASUNAME
4882	"HASUNAME",
4883#endif
4884#if HASUNSETENV
4885	"HASUNSETENV",
4886#endif
4887#if HASWAITPID
4888	"HASWAITPID",
4889#endif
4890#if IDENTPROTO
4891	"IDENTPROTO",
4892#endif
4893#if IP_SRCROUTE
4894	"IP_SRCROUTE",
4895#endif
4896#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
4897	"LOCK_ON_OPEN",
4898#endif
4899#if NEEDFSYNC
4900	"NEEDFSYNC",
4901#endif
4902#if NOFTRUNCATE
4903	"NOFTRUNCATE",
4904#endif
4905#if RLIMIT_NEEDS_SYS_TIME_H
4906	"RLIMIT_NEEDS_SYS_TIME_H",
4907#endif
4908#if SAFENFSPATHCONF
4909	"SAFENFSPATHCONF",
4910#endif
4911#if SECUREWARE
4912	"SECUREWARE",
4913#endif
4914#if SHARE_V1
4915	"SHARE_V1",
4916#endif
4917#if SIOCGIFCONF_IS_BROKEN
4918	"SIOCGIFCONF_IS_BROKEN",
4919#endif
4920#if SIOCGIFNUM_IS_BROKEN
4921	"SIOCGIFNUM_IS_BROKEN",
4922#endif
4923#if SYS5SETPGRP
4924	"SYS5SETPGRP",
4925#endif
4926#if SYSTEM5
4927	"SYSTEM5",
4928#endif
4929#if USE_SA_SIGACTION
4930	"USE_SA_SIGACTION",
4931#endif
4932#if USE_SIGLONGJMP
4933	"USE_SIGLONGJMP",
4934#endif
4935#if USESETEUID
4936	"USESETEUID",
4937#endif
4938	NULL
4939};
4940