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