#! /bin/sh ### BEGIN INIT INFO # Provides: sendsigs # Required-Start: # Required-Stop: umountnfs # Default-Start: # Default-Stop: 0 6 # Short-Description: Kill all remaining processes. # Description: ### END INIT INFO PATH=/sbin:/usr/sbin:/bin:/usr/bin . /lib/lsb/init-functions # Make it possible to see who the misbehaving processes are report_unkillable() { [ -x /usr/share/apport/unkillable_shutdown ] || return if [ ! -e /etc/default/apport ] || ! grep -q '^enabled[[:space:]]*=[[:space:]]*1' /etc/default/apport; then return fi /usr/share/apport/unkillable_shutdown $OMITPIDS } upstart_killed_jobs () { initctl list | grep 'stop/killed' } upstart_jobs () { initctl list | grep -E '(start/|stop/killed)' | sed -n -e "/process [0-9]/s/.*process //p" } do_stop () { OMITPIDS= # Note: /run transition: Now using /run in place of /var/run; # /lib/init/rw will be removed following transition of # /lib/init/rw users to /run. for omitfile in /run/sendsigs.omit /lib/init/rw/sendsigs.omit; do if [ -e $omitfile ]; then for pid in $(cat $omitfile); do OMITPIDS="${OMITPIDS:+$OMITPIDS }-o $pid" done fi done # Load sendsigs.omit.d/packagename files too, to make it # possible for scripts that need to modify the list of pids at # run time without race conditions. # Note: /run transition: both /run and /lib/init/rw are used # at present during the transition to /run; /lib/init/rw will # be removed following transition of /lib/init/rw users to # /run. for omitdir in /run/sendsigs.omit.d /lib/init/rw/sendsigs.omit.d; do if [ -d "${omitdir}" ]; then for pidfile in "${omitdir}/"*; do [ -f "$pidfile" ] || continue for pid in $(cat $pidfile); do OMITPIDS="${OMITPIDS:+$OMITPIDS }-o $pid" done done fi done # Upstart jobs have their own "stop on" clauses that sends # SIGTERM/SIGKILL just like this, so if they're still running, # they're supposed to be if [ -x /sbin/initctl ]; then for pid in $(upstart_jobs); do OMITPIDS="${OMITPIDS:+$OMITPIDS }-o $pid" done fi # Flush the kernel I/O buffer before we start to kill # processes, to make sure the IO of already stopped services to # not slow down the remaining processes to a point where they # are accidentily killed with SIGKILL because they did not # manage to shut down in time. sync clear echo "\rsync" echo "\r" echo "\rps -fC initctl:" ps -fC initctl echo "\rinitctl is not alive, so it will not receive SIGTERM!" echo "\r" echo "\rkillall5 -15 $OMITPIDS # SIGTERM" echo "\r" # Kill all processes. log_action_begin_msg "Asking all remaining processes to terminate" killall5 -15 $OMITPIDS # SIGTERM log_action_end_msg 0 echo "\r" alldead="" OMITPIDS0="$OMITPIDS" for seq in 1 2 3; do OMITPIDS="$OMITPIDS0" # use SIGCONT/signal 18 to check if there are # processes left. No need to check the exit code # value, because either killall5 work and it make # sense to wait for processes to die, or it fail and # there is nothing to wait for. # did an upstart job start since we last polled initctl? check # again on each loop and add any new jobs (e.g., plymouth) to # the list. If we did miss one starting up, this beats waiting # 10 seconds before shutting down. if [ -x /sbin/initctl ]; then for pid in $(upstart_jobs); do OMITPIDS="${OMITPIDS:+$OMITPIDS }-o $pid" done fi echo "\r====== Loop start -- interation $seq ======" echo "\rkillall5 -18 $OMITPIDS" echo "\r" pstree -p | while read line; do echo "\r$line"; done echo "\r" case $seq in 1) echo "\rinitctl has not started yet, however we wait for another processes to end now, sleeping 1..." ;; 2) echo "\ranother processes ended, but initctl process appeared, now we must wait for it, sleeping 1..." ;; *) echo "\rwe are still waiting for initctl to end, sleeping 1..." ;; esac if killall5 -18 $OMITPIDS ; then : else alldead=1 break fi echo "\r====== Loop end -- interation $seq ======" echo "\r" sleep 1 done # Upstart has a method to set a kill timeout and so the job author # may want us to wait longer than 10 seconds (as in the case of # mysql). (LP: #688541) # # We will wait up to 300 seconds for any jobs in stop/killed state. # Any kill timeout higher than that will be overridden by the need # to shutdown. NOTE the re-use of seq from above, since we already # waited up to 10 seconds for them. while [ -n "$(upstart_killed_jobs)" ] ; do seq=$(($seq+1)) if [ $seq -ge 300 ] ; then break fi sleep 1 done if [ -z "$alldead" ] ; then #report_unkillable echo 'OMITPIDS='"$OMITPIDS" echo "Remaining processes:" ps -N --ppid 2 -f log_action_begin_msg "Killing all remaining processes" killall5 -9 $OMITPIDS # SIGKILL log_action_end_msg 1 sleep 30 else log_action_begin_msg "All processes ended within $seq seconds." log_action_end_msg 0 fi } case "$1" in start) # No-op ;; restart|reload|force-reload) echo "Error: argument '$1' not supported" >&2 exit 3 ;; stop) do_stop ;; *) echo "Usage: $0 start|stop" >&2 exit 3 ;; esac :