blob: a2c0cd80958a532ec380f767af6929908a71517b [file] [log] [blame]
#!/bin/sh
# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Timeouts in msec
readonly SIGTERM_TIMEOUT=2500
readonly SIGKILL_TIMEOUT=2000
# Send SIGTERM once to all processes that match regex in $1, then wait for
# them all to terminate. If processes matching $1 are still running after
# SIGTERM_TIMEOUT msec they are all sent SIGKILL once. If processes matching
# $1 are still present after SIGKILL_TIMEOUT msec a message is logged.
term_process() {
local process="$1"
pkill "$process"
local pid i=0
while [ $i -lt $SIGTERM_TIMEOUT ]; do
pid=$(pgrep "$process")
if [ $? -ne 0 ] ; then
return
fi
sleep .1
: $(( i += 100 ))
done
logger -t "term_process($process)" \
"PIDs ["$pid"] did not terminate, sending SIGKILL"
echo "term_process($process)" \
"PIDs ["$pid"] did not terminate, sending SIGKILL"
pkill -KILL "$process"
i=0
while [ $i -lt $SIGKILL_TIMEOUT ]; do
pid=$(pgrep "$process")
if [ $? -ne 0 ] ; then
return
fi
sleep .1
: $(( i += 100 ))
done
logger -t "term_process($process)" "PIDs ["$pid"] not dead after SIGKILL"
echo "term_process($process)" "PIDs ["$pid"] not dead after SIGKILL"
}
# Parses the output of `lsof -Fn` in the format (p<pid>\n(n<filename>\n)*)*,
# and returns the list of <pid>s followed by a non-empty list of filenames.
# This is an utility function for use in kill_open_file_on().
_lsofFn_filter() {
# Save all the most recent 'p' line in the buffer.
# When we get a matching 'n' line, pull the last 'p' line out
# of the buffer and show it (w/out the leading 'p' char).
sed -n -E -e '/^p/h' -e "\\%^n($1)%{x;s:^p::;p}" | sort -u
}
# For a given path pattern regex (the first argument) and given mountpoints
# (the rest), this will kill all processes with open files on that mountpoint
# with its path matches the regex, so that it can be unmounted. It starts off
# by sending a TERM and if the process hasn't exited quickly enough it will
# send KILL.
#
# Since a typical shutdown should have no processes with open files on a
# partition that we care about at this point, we log the set of processes
# to /var/log/shutdown_force_kill_processes
kill_with_open_files_on_path_and_mountpoints() {
local pids pid i
local paths="$1"
shift
pids=$(lsof -Fn "$@" | _lsofFn_filter "${paths}")
if [ -z "${pids}" ] ; then
return # The typical case; no open files at this point.
fi
# pids should have been empty. Since it is not, we log for future inspection.
lsof "$@" > /var/log/shutdown_force_kill_processes
# First try a gentle kill -TERM
for i in 1 2 3 4 5 6 7 8 9 10; do
for pid in ${pids} ; do
! kill -TERM ${pid}
done
pids=$(lsof -Fn "$@" | _lsofFn_filter "${paths}")
if [ -z "${pids}" ] ; then
return
fi
sleep .1
done
# Now kill -KILL as necessary
for i in 1 2 3 4 5 6 7 8 9 10; do
for pid in ${pids} ; do
! kill -KILL ${pid}
done
pids=$(lsof -Fn "$@" | _lsofFn_filter "${paths}")
if [ -z "${pids}" ] ; then
return
fi
sleep .1
done
}
# For a given mountpoint, this will kill all processes with open files
# on that mountpoint so that it can be unmounted. It starts off by sending
# a TERM and if the process hasn't exited quickly enough it will send KILL.
kill_with_open_files_on() {
kill_with_open_files_on_path_and_mountpoints . "$@"
}