How can I verify whether a running process will catch a signal, or ignore it, or block it? Ideally I'd like to see a list of signals, or at least not have to actually send the signal to check.
9 Answers
Under Linux, you can find the PID of your process, then look at /proc/$PID/status
. It contains lines describing which signals are blocked (SigBlk), ignored (SigIgn), or caught (SigCgt).
# cat /proc/1/status
...
SigBlk: 0000000000000000
SigIgn: fffffffe57f0d8fc
SigCgt: 00000000280b2603
...
The number to the right is a bitmask. If you convert it from hex to binary, each 1-bit represents a caught signal, counting from right to left starting with 1. So by interpreting the SigCgt line, we can see that my init
process is catching the following signals:
00000000280b2603 ==> 101000000010110010011000000011
| | | || | || |`-> 1 = SIGHUP
| | | || | || `--> 2 = SIGINT
| | | || | |`----------> 10 = SIGUSR1
| | | || | `-----------> 11 = SIGSEGV
| | | || `--------------> 14 = SIGALRM
| | | |`-----------------> 17 = SIGCHLD
| | | `------------------> 18 = SIGCONT
| | `--------------------> 20 = SIGTSTP
| `----------------------------> 28 = SIGWINCH
`------------------------------> 30 = SIGPWR
(I found the number-to-name mapping by running kill -l
from bash.)
EDIT: And by popular demand, a script, in POSIX sh.
sigparse () {
i=0
# bits="$(printf "16i 2o %X p" "0x$1" | dc)" # variant for busybox
bits="$(printf "ibase=16; obase=2; %X\n" "0x$1" | bc)"
while [ -n "$bits" ] ; do
i="$(expr "$i" + 1)"
case "$bits" in
*1) printf " %s(%s)" "$(kill -l "$i")" "$i" ;;
esac
bits="${bits%?}"
done
}
grep "^Sig...:" "/proc/$1/status" | while read a b ; do
printf "%s%s\n" "$a" "$(sigparse "$b")"
done # | fmt -t # uncomment for pretty-printing
-
2If a signal is listed under
SigBlk
does it also appear inSigCgt
? Because by blocking it, it just means the signal will be resent a little bit later right and the needs to be caught? Commented May 29, 2015 at 7:28 -
No, you can block a signal without being ready to catch it. If you don't catch a signal, a default action will happen depending on the signal (usually process termination). If you want more detail you ought to open a question.– JanderCommented May 29, 2015 at 22:21
-
What's the use of a POSIX version of a script reading from
/proc
? It's only going to work on Linux... Andlocal
isn't POSIX. Well, it kinda is, but its effect is "unspecified".– Kusalananda ♦Commented Jan 7, 2017 at 19:42 -
3@Kusalananda: Linux doesn't imply Bash -- for example, small embedded platforms often use Busybox -- but POSIX compliance is a near guarantee for any modern
/bin/sh
. You're right aboutlocal
; I'll clean that up.– JanderCommented Jan 8, 2017 at 3:52 -
@Jander Fair point. I confess to making a hasty assumption regarding Bash and Linux.– Kusalananda ♦Commented Jan 8, 2017 at 8:48
On Solaris, run psig
on the process id to get a list of signals and how they'll be handled.
For instance:
bash-4.2$ psig $$
11088: bash
HUP caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
INT caught sigint_sighandler 0
QUIT ignored
ILL caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
TRAP caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
ABRT caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
EMT caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
FPE caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
KILL default
BUS caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
SEGV caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
SYS caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
PIPE caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
ALRM caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
TERM ignored
USR1 caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
USR2 caught termsig_sighandler 0 HUP,INT,ILL,TRAP,ABRT,EMT,FPE,BUS,SEGV,SYS,PIPE,ALRM,TERM,USR1,USR2,VTALRM,XCPU,XFSZ,LOST
CLD blocked,caught 0x4898e8 RESTART
PWR default
WINCH caught sigwinch_sighandler 0
[...]
which shows that SIGHUP, SIGILL, etc. will all be caught by the same signal handler function termsig_sighandler
, which will be run without using any of the flags that could be set via sigaction
, and all the signals that will be temporarily masked while the signal handler is running (in this case all the ones using the same signal handler, so it's not reentered while already running). You can also see that SIGQUIT & SIGTERM will be ignored, SIGKILL & SIGPWR use the system default signal actions, and SIGCLD specifies the RESTART flag, so if its signal handler interrupts a system call, the syscall will be restarted.
(This answer is similar to @user18096's answer, in that it creates a script around @Jander's answer.)
I've written a psig script
to take a PID (or all PIDs) and create human-readable output from the signal masks in /proc/<PID>/status
.
Example output:
% ./psig -a
[ 1] Signals Queued: 8/773737
[ 1] Signals Pending:
[ 1] Signals Pending (Shared):
[ 1] Signals Blocked:
[ 1] Signals Ignored: SIGPIPE
[ 1] Signals Caught: SIGHUP,SIGINT,SIGABRT,SIGUSR1,SIGSEGV,SIGALRM,SIGTERM,SIGCHLD,SIGPWR
...
[ 31001] Signals Queued: 0/773737
[ 31001] Signals Pending:
[ 31001] Signals Pending (Shared):
[ 31001] Signals Blocked: SIGHUP,SIGINT,SIGQUIT,SIGILL,SIGTRAP,SIGABRT,SIGBUS,SIGFPE,SIGUSR1,SIGUSR2,SIGPIPE,SIGALRM,SIGTERM,SIGSTKFLT,SIGCHLD,SIGCONT,SIGTSTP,SIGTTIN,SIGTTOU,SIGURG,SIGXCPU,SIGXFSZ,SIGPROF,SIGWINCH,SIGIO,SIGPWR,SIGSYS,SIGRTMIN,SIGRTMIN+1,SIGRTMIN+2,SIGRTMIN+3,SIGRTMIN+4,SIGRTMIN+5,SIGRTMIN+6,SIGRTMIN+7,SIGRTMIN+8,SIGRTMIN+9,SIGRTMIN+10,SIGRTMIN+11,SIGRTMIN+12,SIGRTMIN+13,SIGRTMIN+14,SIGRTMIN+15,SIGRTMAX-14,SIGRTMAX-13,SIGRTMAX-12,SIGRTMAX-11,SIGRTMAX-10,SIGRTMAX-9,SIGRTMAX-8,SIGRTMAX-7,SIGRTMAX-6,SIGRTMAX-5,SIGRTMAX-4,SIGRTMAX-3,SIGRTMAX-2,SIGRTMAX-1,SIGRTMAX
[ 31001] Signals Ignored: SIGHUP,SIGINT,SIGQUIT,SIGPIPE,SIGXFSZ
[ 31001] Signals Caught: SIGBUS,SIGUSR1,SIGSEGV,SIGUSR2,SIGALRM,SIGTERM,SIGVTALRM
Caveats:
- This is a Linux specific answer.
- Might need a relatively new Python version to run the script, it uses
with
andOrderedDict
.
I keep coming back to @Jander's pretty answer hoping for a copy-and-paste decoder when faced with the like of:
user@machine:~$ grep Sig...: /proc/18475/status
SigPnd: 0000000000000000
SigBlk: fffffffe7dfbfaff
SigIgn: 0000000000001000
SigCgt: 0000000182006e47
user@machine:~$
Guess I'll have to knock something up... say:
user@machine:~$ ruby -wn - /proc/18475/status <<'EOF'
if $_.match(/Sig(Pnd|Blk|Ign|Cgt):\s([0-9a-f]{16})/) == nil
next
end
field = $1
mask = $2.to_i(16)
names = []
Signal.list().each_pair() {
|name, number|
if number == 0
# "EXIT" => 0
next
end
if (mask & (1 << (number - 1))) == 0
next
end
names << name
}
puts("Sig#{field}: #{names.join(" | ")}")
EOF
SigPnd:
SigBlk: HUP | INT | QUIT | ILL | TRAP | IOT | ABRT | FPE | BUS | SYS | PIPE | ALRM | TERM | URG | TSTP | CONT | CHLD | CLD | TTIN | TTOU | IO | XCPU | XFSZ | PROF | WINCH | USR1 | USR2 | PWR | POLL
SigIgn: PIPE
SigCgt: HUP | INT | QUIT | BUS | SEGV | ALRM | TERM | VTALRM | USR1 | USR2
user@machine:~$
I wanted it to be somewhat legible, but that's made it a bit clumsier to invoke than I'd like, so, thanks to @alanc's suggestion, I'll save it as ~/bin/psig.
Use this(link broken) this
library to get infos about the jobs, which are running.
There is a special field in the struct Job
for the signals, called sigCgt
You may use something like this:
#include"read_proc.h"
int main(void)
{
struct Root * rt=read_proc();
struct Job * jb=rt->first->job;
printf("%ull\n",jb->sigCgt);
return 0;
}
-
-
1@MichaelFox see my edit. The user has deleted his account. The new link points to the same project Commented Jan 7, 2017 at 17:34
On FreeBSD, use procstat -i <PID>
to see which signals are ignored by the process. Similarly, procstat -j <PID>
to see which signals are blocked by the process threads. Both commands show if a signal is pending.
Sample output:
$ procstat -i 38540
PID COMM SIG FLAGS
38540 nsulfd HUP -I-
38540 nsulfd INT -I-
38540 nsulfd QUIT -I-
38540 nsulfd ILL ---
38540 nsulfd TRAP ---
...
$ procstat -j 38540
PID TID COMM SIG FLAGS
38540 101220 nsulfd HUP --
38540 101220 nsulfd INT --
38540 101220 nsulfd QUIT -B
38540 101220 nsulfd ILL --
38540 101220 nsulfd TRAP --
...
See procstat(1).
I have written a zsh function, which should be highly portable, since the only dependencies are awk
and column
for the fancy output:
psig()
psig()
{
declare -a pid_signal_array
pid_signal_array[1]="SIGHUP"
pid_signal_array[2]="SIGINT"
pid_signal_array[3]="SIGQUIT"
pid_signal_array[4]="SIGKILL"
pid_signal_array[5]="SIGTRAP"
pid_signal_array[6]="SIGABRT"
pid_signal_array[7]="SIGBUS"
pid_signal_array[8]="SIGFPE"
pid_signal_array[9]="SIGKILL"
pid_signal_array[10]="SIGUSR1"
pid_signal_array[11]="SIGSEGV"
pid_signal_array[12]="SIGUSR2"
pid_signal_array[13]="SIGPIPE"
pid_signal_array[14]="SIGALRM"
pid_signal_array[15]="SIGTERM"
pid_signal_array[16]="SIGSTKFLT"
pid_signal_array[17]="SIGCHLD"
pid_signal_array[18]="SIGCONT"
pid_signal_array[19]="SIGSTOP"
pid_signal_array[20]="SIGTSTP"
pid_signal_array[21]="SIGTTIN"
pid_signal_array[22]="SIGTTOU"
pid_signal_array[23]="SIGURG"
pid_signal_array[24]="SIGXCPU"
pid_signal_array[25]="SIGXFSZ"
pid_signal_array[26]="SIGVTALRM"
pid_signal_array[27]="SIGPROF"
pid_signal_array[28]="SIGWINCH"
pid_signal_array[29]="SIGIO"
pid_signal_array[30]="SIGPWR"
pid_signal_array[31]="SIGSYS"
# not defined
#pid_signal_array[32]=""
#pid_signal_array[33]=""
pid_signal_array[34]="SIGRTMIN"
pid_signal_array[35]="SIGRTMIN+1"
pid_signal_array[36]="SIGRTMIN+2"
pid_signal_array[37]="SIGRTMIN+3"
pid_signal_array[38]="SIGRTMIN+4"
pid_signal_array[39]="SIGRTMIN+5"
pid_signal_array[40]="SIGRTMIN+6"
pid_signal_array[41]="SIGRTMIN+7"
pid_signal_array[42]="SIGRTMIN+8"
pid_signal_array[43]="SIGRTMIN+9"
pid_signal_array[44]="SIGRTMIN+10"
pid_signal_array[45]="SIGRTMIN+11"
pid_signal_array[46]="SIGRTMIN+12"
pid_signal_array[47]="SIGRTMIN+13"
pid_signal_array[48]="SIGRTMIN+14"
pid_signal_array[49]="SIGRTMIN+15"
pid_signal_array[50]="SIGRTMAX-14"
pid_signal_array[51]="SIGRTMAX-13"
pid_signal_array[52]="SIGRTMAX-12"
pid_signal_array[53]="SIGRTMAX-11"
pid_signal_array[54]="SIGRTMAX-10"
pid_signal_array[55]="SIGRTMAX-9"
pid_signal_array[56]="SIGRTMAX-8"
pid_signal_array[57]="SIGRTMAX-7"
pid_signal_array[58]="SIGRTMAX-6"
pid_signal_array[59]="SIGRTMAX-5"
pid_signal_array[60]="SIGRTMAX-4"
pid_signal_array[61]="SIGRTMAX-3"
pid_signal_array[62]="SIGRTMAX-2"
pid_signal_array[63]="SIGRTMAX-1"
pid_signal_array[64]="SIGRTMAX"
declare -A hex_value_array
local hex_value_array["0"]="0"
local hex_value_array["1"]="1"
local hex_value_array["2"]="2"
local hex_value_array["3"]="3"
local hex_value_array["4"]="4"
local hex_value_array["5"]="5"
local hex_value_array["6"]="6"
local hex_value_array["7"]="7"
local hex_value_array["8"]="8"
local hex_value_array["9"]="9"
local hex_value_array["a"]="10"
local hex_value_array["b"]="11"
local hex_value_array["c"]="12"
local hex_value_array["d"]="13"
local hex_value_array["e"]="14"
local hex_value_array["f"]="15"
declare -a bin_value_4bit_array=({0..1}{0..1}{0..1}{0..1})
declare -A pid_signal_name_array
local pid_signal_name_array["SigPnd:"]="Signals pending (thread):"
local pid_signal_name_array["ShdPnd:"]="Signals pending (process):"
local pid_signal_name_array["SigBlk:"]="Signals blocked:"
local pid_signal_name_array["SigIgn:"]="Signals ignored:"
local pid_signal_name_array["SigCgt:"]="Signals caught:"
declare -a pid_array=("${@}")
for pid in "${pid_array[@]}"
do
local pid_directory_path="/proc/${pid}"
local pid_status_file="status"
local pid_status=$(< "${pid_directory_path}/${pid_status_file}")
declare -a pid_signal_bin_value_4bit_array
declare -a pid_signal_bin_value_1bit_array
unset hex_position
unset hex_value
unset binary_position
unset binary_value
local hex_position
local hex_value
local binary_position
local binary_value
echo ""
echo -n "PID: ${pid}"
for pid_signal_name in "${(k)pid_signal_name_array[@]}"
do
local pid_signal_hex_value=$(echo "${pid_status}" | awk --assign="pid_signal_name=${pid_signal_name}" '$0 ~ pid_signal_name { print $2 }')
declare -a pid_signal_hex_value_delimited_array
# refactor me: create function "hexToBin()"
# split hexadecimal string per character
# from: 0000000180004a03
# to: 0 0 0 0 0 0 0 1 8 0 0 0 4 a 0 3
unset pid_signal_hex_value_delimited_array
for hex_position in {0.."${#pid_signal_hex_value}"}
do
pid_signal_hex_value_delimited_array+=("${pid_signal_hex_value:${hex_position}:1}")
done
# convert hexadecimal to binary per character
# from: 0 0 0 0 0 0 0 1 8 0 0 0 4 a 0 3
# to: 0000 0000 0000 0000 0000 0000 0000 0001 1000 0000 0000 0000 0100 1010 0000 0011
unset pid_signal_bin_value_4bit_array
for hex_value in "${pid_signal_hex_value_delimited_array[@]}"
do
if [[ "${hex_value}" =~ ^[0-9]$ ]]
then
pid_signal_bin_value_4bit_array+="${bin_value_4bit_array[${hex_value}+1]} "
elif [[ "${hex_value}" =~ ^[a-fA-F]$ ]]
then
pid_signal_bin_value_4bit_array+="${bin_value_4bit_array[hex_value_array[${hex_value}]+1]} "
fi
done
# concatenate binary string and split string per character
# from: 0000 0000 0000 0000 0000 0000 0000 0001 1000 0000 0000 0000 0100 1010 0000 0011
# to: 0000000000000000000000000000000110000000000000000100101000000011
# to: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 1
pid_signal_bin_value_concatenated="${pid_signal_bin_value_4bit_array[@]// /}"
unset pid_signal_bin_value_1bit_array
for binary_position in {0.."$(( ${#pid_signal_bin_value_concatenated} - 1 ))"}
do
pid_signal_bin_value_1bit_array+=("${pid_signal_bin_value_concatenated:${binary_position}:1}")
done
# convert binary to signal name per character
# from: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 1
# to: Signal <signal_number>: <signal_name>
local binary_array_index="${#pid_signal_bin_value_concatenated}"
echo ""
echo "${pid_signal_name_array[${pid_signal_name}]}"
echo "${pid_signal_name_array[${pid_signal_name}]//[a-zA-Z :()]/-}"
for binary_value in "${pid_signal_bin_value_1bit_array[@]}"
do
# signal "32" and "33" are undefined
if [[ "${binary_array_index}" == "32" || "${binary_array_index}" == "33" ]]
then
(( binary_array_index-- ))
continue
elif [[ "${binary_value}" == "0" ]]
then
(( binary_array_index-- ))
continue
elif [[ "${binary_value}" == "1" ]]
then
echo "Signal ${binary_array_index}: ${pid_signal_array[${binary_array_index}]}"
fi
(( binary_array_index-- ))
done
# output results, if there is any signal. also output results, if signal "32" and "33" are set to "1", which actually does not make any sense.
if [[ "${pid_signal_bin_value_concatenated}" == *"1"* ]]
then
echo -e "Hexadecimal: ${pid_signal_hex_value_delimited_array[@]}\nBinary: ${pid_signal_bin_value_4bit_array[@]}" | column --table
else
echo "No signals found."
fi
done
done
}
% psig $$
SigCgt
Signal 28: SIGWINCH
Signal 17: SIGCHLD
Signal 14: SIGALRM
Signal 13: SIGPIPE
Signal 2: SIGINT
Signal 1: SIGHUP
Hexadecimal: 0 0 0 0 0 0 0 0 0 8 0 1 3 0 0 3
Binary: 0000 0000 0000 0000 0000 0000 0000 0000 0000 1000 0000 0001 0011 0000 0000 0011
It is still in development and only covers SigCgt
for now. I am going to port it to Bash
and put it into its own repository.
I ported it to Bash
and it supports all signal types: https://codeberg.org/keks24/psig
psig
#!/bin/bash
#############################################################################
# Copyright 2021 Ramon Fischer #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); #
# you may not use this file except in compliance with the License. #
# You may obtain a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#############################################################################
# define global variables
declare -a pid_signal_array
pid_signal_array[1]="SIGHUP"
pid_signal_array[2]="SIGINT"
pid_signal_array[3]="SIGQUIT"
pid_signal_array[4]="SIGKILL"
pid_signal_array[5]="SIGTRAP"
pid_signal_array[6]="SIGABRT"
pid_signal_array[7]="SIGBUS"
pid_signal_array[8]="SIGFPE"
pid_signal_array[9]="SIGKILL"
pid_signal_array[10]="SIGUSR1"
pid_signal_array[11]="SIGSEGV"
pid_signal_array[12]="SIGUSR2"
pid_signal_array[13]="SIGPIPE"
pid_signal_array[14]="SIGALRM"
pid_signal_array[15]="SIGTERM"
pid_signal_array[16]="SIGSTKFLT"
pid_signal_array[17]="SIGCHLD"
pid_signal_array[18]="SIGCONT"
pid_signal_array[19]="SIGSTOP"
pid_signal_array[20]="SIGTSTP"
pid_signal_array[21]="SIGTTIN"
pid_signal_array[22]="SIGTTOU"
pid_signal_array[23]="SIGURG"
pid_signal_array[24]="SIGXCPU"
pid_signal_array[25]="SIGXFSZ"
pid_signal_array[26]="SIGVTALRM"
pid_signal_array[27]="SIGPROF"
pid_signal_array[28]="SIGWINCH"
pid_signal_array[29]="SIGIO"
pid_signal_array[30]="SIGPWR"
pid_signal_array[31]="SIGSYS"
# not defined
#pid_signal_array[32]=""
#pid_signal_array[33]=""
pid_signal_array[34]="SIGRTMIN"
pid_signal_array[35]="SIGRTMIN+1"
pid_signal_array[36]="SIGRTMIN+2"
pid_signal_array[37]="SIGRTMIN+3"
pid_signal_array[38]="SIGRTMIN+4"
pid_signal_array[39]="SIGRTMIN+5"
pid_signal_array[40]="SIGRTMIN+6"
pid_signal_array[41]="SIGRTMIN+7"
pid_signal_array[42]="SIGRTMIN+8"
pid_signal_array[43]="SIGRTMIN+9"
pid_signal_array[44]="SIGRTMIN+10"
pid_signal_array[45]="SIGRTMIN+11"
pid_signal_array[46]="SIGRTMIN+12"
pid_signal_array[47]="SIGRTMIN+13"
pid_signal_array[48]="SIGRTMIN+14"
pid_signal_array[49]="SIGRTMIN+15"
pid_signal_array[50]="SIGRTMAX-14"
pid_signal_array[51]="SIGRTMAX-13"
pid_signal_array[52]="SIGRTMAX-12"
pid_signal_array[53]="SIGRTMAX-11"
pid_signal_array[54]="SIGRTMAX-10"
pid_signal_array[55]="SIGRTMAX-9"
pid_signal_array[56]="SIGRTMAX-8"
pid_signal_array[57]="SIGRTMAX-7"
pid_signal_array[58]="SIGRTMAX-6"
pid_signal_array[59]="SIGRTMAX-5"
pid_signal_array[60]="SIGRTMAX-4"
pid_signal_array[61]="SIGRTMAX-3"
pid_signal_array[62]="SIGRTMAX-2"
pid_signal_array[63]="SIGRTMAX-1"
pid_signal_array[64]="SIGRTMAX"
declare -A hex_value_array
hex_value_array["0"]="0"
hex_value_array["1"]="1"
hex_value_array["2"]="2"
hex_value_array["3"]="3"
hex_value_array["4"]="4"
hex_value_array["5"]="5"
hex_value_array["6"]="6"
hex_value_array["7"]="7"
hex_value_array["8"]="8"
hex_value_array["9"]="9"
hex_value_array["a"]="10"
hex_value_array["b"]="11"
hex_value_array["c"]="12"
hex_value_array["d"]="13"
hex_value_array["e"]="14"
hex_value_array["f"]="15"
# array index (decimal) points to corresponding binary value
declare -a bin_value_4bit_array=({0..1}{0..1}{0..1}{0..1})
declare -A pid_signal_name_array
pid_signal_name_array["SigPnd:"]="Signals pending (thread):"
pid_signal_name_array["ShdPnd:"]="Signals pending (process):"
pid_signal_name_array["SigBlk:"]="Signals blocked:"
pid_signal_name_array["SigIgn:"]="Signals ignored:"
pid_signal_name_array["SigCgt:"]="Signals caught:"
underline_regex="[a-zA-Z:() ]"
main()
{
declare -a pid_array=("${@}")
for pid in "${pid_array[@]}"
do
local pid_directory_path="/proc/${pid}"
local pid_status_file="status"
local pid_status=$(< "${pid_directory_path}/${pid_status_file}")
local pid_name=$(echo "${pid_status}" | /usr/bin/awk --field-separator="Name:\t" '/Name:/ { print $2 }')
declare -a pid_signal_bin_value_4bit_array
declare -a pid_signal_bin_value_1bit_array
local hex_position
local hex_value
local binary_position
local binary_value
echo ""
echo -e "PID: ${pid}"
echo -n "Name: ${pid_name}"
for pid_signal_name in "${!pid_signal_name_array[@]}"
do
local pid_signal_hex_value=$(echo "${pid_status}" | /usr/bin/awk --assign="pid_signal_name=${pid_signal_name}" '$0 ~ pid_signal_name { print $2 }')
declare -a pid_signal_hex_value_delimited_array
# refactor me: create function "hexToBin()"
# split hexadecimal string per character
# from: 0000000180004a03
# to: 0 0 0 0 0 0 0 1 8 0 0 0 4 a 0 3
unset pid_signal_hex_value_delimited_array
for hex_position in $(eval echo "{0..${#pid_signal_hex_value}}")
do
pid_signal_hex_value_delimited_array+=("${pid_signal_hex_value:${hex_position}:1}")
done
# convert hexadecimal to binary per character
# from: 0 0 0 0 0 0 0 1 8 0 0 0 4 a 0 3
# to: 0000 0000 0000 0000 0000 0000 0000 0001 1000 0000 0000 0000 0100 1010 0000 0011
unset pid_signal_bin_value_4bit_array
for hex_value in "${pid_signal_hex_value_delimited_array[@]}"
do
if [[ "${hex_value}" =~ ^[0-9]$ ]]
then
pid_signal_bin_value_4bit_array+="${bin_value_4bit_array[${hex_value}]} "
elif [[ "${hex_value}" =~ ^[a-fA-F]$ ]]
then
pid_signal_bin_value_4bit_array+="${bin_value_4bit_array[hex_value_array[${hex_value}]]} "
fi
done
# concatenate binary string and split string per character
# from: 0000 0000 0000 0000 0000 0000 0000 0001 1000 0000 0000 0000 0100 1010 0000 0011
# to: 0000000000000000000000000000000110000000000000000100101000000011
# to: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 1
pid_signal_bin_value_concatenated="${pid_signal_bin_value_4bit_array[@]// /}"
unset pid_signal_bin_value_1bit_array
for binary_position in $(eval echo "{0..$(( ${#pid_signal_bin_value_concatenated} ))}")
do
pid_signal_bin_value_1bit_array+=("${pid_signal_bin_value_concatenated:${binary_position}:1}")
done
# convert binary to signal name per character
# from: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 0 0 0 0 0 0 0 1 1
# to: Signal <signal_number>: <signal_name>
local binary_array_index="${#pid_signal_bin_value_concatenated}"
echo ""
echo "${pid_signal_name_array[${pid_signal_name}]}"
echo "${pid_signal_name_array[${pid_signal_name}]//${underline_regex}/-}"
for binary_value in "${pid_signal_bin_value_1bit_array[@]}"
do
# signal "32" and "33" are undefined
if [[ "${binary_array_index}" == "32" || "${binary_array_index}" == "33" ]]
then
(( binary_array_index-- ))
continue
elif [[ "${binary_value}" == "0" ]]
then
(( binary_array_index-- ))
continue
elif [[ "${binary_value}" == "1" ]]
then
echo "Signal ${binary_array_index}: ${pid_signal_array[${binary_array_index}]}"
fi
(( binary_array_index-- ))
done
# output results, if there is any signal. also output results, if signal "32" and "33" are set to "1", which actually does not make any sense.
if [[ "${pid_signal_bin_value_concatenated}" == *"1"* ]]
then
echo -e "Hexadecimal: ${pid_signal_hex_value_delimited_array[@]}\nBinary: ${pid_signal_bin_value_4bit_array[@]}" | column --table
else
echo "No signals found."
fi
done
done
}
main "${@}"
$ psig $$
PID: 10392
Name: zsh
Signals caught:
---------------
Signal 28: SIGWINCH
Signal 17: SIGCHLD
Signal 14: SIGALRM
Signal 13: SIGPIPE
Signal 2: SIGINT
Signal 1: SIGHUP
Hexadecimal: 0 0 0 0 0 0 0 0 0 8 0 1 3 0 0 3
Binary: 0000 0000 0000 0000 0000 0000 0000 0000 0000 1000 0000 0001 0011 0000 0000 0011
Signals pending (process):
--------------------------
No signals found.
Signals pending (thread):
-------------------------
No signals found.
Signals blocked:
----------------
Signal 2: SIGINT
Hexadecimal: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2
Binary: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010
Signals ignored:
----------------
Signal 22: SIGTTOU
Signal 21: SIGTTIN
Signal 20: SIGTSTP
Signal 15: SIGTERM
Hexadecimal: 0 0 0 0 0 0 0 0 0 0 3 8 4 0 0 0
Binary: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 1000 0100 0000 0000 0000
I created a small script in bash to see all those signals for a process:
[mvutcovi@mvutcovi-lap2 ~]$ cat signals.sh
#read -p "PID=" pid
pid=$1
cat /proc/$pid/status|egrep '(Sig|Shd)(Pnd|Blk|Ign|Cgt)'|while read name mask;do
bin=$(echo "ibase=16; obase=2; ${mask^^*}"|bc)
echo -n "$name $mask $bin "
i=1
while [[ $bin -ne 0 ]];do
if [[ ${bin:(-1)} -eq 1 ]];then
kill -l $i | tr '\n' ' '
fi
bin=${bin::-1}
set $((i++))
done
echo
done
# vim:et:sw=4:ts=4:sts=4:
[mvutcovi@mvutcovi-lap2 ~]$ sleep 1234 &./signals.sh $(pgrep -f "sleep .*1234");pkill sleep
[1] 2613776
SigPnd: 0000000000000000 0
ShdPnd: 0000000000000000 0
SigBlk: 0000000000000000 0
SigIgn: 0000000000000000 0
SigCgt: 0000000000000000 0
[1]+ Terminated sleep 1234
[mvutcovi@mvutcovi-lap2 ~]$ nohup sleep 1234 &./signals.sh $(pgrep -f "sleep .*1234");pkill sleep
[1] 2613812
nohup: ignoring input and appending output to 'nohup.out'
SigPnd: 0000000000000000 0
ShdPnd: 0000000000000000 0
SigBlk: 0000000000000000 0
SigIgn: 0000000000000001 1 HUP
SigCgt: 0000000000000000 0
[1]+ Terminated nohup sleep 1234
[mvutcovi@mvutcovi-lap2 ~]$
Awesome work on the bash scripts, thanks for sharing!
In the bash psig.sh I had to change line 202 for it to work on Ubuntu 20.04.
--table
seems to have been deprecated so I changed it to -t
.
before
~/bin$ psig.sh 2625
PID: 2625
Name: file.so
Signals caught:
---------------
Signal 15: SIGTERM
Signal 11: SIGSEGV
Signal 8: SIGFPE
Signal 7: SIGBUS
Signal 6: SIGABRT
Signal 4: SIGKILL
Signal 3: SIGQUIT
Signal 2: SIGINT
column: invalid option -- '-'
usage: column [-txne] [-c columns] [-s sep] [file ...]
after
:~/bin$ psig.sh 2625
PID: 2625
Name: file.so
Signals caught:
---------------
Signal 15: SIGTERM
Signal 11: SIGSEGV
Signal 8: SIGFPE
Signal 7: SIGBUS
Signal 6: SIGABRT
Signal 4: SIGKILL
Signal 3: SIGQUIT
Signal 2: SIGINT
Hexadecimal: 0 0 0 0 0 0 0 1 8 0 0 0 4 4 e e
Binary: 0000 0000 0000 0000 0000 0000 0000 0001 1000 0000 0000 0000 0100 0100 1110 1110