Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Post History

77%
+5 −0
Q&A How can I use SIGUSR1 or SIGUSR2 without risk of terminating the process?

If you are about to start the tool Start the tool from a process that already blocks or ignores SIGUSR1 and/or SIGUSR2 and let the tool inherit these settings. A blocked signal, when generated, w...

posted 1y ago by Kamil Maciorowski‭  ·  edited 1y ago by Kamil Maciorowski‭

Answer
#2: Post edited by user avatar Kamil Maciorowski‭ · 2023-06-30T08:23:37Z (over 1 year ago)
  • ### If you are about to start the tool
  • Start the tool from a process that already blocks or ignores SIGUSR1 and/or SIGUSR2 and let the tool inherit these settings.
  • A blocked signal, when generated, will not be delivered to the process until the process unblocks it. An ignored signal, when delivered, will cause no action.
  • After registering a handler for a signal, a sane tool should unignore and unblock the signal. Blocking SIGUSR1 until the tool is ready to handle it is a good solution to the problem in question. It's crucial to start blocking (or ignoring) in the parent process, so the child is immune to the signal from its very birth and there is no time window when SIGUSR1 can terminate it.
  • The example with GNU `dd` uses shell code to start `dd` and `kill`. Shells I know provide no way to block a signal; with `trap` they can ignore a signal. The snippets below use the flawed code from the question, plus a prior `trap`.
  • The default behavior (like in the question; the explicit `trap` changes nothing\*):
  • trap - USR1 # default handling, SIGUSR1 causes Term
  • dd if=/dev/zero of=/dev/null & kill -s USR1 "$!"; sleep 1; kill -s USR1 "$!"; sleep 1; kill "$!"
  • The fix:
  • trap '' USR1 # ignoring SIGUSR1
  • dd if=/dev/zero of=/dev/null & kill -s USR1 "$!"; sleep 1; kill -s USR1 "$!"; sleep 1; kill "$!"
  • In the fixed code the first `kill` will most likely generate SIGUSR1 that would otherwise terminate `dd`, but here it will be ignored. The second `kill` will most likely generate SIGUSR1 late enough, so it will be handled. The third `kill` will kill `dd` as intended.
  • If we managed to block the signal then the first SIGUSR1 would be handled (as soon as possible) rather than ignored; this would be even better. I cannot do this in a shell though. If you run a program (as opposed to shell code) to start your tool and send SIGUSR1 or SIGUSR2 to it, then probably you have means to block the signal in the parent process.
  • One way or another (i.e. with ignoring or blocking the signal), this is how you can send SIGUSR1 or SIGUSR2 to your tool without any risk of terminating it.
  • ---
  • <sup>\* Strictly `trap - USR1` resets the signal handling to what it was when the shell started. This is not necessarily the default described in `man 7 signal`, as some ancestor of the shell may have interfered. I guess for SIGUSR1 such interference is quite unlikely, unless the shell or one of its ancestors is also a tool the question is about and our fix has been applied to it.</sup>
  • ---
  • ### If the tool is already running
  • If the tool is already running, or if you cannot change the way it's started, you can delay sending SIGUSR1 or SIGUSR2 until you're sure the signal will be caught. You can tell this by parsing the output of `grep SigCgt: /proc/PID/status`, the information is "encoded" there. I'm not a programmer and I don't know if there are functions that allow you to easily retrieve the information in some plain form, I won't elaborate.
  • In general such approach is prone to [TOCTOU][1], but if the tool is sane enough not to get back to the default `Term` after registering a handler, then once you notice there is a handler, you're safe.
  • [1]: https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use
  • ### If you are about to start the tool
  • Start the tool from a process that already blocks or ignores SIGUSR1 and/or SIGUSR2 and let the tool inherit these settings.
  • A blocked signal, when generated, will not be delivered to the process until the process unblocks it. An ignored signal, when delivered, will cause no action.
  • After registering a handler for a signal, a sane tool should unignore and unblock the signal. Blocking SIGUSR1 until the tool is ready to handle it is a good solution to the problem in question. It's crucial to start blocking (or ignoring) in the parent process, so the child is immune to the signal from its very birth and there is no time window when SIGUSR1 can terminate it.
  • The example with GNU `dd` uses shell code to start `dd` and `kill`. Shells I know provide no way to block a signal; with `trap` they can ignore a signal. The snippets below use the flawed code from the question, plus a prior `trap`.
  • The default behavior (like in the question; the explicit `trap` changes nothing\*):
  • trap - USR1 # default handling, SIGUSR1 causes Term
  • dd if=/dev/zero of=/dev/null & kill -s USR1 "$!"; sleep 1; kill -s USR1 "$!"; sleep 1; kill "$!"
  • The fix:
  • trap '' USR1 # ignoring SIGUSR1
  • dd if=/dev/zero of=/dev/null & kill -s USR1 "$!"; sleep 1; kill -s USR1 "$!"; sleep 1; kill "$!"
  • In the fixed code the first `kill` will most likely generate SIGUSR1 that would otherwise terminate `dd`, but here it will be ignored. The second `kill` will most likely generate SIGUSR1 late enough, so it will be handled. The third `kill` will kill `dd` as intended.
  • If we managed to block the signal then the first SIGUSR1 would be handled (as soon as possible) rather than ignored; this would be even better. I cannot do this in a shell though. If you run a program (as opposed to shell code) to start your tool and send SIGUSR1 or SIGUSR2 to it, then probably you have means to block the signal in the parent process.
  • One way or another (i.e. with ignoring or blocking the signal), this is how you can send SIGUSR1 or SIGUSR2 to your tool without any risk of terminating it.
  • ---
  • <sup>\* Strictly `trap - USR1` resets the signal handling to what it was when the shell started. This is not necessarily the default described in `man 7 signal`, as some ancestor of the shell may have interfered. I guess for SIGUSR1 such interference is quite unlikely, unless the shell or one of its ancestors is also a tool the question is about and our fix has been applied to it.</sup>
  • ---
  • ### If the tool is already running
  • If the tool is already running, or if you cannot change the way it's started, you can delay sending SIGUSR1 or SIGUSR2 until you're sure the signal will be caught. You can tell this by parsing the output of `grep ^SigCgt: /proc/PID/status`, the information is "encoded" there. I'm not a programmer and I don't know if there are functions that allow you to easily retrieve the information in some plain form, I won't elaborate.
  • In general such approach is prone to [TOCTOU][1], but if the tool is sane enough not to get back to the default `Term` after registering a handler, then once you notice there is a handler, you're safe.
  • [1]: https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use
