NOFORK fixes
"rm -i FILE" and "yes" can now be interrupted by ^C in hush.
This also now works:
$ usleep 19999999
^C
$ echo $?
130
function old new delta
run_pipe 1668 1711 +43
pseudo_exec_argv 312 321 +9
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 52/0) Total: 52 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/coreutils/rm.c b/coreutils/rm.c
index f91c945..5e4acab 100644
--- a/coreutils/rm.c
+++ b/coreutils/rm.c
@@ -16,7 +16,8 @@
//config: help
//config: rm is used to remove files or directories.
-//applet:IF_RM(APPLET_NOFORK(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm))
+//applet:IF_RM(APPLET_NOEXEC(rm, rm, BB_DIR_BIN, BB_SUID_DROP, rm))
+/* was NOFORK, but then "rm -i FILE" can't be ^C'ed if run by hush */
//kbuild:lib-$(CONFIG_RM) += rm.o
@@ -36,7 +37,7 @@
#include "libbb.h"
-/* This is a NOFORK applet. Be very careful! */
+/* This is a NOEXEC applet. Be very careful! */
int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int rm_main(int argc UNUSED_PARAM, char **argv)
diff --git a/coreutils/seq.c b/coreutils/seq.c
index f36dbb4..c26ff06 100644
--- a/coreutils/seq.c
+++ b/coreutils/seq.c
@@ -12,7 +12,8 @@
//config: help
//config: print a sequence of numbers
-//applet:IF_SEQ(APPLET_NOFORK(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq))
+//applet:IF_SEQ(APPLET_NOEXEC(seq, seq, BB_DIR_USR_BIN, BB_SUID_DROP, seq))
+/* was NOFORK, but then "seq 1 999999999" can't be ^C'ed if run by hush */
//kbuild:lib-$(CONFIG_SEQ) += seq.o
@@ -26,7 +27,7 @@
#include "libbb.h"
-/* This is a NOFORK applet. Be very careful! */
+/* This is a NOEXEC applet. Be very careful! */
int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int seq_main(int argc, char **argv)
diff --git a/coreutils/usleep.c b/coreutils/usleep.c
index 7c25aad..684ab78 100644
--- a/coreutils/usleep.c
+++ b/coreutils/usleep.c
@@ -38,6 +38,13 @@
bb_show_usage();
}
+ /* Safe wrt NOFORK? (noforks are not allowed to run for
+ * a long time). Try "usleep 99999999" + ^C + "echo $?"
+ * in hush with FEATURE_SH_NOFORK=y.
+ * At least on uclibc, usleep() thanslates to nanosleep()
+ * which returns early on any signal (even caught one),
+ * and uclibc does not loop back on EINTR.
+ */
usleep(xatou(argv[1]));
return EXIT_SUCCESS;
diff --git a/coreutils/yes.c b/coreutils/yes.c
index ea35d14..c244bfe 100644
--- a/coreutils/yes.c
+++ b/coreutils/yes.c
@@ -17,7 +17,8 @@
//config: yes is used to repeatedly output a specific string, or
//config: the default string 'y'.
-//applet:IF_YES(APPLET_NOFORK(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes))
+//applet:IF_YES(APPLET_NOEXEC(yes, yes, BB_DIR_USR_BIN, BB_SUID_DROP, yes))
+/* was NOFORK, but then yes can't be ^C'ed if run by hush */
//kbuild:lib-$(CONFIG_YES) += yes.o
diff --git a/docs/nofork_noexec.txt b/docs/nofork_noexec.txt
index a24dd9c..0ad4e6e 100644
--- a/docs/nofork_noexec.txt
+++ b/docs/nofork_noexec.txt
@@ -52,6 +52,9 @@
This poses much more serious limitations on what applet can do:
* all NOEXEC limitations apply.
+* do not run for a long time or wait for user input:
+ hush shell only handles signals (like ^C) after you return
+ from APPLET_main().
* do not ever exit() or exec().
- xfuncs are okay. They are using special trick to return
to the caller applet instead of dying when they detect "x" condition.
diff --git a/shell/hush.c b/shell/hush.c
index 9f946d8..cfefb73 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -7363,6 +7363,8 @@
*/
close_saved_fds_and_FILE_fds();
//FIXME: should also close saved redir fds
+ /* Without this, "rm -i FILE" can't be ^C'ed: */
+ switch_off_special_sigs(G.special_sig_mask);
debug_printf_exec("running applet '%s'\n", argv[0]);
run_applet_no_and_exit(a, argv[0], argv);
}
@@ -8045,6 +8047,24 @@
add_vars(old_vars);
/* clean_up_and_ret0: */
restore_redirects(squirrel);
+ /*
+ * Try "usleep 99999999" + ^C + "echo $?"
+ * with FEATURE_SH_NOFORK=y.
+ */
+ if (!funcp) {
+ /* It was builtin or nofork.
+ * if this would be a real fork/execed program,
+ * it should have died if a fatal sig was received.
+ * But OTOH, there was no separate process,
+ * the sig was sent to _shell_, not to non-existing
+ * child.
+ * Let's just handle ^C only, this one is obvious:
+ * we aren't ok with exitcode 0 when ^C was pressed
+ * during builtin/nofork.
+ */
+ if (sigismember(&G.pending_set, SIGINT))
+ rcode = 128 + SIGINT;
+ }
clean_up_and_ret1:
free(argv_expanded);
IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
@@ -8060,6 +8080,14 @@
if (rcode == 0) {
debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
argv_expanded[0], argv_expanded[1]);
+ /*
+ * Note: signals (^C) can't interrupt here.
+ * We remember them and they will be acted upon
+ * after applet returns.
+ * This makes applets which can run for a long time
+ * and/or wait for user input ineligible for NOFORK:
+ * for example, "yes" or "rm" (rm -i waits for input).
+ */
rcode = run_nofork_applet(n, argv_expanded);
}
goto clean_up_and_ret;
@@ -8491,7 +8519,7 @@
G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
G.last_bg_pid_exitcode = 0;
debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
-/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */
+/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */
rcode = EXIT_SUCCESS;
goto check_traps;
} else {
@@ -10178,6 +10206,7 @@
/* So, did we get a signal? */
sig = check_and_run_traps();
if (sig /*&& sig != SIGCHLD - always true */) {
+ /* Do this for any (non-ignored) signal, not only for ^C */
ret = 128 + sig;
break;
}