add user to config file to allow the servers to run as someone besides
[unix-history] / usr / src / usr.sbin / inetd / inetd.c
CommitLineData
21ed1185 1#ifndef lint
83793daf 2static char sccsid[] = "@(#)inetd.c 4.4 (Berkeley) %G%";
21ed1185
MK
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>
6a3125d9 53#include <syslog.h>
21ed1185
MK
54
55extern int errno;
56
57int reapchild();
58char *index();
59char *malloc();
60
61int debug = 0;
62int allsock;
63int options;
64struct servent *sp;
65
66struct 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
80char *CONFIG = "/etc/inetd.conf";
81
82main(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 }
105nextopt:
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
6a3125d9 127 openlog("inetd", LOG_PID, 0);
21ed1185
MK
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);
6a3125d9
RC
151 if (debug)
152 fprintf(stderr, "accept, ctrl %d\n", ctrl);
21ed1185
MK
153 if (ctrl < 0) {
154 if (errno == EINTR)
155 continue;
6a3125d9 156 syslog(LOG_WARNING, "accept: %m");
21ed1185
MK
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)
6a3125d9
RC
191 recv(0, buf, sizeof (buf), 0);
192 syslog(LOG_ERR, "execv %s: %m", sep->se_server);
21ed1185
MK
193 _exit(1);
194 }
195 if (sep->se_socktype == SOCK_STREAM)
196 close(ctrl);
197 }
198}
199
200reapchild()
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)
6a3125d9
RC
215 syslog(LOG_WARNING,
216 "%s: exit status 0x%x",
21ed1185
MK
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
227config()
228{
229 register struct servtab *sep, *cp, **sepp;
230 struct servtab *getconfigent(), *enter();
bcb894cb 231 int omask, on = 1;
21ed1185
MK
232
233 if (!setconfig()) {
6a3125d9 234 syslog(LOG_ERR, "%s: %m", CONFIG);
21ed1185
MK
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) {
6a3125d9 263 syslog(LOG_ERR, "%s/%s: unknown service",
21ed1185
MK
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) {
6a3125d9 269 syslog(LOG_ERR, "%s/%s: socket: %m",
21ed1185 270 sep->se_service, sep->se_proto);
21ed1185
MK
271 continue;
272 }
bcb894cb
SL
273#define turnon(fd, opt) \
274 setsockopt(fd, SOL_SOCKET, opt, &on, sizeof (on))
21ed1185 275 if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
bcb894cb 276 turnon(sep->se_fd, SO_DEBUG) < 0)
6a3125d9 277 syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
bcb894cb 278 if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
6a3125d9 279 syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
bcb894cb 280#undef turnon
21ed1185
MK
281 if (bind(sep->se_fd, &sep->se_ctrladdr,
282 sizeof (sep->se_ctrladdr), 0) < 0) {
6a3125d9 283 syslog(LOG_ERR, "%s/%s: bind: %m",
21ed1185 284 sep->se_service, sep->se_proto);
21ed1185
MK
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
313struct servtab *
314enter(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) {
6a3125d9 323 syslog(LOG_ERR, "Out of memory.");
21ed1185
MK
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
335FILE *fconfig = NULL;
336struct servtab serv;
337char line[256];
338char *skip(), *nextline();
339
340setconfig()
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
351endconfig()
352{
353
354 if (fconfig == NULL)
355 return;
356 fclose(fconfig);
357 fconfig = NULL;
358}
359
360struct servtab *
361getconfigent()
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
398freeconfig(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
414char *
415skip(cpp)
416 char **cpp;
417{
418 register char *cp = *cpp;
419 char *start;
420
421again:
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
444char *
445nextline(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
458char *
459strdup(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) {
6a3125d9 468 syslog(LOG_ERR, "Out of memory.");
21ed1185
MK
469 exit(-1);
470 }
471 strcpy(new, cp);
472 return (new);
473}