#1: Initial revision by user avatar Kamil Maciorowski‭ · 2023-06-30T08:17:51Z (over 1 year ago)
### If you are about to start the tool

Start the tool from a process that already blocks or ignores SIGUSR1 and/or SIGUSR2 and let the tool inherit these settings.

A blocked signal, when generated, will not be delivered to the process until the process unblocks it. An ignored signal, when delivered, will cause no action.

After registering a handler for a signal, a sane tool should unignore and unblock the signal. Blocking SIGUSR1 until the tool is ready to handle it is a good solution to the problem in question. It's crucial to start blocking (or ignoring) in the parent process, so the child is immune to the signal from its very birth and there is no time window when SIGUSR1 can terminate it.

The example with GNU `dd` uses shell code to start `dd` and `kill`. Shells I know provide no way to block a signal; with `trap` they can ignore a signal. The snippets below use the flawed code from the question, plus a prior `trap`.

The default behavior (like in the question; the explicit `trap` changes nothing\*):

    trap - USR1        # default handling, SIGUSR1 causes Term
    dd if=/dev/zero of=/dev/null & kill -s USR1 "$!"; sleep 1; kill -s USR1 "$!"; sleep 1; kill "$!"

The fix:

    trap '' USR1       # ignoring SIGUSR1
    dd if=/dev/zero of=/dev/null & kill -s USR1 "$!"; sleep 1; kill -s USR1 "$!"; sleep 1; kill "$!"

In the fixed code the first `kill` will most likely generate SIGUSR1 that would otherwise terminate `dd`, but here it will be ignored. The second `kill` will most likely generate SIGUSR1 late enough, so it will be handled. The third `kill` will kill `dd` as intended.

If we managed to block the signal then the first SIGUSR1 would be handled (as soon as possible) rather than ignored; this would be even better. I cannot do this in a shell though. If you run a program (as opposed to shell code) to start your tool and send SIGUSR1 or SIGUSR2 to it, then probably you have means to block the signal in the parent process.

One way or another (i.e. with ignoring or blocking the signal), this is how you can send SIGUSR1 or SIGUSR2 to your tool without any risk of terminating it.

---

<sup>\* Strictly `trap - USR1` resets the signal handling to what it was when the shell started. This is not necessarily the default described in `man 7 signal`, as some ancestor of the shell may have interfered. I guess for SIGUSR1 such interference is quite unlikely, unless the shell or one of its ancestors is also a tool the question is about and our fix has been applied to it.</sup>

---

### If the tool is already running

If the tool is already running, or if you cannot change the way it's started, you can delay sending SIGUSR1 or SIGUSR2 until you're sure the signal will be caught. You can tell this by parsing the output of `grep SigCgt: /proc/PID/status`, the information is "encoded" there. I'm not a programmer and I don't know if there are functions that allow you to easily retrieve the information in some plain form, I won't elaborate.

In general such approach is prone to [TOCTOU][1], but if the tool is sane enough not to get back to the default `Term` after registering a handler, then once you notice there is a handler, you're safe.

  [1]: https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use