* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)conf.c 8.133 (Berkeley) %G%";
** 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.
** 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!
** 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
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
,
"return-receipt-to", H_FROM
|H_RECEIPTTO
,
"errors-to", H_FROM
|H_ERRORSTO
,
"resent-to", H_RCPT
|H_RESENT
,
"resent-cc", H_RCPT
|H_RESENT
,
"resent-bcc", H_RCPT
|H_ACHECK
|H_RESENT
,
/* message identification and control */
"resent-message-id", H_RESENT
,
"received", H_TRACE
|H_FORCE
,
"x400-received", H_TRACE
|H_FORCE
,
"mail-from", H_TRACE
|H_FORCE
,
/* miscellaneous fields */
"return-path", H_FORCE
|H_ACHECK
,
"content-transfer-encoding", H_CTE
,
** Location of system files/databases/etc.
char *PidFile
= _PATH_SENDMAILPID
; /* stores daemon proc id */
struct prival PrivacyValues
[] =
"needmailhelo", PRIV_NEEDMAILHELO
,
"needexpnhelo", PRIV_NEEDEXPNHELO
,
"needvrfyhelo", PRIV_NEEDVRFYHELO
,
"restrictmailq", PRIV_RESTRICTMAILQ
,
"restrictqrun", PRIV_RESTRICTQRUN
,
"authwarnings", PRIV_AUTHWARNINGS
,
"noreceipts", PRIV_NORECEIPTS
,
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
** e -- the default envelope.
** Initializes a bunch of global variables to their
#define DAYS * 24 * 60 * 60
extern void inittimeouts();
extern void setdefuser();
extern void setupmailers();
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;
DefUid
= 1; /* option u */
DefGid
= 1; /* option g */
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
= 300; /* option K */
LogLevel
= 9; /* option L */
inittimeouts(NULL
); /* option r */
PrivacyFlags
= 0; /* option p */
MimeMode
= MM_CVTMIME
|MM_PASS8BIT
; /* option 8 */
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";
** SETDEFUSER -- set/reset DefUser using DefUid (for initgroups())
static char defuserbuf
[40];
if ((defpwent
= getpwuid(DefUid
)) != NULL
)
strcpy(defuserbuf
, defpwent
->pw_name
);
strcpy(defuserbuf
, "nobody");
** HOST_MAP_INIT -- initialize host class structures
bool host_map_init
__P((MAP
*map
, char *args
));
while (isascii(*p
) && isspace(*p
))
while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
if (map
->map_app
!= NULL
)
map
->map_app
= newstr(map
->map_app
);
** SETUPMAILERS -- initialize default mailers
extern void makemailer();
strcpy(buf
, "prog, P=/bin/sh, F=lsoD, A=sh -c $u");
strcpy(buf
, "*file*, P=/dev/null, F=lsDFMPEou, A=FILE");
strcpy(buf
, "*include*, P=/dev/null, F=su, A=INCLUDE");
** 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; \
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
);
MAPDEF("dbm", ".dir", MCF_ALIASOK
|MCF_REBUILDABLE
,
map_parseargs
, ndbm_map_open
, ndbm_map_close
,
ndbm_map_lookup
, ndbm_map_store
);
MAPDEF("nis", NULL
, MCF_ALIASOK
,
map_parseargs
, nis_map_open
, null_map_close
,
nis_map_lookup
, null_map_store
);
MAPDEF("nisplus", NULL
, MCF_ALIASOK
,
map_parseargs
, nisplus_map_open
, null_map_close
,
nisplus_map_lookup
, null_map_store
);
MAPDEF("hesiod", NULL
, MCF_ALIASOK
|MCF_ALIASONLY
,
map_parseargs
, null_map_open
, null_map_close
,
hes_map_lookup
, null_map_store
);
MAPDEF("netinfo", NULL
, MCF_ALIASOK
,
map_parseargs
, ni_map_open
, null_map_close
,
ni_map_lookup
, null_map_store
);
dns_map_init
, null_map_open
, null_map_close
,
dns_map_lookup
, null_map_store
);
MAPDEF("bestmx", NULL
, MCF_OPTFILE
,
map_parseargs
, null_map_open
, null_map_close
,
bestmx_map_lookup
, null_map_store
);
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
);
MAPDEF("dequote", NULL
, 0,
dequote_init
, null_map_open
, null_map_close
,
dequote_map
, null_map_store
);
udb_map_parse
, null_map_open
, null_map_close
,
udb_map_lookup
, null_map_store
);
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
);
** INITHOSTMAPS -- initial host-dependent maps
** This should act as an interface to any local service switch
** provided by the host operating system.
** 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.
char *maptype
[MAXMAPSTACK
];
short mapreturn
[MAXMAPACTIONS
];
** Set up default hosts maps.
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");
else if (strcmp(maptype
[i
], "dns") == 0 &&
stab("hosts.dns", ST_MAP
, ST_FIND
) == NULL
)
strcpy(buf
, "hosts.dns dns A");
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");
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");
** 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");
** 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 implicit /etc/aliases");
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");
else if (strcmp(maptype
[i
], "nis") == 0 &&
stab("aliases.nis", ST_MAP
, ST_FIND
) == NULL
)
strcpy(buf
, "aliases.nis nis -d mail.aliases");
if (stab("aliases", ST_MAP
, ST_FIND
) == NULL
)
strcpy(buf
, "aliases switch aliases");
strcpy(buf
, "switch:aliases");
#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");
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");
else if (strcmp(maptype
[i
], "nis") == 0 &&
stab("users.nis", ST_MAP
, ST_FIND
) == NULL
)
strcpy(buf
, "users.nis nis -m -d passwd.byname");
else if (strcmp(maptype
[i
], "hesiod") == 0) &&
stab("users.hesiod", ST_MAP
, ST_FIND
) == NULL
)
strcpy(buf
, "users.hesiod hesiod");
if (stab("users", ST_MAP
, ST_FIND
) == NULL
)
strcpy(buf
, "users switch -m passwd");
** SWITCH_MAP_FIND -- find the list of types associated with a map
** This is the system-dependent interface to the service switch.
** 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
** The number of map types filled in, or -1 for failure.
#if defined(ultrix) || defined(__osf__)
# include <sys/svcinfo.h>
switch_map_find(service
, maptype
, mapreturn
)
char *maptype
[MAXMAPSTACK
];
short mapreturn
[MAXMAPACTIONS
];
static char buf
[MAXLINE
];
struct __nsw_switchconfig
*nsw_conf
;
enum __nsw_parse_err pserr
;
static struct __nsw_lookup lkp0
=
{ "files", {1, 0, 0, 0}, NULL
, NULL
};
static struct __nsw_switchconfig lkp_default
=
{ 0, "sendmail", 3, &lkp0
};
if ((nsw_conf
= __nsw_getconfig(service
, &pserr
)) == NULL
)
lk
= lkp_default
.lookups
;
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
;
#if defined(ultrix) || defined(__osf__)
if (strcmp(service
, "hosts") == 0)
else if (strcmp(service
, "aliases") == 0)
else if (strcmp(service
, "passwd") == 0)
for (svcno
= 0; svcno
< SVC_PATHSIZE
; svcno
++)
switch (svcinfo
->svcpath
[svc
][svcno
])
maptype
[svcno
] = "files";
maptype
[svcno
] = "hesiod";
#if !defined(SOLARIS) && !defined(ultrix) && !defined(__osf__)
fp
= fopen(ServiceSwitchFile
, "r");
while (fgets(buf
, sizeof buf
, fp
) != NULL
)
if (strcmp(buf
, service
) != 0)
/* got the right service -- extract data */
/* if the service file doesn't work, use an absolute fallback */
if (strcmp(service
, "aliases") == 0)
if (strcmp(service
, "hosts") == 0)
maptype
[svcno
++] = "dns";
# if defined(sun) && !defined(BSD) && !defined(SOLARIS)
maptype
[svcno
++] = "nis";
maptype
[svcno
++] = "files";
** USERNAME -- return the user id of the logged in user.
** The login name of the logged in user.
** The return value is statically allocated.
static char *myname
= NULL
;
register struct passwd
*pw
;
if (myname
== NULL
|| myname
[0] == '\0')
myname
= newstr(pw
->pw_name
);
if ((pw
= getpwnam(myname
)) == NULL
||
(uid
!= 0 && uid
!= pw
->pw_uid
))
myname
= newstr(pw
->pw_name
);
if (myname
== NULL
|| myname
[0] == '\0')
syserr("554 Who are you?");
** 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
** pathname of the user's tty.
** NULL if not logged in or write permission denied.
** Return value is in a local buffer.
/* compute the pathname of the controlling tty */
if ((pathn
= ttyname(2)) == NULL
&& (pathn
= ttyname(1)) == NULL
&&
(pathn
= ttyname(0)) == NULL
)
/* see if we have write permission */
if (stat(pathn
, &stbuf
) < 0 || !bitset(02, stbuf
.st_mode
))
/* see if the user is logged in */
** 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 0 should
** EF_NORETURN can be set in e->e_flags to suppress the return-to-sender
** function; this should be done on huge messages.
** to -- the person being sent to.
** none (unless you include the usrerr stuff)
printf("checkcompat(to=%s, from=%s)\n",
to
->q_paddr
, e
->e_from
.q_paddr
);
/* this code is intended as an example only */
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_NORETURN; to supress return copy */
# endif /* EXAMPLE_CODE */
** SETSIGNAL -- set a signal handler
** This is essentially old BSD "signal(3)".
#if defined(SYS5SIGNALS) || defined(BSD4_3) || defined(_AUX_SOURCE)
return signal(sig
, handler
);
if (sigaction(sig
, &n
, &o
) < 0)
** HOLDSIGS -- arrange to hold all signals
** Arranges that signals are held.
** RLSESIGS -- arrange to release all signals
** This undoes the effect of holdsigs.
** Arranges that signals are released.
** INIT_MD -- do machine dependent initializations
** Systems that have global modes that should be set should do
** them here rather than in main.
setcompat(getcompat() | COMPAT_BSDPROT
);
VendorCode
= VENDOR_DEFAULT
;
VendorCode
= VENDOR_BERKELEY
;
** GETLA -- get the current load average
** This code stolen from la.c.
** The current load average as an integer.
/* 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 */
/* do guesses based on general OS type */
#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_FLOAT) || (LA_TYPE == LA_SHORT)
# define LA_AVENRUN "avenrun"
# define LA_AVENRUN "_avenrun"
/* _PATH_UNIX should be defined in <paths.h> */
# define _PATH_UNIX "/unix"
# define _PATH_UNIX "/vmunix"
# if defined(__alpha) || defined(IRIX)
# define FSCALE (1 << FSHIFT)
kmem
= open("/dev/kmem", 0, 0);
printf("getla: open(/dev/kmem): %s\n",
(void) fcntl(kmem
, F_SETFD
, 1);
if (knlist(Nl
, 1, sizeof Nl
[0]) < 0)
if (nlist(_PATH_UNIX
, Nl
) < 0)
printf("getla: nlist(%s): %s\n", _PATH_UNIX
,
if (Nl
[X_AVENRUN
].n_value
== 0)
printf("getla: nlist(%s, %s) ==> 0\n",
Nl
[X_AVENRUN
].n_value
&= NAMELISTMASK
;
printf("getla: symbol address = %#x\n", 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
))
printf("getla: lseek or read: %s\n", errstring(errno
));
#if (LA_TYPE == LA_INT) || (LA_TYPE == LA_SHORT)
printf("getla: avenrun = %d", avenrun
[0]);
printf(", %d, %d", avenrun
[1], avenrun
[2]);
printf("getla: %d\n", (int) (avenrun
[0] + FSCALE
/2) >> FSHIFT
);
return ((int) (avenrun
[0] + FSCALE
/2) >> FSHIFT
);
printf("getla: avenrun = %g", avenrun
[0]);
printf(", %g, %g", avenrun
[1], avenrun
[2]);
printf("getla: %d\n", (int) (avenrun
[0] +0.5));
return ((int) (avenrun
[0] + 0.5));
#include <sys/dg_sys_info.h>
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
);
printf("getla: %d\n", (int) (load_info
.one_minute
+ 0.5));
return((int) (load_info
.one_minute
+ 0.5));
if (pstat_getdynamic(&pstd
, sizeof(struct pst_dynamic
),
printf("getla: %d\n", (int) (pstd
.psd_avg_1_min
+ 0.5));
return (int) (pstd
.psd_avg_1_min
+ 0.5);
if (getloadavg(avenrun
, sizeof(avenrun
) / sizeof(avenrun
[0])) < 0)
perror("getla: getloadavg failed:");
printf("getla: %d\n", (int) (avenrun
[0] +0.5));
return ((int) (avenrun
[0] + 0.5));
** 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
processor_set_t default_set
;
struct processor_set_basic_info info
;
error
= processor_set_default(host_self(), &default_set
);
if (error
!= KERN_SUCCESS
)
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
)
return (int) (info
.load_average
+ (LOAD_SCALE
/ 2)) / LOAD_SCALE
;
#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.
# define _PATH_LOADAVG "/proc/loadavg"
fp
= fopen(_PATH_LOADAVG
, "r");
printf("getla: fopen(%s): %s\n",
_PATH_LOADAVG
, errstring(errno
));
result
= fscanf(fp
, "%lf", &avenrun
);
printf("getla: fscanf() = %d: %s\n",
result
, errstring(errno
));
printf("getla(): %.2f\n", avenrun
);
return ((int) (avenrun
+ 0.5));
* 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 */
static char rcsid
[] = "@(#)$Id: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $";
# include <apollo/base.h>
int getloadavg( call_data
)
caddr_t call_data
; /* pointer to (double) return value */
double *avenrun
= (double *) call_data
;
proc1_$
get_loadav(loadav
, &st
);
*avenrun
= loadav
[0] / (double) (1 << 16);
** SHOULDQUEUE -- should this message be queued or sent?
** Compares the message cost to the load average to decide.
** pri -- the priority of the message in question.
** ctime -- the message creation time.
** TRUE -- if this message should be queued up for the
** FALSE -- if the load is low enough to send this message.
printf("shouldqueue: CurrentLA=%d, pri=%d: ", CurrentLA
, pri
);
printf("FALSE (CurrentLA < QueueLA)\n");
if (CurrentLA
>= RefuseLA
)
printf("TRUE (CurrentLA >= RefuseLA)\n");
rval
= pri
> (QueueFactor
/ (CurrentLA
- QueueLA
+ 1));
printf("%s (by calculation)\n", rval
? "TRUE" : "FALSE");
** REFUSECONNECTIONS -- decide if connections should be refused
** TRUE if incoming SMTP connections should be refused
** FALSE if we should accept new work.
extern bool enoughspace();
/* this is probably too simplistic */
return CurrentLA
>= RefuseLA
|| !enoughspace(MinBlocksFree
+ 1);
** SETPROCTITLE -- set process title for ps
** fmt -- a printf style format string. If NULL, the first
** parameter is a literal proctitle previously
** returned by getproctitle.
** va_alist -- possible parameters to fmt.
** Clobbers argv of our main procedure so ps(1) will
#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_WRITEUDOT 5 /* write u. area in kmem */
# define SPT_TYPE SPT_REUSEARGV
#if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
# if SPT_TYPE == SPT_PSTAT
# if SPT_TYPE == SPT_PSSTRINGS
# include <machine/vmparam.h>
# ifndef PS_STRINGS /* hmmmm.... apparently not available after all */
# define SPT_TYPE SPT_REUSEARGV
# if SPT_TYPE == SPT_PSSTRINGS
# define SETPROC_STATIC static
#endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
#if SPT_TYPE != SPT_BUILTIN
char ProcTitleBuf
[MAXLINE
];
setproctitle(char *fmt
, ...)
setproctitle(fmt
, va_alist
)
# if SPT_TYPE != SPT_NONE
# if SPT_TYPE == SPT_PSTAT
/* restore old proctitle */
(void) strcpy(ProcTitleBuf
, va_arg(ap
, char *));
/* print sendmail: heading for grep */
(void) strcpy(p
, "sendmail: ");
/* print the argument string */
(void) vsprintf(p
, fmt
, ap
);
i
= strlen(ProcTitleBuf
);
if (i
> LastArgv
- Argv
[0] - 2)
i
= LastArgv
- Argv
[0] - 2;
(void) strcpy(Argv
[0], ProcTitleBuf
);
# endif /* SPT_TYPE == SPT_PSSTRINGS */
# endif /* SPT_TYPE == SPT_PSTAT */
# endif /* SPT_TYPE != SPT_NONE */
#endif /* SPT_TYPE != SPT_BUILTIN */
** REAPCHILD -- pick up the body of my child, lest it become a zombie
** sig -- the signal that got us here (unused).
** Picks up extant zombies.
while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0)
syslog(LOG_ALERT
, "reapchild: waitpid loop: pid=%d, status=%x",
while (wait3(&status
, WNOHANG
, (struct rusage
*) NULL
) > 0)
while (wait(&status
) > 0)
(void) setsignal(SIGCHLD
, reapchild
);
** UNSETENV -- remove a variable from the environment
** Not needed on newer systems.
** name -- the string name of the environment variable to be
** deleted from the current environment.
** environ -- a pointer to the current environment.
for (pp
= environ
; *pp
!= NULL
; pp
++)
if (strncmp(name
, *pp
, len
) == 0 &&
((*pp
)[len
] == '=' || (*pp
)[len
] == '\0'))
for (; *pp
!= NULL
; pp
++)
** GETDTABLESIZE -- return number of file descriptors
** Only on non-BSD systems
** size of file descriptor table
# include <sys/resource.h>
if (getrlimit(RLIMIT_NOFILE
, &rl
) >= 0)
return sysconf(_SC_OPEN_MAX
);
** UNAME -- get the UUCP name of this system.
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
);
n
= strchr(name
->nodename
, '\n');
if (name
->nodename
[0] != '\0')
/* try /usr/include/whoami.h -- has a #define somewhere */
if ((file
= fopen("/usr/include/whoami.h", "r")) != NULL
)
while (fgets(buf
, MAXLINE
, file
) != NULL
)
if (sscanf(buf
, "#define sysname \"%*[^\"]\"",
NODE_LENGTH
, name
->nodename
) > 0)
if (name
->nodename
[0] != '\0')
** 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
);
if (name
->nodename
[0] != '\0')
** INITGROUPS -- initialize groups
** Stub implementation for System V style systems
initgroups(name
, basegid
)
** SETSID -- set session id (for non-POSIX systems)
fd
= open("/dev/tty", O_RDWR
, 0);
(void) ioctl(fd
, (int) TIOCNOTTY
, (char *) 0);
return setpgid(0, getpid());
return fcntl(fd
, F_SETFL
, O_SYNC
);
** 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
** GETOPT -- for old systems or systems with bogus implementations
* 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 */
* get option letter from argument vector
extern int optind
, opterr
;
int opterr
= 1; /* if error message should be printed */
int optind
= 1; /* index into parent argv vector */
int optopt
; /* character checked for validity */
char *optarg
; /* argument associated with option */
#define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \
fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);}
static char *place
= EMSG
; /* option letter processing */
register char *oli
; /* option letter list index */
if(!*place
) { /* update scanning pointer */
if (optind
>= nargc
|| *(place
= nargv
[optind
]) != '-' || !*++place
) {
if (*place
== '-') { /* found "--" */
} /* option letter okay? */
if ((optopt
= (int)*place
++) == (int)':' || !(oli
= strchr(ostr
,optopt
))) {
tell(": illegal option -- ");
if (*++oli
!= ':') { /* don't need argument */
else { /* need an argument */
if (*place
) optarg
= place
; /* no white space */
else if (nargc
<= ++optind
) { /* no arg */
tell(": option requires an argument -- ");
else optarg
= nargv
[optind
]; /* white space */
return(optopt
); /* dump back option letter */
** VFPRINTF, VSPRINTF -- for old 4.3 BSD systems missing a real version
while (*ap
&& i
< MAXARG
)
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]);
while (*ap
&& i
< MAXARG
)
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]);
** USERSHELLOK -- tell if a user's shell is ok for unrestricted use
** shell -- the user's shell from /etc/passwd
** TRUE -- if it is ok to use this for unrestricted access.
** FALSE -- if the shell is restricted.
# define _PATH_SHELLS "/etc/shells"
char *DefaultUserShells
[] =
"/bin/sh", /* standard shell */
"/bin/csh", /* C shell */
"/bin/rsh", /* restricted Bourne shell */
"/bin/ksh", /* Korn shell */
"/bin/rksh", /* restricted Korn shell */
"/usr/bin/keysh", /* key shell (extended Korn shell) */
"/bin/ksh", /* Korn shell */
"/bin/tsh", /* trusted shell */
"/bin/bsh", /* Bourne shell */
#define WILDCARD_SHELL "/SENDMAIL/ANY/SHELL/"
extern char *getusershell();
while ((p
= getusershell()) != NULL
)
if (strcmp(p
, shell
) == 0 || strcmp(p
, WILDCARD_SHELL
) == 0)
shellf
= fopen(_PATH_SHELLS
, "r");
/* no /etc/shells; see if it is one of the std shells */
for (d
= DefaultUserShells
; *d
!= NULL
; d
++)
if (strcmp(shell
, *d
) == 0)
while (fgets(buf
, sizeof buf
, shellf
) != NULL
)
while (*p
!= '\0' && *p
!= '#' && *p
!= '/')
if (*p
== '#' || *p
== '\0')
while (*p
!= '\0' && *p
!= '#' && !isspace(*p
))
if (strcmp(shell
, q
) == 0 || strcmp(WILDCARD_SHELL
, q
) == 0)
** FREESPACE -- see how much free space is on the queue filesystem
** Only implemented if you have statfs.
** dir -- the directory in question.
** bsize -- a variable into which the filesystem
** The number of bytes free on the queue filesystem.
** -1 if the statfs call fails.
** Puts the filesystem block size into bsize.
#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 */
# define SFS_TYPE SFS_NONE
#if SFS_TYPE == SFS_USTAT
#if SFS_TYPE == SFS_4ARGS || SFS_TYPE == SFS_STATFS
#if SFS_TYPE == SFS_MOUNT
#if SFS_TYPE == SFS_STATVFS
# include <sys/statvfs.h>
# if SFS_TYPE == SFS_USTAT
# define FSBLOCKSIZE DEV_BSIZE
# define FSF_BAVAIL f_tfree
# define FSF_BAVAIL fd_bfreen
# define FSBLOCKSIZE 1024L
# if SFS_TYPE == SFS_STATVFS
# define FSBLOCKSIZE fs.f_frsize
# define FSBLOCKSIZE fs.f_bsize
# define FSF_BAVAIL f_bavail
# if SFS_TYPE == SFS_USTAT
if (stat(dir
, &statbuf
) == 0 && ustat(statbuf
.st_dev
, &fs
) == 0)
# if SFS_TYPE == SFS_4ARGS
if (statfs(dir
, &fs
, sizeof fs
, 0) == 0)
# if SFS_TYPE == SFS_STATVFS
if (statvfs(dir
, &fs
) == 0)
if (statfs(dir
, &fs
) > 0)
if (statfs(dir
, &fs
) == 0)
** ENOUGHSPACE -- check to see if there is enough free space on the queue fs
** Only implemented if you have statfs.
** 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.
** TRUE if there is enough space.
if (MinBlocksFree
<= 0 && msize
<= 0)
printf("enoughspace: no threshold\n");
if ((bfree
= freespace(QueueDir
, &bsize
)) >= 0)
printf("enoughspace: bavail=%ld, need=%ld\n",
/* convert msize to block count */
msize
= msize
/ bsize
+ 1;
"%s: low on space (have %ld, %s needs %ld in %s)",
CurEnv
->e_id
== NULL
? "[NOQUEUE]" : CurEnv
->e_id
,
CurHostName
== NULL
? "SMTP-DAEMON" : CurHostName
,
printf("enoughspace failure: min=%ld, need=%ld: %s\n",
MinBlocksFree
, msize
, errstring(errno
));
** 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.
** err -- the errno code to classify.
** TRUE if this is probably transient.
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 */
case ETIMEDOUT
: /* Connection timed out */
case ESTALE
: /* Stale NFS file handle */
case ENETDOWN
: /* Network is down */
case ENETUNREACH
: /* Network is unreachable */
case ENETRESET
: /* Network dropped connection on reset */
case ECONNABORTED
: /* Software caused connection abort */
case ECONNRESET
: /* Connection reset by peer */
case ENOBUFS
: /* No buffer space available */
case ESHUTDOWN
: /* Can't send after socket shutdown */
case ECONNREFUSED
: /* Connection refused */
case EHOSTDOWN
: /* Host is down */
case EHOSTUNREACH
: /* No route to host */
case EDQUOT
: /* Disc quota exceeded */
case EPROCLIM
: /* Too many processes */
case EUSERS
: /* Too many users */
case EDEADLK
: /* Resource deadlock avoided */
case EISCONN
: /* Socket already connected */
case EINPROGRESS
: /* Operation now in progress */
case EALREADY
: /* Operation already in progress */
case EADDRINUSE
: /* Address already in use */
case EADDRNOTAVAIL
: /* Can't assign requested address */
case ETXTBSY
: /* (Apollo) file locked */
#if defined(ENOSR) && (!defined(ENOBUFS) || (ENOBUFS != ENOSR))
case ENOSR
: /* Out of streams resources */
/* nope, must be permanent */
** LOCKFILE -- lock a file using flock or (shudder) fcntl locking
** 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.
** TRUE if the lock was acquired.
lockfile(fd
, filename
, ext
, type
)
if (bitset(LOCK_UN
, type
))
else if (bitset(LOCK_EX
, type
))
if (bitset(LOCK_NB
, type
))
printf("lockfile(%s%s, action=%d, type=%d): ",
filename
, ext
, action
, lfd
.l_type
);
if (fcntl(fd
, action
, &lfd
) >= 0)
printf("(%s) ", errstring(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 (!bitset(LOCK_NB
, type
) || (errno
!= EACCES
&& errno
!= EAGAIN
))
(void) fcntl(fd
, F_GETFL
, &omode
);
syserr("cannot lockf(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
filename
, ext
, fd
, type
, omode
, geteuid());
printf("lockfile(%s%s, type=%o): ", filename
, ext
, type
);
if (flock(fd
, type
) >= 0)
printf("(%s) ", errstring(errno
));
if (!bitset(LOCK_NB
, type
) || errno
!= EWOULDBLOCK
)
(void) fcntl(fd
, F_GETFL
, &omode
);
syserr("cannot flock(%s%s, fd=%d, type=%o, omode=%o, euid=%d)",
filename
, ext
, fd
, type
, omode
, geteuid());
** CHOWNSAFE -- tell if chown is "safe" (executable only by root)
** fd -- the file descriptor to check.
** TRUE -- if only root can chown the file to an arbitrary
** FALSE -- if an arbitrary user can give away a file.
setresuid(stbuf
.st_uid
, stbuf
.st_uid
, -1);
setresgid(stbuf
.st_gid
, stbuf
.st_gid
, -1);
tfd
= open(s
, O_RDONLY
|O_CREAT
, 0600);
rval
= fchown(tfd
, DefUid
, DefGid
) != 0;
setresgid(o_gid
, o_egid
, -1);
# ifdef _POSIX_CHOWN_RESTRICTED
# if _POSIX_CHOWN_RESTRICTED == -1
# ifdef _PC_CHOWN_RESTRICTED
** 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.
rval
= fpathconf(fd
, _PC_CHOWN_RESTRICTED
);
** RESETLIMITS -- reset system controlled resource limits
** This is to avoid denial-of-service attacks
# include <sys/resource.h>
lim
.rlim_cur
= lim
.rlim_max
= RLIM_INFINITY
;
(void) setrlimit(RLIMIT_CPU
, &lim
);
(void) setrlimit(RLIMIT_FSIZE
, &lim
);
(void) ulimit(2, 0x3fffff);
** GETCFNAME -- return the name of the .cf file.
** Some systems (e.g., NeXT) determine this dynamically.
extern char *ni_propval();
cflocation
= ni_propval("/locations", NULL
, "sendmail",
** SETVENDOR -- process vendor code from V configuration line
** vendor -- string representation of vendor.
** FALSE -- if vendor code could not be processed.
** 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.
if (strcasecmp(vendor
, "Berkeley") == 0)
VendorCode
= VENDOR_BERKELEY
;
/* add vendor extensions here */
if (strcasecmp(vendor
, "Sun") == 0)
** 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.
#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.
strtol(nptr
, endptr
, base
)
register const char *s
= nptr
;
register unsigned long acc
;
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.
if ((base
== 0 || base
== 16) &&
c
== '0' && (*s
== 'x' || *s
== 'X')) {
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
cutoff
= neg
? -(unsigned long)LONG_MIN
: LONG_MAX
;
cutlim
= cutoff
% (unsigned long)base
;
cutoff
/= (unsigned long)base
;
for (acc
= 0, any
= 0;; c
= *s
++) {
c
-= isupper(c
) ? 'A' - 10 : 'a' - 10;
if (any
< 0 || acc
> cutoff
|| acc
== cutoff
&& c
> cutlim
)
acc
= neg
? LONG_MIN
: LONG_MAX
;
*endptr
= (char *)(any
? s
- 1 : nptr
);
** SOLARIS_GETHOSTBY{NAME,ADDR} -- compatibility routines for gethostbyXXX
** Solaris versions at least through 2.3 don't properly deliver a
** canonical h_name field. This tries to work around it.
solaris_gethostbyname(name
)
static struct hostent hp
;
extern struct hostent
*_switch_gethostbyname_r();
return _switch_gethostbyname_r(name
, &hp
, buf
, sizeof(buf
), &h_errno
);
extern struct hostent
*__switch_gethostbyname();
return __switch_gethostbyname(name
);
solaris_gethostbyaddr(addr
, len
, type
)
static struct hostent hp
;
extern struct hostent
*_switch_gethostbyaddr_r();
return _switch_gethostbyaddr_r(addr
, len
, type
, &hp
, buf
, sizeof(buf
), &h_errno
);
extern struct hostent
*__switch_gethostbyaddr();
return __switch_gethostbyaddr(addr
, len
, type
);
** NI_PROPVAL -- netinfo property value lookup routine
** keydir -- the Netinfo directory name in which to search
** keyprop -- the name of the property in which to find the
** property we are interested. Defaults to "name".
** keyval -- the value for which we are really searching.
** valprop -- the property name for the value in which we
** sepchar -- if non-nil, this can be multiple-valued, and
** we should return a string separated by this
** 1. the directory is not found
** 2. the property name is not found
** 3. the property contains multiple values
** else -- the location of the config file.
** To search for an alias value, use:
** ni_propval("/aliases", "name", aliasname, "members", ',')
** Caller should free the return value of ni_proval
# define LOCAL_NETINFO_DOMAIN "."
# define PARENT_NETINFO_DOMAIN ".."
# define MAX_NI_LEVELS 256
ni_propval(keydir
, keyprop
, keyval
, valprop
, sepchar
)
** Create the full key from the two parts.
** Note that directory can end with, e.g., "name=" to specify
** an alternate search property.
i
= strlen(keydir
) + strlen(keyval
) + 2;
i
+= strlen(keyprop
) + 1;
** If the passed directory and property name are found
** in one of netinfo domains we need to search (starting
** from the local domain moving all the way back to the
** root domain) set propval to the property's value
for (i
= 0; i
< MAX_NI_LEVELS
; ++i
)
nis
= ni_open(NULL
, LOCAL_NETINFO_DOMAIN
, &ni
);
nis
= ni_open(lastni
, PARENT_NETINFO_DOMAIN
, &ni
);
** Don't bother if we didn't get a handle on a
** proper domain. This is not necessarily an error.
** We would get a positive ni_status if, for instance
** we never found the directory or property and tried
** to open the parent of the root domain!
** Find the path to the server information.
if (ni_pathsearch(ni
, &nid
, keybuf
) != 0)
** Find associated value information.
if (ni_lookupprop(ni
, &nid
, valprop
, &ninl
) != 0)
** See if we have an acceptable number of values.
if (ninl
.ni_namelist_len
<= 0)
if (sepchar
== '\0' && ninl
.ni_namelist_len
> 1)
** Calculate number of bytes needed and build result
for (j
= 0; j
< ninl
.ni_namelist_len
; j
++)
alen
+= strlen(ninl
.ni_namelist_val
[j
]) + 1;
propval
= p
= xalloc(alen
);
for (j
= 0; j
< ninl
.ni_namelist_len
; j
++)
strcpy(p
, ninl
.ni_namelist_val
[j
]);
if (lastni
!= NULL
&& ni
!= lastni
)
** HARD_SYSLOG -- call syslog repeatedly until it works
** Needed on HP-UX, which apparently doesn't guarantee that
** syslog succeeds during interrupt handlers.
# define MAXSYSLOGTRIES 100
hard_syslog(int pri
, char *msg
, ...)
hard_syslog(pri
, msg
, va_alist
)
char buf
[SYSLOG_BUFSIZE
* 2];
for (i
= MAXSYSLOGTRIES
; --i
>= 0 && syslog(pri
, "%s", buf
) < 0; )