xref: /original-bsd/usr.sbin/sendmail/src/conf.c (revision 00695d63)
1 /*
2  * Copyright (c) 1983, 1995 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)conf.c	8.157 (Berkeley) 04/25/95";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include "pathnames.h"
15 # include <sys/ioctl.h>
16 # include <sys/param.h>
17 
18 /*
19 **  CONF.C -- Sendmail Configuration Tables.
20 **
21 **	Defines the configuration of this installation.
22 **
23 **	Configuration Variables:
24 **		HdrInfo -- a table describing well-known header fields.
25 **			Each entry has the field name and some flags,
26 **			which are described in sendmail.h.
27 **
28 **	Notes:
29 **		I have tried to put almost all the reasonable
30 **		configuration information into the configuration
31 **		file read at runtime.  My intent is that anything
32 **		here is a function of the version of UNIX you
33 **		are running, or is really static -- for example
34 **		the headers are a superset of widely used
35 **		protocols.  If you find yourself playing with
36 **		this file too much, you may be making a mistake!
37 */
38 
39 
40 
41 
42 /*
43 **  Header info table
44 **	Final (null) entry contains the flags used for any other field.
45 **
46 **	Not all of these are actually handled specially by sendmail
47 **	at this time.  They are included as placeholders, to let
48 **	you know that "someday" I intend to have sendmail do
49 **	something with them.
50 */
51 
52 struct hdrinfo	HdrInfo[] =
53 {
54 		/* originator fields, most to least significant  */
55 	"resent-sender",		H_FROM|H_RESENT,
56 	"resent-from",			H_FROM|H_RESENT,
57 	"resent-reply-to",		H_FROM|H_RESENT,
58 	"sender",			H_FROM,
59 	"from",				H_FROM,
60 	"reply-to",			H_FROM,
61 	"full-name",			H_ACHECK,
62 	"return-receipt-to",		H_FROM|H_RECEIPTTO,
63 	"errors-to",			H_FROM|H_ERRORSTO,
64 
65 		/* destination fields */
66 	"to",				H_RCPT,
67 	"resent-to",			H_RCPT|H_RESENT,
68 	"cc",				H_RCPT,
69 	"resent-cc",			H_RCPT|H_RESENT,
70 	"bcc",				H_RCPT|H_STRIPVAL,
71 	"resent-bcc",			H_RCPT|H_STRIPVAL|H_RESENT,
72 	"apparently-to",		H_RCPT,
73 
74 		/* message identification and control */
75 	"message-id",			0,
76 	"resent-message-id",		H_RESENT,
77 	"message",			H_EOH,
78 	"text",				H_EOH,
79 
80 		/* date fields */
81 	"date",				0,
82 	"resent-date",			H_RESENT,
83 
84 		/* trace fields */
85 	"received",			H_TRACE|H_FORCE,
86 	"x400-received",		H_TRACE|H_FORCE,
87 	"via",				H_TRACE|H_FORCE,
88 	"mail-from",			H_TRACE|H_FORCE,
89 
90 		/* miscellaneous fields */
91 	"comments",			H_FORCE,
92 	"return-path",			H_FORCE|H_ACHECK,
93 	"content-transfer-encoding",	H_CTE,
94 	"content-type",			H_CTYPE,
95 	"content-length",		H_ACHECK,
96 
97 	NULL,			0,
98 };
99 
100 
101 
102 /*
103 **  Location of system files/databases/etc.
104 */
105 
106 char	*PidFile =	_PATH_SENDMAILPID;	/* stores daemon proc id */
107 
108 
109 
110 /*
111 **  Privacy values
112 */
113 
114 struct prival PrivacyValues[] =
115 {
116 	"public",		PRIV_PUBLIC,
117 	"needmailhelo",		PRIV_NEEDMAILHELO,
118 	"needexpnhelo",		PRIV_NEEDEXPNHELO,
119 	"needvrfyhelo",		PRIV_NEEDVRFYHELO,
120 	"noexpn",		PRIV_NOEXPN,
121 	"novrfy",		PRIV_NOVRFY,
122 	"restrictmailq",	PRIV_RESTRICTMAILQ,
123 	"restrictqrun",		PRIV_RESTRICTQRUN,
124 	"authwarnings",		PRIV_AUTHWARNINGS,
125 	"noreceipts",		PRIV_NORECEIPTS,
126 	"goaway",		PRIV_GOAWAY,
127 	NULL,			0,
128 };
129 
130 
131 
132 /*
133 **  Miscellaneous stuff.
134 */
135 
136 int	DtableSize =	50;		/* max open files; reset in 4.2bsd */
137 /*
138 **  SETDEFAULTS -- set default values
139 **
140 **	Because of the way freezing is done, these must be initialized
141 **	using direct code.
142 **
143 **	Parameters:
144 **		e -- the default envelope.
145 **
146 **	Returns:
147 **		none.
148 **
149 **	Side Effects:
150 **		Initializes a bunch of global variables to their
151 **		default values.
152 */
153 
154 #define DAYS		* 24 * 60 * 60
155 
156 void
157 setdefaults(e)
158 	register ENVELOPE *e;
159 {
160 	int i;
161 	extern void inittimeouts();
162 	extern void setdefuser();
163 	extern void setupmaps();
164 	extern void setupmailers();
165 
166 	SpaceSub = ' ';				/* option B */
167 	QueueLA = 8;				/* option x */
168 	RefuseLA = 12;				/* option X */
169 	WkRecipFact = 30000L;			/* option y */
170 	WkClassFact = 1800L;			/* option z */
171 	WkTimeFact = 90000L;			/* option Z */
172 	QueueFactor = WkRecipFact * 20;		/* option q */
173 	FileMode = (RealUid != geteuid()) ? 0644 : 0600;
174 						/* option F */
175 	DefUid = 1;				/* option u */
176 	DefGid = 1;				/* option g */
177 	CheckpointInterval = 10;		/* option C */
178 	MaxHopCount = 25;			/* option h */
179 	e->e_sendmode = SM_FORK;		/* option d */
180 	e->e_errormode = EM_PRINT;		/* option e */
181 	SevenBitInput = FALSE;			/* option 7 */
182 	MaxMciCache = 1;			/* option k */
183 	MciCacheTimeout = 300;			/* option K */
184 	LogLevel = 9;				/* option L */
185 	inittimeouts(NULL);			/* option r */
186 	PrivacyFlags = 0;			/* option p */
187 	MimeMode = MM_CVTMIME|MM_PASS8BIT;	/* option 8 */
188 	for (i = 0; i < MAXTOCLASS; i++)
189 	{
190 		TimeOuts.to_q_return[i] = 5 DAYS;	/* option T */
191 		TimeOuts.to_q_warning[i] = 0;		/* option T */
192 	}
193 	ServiceSwitchFile = "/etc/service.switch";
194 	setdefuser();
195 	setupmaps();
196 	setupmailers();
197 }
198 
199 
200 /*
201 **  SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
202 */
203 
204 void
205 setdefuser()
206 {
207 	struct passwd *defpwent;
208 	static char defuserbuf[40];
209 
210 	DefUser = defuserbuf;
211 	if ((defpwent = sm_getpwuid(DefUid)) != NULL)
212 		strcpy(defuserbuf, defpwent->pw_name);
213 	else
214 		strcpy(defuserbuf, "nobody");
215 }
216 /*
217 **  HOST_MAP_INIT -- initialize host class structures
218 */
219 
220 bool	host_map_init __P((MAP *map, char *args));
221 
222 bool
223 host_map_init(map, args)
224 	MAP *map;
225 	char *args;
226 {
227 	register char *p = args;
228 
229 	for (;;)
230 	{
231 		while (isascii(*p) && isspace(*p))
232 			p++;
233 		if (*p != '-')
234 			break;
235 		switch (*++p)
236 		{
237 		  case 'a':
238 			map->map_app = ++p;
239 			break;
240 		}
241 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
242 			p++;
243 		if (*p != '\0')
244 			*p++ = '\0';
245 	}
246 	if (map->map_app != NULL)
247 		map->map_app = newstr(map->map_app);
248 	return TRUE;
249 }
250 /*
251 **  SETUPMAILERS -- initialize default mailers
252 */
253 
254 void
255 setupmailers()
256 {
257 	char buf[100];
258 	extern void makemailer();
259 
260 	strcpy(buf, "prog, P=/bin/sh, F=lsoD, T=X-Unix, A=sh -c $u");
261 	makemailer(buf);
262 
263 	strcpy(buf, "*file*, P=[FILE], F=lsDFMPEou, T=X-Unix, A=FILE");
264 	makemailer(buf);
265 
266 	strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE");
267 	makemailer(buf);
268 }
269 /*
270 **  SETUPMAPS -- set up map classes
271 */
272 
273 #define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \
274 	{ \
275 		extern bool parse __P((MAP *, char *)); \
276 		extern bool open __P((MAP *, int)); \
277 		extern void close __P((MAP *)); \
278 		extern char *lookup __P((MAP *, char *, char **, int *)); \
279 		extern void store __P((MAP *, char *, char *)); \
280 		s = stab(name, ST_MAPCLASS, ST_ENTER); \
281 		s->s_mapclass.map_cname = name; \
282 		s->s_mapclass.map_ext = ext; \
283 		s->s_mapclass.map_cflags = flags; \
284 		s->s_mapclass.map_parse = parse; \
285 		s->s_mapclass.map_open = open; \
286 		s->s_mapclass.map_close = close; \
287 		s->s_mapclass.map_lookup = lookup; \
288 		s->s_mapclass.map_store = store; \
289 	}
290 
291 void
292 setupmaps()
293 {
294 	register STAB *s;
295 
296 #ifdef NEWDB
297 	MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
298 		map_parseargs, hash_map_open, db_map_close,
299 		db_map_lookup, db_map_store);
300 
301 	MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
302 		map_parseargs, bt_map_open, db_map_close,
303 		db_map_lookup, db_map_store);
304 #endif
305 
306 #ifdef NDBM
307 	MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE,
308 		map_parseargs, ndbm_map_open, ndbm_map_close,
309 		ndbm_map_lookup, ndbm_map_store);
310 #endif
311 
312 #ifdef NIS
313 	MAPDEF("nis", NULL, MCF_ALIASOK,
314 		map_parseargs, nis_map_open, null_map_close,
315 		nis_map_lookup, null_map_store);
316 #endif
317 
318 #ifdef NISPLUS
319 	MAPDEF("nisplus", NULL, MCF_ALIASOK,
320 		map_parseargs, nisplus_map_open, null_map_close,
321 		nisplus_map_lookup, null_map_store);
322 #endif
323 
324 #ifdef HESIOD
325 	MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY,
326 		map_parseargs, null_map_open, null_map_close,
327 		hes_map_lookup, null_map_store);
328 #endif
329 
330 #ifdef NETINFO
331 	MAPDEF("netinfo", NULL, MCF_ALIASOK,
332 		map_parseargs, ni_map_open, null_map_close,
333 		ni_map_lookup, null_map_store);
334 #endif
335 
336 #if 0
337 	MAPDEF("dns", NULL, 0,
338 		dns_map_init, null_map_open, null_map_close,
339 		dns_map_lookup, null_map_store);
340 #endif
341 
342 #if NAMED_BIND
343 	/* best MX DNS lookup */
344 	MAPDEF("bestmx", NULL, MCF_OPTFILE,
345 		map_parseargs, null_map_open, null_map_close,
346 		bestmx_map_lookup, null_map_store);
347 #endif
348 
349 	MAPDEF("host", NULL, 0,
350 		host_map_init, null_map_open, null_map_close,
351 		host_map_lookup, null_map_store);
352 
353 	MAPDEF("text", NULL, MCF_ALIASOK,
354 		map_parseargs, text_map_open, null_map_close,
355 		text_map_lookup, null_map_store);
356 
357 	MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY,
358 		map_parseargs, stab_map_open, null_map_close,
359 		stab_map_lookup, stab_map_store);
360 
361 	MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE,
362 		map_parseargs, impl_map_open, impl_map_close,
363 		impl_map_lookup, impl_map_store);
364 
365 	/* access to system passwd file */
366 	MAPDEF("user", NULL, MCF_OPTFILE,
367 		map_parseargs, user_map_open, null_map_close,
368 		user_map_lookup, null_map_store);
369 
370 	/* dequote map */
371 	MAPDEF("dequote", NULL, 0,
372 		dequote_init, null_map_open, null_map_close,
373 		dequote_map, null_map_store);
374 
375 #ifdef USERDB
376 	/* user database */
377 	MAPDEF("userdb", ".db", 0,
378 		map_parseargs, null_map_open, null_map_close,
379 		udb_map_lookup, null_map_store);
380 #endif
381 
382 	/* sequenced maps */
383 	MAPDEF("sequence", NULL, MCF_ALIASOK,
384 		seq_map_parse, null_map_open, null_map_close,
385 		seq_map_lookup, seq_map_store);
386 
387 	/* switched interface to sequenced maps */
388 	MAPDEF("switch", NULL, MCF_ALIASOK,
389 		map_parseargs, switch_map_open, null_map_close,
390 		seq_map_lookup, seq_map_store);
391 }
392 
393 #undef MAPDEF
394 /*
395 **  INITHOSTMAPS -- initial host-dependent maps
396 **
397 **	This should act as an interface to any local service switch
398 **	provided by the host operating system.
399 **
400 **	Parameters:
401 **		none
402 **
403 **	Returns:
404 **		none
405 **
406 **	Side Effects:
407 **		Should define maps "host" and "users" as necessary
408 **		for this OS.  If they are not defined, they will get
409 **		a default value later.  It should check to make sure
410 **		they are not defined first, since it's possible that
411 **		the config file has provided an override.
412 */
413 
414 void
415 inithostmaps()
416 {
417 	register int i;
418 	int nmaps;
419 	char *maptype[MAXMAPSTACK];
420 	short mapreturn[MAXMAPACTIONS];
421 	char buf[MAXLINE];
422 
423 	/*
424 	**  Set up default hosts maps.
425 	*/
426 
427 #if 0
428 	nmaps = switch_map_find("hosts", maptype, mapreturn);
429 	for (i = 0; i < nmaps; i++)
430 	{
431 		if (strcmp(maptype[i], "files") == 0 &&
432 		    stab("hosts.files", ST_MAP, ST_FIND) == NULL)
433 		{
434 			strcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts");
435 			makemapentry(buf);
436 		}
437 #if NAMED_BIND
438 		else if (strcmp(maptype[i], "dns") == 0 &&
439 		    stab("hosts.dns", ST_MAP, ST_FIND) == NULL)
440 		{
441 			strcpy(buf, "hosts.dns dns A");
442 			makemapentry(buf);
443 		}
444 #endif
445 #ifdef NISPLUS
446 		else if (strcmp(maptype[i], "nisplus") == 0 &&
447 		    stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL)
448 		{
449 			strcpy(buf, "hosts.nisplus nisplus -k name -v address -d hosts.org_dir");
450 			makemapentry(buf);
451 		}
452 #endif
453 #ifdef NIS
454 		else if (strcmp(maptype[i], "nis") == 0 &&
455 		    stab("hosts.nis", ST_MAP, ST_FIND) == NULL)
456 		{
457 			strcpy(buf, "hosts.nis nis -d -k 0 -v 1 hosts.byname");
458 			makemapentry(buf);
459 		}
460 #endif
461 	}
462 #endif
463 
464 	/*
465 	**  Make sure we have a host map.
466 	*/
467 
468 	if (stab("host", ST_MAP, ST_FIND) == NULL)
469 	{
470 		/* user didn't initialize: set up host map */
471 		strcpy(buf, "host host");
472 #if NAMED_BIND
473 		if (ConfigLevel >= 2)
474 			strcat(buf, " -a.");
475 #endif
476 		makemapentry(buf);
477 	}
478 
479 	/*
480 	**  Set up default aliases maps
481 	*/
482 
483 	nmaps = switch_map_find("aliases", maptype, mapreturn);
484 	for (i = 0; i < nmaps; i++)
485 	{
486 		if (strcmp(maptype[i], "files") == 0 &&
487 		    stab("aliases.files", ST_MAP, ST_FIND) == NULL)
488 		{
489 			strcpy(buf, "aliases.files implicit /etc/aliases");
490 			makemapentry(buf);
491 		}
492 #ifdef NISPLUS
493 		else if (strcmp(maptype[i], "nisplus") == 0 &&
494 		    stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL)
495 		{
496 			strcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion -d mail_aliases.org_dir");
497 			makemapentry(buf);
498 		}
499 #endif
500 #ifdef NIS
501 		else if (strcmp(maptype[i], "nis") == 0 &&
502 		    stab("aliases.nis", ST_MAP, ST_FIND) == NULL)
503 		{
504 			strcpy(buf, "aliases.nis nis -d mail.aliases");
505 			makemapentry(buf);
506 		}
507 #endif
508 	}
509 	if (stab("aliases", ST_MAP, ST_FIND) == NULL)
510 	{
511 		strcpy(buf, "aliases switch aliases");
512 		makemapentry(buf);
513 	}
514 	strcpy(buf, "switch:aliases");
515 	setalias(buf);
516 
517 #if 0		/* "user" map class is a better choice */
518 	/*
519 	**  Set up default users maps.
520 	*/
521 
522 	nmaps = switch_map_find("passwd", maptype, mapreturn);
523 	for (i = 0; i < nmaps; i++)
524 	{
525 		if (strcmp(maptype[i], "files") == 0 &&
526 		    stab("users.files", ST_MAP, ST_FIND) == NULL)
527 		{
528 			strcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd");
529 			makemapentry(buf);
530 		}
531 #ifdef NISPLUS
532 		else if (strcmp(maptype[i], "nisplus") == 0 &&
533 		    stab("users.nisplus", ST_MAP, ST_FIND) == NULL)
534 		{
535 			strcpy(buf, "users.nisplus nisplus -m -kname -vhome -d passwd.org_dir");
536 			makemapentry(buf);
537 		}
538 #endif
539 #ifdef NIS
540 		else if (strcmp(maptype[i], "nis") == 0 &&
541 		    stab("users.nis", ST_MAP, ST_FIND) == NULL)
542 		{
543 			strcpy(buf, "users.nis nis -m -d passwd.byname");
544 			makemapentry(buf);
545 		}
546 #endif
547 #ifdef HESIOD
548 		else if (strcmp(maptype[i], "hesiod") == 0) &&
549 		    stab("users.hesiod", ST_MAP, ST_FIND) == NULL)
550 		{
551 			strcpy(buf, "users.hesiod hesiod");
552 			makemapentry(buf);
553 		}
554 #endif
555 	}
556 	if (stab("users", ST_MAP, ST_FIND) == NULL)
557 	{
558 		strcpy(buf, "users switch -m passwd");
559 		makemapentry(buf);
560 	}
561 #endif
562 }
563 /*
564 **  SWITCH_MAP_FIND -- find the list of types associated with a map
565 **
566 **	This is the system-dependent interface to the service switch.
567 **
568 **	Parameters:
569 **		service -- the name of the service of interest.
570 **		maptype -- an out-array of strings containing the types
571 **			of access to use for this service.  There can
572 **			be at most MAXMAPSTACK types for a single service.
573 **		mapreturn -- an out-array of return information bitmaps
574 **			for the map.
575 **
576 **	Returns:
577 **		The number of map types filled in, or -1 for failure.
578 */
579 
580 #ifdef SOLARIS
581 # include <nsswitch.h>
582 #endif
583 
584 #if defined(ultrix) || defined(__osf__)
585 # include <sys/svcinfo.h>
586 #endif
587 
588 int
589 switch_map_find(service, maptype, mapreturn)
590 	char *service;
591 	char *maptype[MAXMAPSTACK];
592 	short mapreturn[MAXMAPACTIONS];
593 {
594 	register FILE *fp;
595 	int svcno;
596 	static char buf[MAXLINE];
597 
598 #ifdef SOLARIS
599 	struct __nsw_switchconfig *nsw_conf;
600 	enum __nsw_parse_err pserr;
601 	struct __nsw_lookup *lk;
602 	int nsw_rc;
603 	static struct __nsw_lookup lkp0 =
604 		{ "files", {1, 0, 0, 0}, NULL, NULL };
605 	static struct __nsw_switchconfig lkp_default =
606 		{ 0, "sendmail", 3, &lkp0 };
607 
608 	if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL)
609 		lk = lkp_default.lookups;
610 	else
611 		lk = nsw_conf->lookups;
612 	svcno = 0;
613 	while (lk != NULL)
614 	{
615 		maptype[svcno] = lk->service_name;
616 		if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN)
617 			mapreturn[MA_NOTFOUND] |= 1 << svcno;
618 		if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN)
619 			mapreturn[MA_TRYAGAIN] |= 1 << svcno;
620 		if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN)
621 			mapreturn[MA_TRYAGAIN] |= 1 << svcno;
622 		svcno++;
623 		lk = lk->next;
624 	}
625 	return svcno;
626 #endif
627 
628 #if defined(ultrix) || defined(__osf__)
629 	struct svcinfo *svcinfo;
630 	int svc;
631 
632 	svcinfo = getsvc();
633 	if (svcinfo == NULL)
634 		goto punt;
635 	if (strcmp(service, "hosts") == 0)
636 		svc = SVC_HOSTS;
637 	else if (strcmp(service, "aliases") == 0)
638 		svc = SVC_ALIASES;
639 	else if (strcmp(service, "passwd") == 0)
640 		svc = SVC_PASSWD;
641 	else
642 		return -1;
643 	for (svcno = 0; svcno < SVC_PATHSIZE; svcno++)
644 	{
645 		switch (svcinfo->svcpath[svc][svcno])
646 		{
647 		  case SVC_LOCAL:
648 			maptype[svcno] = "files";
649 			break;
650 
651 		  case SVC_YP:
652 			maptype[svcno] = "nis";
653 			break;
654 
655 		  case SVC_BIND:
656 			maptype[svcno] = "dns";
657 			break;
658 
659 #ifdef SVC_HESIOD
660 		  case SVC_HESIOD:
661 			maptype[svcno] = "hesiod";
662 			break;
663 #endif
664 
665 		  case SVC_LAST:
666 			return svcno;
667 		}
668 	}
669 	return svcno;
670 #endif
671 
672 #if !defined(SOLARIS) && !defined(ultrix) && !defined(__osf__)
673 	/*
674 	**  Fall-back mechanism.
675 	*/
676 
677 	svcno = 0;
678 	fp = fopen(ServiceSwitchFile, "r");
679 	if (fp != NULL)
680 	{
681 		while (fgets(buf, sizeof buf, fp) != NULL)
682 		{
683 			register char *p;
684 
685 			p = strpbrk(buf, "#\n");
686 			if (p != NULL)
687 				*p = '\0';
688 			p = strpbrk(buf, " \t");
689 			if (p != NULL)
690 				*p++ = '\0';
691 			if (strcmp(buf, service) != 0)
692 				continue;
693 
694 			/* got the right service -- extract data */
695 			do
696 			{
697 				while (isspace(*p))
698 					p++;
699 				if (*p == '\0')
700 					break;
701 				maptype[svcno++] = p;
702 				p = strpbrk(p, " \t");
703 				if (p != NULL)
704 					*p++ = '\0';
705 			} while (p != NULL);
706 			break;
707 		}
708 		fclose(fp);
709 		return svcno;
710 	}
711 #endif
712 
713 	/* if the service file doesn't work, use an absolute fallback */
714   punt:
715 	if (strcmp(service, "aliases") == 0)
716 	{
717 		maptype[svcno++] = "files";
718 #ifdef AUTO_NIS_ALIASES
719 # ifdef NISPLUS
720 		maptype[svcno++] = "nisplus";
721 # endif
722 # ifdef NIS
723 		maptype[svcno++] = "nis";
724 # endif
725 #endif
726 		return svcno;
727 	}
728 	if (strcmp(service, "hosts") == 0)
729 	{
730 # if NAMED_BIND
731 		maptype[svcno++] = "dns";
732 # else
733 #  if defined(sun) && !defined(BSD) && !defined(SOLARIS)
734 		/* SunOS */
735 		maptype[svcno++] = "nis";
736 #  endif
737 # endif
738 		maptype[svcno++] = "files";
739 		return svcno;
740 	}
741 	return -1;
742 }
743 /*
744 **  USERNAME -- return the user id of the logged in user.
745 **
746 **	Parameters:
747 **		none.
748 **
749 **	Returns:
750 **		The login name of the logged in user.
751 **
752 **	Side Effects:
753 **		none.
754 **
755 **	Notes:
756 **		The return value is statically allocated.
757 */
758 
759 char *
760 username()
761 {
762 	static char *myname = NULL;
763 	extern char *getlogin();
764 	register struct passwd *pw;
765 
766 	/* cache the result */
767 	if (myname == NULL)
768 	{
769 		myname = getlogin();
770 		if (myname == NULL || myname[0] == '\0')
771 		{
772 			pw = sm_getpwuid(RealUid);
773 			if (pw != NULL)
774 				myname = newstr(pw->pw_name);
775 		}
776 		else
777 		{
778 			uid_t uid = RealUid;
779 
780 			myname = newstr(myname);
781 			if ((pw = sm_getpwnam(myname)) == NULL ||
782 			      (uid != 0 && uid != pw->pw_uid))
783 			{
784 				pw = sm_getpwuid(uid);
785 				if (pw != NULL)
786 					myname = newstr(pw->pw_name);
787 			}
788 		}
789 		if (myname == NULL || myname[0] == '\0')
790 		{
791 			syserr("554 Who are you?");
792 			myname = "postmaster";
793 		}
794 	}
795 
796 	return (myname);
797 }
798 /*
799 **  TTYPATH -- Get the path of the user's tty
800 **
801 **	Returns the pathname of the user's tty.  Returns NULL if
802 **	the user is not logged in or if s/he has write permission
803 **	denied.
804 **
805 **	Parameters:
806 **		none
807 **
808 **	Returns:
809 **		pathname of the user's tty.
810 **		NULL if not logged in or write permission denied.
811 **
812 **	Side Effects:
813 **		none.
814 **
815 **	WARNING:
816 **		Return value is in a local buffer.
817 **
818 **	Called By:
819 **		savemail
820 */
821 
822 char *
823 ttypath()
824 {
825 	struct stat stbuf;
826 	register char *pathn;
827 	extern char *ttyname();
828 	extern char *getlogin();
829 
830 	/* compute the pathname of the controlling tty */
831 	if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
832 	    (pathn = ttyname(0)) == NULL)
833 	{
834 		errno = 0;
835 		return (NULL);
836 	}
837 
838 	/* see if we have write permission */
839 	if (stat(pathn, &stbuf) < 0 || !bitset(02, stbuf.st_mode))
840 	{
841 		errno = 0;
842 		return (NULL);
843 	}
844 
845 	/* see if the user is logged in */
846 	if (getlogin() == NULL)
847 		return (NULL);
848 
849 	/* looks good */
850 	return (pathn);
851 }
852 /*
853 **  CHECKCOMPAT -- check for From and To person compatible.
854 **
855 **	This routine can be supplied on a per-installation basis
856 **	to determine whether a person is allowed to send a message.
857 **	This allows restriction of certain types of internet
858 **	forwarding or registration of users.
859 **
860 **	If the hosts are found to be incompatible, an error
861 **	message should be given using "usrerr" and an EX_ code
862 **	should be returned.  You can also set to->q_status to
863 **	a DSN-style status code.
864 **
865 **	EF_NO_BODY_RETN can be set in e->e_flags to suppress the
866 **	body during the return-to-sender function; this should be done
867 **	on huge messages.  This bit may already be set by the ESMTP
868 **	protocol.
869 **
870 **	Parameters:
871 **		to -- the person being sent to.
872 **
873 **	Returns:
874 **		an exit status
875 **
876 **	Side Effects:
877 **		none (unless you include the usrerr stuff)
878 */
879 
880 int
881 checkcompat(to, e)
882 	register ADDRESS *to;
883 	register ENVELOPE *e;
884 {
885 # ifdef lint
886 	if (to == NULL)
887 		to++;
888 # endif /* lint */
889 
890 	if (tTd(49, 1))
891 		printf("checkcompat(to=%s, from=%s)\n",
892 			to->q_paddr, e->e_from.q_paddr);
893 
894 # ifdef EXAMPLE_CODE
895 	/* this code is intended as an example only */
896 	register STAB *s;
897 
898 	s = stab("arpa", ST_MAILER, ST_FIND);
899 	if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 &&
900 	    to->q_mailer == s->s_mailer)
901 	{
902 		usrerr("553 No ARPA mail through this machine: see your system administration");
903 		/* e->e_flags |= EF_NO_BODY_RETN; to supress body on return */
904 		to->q_status = "5.7.1";
905 		return (EX_UNAVAILABLE);
906 	}
907 # endif /* EXAMPLE_CODE */
908 	return (EX_OK);
909 }
910 /*
911 **  SETSIGNAL -- set a signal handler
912 **
913 **	This is essentially old BSD "signal(3)".
914 */
915 
916 sigfunc_t
917 setsignal(sig, handler)
918 	int sig;
919 	sigfunc_t handler;
920 {
921 #if defined(SYS5SIGNALS) || defined(BSD4_3) || defined(_AUX_SOURCE)
922 	return signal(sig, handler);
923 #else
924 	struct sigaction n, o;
925 
926 	bzero(&n, sizeof n);
927 	n.sa_handler = handler;
928 # ifdef SA_RESTART
929 	n.sa_flags = SA_RESTART;
930 # endif
931 	if (sigaction(sig, &n, &o) < 0)
932 		return SIG_ERR;
933 	return o.sa_handler;
934 #endif
935 }
936 /*
937 **  HOLDSIGS -- arrange to hold all signals
938 **
939 **	Parameters:
940 **		none.
941 **
942 **	Returns:
943 **		none.
944 **
945 **	Side Effects:
946 **		Arranges that signals are held.
947 */
948 
949 void
950 holdsigs()
951 {
952 }
953 /*
954 **  RLSESIGS -- arrange to release all signals
955 **
956 **	This undoes the effect of holdsigs.
957 **
958 **	Parameters:
959 **		none.
960 **
961 **	Returns:
962 **		none.
963 **
964 **	Side Effects:
965 **		Arranges that signals are released.
966 */
967 
968 void
969 rlsesigs()
970 {
971 }
972 /*
973 **  INIT_MD -- do machine dependent initializations
974 **
975 **	Systems that have global modes that should be set should do
976 **	them here rather than in main.
977 */
978 
979 #ifdef _AUX_SOURCE
980 # include	<compat.h>
981 #endif
982 
983 void
984 init_md(argc, argv)
985 	int argc;
986 	char **argv;
987 {
988 #ifdef _AUX_SOURCE
989 	setcompat(getcompat() | COMPAT_BSDPROT);
990 #endif
991 
992 #ifdef VENDOR_DEFAULT
993 	VendorCode = VENDOR_DEFAULT;
994 #else
995 	VendorCode = VENDOR_BERKELEY;
996 #endif
997 }
998 /*
999 **  INIT_VENDOR_MACROS -- vendor-dependent macro initializations
1000 **
1001 **	Called once, on startup.
1002 **
1003 **	Parameters:
1004 **		e -- the global envelope.
1005 **
1006 **	Returns:
1007 **		none.
1008 **
1009 **	Side Effects:
1010 **		vendor-dependent.
1011 */
1012 
1013 void
1014 init_vendor_macros(e)
1015 	register ENVELOPE *e;
1016 {
1017 }
1018 /*
1019 **  GETLA -- get the current load average
1020 **
1021 **	This code stolen from la.c.
1022 **
1023 **	Parameters:
1024 **		none.
1025 **
1026 **	Returns:
1027 **		The current load average as an integer.
1028 **
1029 **	Side Effects:
1030 **		none.
1031 */
1032 
1033 /* try to guess what style of load average we have */
1034 #define LA_ZERO		1	/* always return load average as zero */
1035 #define LA_INT		2	/* read kmem for avenrun; interpret as long */
1036 #define LA_FLOAT	3	/* read kmem for avenrun; interpret as float */
1037 #define LA_SUBR		4	/* call getloadavg */
1038 #define LA_MACH		5	/* MACH load averages (as on NeXT boxes) */
1039 #define LA_SHORT	6	/* read kmem for avenrun; interpret as short */
1040 #define LA_PROCSTR	7	/* read string ("1.17") from /proc/loadavg */
1041 
1042 /* do guesses based on general OS type */
1043 #ifndef LA_TYPE
1044 # define LA_TYPE	LA_ZERO
1045 #endif
1046 
1047 #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT)
1048 
1049 #include <nlist.h>
1050 
1051 #ifdef IRIX64
1052 # define nlist		nlist64
1053 #endif
1054 
1055 #ifndef LA_AVENRUN
1056 # ifdef SYSTEM5
1057 #  define LA_AVENRUN	"avenrun"
1058 # else
1059 #  define LA_AVENRUN	"_avenrun"
1060 # endif
1061 #endif
1062 
1063 /* _PATH_UNIX should be defined in <paths.h> */
1064 #ifndef _PATH_UNIX
1065 # if defined(SYSTEM5)
1066 #  define _PATH_UNIX	"/unix"
1067 # else
1068 #  define _PATH_UNIX	"/vmunix"
1069 # endif
1070 #endif
1071 
1072 struct	nlist Nl[] =
1073 {
1074 	{ LA_AVENRUN },
1075 #define	X_AVENRUN	0
1076 	{ 0 },
1077 };
1078 
1079 #ifndef FSHIFT
1080 # if defined(unixpc)
1081 #  define FSHIFT	5
1082 # endif
1083 
1084 # if defined(__alpha) || defined(IRIX)
1085 #  define FSHIFT	10
1086 # endif
1087 
1088 # if defined(_AIX3)
1089 #  define FSHIFT	16
1090 # endif
1091 #endif
1092 
1093 #ifndef FSHIFT
1094 # define FSHIFT		8
1095 #endif
1096 
1097 #ifndef FSCALE
1098 # define FSCALE		(1 << FSHIFT)
1099 #endif
1100 
1101 getla()
1102 {
1103 	static int kmem = -1;
1104 #if LA_TYPE == LA_INT
1105 	long avenrun[3];
1106 #else
1107 # if LA_TYPE == LA_SHORT
1108 	short avenrun[3];
1109 # else
1110 	double avenrun[3];
1111 # endif
1112 #endif
1113 	extern off_t lseek();
1114 	extern int errno;
1115 
1116 	if (kmem < 0)
1117 	{
1118 		kmem = open("/dev/kmem", 0, 0);
1119 		if (kmem < 0)
1120 		{
1121 			if (tTd(3, 1))
1122 				printf("getla: open(/dev/kmem): %s\n",
1123 					errstring(errno));
1124 			return (-1);
1125 		}
1126 		(void) fcntl(kmem, F_SETFD, 1);
1127 #ifdef _AIX3
1128 		if (knlist(Nl, 1, sizeof Nl[0]) < 0)
1129 #else
1130 		if (nlist(_PATH_UNIX, Nl) < 0)
1131 #endif
1132 		{
1133 			if (tTd(3, 1))
1134 				printf("getla: nlist(%s): %s\n", _PATH_UNIX,
1135 					errstring(errno));
1136 			return (-1);
1137 		}
1138 		if (Nl[X_AVENRUN].n_value == 0)
1139 		{
1140 			if (tTd(3, 1))
1141 				printf("getla: nlist(%s, %s) ==> 0\n",
1142 					_PATH_UNIX, LA_AVENRUN);
1143 			return (-1);
1144 		}
1145 #ifdef NAMELISTMASK
1146 		Nl[X_AVENRUN].n_value &= NAMELISTMASK;
1147 #endif
1148 	}
1149 	if (tTd(3, 20))
1150 		printf("getla: symbol address = %#x\n", Nl[X_AVENRUN].n_value);
1151 	if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 ||
1152 	    read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
1153 	{
1154 		/* thank you Ian */
1155 		if (tTd(3, 1))
1156 			printf("getla: lseek or read: %s\n", errstring(errno));
1157 		return (-1);
1158 	}
1159 #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)
1160 	if (tTd(3, 5))
1161 	{
1162 		printf("getla: avenrun = %d", avenrun[0]);
1163 		if (tTd(3, 15))
1164 			printf(", %d, %d", avenrun[1], avenrun[2]);
1165 		printf("\n");
1166 	}
1167 	if (tTd(3, 1))
1168 		printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1169 	return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1170 #else
1171 	if (tTd(3, 5))
1172 	{
1173 		printf("getla: avenrun = %g", avenrun[0]);
1174 		if (tTd(3, 15))
1175 			printf(", %g, %g", avenrun[1], avenrun[2]);
1176 		printf("\n");
1177 	}
1178 	if (tTd(3, 1))
1179 		printf("getla: %d\n", (int) (avenrun[0] +0.5));
1180 	return ((int) (avenrun[0] + 0.5));
1181 #endif
1182 }
1183 
1184 #else
1185 #if LA_TYPE == LA_SUBR
1186 
1187 #ifdef DGUX
1188 
1189 #include <sys/dg_sys_info.h>
1190 
1191 int
1192 getla()
1193 {
1194 	struct dg_sys_info_load_info load_info;
1195 
1196 	dg_sys_info((long *)&load_info,
1197 		DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0);
1198 
1199         if (tTd(3, 1))
1200                 printf("getla: %d\n", (int) (load_info.one_minute + 0.5));
1201 
1202 	return((int) (load_info.one_minute + 0.5));
1203 }
1204 
1205 #else
1206 # ifdef __hpux
1207 
1208 struct pst_dynamic;
1209 
1210 #  include <sys/param.h>
1211 #  include <sys/pstat.h>
1212 
1213 int
1214 getla()
1215 {
1216 	struct pst_dynamic pstd;
1217 
1218 	if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic),
1219 			     (size_t) 1, 0) == -1)
1220 		return 0;
1221 
1222         if (tTd(3, 1))
1223                 printf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5));
1224 
1225 	return (int) (pstd.psd_avg_1_min + 0.5);
1226 }
1227 
1228 # else
1229 
1230 int
1231 getla()
1232 {
1233 	double avenrun[3];
1234 
1235 	if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0)
1236 	{
1237 		if (tTd(3, 1))
1238 			perror("getla: getloadavg failed:");
1239 		return (-1);
1240 	}
1241 	if (tTd(3, 1))
1242 		printf("getla: %d\n", (int) (avenrun[0] +0.5));
1243 	return ((int) (avenrun[0] + 0.5));
1244 }
1245 
1246 # endif /* __hpux */
1247 #endif /* DGUX */
1248 #else
1249 #if LA_TYPE == LA_MACH
1250 
1251 /*
1252 **  This has been tested on NEXTSTEP release 2.1/3.X.
1253 */
1254 
1255 #if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0
1256 # include <mach/mach.h>
1257 #else
1258 # include <mach.h>
1259 #endif
1260 
1261 getla()
1262 {
1263 	processor_set_t default_set;
1264 	kern_return_t error;
1265 	unsigned int info_count;
1266 	struct processor_set_basic_info info;
1267 	host_t host;
1268 
1269 	error = processor_set_default(host_self(), &default_set);
1270 	if (error != KERN_SUCCESS)
1271 		return -1;
1272 	info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
1273 	if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
1274 			       &host, (processor_set_info_t)&info,
1275 			       &info_count) != KERN_SUCCESS)
1276 	{
1277 		return -1;
1278 	}
1279 	return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE;
1280 }
1281 
1282 
1283 #else
1284 #if LA_TYPE == LA_PROCSTR
1285 
1286 /*
1287 **  Read /proc/loadavg for the load average.  This is assumed to be
1288 **  in a format like "0.15 0.12 0.06".
1289 **
1290 **	Initially intended for Linux.  This has been in the kernel
1291 **	since at least 0.99.15.
1292 */
1293 
1294 # ifndef _PATH_LOADAVG
1295 #  define _PATH_LOADAVG	"/proc/loadavg"
1296 # endif
1297 
1298 int
1299 getla()
1300 {
1301 	double avenrun;
1302 	register int result;
1303 	FILE *fp;
1304 
1305 	fp = fopen(_PATH_LOADAVG, "r");
1306 	if (fp == NULL)
1307 	{
1308 		if (tTd(3, 1))
1309 			printf("getla: fopen(%s): %s\n",
1310 				_PATH_LOADAVG, errstring(errno));
1311 		return -1;
1312 	}
1313 	result = fscanf(fp, "%lf", &avenrun);
1314 	fclose(fp);
1315 	if (result != 1)
1316 	{
1317 		if (tTd(3, 1))
1318 			printf("getla: fscanf() = %d: %s\n",
1319 				result, errstring(errno));
1320 		return -1;
1321 	}
1322 
1323 	if (tTd(3, 1))
1324 		printf("getla(): %.2f\n", avenrun);
1325 
1326 	return ((int) (avenrun + 0.5));
1327 }
1328 
1329 #else
1330 
1331 getla()
1332 {
1333 	if (tTd(3, 1))
1334 		printf("getla: ZERO\n");
1335 	return (0);
1336 }
1337 
1338 #endif
1339 #endif
1340 #endif
1341 #endif
1342 
1343 
1344 /*
1345  * Copyright 1989 Massachusetts Institute of Technology
1346  *
1347  * Permission to use, copy, modify, distribute, and sell this software and its
1348  * documentation for any purpose is hereby granted without fee, provided that
1349  * the above copyright notice appear in all copies and that both that
1350  * copyright notice and this permission notice appear in supporting
1351  * documentation, and that the name of M.I.T. not be used in advertising or
1352  * publicity pertaining to distribution of the software without specific,
1353  * written prior permission.  M.I.T. makes no representations about the
1354  * suitability of this software for any purpose.  It is provided "as is"
1355  * without express or implied warranty.
1356  *
1357  * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
1358  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
1359  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1360  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
1361  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1362  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1363  *
1364  * Authors:  Many and varied...
1365  */
1366 
1367 /* Non Apollo stuff removed by Don Lewis 11/15/93 */
1368 #ifndef lint
1369 static char  rcsid[] = "@(#)$Id: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $";
1370 #endif /* !lint */
1371 
1372 #ifdef apollo
1373 # undef volatile
1374 #    include <apollo/base.h>
1375 
1376 /* ARGSUSED */
1377 int getloadavg( call_data )
1378      caddr_t	call_data;	/* pointer to (double) return value */
1379 {
1380      double *avenrun = (double *) call_data;
1381      int i;
1382      status_$t      st;
1383      long loadav[3];
1384      proc1_$get_loadav(loadav, &st);
1385      *avenrun = loadav[0] / (double) (1 << 16);
1386      return(0);
1387 }
1388 #   endif /* apollo */
1389 /*
1390 **  SHOULDQUEUE -- should this message be queued or sent?
1391 **
1392 **	Compares the message cost to the load average to decide.
1393 **
1394 **	Parameters:
1395 **		pri -- the priority of the message in question.
1396 **		ctime -- the message creation time.
1397 **
1398 **	Returns:
1399 **		TRUE -- if this message should be queued up for the
1400 **			time being.
1401 **		FALSE -- if the load is low enough to send this message.
1402 **
1403 **	Side Effects:
1404 **		none.
1405 */
1406 
1407 bool
1408 shouldqueue(pri, ctime)
1409 	long pri;
1410 	time_t ctime;
1411 {
1412 	bool rval;
1413 
1414 	if (tTd(3, 30))
1415 		printf("shouldqueue: CurrentLA=%d, pri=%d: ", CurrentLA, pri);
1416 	if (CurrentLA < QueueLA)
1417 	{
1418 		if (tTd(3, 30))
1419 			printf("FALSE (CurrentLA < QueueLA)\n");
1420 		return (FALSE);
1421 	}
1422 	if (CurrentLA >= RefuseLA)
1423 	{
1424 		if (tTd(3, 30))
1425 			printf("TRUE (CurrentLA >= RefuseLA)\n");
1426 		return (TRUE);
1427 	}
1428 	rval = pri > (QueueFactor / (CurrentLA - QueueLA + 1));
1429 	if (tTd(3, 30))
1430 		printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE");
1431 	return rval;
1432 }
1433 /*
1434 **  REFUSECONNECTIONS -- decide if connections should be refused
1435 **
1436 **	Parameters:
1437 **		none.
1438 **
1439 **	Returns:
1440 **		TRUE if incoming SMTP connections should be refused
1441 **			(for now).
1442 **		FALSE if we should accept new work.
1443 **
1444 **	Side Effects:
1445 **		none.
1446 */
1447 
1448 bool
1449 refuseconnections()
1450 {
1451 	extern bool enoughspace();
1452 
1453 #ifdef XLA
1454 	if (!xla_smtp_ok())
1455 		return TRUE;
1456 #endif
1457 
1458 	/* this is probably too simplistic */
1459 	return CurrentLA >= RefuseLA || !enoughspace(MinBlocksFree + 1);
1460 }
1461 /*
1462 **  SETPROCTITLE -- set process title for ps
1463 **
1464 **	Parameters:
1465 **		fmt -- a printf style format string.
1466 **		a, b, c -- possible parameters to fmt.
1467 **
1468 **	Returns:
1469 **		none.
1470 **
1471 **	Side Effects:
1472 **		Clobbers argv of our main procedure so ps(1) will
1473 **		display the title.
1474 */
1475 
1476 #define SPT_NONE	0	/* don't use it at all */
1477 #define SPT_REUSEARGV	1	/* cover argv with title information */
1478 #define SPT_BUILTIN	2	/* use libc builtin */
1479 #define SPT_PSTAT	3	/* use pstat(PSTAT_SETCMD, ...) */
1480 #define SPT_PSSTRINGS	4	/* use PS_STRINGS->... */
1481 #define SPT_WRITEUDOT	5	/* write u. area in kmem */
1482 
1483 #ifndef SPT_TYPE
1484 # define SPT_TYPE	SPT_REUSEARGV
1485 #endif
1486 
1487 #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
1488 
1489 # if SPT_TYPE == SPT_PSTAT
1490 #  include <sys/pstat.h>
1491 # endif
1492 # if SPT_TYPE == SPT_PSSTRINGS
1493 #  include <machine/vmparam.h>
1494 #  include <sys/exec.h>
1495 #  ifndef PS_STRINGS	/* hmmmm....  apparently not available after all */
1496 #   undef SPT_TYPE
1497 #   define SPT_TYPE	SPT_REUSEARGV
1498 #  else
1499 #   ifndef NKPDE			/* FreeBSD 2.0 */
1500 #    define NKPDE 63
1501 typedef unsigned int	*pt_entry_t;
1502 #   endif
1503 #  endif
1504 # endif
1505 
1506 # if SPT_TYPE == SPT_PSSTRINGS
1507 #  define SETPROC_STATIC	static
1508 # else
1509 #  define SETPROC_STATIC
1510 # endif
1511 
1512 # ifndef SPT_PADCHAR
1513 #  define SPT_PADCHAR	' '
1514 # endif
1515 
1516 #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
1517 
1518 #if SPT_TYPE != SPT_BUILTIN
1519 
1520 /*VARARGS1*/
1521 void
1522 # ifdef __STDC__
1523 setproctitle(char *fmt, ...)
1524 # else
1525 setproctitle(fmt, va_alist)
1526 	char *fmt;
1527 	va_dcl
1528 # endif
1529 {
1530 # if SPT_TYPE != SPT_NONE
1531 	register char *p;
1532 	register int i;
1533 	SETPROC_STATIC char buf[MAXLINE];
1534 	VA_LOCAL_DECL
1535 #  if SPT_TYPE == SPT_PSTAT
1536 	union pstun pst;
1537 #  endif
1538 	extern char **Argv;
1539 	extern char *LastArgv;
1540 
1541 	p = buf;
1542 
1543 	/* print sendmail: heading for grep */
1544 	(void) strcpy(p, "sendmail: ");
1545 	p += strlen(p);
1546 
1547 	/* print the argument string */
1548 	VA_START(fmt);
1549 	(void) vsprintf(p, fmt, ap);
1550 	VA_END;
1551 
1552 	i = strlen(buf);
1553 
1554 #  if SPT_TYPE == SPT_PSTAT
1555 	pst.pst_command = buf;
1556 	pstat(PSTAT_SETCMD, pst, i, 0, 0);
1557 #  else
1558 #   if SPT_TYPE == SPT_PSSTRINGS
1559 	PS_STRINGS->ps_nargvstr = 1;
1560 	PS_STRINGS->ps_argvstr = buf;
1561 #   else
1562 	if (i > LastArgv - Argv[0] - 2)
1563 	{
1564 		i = LastArgv - Argv[0] - 2;
1565 		buf[i] = '\0';
1566 	}
1567 	(void) strcpy(Argv[0], buf);
1568 	p = &Argv[0][i];
1569 	while (p < LastArgv)
1570 		*p++ = SPT_PADCHAR;
1571 	Argv[1] = NULL;
1572 #   endif /* SPT_TYPE == SPT_PSSTRINGS */
1573 #  endif /* SPT_TYPE == SPT_PSTAT */
1574 # endif /* SPT_TYPE != SPT_NONE */
1575 }
1576 
1577 #endif /* SPT_TYPE != SPT_BUILTIN */
1578 /*
1579 **  REAPCHILD -- pick up the body of my child, lest it become a zombie
1580 **
1581 **	Parameters:
1582 **		sig -- the signal that got us here (unused).
1583 **
1584 **	Returns:
1585 **		none.
1586 **
1587 **	Side Effects:
1588 **		Picks up extant zombies.
1589 */
1590 
1591 void
1592 reapchild(sig)
1593 	int sig;
1594 {
1595 	int olderrno = errno;
1596 # ifdef HASWAITPID
1597 	auto int status;
1598 	int count;
1599 	int pid;
1600 
1601 	count = 0;
1602 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
1603 	{
1604 		if (count++ > 1000)
1605 		{
1606 #ifdef LOG
1607 			syslog(LOG_ALERT, "reapchild: waitpid loop: pid=%d, status=%x",
1608 				pid, status);
1609 #endif
1610 			break;
1611 		}
1612 	}
1613 # else
1614 # ifdef WNOHANG
1615 	union wait status;
1616 
1617 	while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0)
1618 		continue;
1619 # else /* WNOHANG */
1620 	auto int status;
1621 
1622 	while (wait(&status) > 0)
1623 		continue;
1624 # endif /* WNOHANG */
1625 # endif
1626 # ifdef SYS5SIGNALS
1627 	(void) setsignal(SIGCHLD, reapchild);
1628 # endif
1629 	errno = olderrno;
1630 }
1631 /*
1632 **  UNSETENV -- remove a variable from the environment
1633 **
1634 **	Not needed on newer systems.
1635 **
1636 **	Parameters:
1637 **		name -- the string name of the environment variable to be
1638 **			deleted from the current environment.
1639 **
1640 **	Returns:
1641 **		none.
1642 **
1643 **	Globals:
1644 **		environ -- a pointer to the current environment.
1645 **
1646 **	Side Effects:
1647 **		Modifies environ.
1648 */
1649 
1650 #ifndef HASUNSETENV
1651 
1652 void
1653 unsetenv(name)
1654 	char *name;
1655 {
1656 	extern char **environ;
1657 	register char **pp;
1658 	int len = strlen(name);
1659 
1660 	for (pp = environ; *pp != NULL; pp++)
1661 	{
1662 		if (strncmp(name, *pp, len) == 0 &&
1663 		    ((*pp)[len] == '=' || (*pp)[len] == '\0'))
1664 			break;
1665 	}
1666 
1667 	for (; *pp != NULL; pp++)
1668 		*pp = pp[1];
1669 }
1670 
1671 #endif
1672 /*
1673 **  GETDTABLESIZE -- return number of file descriptors
1674 **
1675 **	Only on non-BSD systems
1676 **
1677 **	Parameters:
1678 **		none
1679 **
1680 **	Returns:
1681 **		size of file descriptor table
1682 **
1683 **	Side Effects:
1684 **		none
1685 */
1686 
1687 #ifdef SOLARIS
1688 # include <sys/resource.h>
1689 #endif
1690 
1691 int
1692 getdtsize()
1693 {
1694 #ifdef RLIMIT_NOFILE
1695 	struct rlimit rl;
1696 
1697 	if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
1698 		return rl.rlim_cur;
1699 #endif
1700 
1701 # ifdef HASGETDTABLESIZE
1702 	return getdtablesize();
1703 # else
1704 #  ifdef _SC_OPEN_MAX
1705 	return sysconf(_SC_OPEN_MAX);
1706 #  else
1707 	return NOFILE;
1708 #  endif
1709 # endif
1710 }
1711 /*
1712 **  UNAME -- get the UUCP name of this system.
1713 */
1714 
1715 #ifndef HASUNAME
1716 
1717 int
1718 uname(name)
1719 	struct utsname *name;
1720 {
1721 	FILE *file;
1722 	char *n;
1723 
1724 	name->nodename[0] = '\0';
1725 
1726 	/* try /etc/whoami -- one line with the node name */
1727 	if ((file = fopen("/etc/whoami", "r")) != NULL)
1728 	{
1729 		(void) fgets(name->nodename, NODE_LENGTH + 1, file);
1730 		(void) fclose(file);
1731 		n = strchr(name->nodename, '\n');
1732 		if (n != NULL)
1733 			*n = '\0';
1734 		if (name->nodename[0] != '\0')
1735 			return (0);
1736 	}
1737 
1738 	/* try /usr/include/whoami.h -- has a #define somewhere */
1739 	if ((file = fopen("/usr/include/whoami.h", "r")) != NULL)
1740 	{
1741 		char buf[MAXLINE];
1742 
1743 		while (fgets(buf, MAXLINE, file) != NULL)
1744 			if (sscanf(buf, "#define sysname \"%*[^\"]\"",
1745 					NODE_LENGTH, name->nodename) > 0)
1746 				break;
1747 		(void) fclose(file);
1748 		if (name->nodename[0] != '\0')
1749 			return (0);
1750 	}
1751 
1752 #ifdef TRUST_POPEN
1753 	/*
1754 	**  Popen is known to have security holes.
1755 	*/
1756 
1757 	/* try uuname -l to return local name */
1758 	if ((file = popen("uuname -l", "r")) != NULL)
1759 	{
1760 		(void) fgets(name, NODE_LENGTH + 1, file);
1761 		(void) pclose(file);
1762 		n = strchr(name, '\n');
1763 		if (n != NULL)
1764 			*n = '\0';
1765 		if (name->nodename[0] != '\0')
1766 			return (0);
1767 	}
1768 #endif
1769 
1770 	return (-1);
1771 }
1772 #endif /* HASUNAME */
1773 /*
1774 **  INITGROUPS -- initialize groups
1775 **
1776 **	Stub implementation for System V style systems
1777 */
1778 
1779 #ifndef HASINITGROUPS
1780 
1781 initgroups(name, basegid)
1782 	char *name;
1783 	int basegid;
1784 {
1785 	return 0;
1786 }
1787 
1788 #endif
1789 /*
1790 **  SETSID -- set session id (for non-POSIX systems)
1791 */
1792 
1793 #ifndef HASSETSID
1794 
1795 pid_t
1796 setsid __P ((void))
1797 {
1798 #ifdef TIOCNOTTY
1799 	int fd;
1800 
1801 	fd = open("/dev/tty", O_RDWR, 0);
1802 	if (fd >= 0)
1803 	{
1804 		(void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
1805 		(void) close(fd);
1806 	}
1807 #endif /* TIOCNOTTY */
1808 # ifdef SYS5SETPGRP
1809 	return setpgrp();
1810 # else
1811 	return setpgid(0, getpid());
1812 # endif
1813 }
1814 
1815 #endif
1816 /*
1817 **  FSYNC -- dummy fsync
1818 */
1819 
1820 #ifdef NEEDFSYNC
1821 
1822 fsync(fd)
1823 	int fd;
1824 {
1825 # ifdef O_SYNC
1826 	return fcntl(fd, F_SETFL, O_SYNC);
1827 # else
1828 	/* nothing we can do */
1829 	return 0;
1830 # endif
1831 }
1832 
1833 #endif
1834 /*
1835 **  DGUX_INET_ADDR -- inet_addr for DG/UX
1836 **
1837 **	Data General DG/UX version of inet_addr returns a struct in_addr
1838 **	instead of a long.  This patches things.  Only needed on versions
1839 **	prior to 5.4.3.
1840 */
1841 
1842 #ifdef DGUX_5_4_2
1843 
1844 #undef inet_addr
1845 
1846 long
1847 dgux_inet_addr(host)
1848 	char *host;
1849 {
1850 	struct in_addr haddr;
1851 
1852 	haddr = inet_addr(host);
1853 	return haddr.s_addr;
1854 }
1855 
1856 #endif
1857 /*
1858 **  GETOPT -- for old systems or systems with bogus implementations
1859 */
1860 
1861 #ifdef NEEDGETOPT
1862 
1863 /*
1864  * Copyright (c) 1985 Regents of the University of California.
1865  * All rights reserved.  The Berkeley software License Agreement
1866  * specifies the terms and conditions for redistribution.
1867  */
1868 
1869 
1870 /*
1871 ** this version hacked to add `atend' flag to allow state machine
1872 ** to reset if invoked by the program to scan args for a 2nd time
1873 */
1874 
1875 #if defined(LIBC_SCCS) && !defined(lint)
1876 static char sccsid[] = "@(#)getopt.c	4.3 (Berkeley) 3/9/86";
1877 #endif /* LIBC_SCCS and not lint */
1878 
1879 #include <stdio.h>
1880 
1881 /*
1882  * get option letter from argument vector
1883  */
1884 #ifdef _CONVEX_SOURCE
1885 extern int	optind, opterr, optopt;
1886 extern char	*optarg;
1887 #else
1888 int	opterr = 1;		/* if error message should be printed */
1889 int	optind = 1;		/* index into parent argv vector */
1890 int	optopt = 0;		/* character checked for validity */
1891 char	*optarg = NULL;		/* argument associated with option */
1892 #endif
1893 
1894 #define BADCH	(int)'?'
1895 #define EMSG	""
1896 #define tell(s)	if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \
1897 		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);}
1898 
1899 getopt(nargc,nargv,ostr)
1900 	int		nargc;
1901 	char *const	*nargv;
1902 	const char	*ostr;
1903 {
1904 	static char	*place = EMSG;	/* option letter processing */
1905 	static char	atend = 0;
1906 	register char	*oli;		/* option letter list index */
1907 
1908 	if (atend) {
1909 		atend = 0;
1910 		place = EMSG;
1911 	}
1912 	if(!*place) {			/* update scanning pointer */
1913 		if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) {
1914 			atend++;
1915 			return(EOF);
1916 		}
1917 		if (*place == '-') {	/* found "--" */
1918 			++optind;
1919 			atend++;
1920 			return(EOF);
1921 		}
1922 	}				/* option letter okay? */
1923 	if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) {
1924 		if (!*place) ++optind;
1925 		tell(": illegal option -- ");
1926 	}
1927 	if (*++oli != ':') {		/* don't need argument */
1928 		optarg = NULL;
1929 		if (!*place) ++optind;
1930 	}
1931 	else {				/* need an argument */
1932 		if (*place) optarg = place;	/* no white space */
1933 		else if (nargc <= ++optind) {	/* no arg */
1934 			place = EMSG;
1935 			tell(": option requires an argument -- ");
1936 		}
1937 	 	else optarg = nargv[optind];	/* white space */
1938 		place = EMSG;
1939 		++optind;
1940 	}
1941 	return(optopt);			/* dump back option letter */
1942 }
1943 
1944 #endif
1945 /*
1946 **  VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version
1947 */
1948 
1949 #ifdef NEEDVPRINTF
1950 
1951 #define MAXARG	16
1952 
1953 vfprintf(fp, fmt, ap)
1954 	FILE *	fp;
1955 	char *	fmt;
1956 	char **	ap;
1957 {
1958 	char *	bp[MAXARG];
1959 	int	i = 0;
1960 
1961 	while (*ap && i < MAXARG)
1962 		bp[i++] = *ap++;
1963 	fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3],
1964 			 bp[4], bp[5], bp[6], bp[7],
1965 			 bp[8], bp[9], bp[10], bp[11],
1966 			 bp[12], bp[13], bp[14], bp[15]);
1967 }
1968 
1969 vsprintf(s, fmt, ap)
1970 	char *	s;
1971 	char *	fmt;
1972 	char **	ap;
1973 {
1974 	char *	bp[MAXARG];
1975 	int	i = 0;
1976 
1977 	while (*ap && i < MAXARG)
1978 		bp[i++] = *ap++;
1979 	sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3],
1980 			bp[4], bp[5], bp[6], bp[7],
1981 			bp[8], bp[9], bp[10], bp[11],
1982 			bp[12], bp[13], bp[14], bp[15]);
1983 }
1984 
1985 #endif
1986 /*
1987 **  USERSHELLOK -- tell if a user's shell is ok for unrestricted use
1988 **
1989 **	Parameters:
1990 **		shell -- the user's shell from /etc/passwd
1991 **
1992 **	Returns:
1993 **		TRUE -- if it is ok to use this for unrestricted access.
1994 **		FALSE -- if the shell is restricted.
1995 */
1996 
1997 #if !HASGETUSERSHELL
1998 
1999 # ifndef _PATH_SHELLS
2000 #  define _PATH_SHELLS	"/etc/shells"
2001 # endif
2002 
2003 char	*DefaultUserShells[] =
2004 {
2005 	"/bin/sh",		/* standard shell */
2006 	"/usr/bin/sh",
2007 	"/bin/csh",		/* C shell */
2008 	"/usr/bin/csh",
2009 #ifdef __hpux
2010 # ifdef V4FS
2011 	"/usr/bin/rsh",		/* restricted Bourne shell */
2012 	"/usr/bin/ksh",		/* Korn shell */
2013 	"/usr/bin/rksh",	/* restricted Korn shell */
2014 	"/usr/bin/pam",
2015 	"/usr/bin/keysh",	/* key shell (extended Korn shell) */
2016 	"/usr/bin/posix/sh",
2017 # else
2018 	"/bin/rsh",		/* restricted Bourne shell */
2019 	"/bin/ksh",		/* Korn shell */
2020 	"/bin/rksh",		/* restricted Korn shell */
2021 	"/bin/pam",
2022 	"/usr/bin/keysh",	/* key shell (extended Korn shell) */
2023 	"/bin/posix/sh",
2024 # endif
2025 #endif
2026 #ifdef _AIX3
2027 	"/bin/ksh",		/* Korn shell */
2028 	"/usr/bin/ksh",
2029 	"/bin/tsh",		/* trusted shell */
2030 	"/usr/bin/tsh",
2031 	"/bin/bsh",		/* Bourne shell */
2032 	"/usr/bin/bsh",
2033 #endif
2034 	NULL
2035 };
2036 
2037 #endif
2038 
2039 #define WILDCARD_SHELL	"/SENDMAIL/ANY/SHELL/"
2040 
2041 bool
2042 usershellok(shell)
2043 	char *shell;
2044 {
2045 #if HASGETUSERSHELL
2046 	register char *p;
2047 	extern char *getusershell();
2048 
2049 	if (shell == NULL || shell[0] == '\0')
2050 		return TRUE;
2051 
2052 	setusershell();
2053 	while ((p = getusershell()) != NULL)
2054 		if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0)
2055 			break;
2056 	endusershell();
2057 	return p != NULL;
2058 #else
2059 	register FILE *shellf;
2060 	char buf[MAXLINE];
2061 
2062 	if (shell == NULL || shell[0] == '\0')
2063 		return TRUE;
2064 
2065 	shellf = fopen(_PATH_SHELLS, "r");
2066 	if (shellf == NULL)
2067 	{
2068 		/* no /etc/shells; see if it is one of the std shells */
2069 		char **d;
2070 
2071 		for (d = DefaultUserShells; *d != NULL; d++)
2072 		{
2073 			if (strcmp(shell, *d) == 0)
2074 				return TRUE;
2075 		}
2076 		return FALSE;
2077 	}
2078 
2079 	while (fgets(buf, sizeof buf, shellf) != NULL)
2080 	{
2081 		register char *p, *q;
2082 
2083 		p = buf;
2084 		while (*p != '\0' && *p != '#' && *p != '/')
2085 			p++;
2086 		if (*p == '#' || *p == '\0')
2087 			continue;
2088 		q = p;
2089 		while (*p != '\0' && *p != '#' && !isspace(*p))
2090 			p++;
2091 		*p = '\0';
2092 		if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0)
2093 		{
2094 			fclose(shellf);
2095 			return TRUE;
2096 		}
2097 	}
2098 	fclose(shellf);
2099 	return FALSE;
2100 #endif
2101 }
2102 /*
2103 **  FREESPACE -- see how much free space is on the queue filesystem
2104 **
2105 **	Only implemented if you have statfs.
2106 **
2107 **	Parameters:
2108 **		dir -- the directory in question.
2109 **		bsize -- a variable into which the filesystem
2110 **			block size is stored.
2111 **
2112 **	Returns:
2113 **		The number of bytes free on the queue filesystem.
2114 **		-1 if the statfs call fails.
2115 **
2116 **	Side effects:
2117 **		Puts the filesystem block size into bsize.
2118 */
2119 
2120 /* statfs types */
2121 #define SFS_NONE	0	/* no statfs implementation */
2122 #define SFS_USTAT	1	/* use ustat */
2123 #define SFS_4ARGS	2	/* use four-argument statfs call */
2124 #define SFS_VFS		3	/* use <sys/vfs.h> implementation */
2125 #define SFS_MOUNT	4	/* use <sys/mount.h> implementation */
2126 #define SFS_STATFS	5	/* use <sys/statfs.h> implementation */
2127 #define SFS_STATVFS	6	/* use <sys/statvfs.h> implementation */
2128 
2129 #ifndef SFS_TYPE
2130 # define SFS_TYPE	SFS_NONE
2131 #endif
2132 
2133 #if SFS_TYPE == SFS_USTAT
2134 # include <ustat.h>
2135 #endif
2136 #if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
2137 # include <sys/statfs.h>
2138 #endif
2139 #if SFS_TYPE == SFS_VFS
2140 # include <sys/vfs.h>
2141 #endif
2142 #if SFS_TYPE == SFS_MOUNT
2143 # include <sys/mount.h>
2144 #endif
2145 #if SFS_TYPE == SFS_STATVFS
2146 # include <sys/statvfs.h>
2147 #endif
2148 
2149 long
2150 freespace(dir, bsize)
2151 	char *dir;
2152 	long *bsize;
2153 {
2154 #if SFS_TYPE != SFS_NONE
2155 # if SFS_TYPE == SFS_USTAT
2156 	struct ustat fs;
2157 	struct stat statbuf;
2158 #  define FSBLOCKSIZE	DEV_BSIZE
2159 #  define SFS_BAVAIL	f_tfree
2160 # else
2161 #  if defined(ultrix)
2162 	struct fs_data fs;
2163 #   define SFS_BAVAIL	fd_bfreen
2164 #   define FSBLOCKSIZE	1024L
2165 #  else
2166 #   if SFS_TYPE == SFS_STATVFS
2167 	struct statvfs fs;
2168 #    define FSBLOCKSIZE	fs.f_frsize
2169 #   else
2170 	struct statfs fs;
2171 #    define FSBLOCKSIZE	fs.f_bsize
2172 #   endif
2173 #  endif
2174 # endif
2175 # ifndef SFS_BAVAIL
2176 #  define SFS_BAVAIL f_bavail
2177 # endif
2178 
2179 # if SFS_TYPE == SFS_USTAT
2180 	if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
2181 # else
2182 #  if SFS_TYPE == SFS_4ARGS
2183 	if (statfs(dir, &fs, sizeof fs, 0) == 0)
2184 #  else
2185 #   if SFS_TYPE == SFS_STATVFS
2186 	if (statvfs(dir, &fs) == 0)
2187 #   else
2188 #    if defined(ultrix)
2189 	if (statfs(dir, &fs) > 0)
2190 #    else
2191 	if (statfs(dir, &fs) == 0)
2192 #    endif
2193 #   endif
2194 #  endif
2195 # endif
2196 	{
2197 		if (bsize != NULL)
2198 			*bsize = FSBLOCKSIZE;
2199 		return (fs.SFS_BAVAIL);
2200 	}
2201 #endif
2202 	return (-1);
2203 }
2204 /*
2205 **  ENOUGHSPACE -- check to see if there is enough free space on the queue fs
2206 **
2207 **	Only implemented if you have statfs.
2208 **
2209 **	Parameters:
2210 **		msize -- the size to check against.  If zero, we don't yet
2211 **		know how big the message will be, so just check for
2212 **		a "reasonable" amount.
2213 **
2214 **	Returns:
2215 **		TRUE if there is enough space.
2216 **		FALSE otherwise.
2217 */
2218 
2219 bool
2220 enoughspace(msize)
2221 	long msize;
2222 {
2223 	long bfree, bsize;
2224 
2225 	if (MinBlocksFree <= 0 && msize <= 0)
2226 	{
2227 		if (tTd(4, 80))
2228 			printf("enoughspace: no threshold\n");
2229 		return TRUE;
2230 	}
2231 
2232 	if ((bfree = freespace(QueueDir, &bsize)) >= 0)
2233 	{
2234 		if (tTd(4, 80))
2235 			printf("enoughspace: bavail=%ld, need=%ld\n",
2236 				bfree, msize);
2237 
2238 		/* convert msize to block count */
2239 		msize = msize / bsize + 1;
2240 		if (MinBlocksFree >= 0)
2241 			msize += MinBlocksFree;
2242 
2243 		if (bfree < msize)
2244 		{
2245 #ifdef LOG
2246 			if (LogLevel > 0)
2247 				syslog(LOG_ALERT,
2248 					"%s: low on space (have %ld, %s needs %ld in %s)",
2249 					CurEnv->e_id == NULL ? "[NOQUEUE]" : CurEnv->e_id,
2250 					bfree,
2251 					CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
2252 					msize, QueueDir);
2253 #endif
2254 			return FALSE;
2255 		}
2256 	}
2257 	else if (tTd(4, 80))
2258 		printf("enoughspace failure: min=%ld, need=%ld: %s\n",
2259 			MinBlocksFree, msize, errstring(errno));
2260 	return TRUE;
2261 }
2262 /*
2263 **  TRANSIENTERROR -- tell if an error code indicates a transient failure
2264 **
2265 **	This looks at an errno value and tells if this is likely to
2266 **	go away if retried later.
2267 **
2268 **	Parameters:
2269 **		err -- the errno code to classify.
2270 **
2271 **	Returns:
2272 **		TRUE if this is probably transient.
2273 **		FALSE otherwise.
2274 */
2275 
2276 bool
2277 transienterror(err)
2278 	int err;
2279 {
2280 	switch (err)
2281 	{
2282 	  case EIO:			/* I/O error */
2283 	  case ENXIO:			/* Device not configured */
2284 	  case EAGAIN:			/* Resource temporarily unavailable */
2285 	  case ENOMEM:			/* Cannot allocate memory */
2286 	  case ENODEV:			/* Operation not supported by device */
2287 	  case ENFILE:			/* Too many open files in system */
2288 	  case EMFILE:			/* Too many open files */
2289 	  case ENOSPC:			/* No space left on device */
2290 #ifdef ETIMEDOUT
2291 	  case ETIMEDOUT:		/* Connection timed out */
2292 #endif
2293 #ifdef ESTALE
2294 	  case ESTALE:			/* Stale NFS file handle */
2295 #endif
2296 #ifdef ENETDOWN
2297 	  case ENETDOWN:		/* Network is down */
2298 #endif
2299 #ifdef ENETUNREACH
2300 	  case ENETUNREACH:		/* Network is unreachable */
2301 #endif
2302 #ifdef ENETRESET
2303 	  case ENETRESET:		/* Network dropped connection on reset */
2304 #endif
2305 #ifdef ECONNABORTED
2306 	  case ECONNABORTED:		/* Software caused connection abort */
2307 #endif
2308 #ifdef ECONNRESET
2309 	  case ECONNRESET:		/* Connection reset by peer */
2310 #endif
2311 #ifdef ENOBUFS
2312 	  case ENOBUFS:			/* No buffer space available */
2313 #endif
2314 #ifdef ESHUTDOWN
2315 	  case ESHUTDOWN:		/* Can't send after socket shutdown */
2316 #endif
2317 #ifdef ECONNREFUSED
2318 	  case ECONNREFUSED:		/* Connection refused */
2319 #endif
2320 #ifdef EHOSTDOWN
2321 	  case EHOSTDOWN:		/* Host is down */
2322 #endif
2323 #ifdef EHOSTUNREACH
2324 	  case EHOSTUNREACH:		/* No route to host */
2325 #endif
2326 #ifdef EDQUOT
2327 	  case EDQUOT:			/* Disc quota exceeded */
2328 #endif
2329 #ifdef EPROCLIM
2330 	  case EPROCLIM:		/* Too many processes */
2331 #endif
2332 #ifdef EUSERS
2333 	  case EUSERS:			/* Too many users */
2334 #endif
2335 #ifdef EDEADLK
2336 	  case EDEADLK:			/* Resource deadlock avoided */
2337 #endif
2338 #ifdef EISCONN
2339 	  case EISCONN:			/* Socket already connected */
2340 #endif
2341 #ifdef EINPROGRESS
2342 	  case EINPROGRESS:		/* Operation now in progress */
2343 #endif
2344 #ifdef EALREADY
2345 	  case EALREADY:		/* Operation already in progress */
2346 #endif
2347 #ifdef EADDRINUSE
2348 	  case EADDRINUSE:		/* Address already in use */
2349 #endif
2350 #ifdef EADDRNOTAVAIL
2351 	  case EADDRNOTAVAIL:		/* Can't assign requested address */
2352 #endif
2353 #ifdef ETXTBSY
2354 	  case ETXTBSY:			/* (Apollo) file locked */
2355 #endif
2356 #if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
2357 	  case ENOSR:			/* Out of streams resources */
2358 #endif
2359 		return TRUE;
2360 	}
2361 
2362 	/* nope, must be permanent */
2363 	return FALSE;
2364 }
2365 /*
2366 **  LOCKFILE -- lock a file using flock or (shudder) fcntl locking
2367 **
2368 **	Parameters:
2369 **		fd -- the file descriptor of the file.
2370 **		filename -- the file name (for error messages).
2371 **		ext -- the filename extension.
2372 **		type -- type of the lock.  Bits can be:
2373 **			LOCK_EX -- exclusive lock.
2374 **			LOCK_NB -- non-blocking.
2375 **
2376 **	Returns:
2377 **		TRUE if the lock was acquired.
2378 **		FALSE otherwise.
2379 */
2380 
2381 bool
2382 lockfile(fd, filename, ext, type)
2383 	int fd;
2384 	char *filename;
2385 	char *ext;
2386 	int type;
2387 {
2388 # if !HASFLOCK
2389 	int action;
2390 	struct flock lfd;
2391 
2392 	if (ext == NULL)
2393 		ext = "";
2394 
2395 	bzero(&lfd, sizeof lfd);
2396 	if (bitset(LOCK_UN, type))
2397 		lfd.l_type = F_UNLCK;
2398 	else if (bitset(LOCK_EX, type))
2399 		lfd.l_type = F_WRLCK;
2400 	else
2401 		lfd.l_type = F_RDLCK;
2402 
2403 	if (bitset(LOCK_NB, type))
2404 		action = F_SETLK;
2405 	else
2406 		action = F_SETLKW;
2407 
2408 	if (tTd(55, 60))
2409 		printf("lockfile(%s%s, action=%d, type=%d): ",
2410 			filename, ext, action, lfd.l_type);
2411 
2412 	if (fcntl(fd, action, &lfd) >= 0)
2413 	{
2414 		if (tTd(55, 60))
2415 			printf("SUCCESS\n");
2416 		return TRUE;
2417 	}
2418 
2419 	if (tTd(55, 60))
2420 		printf("(%s) ", errstring(errno));
2421 
2422 	/*
2423 	**  On SunOS, if you are testing using -oQ/tmp/mqueue or
2424 	**  -oA/tmp/aliases or anything like that, and /tmp is mounted
2425 	**  as type "tmp" (that is, served from swap space), the
2426 	**  previous fcntl will fail with "Invalid argument" errors.
2427 	**  Since this is fairly common during testing, we will assume
2428 	**  that this indicates that the lock is successfully grabbed.
2429 	*/
2430 
2431 	if (errno == EINVAL)
2432 	{
2433 		if (tTd(55, 60))
2434 			printf("SUCCESS\n");
2435 		return TRUE;
2436 	}
2437 
2438 	if (!bitset(LOCK_NB, type) || (errno != EACCES && errno != EAGAIN))
2439 	{
2440 		int omode = -1;
2441 #  ifdef F_GETFL
2442 		int oerrno = errno;
2443 
2444 		(void) fcntl(fd, F_GETFL, &omode);
2445 		errno = oerrno;
2446 #  endif
2447 		syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
2448 			filename, ext, fd, type, omode, geteuid());
2449 	}
2450 # else
2451 	if (ext == NULL)
2452 		ext = "";
2453 
2454 	if (tTd(55, 60))
2455 		printf("lockfile(%s%s, type=%o): ", filename, ext, type);
2456 
2457 	if (flock(fd, type) >= 0)
2458 	{
2459 		if (tTd(55, 60))
2460 			printf("SUCCESS\n");
2461 		return TRUE;
2462 	}
2463 
2464 	if (tTd(55, 60))
2465 		printf("(%s) ", errstring(errno));
2466 
2467 	if (!bitset(LOCK_NB, type) || errno != EWOULDBLOCK)
2468 	{
2469 		int omode = -1;
2470 #  ifdef F_GETFL
2471 		int oerrno = errno;
2472 
2473 		(void) fcntl(fd, F_GETFL, &omode);
2474 		errno = oerrno;
2475 #  endif
2476 		syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
2477 			filename, ext, fd, type, omode, geteuid());
2478 	}
2479 # endif
2480 	if (tTd(55, 60))
2481 		printf("FAIL\n");
2482 	return FALSE;
2483 }
2484 /*
2485 **  CHOWNSAFE -- tell if chown is "safe" (executable only by root)
2486 **
2487 **	Parameters:
2488 **		fd -- the file descriptor to check.
2489 **
2490 **	Returns:
2491 **		TRUE -- if only root can chown the file to an arbitrary
2492 **			user.
2493 **		FALSE -- if an arbitrary user can give away a file.
2494 */
2495 
2496 bool
2497 chownsafe(fd)
2498 	int fd;
2499 {
2500 #ifdef __hpux
2501 	char *s;
2502 	int tfd;
2503 	uid_t o_uid, o_euid;
2504 	gid_t o_gid, o_egid;
2505 	bool rval;
2506 	struct stat stbuf;
2507 
2508 	o_uid = getuid();
2509 	o_euid = geteuid();
2510 	o_gid = getgid();
2511 	o_egid = getegid();
2512 	fstat(fd, &stbuf);
2513 	setresuid(stbuf.st_uid, stbuf.st_uid, -1);
2514 	setresgid(stbuf.st_gid, stbuf.st_gid, -1);
2515 	s = tmpnam(NULL);
2516 	tfd = open(s, O_RDONLY|O_CREAT, 0600);
2517 	rval = fchown(tfd, DefUid, DefGid) != 0;
2518 	close(tfd);
2519 	unlink(s);
2520 	setreuid(o_uid, o_euid);
2521 	setresgid(o_gid, o_egid, -1);
2522 	return rval;
2523 #else
2524 # ifdef _POSIX_CHOWN_RESTRICTED
2525 #  if _POSIX_CHOWN_RESTRICTED == -1
2526 	return FALSE;
2527 #  else
2528 	return TRUE;
2529 #  endif
2530 # else
2531 #  ifdef _PC_CHOWN_RESTRICTED
2532 	int rval;
2533 
2534 	/*
2535 	**  Some systems (e.g., SunOS) seem to have the call and the
2536 	**  #define _PC_CHOWN_RESTRICTED, but don't actually implement
2537 	**  the call.  This heuristic checks for that.
2538 	*/
2539 
2540 	errno = 0;
2541 	rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
2542 	if (errno == 0)
2543 		return rval > 0;
2544 #  endif
2545 #  ifdef BSD
2546 	return TRUE;
2547 #  else
2548 	return FALSE;
2549 #  endif
2550 # endif
2551 #endif
2552 }
2553 /*
2554 **  RESETLIMITS -- reset system controlled resource limits
2555 **
2556 **	This is to avoid denial-of-service attacks
2557 **
2558 **	Parameters:
2559 **		none
2560 **
2561 **	Returns:
2562 **		none
2563 */
2564 
2565 #if HASSETRLIMIT
2566 # include <sys/resource.h>
2567 #endif
2568 
2569 void
2570 resetlimits()
2571 {
2572 #if HASSETRLIMIT
2573 	struct rlimit lim;
2574 
2575 	lim.rlim_cur = lim.rlim_max = RLIM_INFINITY;
2576 	(void) setrlimit(RLIMIT_CPU, &lim);
2577 	(void) setrlimit(RLIMIT_FSIZE, &lim);
2578 #else
2579 # if HASULIMIT
2580 	(void) ulimit(2, 0x3fffff);
2581 # endif
2582 #endif
2583 }
2584 /*
2585 **  GETCFNAME -- return the name of the .cf file.
2586 **
2587 **	Some systems (e.g., NeXT) determine this dynamically.
2588 */
2589 
2590 char *
2591 getcfname()
2592 {
2593 	int i;
2594 	static char cbuf[200];
2595 
2596 	if (ConfFile != NULL)
2597 		return ConfFile;
2598 #ifdef NETINFO
2599 	{
2600 		extern char *ni_propval();
2601 		char *cflocation;
2602 
2603 		cflocation = ni_propval("/locations", NULL, "sendmail",
2604 					"sendmail.cf", '\0');
2605 		if (cflocation != NULL)
2606 			return cflocation;
2607 	}
2608 #endif
2609 
2610 	/*
2611 	**  Try sendmail.8.6.12.cf, then sendmail.8.6.cf, then
2612 	**  sendmail.8.cf, and finally sendmail.cf.
2613 	**
2614 	**	I suppose it should really try a search path here --
2615 	**	e.g., /etc/sendmail.cf, /etc/mail/sendmail.cf,
2616 	**	/usr/lib/sendmail.cf, and so forth.
2617 	*/
2618 
2619 	strcpy(cbuf, _PATH_SENDMAILCF);
2620 	i = strlen(cbuf);
2621 	if (strcmp(&cbuf[i - 3], ".cf") == 0)
2622 	{
2623 		char *p;
2624 		extern char Version[];
2625 
2626 		strcpy(&cbuf[i - 2], Version);
2627 		p = strchr(&cbuf[i - 2], '/');
2628 		if (p != NULL)
2629 			*p = '\0';
2630 		p = &cbuf[strlen(cbuf)];
2631 		do
2632 		{
2633 			int fd;
2634 
2635 			strcpy(p, ".cf");
2636 			if ((fd = open(cbuf, O_RDONLY, 0)) >= 0)
2637 			{
2638 				close(fd);
2639 				return cbuf;
2640 			}
2641 			*p = '\0';
2642 		} while ((p = strrchr(&cbuf[i - 2], '.')) != NULL);
2643 	}
2644 	return _PATH_SENDMAILCF;
2645 }
2646 /*
2647 **  SETVENDOR -- process vendor code from V configuration line
2648 **
2649 **	Parameters:
2650 **		vendor -- string representation of vendor.
2651 **
2652 **	Returns:
2653 **		TRUE -- if ok.
2654 **		FALSE -- if vendor code could not be processed.
2655 **
2656 **	Side Effects:
2657 **		It is reasonable to set mode flags here to tweak
2658 **		processing in other parts of the code if necessary.
2659 **		For example, if you are a vendor that uses $%y to
2660 **		indicate YP lookups, you could enable that here.
2661 */
2662 
2663 bool
2664 setvendor(vendor)
2665 	char *vendor;
2666 {
2667 	if (strcasecmp(vendor, "Berkeley") == 0)
2668 	{
2669 		VendorCode = VENDOR_BERKELEY;
2670 		return TRUE;
2671 	}
2672 
2673 	/* add vendor extensions here */
2674 
2675 #ifdef SUN_EXTENSIONS
2676 	if (strcasecmp(vendor, "Sun") == 0)
2677 	{
2678 		VendorCode = VENDOR_SUN;
2679 		return TRUE;
2680 	}
2681 #endif
2682 
2683 	return FALSE;
2684 }
2685 /*
2686 **  STRTOL -- convert string to long integer
2687 **
2688 **	For systems that don't have it in the C library.
2689 **
2690 **	This is taken verbatim from the 4.4-Lite C library.
2691 */
2692 
2693 #ifdef NEEDSTRTOL
2694 
2695 #if defined(LIBC_SCCS) && !defined(lint)
2696 static char sccsid[] = "@(#)strtol.c	8.1 (Berkeley) 6/4/93";
2697 #endif /* LIBC_SCCS and not lint */
2698 
2699 #include <limits.h>
2700 
2701 /*
2702  * Convert a string to a long integer.
2703  *
2704  * Ignores `locale' stuff.  Assumes that the upper and lower case
2705  * alphabets and digits are each contiguous.
2706  */
2707 
2708 long
2709 strtol(nptr, endptr, base)
2710 	const char *nptr;
2711 	char **endptr;
2712 	register int base;
2713 {
2714 	register const char *s = nptr;
2715 	register unsigned long acc;
2716 	register int c;
2717 	register unsigned long cutoff;
2718 	register int neg = 0, any, cutlim;
2719 
2720 	/*
2721 	 * Skip white space and pick up leading +/- sign if any.
2722 	 * If base is 0, allow 0x for hex and 0 for octal, else
2723 	 * assume decimal; if base is already 16, allow 0x.
2724 	 */
2725 	do {
2726 		c = *s++;
2727 	} while (isspace(c));
2728 	if (c == '-') {
2729 		neg = 1;
2730 		c = *s++;
2731 	} else if (c == '+')
2732 		c = *s++;
2733 	if ((base == 0 || base == 16) &&
2734 	    c == '0' && (*s == 'x' || *s == 'X')) {
2735 		c = s[1];
2736 		s += 2;
2737 		base = 16;
2738 	}
2739 	if (base == 0)
2740 		base = c == '0' ? 8 : 10;
2741 
2742 	/*
2743 	 * Compute the cutoff value between legal numbers and illegal
2744 	 * numbers.  That is the largest legal value, divided by the
2745 	 * base.  An input number that is greater than this value, if
2746 	 * followed by a legal input character, is too big.  One that
2747 	 * is equal to this value may be valid or not; the limit
2748 	 * between valid and invalid numbers is then based on the last
2749 	 * digit.  For instance, if the range for longs is
2750 	 * [-2147483648..2147483647] and the input base is 10,
2751 	 * cutoff will be set to 214748364 and cutlim to either
2752 	 * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
2753 	 * a value > 214748364, or equal but the next digit is > 7 (or 8),
2754 	 * the number is too big, and we will return a range error.
2755 	 *
2756 	 * Set any if any `digits' consumed; make it negative to indicate
2757 	 * overflow.
2758 	 */
2759 	cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
2760 	cutlim = cutoff % (unsigned long)base;
2761 	cutoff /= (unsigned long)base;
2762 	for (acc = 0, any = 0;; c = *s++) {
2763 		if (isdigit(c))
2764 			c -= '0';
2765 		else if (isalpha(c))
2766 			c -= isupper(c) ? 'A' - 10 : 'a' - 10;
2767 		else
2768 			break;
2769 		if (c >= base)
2770 			break;
2771 		if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
2772 			any = -1;
2773 		else {
2774 			any = 1;
2775 			acc *= base;
2776 			acc += c;
2777 		}
2778 	}
2779 	if (any < 0) {
2780 		acc = neg ? LONG_MIN : LONG_MAX;
2781 		errno = ERANGE;
2782 	} else if (neg)
2783 		acc = -acc;
2784 	if (endptr != 0)
2785 		*endptr = (char *)(any ? s - 1 : nptr);
2786 	return (acc);
2787 }
2788 
2789 #endif
2790 /*
2791 **  SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
2792 **
2793 **	Some operating systems have wierd problems with the gethostbyXXX
2794 **	routines.  For example, Solaris versions at least through 2.3
2795 **	don't properly deliver a canonical h_name field.  This tries to
2796 **	work around these problems.
2797 */
2798 
2799 struct hostent *
2800 sm_gethostbyname(name)
2801 	char *name;
2802 {
2803 #if defined(SOLARIS) && SOLARIS < 204
2804 	extern int h_errno;
2805 
2806 # if SOLARIS == 203
2807 	static struct hostent hp;
2808 	static char buf[1000];
2809 	extern struct hostent *_switch_gethostbyname_r();
2810 
2811 	return _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
2812 # else
2813 	extern struct hostent *__switch_gethostbyname();
2814 
2815 	return __switch_gethostbyname(name);
2816 # endif
2817 #else
2818 	return gethostbyname(name);
2819 #endif
2820 }
2821 
2822 struct hostent *
2823 sm_gethostbyaddr(addr, len, type)
2824 	char *addr;
2825 	int len;
2826 	int type;
2827 {
2828 #if defined(SOLARIS) && SOLARIS < 204
2829 	extern int h_errno;
2830 
2831 # if SOLARIS == 203
2832 	static struct hostent hp;
2833 	static char buf[1000];
2834 	extern struct hostent *_switch_gethostbyaddr_r();
2835 
2836 	return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno);
2837 # else
2838 	extern struct hostent *__switch_gethostbyaddr();
2839 
2840 	return __switch_gethostbyaddr(addr, len, type);
2841 # endif
2842 #else
2843 	return gethostbyaddr(addr, len, type);
2844 #endif
2845 }
2846 /*
2847 **  SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
2848 */
2849 
2850 struct passwd *
2851 sm_getpwnam(user)
2852 	char *user;
2853 {
2854 	return getpwnam(user);
2855 }
2856 
2857 struct passwd *
2858 sm_getpwuid(uid)
2859 	uid_t uid;
2860 {
2861 	return getpwuid(uid);
2862 }
2863 /*
2864 **  NI_PROPVAL -- netinfo property value lookup routine
2865 **
2866 **	Parameters:
2867 **		keydir -- the Netinfo directory name in which to search
2868 **			for the key.
2869 **		keyprop -- the name of the property in which to find the
2870 **			property we are interested.  Defaults to "name".
2871 **		keyval -- the value for which we are really searching.
2872 **		valprop -- the property name for the value in which we
2873 **			are interested.
2874 **		sepchar -- if non-nil, this can be multiple-valued, and
2875 **			we should return a string separated by this
2876 **			character.
2877 **
2878 **	Returns:
2879 **		NULL -- if:
2880 **			1. the directory is not found
2881 **			2. the property name is not found
2882 **			3. the property contains multiple values
2883 **			4. some error occured
2884 **		else -- the location of the config file.
2885 **
2886 **	Example:
2887 **		To search for an alias value, use:
2888 **		  ni_propval("/aliases", "name", aliasname, "members", ',')
2889 **
2890 **	Notes:
2891 **      	Caller should free the return value of ni_proval
2892 */
2893 
2894 #ifdef NETINFO
2895 
2896 # include <netinfo/ni.h>
2897 
2898 # define LOCAL_NETINFO_DOMAIN    "."
2899 # define PARENT_NETINFO_DOMAIN   ".."
2900 # define MAX_NI_LEVELS           256
2901 
2902 char *
2903 ni_propval(keydir, keyprop, keyval, valprop, sepchar)
2904 	char *keydir;
2905 	char *keyprop;
2906 	char *keyval;
2907 	char *valprop;
2908 	char sepchar;
2909 {
2910 	char *propval = NULL;
2911 	int i;
2912 	int j, alen;
2913 	void *ni = NULL;
2914 	void *lastni = NULL;
2915 	ni_status nis;
2916 	ni_id nid;
2917 	ni_namelist ninl;
2918 	register char *p;
2919 	char keybuf[1024];
2920 
2921 	/*
2922 	**  Create the full key from the two parts.
2923 	**
2924 	**	Note that directory can end with, e.g., "name=" to specify
2925 	**	an alternate search property.
2926 	*/
2927 
2928 	i = strlen(keydir) + strlen(keyval) + 2;
2929 	if (keyprop != NULL)
2930 		i += strlen(keyprop) + 1;
2931 	if (i > sizeof keybuf)
2932 		return NULL;
2933 	strcpy(keybuf, keydir);
2934 	strcat(keybuf, "/");
2935 	if (keyprop != NULL)
2936 	{
2937 		strcat(keybuf, keyprop);
2938 		strcat(keybuf, "=");
2939 	}
2940 	strcat(keybuf, keyval);
2941 
2942 	/*
2943 	**  If the passed directory and property name are found
2944 	**  in one of netinfo domains we need to search (starting
2945 	**  from the local domain moving all the way back to the
2946 	**  root domain) set propval to the property's value
2947 	**  and return it.
2948 	*/
2949 
2950 	for (i = 0; i < MAX_NI_LEVELS; ++i)
2951 	{
2952 		if (i == 0)
2953 		{
2954 			nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni);
2955 		}
2956 		else
2957 		{
2958 			if (lastni != NULL)
2959 				ni_free(lastni);
2960 			lastni = ni;
2961 			nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni);
2962 		}
2963 
2964 		/*
2965 		**  Don't bother if we didn't get a handle on a
2966 		**  proper domain.  This is not necessarily an error.
2967 		**  We would get a positive ni_status if, for instance
2968 		**  we never found the directory or property and tried
2969 		**  to open the parent of the root domain!
2970 		*/
2971 
2972 		if (nis != 0)
2973 			break;
2974 
2975 		/*
2976 		**  Find the path to the server information.
2977 		*/
2978 
2979 		if (ni_pathsearch(ni, &nid, keybuf) != 0)
2980 			continue;
2981 
2982 		/*
2983 		**  Find associated value information.
2984 		*/
2985 
2986 		if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0)
2987 			continue;
2988 
2989 		/*
2990 		**  See if we have an acceptable number of values.
2991 		*/
2992 
2993 		if (ninl.ni_namelist_len <= 0)
2994 			continue;
2995 
2996 		if (sepchar == '\0' && ninl.ni_namelist_len > 1)
2997 		{
2998 			ni_namelist_free(&ninl);
2999 			continue;
3000 		}
3001 
3002 		/*
3003 		**  Calculate number of bytes needed and build result
3004 		*/
3005 
3006 		alen = 1;
3007 		for (j = 0; j < ninl.ni_namelist_len; j++)
3008 			alen += strlen(ninl.ni_namelist_val[j]) + 1;
3009 		propval = p = xalloc(alen);
3010 		for (j = 0; j < ninl.ni_namelist_len; j++)
3011 		{
3012 			strcpy(p, ninl.ni_namelist_val[j]);
3013 			p += strlen(p);
3014 			*p++ = sepchar;
3015 		}
3016 		*--p = '\0';
3017 
3018 		ni_namelist_free(&ninl);
3019 	}
3020 
3021 	/*
3022 	**  Clean up.
3023 	*/
3024 
3025 	if (ni != NULL)
3026 		ni_free(ni);
3027 	if (lastni != NULL && ni != lastni)
3028 		ni_free(lastni);
3029 
3030 	return propval;
3031 }
3032 
3033 #endif /* NETINFO */
3034 /*
3035 **  HARD_SYSLOG -- call syslog repeatedly until it works
3036 **
3037 **	Needed on HP-UX, which apparently doesn't guarantee that
3038 **	syslog succeeds during interrupt handlers.
3039 */
3040 
3041 #ifdef __hpux
3042 
3043 # define MAXSYSLOGTRIES	100
3044 # undef syslog
3045 
3046 # ifdef __STDC__
3047 hard_syslog(int pri, char *msg, ...)
3048 # else
3049 hard_syslog(pri, msg, va_alist)
3050 	int pri;
3051 	char *msg;
3052 	va_dcl
3053 # endif
3054 {
3055 	int i;
3056 	char buf[SYSLOG_BUFSIZE * 2];
3057 	VA_LOCAL_DECL;
3058 
3059 	VA_START(msg);
3060 	vsprintf(buf, msg, ap);
3061 	VA_END;
3062 
3063 	for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, "%s", buf) < 0; )
3064 		continue;
3065 }
3066 
3067 #endif
3068 /*
3069 **  LOCAL_HOSTNAME_LENGTH
3070 **
3071 **	This is required to get sendmail to compile against BIND 4.9.x
3072 **	on Ultrix.
3073 */
3074 
3075 #if defined(ultrix) && NAMED_BIND
3076 
3077 # include <resolv.h>
3078 # if __RES >= 19931104
3079 
3080 int
3081 local_hostname_length(hostname)
3082 	char *hostname;
3083 {
3084 	int len_host, len_domain;
3085 
3086 	if (!*_res.defdname)
3087 		res_init();
3088 	len_host = strlen(hostname);
3089 	len_domain = strlen(_res.defdname);
3090 	if (len_host > len_domain &&
3091 	    (strcasecmp(hostname + len_host - len_domain,_res.defdname) == 0) &&
3092 	    hostname[len_host - len_domain - 1] == '.')
3093 		return len_host - len_domain - 1;
3094 	else
3095 		return 0;
3096 }
3097 
3098 # endif
3099 #endif
3100 /*
3101 **  Compile-Time options
3102 */
3103 
3104 char	*CompileOptions[] =
3105 {
3106 #if HESIOD
3107 	"HESIOD",
3108 #endif
3109 #if LOG
3110 	"LOG",
3111 #endif
3112 #if MATCHGECOS
3113 	"MATCHGECOS",
3114 #endif
3115 #if NAMED_BIND
3116 	"NAMED_BIND",
3117 #endif
3118 #if NDBM
3119 	"NDBM",
3120 #endif
3121 #if NETINET
3122 	"NETINET",
3123 #endif
3124 #if NETINFO
3125 	"NETINFO",
3126 #endif
3127 #if NETISO
3128 	"NETISO",
3129 #endif
3130 #if NETNS
3131 	"NETNS",
3132 #endif
3133 #if NETUNIX
3134 	"NETUNIX",
3135 #endif
3136 #if NETX25
3137 	"NETX25",
3138 #endif
3139 #if NEWDB
3140 	"NEWDB",
3141 #endif
3142 #if NIS
3143 	"NIS",
3144 #endif
3145 #if SCANF
3146 	"SCANF",
3147 #endif
3148 #if SUID_ROOT_FILES_OK
3149 	"SUID_ROOT_FILES_OK",
3150 #endif
3151 #if USERDB
3152 	"USERDB",
3153 #endif
3154 #if XDEBUG
3155 	"XDEBUG",
3156 #endif
3157 #if XLA
3158 	"XLA",
3159 #endif
3160 	NULL
3161 };
3162