conf.c revision 102533
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 * $FreeBSD: head/contrib/sendmail/src/conf.c 102533 2002-08-28 18:12:33Z gshapiro $
13 *
14 */
15
16#include <sendmail.h>
17
18SM_RCSID("@(#)$Id: conf.c,v 8.972.2.5 2002/08/16 14:56:01 ca Exp $")
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#if _FFR_SPT_ALIGN
2333
2334/*
2335**  It looks like the Compaq Tru64 5.1A now aligns argv and envp to
2336**  64 bit alignment, so unless each piece of argv and envp is a multiple
2337**  of 8 bytes (including terminating NULL), initsetproctitle() won't use
2338**  any of the space beyond argv[0].  Be sure to set SPT_ALIGN_SIZE if
2339**  you use this FFR.
2340*/
2341
2342# ifdef SPT_ALIGN_SIZE
2343#  define SPT_ALIGN(x, align)	(((((x) + SPT_ALIGN_SIZE) >> (align)) << (align)) - 1)
2344# else /* SPT_ALIGN_SIZE */
2345#  define SPT_ALIGN(x, align)	(x)
2346# endif /* SPT_ALIGN_SIZE */
2347#else /* _FFR_SPT_ALIGN */
2348# define SPT_ALIGN(x, align)	(x)
2349#endif /* _FFR_SPT_ALIGN */
2350
2351/*
2352**  Pointers for setproctitle.
2353**	This allows "ps" listings to give more useful information.
2354*/
2355
2356static char	**Argv = NULL;		/* pointer to argument vector */
2357static char	*LastArgv = NULL;	/* end of argv */
2358#if SPT_TYPE != SPT_BUILTIN
2359static void	setproctitle __P((const char *, ...));
2360#endif /* SPT_TYPE != SPT_BUILTIN */
2361
2362void
2363initsetproctitle(argc, argv, envp)
2364	int argc;
2365	char **argv;
2366	char **envp;
2367{
2368	register int i;
2369	int align;
2370	extern char **environ;
2371
2372	/*
2373	**  Move the environment so setproctitle can use the space at
2374	**  the top of memory.
2375	*/
2376
2377	if (envp != NULL)
2378	{
2379		for (i = 0; envp[i] != NULL; i++)
2380			continue;
2381		environ = (char **) xalloc(sizeof (char *) * (i + 1));
2382		for (i = 0; envp[i] != NULL; i++)
2383			environ[i] = newstr(envp[i]);
2384		environ[i] = NULL;
2385	}
2386
2387	/*
2388	**  Save start and extent of argv for setproctitle.
2389	*/
2390
2391	Argv = argv;
2392
2393	/*
2394	**  Determine how much space we can use for setproctitle.
2395	**  Use all contiguous argv and envp pointers starting at argv[0]
2396	*/
2397
2398	align = -1;
2399#if _FFR_SPT_ALIGN
2400# ifdef SPT_ALIGN_SIZE
2401	for (i = SPT_ALIGN_SIZE; i > 0; i >>= 1)
2402		align++;
2403# endif /* SPT_ALIGN_SIZE */
2404#endif /* _FFR_SPT_ALIGN */
2405
2406	for (i = 0; i < argc; i++)
2407	{
2408		if (i == 0 || LastArgv + 1 == argv[i])
2409			LastArgv = argv[i] + SPT_ALIGN(strlen(argv[i]), align);
2410	}
2411	for (i = 0; LastArgv != NULL && envp != NULL && envp[i] != NULL; i++)
2412	{
2413		if (LastArgv + 1 == envp[i])
2414			LastArgv = envp[i] + SPT_ALIGN(strlen(envp[i]), align);
2415	}
2416}
2417
2418#if SPT_TYPE != SPT_BUILTIN
2419
2420/*VARARGS1*/
2421static void
2422# ifdef __STDC__
2423setproctitle(const char *fmt, ...)
2424# else /* __STDC__ */
2425setproctitle(fmt, va_alist)
2426	const char *fmt;
2427	va_dcl
2428# endif /* __STDC__ */
2429{
2430# if SPT_TYPE != SPT_NONE
2431	register int i;
2432	register char *p;
2433	SETPROC_STATIC char buf[SPT_BUFSIZE];
2434	SM_VA_LOCAL_DECL
2435#  if SPT_TYPE == SPT_PSTAT
2436	union pstun pst;
2437#  endif /* SPT_TYPE == SPT_PSTAT */
2438#  if SPT_TYPE == SPT_SCO
2439	int j;
2440	off_t seek_off;
2441	static int kmem = -1;
2442	static pid_t kmempid = -1;
2443	struct user u;
2444#  endif /* SPT_TYPE == SPT_SCO */
2445
2446	p = buf;
2447
2448	/* print sendmail: heading for grep */
2449	(void) sm_strlcpy(p, "sendmail: ", SPACELEFT(buf, p));
2450	p += strlen(p);
2451
2452	/* print the argument string */
2453	SM_VA_START(ap, fmt);
2454	(void) sm_vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
2455	SM_VA_END(ap);
2456
2457	i = (int) strlen(buf);
2458	if (i < 0)
2459		return;
2460
2461#  if SPT_TYPE == SPT_PSTAT
2462	pst.pst_command = buf;
2463	pstat(PSTAT_SETCMD, pst, i, 0, 0);
2464#  endif /* SPT_TYPE == SPT_PSTAT */
2465#  if SPT_TYPE == SPT_PSSTRINGS
2466	PS_STRINGS->ps_nargvstr = 1;
2467	PS_STRINGS->ps_argvstr = buf;
2468#  endif /* SPT_TYPE == SPT_PSSTRINGS */
2469#  if SPT_TYPE == SPT_SYSMIPS
2470	sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
2471#  endif /* SPT_TYPE == SPT_SYSMIPS */
2472#  if SPT_TYPE == SPT_SCO
2473	if (kmem < 0 || kmempid != CurrentPid)
2474	{
2475		if (kmem >= 0)
2476			(void) close(kmem);
2477		kmem = open(_PATH_KMEM, O_RDWR, 0);
2478		if (kmem < 0)
2479			return;
2480		if ((j = fcntl(kmem, F_GETFD, 0)) < 0 ||
2481		    fcntl(kmem, F_SETFD, j | FD_CLOEXEC) < 0)
2482		{
2483			(void) close(kmem);
2484			kmem = -1;
2485			return;
2486		}
2487		kmempid = CurrentPid;
2488	}
2489	buf[PSARGSZ - 1] = '\0';
2490	seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u;
2491	if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off)
2492		(void) write(kmem, buf, PSARGSZ);
2493#  endif /* SPT_TYPE == SPT_SCO */
2494#  if SPT_TYPE == SPT_REUSEARGV
2495	if (LastArgv == NULL)
2496		return;
2497
2498	if (i > LastArgv - Argv[0] - 2)
2499	{
2500		i = LastArgv - Argv[0] - 2;
2501		buf[i] = '\0';
2502	}
2503	(void) sm_strlcpy(Argv[0], buf, i + 1);
2504	p = &Argv[0][i];
2505	while (p < LastArgv)
2506		*p++ = SPT_PADCHAR;
2507	Argv[1] = NULL;
2508#  endif /* SPT_TYPE == SPT_REUSEARGV */
2509#  if SPT_TYPE == SPT_CHANGEARGV
2510	Argv[0] = buf;
2511	Argv[1] = 0;
2512#  endif /* SPT_TYPE == SPT_CHANGEARGV */
2513# endif /* SPT_TYPE != SPT_NONE */
2514}
2515
2516#endif /* SPT_TYPE != SPT_BUILTIN */
2517/*
2518**  SM_SETPROCTITLE -- set process task and set process title for ps
2519**
2520**	Possibly set process status and call setproctitle() to
2521**	change the ps display.
2522**
2523**	Parameters:
2524**		status -- whether or not to store as process status
2525**		e -- the current envelope.
2526**		fmt -- a printf style format string.
2527**		a, b, c -- possible parameters to fmt.
2528**
2529**	Returns:
2530**		none.
2531*/
2532
2533/*VARARGS2*/
2534void
2535#ifdef __STDC__
2536sm_setproctitle(bool status, ENVELOPE *e, const char *fmt, ...)
2537#else /* __STDC__ */
2538sm_setproctitle(status, e, fmt, va_alist)
2539	bool status;
2540	ENVELOPE *e;
2541	const char *fmt;
2542	va_dcl
2543#endif /* __STDC__ */
2544{
2545	char buf[SPT_BUFSIZE];
2546	SM_VA_LOCAL_DECL
2547
2548	/* print the argument string */
2549	SM_VA_START(ap, fmt);
2550	(void) sm_vsnprintf(buf, sizeof buf, fmt, ap);
2551	SM_VA_END(ap);
2552
2553	if (status)
2554		proc_list_set(CurrentPid, buf);
2555
2556	if (ProcTitlePrefix != NULL)
2557	{
2558		char prefix[SPT_BUFSIZE];
2559
2560		expand(ProcTitlePrefix, prefix, sizeof prefix, e);
2561		setproctitle("%s: %s", prefix, buf);
2562	}
2563	else
2564		setproctitle("%s", buf);
2565}
2566/*
2567**  WAITFOR -- wait for a particular process id.
2568**
2569**	Parameters:
2570**		pid -- process id to wait for.
2571**
2572**	Returns:
2573**		status of pid.
2574**		-1 if pid never shows up.
2575**
2576**	Side Effects:
2577**		none.
2578*/
2579
2580int
2581waitfor(pid)
2582	pid_t pid;
2583{
2584	int st;
2585	pid_t i;
2586
2587	do
2588	{
2589		errno = 0;
2590		i = sm_wait(&st);
2591		if (i > 0)
2592			proc_list_drop(i, st, NULL);
2593	} while ((i >= 0 || errno == EINTR) && i != pid);
2594	if (i < 0)
2595		return -1;
2596	return st;
2597}
2598/*
2599**  SM_WAIT -- wait
2600**
2601**	Parameters:
2602**		status -- pointer to status (return value)
2603**
2604**	Returns:
2605**		pid
2606*/
2607
2608pid_t
2609sm_wait(status)
2610	int *status;
2611{
2612# ifdef WAITUNION
2613	union wait st;
2614# else /* WAITUNION */
2615	auto int st;
2616# endif /* WAITUNION */
2617	pid_t i;
2618# if defined(ISC_UNIX) || defined(_SCO_unix_)
2619	int savesig;
2620# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
2621
2622# if defined(ISC_UNIX) || defined(_SCO_unix_)
2623	savesig = sm_releasesignal(SIGCHLD);
2624# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
2625	i = wait(&st);
2626# if defined(ISC_UNIX) || defined(_SCO_unix_)
2627	if (savesig > 0)
2628		sm_blocksignal(SIGCHLD);
2629# endif /* defined(ISC_UNIX) || defined(_SCO_unix_) */
2630# ifdef WAITUNION
2631	*status = st.w_status;
2632# else /* WAITUNION */
2633	*status = st;
2634# endif /* WAITUNION */
2635	return i;
2636}
2637/*
2638**  REAPCHILD -- pick up the body of my child, lest it become a zombie
2639**
2640**	Parameters:
2641**		sig -- the signal that got us here (unused).
2642**
2643**	Returns:
2644**		none.
2645**
2646**	Side Effects:
2647**		Picks up extant zombies.
2648**		Control socket exits may restart/shutdown daemon.
2649**
2650**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2651**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2652**		DOING.
2653*/
2654
2655/* ARGSUSED0 */
2656SIGFUNC_DECL
2657reapchild(sig)
2658	int sig;
2659{
2660	int save_errno = errno;
2661	int st;
2662	pid_t pid;
2663# if HASWAITPID
2664	auto int status;
2665	int count;
2666
2667	count = 0;
2668	while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
2669	{
2670		st = status;
2671		if (count++ > 1000)
2672			break;
2673# else /* HASWAITPID */
2674#  ifdef WNOHANG
2675	union wait status;
2676
2677	while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0)
2678	{
2679		st = status.w_status;
2680#  else /* WNOHANG */
2681	auto int status;
2682
2683	/*
2684	**  Catch one zombie -- we will be re-invoked (we hope) if there
2685	**  are more.  Unreliable signals probably break this, but this
2686	**  is the "old system" situation -- waitpid or wait3 are to be
2687	**  strongly preferred.
2688	*/
2689
2690	if ((pid = wait(&status)) > 0)
2691	{
2692		st = status;
2693#  endif /* WNOHANG */
2694# endif /* HASWAITPID */
2695		/* Drop PID and check if it was a control socket child */
2696		proc_list_drop(pid, st, NULL);
2697	}
2698	FIX_SYSV_SIGNAL(sig, reapchild);
2699	errno = save_errno;
2700	return SIGFUNC_RETURN;
2701}
2702/*
2703**  GETDTABLESIZE -- return number of file descriptors
2704**
2705**	Only on non-BSD systems
2706**
2707**	Parameters:
2708**		none
2709**
2710**	Returns:
2711**		size of file descriptor table
2712**
2713**	Side Effects:
2714**		none
2715*/
2716
2717#ifdef SOLARIS
2718# include <sys/resource.h>
2719#endif /* SOLARIS */
2720
2721int
2722getdtsize()
2723{
2724# ifdef RLIMIT_NOFILE
2725	struct rlimit rl;
2726
2727	if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
2728		return rl.rlim_cur;
2729# endif /* RLIMIT_NOFILE */
2730
2731# if HASGETDTABLESIZE
2732	return getdtablesize();
2733# else /* HASGETDTABLESIZE */
2734#  ifdef _SC_OPEN_MAX
2735	return sysconf(_SC_OPEN_MAX);
2736#  else /* _SC_OPEN_MAX */
2737	return NOFILE;
2738#  endif /* _SC_OPEN_MAX */
2739# endif /* HASGETDTABLESIZE */
2740}
2741/*
2742**  UNAME -- get the UUCP name of this system.
2743*/
2744
2745#if !HASUNAME
2746
2747int
2748uname(name)
2749	struct utsname *name;
2750{
2751	SM_FILE_T *file;
2752	char *n;
2753
2754	name->nodename[0] = '\0';
2755
2756	/* try /etc/whoami -- one line with the node name */
2757	if ((file = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, "/etc/whoami",
2758			       SM_IO_RDONLY, NULL)) != NULL)
2759	{
2760		(void) sm_io_fgets(file, SM_TIME_DEFAULT, name->nodename,
2761				   NODE_LENGTH + 1);
2762		(void) sm_io_close(file, SM_TIME_DEFAULT);
2763		n = strchr(name->nodename, '\n');
2764		if (n != NULL)
2765			*n = '\0';
2766		if (name->nodename[0] != '\0')
2767			return 0;
2768	}
2769
2770	/* try /usr/include/whoami.h -- has a #define somewhere */
2771	if ((file = sm_io_open(SmFtStdio, SM_TIME_DEFAULT,
2772			       "/usr/include/whoami.h", SM_IO_RDONLY, NULL))
2773	    != NULL)
2774	{
2775		char buf[MAXLINE];
2776
2777		while (sm_io_fgets(file, SM_TIME_DEFAULT,
2778				   buf, sizeof buf) != NULL)
2779		{
2780			if (sm_io_sscanf(buf, "#define sysname \"%*[^\"]\"",
2781					NODE_LENGTH, name->nodename) > 0)
2782				break;
2783		}
2784		(void) sm_io_close(file, SM_TIME_DEFAULT);
2785		if (name->nodename[0] != '\0')
2786			return 0;
2787	}
2788
2789#  if 0
2790	/*
2791	**  Popen is known to have security holes.
2792	*/
2793
2794	/* try uuname -l to return local name */
2795	if ((file = popen("uuname -l", "r")) != NULL)
2796	{
2797		(void) sm_io_fgets(file, SM_TIME_DEFAULT, name,
2798				   NODE_LENGTH + 1);
2799		(void) pclose(file);
2800		n = strchr(name, '\n');
2801		if (n != NULL)
2802			*n = '\0';
2803		if (name->nodename[0] != '\0')
2804			return 0;
2805	}
2806#  endif /* 0 */
2807
2808	return -1;
2809}
2810#endif /* !HASUNAME */
2811/*
2812**  INITGROUPS -- initialize groups
2813**
2814**	Stub implementation for System V style systems
2815*/
2816
2817#if !HASINITGROUPS
2818
2819initgroups(name, basegid)
2820	char *name;
2821	int basegid;
2822{
2823	return 0;
2824}
2825
2826#endif /* !HASINITGROUPS */
2827/*
2828**  SETGROUPS -- set group list
2829**
2830**	Stub implementation for systems that don't have group lists
2831*/
2832
2833#ifndef NGROUPS_MAX
2834
2835int
2836setgroups(ngroups, grouplist)
2837	int ngroups;
2838	GIDSET_T grouplist[];
2839{
2840	return 0;
2841}
2842
2843#endif /* ! NGROUPS_MAX */
2844/*
2845**  SETSID -- set session id (for non-POSIX systems)
2846*/
2847
2848#if !HASSETSID
2849
2850pid_t
2851setsid __P ((void))
2852{
2853#  ifdef TIOCNOTTY
2854	int fd;
2855
2856	fd = open("/dev/tty", O_RDWR, 0);
2857	if (fd >= 0)
2858	{
2859		(void) ioctl(fd, TIOCNOTTY, (char *) 0);
2860		(void) close(fd);
2861	}
2862#  endif /* TIOCNOTTY */
2863#  ifdef SYS5SETPGRP
2864	return setpgrp();
2865#  else /* SYS5SETPGRP */
2866	return setpgid(0, CurrentPid);
2867#  endif /* SYS5SETPGRP */
2868}
2869
2870#endif /* !HASSETSID */
2871/*
2872**  FSYNC -- dummy fsync
2873*/
2874
2875#if NEEDFSYNC
2876
2877fsync(fd)
2878	int fd;
2879{
2880# ifdef O_SYNC
2881	return fcntl(fd, F_SETFL, O_SYNC);
2882# else /* O_SYNC */
2883	/* nothing we can do */
2884	return 0;
2885# endif /* O_SYNC */
2886}
2887
2888#endif /* NEEDFSYNC */
2889/*
2890**  DGUX_INET_ADDR -- inet_addr for DG/UX
2891**
2892**	Data General DG/UX version of inet_addr returns a struct in_addr
2893**	instead of a long.  This patches things.  Only needed on versions
2894**	prior to 5.4.3.
2895*/
2896
2897#ifdef DGUX_5_4_2
2898
2899# undef inet_addr
2900
2901long
2902dgux_inet_addr(host)
2903	char *host;
2904{
2905	struct in_addr haddr;
2906
2907	haddr = inet_addr(host);
2908	return haddr.s_addr;
2909}
2910
2911#endif /* DGUX_5_4_2 */
2912/*
2913**  GETOPT -- for old systems or systems with bogus implementations
2914*/
2915
2916#if !SM_CONF_GETOPT
2917
2918/*
2919 * Copyright (c) 1985 Regents of the University of California.
2920 * All rights reserved.  The Berkeley software License Agreement
2921 * specifies the terms and conditions for redistribution.
2922 */
2923
2924
2925/*
2926**  this version hacked to add `atend' flag to allow state machine
2927**  to reset if invoked by the program to scan args for a 2nd time
2928*/
2929
2930# if defined(LIBC_SCCS) && !defined(lint)
2931static char sccsid[] = "@(#)getopt.c	4.3 (Berkeley) 3/9/86";
2932# endif /* defined(LIBC_SCCS) && !defined(lint) */
2933
2934/*
2935**  get option letter from argument vector
2936*/
2937# ifdef _CONVEX_SOURCE
2938extern int	optind, opterr, optopt;
2939extern char	*optarg;
2940# else /* _CONVEX_SOURCE */
2941int	opterr = 1;		/* if error message should be printed */
2942int	optind = 1;		/* index into parent argv vector */
2943int	optopt = 0;		/* character checked for validity */
2944char	*optarg = NULL;		/* argument associated with option */
2945# endif /* _CONVEX_SOURCE */
2946
2947# define BADCH	(int)'?'
2948# define EMSG	""
2949# define tell(s)	if (opterr) \
2950			{sm_io_fputs(smioerr, SM_TIME_DEFAULT, *nargv); \
2951			(void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, s); \
2952			(void) sm_io_putc(smioerr, SM_TIME_DEFAULT, optopt); \
2953			(void) sm_io_putc(smioerr, SM_TIME_DEFAULT, '\n'); \
2954			return BADCH;}
2955
2956int
2957getopt(nargc,nargv,ostr)
2958	int		nargc;
2959	char *const	*nargv;
2960	const char	*ostr;
2961{
2962	static char	*place = EMSG;	/* option letter processing */
2963	static char	atend = 0;
2964	register char	*oli = NULL;	/* option letter list index */
2965
2966	if (atend) {
2967		atend = 0;
2968		place = EMSG;
2969	}
2970	if(!*place) {			/* update scanning pointer */
2971		if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) {
2972			atend++;
2973			return -1;
2974		}
2975		if (*place == '-') {	/* found "--" */
2976			++optind;
2977			atend++;
2978			return -1;
2979		}
2980	}				/* option letter okay? */
2981	if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) {
2982		if (!*place) ++optind;
2983		tell(": illegal option -- ");
2984	}
2985	if (oli && *++oli != ':') {		/* don't need argument */
2986		optarg = NULL;
2987		if (!*place) ++optind;
2988	}
2989	else {				/* need an argument */
2990		if (*place) optarg = place;	/* no white space */
2991		else if (nargc <= ++optind) {	/* no arg */
2992			place = EMSG;
2993			tell(": option requires an argument -- ");
2994		}
2995		else optarg = nargv[optind];	/* white space */
2996		place = EMSG;
2997		++optind;
2998	}
2999	return optopt;			/* dump back option letter */
3000}
3001
3002#endif /* !SM_CONF_GETOPT */
3003/*
3004**  USERSHELLOK -- tell if a user's shell is ok for unrestricted use
3005**
3006**	Parameters:
3007**		user -- the name of the user we are checking.
3008**		shell -- the user's shell from /etc/passwd
3009**
3010**	Returns:
3011**		true -- if it is ok to use this for unrestricted access.
3012**		false -- if the shell is restricted.
3013*/
3014
3015#if !HASGETUSERSHELL
3016
3017# ifndef _PATH_SHELLS
3018#  define _PATH_SHELLS	"/etc/shells"
3019# endif /* ! _PATH_SHELLS */
3020
3021# if defined(_AIX3) || defined(_AIX4)
3022#  include <userconf.h>
3023#  if _AIX4 >= 40200
3024#   include <userpw.h>
3025#  endif /* _AIX4 >= 40200 */
3026#  include <usersec.h>
3027# endif /* defined(_AIX3) || defined(_AIX4) */
3028
3029static char	*DefaultUserShells[] =
3030{
3031	"/bin/sh",		/* standard shell */
3032# ifdef MPE
3033	"/SYS/PUB/CI",
3034# else /* MPE */
3035	"/usr/bin/sh",
3036	"/bin/csh",		/* C shell */
3037	"/usr/bin/csh",
3038# endif /* MPE */
3039# ifdef __hpux
3040#  ifdef V4FS
3041	"/usr/bin/rsh",		/* restricted Bourne shell */
3042	"/usr/bin/ksh",		/* Korn shell */
3043	"/usr/bin/rksh",	/* restricted Korn shell */
3044	"/usr/bin/pam",
3045	"/usr/bin/keysh",	/* key shell (extended Korn shell) */
3046	"/usr/bin/posix/sh",
3047#  else /* V4FS */
3048	"/bin/rsh",		/* restricted Bourne shell */
3049	"/bin/ksh",		/* Korn shell */
3050	"/bin/rksh",		/* restricted Korn shell */
3051	"/bin/pam",
3052	"/usr/bin/keysh",	/* key shell (extended Korn shell) */
3053	"/bin/posix/sh",
3054	"/sbin/sh"
3055#  endif /* V4FS */
3056# endif /* __hpux */
3057# if defined(_AIX3) || defined(_AIX4)
3058	"/bin/ksh",		/* Korn shell */
3059	"/usr/bin/ksh",
3060	"/bin/tsh",		/* trusted shell */
3061	"/usr/bin/tsh",
3062	"/bin/bsh",		/* Bourne shell */
3063	"/usr/bin/bsh",
3064# endif /* defined(_AIX3) || defined(_AIX4) */
3065# if defined(__svr4__) || defined(__svr5__)
3066	"/bin/ksh",		/* Korn shell */
3067	"/usr/bin/ksh",
3068# endif /* defined(__svr4__) || defined(__svr5__) */
3069# ifdef sgi
3070	"/sbin/sh",		/* SGI's shells really live in /sbin */
3071	"/usr/bin/sh",
3072	"/sbin/bsh",		/* classic borne shell */
3073	"/bin/bsh",
3074	"/usr/bin/bsh",
3075	"/sbin/csh",		/* standard csh */
3076	"/bin/csh",
3077	"/usr/bin/csh",
3078	"/sbin/jsh",		/* classic borne shell w/ job control*/
3079	"/bin/jsh",
3080	"/usr/bin/jsh",
3081	"/bin/ksh",		/* Korn shell */
3082	"/sbin/ksh",
3083	"/usr/bin/ksh",
3084	"/sbin/tcsh",		/* Extended csh */
3085	"/bin/tcsh",
3086	"/usr/bin/tcsh",
3087# endif /* sgi */
3088	NULL
3089};
3090
3091#endif /* !HASGETUSERSHELL */
3092
3093#define WILDCARD_SHELL	"/SENDMAIL/ANY/SHELL/"
3094
3095bool
3096usershellok(user, shell)
3097	char *user;
3098	char *shell;
3099{
3100# if HASGETUSERSHELL
3101	register char *p;
3102	extern char *getusershell();
3103
3104	if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
3105	    ConfigLevel <= 1)
3106		return true;
3107
3108	setusershell();
3109	while ((p = getusershell()) != NULL)
3110		if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0)
3111			break;
3112	endusershell();
3113	return p != NULL;
3114# else /* HASGETUSERSHELL */
3115#  if USEGETCONFATTR
3116	auto char *v;
3117#  endif /* USEGETCONFATTR */
3118	register SM_FILE_T *shellf;
3119	char buf[MAXLINE];
3120
3121	if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
3122	    ConfigLevel <= 1)
3123		return true;
3124
3125#  if USEGETCONFATTR
3126	/*
3127	**  Naturally IBM has a "better" idea.....
3128	**
3129	**	What a crock.  This interface isn't documented, it is
3130	**	considered part of the security library (-ls), and it
3131	**	only works if you are running as root (since the list
3132	**	of valid shells is obviously a source of great concern).
3133	**	I recommend that you do NOT define USEGETCONFATTR,
3134	**	especially since you are going to have to set up an
3135	**	/etc/shells anyhow to handle the cases where getconfattr
3136	**	fails.
3137	*/
3138
3139	if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL)
3140	{
3141		while (*v != '\0')
3142		{
3143			if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0)
3144				return true;
3145			v += strlen(v) + 1;
3146		}
3147		return false;
3148	}
3149#  endif /* USEGETCONFATTR */
3150
3151	shellf = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, _PATH_SHELLS,
3152			    SM_IO_RDONLY, NULL);
3153	if (shellf == NULL)
3154	{
3155		/* no /etc/shells; see if it is one of the std shells */
3156		char **d;
3157
3158		if (errno != ENOENT && LogLevel > 3)
3159			sm_syslog(LOG_ERR, NOQID,
3160				  "usershellok: cannot open %s: %s",
3161				  _PATH_SHELLS, sm_errstring(errno));
3162
3163		for (d = DefaultUserShells; *d != NULL; d++)
3164		{
3165			if (strcmp(shell, *d) == 0)
3166				return true;
3167		}
3168		return false;
3169	}
3170
3171	while (sm_io_fgets(shellf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
3172	{
3173		register char *p, *q;
3174
3175		p = buf;
3176		while (*p != '\0' && *p != '#' && *p != '/')
3177			p++;
3178		if (*p == '#' || *p == '\0')
3179			continue;
3180		q = p;
3181		while (*p != '\0' && *p != '#' && !(isascii(*p) && isspace(*p)))
3182			p++;
3183		*p = '\0';
3184		if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0)
3185		{
3186			(void) sm_io_close(shellf, SM_TIME_DEFAULT);
3187			return true;
3188		}
3189	}
3190	(void) sm_io_close(shellf, SM_TIME_DEFAULT);
3191	return false;
3192# endif /* HASGETUSERSHELL */
3193}
3194/*
3195**  FREEDISKSPACE -- see how much free space is on the queue filesystem
3196**
3197**	Only implemented if you have statfs.
3198**
3199**	Parameters:
3200**		dir -- the directory in question.
3201**		bsize -- a variable into which the filesystem
3202**			block size is stored.
3203**
3204**	Returns:
3205**		The number of blocks free on the queue filesystem.
3206**		-1 if the statfs call fails.
3207**
3208**	Side effects:
3209**		Puts the filesystem block size into bsize.
3210*/
3211
3212/* statfs types */
3213# define SFS_NONE	0	/* no statfs implementation */
3214# define SFS_USTAT	1	/* use ustat */
3215# define SFS_4ARGS	2	/* use four-argument statfs call */
3216# define SFS_VFS	3	/* use <sys/vfs.h> implementation */
3217# define SFS_MOUNT	4	/* use <sys/mount.h> implementation */
3218# define SFS_STATFS	5	/* use <sys/statfs.h> implementation */
3219# define SFS_STATVFS	6	/* use <sys/statvfs.h> implementation */
3220
3221# ifndef SFS_TYPE
3222#  define SFS_TYPE	SFS_NONE
3223# endif /* ! SFS_TYPE */
3224
3225# if SFS_TYPE == SFS_USTAT
3226#  include <ustat.h>
3227# endif /* SFS_TYPE == SFS_USTAT */
3228# if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
3229#  include <sys/statfs.h>
3230# endif /* SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS */
3231# if SFS_TYPE == SFS_VFS
3232#  include <sys/vfs.h>
3233# endif /* SFS_TYPE == SFS_VFS */
3234# if SFS_TYPE == SFS_MOUNT
3235#  include <sys/mount.h>
3236# endif /* SFS_TYPE == SFS_MOUNT */
3237# if SFS_TYPE == SFS_STATVFS
3238#  include <sys/statvfs.h>
3239# endif /* SFS_TYPE == SFS_STATVFS */
3240
3241long
3242freediskspace(dir, bsize)
3243	char *dir;
3244	long *bsize;
3245{
3246# if SFS_TYPE == SFS_NONE
3247	if (bsize != NULL)
3248		*bsize = 4096L;
3249
3250	/* assume free space is plentiful */
3251	return (long) LONG_MAX;
3252# else /* SFS_TYPE == SFS_NONE */
3253#  if SFS_TYPE == SFS_USTAT
3254	struct ustat fs;
3255	struct stat statbuf;
3256#   define FSBLOCKSIZE	DEV_BSIZE
3257#   define SFS_BAVAIL	f_tfree
3258#  else /* SFS_TYPE == SFS_USTAT */
3259#   if defined(ultrix)
3260	struct fs_data fs;
3261#    define SFS_BAVAIL	fd_bfreen
3262#    define FSBLOCKSIZE	1024L
3263#   else /* defined(ultrix) */
3264#    if SFS_TYPE == SFS_STATVFS
3265	struct statvfs fs;
3266#     define FSBLOCKSIZE	fs.f_frsize
3267#    else /* SFS_TYPE == SFS_STATVFS */
3268	struct statfs fs;
3269#     define FSBLOCKSIZE	fs.f_bsize
3270#    endif /* SFS_TYPE == SFS_STATVFS */
3271#   endif /* defined(ultrix) */
3272#  endif /* SFS_TYPE == SFS_USTAT */
3273#  ifndef SFS_BAVAIL
3274#   define SFS_BAVAIL f_bavail
3275#  endif /* ! SFS_BAVAIL */
3276
3277#  if SFS_TYPE == SFS_USTAT
3278	if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
3279#  else /* SFS_TYPE == SFS_USTAT */
3280#   if SFS_TYPE == SFS_4ARGS
3281	if (statfs(dir, &fs, sizeof fs, 0) == 0)
3282#   else /* SFS_TYPE == SFS_4ARGS */
3283#    if SFS_TYPE == SFS_STATVFS
3284	if (statvfs(dir, &fs) == 0)
3285#    else /* SFS_TYPE == SFS_STATVFS */
3286#     if defined(ultrix)
3287	if (statfs(dir, &fs) > 0)
3288#     else /* defined(ultrix) */
3289	if (statfs(dir, &fs) == 0)
3290#     endif /* defined(ultrix) */
3291#    endif /* SFS_TYPE == SFS_STATVFS */
3292#   endif /* SFS_TYPE == SFS_4ARGS */
3293#  endif /* SFS_TYPE == SFS_USTAT */
3294	{
3295		if (bsize != NULL)
3296			*bsize = FSBLOCKSIZE;
3297		if (fs.SFS_BAVAIL <= 0)
3298			return 0;
3299		else if (fs.SFS_BAVAIL > LONG_MAX)
3300			return (long) LONG_MAX;
3301		else
3302			return (long) fs.SFS_BAVAIL;
3303	}
3304	return -1;
3305# endif /* SFS_TYPE == SFS_NONE */
3306}
3307/*
3308**  ENOUGHDISKSPACE -- is there enough free space on the queue file systems?
3309**
3310**	Parameters:
3311**		msize -- the size to check against.  If zero, we don't yet
3312**		know how big the message will be, so just check for
3313**		a "reasonable" amount.
3314**		e -- envelope, or NULL -- controls logging
3315**
3316**	Returns:
3317**		true if in every queue group there is at least one
3318**		queue directory whose file system contains enough free space.
3319**		false otherwise.
3320**
3321**	Side Effects:
3322**		If there is not enough disk space and e != NULL
3323**		then sm_syslog is called.
3324*/
3325
3326bool
3327enoughdiskspace(msize, e)
3328	long msize;
3329	ENVELOPE *e;
3330{
3331	int i;
3332
3333	if (MinBlocksFree <= 0 && msize <= 0)
3334	{
3335		if (tTd(4, 80))
3336			sm_dprintf("enoughdiskspace: no threshold\n");
3337		return true;
3338	}
3339
3340	filesys_update();
3341	for (i = 0; i < NumQueue; ++i)
3342	{
3343		if (pickqdir(Queue[i], msize, e) < 0)
3344			return false;
3345	}
3346	return true;
3347}
3348/*
3349**  TRANSIENTERROR -- tell if an error code indicates a transient failure
3350**
3351**	This looks at an errno value and tells if this is likely to
3352**	go away if retried later.
3353**
3354**	Parameters:
3355**		err -- the errno code to classify.
3356**
3357**	Returns:
3358**		true if this is probably transient.
3359**		false otherwise.
3360*/
3361
3362bool
3363transienterror(err)
3364	int err;
3365{
3366	switch (err)
3367	{
3368	  case EIO:			/* I/O error */
3369	  case ENXIO:			/* Device not configured */
3370	  case EAGAIN:			/* Resource temporarily unavailable */
3371	  case ENOMEM:			/* Cannot allocate memory */
3372	  case ENODEV:			/* Operation not supported by device */
3373	  case ENFILE:			/* Too many open files in system */
3374	  case EMFILE:			/* Too many open files */
3375	  case ENOSPC:			/* No space left on device */
3376	  case ETIMEDOUT:		/* Connection timed out */
3377#ifdef ESTALE
3378	  case ESTALE:			/* Stale NFS file handle */
3379#endif /* ESTALE */
3380#ifdef ENETDOWN
3381	  case ENETDOWN:		/* Network is down */
3382#endif /* ENETDOWN */
3383#ifdef ENETUNREACH
3384	  case ENETUNREACH:		/* Network is unreachable */
3385#endif /* ENETUNREACH */
3386#ifdef ENETRESET
3387	  case ENETRESET:		/* Network dropped connection on reset */
3388#endif /* ENETRESET */
3389#ifdef ECONNABORTED
3390	  case ECONNABORTED:		/* Software caused connection abort */
3391#endif /* ECONNABORTED */
3392#ifdef ECONNRESET
3393	  case ECONNRESET:		/* Connection reset by peer */
3394#endif /* ECONNRESET */
3395#ifdef ENOBUFS
3396	  case ENOBUFS:			/* No buffer space available */
3397#endif /* ENOBUFS */
3398#ifdef ESHUTDOWN
3399	  case ESHUTDOWN:		/* Can't send after socket shutdown */
3400#endif /* ESHUTDOWN */
3401#ifdef ECONNREFUSED
3402	  case ECONNREFUSED:		/* Connection refused */
3403#endif /* ECONNREFUSED */
3404#ifdef EHOSTDOWN
3405	  case EHOSTDOWN:		/* Host is down */
3406#endif /* EHOSTDOWN */
3407#ifdef EHOSTUNREACH
3408	  case EHOSTUNREACH:		/* No route to host */
3409#endif /* EHOSTUNREACH */
3410#ifdef EDQUOT
3411	  case EDQUOT:			/* Disc quota exceeded */
3412#endif /* EDQUOT */
3413#ifdef EPROCLIM
3414	  case EPROCLIM:		/* Too many processes */
3415#endif /* EPROCLIM */
3416#ifdef EUSERS
3417	  case EUSERS:			/* Too many users */
3418#endif /* EUSERS */
3419#ifdef EDEADLK
3420	  case EDEADLK:			/* Resource deadlock avoided */
3421#endif /* EDEADLK */
3422#ifdef EISCONN
3423	  case EISCONN:			/* Socket already connected */
3424#endif /* EISCONN */
3425#ifdef EINPROGRESS
3426	  case EINPROGRESS:		/* Operation now in progress */
3427#endif /* EINPROGRESS */
3428#ifdef EALREADY
3429	  case EALREADY:		/* Operation already in progress */
3430#endif /* EALREADY */
3431#ifdef EADDRINUSE
3432	  case EADDRINUSE:		/* Address already in use */
3433#endif /* EADDRINUSE */
3434#ifdef EADDRNOTAVAIL
3435	  case EADDRNOTAVAIL:		/* Can't assign requested address */
3436#endif /* EADDRNOTAVAIL */
3437#ifdef ETXTBSY
3438	  case ETXTBSY:			/* (Apollo) file locked */
3439#endif /* ETXTBSY */
3440#if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
3441	  case ENOSR:			/* Out of streams resources */
3442#endif /* defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR)) */
3443#ifdef ENOLCK
3444	  case ENOLCK:			/* No locks available */
3445#endif /* ENOLCK */
3446	  case E_SM_OPENTIMEOUT:	/* PSEUDO: open timed out */
3447		return true;
3448	}
3449
3450	/* nope, must be permanent */
3451	return false;
3452}
3453/*
3454**  LOCKFILE -- lock a file using flock or (shudder) fcntl locking
3455**
3456**	Parameters:
3457**		fd -- the file descriptor of the file.
3458**		filename -- the file name (for error messages).
3459**		ext -- the filename extension.
3460**		type -- type of the lock.  Bits can be:
3461**			LOCK_EX -- exclusive lock.
3462**			LOCK_NB -- non-blocking.
3463**			LOCK_UN -- unlock.
3464**
3465**	Returns:
3466**		true if the lock was acquired.
3467**		false otherwise.
3468*/
3469
3470bool
3471lockfile(fd, filename, ext, type)
3472	int fd;
3473	char *filename;
3474	char *ext;
3475	int type;
3476{
3477	int i;
3478	int save_errno;
3479# if !HASFLOCK
3480	int action;
3481	struct flock lfd;
3482
3483	if (ext == NULL)
3484		ext = "";
3485
3486	memset(&lfd, '\0', sizeof lfd);
3487	if (bitset(LOCK_UN, type))
3488		lfd.l_type = F_UNLCK;
3489	else if (bitset(LOCK_EX, type))
3490		lfd.l_type = F_WRLCK;
3491	else
3492		lfd.l_type = F_RDLCK;
3493
3494	if (bitset(LOCK_NB, type))
3495		action = F_SETLK;
3496	else
3497		action = F_SETLKW;
3498
3499	if (tTd(55, 60))
3500		sm_dprintf("lockfile(%s%s, action=%d, type=%d): ",
3501			filename, ext, action, lfd.l_type);
3502
3503	while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
3504		continue;
3505	if (i >= 0)
3506	{
3507		if (tTd(55, 60))
3508			sm_dprintf("SUCCESS\n");
3509		return true;
3510	}
3511	save_errno = errno;
3512
3513	if (tTd(55, 60))
3514		sm_dprintf("(%s) ", sm_errstring(save_errno));
3515
3516	/*
3517	**  On SunOS, if you are testing using -oQ/tmp/mqueue or
3518	**  -oA/tmp/aliases or anything like that, and /tmp is mounted
3519	**  as type "tmp" (that is, served from swap space), the
3520	**  previous fcntl will fail with "Invalid argument" errors.
3521	**  Since this is fairly common during testing, we will assume
3522	**  that this indicates that the lock is successfully grabbed.
3523	*/
3524
3525	if (save_errno == EINVAL)
3526	{
3527		if (tTd(55, 60))
3528			sm_dprintf("SUCCESS\n");
3529		return true;
3530	}
3531
3532	if (!bitset(LOCK_NB, type) ||
3533	    (save_errno != EACCES && save_errno != EAGAIN))
3534	{
3535		int omode = fcntl(fd, F_GETFL, 0);
3536		uid_t euid = geteuid();
3537
3538		errno = save_errno;
3539		syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
3540		       filename, ext, fd, type, omode, euid);
3541		dumpfd(fd, true, true);
3542	}
3543# else /* !HASFLOCK */
3544	if (ext == NULL)
3545		ext = "";
3546
3547	if (tTd(55, 60))
3548		sm_dprintf("lockfile(%s%s, type=%o): ", filename, ext, type);
3549
3550	while ((i = flock(fd, type)) < 0 && errno == EINTR)
3551		continue;
3552	if (i >= 0)
3553	{
3554		if (tTd(55, 60))
3555			sm_dprintf("SUCCESS\n");
3556		return true;
3557	}
3558	save_errno = errno;
3559
3560	if (tTd(55, 60))
3561		sm_dprintf("(%s) ", sm_errstring(save_errno));
3562
3563	if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
3564	{
3565		int omode = fcntl(fd, F_GETFL, 0);
3566		uid_t euid = geteuid();
3567
3568		errno = save_errno;
3569		syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
3570			filename, ext, fd, type, omode, euid);
3571		dumpfd(fd, true, true);
3572	}
3573# endif /* !HASFLOCK */
3574	if (tTd(55, 60))
3575		sm_dprintf("FAIL\n");
3576	errno = save_errno;
3577	return false;
3578}
3579/*
3580**  CHOWNSAFE -- tell if chown is "safe" (executable only by root)
3581**
3582**	Unfortunately, given that we can't predict other systems on which
3583**	a remote mounted (NFS) filesystem will be mounted, the answer is
3584**	almost always that this is unsafe.
3585**
3586**	Note also that many operating systems have non-compliant
3587**	implementations of the _POSIX_CHOWN_RESTRICTED variable and the
3588**	fpathconf() routine.  According to IEEE 1003.1-1990, if
3589**	_POSIX_CHOWN_RESTRICTED is defined and not equal to -1, then
3590**	no non-root process can give away the file.  However, vendors
3591**	don't take NFS into account, so a comfortable value of
3592**	_POSIX_CHOWN_RESTRICTED tells us nothing.
3593**
3594**	Also, some systems (e.g., IRIX 6.2) return 1 from fpathconf()
3595**	even on files where chown is not restricted.  Many systems get
3596**	this wrong on NFS-based filesystems (that is, they say that chown
3597**	is restricted [safe] on NFS filesystems where it may not be, since
3598**	other systems can access the same filesystem and do file giveaway;
3599**	only the NFS server knows for sure!)  Hence, it is important to
3600**	get the value of SAFENFSPATHCONF correct -- it should be defined
3601**	_only_ after testing (see test/t_pathconf.c) a system on an unsafe
3602**	NFS-based filesystem to ensure that you can get meaningful results.
3603**	If in doubt, assume unsafe!
3604**
3605**	You may also need to tweak IS_SAFE_CHOWN -- it should be a
3606**	condition indicating whether the return from pathconf indicates
3607**	that chown is safe (typically either > 0 or >= 0 -- there isn't
3608**	even any agreement about whether a zero return means that a file
3609**	is or is not safe).  It defaults to "> 0".
3610**
3611**	If the parent directory is safe (writable only by owner back
3612**	to the root) then we can relax slightly and trust fpathconf
3613**	in more circumstances.  This is really a crock -- if this is an
3614**	NFS mounted filesystem then we really know nothing about the
3615**	underlying implementation.  However, most systems pessimize and
3616**	return an error (EINVAL or EOPNOTSUPP) on NFS filesystems, which
3617**	we interpret as unsafe, as we should.  Thus, this heuristic gets
3618**	us into a possible problem only on systems that have a broken
3619**	pathconf implementation and which are also poorly configured
3620**	(have :include: files in group- or world-writable directories).
3621**
3622**	Parameters:
3623**		fd -- the file descriptor to check.
3624**		safedir -- set if the parent directory is safe.
3625**
3626**	Returns:
3627**		true -- if the chown(2) operation is "safe" -- that is,
3628**			only root can chown the file to an arbitrary user.
3629**		false -- if an arbitrary user can give away a file.
3630*/
3631
3632#ifndef IS_SAFE_CHOWN
3633# define IS_SAFE_CHOWN	> 0
3634#endif /* ! IS_SAFE_CHOWN */
3635
3636bool
3637chownsafe(fd, safedir)
3638	int fd;
3639	bool safedir;
3640{
3641# if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
3642    (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
3643	int rval;
3644
3645	/* give the system administrator a chance to override */
3646	if (bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail))
3647		return true;
3648
3649	/*
3650	**  Some systems (e.g., SunOS) seem to have the call and the
3651	**  #define _PC_CHOWN_RESTRICTED, but don't actually implement
3652	**  the call.  This heuristic checks for that.
3653	*/
3654
3655	errno = 0;
3656	rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
3657#  if SAFENFSPATHCONF
3658	return errno == 0 && rval IS_SAFE_CHOWN;
3659#  else /* SAFENFSPATHCONF */
3660	return safedir && errno == 0 && rval IS_SAFE_CHOWN;
3661#  endif /* SAFENFSPATHCONF */
3662# else /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */
3663	return bitnset(DBS_ASSUMESAFECHOWN, DontBlameSendmail);
3664# endif /* (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && ... */
3665}
3666/*
3667**  RESETLIMITS -- reset system controlled resource limits
3668**
3669**	This is to avoid denial-of-service attacks
3670**
3671**	Parameters:
3672**		none
3673**
3674**	Returns:
3675**		none
3676*/
3677
3678#if HASSETRLIMIT
3679# ifdef RLIMIT_NEEDS_SYS_TIME_H
3680#  include <sys/time.h>
3681# endif /* RLIMIT_NEEDS_SYS_TIME_H */
3682# include <sys/resource.h>
3683#endif /* HASSETRLIMIT */
3684#ifndef FD_SETSIZE
3685# define FD_SETSIZE	256
3686#endif /* ! FD_SETSIZE */
3687
3688void
3689resetlimits()
3690{
3691#if HASSETRLIMIT
3692	struct rlimit lim;
3693
3694	lim.rlim_cur = lim.rlim_max = RLIM_INFINITY;
3695	(void) setrlimit(RLIMIT_CPU, &lim);
3696	(void) setrlimit(RLIMIT_FSIZE, &lim);
3697# ifdef RLIMIT_NOFILE
3698	lim.rlim_cur = lim.rlim_max = FD_SETSIZE;
3699	(void) setrlimit(RLIMIT_NOFILE, &lim);
3700# endif /* RLIMIT_NOFILE */
3701#else /* HASSETRLIMIT */
3702# if HASULIMIT
3703	(void) ulimit(2, 0x3fffff);
3704	(void) ulimit(4, FD_SETSIZE);
3705# endif /* HASULIMIT */
3706#endif /* HASSETRLIMIT */
3707	errno = 0;
3708}
3709/*
3710**  SETVENDOR -- process vendor code from V configuration line
3711**
3712**	Parameters:
3713**		vendor -- string representation of vendor.
3714**
3715**	Returns:
3716**		true -- if ok.
3717**		false -- if vendor code could not be processed.
3718**
3719**	Side Effects:
3720**		It is reasonable to set mode flags here to tweak
3721**		processing in other parts of the code if necessary.
3722**		For example, if you are a vendor that uses $%y to
3723**		indicate YP lookups, you could enable that here.
3724*/
3725
3726bool
3727setvendor(vendor)
3728	char *vendor;
3729{
3730	if (sm_strcasecmp(vendor, "Berkeley") == 0)
3731	{
3732		VendorCode = VENDOR_BERKELEY;
3733		return true;
3734	}
3735
3736	/* add vendor extensions here */
3737
3738#ifdef SUN_EXTENSIONS
3739	if (sm_strcasecmp(vendor, "Sun") == 0)
3740	{
3741		VendorCode = VENDOR_SUN;
3742		return true;
3743	}
3744#endif /* SUN_EXTENSIONS */
3745
3746#if defined(VENDOR_NAME) && defined(VENDOR_CODE)
3747	if (sm_strcasecmp(vendor, VENDOR_NAME) == 0)
3748	{
3749		VendorCode = VENDOR_CODE;
3750		return true;
3751	}
3752#endif /* defined(VENDOR_NAME) && defined(VENDOR_CODE) */
3753
3754	return false;
3755}
3756/*
3757**  GETVENDOR -- return vendor name based on vendor code
3758**
3759**	Parameters:
3760**		vendorcode -- numeric representation of vendor.
3761**
3762**	Returns:
3763**		string containing vendor name.
3764*/
3765
3766char *
3767getvendor(vendorcode)
3768	int vendorcode;
3769{
3770#if defined(VENDOR_NAME) && defined(VENDOR_CODE)
3771	/*
3772	**  Can't have the same switch case twice so need to
3773	**  handle VENDOR_CODE outside of switch.  It might
3774	**  match one of the existing VENDOR_* codes.
3775	*/
3776
3777	if (vendorcode == VENDOR_CODE)
3778		return VENDOR_NAME;
3779#endif /* defined(VENDOR_NAME) && defined(VENDOR_CODE) */
3780
3781	switch (vendorcode)
3782	{
3783	  case VENDOR_BERKELEY:
3784		return "Berkeley";
3785
3786	  case VENDOR_SUN:
3787		return "Sun";
3788
3789	  case VENDOR_HP:
3790		return "HP";
3791
3792	  case VENDOR_IBM:
3793		return "IBM";
3794
3795	  case VENDOR_SENDMAIL:
3796		return "Sendmail";
3797
3798	  default:
3799		return "Unknown";
3800	}
3801}
3802/*
3803**  VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults
3804**
3805**	Vendor_pre_defaults is called before reading the configuration
3806**	file; vendor_post_defaults is called immediately after.
3807**
3808**	Parameters:
3809**		e -- the global environment to initialize.
3810**
3811**	Returns:
3812**		none.
3813*/
3814
3815#if SHARE_V1
3816int	DefShareUid;	/* default share uid to run as -- unused??? */
3817#endif /* SHARE_V1 */
3818
3819void
3820vendor_pre_defaults(e)
3821	ENVELOPE *e;
3822{
3823#if SHARE_V1
3824	/* OTHERUID is defined in shares.h, do not be alarmed */
3825	DefShareUid = OTHERUID;
3826#endif /* SHARE_V1 */
3827#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
3828	sun_pre_defaults(e);
3829#endif /* defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) */
3830#ifdef apollo
3831	/*
3832	**  stupid domain/os can't even open
3833	**  /etc/mail/sendmail.cf without this
3834	*/
3835
3836	setuserenv("ISP", NULL);
3837	setuserenv("SYSTYPE", NULL);
3838#endif /* apollo */
3839}
3840
3841
3842void
3843vendor_post_defaults(e)
3844	ENVELOPE *e;
3845{
3846#ifdef __QNX__
3847	char *p;
3848
3849	/* Makes sure the SOCK environment variable remains */
3850	if (p = getextenv("SOCK"))
3851		setuserenv("SOCK", p);
3852#endif /* __QNX__ */
3853#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
3854	sun_post_defaults(e);
3855#endif /* defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES) */
3856}
3857/*
3858**  VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode
3859*/
3860
3861void
3862vendor_daemon_setup(e)
3863	ENVELOPE *e;
3864{
3865#if HASSETLOGIN
3866	(void) setlogin(RunAsUserName);
3867#endif /* HASSETLOGIN */
3868#if SECUREWARE
3869	if (getluid() != -1)
3870	{
3871		usrerr("Daemon cannot have LUID");
3872		finis(false, true, EX_USAGE);
3873	}
3874#endif /* SECUREWARE */
3875}
3876/*
3877**  VENDOR_SET_UID -- do setup for setting a user id
3878**
3879**	This is called when we are still root.
3880**
3881**	Parameters:
3882**		uid -- the uid we are about to become.
3883**
3884**	Returns:
3885**		none.
3886*/
3887
3888void
3889vendor_set_uid(uid)
3890	UID_T uid;
3891{
3892	/*
3893	**  We need to setup the share groups (lnodes)
3894	**  and add auditing information (luid's)
3895	**  before we loose our ``root''ness.
3896	*/
3897#if SHARE_V1
3898	if (setupshares(uid, syserr) != 0)
3899		syserr("Unable to set up shares");
3900#endif /* SHARE_V1 */
3901#if SECUREWARE
3902	(void) setup_secure(uid);
3903#endif /* SECUREWARE */
3904}
3905/*
3906**  VALIDATE_CONNECTION -- check connection for rationality
3907**
3908**	If the connection is rejected, this routine should log an
3909**	appropriate message -- but should never issue any SMTP protocol.
3910**
3911**	Parameters:
3912**		sap -- a pointer to a SOCKADDR naming the peer.
3913**		hostname -- the name corresponding to sap.
3914**		e -- the current envelope.
3915**
3916**	Returns:
3917**		error message from rejection.
3918**		NULL if not rejected.
3919*/
3920
3921#if TCPWRAPPERS
3922# include <tcpd.h>
3923
3924/* tcpwrappers does no logging, but you still have to declare these -- ugh */
3925int	allow_severity	= LOG_INFO;
3926int	deny_severity	= LOG_NOTICE;
3927#endif /* TCPWRAPPERS */
3928
3929char *
3930validate_connection(sap, hostname, e)
3931	SOCKADDR *sap;
3932	char *hostname;
3933	ENVELOPE *e;
3934{
3935#if TCPWRAPPERS
3936	char *host;
3937	char *addr;
3938	extern int hosts_ctl();
3939#endif /* TCPWRAPPERS */
3940
3941	if (tTd(48, 3))
3942		sm_dprintf("validate_connection(%s, %s)\n",
3943			hostname, anynet_ntoa(sap));
3944
3945	if (rscheck("check_relay", hostname, anynet_ntoa(sap),
3946		    e, RSF_RMCOMM|RSF_COUNT, 3, NULL, NOQID) != EX_OK)
3947	{
3948		static char reject[BUFSIZ*2];
3949		extern char MsgBuf[];
3950
3951		if (tTd(48, 4))
3952			sm_dprintf("  ... validate_connection: BAD (rscheck)\n");
3953
3954		if (strlen(MsgBuf) >= 3)
3955			(void) sm_strlcpy(reject, MsgBuf, sizeof reject);
3956		else
3957			(void) sm_strlcpy(reject, "Access denied", sizeof reject);
3958
3959		return reject;
3960	}
3961
3962#if TCPWRAPPERS
3963	if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']')
3964		host = "unknown";
3965	else
3966		host = hostname;
3967	addr = anynet_ntoa(sap);
3968
3969# if NETINET6
3970	/* TCP/Wrappers don't want the IPv6: protocol label */
3971	if (addr != NULL && sm_strncasecmp(addr, "IPv6:", 5) == 0)
3972		addr += 5;
3973# endif /* NETINET6 */
3974
3975	if (!hosts_ctl("sendmail", host, addr, STRING_UNKNOWN))
3976	{
3977		if (tTd(48, 4))
3978			sm_dprintf("  ... validate_connection: BAD (tcpwrappers)\n");
3979		if (LogLevel > 3)
3980			sm_syslog(LOG_NOTICE, e->e_id,
3981				  "tcpwrappers (%s, %s) rejection",
3982				  host, addr);
3983		return "Access denied";
3984	}
3985#endif /* TCPWRAPPERS */
3986	if (tTd(48, 4))
3987		sm_dprintf("  ... validate_connection: OK\n");
3988	return NULL;
3989}
3990
3991/*
3992**  STRTOL -- convert string to long integer
3993**
3994**	For systems that don't have it in the C library.
3995**
3996**	This is taken verbatim from the 4.4-Lite C library.
3997*/
3998
3999#if NEEDSTRTOL
4000
4001# if defined(LIBC_SCCS) && !defined(lint)
4002static char sccsid[] = "@(#)strtol.c	8.1 (Berkeley) 6/4/93";
4003# endif /* defined(LIBC_SCCS) && !defined(lint) */
4004
4005/*
4006**  Convert a string to a long integer.
4007**
4008**  Ignores `locale' stuff.  Assumes that the upper and lower case
4009**  alphabets and digits are each contiguous.
4010*/
4011
4012long
4013strtol(nptr, endptr, base)
4014	const char *nptr;
4015	char **endptr;
4016	register int base;
4017{
4018	register const char *s = nptr;
4019	register unsigned long acc;
4020	register int c;
4021	register unsigned long cutoff;
4022	register int neg = 0, any, cutlim;
4023
4024	/*
4025	**  Skip white space and pick up leading +/- sign if any.
4026	**  If base is 0, allow 0x for hex and 0 for octal, else
4027	**  assume decimal; if base is already 16, allow 0x.
4028	*/
4029	do {
4030		c = *s++;
4031	} while (isspace(c));
4032	if (c == '-') {
4033		neg = 1;
4034		c = *s++;
4035	} else if (c == '+')
4036		c = *s++;
4037	if ((base == 0 || base == 16) &&
4038	    c == '0' && (*s == 'x' || *s == 'X')) {
4039		c = s[1];
4040		s += 2;
4041		base = 16;
4042	}
4043	if (base == 0)
4044		base = c == '0' ? 8 : 10;
4045
4046	/*
4047	**  Compute the cutoff value between legal numbers and illegal
4048	**  numbers.  That is the largest legal value, divided by the
4049	**  base.  An input number that is greater than this value, if
4050	**  followed by a legal input character, is too big.  One that
4051	**  is equal to this value may be valid or not; the limit
4052	**  between valid and invalid numbers is then based on the last
4053	**  digit.  For instance, if the range for longs is
4054	**  [-2147483648..2147483647] and the input base is 10,
4055	**  cutoff will be set to 214748364 and cutlim to either
4056	**  7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
4057	**  a value > 214748364, or equal but the next digit is > 7 (or 8),
4058	**  the number is too big, and we will return a range error.
4059	**
4060	**  Set any if any `digits' consumed; make it negative to indicate
4061	**  overflow.
4062	*/
4063	cutoff = neg ? -(unsigned long) LONG_MIN : LONG_MAX;
4064	cutlim = cutoff % (unsigned long) base;
4065	cutoff /= (unsigned long) base;
4066	for (acc = 0, any = 0;; c = *s++) {
4067		if (isdigit(c))
4068			c -= '0';
4069		else if (isalpha(c))
4070			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
4071		else
4072			break;
4073		if (c >= base)
4074			break;
4075		if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
4076			any = -1;
4077		else {
4078			any = 1;
4079			acc *= base;
4080			acc += c;
4081		}
4082	}
4083	if (any < 0) {
4084		acc = neg ? LONG_MIN : LONG_MAX;
4085		errno = ERANGE;
4086	} else if (neg)
4087		acc = -acc;
4088	if (endptr != 0)
4089		*endptr = (char *)(any ? s - 1 : nptr);
4090	return acc;
4091}
4092
4093#endif /* NEEDSTRTOL */
4094/*
4095**  STRSTR -- find first substring in string
4096**
4097**	Parameters:
4098**		big -- the big (full) string.
4099**		little -- the little (sub) string.
4100**
4101**	Returns:
4102**		A pointer to the first instance of little in big.
4103**		big if little is the null string.
4104**		NULL if little is not contained in big.
4105*/
4106
4107#if NEEDSTRSTR
4108
4109char *
4110strstr(big, little)
4111	char *big;
4112	char *little;
4113{
4114	register char *p = big;
4115	int l;
4116
4117	if (*little == '\0')
4118		return big;
4119	l = strlen(little);
4120
4121	while ((p = strchr(p, *little)) != NULL)
4122	{
4123		if (strncmp(p, little, l) == 0)
4124			return p;
4125		p++;
4126	}
4127	return NULL;
4128}
4129
4130#endif /* NEEDSTRSTR */
4131/*
4132**  SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
4133**
4134**	Some operating systems have wierd problems with the gethostbyXXX
4135**	routines.  For example, Solaris versions at least through 2.3
4136**	don't properly deliver a canonical h_name field.  This tries to
4137**	work around these problems.
4138**
4139**	Support IPv6 as well as IPv4.
4140*/
4141
4142#if NETINET6 && NEEDSGETIPNODE
4143
4144# ifndef AI_DEFAULT
4145#  define AI_DEFAULT	0	/* dummy */
4146# endif /* ! AI_DEFAULT */
4147# ifndef AI_ADDRCONFIG
4148#  define AI_ADDRCONFIG	0	/* dummy */
4149# endif /* ! AI_ADDRCONFIG */
4150# ifndef AI_V4MAPPED
4151#  define AI_V4MAPPED	0	/* dummy */
4152# endif /* ! AI_V4MAPPED */
4153# ifndef AI_ALL
4154#  define AI_ALL	0	/* dummy */
4155# endif /* ! AI_ALL */
4156
4157static struct hostent *
4158getipnodebyname(name, family, flags, err)
4159	char *name;
4160	int family;
4161	int flags;
4162	int *err;
4163{
4164	bool resv6 = true;
4165	struct hostent *h;
4166
4167	if (family == AF_INET6)
4168	{
4169		/* From RFC2133, section 6.1 */
4170		resv6 = bitset(RES_USE_INET6, _res.options);
4171		_res.options |= RES_USE_INET6;
4172	}
4173	SM_SET_H_ERRNO(0);
4174	h = gethostbyname(name);
4175	if (!resv6)
4176		_res.options &= ~RES_USE_INET6;
4177	*err = h_errno;
4178	return h;
4179}
4180
4181static struct hostent *
4182getipnodebyaddr(addr, len, family, err)
4183	char *addr;
4184	int len;
4185	int family;
4186	int *err;
4187{
4188	struct hostent *h;
4189
4190	SM_SET_H_ERRNO(0);
4191	h = gethostbyaddr(addr, len, family);
4192	*err = h_errno;
4193	return h;
4194}
4195
4196void
4197freehostent(h)
4198	struct hostent *h;
4199{
4200	/*
4201	**  Stub routine -- if they don't have getipnodeby*(),
4202	**  they probably don't have the free routine either.
4203	*/
4204
4205	return;
4206}
4207#endif /* NETINET6 && NEEDSGETIPNODE */
4208
4209struct hostent *
4210sm_gethostbyname(name, family)
4211	char *name;
4212	int family;
4213{
4214	int save_errno;
4215	struct hostent *h = NULL;
4216#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
4217# if SOLARIS == 20300 || SOLARIS == 203
4218	static struct hostent hp;
4219	static char buf[1000];
4220	extern struct hostent *_switch_gethostbyname_r();
4221
4222	if (tTd(61, 10))
4223		sm_dprintf("_switch_gethostbyname_r(%s)... ", name);
4224	h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
4225	save_errno = errno;
4226# else /* SOLARIS == 20300 || SOLARIS == 203 */
4227	extern struct hostent *__switch_gethostbyname();
4228
4229	if (tTd(61, 10))
4230		sm_dprintf("__switch_gethostbyname(%s)... ", name);
4231	h = __switch_gethostbyname(name);
4232	save_errno = errno;
4233# endif /* SOLARIS == 20300 || SOLARIS == 203 */
4234#else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
4235	int nmaps;
4236# if NETINET6
4237	int flags = AI_DEFAULT|AI_ALL;
4238	int err;
4239# endif /* NETINET6 */
4240	char *maptype[MAXMAPSTACK];
4241	short mapreturn[MAXMAPACTIONS];
4242	char hbuf[MAXNAME];
4243
4244	if (tTd(61, 10))
4245		sm_dprintf("sm_gethostbyname(%s, %d)... ", name, family);
4246
4247# if NETINET6
4248#  if ADDRCONFIG_IS_BROKEN
4249	flags &= ~AI_ADDRCONFIG;
4250#  endif /* ADDRCONFIG_IS_BROKEN */
4251	h = getipnodebyname(name, family, flags, &err);
4252	SM_SET_H_ERRNO(err);
4253# else /* NETINET6 */
4254	h = gethostbyname(name);
4255# endif /* NETINET6 */
4256
4257	save_errno = errno;
4258	if (h == NULL)
4259	{
4260		if (tTd(61, 10))
4261			sm_dprintf("failure\n");
4262
4263		nmaps = switch_map_find("hosts", maptype, mapreturn);
4264		while (--nmaps >= 0)
4265		{
4266			if (strcmp(maptype[nmaps], "nis") == 0 ||
4267			    strcmp(maptype[nmaps], "files") == 0)
4268				break;
4269		}
4270
4271		if (nmaps >= 0)
4272		{
4273			/* try short name */
4274			if (strlen(name) > sizeof hbuf - 1)
4275			{
4276				errno = save_errno;
4277				return NULL;
4278			}
4279			(void) sm_strlcpy(hbuf, name, sizeof hbuf);
4280			(void) shorten_hostname(hbuf);
4281
4282			/* if it hasn't been shortened, there's no point */
4283			if (strcmp(hbuf, name) != 0)
4284			{
4285				if (tTd(61, 10))
4286					sm_dprintf("sm_gethostbyname(%s, %d)... ",
4287					       hbuf, family);
4288
4289# if NETINET6
4290				h = getipnodebyname(hbuf, family, flags, &err);
4291				SM_SET_H_ERRNO(err);
4292				save_errno = errno;
4293# else /* NETINET6 */
4294				h = gethostbyname(hbuf);
4295				save_errno = errno;
4296# endif /* NETINET6 */
4297			}
4298		}
4299	}
4300#endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4)) */
4301	if (tTd(61, 10))
4302	{
4303		if (h == NULL)
4304			sm_dprintf("failure\n");
4305		else
4306		{
4307			sm_dprintf("%s\n", h->h_name);
4308			if (tTd(61, 11))
4309			{
4310#if NETINET6
4311				struct in6_addr ia6;
4312				char buf6[INET6_ADDRSTRLEN];
4313#else /* NETINET6 */
4314				struct in_addr ia;
4315#endif /* NETINET6 */
4316				size_t i;
4317
4318				if (h->h_aliases != NULL)
4319					for (i = 0; h->h_aliases[i] != NULL;
4320					     i++)
4321						sm_dprintf("\talias: %s\n",
4322							h->h_aliases[i]);
4323				for (i = 0; h->h_addr_list[i] != NULL; i++)
4324				{
4325					char *addr;
4326
4327#if NETINET6
4328					memmove(&ia6, h->h_addr_list[i],
4329						IN6ADDRSZ);
4330					addr = anynet_ntop(&ia6,
4331							   buf6, sizeof buf6);
4332#else /* NETINET6 */
4333					memmove(&ia, h->h_addr_list[i],
4334						INADDRSZ);
4335					addr = (char *) inet_ntoa(ia);
4336#endif /* NETINET6 */
4337					if (addr != NULL)
4338						sm_dprintf("\taddr: %s\n", addr);
4339				}
4340			}
4341		}
4342	}
4343	errno = save_errno;
4344	return h;
4345}
4346
4347struct hostent *
4348sm_gethostbyaddr(addr, len, type)
4349	char *addr;
4350	int len;
4351	int type;
4352{
4353	struct hostent *hp;
4354
4355#if NETINET6
4356	if (type == AF_INET6 &&
4357	    IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *) addr))
4358	{
4359		/* Avoid reverse lookup for IPv6 unspecified address */
4360		SM_SET_H_ERRNO(HOST_NOT_FOUND);
4361		return NULL;
4362	}
4363#endif /* NETINET6 */
4364
4365#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204)
4366# if SOLARIS == 20300 || SOLARIS == 203
4367	{
4368		static struct hostent he;
4369		static char buf[1000];
4370		extern struct hostent *_switch_gethostbyaddr_r();
4371
4372		hp = _switch_gethostbyaddr_r(addr, len, type, &he,
4373					     buf, sizeof(buf), &h_errno);
4374	}
4375# else /* SOLARIS == 20300 || SOLARIS == 203 */
4376	{
4377		extern struct hostent *__switch_gethostbyaddr();
4378
4379		hp = __switch_gethostbyaddr(addr, len, type);
4380	}
4381# endif /* SOLARIS == 20300 || SOLARIS == 203 */
4382#else /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) */
4383# if NETINET6
4384	{
4385		int err;
4386
4387		hp = getipnodebyaddr(addr, len, type, &err);
4388		SM_SET_H_ERRNO(err);
4389	}
4390# else /* NETINET6 */
4391	hp = gethostbyaddr(addr, len, type);
4392# endif /* NETINET6 */
4393#endif /* (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) */
4394	return hp;
4395}
4396/*
4397**  SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
4398*/
4399
4400struct passwd *
4401sm_getpwnam(user)
4402	char *user;
4403{
4404#ifdef _AIX4
4405	extern struct passwd *_getpwnam_shadow(const char *, const int);
4406
4407	return _getpwnam_shadow(user, 0);
4408#else /* _AIX4 */
4409	return getpwnam(user);
4410#endif /* _AIX4 */
4411}
4412
4413struct passwd *
4414sm_getpwuid(uid)
4415	UID_T uid;
4416{
4417#if defined(_AIX4) && 0
4418	extern struct passwd *_getpwuid_shadow(const int, const int);
4419
4420	return _getpwuid_shadow(uid,0);
4421#else /* defined(_AIX4) && 0 */
4422	return getpwuid(uid);
4423#endif /* defined(_AIX4) && 0 */
4424}
4425/*
4426**  SECUREWARE_SETUP_SECURE -- Convex SecureWare setup
4427**
4428**	Set up the trusted computing environment for C2 level security
4429**	under SecureWare.
4430**
4431**	Parameters:
4432**		uid -- uid of the user to initialize in the TCB
4433**
4434**	Returns:
4435**		none
4436**
4437**	Side Effects:
4438**		Initialized the user in the trusted computing base
4439*/
4440
4441#if SECUREWARE
4442
4443# include <sys/security.h>
4444# include <prot.h>
4445
4446void
4447secureware_setup_secure(uid)
4448	UID_T uid;
4449{
4450	int rc;
4451
4452	if (getluid() != -1)
4453		return;
4454
4455	if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN)
4456	{
4457		switch (rc)
4458		{
4459		  case SSI_NO_PRPW_ENTRY:
4460			syserr("No protected passwd entry, uid = %d",
4461			       (int) uid);
4462			break;
4463
4464		  case SSI_LOCKED:
4465			syserr("Account has been disabled, uid = %d",
4466			       (int) uid);
4467			break;
4468
4469		  case SSI_RETIRED:
4470			syserr("Account has been retired, uid = %d",
4471			       (int) uid);
4472			break;
4473
4474		  case SSI_BAD_SET_LUID:
4475			syserr("Could not set LUID, uid = %d", (int) uid);
4476			break;
4477
4478		  case SSI_BAD_SET_PRIVS:
4479			syserr("Could not set kernel privs, uid = %d",
4480			       (int) uid);
4481
4482		  default:
4483			syserr("Unknown return code (%d) from set_secure_info(%d)",
4484				rc, (int) uid);
4485			break;
4486		}
4487		finis(false, true, EX_NOPERM);
4488	}
4489}
4490#endif /* SECUREWARE */
4491/*
4492**  ADD_HOSTNAMES -- Add a hostname to class 'w' based on IP address
4493**
4494**	Add hostnames to class 'w' based on the IP address read from
4495**	the network interface.
4496**
4497**	Parameters:
4498**		sa -- a pointer to a SOCKADDR containing the address
4499**
4500**	Returns:
4501**		0 if successful, -1 if host lookup fails.
4502*/
4503
4504static int
4505add_hostnames(sa)
4506	SOCKADDR *sa;
4507{
4508	struct hostent *hp;
4509	char **ha;
4510	char hnb[MAXHOSTNAMELEN];
4511
4512	/* lookup name with IP address */
4513	switch (sa->sa.sa_family)
4514	{
4515#if NETINET
4516	  case AF_INET:
4517		hp = sm_gethostbyaddr((char *) &sa->sin.sin_addr,
4518				      sizeof(sa->sin.sin_addr),
4519				      sa->sa.sa_family);
4520		break;
4521#endif /* NETINET */
4522
4523#if NETINET6
4524	  case AF_INET6:
4525		hp = sm_gethostbyaddr((char *) &sa->sin6.sin6_addr,
4526				      sizeof(sa->sin6.sin6_addr),
4527				      sa->sa.sa_family);
4528		break;
4529#endif /* NETINET6 */
4530
4531	  default:
4532		/* Give warning about unsupported family */
4533		if (LogLevel > 3)
4534			sm_syslog(LOG_WARNING, NOQID,
4535				  "Unsupported address family %d: %.100s",
4536				  sa->sa.sa_family, anynet_ntoa(sa));
4537		return -1;
4538	}
4539
4540	if (hp == NULL)
4541	{
4542		int save_errno = errno;
4543
4544		if (LogLevel > 3 &&
4545#if NETINET6
4546		    !(sa->sa.sa_family == AF_INET6 &&
4547		      IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr)) &&
4548#endif /* NETINET6 */
4549		    true)
4550			sm_syslog(LOG_WARNING, NOQID,
4551				  "gethostbyaddr(%.100s) failed: %d",
4552				  anynet_ntoa(sa),
4553#if NAMED_BIND
4554				  h_errno
4555#else /* NAMED_BIND */
4556				  -1
4557#endif /* NAMED_BIND */
4558				 );
4559		errno = save_errno;
4560		return -1;
4561	}
4562
4563	/* save its cname */
4564	if (!wordinclass((char *) hp->h_name, 'w'))
4565	{
4566		setclass('w', (char *) hp->h_name);
4567		if (tTd(0, 4))
4568			sm_dprintf("\ta.k.a.: %s\n", hp->h_name);
4569
4570		if (sm_snprintf(hnb, sizeof hnb, "[%s]", hp->h_name) < sizeof hnb
4571		    && !wordinclass((char *) hnb, 'w'))
4572			setclass('w', hnb);
4573	}
4574	else
4575	{
4576		if (tTd(0, 43))
4577			sm_dprintf("\ta.k.a.: %s (already in $=w)\n", hp->h_name);
4578	}
4579
4580	/* save all it aliases name */
4581	for (ha = hp->h_aliases; ha != NULL && *ha != NULL; ha++)
4582	{
4583		if (!wordinclass(*ha, 'w'))
4584		{
4585			setclass('w', *ha);
4586			if (tTd(0, 4))
4587				sm_dprintf("\ta.k.a.: %s\n", *ha);
4588			if (sm_snprintf(hnb, sizeof hnb,
4589				     "[%s]", *ha) < sizeof hnb &&
4590			    !wordinclass((char *) hnb, 'w'))
4591				setclass('w', hnb);
4592		}
4593		else
4594		{
4595			if (tTd(0, 43))
4596				sm_dprintf("\ta.k.a.: %s (already in $=w)\n",
4597					*ha);
4598		}
4599	}
4600#if NETINET6
4601	freehostent(hp);
4602#endif /* NETINET6 */
4603	return 0;
4604}
4605/*
4606**  LOAD_IF_NAMES -- load interface-specific names into $=w
4607**
4608**	Parameters:
4609**		none.
4610**
4611**	Returns:
4612**		none.
4613**
4614**	Side Effects:
4615**		Loads $=w with the names of all the interfaces.
4616*/
4617
4618#if !NETINET
4619# define SIOCGIFCONF_IS_BROKEN	1 /* XXX */
4620#endif /* !NETINET */
4621
4622#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
4623struct rtentry;
4624struct mbuf;
4625# ifndef SUNOS403
4626#  include <sys/time.h>
4627# endif /* ! SUNOS403 */
4628# if (_AIX4 >= 40300) && !defined(_NET_IF_H)
4629#  undef __P
4630# endif /* (_AIX4 >= 40300) && !defined(_NET_IF_H) */
4631# include <net/if.h>
4632#endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */
4633
4634void
4635load_if_names()
4636{
4637# if NETINET6 && defined(SIOCGLIFCONF)
4638#  ifdef __hpux
4639
4640    /*
4641    **  Unfortunately, HP has changed all of the structures,
4642    **  making life difficult for implementors.
4643    */
4644
4645#   define lifconf	if_laddrconf
4646#   define lifc_len	iflc_len
4647#   define lifc_buf	iflc_buf
4648#   define lifreq	if_laddrreq
4649#   define lifr_addr	iflr_addr
4650#   define lifr_name	iflr_name
4651#   define lifr_flags	iflr_flags
4652#   define ss_family	sa_family
4653#   undef SIOCGLIFNUM
4654#  endif /* __hpux */
4655
4656	int s;
4657	int i;
4658	size_t len;
4659	int numifs;
4660	char *buf;
4661	struct lifconf lifc;
4662#  ifdef SIOCGLIFNUM
4663	struct lifnum lifn;
4664#  endif /* SIOCGLIFNUM */
4665
4666	s = socket(InetMode, SOCK_DGRAM, 0);
4667	if (s == -1)
4668		return;
4669
4670	/* get the list of known IP address from the kernel */
4671#  ifdef __hpux
4672	i = ioctl(s, SIOCGIFNUM, (char *) &numifs);
4673#  endif /* __hpux */
4674#  ifdef SIOCGLIFNUM
4675	lifn.lifn_family = AF_UNSPEC;
4676	lifn.lifn_flags = 0;
4677	i = ioctl(s, SIOCGLIFNUM, (char *)&lifn);
4678	numifs = lifn.lifn_count;
4679#  endif /* SIOCGLIFNUM */
4680
4681#  if defined(__hpux) || defined(SIOCGLIFNUM)
4682	if (i < 0)
4683	{
4684		/* can't get number of interfaces -- fall back */
4685		if (tTd(0, 4))
4686			sm_dprintf("SIOCGLIFNUM failed: %s\n",
4687				   sm_errstring(errno));
4688		numifs = -1;
4689	}
4690	else if (tTd(0, 42))
4691		sm_dprintf("system has %d interfaces\n", numifs);
4692	if (numifs < 0)
4693#  endif /* defined(__hpux) || defined(SIOCGLIFNUM) */
4694		numifs = MAXINTERFACES;
4695
4696	if (numifs <= 0)
4697	{
4698		(void) close(s);
4699		return;
4700	}
4701
4702	len = lifc.lifc_len = numifs * sizeof (struct lifreq);
4703	buf = lifc.lifc_buf = xalloc(lifc.lifc_len);
4704#  ifndef __hpux
4705	lifc.lifc_family = AF_UNSPEC;
4706	lifc.lifc_flags = 0;
4707#  endif /* __hpux */
4708	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) < 0)
4709	{
4710		if (tTd(0, 4))
4711			sm_dprintf("SIOCGLIFCONF failed: %s\n",
4712				   sm_errstring(errno));
4713		(void) close(s);
4714		sm_free(buf);
4715		return;
4716	}
4717
4718	/* scan the list of IP address */
4719	if (tTd(0, 40))
4720		sm_dprintf("scanning for interface specific names, lifc_len=%ld\n",
4721			   (long) len);
4722
4723	for (i = 0; i < len && i >= 0; )
4724	{
4725		int flags;
4726		struct lifreq *ifr = (struct lifreq *)&buf[i];
4727		SOCKADDR *sa = (SOCKADDR *) &ifr->lifr_addr;
4728		int af = ifr->lifr_addr.ss_family;
4729		char *addr;
4730		char *name;
4731		struct in6_addr ia6;
4732		struct in_addr ia;
4733#  ifdef SIOCGLIFFLAGS
4734		struct lifreq ifrf;
4735#  endif /* SIOCGLIFFLAGS */
4736		char ip_addr[256];
4737		char buf6[INET6_ADDRSTRLEN];
4738
4739		/*
4740		**  We must close and recreate the socket each time
4741		**  since we don't know what type of socket it is now
4742		**  (each status function may change it).
4743		*/
4744
4745		(void) close(s);
4746
4747		s = socket(af, SOCK_DGRAM, 0);
4748		if (s == -1)
4749		{
4750			sm_free(buf); /* XXX */
4751			return;
4752		}
4753
4754		/*
4755		**  If we don't have a complete ifr structure,
4756		**  don't try to use it.
4757		*/
4758
4759		if ((len - i) < sizeof *ifr)
4760			break;
4761
4762#  ifdef BSD4_4_SOCKADDR
4763		if (sa->sa.sa_len > sizeof ifr->lifr_addr)
4764			i += sizeof ifr->lifr_name + sa->sa.sa_len;
4765		else
4766#  endif /* BSD4_4_SOCKADDR */
4767			i += sizeof *ifr;
4768
4769		if (tTd(0, 20))
4770			sm_dprintf("%s\n", anynet_ntoa(sa));
4771
4772		if (af != AF_INET && af != AF_INET6)
4773			continue;
4774
4775#  ifdef SIOCGLIFFLAGS
4776		memset(&ifrf, '\0', sizeof(struct lifreq));
4777		(void) sm_strlcpy(ifrf.lifr_name, ifr->lifr_name,
4778				  sizeof(ifrf.lifr_name));
4779		if (ioctl(s, SIOCGLIFFLAGS, (char *) &ifrf) < 0)
4780		{
4781			if (tTd(0, 4))
4782				sm_dprintf("SIOCGLIFFLAGS failed: %s\n",
4783					   sm_errstring(errno));
4784			continue;
4785		}
4786
4787		name = ifr->lifr_name;
4788		flags = ifrf.lifr_flags;
4789
4790		if (tTd(0, 41))
4791			sm_dprintf("\tflags: %lx\n", (unsigned long) flags);
4792
4793		if (!bitset(IFF_UP, flags))
4794			continue;
4795#  endif /* SIOCGLIFFLAGS */
4796
4797		ip_addr[0] = '\0';
4798
4799		/* extract IP address from the list*/
4800		switch (af)
4801		{
4802		  case AF_INET6:
4803#  ifdef __KAME__
4804			/* convert into proper scoped address */
4805			if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) ||
4806			     IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) &&
4807			    sa->sin6.sin6_scope_id == 0)
4808			{
4809				struct in6_addr *ia6p;
4810
4811				ia6p = &sa->sin6.sin6_addr;
4812				sa->sin6.sin6_scope_id = ntohs(ia6p->s6_addr[3] |
4813							       ((unsigned int)ia6p->s6_addr[2] << 8));
4814				ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0;
4815			}
4816#  endif /* __KAME__ */
4817			ia6 = sa->sin6.sin6_addr;
4818			if (IN6_IS_ADDR_UNSPECIFIED(&ia6))
4819			{
4820				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
4821				message("WARNING: interface %s is UP with %s address",
4822					name, addr == NULL ? "(NULL)" : addr);
4823				continue;
4824			}
4825
4826			/* save IP address in text from */
4827			addr = anynet_ntop(&ia6, buf6, sizeof buf6);
4828			if (addr != NULL)
4829				(void) sm_snprintf(ip_addr, sizeof ip_addr,
4830						   "[%.*s]",
4831						   (int) sizeof ip_addr - 3,
4832						   addr);
4833			break;
4834
4835		  case AF_INET:
4836			ia = sa->sin.sin_addr;
4837			if (ia.s_addr == INADDR_ANY ||
4838			    ia.s_addr == INADDR_NONE)
4839			{
4840				message("WARNING: interface %s is UP with %s address",
4841					name, inet_ntoa(ia));
4842				continue;
4843			}
4844
4845			/* save IP address in text from */
4846			(void) sm_snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
4847					(int) sizeof ip_addr - 3, inet_ntoa(ia));
4848			break;
4849		}
4850
4851		if (*ip_addr == '\0')
4852			continue;
4853
4854		if (!wordinclass(ip_addr, 'w'))
4855		{
4856			setclass('w', ip_addr);
4857			if (tTd(0, 4))
4858				sm_dprintf("\ta.k.a.: %s\n", ip_addr);
4859		}
4860
4861#  ifdef SIOCGLIFFLAGS
4862		/* skip "loopback" interface "lo" */
4863		if (DontProbeInterfaces == DPI_SKIPLOOPBACK &&
4864		    bitset(IFF_LOOPBACK, flags))
4865			continue;
4866#  endif /* SIOCGLIFFLAGS */
4867		(void) add_hostnames(sa);
4868	}
4869	sm_free(buf); /* XXX */
4870	(void) close(s);
4871# else /* NETINET6 && defined(SIOCGLIFCONF) */
4872#  if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
4873	int s;
4874	int i;
4875	struct ifconf ifc;
4876	int numifs;
4877
4878	s = socket(AF_INET, SOCK_DGRAM, 0);
4879	if (s == -1)
4880		return;
4881
4882	/* get the list of known IP address from the kernel */
4883#   if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN
4884	if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0)
4885	{
4886		/* can't get number of interfaces -- fall back */
4887		if (tTd(0, 4))
4888			sm_dprintf("SIOCGIFNUM failed: %s\n",
4889				   sm_errstring(errno));
4890		numifs = -1;
4891	}
4892	else if (tTd(0, 42))
4893		sm_dprintf("system has %d interfaces\n", numifs);
4894	if (numifs < 0)
4895#   endif /* defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN */
4896		numifs = MAXINTERFACES;
4897
4898	if (numifs <= 0)
4899	{
4900		(void) close(s);
4901		return;
4902	}
4903	ifc.ifc_len = numifs * sizeof (struct ifreq);
4904	ifc.ifc_buf = xalloc(ifc.ifc_len);
4905	if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
4906	{
4907		if (tTd(0, 4))
4908			sm_dprintf("SIOCGIFCONF failed: %s\n",
4909				   sm_errstring(errno));
4910		(void) close(s);
4911		return;
4912	}
4913
4914	/* scan the list of IP address */
4915	if (tTd(0, 40))
4916		sm_dprintf("scanning for interface specific names, ifc_len=%d\n",
4917			ifc.ifc_len);
4918
4919	for (i = 0; i < ifc.ifc_len && i >= 0; )
4920	{
4921		int af;
4922		struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
4923		SOCKADDR *sa = (SOCKADDR *) &ifr->ifr_addr;
4924#   if NETINET6
4925		char *addr;
4926		struct in6_addr ia6;
4927#   endif /* NETINET6 */
4928		struct in_addr ia;
4929#   ifdef SIOCGIFFLAGS
4930		struct ifreq ifrf;
4931#   endif /* SIOCGIFFLAGS */
4932		char ip_addr[256];
4933#   if NETINET6
4934		char buf6[INET6_ADDRSTRLEN];
4935#   endif /* NETINET6 */
4936
4937		/*
4938		**  If we don't have a complete ifr structure,
4939		**  don't try to use it.
4940		*/
4941
4942		if ((ifc.ifc_len - i) < sizeof *ifr)
4943			break;
4944
4945#   ifdef BSD4_4_SOCKADDR
4946		if (sa->sa.sa_len > sizeof ifr->ifr_addr)
4947			i += sizeof ifr->ifr_name + sa->sa.sa_len;
4948		else
4949#   endif /* BSD4_4_SOCKADDR */
4950			i += sizeof *ifr;
4951
4952		if (tTd(0, 20))
4953			sm_dprintf("%s\n", anynet_ntoa(sa));
4954
4955		af = ifr->ifr_addr.sa_family;
4956		if (af != AF_INET
4957#   if NETINET6
4958		    && af != AF_INET6
4959#   endif /* NETINET6 */
4960		    )
4961			continue;
4962
4963#   ifdef SIOCGIFFLAGS
4964		memset(&ifrf, '\0', sizeof(struct ifreq));
4965		(void) sm_strlcpy(ifrf.ifr_name, ifr->ifr_name,
4966			       sizeof(ifrf.ifr_name));
4967		(void) ioctl(s, SIOCGIFFLAGS, (char *) &ifrf);
4968		if (tTd(0, 41))
4969			sm_dprintf("\tflags: %lx\n",
4970				(unsigned long) ifrf.ifr_flags);
4971#    define IFRFREF ifrf
4972#   else /* SIOCGIFFLAGS */
4973#    define IFRFREF (*ifr)
4974#   endif /* SIOCGIFFLAGS */
4975
4976		if (!bitset(IFF_UP, IFRFREF.ifr_flags))
4977			continue;
4978
4979		ip_addr[0] = '\0';
4980
4981		/* extract IP address from the list*/
4982		switch (af)
4983		{
4984		  case AF_INET:
4985			ia = sa->sin.sin_addr;
4986			if (ia.s_addr == INADDR_ANY ||
4987			    ia.s_addr == INADDR_NONE)
4988			{
4989				message("WARNING: interface %s is UP with %s address",
4990					ifr->ifr_name, inet_ntoa(ia));
4991				continue;
4992			}
4993
4994			/* save IP address in text from */
4995			(void) sm_snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
4996					(int) sizeof ip_addr - 3,
4997					inet_ntoa(ia));
4998			break;
4999
5000#   if NETINET6
5001		  case AF_INET6:
5002#    ifdef __KAME__
5003			/* convert into proper scoped address */
5004			if ((IN6_IS_ADDR_LINKLOCAL(&sa->sin6.sin6_addr) ||
5005			     IN6_IS_ADDR_SITELOCAL(&sa->sin6.sin6_addr)) &&
5006			    sa->sin6.sin6_scope_id == 0)
5007			{
5008				struct in6_addr *ia6p;
5009
5010				ia6p = &sa->sin6.sin6_addr;
5011				sa->sin6.sin6_scope_id = ntohs(ia6p->s6_addr[3] |
5012							       ((unsigned int)ia6p->s6_addr[2] << 8));
5013				ia6p->s6_addr[2] = ia6p->s6_addr[3] = 0;
5014			}
5015#    endif /* __KAME__ */
5016			ia6 = sa->sin6.sin6_addr;
5017			if (IN6_IS_ADDR_UNSPECIFIED(&ia6))
5018			{
5019				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
5020				message("WARNING: interface %s is UP with %s address",
5021					ifr->ifr_name,
5022					addr == NULL ? "(NULL)" : addr);
5023				continue;
5024			}
5025
5026			/* save IP address in text from */
5027			addr = anynet_ntop(&ia6, buf6, sizeof buf6);
5028			if (addr != NULL)
5029				(void) sm_snprintf(ip_addr, sizeof ip_addr,
5030						   "[%.*s]",
5031						   (int) sizeof ip_addr - 3,
5032						   addr);
5033			break;
5034
5035#   endif /* NETINET6 */
5036		}
5037
5038		if (ip_addr[0] == '\0')
5039			continue;
5040
5041		if (!wordinclass(ip_addr, 'w'))
5042		{
5043			setclass('w', ip_addr);
5044			if (tTd(0, 4))
5045				sm_dprintf("\ta.k.a.: %s\n", ip_addr);
5046		}
5047
5048		/* skip "loopback" interface "lo" */
5049		if (DontProbeInterfaces == DPI_SKIPLOOPBACK &&
5050		    bitset(IFF_LOOPBACK, IFRFREF.ifr_flags))
5051			continue;
5052
5053		(void) add_hostnames(sa);
5054	}
5055	sm_free(ifc.ifc_buf); /* XXX */
5056	(void) close(s);
5057#   undef IFRFREF
5058#  endif /* defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN */
5059# endif /* NETINET6 && defined(SIOCGLIFCONF) */
5060}
5061/*
5062**  ISLOOPBACK -- is socket address in the loopback net?
5063**
5064**	Parameters:
5065**		sa -- socket address.
5066**
5067**	Returns:
5068**		true -- is socket address in the loopback net?
5069**		false -- otherwise
5070**
5071*/
5072
5073bool
5074isloopback(sa)
5075	SOCKADDR sa;
5076{
5077#if NETINET6
5078	if (IN6_IS_ADDR_LOOPBACK(&sa.sin6.sin6_addr))
5079		return true;
5080#else /* NETINET6 */
5081	/* XXX how to correctly extract IN_LOOPBACKNET part? */
5082	if (((ntohl(sa.sin.sin_addr.s_addr) & IN_CLASSA_NET)
5083	     >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
5084		return true;
5085#endif /* NETINET6 */
5086	return false;
5087}
5088/*
5089**  GET_NUM_PROCS_ONLINE -- return the number of processors currently online
5090**
5091**	Parameters:
5092**		none.
5093**
5094**	Returns:
5095**		The number of processors online.
5096*/
5097
5098static int
5099get_num_procs_online()
5100{
5101	int nproc = 0;
5102
5103#ifdef USESYSCTL
5104# if defined(CTL_HW) && defined(HW_NCPU)
5105	size_t sz;
5106	int mib[2];
5107
5108	mib[0] = CTL_HW;
5109	mib[1] = HW_NCPU;
5110	sz = (size_t) sizeof nproc;
5111	(void) sysctl(mib, 2, &nproc, &sz, NULL, 0);
5112# endif /* defined(CTL_HW) && defined(HW_NCPU) */
5113#else /* USESYSCTL */
5114# ifdef _SC_NPROCESSORS_ONLN
5115	nproc = (int) sysconf(_SC_NPROCESSORS_ONLN);
5116# else /* _SC_NPROCESSORS_ONLN */
5117#  ifdef __hpux
5118#   include <sys/pstat.h>
5119	struct pst_dynamic psd;
5120
5121	if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) != -1)
5122		nproc = psd.psd_proc_cnt;
5123#  endif /* __hpux */
5124# endif /* _SC_NPROCESSORS_ONLN */
5125#endif /* USESYSCTL */
5126
5127	if (nproc <= 0)
5128		nproc = 1;
5129	return nproc;
5130}
5131/*
5132**  SEED_RANDOM -- seed the random number generator
5133**
5134**	Parameters:
5135**		none
5136**
5137**	Returns:
5138**		none
5139*/
5140
5141void
5142seed_random()
5143{
5144#if HASSRANDOMDEV
5145	srandomdev();
5146#else /* HASSRANDOMDEV */
5147	long seed;
5148	struct timeval t;
5149
5150	seed = (long) CurrentPid;
5151	if (gettimeofday(&t, NULL) >= 0)
5152		seed += t.tv_sec + t.tv_usec;
5153
5154# if HASRANDOM
5155	(void) srandom(seed);
5156# else /* HASRANDOM */
5157	(void) srand((unsigned int) seed);
5158# endif /* HASRANDOM */
5159#endif /* HASSRANDOMDEV */
5160}
5161/*
5162**  SM_SYSLOG -- syslog wrapper to keep messages under SYSLOG_BUFSIZE
5163**
5164**	Parameters:
5165**		level -- syslog level
5166**		id -- envelope ID or NULL (NOQUEUE)
5167**		fmt -- format string
5168**		arg... -- arguments as implied by fmt.
5169**
5170**	Returns:
5171**		none
5172*/
5173
5174/* VARARGS3 */
5175void
5176#ifdef __STDC__
5177sm_syslog(int level, const char *id, const char *fmt, ...)
5178#else /* __STDC__ */
5179sm_syslog(level, id, fmt, va_alist)
5180	int level;
5181	const char *id;
5182	const char *fmt;
5183	va_dcl
5184#endif /* __STDC__ */
5185{
5186	static char *buf = NULL;
5187	static size_t bufsize;
5188	char *begin, *end;
5189	int save_errno;
5190	int seq = 1;
5191	int idlen;
5192	char buf0[MAXLINE];
5193	char *newstring;
5194	extern int SyslogPrefixLen;
5195	SM_VA_LOCAL_DECL
5196
5197	save_errno = errno;
5198	if (id == NULL)
5199	{
5200		id = "NOQUEUE";
5201		idlen = strlen(id) + SyslogPrefixLen;
5202	}
5203	else if (strcmp(id, NOQID) == 0)
5204	{
5205		id = "";
5206		idlen = SyslogPrefixLen;
5207	}
5208	else
5209		idlen = strlen(id) + SyslogPrefixLen;
5210
5211	if (buf == NULL)
5212	{
5213		buf = buf0;
5214		bufsize = sizeof buf0;
5215	}
5216
5217	for (;;)
5218	{
5219		int n;
5220
5221		/* print log message into buf */
5222		SM_VA_START(ap, fmt);
5223		n = sm_vsnprintf(buf, bufsize, fmt, ap);
5224		SM_VA_END(ap);
5225		SM_ASSERT(n > 0);
5226		if (n < bufsize)
5227			break;
5228
5229		/* String too small, redo with correct size */
5230		bufsize = n + 1;
5231		if (buf != buf0)
5232		{
5233			sm_free(buf);
5234			buf = NULL;
5235		}
5236		buf = sm_malloc_x(bufsize);
5237	}
5238
5239	/* clean up buf after it has been expanded with args */
5240	newstring = str2prt(buf);
5241	if ((strlen(newstring) + idlen + 1) < SYSLOG_BUFSIZE)
5242	{
5243#if LOG
5244		if (*id == '\0')
5245			syslog(level, "%s", newstring);
5246		else
5247			syslog(level, "%s: %s", id, newstring);
5248#else /* LOG */
5249		/*XXX should do something more sensible */
5250		if (*id == '\0')
5251			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "%s\n",
5252					     newstring);
5253		else
5254			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
5255					     "%s: %s\n", id, newstring);
5256#endif /* LOG */
5257		if (buf == buf0)
5258			buf = NULL;
5259		errno = save_errno;
5260		return;
5261	}
5262
5263/*
5264**  additional length for splitting: " ..." + 3, where 3 is magic to
5265**  have some data for the next entry.
5266*/
5267
5268#define SL_SPLIT 7
5269
5270	begin = newstring;
5271	idlen += 5;	/* strlen("[999]"), see below */
5272	while (*begin != '\0' &&
5273	       (strlen(begin) + idlen) > SYSLOG_BUFSIZE)
5274	{
5275		char save;
5276
5277		if (seq >= 999)
5278		{
5279			/* Too many messages */
5280			break;
5281		}
5282		end = begin + SYSLOG_BUFSIZE - idlen - SL_SPLIT;
5283		while (end > begin)
5284		{
5285			/* Break on comma or space */
5286			if (*end == ',' || *end == ' ')
5287			{
5288				end++;	  /* Include separator */
5289				break;
5290			}
5291			end--;
5292		}
5293		/* No separator, break midstring... */
5294		if (end == begin)
5295			end = begin + SYSLOG_BUFSIZE - idlen - SL_SPLIT;
5296		save = *end;
5297		*end = 0;
5298#if LOG
5299		syslog(level, "%s[%d]: %s ...", id, seq++, begin);
5300#else /* LOG */
5301		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
5302				     "%s[%d]: %s ...\n", id, seq++, begin);
5303#endif /* LOG */
5304		*end = save;
5305		begin = end;
5306	}
5307	if (seq >= 999)
5308#if LOG
5309		syslog(level, "%s[%d]: log terminated, too many parts",
5310			id, seq);
5311#else /* LOG */
5312		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
5313			      "%s[%d]: log terminated, too many parts\n", id, seq);
5314#endif /* LOG */
5315	else if (*begin != '\0')
5316#if LOG
5317		syslog(level, "%s[%d]: %s", id, seq, begin);
5318#else /* LOG */
5319		(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
5320				     "%s[%d]: %s\n", id, seq, begin);
5321#endif /* LOG */
5322	if (buf == buf0)
5323		buf = NULL;
5324	errno = save_errno;
5325}
5326/*
5327**  HARD_SYSLOG -- call syslog repeatedly until it works
5328**
5329**	Needed on HP-UX, which apparently doesn't guarantee that
5330**	syslog succeeds during interrupt handlers.
5331*/
5332
5333#if defined(__hpux) && !defined(HPUX11)
5334
5335# define MAXSYSLOGTRIES	100
5336# undef syslog
5337# ifdef V4FS
5338#  define XCNST	const
5339#  define CAST	(const char *)
5340# else /* V4FS */
5341#  define XCNST
5342#  define CAST
5343# endif /* V4FS */
5344
5345void
5346# ifdef __STDC__
5347hard_syslog(int pri, XCNST char *msg, ...)
5348# else /* __STDC__ */
5349hard_syslog(pri, msg, va_alist)
5350	int pri;
5351	XCNST char *msg;
5352	va_dcl
5353# endif /* __STDC__ */
5354{
5355	int i;
5356	char buf[SYSLOG_BUFSIZE];
5357	SM_VA_LOCAL_DECL
5358
5359	SM_VA_START(ap, msg);
5360	(void) sm_vsnprintf(buf, sizeof buf, msg, ap);
5361	SM_VA_END(ap);
5362
5363	for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; )
5364		continue;
5365}
5366
5367# undef CAST
5368#endif /* defined(__hpux) && !defined(HPUX11) */
5369#if NEEDLOCAL_HOSTNAME_LENGTH
5370/*
5371**  LOCAL_HOSTNAME_LENGTH
5372**
5373**	This is required to get sendmail to compile against BIND 4.9.x
5374**	on Ultrix.
5375**
5376**	Unfortunately, a Compaq Y2K patch kit provides it without
5377**	bumping __RES in /usr/include/resolv.h so we can't automatically
5378**	figure out whether it is needed.
5379*/
5380
5381int
5382local_hostname_length(hostname)
5383	char *hostname;
5384{
5385	size_t len_host, len_domain;
5386
5387	if (!*_res.defdname)
5388		res_init();
5389	len_host = strlen(hostname);
5390	len_domain = strlen(_res.defdname);
5391	if (len_host > len_domain &&
5392	    (sm_strcasecmp(hostname + len_host - len_domain,
5393			_res.defdname) == 0) &&
5394	    hostname[len_host - len_domain - 1] == '.')
5395		return len_host - len_domain - 1;
5396	else
5397		return 0;
5398}
5399#endif /* NEEDLOCAL_HOSTNAME_LENGTH */
5400
5401#if NEEDLINK
5402/*
5403**  LINK -- clone a file
5404**
5405**	Some OS's lacks link() and hard links.  Since sendmail is using
5406**	link() as an efficient way to clone files, this implementation
5407**	will simply do a file copy.
5408**
5409**	NOTE: This link() replacement is not a generic replacement as it
5410**	does not handle all of the semantics of the real link(2).
5411**
5412**	Parameters:
5413**		source -- pathname of existing file.
5414**		target -- pathname of link (clone) to be created.
5415**
5416**	Returns:
5417**		0 -- success.
5418**		-1 -- failure, see errno for details.
5419*/
5420
5421int
5422link(source, target)
5423	const char *source;
5424	const char *target;
5425{
5426	int save_errno;
5427	int sff;
5428	int src = -1, dst = -1;
5429	ssize_t readlen;
5430	ssize_t writelen;
5431	char buf[BUFSIZ];
5432	struct stat st;
5433
5434	sff = SFF_REGONLY|SFF_OPENASROOT;
5435	if (DontLockReadFiles)
5436		sff |= SFF_NOLOCK;
5437
5438	/* Open the original file */
5439	src = safeopen((char *)source, O_RDONLY, 0, sff);
5440	if (src < 0)
5441		goto fail;
5442
5443	/* Obtain the size and the mode */
5444	if (fstat(src, &st) < 0)
5445		goto fail;
5446
5447	/* Create the duplicate copy */
5448	sff &= ~SFF_NOLOCK;
5449	sff |= SFF_CREAT;
5450	dst = safeopen((char *)target, O_CREAT|O_EXCL|O_WRONLY,
5451		       st.st_mode, sff);
5452	if (dst < 0)
5453		goto fail;
5454
5455	/* Copy all of the bytes one buffer at a time */
5456	while ((readlen = read(src, &buf, sizeof(buf))) > 0)
5457	{
5458		ssize_t left = readlen;
5459		char *p = buf;
5460
5461		while (left > 0 &&
5462		       (writelen = write(dst, p, (size_t) left)) >= 0)
5463		{
5464			left -= writelen;
5465			p += writelen;
5466		}
5467		if (writelen < 0)
5468			break;
5469	}
5470
5471	/* Any trouble reading? */
5472	if (readlen < 0 || writelen < 0)
5473		goto fail;
5474
5475	/* Close the input file */
5476	if (close(src) < 0)
5477	{
5478		src = -1;
5479		goto fail;
5480	}
5481	src = -1;
5482
5483	/* Close the output file */
5484	if (close(dst) < 0)
5485	{
5486		/* don't set dst = -1 here so we unlink the file */
5487		goto fail;
5488	}
5489
5490	/* Success */
5491	return 0;
5492
5493 fail:
5494	save_errno = errno;
5495	if (src >= 0)
5496		(void) close(src);
5497	if (dst >= 0)
5498	{
5499		(void) unlink(target);
5500		(void) close(dst);
5501	}
5502	errno = save_errno;
5503	return -1;
5504}
5505#endif /* NEEDLINK */
5506
5507/*
5508**  Compile-Time options
5509*/
5510
5511char	*CompileOptions[] =
5512{
5513#if NAMED_BIND
5514# if DNSMAP
5515	"DNSMAP",
5516# endif /* DNSMAP */
5517#endif /* NAMED_BIND */
5518#if EGD
5519	"EGD",
5520#endif /* EGD */
5521#if HESIOD
5522	"HESIOD",
5523#endif /* HESIOD */
5524#if HES_GETMAILHOST
5525	"HES_GETMAILHOST",
5526#endif /* HES_GETMAILHOST */
5527#if LDAPMAP
5528	"LDAPMAP",
5529#endif /* LDAPMAP */
5530#if LOG
5531	"LOG",
5532#endif /* LOG */
5533#if MAP_NSD
5534	"MAP_NSD",
5535#endif /* MAP_NSD */
5536#if MAP_REGEX
5537	"MAP_REGEX",
5538#endif /* MAP_REGEX */
5539#if MATCHGECOS
5540	"MATCHGECOS",
5541#endif /* MATCHGECOS */
5542#if MILTER
5543	"MILTER",
5544#endif /* MILTER */
5545#if MIME7TO8
5546	"MIME7TO8",
5547#endif /* MIME7TO8 */
5548#if MIME8TO7
5549	"MIME8TO7",
5550#endif /* MIME8TO7 */
5551#if NAMED_BIND
5552	"NAMED_BIND",
5553#endif /* NAMED_BIND */
5554#if NDBM
5555	"NDBM",
5556#endif /* NDBM */
5557#if NETINET
5558	"NETINET",
5559#endif /* NETINET */
5560#if NETINET6
5561	"NETINET6",
5562#endif /* NETINET6 */
5563#if NETINFO
5564	"NETINFO",
5565#endif /* NETINFO */
5566#if NETISO
5567	"NETISO",
5568#endif /* NETISO */
5569#if NETNS
5570	"NETNS",
5571#endif /* NETNS */
5572#if NETUNIX
5573	"NETUNIX",
5574#endif /* NETUNIX */
5575#if NETX25
5576	"NETX25",
5577#endif /* NETX25 */
5578#if NEWDB
5579	"NEWDB",
5580#endif /* NEWDB */
5581#if NIS
5582	"NIS",
5583#endif /* NIS */
5584#if NISPLUS
5585	"NISPLUS",
5586#endif /* NISPLUS */
5587#if NO_DH
5588	"NO_DH",
5589#endif /* NO_DH */
5590#if PH_MAP
5591	"PH_MAP",
5592#endif /* PH_MAP */
5593#ifdef PICKY_HELO_CHECK
5594	"PICKY_HELO_CHECK",
5595#endif /* PICKY_HELO_CHECK */
5596#if PIPELINING
5597	"PIPELINING",
5598#endif /* PIPELINING */
5599#if SASL
5600# if SASL >= 20000
5601	"SASLv2",
5602# else /* SASL >= 20000 */
5603	"SASL",
5604# endif /* SASL >= 20000 */
5605#endif /* SASL */
5606#if SCANF
5607	"SCANF",
5608#endif /* SCANF */
5609#if SMTPDEBUG
5610	"SMTPDEBUG",
5611#endif /* SMTPDEBUG */
5612#if STARTTLS
5613	"STARTTLS",
5614#endif /* STARTTLS */
5615#if SUID_ROOT_FILES_OK
5616	"SUID_ROOT_FILES_OK",
5617#endif /* SUID_ROOT_FILES_OK */
5618#if TCPWRAPPERS
5619	"TCPWRAPPERS",
5620#endif /* TCPWRAPPERS */
5621#if TLS_NO_RSA
5622	"TLS_NO_RSA",
5623#endif /* TLS_NO_RSA */
5624#if TLS_VRFY_PER_CTX
5625	"TLS_VRFY_PER_CTX",
5626#endif /* TLS_VRFY_PER_CTX */
5627#if USERDB
5628	"USERDB",
5629#endif /* USERDB */
5630#if USE_LDAP_INIT
5631	"USE_LDAP_INIT",
5632#endif /* USE_LDAP_INIT */
5633#if XDEBUG
5634	"XDEBUG",
5635#endif /* XDEBUG */
5636#if XLA
5637	"XLA",
5638#endif /* XLA */
5639	NULL
5640};
5641
5642
5643/*
5644**  OS compile options.
5645*/
5646
5647char	*OsCompileOptions[] =
5648{
5649#if ADDRCONFIG_IS_BROKEN
5650	"ADDRCONFIG_IS_BROKEN",
5651#endif /* ADDRCONFIG_IS_BROKEN */
5652#ifdef AUTO_NETINFO_HOSTS
5653	"AUTO_NETINFO_HOSTS",
5654#endif /* AUTO_NETINFO_HOSTS */
5655#ifdef AUTO_NIS_ALIASES
5656	"AUTO_NIS_ALIASES",
5657#endif /* AUTO_NIS_ALIASES */
5658#if BROKEN_RES_SEARCH
5659	"BROKEN_RES_SEARCH",
5660#endif /* BROKEN_RES_SEARCH */
5661#ifdef BSD4_4_SOCKADDR
5662	"BSD4_4_SOCKADDR",
5663#endif /* BSD4_4_SOCKADDR */
5664#if BOGUS_O_EXCL
5665	"BOGUS_O_EXCL",
5666#endif /* BOGUS_O_EXCL */
5667#if DEC_OSF_BROKEN_GETPWENT
5668	"DEC_OSF_BROKEN_GETPWENT",
5669#endif /* DEC_OSF_BROKEN_GETPWENT */
5670#if FAST_PID_RECYCLE
5671	"FAST_PID_RECYCLE",
5672#endif /* FAST_PID_RECYCLE */
5673#if HASFCHOWN
5674	"HASFCHOWN",
5675#endif /* HASFCHOWN */
5676#if HASFCHMOD
5677	"HASFCHMOD",
5678#endif /* HASFCHMOD */
5679#if HASFLOCK
5680	"HASFLOCK",
5681#endif /* HASFLOCK */
5682#if HASGETDTABLESIZE
5683	"HASGETDTABLESIZE",
5684#endif /* HASGETDTABLESIZE */
5685#if HASGETUSERSHELL
5686	"HASGETUSERSHELL",
5687#endif /* HASGETUSERSHELL */
5688#if HASINITGROUPS
5689	"HASINITGROUPS",
5690#endif /* HASINITGROUPS */
5691#if HASLSTAT
5692	"HASLSTAT",
5693#endif /* HASLSTAT */
5694#if HASNICE
5695	"HASNICE",
5696#endif /* HASNICE */
5697#if HASRANDOM
5698	"HASRANDOM",
5699#endif /* HASRANDOM */
5700#if HASRRESVPORT
5701	"HASRRESVPORT",
5702#endif /* HASRRESVPORT */
5703#if HASSETEGID
5704	"HASSETEGID",
5705#endif /* HASSETEGID */
5706#if HASSETLOGIN
5707	"HASSETLOGIN",
5708#endif /* HASSETLOGIN */
5709#if HASSETREGID
5710	"HASSETREGID",
5711#endif /* HASSETREGID */
5712#if HASSETRESGID
5713	"HASSETRESGID",
5714#endif /* HASSETRESGID */
5715#if HASSETREUID
5716	"HASSETREUID",
5717#endif /* HASSETREUID */
5718#if HASSETRLIMIT
5719	"HASSETRLIMIT",
5720#endif /* HASSETRLIMIT */
5721#if HASSETSID
5722	"HASSETSID",
5723#endif /* HASSETSID */
5724#if HASSETUSERCONTEXT
5725	"HASSETUSERCONTEXT",
5726#endif /* HASSETUSERCONTEXT */
5727#if HASSETVBUF
5728	"HASSETVBUF",
5729#endif /* HASSETVBUF */
5730#if HAS_ST_GEN
5731	"HAS_ST_GEN",
5732#endif /* HAS_ST_GEN */
5733#if HASSRANDOMDEV
5734	"HASSRANDOMDEV",
5735#endif /* HASSRANDOMDEV */
5736#if HASURANDOMDEV
5737	"HASURANDOMDEV",
5738#endif /* HASURANDOMDEV */
5739#if HASSTRERROR
5740	"HASSTRERROR",
5741#endif /* HASSTRERROR */
5742#if HASULIMIT
5743	"HASULIMIT",
5744#endif /* HASULIMIT */
5745#if HASUNAME
5746	"HASUNAME",
5747#endif /* HASUNAME */
5748#if HASUNSETENV
5749	"HASUNSETENV",
5750#endif /* HASUNSETENV */
5751#if HASWAITPID
5752	"HASWAITPID",
5753#endif /* HASWAITPID */
5754#if IDENTPROTO
5755	"IDENTPROTO",
5756#endif /* IDENTPROTO */
5757#if IP_SRCROUTE
5758	"IP_SRCROUTE",
5759#endif /* IP_SRCROUTE */
5760#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
5761	"LOCK_ON_OPEN",
5762#endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
5763#if NEEDFSYNC
5764	"NEEDFSYNC",
5765#endif /* NEEDFSYNC */
5766#if NEEDLINK
5767	"NEEDLINK",
5768#endif /* NEEDLINK */
5769#if NEEDLOCAL_HOSTNAME_LENGTH
5770	"NEEDLOCAL_HOSTNAME_LENGTH",
5771#endif /* NEEDLOCAL_HOSTNAME_LENGTH */
5772#if NEEDSGETIPNODE
5773	"NEEDSGETIPNODE",
5774#endif /* NEEDSGETIPNODE */
5775#if NEEDSTRSTR
5776	"NEEDSTRSTR",
5777#endif /* NEEDSTRSTR */
5778#if NEEDSTRTOL
5779	"NEEDSTRTOL",
5780#endif /* NEEDSTRTOL */
5781#ifdef NO_GETSERVBYNAME
5782	"NO_GETSERVBYNAME",
5783#endif /* NO_GETSERVBYNAME */
5784#if NOFTRUNCATE
5785	"NOFTRUNCATE",
5786#endif /* NOFTRUNCATE */
5787#if REQUIRES_DIR_FSYNC
5788	"REQUIRES_DIR_FSYNC",
5789#endif /* REQUIRES_DIR_FSYNC */
5790#if RLIMIT_NEEDS_SYS_TIME_H
5791	"RLIMIT_NEEDS_SYS_TIME_H",
5792#endif /* RLIMIT_NEEDS_SYS_TIME_H */
5793#if SAFENFSPATHCONF
5794	"SAFENFSPATHCONF",
5795#endif /* SAFENFSPATHCONF */
5796#if SECUREWARE
5797	"SECUREWARE",
5798#endif /* SECUREWARE */
5799#if SHARE_V1
5800	"SHARE_V1",
5801#endif /* SHARE_V1 */
5802#if SIOCGIFCONF_IS_BROKEN
5803	"SIOCGIFCONF_IS_BROKEN",
5804#endif /* SIOCGIFCONF_IS_BROKEN */
5805#if SIOCGIFNUM_IS_BROKEN
5806	"SIOCGIFNUM_IS_BROKEN",
5807#endif /* SIOCGIFNUM_IS_BROKEN */
5808#if SNPRINTF_IS_BROKEN
5809	"SNPRINTF_IS_BROKEN",
5810#endif /* SNPRINTF_IS_BROKEN */
5811#if SO_REUSEADDR_IS_BROKEN
5812	"SO_REUSEADDR_IS_BROKEN",
5813#endif /* SO_REUSEADDR_IS_BROKEN */
5814#if SYS5SETPGRP
5815	"SYS5SETPGRP",
5816#endif /* SYS5SETPGRP */
5817#if SYSTEM5
5818	"SYSTEM5",
5819#endif /* SYSTEM5 */
5820#if USE_DOUBLE_FORK
5821	"USE_DOUBLE_FORK",
5822#endif /* USE_DOUBLE_FORK */
5823#if USE_ENVIRON
5824	"USE_ENVIRON",
5825#endif /* USE_ENVIRON */
5826#if USE_SA_SIGACTION
5827	"USE_SA_SIGACTION",
5828#endif /* USE_SA_SIGACTION */
5829#if USE_SIGLONGJMP
5830	"USE_SIGLONGJMP",
5831#endif /* USE_SIGLONGJMP */
5832#if USEGETCONFATTR
5833	"USEGETCONFATTR",
5834#endif /* USEGETCONFATTR */
5835#if USESETEUID
5836	"USESETEUID",
5837#endif /* USESETEUID */
5838#ifdef USESYSCTL
5839	"USESYSCTL",
5840#endif /* USESYSCTL */
5841#if USING_NETSCAPE_LDAP
5842	"USING_NETSCAPE_LDAP",
5843#endif /* USING_NETSCAPE_LDAP */
5844#ifdef WAITUNION
5845	"WAITUNION",
5846#endif /* WAITUNION */
5847	NULL
5848};
5849
5850/*
5851**  FFR compile options.
5852*/
5853
5854char	*FFRCompileOptions[] =
5855{
5856#if _FFR_ADAPTIVE_EOL
5857	"_FFR_ADAPTIVE_EOL",
5858#endif /* _FFR_ADAPTIVE_EOL */
5859#if _FFR_ALLOW_SASLINFO
5860	"_FFR_ALLOW_SASLINFO",
5861#endif /* _FFR_ALLOW_SASLINFO */
5862#if _FFR_ALLOW_S0_ERROR_4XX
5863	"_FFR_ALLOW_S0_ERROR_4XX",
5864#endif /* _FFR_ALLOW_S0_ERROR_4XX */
5865#if _FFR_BESTMX_BETTER_TRUNCATION
5866	"_FFR_BESTMX_BETTER_TRUNCATION",
5867#endif /* _FFR_BESTMX_BETTER_TRUNCATION */
5868#if _FFR_CACHE_LPC
5869/* Christophe Wolfhugel of France Telecom Oleane */
5870	"_FFR_CACHE_LPC",
5871#endif /* _FFR_CACHE_LPC */
5872#if _FFR_CATCH_BROKEN_MTAS
5873	"_FFR_CATCH_BROKEN_MTAS",
5874#endif /* _FFR_CATCH_BROKEN_MTAS */
5875#if _FFR_CATCH_LONG_STRINGS
5876	"_FFR_CATCH_LONG_STRINGS",
5877#endif /* _FFR_CATCH_LONG_STRINGS */
5878#if _FFR_CHECK_EOM
5879	"_FFR_CHECK_EOM",
5880#endif /* _FFR_CHECK_EOM */
5881#if _FFR_CHK_QUEUE
5882	"_FFR_CHK_QUEUE",
5883#endif /* _FFR_CHK_QUEUE */
5884#if _FFR_CONTROL_MSTAT
5885	"_FFR_CONTROL_MSTAT",
5886#endif /* _FFR_CONTROL_MSTAT */
5887#if _FFR_DAEMON_NETUNIX
5888	"_FFR_DAEMON_NETUNIX",
5889#endif /* _FFR_DAEMON_NETUNIX */
5890#if _FFR_DEPRECATE_MAILER_FLAG_I
5891	"_FFR_DEPRECATE_MAILER_FLAG_I",
5892#endif /* _FFR_DEPRECATE_MAILER_FLAG_I */
5893#if _FFR_DIGUNIX_SAFECHOWN
5894/* Problem noted by Anne Bennett of Concordia University */
5895	"_FFR_DIGUNIX_SAFECHOWN",
5896#endif /* _FFR_DIGUNIX_SAFECHOWN */
5897#if _FFR_DNSMAP_ALIASABLE
5898/* Don Lewis of TDK */
5899	"_FFR_DNSMAP_ALIASABLE",
5900#endif /* _FFR_DNSMAP_ALIASABLE */
5901#if _FFR_DNSMAP_BASE
5902	"_FFR_DNSMAP_BASE",
5903#endif /* _FFR_DNSMAP_BASE */
5904#if _FFR_DNSMAP_MULTI
5905	"_FFR_DNSMAP_MULTI",
5906# if _FFR_DNSMAP_MULTILIMIT
5907	"_FFR_DNSMAP_MULTILIMIT",
5908# endif /* _FFR_DNSMAP_MULTILIMIT */
5909#endif /* _FFR_DNSMAP_MULTI */
5910#if _FFR_DONTLOCKFILESFORREAD_OPTION
5911	"_FFR_DONTLOCKFILESFORREAD_OPTION",
5912#endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */
5913# if _FFR_DONT_STOP_LOOKING
5914/* Noted by Neil Rickert of Northern Illinois University */
5915	"_FFR_DONT_STOP_LOOKING",
5916# endif /* _FFR_DONT_STOP_LOOKING */
5917#if _FFR_DOTTED_USERNAMES
5918	"_FFR_DOTTED_USERNAMES",
5919#endif /* _FFR_DOTTED_USERNAMES */
5920#if _FFR_DROP_TRUSTUSER_WARNING
5921	"_FFR_DROP_TRUSTUSER_WARNING",
5922#endif /* _FFR_DROP_TRUSTUSER_WARNING */
5923#if _FFR_FIX_DASHT
5924	"_FFR_FIX_DASHT",
5925#endif /* _FFR_FIX_DASHT */
5926#if _FFR_FORWARD_SYSERR
5927	"_FFR_FORWARD_SYSERR",
5928#endif /* _FFR_FORWARD_SYSERR */
5929#if _FFR_GEN_ORCPT
5930	"_FFR_GEN_ORCPT",
5931#endif /* _FFR_GEN_ORCPT */
5932#if _FFR_GROUPREADABLEAUTHINFOFILE
5933	"_FFR_GROUPREADABLEAUTHINFOFILE",
5934#endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
5935#if _FFR_HANDLE_ISO8859_GECOS
5936/* Peter Eriksson of Linkopings universitet */
5937	"_FFR_HANDLE_ISO8859_GECOS",
5938#endif /* _FFR_HANDLE_ISO8859_GECOS */
5939#if _FFR_HDR_TYPE
5940	"_FFR_HDR_TYPE",
5941#endif /* _FFR_HDR_TYPE */
5942#if _FFR_HPUX_NSSWITCH
5943	"_FFR_HPUX_NSSWITCH",
5944#endif /* _FFR_HPUX_NSSWITCH */
5945#if _FFR_IGNORE_EXT_ON_HELO
5946	"_FFR_IGNORE_EXT_ON_HELO",
5947#endif /* _FFR_IGNORE_EXT_ON_HELO */
5948#if _FFR_LDAP_RECURSION
5949/* Andrew Baucom */
5950	"_FFR_LDAP_RECURSION",
5951#endif /* _FFR_LDAP_RECURSION */
5952#if _FFR_LDAP_SETVERSION
5953	"_FFR_LDAP_SETVERSION",
5954#endif /* _FFR_LDAP_SETVERSION */
5955#if _FFR_LDAP_URI
5956	"_FFR_LDAP_URI",
5957#endif /* _FFR_LDAP_URI */
5958#if _FFR_MAX_FORWARD_ENTRIES
5959/* Randall S. Winchester of the University of Maryland */
5960	"_FFR_MAX_FORWARD_ENTRIES",
5961#endif /* _FFR_MAX_FORWARD_ENTRIES */
5962#if MILTER
5963# if  _FFR_MILTER_PERDAEMON
5964	"_FFR_MILTER_PERDAEMON",
5965# endif /* _FFR_MILTER_PERDAEMON */
5966#endif /* MILTER */
5967#if _FFR_NODELAYDSN_ON_HOLD
5968/* Steven Pitzl */
5969	"_FFR_NODELAYDSN_ON_HOLD",
5970#endif /* _FFR_NODELAYDSN_ON_HOLD */
5971#if _FFR_NONSTOP_PERSISTENCE
5972/* Suggested by Jan Krueger of digitalanswers communications consulting gmbh. */
5973	"_FFR_NONSTOP_PERSISTENCE",
5974#endif /* _FFR_NONSTOP_PERSISTENCE */
5975#if _FFR_NO_PIPE
5976	"_FFR_NO_PIPE",
5977#endif /* _FFR_NO_PIPE */
5978#if _FFR_QUARANTINE
5979	"_FFR_QUARANTINE",
5980#endif /* _FFR_QUARANTINE */
5981#if _FFR_QUEUEDELAY
5982	"_FFR_QUEUEDELAY",
5983#endif /* _FFR_QUEUEDELAY */
5984#if _FFR_QUEUE_GROUP_SORTORDER
5985/* XXX: Still need to actually use qgrp->qg_sortorder */
5986	"_FFR_QUEUE_GROUP_SORTORDER",
5987#endif /* _FFR_QUEUE_GROUP_SORTORDER */
5988#if _FFR_QUEUE_MACRO
5989	"_FFR_QUEUE_MACRO",
5990#endif /* _FFR_QUEUE_MACRO */
5991#if _FFR_QUEUE_RUN_PARANOIA
5992	"_FFR_QUEUE_RUN_PARANOIA",
5993#endif /* _FFR_QUEUE_RUN_PARANOIA */
5994#if _FFR_QUEUE_SCHED_DBG
5995	"_FFR_QUEUE_SCHED_DBG",
5996#endif /* _FFR_QUEUE_SCHED_DBG */
5997#if _FFR_REDIRECTEMPTY
5998	"_FFR_REDIRECTEMPTY",
5999#endif /* _FFR_REDIRECTEMPTY */
6000#if _FFR_RESET_MACRO_GLOBALS
6001	"_FFR_RESET_MACRO_GLOBALS",
6002#endif /* _FFR_RESET_MACRO_GLOBALS */
6003#if _FFR_RESPOND_ALL
6004	/* in vacation */
6005	"_FFR_RESPOND_ALL",
6006#endif /* _FFR_RESPOND_ALL */
6007#if _FFR_RHS
6008	"_FFR_RHS",
6009#endif /* _FFR_RHS */
6010#if _FFR_SASL_OPT_M
6011	"_FFR_SASL_OPT_M",
6012#endif /* _FFR_SASL_OPT_M */
6013#if _FFR_SELECT_SHM
6014	"_FFR_SELECT_SHM",
6015#endif /* _FFR_SELECT_SHM */
6016#if _FFR_SHM_STATUS
6017	"_FFR_SHM_STATUS",
6018#endif /* _FFR_SHM_STATUS */
6019#if _FFR_SMFI_OPENSOCKET
6020	"_FFR_SMFI_OPENSOCKET",
6021#endif /* _FFR_SMFI_OPENSOCKET */
6022#if _FFR_SMTP_SSL
6023	"_FFR_SMTP_SSL",
6024#endif /* _FFR_SMTP_SSL */
6025#if _FFR_SOFT_BOUNCE
6026	"_FFR_SOFT_BOUNCE",
6027#endif /* _FFR_SOFT_BOUNCE */
6028#if _FFR_SPT_ALIGN
6029/* Chris Adams of HiWAAY Informations Services */
6030	"_FFR_SPT_ALIGN",
6031#endif /* _FFR_SPT_ALIGN */
6032#if _FFR_TIMERS
6033	"_FFR_TIMERS",
6034#endif /* _FFR_TIMERS */
6035#if _FFR_TLS_1
6036	"_FFR_TLS_1",
6037#endif /* _FFR_TLS_1 */
6038#if _FFR_TRUSTED_QF
6039	"_FFR_TRUSTED_QF",
6040#endif /* _FFR_TRUSTED_QF */
6041#if _FFR_USE_SETLOGIN
6042/* Peter Philipp */
6043	"_FFR_USE_SETLOGIN",
6044#endif /* _FFR_USE_SETLOGIN */
6045	NULL
6046};
6047
6048