14

When a process breaks, as I know no output will be return anymore. But always after breaking ping command we have the statistics of the execution, and as I know it's part of the output.

amirreza@time:~$ ping 4.2.2.4
PING 4.2.2.4 (4.2.2.4) 56(84) bytes of data.
64 bytes from 4.2.2.4: icmp_seq=1 ttl=51 time=95.8 ms
64 bytes from 4.2.2.4: icmp_seq=2 ttl=51 time=92.3 ms
^C
--- 4.2.2.4 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 92.321/94.052/95.783/1.731 ms
amirreza@time:~$

How does it work?

2 Answers2

50

Ctrl+C makes the terminal send SIGINT to the foreground process group. A process that receives SIGINT can do anything, it can even ignore the signal. A common reaction to SIGINT is to exit gracefully, i.e. after cleaning up etc.

Your ping is simply designed to print statistics upon SIGINT and then to exit.

Other tools may or may not exit upon SIGINT at all. E.g. a usual behavior of an interactive shell (while not running a command) is to clear its command line and redraw the prompt.

SIGINT is not the only signal designed to terminate commands. See the manual (man 7 signal), there are many signals whose default action is to terminate the process. kill sends SIGTERM by default. SIGTERM is not SIGINT. Both can be ignored. SIGKILL cannot be caught, blocked, or ignored, but it should be your last choice.

  • 2
    @AmirrezaRiahi The default action of the SIGINT signal is to kill the process. But the process can override this default. – Barmar Nov 09 '20 at 06:12
  • 1
    @Barmar, same with SIGTERM – JoelFan Nov 09 '20 at 08:43
  • @JoelFan True, but what does that have to do with this? – Barmar Nov 09 '20 at 08:48
  • Unfortunately, ping is quite special because it doesn't follow the WCE as expected by bash -- which would require it to kill itself with SIGINT after handling the signal instead of terminating via _exit(2). This could be easily demonstrated by trying to break out with Ctrl-C from this loop: while ping www.google.com; do :; done when run from bash vs. when run from other shell like dash which doesn't follow the WCE thing. –  Nov 09 '20 at 10:54
  • 1
    @AmirrezaRiahi if by chance your problem was not being to break out of ping when called in a bash loop, you can use awk as a wrapper: xawk(){ awk "BEGIN{system(\"$*\")}"; }; ...; while xawk ping www.google.com; do :; done => will promptly die after the first Control-C, no need to keep it pressed ;-) (I've seen this in some answer here -- but I'm not able to find it now). –  Nov 09 '20 at 11:11
  • 1
    @user414777 Actually ping does obey WCE. To it, Ctrl-C is a request to perform a normal exit of the otherwise infinite loop, thus it exits with a normal status, not a signal. This causes bash to correctly continue running additional commands in a script. (Not that you would typically run ping this way in a script anyway.) – Miral Nov 09 '20 at 23:22
  • @Miral that's just a post-hoc rationalization. ping's behaviour predates that WCE thing. And as seen from many questions here and elsewhere, people expect a Ctrl-C to kill both the ping and the script it was called from (which is exactly what happens in non-WCE respecting shells). –  Nov 10 '20 at 14:35
  • My examples were not the best, but fwiw, it's not only a problem in loops and not only when ping returns a non-zero status. E.g. bash -c 'ping 10.13.13.13; echo YEAH $?', a Control-C will continue with the echo instead of cutting short the whole thing (as most users, rightly or wrongly, would expect). –  Nov 10 '20 at 14:45
  • 1
    I disagree that most users should expect that. It's no different from running nano or another interactive editor and using whatever command that provides to perform a normal exit. It just so happens that in the case of ping that it uses Ctrl+C to request a normal exit, that's all. – Miral Nov 10 '20 at 22:53
  • @Miral You may "disagree" as much as you want, but that doesn't change the fact that people expect it, and this looks very much like a POLA violation. Notice that this is NOT a pet-peeve of mine -- I rarely if ever write bash scripts, but I'm familiar with this problem from many questions here and elsewhere (which are usually mis-analyzing it and placing the blame somewhere else). Also, neither nano nor any other editor I know of is doing a normal exit upon catching SIGINT; most of them (like ed) will cancel the current command without exiting when in some kind of "line" mode. –  Nov 11 '20 at 07:35
4

Additional to the accepted answer - here's ping running in one session, and in another I ran different kill commands.

server~ $ ping google.com
PING google.com (172.217.5.110) 56(84) bytes of data.
64 bytes from google.com(172.217.5.110): icmp_seq=1 ttl=111 time=1.68 ms
64 bytes from google.com(172.217.5.110): icmp_seq=2 ttl=111 time=1.73 ms
Terminated

The above was a SIGTERM, which is signal 15. The exit code was 143.

server ~ $ ping google.com
PING google.com (172.217.5.110) 56(84) bytes of data.
64 bytes from google.com(172.217.5.110): icmp_seq=1 ttl=111 time=1.71 ms
64 bytes from google.com(172.217.5.110): icmp_seq=2 ttl=111 time=1.71 ms
Killed

This was a SIGKILL, which is signal 9. Visually it looks the same, but with a different text, and the exit code is 137 this time.

server~ $ ping google.com
PING google.com (172.217.5.110) 56(84) bytes of data.
64 bytes from google.com(172.217.5.110): icmp_seq=1 ttl=111 time=1.69 ms
64 bytes from google.com(172.217.5.110): icmp_seq=2 ttl=111 time=1.70 ms
           **signal sent here**
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.697/1.701/1.705/0.004 ms

This was a SIGINT which is signal 2. So functionally the same as a ^C Curiously the exit code is 0 which to most tests will be a success, as opposed to a failure.

The upshot of this is that ping does different things for different signals.

You can see a source file for ping at https://gist.github.com/kbaribeau/4495181 which references https://ftp.arl.army.mil/~mike/ping.html as the original.

line
408     signal( SIGINT, finish );
409     signal(SIGALRM, catcher);

457 * C A T C H E R 459 * This routine causes another PING to be transmitted, and then 460 * schedules another SIGALRM for 1 second from now.

See the link for the full code.

Criggie
  • 1,781