*
* 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);
}
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;
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 */
}
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)
{
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);
}
/* 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;
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);
* Prevent this: stop being a session leader.
*/
if (fork_or_rexec(argv))
- exit(EXIT_SUCCESS); /* parent */
+ _exit(EXIT_SUCCESS); /* parent */
}
}
while (fd > 2) {