Rewrite inetd(8) to use kqueue(2) for enhanced performance and scalability.

This commit is contained in:
tron 2003-02-12 08:52:03 +00:00
parent df839081ab
commit 8be719490d
1 changed files with 123 additions and 96 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: inetd.c,v 1.84 2002/09/19 21:59:03 mycroft Exp $ */
/* $NetBSD: inetd.c,v 1.85 2003/02/12 08:52:03 tron Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
@ -77,7 +77,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
#if 0
static char sccsid[] = "@(#)inetd.c 8.4 (Berkeley) 4/13/94";
#else
__RCSID("$NetBSD: inetd.c,v 1.84 2002/09/19 21:59:03 mycroft Exp $");
__RCSID("$NetBSD: inetd.c,v 1.85 2003/02/12 08:52:03 tron Exp $");
#endif
#endif /* not lint */
@ -202,6 +202,7 @@ __RCSID("$NetBSD: inetd.c,v 1.84 2002/09/19 21:59:03 mycroft Exp $");
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/event.h>
#ifndef RLIMIT_NOFILE
#define RLIMIT_NOFILE RLIMIT_OFILE
@ -275,7 +276,7 @@ int debug;
int lflag;
#endif
int nsock, maxsock;
fd_set allsock;
int kq;
int options;
int timingout;
struct servent *sp;
@ -348,50 +349,52 @@ struct servtab {
#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE)
void chargen_dg __P((int, struct servtab *));
void chargen_stream __P((int, struct servtab *));
void close_sep __P((struct servtab *));
void config __P((int));
void daytime_dg __P((int, struct servtab *));
void daytime_stream __P((int, struct servtab *));
void discard_dg __P((int, struct servtab *));
void discard_stream __P((int, struct servtab *));
void echo_dg __P((int, struct servtab *));
void echo_stream __P((int, struct servtab *));
void endconfig __P((void));
struct servtab *enter __P((struct servtab *));
void freeconfig __P((struct servtab *));
struct servtab *getconfigent __P((void));
void goaway __P((int));
void machtime_dg __P((int, struct servtab *));
void machtime_stream __P((int, struct servtab *));
char *newstr __P((char *));
char *nextline __P((FILE *));
void print_service __P((char *, struct servtab *));
void reapchild __P((int));
void retry __P((int));
void run_service __P((int, struct servtab *));
int setconfig __P((void));
void setup __P((struct servtab *));
char *sskip __P((char **));
char *skip __P((char **));
void tcpmux __P((int, struct servtab *));
void usage __P((void));
void register_rpc __P((struct servtab *));
void unregister_rpc __P((struct servtab *));
void bump_nofile __P((void));
void inetd_setproctitle __P((char *, int));
void initring __P((void));
uint32_t machtime __P((void));
int port_good_dg __P((struct sockaddr *));
int dg_broadcast __P((struct in_addr *));
static int getline __P((int, char *, int));
int main __P((int, char *[]));
void spawn __P((struct servtab *, int));
void chargen_dg (int, struct servtab *);
void chargen_stream (int, struct servtab *);
void close_sep (struct servtab *);
void config (int);
void daytime_dg (int, struct servtab *);
void daytime_stream (int, struct servtab *);
void discard_dg (int, struct servtab *);
void discard_stream (int, struct servtab *);
void echo_dg (int, struct servtab *);
void echo_stream (int, struct servtab *);
void endconfig (void);
struct servtab *enter (struct servtab *);
void freeconfig (struct servtab *);
struct servtab *getconfigent (void);
void goaway (int);
void machtime_dg (int, struct servtab *);
void machtime_stream (int, struct servtab *);
char *newstr (char *);
char *nextline (FILE *);
void print_service (char *, struct servtab *);
void reapchild (int);
void retry (int);
void run_service (int, struct servtab *);
int setconfig (void);
void setup (struct servtab *);
char *sskip (char **);
char *skip (char **);
void tcpmux (int, struct servtab *);
void usage (void);
void register_rpc (struct servtab *);
void unregister_rpc (struct servtab *);
void bump_nofile (void);
void inetd_setproctitle (char *, int);
void initring (void);
uint32_t machtime (void);
int port_good_dg (struct sockaddr *);
int dg_broadcast (struct in_addr *);
static int my_kevent(const struct kevent *, size_t, struct kevent *,
size_t);
static int getline (int, char *, int);
int main (int, char *[]);
void spawn (struct servtab *, int);
#ifdef MULOG
void dolog __P((struct servtab *, int));
static void timeout __P((int));
char *rfc931_name __P((struct sockaddr *, int));
void dolog (struct servtab *, int);
static void timeout (int);
char *rfc931_name (struct sockaddr *, int);
#endif
struct biltin {
@ -399,7 +402,7 @@ struct biltin {
int bi_socktype; /* type of socket supported */
short bi_fork; /* 1 if should fork before call */
short bi_wait; /* 1 if should wait for child */
void (*bi_fn) __P((int, struct servtab *));
void (*bi_fn) (int, struct servtab *);
/* function which performs it */
} biltins[] = {
/* Echo received data */
@ -443,7 +446,7 @@ main(argc, argv)
int argc;
char *argv[];
{
struct servtab *sep, *nsep;
struct servtab *sep;
struct sigvec sv;
int ch;
@ -479,6 +482,12 @@ main(argc, argv)
openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
pidfile(NULL);
kq = kqueue();
if (kq < 0) {
syslog(LOG_ERR, "kqueue: %m");
return (EXIT_FAILURE);
}
#ifdef RLIMIT_NOFILE
if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
syslog(LOG_ERR, "getrlimit: %m");
@ -492,23 +501,23 @@ main(argc, argv)
memset(&sv, 0, sizeof(sv));
sv.sv_mask = SIGBLOCK;
sv.sv_handler = retry;
sigvec(SIGALRM, &sv, (struct sigvec *)0);
sigvec(SIGALRM, &sv, NULL);
config(SIGHUP);
sv.sv_handler = config;
sigvec(SIGHUP, &sv, (struct sigvec *)0);
sigvec(SIGHUP, &sv, NULL);
sv.sv_handler = reapchild;
sigvec(SIGCHLD, &sv, (struct sigvec *)0);
sigvec(SIGCHLD, &sv, NULL);
sv.sv_handler = goaway;
sigvec(SIGTERM, &sv, (struct sigvec *)0);
sigvec(SIGTERM, &sv, NULL);
sv.sv_handler = goaway;
sigvec(SIGINT, &sv, (struct sigvec *)0);
sigvec(SIGINT, &sv, NULL);
sv.sv_mask = 0L;
sv.sv_handler = SIG_IGN;
sigvec(SIGPIPE, &sv, (struct sigvec *)0);
sigvec(SIGPIPE, &sv, NULL);
for (;;) {
int n, ctrl;
fd_set readable;
int n, ctrl;
struct kevent ev[256], *evp;
if (nsock == 0) {
(void) sigblock(SIGBLOCK);
@ -516,28 +525,19 @@ main(argc, argv)
sigpause(0L);
(void) sigsetmask(0L);
}
readable = allsock;
if ((n = select(maxsock + 1, &readable, (fd_set *)0,
(fd_set *)0, (struct timeval *)0)) <= 0) {
if (n == -1 && errno != EINTR) {
syslog(LOG_WARNING, "select: %m");
sleep(1);
}
continue;
}
for (sep = servtab; n && sep; sep = nsep) {
nsep = sep->se_next;
if (sep->se_fd == -1 ||
!FD_ISSET(sep->se_fd, &readable))
n = my_kevent(NULL, 0, ev, sizeof(ev) / sizeof(ev[0]));
for (evp = ev; n > 0; evp++, n--) {
if (evp->filter != EVFILT_READ)
continue;
sep = (struct servtab *)evp->udata;
/* Paranoia */
if (sep->se_fd != evp->ident)
continue;
n--;
if (debug)
fprintf(stderr, "someone wants %s\n", sep->se_service);
if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
/* XXX here do the libwrap check-before-accept */
ctrl = accept(sep->se_fd, (struct sockaddr *)0,
(int *)0);
ctrl = accept(sep->se_fd, NULL, NULL);
if (debug)
fprintf(stderr, "accept, ctrl %d\n",
ctrl);
@ -573,11 +573,11 @@ spawn(sep, ctrl)
#endif
if (dofork) {
if (sep->se_count++ == 0)
(void)gettimeofday(&sep->se_time, (struct timezone *)0);
(void)gettimeofday(&sep->se_time, NULL);
else if (sep->se_count >= sep->se_max) {
struct timeval now;
(void)gettimeofday(&now, (struct timezone *)0);
(void)gettimeofday(&now, NULL);
if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) {
sep->se_time = now;
sep->se_count = 1;
@ -607,15 +607,19 @@ spawn(sep, ctrl)
return;
}
if (pid != 0 && sep->se_wait) {
struct kevent ev;
sep->se_wait = pid;
FD_CLR(sep->se_fd, &allsock);
EV_SET(&ev, sep->se_fd, EVFILT_READ,
EV_DELETE, 0, 0, 0);
(void) my_kevent(&ev, 1, NULL, 0);
nsock--;
}
if (pid == 0) {
memset(&sv, 0, sizeof(sv));
sv.sv_mask = 0L;
sv.sv_handler = SIG_DFL;
sigvec(SIGPIPE, &sv, (struct sigvec *)0);
sigvec(SIGPIPE, &sv, NULL);
if (debug)
setsid();
}
@ -752,14 +756,16 @@ reapchild(signo)
struct servtab *sep;
for (;;) {
pid = wait3(&status, WNOHANG, (struct rusage *)0);
pid = wait3(&status, WNOHANG, NULL);
if (pid <= 0)
break;
if (debug)
fprintf(stderr, "%d reaped, status %#x\n",
pid, status);
for (sep = servtab; sep; sep = sep->se_next)
for (sep = servtab; sep != NULL; sep = sep->se_next)
if (sep->se_wait == pid) {
struct kevent ev;
if (WIFEXITED(status) && WEXITSTATUS(status))
syslog(LOG_WARNING,
"%s: exit status 0x%x",
@ -769,7 +775,9 @@ reapchild(signo)
"%s: exit signal 0x%x",
sep->se_server, WTERMSIG(status));
sep->se_wait = 1;
FD_SET(sep->se_fd, &allsock);
EV_SET(&ev, sep->se_fd, EVFILT_READ,
EV_ADD | EV_ENABLE, 0, 0, (intptr_t)sep);
(void) my_kevent(&ev, 1, NULL, 0);
nsock++;
if (debug)
fprintf(stderr, "restored %s, fd %d\n",
@ -790,10 +798,10 @@ config(signo)
syslog(LOG_ERR, "%s: %m", CONFIG);
return;
}
for (sep = servtab; sep; sep = sep->se_next)
for (sep = servtab; sep != NULL; sep = sep->se_next)
sep->se_checked = 0;
while ((cp = getconfigent())) {
for (sep = servtab; sep; sep = sep->se_next)
for (sep = servtab; sep != NULL; sep = sep->se_next)
if (strcmp(sep->se_service, cp->se_service) == 0 &&
strcmp(sep->se_hostaddr, cp->se_hostaddr) == 0 &&
strcmp(sep->se_proto, cp->se_proto) == 0 &&
@ -994,7 +1002,7 @@ retry(signo)
struct servtab *sep;
timingout = 0;
for (sep = servtab; sep; sep = sep->se_next) {
for (sep = servtab; sep != NULL; sep = sep->se_next) {
if (sep->se_fd == -1 && !ISMUX(sep)) {
switch (sep->se_family) {
case AF_LOCAL:
@ -1017,7 +1025,7 @@ goaway(signo)
{
struct servtab *sep;
for (sep = servtab; sep; sep = sep->se_next) {
for (sep = servtab; sep != NULL; sep = sep->se_next) {
if (sep->se_fd == -1)
continue;
@ -1042,7 +1050,8 @@ void
setup(sep)
struct servtab *sep;
{
int on = 1;
int on = 1;
struct kevent ev;
if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
if (debug)
@ -1109,7 +1118,9 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
if (sep->se_socktype == SOCK_STREAM)
listen(sep->se_fd, 10);
FD_SET(sep->se_fd, &allsock);
EV_SET(&ev, sep->se_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0,
(intptr_t)sep);
(void) my_kevent(&ev, 1, NULL, 0);
nsock++;
if (sep->se_fd > maxsock) {
maxsock = sep->se_fd;
@ -1130,7 +1141,6 @@ close_sep(sep)
{
if (sep->se_fd >= 0) {
nsock--;
FD_CLR(sep->se_fd, &allsock);
(void) close(sep->se_fd);
sep->se_fd = -1;
}
@ -1216,7 +1226,7 @@ enter(cp)
long omask;
sep = (struct servtab *)malloc(sizeof (*sep));
if (sep == (struct servtab *)0) {
if (sep == NULL) {
syslog(LOG_ERR, "Out of memory.");
exit(1);
}
@ -1338,7 +1348,7 @@ more:
break;
}
if (cp == NULL)
return ((struct servtab *)0);
return (NULL);
/*
* clear the static buffer, since some fields (se_ctrladdr,
* for example) don't get initialized here.
@ -1707,7 +1717,7 @@ skip(cpp)
char *start;
if (*cpp == NULL)
return ((char *)0);
return (NULL);
again:
while (*cp == ' ' || *cp == '\t')
@ -1720,8 +1730,8 @@ again:
if (c == ' ' || c == '\t')
if ((cp = nextline(fconfig)))
goto again;
*cpp = (char *)0;
return ((char *)0);
*cpp = NULL;
return (NULL);
}
start = cp;
while (*cp && *cp != ' ' && *cp != '\t')
@ -1739,7 +1749,7 @@ nextline(fd)
char *cp;
if (fgets(line, sizeof (line), fd) == NULL)
return ((char *)0);
return (NULL);
cp = strchr(line, '\n');
if (cp)
*cp = '\0';
@ -1977,7 +1987,7 @@ machtime()
{
struct timeval tv;
if (gettimeofday(&tv, (struct timezone *)0) < 0) {
if (gettimeofday(&tv, NULL) < 0) {
if (debug)
fprintf(stderr, "Unable to get time of day\n");
return (0);
@ -2175,7 +2185,7 @@ tcpmux(ctrl, sep)
if (!strcasecmp(service, "help")) {
strwrite(ctrl, "+Available services:\r\n");
strwrite(ctrl, "help\r\n");
for (sep = servtab; sep; sep = sep->se_next) {
for (sep = servtab; sep != NULL; sep = sep->se_next) {
if (!ISMUX(sep))
continue;
(void)write(ctrl, sep->se_service,
@ -2186,7 +2196,7 @@ tcpmux(ctrl, sep)
}
/* Try matching a service in inetd.conf with the request */
for (sep = servtab; sep; sep = sep->se_next) {
for (sep = servtab; sep != NULL; sep = sep->se_next) {
if (!ISMUX(sep))
continue;
if (!strcasecmp(service, sep->se_service)) {
@ -2534,3 +2544,20 @@ dg_broadcast(in)
freeifaddrs(ifap);
return (0);
}
static int
my_kevent(const struct kevent *changelist, size_t nchanges,
struct kevent *eventlist, size_t nevents)
{
int result;
while ((result = kevent(kq, changelist, nchanges, eventlist, nevents,
NULL)) < 0)
if (errno != EINTR) {
syslog(LOG_ERR, "kevent: %m");
exit(EXIT_FAILURE);
}
return (result);
}