116374Skarels #ifndef lint 2*16510Sralph static char sccsid[] = "@(#)inetd.c 4.2 (Berkeley) 05/18/84"; 316374Skarels #endif 416374Skarels 516374Skarels /* 616374Skarels * Inetd - Internet super-server 716374Skarels * 816374Skarels * This program invokes all internet services as needed. 916374Skarels * connection-oriented services are invoked each time a 1016374Skarels * connection is made, by creating a process. This process 1116374Skarels * is passed the connection as file descriptor 0 and is 1216374Skarels * expected to do a getpeername to find out the source host 1316374Skarels * and port. 1416374Skarels * 1516374Skarels * Datagram oriented services are invoked when a datagram 1616374Skarels * arrives; a process is created and passed a pending message 1716374Skarels * on file descriptor 0. Datagram servers may either connect 1816374Skarels * to their peer, freeing up the original socket for inetd 1916374Skarels * to receive further messages on, or ``take over the socket'', 2016374Skarels * processing all arriving datagrams and, eventually, timing 2116374Skarels * out. The first type of server is said to be ``multi-threaded''; 2216374Skarels * the second type of server ``single-threaded''. 2316374Skarels * 2416374Skarels * Inetd uses a configuration file which is read at startup 2516374Skarels * and, possibly, at some later time in response to a hangup signal. 2616374Skarels * The configuration file is ``free format'' with fields given in the 2716374Skarels * order shown below. Continuation lines for an entry must being with 2816374Skarels * a space or tab. All fields must be present in each entry. 2916374Skarels * 3016374Skarels * service name must be in /etc/services 3116374Skarels * socket type stream/dgram/raw/rdm/seqpacket 3216374Skarels * protocol must be in /etc/protocols 3316374Skarels * wait/nowait single-threaded/multi-threaded 3416374Skarels * server program full path name 3516374Skarels * server program arguments maximum of MAXARGS (5) 3616374Skarels * 3716374Skarels * Comment lines are indicated by a `#' in column 1. 3816374Skarels */ 3916374Skarels #include <sys/param.h> 4016374Skarels #include <sys/stat.h> 4116374Skarels #include <sys/ioctl.h> 4216374Skarels #include <sys/socket.h> 4316374Skarels #include <sys/file.h> 4416374Skarels #include <sys/wait.h> 4516374Skarels 4616374Skarels #include <netinet/in.h> 4716374Skarels #include <arpa/inet.h> 4816374Skarels 4916374Skarels #include <errno.h> 5016374Skarels #include <stdio.h> 5116374Skarels #include <signal.h> 5216374Skarels #include <netdb.h> 53*16510Sralph #include <syslog.h> 5416374Skarels 5516374Skarels extern int errno; 5616374Skarels 5716374Skarels int reapchild(); 5816374Skarels char *index(); 5916374Skarels char *malloc(); 6016374Skarels 6116374Skarels int debug = 0; 6216374Skarels int allsock; 6316374Skarels int options; 6416374Skarels struct servent *sp; 6516374Skarels 6616374Skarels struct servtab { 6716374Skarels char *se_service; /* name of service */ 6816374Skarels int se_socktype; /* type of socket to use */ 6916374Skarels char *se_proto; /* protocol used */ 7016374Skarels short se_wait; /* single threaded server */ 7116374Skarels short se_checked; /* looked at during merge */ 7216374Skarels char *se_server; /* server program */ 7316374Skarels #define MAXARGV 5 7416374Skarels char *se_argv[MAXARGV+1]; /* program arguments */ 7516374Skarels int se_fd; /* open descriptor */ 7616374Skarels struct sockaddr_in se_ctrladdr;/* bound address */ 7716374Skarels struct servtab *se_next; 7816374Skarels } *servtab; 7916374Skarels 8016374Skarels char *CONFIG = "/etc/inetd.conf"; 8116374Skarels 8216374Skarels main(argc, argv) 8316374Skarels int argc; 8416374Skarels char *argv[]; 8516374Skarels { 8616374Skarels int ctrl; 8716374Skarels register struct servtab *sep; 8816374Skarels char *cp, buf[50]; 8916374Skarels int pid, i; 9016374Skarels 9116374Skarels argc--, argv++; 9216374Skarels while (argc > 0 && *argv[0] == '-') { 9316374Skarels for (cp = &argv[0][1]; *cp; cp++) switch (*cp) { 9416374Skarels 9516374Skarels case 'd': 9616374Skarels debug = 1; 9716374Skarels options |= SO_DEBUG; 9816374Skarels break; 9916374Skarels 10016374Skarels default: 10116374Skarels fprintf(stderr, 10216374Skarels "inetd: Unknown flag -%c ignored.\n", *cp); 10316374Skarels break; 10416374Skarels } 10516374Skarels nextopt: 10616374Skarels argc--, argv++; 10716374Skarels } 10816374Skarels if (argc > 0) 10916374Skarels CONFIG = argv[0]; 11016374Skarels #ifndef DEBUG 11116374Skarels if (fork()) 11216374Skarels exit(0); 11316374Skarels { int s; 11416374Skarels for (s = 0; s < 10; s++) 11516374Skarels (void) close(s); 11616374Skarels } 11716374Skarels (void) open("/", O_RDONLY); 11816374Skarels (void) dup2(0, 1); 11916374Skarels (void) dup2(0, 2); 12016374Skarels { int tt = open("/dev/tty", O_RDWR); 12116374Skarels if (tt > 0) { 12216374Skarels ioctl(tt, TIOCNOTTY, 0); 12316374Skarels close(tt); 12416374Skarels } 12516374Skarels } 12616374Skarels #endif 127*16510Sralph openlog("inetd", LOG_PID, 0); 12816374Skarels config(); 12916374Skarels signal(SIGHUP, config); 13016374Skarels signal(SIGCHLD, reapchild); 13116374Skarels for (;;) { 13216374Skarels int readable, s, ctrl; 13316374Skarels 13416374Skarels while (allsock == 0) 13516374Skarels sigpause(0); 13616374Skarels readable = allsock; 13716374Skarels if (select(32, &readable, 0, 0, 0) <= 0) 13816374Skarels continue; 13916374Skarels s = ffs(readable)-1; 14016374Skarels if (s < 0) 14116374Skarels continue; 14216374Skarels for (sep = servtab; sep; sep = sep->se_next) 14316374Skarels if (s == sep->se_fd) 14416374Skarels goto found; 14516374Skarels abort(1); 14616374Skarels found: 14716374Skarels if (debug) 14816374Skarels fprintf(stderr, "someone wants %s\n", sep->se_service); 14916374Skarels if (sep->se_socktype == SOCK_STREAM) { 15016374Skarels ctrl = accept(s, 0, 0); 151*16510Sralph if (debug) 152*16510Sralph fprintf(stderr, "accept, ctrl %d\n", ctrl); 15316374Skarels if (ctrl < 0) { 15416374Skarels if (errno == EINTR) 15516374Skarels continue; 156*16510Sralph syslog(LOG_WARNING, "accept: %m"); 15716374Skarels continue; 15816374Skarels } 15916374Skarels } else 16016374Skarels ctrl = sep->se_fd; 16116374Skarels #define mask(sig) (1 << (sig - 1)) 16216374Skarels sigblock(mask(SIGCHLD)|mask(SIGHUP)); 16316374Skarels pid = fork(); 16416374Skarels if (pid < 0) { 16516374Skarels if (sep->se_socktype == SOCK_STREAM) 16616374Skarels close(ctrl); 16716374Skarels sleep(1); 16816374Skarels continue; 16916374Skarels } 17016374Skarels if (sep->se_wait) { 17116374Skarels sep->se_wait = pid; 17216374Skarels allsock &= ~(1 << s); 17316374Skarels } 17416374Skarels sigsetmask(0); 17516374Skarels if (pid == 0) { 17616374Skarels #ifdef DEBUG 17716374Skarels int tt = open("/dev/tty", O_RDWR); 17816374Skarels if (tt > 0) { 17916374Skarels ioctl(tt, TIOCNOTTY, 0); 18016374Skarels close(tt); 18116374Skarels } 18216374Skarels #endif 18316374Skarels dup2(ctrl, 0), close(ctrl), dup2(0, 1); 18416374Skarels for (i = getdtablesize(); --i > 2; ) 18516374Skarels close(i); 18616374Skarels if (debug) 18716374Skarels fprintf(stderr, "%d execl %s\n", 18816374Skarels getpid(), sep->se_server); 18916374Skarels execv(sep->se_server, sep->se_argv); 19016374Skarels if (sep->se_socktype != SOCK_STREAM) 191*16510Sralph recv(0, buf, sizeof (buf), 0); 192*16510Sralph syslog(LOG_ERR, "execv %s: %m", sep->se_server); 19316374Skarels _exit(1); 19416374Skarels } 19516374Skarels if (sep->se_socktype == SOCK_STREAM) 19616374Skarels close(ctrl); 19716374Skarels } 19816374Skarels } 19916374Skarels 20016374Skarels reapchild() 20116374Skarels { 20216374Skarels union wait status; 20316374Skarels int pid; 20416374Skarels register struct servtab *sep; 20516374Skarels 20616374Skarels for (;;) { 20716374Skarels pid = wait3(&status, WNOHANG, 0); 20816374Skarels if (pid <= 0) 20916374Skarels break; 21016374Skarels if (debug) 21116374Skarels fprintf(stderr, "%d reaped\n", pid); 21216374Skarels for (sep = servtab; sep; sep = sep->se_next) 21316374Skarels if (sep->se_wait == pid) { 21416374Skarels if (status.w_status) 215*16510Sralph syslog(LOG_WARNING, 216*16510Sralph "%s: exit status 0x%x", 21716374Skarels sep->se_server, status); 21816374Skarels if (debug) 21916374Skarels fprintf(stderr, "restored %s, fd %d\n", 22016374Skarels sep->se_service, sep->se_fd); 22116374Skarels allsock |= 1 << sep->se_fd; 22216374Skarels sep->se_wait = 1; 22316374Skarels } 22416374Skarels } 22516374Skarels } 22616374Skarels 22716374Skarels config() 22816374Skarels { 22916374Skarels register struct servtab *sep, *cp, **sepp; 23016374Skarels struct servtab *getconfigent(), *enter(); 23116374Skarels int omask; 23216374Skarels 23316374Skarels if (!setconfig()) { 234*16510Sralph syslog(LOG_ERR, "%s: %m", CONFIG); 23516374Skarels return; 23616374Skarels } 23716374Skarels for (sep = servtab; sep; sep = sep->se_next) 23816374Skarels sep->se_checked = 0; 23916374Skarels while (cp = getconfigent()) { 24016374Skarels for (sep = servtab; sep; sep = sep->se_next) 24116374Skarels if (strcmp(sep->se_service, cp->se_service) == 0 && 24216374Skarels strcmp(sep->se_proto, cp->se_proto) == 0) 24316374Skarels break; 24416374Skarels if (sep != 0) { 24516374Skarels int i; 24616374Skarels 24716374Skarels omask = sigblock(mask(SIGCHLD)); 24816374Skarels sep->se_wait = cp->se_wait; 24916374Skarels #define SWAP(a, b) { char *c = a; a = b; b = c; } 25016374Skarels if (cp->se_server) 25116374Skarels SWAP(sep->se_server, cp->se_server); 25216374Skarels for (i = 0; i < MAXARGV; i++) 25316374Skarels SWAP(sep->se_argv[i], cp->se_argv[i]); 25416374Skarels sigsetmask(omask); 25516374Skarels freeconfig(cp); 25616374Skarels } else 25716374Skarels sep = enter(cp); 25816374Skarels sep->se_checked = 1; 25916374Skarels if (sep->se_fd != -1) 26016374Skarels continue; 26116374Skarels sp = getservbyname(sep->se_service, sep->se_proto); 26216374Skarels if (sp == 0) { 263*16510Sralph syslog(LOG_ERR, "%s/%s: unknown service", 26416374Skarels sep->se_service, sep->se_proto); 26516374Skarels continue; 26616374Skarels } 26716374Skarels sep->se_ctrladdr.sin_port = sp->s_port; 26816374Skarels if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) { 269*16510Sralph syslog(LOG_ERR, "%s/%s: socket: %m", 27016374Skarels sep->se_service, sep->se_proto); 27116374Skarels continue; 27216374Skarels } 27316374Skarels if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) && 27416374Skarels setsockopt(sep->se_fd, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 275*16510Sralph syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 27616374Skarels if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) 277*16510Sralph syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); 27816374Skarels if (bind(sep->se_fd, &sep->se_ctrladdr, 27916374Skarels sizeof (sep->se_ctrladdr), 0) < 0) { 280*16510Sralph syslog(LOG_ERR, "%s/%s: bind: %m", 28116374Skarels sep->se_service, sep->se_proto); 28216374Skarels continue; 28316374Skarels } 28416374Skarels if (sep->se_socktype == SOCK_STREAM) 28516374Skarels listen(sep->se_fd, 10); 28616374Skarels allsock |= 1 << sep->se_fd; 28716374Skarels } 28816374Skarels endconfig(); 28916374Skarels /* 29016374Skarels * Purge anything not looked at above. 29116374Skarels */ 29216374Skarels omask = sigblock(mask(SIGCHLD)); 29316374Skarels sepp = &servtab; 29416374Skarels while (sep = *sepp) { 29516374Skarels if (sep->se_checked) { 29616374Skarels sepp = &sep->se_next; 29716374Skarels continue; 29816374Skarels } 29916374Skarels *sepp = sep->se_next; 30016374Skarels if (sep->se_fd != -1) { 30116374Skarels allsock &= ~(1 << sep->se_fd); 30216374Skarels (void) close(sep->se_fd); 30316374Skarels } 30416374Skarels freeconfig(sep); 30516374Skarels free((char *)sep); 30616374Skarels } 30716374Skarels (void) sigsetmask(omask); 30816374Skarels } 30916374Skarels 31016374Skarels struct servtab * 31116374Skarels enter(cp) 31216374Skarels struct servtab *cp; 31316374Skarels { 31416374Skarels register struct servtab *sep; 31516374Skarels int omask, i; 31616374Skarels char *strdup(); 31716374Skarels 31816374Skarels sep = (struct servtab *)malloc(sizeof (*sep)); 31916374Skarels if (sep == (struct servtab *)0) { 320*16510Sralph syslog(LOG_ERR, "Out of memory."); 32116374Skarels exit(-1); 32216374Skarels } 32316374Skarels *sep = *cp; 32416374Skarels sep->se_fd = -1; 32516374Skarels omask = sigblock(mask(SIGCHLD)); 32616374Skarels sep->se_next = servtab; 32716374Skarels servtab = sep; 32816374Skarels sigsetmask(omask); 32916374Skarels return (sep); 33016374Skarels } 33116374Skarels 33216374Skarels FILE *fconfig = NULL; 33316374Skarels struct servtab serv; 33416374Skarels char line[256]; 33516374Skarels char *skip(), *nextline(); 33616374Skarels 33716374Skarels setconfig() 33816374Skarels { 33916374Skarels 34016374Skarels if (fconfig != NULL) { 34116374Skarels fseek(fconfig, 0, L_SET); 34216374Skarels return (1); 34316374Skarels } 34416374Skarels fconfig = fopen(CONFIG, "r"); 34516374Skarels return (fconfig != NULL); 34616374Skarels } 34716374Skarels 34816374Skarels endconfig() 34916374Skarels { 35016374Skarels 35116374Skarels if (fconfig == NULL) 35216374Skarels return; 35316374Skarels fclose(fconfig); 35416374Skarels fconfig = NULL; 35516374Skarels } 35616374Skarels 35716374Skarels struct servtab * 35816374Skarels getconfigent() 35916374Skarels { 36016374Skarels register struct servtab *sep = &serv; 36116374Skarels char *cp, *arg; 36216374Skarels int argc; 36316374Skarels 36416374Skarels while ((cp = nextline(fconfig)) && *cp == '#') 36516374Skarels ; 36616374Skarels if (cp == NULL) 36716374Skarels return ((struct servtab *)0); 36816374Skarels sep->se_service = strdup(skip(&cp)); 36916374Skarels arg = skip(&cp); 37016374Skarels if (strcmp(arg, "stream") == 0) 37116374Skarels sep->se_socktype = SOCK_STREAM; 37216374Skarels else if (strcmp(arg, "dgram") == 0) 37316374Skarels sep->se_socktype = SOCK_DGRAM; 37416374Skarels else if (strcmp(arg, "rdm") == 0) 37516374Skarels sep->se_socktype = SOCK_RDM; 37616374Skarels else if (strcmp(arg, "seqpacket") == 0) 37716374Skarels sep->se_socktype = SOCK_SEQPACKET; 37816374Skarels else if (strcmp(arg, "raw") == 0) 37916374Skarels sep->se_socktype = SOCK_RAW; 38016374Skarels else 38116374Skarels sep->se_socktype = -1; 38216374Skarels sep->se_proto = strdup(skip(&cp)); 38316374Skarels arg = skip(&cp); 38416374Skarels sep->se_wait = strcmp(arg, "wait") == 0; 38516374Skarels sep->se_server = strdup(skip(&cp)); 38616374Skarels argc = 0; 38716374Skarels for (arg = skip(&cp); cp; arg = skip(&cp)) 38816374Skarels if (argc < MAXARGV) 38916374Skarels sep->se_argv[argc++] = strdup(arg); 39016374Skarels while (argc <= MAXARGV) 39116374Skarels sep->se_argv[argc++] = NULL; 39216374Skarels return (sep); 39316374Skarels } 39416374Skarels 39516374Skarels freeconfig(cp) 39616374Skarels register struct servtab *cp; 39716374Skarels { 39816374Skarels int i; 39916374Skarels 40016374Skarels if (cp->se_service) 40116374Skarels free(cp->se_service); 40216374Skarels if (cp->se_proto) 40316374Skarels free(cp->se_proto); 40416374Skarels if (cp->se_server) 40516374Skarels free(cp->se_server); 40616374Skarels for (i = 0; i < MAXARGV; i++) 40716374Skarels if (cp->se_argv[i]) 40816374Skarels free(cp->se_argv[i]); 40916374Skarels } 41016374Skarels 41116374Skarels char * 41216374Skarels skip(cpp) 41316374Skarels char **cpp; 41416374Skarels { 41516374Skarels register char *cp = *cpp; 41616374Skarels char *start; 41716374Skarels 41816374Skarels again: 41916374Skarels while (*cp == ' ' || *cp == '\t') 42016374Skarels cp++; 42116374Skarels if (*cp == '\0') { 42216374Skarels char c; 42316374Skarels 42416374Skarels c = getc(fconfig); 42516374Skarels ungetc(c, fconfig); 42616374Skarels if (c == ' ' || c == '\t') 42716374Skarels if (cp = nextline(fconfig)) 42816374Skarels goto again; 42916374Skarels *cpp = (char *)0; 43016374Skarels return ((char *)0); 43116374Skarels } 43216374Skarels start = cp; 43316374Skarels while (*cp && *cp != ' ' && *cp != '\t') 43416374Skarels cp++; 43516374Skarels if (*cp != '\0') 43616374Skarels *cp++ = '\0'; 43716374Skarels *cpp = cp; 43816374Skarels return (start); 43916374Skarels } 44016374Skarels 44116374Skarels char * 44216374Skarels nextline(fd) 44316374Skarels FILE *fd; 44416374Skarels { 44516374Skarels char *cp; 44616374Skarels 44716374Skarels if (fgets(line, sizeof (line), fconfig) == NULL) 44816374Skarels return ((char *)0); 44916374Skarels cp = index(line, '\n'); 45016374Skarels if (cp) 45116374Skarels *cp = '\0'; 45216374Skarels return (line); 45316374Skarels } 45416374Skarels 45516374Skarels char * 45616374Skarels strdup(cp) 45716374Skarels char *cp; 45816374Skarels { 45916374Skarels char *new; 46016374Skarels 46116374Skarels if (cp == NULL) 46216374Skarels cp = ""; 46316374Skarels new = malloc(strlen(cp) + 1); 46416374Skarels if (new == (char *)0) { 465*16510Sralph syslog(LOG_ERR, "Out of memory."); 46616374Skarels exit(-1); 46716374Skarels } 46816374Skarels strcpy(new, cp); 46916374Skarels return (new); 47016374Skarels } 471