hush: fix heredoc_bkslash_newline1.tests failure
[oweals/busybox.git] / libbb / vfork_daemon_rexec.c
index 487ecb0e4ffeb4b03e45bb01efe644b5765b04e5..c0bea0ed21b3eb7bcc806432627e3c25fbb28b86 100644 (file)
  *
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-
 #include "busybox.h" /* uses applet tables */
 #include "NUM_APPLETS.h"
 
-/* This does a fork/exec in one call, using vfork().  Returns PID of new child,
- * -1 for failure.  Runs argv[0], searching path if that has no / in it. */
-pid_t FAST_FUNC spawn(char **argv)
-{
-       /* Compiler should not optimize stores here */
-       volatile int failed;
-       pid_t pid;
-
-       fflush_all();
-
-       /* Be nice to nommu machines. */
-       failed = 0;
-       pid = vfork();
-       if (pid < 0) /* error */
-               return pid;
-       if (!pid) { /* child */
-               /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
-               BB_EXECVP(argv[0], argv);
-
-               /* We are (maybe) sharing a stack with blocked parent,
-                * let parent know we failed and then exit to unblock parent
-                * (but don't run atexit() stuff, which would screw up parent.)
-                */
-               failed = errno;
-               /* mount, for example, does not want the message */
-               /*bb_perror_msg("can't execute '%s'", argv[0]);*/
-               _exit(111);
-       }
-       /* parent */
-       /* Unfortunately, this is not reliable: according to standards
-        * vfork() can be equivalent to fork() and we won't see value
-        * of 'failed'.
-        * Interested party can wait on pid and learn exit code.
-        * If 111 - then it (most probably) failed to exec */
-       if (failed) {
-               safe_waitpid(pid, NULL, 0); /* prevent zombie */
-               errno = failed;
-               return -1;
-       }
-       return pid;
-}
+#define NOFORK_SUPPORT ((NUM_APPLETS > 1) && (ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_NOFORK))
+#define NOEXEC_SUPPORT ((NUM_APPLETS > 1) && (ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE))
 
-/* Die with an error message if we can't spawn a child process. */
-pid_t FAST_FUNC xspawn(char **argv)
+#if defined(__linux__) && (NUM_APPLETS > 1)
+# include <sys/prctl.h>
+# ifndef PR_SET_NAME
+# define PR_SET_NAME 15
+# endif
+# ifndef PR_GET_NAME
+# define PR_GET_NAME 16
+# endif
+void FAST_FUNC set_task_comm(const char *comm)
 {
-       pid_t pid = spawn(argv);
-       if (pid < 0)
-               bb_simple_perror_msg_and_die(*argv);
-       return pid;
+       /* okay if too long (truncates) */
+       prctl(PR_SET_NAME, (long)comm, 0, 0, 0);
 }
+#endif
 
-#if ENABLE_FEATURE_PREFER_APPLETS \
- || ENABLE_FEATURE_SH_NOFORK
+/*
+ * NOFORK/NOEXEC support
+ */
+#if NOFORK_SUPPORT
 static jmp_buf die_jmp;
 static void jump(void)
 {
        /* Special case. We arrive here if NOFORK applet
         * calls xfunc, which then decides to die.
-        * We don't die, but jump instead back to caller.
+        * We don't die, but instead jump back to caller.
         * NOFORK applets still cannot carelessly call xfuncs:
         * p = xmalloc(10);
         * q = xmalloc(10); // BUG! if this dies, we leak p!
         */
        /* | 0x100 allows to pass zero exitcode (longjmp can't pass 0).
         * This works because exitcodes are bytes,
-        * run_nofork_applet() ensures that by "& 0xff" */
+        * run_nofork_applet() ensures that by "& 0xff"
+        */
        longjmp(die_jmp, xfunc_error_retval | 0x100);
 }
 
@@ -123,14 +92,13 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
 
        logmode = LOGMODE_STDIO;
        xfunc_error_retval = EXIT_FAILURE;
-       /* In case getopt() or getopt32() was already called:
+       /* In case getopt() was already called:
         * reset the libc getopt() function, which keeps internal state.
+        * (getopt32() does it itself, but getopt() doesn't (and can't))
         */
        GETOPT_RESET();
 
-       argc = 1;
-       while (argv[argc])
-               argc++;
+       argc = string_array_len(argv);
 
        /* If xfunc "dies" in NOFORK applet, die_func longjmp's here instead */
        die_func = jump;
@@ -143,6 +111,8 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
                applet_name = tmp_argv[0];
                /* Finally we can call NOFORK applet's main() */
                rc = applet_main[applet_no](argc, tmp_argv);
+               /* Important for shells: `which CMD` was failing */
+               fflush_all();
        } else {
                /* xfunc died in NOFORK applet */
        }
@@ -154,7 +124,82 @@ int FAST_FUNC run_nofork_applet(int applet_no, char **argv)
 
        return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
 }
