BSD 4_3_Reno release
[unix-history] / usr / src / usr.sbin / inetd / inetd.c
index 15c8a7b..6973d0c 100644 (file)
@@ -1,6 +1,31 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted provided
+ * that: (1) source distributions retain this entire copyright notice and
+ * comment, and (2) distributions including binaries display the following
+ * acknowledgement:  ``This product includes software developed by the
+ * University of California, Berkeley and its contributors'' in the
+ * documentation or other materials provided with the distribution and in
+ * all advertising materials mentioning features or use of this software.
+ * Neither the name of the University nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
 #ifndef lint
 #ifndef lint
-static char sccsid[] = "@(#)inetd.c    4.1 (Berkeley) %G%";
-#endif
+static char sccsid[] = "@(#)inetd.c    5.25 (Berkeley) 6/29/90";
+#endif /* not lint */
 
 /*
  * Inetd - Internet super-server
 
 /*
  * Inetd - Internet super-server
@@ -31,8 +56,9 @@ static        char sccsid[] = "@(#)inetd.c    4.1 (Berkeley) %G%";
  *     socket type                     stream/dgram/raw/rdm/seqpacket
  *     protocol                        must be in /etc/protocols
  *     wait/nowait                     single-threaded/multi-threaded
  *     socket type                     stream/dgram/raw/rdm/seqpacket
  *     protocol                        must be in /etc/protocols
  *     wait/nowait                     single-threaded/multi-threaded
+ *     user                            user to run daemon as
  *     server program                  full path name
  *     server program                  full path name
- *     server program arguments        maximum of MAXARGS (5)
+ *     server program arguments        maximum of MAXARGS (20)
  *
  * Comment lines are indicated by a `#' in column 1.
  */
  *
  * Comment lines are indicated by a `#' in column 1.
  */
@@ -42,24 +68,38 @@ static      char sccsid[] = "@(#)inetd.c    4.1 (Berkeley) %G%";
 #include <sys/socket.h>
 #include <sys/file.h>
 #include <sys/wait.h>
 #include <sys/socket.h>
 #include <sys/file.h>
 #include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
 #include <errno.h>
 
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
 #include <errno.h>
-#include <stdio.h>
 #include <signal.h>
 #include <netdb.h>
 #include <signal.h>
 #include <netdb.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include "pathnames.h"
+
+#define        TOOMANY         40              /* don't start more than TOOMANY */
+#define        CNT_INTVL       60              /* servers in CNT_INTVL sec. */
+#define        RETRYTIME       (60*10)         /* retry after bind or server fail */
+
+#define        SIGBLOCK        (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
 
 extern int errno;
 
 
 extern int errno;
 
-int    reapchild();
+void   config(), reapchild(), retry();
 char   *index();
 char   *malloc();
 
 int    debug = 0;
 char   *index();
 char   *malloc();
 
 int    debug = 0;
-int    allsock;
+int    nsock, maxsock;
+fd_set allsock;
 int    options;
 int    options;
+int    timingout;
 struct servent *sp;
 
 struct servtab {
 struct servent *sp;
 
 struct servtab {
@@ -68,134 +108,243 @@ struct   servtab {
        char    *se_proto;              /* protocol used */
        short   se_wait;                /* single threaded server */
        short   se_checked;             /* looked at during merge */
        char    *se_proto;              /* protocol used */
        short   se_wait;                /* single threaded server */
        short   se_checked;             /* looked at during merge */
+       char    *se_user;               /* user name to run as */
+       struct  biltin *se_bi;          /* if built-in, description */
        char    *se_server;             /* server program */
        char    *se_server;             /* server program */
-#define MAXARGV 5
+#define        MAXARGV 20
        char    *se_argv[MAXARGV+1];    /* program arguments */
        int     se_fd;                  /* open descriptor */
        struct  sockaddr_in se_ctrladdr;/* bound address */
        char    *se_argv[MAXARGV+1];    /* program arguments */
        int     se_fd;                  /* open descriptor */
        struct  sockaddr_in se_ctrladdr;/* bound address */
+       int     se_count;               /* number started since se_time */
+       struct  timeval se_time;        /* start of se_count */
        struct  servtab *se_next;
 } *servtab;
 
        struct  servtab *se_next;
 } *servtab;
 
-char   *CONFIG = "/etc/inetd.conf";
-
-main(argc, argv)
+int echo_stream(), discard_stream(), machtime_stream();
+int daytime_stream(), chargen_stream();
+int echo_dg(), discard_dg(), machtime_dg(), daytime_dg(), chargen_dg();
+
+struct biltin {
+       char    *bi_service;            /* internally provided service name */
+       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 */
+       int     (*bi_fn)();             /* function which performs it */
+} biltins[] = {
+       /* Echo received data */
+       "echo",         SOCK_STREAM,    1, 0,   echo_stream,
+       "echo",         SOCK_DGRAM,     0, 0,   echo_dg,
+
+       /* Internet /dev/null */
+       "discard",      SOCK_STREAM,    1, 0,   discard_stream,
+       "discard",      SOCK_DGRAM,     0, 0,   discard_dg,
+
+       /* Return 32 bit time since 1970 */
+       "time",         SOCK_STREAM,    0, 0,   machtime_stream,
+       "time",         SOCK_DGRAM,     0, 0,   machtime_dg,
+
+       /* Return human-readable time */
+       "daytime",      SOCK_STREAM,    0, 0,   daytime_stream,
+       "daytime",      SOCK_DGRAM,     0, 0,   daytime_dg,
+
+       /* Familiar character generator */
+       "chargen",      SOCK_STREAM,    1, 0,   chargen_stream,
+       "chargen",      SOCK_DGRAM,     0, 0,   chargen_dg,
+       0
+};
+
+#define NUMINT (sizeof(intab) / sizeof(struct inent))
+char   *CONFIG = _PATH_INETDCONF;
+char   **Argv;
+char   *LastArg;
+
+main(argc, argv, envp)
        int argc;
        int argc;
-       char *argv[];
+       char *argv[], *envp[];
 {
 {
-       int ctrl;
+       extern char *optarg;
+       extern int optind;
        register struct servtab *sep;
        register struct servtab *sep;
-       char *cp, buf[50];
-       int pid, i;
-
-       argc--, argv++;
-       while (argc > 0 && *argv[0] == '-') {
-               for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
-
+       register struct passwd *pwd;
+       register int tmpint;
+       struct sigvec sv;
+       int ch, pid, dofork;
+       char buf[50];
+
+       Argv = argv;
+       if (envp == 0 || *envp == 0)
+               envp = argv;
+       while (*envp)
+               envp++;
+       LastArg = envp[-1] + strlen(envp[-1]);
+
+       while ((ch = getopt(argc, argv, "d")) != EOF)
+               switch(ch) {
                case 'd':
                        debug = 1;
                        options |= SO_DEBUG;
                        break;
                case 'd':
                        debug = 1;
                        options |= SO_DEBUG;
                        break;
-
+               case '?':
                default:
                default:
-                       fprintf(stderr,
-                           "inetd: Unknown flag -%c ignored.\n", *cp);
-                       break;
+                       fprintf(stderr, "usage: inetd [-d]");
+                       exit(1);
                }
                }
-nextopt:
-               argc--, argv++;
-       }
+       argc -= optind;
+       argv += optind;
+
        if (argc > 0)
                CONFIG = argv[0];
        if (argc > 0)
                CONFIG = argv[0];
-#ifndef DEBUG
-       if (fork())
-               exit(0);
-       { int s;
-       for (s = 0; s < 10; s++)
-               (void) close(s);
-       }
-       (void) open("/", O_RDONLY);
-       (void) dup2(0, 1);
-       (void) dup2(0, 2);
-       { int tt = open("/dev/tty", O_RDWR);
-         if (tt > 0) {
-               ioctl(tt, TIOCNOTTY, 0);
-               close(tt);
-         }
-       }
-#endif
+       if (debug == 0)
+               daemon(0, 0);
+       openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+       bzero((char *)&sv, sizeof(sv));
+       sv.sv_mask = SIGBLOCK;
+       sv.sv_handler = retry;
+       sigvec(SIGALRM, &sv, (struct sigvec *)0);
        config();
        config();
-       signal(SIGHUP, config);
-       signal(SIGCHLD, reapchild);
-       for (;;) {
-               int readable, s, ctrl;
+       sv.sv_handler = config;
+       sigvec(SIGHUP, &sv, (struct sigvec *)0);
+       sv.sv_handler = reapchild;
+       sigvec(SIGCHLD, &sv, (struct sigvec *)0);
+
+       {
+               /* space for daemons to overwrite environment for ps */
+#define        DUMMYSIZE       100
+               char dummy[DUMMYSIZE];
+
+               (void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
+               dummy[DUMMYSIZE - 1] = '\0';
+               (void)setenv("inetd_dummy", dummy, 1);
+       }
 
 
-               while (allsock == 0)
-                       sigpause(0);
-               readable = allsock;
-               if (select(32, &readable, 0, 0, 0) <= 0)
-                       continue;
-               s = ffs(readable)-1;
-               if (s < 0)
-                       continue;
-               for (sep = servtab; sep; sep = sep->se_next)
-                       if (s == sep->se_fd)
-                               goto found;
-               abort(1);
-       found:
+       for (;;) {
+           int n, ctrl;
+           fd_set readable;
+
+           if (nsock == 0) {
+               (void) sigblock(SIGBLOCK);
+               while (nsock == 0)
+                   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 < 0 && errno != EINTR)
+                       syslog(LOG_WARNING, "select: %m\n");
+                   sleep(1);
+                   continue;
+           }
+           for (sep = servtab; n && sep; sep = sep->se_next)
+           if (FD_ISSET(sep->se_fd, &readable)) {
+               n--;
                if (debug)
                        fprintf(stderr, "someone wants %s\n", sep->se_service);
                if (sep->se_socktype == SOCK_STREAM) {
                if (debug)
                        fprintf(stderr, "someone wants %s\n", sep->se_service);
                if (sep->se_socktype == SOCK_STREAM) {
-                       ctrl = accept(s, 0, 0);
-               if (debug)
-                       fprintf(stderr, "accept, ctrl %d\n", ctrl);
+                       ctrl = accept(sep->se_fd, (struct sockaddr *)0,
+                           (int *)0);
+                       if (debug)
+                               fprintf(stderr, "accept, ctrl %d\n", ctrl);
                        if (ctrl < 0) {
                                if (errno == EINTR)
                                        continue;
                        if (ctrl < 0) {
                                if (errno == EINTR)
                                        continue;
-                               perror("inetd: accept");
+                               syslog(LOG_WARNING, "accept (for %s): %m",
+                                       sep->se_service);
                                continue;
                        }
                } else
                        ctrl = sep->se_fd;
                                continue;
                        }
                } else
                        ctrl = sep->se_fd;
-#define mask(sig)      (1 << (sig - 1))
-               sigblock(mask(SIGCHLD)|mask(SIGHUP));
-               pid = fork();
+               (void) sigblock(SIGBLOCK);
+               pid = 0;
+               dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
+               if (dofork) {
+                       if (sep->se_count++ == 0)
+                           (void)gettimeofday(&sep->se_time,
+                               (struct timezone *)0);
+                       else if (sep->se_count >= TOOMANY) {
+                               struct timeval now;
+
+                               (void)gettimeofday(&now, (struct timezone *)0);
+                               if (now.tv_sec - sep->se_time.tv_sec >
+                                   CNT_INTVL) {
+                                       sep->se_time = now;
+                                       sep->se_count = 1;
+                               } else {
+                                       syslog(LOG_ERR,
+                       "%s/%s server failing (looping), service terminated\n",
+                                           sep->se_service, sep->se_proto);
+                                       FD_CLR(sep->se_fd, &allsock);
+                                       (void) close(sep->se_fd);
+                                       sep->se_fd = -1;
+                                       sep->se_count = 0;
+                                       nsock--;
+                                       sigsetmask(0L);
+                                       if (!timingout) {
+                                               timingout = 1;
+                                               alarm(RETRYTIME);
+                                       }
+                                       continue;
+                               }
+                       }
+                       pid = fork();
+               }
                if (pid < 0) {
                        if (sep->se_socktype == SOCK_STREAM)
                                close(ctrl);
                if (pid < 0) {
                        if (sep->se_socktype == SOCK_STREAM)
                                close(ctrl);
+                       sigsetmask(0L);
                        sleep(1);
                        continue;
                }
                        sleep(1);
                        continue;
                }
-               if (sep->se_wait) {
+               if (pid && sep->se_wait) {
                        sep->se_wait = pid;
                        sep->se_wait = pid;
-                       allsock &= ~(1 << s);
+                       FD_CLR(sep->se_fd, &allsock);
+                       nsock--;
                }
                }
-               sigsetmask(0);
+               sigsetmask(0L);
                if (pid == 0) {
                if (pid == 0) {
-#ifdef DEBUG
-                       int tt = open("/dev/tty", O_RDWR);
-                       if (tt > 0) {
-                               ioctl(tt, TIOCNOTTY, 0);
-                               close(tt);
+                       if (debug && dofork)
+                               setsid();
+                       if (dofork)
+                               for (tmpint = getdtablesize(); --tmpint > 2; )
+                                       if (tmpint != ctrl)
+                                               close(tmpint);
+                       if (sep->se_bi)
+                               (*sep->se_bi->bi_fn)(ctrl, sep);
+                       else {
+                               dup2(ctrl, 0);
+                               close(ctrl);
+                               dup2(0, 1);
+                               dup2(0, 2);
+                               if ((pwd = getpwnam(sep->se_user)) == NULL) {
+                                       syslog(LOG_ERR,
+                                               "getpwnam: %s: No such user",
+                                               sep->se_user);
+                                       if (sep->se_socktype != SOCK_STREAM)
+                                               recv(0, buf, sizeof (buf), 0);
+                                       _exit(1);
+                               }
+                               if (pwd->pw_uid) {
+                                       (void) setgid((gid_t)pwd->pw_gid);
+                                       initgroups(pwd->pw_name, pwd->pw_gid);
+                                       (void) setuid((uid_t)pwd->pw_uid);
+                               }
+                               if (debug)
+                                       fprintf(stderr, "%d execl %s\n",
+                                           getpid(), sep->se_server);
+                               execv(sep->se_server, sep->se_argv);
+                               if (sep->se_socktype != SOCK_STREAM)
+                                       recv(0, buf, sizeof (buf), 0);
+                               syslog(LOG_ERR, "execv %s: %m", sep->se_server);
+                               _exit(1);
                        }
                        }
-#endif
-                       dup2(ctrl, 0), close(ctrl), dup2(0, 1);
-                       for (i = getdtablesize(); --i > 2; )
-                               close(i);
-                       if (debug)
-                               fprintf(stderr, "%d execl %s\n",
-                                   getpid(), sep->se_server);
-                       execv(sep->se_server, sep->se_argv);
-                       if (sep->se_socktype != SOCK_STREAM)
-                               recv(0, buf, sizeof (buf));
-                       if (debug)
-                               fprintf(stderr, "execl failed\n");
-                       _exit(1);
                }
                if (sep->se_socktype == SOCK_STREAM)
                        close(ctrl);
                }
                if (sep->se_socktype == SOCK_STREAM)
                        close(ctrl);
+           }
        }
 }
 
        }
 }
 
+void
 reapchild()
 {
        union wait status;
 reapchild()
 {
        union wait status;
@@ -203,7 +352,7 @@ reapchild()
        register struct servtab *sep;
 
        for (;;) {
        register struct servtab *sep;
 
        for (;;) {
-               pid = wait3(&status, WNOHANG, 0);
+               pid = wait3(&status, WNOHANG, (struct rusage *)0);
                if (pid <= 0)
                        break;
                if (debug)
                if (pid <= 0)
                        break;
                if (debug)
@@ -211,27 +360,28 @@ reapchild()
                for (sep = servtab; sep; sep = sep->se_next)
                        if (sep->se_wait == pid) {
                                if (status.w_status)
                for (sep = servtab; sep; sep = sep->se_next)
                        if (sep->se_wait == pid) {
                                if (status.w_status)
-                                       fprintf(stderr,
-                                           "inetd: %s: exit status %d\n",
+                                       syslog(LOG_WARNING,
+                                           "%s: exit status 0x%x",
                                            sep->se_server, status);
                                if (debug)
                                        fprintf(stderr, "restored %s, fd %d\n",
                                            sep->se_service, sep->se_fd);
                                            sep->se_server, status);
                                if (debug)
                                        fprintf(stderr, "restored %s, fd %d\n",
                                            sep->se_service, sep->se_fd);
-                               allsock |= 1 << sep->se_fd;
+                               FD_SET(sep->se_fd, &allsock);
+                               nsock++;
                                sep->se_wait = 1;
                        }
        }
 }
 
                                sep->se_wait = 1;
                        }
        }
 }
 
+void
 config()
 {
        register struct servtab *sep, *cp, **sepp;
        struct servtab *getconfigent(), *enter();
 config()
 {
        register struct servtab *sep, *cp, **sepp;
        struct servtab *getconfigent(), *enter();
-       int omask;
+       long omask;
 
        if (!setconfig()) {
 
        if (!setconfig()) {
-               fprintf(stderr, "inetd: ");
-               perror(CONFIG);
+               syslog(LOG_ERR, "%s: %m", CONFIG);
                return;
        }
        for (sep = servtab; sep; sep = sep->se_next)
                return;
        }
        for (sep = servtab; sep; sep = sep->se_next)
@@ -244,55 +394,53 @@ config()
                if (sep != 0) {
                        int i;
 
                if (sep != 0) {
                        int i;
 
-                       omask = sigblock(mask(SIGCHLD));
-                       sep->se_wait = cp->se_wait;
+                       omask = sigblock(SIGBLOCK);
+                       /*
+                        * sep->se_wait may be holding the pid of a daemon
+                        * that we're waiting for.  If so, don't overwrite
+                        * it unless the config file explicitly says don't 
+                        * wait.
+                        */
+                       if (cp->se_bi == 0 && 
+                           (sep->se_wait == 1 || cp->se_wait == 0))
+                               sep->se_wait = cp->se_wait;
 #define SWAP(a, b) { char *c = a; a = b; b = c; }
 #define SWAP(a, b) { char *c = a; a = b; b = c; }
+                       if (cp->se_user)
+                               SWAP(sep->se_user, cp->se_user);
                        if (cp->se_server)
                                SWAP(sep->se_server, cp->se_server);
                        for (i = 0; i < MAXARGV; i++)
                                SWAP(sep->se_argv[i], cp->se_argv[i]);
                        sigsetmask(omask);
                        freeconfig(cp);
                        if (cp->se_server)
                                SWAP(sep->se_server, cp->se_server);
                        for (i = 0; i < MAXARGV; i++)
                                SWAP(sep->se_argv[i], cp->se_argv[i]);
                        sigsetmask(omask);
                        freeconfig(cp);
-               } else
+                       if (debug)
+                               print_service("REDO", sep);
+               } else {
                        sep = enter(cp);
                        sep = enter(cp);
+                       if (debug)
+                               print_service("ADD ", sep);
+               }
                sep->se_checked = 1;
                sep->se_checked = 1;
-               if (sep->se_fd != -1)
-                       continue;
                sp = getservbyname(sep->se_service, sep->se_proto);
                if (sp == 0) {
                sp = getservbyname(sep->se_service, sep->se_proto);
                if (sp == 0) {
-                       fprintf(stderr,
-                           "inetd: %s/%s: unknown service\n",
+                       syslog(LOG_ERR, "%s/%s: unknown service",
                            sep->se_service, sep->se_proto);
                        continue;
                }
                            sep->se_service, sep->se_proto);
                        continue;
                }
-               sep->se_ctrladdr.sin_port = sp->s_port;
-               if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
-                       fprintf(stderr, "inetd: %s/%s: ",
-                           sep->se_service, sep->se_proto);
-                       perror("socket");
-                       continue;
-               }
-               if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
-                   setsockopt(sep->se_fd, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
-                       perror("inetd: setsockopt (SO_DEBUG)");
-               if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
-                       perror("inetd: setsockopt (SO_REUSEADDR)");
-               if (bind(sep->se_fd, &sep->se_ctrladdr,
-                   sizeof (sep->se_ctrladdr), 0) < 0) {
-                       fprintf(stderr, "inetd: %s/%s: ",
-                           sep->se_service, sep->se_proto);
-                       perror("bind");
-                       continue;
+               if (sp->s_port != sep->se_ctrladdr.sin_port) {
+                       sep->se_ctrladdr.sin_port = sp->s_port;
+                       if (sep->se_fd != -1)
+                               (void) close(sep->se_fd);
+                       sep->se_fd = -1;
                }
                }
-               if (sep->se_socktype == SOCK_STREAM)
-                       listen(sep->se_fd, 10);
-               allsock |= 1 << sep->se_fd;
+               if (sep->se_fd == -1)
+                       setup(sep);
        }
        endconfig();
        /*
         * Purge anything not looked at above.
         */
        }
        endconfig();
        /*
         * Purge anything not looked at above.
         */
-       omask = sigblock(mask(SIGCHLD));
+       omask = sigblock(SIGBLOCK);
        sepp = &servtab;
        while (sep = *sepp) {
                if (sep->se_checked) {
        sepp = &servtab;
        while (sep = *sepp) {
                if (sep->se_checked) {
@@ -301,31 +449,82 @@ config()
                }
                *sepp = sep->se_next;
                if (sep->se_fd != -1) {
                }
                *sepp = sep->se_next;
                if (sep->se_fd != -1) {
-                       allsock &= ~(1 << sep->se_fd);
+                       FD_CLR(sep->se_fd, &allsock);
+                       nsock--;
                        (void) close(sep->se_fd);
                }
                        (void) close(sep->se_fd);
                }
+               if (debug)
+                       print_service("FREE", sep);
                freeconfig(sep);
                free((char *)sep);
        }
        (void) sigsetmask(omask);
 }
 
                freeconfig(sep);
                free((char *)sep);
        }
        (void) sigsetmask(omask);
 }
 
+void
+retry()
+{
+       register struct servtab *sep;
+
+       timingout = 0;
+       for (sep = servtab; sep; sep = sep->se_next)
+               if (sep->se_fd == -1)
+                       setup(sep);
+}
+
+setup(sep)
+       register struct servtab *sep;
+{
+       int on = 1;
+
+       if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
+               syslog(LOG_ERR, "%s/%s: socket: %m",
+                   sep->se_service, sep->se_proto);
+               return;
+       }
+#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)
+               syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+       if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
+               syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
+#undef turnon
+       if (bind(sep->se_fd, &sep->se_ctrladdr,
+           sizeof (sep->se_ctrladdr)) < 0) {
+               syslog(LOG_ERR, "%s/%s: bind: %m",
+                   sep->se_service, sep->se_proto);
+               (void) close(sep->se_fd);
+               sep->se_fd = -1;
+               if (!timingout) {
+                       timingout = 1;
+                       alarm(RETRYTIME);
+               }
+               return;
+       }
+       if (sep->se_socktype == SOCK_STREAM)
+               listen(sep->se_fd, 10);
+       FD_SET(sep->se_fd, &allsock);
+       nsock++;
+       if (sep->se_fd > maxsock)
+               maxsock = sep->se_fd;
+}
+
 struct servtab *
 enter(cp)
        struct servtab *cp;
 {
        register struct servtab *sep;
 struct servtab *
 enter(cp)
        struct servtab *cp;
 {
        register struct servtab *sep;
-       int omask, i;
-       char *strdup();
+       long omask;
 
        sep = (struct servtab *)malloc(sizeof (*sep));
        if (sep == (struct servtab *)0) {
 
        sep = (struct servtab *)malloc(sizeof (*sep));
        if (sep == (struct servtab *)0) {
-               fprintf(stderr, "Out of memory.\n");
+               syslog(LOG_ERR, "Out of memory.");
                exit(-1);
        }
        *sep = *cp;
        sep->se_fd = -1;
                exit(-1);
        }
        *sep = *cp;
        sep->se_fd = -1;
-       omask = sigblock(mask(SIGCHLD));
+       omask = sigblock(SIGBLOCK);
        sep->se_next = servtab;
        servtab = sep;
        sigsetmask(omask);
        sep->se_next = servtab;
        servtab = sep;
        sigsetmask(omask);
@@ -341,7 +540,7 @@ setconfig()
 {
 
        if (fconfig != NULL) {
 {
 
        if (fconfig != NULL) {
-               fseek(fconfig, 0, L_SET);
+               fseek(fconfig, 0L, L_SET);
                return (1);
        }
        fconfig = fopen(CONFIG, "r");
                return (1);
        }
        fconfig = fopen(CONFIG, "r");
@@ -350,20 +549,20 @@ setconfig()
 
 endconfig()
 {
 
 endconfig()
 {
-
-       if (fconfig == NULL)
-               return;
-       fclose(fconfig);
-       fconfig = NULL;
+       if (fconfig) {
+               (void) fclose(fconfig);
+               fconfig = NULL;
+       }
 }
 
 struct servtab *
 getconfigent()
 {
        register struct servtab *sep = &serv;
 }
 
 struct servtab *
 getconfigent()
 {
        register struct servtab *sep = &serv;
-       char *cp, *arg;
        int argc;
        int argc;
+       char *cp, *arg, *strdup();
 
 
+more:
        while ((cp = nextline(fconfig)) && *cp == '#')
                ;
        if (cp == NULL)
        while ((cp = nextline(fconfig)) && *cp == '#')
                ;
        if (cp == NULL)
@@ -385,7 +584,24 @@ getconfigent()
        sep->se_proto = strdup(skip(&cp));
        arg = skip(&cp);
        sep->se_wait = strcmp(arg, "wait") == 0;
        sep->se_proto = strdup(skip(&cp));
        arg = skip(&cp);
        sep->se_wait = strcmp(arg, "wait") == 0;
+       sep->se_user = strdup(skip(&cp));
        sep->se_server = strdup(skip(&cp));
        sep->se_server = strdup(skip(&cp));
+       if (strcmp(sep->se_server, "internal") == 0) {
+               register struct biltin *bi;
+
+               for (bi = biltins; bi->bi_service; bi++)
+                       if (bi->bi_socktype == sep->se_socktype &&
+                           strcmp(bi->bi_service, sep->se_service) == 0)
+                               break;
+               if (bi->bi_service == 0) {
+                       syslog(LOG_ERR, "internal service %s unknown\n",
+                               sep->se_service);
+                       goto more;
+               }
+               sep->se_bi = bi;
+               sep->se_wait = bi->bi_wait;
+       } else
+               sep->se_bi = NULL;
        argc = 0;
        for (arg = skip(&cp); cp; arg = skip(&cp))
                if (argc < MAXARGV)
        argc = 0;
        for (arg = skip(&cp); cp; arg = skip(&cp))
                if (argc < MAXARGV)
@@ -404,6 +620,8 @@ freeconfig(cp)
                free(cp->se_service);
        if (cp->se_proto)
                free(cp->se_proto);
                free(cp->se_service);
        if (cp->se_proto)
                free(cp->se_proto);
+       if (cp->se_user)
+               free(cp->se_user);
        if (cp->se_server)
                free(cp->se_server);
        for (i = 0; i < MAXARGV; i++)
        if (cp->se_server)
                free(cp->se_server);
        for (i = 0; i < MAXARGV; i++)
@@ -422,10 +640,10 @@ again:
        while (*cp == ' ' || *cp == '\t')
                cp++;
        if (*cp == '\0') {
        while (*cp == ' ' || *cp == '\t')
                cp++;
        if (*cp == '\0') {
-               char c;
+               int c;
 
                c = getc(fconfig);
 
                c = getc(fconfig);
-               ungetc(c, fconfig);
+               (void) ungetc(c, fconfig);
                if (c == ' ' || c == '\t')
                        if (cp = nextline(fconfig))
                                goto again;
                if (c == ' ' || c == '\t')
                        if (cp = nextline(fconfig))
                                goto again;
@@ -447,7 +665,7 @@ nextline(fd)
 {
        char *cp;
 
 {
        char *cp;
 
-       if (fgets(line, sizeof (line), fconfig) == NULL)
+       if (fgets(line, sizeof (line), fd) == NULL)
                return ((char *)0);
        cp = index(line, '\n');
        if (cp)
                return ((char *)0);
        cp = index(line, '\n');
        if (cp)
@@ -463,11 +681,271 @@ strdup(cp)
 
        if (cp == NULL)
                cp = "";
 
        if (cp == NULL)
                cp = "";
-       new = malloc(strlen(cp) + 1);
+       new = malloc((unsigned)(strlen(cp) + 1));
        if (new == (char *)0) {
        if (new == (char *)0) {
-               fprintf(stderr, "Out of memory.\n");
+               syslog(LOG_ERR, "Out of memory.");
                exit(-1);
        }
                exit(-1);
        }
-       strcpy(new, cp);
+       (void)strcpy(new, cp);
        return (new);
 }
        return (new);
 }
+
+setproctitle(a, s)
+       char *a;
+       int s;
+{
+       int size;
+       register char *cp;
+       struct sockaddr_in sin;
+       char buf[80];
+
+       cp = Argv[0];
+       size = sizeof(sin);
+       if (getpeername(s, &sin, &size) == 0)
+               (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr)); 
+       else
+               (void) sprintf(buf, "-%s", a); 
+       strncpy(cp, buf, LastArg - cp);
+       cp += strlen(cp);
+       while (cp < LastArg)
+               *cp++ = ' ';
+}
+
+/*
+ * Internet services provided internally by inetd:
+ */
+#define        BUFSIZE 4096
+
+/* ARGSUSED */
+echo_stream(s, sep)            /* Echo service -- echo data back */
+       int s;
+       struct servtab *sep;
+{
+       char buffer[BUFSIZE];
+       int i;
+
+       setproctitle(sep->se_service, s);
+       while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
+           write(s, buffer, i) > 0)
+               ;
+       exit(0);
+}
+
+/* ARGSUSED */
+echo_dg(s, sep)                        /* Echo service -- echo data back */
+       int s;
+       struct servtab *sep;
+{
+       char buffer[BUFSIZE];
+       int i, size;
+       struct sockaddr sa;
+
+       size = sizeof(sa);
+       if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
+               return;
+       (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
+}
+
+/* ARGSUSED */
+discard_stream(s, sep)         /* Discard service -- ignore data */
+       int s;
+       struct servtab *sep;
+{
+       char buffer[BUFSIZE];
+
+       setproctitle(sep->se_service, s);
+       while (1) {
+               while (read(s, buffer, sizeof(buffer)) > 0)
+                       ;
+               if (errno != EINTR)
+                       break;
+       }
+       exit(0);
+}
+
+/* ARGSUSED */
+discard_dg(s, sep)             /* Discard service -- ignore data */
+       int s;
+       struct servtab *sep;
+{
+       char buffer[BUFSIZE];
+
+       (void) read(s, buffer, sizeof(buffer));
+}
+
+#include <ctype.h>
+#define LINESIZ 72
+char ring[128];
+char *endring;
+
+initring()
+{
+       register int i;
+
+       endring = ring;
+
+       for (i = 0; i <= 128; ++i)
+               if (isprint(i))
+                       *endring++ = i;
+}
+
+/* ARGSUSED */
+chargen_stream(s, sep)         /* Character generator */
+       int s;
+       struct servtab *sep;
+{
+       register char *rs;
+       int len;
+       char text[LINESIZ+2];
+
+       setproctitle(sep->se_service, s);
+
+       if (!endring) {
+               initring();
+               rs = ring;
+       }
+
+       text[LINESIZ] = '\r';
+       text[LINESIZ + 1] = '\n';
+       for (rs = ring;;) {
+               if ((len = endring - rs) >= LINESIZ)
+                       bcopy(rs, text, LINESIZ);
+               else {
+                       bcopy(rs, text, len);
+                       bcopy(ring, text + len, LINESIZ - len);
+               }
+               if (++rs == endring)
+                       rs = ring;
+               if (write(s, text, sizeof(text)) != sizeof(text))
+                       break;
+       }
+       exit(0);
+}
+
+/* ARGSUSED */
+chargen_dg(s, sep)             /* Character generator */
+       int s;
+       struct servtab *sep;
+{
+       struct sockaddr sa;
+       static char *rs;
+       int len, size;
+       char text[LINESIZ+2];
+
+       if (endring == 0) {
+               initring();
+               rs = ring;
+       }
+
+       size = sizeof(sa);
+       if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
+               return;
+
+       if ((len = endring - rs) >= LINESIZ)
+               bcopy(rs, text, LINESIZ);
+       else {
+               bcopy(rs, text, len);
+               bcopy(ring, text + len, LINESIZ - len);
+       }
+       if (++rs == endring)
+               rs = ring;
+       text[LINESIZ] = '\r';
+       text[LINESIZ + 1] = '\n';
+       (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
+}
+
+/*
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+
+long
+machtime()
+{
+       struct timeval tv;
+
+       if (gettimeofday(&tv, (struct timezone *)0) < 0) {
+               fprintf(stderr, "Unable to get time of day\n");
+               return (0L);
+       }
+       return (htonl((long)tv.tv_sec + 2208988800));
+}
+
+/* ARGSUSED */
+machtime_stream(s, sep)
+       int s;
+       struct servtab *sep;
+{
+       long result;
+
+       result = machtime();
+       (void) write(s, (char *) &result, sizeof(result));
+}
+
+/* ARGSUSED */
+machtime_dg(s, sep)
+       int s;
+       struct servtab *sep;
+{
+       long result;
+       struct sockaddr sa;
+       int size;
+
+       size = sizeof(sa);
+       if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
+               return;
+       result = machtime();
+       (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
+}
+
+/* ARGSUSED */
+daytime_stream(s, sep)         /* Return human-readable time of day */
+       int s;
+       struct servtab *sep;
+{
+       char buffer[256];
+       time_t time(), clock;
+       char *ctime();
+
+       clock = time((time_t *) 0);
+
+       (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
+       (void) write(s, buffer, strlen(buffer));
+}
+
+/* ARGSUSED */
+daytime_dg(s, sep)             /* Return human-readable time of day */
+       int s;
+       struct servtab *sep;
+{
+       char buffer[256];
+       time_t time(), clock;
+       struct sockaddr sa;
+       int size;
+       char *ctime();
+
+       clock = time((time_t *) 0);
+
+       size = sizeof(sa);
+       if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
+               return;
+       (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
+       (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
+}
+
+/*
+ * print_service:
+ *     Dump relevant information to stderr
+ */
+print_service(action, sep)
+       char *action;
+       struct servtab *sep;
+{
+       fprintf(stderr,
+           "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
+           action, sep->se_service, sep->se_proto,
+           sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
+}