mirror of
https://github.com/sheumann/hush.git
synced 2024-12-22 14:30:31 +00:00
first pass at trap
support in hush
This commit is contained in:
parent
0d907eab6c
commit
9f8128f480
139
shell/hush.c
139
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;
|
||||
|
Loading…
Reference in New Issue
Block a user