overhaul the signal handling:

• support NSIG_MAX from http://austingroupbugs.net/view.php?id=741
  and make a TODO for later to use sysconf(_SC_NSIG) at runtime
• bounds-check signals (e.g. no smaller than 1, but smaller than NSIG)
• align trap errorlevel with other shells
• make trap match what’s in POSIX and fixup the manpage
• refactor some code related to signals
• hide from kill builtin both EXIT and ERR pseudo-signals
This commit is contained in:
tg 2015-04-29 20:44:37 +00:00
parent 4e313e705d
commit 93ccb42114
6 changed files with 219 additions and 162 deletions

View File

@ -1,5 +1,5 @@
#!/bin/sh
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.677 2015/04/29 20:39:00 tg Exp $'
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.678 2015/04/29 20:44:31 tg Exp $'
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015
@ -2248,20 +2248,22 @@ if test 0 = $HAVE_SYS_SIGNAME; then
sigseenone=:
sigseentwo=:
echo '#include <signal.h>
#ifndef NSIG
#if defined(_NSIG)
#define NSIG _NSIG
#if defined(NSIG_MAX)
#define cfg_NSIG NSIG_MAX
#elif defined(NSIG)
#define cfg_NSIG NSIG
#elif defined(_NSIG)
#define cfg_NSIG _NSIG
#elif defined(SIGMAX)
#define NSIG (SIGMAX+1)
#define cfg_NSIG (SIGMAX + 1)
#elif defined(_SIGMAX)
#define NSIG (_SIGMAX+1)
#define cfg_NSIG (_SIGMAX + 1)
#else
/* XXX better error out, see sh.h */
#define NSIG 64
#endif
/*XXX better error out, see sh.h */
#define cfg_NSIG 64
#endif
int
mksh_cfg= NSIG
mksh_cfg= cfg_NSIG
;' >conftest.c
# GNU sed 2.03 segfaults when optimising this to sed -n
NSIG=`vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \
@ -2310,7 +2312,7 @@ mksh_cfg= NSIG
sed 's/^ *mksh_cfg *=[ ]*\([0-9][0-9x]*\).*$/:\1 '$name/
done | sed -n '/^:[^ ]/s/^://p' | while read nr name; do
test $printf = echo || nr=`printf %d "$nr" 2>/dev/null`
test $nr -gt 0 && test $nr -le $NSIG || continue
test $nr -gt 0 && test $nr -lt $NSIG || continue
case $sigseentwo in
*:$nr:*) ;;
*) echo " { \"$name\", $nr },"

View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.692 2015/04/29 20:13:47 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.693 2015/04/29 20:44:32 tg Exp $
# -*- mode: sh -*-
#-
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
@ -30,7 +30,7 @@
# (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
expected-stdout:
@(#)MIRBSD KSH R51 2015/04/19
@(#)MIRBSD KSH R51 2015/04/29
description:
Check version of shell.
stdin:
@ -39,7 +39,7 @@ name: KSH_VERSION
category: shell:legacy-no
---
expected-stdout:
@(#)LEGACY KSH R51 2015/04/19
@(#)LEGACY KSH R51 2015/04/29
description:
Check version of legacy shell.
stdin:
@ -4837,7 +4837,7 @@ expected-stdout:
PROG: trap: bad signal 'UNKNOWNSIGNAL'
PROG: trap: bad signal '999999'
PROG: trap: bad signal 'FNORD'
= 3
= 1
trap 2 executed
---
name: read-IFS-1

63
funcs.c
View File

@ -38,7 +38,7 @@
#endif
#endif
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.270 2015/04/29 20:07:32 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.271 2015/04/29 20:44:34 tg Exp $");
#if HAVE_KILLPG
/*
@ -1331,7 +1331,7 @@ c_kill(const char **wp)
/* assume old style options if -digits or -UPPERCASE */
if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
ksh_isupper(p[1]))) {
if (!(t = gettrap(p + 1, false))) {
if (!(t = gettrap(p + 1, false, false))) {
bi_errorf("bad signal '%s'", p + 1);
return (1);
}
@ -1345,7 +1345,8 @@ c_kill(const char **wp)
lflag = true;
break;
case 's':
if (!(t = gettrap(builtin_opt.optarg, true))) {
if (!(t = gettrap(builtin_opt.optarg,
true, false))) {
bi_errorf("bad signal '%s'",
builtin_opt.optarg);
return (1);
@ -1371,24 +1372,25 @@ c_kill(const char **wp)
for (; wp[i]; i++) {
if (!bi_getn(wp[i], &n))
return (1);
#if (NSIG < 128)
if (n > 128 && n < 128 + NSIG)
#if (ksh_NSIG < 128)
if (n > 128 && n < 128 + ksh_NSIG)
n -= 128;
#endif
if (n > 0 && n < NSIG)
if (n > 0 && n < ksh_NSIG)
shprintf("%s\n", sigtraps[n].name);
else
shprintf("%d\n", n);
}
} else {
ssize_t w, mess_cols, mess_octs;
int j;
struct kill_info ki;
ssize_t w, mess_cols = 0, mess_octs = 0;
int j = ksh_NSIG;
struct kill_info ki = { 0, 0 };
for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10)
do {
ki.num_width++;
ki.name_width = mess_cols = mess_octs = 0;
for (j = 0; j < NSIG; j++) {
} while ((j /= 10));
for (j = 1; j < ksh_NSIG; j++) {
w = strlen(sigtraps[j].name);
if (w > ki.name_width)
ki.name_width = w;
@ -1400,7 +1402,7 @@ c_kill(const char **wp)
mess_cols = w;
}
print_columns(shl_stdout, (unsigned int)(NSIG - 1),
print_columns(shl_stdout, (unsigned int)(ksh_NSIG - 1),
kill_fmt_entry, (void *)&ki,
ki.num_width + 1 + ki.name_width + 1 + mess_octs,
ki.num_width + 1 + ki.name_width + 1 + mess_cols,
@ -2286,41 +2288,44 @@ c_eval(const char **wp)
int
c_trap(const char **wp)
{
int i;
Trap *p = sigtraps;
int i = ksh_NSIG + 1;
const char *s;
Trap *p;
if (ksh_getopt(wp, &builtin_opt, null) == '?')
return (1);
wp += builtin_opt.optind;
if (*wp == NULL) {
for (p = sigtraps, i = NSIG + 1; --i >= 0; p++)
if (p->trap != NULL) {
do {
if (p->trap) {
shf_puts("trap -- ", shl_stdout);
print_value_quoted(shl_stdout, p->trap);
shprintf(" %s\n", p->name);
}
++p;
} while (--i);
return (0);
}
/*
* Use case sensitive lookup for first arg so the
* command 'exit' isn't confused with the pseudo-signal
* 'EXIT'.
*/
/* get command */
s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL;
if (s != NULL && ksh_isdash(s))
if (getn(*wp, &i)) {
/* first argument is a signal number, reset them all */
s = NULL;
} else {
/* first argument must be a command, then */
s = *wp++;
/* reset traps? */
if (ksh_isdash(s))
s = NULL;
}
/* set/clear traps */
/* set/clear the traps */
i = 0;
while (*wp != NULL)
if ((p = gettrap(*wp++, true)) == NULL) {
while (*wp)
if (!(p = gettrap(*wp++, true, true))) {
warningf(true, "%s: %s '%s'", builtin_argv0,
"bad signal", wp[-1]);
++i;
i = 1;
} else
settrap(p, s);
return (i);

157
histrap.c
View File

@ -27,9 +27,9 @@
#include <sys/file.h>
#endif
__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.142 2015/04/29 19:11:57 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.143 2015/04/29 20:44:35 tg Exp $");
Trap sigtraps[NSIG + 1];
Trap sigtraps[ksh_NSIG + 1];
static struct sigaction Sigact_ign;
#if HAVE_PERSISTENT_HISTORY
@ -980,55 +980,53 @@ inittraps(void)
trap_exstat = -1;
/* Populate sigtraps based on sys_signame and sys_siglist. */
/*XXX this is idiotic, use a multi-key/value hashtable! */
for (i = 0; i <= NSIG; i++) {
for (i = 1; i < ksh_NSIG; i++) {
sigtraps[i].signal = i;
if (i == ksh_SIGERR) {
sigtraps[i].name = "ERR";
sigtraps[i].mess = "Error handler";
} else {
#if HAVE_SYS_SIGNAME
cs = sys_signame[i];
cs = sys_signame[i];
#else
const struct mksh_sigpair *pair = mksh_sigpairs;
while ((pair->nr != i) && (pair->name != NULL))
++pair;
cs = pair->name;
const struct mksh_sigpair *pair = mksh_sigpairs;
while ((pair->nr != i) && (pair->name != NULL))
++pair;
cs = pair->name;
#endif
if ((cs == NULL) ||
(cs[0] == '\0'))
sigtraps[i].name = shf_smprintf("%d", i);
else {
char *s;
if ((cs == NULL) ||
(cs[0] == '\0'))
sigtraps[i].name = shf_smprintf("%d", i);
else {
char *s;
/* this is not optimal, what about SIGSIG1? */
if ((cs[0] & 0xDF) == 'S' &&
(cs[1] & 0xDF) == 'I' &&
(cs[2] & 0xDF) == 'G' &&
cs[3] != '\0') {
/* skip leading "SIG" */
cs += 3;
}
strdupx(s, cs, APERM);
sigtraps[i].name = s;
while ((*s = ksh_toupper(*s)))
++s;
/* this is not optimal, what about SIGSIG1? */
if (ksh_eq(cs[0], 'S', 's') &&
ksh_eq(cs[1], 'I', 'i') &&
ksh_eq(cs[2], 'G', 'g') &&
cs[3] != '\0') {
/* skip leading "SIG" */
cs += 3;
}
#if HAVE_SYS_SIGLIST
sigtraps[i].mess = sys_siglist[i];
#elif HAVE_STRSIGNAL
sigtraps[i].mess = strsignal(i);
#else
sigtraps[i].mess = NULL;
#endif
if ((sigtraps[i].mess == NULL) ||
(sigtraps[i].mess[0] == '\0'))
sigtraps[i].mess = shf_smprintf("%s %d",
"Signal", i);
strdupx(s, cs, APERM);
sigtraps[i].name = s;
while ((*s = ksh_toupper(*s)))
++s;
}
#if HAVE_SYS_SIGLIST
sigtraps[i].mess = sys_siglist[i];
#elif HAVE_STRSIGNAL
sigtraps[i].mess = strsignal(i);
#else
sigtraps[i].mess = NULL;
#endif
if ((sigtraps[i].mess == NULL) ||
(sigtraps[i].mess[0] == '\0'))
sigtraps[i].mess = shf_smprintf("%s %d",
"Signal", i);
}
/* our name for signal 0 */
sigtraps[ksh_SIGEXIT].signal = ksh_SIGEXIT;
sigtraps[ksh_SIGEXIT].name = "EXIT";
sigtraps[ksh_SIGEXIT].mess = "Exit trap";
sigtraps[ksh_SIGERR].signal = ksh_SIGERR;
sigtraps[ksh_SIGERR].name = "ERR";
sigtraps[ksh_SIGERR].mess = "Error handler";
(void)sigemptyset(&Sigact_ign.sa_mask);
Sigact_ign.sa_flags = 0; /* interruptible */
@ -1076,21 +1074,24 @@ alarm_catcher(int sig MKSH_A_UNUSED)
}
Trap *
gettrap(const char *cs, bool igncase)
gettrap(const char *cs, bool igncase, bool allsigs)
{
int i;
Trap *p;
char *as;
if (ksh_isdigit(*cs)) {
return ((getn(cs, &i) && 0 <= i && i < NSIG) ?
/* signal number (1..ksh_NSIG) or 0? */
if (ksh_isdigit(*cs))
return ((getn(cs, &i) && 0 <= i && i < ksh_NSIG) ?
(&sigtraps[i]) : NULL);
}
/* do a lookup by name then */
/* this breaks SIGSIG1, but we do that above anyway */
if ((cs[0] & 0xDF) == 'S' &&
(cs[1] & 0xDF) == 'I' &&
(cs[2] & 0xDF) == 'G' &&
if (ksh_eq(cs[0], 'S', 's') &&
ksh_eq(cs[1], 'I', 'i') &&
ksh_eq(cs[2], 'G', 'g') &&
cs[3] != '\0') {
/* skip leading "SIG" */
cs += 3;
@ -1105,14 +1106,24 @@ gettrap(const char *cs, bool igncase)
} else
as = NULL;
/* this is idiotic, we really want a hashtable here */
p = sigtraps;
for (i = 0; i <= NSIG; i++) {
i = ksh_NSIG + 1;
do {
if (!strcmp(p->name, cs))
goto found;
++p;
}
p = NULL;
} while (--i);
goto notfound;
found:
if (!allsigs) {
if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR) {
notfound:
p = NULL;
}
}
afree(as, ATEMP);
return (p);
}
@ -1156,14 +1167,16 @@ intrcheck(void)
int
fatal_trap_check(void)
{
int i;
Trap *p;
Trap *p = sigtraps;
int i = ksh_NSIG + 1;
/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
do {
if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
/* return value is used as an exit code */
return (128 + p->signal);
++p;
} while (--i);
return (0);
}
@ -1175,13 +1188,15 @@ fatal_trap_check(void)
int
trap_pending(void)
{
int i;
Trap *p;
Trap *p = sigtraps;
int i = ksh_NSIG + 1;
for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
do {
if (p->set && ((p->trap && p->trap[0]) ||
((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
return (p->signal);
++p;
} while (--i);
return (0);
}
@ -1192,8 +1207,8 @@ trap_pending(void)
void
runtraps(int flag)
{
int i;
Trap *p;
Trap *p = sigtraps;
int i = ksh_NSIG + 1;
if (ksh_tmout_state == TMOUT_LEAVING) {
ksh_tmout_state = TMOUT_EXECUTING;
@ -1212,10 +1227,12 @@ runtraps(int flag)
if (flag & TF_FATAL)
fatal_trap = 0;
++trap_nested;
for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
do {
if (p->set && (!flag ||
((p->flags & flag) && p->trap == NULL)))
runtrap(p, false);
++p;
} while (--i);
if (!--trap_nested)
runtrap(NULL, true);
}
@ -1284,30 +1301,34 @@ runtrap(Trap *p, bool is_last)
void
cleartraps(void)
{
int i;
Trap *p;
Trap *p = sigtraps;
int i = ksh_NSIG + 1;
trap = 0;
intrsig = 0;
fatal_trap = 0;
for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
do {
p->set = 0;
if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
settrap(p, NULL);
}
++p;
} while (--i);
}
/* restore signals just before an exec(2) */
void
restoresigs(void)
{
int i;
Trap *p;
Trap *p = sigtraps;
int i = ksh_NSIG + 1;
for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
do {
if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
SS_RESTORE_CURR|SS_FORCE);
++p;
} while (--i);
}
void

70
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.365 2015/04/29 20:13:26 tg Exp $
.\" $MirOS: src/bin/mksh/mksh.1,v 1.366 2015/04/29 20:44:36 tg Exp $
.\" $OpenBSD: ksh.1,v 1.159 2015/03/25 12:10:52 jca Exp $
.\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -4631,54 +4631,60 @@ The format of the output is:
0m0.00s 0m0.00s
.Ed
.Pp
.It Ic trap Ar n Op Ar signal ...
If the first operand is a decimal unsigned integer, this resets all
specified signals to the default action, i.e. is the same as calling
.Ic trap
with a minus sign
.Pq Sq \-
as
.Ar handler ,
followed by the arguments
.Pq Ar n Op Ar signal ... ,
all of which are treated as signals.
.Pp
.It Ic trap Op Ar handler signal ...
Sets a trap handler that is to be executed when any of the specified signals are
received.
Sets a trap handler that is to be executed when any of the specified
.Ar signal Ns s
are received.
.Ar handler
is either a
.Dv NULL
string, indicating the signals are to be ignored, a minus sign
is either an empty string, indicating the signals are to be ignored,
a minus sign
.Pq Sq \- ,
indicating that the default action is to be taken for the signals (see
.Xr signal 3 ) ,
or a string containing shell commands to be evaluated and executed at the first
opportunity (i.e. when the current command completes, or before printing the
next
indicating that the default action is to be taken for the signals
.Pq see Xr signal 3 ,
or a string containing shell commands to be executed at the first opportunity
(i.e. when the current command completes, or before printing the next
.Ev PS1
prompt) after receipt of one of the signals.
.Ar signal
is the name of a signal (e.g.\&
.Dv PIPE
or
.Dv ALRM )
is the name of a signal
.Pq e.g.\& Dv PIPE or Dv ALRM
or the number of the signal (see the
.Ic kill \-l
.Ic kill Fl l
command above).
.Pp
There are two special signals:
.Dv EXIT
(also known as 0) which is executed when the shell is about to exit, and
.Pq also known as 0 ,
which is executed when the shell is about to exit, and
.Dv ERR ,
which is executed after an error occurs (an error is something that would cause
the shell to exit if the
.Fl e
which is executed after an error occurs; an error is something
that would cause the shell to exit if the
.Ic set Fl e
or
.Ic errexit
option were set \*(en see the
.Ic set
command above).
.Ic set Fl o Ic errexit
option were set.
.Dv EXIT
handlers are executed in the environment of the last executed command.
Note
that for non-interactive shells, the trap handler cannot be changed for signals
that were ignored when the shell started.
.Pp
With no arguments,
Note that, for non-interactive shells, the trap handler cannot be changed
for signals that were ignored when the shell started.
.Pp
With no arguments, the current state of the traps that have been set since
the shell started is shown as a series of
.Ic trap
lists, as a series of
.Ic trap
commands, the current state of the traps that have been set since the shell
started.
commands.
Note that the output of
.Ic trap
cannot be usefully piped to another process (an artifact of the fact that

59
sh.h
View File

@ -169,9 +169,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.728 2015/04/29 20:07:34 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.729 2015/04/29 20:44:37 tg Exp $");
#endif
#define MKSH_VERSION "R51 2015/04/19"
#define MKSH_VERSION "R51 2015/04/29"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@ -329,24 +329,47 @@ struct rusage {
#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
#endif
#ifndef NSIG
#if defined(_NSIG)
#define NSIG _NSIG
/* determine ksh_NSIG: first, use the traditional definitions */
#undef ksh_NSIG
#if defined(NSIG)
#define ksh_NSIG NSIG
#elif defined(_NSIG)
#define ksh_NSIG _NSIG
#elif defined(SIGMAX)
#define NSIG (SIGMAX+1)
#define ksh_NSIG (SIGMAX + 1)
#elif defined(_SIGMAX)
#define NSIG (_SIGMAX+1)
#define ksh_NSIG (_SIGMAX + 1)
#elif defined(NSIG_MAX)
#define ksh_NSIG NSIG_MAX
#else
# error Please have your platform define NSIG.
#define NSIG 64
#endif
#endif
/* get rid of this (and awk/printf(1) in Build.sh) later */
#if (NSIG < 1)
/* range-check them */
#if (ksh_NSIG < 1)
# error Your NSIG value is not positive.
#unset NSIG
#define NSIG 64
#undef ksh_NSIG
#endif
/* second, see if the new POSIX definition is available */
#ifdef NSIG_MAX
#if (NSIG_MAX < 2)
/* and usable */
# error Your NSIG_MAX value is too small.
#undef NSIG_MAX
#elif (ksh_NSIG > NSIG_MAX)
/* and realistic */
# error Your NSIG value is larger than your NSIG_MAX value.
#undef NSIG_MAX
#else
/* since its usable, prefer it */
#undef ksh_NSIG
#define ksh_NSIG NSIG_MAX
#endif
/* if NSIG_MAX is now still defined, use sysconf(_SC_NSIG) at runtime */
#endif
/* third, for cpp without the error directive, default */
#ifndef ksh_NSIG
#define ksh_NSIG 64
#endif
@ -878,13 +901,13 @@ typedef struct trap {
#define SS_USER BIT(4) /* user is doing the set (ie, trap command) */
#define SS_SHTRAP BIT(5) /* trap for internal use (ALRM, CHLD, WINCH) */
#define ksh_SIGEXIT 0 /* for trap EXIT */
#define ksh_SIGERR NSIG /* for trap ERR */
#define ksh_SIGEXIT 0 /* for trap EXIT */
#define ksh_SIGERR ksh_NSIG /* for trap ERR */
EXTERN volatile sig_atomic_t trap; /* traps pending? */
EXTERN volatile sig_atomic_t intrsig; /* pending trap interrupts command */
EXTERN volatile sig_atomic_t fatal_trap; /* received a fatal signal */
extern Trap sigtraps[NSIG+1];
extern Trap sigtraps[ksh_NSIG + 1];
/* got_winch = 1 when we need to re-adjust the window size */
#ifdef SIGWINCH
@ -1745,7 +1768,7 @@ int findhist(int, int, const char *, bool) MKSH_A_PURE;
char **hist_get_newest(bool);
void inittraps(void);
void alarm_init(void);
Trap *gettrap(const char *, bool);
Trap *gettrap(const char *, bool, bool);
void trapsig(int);
void intrcheck(void);
int fatal_trap_check(void);