| #!/bin/sh |
| |
| # Copyright 2010 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # Where to write debug logs to. |
| LOGPATH="/var/log/shutdown_force_kill_processes" |
| |
| # Parses the output of `lsof -Fn` in the format (p<pid>\n(n<filename>\n)*)*, |
| # and returns the list of <pid>s that were followed by some filename matching |
| # the regex argument $1. This is an utility function for use in |
| # kill_with_open_files_on_path_and_mountpoints(). |
| _lsofFn_filter() { |
| sed -n -E -e " |
| # Hold all the most recent 'p' line in the buffer. |
| /^p/h |
| # Look for a matching 'n' line. Pick % as it's unlikely to be in a path. |
| # Enclose $1 by parentheses so that the caller can pass in regular |
| # expressions like /home|/foo. |
| \\%^n($1)%{ |
| # Get the last 'p' line out of the buffer. |
| g |
| # Delete the leading 'p'. |
| s:^p:: |
| # Print it. |
| 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 path_regex="$1" |
| shift |
| local log_path_label="$1" |
| shift |
| |
| # Remove old log files. |
| rm -f "${LOGPATH}" "${LOGPATH}.${log_path_label}" |
| |
| pids="$(lsof -n -Fn "$@" | _lsofFn_filter "${path_regex}")" |
| 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 -n "$@" >"${LOGPATH}.${log_path_label}" |
| |
| # 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 -n -Fn "$@" | _lsofFn_filter "${path_regex}")" |
| 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 -n -Fn "$@" | _lsofFn_filter "${path_regex}") |
| 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 . "$@" |
| } |