Improves and cleanups over inetd(8):

- Teach inetd(8) about kqueue, originally implemented by jmg@[1].
 - Use new C99 style function prototypes instead of K&Rs.
 - Raise WARNS from 2 to 6

Glanced at by:	ru
MFC After:	2 weeks

[1] http://people.freebsd.org/~jmg/inetd.kq.patch,
    http://people.freebsd.org/~jmg/inetd.kq.html
This commit is contained in:
Xin LI 2006-01-18 07:47:46 +00:00
parent 74bcd9d280
commit 4d115fef9e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=154503
3 changed files with 152 additions and 229 deletions

View File

@ -6,7 +6,7 @@ MAN= inetd.8
MLINKS= inetd.8 inetd.conf.5
SRCS= inetd.c builtins.c
WARNS?= 2
WARNS?= 6
CFLAGS+= -DLOGIN_CAP
#CFLAGS+= -DSANITY_CHECK

View File

@ -113,6 +113,8 @@ __FBSDID("$FreeBSD$");
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/stat.h>
#include <sys/un.h>
@ -197,6 +199,7 @@ __FBSDID("$FreeBSD$");
#ifndef TOOMANY
#define TOOMANY 256 /* don't start more than TOOMANY */
#endif
#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
#define RETRYTIME (60*10) /* retry after bind or server fail */
#define MAX_MAXCHLD 32767 /* max allowable max children */
@ -204,8 +207,6 @@ __FBSDID("$FreeBSD$");
#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
void close_sep(struct servtab *);
void flag_signal(int);
void flag_config(int);
void config(void);
int cpmip(const struct servtab *, int);
void endconfig(void);
@ -215,11 +216,8 @@ struct servtab *getconfigent(void);
int matchservent(const char *, const char *, const char *);
char *nextline(FILE *);
void addchild(struct servtab *, int);
void flag_reapchild(int);
void reapchild(void);
void enable(struct servtab *);
void disable(struct servtab *);
void flag_retry(int);
void retry(void);
int setconfig(void);
void setup(struct servtab *);
@ -230,7 +228,6 @@ void unregisterrpc(register struct servtab *sep);
static struct conninfo *search_conn(struct servtab *sep, int ctrl);
static int room_conn(struct servtab *sep, struct conninfo *conn);
static void addchild_conn(struct conninfo *conn, pid_t pid);
static void reapchild_conn(pid_t pid);
static void free_conn(struct conninfo *conn);
static void resize_conn(struct servtab *sep, int maxperip);
static void free_connlist(struct servtab *sep);
@ -245,7 +242,7 @@ int wrap_bi = 0;
int debug = 0;
int dolog = 0;
int maxsock; /* highest-numbered descriptor */
fd_set allsock;
int kqsock;
int options;
int timingout;
int toomany = TOOMANY;
@ -261,7 +258,6 @@ int v4bind_ok = 0;
struct sockaddr_in6 *bind_sa6;
int v6bind_ok = 0;
#endif
int signalpipe[2];
#ifdef SANITY_CHECK
int nsock;
#endif
@ -313,6 +309,7 @@ whichaf(struct request_info *req)
int
main(int argc, char **argv)
{
struct kevent kqevlist[16];
struct servtab *sep;
struct passwd *pwd;
struct group *grp;
@ -336,7 +333,11 @@ main(int argc, char **argv)
#define peer4 p_un.peer_un4
#define peer6 p_un.peer_un6
#define peermax p_un.peer_max
int i;
int i, j;
#ifdef SANITY_CHECK
int k;
#endif
int status;
struct addrinfo hints, *res;
const char *servname;
int error;
@ -519,19 +520,21 @@ main(int argc, char **argv)
}
#endif
kqsock = kqueue();
if (kqsock < 0)
err(EX_OSERR, "ERROR: Cannot allocate kqueue");
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGALRM);
sigaddset(&sa.sa_mask, SIGCHLD);
sigaddset(&sa.sa_mask, SIGHUP);
sa.sa_handler = flag_retry;
sigaction(SIGALRM, &sa, &saalrm);
config();
sa.sa_handler = flag_config;
sigaction(SIGHUP, &sa, &sahup);
sa.sa_handler = flag_reapchild;
sigaction(SIGCHLD, &sa, &sachld);
sa.sa_handler = SIG_IGN;
sigaction(SIGALRM, &sa, &saalrm);
WATCH_SIG(SIGALRM, retry);
config();
sigaction(SIGHUP, &sa, &sahup);
WATCH_SIG(SIGHUP, config);
sigaction(SIGPIPE, &sa, &sapipe);
{
@ -544,27 +547,8 @@ main(int argc, char **argv)
(void)setenv("inetd_dummy", dummy, 1);
}
if (pipe(signalpipe) != 0) {
syslog(LOG_ERR, "pipe: %m");
exit(EX_OSERR);
}
if (fcntl(signalpipe[0], F_SETFD, FD_CLOEXEC) < 0 ||
fcntl(signalpipe[1], F_SETFD, FD_CLOEXEC) < 0) {
syslog(LOG_ERR, "signalpipe: fcntl (F_SETFD, FD_CLOEXEC): %m");
exit(EX_OSERR);
}
FD_SET(signalpipe[0], &allsock);
#ifdef SANITY_CHECK
nsock++;
#endif
if (signalpipe[0] > maxsock)
maxsock = signalpipe[0];
if (signalpipe[1] > maxsock)
maxsock = signalpipe[1];
for (;;) {
int n, ctrl;
fd_set readable;
#ifdef SANITY_CHECK
if (nsock == 0) {
@ -572,46 +556,60 @@ main(int argc, char **argv)
exit(EX_SOFTWARE);
}
#endif
readable = allsock;
if ((n = select(maxsock + 1, &readable, (fd_set *)0,
(fd_set *)0, (struct timeval *)0)) <= 0) {
if (n < 0 && errno != EINTR) {
syslog(LOG_WARNING, "select: %m");
if ((n = kevent(kqsock, NULL, 0, kqevlist,
sizeof kqevlist / sizeof *kqevlist,
(struct timespec *)0)) <= 0) {
if (n == -1 && errno != EINTR) {
syslog(LOG_WARNING, "kevent: %m");
sleep(1);
}
continue;
}
/* handle any queued signal flags */
if (FD_ISSET(signalpipe[0], &readable)) {
int nsig;
if (ioctl(signalpipe[0], FIONREAD, &nsig) != 0) {
syslog(LOG_ERR, "ioctl: %m");
exit(EX_OSERR);
}
while (--nsig >= 0) {
char c;
if (read(signalpipe[0], &c, 1) != 1) {
syslog(LOG_ERR, "read: %m");
exit(EX_OSERR);
}
for (j = 0; j < n; j++) {
if (kqevlist[j].filter == EVFILT_SIGNAL) {
/* handle any queued signal flags */
if (debug)
warnx("handling signal flag %c", c);
switch(c) {
case 'A': /* sigalrm */
retry();
break;
case 'C': /* sigchld */
reapchild();
break;
case 'H': /* sighup */
config();
break;
}
}
}
for (sep = servtab; n && sep; sep = sep->se_next)
if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
n--;
warnx("calling signalhandler for sig %td",
kqevlist[j].ident);
((void (*)(void))kqevlist[j].udata)();
} else if (kqevlist[j].filter == EVFILT_PROC) {
sep = (struct servtab *)kqevlist[j].udata;
pid = wait4(kqevlist[j].ident, &status, WNOHANG,
(struct rusage *)0);
if (debug)
warnx("%d reaped, status %#x", pid, status);
if (pid == 0) {
/* XXX - this could leave a zombie */
syslog(LOG_WARNING, "can't reap pid %td",
kqevlist[j].ident);
continue;
}
#ifdef SANITY_CHECK
for (k = 0; k < sep->se_numchild; k++)
if (sep->se_pids[k] == pid)
break;
if (k != sep->se_numchild)
sep->se_pids[k] =
sep->se_pids[sep->se_numchild - 1];
#endif
if (sep->se_maxchild &&
sep->se_numchild == sep->se_maxchild)
enable(sep);
if (status)
syslog(LOG_WARNING,
"%s[%d]: exit status 0x%x",
sep->se_server, pid, status);
/* XXX - this should never happen */
if (--sep->se_numchild < 0)
sep->se_numchild = 0;
if (sep->se_free && sep->se_numchild == 0) {
freeconfig(sep);
free((char *)sep);
}
} else {
sep = (struct servtab *)kqevlist[j].udata;
if (debug)
warnx("someone wants %s", sep->se_service);
dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep);
@ -882,21 +880,7 @@ main(int argc, char **argv)
if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
close(ctrl);
}
}
}
/*
* Add a signal flag to the signal flag queue for later handling
*/
void
flag_signal(int c)
{
char ch = c;
if (write(signalpipe[1], &ch, 1) != 1) {
syslog(LOG_ERR, "write: %m");
_exit(EX_OSERR);
}
}
}
@ -908,72 +892,18 @@ flag_signal(int c)
void
addchild(struct servtab *sep, pid_t pid)
{
if (sep->se_maxchild <= 0)
return;
#ifdef SANITY_CHECK
if (sep->se_numchild >= sep->se_maxchild) {
if (sep->se_maxchild && sep->se_numchild >= sep->se_maxchild) {
syslog(LOG_ERR, "%s: %d >= %d",
__func__, sep->se_numchild, sep->se_maxchild);
exit(EX_SOFTWARE);
}
sep->se_pids[sep->se_numchild] = pid;
#endif
sep->se_pids[sep->se_numchild++] = pid;
if (sep->se_numchild == sep->se_maxchild)
sep->se_numchild++;
if (sep->se_maxchild && sep->se_numchild == sep->se_maxchild)
disable(sep);
}
/*
* Some child process has exited. See if it's on somebody's list.
*/
void
flag_reapchild(int signo __unused)
{
flag_signal('C');
}
void
reapchild(void)
{
int k, status;
pid_t pid;
struct servtab *sep;
for (;;) {
pid = wait3(&status, WNOHANG, (struct rusage *)0);
if (pid <= 0)
break;
if (debug)
warnx("%d reaped, %s %u", pid,
WIFEXITED(status) ? "status" : "signal",
WIFEXITED(status) ? WEXITSTATUS(status)
: WTERMSIG(status));
for (sep = servtab; sep; sep = sep->se_next) {
for (k = 0; k < sep->se_numchild; k++)
if (sep->se_pids[k] == pid)
break;
if (k == sep->se_numchild)
continue;
if (sep->se_numchild == sep->se_maxchild)
enable(sep);
sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
if (WIFSIGNALED(status) || WEXITSTATUS(status))
syslog(LOG_WARNING,
"%s[%d]: exited, %s %u",
sep->se_server, pid,
WIFEXITED(status) ? "status" : "signal",
WIFEXITED(status) ? WEXITSTATUS(status)
: WTERMSIG(status));
break;
}
reapchild_conn(pid);
}
}
void
flag_config(int signo __unused)
{
flag_signal('H');
WATCH_PROC(pid, sep);
}
void
@ -990,8 +920,10 @@ config(void)
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 ((new = getconfigent())) {
if (getpwnam(new->se_user) == NULL) {
syslog(LOG_ERR,
@ -1037,12 +969,17 @@ config(void)
/* copy over outstanding child pids */
if (sep->se_maxchild > 0 && new->se_maxchild > 0) {
new->se_numchild = sep->se_numchild;
/* XXX - this can cause problems */
if (new->se_numchild > new->se_maxchild)
new->se_numchild = new->se_maxchild;
#ifdef SANITY_CHECK
memcpy(new->se_pids, sep->se_pids,
new->se_numchild * sizeof(*new->se_pids));
#endif
}
#ifdef SANITY_CHECK
SWAP(pid_t *, sep->se_pids, new->se_pids);
#endif
sep->se_maxchild = new->se_maxchild;
sep->se_numchild = new->se_numchild;
sep->se_maxcpm = new->se_maxcpm;
@ -1051,14 +988,11 @@ config(void)
sep->se_bi = new->se_bi;
/* might need to turn on or off service now */
if (sep->se_fd >= 0) {
if (sep->se_maxchild > 0
&& sep->se_numchild == sep->se_maxchild) {
if (FD_ISSET(sep->se_fd, &allsock))
disable(sep);
} else {
if (!FD_ISSET(sep->se_fd, &allsock))
enable(sep);
}
if (sep->se_maxchild
&& sep->se_numchild == sep->se_maxchild)
disable(sep);
else
enable(sep);
}
sep->se_accept = new->se_accept;
SWAP(char *, sep->se_user, new->se_user);
@ -1178,8 +1112,11 @@ config(void)
print_service("FREE", sep);
if (sep->se_rpc && sep->se_rpc_prog > 0)
unregisterrpc(sep);
freeconfig(sep);
free(sep);
if (sep->se_numchild == 0) {
freeconfig(sep);
free((char *)sep);
} else
sep->se_free = 1;
}
(void) sigsetmask(omask);
}
@ -1239,12 +1176,6 @@ unregisterrpc(struct servtab *sep)
(void) sigsetmask(omask);
}
void
flag_retry(int signo __unused)
{
flag_signal('A');
}
void
retry(void)
{
@ -1279,12 +1210,12 @@ setup(struct servtab *sep)
#define turnon(fd, opt) \
setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
turnon(sep->se_fd, SO_DEBUG) < 0)
turnon(sep->se_fd, SO_DEBUG) == -1)
syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
if (turnon(sep->se_fd, SO_REUSEADDR) == -1)
syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
#ifdef SO_PRIVSTATE
if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
if (turnon(sep->se_fd, SO_PRIVSTATE) == -1)
syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
#endif
/* tftpd opens a new connection then needs more infos */
@ -1292,7 +1223,7 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
(strcmp(sep->se_proto, "udp") == 0) &&
(sep->se_accept == 0) &&
(setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
(char *)&on, sizeof (on)) < 0))
(char *)&on, sizeof (on)) == -1))
syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m");
if (sep->se_family == AF_INET6) {
int flag = sep->se_nomapped ? 1 : 0;
@ -1397,8 +1328,7 @@ setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
#ifdef IPSEC
void
ipsecsetup(sep)
struct servtab *sep;
ipsecsetup(struct servtab *sep)
{
char *buf;
char *policy_in = NULL;
@ -1475,8 +1405,7 @@ void
close_sep(struct servtab *sep)
{
if (sep->se_fd >= 0) {
if (FD_ISSET(sep->se_fd, &allsock))
disable(sep);
disable(sep);
(void) close(sep->se_fd);
sep->se_fd = -1;
}
@ -1515,7 +1444,7 @@ enter(struct servtab *cp)
long omask;
sep = (struct servtab *)malloc(sizeof (*sep));
if (sep == (struct servtab *)0) {
if (sep == NULL) {
syslog(LOG_ERR, "malloc: %m");
exit(EX_OSERR);
}
@ -1545,14 +1474,9 @@ enable(struct servtab *sep)
"%s: %s: is mux", __func__, sep->se_service);
exit(EX_SOFTWARE);
}
if (FD_ISSET(sep->se_fd, &allsock)) {
syslog(LOG_ERR,
"%s: %s: not off", __func__, sep->se_service);
exit(EX_SOFTWARE);
}
nsock++;
#endif
FD_SET(sep->se_fd, &allsock);
WATCH_SOCK(sep->se_fd, sep);
if (sep->se_fd > maxsock)
maxsock = sep->se_fd;
}
@ -1574,18 +1498,13 @@ disable(struct servtab *sep)
"%s: %s: is mux", __func__, sep->se_service);
exit(EX_SOFTWARE);
}
if (!FD_ISSET(sep->se_fd, &allsock)) {
syslog(LOG_ERR,
"%s: %s: not on", __func__, sep->se_service);
exit(EX_SOFTWARE);
}
if (nsock == 0) {
syslog(LOG_ERR, "%s: nsock=0", __func__);
exit(EX_SOFTWARE);
}
nsock--;
#endif
FD_CLR(sep->se_fd, &allsock);
UNWATCH_SOCK(sep->se_fd, sep);
if (sep->se_fd == maxsock)
maxsock--;
}
@ -1649,12 +1568,10 @@ getconfigent(void)
for (p = cp + 2; p && *p && isspace(*p); p++)
;
if (*p == '\0') {
if (policy)
free(policy);
free(policy);
policy = NULL;
} else if (ipsec_get_policylen(p) >= 0) {
if (policy)
free(policy);
free(policy);
policy = newstr(p);
} else {
syslog(LOG_ERR,
@ -1970,6 +1887,7 @@ getconfigent(void)
else
sep->se_maxchild = 1;
}
#ifdef SANITY_CHECK
if (sep->se_maxchild > 0) {
sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
if (sep->se_pids == NULL) {
@ -1977,6 +1895,7 @@ getconfigent(void)
exit(EX_OSERR);
}
}
#endif
argc = 0;
for (arg = skip(&cp); cp; arg = skip(&cp))
if (argc < MAXARGV) {
@ -2002,29 +1921,22 @@ freeconfig(struct servtab *cp)
{
int i;
if (cp->se_service)
free(cp->se_service);
if (cp->se_proto)
free(cp->se_proto);
if (cp->se_user)
free(cp->se_user);
if (cp->se_group)
free(cp->se_group);
free(cp->se_service);
free(cp->se_proto);
free(cp->se_user);
free(cp->se_group);
#ifdef LOGIN_CAP
if (cp->se_class)
free(cp->se_class);
free(cp->se_class);
#endif
free(cp->se_server);
#ifdef SANITY_CHECK
free(cp->se_pids);
#endif
if (cp->se_server)
free(cp->se_server);
if (cp->se_pids)
free(cp->se_pids);
for (i = 0; i < MAXARGV; i++)
if (cp->se_argv[i])
free(cp->se_argv[i]);
free(cp->se_argv[i]);
free_connlist(cp);
#ifdef IPSEC
if (cp->se_policy)
free(cp->se_policy);
free(cp->se_policy);
#endif
}
@ -2303,8 +2215,7 @@ cpmip(const struct servtab *sep, int ctrl)
strcmp(sep->se_service, chBest->ch_Service) != 0) {
chBest->ch_Family = sin4->sin_family;
chBest->ch_Addr4 = sin4->sin_addr;
if (chBest->ch_Service)
free(chBest->ch_Service);
free(chBest->ch_Service);
chBest->ch_Service = strdup(sep->se_service);
bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
}
@ -2317,8 +2228,7 @@ cpmip(const struct servtab *sep, int ctrl)
strcmp(sep->se_service, chBest->ch_Service) != 0) {
chBest->ch_Family = sin6->sin6_family;
chBest->ch_Addr6 = sin6->sin6_addr;
if (chBest->ch_Service)
free(chBest->ch_Service);
free(chBest->ch_Service);
chBest->ch_Service = strdup(sep->se_service);
bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
}
@ -2356,6 +2266,25 @@ cpmip(const struct servtab *sep, int ctrl)
return(r);
}
void
watch(short filter, uintptr_t ident, void *data, u_int fflags, int addrm)
{
struct kevent kev;
int i;
EV_SET(&kev, ident, filter, addrm? EV_ADD:EV_DELETE,
fflags, 0, data);
i = kevent(kqsock, &kev, 1, NULL, 0, NULL);
if (i == -1)
syslog(LOG_ERR, "kevent failed: %m");
if (debug) {
warnx("kqueue, ident: %td, addrm: %d, ret: %d, data: %p, errno: %s", ident, addrm, i, data, strerror(errno));
}
}
static struct conninfo *
search_conn(struct servtab *sep, int ctrl)
{
@ -2466,26 +2395,6 @@ addchild_conn(struct conninfo *conn, pid_t pid)
conn->co_proc[conn->co_numchild++] = proc;
}
static void
reapchild_conn(pid_t pid)
{
struct procinfo *proc;
struct conninfo *conn;
int i;
if ((proc = search_proc(pid, 0)) == NULL)
return;
if ((conn = proc->pr_conn) == NULL)
return;
for (i = 0; i < conn->co_numchild; ++i)
if (conn->co_proc[i] == proc) {
conn->co_proc[i] = conn->co_proc[--conn->co_numchild];
break;
}
free_proc(proc);
free_conn(conn);
}
static void
resize_conn(struct servtab *sep, int maxpip)
{

View File

@ -74,7 +74,10 @@ struct servtab {
int se_maxchild; /* max number of children */
int se_maxcpm; /* max connects per IP per minute */
int se_numchild; /* current number of children */
int se_free; /* free when numchild == 0 */
#ifdef SANITY_CHECK
pid_t *se_pids; /* array of child pids */
#endif
char *se_user; /* user name to run as */
char *se_group; /* group name to run as */
#ifdef LOGIN_CAP
@ -145,3 +148,14 @@ struct biltin {
int bi_maxchild; /* max number of children, -1=default */
bi_fn_t *bi_fn; /* function which performs it */
};
void watch(short, uintptr_t, void *, u_int, int);
#define WATCH_SOCK(fd, data) watch(EVFILT_READ, fd, data, 0, 1)
#define UNWATCH_SOCK(fd, data) watch(EVFILT_READ, fd, data, 0, 0)
#define WATCH_SIG(sig, data) watch(EVFILT_SIGNAL, sig, data, 0, 1)
#define UNWATCH_SIG(sig, data) watch(EVFILT_SIGNAL, sig, data, 0, 0)
#define WATCH_PROC(proc, data) watch(EVFILT_PROC, proc, data, NOTE_EXIT, 1)
#define UNWATCH_PROC(proc, data) watch(EVFILT_PROC, proc, data, NOTE_EXIT, 0)
#define WATCH_FD(fd, data) watch(EVFILT_VNODE, fd, data, NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_RENAME, 1)
#define UNWATCH_FD(fd, data) watch(EVFILT_VNODE, fd, data, NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_RENAME, 0)