-#endif /* FEATURE_PREFER_APPLETS || FEATURE_SH_NOFORK */
+#endif
+
+#if NOEXEC_SUPPORT
+void FAST_FUNC run_noexec_applet_and_exit(int a, const char *name, char **argv)
+{
+       /* reset some state and run without execing */
+       /* msg_eol = "\n"; - no caller needs this reinited yet */
+       logmode = LOGMODE_STDIO;
+       xfunc_error_retval = EXIT_FAILURE;
+       die_func = NULL;
+       GETOPT_RESET();
+
+//TODO: think pidof, pgrep, pkill!
+//set_task_comm() makes our pidof find NOEXECs (e.g. "yes >/dev/null"),
+//but one from procps-ng-3.3.10 needs more!
+//Rewrite /proc/PID/cmdline? (need to save argv0 and length at init for this to work!)
+       set_task_comm(name);
+       /* applet_name is set by this function: */
+       run_applet_no_and_exit(a, name, argv);
+}
+#endif
+
+/*
+ * Higher-level code, hiding optional NOFORK/NOEXEC trickery.
+ */
+
+/* This does a fork/exec in one call, using vfork().  Returns PID of new child,
+ * -1 for failure.  Runs argv[0], searching path if that has no / in it. */
+pid_t FAST_FUNC spawn(char **argv)
+{
+       /* Compiler should not optimize stores here */
+       volatile int failed;
+       pid_t pid;
+
+       fflush_all();
+
+       /* Be nice to nommu machines. */
+       failed = 0;
+       pid = vfork();
+       if (pid < 0) /* error */
+               return pid;
+       if (!pid) { /* child */
+               /* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
+               BB_EXECVP(argv[0], argv);
+
+               /* We are (maybe) sharing a stack with blocked parent,
+                * let parent know we failed and then exit to unblock parent
+                * (but don't run atexit() stuff, which would screw up parent.)
+                */
+               failed = errno;
+               /* mount, for example, does not want the message */
+               /*bb_perror_msg("can't execute '%s'", argv[0]);*/
+               _exit(111);
+       }
+       /* parent */
+       /* Unfortunately, this is not reliable: according to standards
+        * vfork() can be equivalent to fork() and we won't see value
+        * of 'failed'.
+        * Interested party can wait on pid and learn exit code.
+        * If 111 - then it (most probably) failed to exec */
+       if (failed) {
+               safe_waitpid(pid, NULL, 0); /* prevent zombie */
+               errno = failed;
+               return -1;
+       }
+       return pid;
+}
+
+/* Die with an error message if we can't spawn a child process. */
+pid_t FAST_FUNC xspawn(char **argv)
+{
+       pid_t pid = spawn(argv);
+       if (pid < 0)
+               bb_simple_perror_msg_and_die(*argv);
+       return pid;
+}
 
 int FAST_FUNC spawn_and_wait(char **argv)
 {
@@ -173,20 +218,11 @@ int FAST_FUNC spawn_and_wait(char **argv)
                                return wait4pid(rc);
 
                        /* child */
-                       /* reset some state and run without execing */
-
-                       /* msg_eol = "\n"; - no caller needs this reinited yet */
-                       logmode = LOGMODE_STDIO;
-                       /* die_func = NULL; - needed if the caller is a shell,
-                        * init, or a NOFORK applet. But none of those call us
-                        * as of yet (and that should probably always stay true).
-                        */
-                       /* xfunc_error_retval and applet_name are init by: */
-                       run_applet_no_and_exit(a, argv[0], argv);
+                       run_noexec_applet_and_exit(a, argv[0], argv);
                }
 # endif
        }
-#endif /* FEATURE_PREFER_APPLETS */
+#endif
        rc = spawn(argv);
        return wait4pid(rc);
 }
@@ -207,6 +243,9 @@ pid_t FAST_FUNC fork_or_rexec(char **argv)
        /* Maybe we are already re-execed and come here again? */
        if (re_execed)
                return 0;
+
+       /* fflush_all(); ? - so far all callers had no buffered output to flush */
+
        pid = xvfork();
        if (pid) /* parent */
                return pid;
@@ -243,8 +282,11 @@ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
                fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
 
        if (!(flags & DAEMON_ONLY_SANITIZE)) {
+
+               /* fflush_all(); - add it in fork_or_rexec() if necessary */
+
                if (fork_or_rexec(argv))
-                       exit(EXIT_SUCCESS); /* parent */
+                       _exit(EXIT_SUCCESS); /* parent */
                /* if daemonizing, detach from stdio & ctty */
                setsid();
                dup2(fd, 0);
@@ -256,7 +298,7 @@ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
                         * Prevent this: stop being a session leader.
                         */
                        if (fork_or_rexec(argv))
-                               exit(EXIT_SUCCESS); /* parent */
+                               _exit(EXIT_SUCCESS); /* parent */
                }
        }
        while (fd > 2) {