xref: /csrg-svn/usr.sbin/inetd/inetd.c (revision 61830)
121134Sdist /*
2*61830Sbostic  * Copyright (c) 1983, 1991, 1993
3*61830Sbostic  *	The Regents of the University of California.  All rights reserved.
433136Sbostic  *
542799Sbostic  * %sccs.include.redist.c%
621134Sdist  */
721134Sdist 
816374Skarels #ifndef lint
9*61830Sbostic static char copyright[] =
10*61830Sbostic "@(#) Copyright (c) 1983, 1991, 1993\n\
11*61830Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1233136Sbostic #endif /* not lint */
1316374Skarels 
1421134Sdist #ifndef lint
15*61830Sbostic static char sccsid[] = "@(#)inetd.c	8.1 (Berkeley) 06/06/93";
1633136Sbostic #endif /* not lint */
1721134Sdist 
1816374Skarels /*
1916374Skarels  * Inetd - Internet super-server
2016374Skarels  *
2157769Sandrew  * This program invokes all internet services as needed.  Connection-oriented
2257769Sandrew  * services are invoked each time a connection is made, by creating a process.
2357769Sandrew  * This process is passed the connection as file descriptor 0 and is expected
2457769Sandrew  * to do a getpeername to find out the source host and port.
2516374Skarels  *
2616374Skarels  * Datagram oriented services are invoked when a datagram
2716374Skarels  * arrives; a process is created and passed a pending message
2816374Skarels  * on file descriptor 0.  Datagram servers may either connect
2916374Skarels  * to their peer, freeing up the original socket for inetd
3016374Skarels  * to receive further messages on, or ``take over the socket'',
3116374Skarels  * processing all arriving datagrams and, eventually, timing
3216374Skarels  * out.	 The first type of server is said to be ``multi-threaded'';
3316374Skarels  * the second type of server ``single-threaded''.
3416374Skarels  *
3516374Skarels  * Inetd uses a configuration file which is read at startup
3616374Skarels  * and, possibly, at some later time in response to a hangup signal.
3716374Skarels  * The configuration file is ``free format'' with fields given in the
3816374Skarels  * order shown below.  Continuation lines for an entry must being with
3916374Skarels  * a space or tab.  All fields must be present in each entry.
4016374Skarels  *
4157769Sandrew  *	service name			must be in /etc/services or must
4257769Sandrew  *					name a tcpmux service
4316374Skarels  *	socket type			stream/dgram/raw/rdm/seqpacket
4416374Skarels  *	protocol			must be in /etc/protocols
4516374Skarels  *	wait/nowait			single-threaded/multi-threaded
4617346Sbloom  *	user				user to run daemon as
4716374Skarels  *	server program			full path name
4840740Sbostic  *	server program arguments	maximum of MAXARGS (20)
4916374Skarels  *
5057769Sandrew  * TCP services without official port numbers are handled with the
5157769Sandrew  * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
5257769Sandrew  * requests. When a connection is made from a foreign host, the service
5357769Sandrew  * requested is passed to tcpmux, which looks it up in the servtab list
5457769Sandrew  * and returns the proper entry for the service. Tcpmux returns a
5557769Sandrew  * negative reply if the service doesn't exist, otherwise the invoked
5657769Sandrew  * server is expected to return the positive reply if the service type in
5757769Sandrew  * inetd.conf file has the prefix "tcpmux/". If the service type has the
5857769Sandrew  * prefix "tcpmux/+", tcpmux will return the positive reply for the
5957769Sandrew  * process; this is for compatibility with older server code, and also
6057769Sandrew  * allows you to invoke programs that use stdin/stdout without putting any
6157769Sandrew  * special server code in them. Services that use tcpmux are "nowait"
6257769Sandrew  * because they do not have a well-known port and hence cannot listen
6357769Sandrew  * for new requests.
6457769Sandrew  *
6516374Skarels  * Comment lines are indicated by a `#' in column 1.
6616374Skarels  */
6716374Skarels #include <sys/param.h>
6816374Skarels #include <sys/stat.h>
6916374Skarels #include <sys/ioctl.h>
7016374Skarels #include <sys/socket.h>
7116374Skarels #include <sys/wait.h>
7226921Slepreau #include <sys/time.h>
7326921Slepreau #include <sys/resource.h>
7416374Skarels 
7516374Skarels #include <netinet/in.h>
7616374Skarels #include <arpa/inet.h>
7716374Skarels 
7816374Skarels #include <errno.h>
7957769Sandrew #include <fcntl.h>
8016374Skarels #include <netdb.h>
8117346Sbloom #include <pwd.h>
8257769Sandrew #include <signal.h>
8336603Sbostic #include <stdio.h>
8457769Sandrew #include <stdlib.h>
8542067Sbostic #include <string.h>
8657769Sandrew #include <syslog.h>
8757769Sandrew #include <unistd.h>
8857769Sandrew 
8937282Sbostic #include "pathnames.h"
9016374Skarels 
9127535Skarels #define	TOOMANY		40		/* don't start more than TOOMANY */
9227535Skarels #define	CNT_INTVL	60		/* servers in CNT_INTVL sec. */
9327535Skarels #define	RETRYTIME	(60*10)		/* retry after bind or server fail */
9427535Skarels 
9527535Skarels #define	SIGBLOCK	(sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
9627535Skarels 
9716374Skarels 
9839838Sbostic void	config(), reapchild(), retry();
9916374Skarels 
10016374Skarels int	debug = 0;
10125046Skarels int	nsock, maxsock;
10225046Skarels fd_set	allsock;
10316374Skarels int	options;
10427535Skarels int	timingout;
10557769Sandrew int	toomany = TOOMANY;
10616374Skarels struct	servent *sp;
10716374Skarels 
10816374Skarels struct	servtab {
10916374Skarels 	char	*se_service;		/* name of service */
11016374Skarels 	int	se_socktype;		/* type of socket to use */
11116374Skarels 	char	*se_proto;		/* protocol used */
11216374Skarels 	short	se_wait;		/* single threaded server */
11316374Skarels 	short	se_checked;		/* looked at during merge */
11417346Sbloom 	char	*se_user;		/* user name to run as */
11526877Skarels 	struct	biltin *se_bi;		/* if built-in, description */
11616374Skarels 	char	*se_server;		/* server program */
11740740Sbostic #define	MAXARGV 20
11816374Skarels 	char	*se_argv[MAXARGV+1];	/* program arguments */
11916374Skarels 	int	se_fd;			/* open descriptor */
12057769Sandrew 	int	se_type;		/* type */
12116374Skarels 	struct	sockaddr_in se_ctrladdr;/* bound address */
12227535Skarels 	int	se_count;		/* number started since se_time */
12327535Skarels 	struct	timeval se_time;	/* start of se_count */
12416374Skarels 	struct	servtab *se_next;
12516374Skarels } *servtab;
12616374Skarels 
12757769Sandrew #define NORM_TYPE	0
12857769Sandrew #define MUX_TYPE	1
12957769Sandrew #define MUXPLUS_TYPE	2
13057769Sandrew #define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
13157769Sandrew 			 ((sep)->se_type == MUXPLUS_TYPE))
13257769Sandrew #define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
13357769Sandrew 
13426877Skarels int echo_stream(), discard_stream(), machtime_stream();
13526877Skarels int daytime_stream(), chargen_stream();
13626877Skarels int echo_dg(), discard_dg(), machtime_dg(), daytime_dg(), chargen_dg();
13757769Sandrew struct servtab *tcpmux();
13826877Skarels 
13926877Skarels struct biltin {
14026877Skarels 	char	*bi_service;		/* internally provided service name */
14126877Skarels 	int	bi_socktype;		/* type of socket supported */
14226877Skarels 	short	bi_fork;		/* 1 if should fork before call */
14326877Skarels 	short	bi_wait;		/* 1 if should wait for child */
14426877Skarels 	int	(*bi_fn)();		/* function which performs it */
14526877Skarels } biltins[] = {
14626877Skarels 	/* Echo received data */
14726877Skarels 	"echo",		SOCK_STREAM,	1, 0,	echo_stream,
14826877Skarels 	"echo",		SOCK_DGRAM,	0, 0,	echo_dg,
14926877Skarels 
15026877Skarels 	/* Internet /dev/null */
15126877Skarels 	"discard",	SOCK_STREAM,	1, 0,	discard_stream,
15226877Skarels 	"discard",	SOCK_DGRAM,	0, 0,	discard_dg,
15326877Skarels 
15426877Skarels 	/* Return 32 bit time since 1970 */
15526877Skarels 	"time",		SOCK_STREAM,	0, 0,	machtime_stream,
15626877Skarels 	"time",		SOCK_DGRAM,	0, 0,	machtime_dg,
15726877Skarels 
15826877Skarels 	/* Return human-readable time */
15926877Skarels 	"daytime",	SOCK_STREAM,	0, 0,	daytime_stream,
16026877Skarels 	"daytime",	SOCK_DGRAM,	0, 0,	daytime_dg,
16126877Skarels 
16226877Skarels 	/* Familiar character generator */
16326877Skarels 	"chargen",	SOCK_STREAM,	1, 0,	chargen_stream,
16426877Skarels 	"chargen",	SOCK_DGRAM,	0, 0,	chargen_dg,
16557769Sandrew 
16657769Sandrew 	"tcpmux",	SOCK_STREAM,	1, 0,	(int (*)())tcpmux,
16757769Sandrew 
16857769Sandrew 	NULL
16926877Skarels };
17026877Skarels 
17126877Skarels #define NUMINT	(sizeof(intab) / sizeof(struct inent))
17237282Sbostic char	*CONFIG = _PATH_INETDCONF;
17326877Skarels char	**Argv;
17426877Skarels char 	*LastArg;
17516374Skarels 
17626877Skarels main(argc, argv, envp)
17716374Skarels 	int argc;
17826877Skarels 	char *argv[], *envp[];
17916374Skarels {
18016374Skarels 	register struct servtab *sep;
18117346Sbloom 	register struct passwd *pwd;
18236603Sbostic 	register int tmpint;
18327535Skarels 	struct sigvec sv;
18436603Sbostic 	int ch, pid, dofork;
18536603Sbostic 	char buf[50];
18616374Skarels 
18726877Skarels 	Argv = argv;
18826877Skarels 	if (envp == 0 || *envp == 0)
18926877Skarels 		envp = argv;
19026877Skarels 	while (*envp)
19126877Skarels 		envp++;
19226877Skarels 	LastArg = envp[-1] + strlen(envp[-1]);
19316374Skarels 
19457769Sandrew 	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
19557769Sandrew 
19657769Sandrew 	while ((ch = getopt(argc, argv, "dR:")) != EOF)
19736603Sbostic 		switch(ch) {
19816374Skarels 		case 'd':
19916374Skarels 			debug = 1;
20016374Skarels 			options |= SO_DEBUG;
20116374Skarels 			break;
20257769Sandrew 		case 'R': {	/* invocation rate */
20357769Sandrew 			char *p;
20457769Sandrew 
20557769Sandrew 			tmpint = strtol(optarg, &p, 0);
20657769Sandrew 			if (tmpint < 1 || *p)
20757769Sandrew 				syslog(LOG_ERR,
20857769Sandrew 			         "-R %s: bad value for service invocation rate",
20957769Sandrew 					optarg);
21057769Sandrew 			else
21157769Sandrew 				toomany = tmpint;
21257769Sandrew 			break;
21357769Sandrew 		}
21436603Sbostic 		case '?':
21516374Skarels 		default:
21657769Sandrew 			syslog(LOG_ERR,
21757769Sandrew 				"usage: inetd [-d] [-R rate] [conf-file]");
21836603Sbostic 			exit(1);
21916374Skarels 		}
22036603Sbostic 	argc -= optind;
22136603Sbostic 	argv += optind;
22236603Sbostic 
22316374Skarels 	if (argc > 0)
22416374Skarels 		CONFIG = argv[0];
22557769Sandrew 	if (debug == 0) {
22644711Skarels 		daemon(0, 0);
22757769Sandrew 	}
22857769Sandrew 	bzero(&sv, sizeof(sv));
22927535Skarels 	sv.sv_mask = SIGBLOCK;
23027535Skarels 	sv.sv_handler = retry;
23127535Skarels 	sigvec(SIGALRM, &sv, (struct sigvec *)0);
23216374Skarels 	config();
23327535Skarels 	sv.sv_handler = config;
23427535Skarels 	sigvec(SIGHUP, &sv, (struct sigvec *)0);
23527535Skarels 	sv.sv_handler = reapchild;
23627535Skarels 	sigvec(SIGCHLD, &sv, (struct sigvec *)0);
23727535Skarels 
23836603Sbostic 	{
23936603Sbostic 		/* space for daemons to overwrite environment for ps */
24036603Sbostic #define	DUMMYSIZE	100
24136603Sbostic 		char dummy[DUMMYSIZE];
24236603Sbostic 
24336603Sbostic 		(void)memset(dummy, 'x', sizeof(DUMMYSIZE) - 1);
24436603Sbostic 		dummy[DUMMYSIZE - 1] = '\0';
24536603Sbostic 		(void)setenv("inetd_dummy", dummy, 1);
24636603Sbostic 	}
24736603Sbostic 
24816374Skarels 	for (;;) {
24936603Sbostic 	    int n, ctrl;
25027535Skarels 	    fd_set readable;
25116374Skarels 
25232091Skarels 	    if (nsock == 0) {
25332091Skarels 		(void) sigblock(SIGBLOCK);
25432091Skarels 		while (nsock == 0)
25532246Sbostic 		    sigpause(0L);
25632246Sbostic 		(void) sigsetmask(0L);
25732091Skarels 	    }
25827535Skarels 	    readable = allsock;
25927535Skarels 	    if ((n = select(maxsock + 1, &readable, (fd_set *)0,
26027535Skarels 		(fd_set *)0, (struct timeval *)0)) <= 0) {
26127535Skarels 		    if (n < 0 && errno != EINTR)
26257769Sandrew 			syslog(LOG_WARNING, "select: %m");
26327535Skarels 		    sleep(1);
26427535Skarels 		    continue;
26527535Skarels 	    }
26627535Skarels 	    for (sep = servtab; n && sep; sep = sep->se_next)
26746973Skarels 	        if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
26846973Skarels 		    n--;
26946973Skarels 		    if (debug)
27046973Skarels 			    fprintf(stderr, "someone wants %s\n",
27146973Skarels 				sep->se_service);
27246973Skarels 		    if (sep->se_socktype == SOCK_STREAM) {
27346973Skarels 			    ctrl = accept(sep->se_fd, (struct sockaddr *)0,
27446973Skarels 				(int *)0);
27546973Skarels 			    if (debug)
27646973Skarels 				    fprintf(stderr, "accept, ctrl %d\n", ctrl);
27746973Skarels 			    if (ctrl < 0) {
27857769Sandrew 				    if (errno != EINTR)
27957769Sandrew 					    syslog(LOG_WARNING,
28057769Sandrew 						"accept (for %s): %m",
28157769Sandrew 						sep->se_service);
28246973Skarels 				    continue;
28346973Skarels 			    }
28457769Sandrew 			    /*
28557769Sandrew 			     * Call tcpmux to find the real service to exec.
28657769Sandrew 			     */
28757769Sandrew 			    if (sep->se_bi &&
28857769Sandrew 				sep->se_bi->bi_fn == (int (*)()) tcpmux) {
28957769Sandrew 				    sep = tcpmux(ctrl);
29057769Sandrew 				    if (sep == NULL) {
29157769Sandrew 					    close(ctrl);
29257769Sandrew 					    continue;
29357769Sandrew 				    }
29457769Sandrew 			    }
29546973Skarels 		    } else
29646973Skarels 			    ctrl = sep->se_fd;
29746973Skarels 		    (void) sigblock(SIGBLOCK);
29846973Skarels 		    pid = 0;
29946973Skarels 		    dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
30046973Skarels 		    if (dofork) {
30146973Skarels 			    if (sep->se_count++ == 0)
30246973Skarels 				(void)gettimeofday(&sep->se_time,
30346973Skarels 				    (struct timezone *)0);
30457769Sandrew 			    else if (sep->se_count >= toomany) {
30527535Skarels 				struct timeval now;
30627535Skarels 
30727535Skarels 				(void)gettimeofday(&now, (struct timezone *)0);
30827535Skarels 				if (now.tv_sec - sep->se_time.tv_sec >
30927535Skarels 				    CNT_INTVL) {
31027535Skarels 					sep->se_time = now;
31127535Skarels 					sep->se_count = 1;
31227535Skarels 				} else {
31327535Skarels 					syslog(LOG_ERR,
31457769Sandrew 			"%s/%s server failing (looping), service terminated",
31527535Skarels 					    sep->se_service, sep->se_proto);
31657769Sandrew 					close_sep(sep);
31757769Sandrew 					sigsetmask(0L);
31827535Skarels 					if (!timingout) {
31927535Skarels 						timingout = 1;
32027535Skarels 						alarm(RETRYTIME);
32127535Skarels 					}
32257769Sandrew 					continue;
32327535Skarels 				}
32446973Skarels 			    }
32546973Skarels 			    pid = fork();
32646973Skarels 		    }
32746973Skarels 		    if (pid < 0) {
32846973Skarels 			    syslog(LOG_ERR, "fork: %m");
32946973Skarels 			    if (sep->se_socktype == SOCK_STREAM)
33046973Skarels 				    close(ctrl);
33146973Skarels 			    sigsetmask(0L);
33246973Skarels 			    sleep(1);
33346973Skarels 			    continue;
33446973Skarels 		    }
33546973Skarels 		    if (pid && sep->se_wait) {
33646973Skarels 			    sep->se_wait = pid;
33749806Sbostic 			    if (sep->se_fd >= 0) {
33849806Sbostic 				FD_CLR(sep->se_fd, &allsock);
33949806Sbostic 			        nsock--;
34049806Sbostic 			    }
34146973Skarels 		    }
34246973Skarels 		    sigsetmask(0L);
34346973Skarels 		    if (pid == 0) {
34446973Skarels 			    if (debug && dofork)
34544711Skarels 				setsid();
34657769Sandrew 			    if (dofork) {
34757769Sandrew 				if (debug)
34857769Sandrew 					fprintf(stderr, "+ Closing from %d\n",
34957769Sandrew 						maxsock);
35057769Sandrew 				for (tmpint = maxsock; tmpint > 2; tmpint--)
35136603Sbostic 					if (tmpint != ctrl)
35236603Sbostic 						close(tmpint);
35357769Sandrew 			    }
35446973Skarels 			    if (sep->se_bi)
35526877Skarels 				(*sep->se_bi->bi_fn)(ctrl, sep);
35646973Skarels 			    else {
35746973Skarels 				if (debug)
35846973Skarels 					fprintf(stderr, "%d execl %s\n",
35946973Skarels 					    getpid(), sep->se_server);
36026877Skarels 				dup2(ctrl, 0);
36126877Skarels 				close(ctrl);
36226877Skarels 				dup2(0, 1);
36326877Skarels 				dup2(0, 2);
36426877Skarels 				if ((pwd = getpwnam(sep->se_user)) == NULL) {
36526877Skarels 					syslog(LOG_ERR,
36657769Sandrew 					    "%s/%s: %s: No such user",
36757769Sandrew 						sep->se_service, sep->se_proto,
36857769Sandrew 						sep->se_user);
36927535Skarels 					if (sep->se_socktype != SOCK_STREAM)
37027535Skarels 						recv(0, buf, sizeof (buf), 0);
37126877Skarels 					_exit(1);
37226877Skarels 				}
37326877Skarels 				if (pwd->pw_uid) {
37457769Sandrew 					if (setgid(pwd->pw_gid) < 0) {
37557769Sandrew 						syslog(LOG_ERR,
37657769Sandrew 						  "%s: can't set gid %d: %m",
37757769Sandrew 						  sep->se_service, pwd->pw_gid);
37857769Sandrew 						_exit(1);
37957769Sandrew 					}
38057769Sandrew 					(void) initgroups(pwd->pw_name,
38157769Sandrew 							pwd->pw_gid);
38257769Sandrew 					if (setuid(pwd->pw_uid) < 0) {
38357769Sandrew 						syslog(LOG_ERR,
38457769Sandrew 						  "%s: can't set uid %d: %m",
38557769Sandrew 						  sep->se_service, pwd->pw_uid);
38657769Sandrew 						_exit(1);
38757769Sandrew 					}
38826877Skarels 				}
38926877Skarels 				execv(sep->se_server, sep->se_argv);
39026877Skarels 				if (sep->se_socktype != SOCK_STREAM)
39126877Skarels 					recv(0, buf, sizeof (buf), 0);
39257769Sandrew 				syslog(LOG_ERR,
39357769Sandrew 				    "cannot execute %s: %m", sep->se_server);
39426877Skarels 				_exit(1);
39546973Skarels 			    }
39646973Skarels 		    }
39746973Skarels 		    if (sep->se_socktype == SOCK_STREAM)
39846973Skarels 			    close(ctrl);
39916374Skarels 		}
40016374Skarels 	}
40116374Skarels }
40216374Skarels 
40339838Sbostic void
40416374Skarels reapchild()
40516374Skarels {
40646973Skarels 	int status;
40716374Skarels 	int pid;
40816374Skarels 	register struct servtab *sep;
40916374Skarels 
41016374Skarels 	for (;;) {
41146973Skarels 		pid = wait3(&status, WNOHANG, (struct rusage *)0);
41216374Skarels 		if (pid <= 0)
41316374Skarels 			break;
41416374Skarels 		if (debug)
41557769Sandrew 			fprintf(stderr, "%d reaped, status %#x\n",
41657769Sandrew 				pid, status);
41716374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
41816374Skarels 			if (sep->se_wait == pid) {
41946973Skarels 				if (status)
42016510Sralph 					syslog(LOG_WARNING,
42116510Sralph 					    "%s: exit status 0x%x",
42216374Skarels 					    sep->se_server, status);
42316374Skarels 				if (debug)
42416374Skarels 					fprintf(stderr, "restored %s, fd %d\n",
42516374Skarels 					    sep->se_service, sep->se_fd);
42625046Skarels 				FD_SET(sep->se_fd, &allsock);
42725046Skarels 				nsock++;
42816374Skarels 				sep->se_wait = 1;
42916374Skarels 			}
43016374Skarels 	}
43116374Skarels }
43216374Skarels 
43339838Sbostic void
43416374Skarels config()
43516374Skarels {
43616374Skarels 	register struct servtab *sep, *cp, **sepp;
43716374Skarels 	struct servtab *getconfigent(), *enter();
43857769Sandrew 	struct passwd *pwd;
43932246Sbostic 	long omask;
44016374Skarels 
44116374Skarels 	if (!setconfig()) {
44216510Sralph 		syslog(LOG_ERR, "%s: %m", CONFIG);
44316374Skarels 		return;
44416374Skarels 	}
44516374Skarels 	for (sep = servtab; sep; sep = sep->se_next)
44616374Skarels 		sep->se_checked = 0;
44716374Skarels 	while (cp = getconfigent()) {
44857769Sandrew 		if ((pwd = getpwnam(cp->se_user)) == NULL) {
44957769Sandrew 			syslog(LOG_ERR,
45057769Sandrew 				"%s/%s: No such user '%s', service ignored",
45157769Sandrew 				cp->se_service, cp->se_proto, cp->se_user);
45257769Sandrew 			continue;
45357769Sandrew 		}
45416374Skarels 		for (sep = servtab; sep; sep = sep->se_next)
45516374Skarels 			if (strcmp(sep->se_service, cp->se_service) == 0 &&
45616374Skarels 			    strcmp(sep->se_proto, cp->se_proto) == 0)
45716374Skarels 				break;
45816374Skarels 		if (sep != 0) {
45916374Skarels 			int i;
46016374Skarels 
46127535Skarels 			omask = sigblock(SIGBLOCK);
46240745Sbostic 			/*
46340745Sbostic 			 * sep->se_wait may be holding the pid of a daemon
46440745Sbostic 			 * that we're waiting for.  If so, don't overwrite
46540745Sbostic 			 * it unless the config file explicitly says don't
46640745Sbostic 			 * wait.
46740745Sbostic 			 */
46840745Sbostic 			if (cp->se_bi == 0 &&
46940745Sbostic 			    (sep->se_wait == 1 || cp->se_wait == 0))
47027535Skarels 				sep->se_wait = cp->se_wait;
47116374Skarels #define SWAP(a, b) { char *c = a; a = b; b = c; }
47226921Slepreau 			if (cp->se_user)
47326921Slepreau 				SWAP(sep->se_user, cp->se_user);
47416374Skarels 			if (cp->se_server)
47516374Skarels 				SWAP(sep->se_server, cp->se_server);
47616374Skarels 			for (i = 0; i < MAXARGV; i++)
47716374Skarels 				SWAP(sep->se_argv[i], cp->se_argv[i]);
47816374Skarels 			sigsetmask(omask);
47916374Skarels 			freeconfig(cp);
48029794Skarels 			if (debug)
48129794Skarels 				print_service("REDO", sep);
48229794Skarels 		} else {
48316374Skarels 			sep = enter(cp);
48429794Skarels 			if (debug)
48529794Skarels 				print_service("ADD ", sep);
48629794Skarels 		}
48716374Skarels 		sep->se_checked = 1;
48857769Sandrew 		if (ISMUX(sep)) {
48957769Sandrew 			sep->se_fd = -1;
49057769Sandrew 			continue;
49157769Sandrew 		}
49216374Skarels 		sp = getservbyname(sep->se_service, sep->se_proto);
49316374Skarels 		if (sp == 0) {
49416510Sralph 			syslog(LOG_ERR, "%s/%s: unknown service",
49516374Skarels 			    sep->se_service, sep->se_proto);
49657769Sandrew 			sep->se_checked = 0;
49716374Skarels 			continue;
49816374Skarels 		}
49927535Skarels 		if (sp->s_port != sep->se_ctrladdr.sin_port) {
50060924Smckusick 			sep->se_ctrladdr.sin_family = AF_INET;
50127535Skarels 			sep->se_ctrladdr.sin_port = sp->s_port;
50257769Sandrew 			if (sep->se_fd >= 0)
50357769Sandrew 				close_sep(sep);
50416374Skarels 		}
50527535Skarels 		if (sep->se_fd == -1)
50627535Skarels 			setup(sep);
50716374Skarels 	}
50816374Skarels 	endconfig();
50916374Skarels 	/*
51016374Skarels 	 * Purge anything not looked at above.
51116374Skarels 	 */
51227535Skarels 	omask = sigblock(SIGBLOCK);
51316374Skarels 	sepp = &servtab;
51416374Skarels 	while (sep = *sepp) {
51516374Skarels 		if (sep->se_checked) {
51616374Skarels 			sepp = &sep->se_next;
51716374Skarels 			continue;
51816374Skarels 		}
51916374Skarels 		*sepp = sep->se_next;
52057769Sandrew 		if (sep->se_fd >= 0)
52157769Sandrew 			close_sep(sep);
52229794Skarels 		if (debug)
52329794Skarels 			print_service("FREE", sep);
52416374Skarels 		freeconfig(sep);
52516374Skarels 		free((char *)sep);
52616374Skarels 	}
52716374Skarels 	(void) sigsetmask(omask);
52816374Skarels }
52916374Skarels 
53039838Sbostic void
53127535Skarels retry()
53227535Skarels {
53327535Skarels 	register struct servtab *sep;
53427535Skarels 
53527535Skarels 	timingout = 0;
53627535Skarels 	for (sep = servtab; sep; sep = sep->se_next)
53727535Skarels 		if (sep->se_fd == -1)
53827535Skarels 			setup(sep);
53927535Skarels }
54027535Skarels 
54127535Skarels setup(sep)
54227535Skarels 	register struct servtab *sep;
54327535Skarels {
54427535Skarels 	int on = 1;
54527535Skarels 
54627535Skarels 	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
54757769Sandrew 		if (debug)
54857769Sandrew 			fprintf(stderr, "socket failed on %s/%s: %s\n",
54957769Sandrew 				sep->se_service, sep->se_proto,
55057769Sandrew 				strerror(errno));
55127535Skarels 		syslog(LOG_ERR, "%s/%s: socket: %m",
55227535Skarels 		    sep->se_service, sep->se_proto);
55327535Skarels 		return;
55427535Skarels 	}
55527535Skarels #define	turnon(fd, opt) \
55627535Skarels setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
55727535Skarels 	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
55827535Skarels 	    turnon(sep->se_fd, SO_DEBUG) < 0)
55927535Skarels 		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
56027535Skarels 	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
56127535Skarels 		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
56227535Skarels #undef turnon
56346907Sbostic 	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
56427535Skarels 	    sizeof (sep->se_ctrladdr)) < 0) {
56557769Sandrew 		if (debug)
56657769Sandrew 			fprintf(stderr, "bind failed on %s/%s: %s\n",
56757769Sandrew 				sep->se_service, sep->se_proto,
56857769Sandrew 				strerror(errno));
56927535Skarels 		syslog(LOG_ERR, "%s/%s: bind: %m",
57027535Skarels 		    sep->se_service, sep->se_proto);
57127535Skarels 		(void) close(sep->se_fd);
57227535Skarels 		sep->se_fd = -1;
57327535Skarels 		if (!timingout) {
57427535Skarels 			timingout = 1;
57527535Skarels 			alarm(RETRYTIME);
57627535Skarels 		}
57727535Skarels 		return;
57827535Skarels 	}
57927535Skarels 	if (sep->se_socktype == SOCK_STREAM)
58027535Skarels 		listen(sep->se_fd, 10);
58127535Skarels 	FD_SET(sep->se_fd, &allsock);
58227535Skarels 	nsock++;
58327535Skarels 	if (sep->se_fd > maxsock)
58427535Skarels 		maxsock = sep->se_fd;
58557769Sandrew 	if (debug) {
58657769Sandrew 		fprintf(stderr, "registered %s on %d\n",
58757769Sandrew 			sep->se_server, sep->se_fd);
58857769Sandrew 	}
58927535Skarels }
59027535Skarels 
59157769Sandrew /*
59257769Sandrew  * Finish with a service and its socket.
59357769Sandrew  */
59457769Sandrew close_sep(sep)
59557769Sandrew 	register struct servtab *sep;
59657769Sandrew {
59757769Sandrew 	if (sep->se_fd >= 0) {
59857769Sandrew 		nsock--;
59957769Sandrew 		FD_CLR(sep->se_fd, &allsock);
60057769Sandrew 		(void) close(sep->se_fd);
60157769Sandrew 		sep->se_fd = -1;
60257769Sandrew 	}
60357769Sandrew 	sep->se_count = 0;
60457769Sandrew 	/*
60557769Sandrew 	 * Don't keep the pid of this running deamon: when reapchild()
60657769Sandrew 	 * reaps this pid, it would erroneously increment nsock.
60757769Sandrew 	 */
60857769Sandrew 	if (sep->se_wait > 1)
60957769Sandrew 		sep->se_wait = 1;
61057769Sandrew }
61157769Sandrew 
61216374Skarels struct servtab *
61316374Skarels enter(cp)
61416374Skarels 	struct servtab *cp;
61516374Skarels {
61616374Skarels 	register struct servtab *sep;
61732246Sbostic 	long omask;
61816374Skarels 
61916374Skarels 	sep = (struct servtab *)malloc(sizeof (*sep));
62016374Skarels 	if (sep == (struct servtab *)0) {
62116510Sralph 		syslog(LOG_ERR, "Out of memory.");
62216374Skarels 		exit(-1);
62316374Skarels 	}
62416374Skarels 	*sep = *cp;
62516374Skarels 	sep->se_fd = -1;
62627535Skarels 	omask = sigblock(SIGBLOCK);
62716374Skarels 	sep->se_next = servtab;
62816374Skarels 	servtab = sep;
62916374Skarels 	sigsetmask(omask);
63016374Skarels 	return (sep);
63116374Skarels }
63216374Skarels 
63316374Skarels FILE	*fconfig = NULL;
63416374Skarels struct	servtab serv;
63516374Skarels char	line[256];
63657769Sandrew char	*sskip(), *skip(), *nextline();
63716374Skarels 
63816374Skarels setconfig()
63916374Skarels {
64016374Skarels 
64116374Skarels 	if (fconfig != NULL) {
64257769Sandrew 		fseek(fconfig, 0L, SEEK_SET);
64316374Skarels 		return (1);
64416374Skarels 	}
64516374Skarels 	fconfig = fopen(CONFIG, "r");
64616374Skarels 	return (fconfig != NULL);
64716374Skarels }
64816374Skarels 
64916374Skarels endconfig()
65016374Skarels {
65136603Sbostic 	if (fconfig) {
65236603Sbostic 		(void) fclose(fconfig);
65336603Sbostic 		fconfig = NULL;
65436603Sbostic 	}
65516374Skarels }
65616374Skarels 
65716374Skarels struct servtab *
65816374Skarels getconfigent()
65916374Skarels {
66016374Skarels 	register struct servtab *sep = &serv;
66116374Skarels 	int argc;
66246907Sbostic 	char *cp, *arg, *newstr();
66357769Sandrew 	static char TCPMUX_TOKEN[] = "tcpmux/";
66457769Sandrew #define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
66516374Skarels 
66626877Skarels more:
66757769Sandrew 	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
66816374Skarels 		;
66916374Skarels 	if (cp == NULL)
67016374Skarels 		return ((struct servtab *)0);
67157769Sandrew 	/*
67257769Sandrew 	 * clear the static buffer, since some fields (se_ctrladdr,
67357769Sandrew 	 * for example) don't get initialized here.
67457769Sandrew 	 */
67557769Sandrew 	bzero((caddr_t)sep, sizeof *sep);
67616374Skarels 	arg = skip(&cp);
67757769Sandrew 	if (cp == NULL) {
67857769Sandrew 		/* got an empty line containing just blanks/tabs. */
67957769Sandrew 		goto more;
68057769Sandrew 	}
68157769Sandrew 	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
68257769Sandrew 		char *c = arg + MUX_LEN;
68357769Sandrew 		if (*c == '+') {
68457769Sandrew 			sep->se_type = MUXPLUS_TYPE;
68557769Sandrew 			c++;
68657769Sandrew 		} else
68757769Sandrew 			sep->se_type = MUX_TYPE;
68857769Sandrew 		sep->se_service = newstr(c);
68957769Sandrew 	} else {
69057769Sandrew 		sep->se_service = newstr(arg);
69157769Sandrew 		sep->se_type = NORM_TYPE;
69257769Sandrew 	}
69357769Sandrew 	arg = sskip(&cp);
69416374Skarels 	if (strcmp(arg, "stream") == 0)
69516374Skarels 		sep->se_socktype = SOCK_STREAM;
69616374Skarels 	else if (strcmp(arg, "dgram") == 0)
69716374Skarels 		sep->se_socktype = SOCK_DGRAM;
69816374Skarels 	else if (strcmp(arg, "rdm") == 0)
69916374Skarels 		sep->se_socktype = SOCK_RDM;
70016374Skarels 	else if (strcmp(arg, "seqpacket") == 0)
70116374Skarels 		sep->se_socktype = SOCK_SEQPACKET;
70216374Skarels 	else if (strcmp(arg, "raw") == 0)
70316374Skarels 		sep->se_socktype = SOCK_RAW;
70416374Skarels 	else
70516374Skarels 		sep->se_socktype = -1;
70657769Sandrew 	sep->se_proto = newstr(sskip(&cp));
70757769Sandrew 	arg = sskip(&cp);
70816374Skarels 	sep->se_wait = strcmp(arg, "wait") == 0;
70957769Sandrew 	if (ISMUX(sep)) {
71057769Sandrew 		/*
71157769Sandrew 		 * Silently enforce "nowait" for TCPMUX services since
71257769Sandrew 		 * they don't have an assigned port to listen on.
71357769Sandrew 		 */
71457769Sandrew 		sep->se_wait = 0;
71557769Sandrew 
71657769Sandrew 		if (strcmp(sep->se_proto, "tcp")) {
71757769Sandrew 			syslog(LOG_ERR,
71857769Sandrew 				"%s: bad protocol for tcpmux service %s",
71957769Sandrew 				CONFIG, sep->se_service);
72057769Sandrew 			goto more;
72157769Sandrew 		}
72257769Sandrew 		if (sep->se_socktype != SOCK_STREAM) {
72357769Sandrew 			syslog(LOG_ERR,
72457769Sandrew 				"%s: bad socket type for tcpmux service %s",
72557769Sandrew 				CONFIG, sep->se_service);
72657769Sandrew 			goto more;
72757769Sandrew 		}
72857769Sandrew 	}
72957769Sandrew 	sep->se_user = newstr(sskip(&cp));
73057769Sandrew 	sep->se_server = newstr(sskip(&cp));
73126877Skarels 	if (strcmp(sep->se_server, "internal") == 0) {
73226877Skarels 		register struct biltin *bi;
73326877Skarels 
73426877Skarels 		for (bi = biltins; bi->bi_service; bi++)
73526877Skarels 			if (bi->bi_socktype == sep->se_socktype &&
73626877Skarels 			    strcmp(bi->bi_service, sep->se_service) == 0)
73726877Skarels 				break;
73826877Skarels 		if (bi->bi_service == 0) {
73957769Sandrew 			syslog(LOG_ERR, "internal service %s unknown",
74026877Skarels 				sep->se_service);
74126877Skarels 			goto more;
74226877Skarels 		}
74326877Skarels 		sep->se_bi = bi;
74426877Skarels 		sep->se_wait = bi->bi_wait;
74529794Skarels 	} else
74629794Skarels 		sep->se_bi = NULL;
74716374Skarels 	argc = 0;
74816374Skarels 	for (arg = skip(&cp); cp; arg = skip(&cp))
74916374Skarels 		if (argc < MAXARGV)
75046907Sbostic 			sep->se_argv[argc++] = newstr(arg);
75116374Skarels 	while (argc <= MAXARGV)
75216374Skarels 		sep->se_argv[argc++] = NULL;
75316374Skarels 	return (sep);
75416374Skarels }
75516374Skarels 
75616374Skarels freeconfig(cp)
75716374Skarels 	register struct servtab *cp;
75816374Skarels {
75916374Skarels 	int i;
76016374Skarels 
76116374Skarels 	if (cp->se_service)
76216374Skarels 		free(cp->se_service);
76316374Skarels 	if (cp->se_proto)
76416374Skarels 		free(cp->se_proto);
76526921Slepreau 	if (cp->se_user)
76626921Slepreau 		free(cp->se_user);
76716374Skarels 	if (cp->se_server)
76816374Skarels 		free(cp->se_server);
76916374Skarels 	for (i = 0; i < MAXARGV; i++)
77016374Skarels 		if (cp->se_argv[i])
77116374Skarels 			free(cp->se_argv[i]);
77216374Skarels }
77316374Skarels 
77457769Sandrew 
77557769Sandrew /*
77657769Sandrew  * Safe skip - if skip returns null, log a syntax error in the
77757769Sandrew  * configuration file and exit.
77857769Sandrew  */
77916374Skarels char *
78057769Sandrew sskip(cpp)
78157769Sandrew 	char **cpp;
78257769Sandrew {
78357769Sandrew 	register char *cp;
78457769Sandrew 
78557769Sandrew 	cp = skip(cpp);
78657769Sandrew 	if (cp == NULL) {
78757769Sandrew 		syslog(LOG_ERR, "%s: syntax error", CONFIG);
78857769Sandrew 		exit(-1);
78957769Sandrew 	}
79057769Sandrew 	return (cp);
79157769Sandrew }
79257769Sandrew 
79357769Sandrew char *
79416374Skarels skip(cpp)
79516374Skarels 	char **cpp;
79616374Skarels {
79716374Skarels 	register char *cp = *cpp;
79816374Skarels 	char *start;
79916374Skarels 
80016374Skarels again:
80116374Skarels 	while (*cp == ' ' || *cp == '\t')
80216374Skarels 		cp++;
80316374Skarels 	if (*cp == '\0') {
80443455Sbostic 		int c;
80516374Skarels 
80616374Skarels 		c = getc(fconfig);
80736603Sbostic 		(void) ungetc(c, fconfig);
80816374Skarels 		if (c == ' ' || c == '\t')
80916374Skarels 			if (cp = nextline(fconfig))
81016374Skarels 				goto again;
81116374Skarels 		*cpp = (char *)0;
81216374Skarels 		return ((char *)0);
81316374Skarels 	}
81416374Skarels 	start = cp;
81516374Skarels 	while (*cp && *cp != ' ' && *cp != '\t')
81616374Skarels 		cp++;
81716374Skarels 	if (*cp != '\0')
81816374Skarels 		*cp++ = '\0';
81916374Skarels 	*cpp = cp;
82016374Skarels 	return (start);
82116374Skarels }
82216374Skarels 
82316374Skarels char *
82416374Skarels nextline(fd)
82516374Skarels 	FILE *fd;
82616374Skarels {
82716374Skarels 	char *cp;
82816374Skarels 
82926921Slepreau 	if (fgets(line, sizeof (line), fd) == NULL)
83016374Skarels 		return ((char *)0);
83116374Skarels 	cp = index(line, '\n');
83216374Skarels 	if (cp)
83316374Skarels 		*cp = '\0';
83416374Skarels 	return (line);
83516374Skarels }
83616374Skarels 
83716374Skarels char *
83846907Sbostic newstr(cp)
83916374Skarels 	char *cp;
84016374Skarels {
84146973Skarels 	if (cp = strdup(cp ? cp : ""))
84246907Sbostic 		return(cp);
84346973Skarels 	syslog(LOG_ERR, "strdup: %m");
84446907Sbostic 	exit(-1);
84516374Skarels }
84626877Skarels 
84726877Skarels setproctitle(a, s)
84826877Skarels 	char *a;
84926877Skarels 	int s;
85026877Skarels {
85126877Skarels 	int size;
85226877Skarels 	register char *cp;
85326877Skarels 	struct sockaddr_in sin;
85426877Skarels 	char buf[80];
85526877Skarels 
85626877Skarels 	cp = Argv[0];
85726877Skarels 	size = sizeof(sin);
85846907Sbostic 	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
85932442Sbostic 		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
86026877Skarels 	else
86132442Sbostic 		(void) sprintf(buf, "-%s", a);
86226877Skarels 	strncpy(cp, buf, LastArg - cp);
86326877Skarels 	cp += strlen(cp);
86426877Skarels 	while (cp < LastArg)
86526877Skarels 		*cp++ = ' ';
86626877Skarels }
86726877Skarels 
86826877Skarels /*
86926877Skarels  * Internet services provided internally by inetd:
87026877Skarels  */
87149986Skarels #define	BUFSIZE	8192
87226877Skarels 
87326877Skarels /* ARGSUSED */
87426877Skarels echo_stream(s, sep)		/* Echo service -- echo data back */
87526877Skarels 	int s;
87626877Skarels 	struct servtab *sep;
87726877Skarels {
87838573Skarels 	char buffer[BUFSIZE];
87926877Skarels 	int i;
88026877Skarels 
88132091Skarels 	setproctitle(sep->se_service, s);
88226877Skarels 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
88326877Skarels 	    write(s, buffer, i) > 0)
88426877Skarels 		;
88526877Skarels 	exit(0);
88626877Skarels }
88726877Skarels 
88826877Skarels /* ARGSUSED */
88926877Skarels echo_dg(s, sep)			/* Echo service -- echo data back */
89026877Skarels 	int s;
89126877Skarels 	struct servtab *sep;
89226877Skarels {
89338573Skarels 	char buffer[BUFSIZE];
89426877Skarels 	int i, size;
89526877Skarels 	struct sockaddr sa;
89626877Skarels 
89726877Skarels 	size = sizeof(sa);
89826877Skarels 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
89926877Skarels 		return;
90026877Skarels 	(void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
90126877Skarels }
90226877Skarels 
90326877Skarels /* ARGSUSED */
90426877Skarels discard_stream(s, sep)		/* Discard service -- ignore data */
90526877Skarels 	int s;
90626877Skarels 	struct servtab *sep;
90726877Skarels {
90849986Skarels 	int ret;
90938573Skarels 	char buffer[BUFSIZE];
91026877Skarels 
91132091Skarels 	setproctitle(sep->se_service, s);
91226877Skarels 	while (1) {
91349986Skarels 		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
91426877Skarels 			;
91549986Skarels 		if (ret == 0 || errno != EINTR)
91626877Skarels 			break;
91726877Skarels 	}
91826877Skarels 	exit(0);
91926877Skarels }
92026877Skarels 
92126877Skarels /* ARGSUSED */
92226877Skarels discard_dg(s, sep)		/* Discard service -- ignore data */
92326877Skarels 	int s;
92426877Skarels 	struct servtab *sep;
92526877Skarels {
92638573Skarels 	char buffer[BUFSIZE];
92726877Skarels 
92826877Skarels 	(void) read(s, buffer, sizeof(buffer));
92926877Skarels }
93026877Skarels 
93126877Skarels #include <ctype.h>
93226877Skarels #define LINESIZ 72
93326877Skarels char ring[128];
93426877Skarels char *endring;
93526877Skarels 
93626877Skarels initring()
93726877Skarels {
93826877Skarels 	register int i;
93926877Skarels 
94026877Skarels 	endring = ring;
94126877Skarels 
94226877Skarels 	for (i = 0; i <= 128; ++i)
94326877Skarels 		if (isprint(i))
94426877Skarels 			*endring++ = i;
94526877Skarels }
94626877Skarels 
94726877Skarels /* ARGSUSED */
94826877Skarels chargen_stream(s, sep)		/* Character generator */
94926877Skarels 	int s;
95026877Skarels 	struct servtab *sep;
95126877Skarels {
95232246Sbostic 	register char *rs;
95332246Sbostic 	int len;
95426877Skarels 	char text[LINESIZ+2];
95526877Skarels 
95632091Skarels 	setproctitle(sep->se_service, s);
95732246Sbostic 
95832246Sbostic 	if (!endring) {
95926877Skarels 		initring();
96032246Sbostic 		rs = ring;
96132246Sbostic 	}
96226877Skarels 
96332246Sbostic 	text[LINESIZ] = '\r';
96432246Sbostic 	text[LINESIZ + 1] = '\n';
96532246Sbostic 	for (rs = ring;;) {
96632246Sbostic 		if ((len = endring - rs) >= LINESIZ)
96732246Sbostic 			bcopy(rs, text, LINESIZ);
96832246Sbostic 		else {
96932246Sbostic 			bcopy(rs, text, len);
97032246Sbostic 			bcopy(ring, text + len, LINESIZ - len);
97132246Sbostic 		}
97232246Sbostic 		if (++rs == endring)
97326877Skarels 			rs = ring;
97432246Sbostic 		if (write(s, text, sizeof(text)) != sizeof(text))
97526877Skarels 			break;
97626877Skarels 	}
97726877Skarels 	exit(0);
97826877Skarels }
97926877Skarels 
98026877Skarels /* ARGSUSED */
98126877Skarels chargen_dg(s, sep)		/* Character generator */
98226877Skarels 	int s;
98326877Skarels 	struct servtab *sep;
98426877Skarels {
98532246Sbostic 	struct sockaddr sa;
98632246Sbostic 	static char *rs;
98732246Sbostic 	int len, size;
98826877Skarels 	char text[LINESIZ+2];
98926877Skarels 
99032246Sbostic 	if (endring == 0) {
99126877Skarels 		initring();
99232246Sbostic 		rs = ring;
99332246Sbostic 	}
99426877Skarels 
99526877Skarels 	size = sizeof(sa);
99626877Skarels 	if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
99726877Skarels 		return;
99832246Sbostic 
99932246Sbostic 	if ((len = endring - rs) >= LINESIZ)
100032246Sbostic 		bcopy(rs, text, LINESIZ);
100132246Sbostic 	else {
100232246Sbostic 		bcopy(rs, text, len);
100332246Sbostic 		bcopy(ring, text + len, LINESIZ - len);
100432246Sbostic 	}
100532246Sbostic 	if (++rs == endring)
100626877Skarels 		rs = ring;
100732246Sbostic 	text[LINESIZ] = '\r';
100832246Sbostic 	text[LINESIZ + 1] = '\n';
100926877Skarels 	(void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
101026877Skarels }
101126877Skarels 
101226877Skarels /*
101326877Skarels  * Return a machine readable date and time, in the form of the
101426877Skarels  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
101526877Skarels  * returns the number of seconds since midnight, Jan 1, 1970,
101626877Skarels  * we must add 2208988800 seconds to this figure to make up for
101726877Skarels  * some seventy years Bell Labs was asleep.
101826877Skarels  */
101926877Skarels 
102026877Skarels long
102126877Skarels machtime()
102226877Skarels {
102326877Skarels 	struct timeval tv;
102426877Skarels 
102526921Slepreau 	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
102657769Sandrew 		if (debug)
102757769Sandrew 			fprintf(stderr, "Unable to get time of day\n");
102826921Slepreau 		return (0L);
102926877Skarels 	}
103060073Storek #define	OFFSET ((u_long)25567 * 24*60*60)
103160073Storek 	return (htonl((long)(tv.tv_sec + OFFSET)));
103260073Storek #undef OFFSET
103326877Skarels }
103426877Skarels 
103526877Skarels /* ARGSUSED */
103626877Skarels machtime_stream(s, sep)
103726877Skarels 	int s;
103826877Skarels 	struct servtab *sep;
103926877Skarels {
104026877Skarels 	long result;
104126877Skarels 
104226877Skarels 	result = machtime();
104326877Skarels 	(void) write(s, (char *) &result, sizeof(result));
104426877Skarels }
104526877Skarels 
104626877Skarels /* ARGSUSED */
104726877Skarels machtime_dg(s, sep)
104826877Skarels 	int s;
104926877Skarels 	struct servtab *sep;
105026877Skarels {
105126877Skarels 	long result;
105226877Skarels 	struct sockaddr sa;
105326877Skarels 	int size;
105426877Skarels 
105526877Skarels 	size = sizeof(sa);
105626921Slepreau 	if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
105726877Skarels 		return;
105826877Skarels 	result = machtime();
105926877Skarels 	(void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
106026877Skarels }
106126877Skarels 
106226877Skarels /* ARGSUSED */
106326877Skarels daytime_stream(s, sep)		/* Return human-readable time of day */
106426877Skarels 	int s;
106526877Skarels 	struct servtab *sep;
106626877Skarels {
106726877Skarels 	char buffer[256];
106857769Sandrew 	time_t clock;
106926877Skarels 
107026877Skarels 	clock = time((time_t *) 0);
107126877Skarels 
107232442Sbostic 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
107326921Slepreau 	(void) write(s, buffer, strlen(buffer));
107426877Skarels }
107526877Skarels 
107626877Skarels /* ARGSUSED */
107726877Skarels daytime_dg(s, sep)		/* Return human-readable time of day */
107826877Skarels 	int s;
107926877Skarels 	struct servtab *sep;
108026877Skarels {
108126877Skarels 	char buffer[256];
108257769Sandrew 	time_t clock;
108326877Skarels 	struct sockaddr sa;
108426877Skarels 	int size;
108526877Skarels 
108626877Skarels 	clock = time((time_t *) 0);
108726877Skarels 
108826877Skarels 	size = sizeof(sa);
108926877Skarels 	if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
109026877Skarels 		return;
109132442Sbostic 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
109226877Skarels 	(void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
109326877Skarels }
109429794Skarels 
109529794Skarels /*
109629794Skarels  * print_service:
109729794Skarels  *	Dump relevant information to stderr
109829794Skarels  */
109929794Skarels print_service(action, sep)
110029794Skarels 	char *action;
110129794Skarels 	struct servtab *sep;
110229794Skarels {
110329794Skarels 	fprintf(stderr,
110429794Skarels 	    "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
110529794Skarels 	    action, sep->se_service, sep->se_proto,
110636603Sbostic 	    sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
110729794Skarels }
110857769Sandrew 
110957769Sandrew /*
111057769Sandrew  *  Based on TCPMUX.C by Mark K. Lottor November 1988
111157769Sandrew  *  sri-nic::ps:<mkl>tcpmux.c
111257769Sandrew  */
111357769Sandrew 
111457769Sandrew 
111557769Sandrew static int		/* # of characters upto \r,\n or \0 */
111657769Sandrew getline(fd, buf, len)
111757769Sandrew 	int fd;
111857769Sandrew 	char *buf;
111957769Sandrew 	int len;
112057769Sandrew {
112157769Sandrew 	int count = 0, n;
112257769Sandrew 
112357769Sandrew 	do {
112457769Sandrew 		n = read(fd, buf, len-count);
112557769Sandrew 		if (n == 0)
112657769Sandrew 			return count;
112757769Sandrew 		if (n < 0)
112857769Sandrew 			return (-1);
112957769Sandrew 		while (--n >= 0) {
113057769Sandrew 			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
113157769Sandrew 				return count;
113257769Sandrew 			count++;
113357769Sandrew 			buf++;
113457769Sandrew 		}
113557769Sandrew 	} while (count < len);
113657769Sandrew 	return (count);
113757769Sandrew }
113857769Sandrew 
113957769Sandrew #define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
114057769Sandrew 
114157769Sandrew #define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
114257769Sandrew 
114357769Sandrew struct servtab *
114457769Sandrew tcpmux(s)
114557769Sandrew 	int s;
114657769Sandrew {
114757769Sandrew 	register struct servtab *sep;
114857769Sandrew 	char service[MAX_SERV_LEN+1];
114957769Sandrew 	int len;
115057769Sandrew 
115157769Sandrew 	/* Get requested service name */
115257769Sandrew 	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
115357769Sandrew 	    strwrite(s, "-Error reading service name\r\n");
115457769Sandrew 	    return(NULL);
115557769Sandrew 	}
115657769Sandrew 	service[len] = '\0';
115757769Sandrew 
115857769Sandrew 	if (debug)
115957769Sandrew 	    fprintf(stderr, "tcpmux: someone wants %s\n", service);
116057769Sandrew 
116157769Sandrew 	/*
116257769Sandrew 	 * Help is a required command, and lists available services,
116357769Sandrew 	 * one per line.
116457769Sandrew 	 */
116557769Sandrew 	if (!strcasecmp(service,"help")) {
116657769Sandrew 	    for (sep = servtab; sep; sep = sep->se_next) {
116757769Sandrew 		if (!ISMUX(sep))
116857769Sandrew 		    continue;
116957769Sandrew 		(void) write(s, sep->se_service, strlen(sep->se_service));
117057769Sandrew 		strwrite(s, "\r\n");
117157769Sandrew 	    }
117257769Sandrew 	    return(NULL);
117357769Sandrew 	}
117457769Sandrew 
117557769Sandrew 	/* Try matching a service in inetd.conf with the request */
117657769Sandrew 	for (sep = servtab; sep; sep = sep->se_next) {
117757769Sandrew 	    if (!ISMUX(sep))
117857769Sandrew 		continue;
117957769Sandrew 	    if (!strcasecmp(service,sep->se_service)) {
118057769Sandrew 		if (ISMUXPLUS(sep)) {
118157769Sandrew 		    strwrite(s, "+Go\r\n");
118257769Sandrew 		}
118357769Sandrew 		return(sep);
118457769Sandrew 	    }
118557769Sandrew 	}
118657769Sandrew 	strwrite(s, "-Service not available\r\n");
118757769Sandrew 	return(NULL);
118857769Sandrew }
1189