xref: /csrg-svn/usr.sbin/inetd/inetd.c (revision 57769)
121134Sdist /*
2*57769Sandrew  * Copyright (c) 1983, 1991, 1993 The Regents of the University of California.
333136Sbostic  * All rights reserved.
433136Sbostic  *
542799Sbostic  * %sccs.include.redist.c%
621134Sdist  */
721134Sdist 
816374Skarels #ifndef lint
921134Sdist char copyright[] =
10*57769Sandrew "@(#) Copyright (c) 1983, 1991, 1993 Regents of the University of California.\n\
1121134Sdist  All rights reserved.\n";
1233136Sbostic #endif /* not lint */
1316374Skarels 
1421134Sdist #ifndef lint
15*57769Sandrew static char sccsid[] = "@(#)inetd.c	5.31 (Berkeley) 02/01/93";
1633136Sbostic #endif /* not lint */
1721134Sdist 
1816374Skarels /*
1916374Skarels  * Inetd - Internet super-server
2016374Skarels  *
21*57769Sandrew  * This program invokes all internet services as needed.  Connection-oriented
22*57769Sandrew  * services are invoked each time a connection is made, by creating a process.
23*57769Sandrew  * This process is passed the connection as file descriptor 0 and is expected
24*57769Sandrew  * 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  *
41*57769Sandrew  *	service name			must be in /etc/services or must
42*57769Sandrew  *					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  *
50*57769Sandrew  * TCP services without official port numbers are handled with the
51*57769Sandrew  * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
52*57769Sandrew  * requests. When a connection is made from a foreign host, the service
53*57769Sandrew  * requested is passed to tcpmux, which looks it up in the servtab list
54*57769Sandrew  * and returns the proper entry for the service. Tcpmux returns a
55*57769Sandrew  * negative reply if the service doesn't exist, otherwise the invoked
56*57769Sandrew  * server is expected to return the positive reply if the service type in
57*57769Sandrew  * inetd.conf file has the prefix "tcpmux/". If the service type has the
58*57769Sandrew  * prefix "tcpmux/+", tcpmux will return the positive reply for the
59*57769Sandrew  * process; this is for compatibility with older server code, and also
60*57769Sandrew  * allows you to invoke programs that use stdin/stdout without putting any
61*57769Sandrew  * special server code in them. Services that use tcpmux are "nowait"
62*57769Sandrew  * because they do not have a well-known port and hence cannot listen
63*57769Sandrew  * for new requests.
64*57769Sandrew  *
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>
79*57769Sandrew #include <fcntl.h>
8016374Skarels #include <netdb.h>
8117346Sbloom #include <pwd.h>
82*57769Sandrew #include <signal.h>
8336603Sbostic #include <stdio.h>
84*57769Sandrew #include <stdlib.h>
8542067Sbostic #include <string.h>
86*57769Sandrew #include <syslog.h>
87*57769Sandrew #include <unistd.h>
88*57769Sandrew 
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;
105*57769Sandrew 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 */
120*57769Sandrew 	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 
127*57769Sandrew #define NORM_TYPE	0
128*57769Sandrew #define MUX_TYPE	1
129*57769Sandrew #define MUXPLUS_TYPE	2
130*57769Sandrew #define ISMUX(sep)	(((sep)->se_type == MUX_TYPE) || \
131*57769Sandrew 			 ((sep)->se_type == MUXPLUS_TYPE))
132*57769Sandrew #define ISMUXPLUS(sep)	((sep)->se_type == MUXPLUS_TYPE)
133*57769Sandrew 
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();
137*57769Sandrew 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,
165*57769Sandrew 
166*57769Sandrew 	"tcpmux",	SOCK_STREAM,	1, 0,	(int (*)())tcpmux,
167*57769Sandrew 
168*57769Sandrew 	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 
194*57769Sandrew 	openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
195*57769Sandrew 
196*57769Sandrew 	while ((ch = getopt(argc, argv, "dR:")) != EOF)
19736603Sbostic 		switch(ch) {
19816374Skarels 		case 'd':
19916374Skarels 			debug = 1;
20016374Skarels 			options |= SO_DEBUG;
20116374Skarels 			break;
202*57769Sandrew 		case 'R': {	/* invocation rate */
203*57769Sandrew 			char *p;
204*57769Sandrew 
205*57769Sandrew 			tmpint = strtol(optarg, &p, 0);
206*57769Sandrew 			if (tmpint < 1 || *p)
207*57769Sandrew 				syslog(LOG_ERR,
208*57769Sandrew 			         "-R %s: bad value for service invocation rate",
209*57769Sandrew 					optarg);
210*57769Sandrew 			else
211*57769Sandrew 				toomany = tmpint;
212*57769Sandrew 			break;
213*57769Sandrew 		}
21436603Sbostic 		case '?':
21516374Skarels 		default:
216*57769Sandrew 			syslog(LOG_ERR,
217*57769Sandrew 				"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];
225*57769Sandrew 	if (debug == 0) {
22644711Skarels 		daemon(0, 0);
227*57769Sandrew 	}
228*57769Sandrew 	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)
262*57769Sandrew 			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) {
278*57769Sandrew 				    if (errno != EINTR)
279*57769Sandrew 					    syslog(LOG_WARNING,
280*57769Sandrew 						"accept (for %s): %m",
281*57769Sandrew 						sep->se_service);
28246973Skarels 				    continue;
28346973Skarels 			    }
284*57769Sandrew 			    /*
285*57769Sandrew 			     * Call tcpmux to find the real service to exec.
286*57769Sandrew 			     */
287*57769Sandrew 			    if (sep->se_bi &&
288*57769Sandrew 				sep->se_bi->bi_fn == (int (*)()) tcpmux) {
289*57769Sandrew 				    sep = tcpmux(ctrl);
290*57769Sandrew 				    if (sep == NULL) {
291*57769Sandrew 					    close(ctrl);
292*57769Sandrew 					    continue;
293*57769Sandrew 				    }
294*57769Sandrew 			    }
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);
304*57769Sandrew 			    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,
314*57769Sandrew 			"%s/%s server failing (looping), service terminated",
31527535Skarels 					    sep->se_service, sep->se_proto);
316*57769Sandrew 					close_sep(sep);
317*57769Sandrew 					sigsetmask(0L);
31827535Skarels 					if (!timingout) {
31927535Skarels 						timingout = 1;
32027535Skarels 						alarm(RETRYTIME);
32127535Skarels 					}
322*57769Sandrew 					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();
346*57769Sandrew 			    if (dofork) {
347*57769Sandrew 				if (debug)
348*57769Sandrew 					fprintf(stderr, "+ Closing from %d\n",
349*57769Sandrew 						maxsock);
350*57769Sandrew 				for (tmpint = maxsock; tmpint > 2; tmpint--)
35136603Sbostic 					if (tmpint != ctrl)
35236603Sbostic 						close(tmpint);
353*57769Sandrew 			    }
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,
366*57769Sandrew 					    "%s/%s: %s: No such user",
367*57769Sandrew 						sep->se_service, sep->se_proto,
368*57769Sandrew 						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) {
374*57769Sandrew 					if (setgid(pwd->pw_gid) < 0) {
375*57769Sandrew 						syslog(LOG_ERR,
376*57769Sandrew 						  "%s: can't set gid %d: %m",
377*57769Sandrew 						  sep->se_service, pwd->pw_gid);
378*57769Sandrew 						_exit(1);
379*57769Sandrew 					}
380*57769Sandrew 					(void) initgroups(pwd->pw_name,
381*57769Sandrew 							pwd->pw_gid);
382*57769Sandrew 					if (setuid(pwd->pw_uid) < 0) {
383*57769Sandrew 						syslog(LOG_ERR,
384*57769Sandrew 						  "%s: can't set uid %d: %m",
385*57769Sandrew 						  sep->se_service, pwd->pw_uid);
386*57769Sandrew 						_exit(1);
387*57769Sandrew 					}
38826877Skarels 				}
38926877Skarels 				execv(sep->se_server, sep->se_argv);
39026877Skarels 				if (sep->se_socktype != SOCK_STREAM)
39126877Skarels 					recv(0, buf, sizeof (buf), 0);
392*57769Sandrew 				syslog(LOG_ERR,
393*57769Sandrew 				    "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)
415*57769Sandrew 			fprintf(stderr, "%d reaped, status %#x\n",
416*57769Sandrew 				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();
438*57769Sandrew 	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()) {
448*57769Sandrew 		if ((pwd = getpwnam(cp->se_user)) == NULL) {
449*57769Sandrew 			syslog(LOG_ERR,
450*57769Sandrew 				"%s/%s: No such user '%s', service ignored",
451*57769Sandrew 				cp->se_service, cp->se_proto, cp->se_user);
452*57769Sandrew 			continue;
453*57769Sandrew 		}
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;
488*57769Sandrew 		if (ISMUX(sep)) {
489*57769Sandrew 			sep->se_fd = -1;
490*57769Sandrew 			continue;
491*57769Sandrew 		}
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);
496*57769Sandrew 			sep->se_checked = 0;
49716374Skarels 			continue;
49816374Skarels 		}
49927535Skarels 		if (sp->s_port != sep->se_ctrladdr.sin_port) {
50027535Skarels 			sep->se_ctrladdr.sin_port = sp->s_port;
501*57769Sandrew 			if (sep->se_fd >= 0)
502*57769Sandrew 				close_sep(sep);
50316374Skarels 		}
50427535Skarels 		if (sep->se_fd == -1)
50527535Skarels 			setup(sep);
50616374Skarels 	}
50716374Skarels 	endconfig();
50816374Skarels 	/*
50916374Skarels 	 * Purge anything not looked at above.
51016374Skarels 	 */
51127535Skarels 	omask = sigblock(SIGBLOCK);
51216374Skarels 	sepp = &servtab;
51316374Skarels 	while (sep = *sepp) {
51416374Skarels 		if (sep->se_checked) {
51516374Skarels 			sepp = &sep->se_next;
51616374Skarels 			continue;
51716374Skarels 		}
51816374Skarels 		*sepp = sep->se_next;
519*57769Sandrew 		if (sep->se_fd >= 0)
520*57769Sandrew 			close_sep(sep);
52129794Skarels 		if (debug)
52229794Skarels 			print_service("FREE", sep);
52316374Skarels 		freeconfig(sep);
52416374Skarels 		free((char *)sep);
52516374Skarels 	}
52616374Skarels 	(void) sigsetmask(omask);
52716374Skarels }
52816374Skarels 
52939838Sbostic void
53027535Skarels retry()
53127535Skarels {
53227535Skarels 	register struct servtab *sep;
53327535Skarels 
53427535Skarels 	timingout = 0;
53527535Skarels 	for (sep = servtab; sep; sep = sep->se_next)
53627535Skarels 		if (sep->se_fd == -1)
53727535Skarels 			setup(sep);
53827535Skarels }
53927535Skarels 
54027535Skarels setup(sep)
54127535Skarels 	register struct servtab *sep;
54227535Skarels {
54327535Skarels 	int on = 1;
54427535Skarels 
54527535Skarels 	if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
546*57769Sandrew 		if (debug)
547*57769Sandrew 			fprintf(stderr, "socket failed on %s/%s: %s\n",
548*57769Sandrew 				sep->se_service, sep->se_proto,
549*57769Sandrew 				strerror(errno));
55027535Skarels 		syslog(LOG_ERR, "%s/%s: socket: %m",
55127535Skarels 		    sep->se_service, sep->se_proto);
55227535Skarels 		return;
55327535Skarels 	}
55427535Skarels #define	turnon(fd, opt) \
55527535Skarels setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
55627535Skarels 	if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
55727535Skarels 	    turnon(sep->se_fd, SO_DEBUG) < 0)
55827535Skarels 		syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
55927535Skarels 	if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
56027535Skarels 		syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
56127535Skarels #undef turnon
56246907Sbostic 	if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
56327535Skarels 	    sizeof (sep->se_ctrladdr)) < 0) {
564*57769Sandrew 		if (debug)
565*57769Sandrew 			fprintf(stderr, "bind failed on %s/%s: %s\n",
566*57769Sandrew 				sep->se_service, sep->se_proto,
567*57769Sandrew 				strerror(errno));
56827535Skarels 		syslog(LOG_ERR, "%s/%s: bind: %m",
56927535Skarels 		    sep->se_service, sep->se_proto);
57027535Skarels 		(void) close(sep->se_fd);
57127535Skarels 		sep->se_fd = -1;
57227535Skarels 		if (!timingout) {
57327535Skarels 			timingout = 1;
57427535Skarels 			alarm(RETRYTIME);
57527535Skarels 		}
57627535Skarels 		return;
57727535Skarels 	}
57827535Skarels 	if (sep->se_socktype == SOCK_STREAM)
57927535Skarels 		listen(sep->se_fd, 10);
58027535Skarels 	FD_SET(sep->se_fd, &allsock);
58127535Skarels 	nsock++;
58227535Skarels 	if (sep->se_fd > maxsock)
58327535Skarels 		maxsock = sep->se_fd;
584*57769Sandrew 	if (debug) {
585*57769Sandrew 		fprintf(stderr, "registered %s on %d\n",
586*57769Sandrew 			sep->se_server, sep->se_fd);
587*57769Sandrew 	}
58827535Skarels }
58927535Skarels 
590*57769Sandrew /*
591*57769Sandrew  * Finish with a service and its socket.
592*57769Sandrew  */
593*57769Sandrew close_sep(sep)
594*57769Sandrew 	register struct servtab *sep;
595*57769Sandrew {
596*57769Sandrew 	if (sep->se_fd >= 0) {
597*57769Sandrew 		nsock--;
598*57769Sandrew 		FD_CLR(sep->se_fd, &allsock);
599*57769Sandrew 		(void) close(sep->se_fd);
600*57769Sandrew 		sep->se_fd = -1;
601*57769Sandrew 	}
602*57769Sandrew 	sep->se_count = 0;
603*57769Sandrew 	/*
604*57769Sandrew 	 * Don't keep the pid of this running deamon: when reapchild()
605*57769Sandrew 	 * reaps this pid, it would erroneously increment nsock.
606*57769Sandrew 	 */
607*57769Sandrew 	if (sep->se_wait > 1)
608*57769Sandrew 		sep->se_wait = 1;
609*57769Sandrew }
610*57769Sandrew 
61116374Skarels struct servtab *
61216374Skarels enter(cp)
61316374Skarels 	struct servtab *cp;
61416374Skarels {
61516374Skarels 	register struct servtab *sep;
61632246Sbostic 	long omask;
61716374Skarels 
61816374Skarels 	sep = (struct servtab *)malloc(sizeof (*sep));
61916374Skarels 	if (sep == (struct servtab *)0) {
62016510Sralph 		syslog(LOG_ERR, "Out of memory.");
62116374Skarels 		exit(-1);
62216374Skarels 	}
62316374Skarels 	*sep = *cp;
62416374Skarels 	sep->se_fd = -1;
62527535Skarels 	omask = sigblock(SIGBLOCK);
62616374Skarels 	sep->se_next = servtab;
62716374Skarels 	servtab = sep;
62816374Skarels 	sigsetmask(omask);
62916374Skarels 	return (sep);
63016374Skarels }
63116374Skarels 
63216374Skarels FILE	*fconfig = NULL;
63316374Skarels struct	servtab serv;
63416374Skarels char	line[256];
635*57769Sandrew char	*sskip(), *skip(), *nextline();
63616374Skarels 
63716374Skarels setconfig()
63816374Skarels {
63916374Skarels 
64016374Skarels 	if (fconfig != NULL) {
641*57769Sandrew 		fseek(fconfig, 0L, SEEK_SET);
64216374Skarels 		return (1);
64316374Skarels 	}
64416374Skarels 	fconfig = fopen(CONFIG, "r");
64516374Skarels 	return (fconfig != NULL);
64616374Skarels }
64716374Skarels 
64816374Skarels endconfig()
64916374Skarels {
65036603Sbostic 	if (fconfig) {
65136603Sbostic 		(void) fclose(fconfig);
65236603Sbostic 		fconfig = NULL;
65336603Sbostic 	}
65416374Skarels }
65516374Skarels 
65616374Skarels struct servtab *
65716374Skarels getconfigent()
65816374Skarels {
65916374Skarels 	register struct servtab *sep = &serv;
66016374Skarels 	int argc;
66146907Sbostic 	char *cp, *arg, *newstr();
662*57769Sandrew 	static char TCPMUX_TOKEN[] = "tcpmux/";
663*57769Sandrew #define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
66416374Skarels 
66526877Skarels more:
666*57769Sandrew 	while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
66716374Skarels 		;
66816374Skarels 	if (cp == NULL)
66916374Skarels 		return ((struct servtab *)0);
670*57769Sandrew 	/*
671*57769Sandrew 	 * clear the static buffer, since some fields (se_ctrladdr,
672*57769Sandrew 	 * for example) don't get initialized here.
673*57769Sandrew 	 */
674*57769Sandrew 	bzero((caddr_t)sep, sizeof *sep);
67516374Skarels 	arg = skip(&cp);
676*57769Sandrew 	if (cp == NULL) {
677*57769Sandrew 		/* got an empty line containing just blanks/tabs. */
678*57769Sandrew 		goto more;
679*57769Sandrew 	}
680*57769Sandrew 	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
681*57769Sandrew 		char *c = arg + MUX_LEN;
682*57769Sandrew 		if (*c == '+') {
683*57769Sandrew 			sep->se_type = MUXPLUS_TYPE;
684*57769Sandrew 			c++;
685*57769Sandrew 		} else
686*57769Sandrew 			sep->se_type = MUX_TYPE;
687*57769Sandrew 		sep->se_service = newstr(c);
688*57769Sandrew 	} else {
689*57769Sandrew 		sep->se_service = newstr(arg);
690*57769Sandrew 		sep->se_type = NORM_TYPE;
691*57769Sandrew 	}
692*57769Sandrew 	arg = sskip(&cp);
69316374Skarels 	if (strcmp(arg, "stream") == 0)
69416374Skarels 		sep->se_socktype = SOCK_STREAM;
69516374Skarels 	else if (strcmp(arg, "dgram") == 0)
69616374Skarels 		sep->se_socktype = SOCK_DGRAM;
69716374Skarels 	else if (strcmp(arg, "rdm") == 0)
69816374Skarels 		sep->se_socktype = SOCK_RDM;
69916374Skarels 	else if (strcmp(arg, "seqpacket") == 0)
70016374Skarels 		sep->se_socktype = SOCK_SEQPACKET;
70116374Skarels 	else if (strcmp(arg, "raw") == 0)
70216374Skarels 		sep->se_socktype = SOCK_RAW;
70316374Skarels 	else
70416374Skarels 		sep->se_socktype = -1;
705*57769Sandrew 	sep->se_proto = newstr(sskip(&cp));
706*57769Sandrew 	arg = sskip(&cp);
70716374Skarels 	sep->se_wait = strcmp(arg, "wait") == 0;
708*57769Sandrew 	if (ISMUX(sep)) {
709*57769Sandrew 		/*
710*57769Sandrew 		 * Silently enforce "nowait" for TCPMUX services since
711*57769Sandrew 		 * they don't have an assigned port to listen on.
712*57769Sandrew 		 */
713*57769Sandrew 		sep->se_wait = 0;
714*57769Sandrew 
715*57769Sandrew 		if (strcmp(sep->se_proto, "tcp")) {
716*57769Sandrew 			syslog(LOG_ERR,
717*57769Sandrew 				"%s: bad protocol for tcpmux service %s",
718*57769Sandrew 				CONFIG, sep->se_service);
719*57769Sandrew 			goto more;
720*57769Sandrew 		}
721*57769Sandrew 		if (sep->se_socktype != SOCK_STREAM) {
722*57769Sandrew 			syslog(LOG_ERR,
723*57769Sandrew 				"%s: bad socket type for tcpmux service %s",
724*57769Sandrew 				CONFIG, sep->se_service);
725*57769Sandrew 			goto more;
726*57769Sandrew 		}
727*57769Sandrew 	}
728*57769Sandrew 	sep->se_user = newstr(sskip(&cp));
729*57769Sandrew 	sep->se_server = newstr(sskip(&cp));
73026877Skarels 	if (strcmp(sep->se_server, "internal") == 0) {
73126877Skarels 		register struct biltin *bi;
73226877Skarels 
73326877Skarels 		for (bi = biltins; bi->bi_service; bi++)
73426877Skarels 			if (bi->bi_socktype == sep->se_socktype &&
73526877Skarels 			    strcmp(bi->bi_service, sep->se_service) == 0)
73626877Skarels 				break;
73726877Skarels 		if (bi->bi_service == 0) {
738*57769Sandrew 			syslog(LOG_ERR, "internal service %s unknown",
73926877Skarels 				sep->se_service);
74026877Skarels 			goto more;
74126877Skarels 		}
74226877Skarels 		sep->se_bi = bi;
74326877Skarels 		sep->se_wait = bi->bi_wait;
74429794Skarels 	} else
74529794Skarels 		sep->se_bi = NULL;
74616374Skarels 	argc = 0;
74716374Skarels 	for (arg = skip(&cp); cp; arg = skip(&cp))
74816374Skarels 		if (argc < MAXARGV)
74946907Sbostic 			sep->se_argv[argc++] = newstr(arg);
75016374Skarels 	while (argc <= MAXARGV)
75116374Skarels 		sep->se_argv[argc++] = NULL;
75216374Skarels 	return (sep);
75316374Skarels }
75416374Skarels 
75516374Skarels freeconfig(cp)
75616374Skarels 	register struct servtab *cp;
75716374Skarels {
75816374Skarels 	int i;
75916374Skarels 
76016374Skarels 	if (cp->se_service)
76116374Skarels 		free(cp->se_service);
76216374Skarels 	if (cp->se_proto)
76316374Skarels 		free(cp->se_proto);
76426921Slepreau 	if (cp->se_user)
76526921Slepreau 		free(cp->se_user);
76616374Skarels 	if (cp->se_server)
76716374Skarels 		free(cp->se_server);
76816374Skarels 	for (i = 0; i < MAXARGV; i++)
76916374Skarels 		if (cp->se_argv[i])
77016374Skarels 			free(cp->se_argv[i]);
77116374Skarels }
77216374Skarels 
773*57769Sandrew 
774*57769Sandrew /*
775*57769Sandrew  * Safe skip - if skip returns null, log a syntax error in the
776*57769Sandrew  * configuration file and exit.
777*57769Sandrew  */
77816374Skarels char *
779*57769Sandrew sskip(cpp)
780*57769Sandrew 	char **cpp;
781*57769Sandrew {
782*57769Sandrew 	register char *cp;
783*57769Sandrew 
784*57769Sandrew 	cp = skip(cpp);
785*57769Sandrew 	if (cp == NULL) {
786*57769Sandrew 		syslog(LOG_ERR, "%s: syntax error", CONFIG);
787*57769Sandrew 		exit(-1);
788*57769Sandrew 	}
789*57769Sandrew 	return (cp);
790*57769Sandrew }
791*57769Sandrew 
792*57769Sandrew char *
79316374Skarels skip(cpp)
79416374Skarels 	char **cpp;
79516374Skarels {
79616374Skarels 	register char *cp = *cpp;
79716374Skarels 	char *start;
79816374Skarels 
79916374Skarels again:
80016374Skarels 	while (*cp == ' ' || *cp == '\t')
80116374Skarels 		cp++;
80216374Skarels 	if (*cp == '\0') {
80343455Sbostic 		int c;
80416374Skarels 
80516374Skarels 		c = getc(fconfig);
80636603Sbostic 		(void) ungetc(c, fconfig);
80716374Skarels 		if (c == ' ' || c == '\t')
80816374Skarels 			if (cp = nextline(fconfig))
80916374Skarels 				goto again;
81016374Skarels 		*cpp = (char *)0;
81116374Skarels 		return ((char *)0);
81216374Skarels 	}
81316374Skarels 	start = cp;
81416374Skarels 	while (*cp && *cp != ' ' && *cp != '\t')
81516374Skarels 		cp++;
81616374Skarels 	if (*cp != '\0')
81716374Skarels 		*cp++ = '\0';
81816374Skarels 	*cpp = cp;
81916374Skarels 	return (start);
82016374Skarels }
82116374Skarels 
82216374Skarels char *
82316374Skarels nextline(fd)
82416374Skarels 	FILE *fd;
82516374Skarels {
82616374Skarels 	char *cp;
82716374Skarels 
82826921Slepreau 	if (fgets(line, sizeof (line), fd) == NULL)
82916374Skarels 		return ((char *)0);
83016374Skarels 	cp = index(line, '\n');
83116374Skarels 	if (cp)
83216374Skarels 		*cp = '\0';
83316374Skarels 	return (line);
83416374Skarels }
83516374Skarels 
83616374Skarels char *
83746907Sbostic newstr(cp)
83816374Skarels 	char *cp;
83916374Skarels {
84046973Skarels 	if (cp = strdup(cp ? cp : ""))
84146907Sbostic 		return(cp);
84246973Skarels 	syslog(LOG_ERR, "strdup: %m");
84346907Sbostic 	exit(-1);
84416374Skarels }
84526877Skarels 
84626877Skarels setproctitle(a, s)
84726877Skarels 	char *a;
84826877Skarels 	int s;
84926877Skarels {
85026877Skarels 	int size;
85126877Skarels 	register char *cp;
85226877Skarels 	struct sockaddr_in sin;
85326877Skarels 	char buf[80];
85426877Skarels 
85526877Skarels 	cp = Argv[0];
85626877Skarels 	size = sizeof(sin);
85746907Sbostic 	if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
85832442Sbostic 		(void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
85926877Skarels 	else
86032442Sbostic 		(void) sprintf(buf, "-%s", a);
86126877Skarels 	strncpy(cp, buf, LastArg - cp);
86226877Skarels 	cp += strlen(cp);
86326877Skarels 	while (cp < LastArg)
86426877Skarels 		*cp++ = ' ';
86526877Skarels }
86626877Skarels 
86726877Skarels /*
86826877Skarels  * Internet services provided internally by inetd:
86926877Skarels  */
87049986Skarels #define	BUFSIZE	8192
87126877Skarels 
87226877Skarels /* ARGSUSED */
87326877Skarels echo_stream(s, sep)		/* Echo service -- echo data back */
87426877Skarels 	int s;
87526877Skarels 	struct servtab *sep;
87626877Skarels {
87738573Skarels 	char buffer[BUFSIZE];
87826877Skarels 	int i;
87926877Skarels 
88032091Skarels 	setproctitle(sep->se_service, s);
88126877Skarels 	while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
88226877Skarels 	    write(s, buffer, i) > 0)
88326877Skarels 		;
88426877Skarels 	exit(0);
88526877Skarels }
88626877Skarels 
88726877Skarels /* ARGSUSED */
88826877Skarels echo_dg(s, sep)			/* Echo service -- echo data back */
88926877Skarels 	int s;
89026877Skarels 	struct servtab *sep;
89126877Skarels {
89238573Skarels 	char buffer[BUFSIZE];
89326877Skarels 	int i, size;
89426877Skarels 	struct sockaddr sa;
89526877Skarels 
89626877Skarels 	size = sizeof(sa);
89726877Skarels 	if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0)
89826877Skarels 		return;
89926877Skarels 	(void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
90026877Skarels }
90126877Skarels 
90226877Skarels /* ARGSUSED */
90326877Skarels discard_stream(s, sep)		/* Discard service -- ignore data */
90426877Skarels 	int s;
90526877Skarels 	struct servtab *sep;
90626877Skarels {
90749986Skarels 	int ret;
90838573Skarels 	char buffer[BUFSIZE];
90926877Skarels 
91032091Skarels 	setproctitle(sep->se_service, s);
91126877Skarels 	while (1) {
91249986Skarels 		while ((ret = read(s, buffer, sizeof(buffer))) > 0)
91326877Skarels 			;
91449986Skarels 		if (ret == 0 || errno != EINTR)
91526877Skarels 			break;
91626877Skarels 	}
91726877Skarels 	exit(0);
91826877Skarels }
91926877Skarels 
92026877Skarels /* ARGSUSED */
92126877Skarels discard_dg(s, sep)		/* Discard service -- ignore data */
92226877Skarels 	int s;
92326877Skarels 	struct servtab *sep;
92426877Skarels {
92538573Skarels 	char buffer[BUFSIZE];
92626877Skarels 
92726877Skarels 	(void) read(s, buffer, sizeof(buffer));
92826877Skarels }
92926877Skarels 
93026877Skarels #include <ctype.h>
93126877Skarels #define LINESIZ 72
93226877Skarels char ring[128];
93326877Skarels char *endring;
93426877Skarels 
93526877Skarels initring()
93626877Skarels {
93726877Skarels 	register int i;
93826877Skarels 
93926877Skarels 	endring = ring;
94026877Skarels 
94126877Skarels 	for (i = 0; i <= 128; ++i)
94226877Skarels 		if (isprint(i))
94326877Skarels 			*endring++ = i;
94426877Skarels }
94526877Skarels 
94626877Skarels /* ARGSUSED */
94726877Skarels chargen_stream(s, sep)		/* Character generator */
94826877Skarels 	int s;
94926877Skarels 	struct servtab *sep;
95026877Skarels {
95132246Sbostic 	register char *rs;
95232246Sbostic 	int len;
95326877Skarels 	char text[LINESIZ+2];
95426877Skarels 
95532091Skarels 	setproctitle(sep->se_service, s);
95632246Sbostic 
95732246Sbostic 	if (!endring) {
95826877Skarels 		initring();
95932246Sbostic 		rs = ring;
96032246Sbostic 	}
96126877Skarels 
96232246Sbostic 	text[LINESIZ] = '\r';
96332246Sbostic 	text[LINESIZ + 1] = '\n';
96432246Sbostic 	for (rs = ring;;) {
96532246Sbostic 		if ((len = endring - rs) >= LINESIZ)
96632246Sbostic 			bcopy(rs, text, LINESIZ);
96732246Sbostic 		else {
96832246Sbostic 			bcopy(rs, text, len);
96932246Sbostic 			bcopy(ring, text + len, LINESIZ - len);
97032246Sbostic 		}
97132246Sbostic 		if (++rs == endring)
97226877Skarels 			rs = ring;
97332246Sbostic 		if (write(s, text, sizeof(text)) != sizeof(text))
97426877Skarels 			break;
97526877Skarels 	}
97626877Skarels 	exit(0);
97726877Skarels }
97826877Skarels 
97926877Skarels /* ARGSUSED */
98026877Skarels chargen_dg(s, sep)		/* Character generator */
98126877Skarels 	int s;
98226877Skarels 	struct servtab *sep;
98326877Skarels {
98432246Sbostic 	struct sockaddr sa;
98532246Sbostic 	static char *rs;
98632246Sbostic 	int len, size;
98726877Skarels 	char text[LINESIZ+2];
98826877Skarels 
98932246Sbostic 	if (endring == 0) {
99026877Skarels 		initring();
99132246Sbostic 		rs = ring;
99232246Sbostic 	}
99326877Skarels 
99426877Skarels 	size = sizeof(sa);
99526877Skarels 	if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
99626877Skarels 		return;
99732246Sbostic 
99832246Sbostic 	if ((len = endring - rs) >= LINESIZ)
99932246Sbostic 		bcopy(rs, text, LINESIZ);
100032246Sbostic 	else {
100132246Sbostic 		bcopy(rs, text, len);
100232246Sbostic 		bcopy(ring, text + len, LINESIZ - len);
100332246Sbostic 	}
100432246Sbostic 	if (++rs == endring)
100526877Skarels 		rs = ring;
100632246Sbostic 	text[LINESIZ] = '\r';
100732246Sbostic 	text[LINESIZ + 1] = '\n';
100826877Skarels 	(void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
100926877Skarels }
101026877Skarels 
101126877Skarels /*
101226877Skarels  * Return a machine readable date and time, in the form of the
101326877Skarels  * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
101426877Skarels  * returns the number of seconds since midnight, Jan 1, 1970,
101526877Skarels  * we must add 2208988800 seconds to this figure to make up for
101626877Skarels  * some seventy years Bell Labs was asleep.
101726877Skarels  */
101826877Skarels 
101926877Skarels long
102026877Skarels machtime()
102126877Skarels {
102226877Skarels 	struct timeval tv;
102326877Skarels 
102426921Slepreau 	if (gettimeofday(&tv, (struct timezone *)0) < 0) {
1025*57769Sandrew 		if (debug)
1026*57769Sandrew 			fprintf(stderr, "Unable to get time of day\n");
102726921Slepreau 		return (0L);
102826877Skarels 	}
102926877Skarels 	return (htonl((long)tv.tv_sec + 2208988800));
103026877Skarels }
103126877Skarels 
103226877Skarels /* ARGSUSED */
103326877Skarels machtime_stream(s, sep)
103426877Skarels 	int s;
103526877Skarels 	struct servtab *sep;
103626877Skarels {
103726877Skarels 	long result;
103826877Skarels 
103926877Skarels 	result = machtime();
104026877Skarels 	(void) write(s, (char *) &result, sizeof(result));
104126877Skarels }
104226877Skarels 
104326877Skarels /* ARGSUSED */
104426877Skarels machtime_dg(s, sep)
104526877Skarels 	int s;
104626877Skarels 	struct servtab *sep;
104726877Skarels {
104826877Skarels 	long result;
104926877Skarels 	struct sockaddr sa;
105026877Skarels 	int size;
105126877Skarels 
105226877Skarels 	size = sizeof(sa);
105326921Slepreau 	if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0)
105426877Skarels 		return;
105526877Skarels 	result = machtime();
105626877Skarels 	(void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
105726877Skarels }
105826877Skarels 
105926877Skarels /* ARGSUSED */
106026877Skarels daytime_stream(s, sep)		/* Return human-readable time of day */
106126877Skarels 	int s;
106226877Skarels 	struct servtab *sep;
106326877Skarels {
106426877Skarels 	char buffer[256];
1065*57769Sandrew 	time_t clock;
106626877Skarels 
106726877Skarels 	clock = time((time_t *) 0);
106826877Skarels 
106932442Sbostic 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
107026921Slepreau 	(void) write(s, buffer, strlen(buffer));
107126877Skarels }
107226877Skarels 
107326877Skarels /* ARGSUSED */
107426877Skarels daytime_dg(s, sep)		/* Return human-readable time of day */
107526877Skarels 	int s;
107626877Skarels 	struct servtab *sep;
107726877Skarels {
107826877Skarels 	char buffer[256];
1079*57769Sandrew 	time_t clock;
108026877Skarels 	struct sockaddr sa;
108126877Skarels 	int size;
108226877Skarels 
108326877Skarels 	clock = time((time_t *) 0);
108426877Skarels 
108526877Skarels 	size = sizeof(sa);
108626877Skarels 	if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
108726877Skarels 		return;
108832442Sbostic 	(void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
108926877Skarels 	(void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
109026877Skarels }
109129794Skarels 
109229794Skarels /*
109329794Skarels  * print_service:
109429794Skarels  *	Dump relevant information to stderr
109529794Skarels  */
109629794Skarels print_service(action, sep)
109729794Skarels 	char *action;
109829794Skarels 	struct servtab *sep;
109929794Skarels {
110029794Skarels 	fprintf(stderr,
110129794Skarels 	    "%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
110229794Skarels 	    action, sep->se_service, sep->se_proto,
110336603Sbostic 	    sep->se_wait, sep->se_user, (int)sep->se_bi, sep->se_server);
110429794Skarels }
1105*57769Sandrew 
1106*57769Sandrew /*
1107*57769Sandrew  *  Based on TCPMUX.C by Mark K. Lottor November 1988
1108*57769Sandrew  *  sri-nic::ps:<mkl>tcpmux.c
1109*57769Sandrew  */
1110*57769Sandrew 
1111*57769Sandrew 
1112*57769Sandrew static int		/* # of characters upto \r,\n or \0 */
1113*57769Sandrew getline(fd, buf, len)
1114*57769Sandrew 	int fd;
1115*57769Sandrew 	char *buf;
1116*57769Sandrew 	int len;
1117*57769Sandrew {
1118*57769Sandrew 	int count = 0, n;
1119*57769Sandrew 
1120*57769Sandrew 	do {
1121*57769Sandrew 		n = read(fd, buf, len-count);
1122*57769Sandrew 		if (n == 0)
1123*57769Sandrew 			return count;
1124*57769Sandrew 		if (n < 0)
1125*57769Sandrew 			return (-1);
1126*57769Sandrew 		while (--n >= 0) {
1127*57769Sandrew 			if (*buf == '\r' || *buf == '\n' || *buf == '\0')
1128*57769Sandrew 				return count;
1129*57769Sandrew 			count++;
1130*57769Sandrew 			buf++;
1131*57769Sandrew 		}
1132*57769Sandrew 	} while (count < len);
1133*57769Sandrew 	return (count);
1134*57769Sandrew }
1135*57769Sandrew 
1136*57769Sandrew #define MAX_SERV_LEN	(256+2)		/* 2 bytes for \r\n */
1137*57769Sandrew 
1138*57769Sandrew #define strwrite(fd, buf)	(void) write(fd, buf, sizeof(buf)-1)
1139*57769Sandrew 
1140*57769Sandrew struct servtab *
1141*57769Sandrew tcpmux(s)
1142*57769Sandrew 	int s;
1143*57769Sandrew {
1144*57769Sandrew 	register struct servtab *sep;
1145*57769Sandrew 	char service[MAX_SERV_LEN+1];
1146*57769Sandrew 	int len;
1147*57769Sandrew 
1148*57769Sandrew 	/* Get requested service name */
1149*57769Sandrew 	if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
1150*57769Sandrew 	    strwrite(s, "-Error reading service name\r\n");
1151*57769Sandrew 	    return(NULL);
1152*57769Sandrew 	}
1153*57769Sandrew 	service[len] = '\0';
1154*57769Sandrew 
1155*57769Sandrew 	if (debug)
1156*57769Sandrew 	    fprintf(stderr, "tcpmux: someone wants %s\n", service);
1157*57769Sandrew 
1158*57769Sandrew 	/*
1159*57769Sandrew 	 * Help is a required command, and lists available services,
1160*57769Sandrew 	 * one per line.
1161*57769Sandrew 	 */
1162*57769Sandrew 	if (!strcasecmp(service,"help")) {
1163*57769Sandrew 	    for (sep = servtab; sep; sep = sep->se_next) {
1164*57769Sandrew 		if (!ISMUX(sep))
1165*57769Sandrew 		    continue;
1166*57769Sandrew 		(void) write(s, sep->se_service, strlen(sep->se_service));
1167*57769Sandrew 		strwrite(s, "\r\n");
1168*57769Sandrew 	    }
1169*57769Sandrew 	    return(NULL);
1170*57769Sandrew 	}
1171*57769Sandrew 
1172*57769Sandrew 	/* Try matching a service in inetd.conf with the request */
1173*57769Sandrew 	for (sep = servtab; sep; sep = sep->se_next) {
1174*57769Sandrew 	    if (!ISMUX(sep))
1175*57769Sandrew 		continue;
1176*57769Sandrew 	    if (!strcasecmp(service,sep->se_service)) {
1177*57769Sandrew 		if (ISMUXPLUS(sep)) {
1178*57769Sandrew 		    strwrite(s, "+Go\r\n");
1179*57769Sandrew 		}
1180*57769Sandrew 		return(sep);
1181*57769Sandrew 	    }
1182*57769Sandrew 	}
1183*57769Sandrew 	strwrite(s, "-Service not available\r\n");
1184*57769Sandrew 	return(NULL);
1185*57769Sandrew }
1186