diff options
Diffstat (limited to 'busybox/networking/inetd.c')
-rw-r--r-- | busybox/networking/inetd.c | 1221 |
1 files changed, 1221 insertions, 0 deletions
diff --git a/busybox/networking/inetd.c b/busybox/networking/inetd.c new file mode 100644 index 000000000..169cc8716 --- /dev/null +++ b/busybox/networking/inetd.c | |||
@@ -0,0 +1,1221 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1983,1991 The Regents of the University of California. | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * This code is derived from software contributed to Berkeley by | ||
6 | * David A. Holland. | ||
7 | * | ||
8 | * Busybox port by Vladimir Oleynik (C) 2001-2003 <dzo@simtreas.ru> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | * General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | /* | ||
27 | * Inetd - Internet super-server | ||
28 | * | ||
29 | * This program invokes all internet services as needed. | ||
30 | * connection-oriented services are invoked each time a | ||
31 | * connection is made, by creating a process. This process | ||
32 | * is passed the connection as file descriptor 0 and is | ||
33 | * expected to do a getpeername to find out the source host | ||
34 | * and port. | ||
35 | * | ||
36 | * Datagram oriented services are invoked when a datagram | ||
37 | * arrives; a process is created and passed a pending message | ||
38 | * on file descriptor 0. Datagram servers may either connect | ||
39 | * to their peer, freeing up the original socket for inetd | ||
40 | * to receive further messages on, or ``take over the socket'', | ||
41 | * processing all arriving datagrams and, eventually, timing | ||
42 | * out. The first type of server is said to be ``multi-threaded''; | ||
43 | * the second type of server ``single-threaded''. | ||
44 | * | ||
45 | * Inetd uses a configuration file which is read at startup | ||
46 | * and, possibly, at some later time in response to a hangup signal. | ||
47 | * The configuration file is ``free format'' with fields given in the | ||
48 | * order shown below. Continuation lines for an entry must being with | ||
49 | * a space or tab. All fields must be present in each entry. | ||
50 | * | ||
51 | * service name must be in /etc/services | ||
52 | * socket type stream/dgram/raw/rdm/seqpacket | ||
53 | * protocol must be in /etc/protocols | ||
54 | * wait/nowait[.max] single-threaded/multi-threaded, max # | ||
55 | * user[.group] user/group to run daemon as | ||
56 | * server program full path name | ||
57 | * server program arguments maximum of MAXARGS (20) | ||
58 | * | ||
59 | * RPC services unsupported | ||
60 | * | ||
61 | * Comment lines are indicated by a `#' in column 1. | ||
62 | */ | ||
63 | |||
64 | /* | ||
65 | * Here's the scoop concerning the user.group feature: | ||
66 | * | ||
67 | * 1) No group listed. | ||
68 | * | ||
69 | * a) for root: NO setuid() or setgid() is done | ||
70 | * | ||
71 | * b) nonroot: setuid() | ||
72 | * setgid(primary group as found in passwd) | ||
73 | * initgroups(name, primary group) | ||
74 | * | ||
75 | * 2) set-group-option on. | ||
76 | * | ||
77 | * a) for root: NO setuid() | ||
78 | * setgid(specified group) | ||
79 | * setgroups(1, specified group) | ||
80 | * | ||
81 | * b) nonroot: setuid() | ||
82 | * setgid(specified group) | ||
83 | * initgroups(name, specified group) | ||
84 | * | ||
85 | * All supplementary groups are discarded at startup in case inetd was | ||
86 | * run manually. | ||
87 | */ | ||
88 | |||
89 | #define __USE_BSD_SIGNAL | ||
90 | |||
91 | #include "busybox.h" | ||
92 | |||
93 | |||
94 | #ifndef __linux__ | ||
95 | #ifndef RLIMIT_NOFILE | ||
96 | #define RLIMIT_NOFILE RLIMIT_OFILE | ||
97 | #endif | ||
98 | #endif | ||
99 | |||
100 | #include <sys/file.h> | ||
101 | #include <sys/ioctl.h> | ||
102 | #include <sys/param.h> | ||
103 | #include <sys/resource.h> | ||
104 | #include <sys/socket.h> | ||
105 | #include <sys/stat.h> | ||
106 | #include <sys/time.h> | ||
107 | #include <sys/un.h> | ||
108 | #include <sys/wait.h> | ||
109 | |||
110 | #include <netinet/in.h> | ||
111 | #include <netinet/ip.h> | ||
112 | #include <arpa/inet.h> | ||
113 | |||
114 | #include <errno.h> | ||
115 | #include <signal.h> | ||
116 | #include <netdb.h> | ||
117 | #include <syslog.h> | ||
118 | #include <stdio.h> | ||
119 | #include <stdlib.h> | ||
120 | #include <string.h> | ||
121 | #include <getopt.h> | ||
122 | #include <unistd.h> | ||
123 | #include <stdarg.h> | ||
124 | #include <time.h> | ||
125 | |||
126 | #ifndef OPEN_MAX | ||
127 | #define OPEN_MAX 64 | ||
128 | #endif | ||
129 | |||
130 | #define _PATH_INETDCONF "/etc/inetd.conf" | ||
131 | #define _PATH_INETDPID "/var/run/inetd.pid" | ||
132 | |||
133 | #define TOOMANY 40 /* don't start more than TOOMANY */ | ||
134 | #define CNT_INTVL 60 /* servers in CNT_INTVL sec. */ | ||
135 | #define RETRYTIME (60*10) /* retry after bind or server fail */ | ||
136 | #define MAXARGV 20 | ||
137 | |||
138 | #define se_ctrladdr se_un.se_un_ctrladdr | ||
139 | #define se_ctrladdr_in se_un.se_un_ctrladdr_in | ||
140 | #define se_ctrladdr_un se_un.se_un_ctrladdr_un | ||
141 | |||
142 | /* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */ | ||
143 | #define FD_MARGIN (8) | ||
144 | |||
145 | /* Check unsupporting builtin */ | ||
146 | #if defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO || \ | ||
147 | defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD || \ | ||
148 | defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME || \ | ||
149 | defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME || \ | ||
150 | defined CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN | ||
151 | # define INETD_FEATURE_ENABLED | ||
152 | #endif | ||
153 | |||
154 | typedef struct servtab_s { | ||
155 | char *se_service; /* name of service */ | ||
156 | int se_socktype; /* type of socket to use */ | ||
157 | int se_family; /* address family */ | ||
158 | char *se_proto; /* protocol used */ | ||
159 | short se_wait; /* single threaded server */ | ||
160 | short se_checked; /* looked at during merge */ | ||
161 | char *se_user; /* user name to run as */ | ||
162 | char *se_group; /* group name to run as */ | ||
163 | #ifdef INETD_FEATURE_ENABLED | ||
164 | const struct biltin *se_bi; /* if built-in, description */ | ||
165 | #endif | ||
166 | char *se_server; /* server program */ | ||
167 | char *se_argv[MAXARGV+1]; /* program arguments */ | ||
168 | int se_fd; /* open descriptor */ | ||
169 | union { | ||
170 | struct sockaddr se_un_ctrladdr; | ||
171 | struct sockaddr_in se_un_ctrladdr_in; | ||
172 | struct sockaddr_un se_un_ctrladdr_un; | ||
173 | } se_un; /* bound address */ | ||
174 | int se_ctrladdr_size; | ||
175 | int se_max; /* max # of instances of this service */ | ||
176 | int se_count; /* number started since se_time */ | ||
177 | struct timeval se_time; /* start of se_count */ | ||
178 | struct servtab_s *se_next; | ||
179 | } servtab_t; | ||
180 | |||
181 | static servtab_t *servtab; | ||
182 | |||
183 | #ifdef INETD_FEATURE_ENABLED | ||
184 | struct biltin { | ||
185 | const char *bi_service; /* internally provided service name */ | ||
186 | int bi_socktype; /* type of socket supported */ | ||
187 | short bi_fork; /* 1 if should fork before call */ | ||
188 | short bi_wait; /* 1 if should wait for child */ | ||
189 | void (*bi_fn)(int, servtab_t *); /* fn which performs it */ | ||
190 | }; | ||
191 | |||
192 | /* Echo received data */ | ||
193 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO | ||
194 | static void echo_stream(int, servtab_t *); | ||
195 | static void echo_dg(int, servtab_t *); | ||
196 | #endif | ||
197 | /* Internet /dev/null */ | ||
198 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD | ||
199 | static void discard_stream(int, servtab_t *); | ||
200 | static void discard_dg(int, servtab_t *); | ||
201 | #endif | ||
202 | /* Return 32 bit time since 1900 */ | ||
203 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME | ||
204 | static void machtime_stream(int, servtab_t *); | ||
205 | static void machtime_dg(int, servtab_t *); | ||
206 | #endif | ||
207 | /* Return human-readable time */ | ||
208 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME | ||
209 | static void daytime_stream(int, servtab_t *); | ||
210 | static void daytime_dg(int, servtab_t *); | ||
211 | #endif | ||
212 | /* Familiar character generator */ | ||
213 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN | ||
214 | static void chargen_stream(int, servtab_t *); | ||
215 | static void chargen_dg(int, servtab_t *); | ||
216 | #endif | ||
217 | |||
218 | static const struct biltin biltins[] = { | ||
219 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO | ||
220 | /* Echo received data */ | ||
221 | { "echo", SOCK_STREAM, 1, 0, echo_stream, }, | ||
222 | { "echo", SOCK_DGRAM, 0, 0, echo_dg, }, | ||
223 | #endif | ||
224 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD | ||
225 | /* Internet /dev/null */ | ||
226 | { "discard", SOCK_STREAM, 1, 0, discard_stream, }, | ||
227 | { "discard", SOCK_DGRAM, 0, 0, discard_dg, }, | ||
228 | #endif | ||
229 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME | ||
230 | /* Return 32 bit time since 1900 */ | ||
231 | { "time", SOCK_STREAM, 0, 0, machtime_stream, }, | ||
232 | { "time", SOCK_DGRAM, 0, 0, machtime_dg, }, | ||
233 | #endif | ||
234 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME | ||
235 | /* Return human-readable time */ | ||
236 | { "daytime", SOCK_STREAM, 0, 0, daytime_stream, }, | ||
237 | { "daytime", SOCK_DGRAM, 0, 0, daytime_dg, }, | ||
238 | #endif | ||
239 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN | ||
240 | /* Familiar character generator */ | ||
241 | { "chargen", SOCK_STREAM, 1, 0, chargen_stream, }, | ||
242 | { "chargen", SOCK_DGRAM, 0, 0, chargen_dg, }, | ||
243 | #endif | ||
244 | { NULL, 0, 0, 0, NULL } | ||
245 | }; | ||
246 | #endif /* INETD_FEATURE_ENABLED */ | ||
247 | |||
248 | #ifdef RLIMIT_NOFILE | ||
249 | static struct rlimit rlim_ofile; | ||
250 | #endif | ||
251 | |||
252 | /* Length of socket listen queue. Should be per-service probably. */ | ||
253 | static int global_queuelen = 128; | ||
254 | |||
255 | static FILE *fconfig; | ||
256 | static sigset_t blockmask; | ||
257 | static sigset_t emptymask; | ||
258 | static fd_set allsock; | ||
259 | static int nsock; | ||
260 | static int maxsock; | ||
261 | static int timingout; | ||
262 | static int rlim_ofile_cur = OPEN_MAX; | ||
263 | static const char *CONFIG = _PATH_INETDCONF; | ||
264 | |||
265 | static void | ||
266 | syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) | ||
267 | __attribute__ ((noreturn, format (printf, 2, 3))); | ||
268 | |||
269 | static void | ||
270 | syslog_err_and_discard_dg(int se_socktype, const char *msg, ...) | ||
271 | { | ||
272 | char buf[50]; | ||
273 | va_list p; | ||
274 | |||
275 | va_start(p, msg); | ||
276 | vsyslog(LOG_ERR, msg, p); | ||
277 | if (se_socktype != SOCK_STREAM) | ||
278 | recv(0, buf, sizeof (buf), 0); | ||
279 | _exit(1); | ||
280 | } | ||
281 | |||
282 | static char * inetd_strdup(const char *s) | ||
283 | { | ||
284 | char *ms = strdup(s); | ||
285 | |||
286 | if(ms == NULL) | ||
287 | syslog_err_and_discard_dg(SOCK_STREAM, "strdup: %m"); | ||
288 | return ms; | ||
289 | } | ||
290 | |||
291 | |||
292 | static servtab_t *getconfigent(void) | ||
293 | { | ||
294 | static servtab_t serv; | ||
295 | servtab_t *sep = &serv; | ||
296 | int argc; | ||
297 | char *cp = NULL; | ||
298 | char *cp_ptr; | ||
299 | char *cp_ptr_ptr = NULL; | ||
300 | |||
301 | more: | ||
302 | free(cp); | ||
303 | cp = bb_get_chomped_line_from_file(fconfig); | ||
304 | if (feof(fconfig)) { | ||
305 | free(cp); | ||
306 | return (NULL); | ||
307 | } | ||
308 | if ((cp == NULL) || (*cp == '#')) { | ||
309 | goto more; | ||
310 | } | ||
311 | /* make bind 0.0.0.0 and other zero default */ | ||
312 | memset((char *)sep, 0, sizeof *sep); | ||
313 | |||
314 | cp_ptr = strtok_r(cp, " \t", &cp_ptr_ptr); | ||
315 | if (cp_ptr == NULL) { | ||
316 | /* Error */ | ||
317 | goto more; | ||
318 | } | ||
319 | sep->se_service = inetd_strdup(cp_ptr); | ||
320 | |||
321 | cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); | ||
322 | if (cp_ptr == NULL) { | ||
323 | /* Error */ | ||
324 | goto more; | ||
325 | } | ||
326 | if (strcmp(cp_ptr, "stream") == 0) | ||
327 | sep->se_socktype = SOCK_STREAM; | ||
328 | else if (strcmp(cp_ptr, "dgram") == 0) | ||
329 | sep->se_socktype = SOCK_DGRAM; | ||
330 | else if (strcmp(cp_ptr, "rdm") == 0) | ||
331 | sep->se_socktype = SOCK_RDM; | ||
332 | else if (strcmp(cp_ptr, "seqpacket") == 0) | ||
333 | sep->se_socktype = SOCK_SEQPACKET; | ||
334 | else if (strcmp(cp_ptr, "raw") == 0) | ||
335 | sep->se_socktype = SOCK_RAW; | ||
336 | else | ||
337 | sep->se_socktype = -1; | ||
338 | |||
339 | cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); | ||
340 | if (cp_ptr == NULL) { | ||
341 | /* error */ | ||
342 | goto more; | ||
343 | } | ||
344 | if (strcmp(cp_ptr, "unix") == 0) { | ||
345 | sep->se_family = AF_UNIX; | ||
346 | } else { | ||
347 | if (strncmp(cp_ptr, "rpc/", 4) == 0) { | ||
348 | syslog(LOG_ERR, "%s: rpc services not supported", | ||
349 | sep->se_service); | ||
350 | goto more; | ||
351 | } | ||
352 | sep->se_family = AF_INET; | ||
353 | } | ||
354 | sep->se_proto = inetd_strdup(cp_ptr); | ||
355 | |||
356 | cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); | ||
357 | if (cp_ptr == NULL) { | ||
358 | /* error */ | ||
359 | goto more; | ||
360 | } | ||
361 | { | ||
362 | char *s = strchr(cp_ptr, '.'); | ||
363 | if (s) { | ||
364 | *s++ = '\0'; | ||
365 | sep->se_max = atoi(s); | ||
366 | } else | ||
367 | sep->se_max = TOOMANY; | ||
368 | } | ||
369 | sep->se_wait = strcmp(cp_ptr, "wait") == 0; | ||
370 | |||
371 | cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); | ||
372 | if (cp_ptr == NULL) { | ||
373 | /* error */ | ||
374 | goto more; | ||
375 | } | ||
376 | |||
377 | sep->se_user = inetd_strdup(cp_ptr); | ||
378 | { | ||
379 | char *cp_ptr2 = strchr(sep->se_user, '.'); | ||
380 | |||
381 | if (cp_ptr2) { | ||
382 | *cp_ptr2++ = '\0'; | ||
383 | } | ||
384 | sep->se_group = cp_ptr2; | ||
385 | } | ||
386 | |||
387 | cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr); | ||
388 | if (cp_ptr == NULL) { | ||
389 | /* error */ | ||
390 | goto more; | ||
391 | } | ||
392 | if (strcmp(cp_ptr, "internal") == 0) { | ||
393 | #ifdef INETD_FEATURE_ENABLED | ||
394 | const struct biltin *bi; | ||
395 | |||
396 | for (bi = biltins; bi->bi_service; bi++) { | ||
397 | if ((bi->bi_socktype == sep->se_socktype) && | ||
398 | (strcmp(bi->bi_service, sep->se_service) == 0)) { | ||
399 | break; | ||
400 | } | ||
401 | } | ||
402 | if (bi->bi_service == 0) { | ||
403 | syslog(LOG_ERR, "internal service %s unknown", sep->se_service); | ||
404 | goto more; | ||
405 | } | ||
406 | sep->se_bi = bi; | ||
407 | sep->se_wait = bi->bi_wait; | ||
408 | #else | ||
409 | syslog(LOG_ERR, "internal service %s unknown", cp_ptr); | ||
410 | goto more; | ||
411 | #endif | ||
412 | } | ||
413 | #ifdef INETD_FEATURE_ENABLED | ||
414 | else { | ||
415 | sep->se_bi = NULL; | ||
416 | } | ||
417 | #endif | ||
418 | sep->se_server = inetd_strdup(cp_ptr); | ||
419 | |||
420 | argc = 0; | ||
421 | while ((cp_ptr = strtok_r(NULL, " \t", &cp_ptr_ptr)) != NULL) { | ||
422 | if (argc < MAXARGV) { | ||
423 | sep->se_argv[argc++] = inetd_strdup(cp_ptr); | ||
424 | } | ||
425 | } | ||
426 | free(cp); | ||
427 | |||
428 | return (sep); | ||
429 | } | ||
430 | |||
431 | static void freeconfig(servtab_t *cp) | ||
432 | { | ||
433 | int i; | ||
434 | |||
435 | free(cp->se_service); | ||
436 | free(cp->se_proto); | ||
437 | free(cp->se_user); | ||
438 | /* Note: se_group is part of the newstr'ed se_user */ | ||
439 | free(cp->se_server); | ||
440 | for (i = 0; i < MAXARGV; i++) | ||
441 | free(cp->se_argv[i]); | ||
442 | } | ||
443 | |||
444 | #ifdef INETD_FEATURE_ENABLED | ||
445 | static char **Argv; | ||
446 | static char *LastArg; | ||
447 | |||
448 | static void setproctitle(char *a, int s) | ||
449 | { | ||
450 | size_t size; | ||
451 | char *cp; | ||
452 | struct sockaddr_in sn; | ||
453 | char buf[80]; | ||
454 | |||
455 | cp = Argv[0]; | ||
456 | size = sizeof(sn); | ||
457 | if (getpeername(s, (struct sockaddr *)&sn, &size) == 0) | ||
458 | (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sn.sin_addr)); | ||
459 | else | ||
460 | (void) sprintf(buf, "-%s", a); | ||
461 | strncpy(cp, buf, LastArg - cp); | ||
462 | cp += strlen(cp); | ||
463 | while (cp < LastArg) | ||
464 | *cp++ = ' '; | ||
465 | } | ||
466 | #endif /* INETD_FEATURE_ENABLED */ | ||
467 | |||
468 | |||
469 | static void setup(servtab_t *sep) | ||
470 | { | ||
471 | int on = 1; | ||
472 | |||
473 | if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) { | ||
474 | syslog(LOG_ERR, "%s/%s: socket: %m", | ||
475 | sep->se_service, sep->se_proto); | ||
476 | return; | ||
477 | } | ||
478 | if (setsockopt(sep->se_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, | ||
479 | sizeof(on)) < 0) | ||
480 | syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m"); | ||
481 | if (bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size) < 0) { | ||
482 | syslog(LOG_ERR, "%s/%s: bind: %m", | ||
483 | sep->se_service, sep->se_proto); | ||
484 | (void) close(sep->se_fd); | ||
485 | sep->se_fd = -1; | ||
486 | if (!timingout) { | ||
487 | timingout = 1; | ||
488 | alarm(RETRYTIME); | ||
489 | } | ||
490 | return; | ||
491 | } | ||
492 | if (sep->se_socktype == SOCK_STREAM) | ||
493 | listen(sep->se_fd, global_queuelen); | ||
494 | |||
495 | FD_SET(sep->se_fd, &allsock); | ||
496 | nsock++; | ||
497 | if (sep->se_fd > maxsock) { | ||
498 | maxsock = sep->se_fd; | ||
499 | if (maxsock > rlim_ofile_cur - FD_MARGIN) { | ||
500 | #ifdef RLIMIT_NOFILE | ||
501 | # define FD_CHUNK 32 | ||
502 | struct rlimit rl; | ||
503 | |||
504 | if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { | ||
505 | syslog(LOG_ERR, "getrlimit: %m"); | ||
506 | return; | ||
507 | } | ||
508 | rl.rlim_cur = rl.rlim_max < (rl.rlim_cur + FD_CHUNK) ? rl.rlim_max : (rl.rlim_cur + FD_CHUNK); | ||
509 | if (rl.rlim_cur <= rlim_ofile_cur) { | ||
510 | syslog(LOG_ERR, | ||
511 | # if _FILE_OFFSET_BITS == 64 | ||
512 | "bump_nofile: cannot extend file limit, max = %lld", | ||
513 | # else | ||
514 | "bump_nofile: cannot extend file limit, max = %ld", | ||
515 | # endif | ||
516 | rl.rlim_cur); | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | if (setrlimit(RLIMIT_NOFILE, &rl) < 0) { | ||
521 | syslog(LOG_ERR, "setrlimit: %m"); | ||
522 | return; | ||
523 | } | ||
524 | |||
525 | rlim_ofile_cur = rl.rlim_cur; | ||
526 | return; | ||
527 | #else | ||
528 | syslog(LOG_ERR, "bump_nofile: cannot extend file limit"); | ||
529 | return; | ||
530 | #endif /* RLIMIT_NOFILE */ | ||
531 | } | ||
532 | } | ||
533 | } | ||
534 | |||
535 | static void config(int signum) | ||
536 | { | ||
537 | servtab_t *sep, *cp, **sepp; | ||
538 | sigset_t oldmask; | ||
539 | unsigned n; | ||
540 | |||
541 | (void)signum; | ||
542 | |||
543 | if (fconfig != NULL) { | ||
544 | fseek(fconfig, 0L, L_SET); | ||
545 | } else { | ||
546 | fconfig = fopen(CONFIG, "r"); | ||
547 | if (fconfig == NULL) { | ||
548 | syslog(LOG_ERR, "%s: %m", CONFIG); | ||
549 | return; | ||
550 | } | ||
551 | } | ||
552 | |||
553 | for (sep = servtab; sep; sep = sep->se_next) | ||
554 | sep->se_checked = 0; | ||
555 | while ((cp = getconfigent()) != NULL) { | ||
556 | for (sep = servtab; sep; sep = sep->se_next) | ||
557 | if (strcmp(sep->se_service, cp->se_service) == 0 && | ||
558 | strcmp(sep->se_proto, cp->se_proto) == 0) | ||
559 | break; | ||
560 | if (sep != 0) { | ||
561 | int i; | ||
562 | |||
563 | #define SWAP(type, a, b) {type c=(type)a; (type)a=(type)b; (type)b=(type)c;} | ||
564 | |||
565 | sigprocmask(SIG_BLOCK, &emptymask, &oldmask); | ||
566 | /* | ||
567 | * sep->se_wait may be holding the pid of a daemon | ||
568 | * that we're waiting for. If so, don't overwrite | ||
569 | * it unless the config file explicitly says don't | ||
570 | * wait. | ||
571 | */ | ||
572 | if ( | ||
573 | #ifdef INETD_FEATURE_ENABLED | ||
574 | cp->se_bi == 0 && | ||
575 | #endif | ||
576 | (sep->se_wait == 1 || cp->se_wait == 0)) | ||
577 | sep->se_wait = cp->se_wait; | ||
578 | if (cp->se_max != sep->se_max) | ||
579 | SWAP(int, cp->se_max, sep->se_max); | ||
580 | if (cp->se_user) | ||
581 | SWAP(char *, sep->se_user, cp->se_user); | ||
582 | if (cp->se_group) | ||
583 | SWAP(char *, sep->se_group, cp->se_group); | ||
584 | if (cp->se_server) | ||
585 | SWAP(char *, sep->se_server, cp->se_server); | ||
586 | for (i = 0; i < MAXARGV; i++) | ||
587 | SWAP(char *, sep->se_argv[i], cp->se_argv[i]); | ||
588 | #undef SWAP | ||
589 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||
590 | // This freeconfig() is probably a bug, since it will try and free() | ||
591 | // each of the argv[] values, which are really just pointers | ||
592 | // into the middle of a single line buffer for the config file. | ||
593 | //freeconfig(cp); // BUG? | ||
594 | } else { | ||
595 | sep = (servtab_t *)xmalloc(sizeof (*sep)); | ||
596 | *sep = *cp; | ||
597 | sep->se_fd = -1; | ||
598 | sigprocmask(SIG_BLOCK, &blockmask, &oldmask); | ||
599 | sep->se_next = servtab; | ||
600 | servtab = sep; | ||
601 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||
602 | } | ||
603 | sep->se_checked = 1; | ||
604 | |||
605 | switch (sep->se_family) { | ||
606 | case AF_UNIX: | ||
607 | if (sep->se_fd != -1) | ||
608 | break; | ||
609 | (void)unlink(sep->se_service); | ||
610 | n = strlen(sep->se_service); | ||
611 | if (n > sizeof(sep->se_ctrladdr_un.sun_path) - 1) | ||
612 | n = sizeof(sep->se_ctrladdr_un.sun_path) - 1; | ||
613 | strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n); | ||
614 | sep->se_ctrladdr_un.sun_family = AF_UNIX; | ||
615 | sep->se_ctrladdr_size = n + | ||
616 | sizeof sep->se_ctrladdr_un.sun_family; | ||
617 | setup(sep); | ||
618 | break; | ||
619 | case AF_INET: | ||
620 | sep->se_ctrladdr_in.sin_family = AF_INET; | ||
621 | sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in; | ||
622 | { | ||
623 | u_short port = bb_lookup_port(sep->se_service, sep->se_proto, 0); | ||
624 | |||
625 | if (port == 0) { | ||
626 | syslog(LOG_ERR, | ||
627 | "%s/%s: unknown service", | ||
628 | sep->se_service, sep->se_proto); | ||
629 | continue; | ||
630 | } | ||
631 | if (port != sep->se_ctrladdr_in.sin_port) { | ||
632 | sep->se_ctrladdr_in.sin_port = port; | ||
633 | if (sep->se_fd != -1) { | ||
634 | FD_CLR(sep->se_fd, &allsock); | ||
635 | nsock--; | ||
636 | (void) close(sep->se_fd); | ||
637 | } | ||
638 | sep->se_fd = -1; | ||
639 | } | ||
640 | if (sep->se_fd == -1) | ||
641 | setup(sep); | ||
642 | } | ||
643 | } | ||
644 | } | ||
645 | if (fconfig) { | ||
646 | (void) fclose(fconfig); | ||
647 | fconfig = NULL; | ||
648 | } | ||
649 | /* | ||
650 | * Purge anything not looked at above. | ||
651 | */ | ||
652 | sigprocmask(SIG_SETMASK, &blockmask, &oldmask); | ||
653 | sepp = &servtab; | ||
654 | while ((sep = *sepp) != NULL) { | ||
655 | if (sep->se_checked) { | ||
656 | sepp = &sep->se_next; | ||
657 | continue; | ||
658 | } | ||
659 | *sepp = sep->se_next; | ||
660 | if (sep->se_fd != -1) { | ||
661 | FD_CLR(sep->se_fd, &allsock); | ||
662 | nsock--; | ||
663 | (void) close(sep->se_fd); | ||
664 | } | ||
665 | if (sep->se_family == AF_UNIX) | ||
666 | (void)unlink(sep->se_service); | ||
667 | freeconfig(sep); | ||
668 | free((char *)sep); | ||
669 | } | ||
670 | sigprocmask(SIG_SETMASK, &oldmask, NULL); | ||
671 | } | ||
672 | |||
673 | |||
674 | |||
675 | static void reapchild(int signum) | ||
676 | { | ||
677 | int status; | ||
678 | int pid; | ||
679 | servtab_t *sep; | ||
680 | |||
681 | (void)signum; | ||
682 | for (;;) { | ||
683 | pid = wait3(&status, WNOHANG, (struct rusage *)0); | ||
684 | if (pid <= 0) | ||
685 | break; | ||
686 | for (sep = servtab; sep; sep = sep->se_next) | ||
687 | if (sep->se_wait == pid) { | ||
688 | if (WIFEXITED(status) && WEXITSTATUS(status)) | ||
689 | syslog(LOG_WARNING, | ||
690 | "%s: exit status 0x%x", | ||
691 | sep->se_server, WEXITSTATUS(status)); | ||
692 | else if (WIFSIGNALED(status)) | ||
693 | syslog(LOG_WARNING, | ||
694 | "%s: exit signal 0x%x", | ||
695 | sep->se_server, WTERMSIG(status)); | ||
696 | sep->se_wait = 1; | ||
697 | FD_SET(sep->se_fd, &allsock); | ||
698 | nsock++; | ||
699 | } | ||
700 | } | ||
701 | } | ||
702 | |||
703 | static void retry(int signum) | ||
704 | { | ||
705 | servtab_t *sep; | ||
706 | |||
707 | (void)signum; | ||
708 | timingout = 0; | ||
709 | for (sep = servtab; sep; sep = sep->se_next) { | ||
710 | if (sep->se_fd == -1) { | ||
711 | switch (sep->se_family) { | ||
712 | case AF_UNIX: | ||
713 | case AF_INET: | ||
714 | setup(sep); | ||
715 | break; | ||
716 | } | ||
717 | } | ||
718 | } | ||
719 | } | ||
720 | |||
721 | static void goaway(int signum) | ||
722 | { | ||
723 | servtab_t *sep; | ||
724 | |||
725 | (void)signum; | ||
726 | for (sep = servtab; sep; sep = sep->se_next) | ||
727 | if (sep->se_fd != -1 && sep->se_family == AF_UNIX) | ||
728 | (void)unlink(sep->se_service); | ||
729 | (void)unlink(_PATH_INETDPID); | ||
730 | exit(0); | ||
731 | } | ||
732 | |||
733 | |||
734 | |||
735 | extern int inetd_main(int argc, char *argv[]) | ||
736 | { | ||
737 | servtab_t *sep; | ||
738 | struct group *grp = NULL; | ||
739 | struct sigaction sa; | ||
740 | int pid; | ||
741 | unsigned long opt; | ||
742 | char *sq; | ||
743 | gid_t gid; | ||
744 | |||
745 | #ifdef INETD_FEATURE_ENABLED | ||
746 | extern char **environ; | ||
747 | #endif | ||
748 | |||
749 | gid = getgid(); | ||
750 | setgroups(1, &gid); | ||
751 | |||
752 | #ifdef INETD_FEATURE_ENABLED | ||
753 | Argv = argv; | ||
754 | if (environ == 0 || *environ == 0) | ||
755 | environ = argv; | ||
756 | while (*environ) | ||
757 | environ++; | ||
758 | LastArg = environ[-1] + strlen(environ[-1]); | ||
759 | #endif | ||
760 | |||
761 | #if defined(__uClinux__) | ||
762 | opt = bb_getopt_ulflags(argc, argv, "q:f", &sq); | ||
763 | if (!(opt & 2)) { | ||
764 | daemon(0, 0); | ||
765 | /* reexec for vfork() do continue parent */ | ||
766 | vfork_daemon_rexec(argc, argv, "-f"); | ||
767 | } | ||
768 | #else | ||
769 | opt = bb_getopt_ulflags(argc, argv, "q:", &sq); | ||
770 | daemon(0, 0); | ||
771 | #endif /* uClinux */ | ||
772 | |||
773 | if(opt & 1) { | ||
774 | global_queuelen = atoi(sq); | ||
775 | if (global_queuelen < 8) global_queuelen=8; | ||
776 | } | ||
777 | argc -= optind; | ||
778 | argv += optind; | ||
779 | |||
780 | if (argc > 0) | ||
781 | CONFIG = argv[0]; | ||
782 | |||
783 | openlog(bb_applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON); | ||
784 | { | ||
785 | FILE *fp; | ||
786 | |||
787 | if ((fp = fopen(_PATH_INETDPID, "w")) != NULL) { | ||
788 | fprintf(fp, "%u\n", getpid()); | ||
789 | (void)fclose(fp); | ||
790 | } | ||
791 | } | ||
792 | |||
793 | #ifdef RLIMIT_NOFILE | ||
794 | if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { | ||
795 | syslog(LOG_ERR, "getrlimit: %m"); | ||
796 | } else { | ||
797 | rlim_ofile_cur = rlim_ofile.rlim_cur; | ||
798 | if (rlim_ofile_cur == RLIM_INFINITY) /* ! */ | ||
799 | rlim_ofile_cur = OPEN_MAX; | ||
800 | } | ||
801 | #endif | ||
802 | |||
803 | config(0); | ||
804 | |||
805 | sigemptyset(&emptymask); | ||
806 | sigemptyset(&blockmask); | ||
807 | sigaddset(&blockmask, SIGCHLD); | ||
808 | sigaddset(&blockmask, SIGHUP); | ||
809 | sigaddset(&blockmask, SIGALRM); | ||
810 | |||
811 | memset(&sa, 0, sizeof(sa)); | ||
812 | sa.sa_mask = blockmask; | ||
813 | sa.sa_handler = retry; | ||
814 | sigaction(SIGALRM, &sa, NULL); | ||
815 | sa.sa_handler = config; | ||
816 | sigaction(SIGHUP, &sa, NULL); | ||
817 | sa.sa_handler = reapchild; | ||
818 | sigaction(SIGCHLD, &sa, NULL); | ||
819 | sa.sa_handler = goaway; | ||
820 | sigaction(SIGTERM, &sa, NULL); | ||
821 | sa.sa_handler = goaway; | ||
822 | sigaction(SIGINT, &sa, NULL); | ||
823 | sa.sa_handler = SIG_IGN; | ||
824 | sigaction(SIGPIPE, &sa, NULL); | ||
825 | |||
826 | { | ||
827 | /* space for daemons to overwrite environment for ps */ | ||
828 | #define DUMMYSIZE 100 | ||
829 | char dummy[DUMMYSIZE]; | ||
830 | |||
831 | (void)memset(dummy, 'x', DUMMYSIZE - 1); | ||
832 | dummy[DUMMYSIZE - 1] = '\0'; | ||
833 | |||
834 | (void)setenv("inetd_dummy", dummy, 1); | ||
835 | } | ||
836 | |||
837 | for (;;) { | ||
838 | fd_set readable; | ||
839 | int ctrl; | ||
840 | int n; | ||
841 | |||
842 | if (nsock == 0) { | ||
843 | sigprocmask(SIG_BLOCK, &blockmask, NULL); | ||
844 | while (nsock == 0) { | ||
845 | sigsuspend(&emptymask); | ||
846 | } | ||
847 | sigprocmask(SIG_SETMASK, &emptymask, NULL); | ||
848 | } | ||
849 | readable = allsock; | ||
850 | n = select(maxsock + 1, &readable, (fd_set *)0, (fd_set *)0, (struct timeval *)0); | ||
851 | if (n <= 0) { | ||
852 | if (n < 0 && errno != EINTR) { | ||
853 | syslog(LOG_WARNING, "select: %m"); | ||
854 | } | ||
855 | sleep(1); | ||
856 | continue; | ||
857 | } | ||
858 | for (sep = servtab; n && sep; sep = sep->se_next) { | ||
859 | if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { | ||
860 | n--; | ||
861 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { | ||
862 | /* Fixed AGC */ | ||
863 | fcntl(sep->se_fd, F_SETFL, O_NDELAY); | ||
864 | /* --------- */ | ||
865 | ctrl = accept(sep->se_fd, NULL, NULL); | ||
866 | fcntl(sep->se_fd, F_SETFL, 0); | ||
867 | if (ctrl < 0) { | ||
868 | if (errno == EINTR || errno == EWOULDBLOCK) { | ||
869 | continue; | ||
870 | } | ||
871 | syslog(LOG_WARNING, "accept (for %s): %m", | ||
872 | sep->se_service); | ||
873 | continue; | ||
874 | } | ||
875 | } else { | ||
876 | ctrl = sep->se_fd; | ||
877 | } | ||
878 | sigprocmask(SIG_BLOCK, &blockmask, NULL); | ||
879 | pid = 0; | ||
880 | #ifdef INETD_FEATURE_ENABLED | ||
881 | if (sep->se_bi == 0 || sep->se_bi->bi_fork) | ||
882 | #endif | ||
883 | { | ||
884 | if (sep->se_count++ == 0) { | ||
885 | gettimeofday(&sep->se_time, (struct timezone *)0); | ||
886 | } | ||
887 | else if (sep->se_count >= sep->se_max) { | ||
888 | struct timeval now; | ||
889 | |||
890 | gettimeofday(&now, (struct timezone *)0); | ||
891 | if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { | ||
892 | sep->se_time = now; | ||
893 | sep->se_count = 1; | ||
894 | } else { | ||
895 | syslog(LOG_ERR, | ||
896 | "%s/%s server failing (looping), service terminated", | ||
897 | sep->se_service, sep->se_proto); | ||
898 | FD_CLR(sep->se_fd, &allsock); | ||
899 | close(sep->se_fd); | ||
900 | sep->se_fd = -1; | ||
901 | sep->se_count = 0; | ||
902 | nsock--; | ||
903 | sigprocmask(SIG_SETMASK, &emptymask, NULL); | ||
904 | if (!timingout) { | ||
905 | timingout = 1; | ||
906 | alarm(RETRYTIME); | ||
907 | } | ||
908 | continue; | ||
909 | } | ||
910 | } | ||
911 | pid = fork(); | ||
912 | if (pid < 0) { | ||
913 | syslog(LOG_ERR, "fork: %m"); | ||
914 | if (sep->se_socktype == SOCK_STREAM) { | ||
915 | close(ctrl); | ||
916 | } | ||
917 | sigprocmask(SIG_SETMASK, &emptymask, NULL); | ||
918 | sleep(1); | ||
919 | continue; | ||
920 | } | ||
921 | if (pid && sep->se_wait) { | ||
922 | sep->se_wait = pid; | ||
923 | FD_CLR(sep->se_fd, &allsock); | ||
924 | nsock--; | ||
925 | } | ||
926 | } | ||
927 | sigprocmask(SIG_SETMASK, &emptymask, NULL); | ||
928 | if (pid == 0) { | ||
929 | #ifdef INETD_FEATURE_ENABLED | ||
930 | if (sep->se_bi) { | ||
931 | (*sep->se_bi->bi_fn)(ctrl, sep); | ||
932 | } else | ||
933 | #endif | ||
934 | { | ||
935 | struct passwd *pwd = getpwnam(sep->se_user); | ||
936 | if (pwd == NULL) { | ||
937 | syslog_err_and_discard_dg( | ||
938 | sep->se_socktype, | ||
939 | "getpwnam: %s: No such user", | ||
940 | sep->se_user); | ||
941 | } | ||
942 | if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) { | ||
943 | syslog_err_and_discard_dg(sep->se_socktype, | ||
944 | "getgrnam: %s: No such group", sep->se_group); | ||
945 | } | ||
946 | /* | ||
947 | * Ok. There are four cases here: | ||
948 | * 1. nonroot user, no group specified | ||
949 | * 2. nonroot user, some group specified | ||
950 | * 3. root user, no group specified | ||
951 | * 4. root user, some group specified | ||
952 | * In cases 2 and 4 we setgid to the specified | ||
953 | * group. In cases 1 and 2 we run initgroups | ||
954 | * to run with the groups of the given user. | ||
955 | * In case 4 we do setgroups to run with the | ||
956 | * given group. In case 3 we do nothing. | ||
957 | */ | ||
958 | if (pwd->pw_uid) { | ||
959 | if (sep->se_group) { | ||
960 | pwd->pw_gid = grp->gr_gid; | ||
961 | } | ||
962 | setgid((gid_t)pwd->pw_gid); | ||
963 | initgroups(pwd->pw_name, pwd->pw_gid); | ||
964 | setuid((uid_t)pwd->pw_uid); | ||
965 | } else if (sep->se_group) { | ||
966 | setgid((gid_t)grp->gr_gid); | ||
967 | setgroups(1, &grp->gr_gid); | ||
968 | } | ||
969 | dup2(ctrl, 0); | ||
970 | close(ctrl); | ||
971 | dup2(0, 1); | ||
972 | dup2(0, 2); | ||
973 | #ifdef RLIMIT_NOFILE | ||
974 | if (rlim_ofile.rlim_cur != rlim_ofile_cur) { | ||
975 | if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { | ||
976 | syslog(LOG_ERR,"setrlimit: %m"); | ||
977 | } | ||
978 | } | ||
979 | #endif | ||
980 | for (ctrl = rlim_ofile_cur-1; --ctrl > 2; ) { | ||
981 | (void)close(ctrl); | ||
982 | } | ||
983 | memset(&sa, 0, sizeof(sa)); | ||
984 | sa.sa_handler = SIG_DFL; | ||
985 | sigaction(SIGPIPE, &sa, NULL); | ||
986 | |||
987 | execv(sep->se_server, sep->se_argv); | ||
988 | syslog_err_and_discard_dg(sep->se_socktype, "execv %s: %m", sep->se_server); | ||
989 | } | ||
990 | } | ||
991 | if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { | ||
992 | close(ctrl); | ||
993 | } | ||
994 | } | ||
995 | } | ||
996 | } | ||
997 | } | ||
998 | |||
999 | |||
1000 | /* | ||
1001 | * Internet services provided internally by inetd: | ||
1002 | */ | ||
1003 | #define BUFSIZE 4096 | ||
1004 | |||
1005 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO | ||
1006 | /* Echo service -- echo data back */ | ||
1007 | static void echo_stream(int s, servtab_t *sep) | ||
1008 | { | ||
1009 | char buffer[BUFSIZE]; | ||
1010 | int i; | ||
1011 | |||
1012 | setproctitle(sep->se_service, s); | ||
1013 | while ((i = read(s, buffer, sizeof(buffer))) > 0 && | ||
1014 | write(s, buffer, i) > 0) | ||
1015 | ; | ||
1016 | exit(0); | ||
1017 | } | ||
1018 | |||
1019 | /* Echo service -- echo data back */ | ||
1020 | static void echo_dg(int s, servtab_t *sep) | ||
1021 | { | ||
1022 | char buffer[BUFSIZE]; | ||
1023 | int i; | ||
1024 | size_t size; | ||
1025 | struct sockaddr sa; | ||
1026 | |||
1027 | (void)sep; | ||
1028 | |||
1029 | size = sizeof(sa); | ||
1030 | if ((i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size)) < 0) | ||
1031 | return; | ||
1032 | (void) sendto(s, buffer, i, 0, &sa, sizeof(sa)); | ||
1033 | } | ||
1034 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_ECHO */ | ||
1035 | |||
1036 | |||
1037 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD | ||
1038 | /* Discard service -- ignore data */ | ||
1039 | static void discard_stream(int s, servtab_t *sep) | ||
1040 | { | ||
1041 | char buffer[BUFSIZE]; | ||
1042 | |||
1043 | setproctitle(sep->se_service, s); | ||
1044 | while ((errno = 0, read(s, buffer, sizeof(buffer)) > 0) || | ||
1045 | errno == EINTR) | ||
1046 | ; | ||
1047 | exit(0); | ||
1048 | } | ||
1049 | |||
1050 | /* Discard service -- ignore data */ | ||
1051 | static void discard_dg(int s, servtab_t *sep) | ||
1052 | { | ||
1053 | char buffer[BUFSIZE]; | ||
1054 | (void)sep; | ||
1055 | read(s, buffer, sizeof(buffer)); | ||
1056 | } | ||
1057 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DISCARD */ | ||
1058 | |||
1059 | |||
1060 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN | ||
1061 | #include <ctype.h> | ||
1062 | #define LINESIZ 72 | ||
1063 | static char ring[128]; | ||
1064 | static char *endring; | ||
1065 | |||
1066 | static void initring(void) | ||
1067 | { | ||
1068 | int i; | ||
1069 | |||
1070 | endring = ring; | ||
1071 | |||
1072 | for (i = 0; i <= 128; ++i) | ||
1073 | if (isprint(i)) | ||
1074 | *endring++ = i; | ||
1075 | } | ||
1076 | |||
1077 | /* Character generator */ | ||
1078 | static void chargen_stream(int s, servtab_t *sep) | ||
1079 | { | ||
1080 | char *rs; | ||
1081 | int len; | ||
1082 | char text[LINESIZ+2]; | ||
1083 | |||
1084 | setproctitle(sep->se_service, s); | ||
1085 | |||
1086 | if (!endring) { | ||
1087 | initring(); | ||
1088 | rs = ring; | ||
1089 | } | ||
1090 | |||
1091 | text[LINESIZ] = '\r'; | ||
1092 | text[LINESIZ + 1] = '\n'; | ||
1093 | for (rs = ring;;) { | ||
1094 | if ((len = endring - rs) >= LINESIZ) | ||
1095 | memcpy(rs, text, LINESIZ); | ||
1096 | else { | ||
1097 | memcpy(rs, text, len); | ||
1098 | memcpy(ring, text + len, LINESIZ - len); | ||
1099 | } | ||
1100 | if (++rs == endring) | ||
1101 | rs = ring; | ||
1102 | if (write(s, text, sizeof(text)) != sizeof(text)) | ||
1103 | break; | ||
1104 | } | ||
1105 | exit(0); | ||
1106 | } | ||
1107 | |||
1108 | /* Character generator */ | ||
1109 | static void chargen_dg(int s, servtab_t *sep) | ||
1110 | { | ||
1111 | struct sockaddr sa; | ||
1112 | static char *rs; | ||
1113 | size_t len, size; | ||
1114 | char text[LINESIZ+2]; | ||
1115 | |||
1116 | (void)sep; | ||
1117 | |||
1118 | if (endring == 0) { | ||
1119 | initring(); | ||
1120 | rs = ring; | ||
1121 | } | ||
1122 | |||
1123 | size = sizeof(sa); | ||
1124 | if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0) | ||
1125 | return; | ||
1126 | |||
1127 | if ((len = endring - rs) >= LINESIZ) | ||
1128 | memcpy(rs, text, LINESIZ); | ||
1129 | else { | ||
1130 | memcpy(rs, text, len); | ||
1131 | memcpy(ring, text + len, LINESIZ - len); | ||
1132 | } | ||
1133 | if (++rs == endring) | ||
1134 | rs = ring; | ||
1135 | text[LINESIZ] = '\r'; | ||
1136 | text[LINESIZ + 1] = '\n'; | ||
1137 | (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa)); | ||
1138 | } | ||
1139 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_CHARGEN */ | ||
1140 | |||
1141 | |||
1142 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME | ||
1143 | /* | ||
1144 | * Return a machine readable date and time, in the form of the | ||
1145 | * number of seconds since midnight, Jan 1, 1900. Since gettimeofday | ||
1146 | * returns the number of seconds since midnight, Jan 1, 1970, | ||
1147 | * we must add 2208988800 seconds to this figure to make up for | ||
1148 | * some seventy years Bell Labs was asleep. | ||
1149 | */ | ||
1150 | |||
1151 | static long machtime(void) | ||
1152 | { | ||
1153 | struct timeval tv; | ||
1154 | |||
1155 | if (gettimeofday(&tv, (struct timezone *)0) < 0) { | ||
1156 | fprintf(stderr, "Unable to get time of day\n"); | ||
1157 | return (0L); | ||
1158 | } | ||
1159 | return (htonl((long)tv.tv_sec + 2208988800UL)); | ||
1160 | } | ||
1161 | |||
1162 | static void machtime_stream(int s, servtab_t *sep) | ||
1163 | { | ||
1164 | long result; | ||
1165 | (void)sep; | ||
1166 | |||
1167 | result = machtime(); | ||
1168 | write(s, (char *) &result, sizeof(result)); | ||
1169 | } | ||
1170 | |||
1171 | static void machtime_dg(int s, servtab_t *sep) | ||
1172 | { | ||
1173 | long result; | ||
1174 | struct sockaddr sa; | ||
1175 | size_t size; | ||
1176 | (void)sep; | ||
1177 | |||
1178 | size = sizeof(sa); | ||
1179 | if (recvfrom(s, (char *)&result, sizeof(result), 0, &sa, &size) < 0) | ||
1180 | return; | ||
1181 | result = machtime(); | ||
1182 | (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa)); | ||
1183 | } | ||
1184 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_TIME */ | ||
1185 | |||
1186 | |||
1187 | #ifdef CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME | ||
1188 | /* Return human-readable time of day */ | ||
1189 | static int human_readable_time_sprintf(char *buffer) | ||
1190 | { | ||
1191 | time_t clocc = time(NULL); | ||
1192 | |||
1193 | return sprintf(buffer, "%.24s\r\n", ctime(&clocc)); | ||
1194 | } | ||
1195 | |||
1196 | static void daytime_stream(int s, servtab_t *sep) | ||
1197 | { | ||
1198 | char buffer[256]; | ||
1199 | size_t st = human_readable_time_sprintf(buffer); | ||
1200 | |||
1201 | (void)sep; | ||
1202 | |||
1203 | write(s, buffer, st); | ||
1204 | } | ||
1205 | |||
1206 | /* Return human-readable time of day */ | ||
1207 | static void daytime_dg(int s, servtab_t *sep) | ||
1208 | { | ||
1209 | char buffer[256]; | ||
1210 | struct sockaddr sa; | ||
1211 | size_t size; | ||
1212 | |||
1213 | (void)sep; | ||
1214 | |||
1215 | size = sizeof(sa); | ||
1216 | if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0) | ||
1217 | return; | ||
1218 | size = human_readable_time_sprintf(buffer); | ||
1219 | sendto(s, buffer, size, 0, &sa, sizeof(sa)); | ||
1220 | } | ||
1221 | #endif /* CONFIG_FEATURE_INETD_SUPPORT_BILTIN_DAYTIME */ | ||