inetd: be more careful with syslog socket; other small fixes
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 4 Sep 2009 00:19:46 +0000 (02:19 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 4 Sep 2009 00:19:46 +0000 (02:19 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/inetd.c

index 331c494418811c64ddb02dbd167a5cf2f26f7c63..4b9d01294cd1791843525eba6aaad8c7121b0626 100644 (file)
@@ -1031,10 +1031,10 @@ static void reap_child(int sig UNUSED_PARAM)
                                continue;
                        /* One of our "wait" services */
                        if (WIFEXITED(status) && WEXITSTATUS(status))
-                               bb_error_msg("%s: exit status 0x%x",
+                               bb_error_msg("%s: exit status %u",
                                                sep->se_program, WEXITSTATUS(status));
                        else if (WIFSIGNALED(status))
-                               bb_error_msg("%s: exit signal 0x%x",
+                               bb_error_msg("%s: exit signal %u",
                                                sep->se_program, WTERMSIG(status));
                        sep->se_wait = 1;
                        add_fd_to_set(sep->se_fd);
@@ -1119,7 +1119,12 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
        else
                bb_sanitize_stdio();
        if (!(opt & 4)) {
-               openlog(applet_name, LOG_PID, LOG_DAEMON);
+               /* LOG_NDELAY: connect to syslog daemon NOW.
+                * Otherwise, we may open syslog socket
+                * in vforked child, making opened fds and syslog()
+                * internal state inconsistent.
+                * This was observed to leak file descriptors. */
+               openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
                logmode = LOGMODE_SYSLOG;
        }
 
@@ -1355,17 +1360,23 @@ int inetd_main(int argc UNUSED_PARAM, char **argv)
                        if (rlim_ofile.rlim_cur != rlim_ofile_cur)
                                if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
                                        bb_perror_msg("setrlimit");
-                       closelog();
+
+                       /*closelog(); - BAD, we are after vfork,
+                        * this may confuse syslog() state.
+                        * Let's hope libc set syslog fd to CLOEXEC...
+                        */
                        xmove_fd(ctrl, STDIN_FILENO);
                        xdup2(STDIN_FILENO, STDOUT_FILENO);
                        /* 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 (!) */
-                       xdup2(STDIN_FILENO, STDERR_FILENO);
-                       /* NB: among others, this loop closes listening socket
+                       if (!sep->se_wait) /* only for usual "tcp nowait" */
+                               xdup2(STDIN_FILENO, STDERR_FILENO);
+                       /* NB: among others, this loop closes listening sockets
                         * for nowait stream children */
                        for (sep2 = serv_list; sep2; sep2 = sep2->se_next)
-                               maybe_close(sep2->se_fd);
+                               if (sep2->se_fd != ctrl)
+                                       maybe_close(sep2->se_fd);
                        sigaction_set(SIGPIPE, &saved_pipe_handler);
                        restore_sigmask(&omask);
                        BB_EXECVP(sep->se_program, sep->se_argv);