Commit | Line | Data |
---|---|---|
21ed1185 | 1 | #ifndef lint |
83793daf | 2 | static 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 | |
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 | |
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 | ||
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) | |
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 | ||
227 | config() | |
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 | ||
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) { | |
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 | ||
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) { | |
6a3125d9 | 468 | syslog(LOG_ERR, "Out of memory."); |
21ed1185 MK |
469 | exit(-1); |
470 | } | |
471 | strcpy(new, cp); | |
472 | return (new); | |
473 | } |