summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shell/hush.c139
1 files changed, 129 insertions, 10 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 5203f2e..17673c0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -489,6 +489,10 @@ struct globals {
#endif
unsigned char charmap[256];
char user_input_buf[ENABLE_FEATURE_EDITING ? BUFSIZ : 2];
+ struct {
+ char *cmd;
+ struct sigaction oact;
+ } *traps;
};
#define G (*ptr_to_globals)
@@ -517,6 +521,8 @@ static int builtin_help(char **argv);
static int builtin_pwd(char **argv);
static int builtin_read(char **argv);
static int builtin_test(char **argv);
+static void handle_trap(int sig);
+static int builtin_trap(char **argv);
static int builtin_true(char **argv);
static int builtin_set(char **argv);
static int builtin_set_mode(const char, const char);
@@ -578,8 +584,8 @@ static const struct built_in_command bltins[] = {
// BLTIN("return", builtin_not_written, "Return from a function"),
BLTIN("set" , builtin_set, "Set/unset shell local variables"),
BLTIN("shift" , builtin_shift, "Shift positional parameters"),
-// BLTIN("trap" , builtin_not_written, "Trap signals"),
BLTIN("test" , builtin_test, "Test condition"),
+ BLTIN("trap" , builtin_trap, "Trap signals"),
// BLTIN("ulimit", builtin_not_written, "Control resource limits"),
BLTIN("umask" , builtin_umask, "Set file creation mask"),
BLTIN("unset" , builtin_unset, "Unset environment variable"),
@@ -857,22 +863,27 @@ static void sigexit(int sig)
kill_myself_with_sig(sig); /* does not return */
}
-/* Restores tty foreground process group, and exits. */
-static void hush_exit(int exitcode) NORETURN;
-static void hush_exit(int exitcode)
-{
- fflush(NULL); /* flush all streams */
- sigexit(- (exitcode & 0xff));
-}
-
#else /* !JOB */
#define set_fatal_sighandler(handler) ((void)0)
#define set_jobctrl_sighandler(handler) ((void)0)
-#define hush_exit(e) exit(e)
#endif /* JOB */
+/* Restores tty foreground process group, and exits. */
+static void hush_exit(int exitcode) NORETURN;
+static void hush_exit(int exitcode)
+{
+ if (G.traps && G.traps[0].cmd)
+ handle_trap(0);
+
+ if (ENABLE_HUSH_JOB) {
+ fflush(NULL); /* flush all streams */
+ sigexit(- (exitcode & 0xff));
+ } else
+ exit(exitcode);
+}
+
static const char *set_cwd(void)
{
@@ -4516,6 +4527,114 @@ int lash_main(int argc, char **argv)
/*
* Built-ins
*/
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_28
+ *
+ * Traps are also evaluated immediately instead of being delayed properly:
+ * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_11
+ * Example: hush -c 'trap "echo hi" 31; sleep 10; echo moo' & sleep 1; kill -31 $!
+ * "hi" should not be displayed until the sleep finishes
+ * This will have to get fixed ...
+ */
+static void handle_trap(int sig)
+{
+ int save_errno, save_rcode;
+ char *argv[] = { NULL, G.traps[sig].cmd, NULL };
+ /* Race! We transitioned from handled to ignore/default, but
+ * the signal came in after updating .cmd but before we could
+ * register the new signal handler.
+ */
+ if (!argv[1] || argv[1][0] == '\0')
+ return;
+ /* need to save/restore errno/$? across traps */
+ save_errno = errno;
+ save_rcode = G.last_return_code;
+ builtin_eval(argv);
+ errno = save_errno;
+ G.last_return_code = save_rcode;
+}
+static int builtin_trap(char **argv)
+{
+ size_t i;
+ int sig;
+ bool ign = false;
+ char *new_cmd = NULL;
+
+ if (!G.traps)
+ G.traps = xzalloc(sizeof(*G.traps) * NSIG);
+
+ if (!argv[1]) {
+ /* No args: print all trapped. This isn't 100% correct as we should
+ * be escaping the cmd so that it can be pasted back in ...
+ */
+ for (i = 0; i < NSIG; ++i)
+ if (G.traps[i].cmd)
+ printf("trap -- '%s' %s\n", G.traps[i].cmd, get_signame(i));
+ return EXIT_SUCCESS;
+ }
+
+ /* first arg is decimal: reset all specified */
+ sig = bb_strtou(argv[1], NULL, 10);
+ if (errno == 0) {
+ int ret;
+ i = 0;
+ set_all:
+ ret = EXIT_SUCCESS;
+ while (argv[++i]) {
+ char *old_cmd;
+
+ sig = get_signum(argv[i]);
+ if (sig < 0 || sig >= NSIG) {
+ ret = EXIT_FAILURE;
+ bb_perror_msg("trap: %s: invalid signal specification", argv[i]);
+ continue;
+ }
+
+ /* Make sure .cmd is always a valid command list since
+ * signals can occur at any time ...
+ */
+ old_cmd = G.traps[sig].cmd;
+ G.traps[sig].cmd = xstrdup(new_cmd);
+ free(old_cmd);
+
+ debug_printf("trap: setting SIG%s (%i) to: %s",
+ get_signame(sig), sig, G.traps[sig].cmd);
+
+ /* There is no signal for 0 (EXIT) */
+ if (sig == 0)
+ continue;
+
+ if (new_cmd) {
+ /* add/update a handler */
+ struct sigaction act = {
+ .sa_handler = ign ? SIG_IGN : handle_trap,
+ .sa_flags = SA_RESTART,
+ };
+ sigemptyset(&act.sa_mask);
+ sigaction(sig, &act, old_cmd ? NULL : &G.traps[sig].oact);
+ } else if (old_cmd && !new_cmd)
+ /* there was a handler, and we are removing it */
+ sigaction_set(sig, &G.traps[sig].oact);
+ }
+ return ret;
+ }
+
+ /* first arg is "-": reset all specified to default */
+ /* first arg is "": ignore all specified */
+ /* everything else: execute first arg upon signal */
+ if (!argv[2]) {
+ bb_error_msg("trap: invalid arguments");
+ return EXIT_FAILURE;
+ }
+ if (LONE_DASH(argv[1]))
+ /* nothing! */;
+ else
+ new_cmd = argv[1];
+ if (argv[1][0] == '\0')
+ ign = true;
+ i = 1;
+ goto set_all;
+}
+
static int builtin_true(char **argv UNUSED_PARAM)
{
return 0;