]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/conf.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / conf.c
1 /*
2  * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4  * Copyright (c) 1988, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * By using this file, you agree to the terms and conditions set
8  * forth in the LICENSE file which can be found at the top level of
9  * the sendmail distribution.
10  *
11  */
12
13 #ifndef lint
14 static char sccsid[] = "@(#)conf.c      8.452 (Berkeley) 1/26/1999";
15 #endif /* not lint */
16
17 # include "sendmail.h"
18 # include "pathnames.h"
19 # include <sys/ioctl.h>
20 # include <sys/param.h>
21 # include <limits.h>
22
23 /*
24 **  CONF.C -- Sendmail Configuration Tables.
25 **
26 **      Defines the configuration of this installation.
27 **
28 **      Configuration Variables:
29 **              HdrInfo -- a table describing well-known header fields.
30 **                      Each entry has the field name and some flags,
31 **                      which are described in sendmail.h.
32 **
33 **      Notes:
34 **              I have tried to put almost all the reasonable
35 **              configuration information into the configuration
36 **              file read at runtime.  My intent is that anything
37 **              here is a function of the version of UNIX you
38 **              are running, or is really static -- for example
39 **              the headers are a superset of widely used
40 **              protocols.  If you find yourself playing with
41 **              this file too much, you may be making a mistake!
42 */
43
44
45 /*
46 **  Header info table
47 **      Final (null) entry contains the flags used for any other field.
48 **
49 **      Not all of these are actually handled specially by sendmail
50 **      at this time.  They are included as placeholders, to let
51 **      you know that "someday" I intend to have sendmail do
52 **      something with them.
53 */
54
55 struct hdrinfo  HdrInfo[] =
56 {
57                 /* originator fields, most to least significant  */
58         { "resent-sender",              H_FROM|H_RESENT                 },
59         { "resent-from",                H_FROM|H_RESENT                 },
60         { "resent-reply-to",            H_FROM|H_RESENT                 },
61         { "sender",                     H_FROM                          },
62         { "from",                       H_FROM                          },
63         { "reply-to",                   H_FROM                          },
64         { "errors-to",                  H_FROM|H_ERRORSTO               },
65         { "full-name",                  H_ACHECK                        },
66         { "return-receipt-to",          H_RECEIPTTO                     },
67
68                 /* destination fields */
69         { "to",                         H_RCPT                          },
70         { "resent-to",                  H_RCPT|H_RESENT                 },
71         { "cc",                         H_RCPT                          },
72         { "resent-cc",                  H_RCPT|H_RESENT                 },
73         { "bcc",                        H_RCPT|H_BCC                    },
74         { "resent-bcc",                 H_RCPT|H_BCC|H_RESENT           },
75         { "apparently-to",              H_RCPT                          },
76
77                 /* message identification and control */
78         { "message-id",                 0                               },
79         { "resent-message-id",          H_RESENT                        },
80         { "message",                    H_EOH                           },
81         { "text",                       H_EOH                           },
82
83                 /* date fields */
84         { "date",                       0                               },
85         { "resent-date",                H_RESENT                        },
86
87                 /* trace fields */
88         { "received",                   H_TRACE|H_FORCE                 },
89         { "x400-received",              H_TRACE|H_FORCE                 },
90         { "via",                        H_TRACE|H_FORCE                 },
91         { "mail-from",                  H_TRACE|H_FORCE                 },
92
93                 /* miscellaneous fields */
94         { "comments",                   H_FORCE|H_ENCODABLE             },
95         { "return-path",                H_FORCE|H_ACHECK                },
96         { "content-transfer-encoding",  H_CTE                           },
97         { "content-type",               H_CTYPE                         },
98         { "content-length",             H_ACHECK                        },
99         { "subject",                    H_ENCODABLE                     },
100
101         { NULL,                         0                               }
102 };
103
104
105
106 /*
107 **  Privacy values
108 */
109
110 struct prival PrivacyValues[] =
111 {
112         { "public",             PRIV_PUBLIC             },
113         { "needmailhelo",       PRIV_NEEDMAILHELO       },
114         { "needexpnhelo",       PRIV_NEEDEXPNHELO       },
115         { "needvrfyhelo",       PRIV_NEEDVRFYHELO       },
116         { "noexpn",             PRIV_NOEXPN             },
117         { "novrfy",             PRIV_NOVRFY             },
118         { "restrictmailq",      PRIV_RESTRICTMAILQ      },
119         { "restrictqrun",       PRIV_RESTRICTQRUN       },
120         { "noetrn",             PRIV_NOETRN             },
121         { "noverb",             PRIV_NOVERB             },
122         { "authwarnings",       PRIV_AUTHWARNINGS       },
123         { "noreceipts",         PRIV_NORECEIPTS         },
124         { "goaway",             PRIV_GOAWAY             },
125         { NULL,                 0                       }
126 };
127
128 /*
129 **  DontBlameSendmail values
130 */
131 struct dbsval DontBlameSendmailValues[] =
132 {
133         { "safe",                       DBS_SAFE                        },
134         { "assumesafechown",            DBS_ASSUMESAFECHOWN             },
135         { "groupwritabledirpathsafe",   DBS_GROUPWRITABLEDIRPATHSAFE    },
136         { "groupwritableforwardfilesafe",
137                                         DBS_GROUPWRITABLEFORWARDFILESAFE },
138         { "groupwritableincludefilesafe",
139                                         DBS_GROUPWRITABLEINCLUDEFILESAFE },
140         { "groupwritablealiasfile",     DBS_GROUPWRITABLEALIASFILE      },
141         { "worldwritablealiasfile",     DBS_WORLDWRITABLEALIASFILE      },
142         { "forwardfileinunsafedirpath", DBS_FORWARDFILEINUNSAFEDIRPATH  },
143         { "includefileinunsafedirpath", DBS_INCLUDEFILEINUNSAFEDIRPATH  },
144         { "mapinunsafedirpath",         DBS_MAPINUNSAFEDIRPATH  },
145         { "linkedaliasfileinwritabledir",
146                                         DBS_LINKEDALIASFILEINWRITABLEDIR },
147         { "linkedclassfileinwritabledir",
148                                         DBS_LINKEDCLASSFILEINWRITABLEDIR },
149         { "linkedforwardfileinwritabledir",
150                                         DBS_LINKEDFORWARDFILEINWRITABLEDIR },
151         { "linkedincludefileinwritabledir",
152                                         DBS_LINKEDINCLUDEFILEINWRITABLEDIR },
153         { "linkedmapinwritabledir",     DBS_LINKEDMAPINWRITABLEDIR      },
154         { "linkedserviceswitchfileinwritabledir",
155                                         DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR },
156         { "filedeliverytohardlink",     DBS_FILEDELIVERYTOHARDLINK      },
157         { "filedeliverytosymlink",      DBS_FILEDELIVERYTOSYMLINK       },
158         { "writemaptohardlink",         DBS_WRITEMAPTOHARDLINK          },
159         { "writemaptosymlink",          DBS_WRITEMAPTOSYMLINK           },
160         { "writestatstohardlink",       DBS_WRITESTATSTOHARDLINK        },
161         { "writestatstosymlink",        DBS_WRITESTATSTOSYMLINK         },
162         { "forwardfileingroupwritabledirpath",
163                                         DBS_FORWARDFILEINGROUPWRITABLEDIRPATH },
164         { "includefileingroupwritabledirpath",
165                                         DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH },
166         { "classfileinunsafedirpath",   DBS_CLASSFILEINUNSAFEDIRPATH    },
167         { "errorheaderinunsafedirpath", DBS_ERRORHEADERINUNSAFEDIRPATH  },
168         { "helpfileinunsafedirpath",    DBS_HELPFILEINUNSAFEDIRPATH     },
169         { "forwardfileinunsafedirpathsafe",
170                                         DBS_FORWARDFILEINUNSAFEDIRPATHSAFE },
171         { "includefileinunsafedirpathsafe",
172                                         DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE },
173         { "runprograminunsafedirpath",  DBS_RUNPROGRAMINUNSAFEDIRPATH   },
174         { "runwritableprogram",         DBS_RUNWRITABLEPROGRAM          },
175         { NULL,                         0                               }
176 };
177
178
179 /*
180 **  Miscellaneous stuff.
181 */
182
183 int     DtableSize =    50;             /* max open files; reset in 4.2bsd */
184 \f/*
185 **  SETDEFAULTS -- set default values
186 **
187 **      Because of the way freezing is done, these must be initialized
188 **      using direct code.
189 **
190 **      Parameters:
191 **              e -- the default envelope.
192 **
193 **      Returns:
194 **              none.
195 **
196 **      Side Effects:
197 **              Initializes a bunch of global variables to their
198 **              default values.
199 */
200
201 #define MINUTES         * 60
202 #define HOURS           * 60 MINUTES
203 #define DAYS            * 24 HOURS
204
205 #ifndef _PATH_VARTMP
206 # define _PATH_VARTMP   "/usr/tmp/"
207 #endif
208
209 #ifndef MAXRULERECURSION
210 # define MAXRULERECURSION       50      /* max ruleset recursion depth */
211 #endif
212
213 void
214 setdefaults(e)
215         register ENVELOPE *e;
216 {
217         int i;
218         struct passwd *pw;
219         char buf[MAXNAME];
220         extern void setdefuser __P((void));
221         extern void setupmaps __P((void));
222         extern void setupmailers __P((void));
223         extern void setupheaders __P((void));
224
225         SpaceSub = ' ';                         /* option B */
226         QueueLA = 8;                            /* option x */
227         RefuseLA = 12;                          /* option X */
228         WkRecipFact = 30000L;                   /* option y */
229         WkClassFact = 1800L;                    /* option z */
230         WkTimeFact = 90000L;                    /* option Z */
231         QueueFactor = WkRecipFact * 20;         /* option q */
232         FileMode = (RealUid != geteuid()) ? 0644 : 0600;
233                                                 /* option F */
234
235         if (((pw = getpwnam("mailnull")) != NULL && pw->pw_uid != 0) ||
236             ((pw = getpwnam("sendmail")) != NULL && pw->pw_uid != 0) ||
237             ((pw = getpwnam("daemon")) != NULL && pw->pw_uid != 0))
238         {
239                 DefUid = pw->pw_uid;            /* option u */
240                 DefGid = pw->pw_gid;            /* option g */
241                 DefUser = newstr(pw->pw_name);
242         }
243         else
244         {
245                 DefUid = 1;                     /* option u */
246                 DefGid = 1;                     /* option g */
247                 setdefuser();
248         }
249         TrustedUid = 0;
250         if (tTd(37, 4))
251                 printf("setdefaults: DefUser=%s, DefUid=%d, DefGid=%d\n",
252                        DefUser != NULL ? DefUser : "<1:1>",
253                        (int) DefUid, (int) DefGid);
254         CheckpointInterval = 10;                /* option C */
255         MaxHopCount = 25;                       /* option h */
256         e->e_sendmode = SM_FORK;                /* option d */
257         e->e_errormode = EM_PRINT;              /* option e */
258         SevenBitInput = FALSE;                  /* option 7 */
259         MaxMciCache = 1;                        /* option k */
260         MciCacheTimeout = 5 MINUTES;            /* option K */
261         LogLevel = 9;                           /* option L */
262         inittimeouts(NULL);                     /* option r */
263         PrivacyFlags = PRIV_PUBLIC;             /* option p */
264         DontBlameSendmail = DBS_SAFE;           /* DontBlameSendmail option */
265 #if MIME8TO7
266         MimeMode = MM_CVTMIME|MM_PASS8BIT;      /* option 8 */
267 #else
268         MimeMode = MM_PASS8BIT;
269 #endif
270         for (i = 0; i < MAXTOCLASS; i++)
271         {
272                 TimeOuts.to_q_return[i] = 5 DAYS;       /* option T */
273                 TimeOuts.to_q_warning[i] = 0;           /* option T */
274         }
275         ServiceSwitchFile = "/etc/service.switch";
276         ServiceCacheMaxAge = (time_t) 10;
277         HostsFile = _PATH_HOSTS;
278         PidFile = newstr(_PATH_SENDMAILPID);
279         MustQuoteChars = "@,;:\\()[].'";
280         MciInfoTimeout = 30 MINUTES;
281         MaxRuleRecursion = MAXRULERECURSION;
282         MaxAliasRecursion = 10;
283         MaxMacroRecursion = 10;
284         ColonOkInAddr = TRUE;
285         DontLockReadFiles = TRUE;
286         DoubleBounceAddr = "postmaster";
287         MaxHeadersLength = MAXHDRSLEN;
288         snprintf(buf, sizeof buf, "%s%sdead.letter",
289                 _PATH_VARTMP,
290                 _PATH_VARTMP[sizeof _PATH_VARTMP - 2] == '/' ? "" : "/");
291         DeadLetterDrop = newstr(buf);
292 #ifdef HESIOD_INIT
293         HesiodContext = NULL;
294 #endif
295         ControlSocketName = NULL;
296         setupmaps();
297         setupmailers();
298         setupheaders();
299 }
300
301
302 /*
303 **  SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
304 */
305
306 void
307 setdefuser()
308 {
309         struct passwd *defpwent;
310         static char defuserbuf[40];
311
312         DefUser = defuserbuf;
313         defpwent = sm_getpwuid(DefUid);
314         snprintf(defuserbuf, sizeof defuserbuf, "%s",
315                 defpwent == NULL ? "nobody" : defpwent->pw_name);
316         if (tTd(37, 4))
317                 printf("setdefuser: DefUid=%d, DefUser=%s\n",
318                        (int) DefUid, DefUser);
319 }
320 \f/*
321 **  SETUPMAILERS -- initialize default mailers
322 */
323
324 void
325 setupmailers()
326 {
327         char buf[100];
328
329         strcpy(buf, "prog, P=/bin/sh, F=lsoDq9, T=DNS/RFC822/X-Unix, A=sh -c \201u");
330         makemailer(buf);
331
332         strcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=DNS/RFC822/X-Unix, A=FILE \201u");
333         makemailer(buf);
334
335         strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u");
336         makemailer(buf);
337 }
338 \f/*
339 **  SETUPMAPS -- set up map classes
340 */
341
342 #define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \
343         { \
344                 extern bool parse __P((MAP *, char *)); \
345                 extern bool open __P((MAP *, int)); \
346                 extern void close __P((MAP *)); \
347                 extern char *lookup __P((MAP *, char *, char **, int *)); \
348                 extern void store __P((MAP *, char *, char *)); \
349                 s = stab(name, ST_MAPCLASS, ST_ENTER); \
350                 s->s_mapclass.map_cname = name; \
351                 s->s_mapclass.map_ext = ext; \
352                 s->s_mapclass.map_cflags = flags; \
353                 s->s_mapclass.map_parse = parse; \
354                 s->s_mapclass.map_open = open; \
355                 s->s_mapclass.map_close = close; \
356                 s->s_mapclass.map_lookup = lookup; \
357                 s->s_mapclass.map_store = store; \
358         }
359
360 void
361 setupmaps()
362 {
363         register STAB *s;
364
365 #ifdef NEWDB
366         MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
367                 map_parseargs, hash_map_open, db_map_close,
368                 db_map_lookup, db_map_store);
369
370         MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
371                 map_parseargs, bt_map_open, db_map_close,
372                 db_map_lookup, db_map_store);
373 #endif
374
375 #ifdef NDBM
376         MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE,
377                 map_parseargs, ndbm_map_open, ndbm_map_close,
378                 ndbm_map_lookup, ndbm_map_store);
379 #endif
380
381 #ifdef NIS
382         MAPDEF("nis", NULL, MCF_ALIASOK,
383                 map_parseargs, nis_map_open, null_map_close,
384                 nis_map_lookup, null_map_store);
385 #endif
386
387 #ifdef NISPLUS
388         MAPDEF("nisplus", NULL, MCF_ALIASOK,
389                 map_parseargs, nisplus_map_open, null_map_close,
390                 nisplus_map_lookup, null_map_store);
391 #endif
392 #ifdef LDAPMAP
393         MAPDEF("ldapx", NULL, 0,
394                 ldap_map_parseargs, ldap_map_open, ldap_map_close,
395                 ldap_map_lookup, null_map_store);
396 #endif
397
398 #ifdef HESIOD
399         MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY,
400                 map_parseargs, hes_map_open, null_map_close,
401                 hes_map_lookup, null_map_store);
402 #endif
403
404 #if NETINFO
405         MAPDEF("netinfo", NULL, MCF_ALIASOK,
406                 map_parseargs, ni_map_open, null_map_close,
407                 ni_map_lookup, null_map_store);
408 #endif
409
410 #if 0
411         MAPDEF("dns", NULL, 0,
412                 dns_map_init, null_map_open, null_map_close,
413                 dns_map_lookup, null_map_store);
414 #endif
415
416 #if NAMED_BIND
417         /* best MX DNS lookup */
418         MAPDEF("bestmx", NULL, MCF_OPTFILE,
419                 map_parseargs, null_map_open, null_map_close,
420                 bestmx_map_lookup, null_map_store);
421 #endif
422
423         MAPDEF("host", NULL, 0,
424                 host_map_init, null_map_open, null_map_close,
425                 host_map_lookup, null_map_store);
426
427         MAPDEF("text", NULL, MCF_ALIASOK,
428                 map_parseargs, text_map_open, null_map_close,
429                 text_map_lookup, null_map_store);
430
431         MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY,
432                 map_parseargs, stab_map_open, null_map_close,
433                 stab_map_lookup, stab_map_store);
434
435         MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE,
436                 map_parseargs, impl_map_open, impl_map_close,
437                 impl_map_lookup, impl_map_store);
438
439         /* access to system passwd file */
440         MAPDEF("user", NULL, MCF_OPTFILE,
441                 map_parseargs, user_map_open, null_map_close,
442                 user_map_lookup, null_map_store);
443
444         /* dequote map */
445         MAPDEF("dequote", NULL, 0,
446                 dequote_init, null_map_open, null_map_close,
447                 dequote_map, null_map_store);
448
449 #ifdef MAP_REGEX
450         MAPDEF("regex", NULL, 0,
451                 regex_map_init, null_map_open, null_map_close,
452                 regex_map_lookup, null_map_store);
453 #endif
454
455 #if USERDB
456         /* user database */
457         MAPDEF("userdb", ".db", 0,
458                 map_parseargs, null_map_open, null_map_close,
459                 udb_map_lookup, null_map_store);
460 #endif
461
462         /* arbitrary programs */
463         MAPDEF("program", NULL, MCF_ALIASOK,
464                 map_parseargs, null_map_open, null_map_close,
465                 prog_map_lookup, null_map_store);
466
467         /* sequenced maps */
468         MAPDEF("sequence", NULL, MCF_ALIASOK,
469                 seq_map_parse, null_map_open, null_map_close,
470                 seq_map_lookup, seq_map_store);
471
472         /* switched interface to sequenced maps */
473         MAPDEF("switch", NULL, MCF_ALIASOK,
474                 map_parseargs, switch_map_open, null_map_close,
475                 seq_map_lookup, seq_map_store);
476
477         /* null map lookup -- really for internal use only */
478         MAPDEF("null", NULL, MCF_ALIASOK|MCF_OPTFILE,
479                 map_parseargs, null_map_open, null_map_close,
480                 null_map_lookup, null_map_store);
481
482 #if _FFR_MAP_SYSLOG
483         /* syslog map -- logs information to syslog */
484         MAPDEF("syslog", NULL, 0,
485                syslog_map_parseargs, null_map_open, null_map_close,
486                syslog_map_lookup, null_map_store);
487 #endif
488 }
489
490 #undef MAPDEF
491 \f/*
492 **  INITHOSTMAPS -- initial host-dependent maps
493 **
494 **      This should act as an interface to any local service switch
495 **      provided by the host operating system.
496 **
497 **      Parameters:
498 **              none
499 **
500 **      Returns:
501 **              none
502 **
503 **      Side Effects:
504 **              Should define maps "host" and "users" as necessary
505 **              for this OS.  If they are not defined, they will get
506 **              a default value later.  It should check to make sure
507 **              they are not defined first, since it's possible that
508 **              the config file has provided an override.
509 */
510
511 void
512 inithostmaps()
513 {
514         register int i;
515         int nmaps;
516         char *maptype[MAXMAPSTACK];
517         short mapreturn[MAXMAPACTIONS];
518         char buf[MAXLINE];
519
520         /*
521         **  Set up default hosts maps.
522         */
523
524 #if 0
525         nmaps = switch_map_find("hosts", maptype, mapreturn);
526         for (i = 0; i < nmaps; i++)
527         {
528                 if (strcmp(maptype[i], "files") == 0 &&
529                     stab("hosts.files", ST_MAP, ST_FIND) == NULL)
530                 {
531                         strcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts");
532                         (void) makemapentry(buf);
533                 }
534 #if NAMED_BIND
535                 else if (strcmp(maptype[i], "dns") == 0 &&
536                     stab("hosts.dns", ST_MAP, ST_FIND) == NULL)
537                 {
538                         strcpy(buf, "hosts.dns dns A");
539                         (void) makemapentry(buf);
540                 }
541 #endif
542 #ifdef NISPLUS
543                 else if (strcmp(maptype[i], "nisplus") == 0 &&
544                     stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL)
545                 {
546                         strcpy(buf, "hosts.nisplus nisplus -k name -v address -d hosts.org_dir");
547                         (void) makemapentry(buf);
548                 }
549 #endif
550 #ifdef NIS
551                 else if (strcmp(maptype[i], "nis") == 0 &&
552                     stab("hosts.nis", ST_MAP, ST_FIND) == NULL)
553                 {
554                         strcpy(buf, "hosts.nis nis -d -k 0 -v 1 hosts.byname");
555                         (void) makemapentry(buf);
556                 }
557 #endif
558 #if NETINFO
559                 else if (strcmp(maptype[i], "netinfo") == 0) &&
560                     stab("hosts.netinfo", ST_MAP, ST_FIND) == NULL)
561                 {
562                         strcpy(buf, "hosts.netinfo netinfo -v name /machines");
563                         (void) makemapentry(buf);
564                 }
565 #endif
566         }
567 #endif
568
569         /*
570         **  Make sure we have a host map.
571         */
572
573         if (stab("host", ST_MAP, ST_FIND) == NULL)
574         {
575                 /* user didn't initialize: set up host map */
576                 strcpy(buf, "host host");
577 #if NAMED_BIND
578                 if (ConfigLevel >= 2)
579                         strcat(buf, " -a.");
580 #endif
581                 (void) makemapentry(buf);
582         }
583
584         /*
585         **  Set up default aliases maps
586         */
587
588         nmaps = switch_map_find("aliases", maptype, mapreturn);
589         for (i = 0; i < nmaps; i++)
590         {
591                 if (strcmp(maptype[i], "files") == 0 &&
592                     stab("aliases.files", ST_MAP, ST_FIND) == NULL)
593                 {
594                         strcpy(buf, "aliases.files null");
595                         (void) makemapentry(buf);
596                 }
597 #ifdef NISPLUS
598                 else if (strcmp(maptype[i], "nisplus") == 0 &&
599                     stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL)
600                 {
601                         strcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion -d mail_aliases.org_dir");
602                         (void) makemapentry(buf);
603                 }
604 #endif
605 #ifdef NIS
606                 else if (strcmp(maptype[i], "nis") == 0 &&
607                     stab("aliases.nis", ST_MAP, ST_FIND) == NULL)
608                 {
609                         strcpy(buf, "aliases.nis nis -d mail.aliases");
610                         (void) makemapentry(buf);
611                 }
612 #endif
613 #ifdef NETINFO
614                 else if (strcmp(maptype[i], "netinfo") == 0 &&
615                     stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL)
616                 {
617                         strcpy(buf, "aliases.netinfo netinfo -z, /aliases");
618                         (void) makemapentry(buf);
619                 }
620 #endif
621 #ifdef HESIOD
622                 else if (strcmp(maptype[i], "hesiod") == 0 &&
623                     stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL)
624                 {
625                         strcpy(buf, "aliases.hesiod hesiod aliases");
626                         (void) makemapentry(buf);
627                 }
628 #endif
629         }
630         if (stab("aliases", ST_MAP, ST_FIND) == NULL)
631         {
632                 strcpy(buf, "aliases switch aliases");
633                 (void) makemapentry(buf);
634         }
635
636 #if 0           /* "user" map class is a better choice */
637         /*
638         **  Set up default users maps.
639         */
640
641         nmaps = switch_map_find("passwd", maptype, mapreturn);
642         for (i = 0; i < nmaps; i++)
643         {
644                 if (strcmp(maptype[i], "files") == 0 &&
645                     stab("users.files", ST_MAP, ST_FIND) == NULL)
646                 {
647                         strcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd");
648                         (void) makemapentry(buf);
649                 }
650 #ifdef NISPLUS
651                 else if (strcmp(maptype[i], "nisplus") == 0 &&
652                     stab("users.nisplus", ST_MAP, ST_FIND) == NULL)
653                 {
654                         strcpy(buf, "users.nisplus nisplus -m -kname -vhome -d passwd.org_dir");
655                         (void) makemapentry(buf);
656                 }
657 #endif
658 #ifdef NIS
659                 else if (strcmp(maptype[i], "nis") == 0 &&
660                     stab("users.nis", ST_MAP, ST_FIND) == NULL)
661                 {
662                         strcpy(buf, "users.nis nis -m -d passwd.byname");
663                         (void) makemapentry(buf);
664                 }
665 #endif
666 #ifdef HESIOD
667                 else if (strcmp(maptype[i], "hesiod") == 0) &&
668                     stab("users.hesiod", ST_MAP, ST_FIND) == NULL)
669                 {
670                         strcpy(buf, "users.hesiod hesiod");
671                         (void) makemapentry(buf);
672                 }
673 #endif
674         }
675         if (stab("users", ST_MAP, ST_FIND) == NULL)
676         {
677                 strcpy(buf, "users switch -m passwd");
678                 (void) makemapentry(buf);
679         }
680 #endif
681 }
682 \f/*
683 **  SWITCH_MAP_FIND -- find the list of types associated with a map
684 **
685 **      This is the system-dependent interface to the service switch.
686 **
687 **      Parameters:
688 **              service -- the name of the service of interest.
689 **              maptype -- an out-array of strings containing the types
690 **                      of access to use for this service.  There can
691 **                      be at most MAXMAPSTACK types for a single service.
692 **              mapreturn -- an out-array of return information bitmaps
693 **                      for the map.
694 **
695 **      Returns:
696 **              The number of map types filled in, or -1 for failure.
697 */
698
699 #if defined(SOLARIS) || (defined(sony_news) && defined(__svr4))
700 # define _USE_SUN_NSSWITCH_
701 #endif
702
703 #ifdef _USE_SUN_NSSWITCH_
704 # include <nsswitch.h>
705 #endif
706
707 #if defined(ultrix) || (defined(__osf__) && defined(__alpha))
708 # define _USE_DEC_SVC_CONF_
709 #endif
710
711 #ifdef _USE_DEC_SVC_CONF_
712 # include <sys/svcinfo.h>
713 #endif
714
715 int
716 switch_map_find(service, maptype, mapreturn)
717         char *service;
718         char *maptype[MAXMAPSTACK];
719         short mapreturn[MAXMAPACTIONS];
720 {
721         int svcno;
722
723 #ifdef _USE_SUN_NSSWITCH_
724         struct __nsw_switchconfig *nsw_conf;
725         enum __nsw_parse_err pserr;
726         struct __nsw_lookup *lk;
727         static struct __nsw_lookup lkp0 =
728                 { "files", {1, 0, 0, 0}, NULL, NULL };
729         static struct __nsw_switchconfig lkp_default =
730                 { 0, "sendmail", 3, &lkp0 };
731
732         for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
733                 mapreturn[svcno] = 0;
734
735         if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL)
736                 lk = lkp_default.lookups;
737         else
738                 lk = nsw_conf->lookups;
739         svcno = 0;
740         while (lk != NULL)
741         {
742                 maptype[svcno] = lk->service_name;
743                 if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN)
744                         mapreturn[MA_NOTFOUND] |= 1 << svcno;
745                 if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN)
746                         mapreturn[MA_TRYAGAIN] |= 1 << svcno;
747                 if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN)
748                         mapreturn[MA_TRYAGAIN] |= 1 << svcno;
749                 svcno++;
750                 lk = lk->next;
751         }
752         return svcno;
753 #endif
754
755 #ifdef _USE_DEC_SVC_CONF_
756         struct svcinfo *svcinfo;
757         int svc;
758
759         for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
760                 mapreturn[svcno] = 0;
761
762         svcinfo = getsvc();
763         if (svcinfo == NULL)
764                 goto punt;
765         if (strcmp(service, "hosts") == 0)
766                 svc = SVC_HOSTS;
767         else if (strcmp(service, "aliases") == 0)
768                 svc = SVC_ALIASES;
769         else if (strcmp(service, "passwd") == 0)
770                 svc = SVC_PASSWD;
771         else
772                 return -1;
773         for (svcno = 0; svcno < SVC_PATHSIZE; svcno++)
774         {
775                 switch (svcinfo->svcpath[svc][svcno])
776                 {
777                   case SVC_LOCAL:
778                         maptype[svcno] = "files";
779                         break;
780
781                   case SVC_YP:
782                         maptype[svcno] = "nis";
783                         break;
784
785                   case SVC_BIND:
786                         maptype[svcno] = "dns";
787                         break;
788
789 #ifdef SVC_HESIOD
790                   case SVC_HESIOD:
791                         maptype[svcno] = "hesiod";
792                         break;
793 #endif
794
795                   case SVC_LAST:
796                         return svcno;
797                 }
798         }
799         return svcno;
800 #endif
801
802 #if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
803         /*
804         **  Fall-back mechanism.
805         */
806
807         STAB *st;
808         time_t now = curtime();
809
810         for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
811                 mapreturn[svcno] = 0;
812
813         if ((now - ServiceCacheTime) > (time_t) ServiceCacheMaxAge)
814         {
815                 /* (re)read service switch */
816                 register FILE *fp;
817                 int sff = SFF_REGONLY|SFF_OPENASROOT|SFF_NOLOCK;
818
819                 if (!bitset(DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR, DontBlameSendmail))
820                         sff |= SFF_NOWLINK;
821
822                 if (ConfigFileRead)
823                         ServiceCacheTime = now;
824                 fp = safefopen(ServiceSwitchFile, O_RDONLY, 0, sff);
825                 if (fp != NULL)
826                 {
827                         char buf[MAXLINE];
828
829                         while (fgets(buf, sizeof buf, fp) != NULL)
830                         {
831                                 register char *p;
832
833                                 p = strpbrk(buf, "#\n");
834                                 if (p != NULL)
835                                         *p = '\0';
836                                 p = strpbrk(buf, " \t");
837                                 if (p != NULL)
838                                         *p++ = '\0';
839                                 if (buf[0] == '\0')
840                                         continue;
841                                 if (p == NULL)
842                                 {
843                                         sm_syslog(LOG_ERR, NOQID,
844                                                   "Bad line on %.100s: %.100s",
845                                                   ServiceSwitchFile,
846                                                   buf);
847                                         continue;
848                                 }
849                                 while (isspace(*p))
850                                         p++;
851                                 if (*p == '\0')
852                                         continue;
853
854                                 /*
855                                 **  Find/allocate space for this service entry.
856                                 **      Space for all of the service strings
857                                 **      are allocated at once.  This means
858                                 **      that we only have to free the first
859                                 **      one to free all of them.
860                                 */
861
862                                 st = stab(buf, ST_SERVICE, ST_ENTER);
863                                 if (st->s_service[0] != NULL)
864                                         free((void *) st->s_service[0]);
865                                 p = newstr(p);
866                                 for (svcno = 0; svcno < MAXMAPSTACK; )
867                                 {
868                                         if (*p == '\0')
869                                                 break;
870                                         st->s_service[svcno++] = p;
871                                         p = strpbrk(p, " \t");
872                                         if (p == NULL)
873                                                 break;
874                                         *p++ = '\0';
875                                         while (isspace(*p))
876                                                 p++;
877                                 }
878                                 if (svcno < MAXMAPSTACK)
879                                         st->s_service[svcno] = NULL;
880                         }
881                         fclose(fp);
882                 }
883         }
884
885         /* look up entry in cache */
886         st = stab(service, ST_SERVICE, ST_FIND);
887         if (st != NULL && st->s_service[0] != NULL)
888         {
889                 /* extract data */
890                 svcno = 0;
891                 while (svcno < MAXMAPSTACK)
892                 {
893                         maptype[svcno] = st->s_service[svcno];
894                         if (maptype[svcno++] == NULL)
895                                 break;
896                 }
897                 return --svcno;
898         }
899 #endif
900
901 #if !defined(_USE_SUN_NSSWITCH_)
902         /* if the service file doesn't work, use an absolute fallback */
903 # ifdef _USE_DEC_SVC_CONF_
904   punt:
905 # endif
906         for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
907                 mapreturn[svcno] = 0;
908         svcno = 0;
909         if (strcmp(service, "aliases") == 0)
910         {
911                 maptype[svcno++] = "files";
912 # ifdef AUTO_NIS_ALIASES
913 #  ifdef NISPLUS
914                 maptype[svcno++] = "nisplus";
915 #  endif
916 #  ifdef NIS
917                 maptype[svcno++] = "nis";
918 #  endif
919 # endif
920                 return svcno;
921         }
922         if (strcmp(service, "hosts") == 0)
923         {
924 #  if NAMED_BIND
925                 maptype[svcno++] = "dns";
926 #  else
927 #   if defined(sun) && !defined(BSD)
928                 /* SunOS */
929                 maptype[svcno++] = "nis";
930 #   endif
931 #  endif
932                 maptype[svcno++] = "files";
933                 return svcno;
934         }
935         return -1;
936 #endif
937 }
938 \f/*
939 **  USERNAME -- return the user id of the logged in user.
940 **
941 **      Parameters:
942 **              none.
943 **
944 **      Returns:
945 **              The login name of the logged in user.
946 **
947 **      Side Effects:
948 **              none.
949 **
950 **      Notes:
951 **              The return value is statically allocated.
952 */
953
954 char *
955 username()
956 {
957         static char *myname = NULL;
958         extern char *getlogin();
959         register struct passwd *pw;
960
961         /* cache the result */
962         if (myname == NULL)
963         {
964                 myname = getlogin();
965                 if (myname == NULL || myname[0] == '\0')
966                 {
967                         pw = sm_getpwuid(RealUid);
968                         if (pw != NULL)
969                                 myname = newstr(pw->pw_name);
970                 }
971                 else
972                 {
973                         uid_t uid = RealUid;
974
975                         myname = newstr(myname);
976                         if ((pw = sm_getpwnam(myname)) == NULL ||
977                               (uid != 0 && uid != pw->pw_uid))
978                         {
979                                 pw = sm_getpwuid(uid);
980                                 if (pw != NULL)
981                                         myname = newstr(pw->pw_name);
982                         }
983                 }
984                 if (myname == NULL || myname[0] == '\0')
985                 {
986                         syserr("554 Who are you?");
987                         myname = "postmaster";
988                 }
989         }
990
991         return (myname);
992 }
993 \f/*
994 **  TTYPATH -- Get the path of the user's tty
995 **
996 **      Returns the pathname of the user's tty.  Returns NULL if
997 **      the user is not logged in or if s/he has write permission
998 **      denied.
999 **
1000 **      Parameters:
1001 **              none
1002 **
1003 **      Returns:
1004 **              pathname of the user's tty.
1005 **              NULL if not logged in or write permission denied.
1006 **
1007 **      Side Effects:
1008 **              none.
1009 **
1010 **      WARNING:
1011 **              Return value is in a local buffer.
1012 **
1013 **      Called By:
1014 **              savemail
1015 */
1016
1017 char *
1018 ttypath()
1019 {
1020         struct stat stbuf;
1021         register char *pathn;
1022         extern char *ttyname();
1023         extern char *getlogin();
1024
1025         /* compute the pathname of the controlling tty */
1026         if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
1027             (pathn = ttyname(0)) == NULL)
1028         {
1029                 errno = 0;
1030                 return (NULL);
1031         }
1032
1033         /* see if we have write permission */
1034         if (stat(pathn, &stbuf) < 0 || !bitset(S_IWOTH, stbuf.st_mode))
1035         {
1036                 errno = 0;
1037                 return (NULL);
1038         }
1039
1040         /* see if the user is logged in */
1041         if (getlogin() == NULL)
1042                 return (NULL);
1043
1044         /* looks good */
1045         return (pathn);
1046 }
1047 \f/*
1048 **  CHECKCOMPAT -- check for From and To person compatible.
1049 **
1050 **      This routine can be supplied on a per-installation basis
1051 **      to determine whether a person is allowed to send a message.
1052 **      This allows restriction of certain types of internet
1053 **      forwarding or registration of users.
1054 **
1055 **      If the hosts are found to be incompatible, an error
1056 **      message should be given using "usrerr" and an EX_ code
1057 **      should be returned.  You can also set to->q_status to
1058 **      a DSN-style status code.
1059 **
1060 **      EF_NO_BODY_RETN can be set in e->e_flags to suppress the
1061 **      body during the return-to-sender function; this should be done
1062 **      on huge messages.  This bit may already be set by the ESMTP
1063 **      protocol.
1064 **
1065 **      Parameters:
1066 **              to -- the person being sent to.
1067 **
1068 **      Returns:
1069 **              an exit status
1070 **
1071 **      Side Effects:
1072 **              none (unless you include the usrerr stuff)
1073 */
1074
1075 int
1076 checkcompat(to, e)
1077         register ADDRESS *to;
1078         register ENVELOPE *e;
1079 {
1080 # ifdef lint
1081         if (to == NULL)
1082                 to++;
1083 # endif /* lint */
1084
1085         if (tTd(49, 1))
1086                 printf("checkcompat(to=%s, from=%s)\n",
1087                         to->q_paddr, e->e_from.q_paddr);
1088
1089 # ifdef EXAMPLE_CODE
1090         /* this code is intended as an example only */
1091         register STAB *s;
1092
1093         s = stab("arpa", ST_MAILER, ST_FIND);
1094         if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 &&
1095             to->q_mailer == s->s_mailer)
1096         {
1097                 usrerr("553 No ARPA mail through this machine: see your system administration");
1098                 /* e->e_flags |= EF_NO_BODY_RETN; to supress body on return */
1099                 to->q_status = "5.7.1";
1100                 return (EX_UNAVAILABLE);
1101         }
1102 # endif /* EXAMPLE_CODE */
1103         return (EX_OK);
1104 }
1105 \f/*
1106 **  SETSIGNAL -- set a signal handler
1107 **
1108 **      This is essentially old BSD "signal(3)".
1109 */
1110
1111 sigfunc_t
1112 setsignal(sig, handler)
1113         int sig;
1114         sigfunc_t handler;
1115 {
1116 #if defined(SYS5SIGNALS) || defined(BSD4_3)
1117 # ifdef BSD4_3
1118         return signal(sig, handler);
1119 # else
1120         return sigset(sig, handler);
1121 # endif
1122 #else
1123         struct sigaction n, o;
1124
1125         bzero(&n, sizeof n);
1126 # if USE_SA_SIGACTION
1127         n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler;
1128         n.sa_flags = SA_RESTART|SA_SIGINFO;
1129 # else
1130         n.sa_handler = handler;
1131 #  ifdef SA_RESTART
1132         n.sa_flags = SA_RESTART;
1133 #  endif
1134 # endif
1135         if (sigaction(sig, &n, &o) < 0)
1136                 return SIG_ERR;
1137         return o.sa_handler;
1138 #endif
1139 }
1140 \f/*
1141 **  BLOCKSIGNAL -- hold a signal to prevent delivery
1142 **
1143 **      Parameters:
1144 **              sig -- the signal to block.
1145 **
1146 **      Returns:
1147 **              1 signal was previously blocked
1148 **              0 signal was not previously blocked
1149 **              -1 on failure.
1150 */
1151
1152 int
1153 blocksignal(sig)
1154         int sig;
1155 {
1156 #ifdef BSD4_3
1157 # ifndef sigmask
1158 #  define sigmask(s)    (1 << ((s) - 1))
1159 # endif
1160         return (sigblock(sigmask(sig)) & sigmask(sig)) != 0;
1161 #else
1162 # ifdef ALTOS_SYSTEM_V
1163         sigfunc_t handler;
1164
1165         handler = sigset(sig, SIG_HOLD);
1166         if (handler == SIG_ERR)
1167                 return -1;
1168         else
1169                 return handler == SIG_HOLD;
1170 # else
1171         sigset_t sset, oset;
1172
1173         sigemptyset(&sset);
1174         sigaddset(&sset, sig);
1175         if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0)
1176                 return -1;
1177         else
1178                 return sigismember(&oset, sig);
1179 # endif
1180 #endif
1181 }
1182 \f/*
1183 **  RELEASESIGNAL -- release a held signal
1184 **
1185 **      Parameters:
1186 **              sig -- the signal to release.
1187 **
1188 **      Returns:
1189 **              1 signal was previously blocked
1190 **              0 signal was not previously blocked
1191 **              -1 on failure.
1192 */
1193
1194 int
1195 releasesignal(sig)
1196         int sig;
1197 {
1198 #ifdef BSD4_3
1199         return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0;
1200 #else
1201 # ifdef ALTOS_SYSTEM_V
1202         sigfunc_t handler;
1203
1204         handler = sigset(sig, SIG_HOLD);
1205         if (sigrelse(sig) < 0)
1206                 return -1;
1207         else
1208                 return handler == SIG_HOLD;
1209 # else
1210         sigset_t sset, oset;
1211
1212         sigemptyset(&sset);
1213         sigaddset(&sset, sig);
1214         if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0)
1215                 return -1;
1216         else
1217                 return sigismember(&oset, sig);
1218 # endif
1219 #endif
1220 }
1221 \f/*
1222 **  HOLDSIGS -- arrange to hold all signals
1223 **
1224 **      Parameters:
1225 **              none.
1226 **
1227 **      Returns:
1228 **              none.
1229 **
1230 **      Side Effects:
1231 **              Arranges that signals are held.
1232 */
1233
1234 void
1235 holdsigs()
1236 {
1237 }
1238 \f/*
1239 **  RLSESIGS -- arrange to release all signals
1240 **
1241 **      This undoes the effect of holdsigs.
1242 **
1243 **      Parameters:
1244 **              none.
1245 **
1246 **      Returns:
1247 **              none.
1248 **
1249 **      Side Effects:
1250 **              Arranges that signals are released.
1251 */
1252
1253 void
1254 rlsesigs()
1255 {
1256 }
1257 \f/*
1258 **  INIT_MD -- do machine dependent initializations
1259 **
1260 **      Systems that have global modes that should be set should do
1261 **      them here rather than in main.
1262 */
1263
1264 #ifdef _AUX_SOURCE
1265 # include <compat.h>
1266 #endif
1267
1268 #if SHARE_V1
1269 # include <shares.h>
1270 #endif
1271
1272 void
1273 init_md(argc, argv)
1274         int argc;
1275         char **argv;
1276 {
1277 #ifdef _AUX_SOURCE
1278         setcompat(getcompat() | COMPAT_BSDPROT);
1279 #endif
1280
1281 #ifdef SUN_EXTENSIONS
1282         init_md_sun();
1283 #endif
1284
1285 #if _CONVEX_SOURCE
1286         /* keep gethostby*() from stripping the local domain name */
1287         set_domain_trim_off();
1288 #endif
1289 #ifdef __QNX__
1290         /*
1291         **  Due to QNX's network distributed nature, you can target a tcpip
1292         **  stack on a different node in the qnx network; this patch lets
1293         **  this feature work.  The __sock_locate() must be done before the
1294         **  environment is clear.
1295         */
1296         __sock_locate();
1297 #endif
1298 #if SECUREWARE || defined(_SCO_unix_)
1299         set_auth_parameters(argc, argv);
1300
1301 # ifdef _SCO_unix_
1302         /*
1303         **  This is required for highest security levels (the kernel
1304         **  won't let it call set*uid() or run setuid binaries without
1305         **  it).  It may be necessary on other SECUREWARE systems.
1306         */
1307
1308         if (getluid() == -1)
1309                 setluid(0);
1310 # endif
1311 #endif
1312
1313 #ifdef VENDOR_DEFAULT
1314         VendorCode = VENDOR_DEFAULT;
1315 #else
1316         VendorCode = VENDOR_BERKELEY;
1317 #endif
1318 }
1319 \f/*
1320 **  INIT_VENDOR_MACROS -- vendor-dependent macro initializations
1321 **
1322 **      Called once, on startup.
1323 **
1324 **      Parameters:
1325 **              e -- the global envelope.
1326 **
1327 **      Returns:
1328 **              none.
1329 **
1330 **      Side Effects:
1331 **              vendor-dependent.
1332 */
1333
1334 void
1335 init_vendor_macros(e)
1336         register ENVELOPE *e;
1337 {
1338 }
1339 \f/*
1340 **  GETLA -- get the current load average
1341 **
1342 **      This code stolen from la.c.
1343 **
1344 **      Parameters:
1345 **              none.
1346 **
1347 **      Returns:
1348 **              The current load average as an integer.
1349 **
1350 **      Side Effects:
1351 **              none.
1352 */
1353
1354 /* try to guess what style of load average we have */
1355 #define LA_ZERO         1       /* always return load average as zero */
1356 #define LA_INT          2       /* read kmem for avenrun; interpret as long */
1357 #define LA_FLOAT        3       /* read kmem for avenrun; interpret as float */
1358 #define LA_SUBR         4       /* call getloadavg */
1359 #define LA_MACH         5       /* MACH load averages (as on NeXT boxes) */
1360 #define LA_SHORT        6       /* read kmem for avenrun; interpret as short */
1361 #define LA_PROCSTR      7       /* read string ("1.17") from /proc/loadavg */
1362 #define LA_READKSYM     8       /* SVR4: use MIOC_READKSYM ioctl call */
1363 #define LA_DGUX         9       /* special DGUX implementation */
1364 #define LA_HPUX         10      /* special HPUX implementation */
1365 #define LA_IRIX6        11      /* special IRIX 6.2 implementation */
1366 #define LA_KSTAT        12      /* special Solaris kstat(3k) implementation */
1367 #define LA_DEVSHORT     13      /* read short from a device */
1368 #define LA_ALPHAOSF     14      /* Digital UNIX (OSF/1 on Alpha) table() call */
1369
1370 /* do guesses based on general OS type */
1371 #ifndef LA_TYPE
1372 # define LA_TYPE        LA_ZERO
1373 #endif
1374
1375 #ifndef FSHIFT
1376 # if defined(unixpc)
1377 #  define FSHIFT        5
1378 # endif
1379
1380 # if defined(__alpha) || defined(IRIX)
1381 #  define FSHIFT        10
1382 # endif
1383
1384 #endif
1385
1386 #ifndef FSHIFT
1387 # define FSHIFT         8
1388 #endif
1389
1390 #ifndef FSCALE
1391 # define FSCALE         (1 << FSHIFT)
1392 #endif
1393
1394 #ifndef LA_AVENRUN
1395 # ifdef SYSTEM5
1396 #  define LA_AVENRUN    "avenrun"
1397 # else
1398 #  define LA_AVENRUN    "_avenrun"
1399 # endif
1400 #endif
1401
1402 /* _PATH_KMEM should be defined in <paths.h> */
1403 #ifndef _PATH_KMEM
1404 # define _PATH_KMEM     "/dev/kmem"
1405 #endif
1406
1407 #if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT)
1408
1409 #include <nlist.h>
1410
1411 /* _PATH_UNIX should be defined in <paths.h> */
1412 #ifndef _PATH_UNIX
1413 # if defined(SYSTEM5)
1414 #  define _PATH_UNIX    "/unix"
1415 # else
1416 #  define _PATH_UNIX    "/vmunix"
1417 # endif
1418 #endif
1419
1420 #ifdef _AUX_SOURCE
1421 struct nlist    Nl[2];
1422 #else
1423 struct nlist    Nl[] =
1424 {
1425         { LA_AVENRUN },
1426         { 0 },
1427 };
1428 #endif
1429 #define X_AVENRUN       0
1430
1431 int
1432 getla()
1433 {
1434         static int kmem = -1;
1435 #if LA_TYPE == LA_INT
1436         long avenrun[3];
1437 #else
1438 # if LA_TYPE == LA_SHORT
1439         short avenrun[3];
1440 # else
1441         double avenrun[3];
1442 # endif
1443 #endif
1444         extern int errno;
1445         extern off_t lseek();
1446
1447         if (kmem < 0)
1448         {
1449 #ifdef _AUX_SOURCE
1450                 strcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN);
1451                 Nl[1].n_name[0] = '\0';
1452 #endif
1453
1454 #if defined(_AIX3) || defined(_AIX4)
1455                 if (knlist(Nl, 1, sizeof Nl[0]) < 0)
1456 #else
1457                 if (nlist(_PATH_UNIX, Nl) < 0)
1458 #endif
1459                 {
1460                         if (tTd(3, 1))
1461                                 printf("getla: nlist(%s): %s\n", _PATH_UNIX,
1462                                         errstring(errno));
1463                         return (-1);
1464                 }
1465                 if (Nl[X_AVENRUN].n_value == 0)
1466                 {
1467                         if (tTd(3, 1))
1468                                 printf("getla: nlist(%s, %s) ==> 0\n",
1469                                         _PATH_UNIX, LA_AVENRUN);
1470                         return (-1);
1471                 }
1472 #ifdef NAMELISTMASK
1473                 Nl[X_AVENRUN].n_value &= NAMELISTMASK;
1474 #endif
1475
1476                 kmem = open(_PATH_KMEM, 0, 0);
1477                 if (kmem < 0)
1478                 {
1479                         if (tTd(3, 1))
1480                                 printf("getla: open(/dev/kmem): %s\n",
1481                                         errstring(errno));
1482                         return (-1);
1483                 }
1484                 (void) fcntl(kmem, F_SETFD, 1);
1485         }
1486         if (tTd(3, 20))
1487                 printf("getla: symbol address = %#lx\n",
1488                         (u_long) Nl[X_AVENRUN].n_value);
1489         if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 ||
1490             read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
1491         {
1492                 /* thank you Ian */
1493                 if (tTd(3, 1))
1494                         printf("getla: lseek or read: %s\n", errstring(errno));
1495                 return (-1);
1496         }
1497 # if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)
1498         if (tTd(3, 5))
1499         {
1500 #  if LA_TYPE == LA_SHORT
1501                 printf("getla: avenrun = %d", avenrun[0]);
1502                 if (tTd(3, 15))
1503                         printf(", %d, %d", avenrun[1], avenrun[2]);
1504 #  else
1505                 printf("getla: avenrun = %ld", avenrun[0]);
1506                 if (tTd(3, 15))
1507                         printf(", %ld, %ld", avenrun[1], avenrun[2]);
1508 #  endif
1509                 printf("\n");
1510         }
1511         if (tTd(3, 1))
1512                 printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1513         return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1514 # else /* LA_TYPE == LA_FLOAT */
1515         if (tTd(3, 5))
1516         {
1517                 printf("getla: avenrun = %g", avenrun[0]);
1518                 if (tTd(3, 15))
1519                         printf(", %g, %g", avenrun[1], avenrun[2]);
1520                 printf("\n");
1521         }
1522         if (tTd(3, 1))
1523                 printf("getla: %d\n", (int) (avenrun[0] +0.5));
1524         return ((int) (avenrun[0] + 0.5));
1525 # endif
1526 }
1527
1528 #endif /* LA_TYPE == LA_INT or LA_SHORT or LA_FLOAT */
1529
1530 #if LA_TYPE == LA_READKSYM
1531
1532 # include <sys/ksym.h>
1533
1534 getla()
1535 {
1536         static int kmem = -1;
1537         long avenrun[3];
1538         extern int errno;
1539         struct mioc_rksym mirk;
1540
1541         if (kmem < 0)
1542         {
1543                 kmem = open("/dev/kmem", 0, 0);
1544                 if (kmem < 0)
1545                 {
1546                         if (tTd(3, 1))
1547                                 printf("getla: open(/dev/kmem): %s\n",
1548                                         errstring(errno));
1549                         return (-1);
1550                 }
1551                 (void) fcntl(kmem, F_SETFD, 1);
1552         }
1553         mirk.mirk_symname = LA_AVENRUN;
1554         mirk.mirk_buf = avenrun;
1555         mirk.mirk_buflen = sizeof(avenrun);
1556         if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0)
1557         {
1558                 if (tTd(3, 1))
1559                         printf("getla: ioctl(MIOC_READKSYM) failed: %s\n",
1560                                 errstring(errno));
1561                 return -1;
1562         }
1563         if (tTd(3, 5))
1564         {
1565                 printf("getla: avenrun = %d", avenrun[0]);
1566                 if (tTd(3, 15))
1567                         printf(", %d, %d", avenrun[1], avenrun[2]);
1568                 printf("\n");
1569         }
1570         if (tTd(3, 1))
1571                 printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1572         return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1573 }
1574
1575 #endif /* LA_TYPE == LA_READKSYM */
1576
1577 #if LA_TYPE == LA_DGUX
1578
1579 # include <sys/dg_sys_info.h>
1580
1581 int
1582 getla()
1583 {
1584         struct dg_sys_info_load_info load_info;
1585
1586         dg_sys_info((long *)&load_info,
1587                 DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0);
1588
1589         if (tTd(3, 1))
1590                 printf("getla: %d\n", (int) (load_info.one_minute + 0.5));
1591
1592         return((int) (load_info.one_minute + 0.5));
1593 }
1594
1595 #endif /* LA_TYPE == LA_DGUX */
1596
1597 #if LA_TYPE == LA_HPUX
1598
1599 /* forward declarations to keep gcc from complaining */
1600 struct pst_dynamic;
1601 struct pst_status;
1602 struct pst_static;
1603 struct pst_vminfo;
1604 struct pst_diskinfo;
1605 struct pst_processor;
1606 struct pst_lv;
1607 struct pst_swapinfo;
1608
1609 # include <sys/param.h>
1610 # include <sys/pstat.h>
1611
1612 int
1613 getla()
1614 {
1615         struct pst_dynamic pstd;
1616
1617         if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic),
1618                              (size_t) 1, 0) == -1)
1619                 return 0;
1620
1621         if (tTd(3, 1))
1622                 printf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5));
1623
1624         return (int) (pstd.psd_avg_1_min + 0.5);
1625 }
1626
1627 #endif /* LA_TYPE == LA_HPUX */
1628
1629 #if LA_TYPE == LA_SUBR
1630
1631 int
1632 getla()
1633 {
1634         double avenrun[3];
1635
1636         if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0)
1637         {
1638                 if (tTd(3, 1))
1639                         perror("getla: getloadavg failed:");
1640                 return (-1);
1641         }
1642         if (tTd(3, 1))
1643                 printf("getla: %d\n", (int) (avenrun[0] +0.5));
1644         return ((int) (avenrun[0] + 0.5));
1645 }
1646
1647 #endif /* LA_TYPE == LA_SUBR */
1648
1649 #if LA_TYPE == LA_MACH
1650
1651 /*
1652 **  This has been tested on NEXTSTEP release 2.1/3.X.
1653 */
1654
1655 #if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0
1656 # include <mach/mach.h>
1657 #else
1658 # include <mach.h>
1659 #endif
1660
1661 int
1662 getla()
1663 {
1664         processor_set_t default_set;
1665         kern_return_t error;
1666         unsigned int info_count;
1667         struct processor_set_basic_info info;
1668         host_t host;
1669
1670         error = processor_set_default(host_self(), &default_set);
1671         if (error != KERN_SUCCESS)
1672         {
1673                 if (tTd(3, 1))
1674                         perror("getla: processor_set_default failed:");
1675                 return -1;
1676         }
1677         info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
1678         if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
1679                                &host, (processor_set_info_t)&info,
1680                                &info_count) != KERN_SUCCESS)
1681         {
1682                 if (tTd(3, 1))
1683                         perror("getla: processor_set_info failed:");
1684                 return -1;
1685         }
1686         if (tTd(3, 1))
1687                 printf("getla: %d\n", (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE);
1688         return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE;
1689 }
1690
1691 #endif /* LA_TYPE == LA_MACH */
1692
1693 #if LA_TYPE == LA_PROCSTR
1694
1695 /*
1696 **  Read /proc/loadavg for the load average.  This is assumed to be
1697 **  in a format like "0.15 0.12 0.06".
1698 **
1699 **      Initially intended for Linux.  This has been in the kernel
1700 **      since at least 0.99.15.
1701 */
1702
1703 # ifndef _PATH_LOADAVG
1704 #  define _PATH_LOADAVG "/proc/loadavg"
1705 # endif
1706
1707 int
1708 getla()
1709 {
1710         double avenrun;
1711         register int result;
1712         FILE *fp;
1713
1714         fp = fopen(_PATH_LOADAVG, "r");
1715         if (fp == NULL)
1716         {
1717                 if (tTd(3, 1))
1718                         printf("getla: fopen(%s): %s\n",
1719                                 _PATH_LOADAVG, errstring(errno));
1720                 return -1;
1721         }
1722         result = fscanf(fp, "%lf", &avenrun);
1723         fclose(fp);
1724         if (result != 1)
1725         {
1726                 if (tTd(3, 1))
1727                         printf("getla: fscanf() = %d: %s\n",
1728                                 result, errstring(errno));
1729                 return -1;
1730         }
1731
1732         if (tTd(3, 1))
1733                 printf("getla(): %.2f\n", avenrun);
1734
1735         return ((int) (avenrun + 0.5));
1736 }
1737
1738 #endif /* LA_TYPE == LA_PROCSTR */
1739
1740 #if LA_TYPE == LA_IRIX6
1741 #include <sys/sysmp.h>
1742
1743 int getla(void)
1744 {
1745         static int kmem = -1;
1746         int avenrun[3];
1747
1748         if (kmem < 0)
1749         {
1750                 kmem = open(_PATH_KMEM, 0, 0);
1751                 if (kmem < 0)
1752                 {
1753                         if (tTd(3, 1))
1754                                 printf("getla: open(%s): %s\n", _PATH_KMEM,
1755                                         errstring(errno));
1756                         return -1;
1757                 }
1758                 (void) fcntl(kmem, F_SETFD, 1);
1759         }
1760
1761         if (lseek(kmem, (sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff), SEEK_SET) == -1 || 
1762             read(kmem, (char *)avenrun, sizeof(avenrun)) < sizeof(avenrun)) 
1763         {
1764                 if (tTd(3, 1))
1765                         printf("getla: lseek or read: %s\n",
1766                                errstring(errno));
1767                 return -1;
1768         }
1769         if (tTd(3, 5))
1770         {
1771                 printf("getla: avenrun = %ld", (long int) avenrun[0]);
1772                 if (tTd(3, 15))
1773                         printf(", %ld, %ld",
1774                                (long int) avenrun[1], (long int) avenrun[2]);
1775                 printf("\n");
1776         }
1777
1778         if (tTd(3, 1))
1779                 printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1780         return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
1781
1782 }
1783 #endif
1784
1785 #if LA_TYPE == LA_KSTAT
1786
1787 #include <kstat.h>
1788
1789 int
1790 getla()
1791 {
1792         static kstat_ctl_t *kc = NULL;
1793         static kstat_t *ksp = NULL;
1794         kstat_named_t *ksn;
1795         int la;
1796
1797         if (kc == NULL)         /* if not initialized before */
1798                 kc = kstat_open();
1799         if (kc == NULL)
1800         {
1801                 if (tTd(3, 1))
1802                         printf("getla: kstat_open(): %s\n",
1803                                 errstring(errno));
1804                 return -1;
1805         }
1806         if (ksp == NULL)
1807                 ksp = kstat_lookup(kc, "unix", 0, "system_misc");
1808         if (ksp == NULL)
1809         {
1810                 if (tTd(3, 1))
1811                         printf("getla: kstat_lookup(): %s\n",
1812                                 errstring(errno));
1813                 return -1;
1814         }
1815         if (kstat_read(kc, ksp, NULL) < 0)
1816         {
1817                 if (tTd(3, 1))
1818                         printf("getla: kstat_read(): %s\n",
1819                                 errstring(errno));
1820                 return -1;
1821         }
1822         ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min");
1823         la = ((double)ksn->value.ul + FSCALE/2) / FSCALE;
1824         /* kstat_close(kc); /o do not close for fast access */
1825         return la;
1826 }
1827
1828 #endif /* LA_TYPE == LA_KSTAT */
1829
1830 #if LA_TYPE == LA_DEVSHORT
1831
1832 /*
1833 **  Read /dev/table/avenrun for the load average.  This should contain
1834 **  three shorts for the 1, 5, and 15 minute loads.  We only read the
1835 **  first, since that's all we care about.
1836 **
1837 **      Intended for SCO OpenServer 5.
1838 */
1839
1840 # ifndef _PATH_AVENRUN
1841 #  define _PATH_AVENRUN "/dev/table/avenrun"
1842 # endif
1843
1844 int
1845 getla()
1846 {
1847         static int afd = -1;
1848         short avenrun;
1849         int loadav;
1850         int r;
1851
1852         errno = EBADF;
1853
1854         if (afd == -1 || lseek(afd, 0L, SEEK_SET) == -1)
1855         {
1856                 if (errno != EBADF)
1857                         return -1;
1858                 afd = open(_PATH_AVENRUN, O_RDONLY|O_SYNC);
1859                 if (afd < 0)
1860                 {
1861                         sm_syslog(LOG_ERR, NOQID,
1862                                 "can't open %s: %m",
1863                                 _PATH_AVENRUN);
1864                         return -1;
1865                 }
1866         }
1867
1868         r = read(afd, &avenrun, sizeof avenrun);
1869
1870         if (tTd(3, 5))
1871                 printf("getla: avenrun = %d\n", avenrun);
1872         loadav = (int) (avenrun + FSCALE/2) >> FSHIFT;
1873         if (tTd(3, 1))
1874                 printf("getla: %d\n", loadav);
1875         return loadav;
1876 }
1877
1878 #endif /* LA_TYPE == LA_DEVSHORT */
1879
1880 #if LA_TYPE == LA_ALPHAOSF
1881 struct rtentry;
1882 struct mbuf;
1883 # include <sys/table.h>
1884
1885 int getla()
1886 {
1887         int ave = 0;
1888         struct tbl_loadavg tab;
1889
1890         if (table(TBL_LOADAVG, 0, &tab, 1, sizeof(tab)) == -1)
1891         {
1892                 if (tTd(3, 1))
1893                         printf("getla: table %s\n", errstring(errno));
1894                 return (-1);
1895         }
1896
1897         if (tTd(3, 1))
1898                 printf("getla: scale = %d\n", tab.tl_lscale);
1899
1900         if (tab.tl_lscale)
1901                 ave = (tab.tl_avenrun.l[0] + (tab.tl_lscale/2)) / tab.tl_lscale;
1902         else
1903                 ave = (int) (tab.tl_avenrun.d[0] + 0.5);
1904
1905         if (tTd(3, 1))
1906                 printf("getla: %d\n", ave);
1907
1908         return ave;
1909 }
1910
1911 #endif
1912
1913 #if LA_TYPE == LA_ZERO
1914
1915 int
1916 getla()
1917 {
1918         if (tTd(3, 1))
1919                 printf("getla: ZERO\n");
1920         return (0);
1921 }
1922
1923 #endif /* LA_TYPE == LA_ZERO */
1924
1925 /*
1926  * Copyright 1989 Massachusetts Institute of Technology
1927  *
1928  * Permission to use, copy, modify, distribute, and sell this software and its
1929  * documentation for any purpose is hereby granted without fee, provided that
1930  * the above copyright notice appear in all copies and that both that
1931  * copyright notice and this permission notice appear in supporting
1932  * documentation, and that the name of M.I.T. not be used in advertising or
1933  * publicity pertaining to distribution of the software without specific,
1934  * written prior permission.  M.I.T. makes no representations about the
1935  * suitability of this software for any purpose.  It is provided "as is"
1936  * without express or implied warranty.
1937  *
1938  * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
1939  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
1940  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1941  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
1942  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
1943  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1944  *
1945  * Authors:  Many and varied...
1946  */
1947
1948 /* Non Apollo stuff removed by Don Lewis 11/15/93 */
1949 #ifndef lint
1950 static char  rcsid[] = "@(#)$Id: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $";
1951 #endif /* !lint */
1952
1953 #ifdef apollo
1954 # undef volatile
1955 #    include <apollo/base.h>
1956
1957 /* ARGSUSED */
1958 int getloadavg( call_data )
1959      caddr_t    call_data;      /* pointer to (double) return value */
1960 {
1961      double *avenrun = (double *) call_data;
1962      int i;
1963      status_$t      st;
1964      long loadav[3];
1965      proc1_$get_loadav(loadav, &st);
1966      *avenrun = loadav[0] / (double) (1 << 16);
1967      return(0);
1968 }
1969 #   endif /* apollo */
1970 \f/*
1971 **  SHOULDQUEUE -- should this message be queued or sent?
1972 **
1973 **      Compares the message cost to the load average to decide.
1974 **
1975 **      Parameters:
1976 **              pri -- the priority of the message in question.
1977 **              ctime -- the message creation time.
1978 **
1979 **      Returns:
1980 **              TRUE -- if this message should be queued up for the
1981 **                      time being.
1982 **              FALSE -- if the load is low enough to send this message.
1983 **
1984 **      Side Effects:
1985 **              none.
1986 */
1987
1988 extern int      get_num_procs_online __P((void));
1989
1990 bool
1991 shouldqueue(pri, ctime)
1992         long pri;
1993         time_t ctime;
1994 {
1995         bool rval;
1996         int queuela = QueueLA * get_num_procs_online();
1997
1998         if (tTd(3, 30))
1999                 printf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri);
2000         if (CurrentLA < queuela)
2001         {
2002                 if (tTd(3, 30))
2003                         printf("FALSE (CurrentLA < QueueLA)\n");
2004                 return (FALSE);
2005         }
2006 #if 0   /* this code is reported to cause oscillation around RefuseLA */
2007         if (CurrentLA >= RefuseLA && QueueLA < RefuseLA)
2008         {
2009                 if (tTd(3, 30))
2010                         printf("TRUE (CurrentLA >= RefuseLA)\n");
2011                 return (TRUE);
2012         }
2013 #endif
2014         rval = pri > (QueueFactor / (CurrentLA - queuela + 1));
2015         if (tTd(3, 30))
2016                 printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE");
2017         return rval;
2018 }
2019 \f/*
2020 **  REFUSECONNECTIONS -- decide if connections should be refused
2021 **
2022 **      Parameters:
2023 **              port -- port number (for error messages only)
2024 **
2025 **      Returns:
2026 **              TRUE if incoming SMTP connections should be refused
2027 **                      (for now).
2028 **              FALSE if we should accept new work.
2029 **
2030 **      Side Effects:
2031 **              Sets process title when it is rejecting connections.
2032 */
2033
2034 bool
2035 refuseconnections(port)
2036         int port;
2037 {
2038         int refusela = RefuseLA * get_num_procs_online();
2039         time_t now;
2040         static time_t lastconn = (time_t) 0;
2041         static int conncnt = 0;
2042         extern bool enoughdiskspace __P((long));
2043
2044 #ifdef XLA
2045         if (!xla_smtp_ok())
2046                 return TRUE;
2047 #endif
2048
2049         now = curtime();
2050         if (now != lastconn)
2051         {
2052                 lastconn = now;
2053                 conncnt = 0;
2054         }
2055         else if (conncnt++ > ConnRateThrottle && ConnRateThrottle > 0)
2056         {
2057                 /* sleep to flatten out connection load */
2058                 sm_setproctitle(TRUE, "deferring connections on port %d: %d per second",
2059                         port, ConnRateThrottle);
2060                 if (LogLevel >= 14)
2061                         sm_syslog(LOG_INFO, NOQID,
2062                                 "deferring connections on port %d: %d per second",
2063                                 port, ConnRateThrottle);
2064                 sleep(1);
2065         }
2066
2067         CurrentLA = getla();
2068         if (CurrentLA >= refusela)
2069         {
2070                 sm_setproctitle(TRUE, "rejecting connections on port %d: load average: %d",
2071                         port, CurrentLA);
2072                 if (LogLevel >= 14)
2073                         sm_syslog(LOG_INFO, NOQID,
2074                                 "rejecting connections on port %d: load average: %d",
2075                                 port, CurrentLA);
2076                 return TRUE;
2077         }
2078
2079         if (!enoughdiskspace(MinBlocksFree + 1))
2080         {
2081                 sm_setproctitle(TRUE, "rejecting connections on port %d: min free: %d",
2082                         port, MinBlocksFree);
2083                 if (LogLevel >= 14)
2084                         sm_syslog(LOG_INFO, NOQID,
2085                                 "rejecting connections on port %d: min free: %d",
2086                                 port, MinBlocksFree);
2087                 return TRUE;
2088         }
2089
2090         if (MaxChildren > 0 && CurChildren >= MaxChildren)
2091         {
2092                 proc_list_probe();
2093                 if (CurChildren >= MaxChildren)
2094                 {
2095                         sm_setproctitle(TRUE, "rejecting connections on port %d: %d children, max %d",
2096                                 port, CurChildren, MaxChildren);
2097                         if (LogLevel >= 14)
2098                                 sm_syslog(LOG_INFO, NOQID,
2099                                         "rejecting connections on port %d: %d children, max %d",
2100                                         port, CurChildren, MaxChildren);
2101                         return TRUE;
2102                 }
2103         }
2104
2105         return FALSE;
2106 }
2107 \f/*
2108 **  SETPROCTITLE -- set process title for ps
2109 **
2110 **      Parameters:
2111 **              fmt -- a printf style format string.
2112 **              a, b, c -- possible parameters to fmt.
2113 **
2114 **      Returns:
2115 **              none.
2116 **
2117 **      Side Effects:
2118 **              Clobbers argv of our main procedure so ps(1) will
2119 **              display the title.
2120 */
2121
2122 #define SPT_NONE        0       /* don't use it at all */
2123 #define SPT_REUSEARGV   1       /* cover argv with title information */
2124 #define SPT_BUILTIN     2       /* use libc builtin */
2125 #define SPT_PSTAT       3       /* use pstat(PSTAT_SETCMD, ...) */
2126 #define SPT_PSSTRINGS   4       /* use PS_STRINGS->... */
2127 #define SPT_SYSMIPS     5       /* use sysmips() supported by NEWS-OS 6 */
2128 #define SPT_SCO         6       /* write kernel u. area */
2129 #define SPT_CHANGEARGV  7       /* write our own strings into argv[] */
2130
2131 #ifndef SPT_TYPE
2132 # define SPT_TYPE       SPT_REUSEARGV
2133 #endif
2134
2135 #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
2136
2137 # if SPT_TYPE == SPT_PSTAT
2138 #  include <sys/pstat.h>
2139 # endif
2140 # if SPT_TYPE == SPT_PSSTRINGS
2141 #  include <machine/vmparam.h>
2142 #  include <sys/exec.h>
2143 #  ifndef PS_STRINGS    /* hmmmm....  apparently not available after all */
2144 #   undef SPT_TYPE
2145 #   define SPT_TYPE     SPT_REUSEARGV
2146 #  else
2147 #   ifndef NKPDE                        /* FreeBSD 2.0 */
2148 #    define NKPDE 63
2149 typedef unsigned int    *pt_entry_t;
2150 #   endif
2151 #  endif
2152 # endif
2153
2154 # if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
2155 #  define SETPROC_STATIC        static
2156 # else
2157 #  define SETPROC_STATIC
2158 # endif
2159
2160 # if SPT_TYPE == SPT_SYSMIPS
2161 #  include <sys/sysmips.h>
2162 #  include <sys/sysnews.h>
2163 # endif
2164
2165 # if SPT_TYPE == SPT_SCO
2166 #  include <sys/immu.h>
2167 #  include <sys/dir.h>
2168 #  include <sys/user.h>
2169 #  include <sys/fs/s5param.h>
2170 #  if PSARGSZ > MAXLINE
2171 #   define SPT_BUFSIZE  PSARGSZ
2172 #  endif
2173 # endif
2174
2175 # ifndef SPT_PADCHAR
2176 #  define SPT_PADCHAR   ' '
2177 # endif
2178
2179 #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
2180
2181 # ifndef SPT_BUFSIZE
2182 #  define SPT_BUFSIZE   MAXLINE
2183 # endif
2184
2185 /*
2186 **  Pointers for setproctitle.
2187 **      This allows "ps" listings to give more useful information.
2188 */
2189
2190 char            **Argv = NULL;          /* pointer to argument vector */
2191 char            *LastArgv = NULL;       /* end of argv */
2192
2193 void
2194 initsetproctitle(argc, argv, envp)
2195         int argc;
2196         char **argv;
2197         char **envp;
2198 {
2199         register int i, envpsize = 0;
2200         extern char **environ;
2201
2202         /*
2203         **  Move the environment so setproctitle can use the space at
2204         **  the top of memory.
2205         */
2206
2207         for (i = 0; envp[i] != NULL; i++)
2208                 envpsize += strlen(envp[i]) + 1;
2209         environ = (char **) xalloc(sizeof (char *) * (i + 1));
2210         for (i = 0; envp[i] != NULL; i++)
2211                 environ[i] = newstr(envp[i]);
2212         environ[i] = NULL;
2213
2214         /*
2215         **  Save start and extent of argv for setproctitle.
2216         */
2217
2218         Argv = argv;
2219
2220         /*
2221         **  Determine how much space we can use for setproctitle.  
2222         **  Use all contiguous argv and envp pointers starting at argv[0]
2223         */
2224         for (i = 0; i < argc; i++)
2225         {
2226                 if (i==0 || LastArgv + 1 == argv[i])
2227                         LastArgv = argv[i] + strlen(argv[i]);
2228                 else
2229                         continue;
2230         }
2231         for (i=0; envp[i] != NULL; i++)
2232         {
2233                 if (LastArgv + 1 == envp[i])
2234                         LastArgv = envp[i] + strlen(envp[i]);
2235                 else
2236                         continue;
2237         }
2238 }
2239
2240 #if SPT_TYPE != SPT_BUILTIN
2241
2242
2243 /*VARARGS1*/
2244 void
2245 # ifdef __STDC__
2246 setproctitle(const char *fmt, ...)
2247 # else
2248 setproctitle(fmt, va_alist)
2249         const char *fmt;
2250         va_dcl
2251 # endif
2252 {
2253 # if SPT_TYPE != SPT_NONE
2254         register char *p;
2255         register int i;
2256         SETPROC_STATIC char buf[SPT_BUFSIZE];
2257         VA_LOCAL_DECL
2258 #  if SPT_TYPE == SPT_PSTAT
2259         union pstun pst;
2260 #  endif
2261 #  if SPT_TYPE == SPT_SCO
2262         off_t seek_off;
2263         static int kmem = -1;
2264         static int kmempid = -1;
2265         struct user u;
2266 #  endif
2267
2268         p = buf;
2269
2270         /* print sendmail: heading for grep */
2271         (void) strcpy(p, "sendmail: ");
2272         p += strlen(p);
2273
2274         /* print the argument string */
2275         VA_START(fmt);
2276         (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
2277         VA_END;
2278
2279         i = strlen(buf);
2280
2281 #  if SPT_TYPE == SPT_PSTAT
2282         pst.pst_command = buf;
2283         pstat(PSTAT_SETCMD, pst, i, 0, 0);
2284 #  endif
2285 #  if SPT_TYPE == SPT_PSSTRINGS
2286         PS_STRINGS->ps_nargvstr = 1;
2287         PS_STRINGS->ps_argvstr = buf;
2288 #  endif
2289 #  if SPT_TYPE == SPT_SYSMIPS
2290         sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
2291 #  endif
2292 #  if SPT_TYPE == SPT_SCO
2293         if (kmem < 0 || kmempid != getpid())
2294         {
2295                 if (kmem >= 0)
2296                         close(kmem);
2297                 kmem = open(_PATH_KMEM, O_RDWR, 0);
2298                 if (kmem < 0)
2299                         return;
2300                 (void) fcntl(kmem, F_SETFD, 1);
2301                 kmempid = getpid();
2302         }
2303         buf[PSARGSZ - 1] = '\0';
2304         seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u;
2305         if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off)
2306                 (void) write(kmem, buf, PSARGSZ);
2307 #  endif
2308 #  if SPT_TYPE == SPT_REUSEARGV
2309         if (i > LastArgv - Argv[0] - 2)
2310         {
2311                 i = LastArgv - Argv[0] - 2;
2312                 buf[i] = '\0';
2313         }
2314         (void) strcpy(Argv[0], buf);
2315         p = &Argv[0][i];
2316         while (p < LastArgv)
2317                 *p++ = SPT_PADCHAR;
2318         Argv[1] = NULL;
2319 #  endif
2320 #  if SPT_TYPE == SPT_CHANGEARGV
2321         Argv[0] = buf;
2322         Argv[1] = 0;
2323 #  endif
2324 # endif /* SPT_TYPE != SPT_NONE */
2325 }
2326
2327 #endif /* SPT_TYPE != SPT_BUILTIN */
2328 \f/*
2329 **  SM_SETPROCTITLE -- set process task and set process title for ps
2330 **
2331 **      Possibly set process status and call setproctitle() to
2332 **      change the ps display.
2333 **
2334 **      Parameters:
2335 **              status -- whether or not to store as process status
2336 **              fmt -- a printf style format string.
2337 **              a, b, c -- possible parameters to fmt.
2338 **
2339 **      Returns:
2340 **              none.
2341 */
2342
2343 /*VARARGS2*/
2344 void
2345 # ifdef __STDC__
2346 sm_setproctitle(bool status, const char *fmt, ...)
2347 # else
2348 sm_setproctitle(status, fmt, va_alist)
2349         bool status;
2350         const char *fmt;
2351         va_dcl
2352 #endif
2353 {
2354         char buf[SPT_BUFSIZE];
2355
2356         VA_LOCAL_DECL
2357         /* print the argument string */
2358         VA_START(fmt);
2359         (void) vsnprintf(buf, SPT_BUFSIZE, fmt, ap);
2360         VA_END;
2361
2362         if (status)
2363                 proc_list_set(getpid(), buf);
2364         setproctitle("%s", buf);
2365 }
2366 \f/*
2367 **  WAITFOR -- wait for a particular process id.
2368 **
2369 **      Parameters:
2370 **              pid -- process id to wait for.
2371 **
2372 **      Returns:
2373 **              status of pid.
2374 **              -1 if pid never shows up.
2375 **
2376 **      Side Effects:
2377 **              none.
2378 */
2379
2380 int
2381 waitfor(pid)
2382         pid_t pid;
2383 {
2384 #ifdef WAITUNION
2385         union wait st;
2386 #else
2387         auto int st;
2388 #endif
2389         pid_t i;
2390 #if defined(ISC_UNIX) || defined(_SCO_unix_)
2391         int savesig;
2392 #endif
2393
2394         do
2395         {
2396                 errno = 0;
2397 #if defined(ISC_UNIX) || defined(_SCO_unix_)
2398                 savesig = releasesignal(SIGCHLD);
2399 #endif
2400                 i = wait(&st);
2401 #if defined(ISC_UNIX) || defined(_SCO_unix_)
2402                 if (savesig > 0)
2403                         blocksignal(SIGCHLD);
2404 #endif
2405                 if (i > 0)
2406                         proc_list_drop(i);
2407         } while ((i >= 0 || errno == EINTR) && i != pid);
2408         if (i < 0)
2409                 return -1;
2410 #ifdef WAITUNION
2411         return st.w_status;
2412 #else
2413         return st;
2414 #endif
2415 }
2416 \f/*
2417 **  REAPCHILD -- pick up the body of my child, lest it become a zombie
2418 **
2419 **      Parameters:
2420 **              sig -- the signal that got us here (unused).
2421 **
2422 **      Returns:
2423 **              none.
2424 **
2425 **      Side Effects:
2426 **              Picks up extant zombies.
2427 */
2428
2429 SIGFUNC_DECL
2430 reapchild(sig)
2431         int sig;
2432 {
2433         int olderrno = errno;
2434         pid_t pid;
2435 # ifdef HASWAITPID
2436         auto int status;
2437         int count;
2438
2439         count = 0;
2440         while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
2441         {
2442                 if (count++ > 1000)
2443                 {
2444                         if (LogLevel > 0)
2445                                 sm_syslog(LOG_ALERT, NOQID,
2446                                         "reapchild: waitpid loop: pid=%d, status=%x",
2447                                         pid, status);
2448                         break;
2449                 }
2450                 proc_list_drop(pid);
2451         }
2452 # else
2453 # ifdef WNOHANG
2454         union wait status;
2455
2456         while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0)
2457                 proc_list_drop(pid);
2458 # else /* WNOHANG */
2459         auto int status;
2460
2461         /*
2462         **  Catch one zombie -- we will be re-invoked (we hope) if there
2463         **  are more.  Unreliable signals probably break this, but this
2464         **  is the "old system" situation -- waitpid or wait3 are to be
2465         **  strongly preferred.
2466         */
2467
2468         if ((pid = wait(&status)) > 0)
2469                 proc_list_drop(pid);
2470 # endif /* WNOHANG */
2471 # endif
2472 # ifdef SYS5SIGNALS
2473         (void) setsignal(SIGCHLD, reapchild);
2474 # endif
2475         errno = olderrno;
2476         return SIGFUNC_RETURN;
2477 }
2478 \f/*
2479 **  PUTENV -- emulation of putenv() in terms of setenv()
2480 **
2481 **      Not needed on Posix-compliant systems.
2482 **      This doesn't have full Posix semantics, but it's good enough
2483 **              for sendmail.
2484 **
2485 **      Parameter:
2486 **              env -- the environment to put.
2487 **
2488 **      Returns:
2489 **              none.
2490 */
2491
2492 #ifdef NEEDPUTENV
2493
2494 # if NEEDPUTENV == 2            /* no setenv(3) call available */
2495
2496 int
2497 putenv(str)
2498         char *str;
2499 {
2500         char **current;
2501         int matchlen, envlen=0;
2502         char *tmp;
2503         char **newenv;
2504         static int first=1;
2505         extern char **environ;
2506
2507         /*
2508          * find out how much of str to match when searching
2509          * for a string to replace.
2510          */
2511         if ((tmp = strchr(str, '=')) == NULL || tmp == str)
2512                 matchlen = strlen(str);
2513         else
2514                 matchlen = (int) (tmp - str);
2515         ++matchlen;
2516
2517         /*
2518          * Search for an existing string in the environment and find the
2519          * length of environ.  If found, replace and exit.
2520          */
2521         for (current=environ; *current; current++) {
2522                 ++envlen;
2523
2524                 if (strncmp(str, *current, matchlen) == 0) {
2525                         /* found it, now insert the new version */
2526                         *current = (char *)str;
2527                         return(0);
2528                 }
2529         }
2530
2531         /*
2532          * There wasn't already a slot so add space for a new slot.
2533          * If this is our first time through, use malloc(), else realloc().
2534          */
2535         if (first) {
2536                 newenv = (char **) malloc(sizeof(char *) * (envlen + 2));
2537                 if (newenv == NULL)
2538                         return(-1);
2539
2540                 first=0;
2541                 (void) memcpy(newenv, environ, sizeof(char *) * envlen);
2542         } else {
2543                 newenv = (char **) realloc((char *)environ, sizeof(char *) * (envlen + 2));
2544                 if (newenv == NULL)
2545                         return(-1);
2546         }
2547
2548         /* actually add in the new entry */
2549         environ = newenv;
2550         environ[envlen] = (char *)str;
2551         environ[envlen+1] = NULL;
2552
2553         return(0);
2554 }
2555
2556 #else                   /* implement putenv() in terms of setenv() */
2557
2558 int
2559 putenv(env)
2560         char *env;
2561 {
2562         char *p;
2563         int l;
2564         char nbuf[100];
2565
2566         p = strchr(env, '=');
2567         if (p == NULL)
2568                 return 0;
2569         l = p - env;
2570         if (l > sizeof nbuf - 1)
2571                 l = sizeof nbuf - 1;
2572         bcopy(env, nbuf, l);
2573         nbuf[l] = '\0';
2574         return setenv(nbuf, ++p, 1);
2575 }
2576
2577 # endif
2578 #endif
2579 \f/*
2580 **  UNSETENV -- remove a variable from the environment
2581 **
2582 **      Not needed on newer systems.
2583 **
2584 **      Parameters:
2585 **              name -- the string name of the environment variable to be
2586 **                      deleted from the current environment.
2587 **
2588 **      Returns:
2589 **              none.
2590 **
2591 **      Globals:
2592 **              environ -- a pointer to the current environment.
2593 **
2594 **      Side Effects:
2595 **              Modifies environ.
2596 */
2597
2598 #ifndef HASUNSETENV
2599
2600 void
2601 unsetenv(name)
2602         char *name;
2603 {
2604         extern char **environ;
2605         register char **pp;
2606         int len = strlen(name);
2607
2608         for (pp = environ; *pp != NULL; pp++)
2609         {
2610                 if (strncmp(name, *pp, len) == 0 &&
2611                     ((*pp)[len] == '=' || (*pp)[len] == '\0'))
2612                         break;
2613         }
2614
2615         for (; *pp != NULL; pp++)
2616                 *pp = pp[1];
2617 }
2618
2619 #endif
2620 \f/*
2621 **  GETDTABLESIZE -- return number of file descriptors
2622 **
2623 **      Only on non-BSD systems
2624 **
2625 **      Parameters:
2626 **              none
2627 **
2628 **      Returns:
2629 **              size of file descriptor table
2630 **
2631 **      Side Effects:
2632 **              none
2633 */
2634
2635 #ifdef SOLARIS
2636 # include <sys/resource.h>
2637 #endif
2638
2639 int
2640 getdtsize()
2641 {
2642 #ifdef RLIMIT_NOFILE
2643         struct rlimit rl;
2644
2645         if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
2646                 return rl.rlim_cur;
2647 #endif
2648
2649 # ifdef HASGETDTABLESIZE
2650         return getdtablesize();
2651 # else
2652 #  ifdef _SC_OPEN_MAX
2653         return sysconf(_SC_OPEN_MAX);
2654 #  else
2655         return NOFILE;
2656 #  endif
2657 # endif
2658 }
2659 \f/*
2660 **  UNAME -- get the UUCP name of this system.
2661 */
2662
2663 #ifndef HASUNAME
2664
2665 int
2666 uname(name)
2667         struct utsname *name;
2668 {
2669         FILE *file;
2670         char *n;
2671
2672         name->nodename[0] = '\0';
2673
2674         /* try /etc/whoami -- one line with the node name */
2675         if ((file = fopen("/etc/whoami", "r")) != NULL)
2676         {
2677                 (void) fgets(name->nodename, NODE_LENGTH + 1, file);
2678                 (void) fclose(file);
2679                 n = strchr(name->nodename, '\n');
2680                 if (n != NULL)
2681                         *n = '\0';
2682                 if (name->nodename[0] != '\0')
2683                         return (0);
2684         }
2685
2686         /* try /usr/include/whoami.h -- has a #define somewhere */
2687         if ((file = fopen("/usr/include/whoami.h", "r")) != NULL)
2688         {
2689                 char buf[MAXLINE];
2690
2691                 while (fgets(buf, MAXLINE, file) != NULL)
2692                         if (sscanf(buf, "#define sysname \"%*[^\"]\"",
2693                                         NODE_LENGTH, name->nodename) > 0)
2694                                 break;
2695                 (void) fclose(file);
2696                 if (name->nodename[0] != '\0')
2697                         return (0);
2698         }
2699
2700 #ifdef TRUST_POPEN
2701         /*
2702         **  Popen is known to have security holes.
2703         */
2704
2705         /* try uuname -l to return local name */
2706         if ((file = popen("uuname -l", "r")) != NULL)
2707         {
2708                 (void) fgets(name, NODE_LENGTH + 1, file);
2709                 (void) pclose(file);
2710                 n = strchr(name, '\n');
2711                 if (n != NULL)
2712                         *n = '\0';
2713                 if (name->nodename[0] != '\0')
2714                         return (0);
2715         }
2716 #endif
2717
2718         return (-1);
2719 }
2720 #endif /* HASUNAME */
2721 \f/*
2722 **  INITGROUPS -- initialize groups
2723 **
2724 **      Stub implementation for System V style systems
2725 */
2726
2727 #ifndef HASINITGROUPS
2728
2729 initgroups(name, basegid)
2730         char *name;
2731         int basegid;
2732 {
2733         return 0;
2734 }
2735
2736 #endif
2737 \f/*
2738 **  SETGROUPS -- set group list
2739 **
2740 **      Stub implementation for systems that don't have group lists
2741 */
2742
2743 #ifndef NGROUPS_MAX
2744
2745 int
2746 setgroups(ngroups, grouplist)
2747         int ngroups;
2748         GIDSET_T grouplist[];
2749 {
2750         return 0;
2751 }
2752
2753 #endif
2754 \f/*
2755 **  SETSID -- set session id (for non-POSIX systems)
2756 */
2757
2758 #ifndef HASSETSID
2759
2760 pid_t
2761 setsid __P ((void))
2762 {
2763 #ifdef TIOCNOTTY
2764         int fd;
2765
2766         fd = open("/dev/tty", O_RDWR, 0);
2767         if (fd >= 0)
2768         {
2769                 (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
2770                 (void) close(fd);
2771         }
2772 #endif /* TIOCNOTTY */
2773 # ifdef SYS5SETPGRP
2774         return setpgrp();
2775 # else
2776         return setpgid(0, getpid());
2777 # endif
2778 }
2779
2780 #endif
2781 \f/*
2782 **  FSYNC -- dummy fsync
2783 */
2784
2785 #ifdef NEEDFSYNC
2786
2787 fsync(fd)
2788         int fd;
2789 {
2790 # ifdef O_SYNC
2791         return fcntl(fd, F_SETFL, O_SYNC);
2792 # else
2793         /* nothing we can do */
2794         return 0;
2795 # endif
2796 }
2797
2798 #endif
2799 \f/*
2800 **  DGUX_INET_ADDR -- inet_addr for DG/UX
2801 **
2802 **      Data General DG/UX version of inet_addr returns a struct in_addr
2803 **      instead of a long.  This patches things.  Only needed on versions
2804 **      prior to 5.4.3.
2805 */
2806
2807 #ifdef DGUX_5_4_2
2808
2809 #undef inet_addr
2810
2811 long
2812 dgux_inet_addr(host)
2813         char *host;
2814 {
2815         struct in_addr haddr;
2816
2817         haddr = inet_addr(host);
2818         return haddr.s_addr;
2819 }
2820
2821 #endif
2822 \f/*
2823 **  GETOPT -- for old systems or systems with bogus implementations
2824 */
2825
2826 #ifdef NEEDGETOPT
2827
2828 /*
2829  * Copyright (c) 1985 Regents of the University of California.
2830  * All rights reserved.  The Berkeley software License Agreement
2831  * specifies the terms and conditions for redistribution.
2832  */
2833
2834
2835 /*
2836 **  this version hacked to add `atend' flag to allow state machine
2837 **  to reset if invoked by the program to scan args for a 2nd time
2838 */
2839
2840 #if defined(LIBC_SCCS) && !defined(lint)
2841 static char sccsid[] = "@(#)getopt.c    4.3 (Berkeley) 3/9/86";
2842 #endif /* LIBC_SCCS and not lint */
2843
2844 #include <stdio.h>
2845
2846 /*
2847  * get option letter from argument vector
2848  */
2849 #ifdef _CONVEX_SOURCE
2850 extern int      optind, opterr, optopt;
2851 extern char     *optarg;
2852 #else
2853 int     opterr = 1;             /* if error message should be printed */
2854 int     optind = 1;             /* index into parent argv vector */
2855 int     optopt = 0;             /* character checked for validity */
2856 char    *optarg = NULL;         /* argument associated with option */
2857 #endif
2858
2859 #define BADCH   (int)'?'
2860 #define EMSG    ""
2861 #define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \
2862                 fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);}
2863
2864 int
2865 getopt(nargc,nargv,ostr)
2866         int             nargc;
2867         char *const     *nargv;
2868         const char      *ostr;
2869 {
2870         static char     *place = EMSG;  /* option letter processing */
2871         static char     atend = 0;
2872         register char   *oli = NULL;    /* option letter list index */
2873
2874         if (atend) {
2875                 atend = 0;
2876                 place = EMSG;
2877         }
2878         if(!*place) {                   /* update scanning pointer */
2879                 if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) {
2880                         atend++;
2881                         return -1;
2882                 }
2883                 if (*place == '-') {    /* found "--" */
2884                         ++optind;
2885                         atend++;
2886                         return -1;
2887                 }
2888         }                               /* option letter okay? */
2889         if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) {
2890                 if (!*place) ++optind;
2891                 tell(": illegal option -- ");
2892         }
2893         if (oli && *++oli != ':') {             /* don't need argument */
2894                 optarg = NULL;
2895                 if (!*place) ++optind;
2896         }
2897         else {                          /* need an argument */
2898                 if (*place) optarg = place;     /* no white space */
2899                 else if (nargc <= ++optind) {   /* no arg */
2900                         place = EMSG;
2901                         tell(": option requires an argument -- ");
2902                 }
2903                 else optarg = nargv[optind];    /* white space */
2904                 place = EMSG;
2905                 ++optind;
2906         }
2907         return(optopt);                 /* dump back option letter */
2908 }
2909
2910 #endif
2911 \f/*
2912 **  VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version
2913 */
2914
2915 #ifdef NEEDVPRINTF
2916
2917 #define MAXARG  16
2918
2919 vfprintf(fp, fmt, ap)
2920         FILE *fp;
2921         char *fmt;
2922         char **ap;
2923 {
2924         char *bp[MAXARG];
2925         int i = 0;
2926
2927         while (*ap && i < MAXARG)
2928                 bp[i++] = *ap++;
2929         fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3],
2930                          bp[4], bp[5], bp[6], bp[7],
2931                          bp[8], bp[9], bp[10], bp[11],
2932                          bp[12], bp[13], bp[14], bp[15]);
2933 }
2934
2935 vsprintf(s, fmt, ap)
2936         char *s;
2937         char *fmt;
2938         char **ap;
2939 {
2940         char *bp[MAXARG];
2941         int i = 0;
2942
2943         while (*ap && i < MAXARG)
2944                 bp[i++] = *ap++;
2945         sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3],
2946                         bp[4], bp[5], bp[6], bp[7],
2947                         bp[8], bp[9], bp[10], bp[11],
2948                         bp[12], bp[13], bp[14], bp[15]);
2949 }
2950
2951 #endif
2952 \f/*
2953 **  USERSHELLOK -- tell if a user's shell is ok for unrestricted use
2954 **
2955 **      Parameters:
2956 **              user -- the name of the user we are checking.
2957 **              shell -- the user's shell from /etc/passwd
2958 **
2959 **      Returns:
2960 **              TRUE -- if it is ok to use this for unrestricted access.
2961 **              FALSE -- if the shell is restricted.
2962 */
2963
2964 #if !HASGETUSERSHELL
2965
2966 # ifndef _PATH_SHELLS
2967 #  define _PATH_SHELLS  "/etc/shells"
2968 # endif
2969
2970 # if defined(_AIX3) || defined(_AIX4)
2971 #  include <userconf.h>
2972 #  if _AIX4 >= 40200
2973 #   include <userpw.h>
2974 #  endif
2975 #  include <usersec.h>
2976 # endif
2977
2978 char    *DefaultUserShells[] =
2979 {
2980         "/bin/sh",              /* standard shell */
2981         "/usr/bin/sh",
2982         "/bin/csh",             /* C shell */
2983         "/usr/bin/csh",
2984 #ifdef __hpux
2985 # ifdef V4FS
2986         "/usr/bin/rsh",         /* restricted Bourne shell */
2987         "/usr/bin/ksh",         /* Korn shell */
2988         "/usr/bin/rksh",        /* restricted Korn shell */
2989         "/usr/bin/pam",
2990         "/usr/bin/keysh",       /* key shell (extended Korn shell) */
2991         "/usr/bin/posix/sh",
2992 # else
2993         "/bin/rsh",             /* restricted Bourne shell */
2994         "/bin/ksh",             /* Korn shell */
2995         "/bin/rksh",            /* restricted Korn shell */
2996         "/bin/pam",
2997         "/usr/bin/keysh",       /* key shell (extended Korn shell) */
2998         "/bin/posix/sh",
2999 # endif
3000 #endif
3001 #if defined(_AIX3) || defined(_AIX4)
3002         "/bin/ksh",             /* Korn shell */
3003         "/usr/bin/ksh",
3004         "/bin/tsh",             /* trusted shell */
3005         "/usr/bin/tsh",
3006         "/bin/bsh",             /* Bourne shell */
3007         "/usr/bin/bsh",
3008 #endif
3009 #if defined(__svr4__) || defined(__svr5__)
3010         "/bin/ksh",             /* Korn shell */
3011         "/usr/bin/ksh",
3012 #endif
3013 #ifdef sgi
3014         "/sbin/sh",             /* SGI's shells really live in /sbin */
3015         "/sbin/csh",
3016         "/bin/ksh",             /* Korn shell */
3017         "/sbin/ksh",
3018         "/usr/bin/ksh",
3019         "/bin/tcsh",            /* Extended csh */
3020         "/usr/bin/tcsh",
3021 #endif
3022         NULL
3023 };
3024
3025 #endif
3026
3027 #define WILDCARD_SHELL  "/SENDMAIL/ANY/SHELL/"
3028
3029 bool
3030 usershellok(user, shell)
3031         char *user;
3032         char *shell;
3033 {
3034 #if HASGETUSERSHELL
3035         register char *p;
3036         extern char *getusershell();
3037
3038         if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
3039             ConfigLevel <= 1)
3040                 return TRUE;
3041
3042         setusershell();
3043         while ((p = getusershell()) != NULL)
3044                 if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0)
3045                         break;
3046         endusershell();
3047         return p != NULL;
3048 #else
3049 # if USEGETCONFATTR
3050         auto char *v;
3051 # endif
3052         register FILE *shellf;
3053         char buf[MAXLINE];
3054
3055         if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
3056             ConfigLevel <= 1)
3057                 return TRUE;
3058
3059 # if USEGETCONFATTR
3060         /*
3061         **  Naturally IBM has a "better" idea.....
3062         **
3063         **      What a crock.  This interface isn't documented, it is
3064         **      considered part of the security library (-ls), and it
3065         **      only works if you are running as root (since the list
3066         **      of valid shells is obviously a source of great concern).
3067         **      I recommend that you do NOT define USEGETCONFATTR,
3068         **      especially since you are going to have to set up an
3069         **      /etc/shells anyhow to handle the cases where getconfattr
3070         **      fails.
3071         */
3072
3073         if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL)
3074         {
3075                 while (*v != '\0')
3076                 {
3077                         if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0)
3078                                 return TRUE;
3079                         v += strlen(v) + 1;
3080                 }
3081                 return FALSE;
3082         }
3083 # endif
3084
3085         shellf = fopen(_PATH_SHELLS, "r");
3086         if (shellf == NULL)
3087         {
3088                 /* no /etc/shells; see if it is one of the std shells */
3089                 char **d;
3090                 
3091                 if (errno != ENOENT && LogLevel > 3)
3092                         sm_syslog(LOG_ERR, NOQID,
3093                                   "usershellok: cannot open %s: %s",
3094                                   _PATH_SHELLS, errstring(errno));
3095
3096                 for (d = DefaultUserShells; *d != NULL; d++)
3097                 {
3098                         if (strcmp(shell, *d) == 0)
3099                                 return TRUE;
3100                 }
3101                 return FALSE;
3102         }
3103
3104         while (fgets(buf, sizeof buf, shellf) != NULL)
3105         {
3106                 register char *p, *q;
3107
3108                 p = buf;
3109                 while (*p != '\0' && *p != '#' && *p != '/')
3110                         p++;
3111                 if (*p == '#' || *p == '\0')
3112                         continue;
3113                 q = p;
3114                 while (*p != '\0' && *p != '#' && !(isascii(*p) && isspace(*p)))
3115                         p++;
3116                 *p = '\0';
3117                 if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0)
3118                 {
3119                         fclose(shellf);
3120                         return TRUE;
3121                 }
3122         }
3123         fclose(shellf);
3124         return FALSE;
3125 #endif
3126 }
3127 \f/*
3128 **  FREEDISKSPACE -- see how much free space is on the queue filesystem
3129 **
3130 **      Only implemented if you have statfs.
3131 **
3132 **      Parameters:
3133 **              dir -- the directory in question.
3134 **              bsize -- a variable into which the filesystem
3135 **                      block size is stored.
3136 **
3137 **      Returns:
3138 **              The number of bytes free on the queue filesystem.
3139 **              -1 if the statfs call fails.
3140 **
3141 **      Side effects:
3142 **              Puts the filesystem block size into bsize.
3143 */
3144
3145 /* statfs types */
3146 #define SFS_NONE        0       /* no statfs implementation */
3147 #define SFS_USTAT       1       /* use ustat */
3148 #define SFS_4ARGS       2       /* use four-argument statfs call */
3149 #define SFS_VFS         3       /* use <sys/vfs.h> implementation */
3150 #define SFS_MOUNT       4       /* use <sys/mount.h> implementation */
3151 #define SFS_STATFS      5       /* use <sys/statfs.h> implementation */
3152 #define SFS_STATVFS     6       /* use <sys/statvfs.h> implementation */
3153
3154 #ifndef SFS_TYPE
3155 # define SFS_TYPE       SFS_NONE
3156 #endif
3157
3158 #if SFS_TYPE == SFS_USTAT
3159 # include <ustat.h>
3160 #endif
3161 #if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
3162 # include <sys/statfs.h>
3163 #endif
3164 #if SFS_TYPE == SFS_VFS
3165 # include <sys/vfs.h>
3166 #endif
3167 #if SFS_TYPE == SFS_MOUNT
3168 # include <sys/mount.h>
3169 #endif
3170 #if SFS_TYPE == SFS_STATVFS
3171 # include <sys/statvfs.h>
3172 #endif
3173
3174 long
3175 freediskspace(dir, bsize)
3176         char *dir;
3177         long *bsize;
3178 {
3179 #if SFS_TYPE != SFS_NONE
3180 # if SFS_TYPE == SFS_USTAT
3181         struct ustat fs;
3182         struct stat statbuf;
3183 #  define FSBLOCKSIZE   DEV_BSIZE
3184 #  define SFS_BAVAIL    f_tfree
3185 # else
3186 #  if defined(ultrix)
3187         struct fs_data fs;
3188 #   define SFS_BAVAIL   fd_bfreen
3189 #   define FSBLOCKSIZE  1024L
3190 #  else
3191 #   if SFS_TYPE == SFS_STATVFS
3192         struct statvfs fs;
3193 #    define FSBLOCKSIZE fs.f_frsize
3194 #   else
3195         struct statfs fs;
3196 #    define FSBLOCKSIZE fs.f_bsize
3197 #   endif
3198 #  endif
3199 # endif
3200 # ifndef SFS_BAVAIL
3201 #  define SFS_BAVAIL f_bavail
3202 # endif
3203
3204 # if SFS_TYPE == SFS_USTAT
3205         if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
3206 # else
3207 #  if SFS_TYPE == SFS_4ARGS
3208         if (statfs(dir, &fs, sizeof fs, 0) == 0)
3209 #  else
3210 #   if SFS_TYPE == SFS_STATVFS
3211         if (statvfs(dir, &fs) == 0)
3212 #   else
3213 #    if defined(ultrix)
3214         if (statfs(dir, &fs) > 0)
3215 #    else
3216         if (statfs(dir, &fs) == 0)
3217 #    endif
3218 #   endif
3219 #  endif
3220 # endif
3221         {
3222                 if (bsize != NULL)
3223                         *bsize = FSBLOCKSIZE;
3224                 if (fs.SFS_BAVAIL <= 0)
3225                         return 0;
3226                 else if (fs.SFS_BAVAIL > LONG_MAX)
3227                         return LONG_MAX;
3228                 else
3229                         return (long) fs.SFS_BAVAIL;
3230         }
3231 #endif
3232         return (-1);
3233 }
3234 \f/*
3235 **  ENOUGHDISKSPACE -- is there enough free space on the queue fs?
3236 **
3237 **      Only implemented if you have statfs.
3238 **
3239 **      Parameters:
3240 **              msize -- the size to check against.  If zero, we don't yet
3241 **              know how big the message will be, so just check for
3242 **              a "reasonable" amount.
3243 **
3244 **      Returns:
3245 **              TRUE if there is enough space.
3246 **              FALSE otherwise.
3247 */
3248
3249 bool
3250 enoughdiskspace(msize)
3251         long msize;
3252 {
3253         long bfree, bsize;
3254
3255         if (MinBlocksFree <= 0 && msize <= 0)
3256         {
3257                 if (tTd(4, 80))
3258                         printf("enoughdiskspace: no threshold\n");
3259                 return TRUE;
3260         }
3261
3262         if ((bfree = freediskspace(QueueDir, &bsize)) >= 0)
3263         {
3264                 if (tTd(4, 80))
3265                         printf("enoughdiskspace: bavail=%ld, need=%ld\n",
3266                                 bfree, msize);
3267
3268                 /* convert msize to block count */
3269                 msize = msize / bsize + 1;
3270                 if (MinBlocksFree >= 0)
3271                         msize += MinBlocksFree;
3272
3273                 if (bfree < msize)
3274                 {
3275                         if (LogLevel > 0)
3276                                 sm_syslog(LOG_ALERT, CurEnv->e_id,
3277                                         "low on space (have %ld, %s needs %ld in %s)",
3278                                         bfree,
3279                                         CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
3280                                         msize, QueueDir);
3281                         return FALSE;
3282                 }
3283         }
3284         else if (tTd(4, 80))
3285                 printf("enoughdiskspace failure: min=%ld, need=%ld: %s\n",
3286                         MinBlocksFree, msize, errstring(errno));
3287         return TRUE;
3288 }
3289 \f/*
3290 **  TRANSIENTERROR -- tell if an error code indicates a transient failure
3291 **
3292 **      This looks at an errno value and tells if this is likely to
3293 **      go away if retried later.
3294 **
3295 **      Parameters:
3296 **              err -- the errno code to classify.
3297 **
3298 **      Returns:
3299 **              TRUE if this is probably transient.
3300 **              FALSE otherwise.
3301 */
3302
3303 bool
3304 transienterror(err)
3305         int err;
3306 {
3307         switch (err)
3308         {
3309           case EIO:                     /* I/O error */
3310           case ENXIO:                   /* Device not configured */
3311           case EAGAIN:                  /* Resource temporarily unavailable */
3312           case ENOMEM:                  /* Cannot allocate memory */
3313           case ENODEV:                  /* Operation not supported by device */
3314           case ENFILE:                  /* Too many open files in system */
3315           case EMFILE:                  /* Too many open files */
3316           case ENOSPC:                  /* No space left on device */
3317 #ifdef ETIMEDOUT
3318           case ETIMEDOUT:               /* Connection timed out */
3319 #endif
3320 #ifdef ESTALE
3321           case ESTALE:                  /* Stale NFS file handle */
3322 #endif
3323 #ifdef ENETDOWN
3324           case ENETDOWN:                /* Network is down */
3325 #endif
3326 #ifdef ENETUNREACH
3327           case ENETUNREACH:             /* Network is unreachable */
3328 #endif
3329 #ifdef ENETRESET
3330           case ENETRESET:               /* Network dropped connection on reset */
3331 #endif
3332 #ifdef ECONNABORTED
3333           case ECONNABORTED:            /* Software caused connection abort */
3334 #endif
3335 #ifdef ECONNRESET
3336           case ECONNRESET:              /* Connection reset by peer */
3337 #endif
3338 #ifdef ENOBUFS
3339           case ENOBUFS:                 /* No buffer space available */
3340 #endif
3341 #ifdef ESHUTDOWN
3342           case ESHUTDOWN:               /* Can't send after socket shutdown */
3343 #endif
3344 #ifdef ECONNREFUSED
3345           case ECONNREFUSED:            /* Connection refused */
3346 #endif
3347 #ifdef EHOSTDOWN
3348           case EHOSTDOWN:               /* Host is down */
3349 #endif
3350 #ifdef EHOSTUNREACH
3351           case EHOSTUNREACH:            /* No route to host */
3352 #endif
3353 #ifdef EDQUOT
3354           case EDQUOT:                  /* Disc quota exceeded */
3355 #endif
3356 #ifdef EPROCLIM
3357           case EPROCLIM:                /* Too many processes */
3358 #endif
3359 #ifdef EUSERS
3360           case EUSERS:                  /* Too many users */
3361 #endif
3362 #ifdef EDEADLK
3363           case EDEADLK:                 /* Resource deadlock avoided */
3364 #endif
3365 #ifdef EISCONN
3366           case EISCONN:                 /* Socket already connected */
3367 #endif
3368 #ifdef EINPROGRESS
3369           case EINPROGRESS:             /* Operation now in progress */
3370 #endif
3371 #ifdef EALREADY
3372           case EALREADY:                /* Operation already in progress */
3373 #endif
3374 #ifdef EADDRINUSE
3375           case EADDRINUSE:              /* Address already in use */
3376 #endif
3377 #ifdef EADDRNOTAVAIL
3378           case EADDRNOTAVAIL:           /* Can't assign requested address */
3379 #endif
3380 #ifdef ETXTBSY
3381           case ETXTBSY:                 /* (Apollo) file locked */
3382 #endif
3383 #if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
3384           case ENOSR:                   /* Out of streams resources */
3385 #endif
3386           case E_SM_OPENTIMEOUT:        /* PSEUDO: open timed out */
3387                 return TRUE;
3388         }
3389
3390         /* nope, must be permanent */
3391         return FALSE;
3392 }
3393 \f/*
3394 **  LOCKFILE -- lock a file using flock or (shudder) fcntl locking
3395 **
3396 **      Parameters:
3397 **              fd -- the file descriptor of the file.
3398 **              filename -- the file name (for error messages).
3399 **              ext -- the filename extension.
3400 **              type -- type of the lock.  Bits can be:
3401 **                      LOCK_EX -- exclusive lock.
3402 **                      LOCK_NB -- non-blocking.
3403 **
3404 **      Returns:
3405 **              TRUE if the lock was acquired.
3406 **              FALSE otherwise.
3407 */
3408
3409 bool
3410 lockfile(fd, filename, ext, type)
3411         int fd;
3412         char *filename;
3413         char *ext;
3414         int type;
3415 {
3416         int i;
3417         int save_errno;
3418 # if !HASFLOCK
3419         int action;
3420         struct flock lfd;
3421
3422         if (ext == NULL)
3423                 ext = "";
3424
3425         bzero(&lfd, sizeof lfd);
3426         if (bitset(LOCK_UN, type))
3427                 lfd.l_type = F_UNLCK;
3428         else if (bitset(LOCK_EX, type))
3429                 lfd.l_type = F_WRLCK;
3430         else
3431                 lfd.l_type = F_RDLCK;
3432
3433         if (bitset(LOCK_NB, type))
3434                 action = F_SETLK;
3435         else
3436                 action = F_SETLKW;
3437
3438         if (tTd(55, 60))
3439                 printf("lockfile(%s%s, action=%d, type=%d): ",
3440                         filename, ext, action, lfd.l_type);
3441
3442         while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
3443                 continue;
3444         if (i >= 0)
3445         {
3446                 if (tTd(55, 60))
3447                         printf("SUCCESS\n");
3448                 return TRUE;
3449         }
3450         save_errno = errno;
3451
3452         if (tTd(55, 60))
3453                 printf("(%s) ", errstring(save_errno));
3454
3455         /*
3456         **  On SunOS, if you are testing using -oQ/tmp/mqueue or
3457         **  -oA/tmp/aliases or anything like that, and /tmp is mounted
3458         **  as type "tmp" (that is, served from swap space), the
3459         **  previous fcntl will fail with "Invalid argument" errors.
3460         **  Since this is fairly common during testing, we will assume
3461         **  that this indicates that the lock is successfully grabbed.
3462         */
3463
3464         if (save_errno == EINVAL)
3465         {
3466                 if (tTd(55, 60))
3467                         printf("SUCCESS\n");
3468                 return TRUE;
3469         }
3470
3471         if (!bitset(LOCK_NB, type) || (save_errno != EACCES && save_errno != EAGAIN))
3472         {
3473                 int omode = -1;
3474 #  ifdef F_GETFL
3475                 (void) fcntl(fd, F_GETFL, &omode);
3476                 errno = save_errno;
3477 #  endif
3478                 syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
3479                         filename, ext, fd, type, omode, geteuid());
3480                 dumpfd(fd, TRUE, TRUE);
3481         }
3482 # else
3483         if (ext == NULL)
3484                 ext = "";
3485
3486         if (tTd(55, 60))
3487                 printf("lockfile(%s%s, type=%o): ", filename, ext, type);
3488
3489         while ((i = flock(fd, type)) < 0 && errno == EINTR)
3490                 continue;
3491         if (i >= 0)
3492         {
3493                 if (tTd(55, 60))
3494                         printf("SUCCESS\n");
3495                 return TRUE;
3496         }
3497         save_errno = errno;
3498
3499         if (tTd(55, 60))
3500                 printf("(%s) ", errstring(save_errno));
3501
3502         if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
3503         {
3504                 int omode = -1;
3505 #  ifdef F_GETFL
3506                 (void) fcntl(fd, F_GETFL, &omode);
3507                 errno = save_errno;
3508 #  endif
3509                 syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
3510                         filename, ext, fd, type, omode, geteuid());
3511                 dumpfd(fd, TRUE, TRUE);
3512         }
3513 # endif
3514         if (tTd(55, 60))
3515                 printf("FAIL\n");
3516         errno = save_errno;
3517         return FALSE;
3518 }
3519 \f/*
3520 **  CHOWNSAFE -- tell if chown is "safe" (executable only by root)
3521 **
3522 **      Unfortunately, given that we can't predict other systems on which
3523 **      a remote mounted (NFS) filesystem will be mounted, the answer is
3524 **      almost always that this is unsafe.
3525 **
3526 **      Note also that many operating systems have non-compliant
3527 **      implementations of the _POSIX_CHOWN_RESTRICTED variable and the
3528 **      fpathconf() routine.  According to IEEE 1003.1-1990, if
3529 **      _POSIX_CHOWN_RESTRICTED is defined and not equal to -1, then
3530 **      no non-root process can give away the file.  However, vendors
3531 **      don't take NFS into account, so a comfortable value of
3532 **      _POSIX_CHOWN_RESTRICTED tells us nothing.
3533 **
3534 **      Also, some systems (e.g., IRIX 6.2) return 1 from fpathconf()
3535 **      even on files where chown is not restricted.  Many systems get
3536 **      this wrong on NFS-based filesystems (that is, they say that chown
3537 **      is restricted [safe] on NFS filesystems where it may not be, since
3538 **      other systems can access the same filesystem and do file giveaway;
3539 **      only the NFS server knows for sure!)  Hence, it is important to
3540 **      get the value of SAFENFSPATHCONF correct -- it should be defined
3541 **      _only_ after testing (see test/t_pathconf.c) a system on an unsafe
3542 **      NFS-based filesystem to ensure that you can get meaningful results.
3543 **      If in doubt, assume unsafe!
3544 **
3545 **      You may also need to tweak IS_SAFE_CHOWN -- it should be a
3546 **      condition indicating whether the return from pathconf indicates
3547 **      that chown is safe (typically either > 0 or >= 0 -- there isn't
3548 **      even any agreement about whether a zero return means that a file
3549 **      is or is not safe).  It defaults to "> 0".
3550 **
3551 **      If the parent directory is safe (writable only by owner back
3552 **      to the root) then we can relax slightly and trust fpathconf
3553 **      in more circumstances.  This is really a crock -- if this is an
3554 **      NFS mounted filesystem then we really know nothing about the
3555 **      underlying implementation.  However, most systems pessimize and
3556 **      return an error (EINVAL or EOPNOTSUPP) on NFS filesystems, which
3557 **      we interpret as unsafe, as we should.  Thus, this heuristic gets
3558 **      us into a possible problem only on systems that have a broken
3559 **      pathconf implementation and which are also poorly configured
3560 **      (have :include: files in group- or world-writable directories).
3561 **
3562 **      Parameters:
3563 **              fd -- the file descriptor to check.
3564 **              safedir -- set if the parent directory is safe.
3565 **
3566 **      Returns:
3567 **              TRUE -- if the chown(2) operation is "safe" -- that is,
3568 **                      only root can chown the file to an arbitrary user.
3569 **              FALSE -- if an arbitrary user can give away a file.
3570 */
3571
3572 #ifndef IS_SAFE_CHOWN
3573 # define IS_SAFE_CHOWN  > 0
3574 #endif
3575
3576 bool
3577 chownsafe(fd, safedir)
3578         int fd;
3579         bool safedir;
3580 {
3581 #if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
3582     (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
3583         int rval;
3584
3585         /* give the system administrator a chance to override */
3586         if (bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail))
3587                 return TRUE;
3588
3589         /*
3590         **  Some systems (e.g., SunOS) seem to have the call and the
3591         **  #define _PC_CHOWN_RESTRICTED, but don't actually implement
3592         **  the call.  This heuristic checks for that.
3593         */
3594
3595         errno = 0;
3596         rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
3597 # if SAFENFSPATHCONF
3598         return errno == 0 && rval IS_SAFE_CHOWN;
3599 # else
3600         return safedir && errno == 0 && rval IS_SAFE_CHOWN;
3601 # endif
3602 #else
3603         return bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail);
3604 #endif
3605 }
3606 \f/*
3607 **  RESETLIMITS -- reset system controlled resource limits
3608 **
3609 **      This is to avoid denial-of-service attacks
3610 **
3611 **      Parameters:
3612 **              none
3613 **
3614 **      Returns:
3615 **              none
3616 */
3617
3618 #if HASSETRLIMIT
3619 # ifdef RLIMIT_NEEDS_SYS_TIME_H
3620 #  include <sys/time.h>
3621 # endif
3622 # include <sys/resource.h>
3623 #endif
3624 #ifndef FD_SETSIZE
3625 # define FD_SETSIZE     256
3626 #endif
3627
3628 void
3629 resetlimits()
3630 {
3631 #if HASSETRLIMIT
3632         struct rlimit lim;
3633
3634         lim.rlim_cur = lim.rlim_max = RLIM_INFINITY;
3635         (void) setrlimit(RLIMIT_CPU, &lim);
3636         (void) setrlimit(RLIMIT_FSIZE, &lim);
3637 # ifdef RLIMIT_NOFILE
3638         lim.rlim_cur = lim.rlim_max = FD_SETSIZE;
3639         (void) setrlimit(RLIMIT_NOFILE, &lim);
3640 # endif
3641 #else
3642 # if HASULIMIT
3643         (void) ulimit(2, 0x3fffff);
3644         (void) ulimit(4, FD_SETSIZE);
3645 # endif
3646 #endif
3647         errno = 0;
3648 }
3649 \f/*
3650 **  GETCFNAME -- return the name of the .cf file.
3651 **
3652 **      Some systems (e.g., NeXT) determine this dynamically.
3653 */
3654
3655 char *
3656 getcfname()
3657 {
3658
3659         if (ConfFile != NULL)
3660                 return ConfFile;
3661 #if NETINFO
3662         {
3663                 extern char *ni_propval __P((char *, char *, char *, char *, int));
3664                 char *cflocation;
3665
3666                 cflocation = ni_propval("/locations", NULL, "sendmail",
3667                                         "sendmail.cf", '\0');
3668                 if (cflocation != NULL)
3669                         return cflocation;
3670         }
3671 #endif
3672
3673         return _PATH_SENDMAILCF;
3674 }
3675 \f/*
3676 **  SETVENDOR -- process vendor code from V configuration line
3677 **
3678 **      Parameters:
3679 **              vendor -- string representation of vendor.
3680 **
3681 **      Returns:
3682 **              TRUE -- if ok.
3683 **              FALSE -- if vendor code could not be processed.
3684 **
3685 **      Side Effects:
3686 **              It is reasonable to set mode flags here to tweak
3687 **              processing in other parts of the code if necessary.
3688 **              For example, if you are a vendor that uses $%y to
3689 **              indicate YP lookups, you could enable that here.
3690 */
3691
3692 bool
3693 setvendor(vendor)
3694         char *vendor;
3695 {
3696         if (strcasecmp(vendor, "Berkeley") == 0)
3697         {
3698                 VendorCode = VENDOR_BERKELEY;
3699                 return TRUE;
3700         }
3701
3702         /* add vendor extensions here */
3703
3704 #ifdef SUN_EXTENSIONS
3705         if (strcasecmp(vendor, "Sun") == 0)
3706         {
3707                 VendorCode = VENDOR_SUN;
3708                 return TRUE;
3709         }
3710 #endif
3711
3712 #if defined(VENDOR_NAME) && defined(VENDOR_CODE)
3713         if (strcasecmp(vendor, VENDOR_NAME) == 0)
3714         {
3715                 VendorCode = VENDOR_CODE;
3716                 return TRUE;
3717         }
3718 #endif
3719
3720         return FALSE;
3721 }
3722 \f/*
3723 **  GETVENDOR -- return vendor name based on vendor code
3724 **
3725 **      Parameters:
3726 **              vendorcode -- numeric representation of vendor.
3727 **
3728 **      Returns:
3729 **              string containing vendor name.
3730 */
3731
3732 char *
3733 getvendor(vendorcode)
3734         int vendorcode;
3735 {
3736 #if defined(VENDOR_NAME) && defined(VENDOR_CODE)
3737         /*
3738         **  Can't have the same switch case twice so need to 
3739         **  handle VENDOR_CODE outside of switch.  It might
3740         **  match one of the existing VENDOR_* codes.
3741         */
3742
3743         if (vendorcode == VENDOR_CODE)
3744                 return VENDOR_NAME;
3745 #endif
3746
3747         switch (vendorcode)
3748         {
3749                 case VENDOR_BERKELEY:
3750                         return "Berkeley";
3751                 
3752                 case VENDOR_SUN:
3753                         return "Sun";
3754
3755                 case VENDOR_HP:
3756                         return "HP";
3757
3758                 case VENDOR_IBM:
3759                         return "IBM";
3760
3761                 case VENDOR_SENDMAIL:
3762                         return "Sendmail";
3763
3764                 default:
3765                         return "Unknown";
3766         }
3767 }
3768 \f/*
3769 **  VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults
3770 **
3771 **      Vendor_pre_defaults is called before reading the configuration
3772 **      file; vendor_post_defaults is called immediately after.
3773 **
3774 **      Parameters:
3775 **              e -- the global environment to initialize.
3776 **
3777 **      Returns:
3778 **              none.
3779 */
3780
3781 #if SHARE_V1
3782 int     DefShareUid;    /* default share uid to run as -- unused??? */
3783 #endif
3784
3785 void
3786 vendor_pre_defaults(e)
3787         ENVELOPE *e;
3788 {
3789 #if SHARE_V1
3790         /* OTHERUID is defined in shares.h, do not be alarmed */
3791         DefShareUid = OTHERUID;
3792 #endif
3793 #if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
3794         sun_pre_defaults(e);
3795 #endif
3796 #ifdef apollo
3797         /* stupid domain/os can't even open /etc/sendmail.cf without this */
3798         setuserenv("ISP", NULL);
3799         setuserenv("SYSTYPE", NULL);
3800 #endif
3801 }
3802
3803
3804 void
3805 vendor_post_defaults(e)
3806         ENVELOPE *e;
3807 {
3808 #ifdef __QNX__
3809         char *p;
3810         
3811         /* Makes sure the SOCK environment variable remains */
3812         if (p = getextenv("SOCK"))
3813                 setuserenv("SOCK", p);
3814 #endif
3815 #if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
3816         sun_post_defaults(e);
3817 #endif
3818 }
3819 \f/*
3820 **  VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode
3821 */
3822
3823 void
3824 vendor_daemon_setup(e)
3825         ENVELOPE *e;
3826 {
3827 #if SECUREWARE
3828         if (getluid() != -1)
3829         {
3830                 usrerr("Daemon cannot have LUID");
3831                 finis(FALSE, EX_USAGE);
3832         }
3833 #endif /* SECUREWARE */
3834 }
3835 \f/*
3836 **  VENDOR_SET_UID -- do setup for setting a user id
3837 **
3838 **      This is called when we are still root.
3839 **
3840 **      Parameters:
3841 **              uid -- the uid we are about to become.
3842 **
3843 **      Returns:
3844 **              none.
3845 */
3846
3847 void
3848 vendor_set_uid(uid)
3849         UID_T uid;
3850 {
3851         /* 
3852         **  We need to setup the share groups (lnodes)
3853         **  and and auditing inforation (luid's)
3854         **  before we loose our ``root''ness.
3855         */
3856 #if SHARE_V1
3857         if (setupshares(uid, syserr) != 0)
3858                 syserr("Unable to set up shares");
3859 #endif
3860 #if SECUREWARE
3861         (void) setup_secure(uid);
3862 #endif
3863 }
3864 \f/*
3865 **  VALIDATE_CONNECTION -- check connection for rationality
3866 **
3867 **      If the connection is rejected, this routine should log an
3868 **      appropriate message -- but should never issue any SMTP protocol.
3869 **
3870 **      Parameters:
3871 **              sap -- a pointer to a SOCKADDR naming the peer.
3872 **              hostname -- the name corresponding to sap.
3873 **              e -- the current envelope.
3874 **
3875 **      Returns:
3876 **              error message from rejection.
3877 **              NULL if not rejected.
3878 */
3879
3880 #if TCPWRAPPERS
3881 # include <tcpd.h>
3882
3883 /* tcpwrappers does no logging, but you still have to declare these -- ugh */
3884 int     allow_severity  = LOG_INFO;
3885 int     deny_severity   = LOG_NOTICE;
3886 #endif
3887
3888 #if DAEMON
3889 char *
3890 validate_connection(sap, hostname, e)
3891         SOCKADDR *sap;
3892         char *hostname;
3893         ENVELOPE *e;
3894 {
3895 #if TCPWRAPPERS
3896         char *host;
3897 #endif
3898
3899         if (tTd(48, 3))
3900                 printf("validate_connection(%s, %s)\n",
3901                         hostname, anynet_ntoa(sap));
3902
3903         if (rscheck("check_relay", hostname, anynet_ntoa(sap), e) != EX_OK)
3904         {
3905                 static char reject[BUFSIZ*2];
3906                 extern char MsgBuf[];
3907
3908                 if (tTd(48, 4))
3909                         printf("  ... validate_connection: BAD (rscheck)\n");
3910
3911                 if (strlen(MsgBuf) > 5)
3912                 {
3913                         if (isascii(MsgBuf[0]) && isdigit(MsgBuf[0]) &&
3914                             isascii(MsgBuf[1]) && isdigit(MsgBuf[1]) &&
3915                             isascii(MsgBuf[2]) && isdigit(MsgBuf[2]))
3916                                 strcpy(reject, &MsgBuf[4]);
3917                         else
3918                                 strcpy(reject, MsgBuf);
3919                 }
3920                 else
3921                         strcpy(reject, "Access denied");
3922
3923                 return reject;
3924         }
3925
3926 #if TCPWRAPPERS
3927         if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']')
3928                 host = "unknown";
3929         else
3930                 host = hostname;
3931         if (!hosts_ctl("sendmail", host, anynet_ntoa(sap), STRING_UNKNOWN))
3932         {
3933                 if (tTd(48, 4))
3934                         printf("  ... validate_connection: BAD (tcpwrappers)\n");
3935                 if (LogLevel >= 4)
3936                         sm_syslog(LOG_NOTICE, NOQID,
3937                                 "tcpwrappers (%s, %s) rejection",
3938                                 host, anynet_ntoa(sap));
3939                 return "Access denied";
3940         }
3941 #endif
3942         if (tTd(48, 4))
3943                 printf("  ... validate_connection: OK\n");
3944         return NULL;
3945 }
3946
3947 #endif
3948 \f/*
3949 **  STRTOL -- convert string to long integer
3950 **
3951 **      For systems that don't have it in the C library.
3952 **
3953 **      This is taken verbatim from the 4.4-Lite C library.
3954 */
3955
3956 #ifdef NEEDSTRTOL
3957
3958 #if defined(LIBC_SCCS) && !defined(lint)
3959 static char sccsid[] = "@(#)strtol.c    8.1 (Berkeley) 6/4/93";
3960 #endif /* LIBC_SCCS and not lint */
3961
3962 /*
3963  * Convert a string to a long integer.
3964  *
3965  * Ignores `locale' stuff.  Assumes that the upper and lower case
3966  * alphabets and digits are each contiguous.
3967  */
3968
3969 long
3970 strtol(nptr, endptr, base)
3971         const char *nptr;
3972         char **endptr;
3973         register int base;
3974 {
3975         register const char *s = nptr;
3976         register unsigned long acc;
3977         register int c;
3978         register unsigned long cutoff;
3979         register int neg = 0, any, cutlim;
3980
3981         /*
3982          * Skip white space and pick up leading +/- sign if any.
3983          * If base is 0, allow 0x for hex and 0 for octal, else
3984          * assume decimal; if base is already 16, allow 0x.
3985          */
3986         do {
3987                 c = *s++;
3988         } while (isspace(c));
3989         if (c == '-') {
3990                 neg = 1;
3991                 c = *s++;
3992         } else if (c == '+')
3993                 c = *s++;
3994         if ((base == 0 || base == 16) &&
3995             c == '0' && (*s == 'x' || *s == 'X')) {
3996                 c = s[1];
3997                 s += 2;
3998                 base = 16;
3999         }
4000         if (base == 0)
4001                 base = c == '0' ? 8 : 10;
4002
4003         /*
4004          * Compute the cutoff value between legal numbers and illegal
4005          * numbers.  That is the largest legal value, divided by the
4006          * base.  An input number that is greater than this value, if
4007          * followed by a legal input character, is too big.  One that
4008          * is equal to this value may be valid or not; the limit
4009          * between valid and invalid numbers is then based on the last
4010          * digit.  For instance, if the range for longs is
4011          * [-2147483648..2147483647] and the input base is 10,
4012          * cutoff will be set to 214748364 and cutlim to either
4013          * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
4014          * a value > 214748364, or equal but the next digit is > 7 (or 8),
4015          * the number is too big, and we will return a range error.
4016          *
4017          * Set any if any `digits' consumed; make it negative to indicate
4018          * overflow.
4019          */
4020         cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
4021         cutlim = cutoff % (unsigned long)base;
4022         cutoff /= (unsigned long)base;
4023         for (acc = 0, any = 0;; c = *s++) {
4024                 if (isdigit(c))
4025                         c -= '0';
4026                 else if (isalpha(c))
4027                         c -= isupper(c) ? 'A' - 10 : 'a' - 10;
4028                 else
4029                         break;
4030                 if (c >= base)
4031                         break;
4032                 if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
4033                         any = -1;
4034                 else {
4035                         any = 1;
4036                         acc *= base;
4037                         acc += c;
4038                 }
4039         }
4040         if (any < 0) {
4041                 acc = neg ? LONG_MIN : LONG_MAX;
4042                 errno = ERANGE;
4043         } else if (neg)
4044                 acc = -acc;
4045         if (endptr != 0)
4046                 *endptr = (char *)(any ? s - 1 : nptr);
4047         return (acc);
4048 }
4049
4050 #endif
4051 \f/*
4052 **  STRSTR -- find first substring in string
4053 **
4054 **      Parameters:
4055 **              big -- the big (full) string.
4056 **              little -- the little (sub) string.
4057 **
4058 **      Returns:
4059 **              A pointer to the first instance of little in big.
4060 **              big if little is the null string.
4061 **              NULL if little is not contained in big.
4062 */
4063
4064 #ifdef NEEDSTRSTR
4065
4066 char *
4067 strstr(big, little)
4068         char *big;
4069         char *little;
4070 {
4071         register char *p = big;
4072         int l;
4073
4074         if (*little == '\0')
4075                 return big;
4076         l = strlen(little);
4077
4078         while ((p = strchr(p, *little)) != NULL)
4079         {
4080                 if (strncmp(p, little, l) == 0)
4081                         return p;
4082                 p++;
4083         }
4084         return NULL;
4085 }
4086
4087 #endif
4088 \f/*
4089 **  SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
4090 **
4091 **      Some operating systems have wierd problems with the gethostbyXXX
4092 **      routines.  For example, Solaris versions at least through 2.3
4093 **      don't properly deliver a canonical h_name field.  This tries to
4094 **      work around these problems.
4095 */
4096
4097 struct hostent *
4098 sm_gethostbyname(name)
4099         char *name;
4100 {
4101         struct hostent *h;
4102 #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
4103 # if SOLARIS == 20300 || SOLARIS == 203
4104         static struct hostent hp;
4105         static char buf[1000];
4106         extern struct hostent *_switch_gethostbyname_r();
4107
4108         if (tTd(61, 10))
4109                 printf("_switch_gethostbyname_r(%s)... ", name);
4110         h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
4111 # else
4112         extern struct hostent *__switch_gethostbyname();
4113
4114         if (tTd(61, 10))
4115                 printf("__switch_gethostbyname(%s)... ", name);
4116         h = __switch_gethostbyname(name);
4117 # endif
4118 #else
4119         int nmaps;
4120         char *maptype[MAXMAPSTACK];
4121         short mapreturn[MAXMAPACTIONS];
4122         char hbuf[MAXNAME];
4123
4124         if (tTd(61, 10))
4125                 printf("gethostbyname(%s)... ", name);
4126         h = gethostbyname(name);
4127         if (h == NULL)
4128         {
4129                 if (tTd(61, 10))
4130                         printf("failure\n");
4131
4132                 nmaps = switch_map_find("hosts", maptype, mapreturn);
4133                 while (--nmaps >= 0)
4134                         if (strcmp(maptype[nmaps], "nis") == 0 ||
4135                             strcmp(maptype[nmaps], "files") == 0)
4136                                 break;
4137                 if (nmaps >= 0)
4138                 {
4139                         /* try short name */
4140                         if (strlen(name) > (SIZE_T) sizeof hbuf - 1)
4141                                 return NULL;
4142                         strcpy(hbuf, name);
4143                         shorten_hostname(hbuf);
4144
4145                         /* if it hasn't been shortened, there's no point */
4146                         if (strcmp(hbuf, name) != 0)
4147                         {
4148                                 if (tTd(61, 10))
4149                                         printf("gethostbyname(%s)... ", hbuf);
4150                                 h = gethostbyname(hbuf);
4151                         }
4152                 }
4153         }
4154 #endif
4155         if (tTd(61, 10))
4156         {
4157                 if (h == NULL)
4158                         printf("failure\n");
4159                 else
4160                         printf("%s\n", h->h_name);
4161         }
4162         return h;
4163 }
4164
4165 struct hostent *
4166 sm_gethostbyaddr(addr, len, type)
4167         char *addr;
4168         int len;
4169         int type;
4170 {
4171 #if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204)
4172 # if SOLARIS == 20300 || SOLARIS == 203
4173         static struct hostent hp;
4174         static char buf[1000];
4175         extern struct hostent *_switch_gethostbyaddr_r();
4176
4177         return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno);
4178 # else
4179         extern struct hostent *__switch_gethostbyaddr();
4180
4181         return __switch_gethostbyaddr(addr, len, type);
4182 # endif
4183 #else
4184         return gethostbyaddr(addr, len, type);
4185 #endif
4186 }
4187 \f/*
4188 **  SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
4189 */
4190
4191 struct passwd *
4192 sm_getpwnam(user)
4193         char *user;
4194 {
4195 #ifdef _AIX4
4196         extern struct passwd *_getpwnam_shadow(const char *, const int);
4197
4198         return _getpwnam_shadow(user, 0);
4199 #else
4200         return getpwnam(user);
4201 #endif
4202 }
4203
4204 struct passwd *
4205 sm_getpwuid(uid)
4206         UID_T uid;
4207 {
4208 #if defined(_AIX4) && 0
4209         extern struct passwd *_getpwuid_shadow(const int, const int);
4210
4211         return _getpwuid_shadow(uid,0);
4212 #else
4213         return getpwuid(uid);
4214 #endif
4215 }
4216 \f/*
4217 **  SECUREWARE_SETUP_SECURE -- Convex SecureWare setup
4218 **
4219 **      Set up the trusted computing environment for C2 level security
4220 **      under SecureWare.
4221 **
4222 **      Parameters:
4223 **              uid -- uid of the user to initialize in the TCB
4224 **
4225 **      Returns:
4226 **              none
4227 **
4228 **      Side Effects:
4229 **              Initialized the user in the trusted computing base
4230 */
4231
4232 #if SECUREWARE
4233
4234 # include <sys/security.h>
4235 # include <prot.h>
4236
4237 void
4238 secureware_setup_secure(uid)
4239         UID_T uid;
4240 {
4241         int rc;
4242
4243         if (getluid() != -1)
4244                 return;
4245         
4246         if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN)
4247         {
4248                 switch (rc)
4249                 {
4250                   case SSI_NO_PRPW_ENTRY:
4251                         syserr("No protected passwd entry, uid = %d", uid);
4252                         break;
4253
4254                   case SSI_LOCKED:
4255                         syserr("Account has been disabled, uid = %d", uid);
4256                         break;
4257
4258                   case SSI_RETIRED:
4259                         syserr("Account has been retired, uid = %d", uid);
4260                         break;
4261
4262                   case SSI_BAD_SET_LUID:
4263                         syserr("Could not set LUID, uid = %d", uid);
4264                         break;
4265
4266                   case SSI_BAD_SET_PRIVS:
4267                         syserr("Could not set kernel privs, uid = %d", uid);
4268
4269                   default:
4270                         syserr("Unknown return code (%d) from set_secure_info(%d)", 
4271                                 rc, uid);
4272                         break;
4273                 }
4274                 finis(FALSE, EX_NOPERM);
4275         }
4276 }
4277 #endif /* SECUREWARE */
4278 \f/*
4279 **  ADD_LOCAL_HOST_NAMES -- Add a hostname to class 'w' based on IP address
4280 **
4281 **      Add hostnames to class 'w' based on the IP address read from
4282 **      the network interface.
4283 **
4284 **      Parameters:
4285 **              sa -- a pointer to a SOCKADDR containing the address
4286 **
4287 **      Returns:
4288 **              0 if successful, -1 if host lookup fails.
4289 */
4290
4291 int
4292 add_hostnames(sa)
4293         SOCKADDR *sa;
4294 {
4295         struct hostent *hp;
4296
4297         /* lookup name with IP address */
4298         switch (sa->sa.sa_family)
4299         {
4300                 case AF_INET:
4301                         hp = sm_gethostbyaddr((char *) &sa->sin.sin_addr,
4302                                 sizeof(sa->sin.sin_addr), sa->sa.sa_family);
4303                         break;
4304
4305                 default:
4306 #if _FFR_LOG_UNSUPPORTED_FAMILIES
4307                         /* XXX: Give warning about unsupported family */
4308                         if (LogLevel > 3)
4309                                 sm_syslog(LOG_WARNING, NOQID,
4310                                           "Unsupported address family %d: %.100s",
4311                                           sa->sa.sa_family, anynet_ntoa(sa));
4312 #endif
4313                         return -1;
4314         }
4315
4316         if (hp == NULL)
4317         {
4318                 int save_errno = errno;
4319
4320                 if (LogLevel > 3)
4321                         sm_syslog(LOG_WARNING, NOQID,
4322                                 "gethostbyaddr(%.100s) failed: %d\n",
4323                                 anynet_ntoa(sa),
4324 #if NAMED_BIND
4325                                 h_errno
4326 #else
4327                                 -1
4328 #endif
4329                                 );
4330                 errno = save_errno;
4331                 return -1;
4332         }
4333
4334         /* save its cname */
4335         if (!wordinclass((char *) hp->h_name, 'w'))
4336         {
4337                 setclass('w', (char *) hp->h_name);
4338                 if (tTd(0, 4))
4339                         printf("\ta.k.a.: %s\n", hp->h_name);
4340         }
4341
4342         /* save all it aliases name */
4343         while (*hp->h_aliases)
4344         {
4345                 if (!wordinclass(*hp->h_aliases, 'w'))
4346                 {
4347                         setclass('w', *hp->h_aliases);
4348                         if (tTd(0, 4))
4349                                 printf("\ta.k.a.: %s\n", *hp->h_aliases);
4350                 }
4351                 hp->h_aliases++;
4352         }
4353         return 0;
4354 }
4355 \f/*
4356 **  LOAD_IF_NAMES -- load interface-specific names into $=w
4357 **
4358 **      Parameters:
4359 **              none.
4360 **
4361 **      Returns:
4362 **              none.
4363 **
4364 **      Side Effects:
4365 **              Loads $=w with the names of all the interfaces.
4366 */
4367
4368 #if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
4369 struct rtentry;
4370 struct mbuf;
4371 # include <arpa/inet.h>
4372 # ifndef SUNOS403
4373 #  include <sys/time.h>
4374 # endif
4375 # if _AIX4 >= 40300
4376 #  undef __P
4377 # endif
4378 # include <net/if.h>
4379 #endif
4380
4381 void
4382 load_if_names()
4383 {
4384 #if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
4385         int s;
4386         int i;
4387         struct ifconf ifc;
4388         int numifs;
4389
4390         s = socket(AF_INET, SOCK_DGRAM, 0);
4391         if (s == -1)
4392                 return;
4393
4394         /* get the list of known IP address from the kernel */
4395 # if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN
4396         if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0)
4397         {
4398                 /* can't get number of interfaces -- fall back */
4399                 if (tTd(0, 4))
4400                         printf("SIOCGIFNUM failed: %s\n", errstring(errno));
4401                 numifs = -1;
4402         }
4403         else if (tTd(0, 42))
4404                 printf("system has %d interfaces\n", numifs);
4405         if (numifs < 0)
4406 # endif
4407                 numifs = 512;
4408
4409         if (numifs <= 0)
4410         {
4411                 close(s);
4412                 return;
4413         }
4414         ifc.ifc_len = numifs * sizeof (struct ifreq);
4415         ifc.ifc_buf = xalloc(ifc.ifc_len);
4416         if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
4417         {
4418                 if (tTd(0, 4))
4419                         printf("SIOGIFCONF failed: %s\n", errstring(errno));
4420                 close(s);
4421                 return;
4422         }
4423
4424         /* scan the list of IP address */
4425         if (tTd(0, 40))
4426                 printf("scanning for interface specific names, ifc_len=%d\n",
4427                         ifc.ifc_len);
4428
4429         for (i = 0; i < ifc.ifc_len; )
4430         {
4431                 struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
4432                 SOCKADDR *sa = (SOCKADDR *) &ifr->ifr_addr;
4433                 struct in_addr ia;
4434 #ifdef SIOCGIFFLAGS
4435                 struct ifreq ifrf;
4436 #endif
4437                 char ip_addr[256];
4438                 extern char *inet_ntoa();
4439
4440 #ifdef BSD4_4_SOCKADDR
4441                 if (sa->sa.sa_len > sizeof ifr->ifr_addr)
4442                         i += sizeof ifr->ifr_name + sa->sa.sa_len;
4443                 else
4444 #endif
4445                         i += sizeof *ifr;
4446
4447                 if (tTd(0, 20))
4448                         printf("%s\n", anynet_ntoa(sa));
4449
4450                 if (ifr->ifr_addr.sa_family != AF_INET)
4451                         continue;
4452
4453 #ifdef SIOCGIFFLAGS
4454                 bzero(&ifrf, sizeof(struct ifreq));
4455                 strncpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name));
4456                 ioctl(s, SIOCGIFFLAGS, (char *) &ifrf);
4457                 if (tTd(0, 41))
4458                         printf("\tflags: %x\n", ifrf.ifr_flags);
4459 # define IFRFREF ifrf
4460 #else
4461 # define IFRFREF (*ifr)
4462 #endif
4463                 if (!bitset(IFF_UP, IFRFREF.ifr_flags))
4464                         continue;
4465
4466                 /* extract IP address from the list*/
4467                 ia = sa->sin.sin_addr;
4468                 if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE)
4469                 {
4470                         message("WARNING: interface %s is UP with %s address",
4471                                 ifr->ifr_name, inet_ntoa(ia));
4472                         continue;
4473                 }
4474
4475                 /* save IP address in text from */
4476                 (void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
4477                         (int)sizeof ip_addr - 3,
4478                         inet_ntoa(ia));
4479                 if (!wordinclass(ip_addr, 'w'))
4480                 {
4481                         setclass('w', ip_addr);
4482                         if (tTd(0, 4))
4483                                 printf("\ta.k.a.: %s\n", ip_addr);
4484                 }
4485
4486                 /* skip "loopback" interface "lo" */
4487                 if (bitset(IFF_LOOPBACK, IFRFREF.ifr_flags))
4488                         continue;
4489
4490                 (void) add_hostnames(sa);
4491         }
4492         free(ifc.ifc_buf);
4493         close(s);
4494 # undef IFRFREF
4495 #endif
4496 }
4497 \f/*
4498 **  GET_NUM_PROCS_ONLINE -- return the number of processors currently online
4499 **
4500 **      Parameters:
4501 **              none.
4502 **
4503 **      Returns:
4504 **              The number of processors online.
4505 */
4506
4507 int
4508 get_num_procs_online()
4509 {
4510         int nproc = 0;
4511
4512 #if _FFR_SCALE_LA_BY_NUM_PROCS
4513 #ifdef _SC_NPROCESSORS_ONLN
4514         nproc = (int) sysconf(_SC_NPROCESSORS_ONLN);
4515 #endif
4516 #endif
4517         if (nproc <= 0)
4518                 nproc = 1;
4519         return nproc;
4520 }
4521 \f/*
4522 **  SM_SYSLOG -- syslog wrapper to keep messages under SYSLOG_BUFSIZE
4523 **
4524 **      Parameters:
4525 **              level -- syslog level
4526 **              id -- envelope ID or NULL (NOQUEUE)
4527 **              fmt -- format string
4528 **              arg... -- arguments as implied by fmt.
4529 **
4530 **      Returns:
4531 **              none
4532 */
4533
4534 /* VARARGS3 */
4535 void
4536 # ifdef __STDC__
4537 sm_syslog(int level, const char *id, const char *fmt, ...)
4538 # else
4539 sm_syslog(level, id, fmt, va_alist)
4540         int level;
4541         const char *id;
4542         const char *fmt;
4543         va_dcl
4544 #endif
4545 {
4546         static char *buf = NULL;
4547         static size_t bufsize = MAXLINE;
4548         char *begin, *end;
4549         int seq = 1;
4550         int idlen;
4551         extern int SnprfOverflow;
4552         extern int SyslogErrno;
4553         extern char *DoprEnd;
4554         VA_LOCAL_DECL
4555         extern void sm_dopr __P((char *, const char *, va_list));
4556         
4557         SyslogErrno = errno;
4558         if (id == NULL)
4559         {
4560                 id = "NOQUEUE";
4561                 idlen = 9;
4562         }
4563         else if (strcmp(id, NOQID) == 0)
4564         {
4565                 id = "";
4566                 idlen = 0;
4567         }
4568         else
4569                 idlen = strlen(id + 2);
4570 bufalloc:
4571         if (buf == NULL)
4572                 buf = (char *) xalloc(sizeof(char) * bufsize);
4573
4574         /* do a virtual vsnprintf into buf */
4575         VA_START(fmt);
4576         buf[0] = 0;
4577         DoprEnd = buf + bufsize - 1;
4578         SnprfOverflow = 0;
4579         sm_dopr(buf, fmt, ap);
4580         *DoprEnd = '\0';
4581         VA_END;
4582         /* end of virtual vsnprintf */
4583
4584         if (SnprfOverflow)
4585         {
4586                 /* String too small, redo with correct size */
4587                 bufsize += SnprfOverflow + 1;
4588                 free(buf);
4589                 buf = NULL;
4590                 goto bufalloc;
4591         }
4592         if ((strlen(buf) + idlen + 1) < SYSLOG_BUFSIZE)
4593         {
4594 #if LOG
4595                 if (*id == '\0')
4596                         syslog(level, "%s", buf);
4597                 else
4598                         syslog(level, "%s: %s", id, buf);
4599 #else
4600                 /*XXX should do something more sensible */
4601                 if (*id == '\0')
4602                         fprintf(stderr, "%s\n", buf);
4603                 else
4604                         fprintf(stderr, "%s: %s\n", id, buf);
4605 #endif
4606                 return;
4607         }
4608
4609         begin = buf;
4610         while (*begin != '\0' &&
4611                (strlen(begin) + idlen + 5) > SYSLOG_BUFSIZE) 
4612         {
4613                 char save;
4614         
4615                 if (seq == 999)
4616                 {
4617                         /* Too many messages */
4618                         break;
4619                 }
4620                 end = begin + SYSLOG_BUFSIZE - idlen - 12;
4621                 while (end > begin)
4622                 {
4623                         /* Break on comma or space */
4624                         if (*end == ',' || *end == ' ')
4625                         {
4626                                 end++;    /* Include separator */
4627                                 break;
4628                         }
4629                         end--;
4630                 }
4631                 /* No separator, break midstring... */
4632                 if (end == begin)
4633                         end = begin + SYSLOG_BUFSIZE - idlen - 12;
4634                 save = *end;
4635                 *end = 0;
4636 #if LOG
4637                 syslog(level, "%s[%d]: %s ...", id, seq++, begin);
4638 #else
4639                 fprintf(stderr, "%s[%d]: %s ...\n", id, seq++, begin);
4640 #endif
4641                 *end = save;
4642                 begin = end;
4643         }
4644         if (seq == 999)
4645 #if LOG
4646                 syslog(level, "%s[%d]: log terminated, too many parts", id, seq);
4647 #else
4648                 fprintf(stderr, "%s[%d]: log terminated, too many parts\n", id, seq);
4649 #endif
4650         else if (*begin != '\0')
4651 #if LOG
4652                 syslog(level, "%s[%d]: %s", id, seq, begin);
4653 #else
4654                 fprintf(stderr, "%s[%d]: %s\n", id, seq, begin);
4655 #endif
4656 }
4657 \f/*
4658 **  HARD_SYSLOG -- call syslog repeatedly until it works
4659 **
4660 **      Needed on HP-UX, which apparently doesn't guarantee that
4661 **      syslog succeeds during interrupt handlers.
4662 */
4663
4664 #if defined(__hpux) && !defined(HPUX11)
4665
4666 # define MAXSYSLOGTRIES 100
4667 # undef syslog
4668 # ifdef V4FS
4669 #  define XCNST const
4670 #  define CAST  (const char *)
4671 # else
4672 #  define XCNST
4673 #  define CAST
4674 # endif
4675
4676 void
4677 # ifdef __STDC__
4678 hard_syslog(int pri, XCNST char *msg, ...)
4679 # else
4680 hard_syslog(pri, msg, va_alist)
4681         int pri;
4682         XCNST char *msg;
4683         va_dcl
4684 # endif
4685 {
4686         int i;
4687         char buf[SYSLOG_BUFSIZE];
4688         VA_LOCAL_DECL;
4689
4690         VA_START(msg);
4691         vsnprintf(buf, sizeof buf, msg, ap);
4692         VA_END;
4693
4694         for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; )
4695                 continue;
4696 }
4697
4698 # undef CAST
4699 #endif
4700 \f/*
4701 **  LOCAL_HOSTNAME_LENGTH
4702 **
4703 **      This is required to get sendmail to compile against BIND 4.9.x
4704 **      on Ultrix.
4705 */
4706
4707 #if defined(ultrix) && NAMED_BIND
4708
4709 # include <resolv.h>
4710 # if __RES >= 19931104 && __RES < 19950621
4711
4712 int
4713 local_hostname_length(hostname)
4714         char *hostname;
4715 {
4716         int len_host, len_domain;
4717
4718         if (!*_res.defdname)
4719                 res_init();
4720         len_host = strlen(hostname);
4721         len_domain = strlen(_res.defdname);
4722         if (len_host > len_domain &&
4723             (strcasecmp(hostname + len_host - len_domain,_res.defdname) == 0) &&
4724             hostname[len_host - len_domain - 1] == '.')
4725                 return len_host - len_domain - 1;
4726         else
4727                 return 0;
4728 }
4729
4730 # endif
4731 #endif
4732 \f/*
4733 **  Compile-Time options
4734 */
4735
4736 char    *CompileOptions[] =
4737 {
4738 #ifdef HESIOD
4739         "HESIOD",
4740 #endif
4741 #if HES_GETMAILHOST
4742         "HES_GETMAILHOST",
4743 #endif
4744 #ifdef LDAPMAP
4745         "LDAPMAP",
4746 #endif
4747 #ifdef MAP_REGEX
4748         "MAP_REGEX",
4749 #endif
4750 #if LOG
4751         "LOG",
4752 #endif
4753 #if MATCHGECOS
4754         "MATCHGECOS",
4755 #endif
4756 #if MIME7TO8
4757         "MIME7TO8",
4758 #endif
4759 #if MIME8TO7
4760         "MIME8TO7",
4761 #endif
4762 #if NAMED_BIND
4763         "NAMED_BIND",
4764 #endif
4765 #ifdef NDBM
4766         "NDBM",
4767 #endif
4768 #if NETINET
4769         "NETINET",
4770 #endif
4771 #if NETINFO
4772         "NETINFO",
4773 #endif
4774 #if NETISO
4775         "NETISO",
4776 #endif
4777 #if NETNS
4778         "NETNS",
4779 #endif
4780 #if NETUNIX
4781         "NETUNIX",
4782 #endif
4783 #if NETX25
4784         "NETX25",
4785 #endif
4786 #ifdef NEWDB
4787         "NEWDB",
4788 #endif
4789 #ifdef NIS
4790         "NIS",
4791 #endif
4792 #ifdef NISPLUS
4793         "NISPLUS",
4794 #endif
4795 #if QUEUE
4796         "QUEUE",
4797 #endif
4798 #if SCANF
4799         "SCANF",
4800 #endif
4801 #if SMTP
4802         "SMTP",
4803 #endif
4804 #if SMTPDEBUG
4805         "SMTPDEBUG",
4806 #endif
4807 #ifdef SUID_ROOT_FILES_OK
4808         "SUID_ROOT_FILES_OK",
4809 #endif
4810 #if TCPWRAPPERS
4811         "TCPWRAPPERS",
4812 #endif
4813 #if USERDB
4814         "USERDB",
4815 #endif
4816 #if XDEBUG
4817         "XDEBUG",
4818 #endif
4819 #ifdef XLA
4820         "XLA",
4821 #endif
4822         NULL
4823 };
4824
4825
4826 /*
4827 **  OS compile options.
4828 */
4829
4830 char    *OsCompileOptions[] =
4831 {
4832 #if BOGUS_O_EXCL
4833         "BOGUS_O_EXCL",
4834 #endif
4835 #if HASFCHMOD
4836         "HASFCHMOD",
4837 #endif
4838 #if HASFLOCK
4839         "HASFLOCK",
4840 #endif
4841 #if HASGETDTABLESIZE
4842         "HASGETDTABLESIZE",
4843 #endif
4844 #if HASGETUSERSHELL
4845         "HASGETUSERSHELL",
4846 #endif
4847 #if HASINITGROUPS
4848         "HASINITGROUPS",
4849 #endif
4850 #if HASLSTAT
4851         "HASLSTAT",
4852 #endif
4853 #if HASSETREUID
4854         "HASSETREUID",
4855 #endif
4856 #if HASSETRLIMIT
4857         "HASSETRLIMIT",
4858 #endif
4859 #if HASSETSID
4860         "HASSETSID",
4861 #endif
4862 #if HASSETUSERCONTEXT
4863         "HASSETUSERCONTEXT",
4864 #endif
4865 #if HASSETVBUF
4866         "HASSETVBUF",
4867 #endif
4868 #if HASSNPRINTF
4869         "HASSNPRINTF",
4870 #endif
4871 #if HAS_ST_GEN
4872         "HAS_ST_GEN",
4873 #endif
4874 #if HASSTRERROR
4875         "HASSTRERROR",
4876 #endif
4877 #if HASULIMIT
4878         "HASULIMIT",
4879 #endif
4880 #if HASUNAME
4881         "HASUNAME",
4882 #endif
4883 #if HASUNSETENV
4884         "HASUNSETENV",
4885 #endif
4886 #if HASWAITPID
4887         "HASWAITPID",
4888 #endif
4889 #if IDENTPROTO
4890         "IDENTPROTO",
4891 #endif
4892 #if IP_SRCROUTE
4893         "IP_SRCROUTE",
4894 #endif
4895 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
4896         "LOCK_ON_OPEN",
4897 #endif
4898 #if NEEDFSYNC
4899         "NEEDFSYNC",
4900 #endif
4901 #if NOFTRUNCATE
4902         "NOFTRUNCATE",
4903 #endif
4904 #if RLIMIT_NEEDS_SYS_TIME_H
4905         "RLIMIT_NEEDS_SYS_TIME_H",
4906 #endif
4907 #if SAFENFSPATHCONF
4908         "SAFENFSPATHCONF",
4909 #endif
4910 #if SECUREWARE
4911         "SECUREWARE",
4912 #endif
4913 #if SHARE_V1
4914         "SHARE_V1",
4915 #endif
4916 #if SIOCGIFCONF_IS_BROKEN
4917         "SIOCGIFCONF_IS_BROKEN",
4918 #endif
4919 #if SIOCGIFNUM_IS_BROKEN
4920         "SIOCGIFNUM_IS_BROKEN",
4921 #endif
4922 #if SYS5SETPGRP
4923         "SYS5SETPGRP",
4924 #endif
4925 #if SYSTEM5
4926         "SYSTEM5",
4927 #endif
4928 #if USE_SA_SIGACTION
4929         "USE_SA_SIGACTION",
4930 #endif
4931 #if USE_SIGLONGJMP
4932         "USE_SIGLONGJMP",
4933 #endif
4934 #if USESETEUID
4935         "USESETEUID",
4936 #endif
4937         NULL
4938 };