Linux Scripting Signals & Traps Signals

• Linux supports both POSIX reliable signals and POSIX real-time signals. • A is a “software interrupt” • A signal interrupts a • The process may halt • The process may “catch” the signal • Signals have no “bodies” Signals

• In Linux, every signal has a name that begins with characters SIG. For example : • A SIGINT signal that is generated when a user presses ctrl+c. This is the way to terminate programs from terminal. • A SIGALRM is generated when the timer set by alarm function goes off. • A SIGABRT signal is generated when a process calls the abort function. • Etc

To see the signals available on your system type: -l Signal Name Description SIGSTOP 19 Stop executing(can't be caught or ignored) (POSIX) SIGHUP 1 Hangup (POSIX) SIGTSTP 20 Terminal stop signal (POSIX) SIGINT 2 Terminal interrupt (ANSI) 21 trying to read, from TTY SIGQUIT 3 Terminal quit (POSIX) SIGTTIN (POSIX) SIGILL 4 Illegal instruction (ANSI) SIGTTOU 22 Background process trying to write, to TTY (POSIX) SIGTRAP 5 Trace trap (POSIX) SIGIOT 6 IOT Trap (4.2 BSD) SIGURG 23 Urgent condition on socket (4.2 BSD) SIGBUS 7 BUS error (4.2 BSD) SIGXCPU 24 CPU limit exceeded (4.2 BSD) Floating point exception SIGFPE 8 25 (ANSI) SIGXFSZ File size limit exceeded (4.2 BSD) Kill(can't be caught or SIGVTALRM 26 Virtual alarm clock (4.2 BSD) SIGKILL 9 ignored) (POSIX) SIGPROF 27 Profiling alarm clock (4.2 BSD) User defined signal 1 SIGUSR1 10 (POSIX) SIGWINCH 28 Window size change (4.3 BSD, Sun) Invalid memory segment SIGSEGV 11 SIGIO 29 I/O now possible (4.2 BSD) access (ANSI) User defined signal 2 SIGPWR 30 Power failure restart (System V) SIGUSR2 12 (POSIX) Write on a pipe with no SIGPIPE 13 reader, Broken pipe (POSIX) SIGALRM 14 Alarm clock (POSIX) To see the signals available on your system type: SIGTERM 15 Termination (ANSI) kill -l SIGSTKFLT 16 Stack fault Child process has stopped SIGCHLD 17 or exited, changed (POSIX) Continue executing, if SIGCONTv 18 stopped (POSIX) Signals

• When the signal occurs, the process has to tell the kernel (i.e., the OS) what to do with it. • There can be three options through which a signal can be disposed : • The signal can be ignored. Nothing will be done when signal occurs. • The signal can be caught. • Let the default action apply. Signals

• three options: • The signal can be ignored. Nothing will be done when signal occurs. • Most of the signals can be ignored but signals generated by hardware exceptions like divide by zero, if ignored can have weird consequences. • A couple of signals like SIGKILL and SIGSTOP cannot be ignored. Signals

• three options: • The signal can be caught. • The process must register a function with kernel. • This function is called by kernel when that signal occurs. • If the signal is non fatal for then in that function the process can handle the signal properly Signals

• three options: • Let the default action apply. • Every signal has a default action. This could be process terminate, ignore etc. • The default action of SIGKILL and SIGSTOP is to terminate the process. Signals: catching

• In the process code this is done by registering a function to kernel which the kernel calls when the signal occurs. • the function that the process registers must be reentrant. • what are reentrant functions? • A reentrant function is a function whose execution can be stopped in between due to any reason (like due to interrupt or signal) • and then can be reentered again safely before its previous invocations complete the execution. Signals: catching

• Suppose a function func() is registered for a call back on a signal occurrence. • Now assume that this func() was already in execution when the signal occurred. • Since this function is call back for this signal so the current execution on this signal will be stopped by the scheduler • and this function will be called again (due to signal). • Assume that func() works on some global values or data structures • Could be left in inconsistent state when the execution of this function was stopped in middle • the second call to same function(due to signal) may cause some undesired results. Signals: BASH

• In the absence of any traps, an interactive Bash shell ignores SIGTERM and SIGQUIT. • SIGINT is caught and handled, • if is active, SIGTTIN, SIGTTOU and SIGTSTP are also ignored. • Commands that are run as the result of a command substitution also ignore these signals, when keyboard generated. Signals: BASH

• SIGHUP by default exits a shell. • An interactive shell will send a SIGHUP to all jobs, running or stopped; • see the documentation on the built-in if you want to disable this default behavior for a particular process. • Use the huponexit option for killing all jobs upon receiving a SIGHUP signal, using the shopt built-in. Bash: sending signals

