source: MondoRescue/branches/stable/mindi-busybox/networking/inetd.c@ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

File size: 46.4 KB
Line 
1/* vi: set sw=4 ts=4: */
2/* $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $ */
3/* $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $ */
4/* $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $ */
5/* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru> */
6/*
7 * Copyright (c) 1983,1991 The Regents of the University of California.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39/* Inetd - Internet super-server
40 *
41 * This program invokes all internet services as needed.
42 * connection-oriented services are invoked each time a
43 * connection is made, by creating a process. This process
44 * is passed the connection as file descriptor 0 and is
45 * expected to do a getpeername to find out the source host
46 * and port.
47 *
48 * Datagram oriented services are invoked when a datagram
49 * arrives; a process is created and passed a pending message
50 * on file descriptor 0. Datagram servers may either connect
51 * to their peer, freeing up the original socket for inetd
52 * to receive further messages on, or "take over the socket",
53 * processing all arriving datagrams and, eventually, timing
54 * out. The first type of server is said to be "multi-threaded";
55 * the second type of server "single-threaded".
56 *
57 * Inetd uses a configuration file which is read at startup
58 * and, possibly, at some later time in response to a hangup signal.
59 * The configuration file is "free format" with fields given in the
60 * order shown below. Continuation lines for an entry must begin with
61 * a space or tab. All fields must be present in each entry.
62 *
63 * service name must be in /etc/services
64 * socket type stream/dgram/raw/rdm/seqpacket
65 * protocol must be in /etc/protocols
66 * wait/nowait[.max] single-threaded/multi-threaded, max #
67 * user[.group] or user[:group] user/group to run daemon as
68 * server program full path name
69 * server program arguments maximum of MAXARGS (20)
70 *
71 * For RPC services
72 * service name/version must be in /etc/rpc
73 * socket type stream/dgram/raw/rdm/seqpacket
74 * protocol must be in /etc/protocols
75 * wait/nowait[.max] single-threaded/multi-threaded
76 * user[.group] or user[:group] user to run daemon as
77 * server program full path name
78 * server program arguments maximum of MAXARGS (20)
79 *
80 * For non-RPC services, the "service name" can be of the form
81 * hostaddress:servicename, in which case the hostaddress is used
82 * as the host portion of the address to listen on. If hostaddress
83 * consists of a single `*' character, INADDR_ANY is used.
84 *
85 * A line can also consist of just
86 * hostaddress:
87 * where hostaddress is as in the preceding paragraph. Such a line must
88 * have no further fields; the specified hostaddress is remembered and
89 * used for all further lines that have no hostaddress specified,
90 * until the next such line (or EOF). (This is why * is provided to
91 * allow explicit specification of INADDR_ANY.) A line
92 * *:
93 * is implicitly in effect at the beginning of the file.
94 *
95 * The hostaddress specifier may (and often will) contain dots;
96 * the service name must not.
97 *
98 * For RPC services, host-address specifiers are accepted and will
99 * work to some extent; however, because of limitations in the
100 * portmapper interface, it will not work to try to give more than
101 * one line for any given RPC service, even if the host-address
102 * specifiers are different.
103 *
104 * Comment lines are indicated by a `#' in column 1.
105 */
106
107/* inetd rules for passing file descriptors to children
108 * (http://www.freebsd.org/cgi/man.cgi?query=inetd):
109 *
110 * The wait/nowait entry specifies whether the server that is invoked by
111 * inetd will take over the socket associated with the service access point,
112 * and thus whether inetd should wait for the server to exit before listen-
113 * ing for new service requests. Datagram servers must use "wait", as
114 * they are always invoked with the original datagram socket bound to the
115 * specified service address. These servers must read at least one datagram
116 * from the socket before exiting. If a datagram server connects to its
117 * peer, freeing the socket so inetd can receive further messages on the
118 * socket, it is said to be a "multi-threaded" server; it should read one
119 * datagram from the socket and create a new socket connected to the peer.
120 * It should fork, and the parent should then exit to allow inetd to check
121 * for new service requests to spawn new servers. Datagram servers which
122 * process all incoming datagrams on a socket and eventually time out are
123 * said to be "single-threaded". The comsat(8), (biff(1)) and talkd(8)
124 * utilities are both examples of the latter type of datagram server. The
125 * tftpd(8) utility is an example of a multi-threaded datagram server.
126 *
127 * Servers using stream sockets generally are multi-threaded and use the
128 * "nowait" entry. Connection requests for these services are accepted by
129 * inetd, and the server is given only the newly-accepted socket connected
130 * to a client of the service. Most stream-based services operate in this
131 * manner. Stream-based servers that use "wait" are started with the lis-
132 * tening service socket, and must accept at least one connection request
133 * before exiting. Such a server would normally accept and process incoming
134 * connection requests until a timeout.
135 */
136
137/* Here's the scoop concerning the user[.:]group feature:
138 *
139 * 1) set-group-option off.
140 *
141 * a) user = root: NO setuid() or setgid() is done
142 *
143 * b) other: setgid(primary group as found in passwd)
144 * initgroups(name, primary group)
145 * setuid()
146 *
147 * 2) set-group-option on.
148 *
149 * a) user = root: setgid(specified group)
150 * NO initgroups()
151 * NO setuid()
152 *
153 * b) other: setgid(specified group)
154 * initgroups(name, specified group)
155 * setuid()
156 */
157
158#include "libbb.h"
159#include <syslog.h>
160#include <sys/un.h>
161
162//#define ENABLE_FEATURE_INETD_RPC 1
163//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO 1
164//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD 1
165//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME 1
166//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME 1
167//#define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 1
168//#define ENABLE_FEATURE_IPV6 1
169
170#if ENABLE_FEATURE_INETD_RPC
171#include <rpc/rpc.h>
172#include <rpc/pmap_clnt.h>
173#endif
174
175extern char **environ;
176
177
178#define _PATH_INETDPID "/var/run/inetd.pid"
179
180#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
181#define RETRYTIME (60*10) /* retry after bind or server fail */
182
183#ifndef RLIMIT_NOFILE
184#define RLIMIT_NOFILE RLIMIT_OFILE
185#endif
186
187#ifndef OPEN_MAX
188#define OPEN_MAX 64
189#endif
190
191/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
192#define FD_MARGIN 8
193static rlim_t rlim_ofile_cur = OPEN_MAX;
194static struct rlimit rlim_ofile;
195
196
197/* Check unsupporting builtin */
198#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
199 ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \
200 ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME || \
201 ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME || \
202 ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
203# define INETD_FEATURE_ENABLED
204#endif
205
206#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
207 ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD || \
208 ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
209# define INETD_SETPROCTITLE
210#endif
211
212typedef struct servtab {
213 char *se_hostaddr; /* host address to listen on */
214 char *se_service; /* name of service */
215 int se_socktype; /* type of socket to use */
216 int se_family; /* address family */
217 char *se_proto; /* protocol used */
218#if ENABLE_FEATURE_INETD_RPC
219 int se_rpcprog; /* rpc program number */
220 int se_rpcversl; /* rpc program lowest version */
221 int se_rpcversh; /* rpc program highest version */
222#define isrpcservice(sep) ((sep)->se_rpcversl != 0)
223#else
224#define isrpcservice(sep) 0
225#endif
226 pid_t se_wait; /* single threaded server */
227 short se_checked; /* looked at during merge */
228 char *se_user; /* user name to run as */
229 char *se_group; /* group name to run as */
230#ifdef INETD_FEATURE_ENABLED
231 const struct builtin *se_bi; /* if built-in, description */
232#endif
233 char *se_server; /* server program */
234#define MAXARGV 20
235 char *se_argv[MAXARGV + 1]; /* program arguments */
236 int se_fd; /* open descriptor */
237 union {
238 struct sockaddr se_un_ctrladdr;
239 struct sockaddr_in se_un_ctrladdr_in;
240#if ENABLE_FEATURE_IPV6
241 struct sockaddr_in6 se_un_ctrladdr_in6;
242#endif
243 struct sockaddr_un se_un_ctrladdr_un;
244 } se_un; /* bound address */
245#define se_ctrladdr se_un.se_un_ctrladdr
246#define se_ctrladdr_in se_un.se_un_ctrladdr_in
247#define se_ctrladdr_in6 se_un.se_un_ctrladdr_in6
248#define se_ctrladdr_un se_un.se_un_ctrladdr_un
249 int se_ctrladdr_size;
250 int se_max; /* max # of instances of this service */
251 int se_count; /* number started since se_time */
252 struct timeval se_time; /* start of se_count */
253 struct servtab *se_next;
254} servtab_t;
255
256static servtab_t *servtab;
257
258#ifdef INETD_FEATURE_ENABLED
259struct builtin {
260 const char *bi_service; /* internally provided service name */
261 int bi_socktype; /* type of socket supported */
262 short bi_fork; /* 1 if should fork before call */
263 short bi_wait; /* 1 if should wait for child */
264 void (*bi_fn) (int, servtab_t *);
265};
266
267 /* Echo received data */
268#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
269static void echo_stream(int, servtab_t *);
270static void echo_dg(int, servtab_t *);
271#endif
272 /* Internet /dev/null */
273#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
274static void discard_stream(int, servtab_t *);
275static void discard_dg(int, servtab_t *);
276#endif
277 /* Return 32 bit time since 1900 */
278#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
279static void machtime_stream(int, servtab_t *);
280static void machtime_dg(int, servtab_t *);
281#endif
282 /* Return human-readable time */
283#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
284static void daytime_stream(int, servtab_t *);
285static void daytime_dg(int, servtab_t *);
286#endif
287 /* Familiar character generator */
288#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
289static void chargen_stream(int, servtab_t *);
290static void chargen_dg(int, servtab_t *);
291#endif
292
293static const struct builtin builtins[] = {
294#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
295 /* Echo received data */
296 {"echo", SOCK_STREAM, 1, 0, echo_stream,},
297 {"echo", SOCK_DGRAM, 0, 0, echo_dg,},
298#endif
299#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
300 /* Internet /dev/null */
301 {"discard", SOCK_STREAM, 1, 0, discard_stream,},
302 {"discard", SOCK_DGRAM, 0, 0, discard_dg,},
303#endif
304#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
305 /* Return 32 bit time since 1900 */
306 {"time", SOCK_STREAM, 0, 0, machtime_stream,},
307 {"time", SOCK_DGRAM, 0, 0, machtime_dg,},
308#endif
309#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
310 /* Return human-readable time */
311 {"daytime", SOCK_STREAM, 0, 0, daytime_stream,},
312 {"daytime", SOCK_DGRAM, 0, 0, daytime_dg,},
313#endif
314#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
315 /* Familiar character generator */
316 {"chargen", SOCK_STREAM, 1, 0, chargen_stream,},
317 {"chargen", SOCK_DGRAM, 0, 0, chargen_dg,},
318#endif
319 {NULL, 0, 0, 0, NULL}
320};
321#endif /* INETD_FEATURE_ENABLED */
322
323static int global_queuelen = 128;
324static int nsock, maxsock;
325static fd_set allsock;
326static int toomany;
327static int timingout;
328static struct servent *sp;
329static uid_t uid;
330
331static const char *config_filename = "/etc/inetd.conf";
332
333static FILE *fconfig;
334static char *defhost;
335
336/* xstrdup(NULL) returns NULL, but this one
337 * will return newly-allocated "" if called with NULL arg
338 * TODO: audit whether this makes any real difference
339 */
340static char *xxstrdup(char *cp)
341{
342 return xstrdup(cp ? cp : "");
343}
344
345static int setconfig(void)
346{
347 free(defhost);
348 defhost = xstrdup("*");
349 if (fconfig != NULL) {
350 fseek(fconfig, 0L, SEEK_SET);
351 return 1;
352 }
353 fconfig = fopen(config_filename, "r");
354 return (fconfig != NULL);
355}
356
357static void endconfig(void)
358{
359 if (fconfig) {
360 (void) fclose(fconfig);
361 fconfig = NULL;
362 }
363 free(defhost);
364 defhost = 0;
365}
366
367#if ENABLE_FEATURE_INETD_RPC
368static void register_rpc(servtab_t *sep)
369{
370 int n;
371 struct sockaddr_in ir_sin;
372 struct protoent *pp;
373 socklen_t size;
374
375 if ((pp = getprotobyname(sep->se_proto + 4)) == NULL) {
376 bb_perror_msg("%s: getproto", sep->se_proto);
377 return;
378 }
379 size = sizeof ir_sin;
380 if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {
381 bb_perror_msg("%s/%s: getsockname",
382 sep->se_service, sep->se_proto);
383 return;
384 }
385
386 for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
387 (void) pmap_unset(sep->se_rpcprog, n);
388 if (!pmap_set(sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port)))
389 bb_perror_msg("%s %s: pmap_set: %u %u %u %u",
390 sep->se_service, sep->se_proto,
391 sep->se_rpcprog, n, pp->p_proto, ntohs(ir_sin.sin_port));
392 }
393}
394
395static void unregister_rpc(servtab_t *sep)
396{
397 int n;
398
399 for (n = sep->se_rpcversl; n <= sep->se_rpcversh; n++) {
400 if (!pmap_unset(sep->se_rpcprog, n))
401 bb_error_msg("pmap_unset(%u, %u)", sep->se_rpcprog, n);
402 }
403}
404#endif /* FEATURE_INETD_RPC */
405
406static void freeconfig(servtab_t *cp)
407{
408 int i;
409
410 free(cp->se_hostaddr);
411 free(cp->se_service);
412 free(cp->se_proto);
413 free(cp->se_user);
414 free(cp->se_group);
415 free(cp->se_server);
416 for (i = 0; i < MAXARGV; i++)
417 free(cp->se_argv[i]);
418}
419
420static int bump_nofile(void)
421{
422#define FD_CHUNK 32
423
424 struct rlimit rl;
425
426 if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
427 bb_perror_msg("getrlimit");
428 return -1;
429 }
430 rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
431 rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
432 if (rl.rlim_cur <= rlim_ofile_cur) {
433 bb_error_msg("bump_nofile: cannot extend file limit, max = %d",
434 (int) rl.rlim_cur);
435 return -1;
436 }
437
438 if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
439 bb_perror_msg("setrlimit");
440 return -1;
441 }
442
443 rlim_ofile_cur = rl.rlim_cur;
444 return 0;
445}
446
447static void setup(servtab_t *sep)
448{
449 int r;
450
451 sep->se_fd = socket(sep->se_family, sep->se_socktype, 0);
452 if (sep->se_fd < 0) {
453 bb_perror_msg("%s/%s: socket", sep->se_service, sep->se_proto);
454 return;
455 }
456 setsockopt_reuseaddr(sep->se_fd);
457
458#if ENABLE_FEATURE_INETD_RPC
459 if (isrpcservice(sep)) {
460 struct passwd *pwd;
461
462 /*
463 * for RPC services, attempt to use a reserved port
464 * if they are going to be running as root.
465 *
466 * Also, zero out the port for all RPC services; let bind()
467 * find one.
468 */
469 sep->se_ctrladdr_in.sin_port = 0;
470 if (sep->se_user && (pwd = getpwnam(sep->se_user)) &&
471 pwd->pw_uid == 0 && uid == 0)
472 r = bindresvport(sep->se_fd, &sep->se_ctrladdr_in);
473 else {
474 r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);
475 if (r == 0) {
476 socklen_t len = sep->se_ctrladdr_size;
477 int saveerrno = errno;
478
479 /* update se_ctrladdr_in.sin_port */
480 r = getsockname(sep->se_fd, &sep->se_ctrladdr, &len);
481 if (r <= 0)
482 errno = saveerrno;
483 }
484 }
485 } else
486#endif
487 r = bind(sep->se_fd, &sep->se_ctrladdr, sep->se_ctrladdr_size);
488 if (r < 0) {
489 bb_perror_msg("%s/%s (%d): bind",
490 sep->se_service, sep->se_proto, sep->se_ctrladdr.sa_family);
491 close(sep->se_fd);
492 sep->se_fd = -1;
493 if (!timingout) {
494 timingout = 1;
495 alarm(RETRYTIME);
496 }
497 return;
498 }
499 if (sep->se_socktype == SOCK_STREAM)
500 listen(sep->se_fd, global_queuelen);
501
502 FD_SET(sep->se_fd, &allsock);
503 nsock++;
504 if (sep->se_fd > maxsock) {
505 maxsock = sep->se_fd;
506 if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
507 bump_nofile();
508 }
509}
510
511static char *nextline(void)
512{
513#define line bb_common_bufsiz1
514
515 char *cp;
516 FILE *fd = fconfig;
517
518 if (fgets(line, sizeof(line), fd) == NULL)
519 return NULL;
520 cp = strchr(line, '\n');
521 if (cp)
522 *cp = '\0';
523 return line;
524}
525
526static char *skip(char **cpp) /* int report; */
527{
528 char *cp = *cpp;
529 char *start;
530
531/* erp: */
532 if (*cpp == NULL) {
533 /* if (report) */
534 /* bb_error_msg("syntax error in inetd config file"); */
535 return NULL;
536 }
537
538 again:
539 while (*cp == ' ' || *cp == '\t')
540 cp++;
541 if (*cp == '\0') {
542 int c;
543
544 c = getc(fconfig);
545 ungetc(c, fconfig);
546 if (c == ' ' || c == '\t') {
547 cp = nextline();
548 if (cp)
549 goto again;
550 }
551 *cpp = NULL;
552 /* goto erp; */
553 return NULL;
554 }
555 start = cp;
556 while (*cp && *cp != ' ' && *cp != '\t')
557 cp++;
558 if (*cp != '\0')
559 *cp++ = '\0';
560 /* if ((*cpp = cp) == NULL) */
561 /* goto erp; */
562
563 *cpp = cp;
564 return start;
565}
566
567static servtab_t *new_servtab(void)
568{
569 return xmalloc(sizeof(servtab_t));
570}
571
572static servtab_t *dupconfig(servtab_t *sep)
573{
574 servtab_t *newtab;
575 int argc;
576
577 newtab = new_servtab();
578 memset(newtab, 0, sizeof(servtab_t));
579 newtab->se_service = xstrdup(sep->se_service);
580 newtab->se_socktype = sep->se_socktype;
581 newtab->se_family = sep->se_family;
582 newtab->se_proto = xstrdup(sep->se_proto);
583#if ENABLE_FEATURE_INETD_RPC
584 newtab->se_rpcprog = sep->se_rpcprog;
585 newtab->se_rpcversl = sep->se_rpcversl;
586 newtab->se_rpcversh = sep->se_rpcversh;
587#endif
588 newtab->se_wait = sep->se_wait;
589 newtab->se_user = xstrdup(sep->se_user);
590 newtab->se_group = xstrdup(sep->se_group);
591#ifdef INETD_FEATURE_ENABLED
592 newtab->se_bi = sep->se_bi;
593#endif
594 newtab->se_server = xstrdup(sep->se_server);
595
596 for (argc = 0; argc <= MAXARGV; argc++)
597 newtab->se_argv[argc] = xstrdup(sep->se_argv[argc]);
598 newtab->se_max = sep->se_max;
599
600 return newtab;
601}
602
603static servtab_t *getconfigent(void)
604{
605 servtab_t *sep;
606 int argc;
607 char *cp, *arg;
608 char *hostdelim;
609 servtab_t *nsep;
610 servtab_t *psep;
611
612 sep = new_servtab();
613
614 /* memset(sep, 0, sizeof *sep); */
615 more:
616 /* freeconfig(sep); */
617
618 while ((cp = nextline()) && *cp == '#') /* skip comment line */;
619 if (cp == NULL) {
620 /* free(sep); */
621 return NULL;
622 }
623
624 memset((char *) sep, 0, sizeof *sep);
625 arg = skip(&cp);
626 if (arg == NULL) {
627 /* A blank line. */
628 goto more;
629 }
630
631 /* Check for a host name. */
632 hostdelim = strrchr(arg, ':');
633 if (hostdelim) {
634 *hostdelim = '\0';
635 sep->se_hostaddr = xstrdup(arg);
636 arg = hostdelim + 1;
637 /*
638 * If the line is of the form `host:', then just change the
639 * default host for the following lines.
640 */
641 if (*arg == '\0') {
642 arg = skip(&cp);
643 if (cp == NULL) {
644 free(defhost);
645 defhost = sep->se_hostaddr;
646 goto more;
647 }
648 }
649 } else
650 sep->se_hostaddr = xxstrdup(defhost);
651
652 sep->se_service = xxstrdup(arg);
653 arg = skip(&cp);
654
655 if (strcmp(arg, "stream") == 0)
656 sep->se_socktype = SOCK_STREAM;
657 else if (strcmp(arg, "dgram") == 0)
658 sep->se_socktype = SOCK_DGRAM;
659 else if (strcmp(arg, "rdm") == 0)
660 sep->se_socktype = SOCK_RDM;
661 else if (strcmp(arg, "seqpacket") == 0)
662 sep->se_socktype = SOCK_SEQPACKET;
663 else if (strcmp(arg, "raw") == 0)
664 sep->se_socktype = SOCK_RAW;
665 else
666 sep->se_socktype = -1;
667
668 sep->se_proto = xxstrdup(skip(&cp));
669
670 if (strcmp(sep->se_proto, "unix") == 0) {
671 sep->se_family = AF_UNIX;
672 } else {
673 sep->se_family = AF_INET;
674 if (sep->se_proto[strlen(sep->se_proto) - 1] == '6')
675#if ENABLE_FEATURE_IPV6
676 sep->se_family = AF_INET6;
677#else
678 bb_error_msg("%s: IPV6 not supported", sep->se_proto);
679#endif
680 if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
681#if ENABLE_FEATURE_INETD_RPC
682 char *p, *ccp;
683 long l;
684
685 p = strchr(sep->se_service, '/');
686 if (p == 0) {
687 bb_error_msg("%s: no rpc version", sep->se_service);
688 goto more;
689 }
690 *p++ = '\0';
691 l = strtol(p, &ccp, 0);
692 if (ccp == p || l < 0 || l > INT_MAX) {
693 badafterall:
694 bb_error_msg("%s/%s: bad rpc version", sep->se_service, p);
695 goto more;
696 }
697 sep->se_rpcversl = sep->se_rpcversh = l;
698 if (*ccp == '-') {
699 p = ccp + 1;
700 l = strtol(p, &ccp, 0);
701 if (ccp == p || l < 0 || l > INT_MAX || l < sep->se_rpcversl || *ccp)
702 goto badafterall;
703 sep->se_rpcversh = l;
704 } else if (*ccp != '\0')
705 goto badafterall;
706#else
707 bb_error_msg("%s: rpc services not supported", sep->se_service);
708#endif
709 }
710 }
711 arg = skip(&cp);
712 if (arg == NULL)
713 goto more;
714
715 {
716 char *s = strchr(arg, '.');
717 if (s) {
718 *s++ = '\0';
719 sep->se_max = xatoi(s);
720 } else
721 sep->se_max = toomany;
722 }
723 sep->se_wait = strcmp(arg, "wait") == 0;
724 /* if ((arg = skip(&cp, 1)) == NULL) */
725 /* goto more; */
726 sep->se_user = xxstrdup(skip(&cp));
727 arg = strchr(sep->se_user, '.');
728 if (arg == NULL)
729 arg = strchr(sep->se_user, ':');
730 if (arg) {
731 *arg++ = '\0';
732 sep->se_group = xstrdup(arg);
733 }
734 /* if ((arg = skip(&cp, 1)) == NULL) */
735 /* goto more; */
736
737 sep->se_server = xxstrdup(skip(&cp));
738 if (strcmp(sep->se_server, "internal") == 0) {
739#ifdef INETD_FEATURE_ENABLED
740 const struct builtin *bi;
741
742 for (bi = builtins; bi->bi_service; bi++)
743 if (bi->bi_socktype == sep->se_socktype &&
744 strcmp(bi->bi_service, sep->se_service) == 0)
745 break;
746 if (bi->bi_service == 0) {
747 bb_error_msg("internal service %s unknown", sep->se_service);
748 goto more;
749 }
750 sep->se_bi = bi;
751 sep->se_wait = bi->bi_wait;
752#else
753 bb_perror_msg("internal service %s unknown", sep->se_service);
754 goto more;
755#endif
756 }
757#ifdef INETD_FEATURE_ENABLED
758 else
759 sep->se_bi = NULL;
760#endif
761 argc = 0;
762 for (arg = skip(&cp); cp; arg = skip(&cp)) {
763 if (argc < MAXARGV)
764 sep->se_argv[argc++] = xxstrdup(arg);
765 }
766 while (argc <= MAXARGV)
767 sep->se_argv[argc++] = NULL;
768
769 /*
770 * Now that we've processed the entire line, check if the hostname
771 * specifier was a comma separated list of hostnames. If so
772 * we'll make new entries for each address.
773 */
774 while ((hostdelim = strrchr(sep->se_hostaddr, ',')) != NULL) {
775 nsep = dupconfig(sep);
776
777 /*
778 * NULL terminate the hostname field of the existing entry,
779 * and make a dup for the new entry.
780 */
781 *hostdelim++ = '\0';
782 nsep->se_hostaddr = xstrdup(hostdelim);
783
784 nsep->se_next = sep->se_next;
785 sep->se_next = nsep;
786 }
787
788 nsep = sep;
789 while (nsep != NULL) {
790 nsep->se_checked = 1;
791 if (nsep->se_family == AF_INET) {
792 if (LONE_CHAR(nsep->se_hostaddr, '*'))
793 nsep->se_ctrladdr_in.sin_addr.s_addr = INADDR_ANY;
794 else if (!inet_aton(nsep->se_hostaddr, &nsep->se_ctrladdr_in.sin_addr)) {
795 struct hostent *hp;
796
797 hp = gethostbyname(nsep->se_hostaddr);
798 if (hp == 0) {
799 bb_error_msg("%s: unknown host", nsep->se_hostaddr);
800 nsep->se_checked = 0;
801 goto skip;
802 } else if (hp->h_addrtype != AF_INET) {
803 bb_error_msg("%s: address isn't an Internet "
804 "address", nsep->se_hostaddr);
805 nsep->se_checked = 0;
806 goto skip;
807 } else {
808 int i = 1;
809
810 memmove(&nsep->se_ctrladdr_in.sin_addr,
811 hp->h_addr_list[0], sizeof(struct in_addr));
812 while (hp->h_addr_list[i] != NULL) {
813 psep = dupconfig(nsep);
814 psep->se_hostaddr = xxstrdup(nsep->se_hostaddr);
815 psep->se_checked = 1;
816 memmove(&psep->se_ctrladdr_in.sin_addr,
817 hp->h_addr_list[i], sizeof(struct in_addr));
818 psep->se_ctrladdr_size = sizeof(psep->se_ctrladdr_in);
819 i++;
820 /* Prepend to list, don't want to look up */
821 /* its hostname again. */
822 psep->se_next = sep;
823 sep = psep;
824 }
825 }
826 }
827 }
828/* XXX BUG?: is this skip: label supposed to remain? */
829 skip:
830 nsep = nsep->se_next;
831 }
832
833 /*
834 * Finally, free any entries which failed the gethostbyname
835 * check.
836 */
837 psep = NULL;
838 nsep = sep;
839 while (nsep != NULL) {
840 servtab_t *tsep;
841
842 if (nsep->se_checked == 0) {
843 tsep = nsep;
844 if (psep == NULL) {
845 sep = nsep->se_next;
846 nsep = sep;
847 } else {
848 nsep = nsep->se_next;
849 psep->se_next = nsep;
850 }
851 freeconfig(tsep);
852 } else {
853 nsep->se_checked = 0;
854 psep = nsep;
855 nsep = nsep->se_next;
856 }
857 }
858
859 return sep;
860}
861
862#define Block_Using_Signals(m) do { \
863 sigemptyset(&m); \
864 sigaddset(&m, SIGCHLD); \
865 sigaddset(&m, SIGHUP); \
866 sigaddset(&m, SIGALRM); \
867 sigprocmask(SIG_BLOCK, &m, NULL); \
868} while (0)
869
870static servtab_t *enter(servtab_t *cp)
871{
872 servtab_t *sep;
873 sigset_t omask;
874
875 sep = new_servtab();
876 *sep = *cp;
877 sep->se_fd = -1;
878#if ENABLE_FEATURE_INETD_RPC
879 sep->se_rpcprog = -1;
880#endif
881 Block_Using_Signals(omask);
882 sep->se_next = servtab;
883 servtab = sep;
884 sigprocmask(SIG_UNBLOCK, &omask, NULL);
885 return sep;
886}
887
888static int matchconf(servtab_t *old, servtab_t *new)
889{
890 if (strcmp(old->se_service, new->se_service) != 0)
891 return 0;
892
893 if (strcmp(old->se_hostaddr, new->se_hostaddr) != 0)
894 return 0;
895
896 if (strcmp(old->se_proto, new->se_proto) != 0)
897 return 0;
898
899 /*
900 * If the new servtab is bound to a specific address, check that the
901 * old servtab is bound to the same entry. If the new service is not
902 * bound to a specific address then the check of se_hostaddr above
903 * is sufficient.
904 */
905
906 if (old->se_family == AF_INET && new->se_family == AF_INET &&
907 memcmp(&old->se_ctrladdr_in.sin_addr,
908 &new->se_ctrladdr_in.sin_addr,
909 sizeof(new->se_ctrladdr_in.sin_addr)) != 0)
910 return 0;
911
912#if ENABLE_FEATURE_IPV6
913 if (old->se_family == AF_INET6 && new->se_family == AF_INET6 &&
914 memcmp(&old->se_ctrladdr_in6.sin6_addr,
915 &new->se_ctrladdr_in6.sin6_addr,
916 sizeof(new->se_ctrladdr_in6.sin6_addr)) != 0)
917 return 0;
918#endif
919 return 1;
920}
921
922static void config(int sig ATTRIBUTE_UNUSED)
923{
924 servtab_t *sep, *cp, **sepp;
925 sigset_t omask;
926 size_t n;
927 char protoname[10];
928
929 if (!setconfig()) {
930 bb_perror_msg("%s", config_filename);
931 return;
932 }
933 for (sep = servtab; sep; sep = sep->se_next)
934 sep->se_checked = 0;
935 cp = getconfigent();
936 while (cp != NULL) {
937 for (sep = servtab; sep; sep = sep->se_next)
938 if (matchconf(sep, cp))
939 break;
940
941 if (sep != 0) {
942 int i;
943
944#define SWAP(type, a, b) do {type c=(type)a; a=(type)b; b=(type)c;} while (0)
945
946 Block_Using_Signals(omask);
947 /*
948 * sep->se_wait may be holding the pid of a daemon
949 * that we're waiting for. If so, don't overwrite
950 * it unless the config file explicitly says don't
951 * wait.
952 */
953 if (
954#ifdef INETD_FEATURE_ENABLED
955 cp->se_bi == 0 &&
956#endif
957 (sep->se_wait == 1 || cp->se_wait == 0))
958 sep->se_wait = cp->se_wait;
959 SWAP(int, cp->se_max, sep->se_max);
960 SWAP(char *, sep->se_user, cp->se_user);
961 SWAP(char *, sep->se_group, cp->se_group);
962 SWAP(char *, sep->se_server, cp->se_server);
963 for (i = 0; i < MAXARGV; i++)
964 SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
965#undef SWAP
966
967#if ENABLE_FEATURE_INETD_RPC
968 if (isrpcservice(sep))
969 unregister_rpc(sep);
970 sep->se_rpcversl = cp->se_rpcversl;
971 sep->se_rpcversh = cp->se_rpcversh;
972#endif
973 sigprocmask(SIG_UNBLOCK, &omask, NULL);
974 freeconfig(cp);
975 } else {
976 sep = enter(cp);
977 }
978 sep->se_checked = 1;
979
980 switch (sep->se_family) {
981 case AF_UNIX:
982 if (sep->se_fd != -1)
983 break;
984 (void) unlink(sep->se_service);
985 n = strlen(sep->se_service);
986 if (n > sizeof sep->se_ctrladdr_un.sun_path - 1)
987 n = sizeof sep->se_ctrladdr_un.sun_path - 1;
988 safe_strncpy(sep->se_ctrladdr_un.sun_path, sep->se_service, n + 1);
989 sep->se_ctrladdr_un.sun_family = AF_UNIX;
990 sep->se_ctrladdr_size = n + sizeof sep->se_ctrladdr_un.sun_family;
991 setup(sep);
992 break;
993 case AF_INET:
994 sep->se_ctrladdr_in.sin_family = AF_INET;
995 /* se_ctrladdr_in was set in getconfigent */
996 sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in;
997
998#if ENABLE_FEATURE_INETD_RPC
999 if (isrpcservice(sep)) {
1000 struct rpcent *rp;
1001 // FIXME: atoi_or_else(str, 0) would be handy here
1002 sep->se_rpcprog = atoi(sep->se_service);
1003 if (sep->se_rpcprog == 0) {
1004 rp = getrpcbyname(sep->se_service);
1005 if (rp == 0) {
1006 bb_error_msg("%s: unknown rpc service", sep->se_service);
1007 goto serv_unknown;
1008 }
1009 sep->se_rpcprog = rp->r_number;
1010 }
1011 if (sep->se_fd == -1)
1012 setup(sep);
1013 if (sep->se_fd != -1)
1014 register_rpc(sep);
1015 } else
1016#endif
1017 {
1018 uint16_t port = htons(atoi(sep->se_service));
1019 // FIXME: atoi_or_else(str, 0) would be handy here
1020 if (!port) {
1021 /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname));
1022 if (isdigit(protoname[strlen(protoname) - 1]))
1023 protoname[strlen(protoname) - 1] = '\0';
1024 sp = getservbyname(sep->se_service, protoname);
1025 if (sp == 0) {
1026 bb_error_msg("%s/%s: unknown service",
1027 sep->se_service, sep->se_proto);
1028 goto serv_unknown;
1029 }
1030 port = sp->s_port;
1031 }
1032 if (port != sep->se_ctrladdr_in.sin_port) {
1033 sep->se_ctrladdr_in.sin_port = port;
1034 if (sep->se_fd != -1) {
1035 FD_CLR(sep->se_fd, &allsock);
1036 nsock--;
1037 (void) close(sep->se_fd);
1038 }
1039 sep->se_fd = -1;
1040 }
1041 if (sep->se_fd == -1)
1042 setup(sep);
1043 }
1044 break;
1045#if ENABLE_FEATURE_IPV6
1046 case AF_INET6:
1047 sep->se_ctrladdr_in6.sin6_family = AF_INET6;
1048 /* se_ctrladdr_in was set in getconfigent */
1049 sep->se_ctrladdr_size = sizeof sep->se_ctrladdr_in6;
1050
1051#if ENABLE_FEATURE_INETD_RPC
1052 if (isrpcservice(sep)) {
1053 struct rpcent *rp;
1054
1055 sep->se_rpcprog = atoi(sep->se_service);
1056 if (sep->se_rpcprog == 0) {
1057 rp = getrpcbyname(sep->se_service);
1058 if (rp == 0) {
1059 bb_error_msg("%s: unknown rpc service", sep->se_service);
1060 goto serv_unknown;
1061 }
1062 sep->se_rpcprog = rp->r_number;
1063 }
1064 if (sep->se_fd == -1)
1065 setup(sep);
1066 if (sep->se_fd != -1)
1067 register_rpc(sep);
1068 } else
1069#endif
1070 {
1071 uint16_t port = htons(atoi(sep->se_service));
1072
1073 if (!port) {
1074 /*XXX*/ strncpy(protoname, sep->se_proto, sizeof(protoname));
1075 if (isdigit(protoname[strlen(protoname) - 1]))
1076 protoname[strlen(protoname) - 1] = '\0';
1077 sp = getservbyname(sep->se_service, protoname);
1078 if (sp == 0) {
1079 bb_error_msg("%s/%s: unknown service",
1080 sep->se_service, sep->se_proto);
1081 goto serv_unknown;
1082 }
1083 port = sp->s_port;
1084 }
1085 if (port != sep->se_ctrladdr_in6.sin6_port) {
1086 sep->se_ctrladdr_in6.sin6_port = port;
1087 if (sep->se_fd != -1) {
1088 FD_CLR(sep->se_fd, &allsock);
1089 nsock--;
1090 (void) close(sep->se_fd);
1091 }
1092 sep->se_fd = -1;
1093 }
1094 if (sep->se_fd == -1)
1095 setup(sep);
1096 }
1097 break;
1098#endif /* FEATURE_IPV6 */
1099 }
1100 serv_unknown:
1101 if (cp->se_next != NULL) {
1102 servtab_t *tmp = cp;
1103
1104 cp = cp->se_next;
1105 free(tmp);
1106 } else {
1107 free(cp);
1108 cp = getconfigent();
1109 }
1110 }
1111 endconfig();
1112 /*
1113 * Purge anything not looked at above.
1114 */
1115 Block_Using_Signals(omask);
1116 sepp = &servtab;
1117 while ((sep = *sepp)) {
1118 if (sep->se_checked) {
1119 sepp = &sep->se_next;
1120 continue;
1121 }
1122 *sepp = sep->se_next;
1123 if (sep->se_fd != -1) {
1124 FD_CLR(sep->se_fd, &allsock);
1125 nsock--;
1126 (void) close(sep->se_fd);
1127 }
1128#if ENABLE_FEATURE_INETD_RPC
1129 if (isrpcservice(sep))
1130 unregister_rpc(sep);
1131#endif
1132 if (sep->se_family == AF_UNIX)
1133 (void) unlink(sep->se_service);
1134 freeconfig(sep);
1135 free(sep);
1136 }
1137 sigprocmask(SIG_UNBLOCK, &omask, NULL);
1138}
1139
1140
1141static void reapchild(int sig ATTRIBUTE_UNUSED)
1142{
1143 pid_t pid;
1144 int save_errno = errno, status;
1145 servtab_t *sep;
1146
1147 for (;;) {
1148 pid = wait3(&status, WNOHANG, NULL);
1149 if (pid <= 0)
1150 break;
1151 for (sep = servtab; sep; sep = sep->se_next)
1152 if (sep->se_wait == pid) {
1153 if (WIFEXITED(status) && WEXITSTATUS(status))
1154 bb_error_msg("%s: exit status 0x%x",
1155 sep->se_server, WEXITSTATUS(status));
1156 else if (WIFSIGNALED(status))
1157 bb_error_msg("%s: exit signal 0x%x",
1158 sep->se_server, WTERMSIG(status));
1159 sep->se_wait = 1;
1160 FD_SET(sep->se_fd, &allsock);
1161 nsock++;
1162 }
1163 }
1164 errno = save_errno;
1165}
1166
1167static void retry(int sig ATTRIBUTE_UNUSED)
1168{
1169 servtab_t *sep;
1170
1171 timingout = 0;
1172 for (sep = servtab; sep; sep = sep->se_next) {
1173 if (sep->se_fd == -1) {
1174 switch (sep->se_family) {
1175 case AF_UNIX:
1176 case AF_INET:
1177#if ENABLE_FEATURE_IPV6
1178 case AF_INET6:
1179#endif
1180 setup(sep);
1181#if ENABLE_FEATURE_INETD_RPC
1182 if (sep->se_fd != -1 && isrpcservice(sep))
1183 register_rpc(sep);
1184#endif
1185 break;
1186 }
1187 }
1188 }
1189}
1190
1191static void goaway(int sig ATTRIBUTE_UNUSED)
1192{
1193 servtab_t *sep;
1194
1195 /* XXX signal race walking sep list */
1196 for (sep = servtab; sep; sep = sep->se_next) {
1197 if (sep->se_fd == -1)
1198 continue;
1199
1200 switch (sep->se_family) {
1201 case AF_UNIX:
1202 (void) unlink(sep->se_service);
1203 break;
1204 case AF_INET:
1205#if ENABLE_FEATURE_IPV6
1206 case AF_INET6:
1207#endif
1208#if ENABLE_FEATURE_INETD_RPC
1209 if (sep->se_wait == 1 && isrpcservice(sep))
1210 unregister_rpc(sep); /* XXX signal race */
1211#endif
1212 break;
1213 }
1214 (void) close(sep->se_fd);
1215 }
1216 remove_pidfile(_PATH_INETDPID);
1217 exit(0);
1218}
1219
1220
1221#ifdef INETD_SETPROCTITLE
1222static char **Argv;
1223static char *LastArg;
1224
1225static void
1226inetd_setproctitle(char *a, int s)
1227{
1228 socklen_t size;
1229 char *cp;
1230 struct sockaddr_in prt_sin;
1231 char buf[80];
1232
1233 cp = Argv[0];
1234 size = sizeof(prt_sin);
1235 (void) snprintf(buf, sizeof buf, "-%s", a);
1236 if (getpeername(s, (struct sockaddr *) &prt_sin, &size) == 0) {
1237 char *sa = inet_ntoa(prt_sin.sin_addr);
1238
1239 buf[sizeof(buf) - 1 - strlen(sa) - 3] = '\0';
1240 strcat(buf, " [");
1241 strcat(buf, sa);
1242 strcat(buf, "]");
1243 }
1244 strncpy(cp, buf, LastArg - cp);
1245 cp += strlen(cp);
1246 while (cp < LastArg)
1247 *cp++ = ' ';
1248}
1249#endif
1250
1251
1252int inetd_main(int argc, char **argv);
1253int inetd_main(int argc, char **argv)
1254{
1255 servtab_t *sep;
1256 struct passwd *pwd;
1257 struct group *grp = NULL;
1258 int tmpint;
1259 struct sigaction sa, sapipe;
1260 int opt;
1261 pid_t pid;
1262 char buf[50];
1263 char *stoomany;
1264 sigset_t omask, wait_mask;
1265
1266#ifdef INETD_SETPROCTITLE
1267 char **envp = environ;
1268
1269 Argv = argv;
1270 if (envp == 0 || *envp == 0)
1271 envp = argv;
1272 while (*envp)
1273 envp++;
1274 LastArg = envp[-1] + strlen(envp[-1]);
1275#endif
1276
1277 uid = getuid();
1278 if (uid != 0)
1279 config_filename = NULL;
1280
1281 opt = getopt32(argv, "R:f", &stoomany);
1282 if (opt & 1)
1283 toomany = xatoi_u(stoomany);
1284 argv += optind;
1285 argc -= optind;
1286 if (argc)
1287 config_filename = argv[0];
1288 if (config_filename == NULL)
1289 bb_error_msg_and_die("non-root must specify a config file");
1290
1291 if (!(opt & 2))
1292 bb_daemonize_or_rexec(0, argv - optind);
1293 else
1294 bb_sanitize_stdio();
1295 openlog(applet_name, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
1296 logmode = LOGMODE_SYSLOG;
1297
1298 if (uid == 0) {
1299 /* If run by hand, ensure groups vector gets trashed */
1300 gid_t gid = getgid();
1301 setgroups(1, &gid);
1302 }
1303
1304 write_pidfile(_PATH_INETDPID);
1305
1306 if (getrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) {
1307 bb_perror_msg("getrlimit");
1308 } else {
1309 rlim_ofile_cur = rlim_ofile.rlim_cur;
1310 if (rlim_ofile_cur == RLIM_INFINITY) /* ! */
1311 rlim_ofile_cur = OPEN_MAX;
1312 }
1313
1314 memset((char *) &sa, 0, sizeof(sa));
1315 sigemptyset(&sa.sa_mask);
1316 sigaddset(&sa.sa_mask, SIGALRM);
1317 sigaddset(&sa.sa_mask, SIGCHLD);
1318 sigaddset(&sa.sa_mask, SIGHUP);
1319 sa.sa_handler = retry;
1320 sigaction(SIGALRM, &sa, NULL);
1321 config(SIGHUP);
1322 sa.sa_handler = config;
1323 sigaction(SIGHUP, &sa, NULL);
1324 sa.sa_handler = reapchild;
1325 sigaction(SIGCHLD, &sa, NULL);
1326 sa.sa_handler = goaway;
1327 sigaction(SIGTERM, &sa, NULL);
1328 sa.sa_handler = goaway;
1329 sigaction(SIGINT, &sa, NULL);
1330 sa.sa_handler = SIG_IGN;
1331 sigaction(SIGPIPE, &sa, &sapipe);
1332 memset(&wait_mask, 0, sizeof(wait_mask));
1333 {
1334 /* space for daemons to overwrite environment for ps */
1335#define DUMMYSIZE 100
1336 char dummy[DUMMYSIZE];
1337
1338 (void) memset(dummy, 'x', DUMMYSIZE - 1);
1339 dummy[DUMMYSIZE - 1] = '\0';
1340
1341 (void) setenv("inetd_dummy", dummy, 1);
1342 }
1343
1344 for (;;) {
1345 int n, ctrl = -1;
1346 fd_set readable;
1347
1348 if (nsock == 0) {
1349 Block_Using_Signals(omask);
1350 while (nsock == 0)
1351 sigsuspend(&wait_mask);
1352 sigprocmask(SIG_UNBLOCK, &omask, NULL);
1353 }
1354
1355 readable = allsock;
1356 n = select(maxsock + 1, &readable, NULL, NULL, NULL);
1357 if (n <= 0) {
1358 if (n < 0 && errno != EINTR) {
1359 bb_perror_msg("select");
1360 sleep(1);
1361 }
1362 continue;
1363 }
1364
1365 for (sep = servtab; n && sep; sep = sep->se_next) {
1366 if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
1367 continue;
1368
1369 n--;
1370 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) {
1371 ctrl = accept(sep->se_fd, NULL, NULL);
1372 if (ctrl < 0) {
1373 if (errno == EINTR)
1374 continue;
1375 bb_perror_msg("accept (for %s)", sep->se_service);
1376 continue;
1377 }
1378 if (sep->se_family == AF_INET && sep->se_socktype == SOCK_STREAM) {
1379 struct sockaddr_in peer;
1380 socklen_t plen = sizeof(peer);
1381
1382 if (getpeername(ctrl, (struct sockaddr *) &peer, &plen) < 0) {
1383 bb_error_msg("cannot getpeername");
1384 close(ctrl);
1385 continue;
1386 }
1387 if (ntohs(peer.sin_port) == 20) {
1388 /* XXX ftp bounce */
1389 close(ctrl);
1390 continue;
1391 }
1392 }
1393 } else
1394 ctrl = sep->se_fd;
1395
1396 Block_Using_Signals(omask);
1397 pid = 0;
1398#ifdef INETD_FEATURE_ENABLED
1399 if (sep->se_bi == 0 || sep->se_bi->bi_fork)
1400#endif
1401 {
1402 if (sep->se_count++ == 0)
1403 (void) gettimeofday(&sep->se_time, NULL);
1404 else if (toomany > 0 && sep->se_count >= sep->se_max) {
1405 struct timeval now;
1406
1407 (void) gettimeofday(&now, NULL);
1408 if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) {
1409 sep->se_time = now;
1410 sep->se_count = 1;
1411 } else {
1412 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
1413 close(ctrl);
1414 if (sep->se_family == AF_INET &&
1415 ntohs(sep->se_ctrladdr_in.sin_port) >= IPPORT_RESERVED) {
1416 /*
1417 * Cannot close it -- there are
1418 * thieves on the system.
1419 * Simply ignore the connection.
1420 */
1421 --sep->se_count;
1422 continue;
1423 }
1424 bb_error_msg("%s/%s server failing (looping), service terminated",
1425 sep->se_service, sep->se_proto);
1426 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
1427 close(ctrl);
1428 FD_CLR(sep->se_fd, &allsock);
1429 (void) close(sep->se_fd);
1430 sep->se_fd = -1;
1431 sep->se_count = 0;
1432 nsock--;
1433 sigprocmask(SIG_UNBLOCK, &omask, NULL);
1434 if (!timingout) {
1435 timingout = 1;
1436 alarm(RETRYTIME);
1437 }
1438 continue;
1439 }
1440 }
1441 pid = fork();
1442 }
1443 if (pid < 0) {
1444 bb_perror_msg("fork");
1445 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
1446 close(ctrl);
1447 sigprocmask(SIG_UNBLOCK, &omask, NULL);
1448 sleep(1);
1449 continue;
1450 }
1451 if (pid && sep->se_wait) {
1452 sep->se_wait = pid;
1453 FD_CLR(sep->se_fd, &allsock);
1454 nsock--;
1455 }
1456 sigprocmask(SIG_UNBLOCK, &omask, NULL);
1457 if (pid == 0) {
1458#ifdef INETD_FEATURE_ENABLED
1459 if (sep->se_bi) {
1460 (*sep->se_bi->bi_fn)(ctrl, sep);
1461 } else
1462#endif
1463 {
1464 pwd = getpwnam(sep->se_user);
1465 if (pwd == NULL) {
1466 bb_error_msg("getpwnam: %s: no such user", sep->se_user);
1467 goto do_exit1;
1468 }
1469 if (setsid() < 0)
1470 bb_perror_msg("%s: setsid", sep->se_service);
1471 if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
1472 bb_error_msg("getgrnam: %s: no such group", sep->se_group);
1473 goto do_exit1;
1474 }
1475 if (uid != 0) {
1476 /* a user running private inetd */
1477 if (uid != pwd->pw_uid)
1478 _exit(1);
1479 } else if (pwd->pw_uid) {
1480 if (sep->se_group)
1481 pwd->pw_gid = grp->gr_gid;
1482 xsetgid((gid_t) pwd->pw_gid);
1483 initgroups(pwd->pw_name, pwd->pw_gid);
1484 xsetuid((uid_t) pwd->pw_uid);
1485 } else if (sep->se_group) {
1486 xsetgid(grp->gr_gid);
1487 setgroups(1, &grp->gr_gid);
1488 }
1489 dup2(ctrl, 0);
1490 if (ctrl) close(ctrl);
1491 dup2(0, 1);
1492 dup2(0, 2);
1493 if (rlim_ofile.rlim_cur != rlim_ofile_cur)
1494 if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
1495 bb_perror_msg("setrlimit");
1496 closelog();
1497 for (tmpint = rlim_ofile_cur - 1; --tmpint > 2;)
1498 (void) close(tmpint);
1499 sigaction(SIGPIPE, &sapipe, NULL);
1500 execv(sep->se_server, sep->se_argv);
1501 bb_perror_msg("execv %s", sep->se_server);
1502 do_exit1:
1503 if (sep->se_socktype != SOCK_STREAM)
1504 recv(0, buf, sizeof(buf), 0);
1505 _exit(1);
1506 }
1507 }
1508 if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
1509 close(ctrl);
1510 } /* for (sep = servtab...) */
1511 } /* for (;;) */
1512}
1513
1514/*
1515 * Internet services provided internally by inetd:
1516 */
1517#define BUFSIZE 4096
1518
1519#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO || \
1520 ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN || \
1521 ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
1522static int dg_badinput(struct sockaddr_in *dg_sin)
1523{
1524 if (ntohs(dg_sin->sin_port) < IPPORT_RESERVED)
1525 return 1;
1526 if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST))
1527 return 1;
1528 /* XXX compare against broadcast addresses in SIOCGIFCONF list? */
1529 return 0;
1530}
1531#endif
1532
1533#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
1534/* Echo service -- echo data back */
1535/* ARGSUSED */
1536static void
1537echo_stream(int s, servtab_t *sep)
1538{
1539 char buffer[BUFSIZE];
1540 int i;
1541
1542 inetd_setproctitle(sep->se_service, s);
1543 while (1) {
1544 i = read(s, buffer, sizeof(buffer));
1545 if (i <= 0) break;
1546 /* FIXME: this isnt correct - safe_write()? */
1547 if (write(s, buffer, i) <= 0) break;
1548 }
1549 exit(0);
1550}
1551
1552/* Echo service -- echo data back */
1553/* ARGSUSED */
1554static void
1555echo_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
1556{
1557 char buffer[BUFSIZE];
1558 int i;
1559 socklen_t size;
1560 /* struct sockaddr_storage ss; */
1561 struct sockaddr sa;
1562
1563 size = sizeof(sa);
1564 i = recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size);
1565 if (i < 0)
1566 return;
1567 if (dg_badinput((struct sockaddr_in *) &sa))
1568 return;
1569 (void) sendto(s, buffer, i, 0, &sa, sizeof(sa));
1570}
1571#endif /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */
1572
1573#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
1574/* Discard service -- ignore data */
1575/* ARGSUSED */
1576static void
1577discard_stream(int s, servtab_t *sep)
1578{
1579 char buffer[BUFSIZE];
1580
1581 inetd_setproctitle(sep->se_service, s);
1582 while (1) {
1583 errno = 0;
1584 if (read(s, buffer, sizeof(buffer)) <= 0 && errno != EINTR)
1585 exit(0);
1586 }
1587}
1588
1589/* Discard service -- ignore data */
1590/* ARGSUSED */
1591static void
1592discard_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
1593{
1594 char buffer[BUFSIZE];
1595
1596 (void) read(s, buffer, sizeof(buffer));
1597}
1598#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */
1599
1600
1601#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
1602#define LINESIZ 72
1603static char ring[128];
1604static char *endring;
1605
1606static void
1607initring(void)
1608{
1609 int i;
1610
1611 endring = ring;
1612
1613 for (i = 0; i <= 128; ++i)
1614 if (isprint(i))
1615 *endring++ = i;
1616}
1617
1618/* Character generator */
1619/* ARGSUSED */
1620static void
1621chargen_stream(int s, servtab_t *sep)
1622{
1623 char *rs;
1624 int len;
1625 char text[LINESIZ + 2];
1626
1627 inetd_setproctitle(sep->se_service, s);
1628
1629 if (!endring) {
1630 initring();
1631 rs = ring;
1632 }
1633
1634 text[LINESIZ] = '\r';
1635 text[LINESIZ + 1] = '\n';
1636 rs = ring;
1637 for (;;) {
1638 len = endring - rs;
1639 if (len >= LINESIZ)
1640 memmove(text, rs, LINESIZ);
1641 else {
1642 memmove(text, rs, len);
1643 memmove(text + len, ring, LINESIZ - len);
1644 }
1645 if (++rs == endring)
1646 rs = ring;
1647 if (write(s, text, sizeof(text)) != sizeof(text))
1648 break;
1649 }
1650 exit(0);
1651}
1652
1653/* Character generator */
1654/* ARGSUSED */
1655static void
1656chargen_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
1657{
1658 /* struct sockaddr_storage ss; */
1659 struct sockaddr sa;
1660 static char *rs;
1661 int len;
1662 char text[LINESIZ + 2];
1663 socklen_t size;
1664
1665 if (endring == 0) {
1666 initring();
1667 rs = ring;
1668 }
1669
1670 size = sizeof(sa);
1671 if (recvfrom(s, text, sizeof(text), 0, &sa, &size) < 0)
1672 return;
1673 if (dg_badinput((struct sockaddr_in *) &sa))
1674 return;
1675
1676 if ((len = endring - rs) >= LINESIZ)
1677 memmove(text, rs, LINESIZ);
1678 else {
1679 memmove(text, rs, len);
1680 memmove(text + len, ring, LINESIZ - len);
1681 }
1682 if (++rs == endring)
1683 rs = ring;
1684 text[LINESIZ] = '\r';
1685 text[LINESIZ + 1] = '\n';
1686 (void) sendto(s, text, sizeof(text), 0, &sa, sizeof(sa));
1687}
1688#endif /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */
1689
1690
1691#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
1692/*
1693 * Return a machine readable date and time, in the form of the
1694 * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
1695 * returns the number of seconds since midnight, Jan 1, 1970,
1696 * we must add 2208988800 seconds to this figure to make up for
1697 * some seventy years Bell Labs was asleep.
1698 */
1699
1700static unsigned machtime(void)
1701{
1702 struct timeval tv;
1703
1704 if (gettimeofday(&tv, NULL) < 0) {
1705 fprintf(stderr, "Unable to get time of day\n");
1706 return 0L;
1707 }
1708 return htonl((unsigned) tv.tv_sec + 2208988800UL);
1709}
1710
1711/* ARGSUSED */
1712static void
1713machtime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)
1714{
1715 unsigned result;
1716
1717 result = machtime();
1718 (void) write(s, (char *) &result, sizeof(result));
1719}
1720
1721/* ARGSUSED */
1722static void
1723machtime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
1724{
1725 unsigned result;
1726 /* struct sockaddr_storage ss; */
1727 struct sockaddr sa;
1728 struct sockaddr_in *dg_sin;
1729 socklen_t size;
1730
1731 size = sizeof(sa);
1732 if (recvfrom(s, (char *) &result, sizeof(result), 0, &sa, &size) < 0)
1733 return;
1734 /* if (dg_badinput((struct sockaddr *)&ss)) */
1735 dg_sin = (struct sockaddr_in *) &sa;
1736 if (dg_sin->sin_addr.s_addr == htonl(INADDR_BROADCAST) ||
1737 ntohs(dg_sin->sin_port) < IPPORT_RESERVED / 2)
1738 return;
1739 result = machtime();
1740 (void) sendto(s, (char *) &result, sizeof(result), 0, &sa, sizeof(sa));
1741}
1742#endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */
1743
1744
1745#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
1746/* Return human-readable time of day */
1747/* ARGSUSED */
1748static void daytime_stream(int s, servtab_t *sep ATTRIBUTE_UNUSED)
1749{
1750 char buffer[32];
1751 time_t t;
1752
1753 t = time(NULL);
1754
1755// fdprintf instead?
1756 (void) sprintf(buffer, "%.24s\r\n", ctime(&t));
1757 (void) write(s, buffer, strlen(buffer));
1758}
1759
1760/* Return human-readable time of day */
1761/* ARGSUSED */
1762void
1763daytime_dg(int s, servtab_t *sep ATTRIBUTE_UNUSED)
1764{
1765 char buffer[256];
1766 time_t t;
1767 /* struct sockaddr_storage ss; */
1768 struct sockaddr sa;
1769 socklen_t size;
1770
1771 t = time(NULL);
1772
1773 size = sizeof(sa);
1774 if (recvfrom(s, buffer, sizeof(buffer), 0, &sa, &size) < 0)
1775 return;
1776 if (dg_badinput((struct sockaddr_in *) &sa))
1777 return;
1778 (void) sprintf(buffer, "%.24s\r\n", ctime(&t));
1779 (void) sendto(s, buffer, strlen(buffer), 0, &sa, sizeof(sa));
1780}
1781#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */
Note: See TracBrowser for help on using the repository browser.