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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 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.