Skip to content

v0.x: a signal-ended run dies by its signal

A GTB tool stopped by SIGINT, SIGTERM or SIGHUP now drains and then dies by that signal, instead of exiting with status 128 + signum (spec 0207). Nothing in a tool's code changes. What changes is what the process that started it observes.

What changes

  • A process manager sees a clean stop. systemd counts death by SIGINT, SIGTERM or SIGHUP as success, but did not count exit status 143, so a unit stopped with systemctl stop used to end failed. It no longer does, and a SuccessExitStatus=143 line added to work around this can go. Anything reading the wait status (waitpid, Python's subprocess, Go's ProcessState) now sees a death by the signal, not exit status 130 or 143. Go's ProcessState.ExitCode() reports -1 for it.
  • A shell's $? is unchanged: still 130 after SIGINT and 143 after SIGTERM, and now 129 after SIGHUP. A bash script that runs the tool now stops when the tool dies by SIGINT, the way it does for any program interrupted by Ctrl-C. An exit status of 130 did not trigger that.
  • SIGHUP drains. A command that loses its terminal, for example over a dropped SSH session, now cancels its context and drains like SIGINT, where it used to die at once. Output it writes to that terminal while draining may be lost.
  • An inherited ignore stays. A tool started with SIGINT ignored (a background job, tool &, in a script) or with SIGHUP ignored (nohup) no longer subscribes to that signal, so it no longer drains on it. It used to, because subscribing undid the ignore. SIGTERM is unaffected.
  • A failed drain is reported. If a command returns an error while draining that is not the cancellation (nil, or an error wrapping context.Canceled), the error is reported like any other failure and the run exits with its code. It used to be discarded in favour of 128 + signum.
  • Windows is unchanged apart from the failed-drain rule: a run interrupted there still exits 128 + signum.

What to check

  • A test or script that asserts exit status 130 or 143 through a wait status, rather than through a shell's $?, needs to assert a death by the signal instead.
  • A command that wraps its cancellation in an error of its own must wrap context.Canceled (%w), or its stop will read as a failed drain.