Index: inetd.c =================================================================== RCS file: /home/ncvs/src/usr.sbin/inetd/inetd.c,v retrieving revision 1.81 diff -u -r1.81 inetd.c --- inetd.c 2000/04/02 16:11:14 1.81 +++ inetd.c 2000/06/12 02:11:38 @@ -115,6 +115,8 @@ #include #include #include +#include +#include #include #include @@ -191,7 +193,10 @@ < 0 = no limit */ #endif +#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 */ @@ -205,7 +210,7 @@ int debug = 0; int log = 0; int maxsock; /* highest-numbered descriptor */ -fd_set allsock; +int kqsock; int options; int timingout; int toomany = TOOMANY; @@ -220,7 +225,6 @@ struct sockaddr_in6 *bind_sa6; int no_v6bind = 1; #endif -int signalpipe[2]; #ifdef SANITY_CHECK int nsock; #endif @@ -260,6 +264,7 @@ int argc; char *argv[], *envp[]; { + struct kevent kqevlist[16]; struct servtab *sep; struct passwd *pwd; struct group *grp; @@ -284,7 +289,11 @@ #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; char *servname; int error; @@ -430,19 +439,20 @@ syslog(LOG_WARNING, "%s: %m", pid_file); } } + + kqsock = 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; + sa.sa_handler = SIG_IGN; sigaction(SIGALRM, &sa, &saalrm); + WATCH_SIG(SIGALRM, retry); config(); - sa.sa_handler = flag_config; sigaction(SIGHUP, &sa, &sahup); - sa.sa_handler = flag_reapchild; - sigaction(SIGCHLD, &sa, &sachld); - sa.sa_handler = SIG_IGN; + WATCH_SIG(SIGHUP, config); sigaction(SIGPIPE, &sa, &sapipe); { @@ -455,22 +465,8 @@ (void)setenv("inetd_dummy", dummy, 1); } - if (pipe(signalpipe) != 0) { - syslog(LOG_ERR, "pipe: %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) { @@ -478,46 +474,59 @@ 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, 0, NULL, sizeof kqevlist / sizeof *kqevlist, + 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 n; - if (ioctl(signalpipe[0], FIONREAD, &n) != 0) { - syslog(LOG_ERR, "ioctl: %m"); - exit(EX_OSERR); - } - while (--n >= 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 %d", + kqevlist[j].ident); + ((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 %d", + 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); if (sep->se_accept && sep->se_socktype == SOCK_STREAM) { @@ -590,7 +599,7 @@ if (dofork) { if (sep->se_count++ == 0) (void)gettimeofday(&sep->se_time, (struct timezone *)NULL); - else if (sep->se_count >= toomany) { + else if (toomany > 0 && sep->se_count >= toomany) { struct timeval now; (void)gettimeofday(&now, (struct timezone *)NULL); @@ -769,19 +778,7 @@ 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(c) - char c; -{ - if (write(signalpipe[1], &c, 1) != 1) { - syslog(LOG_ERR, "write: %m"); - _exit(EX_OSERR); + } } } @@ -794,69 +791,21 @@ addchild(struct servtab *sep, pid_t pid) { #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", __FUNCTION__, sep->se_numchild, sep->se_maxchild); exit(EX_SOFTWARE); } + sep->se_pids[sep->se_numchild] = pid; #endif - if (sep->se_maxchild == 0) - return; - 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(signo) - int signo; -{ - flag_signal('C'); -} - -void -reapchild() -{ - 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, status %#x", pid, 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 (status) - syslog(LOG_WARNING, - "%s[%d]: exit status 0x%x", - sep->se_server, pid, status); - break; - } - } + WATCH_PROC(pid, sep); } void -flag_config(signo) - int signo; -{ - flag_signal('H'); -} - -void config() +config() { struct servtab *sep, *new, **sepp; long omask; @@ -865,8 +814,10 @@ 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, @@ -906,25 +857,27 @@ /* copy over outstanding child pids */ if (sep->se_maxchild && new->se_maxchild) { 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(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; /* might need to turn on or off service now */ if (sep->se_fd >= 0) { - if (sep->se_maxchild - && 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(sep->se_user, new->se_user); @@ -1051,8 +1004,11 @@ print_service("FREE", sep); if (sep->se_rpc && sep->se_rpc_prog > 0) unregisterrpc(sep); - freeconfig(sep); - free((char *)sep); + if (sep->se_numchild == 0) { + freeconfig(sep); + free((char *)sep); + } else + sep->se_free = 1; } (void) sigsetmask(omask); } @@ -1086,13 +1042,6 @@ } void -flag_retry(signo) - int signo; -{ - flag_signal('A'); -} - -void retry() { struct servtab *sep; @@ -1120,12 +1069,12 @@ #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 */ @@ -1133,7 +1082,7 @@ (strcmp(sep->se_proto, "udp") == 0) && (sep->se_accept == 0) && (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_PKTINFO, - (char *)&on, sizeof (on)) < 0)) + (char *)&on, sizeof (on)) == -1)) syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m"); #ifdef IPV6_BINDV6ONLY if (sep->se_family == AF_INET6) { @@ -1292,8 +1241,7 @@ 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; } @@ -1328,7 +1276,7 @@ long omask; sep = (struct servtab *)malloc(sizeof (*sep)); - if (sep == (struct servtab *)0) { + if (sep == NULL) { syslog(LOG_ERR, "malloc: %m"); exit(EX_OSERR); } @@ -1358,14 +1306,9 @@ "%s: %s: is mux", __FUNCTION__, sep->se_service); exit(EX_SOFTWARE); } - if (FD_ISSET(sep->se_fd, &allsock)) { - syslog(LOG_ERR, - "%s: %s: not off", __FUNCTION__, 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; } @@ -1387,18 +1330,13 @@ "%s: %s: is mux", __FUNCTION__, sep->se_service); exit(EX_SOFTWARE); } - if (!FD_ISSET(sep->se_fd, &allsock)) { - syslog(LOG_ERR, - "%s: %s: not on", __FUNCTION__, sep->se_service); - exit(EX_SOFTWARE); - } if (nsock == 0) { syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__); exit(EX_SOFTWARE); } nsock--; #endif - FD_CLR(sep->se_fd, &allsock); + UNWATCH_SOCK(sep->se_fd, sep); if (sep->se_fd == maxsock) maxsock--; } @@ -1716,6 +1654,7 @@ else sep->se_maxchild = 1; } +#ifdef SANITY_CHECK if (sep->se_maxchild) { sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids)); if (sep->se_pids == NULL) { @@ -1723,6 +1662,7 @@ exit(EX_OSERR); } } +#endif argc = 0; for (arg = skip(&cp); cp; arg = skip(&cp)) if (argc < MAXARGV) { @@ -1761,8 +1701,10 @@ #endif if (cp->se_server) free(cp->se_server); +#ifdef SANITY_CHECK if (cp->se_pids) free(cp->se_pids); +#endif for (i = 0; i < MAXARGV; i++) if (cp->se_argv[i]) free(cp->se_argv[i]); @@ -2138,4 +2080,34 @@ } } return(r); +} + +void +watch(filter, ident, data, fflags, addrm) + short filter; + uintptr_t ident; + void *data; + u_int fflags; + int addrm; +{ + struct kevent kev; + struct kevent *kptr; + int i; + + kptr = &kev; + + kev.ident = ident; + kev.filter = filter; + kev.flags = addrm ? EV_ADD|EV_ENABLE : EV_DELETE|EV_DISABLE; + kev.fflags = fflags; + kev.udata = data; + + i = kevent(kqsock, 1, &kptr, 0, NULL, NULL); + + if (i == -1) + syslog(LOG_ERR, "kevent failed: %m"); + + if (debug) { + warnx("kqueue, ident: %d, addrm: %d, ret: %d, data: %p, errno: %s", ident, addrm, i, data, strerror(errno)); + } } Index: inetd.h =================================================================== RCS file: /home/ncvs/src/usr.sbin/inetd/inetd.h,v retrieving revision 1.4 diff -u -r1.4 inetd.h --- inetd.h 2000/01/25 14:52:10 1.4 +++ inetd.h 2000/06/12 01:54:46 @@ -61,7 +61,10 @@ 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 @@ -109,8 +112,6 @@ void chargen_dg __P((int, struct servtab *)); void chargen_stream __P((int, struct servtab *)); void close_sep __P((struct servtab *)); -void flag_signal __P((char)); -void flag_config __P((int)); void config __P((void)); void daytime_dg __P((int, struct servtab *)); void daytime_stream __P((int, struct servtab *)); @@ -131,11 +132,8 @@ char *nextline __P((FILE *)); void print_service __P((char *, struct servtab *)); void addchild __P((struct servtab *, int)); -void flag_reapchild __P((int)); -void reapchild __P((void)); void enable __P((struct servtab *)); void disable __P((struct servtab *)); -void flag_retry __P((int)); void retry __P((void)); int setconfig __P((void)); void setup __P((struct servtab *)); @@ -157,3 +155,13 @@ int bi_maxchild; /* max number of children, -1=default */ void (*bi_fn)(); /* function which performs it */ }; + +void watch __P((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)