Why this kill doesn't work in this Bash script but only outside of the script?

The following is a contrived example that demonstrates the effect and has to run with root. It executes ping process in the background and attempts to kill it.

#!/bin/bash
# Ensure that there is no ping process before we begin.
killall ping
sudo ping google.com > /dev/null &
PID=$!
sleep 0.5
kill $PID
echo "Exit code of kill $PID: $?"
# Check the running ping processes. There should be no ping
# process if the above `kill $PID` worked correctly.
ps aux | grep -v grep | grep ping

However, the script is unable to kill the process, even if kill's return code is 0. The following is an example output.

$ bash test.sh
Exit code of kill 16516: 0
root 16516 0.0 0.0 14956 2212 pts/2 S 13:22 0:00 sudo ping google.com
root 16518 1.0 0.0 13112 1292 pts/2 S 13:22 0:00 ping google.com

I noticed that if I take out sudo then it properly kills it. Why is this happening? I suspect that sudo's child process messes it up somehow though.

Update 1:

Even more weird. If I execute the same kill command after the script, it works.

$ bash test.sh
Exit code of kill 16631: 0
root 16631 3.0 0.0 14956 2212 pts/2 S 13:29 0:00 sudo ping google.com
root 16633 0.0 0.0 13112 1292 pts/2 S 13:29 0:00 ping google.com
$ ps aux | grep -v grep | grep ping
root 16631 0.5 0.0 14956 2212 pts/2 S 13:29 0:00 sudo ping google.com
root 16633 0.0 0.0 13112 1292 pts/2 S 13:29 0:00 ping google.com
$ kill 16631
$ ps aux | grep -v grep | grep ping
$
$ kill 16631
-bash: kill: (16631) - No such process
$
5

1 Answer

This happens because the controlling sudo process does not propagate signals coming from its own process group (source):

/* * Do not forward signals sent by a process in the command's process * group, as we don't want the command to indirectly kill itself. * For example, this can happen with some versions of reboot that * call kill(-1, SIGTERM) to kill all other processes. */
if (USER_SIGNALED(sc->siginfo) && sc->siginfo->si_pid != 0) { pid_t si_pgrp = getpgid(sc->siginfo->si_pid); if (si_pgrp != -1) { if (si_pgrp == ec->ppgrp || si_pgrp == ec->cmnd_pid) debug_return; } else if (sc->siginfo->si_pid == ec->cmnd_pid) { debug_return; }
}

When you execute the command outside the script, you run it in a separate process group, so the signal is relayed.

1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like