X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=networking%2Finetd.c;h=8d44b5198add30342d857ce7da4efc67125aefe3;hb=47cfbf32fd66563f8c4e09ad6cced6abfbe2fad5;hp=fb00c6cd71e8df10d42ba7ee643c5d8078672d96;hpb=a945f6190b9ffdd316e8590c0b4fcb25dee7676e;p=oweals%2Fbusybox.git diff --git a/networking/inetd.c b/networking/inetd.c index fb00c6cd7..8d44b5198 100644 --- a/networking/inetd.c +++ b/networking/inetd.c @@ -154,14 +154,30 @@ * setuid() */ +//usage:#define inetd_trivial_usage +//usage: "[-fe] [-q N] [-R N] [CONFFILE]" +//usage:#define inetd_full_usage "\n\n" +//usage: "Listen for network connections and launch programs\n" +//usage: "\n -f Run in foreground" +//usage: "\n -e Log to stderr" +//usage: "\n -q N Socket listen queue (default: 128)" +//usage: "\n -R N Pause services after N connects/min" +//usage: "\n (default: 0 - disabled)" + #include +#include /* setrlimit */ +#include /* un.h may need this */ #include #include "libbb.h" +#include "common_bufsiz.h" #if ENABLE_FEATURE_INETD_RPC -#include -#include +# if defined(__UCLIBC__) && ! defined(__UCLIBC_HAS_RPC__) +# error "You need to build uClibc with UCLIBC_HAS_RPC for NFS support" +# endif +# include +# include #endif #if !BB_MMU @@ -171,8 +187,6 @@ #define ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN 0 #endif -#define _PATH_INETDPID "/var/run/inetd.pid" - #define CNT_INTERVAL 60 /* servers in CNT_INTERVAL sec. */ #define RETRYTIME 60 /* retry after bind or server fail */ @@ -314,11 +328,8 @@ struct globals { /* Used in next_line(), and as scratch read buffer */ char line[256]; /* _at least_ 256, see LINE_SIZE */ } FIX_ALIASING; -#define G (*(struct globals*)&bb_common_bufsiz1) +#define G (*(struct globals*)bb_common_bufsiz1) enum { LINE_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line) }; -struct BUG_G_too_big { - char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; -}; #define rlim_ofile_cur (G.rlim_ofile_cur ) #define rlim_ofile (G.rlim_ofile ) #define serv_list (G.serv_list ) @@ -339,15 +350,33 @@ struct BUG_G_too_big { #define allsock (G.allsock ) #define line (G.line ) #define INIT_G() do { \ + setup_common_bufsiz(); \ + BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \ rlim_ofile_cur = OPEN_MAX; \ global_queuelen = 128; \ config_filename = "/etc/inetd.conf"; \ } while (0) +#if 1 +# define dbg(...) ((void)0) +#else +# define dbg(...) \ +do { \ + int dbg_fd = open("inetd_debug.log", O_WRONLY | O_CREAT | O_APPEND, 0666); \ + if (dbg_fd >= 0) { \ + fdprintf(dbg_fd, "%d: ", getpid()); \ + fdprintf(dbg_fd, __VA_ARGS__); \ + close(dbg_fd); \ + } \ +} while (0) +#endif + static void maybe_close(int fd) { - if (fd >= 0) + if (fd >= 0) { close(fd); + dbg("closed fd:%d\n", fd); + } } // TODO: move to libbb? @@ -451,7 +480,9 @@ static void remove_fd_from_set(int fd) { if (fd >= 0) { FD_CLR(fd, &allsock); + dbg("stopped listening on fd:%d\n", fd); maxsock = -1; + dbg("maxsock:%d\n", maxsock); } } @@ -459,8 +490,10 @@ static void add_fd_to_set(int fd) { if (fd >= 0) { FD_SET(fd, &allsock); + dbg("started listening on fd:%d\n", fd); if (maxsock >= 0 && fd > maxsock) { prev_maxsock = maxsock = fd; + dbg("maxsock:%d\n", maxsock); if ((rlim_t)fd > rlim_ofile_cur - FD_MARGIN) bump_nofile(); } @@ -479,6 +512,7 @@ static void recalculate_maxsock(void) maxsock = fd; fd++; } + dbg("recalculated maxsock:%d\n", maxsock); prev_maxsock = maxsock; if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN) bump_nofile(); @@ -501,7 +535,7 @@ static void prepare_socket_fd(servtab_t *sep) /* zero out the port for all RPC services; let bind() * find one. */ - set_nport(sep->se_lsa, 0); + set_nport(&sep->se_lsa->u.sa, 0); /* for RPC services, attempt to use a reserved port * if they are going to be running as root. */ @@ -536,8 +570,13 @@ static void prepare_socket_fd(servtab_t *sep) rearm_alarm(); return; } - if (sep->se_socktype == SOCK_STREAM) + + if (sep->se_socktype == SOCK_STREAM) { listen(fd, global_queuelen); + dbg("new sep->se_fd:%d (stream)\n", fd); + } else { + dbg("new sep->se_fd:%d (!stream)\n", fd); + } add_fd_to_set(fd); sep->se_fd = fd; @@ -606,7 +645,7 @@ static servtab_t *dup_servtab(servtab_t *sep) } /* gcc generates much more code if this is inlined */ -static servtab_t *parse_one_line(void) +static NOINLINE servtab_t *parse_one_line(void) { int argc; char *token[6+MAXARGV]; @@ -636,6 +675,8 @@ static servtab_t *parse_one_line(void) * default host for the following lines. */ free(default_local_hostname); default_local_hostname = sep->se_local_hostname; + /*sep->se_local_hostname = NULL; - redundant */ + /* (we'll overwrite this field anyway) */ goto more; } } else @@ -649,10 +690,10 @@ static servtab_t *parse_one_line(void) parse_err: bb_error_msg("parse error on line %u, line is ignored", parser->lineno); - free_servtab_strings(sep); /* Just "goto more" can make sep to carry over e.g. * "rpc"-ness (by having se_rpcver_lo != 0). * We will be more paranoid: */ + free_servtab_strings(sep); free(sep); goto new; } @@ -686,7 +727,7 @@ static servtab_t *parse_one_line(void) goto parse_err; #endif } - if (strncmp(arg, "rpc/", 4) == 0) { + if (is_prefixed_with(arg, "rpc/")) { #if ENABLE_FEATURE_INETD_RPC unsigned n; arg += 4; @@ -776,7 +817,7 @@ static servtab_t *parse_one_line(void) } #endif argc = 0; - while ((arg = token[6+argc]) != NULL && argc < MAXARGV) + while (argc < MAXARGV && (arg = token[6+argc]) != NULL) sep->se_argv[argc++] = xstrdup(arg); /* Some inetd.conf files have no argv's, not even argv[0]. * Fix them up. @@ -795,10 +836,10 @@ static servtab_t *parse_one_line(void) goto parse_err; } -// bb_info_msg( -// "ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]", -// sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no, -// sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program); + //bb_error_msg( + // "ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]", + // sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no, + // sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program); /* check if the hostname specifier is a comma separated list * of hostnames. we'll make new entries for each address. */ @@ -959,7 +1000,7 @@ static void reread_config_file(int sig UNUSED_PARAM) } if (LONE_CHAR(sep->se_local_hostname, '*')) { lsa = xzalloc_lsa(sep->se_family); - set_nport(lsa, port); + set_nport(&lsa->u.sa, port); } else { lsa = host_and_af2sockaddr(sep->se_local_hostname, ntohs(port), sep->se_family); @@ -999,7 +1040,7 @@ static void reread_config_file(int sig UNUSED_PARAM) * new config file doesnt have them. */ block_CHLD_HUP_ALRM(&omask); sepp = &serv_list; - while ((sep = *sepp)) { + while ((sep = *sepp) != NULL) { if (sep->se_checked) { sepp = &sep->se_next; continue; @@ -1091,7 +1132,7 @@ static void clean_up_and_exit(int sig UNUSED_PARAM) if (ENABLE_FEATURE_CLEAN_UP) close(sep->se_fd); } - remove_pidfile(_PATH_INETDPID); + remove_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid"); exit(EXIT_SUCCESS); } @@ -1140,7 +1181,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) setgroups(1, &gid); } - write_pidfile(_PATH_INETDPID); + write_pidfile(CONFIG_PID_FILE_PATH "/inetd.pid"); /* never fails under Linux (except if you pass it bad arguments) */ getrlimit(RLIMIT_NOFILE, &rlim_ofile); @@ -1153,12 +1194,17 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) sigaddset(&sa.sa_mask, SIGALRM); sigaddset(&sa.sa_mask, SIGCHLD); sigaddset(&sa.sa_mask, SIGHUP); +//FIXME: explain why no SA_RESTART +//FIXME: retry_network_setup is unsafe to run in signal handler (many reasons)! sa.sa_handler = retry_network_setup; sigaction_set(SIGALRM, &sa); +//FIXME: reread_config_file is unsafe to run in signal handler(many reasons)! sa.sa_handler = reread_config_file; sigaction_set(SIGHUP, &sa); +//FIXME: reap_child is unsafe to run in signal handler (uses stdio)! sa.sa_handler = reap_child; sigaction_set(SIGCHLD, &sa); +//FIXME: clean_up_and_exit is unsafe to run in signal handler (uses stdio)! sa.sa_handler = clean_up_and_exit; sigaction_set(SIGTERM, &sa); sa.sa_handler = clean_up_and_exit; @@ -1188,11 +1234,13 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) } continue; } + dbg("ready_fd_cnt:%d\n", ready_fd_cnt); for (sep = serv_list; ready_fd_cnt && sep; sep = sep->se_next) { if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable)) continue; + dbg("ready fd:%d\n", sep->se_fd); ready_fd_cnt--; ctrl = sep->se_fd; accepted_fd = -1; @@ -1200,6 +1248,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) if (!sep->se_wait) { if (sep->se_socktype == SOCK_STREAM) { ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL); + dbg("accepted_fd:%d\n", accepted_fd); if (ctrl < 0) { if (errno != EINTR) bb_perror_msg("accept (for %s)", sep->se_service); @@ -1220,19 +1269,22 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) * (can create many copies of same child, etc). * Parent must create and use new socket instead. */ new_udp_fd = socket(sep->se_family, SOCK_DGRAM, 0); + dbg("new_udp_fd:%d\n", new_udp_fd); if (new_udp_fd < 0) { /* error: eat packet, forget about it */ udp_err: recv(sep->se_fd, line, LINE_SIZE, MSG_DONTWAIT); continue; } setsockopt_reuseaddr(new_udp_fd); - /* TODO: better do bind after vfork in parent, + /* TODO: better do bind after fork in parent, * so that we don't have two wildcard bound sockets * even for a brief moment? */ if (bind(new_udp_fd, &sep->se_lsa->u.sa, sep->se_lsa->len) < 0) { + dbg("bind(new_udp_fd) failed\n"); close(new_udp_fd); goto udp_err; } + dbg("bind(new_udp_fd) succeeded\n"); } } @@ -1260,6 +1312,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) sep->se_count = 0; rearm_alarm(); /* will revive it in RETRYTIME sec */ restore_sigmask(&omask); + maybe_close(new_udp_fd); maybe_close(accepted_fd); continue; /* -> check next fd in fd set */ } @@ -1280,17 +1333,18 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) bb_perror_msg("vfork"+1); sleep(1); restore_sigmask(&omask); + maybe_close(new_udp_fd); maybe_close(accepted_fd); continue; /* -> check next fd in fd set */ } if (pid == 0) pid--; /* -1: "we did fork and we are child" */ } - /* if pid == 0 here, we never forked */ + /* if pid == 0 here, we didn't fork */ if (pid > 0) { /* parent */ if (sep->se_wait) { - /* tcp wait: we passed listening socket to child, + /* wait: we passed socket to child, * will wait for child to terminate */ sep->se_wait = pid; remove_fd_from_set(sep->se_fd); @@ -1299,17 +1353,19 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) /* udp nowait: child connected the socket, * we created and will use new, unconnected one */ xmove_fd(new_udp_fd, sep->se_fd); + dbg("moved new_udp_fd:%d to sep->se_fd:%d\n", new_udp_fd, sep->se_fd); } restore_sigmask(&omask); maybe_close(accepted_fd); continue; /* -> check next fd in fd set */ } - /* we are either child or didn't vfork at all */ + /* we are either child or didn't fork at all */ #ifdef INETD_BUILTINS_ENABLED if (sep->se_builtin) { - if (pid) { /* "pid" is -1: we did vfork */ + if (pid) { /* "pid" is -1: we did fork */ close(sep->se_fd); /* listening socket */ + dbg("closed sep->se_fd:%d\n", sep->se_fd); logmode = LOGMODE_NONE; /* make xwrite etc silent */ } restore_sigmask(&omask); @@ -1317,7 +1373,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) sep->se_builtin->bi_stream_fn(ctrl, sep); else sep->se_builtin->bi_dgram_fn(ctrl, sep); - if (pid) /* we did vfork */ + if (pid) /* we did fork */ _exit(EXIT_FAILURE); maybe_close(accepted_fd); continue; /* -> check next fd in fd set */ @@ -1327,9 +1383,14 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) setsid(); /* "nowait" udp */ if (new_udp_fd >= 0) { - len_and_sockaddr *lsa = xzalloc_lsa(sep->se_family); + len_and_sockaddr *lsa; + int r; + + close(new_udp_fd); + dbg("closed new_udp_fd:%d\n", new_udp_fd); + lsa = xzalloc_lsa(sep->se_family); /* peek at the packet and remember peer addr */ - int r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT, + r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT, &lsa->u.sa, &lsa->len); if (r < 0) goto do_exit1; @@ -1337,6 +1398,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) * only packets from this peer will be recv'ed, * and bare write()/send() will work on it */ connect(ctrl, &lsa->u.sa, lsa->len); + dbg("connected ctrl:%d to remote peer\n", ctrl); free(lsa); } /* prepare env and exec program */ @@ -1354,7 +1416,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) bb_error_msg("non-root must run services as himself"); goto do_exit1; } - if (pwd->pw_uid) { + if (pwd->pw_uid != 0) { if (sep->se_group) pwd->pw_gid = grp->gr_gid; /* initgroups, setgid, setuid: */ @@ -1373,6 +1435,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) */ xmove_fd(ctrl, STDIN_FILENO); xdup2(STDIN_FILENO, STDOUT_FILENO); + dbg("moved ctrl:%d to fd 0,1[,2]\n", ctrl); /* manpages of inetd I managed to find either say * that stderr is also redirected to the network, * or do not talk about redirection at all (!) */ @@ -1385,6 +1448,7 @@ int inetd_main(int argc UNUSED_PARAM, char **argv) maybe_close(sep2->se_fd); sigaction_set(SIGPIPE, &saved_pipe_handler); restore_sigmask(&omask); + dbg("execing:'%s'\n", sep->se_program); BB_EXECVP(sep->se_program, sep->se_argv); bb_perror_msg("can't execute '%s'", sep->se_program); do_exit1: @@ -1592,7 +1656,7 @@ static void FAST_FUNC daytime_stream(int s, servtab_t *sep UNUSED_PARAM) { time_t t; - t = time(NULL); + time(&t); fdprintf(s, "%.24s\r\n", ctime(&t)); } static void FAST_FUNC daytime_dg(int s, servtab_t *sep)