first pass at trap support in hush

This commit is contained in:
Mike Frysinger 2009-03-29 23:49:37 +00:00
parent 0d907eab6c
commit 9f8128f480

View File

@ -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;