aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/src/conf.c
diff options
context:
space:
mode:
authorPeter Wemm <peter@FreeBSD.org>1998-08-03 05:56:20 +0000
committerPeter Wemm <peter@FreeBSD.org>1998-08-03 05:56:20 +0000
commitc2aa98e247e56d5266d789dfc9b90b524b0019fe (patch)
tree0772be9f4640bcba9db4a0de9e79a39ad377d80b /contrib/sendmail/src/conf.c
parentff37c899d76b8a843614fb348674c7e94e4f53ce (diff)
downloadsrc-c2aa98e247e56d5266d789dfc9b90b524b0019fe.tar.gz
src-c2aa98e247e56d5266d789dfc9b90b524b0019fe.zip
Import sendmail-8.9.1 (slightly trimmed) onto a fresh branch undervendor/sendmail/8.9.1
src/contrib as per various discussions. I will copy across our changes and then point the Makefiles across once the dust has settled..
Notes
Notes: svn path=/vendor/sendmail/dist/; revision=38032 svn path=/vendor/sendmail/8.9.1/; revision=38034; tag=vendor/sendmail/8.9.1
Diffstat (limited to 'contrib/sendmail/src/conf.c')
-rw-r--r--contrib/sendmail/src/conf.c4798
1 files changed, 4798 insertions, 0 deletions
diff --git a/contrib/sendmail/src/conf.c b/contrib/sendmail/src/conf.c
new file mode 100644
index 000000000000..838cd17d2356
--- /dev/null
+++ b/contrib/sendmail/src/conf.c
@@ -0,0 +1,4798 @@
+/*
+ * Copyright (c) 1998 Sendmail, Inc. All rights reserved.
+ * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved.
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)conf.c 8.431 (Berkeley) 6/25/98";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include "pathnames.h"
+# include <sys/ioctl.h>
+# include <sys/param.h>
+# include <limits.h>
+
+/*
+** CONF.C -- Sendmail Configuration Tables.
+**
+** Defines the configuration of this installation.
+**
+** Configuration Variables:
+** HdrInfo -- a table describing well-known header fields.
+** Each entry has the field name and some flags,
+** which are described in sendmail.h.
+**
+** Notes:
+** I have tried to put almost all the reasonable
+** configuration information into the configuration
+** file read at runtime. My intent is that anything
+** here is a function of the version of UNIX you
+** are running, or is really static -- for example
+** the headers are a superset of widely used
+** protocols. If you find yourself playing with
+** this file too much, you may be making a mistake!
+*/
+
+
+/*
+** Header info table
+** Final (null) entry contains the flags used for any other field.
+**
+** Not all of these are actually handled specially by sendmail
+** at this time. They are included as placeholders, to let
+** you know that "someday" I intend to have sendmail do
+** something with them.
+*/
+
+struct hdrinfo HdrInfo[] =
+{
+ /* originator fields, most to least significant */
+ { "resent-sender", H_FROM|H_RESENT },
+ { "resent-from", H_FROM|H_RESENT },
+ { "resent-reply-to", H_FROM|H_RESENT },
+ { "sender", H_FROM },
+ { "from", H_FROM },
+ { "reply-to", H_FROM },
+ { "errors-to", H_FROM|H_ERRORSTO },
+ { "full-name", H_ACHECK },
+ { "return-receipt-to", H_RECEIPTTO },
+
+ /* destination fields */
+ { "to", H_RCPT },
+ { "resent-to", H_RCPT|H_RESENT },
+ { "cc", H_RCPT },
+ { "resent-cc", H_RCPT|H_RESENT },
+ { "bcc", H_RCPT|H_BCC },
+ { "resent-bcc", H_RCPT|H_BCC|H_RESENT },
+ { "apparently-to", H_RCPT },
+
+ /* message identification and control */
+ { "message-id", 0 },
+ { "resent-message-id", H_RESENT },
+ { "message", H_EOH },
+ { "text", H_EOH },
+
+ /* date fields */
+ { "date", 0 },
+ { "resent-date", H_RESENT },
+
+ /* trace fields */
+ { "received", H_TRACE|H_FORCE },
+ { "x400-received", H_TRACE|H_FORCE },
+ { "via", H_TRACE|H_FORCE },
+ { "mail-from", H_TRACE|H_FORCE },
+
+ /* miscellaneous fields */
+ { "comments", H_FORCE|H_ENCODABLE },
+ { "return-path", H_FORCE|H_ACHECK },
+ { "content-transfer-encoding", H_CTE },
+ { "content-type", H_CTYPE },
+ { "content-length", H_ACHECK },
+ { "subject", H_ENCODABLE },
+
+ { NULL, 0 }
+};
+
+
+
+/*
+** Privacy values
+*/
+
+struct prival PrivacyValues[] =
+{
+ { "public", PRIV_PUBLIC },
+ { "needmailhelo", PRIV_NEEDMAILHELO },
+ { "needexpnhelo", PRIV_NEEDEXPNHELO },
+ { "needvrfyhelo", PRIV_NEEDVRFYHELO },
+ { "noexpn", PRIV_NOEXPN },
+ { "novrfy", PRIV_NOVRFY },
+ { "restrictmailq", PRIV_RESTRICTMAILQ },
+ { "restrictqrun", PRIV_RESTRICTQRUN },
+ { "noetrn", PRIV_NOETRN },
+ { "noverb", PRIV_NOVERB },
+ { "authwarnings", PRIV_AUTHWARNINGS },
+ { "noreceipts", PRIV_NORECEIPTS },
+ { "goaway", PRIV_GOAWAY },
+ { NULL, 0 }
+};
+
+/*
+** DontBlameSendmail values
+*/
+struct dbsval DontBlameSendmailValues[] =
+{
+ { "safe", DBS_SAFE },
+ { "assumesafechown", DBS_ASSUMESAFECHOWN },
+ { "groupwritabledirpathsafe", DBS_GROUPWRITABLEDIRPATHSAFE },
+ { "groupwritableforwardfilesafe",
+ DBS_GROUPWRITABLEFORWARDFILESAFE },
+ { "groupwritableincludefilesafe",
+ DBS_GROUPWRITABLEINCLUDEFILESAFE },
+ { "groupwritablealiasfile", DBS_GROUPWRITABLEALIASFILE },
+ { "worldwritablealiasfile", DBS_WORLDWRITABLEALIASFILE },
+ { "forwardfileinunsafedirpath", DBS_FORWARDFILEINUNSAFEDIRPATH },
+ { "includefileinunsafedirpath", DBS_INCLUDEFILEINUNSAFEDIRPATH },
+ { "mapinunsafedirpath", DBS_MAPINUNSAFEDIRPATH },
+ { "linkedaliasfileinwritabledir",
+ DBS_LINKEDALIASFILEINWRITABLEDIR },
+ { "linkedclassfileinwritabledir",
+ DBS_LINKEDCLASSFILEINWRITABLEDIR },
+ { "linkedforwardfileinwritabledir",
+ DBS_LINKEDFORWARDFILEINWRITABLEDIR },
+ { "linkedincludefileinwritabledir",
+ DBS_LINKEDINCLUDEFILEINWRITABLEDIR },
+ { "linkedmapinwritabledir", DBS_LINKEDMAPINWRITABLEDIR },
+ { "linkedserviceswitchfileinwritabledir",
+ DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR },
+ { "filedeliverytohardlink", DBS_FILEDELIVERYTOHARDLINK },
+ { "filedeliverytosymlink", DBS_FILEDELIVERYTOSYMLINK },
+ { "writemaptohardlink", DBS_WRITEMAPTOHARDLINK },
+ { "writemaptosymlink", DBS_WRITEMAPTOSYMLINK },
+ { "writestatstohardlink", DBS_WRITESTATSTOHARDLINK },
+ { "writestatstosymlink", DBS_WRITESTATSTOSYMLINK },
+ { "forwardfileingroupwritabledirpath",
+ DBS_FORWARDFILEINGROUPWRITABLEDIRPATH },
+ { "includefileingroupwritabledirpath",
+ DBS_INCLUDEFILEINGROUPWRITABLEDIRPATH },
+ { "classfileinunsafedirpath", DBS_CLASSFILEINUNSAFEDIRPATH },
+ { "errorheaderinunsafedirpath", DBS_ERRORHEADERINUNSAFEDIRPATH },
+ { "helpfileinunsafedirpath", DBS_HELPFILEINUNSAFEDIRPATH },
+ { "forwardfileinunsafedirpathsafe",
+ DBS_FORWARDFILEINUNSAFEDIRPATHSAFE },
+ { "includefileinunsafedirpathsafe",
+ DBS_INCLUDEFILEINUNSAFEDIRPATHSAFE },
+ { "runprograminunsafedirpath", DBS_RUNPROGRAMINUNSAFEDIRPATH },
+ { "runwritableprogram", DBS_RUNWRITABLEPROGRAM },
+ { NULL, 0 }
+};
+
+
+/*
+** Miscellaneous stuff.
+*/
+
+int DtableSize = 50; /* max open files; reset in 4.2bsd */
+ /*
+** SETDEFAULTS -- set default values
+**
+** Because of the way freezing is done, these must be initialized
+** using direct code.
+**
+** Parameters:
+** e -- the default envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Initializes a bunch of global variables to their
+** default values.
+*/
+
+#define MINUTES * 60
+#define HOURS * 60 MINUTES
+#define DAYS * 24 HOURS
+
+#ifndef _PATH_VARTMP
+# define _PATH_VARTMP "/usr/tmp/"
+#endif
+
+#ifndef MAXRULERECURSION
+# define MAXRULERECURSION 50 /* max ruleset recursion depth */
+#endif
+
+void
+setdefaults(e)
+ register ENVELOPE *e;
+{
+ int i;
+ struct passwd *pw;
+ char buf[MAXNAME];
+ extern void inittimeouts __P((char *));
+ extern void setdefuser __P((void));
+ extern void setupmaps __P((void));
+ extern void setupmailers __P((void));
+ extern void setupheaders __P((void));
+
+ SpaceSub = ' '; /* option B */
+ QueueLA = 8; /* option x */
+ RefuseLA = 12; /* option X */
+ WkRecipFact = 30000L; /* option y */
+ WkClassFact = 1800L; /* option z */
+ WkTimeFact = 90000L; /* option Z */
+ QueueFactor = WkRecipFact * 20; /* option q */
+ FileMode = (RealUid != geteuid()) ? 0644 : 0600;
+ /* option F */
+
+ if (((pw = getpwnam("mailnull")) != NULL && pw->pw_uid != 0) ||
+ ((pw = getpwnam("sendmail")) != NULL && pw->pw_uid != 0) ||
+ ((pw = getpwnam("daemon")) != NULL && pw->pw_uid != 0))
+ {
+ DefUid = pw->pw_uid; /* option u */
+ DefGid = pw->pw_gid; /* option g */
+ DefUser = newstr(pw->pw_name);
+ }
+ else
+ {
+ DefUid = 1; /* option u */
+ DefGid = 1; /* option g */
+ setdefuser();
+ }
+ TrustedFileUid = 0;
+ if (tTd(37, 4))
+ printf("setdefaults: DefUser=%s, DefUid=%d, DefGid=%d\n",
+ DefUser != NULL ? DefUser : "<1:1>",
+ (int) DefUid, (int) DefGid);
+ CheckpointInterval = 10; /* option C */
+ MaxHopCount = 25; /* option h */
+ e->e_sendmode = SM_FORK; /* option d */
+ e->e_errormode = EM_PRINT; /* option e */
+ SevenBitInput = FALSE; /* option 7 */
+ MaxMciCache = 1; /* option k */
+ MciCacheTimeout = 5 MINUTES; /* option K */
+ LogLevel = 9; /* option L */
+ inittimeouts(NULL); /* option r */
+ PrivacyFlags = PRIV_PUBLIC; /* option p */
+ DontBlameSendmail = DBS_SAFE; /* DontBlameSendmail option */
+#if MIME8TO7
+ MimeMode = MM_CVTMIME|MM_PASS8BIT; /* option 8 */
+#else
+ MimeMode = MM_PASS8BIT;
+#endif
+ for (i = 0; i < MAXTOCLASS; i++)
+ {
+ TimeOuts.to_q_return[i] = 5 DAYS; /* option T */
+ TimeOuts.to_q_warning[i] = 0; /* option T */
+ }
+ ServiceSwitchFile = "/etc/service.switch";
+ ServiceCacheMaxAge = (time_t) 10;
+ HostsFile = _PATH_HOSTS;
+ PidFile = newstr(_PATH_SENDMAILPID);
+ MustQuoteChars = "@,;:\\()[].'";
+ MciInfoTimeout = 30 MINUTES;
+ MaxRuleRecursion = MAXRULERECURSION;
+ MaxAliasRecursion = 10;
+ MaxMacroRecursion = 10;
+ ColonOkInAddr = TRUE;
+ DontLockReadFiles = TRUE;
+ DoubleBounceAddr = "postmaster";
+ snprintf(buf, sizeof buf, "%s%sdead.letter",
+ _PATH_VARTMP,
+ _PATH_VARTMP[sizeof _PATH_VARTMP - 2] == '/' ? "" : "/");
+ DeadLetterDrop = newstr(buf);
+#ifdef HESIOD_INIT
+ HesiodContext = NULL;
+#endif
+ setupmaps();
+ setupmailers();
+ setupheaders();
+}
+
+
+/*
+** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
+*/
+
+void
+setdefuser()
+{
+ struct passwd *defpwent;
+ static char defuserbuf[40];
+
+ DefUser = defuserbuf;
+ defpwent = sm_getpwuid(DefUid);
+ snprintf(defuserbuf, sizeof defuserbuf, "%s",
+ defpwent == NULL ? "nobody" : defpwent->pw_name);
+ if (tTd(37, 4))
+ printf("setdefuser: DefUid=%d, DefUser=%s\n",
+ (int) DefUid, DefUser);
+}
+ /*
+** SETUPMAILERS -- initialize default mailers
+*/
+
+void
+setupmailers()
+{
+ char buf[100];
+ extern void makemailer __P((char *));
+
+ strcpy(buf, "prog, P=/bin/sh, F=lsoDq9, T=DNS/RFC822/X-Unix, A=sh -c \201u");
+ makemailer(buf);
+
+ strcpy(buf, "*file*, P=[FILE], F=lsDFMPEouq9, T=DNS/RFC822/X-Unix, A=FILE \201u");
+ makemailer(buf);
+
+ strcpy(buf, "*include*, P=/dev/null, F=su, A=INCLUDE \201u");
+ makemailer(buf);
+}
+ /*
+** SETUPMAPS -- set up map classes
+*/
+
+#define MAPDEF(name, ext, flags, parse, open, close, lookup, store) \
+ { \
+ extern bool parse __P((MAP *, char *)); \
+ extern bool open __P((MAP *, int)); \
+ extern void close __P((MAP *)); \
+ extern char *lookup __P((MAP *, char *, char **, int *)); \
+ extern void store __P((MAP *, char *, char *)); \
+ s = stab(name, ST_MAPCLASS, ST_ENTER); \
+ s->s_mapclass.map_cname = name; \
+ s->s_mapclass.map_ext = ext; \
+ s->s_mapclass.map_cflags = flags; \
+ s->s_mapclass.map_parse = parse; \
+ s->s_mapclass.map_open = open; \
+ s->s_mapclass.map_close = close; \
+ s->s_mapclass.map_lookup = lookup; \
+ s->s_mapclass.map_store = store; \
+ }
+
+void
+setupmaps()
+{
+ register STAB *s;
+
+#ifdef NEWDB
+ MAPDEF("hash", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
+ map_parseargs, hash_map_open, db_map_close,
+ db_map_lookup, db_map_store);
+
+ MAPDEF("btree", ".db", MCF_ALIASOK|MCF_REBUILDABLE,
+ map_parseargs, bt_map_open, db_map_close,
+ db_map_lookup, db_map_store);
+#endif
+
+#ifdef NDBM
+ MAPDEF("dbm", ".dir", MCF_ALIASOK|MCF_REBUILDABLE,
+ map_parseargs, ndbm_map_open, ndbm_map_close,
+ ndbm_map_lookup, ndbm_map_store);
+#endif
+
+#ifdef NIS
+ MAPDEF("nis", NULL, MCF_ALIASOK,
+ map_parseargs, nis_map_open, null_map_close,
+ nis_map_lookup, null_map_store);
+#endif
+
+#ifdef NISPLUS
+ MAPDEF("nisplus", NULL, MCF_ALIASOK,
+ map_parseargs, nisplus_map_open, null_map_close,
+ nisplus_map_lookup, null_map_store);
+#endif
+#ifdef LDAPMAP
+ MAPDEF("ldapx", NULL, 0,
+ ldap_map_parseargs, ldap_map_open, ldap_map_close,
+ ldap_map_lookup, null_map_store);
+#endif
+
+#ifdef HESIOD
+ MAPDEF("hesiod", NULL, MCF_ALIASOK|MCF_ALIASONLY,
+ map_parseargs, hes_map_open, null_map_close,
+ hes_map_lookup, null_map_store);
+#endif
+
+#if NETINFO
+ MAPDEF("netinfo", NULL, MCF_ALIASOK,
+ map_parseargs, ni_map_open, null_map_close,
+ ni_map_lookup, null_map_store);
+#endif
+
+#if 0
+ MAPDEF("dns", NULL, 0,
+ dns_map_init, null_map_open, null_map_close,
+ dns_map_lookup, null_map_store);
+#endif
+
+#if NAMED_BIND
+ /* best MX DNS lookup */
+ MAPDEF("bestmx", NULL, MCF_OPTFILE,
+ map_parseargs, null_map_open, null_map_close,
+ bestmx_map_lookup, null_map_store);
+#endif
+
+ MAPDEF("host", NULL, 0,
+ host_map_init, null_map_open, null_map_close,
+ host_map_lookup, null_map_store);
+
+ MAPDEF("text", NULL, MCF_ALIASOK,
+ map_parseargs, text_map_open, null_map_close,
+ text_map_lookup, null_map_store);
+
+ MAPDEF("stab", NULL, MCF_ALIASOK|MCF_ALIASONLY,
+ map_parseargs, stab_map_open, null_map_close,
+ stab_map_lookup, stab_map_store);
+
+ MAPDEF("implicit", NULL, MCF_ALIASOK|MCF_ALIASONLY|MCF_REBUILDABLE,
+ map_parseargs, impl_map_open, impl_map_close,
+ impl_map_lookup, impl_map_store);
+
+ /* access to system passwd file */
+ MAPDEF("user", NULL, MCF_OPTFILE,
+ map_parseargs, user_map_open, null_map_close,
+ user_map_lookup, null_map_store);
+
+ /* dequote map */
+ MAPDEF("dequote", NULL, 0,
+ dequote_init, null_map_open, null_map_close,
+ dequote_map, null_map_store);
+
+#ifdef MAP_REGEX
+ MAPDEF("regex", NULL, 0,
+ regex_map_init, null_map_open, null_map_close,
+ regex_map_lookup, null_map_store);
+#endif
+
+#if USERDB
+ /* user database */
+ MAPDEF("userdb", ".db", 0,
+ map_parseargs, null_map_open, null_map_close,
+ udb_map_lookup, null_map_store);
+#endif
+
+ /* arbitrary programs */
+ MAPDEF("program", NULL, MCF_ALIASOK,
+ map_parseargs, null_map_open, null_map_close,
+ prog_map_lookup, null_map_store);
+
+ /* sequenced maps */
+ MAPDEF("sequence", NULL, MCF_ALIASOK,
+ seq_map_parse, null_map_open, null_map_close,
+ seq_map_lookup, seq_map_store);
+
+ /* switched interface to sequenced maps */
+ MAPDEF("switch", NULL, MCF_ALIASOK,
+ map_parseargs, switch_map_open, null_map_close,
+ seq_map_lookup, seq_map_store);
+
+ /* null map lookup -- really for internal use only */
+ MAPDEF("null", NULL, MCF_ALIASOK|MCF_OPTFILE,
+ map_parseargs, null_map_open, null_map_close,
+ null_map_lookup, null_map_store);
+
+#if _FFR_MAP_SYSLOG
+ /* syslog map -- logs information to syslog */
+ MAPDEF("syslog", NULL, 0,
+ syslog_map_parseargs, null_map_open, null_map_close,
+ syslog_map_lookup, null_map_store);
+#endif
+}
+
+#undef MAPDEF
+ /*
+** INITHOSTMAPS -- initial host-dependent maps
+**
+** This should act as an interface to any local service switch
+** provided by the host operating system.
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Should define maps "host" and "users" as necessary
+** for this OS. If they are not defined, they will get
+** a default value later. It should check to make sure
+** they are not defined first, since it's possible that
+** the config file has provided an override.
+*/
+
+void
+inithostmaps()
+{
+ register int i;
+ int nmaps;
+ char *maptype[MAXMAPSTACK];
+ short mapreturn[MAXMAPACTIONS];
+ char buf[MAXLINE];
+
+ /*
+ ** Set up default hosts maps.
+ */
+
+#if 0
+ nmaps = switch_map_find("hosts", maptype, mapreturn);
+ for (i = 0; i < nmaps; i++)
+ {
+ if (strcmp(maptype[i], "files") == 0 &&
+ stab("hosts.files", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.files text -k 0 -v 1 /etc/hosts");
+ (void) makemapentry(buf);
+ }
+#if NAMED_BIND
+ else if (strcmp(maptype[i], "dns") == 0 &&
+ stab("hosts.dns", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.dns dns A");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NISPLUS
+ else if (strcmp(maptype[i], "nisplus") == 0 &&
+ stab("hosts.nisplus", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.nisplus nisplus -k name -v address -d hosts.org_dir");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NIS
+ else if (strcmp(maptype[i], "nis") == 0 &&
+ stab("hosts.nis", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.nis nis -d -k 0 -v 1 hosts.byname");
+ (void) makemapentry(buf);
+ }
+#endif
+#if NETINFO
+ else if (strcmp(maptype[i], "netinfo") == 0) &&
+ stab("hosts.netinfo", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "hosts.netinfo netinfo -v name /machines");
+ (void) makemapentry(buf);
+ }
+#endif
+ }
+#endif
+
+ /*
+ ** Make sure we have a host map.
+ */
+
+ if (stab("host", ST_MAP, ST_FIND) == NULL)
+ {
+ /* user didn't initialize: set up host map */
+ strcpy(buf, "host host");
+#if NAMED_BIND
+ if (ConfigLevel >= 2)
+ strcat(buf, " -a.");
+#endif
+ (void) makemapentry(buf);
+ }
+
+ /*
+ ** Set up default aliases maps
+ */
+
+ nmaps = switch_map_find("aliases", maptype, mapreturn);
+ for (i = 0; i < nmaps; i++)
+ {
+ if (strcmp(maptype[i], "files") == 0 &&
+ stab("aliases.files", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.files null");
+ (void) makemapentry(buf);
+ }
+#ifdef NISPLUS
+ else if (strcmp(maptype[i], "nisplus") == 0 &&
+ stab("aliases.nisplus", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.nisplus nisplus -kalias -vexpansion -d mail_aliases.org_dir");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NIS
+ else if (strcmp(maptype[i], "nis") == 0 &&
+ stab("aliases.nis", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.nis nis -d mail.aliases");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NETINFO
+ else if (strcmp(maptype[i], "netinfo") == 0 &&
+ stab("aliases.netinfo", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.netinfo netinfo -z, /aliases");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef HESIOD
+ else if (strcmp(maptype[i], "hesiod") == 0 &&
+ stab("aliases.hesiod", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases.hesiod hesiod aliases");
+ (void) makemapentry(buf);
+ }
+#endif
+ }
+ if (stab("aliases", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "aliases switch aliases");
+ (void) makemapentry(buf);
+ }
+
+#if 0 /* "user" map class is a better choice */
+ /*
+ ** Set up default users maps.
+ */
+
+ nmaps = switch_map_find("passwd", maptype, mapreturn);
+ for (i = 0; i < nmaps; i++)
+ {
+ if (strcmp(maptype[i], "files") == 0 &&
+ stab("users.files", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users.files text -m -z: -k0 -v6 /etc/passwd");
+ (void) makemapentry(buf);
+ }
+#ifdef NISPLUS
+ else if (strcmp(maptype[i], "nisplus") == 0 &&
+ stab("users.nisplus", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users.nisplus nisplus -m -kname -vhome -d passwd.org_dir");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef NIS
+ else if (strcmp(maptype[i], "nis") == 0 &&
+ stab("users.nis", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users.nis nis -m -d passwd.byname");
+ (void) makemapentry(buf);
+ }
+#endif
+#ifdef HESIOD
+ else if (strcmp(maptype[i], "hesiod") == 0) &&
+ stab("users.hesiod", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users.hesiod hesiod");
+ (void) makemapentry(buf);
+ }
+#endif
+ }
+ if (stab("users", ST_MAP, ST_FIND) == NULL)
+ {
+ strcpy(buf, "users switch -m passwd");
+ (void) makemapentry(buf);
+ }
+#endif
+}
+ /*
+** SWITCH_MAP_FIND -- find the list of types associated with a map
+**
+** This is the system-dependent interface to the service switch.
+**
+** Parameters:
+** service -- the name of the service of interest.
+** maptype -- an out-array of strings containing the types
+** of access to use for this service. There can
+** be at most MAXMAPSTACK types for a single service.
+** mapreturn -- an out-array of return information bitmaps
+** for the map.
+**
+** Returns:
+** The number of map types filled in, or -1 for failure.
+*/
+
+#if defined(SOLARIS) || (defined(sony_news) && defined(__svr4))
+# define _USE_SUN_NSSWITCH_
+#endif
+
+#ifdef _USE_SUN_NSSWITCH_
+# include <nsswitch.h>
+#endif
+
+#if defined(ultrix) || (defined(__osf__) && defined(__alpha))
+# define _USE_DEC_SVC_CONF_
+#endif
+
+#ifdef _USE_DEC_SVC_CONF_
+# include <sys/svcinfo.h>
+#endif
+
+int
+switch_map_find(service, maptype, mapreturn)
+ char *service;
+ char *maptype[MAXMAPSTACK];
+ short mapreturn[MAXMAPACTIONS];
+{
+ int svcno;
+
+#ifdef _USE_SUN_NSSWITCH_
+ struct __nsw_switchconfig *nsw_conf;
+ enum __nsw_parse_err pserr;
+ struct __nsw_lookup *lk;
+ static struct __nsw_lookup lkp0 =
+ { "files", {1, 0, 0, 0}, NULL, NULL };
+ static struct __nsw_switchconfig lkp_default =
+ { 0, "sendmail", 3, &lkp0 };
+
+ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+ mapreturn[svcno] = 0;
+
+ if ((nsw_conf = __nsw_getconfig(service, &pserr)) == NULL)
+ lk = lkp_default.lookups;
+ else
+ lk = nsw_conf->lookups;
+ svcno = 0;
+ while (lk != NULL)
+ {
+ maptype[svcno] = lk->service_name;
+ if (lk->actions[__NSW_NOTFOUND] == __NSW_RETURN)
+ mapreturn[MA_NOTFOUND] |= 1 << svcno;
+ if (lk->actions[__NSW_TRYAGAIN] == __NSW_RETURN)
+ mapreturn[MA_TRYAGAIN] |= 1 << svcno;
+ if (lk->actions[__NSW_UNAVAIL] == __NSW_RETURN)
+ mapreturn[MA_TRYAGAIN] |= 1 << svcno;
+ svcno++;
+ lk = lk->next;
+ }
+ return svcno;
+#endif
+
+#ifdef _USE_DEC_SVC_CONF_
+ struct svcinfo *svcinfo;
+ int svc;
+
+ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+ mapreturn[svcno] = 0;
+
+ svcinfo = getsvc();
+ if (svcinfo == NULL)
+ goto punt;
+ if (strcmp(service, "hosts") == 0)
+ svc = SVC_HOSTS;
+ else if (strcmp(service, "aliases") == 0)
+ svc = SVC_ALIASES;
+ else if (strcmp(service, "passwd") == 0)
+ svc = SVC_PASSWD;
+ else
+ return -1;
+ for (svcno = 0; svcno < SVC_PATHSIZE; svcno++)
+ {
+ switch (svcinfo->svcpath[svc][svcno])
+ {
+ case SVC_LOCAL:
+ maptype[svcno] = "files";
+ break;
+
+ case SVC_YP:
+ maptype[svcno] = "nis";
+ break;
+
+ case SVC_BIND:
+ maptype[svcno] = "dns";
+ break;
+
+#ifdef SVC_HESIOD
+ case SVC_HESIOD:
+ maptype[svcno] = "hesiod";
+ break;
+#endif
+
+ case SVC_LAST:
+ return svcno;
+ }
+ }
+ return svcno;
+#endif
+
+#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
+ /*
+ ** Fall-back mechanism.
+ */
+
+ STAB *st;
+ time_t now = curtime();
+
+ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+ mapreturn[svcno] = 0;
+
+ if ((now - ServiceCacheTime) > (time_t) ServiceCacheMaxAge)
+ {
+ /* (re)read service switch */
+ register FILE *fp;
+ int sff = SFF_REGONLY|SFF_OPENASROOT|SFF_NOLOCK;
+
+ if (!bitset(DBS_LINKEDSERVICESWITCHFILEINWRITABLEDIR, DontBlameSendmail))
+ sff |= SFF_NOWLINK;
+
+ if (ConfigFileRead)
+ ServiceCacheTime = now;
+ fp = safefopen(ServiceSwitchFile, O_RDONLY, 0, sff);
+ if (fp != NULL)
+ {
+ char buf[MAXLINE];
+
+ while (fgets(buf, sizeof buf, fp) != NULL)
+ {
+ register char *p;
+
+ p = strpbrk(buf, "#\n");
+ if (p != NULL)
+ *p = '\0';
+ p = strpbrk(buf, " \t");
+ if (p != NULL)
+ *p++ = '\0';
+ if (buf[0] == '\0')
+ continue;
+ while (isspace(*p))
+ p++;
+ if (*p == '\0')
+ continue;
+
+ /*
+ ** Find/allocate space for this service entry.
+ ** Space for all of the service strings
+ ** are allocated at once. This means
+ ** that we only have to free the first
+ ** one to free all of them.
+ */
+
+ st = stab(buf, ST_SERVICE, ST_ENTER);
+ if (st->s_service[0] != NULL)
+ free((void *) st->s_service[0]);
+ p = newstr(p);
+ for (svcno = 0; svcno < MAXMAPSTACK; )
+ {
+ if (*p == '\0')
+ break;
+ st->s_service[svcno++] = p;
+ p = strpbrk(p, " \t");
+ if (p == NULL)
+ break;
+ *p++ = '\0';
+ while (isspace(*p))
+ p++;
+ }
+ if (svcno < MAXMAPSTACK)
+ st->s_service[svcno] = NULL;
+ }
+ fclose(fp);
+ }
+ }
+
+ /* look up entry in cache */
+ st = stab(service, ST_SERVICE, ST_FIND);
+ if (st != NULL && st->s_service[0] != NULL)
+ {
+ /* extract data */
+ svcno = 0;
+ while (svcno < MAXMAPSTACK)
+ {
+ maptype[svcno] = st->s_service[svcno];
+ if (maptype[svcno++] == NULL)
+ break;
+ }
+ return --svcno;
+ }
+#endif
+
+#if !defined(_USE_SUN_NSSWITCH_)
+ /* if the service file doesn't work, use an absolute fallback */
+# ifdef _USE_DEC_SVC_CONF_
+ punt:
+# endif
+ for (svcno = 0; svcno < MAXMAPACTIONS; svcno++)
+ mapreturn[svcno] = 0;
+ svcno = 0;
+ if (strcmp(service, "aliases") == 0)
+ {
+ maptype[svcno++] = "files";
+# ifdef AUTO_NIS_ALIASES
+# ifdef NISPLUS
+ maptype[svcno++] = "nisplus";
+# endif
+# ifdef NIS
+ maptype[svcno++] = "nis";
+# endif
+# endif
+ return svcno;
+ }
+ if (strcmp(service, "hosts") == 0)
+ {
+# if NAMED_BIND
+ maptype[svcno++] = "dns";
+# else
+# if defined(sun) && !defined(BSD)
+ /* SunOS */
+ maptype[svcno++] = "nis";
+# endif
+# endif
+ maptype[svcno++] = "files";
+ return svcno;
+ }
+ return -1;
+#endif
+}
+ /*
+** USERNAME -- return the user id of the logged in user.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** The login name of the logged in user.
+**
+** Side Effects:
+** none.
+**
+** Notes:
+** The return value is statically allocated.
+*/
+
+char *
+username()
+{
+ static char *myname = NULL;
+ extern char *getlogin();
+ register struct passwd *pw;
+
+ /* cache the result */
+ if (myname == NULL)
+ {
+ myname = getlogin();
+ if (myname == NULL || myname[0] == '\0')
+ {
+ pw = sm_getpwuid(RealUid);
+ if (pw != NULL)
+ myname = newstr(pw->pw_name);
+ }
+ else
+ {
+ uid_t uid = RealUid;
+
+ myname = newstr(myname);
+ if ((pw = sm_getpwnam(myname)) == NULL ||
+ (uid != 0 && uid != pw->pw_uid))
+ {
+ pw = sm_getpwuid(uid);
+ if (pw != NULL)
+ myname = newstr(pw->pw_name);
+ }
+ }
+ if (myname == NULL || myname[0] == '\0')
+ {
+ syserr("554 Who are you?");
+ myname = "postmaster";
+ }
+ }
+
+ return (myname);
+}
+ /*
+** TTYPATH -- Get the path of the user's tty
+**
+** Returns the pathname of the user's tty. Returns NULL if
+** the user is not logged in or if s/he has write permission
+** denied.
+**
+** Parameters:
+** none
+**
+** Returns:
+** pathname of the user's tty.
+** NULL if not logged in or write permission denied.
+**
+** Side Effects:
+** none.
+**
+** WARNING:
+** Return value is in a local buffer.
+**
+** Called By:
+** savemail
+*/
+
+char *
+ttypath()
+{
+ struct stat stbuf;
+ register char *pathn;
+ extern char *ttyname();
+ extern char *getlogin();
+
+ /* compute the pathname of the controlling tty */
+ if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
+ (pathn = ttyname(0)) == NULL)
+ {
+ errno = 0;
+ return (NULL);
+ }
+
+ /* see if we have write permission */
+ if (stat(pathn, &stbuf) < 0 || !bitset(S_IWOTH, stbuf.st_mode))
+ {
+ errno = 0;
+ return (NULL);
+ }
+
+ /* see if the user is logged in */
+ if (getlogin() == NULL)
+ return (NULL);
+
+ /* looks good */
+ return (pathn);
+}
+ /*
+** CHECKCOMPAT -- check for From and To person compatible.
+**
+** This routine can be supplied on a per-installation basis
+** to determine whether a person is allowed to send a message.
+** This allows restriction of certain types of internet
+** forwarding or registration of users.
+**
+** If the hosts are found to be incompatible, an error
+** message should be given using "usrerr" and an EX_ code
+** should be returned. You can also set to->q_status to
+** a DSN-style status code.
+**
+** EF_NO_BODY_RETN can be set in e->e_flags to suppress the
+** body during the return-to-sender function; this should be done
+** on huge messages. This bit may already be set by the ESMTP
+** protocol.
+**
+** Parameters:
+** to -- the person being sent to.
+**
+** Returns:
+** an exit status
+**
+** Side Effects:
+** none (unless you include the usrerr stuff)
+*/
+
+int
+checkcompat(to, e)
+ register ADDRESS *to;
+ register ENVELOPE *e;
+{
+# ifdef lint
+ if (to == NULL)
+ to++;
+# endif /* lint */
+
+ if (tTd(49, 1))
+ printf("checkcompat(to=%s, from=%s)\n",
+ to->q_paddr, e->e_from.q_paddr);
+
+# ifdef EXAMPLE_CODE
+ /* this code is intended as an example only */
+ register STAB *s;
+
+ s = stab("arpa", ST_MAILER, ST_FIND);
+ if (s != NULL && strcmp(e->e_from.q_mailer->m_name, "local") != 0 &&
+ to->q_mailer == s->s_mailer)
+ {
+ usrerr("553 No ARPA mail through this machine: see your system administration");
+ /* e->e_flags |= EF_NO_BODY_RETN; to supress body on return */
+ to->q_status = "5.7.1";
+ return (EX_UNAVAILABLE);
+ }
+# endif /* EXAMPLE_CODE */
+ return (EX_OK);
+}
+ /*
+** SETSIGNAL -- set a signal handler
+**
+** This is essentially old BSD "signal(3)".
+*/
+
+sigfunc_t
+setsignal(sig, handler)
+ int sig;
+ sigfunc_t handler;
+{
+#if defined(SYS5SIGNALS) || defined(BSD4_3)
+# ifdef BSD4_3
+ return signal(sig, handler);
+# else
+ return sigset(sig, handler);
+# endif
+#else
+ struct sigaction n, o;
+
+ bzero(&n, sizeof n);
+# if USE_SA_SIGACTION
+ n.sa_sigaction = (void(*)(int, siginfo_t *, void *)) handler;
+ n.sa_flags = SA_RESTART|SA_SIGINFO;
+# else
+ n.sa_handler = handler;
+# ifdef SA_RESTART
+ n.sa_flags = SA_RESTART;
+# endif
+# endif
+ if (sigaction(sig, &n, &o) < 0)
+ return SIG_ERR;
+ return o.sa_handler;
+#endif
+}
+ /*
+** BLOCKSIGNAL -- hold a signal to prevent delivery
+**
+** Parameters:
+** sig -- the signal to block.
+**
+** Returns:
+** 1 signal was previously blocked
+** 0 signal was not previously blocked
+** -1 on failure.
+*/
+
+int
+blocksignal(sig)
+ int sig;
+{
+#ifdef BSD4_3
+# ifndef sigmask
+# define sigmask(s) (1 << ((s) - 1))
+# endif
+ return (sigblock(sigmask(sig)) & sigmask(sig)) != 0;
+#else
+# ifdef ALTOS_SYSTEM_V
+ sigfunc_t handler;
+
+ handler = sigset(sig, SIG_HOLD);
+ if (handler == SIG_ERR)
+ return -1;
+ else
+ return handler == SIG_HOLD;
+# else
+ sigset_t sset, oset;
+
+ sigemptyset(&sset);
+ sigaddset(&sset, sig);
+ if (sigprocmask(SIG_BLOCK, &sset, &oset) < 0)
+ return -1;
+ else
+ return sigismember(&oset, sig);
+# endif
+#endif
+}
+ /*
+** RELEASESIGNAL -- release a held signal
+**
+** Parameters:
+** sig -- the signal to release.
+**
+** Returns:
+** 1 signal was previously blocked
+** 0 signal was not previously blocked
+** -1 on failure.
+*/
+
+int
+releasesignal(sig)
+ int sig;
+{
+#ifdef BSD4_3
+ return (sigsetmask(sigblock(0) & ~sigmask(sig)) & sigmask(sig)) != 0;
+#else
+# ifdef ALTOS_SYSTEM_V
+ sigfunc_t handler;
+
+ handler = sigset(sig, SIG_HOLD);
+ if (sigrelse(sig) < 0)
+ return -1;
+ else
+ return handler == SIG_HOLD;
+# else
+ sigset_t sset, oset;
+
+ sigemptyset(&sset);
+ sigaddset(&sset, sig);
+ if (sigprocmask(SIG_UNBLOCK, &sset, &oset) < 0)
+ return -1;
+ else
+ return sigismember(&oset, sig);
+# endif
+#endif
+}
+ /*
+** HOLDSIGS -- arrange to hold all signals
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Arranges that signals are held.
+*/
+
+void
+holdsigs()
+{
+}
+ /*
+** RLSESIGS -- arrange to release all signals
+**
+** This undoes the effect of holdsigs.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Arranges that signals are released.
+*/
+
+void
+rlsesigs()
+{
+}
+ /*
+** INIT_MD -- do machine dependent initializations
+**
+** Systems that have global modes that should be set should do
+** them here rather than in main.
+*/
+
+#ifdef _AUX_SOURCE
+# include <compat.h>
+#endif
+
+#if SHARE_V1
+# include <shares.h>
+#endif
+
+void
+init_md(argc, argv)
+ int argc;
+ char **argv;
+{
+#ifdef _AUX_SOURCE
+ setcompat(getcompat() | COMPAT_BSDPROT);
+#endif
+
+#ifdef SUN_EXTENSIONS
+ init_md_sun();
+#endif
+
+#if _CONVEX_SOURCE
+ /* keep gethostby*() from stripping the local domain name */
+ set_domain_trim_off();
+#endif
+#ifdef __QNX__
+ /*
+ ** Due to QNX's network distributed nature, you can target a tcpip
+ ** stack on a different node in the qnx network; this patch lets
+ ** this feature work. The __sock_locate() must be done before the
+ ** environment is clear.
+ */
+ __sock_locate();
+#endif
+#if SECUREWARE || defined(_SCO_unix_)
+ set_auth_parameters(argc, argv);
+
+# ifdef _SCO_unix_
+ /*
+ ** This is required for highest security levels (the kernel
+ ** won't let it call set*uid() or run setuid binaries without
+ ** it). It may be necessary on other SECUREWARE systems.
+ */
+
+ if (getluid() == -1)
+ setluid(0);
+# endif
+#endif
+
+#ifdef VENDOR_DEFAULT
+ VendorCode = VENDOR_DEFAULT;
+#else
+ VendorCode = VENDOR_BERKELEY;
+#endif
+}
+ /*
+** INIT_VENDOR_MACROS -- vendor-dependent macro initializations
+**
+** Called once, on startup.
+**
+** Parameters:
+** e -- the global envelope.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** vendor-dependent.
+*/
+
+void
+init_vendor_macros(e)
+ register ENVELOPE *e;
+{
+}
+ /*
+** GETLA -- get the current load average
+**
+** This code stolen from la.c.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** The current load average as an integer.
+**
+** Side Effects:
+** none.
+*/
+
+/* try to guess what style of load average we have */
+#define LA_ZERO 1 /* always return load average as zero */
+#define LA_INT 2 /* read kmem for avenrun; interpret as long */
+#define LA_FLOAT 3 /* read kmem for avenrun; interpret as float */
+#define LA_SUBR 4 /* call getloadavg */
+#define LA_MACH 5 /* MACH load averages (as on NeXT boxes) */
+#define LA_SHORT 6 /* read kmem for avenrun; interpret as short */
+#define LA_PROCSTR 7 /* read string ("1.17") from /proc/loadavg */
+#define LA_READKSYM 8 /* SVR4: use MIOC_READKSYM ioctl call */
+#define LA_DGUX 9 /* special DGUX implementation */
+#define LA_HPUX 10 /* special HPUX implementation */
+#define LA_IRIX6 11 /* special IRIX 6.2 implementation */
+#define LA_KSTAT 12 /* special Solaris kstat(3k) implementation */
+#define LA_DEVSHORT 13 /* read short from a device */
+#define LA_ALPHAOSF 14 /* Digital UNIX (OSF/1 on Alpha) table() call */
+
+/* do guesses based on general OS type */
+#ifndef LA_TYPE
+# define LA_TYPE LA_ZERO
+#endif
+
+#ifndef FSHIFT
+# if defined(unixpc)
+# define FSHIFT 5
+# endif
+
+# if defined(__alpha) || defined(IRIX)
+# define FSHIFT 10
+# endif
+
+#endif
+
+#ifndef FSHIFT
+# define FSHIFT 8
+#endif
+
+#ifndef FSCALE
+# define FSCALE (1 << FSHIFT)
+#endif
+
+#ifndef LA_AVENRUN
+# ifdef SYSTEM5
+# define LA_AVENRUN "avenrun"
+# else
+# define LA_AVENRUN "_avenrun"
+# endif
+#endif
+
+/* _PATH_KMEM should be defined in <paths.h> */
+#ifndef _PATH_KMEM
+# define _PATH_KMEM "/dev/kmem"
+#endif
+
+#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT)
+
+#include <nlist.h>
+
+/* _PATH_UNIX should be defined in <paths.h> */
+#ifndef _PATH_UNIX
+# if defined(SYSTEM5)
+# define _PATH_UNIX "/unix"
+# else
+# define _PATH_UNIX "/vmunix"
+# endif
+#endif
+
+#ifdef _AUX_SOURCE
+struct nlist Nl[2];
+#else
+struct nlist Nl[] =
+{
+ { LA_AVENRUN },
+ { 0 },
+};
+#endif
+#define X_AVENRUN 0
+
+int
+getla()
+{
+ static int kmem = -1;
+#if LA_TYPE == LA_INT
+ long avenrun[3];
+#else
+# if LA_TYPE == LA_SHORT
+ short avenrun[3];
+# else
+ double avenrun[3];
+# endif
+#endif
+ extern int errno;
+ extern off_t lseek();
+
+ if (kmem < 0)
+ {
+#ifdef _AUX_SOURCE
+ strcpy(Nl[X_AVENRUN].n_name, LA_AVENRUN);
+ Nl[1].n_name[0] = '\0';
+#endif
+
+#if defined(_AIX3) || defined(_AIX4)
+ if (knlist(Nl, 1, sizeof Nl[0]) < 0)
+#else
+ if (nlist(_PATH_UNIX, Nl) < 0)
+#endif
+ {
+ if (tTd(3, 1))
+ printf("getla: nlist(%s): %s\n", _PATH_UNIX,
+ errstring(errno));
+ return (-1);
+ }
+ if (Nl[X_AVENRUN].n_value == 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: nlist(%s, %s) ==> 0\n",
+ _PATH_UNIX, LA_AVENRUN);
+ return (-1);
+ }
+#ifdef NAMELISTMASK
+ Nl[X_AVENRUN].n_value &= NAMELISTMASK;
+#endif
+
+ kmem = open(_PATH_KMEM, 0, 0);
+ if (kmem < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: open(/dev/kmem): %s\n",
+ errstring(errno));
+ return (-1);
+ }
+ (void) fcntl(kmem, F_SETFD, 1);
+ }
+ if (tTd(3, 20))
+ printf("getla: symbol address = %#lx\n",
+ (u_long) Nl[X_AVENRUN].n_value);
+ if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, SEEK_SET) == -1 ||
+ read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
+ {
+ /* thank you Ian */
+ if (tTd(3, 1))
+ printf("getla: lseek or read: %s\n", errstring(errno));
+ return (-1);
+ }
+# if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)
+ if (tTd(3, 5))
+ {
+# if LA_TYPE == LA_SHORT
+ printf("getla: avenrun = %d", avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %d, %d", avenrun[1], avenrun[2]);
+# else
+ printf("getla: avenrun = %ld", avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %ld, %ld", avenrun[1], avenrun[2]);
+# endif
+ printf("\n");
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+ return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+# else /* LA_TYPE == LA_FLOAT */
+ if (tTd(3, 5))
+ {
+ printf("getla: avenrun = %g", avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %g, %g", avenrun[1], avenrun[2]);
+ printf("\n");
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] +0.5));
+ return ((int) (avenrun[0] + 0.5));
+# endif
+}
+
+#endif /* LA_TYPE == LA_INT or LA_SHORT or LA_FLOAT */
+
+#if LA_TYPE == LA_READKSYM
+
+# include <sys/ksym.h>
+
+getla()
+{
+ static int kmem = -1;
+ long avenrun[3];
+ extern int errno;
+ struct mioc_rksym mirk;
+
+ if (kmem < 0)
+ {
+ kmem = open("/dev/kmem", 0, 0);
+ if (kmem < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: open(/dev/kmem): %s\n",
+ errstring(errno));
+ return (-1);
+ }
+ (void) fcntl(kmem, F_SETFD, 1);
+ }
+ mirk.mirk_symname = LA_AVENRUN;
+ mirk.mirk_buf = avenrun;
+ mirk.mirk_buflen = sizeof(avenrun);
+ if (ioctl(kmem, MIOC_READKSYM, &mirk) < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: ioctl(MIOC_READKSYM) failed: %s\n",
+ errstring(errno));
+ return -1;
+ }
+ if (tTd(3, 5))
+ {
+ printf("getla: avenrun = %d", avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %d, %d", avenrun[1], avenrun[2]);
+ printf("\n");
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+ return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+}
+
+#endif /* LA_TYPE == LA_READKSYM */
+
+#if LA_TYPE == LA_DGUX
+
+# include <sys/dg_sys_info.h>
+
+int
+getla()
+{
+ struct dg_sys_info_load_info load_info;
+
+ dg_sys_info((long *)&load_info,
+ DG_SYS_INFO_LOAD_INFO_TYPE, DG_SYS_INFO_LOAD_VERSION_0);
+
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (load_info.one_minute + 0.5));
+
+ return((int) (load_info.one_minute + 0.5));
+}
+
+#endif /* LA_TYPE == LA_DGUX */
+
+#if LA_TYPE == LA_HPUX
+
+/* forward declarations to keep gcc from complaining */
+struct pst_dynamic;
+struct pst_status;
+struct pst_static;
+struct pst_vminfo;
+struct pst_diskinfo;
+struct pst_processor;
+struct pst_lv;
+struct pst_swapinfo;
+
+# include <sys/param.h>
+# include <sys/pstat.h>
+
+int
+getla()
+{
+ struct pst_dynamic pstd;
+
+ if (pstat_getdynamic(&pstd, sizeof(struct pst_dynamic),
+ (size_t) 1, 0) == -1)
+ return 0;
+
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (pstd.psd_avg_1_min + 0.5));
+
+ return (int) (pstd.psd_avg_1_min + 0.5);
+}
+
+#endif /* LA_TYPE == LA_HPUX */
+
+#if LA_TYPE == LA_SUBR
+
+int
+getla()
+{
+ double avenrun[3];
+
+ if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) < 0)
+ {
+ if (tTd(3, 1))
+ perror("getla: getloadavg failed:");
+ return (-1);
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] +0.5));
+ return ((int) (avenrun[0] + 0.5));
+}
+
+#endif /* LA_TYPE == LA_SUBR */
+
+#if LA_TYPE == LA_MACH
+
+/*
+** This has been tested on NEXTSTEP release 2.1/3.X.
+*/
+
+#if defined(NX_CURRENT_COMPILER_RELEASE) && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_0
+# include <mach/mach.h>
+#else
+# include <mach.h>
+#endif
+
+int
+getla()
+{
+ processor_set_t default_set;
+ kern_return_t error;
+ unsigned int info_count;
+ struct processor_set_basic_info info;
+ host_t host;
+
+ error = processor_set_default(host_self(), &default_set);
+ if (error != KERN_SUCCESS)
+ {
+ if (tTd(3, 1))
+ perror("getla: processor_set_default failed:");
+ return -1;
+ }
+ info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
+ if (processor_set_info(default_set, PROCESSOR_SET_BASIC_INFO,
+ &host, (processor_set_info_t)&info,
+ &info_count) != KERN_SUCCESS)
+ {
+ if (tTd(3, 1))
+ perror("getla: processor_set_info failed:");
+ return -1;
+ }
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE);
+ return (int) (info.load_average + (LOAD_SCALE / 2)) / LOAD_SCALE;
+}
+
+#endif /* LA_TYPE == LA_MACH */
+
+#if LA_TYPE == LA_PROCSTR
+
+/*
+** Read /proc/loadavg for the load average. This is assumed to be
+** in a format like "0.15 0.12 0.06".
+**
+** Initially intended for Linux. This has been in the kernel
+** since at least 0.99.15.
+*/
+
+# ifndef _PATH_LOADAVG
+# define _PATH_LOADAVG "/proc/loadavg"
+# endif
+
+int
+getla()
+{
+ double avenrun;
+ register int result;
+ FILE *fp;
+
+ fp = fopen(_PATH_LOADAVG, "r");
+ if (fp == NULL)
+ {
+ if (tTd(3, 1))
+ printf("getla: fopen(%s): %s\n",
+ _PATH_LOADAVG, errstring(errno));
+ return -1;
+ }
+ result = fscanf(fp, "%lf", &avenrun);
+ fclose(fp);
+ if (result != 1)
+ {
+ if (tTd(3, 1))
+ printf("getla: fscanf() = %d: %s\n",
+ result, errstring(errno));
+ return -1;
+ }
+
+ if (tTd(3, 1))
+ printf("getla(): %.2f\n", avenrun);
+
+ return ((int) (avenrun + 0.5));
+}
+
+#endif /* LA_TYPE == LA_PROCSTR */
+
+#if LA_TYPE == LA_IRIX6
+#include <sys/sysmp.h>
+
+int getla(void)
+{
+ static int kmem = -1;
+ int avenrun[3];
+
+ if (kmem < 0)
+ {
+ kmem = open(_PATH_KMEM, 0, 0);
+ if (kmem < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: open(%s): %s\n", _PATH_KMEM,
+ errstring(errno));
+ return -1;
+ }
+ (void) fcntl(kmem, F_SETFD, 1);
+ }
+
+ if (lseek(kmem, (sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff), SEEK_SET) == -1 ||
+ read(kmem, (char *)avenrun, sizeof(avenrun)) < sizeof(avenrun))
+ {
+ if (tTd(3, 1))
+ printf("getla: lseek or read: %s\n",
+ errstring(errno));
+ return -1;
+ }
+ if (tTd(3, 5))
+ {
+ printf("getla: avenrun = %ld", (long int) avenrun[0]);
+ if (tTd(3, 15))
+ printf(", %ld, %ld",
+ (long int) avenrun[1], (long int) avenrun[2]);
+ printf("\n");
+ }
+
+ if (tTd(3, 1))
+ printf("getla: %d\n", (int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+ return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
+
+}
+#endif
+
+#if LA_TYPE == LA_KSTAT
+
+#include <kstat.h>
+
+int
+getla()
+{
+ static kstat_ctl_t *kc = NULL;
+ static kstat_t *ksp = NULL;
+ kstat_named_t *ksn;
+ int la;
+
+ if (kc == NULL) /* if not initialized before */
+ kc = kstat_open();
+ if (kc == NULL)
+ {
+ if (tTd(3, 1))
+ printf("getla: kstat_open(): %s\n",
+ errstring(errno));
+ return -1;
+ }
+ if (ksp == NULL)
+ ksp = kstat_lookup(kc, "unix", 0, "system_misc");
+ if (ksp == NULL)
+ {
+ if (tTd(3, 1))
+ printf("getla: kstat_lookup(): %s\n",
+ errstring(errno));
+ return -1;
+ }
+ if (kstat_read(kc, ksp, NULL) < 0)
+ {
+ if (tTd(3, 1))
+ printf("getla: kstat_read(): %s\n",
+ errstring(errno));
+ return -1;
+ }
+ ksn = (kstat_named_t *) kstat_data_lookup(ksp, "avenrun_1min");
+ la = ((double)ksn->value.ul + FSCALE/2) / FSCALE;
+ /* kstat_close(kc); /o do not close for fast access */
+ return la;
+}
+
+#endif /* LA_TYPE == LA_KSTAT */
+
+#if LA_TYPE == LA_DEVSHORT
+
+/*
+** Read /dev/table/avenrun for the load average. This should contain
+** three shorts for the 1, 5, and 15 minute loads. We only read the
+** first, since that's all we care about.
+**
+** Intended for SCO OpenServer 5.
+*/
+
+# ifndef _PATH_AVENRUN
+# define _PATH_AVENRUN "/dev/table/avenrun"
+# endif
+
+int
+getla()
+{
+ static int afd = -1;
+ short avenrun;
+ int loadav;
+ int r;
+
+ errno = EBADF;
+
+ if (afd == -1 || lseek(afd, 0L, SEEK_SET) == -1)
+ {
+ if (errno != EBADF)
+ return -1;
+ afd = open(_PATH_AVENRUN, O_RDONLY|O_SYNC);
+ if (afd < 0)
+ {
+ sm_syslog(LOG_ERR, NOQID,
+ "can't open %s: %m",
+ _PATH_AVENRUN);
+ return -1;
+ }
+ }
+
+ r = read(afd, &avenrun, sizeof avenrun);
+
+ if (tTd(3, 5))
+ printf("getla: avenrun = %d\n", avenrun);
+ loadav = (int) (avenrun + FSCALE/2) >> FSHIFT;
+ if (tTd(3, 1))
+ printf("getla: %d\n", loadav);
+ return loadav;
+}
+
+#endif /* LA_TYPE == LA_DEVSHORT */
+
+#if LA_TYPE == LA_ALPHAOSF
+struct rtentry;
+struct mbuf;
+# include <sys/table.h>
+
+int getla()
+{
+ int ave = 0;
+ struct tbl_loadavg tab;
+
+ if (table(TBL_LOADAVG, 0, &tab, 1, sizeof(tab)) == -1)
+ {
+ if (tTd(3, 1))
+ printf("getla: table %s\n", errstring(errno));
+ return (-1);
+ }
+
+ if (tTd(3, 1))
+ printf("getla: scale = %d\n", tab.tl_lscale);
+
+ if (tab.tl_lscale)
+ ave = (tab.tl_avenrun.l[0] + (tab.tl_lscale/2)) / tab.tl_lscale;
+ else
+ ave = (int) (tab.tl_avenrun.d[0] + 0.5);
+
+ if (tTd(3, 1))
+ printf("getla: %d\n", ave);
+
+ return ave;
+}
+
+#endif
+
+#if LA_TYPE == LA_ZERO
+
+int
+getla()
+{
+ if (tTd(3, 1))
+ printf("getla: ZERO\n");
+ return (0);
+}
+
+#endif /* LA_TYPE == LA_ZERO */
+
+/*
+ * Copyright 1989 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of M.I.T. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. M.I.T. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Authors: Many and varied...
+ */
+
+/* Non Apollo stuff removed by Don Lewis 11/15/93 */
+#ifndef lint
+static char rcsid[] = "@(#)$Id: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $";
+#endif /* !lint */
+
+#ifdef apollo
+# undef volatile
+# include <apollo/base.h>
+
+/* ARGSUSED */
+int getloadavg( call_data )
+ caddr_t call_data; /* pointer to (double) return value */
+{
+ double *avenrun = (double *) call_data;
+ int i;
+ status_$t st;
+ long loadav[3];
+ proc1_$get_loadav(loadav, &st);
+ *avenrun = loadav[0] / (double) (1 << 16);
+ return(0);
+}
+# endif /* apollo */
+ /*
+** SHOULDQUEUE -- should this message be queued or sent?
+**
+** Compares the message cost to the load average to decide.
+**
+** Parameters:
+** pri -- the priority of the message in question.
+** ctime -- the message creation time.
+**
+** Returns:
+** TRUE -- if this message should be queued up for the
+** time being.
+** FALSE -- if the load is low enough to send this message.
+**
+** Side Effects:
+** none.
+*/
+
+extern int get_num_procs_online __P((void));
+
+bool
+shouldqueue(pri, ctime)
+ long pri;
+ time_t ctime;
+{
+ bool rval;
+ int queuela = QueueLA * get_num_procs_online();
+
+ if (tTd(3, 30))
+ printf("shouldqueue: CurrentLA=%d, pri=%ld: ", CurrentLA, pri);
+ if (CurrentLA < queuela)
+ {
+ if (tTd(3, 30))
+ printf("FALSE (CurrentLA < QueueLA)\n");
+ return (FALSE);
+ }
+#if 0 /* this code is reported to cause oscillation around RefuseLA */
+ if (CurrentLA >= RefuseLA && QueueLA < RefuseLA)
+ {
+ if (tTd(3, 30))
+ printf("TRUE (CurrentLA >= RefuseLA)\n");
+ return (TRUE);
+ }
+#endif
+ rval = pri > (QueueFactor / (CurrentLA - queuela + 1));
+ if (tTd(3, 30))
+ printf("%s (by calculation)\n", rval ? "TRUE" : "FALSE");
+ return rval;
+}
+ /*
+** REFUSECONNECTIONS -- decide if connections should be refused
+**
+** Parameters:
+** port -- port number (for error messages only)
+**
+** Returns:
+** TRUE if incoming SMTP connections should be refused
+** (for now).
+** FALSE if we should accept new work.
+**
+** Side Effects:
+** Sets process title when it is rejecting connections.
+*/
+
+bool
+refuseconnections(port)
+ int port;
+{
+ int refusela = RefuseLA * get_num_procs_online();
+ time_t now;
+ static time_t lastconn = (time_t) 0;
+ static int conncnt = 0;
+ extern bool enoughdiskspace __P((long));
+
+#ifdef XLA
+ if (!xla_smtp_ok())
+ return TRUE;
+#endif
+
+ now = curtime();
+ if (now != lastconn)
+ {
+ lastconn = now;
+ conncnt = 0;
+ }
+ else if (conncnt++ > ConnRateThrottle && ConnRateThrottle > 0)
+ {
+ /* sleep to flatten out connection load */
+ setproctitle("deferring connections on port %d: %d per second",
+ port, ConnRateThrottle);
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "deferring connections on port %d: %d per second",
+ port, ConnRateThrottle);
+ sleep(1);
+ }
+
+ CurrentLA = getla();
+ if (CurrentLA >= refusela)
+ {
+ setproctitle("rejecting connections on port %d: load average: %d",
+ port, CurrentLA);
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "rejecting connections on port %d: load average: %d",
+ port, CurrentLA);
+ return TRUE;
+ }
+
+ if (!enoughdiskspace(MinBlocksFree + 1))
+ {
+ setproctitle("rejecting connections on port %d: min free: %d",
+ port, MinBlocksFree);
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "rejecting connections on port %d: min free: %d",
+ port, MinBlocksFree);
+ return TRUE;
+ }
+
+ if (MaxChildren > 0 && CurChildren >= MaxChildren)
+ {
+ extern void proc_list_probe __P((void));
+
+ proc_list_probe();
+ if (CurChildren >= MaxChildren)
+ {
+ setproctitle("rejecting connections on port %d: %d children, max %d",
+ port, CurChildren, MaxChildren);
+ if (LogLevel >= 14)
+ sm_syslog(LOG_INFO, NOQID,
+ "rejecting connections on port %d: %d children, max %d",
+ port, CurChildren, MaxChildren);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+ /*
+** SETPROCTITLE -- set process title for ps
+**
+** Parameters:
+** fmt -- a printf style format string.
+** a, b, c -- possible parameters to fmt.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Clobbers argv of our main procedure so ps(1) will
+** display the title.
+*/
+
+#define SPT_NONE 0 /* don't use it at all */
+#define SPT_REUSEARGV 1 /* cover argv with title information */
+#define SPT_BUILTIN 2 /* use libc builtin */
+#define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */
+#define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */
+#define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */
+#define SPT_SCO 6 /* write kernel u. area */
+#define SPT_CHANGEARGV 7 /* write our own strings into argv[] */
+
+#ifndef SPT_TYPE
+# define SPT_TYPE SPT_REUSEARGV
+#endif
+
+#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
+
+# if SPT_TYPE == SPT_PSTAT
+# include <sys/pstat.h>
+# endif
+# if SPT_TYPE == SPT_PSSTRINGS
+# include <machine/vmparam.h>
+# include <sys/exec.h>
+# ifndef PS_STRINGS /* hmmmm.... apparently not available after all */
+# undef SPT_TYPE
+# define SPT_TYPE SPT_REUSEARGV
+# else
+# ifndef NKPDE /* FreeBSD 2.0 */
+# define NKPDE 63
+typedef unsigned int *pt_entry_t;
+# endif
+# endif
+# endif
+
+# if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
+# define SETPROC_STATIC static
+# else
+# define SETPROC_STATIC
+# endif
+
+# if SPT_TYPE == SPT_SYSMIPS
+# include <sys/sysmips.h>
+# include <sys/sysnews.h>
+# endif
+
+# if SPT_TYPE == SPT_SCO
+# include <sys/immu.h>
+# include <sys/dir.h>
+# include <sys/user.h>
+# include <sys/fs/s5param.h>
+# if PSARGSZ > MAXLINE
+# define SPT_BUFSIZE PSARGSZ
+# endif
+# endif
+
+# ifndef SPT_PADCHAR
+# define SPT_PADCHAR ' '
+# endif
+
+# ifndef SPT_BUFSIZE
+# define SPT_BUFSIZE MAXLINE
+# endif
+
+#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
+
+/*
+** Pointers for setproctitle.
+** This allows "ps" listings to give more useful information.
+*/
+
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArgv = NULL; /* end of argv */
+
+void
+initsetproctitle(argc, argv, envp)
+ int argc;
+ char **argv;
+ char **envp;
+{
+ register int i, envpsize = 0;
+ extern char **environ;
+
+ /*
+ ** Move the environment so setproctitle can use the space at
+ ** the top of memory.
+ */
+
+ for (i = 0; envp[i] != NULL; i++)
+ envpsize += strlen(envp[i]) + 1;
+ environ = (char **) xalloc(sizeof (char *) * (i + 1));
+ for (i = 0; envp[i] != NULL; i++)
+ environ[i] = newstr(envp[i]);
+ environ[i] = NULL;
+
+ /*
+ ** Save start and extent of argv for setproctitle.
+ */
+
+ Argv = argv;
+
+ /*
+ ** Determine how much space we can use for setproctitle.
+ ** Use all contiguous argv and envp pointers starting at argv[0]
+ */
+ for (i = 0; i < argc; i++)
+ {
+ if (i==0 || LastArgv + 1 == argv[i])
+ LastArgv = argv[i] + strlen(argv[i]);
+ else
+ continue;
+ }
+ for (i=0; envp[i] != NULL; i++)
+ {
+ if (LastArgv + 1 == envp[i])
+ LastArgv = envp[i] + strlen(envp[i]);
+ else
+ continue;
+ }
+}
+
+#if SPT_TYPE != SPT_BUILTIN
+
+
+/*VARARGS1*/
+void
+# ifdef __STDC__
+setproctitle(const char *fmt, ...)
+# else
+setproctitle(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+# endif
+{
+# if SPT_TYPE != SPT_NONE
+ register char *p;
+ register int i;
+ SETPROC_STATIC char buf[SPT_BUFSIZE];
+ VA_LOCAL_DECL
+# if SPT_TYPE == SPT_PSTAT
+ union pstun pst;
+# endif
+# if SPT_TYPE == SPT_SCO
+ off_t seek_off;
+ static int kmem = -1;
+ static int kmempid = -1;
+ struct user u;
+# endif
+
+ p = buf;
+
+ /* print sendmail: heading for grep */
+ (void) strcpy(p, "sendmail: ");
+ p += strlen(p);
+
+ /* print the argument string */
+ VA_START(fmt);
+ (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
+ VA_END;
+
+ i = strlen(buf);
+
+# if SPT_TYPE == SPT_PSTAT
+ pst.pst_command = buf;
+ pstat(PSTAT_SETCMD, pst, i, 0, 0);
+# endif
+# if SPT_TYPE == SPT_PSSTRINGS
+ PS_STRINGS->ps_nargvstr = 1;
+ PS_STRINGS->ps_argvstr = buf;
+# endif
+# if SPT_TYPE == SPT_SYSMIPS
+ sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
+# endif
+# if SPT_TYPE == SPT_SCO
+ if (kmem < 0 || kmempid != getpid())
+ {
+ if (kmem >= 0)
+ close(kmem);
+ kmem = open(_PATH_KMEM, O_RDWR, 0);
+ if (kmem < 0)
+ return;
+ (void) fcntl(kmem, F_SETFD, 1);
+ kmempid = getpid();
+ }
+ buf[PSARGSZ - 1] = '\0';
+ seek_off = UVUBLK + (off_t) u.u_psargs - (off_t) &u;
+ if (lseek(kmem, (off_t) seek_off, SEEK_SET) == seek_off)
+ (void) write(kmem, buf, PSARGSZ);
+# endif
+# if SPT_TYPE == SPT_REUSEARGV
+ if (i > LastArgv - Argv[0] - 2)
+ {
+ i = LastArgv - Argv[0] - 2;
+ buf[i] = '\0';
+ }
+ (void) strcpy(Argv[0], buf);
+ p = &Argv[0][i];
+ while (p < LastArgv)
+ *p++ = SPT_PADCHAR;
+ Argv[1] = NULL;
+# endif
+# if SPT_TYPE == SPT_CHANGEARGV
+ Argv[0] = buf;
+ Argv[1] = 0;
+# endif
+# endif /* SPT_TYPE != SPT_NONE */
+}
+
+#endif /* SPT_TYPE != SPT_BUILTIN */
+ /*
+** WAITFOR -- wait for a particular process id.
+**
+** Parameters:
+** pid -- process id to wait for.
+**
+** Returns:
+** status of pid.
+** -1 if pid never shows up.
+**
+** Side Effects:
+** none.
+*/
+
+int
+waitfor(pid)
+ pid_t pid;
+{
+#ifdef WAITUNION
+ union wait st;
+#else
+ auto int st;
+#endif
+ pid_t i;
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
+ int savesig;
+#endif
+
+ do
+ {
+ errno = 0;
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
+ savesig = releasesignal(SIGCHLD);
+#endif
+ i = wait(&st);
+#if defined(ISC_UNIX) || defined(_SCO_unix_)
+ if (savesig > 0)
+ blocksignal(SIGCHLD);
+#endif
+ if (i > 0)
+ proc_list_drop(i);
+ } while ((i >= 0 || errno == EINTR) && i != pid);
+ if (i < 0)
+ return -1;
+#ifdef WAITUNION
+ return st.w_status;
+#else
+ return st;
+#endif
+}
+ /*
+** REAPCHILD -- pick up the body of my child, lest it become a zombie
+**
+** Parameters:
+** sig -- the signal that got us here (unused).
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Picks up extant zombies.
+*/
+
+SIGFUNC_DECL
+reapchild(sig)
+ int sig;
+{
+ int olderrno = errno;
+ pid_t pid;
+# ifdef HASWAITPID
+ auto int status;
+ int count;
+
+ count = 0;
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ {
+ if (count++ > 1000)
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, NOQID,
+ "reapchild: waitpid loop: pid=%d, status=%x",
+ pid, status);
+ break;
+ }
+ proc_list_drop(pid);
+ }
+# else
+# ifdef WNOHANG
+ union wait status;
+
+ while ((pid = wait3(&status, WNOHANG, (struct rusage *) NULL)) > 0)
+ proc_list_drop(pid);
+# else /* WNOHANG */
+ auto int status;
+
+ /*
+ ** Catch one zombie -- we will be re-invoked (we hope) if there
+ ** are more. Unreliable signals probably break this, but this
+ ** is the "old system" situation -- waitpid or wait3 are to be
+ ** strongly preferred.
+ */
+
+ if ((pid = wait(&status)) > 0)
+ proc_list_drop(pid);
+# endif /* WNOHANG */
+# endif
+# ifdef SYS5SIGNALS
+ (void) setsignal(SIGCHLD, reapchild);
+# endif
+ errno = olderrno;
+ return SIGFUNC_RETURN;
+}
+ /*
+** PUTENV -- emulation of putenv() in terms of setenv()
+**
+** Not needed on Posix-compliant systems.
+** This doesn't have full Posix semantics, but it's good enough
+** for sendmail.
+**
+** Parameter:
+** env -- the environment to put.
+**
+** Returns:
+** none.
+*/
+
+#ifdef NEEDPUTENV
+
+# if NEEDPUTENV == 2 /* no setenv(3) call available */
+
+int
+putenv(str)
+ char *str;
+{
+ char **current;
+ int matchlen, envlen=0;
+ char *tmp;
+ char **newenv;
+ static int first=1;
+ extern char **environ;
+
+ /*
+ * find out how much of str to match when searching
+ * for a string to replace.
+ */
+ if ((tmp = strchr(str, '=')) == NULL || tmp == str)
+ matchlen = strlen(str);
+ else
+ matchlen = (int) (tmp - str);
+ ++matchlen;
+
+ /*
+ * Search for an existing string in the environment and find the
+ * length of environ. If found, replace and exit.
+ */
+ for (current=environ; *current; current++) {
+ ++envlen;
+
+ if (strncmp(str, *current, matchlen) == 0) {
+ /* found it, now insert the new version */
+ *current = (char *)str;
+ return(0);
+ }
+ }
+
+ /*
+ * There wasn't already a slot so add space for a new slot.
+ * If this is our first time through, use malloc(), else realloc().
+ */
+ if (first) {
+ newenv = (char **) malloc(sizeof(char *) * (envlen + 2));
+ if (newenv == NULL)
+ return(-1);
+
+ first=0;
+ (void) memcpy(newenv, environ, sizeof(char *) * envlen);
+ } else {
+ newenv = (char **) realloc((char *)environ, sizeof(char *) * (envlen + 2));
+ if (newenv == NULL)
+ return(-1);
+ }
+
+ /* actually add in the new entry */
+ environ = newenv;
+ environ[envlen] = (char *)str;
+ environ[envlen+1] = NULL;
+
+ return(0);
+}
+
+#else /* implement putenv() in terms of setenv() */
+
+int
+putenv(env)
+ char *env;
+{
+ char *p;
+ int l;
+ char nbuf[100];
+
+ p = strchr(env, '=');
+ if (p == NULL)
+ return 0;
+ l = p - env;
+ if (l > sizeof nbuf - 1)
+ l = sizeof nbuf - 1;
+ bcopy(env, nbuf, l);
+ nbuf[l] = '\0';
+ return setenv(nbuf, ++p, 1);
+}
+
+# endif
+#endif
+ /*
+** UNSETENV -- remove a variable from the environment
+**
+** Not needed on newer systems.
+**
+** Parameters:
+** name -- the string name of the environment variable to be
+** deleted from the current environment.
+**
+** Returns:
+** none.
+**
+** Globals:
+** environ -- a pointer to the current environment.
+**
+** Side Effects:
+** Modifies environ.
+*/
+
+#ifndef HASUNSETENV
+
+void
+unsetenv(name)
+ char *name;
+{
+ extern char **environ;
+ register char **pp;
+ int len = strlen(name);
+
+ for (pp = environ; *pp != NULL; pp++)
+ {
+ if (strncmp(name, *pp, len) == 0 &&
+ ((*pp)[len] == '=' || (*pp)[len] == '\0'))
+ break;
+ }
+
+ for (; *pp != NULL; pp++)
+ *pp = pp[1];
+}
+
+#endif
+ /*
+** GETDTABLESIZE -- return number of file descriptors
+**
+** Only on non-BSD systems
+**
+** Parameters:
+** none
+**
+** Returns:
+** size of file descriptor table
+**
+** Side Effects:
+** none
+*/
+
+#ifdef SOLARIS
+# include <sys/resource.h>
+#endif
+
+int
+getdtsize()
+{
+#ifdef RLIMIT_NOFILE
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) >= 0)
+ return rl.rlim_cur;
+#endif
+
+# ifdef HASGETDTABLESIZE
+ return getdtablesize();
+# else
+# ifdef _SC_OPEN_MAX
+ return sysconf(_SC_OPEN_MAX);
+# else
+ return NOFILE;
+# endif
+# endif
+}
+ /*
+** UNAME -- get the UUCP name of this system.
+*/
+
+#ifndef HASUNAME
+
+int
+uname(name)
+ struct utsname *name;
+{
+ FILE *file;
+ char *n;
+
+ name->nodename[0] = '\0';
+
+ /* try /etc/whoami -- one line with the node name */
+ if ((file = fopen("/etc/whoami", "r")) != NULL)
+ {
+ (void) fgets(name->nodename, NODE_LENGTH + 1, file);
+ (void) fclose(file);
+ n = strchr(name->nodename, '\n');
+ if (n != NULL)
+ *n = '\0';
+ if (name->nodename[0] != '\0')
+ return (0);
+ }
+
+ /* try /usr/include/whoami.h -- has a #define somewhere */
+ if ((file = fopen("/usr/include/whoami.h", "r")) != NULL)
+ {
+ char buf[MAXLINE];
+
+ while (fgets(buf, MAXLINE, file) != NULL)
+ if (sscanf(buf, "#define sysname \"%*[^\"]\"",
+ NODE_LENGTH, name->nodename) > 0)
+ break;
+ (void) fclose(file);
+ if (name->nodename[0] != '\0')
+ return (0);
+ }
+
+#ifdef TRUST_POPEN
+ /*
+ ** Popen is known to have security holes.
+ */
+
+ /* try uuname -l to return local name */
+ if ((file = popen("uuname -l", "r")) != NULL)
+ {
+ (void) fgets(name, NODE_LENGTH + 1, file);
+ (void) pclose(file);
+ n = strchr(name, '\n');
+ if (n != NULL)
+ *n = '\0';
+ if (name->nodename[0] != '\0')
+ return (0);
+ }
+#endif
+
+ return (-1);
+}
+#endif /* HASUNAME */
+ /*
+** INITGROUPS -- initialize groups
+**
+** Stub implementation for System V style systems
+*/
+
+#ifndef HASINITGROUPS
+
+initgroups(name, basegid)
+ char *name;
+ int basegid;
+{
+ return 0;
+}
+
+#endif
+ /*
+** SETGROUPS -- set group list
+**
+** Stub implementation for systems that don't have group lists
+*/
+
+#ifndef NGROUPS_MAX
+
+int
+setgroups(ngroups, grouplist)
+ int ngroups;
+ GIDSET_T grouplist[];
+{
+ return 0;
+}
+
+#endif
+ /*
+** SETSID -- set session id (for non-POSIX systems)
+*/
+
+#ifndef HASSETSID
+
+pid_t
+setsid __P ((void))
+{
+#ifdef TIOCNOTTY
+ int fd;
+
+ fd = open("/dev/tty", O_RDWR, 0);
+ if (fd >= 0)
+ {
+ (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
+ (void) close(fd);
+ }
+#endif /* TIOCNOTTY */
+# ifdef SYS5SETPGRP
+ return setpgrp();
+# else
+ return setpgid(0, getpid());
+# endif
+}
+
+#endif
+ /*
+** FSYNC -- dummy fsync
+*/
+
+#ifdef NEEDFSYNC
+
+fsync(fd)
+ int fd;
+{
+# ifdef O_SYNC
+ return fcntl(fd, F_SETFL, O_SYNC);
+# else
+ /* nothing we can do */
+ return 0;
+# endif
+}
+
+#endif
+ /*
+** DGUX_INET_ADDR -- inet_addr for DG/UX
+**
+** Data General DG/UX version of inet_addr returns a struct in_addr
+** instead of a long. This patches things. Only needed on versions
+** prior to 5.4.3.
+*/
+
+#ifdef DGUX_5_4_2
+
+#undef inet_addr
+
+long
+dgux_inet_addr(host)
+ char *host;
+{
+ struct in_addr haddr;
+
+ haddr = inet_addr(host);
+ return haddr.s_addr;
+}
+
+#endif
+ /*
+** GETOPT -- for old systems or systems with bogus implementations
+*/
+
+#ifdef NEEDGETOPT
+
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+
+/*
+** this version hacked to add `atend' flag to allow state machine
+** to reset if invoked by the program to scan args for a 2nd time
+*/
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c 4.3 (Berkeley) 3/9/86";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+
+/*
+ * get option letter from argument vector
+ */
+#ifdef _CONVEX_SOURCE
+extern int optind, opterr, optopt;
+extern char *optarg;
+#else
+int opterr = 1; /* if error message should be printed */
+int optind = 1; /* index into parent argv vector */
+int optopt = 0; /* character checked for validity */
+char *optarg = NULL; /* argument associated with option */
+#endif
+
+#define BADCH (int)'?'
+#define EMSG ""
+#define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \
+ fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);}
+
+int
+getopt(nargc,nargv,ostr)
+ int nargc;
+ char *const *nargv;
+ const char *ostr;
+{
+ static char *place = EMSG; /* option letter processing */
+ static char atend = 0;
+ register char *oli = NULL; /* option letter list index */
+
+ if (atend) {
+ atend = 0;
+ place = EMSG;
+ }
+ if(!*place) { /* update scanning pointer */
+ if (optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) {
+ atend++;
+ return -1;
+ }
+ if (*place == '-') { /* found "--" */
+ ++optind;
+ atend++;
+ return -1;
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' || !(oli = strchr(ostr,optopt))) {
+ if (!*place) ++optind;
+ tell(": illegal option -- ");
+ }
+ if (oli && *++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place) ++optind;
+ }
+ else { /* need an argument */
+ if (*place) optarg = place; /* no white space */
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ tell(": option requires an argument -- ");
+ }
+ else optarg = nargv[optind]; /* white space */
+ place = EMSG;
+ ++optind;
+ }
+ return(optopt); /* dump back option letter */
+}
+
+#endif
+ /*
+** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version
+*/
+
+#ifdef NEEDVPRINTF
+
+#define MAXARG 16
+
+vfprintf(fp, fmt, ap)
+ FILE *fp;
+ char *fmt;
+ char **ap;
+{
+ char *bp[MAXARG];
+ int i = 0;
+
+ while (*ap && i < MAXARG)
+ bp[i++] = *ap++;
+ fprintf(fp, fmt, bp[0], bp[1], bp[2], bp[3],
+ bp[4], bp[5], bp[6], bp[7],
+ bp[8], bp[9], bp[10], bp[11],
+ bp[12], bp[13], bp[14], bp[15]);
+}
+
+vsprintf(s, fmt, ap)
+ char *s;
+ char *fmt;
+ char **ap;
+{
+ char *bp[MAXARG];
+ int i = 0;
+
+ while (*ap && i < MAXARG)
+ bp[i++] = *ap++;
+ sprintf(s, fmt, bp[0], bp[1], bp[2], bp[3],
+ bp[4], bp[5], bp[6], bp[7],
+ bp[8], bp[9], bp[10], bp[11],
+ bp[12], bp[13], bp[14], bp[15]);
+}
+
+#endif
+ /*
+** USERSHELLOK -- tell if a user's shell is ok for unrestricted use
+**
+** Parameters:
+** user -- the name of the user we are checking.
+** shell -- the user's shell from /etc/passwd
+**
+** Returns:
+** TRUE -- if it is ok to use this for unrestricted access.
+** FALSE -- if the shell is restricted.
+*/
+
+#if !HASGETUSERSHELL
+
+# ifndef _PATH_SHELLS
+# define _PATH_SHELLS "/etc/shells"
+# endif
+
+# if defined(_AIX3) || defined(_AIX4)
+# include <userconf.h>
+# if _AIX4 >= 40200
+# include <userpw.h>
+# endif
+# include <usersec.h>
+# endif
+
+char *DefaultUserShells[] =
+{
+ "/bin/sh", /* standard shell */
+ "/usr/bin/sh",
+ "/bin/csh", /* C shell */
+ "/usr/bin/csh",
+#ifdef __hpux
+# ifdef V4FS
+ "/usr/bin/rsh", /* restricted Bourne shell */
+ "/usr/bin/ksh", /* Korn shell */
+ "/usr/bin/rksh", /* restricted Korn shell */
+ "/usr/bin/pam",
+ "/usr/bin/keysh", /* key shell (extended Korn shell) */
+ "/usr/bin/posix/sh",
+# else
+ "/bin/rsh", /* restricted Bourne shell */
+ "/bin/ksh", /* Korn shell */
+ "/bin/rksh", /* restricted Korn shell */
+ "/bin/pam",
+ "/usr/bin/keysh", /* key shell (extended Korn shell) */
+ "/bin/posix/sh",
+# endif
+#endif
+#if defined(_AIX3) || defined(_AIX4)
+ "/bin/ksh", /* Korn shell */
+ "/usr/bin/ksh",
+ "/bin/tsh", /* trusted shell */
+ "/usr/bin/tsh",
+ "/bin/bsh", /* Bourne shell */
+ "/usr/bin/bsh",
+#endif
+#ifdef __svr4__
+ "/bin/ksh", /* Korn shell */
+ "/usr/bin/ksh",
+#endif
+#ifdef sgi
+ "/sbin/sh", /* SGI's shells really live in /sbin */
+ "/sbin/csh",
+ "/bin/ksh", /* Korn shell */
+ "/sbin/ksh",
+ "/usr/bin/ksh",
+ "/bin/tcsh", /* Extended csh */
+ "/usr/bin/tcsh",
+#endif
+ NULL
+};
+
+#endif
+
+#define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/"
+
+bool
+usershellok(user, shell)
+ char *user;
+ char *shell;
+{
+#if HASGETUSERSHELL
+ register char *p;
+ extern char *getusershell();
+
+ if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
+ ConfigLevel <= 1)
+ return TRUE;
+
+ setusershell();
+ while ((p = getusershell()) != NULL)
+ if (strcmp(p, shell) == 0 || strcmp(p, WILDCARD_SHELL) == 0)
+ break;
+ endusershell();
+ return p != NULL;
+#else
+# if USEGETCONFATTR
+ auto char *v;
+# endif
+ register FILE *shellf;
+ char buf[MAXLINE];
+
+ if (shell == NULL || shell[0] == '\0' || wordinclass(user, 't') ||
+ ConfigLevel <= 1)
+ return TRUE;
+
+# if USEGETCONFATTR
+ /*
+ ** Naturally IBM has a "better" idea.....
+ **
+ ** What a crock. This interface isn't documented, it is
+ ** considered part of the security library (-ls), and it
+ ** only works if you are running as root (since the list
+ ** of valid shells is obviously a source of great concern).
+ ** I recommend that you do NOT define USEGETCONFATTR,
+ ** especially since you are going to have to set up an
+ ** /etc/shells anyhow to handle the cases where getconfattr
+ ** fails.
+ */
+
+ if (getconfattr(SC_SYS_LOGIN, SC_SHELLS, &v, SEC_LIST) == 0 && v != NULL)
+ {
+ while (*v != '\0')
+ {
+ if (strcmp(v, shell) == 0 || strcmp(v, WILDCARD_SHELL) == 0)
+ return TRUE;
+ v += strlen(v) + 1;
+ }
+ return FALSE;
+ }
+# endif
+
+ shellf = fopen(_PATH_SHELLS, "r");
+ if (shellf == NULL)
+ {
+ /* no /etc/shells; see if it is one of the std shells */
+ char **d;
+
+ if (errno != ENOENT && LogLevel > 3)
+ sm_syslog(LOG_ERR, NOQID,
+ "usershellok: cannot open %s: %s",
+ _PATH_SHELLS, errstring(errno));
+
+ for (d = DefaultUserShells; *d != NULL; d++)
+ {
+ if (strcmp(shell, *d) == 0)
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ while (fgets(buf, sizeof buf, shellf) != NULL)
+ {
+ register char *p, *q;
+
+ p = buf;
+ while (*p != '\0' && *p != '#' && *p != '/')
+ p++;
+ if (*p == '#' || *p == '\0')
+ continue;
+ q = p;
+ while (*p != '\0' && *p != '#' && !(isascii(*p) && isspace(*p)))
+ p++;
+ *p = '\0';
+ if (strcmp(shell, q) == 0 || strcmp(WILDCARD_SHELL, q) == 0)
+ {
+ fclose(shellf);
+ return TRUE;
+ }
+ }
+ fclose(shellf);
+ return FALSE;
+#endif
+}
+ /*
+** FREEDISKSPACE -- see how much free space is on the queue filesystem
+**
+** Only implemented if you have statfs.
+**
+** Parameters:
+** dir -- the directory in question.
+** bsize -- a variable into which the filesystem
+** block size is stored.
+**
+** Returns:
+** The number of bytes free on the queue filesystem.
+** -1 if the statfs call fails.
+**
+** Side effects:
+** Puts the filesystem block size into bsize.
+*/
+
+/* statfs types */
+#define SFS_NONE 0 /* no statfs implementation */
+#define SFS_USTAT 1 /* use ustat */
+#define SFS_4ARGS 2 /* use four-argument statfs call */
+#define SFS_VFS 3 /* use <sys/vfs.h> implementation */
+#define SFS_MOUNT 4 /* use <sys/mount.h> implementation */
+#define SFS_STATFS 5 /* use <sys/statfs.h> implementation */
+#define SFS_STATVFS 6 /* use <sys/statvfs.h> implementation */
+
+#ifndef SFS_TYPE
+# define SFS_TYPE SFS_NONE
+#endif
+
+#if SFS_TYPE == SFS_USTAT
+# include <ustat.h>
+#endif
+#if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
+# include <sys/statfs.h>
+#endif
+#if SFS_TYPE == SFS_VFS
+# include <sys/vfs.h>
+#endif
+#if SFS_TYPE == SFS_MOUNT
+# include <sys/mount.h>
+#endif
+#if SFS_TYPE == SFS_STATVFS
+# include <sys/statvfs.h>
+#endif
+
+long
+freediskspace(dir, bsize)
+ char *dir;
+ long *bsize;
+{
+#if SFS_TYPE != SFS_NONE
+# if SFS_TYPE == SFS_USTAT
+ struct ustat fs;
+ struct stat statbuf;
+# define FSBLOCKSIZE DEV_BSIZE
+# define SFS_BAVAIL f_tfree
+# else
+# if defined(ultrix)
+ struct fs_data fs;
+# define SFS_BAVAIL fd_bfreen
+# define FSBLOCKSIZE 1024L
+# else
+# if SFS_TYPE == SFS_STATVFS
+ struct statvfs fs;
+# define FSBLOCKSIZE fs.f_frsize
+# else
+ struct statfs fs;
+# define FSBLOCKSIZE fs.f_bsize
+# endif
+# endif
+# endif
+# ifndef SFS_BAVAIL
+# define SFS_BAVAIL f_bavail
+# endif
+
+# if SFS_TYPE == SFS_USTAT
+ if (stat(dir, &statbuf) == 0 && ustat(statbuf.st_dev, &fs) == 0)
+# else
+# if SFS_TYPE == SFS_4ARGS
+ if (statfs(dir, &fs, sizeof fs, 0) == 0)
+# else
+# if SFS_TYPE == SFS_STATVFS
+ if (statvfs(dir, &fs) == 0)
+# else
+# if defined(ultrix)
+ if (statfs(dir, &fs) > 0)
+# else
+ if (statfs(dir, &fs) == 0)
+# endif
+# endif
+# endif
+# endif
+ {
+ if (bsize != NULL)
+ *bsize = FSBLOCKSIZE;
+ if (fs.SFS_BAVAIL <= 0)
+ return 0;
+ else if (fs.SFS_BAVAIL > LONG_MAX)
+ return LONG_MAX;
+ else
+ return (long) fs.SFS_BAVAIL;
+ }
+#endif
+ return (-1);
+}
+ /*
+** ENOUGHDISKSPACE -- is there enough free space on the queue fs?
+**
+** Only implemented if you have statfs.
+**
+** Parameters:
+** msize -- the size to check against. If zero, we don't yet
+** know how big the message will be, so just check for
+** a "reasonable" amount.
+**
+** Returns:
+** TRUE if there is enough space.
+** FALSE otherwise.
+*/
+
+bool
+enoughdiskspace(msize)
+ long msize;
+{
+ long bfree, bsize;
+
+ if (MinBlocksFree <= 0 && msize <= 0)
+ {
+ if (tTd(4, 80))
+ printf("enoughdiskspace: no threshold\n");
+ return TRUE;
+ }
+
+ if ((bfree = freediskspace(QueueDir, &bsize)) >= 0)
+ {
+ if (tTd(4, 80))
+ printf("enoughdiskspace: bavail=%ld, need=%ld\n",
+ bfree, msize);
+
+ /* convert msize to block count */
+ msize = msize / bsize + 1;
+ if (MinBlocksFree >= 0)
+ msize += MinBlocksFree;
+
+ if (bfree < msize)
+ {
+ if (LogLevel > 0)
+ sm_syslog(LOG_ALERT, CurEnv->e_id,
+ "low on space (have %ld, %s needs %ld in %s)",
+ bfree,
+ CurHostName == NULL ? "SMTP-DAEMON" : CurHostName,
+ msize, QueueDir);
+ return FALSE;
+ }
+ }
+ else if (tTd(4, 80))
+ printf("enoughdiskspace failure: min=%ld, need=%ld: %s\n",
+ MinBlocksFree, msize, errstring(errno));
+ return TRUE;
+}
+ /*
+** TRANSIENTERROR -- tell if an error code indicates a transient failure
+**
+** This looks at an errno value and tells if this is likely to
+** go away if retried later.
+**
+** Parameters:
+** err -- the errno code to classify.
+**
+** Returns:
+** TRUE if this is probably transient.
+** FALSE otherwise.
+*/
+
+bool
+transienterror(err)
+ int err;
+{
+ switch (err)
+ {
+ case EIO: /* I/O error */
+ case ENXIO: /* Device not configured */
+ case EAGAIN: /* Resource temporarily unavailable */
+ case ENOMEM: /* Cannot allocate memory */
+ case ENODEV: /* Operation not supported by device */
+ case ENFILE: /* Too many open files in system */
+ case EMFILE: /* Too many open files */
+ case ENOSPC: /* No space left on device */
+#ifdef ETIMEDOUT
+ case ETIMEDOUT: /* Connection timed out */
+#endif
+#ifdef ESTALE
+ case ESTALE: /* Stale NFS file handle */
+#endif
+#ifdef ENETDOWN
+ case ENETDOWN: /* Network is down */
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH: /* Network is unreachable */
+#endif
+#ifdef ENETRESET
+ case ENETRESET: /* Network dropped connection on reset */
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED: /* Software caused connection abort */
+#endif
+#ifdef ECONNRESET
+ case ECONNRESET: /* Connection reset by peer */
+#endif
+#ifdef ENOBUFS
+ case ENOBUFS: /* No buffer space available */
+#endif
+#ifdef ESHUTDOWN
+ case ESHUTDOWN: /* Can't send after socket shutdown */
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED: /* Connection refused */
+#endif
+#ifdef EHOSTDOWN
+ case EHOSTDOWN: /* Host is down */
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH: /* No route to host */
+#endif
+#ifdef EDQUOT
+ case EDQUOT: /* Disc quota exceeded */
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM: /* Too many processes */
+#endif
+#ifdef EUSERS
+ case EUSERS: /* Too many users */
+#endif
+#ifdef EDEADLK
+ case EDEADLK: /* Resource deadlock avoided */
+#endif
+#ifdef EISCONN
+ case EISCONN: /* Socket already connected */
+#endif
+#ifdef EINPROGRESS
+ case EINPROGRESS: /* Operation now in progress */
+#endif
+#ifdef EALREADY
+ case EALREADY: /* Operation already in progress */
+#endif
+#ifdef EADDRINUSE
+ case EADDRINUSE: /* Address already in use */
+#endif
+#ifdef EADDRNOTAVAIL
+ case EADDRNOTAVAIL: /* Can't assign requested address */
+#endif
+#ifdef ETXTBSY
+ case ETXTBSY: /* (Apollo) file locked */
+#endif
+#if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
+ case ENOSR: /* Out of streams resources */
+#endif
+ case E_SM_OPENTIMEOUT: /* PSEUDO: open timed out */
+ return TRUE;
+ }
+
+ /* nope, must be permanent */
+ return FALSE;
+}
+ /*
+** LOCKFILE -- lock a file using flock or (shudder) fcntl locking
+**
+** Parameters:
+** fd -- the file descriptor of the file.
+** filename -- the file name (for error messages).
+** ext -- the filename extension.
+** type -- type of the lock. Bits can be:
+** LOCK_EX -- exclusive lock.
+** LOCK_NB -- non-blocking.
+**
+** Returns:
+** TRUE if the lock was acquired.
+** FALSE otherwise.
+*/
+
+bool
+lockfile(fd, filename, ext, type)
+ int fd;
+ char *filename;
+ char *ext;
+ int type;
+{
+ int i;
+ int save_errno;
+# if !HASFLOCK
+ int action;
+ struct flock lfd;
+
+ if (ext == NULL)
+ ext = "";
+
+ bzero(&lfd, sizeof lfd);
+ if (bitset(LOCK_UN, type))
+ lfd.l_type = F_UNLCK;
+ else if (bitset(LOCK_EX, type))
+ lfd.l_type = F_WRLCK;
+ else
+ lfd.l_type = F_RDLCK;
+
+ if (bitset(LOCK_NB, type))
+ action = F_SETLK;
+ else
+ action = F_SETLKW;
+
+ if (tTd(55, 60))
+ printf("lockfile(%s%s, action=%d, type=%d): ",
+ filename, ext, action, lfd.l_type);
+
+ while ((i = fcntl(fd, action, &lfd)) < 0 && errno == EINTR)
+ continue;
+ if (i >= 0)
+ {
+ if (tTd(55, 60))
+ printf("SUCCESS\n");
+ return TRUE;
+ }
+ save_errno = errno;
+
+ if (tTd(55, 60))
+ printf("(%s) ", errstring(save_errno));
+
+ /*
+ ** On SunOS, if you are testing using -oQ/tmp/mqueue or
+ ** -oA/tmp/aliases or anything like that, and /tmp is mounted
+ ** as type "tmp" (that is, served from swap space), the
+ ** previous fcntl will fail with "Invalid argument" errors.
+ ** Since this is fairly common during testing, we will assume
+ ** that this indicates that the lock is successfully grabbed.
+ */
+
+ if (save_errno == EINVAL)
+ {
+ if (tTd(55, 60))
+ printf("SUCCESS\n");
+ return TRUE;
+ }
+
+ if (!bitset(LOCK_NB, type) || (save_errno != EACCES && save_errno != EAGAIN))
+ {
+ int omode = -1;
+# ifdef F_GETFL
+ (void) fcntl(fd, F_GETFL, &omode);
+ errno = save_errno;
+# endif
+ syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
+ filename, ext, fd, type, omode, geteuid());
+ dumpfd(fd, TRUE, TRUE);
+ }
+# else
+ if (ext == NULL)
+ ext = "";
+
+ if (tTd(55, 60))
+ printf("lockfile(%s%s, type=%o): ", filename, ext, type);
+
+ while ((i = flock(fd, type)) < 0 && errno == EINTR)
+ continue;
+ if (i >= 0)
+ {
+ if (tTd(55, 60))
+ printf("SUCCESS\n");
+ return TRUE;
+ }
+ save_errno = errno;
+
+ if (tTd(55, 60))
+ printf("(%s) ", errstring(save_errno));
+
+ if (!bitset(LOCK_NB, type) || save_errno != EWOULDBLOCK)
+ {
+ int omode = -1;
+# ifdef F_GETFL
+ (void) fcntl(fd, F_GETFL, &omode);
+ errno = save_errno;
+# endif
+ syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
+ filename, ext, fd, type, omode, geteuid());
+ dumpfd(fd, TRUE, TRUE);
+ }
+# endif
+ if (tTd(55, 60))
+ printf("FAIL\n");
+ errno = save_errno;
+ return FALSE;
+}
+ /*
+** CHOWNSAFE -- tell if chown is "safe" (executable only by root)
+**
+** Unfortunately, given that we can't predict other systems on which
+** a remote mounted (NFS) filesystem will be mounted, the answer is
+** almost always that this is unsafe.
+**
+** Note also that many operating systems have non-compliant
+** implementations of the _POSIX_CHOWN_RESTRICTED variable and the
+** fpathconf() routine. According to IEEE 1003.1-1990, if
+** _POSIX_CHOWN_RESTRICTED is defined and not equal to -1, then
+** no non-root process can give away the file. However, vendors
+** don't take NFS into account, so a comfortable value of
+** _POSIX_CHOWN_RESTRICTED tells us nothing.
+**
+** Also, some systems (e.g., IRIX 6.2) return 1 from fpathconf()
+** even on files where chown is not restricted. Many systems get
+** this wrong on NFS-based filesystems (that is, they say that chown
+** is restricted [safe] on NFS filesystems where it may not be, since
+** other systems can access the same filesystem and do file giveaway;
+** only the NFS server knows for sure!) Hence, it is important to
+** get the value of SAFENFSPATHCONF correct -- it should be defined
+** _only_ after testing (see test/t_pathconf.c) a system on an unsafe
+** NFS-based filesystem to ensure that you can get meaningful results.
+** If in doubt, assume unsafe!
+**
+** You may also need to tweak IS_SAFE_CHOWN -- it should be a
+** condition indicating whether the return from pathconf indicates
+** that chown is safe (typically either > 0 or >= 0 -- there isn't
+** even any agreement about whether a zero return means that a file
+** is or is not safe). It defaults to "> 0".
+**
+** If the parent directory is safe (writable only by owner back
+** to the root) then we can relax slightly and trust fpathconf
+** in more circumstances. This is really a crock -- if this is an
+** NFS mounted filesystem then we really know nothing about the
+** underlying implementation. However, most systems pessimize and
+** return an error (EINVAL or EOPNOTSUPP) on NFS filesystems, which
+** we interpret as unsafe, as we should. Thus, this heuristic gets
+** us into a possible problem only on systems that have a broken
+** pathconf implementation and which are also poorly configured
+** (have :include: files in group- or world-writable directories).
+**
+** Parameters:
+** fd -- the file descriptor to check.
+** safedir -- set if the parent directory is safe.
+**
+** Returns:
+** TRUE -- if the chown(2) operation is "safe" -- that is,
+** only root can chown the file to an arbitrary user.
+** FALSE -- if an arbitrary user can give away a file.
+*/
+
+#ifndef IS_SAFE_CHOWN
+# define IS_SAFE_CHOWN > 0
+#endif
+
+bool
+chownsafe(fd, safedir)
+ int fd;
+ bool safedir;
+{
+#if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
+ (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
+ int rval;
+
+ /* give the system administrator a chance to override */
+ if (bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail))
+ return TRUE;
+
+ /*
+ ** Some systems (e.g., SunOS) seem to have the call and the
+ ** #define _PC_CHOWN_RESTRICTED, but don't actually implement
+ ** the call. This heuristic checks for that.
+ */
+
+ errno = 0;
+ rval = fpathconf(fd, _PC_CHOWN_RESTRICTED);
+# if SAFENFSPATHCONF
+ return errno == 0 && rval IS_SAFE_CHOWN;
+# else
+ return safedir && errno == 0 && rval IS_SAFE_CHOWN;
+# endif
+#else
+ return bitset(DBS_ASSUMESAFECHOWN, DontBlameSendmail);
+#endif
+}
+ /*
+** RESETLIMITS -- reset system controlled resource limits
+**
+** This is to avoid denial-of-service attacks
+**
+** Parameters:
+** none
+**
+** Returns:
+** none
+*/
+
+#if HASSETRLIMIT
+# ifdef RLIMIT_NEEDS_SYS_TIME_H
+# include <sys/time.h>
+# endif
+# include <sys/resource.h>
+#endif
+#ifndef FD_SETSIZE
+# define FD_SETSIZE 256
+#endif
+
+void
+resetlimits()
+{
+#if HASSETRLIMIT
+ struct rlimit lim;
+
+ lim.rlim_cur = lim.rlim_max = RLIM_INFINITY;
+ (void) setrlimit(RLIMIT_CPU, &lim);
+ (void) setrlimit(RLIMIT_FSIZE, &lim);
+# ifdef RLIMIT_NOFILE
+ lim.rlim_cur = lim.rlim_max = FD_SETSIZE;
+ (void) setrlimit(RLIMIT_NOFILE, &lim);
+# endif
+#else
+# if HASULIMIT
+ (void) ulimit(2, 0x3fffff);
+ (void) ulimit(4, FD_SETSIZE);
+# endif
+#endif
+ errno = 0;
+}
+ /*
+** GETCFNAME -- return the name of the .cf file.
+**
+** Some systems (e.g., NeXT) determine this dynamically.
+*/
+
+char *
+getcfname()
+{
+
+ if (ConfFile != NULL)
+ return ConfFile;
+#if NETINFO
+ {
+ extern char *ni_propval __P((char *, char *, char *, char *, int));
+ char *cflocation;
+
+ cflocation = ni_propval("/locations", NULL, "sendmail",
+ "sendmail.cf", '\0');
+ if (cflocation != NULL)
+ return cflocation;
+ }
+#endif
+
+ return _PATH_SENDMAILCF;
+}
+ /*
+** SETVENDOR -- process vendor code from V configuration line
+**
+** Parameters:
+** vendor -- string representation of vendor.
+**
+** Returns:
+** TRUE -- if ok.
+** FALSE -- if vendor code could not be processed.
+**
+** Side Effects:
+** It is reasonable to set mode flags here to tweak
+** processing in other parts of the code if necessary.
+** For example, if you are a vendor that uses $%y to
+** indicate YP lookups, you could enable that here.
+*/
+
+bool
+setvendor(vendor)
+ char *vendor;
+{
+ if (strcasecmp(vendor, "Berkeley") == 0)
+ {
+ VendorCode = VENDOR_BERKELEY;
+ return TRUE;
+ }
+
+ /* add vendor extensions here */
+
+#ifdef SUN_EXTENSIONS
+ if (strcasecmp(vendor, "Sun") == 0)
+ {
+ VendorCode = VENDOR_SUN;
+ return TRUE;
+ }
+#endif
+
+ return FALSE;
+}
+ /*
+** VENDOR_PRE_DEFAULTS, VENDOR_POST_DEFAULTS -- set vendor-specific defaults
+**
+** Vendor_pre_defaults is called before reading the configuration
+** file; vendor_post_defaults is called immediately after.
+**
+** Parameters:
+** e -- the global environment to initialize.
+**
+** Returns:
+** none.
+*/
+
+#if SHARE_V1
+int DefShareUid; /* default share uid to run as -- unused??? */
+#endif
+
+void
+vendor_pre_defaults(e)
+ ENVELOPE *e;
+{
+#if SHARE_V1
+ /* OTHERUID is defined in shares.h, do not be alarmed */
+ DefShareUid = OTHERUID;
+#endif
+#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
+ sun_pre_defaults(e);
+#endif
+#ifdef apollo
+ /* stupid domain/os can't even open /etc/sendmail.cf without this */
+ setuserenv("ISP", NULL);
+ setuserenv("SYSTYPE", NULL);
+#endif
+}
+
+
+void
+vendor_post_defaults(e)
+ ENVELOPE *e;
+{
+#ifdef __QNX__
+ char *p;
+
+ /* Makes sure the SOCK environment variable remains */
+ if (p = getextenv("SOCK"))
+ setuserenv("SOCK", p);
+#endif
+#if defined(SUN_EXTENSIONS) && defined(SUN_DEFAULT_VALUES)
+ sun_post_defaults(e);
+#endif
+}
+ /*
+** VENDOR_DAEMON_SETUP -- special vendor setup needed for daemon mode
+*/
+
+void
+vendor_daemon_setup(e)
+ ENVELOPE *e;
+{
+#if SECUREWARE
+ if (getluid() != -1)
+ {
+ usrerr("Daemon cannot have LUID");
+ exit(EX_USAGE);
+ }
+#endif /* SECUREWARE */
+}
+ /*
+** VENDOR_SET_UID -- do setup for setting a user id
+**
+** This is called when we are still root.
+**
+** Parameters:
+** uid -- the uid we are about to become.
+**
+** Returns:
+** none.
+*/
+
+void
+vendor_set_uid(uid)
+ UID_T uid;
+{
+ /*
+ ** We need to setup the share groups (lnodes)
+ ** and and auditing inforation (luid's)
+ ** before we loose our ``root''ness.
+ */
+#if SHARE_V1
+ if (setupshares(uid, syserr) != 0)
+ syserr("Unable to set up shares");
+#endif
+#if SECUREWARE
+ (void) setup_secure(uid);
+#endif
+}
+ /*
+** VALIDATE_CONNECTION -- check connection for rationality
+**
+** If the connection is rejected, this routine should log an
+** appropriate message -- but should never issue any SMTP protocol.
+**
+** Parameters:
+** sap -- a pointer to a SOCKADDR naming the peer.
+** hostname -- the name corresponding to sap.
+** e -- the current envelope.
+**
+** Returns:
+** error message from rejection.
+** NULL if not rejected.
+*/
+
+#if TCPWRAPPERS
+# include <tcpd.h>
+
+/* tcpwrappers does no logging, but you still have to declare these -- ugh */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_NOTICE;
+#endif
+
+#if DAEMON
+char *
+validate_connection(sap, hostname, e)
+ SOCKADDR *sap;
+ char *hostname;
+ ENVELOPE *e;
+{
+#if TCPWRAPPERS
+ char *host;
+#endif
+
+ if (tTd(48, 3))
+ printf("validate_connection(%s, %s)\n",
+ hostname, anynet_ntoa(sap));
+
+ if (rscheck("check_relay", hostname, anynet_ntoa(sap), e) != EX_OK)
+ {
+ static char reject[BUFSIZ*2];
+ extern char MsgBuf[];
+
+ if (tTd(48, 4))
+ printf(" ... validate_connection: BAD (rscheck)\n");
+
+ if (strlen(MsgBuf) > 5)
+ {
+ if (isascii(MsgBuf[0]) && isdigit(MsgBuf[0]) &&
+ isascii(MsgBuf[1]) && isdigit(MsgBuf[1]) &&
+ isascii(MsgBuf[2]) && isdigit(MsgBuf[2]))
+ strcpy(reject, &MsgBuf[4]);
+ else
+ strcpy(reject, MsgBuf);
+ }
+ else
+ strcpy(reject, "Access denied");
+
+ return reject;
+ }
+
+#if TCPWRAPPERS
+ if (hostname[0] == '[' && hostname[strlen(hostname) - 1] == ']')
+ host = "unknown";
+ else
+ host = hostname;
+ if (!hosts_ctl("sendmail", host, anynet_ntoa(sap), STRING_UNKNOWN))
+ {
+ if (tTd(48, 4))
+ printf(" ... validate_connection: BAD (tcpwrappers)\n");
+ if (LogLevel >= 4)
+ sm_syslog(LOG_NOTICE, NOQID,
+ "tcpwrappers (%s, %s) rejection",
+ host, anynet_ntoa(sap));
+ return "Access denied";
+ }
+#endif
+ if (tTd(48, 4))
+ printf(" ... validate_connection: OK\n");
+ return NULL;
+}
+
+#endif
+ /*
+** STRTOL -- convert string to long integer
+**
+** For systems that don't have it in the C library.
+**
+** This is taken verbatim from the 4.4-Lite C library.
+*/
+
+#ifdef NEEDSTRTOL
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtol.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Convert a string to a long integer.
+ *
+ * Ignores `locale' stuff. Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+
+long
+strtol(nptr, endptr, base)
+ const char *nptr;
+ char **endptr;
+ register int base;
+{
+ register const char *s = nptr;
+ register unsigned long acc;
+ register int c;
+ register unsigned long cutoff;
+ register int neg = 0, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ do {
+ c = *s++;
+ } while (isspace(c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else if (c == '+')
+ c = *s++;
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X')) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX;
+ cutlim = cutoff % (unsigned long)base;
+ cutoff /= (unsigned long)base;
+ for (acc = 0, any = 0;; c = *s++) {
+ if (isdigit(c))
+ c -= '0';
+ else if (isalpha(c))
+ c -= isupper(c) ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim)
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LONG_MIN : LONG_MAX;
+ errno = ERANGE;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != 0)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
+
+#endif
+ /*
+** STRSTR -- find first substring in string
+**
+** Parameters:
+** big -- the big (full) string.
+** little -- the little (sub) string.
+**
+** Returns:
+** A pointer to the first instance of little in big.
+** big if little is the null string.
+** NULL if little is not contained in big.
+*/
+
+#ifdef NEEDSTRSTR
+
+char *
+strstr(big, little)
+ char *big;
+ char *little;
+{
+ register char *p = big;
+ int l;
+
+ if (*little == '\0')
+ return big;
+ l = strlen(little);
+
+ while ((p = strchr(p, *little)) != NULL)
+ {
+ if (strncmp(p, little, l) == 0)
+ return p;
+ p++;
+ }
+ return NULL;
+}
+
+#endif
+ /*
+** SM_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
+**
+** Some operating systems have wierd problems with the gethostbyXXX
+** routines. For example, Solaris versions at least through 2.3
+** don't properly deliver a canonical h_name field. This tries to
+** work around these problems.
+*/
+
+struct hostent *
+sm_gethostbyname(name)
+ char *name;
+{
+ struct hostent *h;
+#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204) || (defined(sony_news) && defined(__svr4))
+# if SOLARIS == 20300 || SOLARIS == 203
+ static struct hostent hp;
+ static char buf[1000];
+ extern struct hostent *_switch_gethostbyname_r();
+
+ if (tTd(61, 10))
+ printf("_switch_gethostbyname_r(%s)... ", name);
+ h = _switch_gethostbyname_r(name, &hp, buf, sizeof(buf), &h_errno);
+# else
+ extern struct hostent *__switch_gethostbyname();
+
+ if (tTd(61, 10))
+ printf("__switch_gethostbyname(%s)... ", name);
+ h = __switch_gethostbyname(name);
+# endif
+#else
+ int nmaps;
+ char *maptype[MAXMAPSTACK];
+ short mapreturn[MAXMAPACTIONS];
+ char hbuf[MAXNAME];
+
+ if (tTd(61, 10))
+ printf("gethostbyname(%s)... ", name);
+ h = gethostbyname(name);
+ if (h == NULL)
+ {
+ if (tTd(61, 10))
+ printf("failure\n");
+
+ nmaps = switch_map_find("hosts", maptype, mapreturn);
+ while (--nmaps >= 0)
+ if (strcmp(maptype[nmaps], "nis") == 0 ||
+ strcmp(maptype[nmaps], "files") == 0)
+ break;
+ if (nmaps >= 0)
+ {
+ /* try short name */
+ if (strlen(name) > (SIZE_T) sizeof hbuf - 1)
+ return NULL;
+ strcpy(hbuf, name);
+ shorten_hostname(hbuf);
+
+ /* if it hasn't been shortened, there's no point */
+ if (strcmp(hbuf, name) != 0)
+ {
+ if (tTd(61, 10))
+ printf("gethostbyname(%s)... ", hbuf);
+ h = gethostbyname(hbuf);
+ }
+ }
+ }
+#endif
+ if (tTd(61, 10))
+ {
+ if (h == NULL)
+ printf("failure\n");
+ else
+ printf("%s\n", h->h_name);
+ }
+ return h;
+}
+
+struct hostent *
+sm_gethostbyaddr(addr, len, type)
+ char *addr;
+ int len;
+ int type;
+{
+#if (SOLARIS > 10000 && SOLARIS < 20400) || (defined(SOLARIS) && SOLARIS < 204)
+# if SOLARIS == 20300 || SOLARIS == 203
+ static struct hostent hp;
+ static char buf[1000];
+ extern struct hostent *_switch_gethostbyaddr_r();
+
+ return _switch_gethostbyaddr_r(addr, len, type, &hp, buf, sizeof(buf), &h_errno);
+# else
+ extern struct hostent *__switch_gethostbyaddr();
+
+ return __switch_gethostbyaddr(addr, len, type);
+# endif
+#else
+ return gethostbyaddr(addr, len, type);
+#endif
+}
+ /*
+** SM_GETPW{NAM,UID} -- wrapper for getpwnam and getpwuid
+*/
+
+struct passwd *
+sm_getpwnam(user)
+ char *user;
+{
+#ifdef _AIX4
+ extern struct passwd *_getpwnam_shadow(const char *, const int);
+
+ return _getpwnam_shadow(user, 0);
+#else
+ return getpwnam(user);
+#endif
+}
+
+struct passwd *
+sm_getpwuid(uid)
+ UID_T uid;
+{
+#if defined(_AIX4) && 0
+ extern struct passwd *_getpwuid_shadow(const int, const int);
+
+ return _getpwuid_shadow(uid,0);
+#else
+ return getpwuid(uid);
+#endif
+}
+ /*
+** SECUREWARE_SETUP_SECURE -- Convex SecureWare setup
+**
+** Set up the trusted computing environment for C2 level security
+** under SecureWare.
+**
+** Parameters:
+** uid -- uid of the user to initialize in the TCB
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Initialized the user in the trusted computing base
+*/
+
+#if SECUREWARE
+
+# include <sys/security.h>
+# include <prot.h>
+
+void
+secureware_setup_secure(uid)
+ UID_T uid;
+{
+ int rc;
+
+ if (getluid() != -1)
+ return;
+
+ if ((rc = set_secure_info(uid)) != SSI_GOOD_RETURN)
+ {
+ switch (rc)
+ {
+ case SSI_NO_PRPW_ENTRY:
+ syserr("No protected passwd entry, uid = %d", uid);
+ break;
+
+ case SSI_LOCKED:
+ syserr("Account has been disabled, uid = %d", uid);
+ break;
+
+ case SSI_RETIRED:
+ syserr("Account has been retired, uid = %d", uid);
+ break;
+
+ case SSI_BAD_SET_LUID:
+ syserr("Could not set LUID, uid = %d", uid);
+ break;
+
+ case SSI_BAD_SET_PRIVS:
+ syserr("Could not set kernel privs, uid = %d", uid);
+
+ default:
+ syserr("Unknown return code (%d) from set_secure_info(%d)",
+ rc, uid);
+ break;
+ }
+ exit(EX_NOPERM);
+ }
+}
+#endif /* SECUREWARE */
+ /*
+** LOAD_IF_NAMES -- load interface-specific names into $=w
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Loads $=w with the names of all the interfaces.
+*/
+
+#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
+struct rtentry;
+struct mbuf;
+# include <arpa/inet.h>
+# ifndef SUNOS403
+# include <sys/time.h>
+# endif
+# if _AIX4 >= 40300
+# undef __P
+# endif
+# include <net/if.h>
+#endif
+
+void
+load_if_names()
+{
+#if defined(SIOCGIFCONF) && !SIOCGIFCONF_IS_BROKEN
+ int s;
+ int i;
+ struct ifconf ifc;
+ int numifs;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1)
+ return;
+
+ /* get the list of known IP address from the kernel */
+# if defined(SIOCGIFNUM) && !SIOCGIFNUM_IS_BROKEN
+ if (ioctl(s, SIOCGIFNUM, (char *) &numifs) < 0)
+ {
+ /* can't get number of interfaces -- fall back */
+ if (tTd(0, 4))
+ printf("SIOCGIFNUM failed: %s\n", errstring(errno));
+ numifs = -1;
+ }
+ else if (tTd(0, 42))
+ printf("system has %d interfaces\n", numifs);
+ if (numifs < 0)
+# endif
+ numifs = 512;
+
+ if (numifs <= 0)
+ {
+ close(s);
+ return;
+ }
+ ifc.ifc_len = numifs * sizeof (struct ifreq);
+ ifc.ifc_buf = xalloc(ifc.ifc_len);
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0)
+ {
+ if (tTd(0, 4))
+ printf("SIOGIFCONF failed: %s\n", errstring(errno));
+ close(s);
+ return;
+ }
+
+ /* scan the list of IP address */
+ if (tTd(0, 40))
+ printf("scanning for interface specific names, ifc_len=%d\n",
+ ifc.ifc_len);
+
+ for (i = 0; i < ifc.ifc_len; )
+ {
+ struct ifreq *ifr = (struct ifreq *) &ifc.ifc_buf[i];
+ struct sockaddr *sa = &ifr->ifr_addr;
+ struct in_addr ia;
+ struct hostent *hp;
+#ifdef SIOCGIFFLAGS
+ struct ifreq ifrf;
+#endif
+ char ip_addr[256];
+ extern char *inet_ntoa();
+
+#ifdef BSD4_4_SOCKADDR
+ if (sa->sa_len > sizeof ifr->ifr_addr)
+ i += sizeof ifr->ifr_name + sa->sa_len;
+ else
+#endif
+ i += sizeof *ifr;
+
+ if (tTd(0, 20))
+ printf("%s\n", anynet_ntoa((SOCKADDR *) sa));
+
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+
+#ifdef SIOCGIFFLAGS
+ bzero(&ifrf, sizeof(struct ifreq));
+ strncpy(ifrf.ifr_name, ifr->ifr_name, sizeof(ifrf.ifr_name));
+ ioctl(s, SIOCGIFFLAGS, (char *) &ifrf);
+ if (tTd(0, 41))
+ printf("\tflags: %x\n", ifrf.ifr_flags);
+# define IFRFREF ifrf
+#else
+# define IFRFREF (*ifr)
+#endif
+ if (!bitset(IFF_UP, IFRFREF.ifr_flags))
+ continue;
+
+ /* extract IP address from the list*/
+ ia = (((struct sockaddr_in *) sa)->sin_addr);
+ if (ia.s_addr == INADDR_ANY || ia.s_addr == INADDR_NONE)
+ {
+ message("WARNING: interface %s is UP with %s address",
+ ifr->ifr_name, inet_ntoa(ia));
+ continue;
+ }
+
+ /* save IP address in text from */
+ (void) snprintf(ip_addr, sizeof ip_addr, "[%.*s]",
+ sizeof ip_addr - 3,
+ inet_ntoa(ia));
+ if (!wordinclass(ip_addr, 'w'))
+ {
+ setclass('w', ip_addr);
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", ip_addr);
+ }
+
+ /* skip "loopback" interface "lo" */
+ if (bitset(IFF_LOOPBACK, IFRFREF.ifr_flags))
+ continue;
+
+ /* lookup name with IP address */
+ hp = sm_gethostbyaddr((char *) &ia, sizeof(ia), AF_INET);
+ if (hp == NULL)
+ {
+ if (LogLevel > 3)
+ sm_syslog(LOG_WARNING, NOQID,
+ "gethostbyaddr(%.100s) failed: %d\n",
+ inet_ntoa(ia),
+#if NAMED_BIND
+ h_errno);
+#else
+ -1);
+#endif
+ continue;
+ }
+
+ /* save its cname */
+ if (!wordinclass((char *) hp->h_name, 'w'))
+ {
+ setclass('w', (char *) hp->h_name);
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", hp->h_name);
+ }
+
+ /* save all it aliases name */
+ while (*hp->h_aliases)
+ {
+ if (!wordinclass(*hp->h_aliases, 'w'))
+ {
+ setclass('w', *hp->h_aliases);
+ if (tTd(0, 4))
+ printf("\ta.k.a.: %s\n", *hp->h_aliases);
+ }
+ hp->h_aliases++;
+ }
+ }
+ free(ifc.ifc_buf);
+ close(s);
+# undef IFRFREF
+#endif
+}
+ /*
+** GET_NUM_PROCS_ONLINE -- return the number of processors currently online
+**
+** Parameters:
+** none.
+**
+** Returns:
+** The number of processors online.
+*/
+
+int
+get_num_procs_online()
+{
+ int nproc = 0;
+
+#if _FFR_SCALE_LA_BY_NUM_PROCS
+#ifdef _SC_NPROCESSORS_ONLN
+ nproc = (int) sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+#endif
+ if (nproc <= 0)
+ nproc = 1;
+ return nproc;
+}
+ /*
+** SM_SYSLOG -- syslog wrapper to keep messages under SYSLOG_BUFSIZE
+**
+** Parameters:
+** level -- syslog level
+** id -- envelope ID or NULL (NOQUEUE)
+** fmt -- format string
+** arg... -- arguments as implied by fmt.
+**
+** Returns:
+** none
+*/
+
+/* VARARGS3 */
+void
+# ifdef __STDC__
+sm_syslog(int level, const char *id, const char *fmt, ...)
+# else
+sm_syslog(level, id, fmt, va_alist)
+ int level;
+ const char *id;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ static char *buf = NULL;
+ static size_t bufsize = MAXLINE;
+ char *begin, *end;
+ int seq = 1;
+ int idlen;
+ extern int SnprfOverflow;
+ extern int SyslogErrno;
+ extern char *DoprEnd;
+ VA_LOCAL_DECL
+ extern void sm_dopr __P((char *, const char *, ...));
+
+ SyslogErrno = errno;
+ if (id == NULL)
+ {
+ id = "NOQUEUE";
+ idlen = 9;
+ }
+ else if (strcmp(id, NOQID) == 0)
+ {
+ id = "";
+ idlen = 0;
+ }
+ else
+ idlen = strlen(id + 2);
+bufalloc:
+ if (buf == NULL)
+ buf = (char *) xalloc(sizeof(char) * bufsize);
+
+ /* do a virtual vsnprintf into buf */
+ VA_START(fmt);
+ buf[0] = 0;
+ DoprEnd = buf + bufsize - 1;
+ SnprfOverflow = 0;
+ sm_dopr(buf, fmt, ap);
+ *DoprEnd = '\0';
+ VA_END;
+ /* end of virtual vsnprintf */
+
+ if (SnprfOverflow)
+ {
+ /* String too small, redo with correct size */
+ bufsize += SnprfOverflow + 1;
+ free(buf);
+ buf = NULL;
+ goto bufalloc;
+ }
+ if ((strlen(buf) + idlen + 1) < SYSLOG_BUFSIZE)
+ {
+#if LOG
+ if (*id == '\0')
+ syslog(level, "%s", buf);
+ else
+ syslog(level, "%s: %s", id, buf);
+#else
+ /*XXX should do something more sensible */
+ if (*id == '\0')
+ fprintf(stderr, "%s\n", buf);
+ else
+ fprintf(stderr, "%s: %s\n", id, buf);
+#endif
+ return;
+ }
+
+ begin = buf;
+ while (*begin != '\0' &&
+ (strlen(begin) + idlen + 5) > SYSLOG_BUFSIZE)
+ {
+ char save;
+
+ if (seq == 999)
+ {
+ /* Too many messages */
+ break;
+ }
+ end = begin + SYSLOG_BUFSIZE - idlen - 12;
+ while (end > begin)
+ {
+ /* Break on comma or space */
+ if (*end == ',' || *end == ' ')
+ {
+ end++; /* Include separator */
+ break;
+ }
+ end--;
+ }
+ /* No separator, break midstring... */
+ if (end == begin)
+ end = begin + SYSLOG_BUFSIZE - idlen - 12;
+ save = *end;
+ *end = 0;
+#if LOG
+ syslog(level, "%s[%d]: %s ...", id, seq++, begin);
+#else
+ fprintf(stderr, "%s[%d]: %s ...\n", id, seq++, begin);
+#endif
+ *end = save;
+ begin = end;
+ }
+ if (seq == 999)
+#if LOG
+ syslog(level, "%s[%d]: log terminated, too many parts", id, seq);
+#else
+ fprintf(stderr, "%s[%d]: log terminated, too many parts\n", id, seq);
+#endif
+ else if (*begin != '\0')
+#if LOG
+ syslog(level, "%s[%d]: %s", id, seq, begin);
+#else
+ fprintf(stderr, "%s[%d]: %s\n", id, seq, begin);
+#endif
+}
+ /*
+** HARD_SYSLOG -- call syslog repeatedly until it works
+**
+** Needed on HP-UX, which apparently doesn't guarantee that
+** syslog succeeds during interrupt handlers.
+*/
+
+#if defined(__hpux) && !defined(HPUX11)
+
+# define MAXSYSLOGTRIES 100
+# undef syslog
+# ifdef V4FS
+# define XCNST const
+# define CAST (const char *)
+# else
+# define XCNST
+# define CAST
+# endif
+
+void
+# ifdef __STDC__
+hard_syslog(int pri, XCNST char *msg, ...)
+# else
+hard_syslog(pri, msg, va_alist)
+ int pri;
+ XCNST char *msg;
+ va_dcl
+# endif
+{
+ int i;
+ char buf[SYSLOG_BUFSIZE];
+ VA_LOCAL_DECL;
+
+ VA_START(msg);
+ vsnprintf(buf, sizeof buf, msg, ap);
+ VA_END;
+
+ for (i = MAXSYSLOGTRIES; --i >= 0 && syslog(pri, CAST "%s", buf) < 0; )
+ continue;
+}
+
+# undef CAST
+#endif
+ /*
+** LOCAL_HOSTNAME_LENGTH
+**
+** This is required to get sendmail to compile against BIND 4.9.x
+** on Ultrix.
+*/
+
+#if defined(ultrix) && NAMED_BIND
+
+# include <resolv.h>
+# if __RES >= 19931104 && __RES < 19950621
+
+int
+local_hostname_length(hostname)
+ char *hostname;
+{
+ int len_host, len_domain;
+
+ if (!*_res.defdname)
+ res_init();
+ len_host = strlen(hostname);
+ len_domain = strlen(_res.defdname);
+ if (len_host > len_domain &&
+ (strcasecmp(hostname + len_host - len_domain,_res.defdname) == 0) &&
+ hostname[len_host - len_domain - 1] == '.')
+ return len_host - len_domain - 1;
+ else
+ return 0;
+}
+
+# endif
+#endif
+ /*
+** Compile-Time options
+*/
+
+char *CompileOptions[] =
+{
+#ifdef HESIOD
+ "HESIOD",
+#endif
+#if HES_GETMAILHOST
+ "HES_GETMAILHOST",
+#endif
+#ifdef LDAPMAP
+ "LDAPMAP",
+#endif
+#ifdef MAP_REGEX
+ "MAP_REGEX",
+#endif
+#if LOG
+ "LOG",
+#endif
+#if MATCHGECOS
+ "MATCHGECOS",
+#endif
+#if MIME7TO8
+ "MIME7TO8",
+#endif
+#if MIME8TO7
+ "MIME8TO7",
+#endif
+#if NAMED_BIND
+ "NAMED_BIND",
+#endif
+#ifdef NDBM
+ "NDBM",
+#endif
+#if NETINET
+ "NETINET",
+#endif
+#if NETINFO
+ "NETINFO",
+#endif
+#if NETISO
+ "NETISO",
+#endif
+#if NETNS
+ "NETNS",
+#endif
+#if NETUNIX
+ "NETUNIX",
+#endif
+#if NETX25
+ "NETX25",
+#endif
+#ifdef NEWDB
+ "NEWDB",
+#endif
+#ifdef NIS
+ "NIS",
+#endif
+#ifdef NISPLUS
+ "NISPLUS",
+#endif
+#if QUEUE
+ "QUEUE",
+#endif
+#if SCANF
+ "SCANF",
+#endif
+#if SMTP
+ "SMTP",
+#endif
+#if SMTPDEBUG
+ "SMTPDEBUG",
+#endif
+#ifdef SUID_ROOT_FILES_OK
+ "SUID_ROOT_FILES_OK",
+#endif
+#if TCPWRAPPERS
+ "TCPWRAPPERS",
+#endif
+#if USERDB
+ "USERDB",
+#endif
+#if XDEBUG
+ "XDEBUG",
+#endif
+#ifdef XLA
+ "XLA",
+#endif
+ NULL
+};
+
+
+/*
+** OS compile options.
+*/
+
+char *OsCompileOptions[] =
+{
+#if BOGUS_O_EXCL
+ "BOGUS_O_EXCL",
+#endif
+#if HASFCHMOD
+ "HASFCHMOD",
+#endif
+#if HASFLOCK
+ "HASFLOCK",
+#endif
+#if HASGETDTABLESIZE
+ "HASGETDTABLESIZE",
+#endif
+#if HASGETUSERSHELL
+ "HASGETUSERSHELL",
+#endif
+#if HASINITGROUPS
+ "HASINITGROUPS",
+#endif
+#if HASLSTAT
+ "HASLSTAT",
+#endif
+#if HASSETREUID
+ "HASSETREUID",
+#endif
+#if HASSETRLIMIT
+ "HASSETRLIMIT",
+#endif
+#if HASSETSID
+ "HASSETSID",
+#endif
+#if HASSETUSERCONTEXT
+ "HASSETUSERCONTEXT",
+#endif
+#if HASSETVBUF
+ "HASSETVBUF",
+#endif
+#if HASSNPRINTF
+ "HASSNPRINTF",
+#endif
+#if HAS_ST_GEN
+ "HAS_ST_GEN",
+#endif
+#if HASSTRERROR
+ "HASSTRERROR",
+#endif
+#if HASULIMIT
+ "HASULIMIT",
+#endif
+#if HASUNAME
+ "HASUNAME",
+#endif
+#if HASUNSETENV
+ "HASUNSETENV",
+#endif
+#if HASWAITPID
+ "HASWAITPID",
+#endif
+#if IDENTPROTO
+ "IDENTPROTO",
+#endif
+#if IP_SRCROUTE
+ "IP_SRCROUTE",
+#endif
+#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
+ "LOCK_ON_OPEN",
+#endif
+#if NEEDFSYNC
+ "NEEDFSYNC",
+#endif
+#if NOFTRUNCATE
+ "NOFTRUNCATE",
+#endif
+#if RLIMIT_NEEDS_SYS_TIME_H
+ "RLIMIT_NEEDS_SYS_TIME_H",
+#endif
+#if SAFENFSPATHCONF
+ "SAFENFSPATHCONF",
+#endif
+#if SECUREWARE
+ "SECUREWARE",
+#endif
+#if SHARE_V1
+ "SHARE_V1",
+#endif
+#if SIOCGIFCONF_IS_BROKEN
+ "SIOCGIFCONF_IS_BROKEN",
+#endif
+#if SIOCGIFNUM_IS_BROKEN
+ "SIOCGIFNUM_IS_BROKEN",
+#endif
+#if SYS5SETPGRP
+ "SYS5SETPGRP",
+#endif
+#if SYSTEM5
+ "SYSTEM5",
+#endif
+#if USE_SA_SIGACTION
+ "USE_SA_SIGACTION",
+#endif
+#if USE_SIGLONGJMP
+ "USE_SIGLONGJMP",
+#endif
+#if USESETEUID
+ "USESETEUID",
+#endif
+ NULL
+};