xref: /csrg-svn/usr.sbin/inetd/inetd.c (revision 17156)
1 #ifndef lint
2 static	char sccsid[] = "@(#)inetd.c	4.3 (Berkeley) 09/04/84";
3 #endif
4 
5 /*
6  * Inetd - Internet super-server
7  *
8  * This program invokes all internet services as needed.
9  * connection-oriented services are invoked each time a
10  * connection is made, by creating a process.  This process
11  * is passed the connection as file descriptor 0 and is
12  * expected to do a getpeername to find out the source host
13  * and port.
14  *
15  * Datagram oriented services are invoked when a datagram
16  * arrives; a process is created and passed a pending message
17  * on file descriptor 0.  Datagram servers may either connect
18  * to their peer, freeing up the original socket for inetd
19  * to receive further messages on, or ``take over the socket'',
20  * processing all arriving datagrams and, eventually, timing
21  * out.	 The first type of server is said to be ``multi-threaded'';
22  * the second type of server ``single-threaded''.
23  *
24  * Inetd uses a configuration file which is read at startup
25  * and, possibly, at some later time in response to a hangup signal.
26  * The configuration file is ``free format'' with fields given in the
27  * order shown below.  Continuation lines for an entry must being with
28  * a space or tab.  All fields must be present in each entry.
29  *
30  *	service name			must be in /etc/services
31  *	socket type			stream/dgram/raw/rdm/seqpacket
32  *	protocol			must be in /etc/protocols
33  *	wait/nowait			single-threaded/multi-threaded
34  *	server program			full path name
35  *	server program arguments	maximum of MAXARGS (5)
36  *
37  * Comment lines are indicated by a `#' in column 1.
38  */
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/ioctl.h>
42 #include <sys/socket.h>
43 #include <sys/file.h>
44 #include <sys/wait.h>
45 
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 
49 #include <errno.h>
50 #include <stdio.h>
51 #include <signal.h>
52 #include <netdb.h>
53 #include <syslog.h>
54 
55 extern	int errno;
56 
57 int	reapchild();
58 char	*index();
59 char	*malloc();
60 
61 int	debug = 0;
62 int	allsock;
63 int	options;
64 struct	servent *sp;
65 
66 struct	servtab {
67 	char	*se_service;		/* name of service */
68 	int	se_socktype;		/* type of socket to use */
69 	char	*se_proto;		/* protocol used */
70 	short	se_wait;		/* single threaded server */
71 	short	se_checked;		/* looked at during merge */
72 	char	*se_server;		/* server program */
73 #define MAXARGV 5
74 	char	*se_argv[MAXARGV+1];	/* program arguments */
75 	int	se_fd;			/* open descriptor */
76 	struct	sockaddr_in se_ctrladdr;/* bound address */
77 	struct	servtab *se_next;
78 } *servtab;
79 
80 char	*CONFIG = "/etc/inetd.conf";
81 
82 main(argc, argv)
83 	int argc;
84 	char *argv[];
85 {
86 	int ctrl;
87 	register struct servtab *sep;
88 	char *cp, buf[50];
89 	int pid, i;
90 
91 	argc--, argv++;
92 	while (argc > 0 && *argv[0] == '-') {
93 		for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
94 
95 		case 'd':
96 			debug = 1;
97 			options |= SO_DEBUG;
98 			break;
99 
100 		default:
101 			fprintf(stderr,
102 			    "inetd: Unknown flag -%c ignored.\n", *cp);
103 			break;
104 		}
105 nextopt:
106 		argc--, argv++;
107 	}
108 	if (argc > 0)
109 		CONFIG = argv[0];
110 #ifndef DEBUG
111 	if (fork())
112 		exit(0);
113 	{ int s;
114 	for (s = 0; s < 10; s++)
115 		(void) close(s);
116 	}
117 	(void) open("/", O_RDONLY);
118 	(void) dup2(0, 1);
119 	(void) dup2(0, 2);
120 	{ int tt = open("/dev/tty", O_RDWR);
121 	  if (tt > 0) {
122 		ioctl(tt, TIOCNOTTY, 0);
123 		close(tt);
124 	  }
125 	}
126 #endif
127 	openlog("inetd", LOG_PID, 0);
128 	config();
129 	signal(SIGHUP, config);
130 	signal(SIGCHLD, reapchild);
131 	for (;;) {
132 		int readable, s, ctrl;
133 
134 		while (allsock == 0)
135 			sigpause(0);
136 		readable = allsock;
137 		if (select(32, &readable, 0, 0, 0) <= 0)
138 			continue;
139 		s = ffs(readable)-1;
140 		if (s < 0)
141 			continue;
142 		for (sep = servtab; sep; sep = sep->se_next)
143 			if (s == sep->se_fd)
144 				goto found;
145 		abort(1);
146 	found:
147 		if (debug)
148 			fprintf(stderr, "someone wants %s\n", sep->se_service);
149 		if (sep->se_socktype == SOCK_STREAM) {
150 			ctrl = accept(s, 0, 0);
151 			if (debug)
152 				fprintf(stderr, "accept, ctrl %d\n", ctrl);
153 			if (ctrl < 0) {
154 				if (errno == EINTR)
155 					continue;
156 				syslog(LOG_WARNING, "accept: %m");
157 				continue;
158 			}
159 		} else
160 			ctrl = sep->se_fd;
161 #define mask(sig)	(1 << (sig - 1))
162 		sigblock(mask(SIGCHLD)|mask(SIGHUP));
163 		pid = fork();
164 		if (pid < 0) {
165 			if (sep->se_socktype == SOCK_STREAM)
166 				close(ctrl);
167 			sleep(1);
168 			continue;
169 		}
170 		if (sep->se_wait) {
171 			sep->se_wait = pid;
172 			allsock &= ~(1 << s);
173 		}
174 		sigsetmask(0);
175 		if (pid == 0) {
176 #ifdef	DEBUG
177 			int tt = open("/dev/tty", O_RDWR);
178 			if (tt > 0) {
179 				ioctl(tt, TIOCNOTTY, 0);
180 				close(tt);
181 			}
182 #endif
183 			dup2(ctrl, 0), close(ctrl), dup2(0, 1);
184 			for (i = getdtablesize(); --i > 2; )
185 				close(i);
186 			if (debug)
187 				fprintf(stderr, "%d execl %s\n",
188 				    getpid(), sep->se_server);
189 			execv(sep->se_server, sep->se_argv);
190 			if (sep->se_socktype != SOCK_STREAM)
191 				recv(0, buf, sizeof (buf), 0);
192 			syslog(LOG_ERR, "execv %s: %m", sep->se_server);
193 			_exit(1);
194 		}
195 		if (sep->se_socktype == SOCK_STREAM)
196 			close(ctrl);
197 	}
198 }
199 
200 reapchild()
201 {
202 	union wait status;
203 	int pid;
204 	register struct servtab *sep;
205 
206 	for (;;) {
207 		pid = wait3(&status, WNOHANG, 0);
208 		if (pid <= 0)
209 			break;
210 		if (debug)
211 			fprintf(stderr, "%d reaped\n", pid);
212 		for (sep = servtab; sep; sep = sep->se_next)
213 			if (sep->se_wait == pid) {
214 				if (status.w_status)
215 					syslog(LOG_WARNING,
216 					    "%s: exit status 0x%x",
217 					    sep->se_server, status);
218 				if (debug)
219 					fprintf(stderr, "restored %s, fd %d\n",
220 					    sep->se_service, sep->se_fd);
221 				allsock |= 1 << sep->se_fd;
222 				sep->se_wait = 1;
223 			}
224 	}
225 }
226 
227 config()
228 {
229 	register struct servtab *sep, *cp, **sepp;
230 	struct servtab *getconfigent(), *enter();
231 	int omask, on = 1;
232 
233 	if (!setconfig()) {
234 		syslog(LOG_ERR, "%s: %m", CONFIG);
235 		return;
236 	}
237 	for (sep = servtab; sep; sep = sep->se_next)
238 		sep->se_checked = 0;
239 	while (cp = getconfigent()) {
240 		for (sep = servtab; sep; sep = sep->se_next)
241 			if (strcmp(sep->se_service, cp->se_service) == 0 &&
242 			    strcmp(sep->se_proto, cp->se_proto) == 0)
243 				break;
244 		if (sep != 0) {
245 			int i;
246 
247 			omask = sigblock(mask(SIGCHLD));
248 			sep->se_wait = cp->se_wait;
249 #define SWAP(a, b) { char *c = a; a = b; b = c; }
250 			if (cp->se_server)
251 				SWAP(sep->se_server, cp->se_server);
252 			for (i = 0; i < MAXARGV; i++)
253 				SWAP(sep->se_argv[i], cp->se_argv[i]);
254 			sigsetmask(omask);
255 			freeconfig(cp);
256 		} else
257 			sep = enter(cp);
258 		sep->se_checked = 1;
259 		if (sep->se_fd != -1)
260 			continue;
261 		sp = getservbyname(sep->se_service, sep->se_proto);
262 		if (sp == 0) {
263 			syslog(LOG_ERR, "%s/%s: unknown service",
264 			    sep->se_service, sep->se_proto);
265 			continue;
266 		}
267 		sep->se_ctrladdr.sin_port = sp->s_port;
268 		if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
269 			syslog(LOG_ERR, "%s/%s: socket: %m",
270 			    sep->se_service, sep->se_proto);
271 			continue;
272 		}
273 #define	turnon(fd, opt) \
274 	setsockopt(fd, SOL_SOCKET, opt, &on, sizeof (on))
275 		if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
276 		    turnon(sep->se_fd, SO_DEBUG) < 0)
277 			syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
278 		if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
279 			syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
280 #undef turnon
281 		if (bind(sep->se_fd, &sep->se_ctrladdr,
282 		    sizeof (sep->se_ctrladdr), 0) < 0) {
283 			syslog(LOG_ERR, "%s/%s: bind: %m",
284 			    sep->se_service, sep->se_proto);
285 			continue;
286 		}
287 		if (sep->se_socktype == SOCK_STREAM)
288 			listen(sep->se_fd, 10);
289 		allsock |= 1 << sep->se_fd;
290 	}
291 	endconfig();
292 	/*
293 	 * Purge anything not looked at above.
294 	 */
295 	omask = sigblock(mask(SIGCHLD));
296 	sepp = &servtab;
297 	while (sep = *sepp) {
298 		if (sep->se_checked) {
299 			sepp = &sep->se_next;
300 			continue;
301 		}
302 		*sepp = sep->se_next;
303 		if (sep->se_fd != -1) {
304 			allsock &= ~(1 << sep->se_fd);
305 			(void) close(sep->se_fd);
306 		}
307 		freeconfig(sep);
308 		free((char *)sep);
309 	}
310 	(void) sigsetmask(omask);
311 }
312 
313 struct servtab *
314 enter(cp)
315 	struct servtab *cp;
316 {
317 	register struct servtab *sep;
318 	int omask, i;
319 	char *strdup();
320 
321 	sep = (struct servtab *)malloc(sizeof (*sep));
322 	if (sep == (struct servtab *)0) {
323 		syslog(LOG_ERR, "Out of memory.");
324 		exit(-1);
325 	}
326 	*sep = *cp;
327 	sep->se_fd = -1;
328 	omask = sigblock(mask(SIGCHLD));
329 	sep->se_next = servtab;
330 	servtab = sep;
331 	sigsetmask(omask);
332 	return (sep);
333 }
334 
335 FILE	*fconfig = NULL;
336 struct	servtab serv;
337 char	line[256];
338 char	*skip(), *nextline();
339 
340 setconfig()
341 {
342 
343 	if (fconfig != NULL) {
344 		fseek(fconfig, 0, L_SET);
345 		return (1);
346 	}
347 	fconfig = fopen(CONFIG, "r");
348 	return (fconfig != NULL);
349 }
350 
351 endconfig()
352 {
353 
354 	if (fconfig == NULL)
355 		return;
356 	fclose(fconfig);
357 	fconfig = NULL;
358 }
359 
360 struct servtab *
361 getconfigent()
362 {
363 	register struct servtab *sep = &serv;
364 	char *cp, *arg;
365 	int argc;
366 
367 	while ((cp = nextline(fconfig)) && *cp == '#')
368 		;
369 	if (cp == NULL)
370 		return ((struct servtab *)0);
371 	sep->se_service = strdup(skip(&cp));
372 	arg = skip(&cp);
373 	if (strcmp(arg, "stream") == 0)
374 		sep->se_socktype = SOCK_STREAM;
375 	else if (strcmp(arg, "dgram") == 0)
376 		sep->se_socktype = SOCK_DGRAM;
377 	else if (strcmp(arg, "rdm") == 0)
378 		sep->se_socktype = SOCK_RDM;
379 	else if (strcmp(arg, "seqpacket") == 0)
380 		sep->se_socktype = SOCK_SEQPACKET;
381 	else if (strcmp(arg, "raw") == 0)
382 		sep->se_socktype = SOCK_RAW;
383 	else
384 		sep->se_socktype = -1;
385 	sep->se_proto = strdup(skip(&cp));
386 	arg = skip(&cp);
387 	sep->se_wait = strcmp(arg, "wait") == 0;
388 	sep->se_server = strdup(skip(&cp));
389 	argc = 0;
390 	for (arg = skip(&cp); cp; arg = skip(&cp))
391 		if (argc < MAXARGV)
392 			sep->se_argv[argc++] = strdup(arg);
393 	while (argc <= MAXARGV)
394 		sep->se_argv[argc++] = NULL;
395 	return (sep);
396 }
397 
398 freeconfig(cp)
399 	register struct servtab *cp;
400 {
401 	int i;
402 
403 	if (cp->se_service)
404 		free(cp->se_service);
405 	if (cp->se_proto)
406 		free(cp->se_proto);
407 	if (cp->se_server)
408 		free(cp->se_server);
409 	for (i = 0; i < MAXARGV; i++)
410 		if (cp->se_argv[i])
411 			free(cp->se_argv[i]);
412 }
413 
414 char *
415 skip(cpp)
416 	char **cpp;
417 {
418 	register char *cp = *cpp;
419 	char *start;
420 
421 again:
422 	while (*cp == ' ' || *cp == '\t')
423 		cp++;
424 	if (*cp == '\0') {
425 		char c;
426 
427 		c = getc(fconfig);
428 		ungetc(c, fconfig);
429 		if (c == ' ' || c == '\t')
430 			if (cp = nextline(fconfig))
431 				goto again;
432 		*cpp = (char *)0;
433 		return ((char *)0);
434 	}
435 	start = cp;
436 	while (*cp && *cp != ' ' && *cp != '\t')
437 		cp++;
438 	if (*cp != '\0')
439 		*cp++ = '\0';
440 	*cpp = cp;
441 	return (start);
442 }
443 
444 char *
445 nextline(fd)
446 	FILE *fd;
447 {
448 	char *cp;
449 
450 	if (fgets(line, sizeof (line), fconfig) == NULL)
451 		return ((char *)0);
452 	cp = index(line, '\n');
453 	if (cp)
454 		*cp = '\0';
455 	return (line);
456 }
457 
458 char *
459 strdup(cp)
460 	char *cp;
461 {
462 	char *new;
463 
464 	if (cp == NULL)
465 		cp = "";
466 	new = malloc(strlen(cp) + 1);
467 	if (new == (char *)0) {
468 		syslog(LOG_ERR, "Out of memory.");
469 		exit(-1);
470 	}
471 	strcpy(new, cp);
472 	return (new);
473 }
474