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