Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion builtin/trap_osh.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python2
from __future__ import print_function

from signal import SIG_DFL, SIGINT, SIGWINCH
from signal import SIG_DFL, SIG_IGN, SIGINT, SIGWINCH

from _devbuild.gen import arg_types
from _devbuild.gen.runtime_asdl import cmd_value
Expand Down Expand Up @@ -49,6 +49,7 @@ def __init__(self, signal_safe):
self.signal_safe = signal_safe
self.hooks = {} # type: Dict[str, command_t]
self.traps = {} # type: Dict[int, command_t]
self.ignored_sigs = {} # type: Dict[int, bool] # Signals set to SIG_IGN

def ClearForSubProgram(self, inherit_errtrace):
# type: (bool) -> None
Expand All @@ -62,6 +63,7 @@ def ClearForSubProgram(self, inherit_errtrace):
self.hooks['ERR'] = hook_err

self.traps.clear()
self.ignored_sigs.clear()

def GetHook(self, hook_name):
# type: (str) -> command_t
Expand Down Expand Up @@ -93,6 +95,7 @@ def _RemoveUserTrap(self, sig_num):
# type: (int) -> None

mylib.dict_erase(self.traps, sig_num)
mylib.dict_erase(self.ignored_sigs, sig_num)

if sig_num == SIGINT:
self.signal_safe.SetSigIntTrapped(False)
Expand All @@ -110,6 +113,20 @@ def _RemoveUserTrap(self, sig_num):
# shells do.
iolib.sigaction(sig_num, SIG_DFL)

def _IgnoreSignal(self, sig_num):
# type: (int) -> None
"""Set a signal to be ignored (SIG_IGN)."""
mylib.dict_erase(self.traps, sig_num)
self.ignored_sigs[sig_num] = True

if sig_num == SIGINT:
# Don't disturb SIGINT handling
self.signal_safe.SetSigIntTrapped(False)
elif sig_num == SIGWINCH:
self.signal_safe.SetSigWinchCode(iolib.UNTRAPPED_SIGWINCH)
else:
iolib.sigaction(sig_num, SIG_IGN)

def AddItem(self, parsed_id, handler):
# type: (str, command_t) -> None
"""Add trap or hook, parsed to EXIT or INT (not 0 or SIGINT)"""
Expand Down Expand Up @@ -295,6 +312,13 @@ def _PrintState(self):
# Print in order of signal number
n = signal_def.MaxSigNumber() + 1
for sig_num in xrange(n):
# Check for explicitly ignored signals
if sig_num in self.trap_state.ignored_sigs:
sig_name = signal_def.GetName(sig_num)
assert sig_name is not None
print("trap -- '' %s" % sig_name)
continue

handler = self.trap_state.GetTrap(sig_num)
if handler is None:
continue
Expand Down Expand Up @@ -346,6 +370,31 @@ def _RemoveTheRest(self, arg_r, allow_legacy=True):
self.trap_state.RemoveItem(parsed_id)
arg_r.Next()

def _IgnoreTheRest(self, arg_r):
# type: (args.Reader) -> int
"""Ignore (set to SIG_IGN) all remaining signal arguments"""
while not arg_r.AtEnd():
arg_str, arg_loc = arg_r.Peek2()
parsed_id = ParseSignalOrHook(arg_str, arg_loc, allow_legacy=True)

if parsed_id in _HOOK_NAMES:
self.errfmt.Print_(
"trap: can't ignore hook %r" % arg_str,
blame_loc=arg_loc)
return 2

sig_num = signal_def.GetNumber(parsed_id)
assert sig_num is not signal_def.NO_SIGNAL

if parsed_id == 'STOP' or parsed_id == 'KILL':
self.errfmt.Print_("Signal %r can't be handled" % arg_str,
blame_loc=arg_loc)
return 2

self.trap_state._IgnoreSignal(sig_num)
arg_r.Next()
return 0

def Run(self, cmd_val):
# type: (cmd_value.Argv) -> int
attrs, arg_r = flag_util.ParseCmdVal('trap',
Expand All @@ -357,6 +406,9 @@ def Run(self, cmd_val):
cmd_frag = typed_args.RequiredBlockAsFrag(cmd_val)
return self._AddTheRest(arg_r, cmd_frag, allow_legacy=False)

if arg.ignore: # trap --ignore
return self._IgnoreTheRest(arg_r)

if arg.remove: # trap --remove
self._RemoveTheRest(arg_r, allow_legacy=False)
return 0
Expand Down Expand Up @@ -396,6 +448,10 @@ def Run(self, cmd_val):

arg_r.Next()

# If first arg is empty string '', ignore the specified signals
if first_arg == '':
return self._IgnoreTheRest(arg_r)

# Legacy behavior for only one arg: 'trap SIGNAL' removes the handler
if arg_r.AtEnd():
parsed_id = ParseSignalOrHook(first_arg, first_loc)
Expand Down
1 change: 1 addition & 0 deletions frontend/flag_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@
TRAP_SPEC.ShortFlag('-l')
TRAP_SPEC.LongFlag('--add')
TRAP_SPEC.LongFlag('--remove')
TRAP_SPEC.LongFlag('--ignore') # YSH: ignore signals (same as trap '')

KILL_SPEC = FlagSpec('kill')
KILL_SPEC.ShortFlag('-l', args.Bool)
Expand Down
46 changes: 46 additions & 0 deletions spec/builtin-trap.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -561,3 +561,49 @@ ok07
trap-exit
failure
## END

#### trap '' sets handler to empty string (SIG_IGN)

trap '' USR1
trap

## STDOUT:
trap -- '' SIGUSR1
## END

#### trap '' with multiple signals

trap '' USR1 USR2
trap

## STDOUT:
trap -- '' SIGUSR1
trap -- '' SIGUSR2
## END

#### trap '' rejects hooks

trap '' EXIT
echo should not get here

## STDOUT:
## END
## status: 2

#### trap '' rejects STOP signal

trap '' STOP
echo should not get here

## STDOUT:
## END
## status: 2

#### trap '' rejects KILL signal

trap '' KILL
echo should not get here

## STDOUT:
## END
## status: 2
26 changes: 26 additions & 0 deletions spec/ysh-builtin-trap.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,29 @@ register
## STDOUT:
x = global
## END

#### trap --ignore INT USR1

trap --ignore INT USR1
trap -p

## STDOUT:
trap -- '' SIGINT
trap -- '' SIGUSR1
## END

#### trap --ignore rejects hooks

trap --ignore EXIT

## STDOUT:
## END
## status: 2

#### trap --ignore rejects STOP

trap --ignore STOP

## STDOUT:
## END
## status: 2
Loading