blob: 5d1f2897f39c49357dbdef3dfa8390913fb61373 [file] [log] [blame]
# Copyright 2013 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Some startup functions are split into a separate library which may be
# different for different targets (e.g., regular Chrome OS vs. embedded).
. /usr/share/cros/
# Shutdown is best-effort. We don't want to die on errors.
set +e
bootstat shutdown-start
. /sbin/killers
# Set nologin so services (like usb_bouncer) can check that a shutdown is
# happening.
touch /run/nologin
# Remount root in case a developer has remounted it rw for some reason.
mount -n -o remount,ro /
# TODO: swapoff as necessary.
# Kill any that may prevent us from unmounting the stateful partition
# or the crypto-home and then unmount. These should be all that we need
# to unmount for a clean shutdown.
process_killer --shutdown --log_to_stderr --file_holders \
--mount_holders >"${PROCESS_KILLER_LOG}" 2>&1
# CROS_DEBUG equals one if we've booted in developer mode or we've booted a
# developer image.
crossystem "cros_debug?1"
CROS_DEBUG="$((! $?))"
dev_unmount_packages() { true; }
dev_push_paths_to_preserve() { true; }
collect_shutdown_umount_failure_logs() {
# Get mount table as seen by this process. This list may not be
# comprehensive: private mounts inside mount namespaces may not
# appear in this list.
echo "Active mounts:"
cat "/proc/self/mountinfo"
# Log information about the dm-crypt device.
echo "Device-mapper target state:"
dmsetup info
# Log upstart jobs that are still running: jobs here can stop the
# stateful partition from getting unmounted.
echo "Active Upstart jobs:"
initctl list | grep running
# Log dbus services still connected to the bus, with a 1 second timeout.
echo "Active D-Bus services:"
dbus-send --system --dest=org.freedesktop.DBus --type=method_call \
--print-reply --reply-timeout=1000 /org/freedesktop/DBus \
# Print process tree.
echo "Process tree:"
ps --deselect --ppid 2 --forest -f -o pid,ppid,state,comm
# Parse pids of processes with active mount namespaces. PID is the fourth
# field and the first line refers to the root mount namespace.
for pid in $(lsns --raw -n -t mnt | cut -f4 -d' ' | tail -n+2); do
echo "====== PID: ${pid} Comm: $(cat /proc/${pid}/comm) ======"
# Use findmnt to look inside the mount namespace for any mounts that
# refer to the (enc)stateful partition. These mounts may keep the
# stateful partition from unmounting cleanly.
echo "Open mounts into (enc)stateful:"
findmnt --raw -N ${pid} | grep -e stateful -e mmcblk0p1 -e nvme0n1p1 \
-e sda1
# Get a list of processes with files open. lsof is verbose: keep it at the
# end of the log.
echo "Processes with files open:"
lsof -n /mnt/stateful_partition /var /home/chronos
) >/run/shutdown_umount_failure.log 2>&1
# Attempt multiple retries for each mount point. This prevents transient
# failures from stopping the unmount process.
umount_mountpoint() {
local mnt="$1"
# Check if the mount point exists.
if ! mountpoint -q "${mnt}"; then
return 0
local rc=0
for _ in 1 2 3 4 5 6 7 8 9 10; do
umount -n "${mnt}"
if [ "${rc}" -eq "0" ]; then
sleep 0.1
return "${rc}"
if [ "${CROS_DEBUG}" -eq 1 ]; then
. /usr/share/cros/
# target_version should only be created for test lab DUTs.
if [ "${CROS_DEBUG}" = "1" ] && [ -f "${STATEFUL_UPDATE}" ]; then
if [ -r "${TARGET_VERSION}" ] && [ ! -L "${TARGET_VERSION}" ]; then
# Used later to clear Quota parameters from stateful.
UPDATE_TARGET="$(cut -d '.' -f 1 "${TARGET_VERSION}")"
if [ "${STATEFUL_UPDATE_ARGS}" = "clobber" ]; then
# Measure shutdown time up to this point.
bootstat before_preserve
# We preserve a few files. Make sure preservation directory starts empty.
rm -rf "${PRESERVE_DIR}/log"
mkdir -p -m 0755 "${PRESERVE_DIR}"
cp -a "${MNTS}/var/log" "${PRESERVE_DIR}"
# We are about to put this into a directory that will shortly be wiped
# out. Keep a timestamp where it will be preserved as well.
bootstat_archive "${PRESERVE_METRICS}/shutdown.$(date '+%Y%m%d%H%M%S')"
# Signal that the clean shutdown point was reached (or at least as
# close to that point as we can be before stateful is unmounted).
# Log to stderr since syslog may not be available at this stage.
crash_reporter --log_to_stderr --clean_shutdown
# Flush buffers to disk to reflect this part of shutdown in the metrics.
# Measure shutdown time up to this point.
bootstat_archive "/var/log/metrics/shutdown.$(date '+%Y%m%d%H%M%S')"
# To be safe, flush buffers to disk again before unmounting. (From
# it seems that a failed umount can get the filesystem
# into a state that renders a subsequent sync ineffective.)
# Log all the unmount logic to a temp file and move it over to stateful if any
# of the steps failed.
set -x
# Unmount stateful partition for dev packages. Will be a NOP unless we're in
# dev mode.
# Unmount /var/run and /var/lock, which were bind mounted to /run and /run/lock
# respectively to enable backwards compatibility for accessing /run (tmpfs for
# runtime data) through /var.
umount_mountpoint "/var/run"
umount_mountpoint "/var/lock"
# Unmount /var, /home and encrypted mountpoints, then try to
# unmount /mnt/stateful_partition. Log to /mnt/stateful_partition if any of
# them failed to unmount.
# Note that the other mounts are submounts of /mnt/stateful_partition on
# regular images, but not always true on factory images. To handle both, we
# should unmount /mnt/stateful_partition only if the others successfully
# unmounted, otherwise system may fail to log. See
# Check if /home is mounted before attempting to umount().
umount_mountpoint "/home"
: "$(( rc |= $? ))"
# Unmount /mnt/stateful_partition only if the previous unmounts succeeded.
if [ "${rc}" -eq 0 ]; then
umount_mountpoint "${STATEFUL_PARTITION}"
exit "$(( rc | $? ))"
) >/run/mount_encrypted/umount-encrypted.log 2>&1
if [ "$?" -ne 0 ]; then
# Collect information about active mount namespaces and if there are bind
# mounts open inside these namespaces into the (enc)stateful partition.
crash_reporter --early --log_to_stderr --umount_failure \
crash_reporter --early --log_to_stderr --preserve_across_clobber \
mv /run/mount_encrypted/umount-encrypted.log "${STATEFUL_PARTITION}/"
mv /run/shutdown_umount_failure.log "${STATEFUL_PARTITION}/"
if [ -n "${UPDATE_TARGET}" ] && [ -n "${STATE_DEV}" ]; then
# 10756.0.0 is the first build to turn on ext4 quota.
# See
# Older builds will fail to mount stateful if quota is enabled.
# This code can be removed when we stop testing pre-R69 FSI updates.
if [ "${UPDATE_TARGET}" -lt 10756 ]; then
if dumpe2fs -h "${STATE_DEV}" 2>/dev/null | \
grep -qe "^Filesystem features:.* quota.*"; then
tune2fs -O^quota -Q^usrquota,^grpquota,^prjquota "${STATE_DEV}"
rm -f /run/mount_encrypted/umount-encrypted.log
# Just in case something didn't unmount properly above.
# Display low battery icon if shutting down due to low battery.
# SHUTDOWN_REASON is passed in with the runlevel event from power manager.
if [ "${SHUTDOWN_REASON}" = "low-battery" ]; then
# Ensure that we always claim success.
exit 0