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