• Via the command line: • CTRL-C • The interrupt signal, • sends SIGINT to the job running in the foreground. • CTRL-Y • The delayed suspend character. • Causes a running process to be stopped when it attempts to read input from the terminal. • Control is returned to the shell, the user can foreground, background or kill the process. • Delayed suspend is only available on operating systems supporting this feature. • CTRL-Z • The suspend signal, sends a SIGTSTP to a running program, thus stopping it and returning control to the shell. Sending signals with kill

• In Bash, both signal names and numbers are accepted as options, and arguments may be job or process IDs. • An exit status can be reported using the -l option: zero when at least one signal was successfully sent, non-zero if an error occurred. • Using the kill command from /usr/bin, your system might enable extra options, • such as the ability to kill processes from other than your own user ID • and specifying processes by name, like with pgrep and pkill. • Both kill commands send the TERM signal if none is given. Sending signals with kill

Signal name Signal value Effect SIGHUP 1 Hangup SIGINT 2 Interrupt from keyboard SIGKILL 9 Kill signal SIGTERM 15 Termination signal SIGSTOP 17,19,23 Stop the process BASH: sending signals

• When killing a process or series of processes, it is common sense to start trying with the least dangerous signal, SIGTERM. • programs that care about an orderly shutdown get the chance to follow the procedures that they have been designed to execute when getting the SIGTERM signal, • such as cleaning up and closing open files. • If you send a SIGKILL to a process, you remove any chance for the process to do a tidy cleanup and shutdown. traps

• The trap statement allows signals to be caught • can be programmed to execute a list of commands upon catching those signals.

• Syntax: trap [COMMANDS] [SIGNALS] traps

• Syntax: trap [COMMANDS] [SIGNALS] • This instructs the trap command to catch the listed SIGNALS, • SIGNALS may be signal names with or without the SIG prefix, or signal numbers. • If a signal is 0 or EXIT, the COMMANDS are executed when the shell exits. • If one of the signals is DEBUG, the list of COMMANDS is executed after every simple command. • A signal may also be specified as ERR; in that case COMMANDS are executed each time a simple command (not a complex command like an if) exits with a non-zero status. traps

• Syntax: trap [COMMANDS] [SIGNALS] • The return status of the trap command itself is zero unless an invalid signal specification is encountered. • The trap command takes a couple of options, which are documented in the Bash info pages. Example

#!/bin/bash # traptest.sh trap "echo Booh!" SIGINT SIGTERM echo "pid is $$" while : # This is the same as "while true". do sleep 60 # This script is not really doing anything. done BASH: recieving signals

• When Bash receives a signal for which a trap has been set • If the Bash is waiting for a command to complete, the trap will not be executed until the command completes. • If the Bash is waiting for an asynchronous command via the wait built-in, the reception of a signal for which a trap has been set will cause the wait built-in to return immediately with an exit status greater than 128, immediately after which the trap is executed. Example 1

• When debugging longer scripts, you might want to give a variable the trace attribute and trap DEBUG messages for that variable. • Normally you would just declare a variable using an assignment like VARIABLE=value. • To trace, replace the declaration of the variable with the declare line:

declare -t VARIABLE=value # the –t option traces trap "echo VARIABLE is being used here." DEBUG

# rest of the script Example 2

• The whatis command relies on a database which is regularly built using the makewhatis. script with cron:

#!/bin/bash LOCKFILE=/var/lock/makewhatis.lock # Previous makewhatis should execute successfully: [ -f $LOCKFILE ] && exit 0 # Upon exit, remove lockfile. trap "{ rm -f $LOCKFILE ; exit 255; }" EXIT touch $LOCKFILE makewhatis -u -w exit 0 Example 3

#!/bin/sh Signal 1 is SIGUP trap cleanup 1 2 3 6 Signal 2 is SIGINT (CTRL-C) Signal 3 is SIGQUIT cleanup() Signal 6 is SIGKILL { echo "Caught Signal ... cleaning up." rm -rf /tmp/temp_*.$$ echo "Done cleanup ... quitting." Exercise: create a script that creates a new file and then exit 1 loops continuously. } When you get a SIGINT signal, remove the file and exit. ### main script for i in * do sed s/FOO/BAR/g $i > /tmp/temp_${i}.$$ && mv /tmp/temp_${i}.$$ $i done Example 4

#!/bin/sh ### main script trap 'increment' 2 X=0 while : increment() do What does this do? { echo "X=$X" echo "Caught SIGINT ..." X=`expr ${X} + 1` X=`expr ${X} + 500` sleep 1 done if [ "${X}" -gt "2000" ] then echo "Okay, I'll quit ..." exit 1 fi }