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