X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=libbb%2Fvfork_daemon_rexec.c;h=c0bea0ed21b3eb7bcc806432627e3c25fbb28b86;hb=a94eeb0b4222de7f05594c93ffdd84faea5b8e2d;hp=487ecb0e4ffeb4b03e45bb01efe644b5765b04e5;hpb=39194f030918b87eeb3e11e94cfa05f575fb47b4;p=oweals%2Fbusybox.git diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index 487ecb0e4..c0bea0ed2 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c @@ -14,76 +14,45 @@ * * 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 +# 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) {