build/lib/functions/host/tmpfs-utils.sh

129 lines
4.9 KiB
Bash

#!/usr/bin/env bash
#
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2013-2023 Igor Pecovnik, igor@armbian.com
#
# This file is a part of the Armbian Build Framework
# https://github.com/armbian/build/
# Call: prepare_tmpfs_for "NAME_OF_TMPFS_DIR" "${PATH_TO_DIR}" # this adds its own cleanup handler
function prepare_tmpfs_for() {
declare tmpfs_name="${1}"
declare tmpfs_path="${2}"
# validate parameters
if [[ -z "${tmpfs_name}" ]]; then
exit_with_error "prepare_tmpfs_for: tmpfs_name (arg 1) is empty"
fi
if [[ -z "${tmpfs_path}" ]]; then
exit_with_error "prepare_tmpfs_for: tmpfs_path (arg 2) is empty"
fi
# params for the handler; initially just repeat our own params
declare -a cleanup_params=("${tmpfs_name}" "${tmpfs_path}")
# create the dir if not exists and note that down for the cleanup handler
if [[ ! -d "${tmpfs_path}" ]]; then
display_alert "prepare_tmpfs_for: creating dir" "${tmpfs_path}" "cleanup"
mkdir -p "${tmpfs_path}"
cleanup_params+=("remove_dir")
else
display_alert "prepare_tmpfs_for: dir exists" "${tmpfs_path}" "cleanup"
cleanup_params+=("no_remove_dir")
fi
# Do nothing if we're not on Linux (detect via OSTYPE) and root. Still, setup the cleanup handler.
if [[ "${OSTYPE}" != "linux"* ]] || [[ "${EUID}" -ne 0 ]]; then
display_alert "prepare_tmpfs_for: not on Linux or not root, skipping" "${tmpfs_name}" "cleanup"
cleanup_params+=("no_umount_tmpfs")
elif [[ "${ARMBIAN_INSIDE_DOCKERFILE_BUILD}" == "yes" ]]; then
display_alert "prepare_tmpfs_for: inside Dockerfile build, skipping" "${tmpfs_name}" "cleanup"
cleanup_params+=("no_umount_tmpfs")
elif [[ "${USE_TMPFS}" == "no" ]]; then
display_alert "prepare_tmpfs_for:" "USE_TMPFS=no, skipping: '${tmpfs_name}'" "warning"
cleanup_params+=("no_umount_tmpfs")
else
display_alert "prepare_tmpfs_for: on Linux and root, MOUNTING TMPFS" "${tmpfs_name}" "cleanup"
# mount tmpfs on it
mount -t tmpfs -o "size=99%" tmpfs "${tmpfs_path}" # size=50% is the Linux default, but we need more.
cleanup_params+=("umount_tmpfs")
#cleanup_params+=("no_umount_tmpfs")
fi
# add the cleanup handler
declare cleanup_handler="cleanup_tmpfs_for ${cleanup_params[*]@Q}"
display_alert "prepare_tmpfs_for: add cleanup handler" "${cleanup_handler}" "cleanup"
add_cleanup_handler "${cleanup_handler}"
return 0
}
function cleanup_tmpfs_for() {
declare tmpfs_name="${1}"
declare tmpfs_path="${2}"
declare remove_dir="${3}"
declare umount_tmpfs="${4}"
# validate parameters
if [[ -z "${tmpfs_name}" ]]; then
exit_with_error "cleanup_tmpfs_for: tmpfs_name (arg 1) is empty"
fi
if [[ -z "${tmpfs_path}" ]]; then
exit_with_error "cleanup_tmpfs_for: tmpfs_path (arg 2) is empty"
fi
if [[ -z "${remove_dir}" ]]; then
exit_with_error "cleanup_tmpfs_for: remove_dir (arg 3) is empty"
fi
if [[ -z "${umount_tmpfs}" ]]; then
exit_with_error "cleanup_tmpfs_for: umount_tmpfs (arg 4) is empty"
fi
# umount tmpfs
if [[ "${umount_tmpfs}" == "umount_tmpfs" ]]; then
display_alert "cleanup_tmpfs_for: umount tmpfs" "${tmpfs_name}" "cleanup"
cd "${SRC}" || echo "cleanup_tmpfs_for: cd failed to ${SRC}" >&2 # avoid cwd in use error
wait_for_disk_sync "before tmps cleanup ${tmpfs_name}" # let disk coalesce
umount "${tmpfs_path}" &> /dev/null || {
display_alert "cleanup_tmpfs_for: umount failed" "${tmpfs_name} tmpfs umount failed, will try lazy" "cleanup"
# Do a lazy umount... last-resort...
wait_for_disk_sync "before tmps cleanup ${tmpfs_name} lazy" # let disk coalesce
umount -l "${tmpfs_path}" &> /dev/null || display_alert "cleanup_tmpfs_for: lazy umount failed" "${tmpfs_name} tmpfs lazy umount also failed" "cleanup"
wait_for_disk_sync "after tmps cleanup ${tmpfs_name} lazy" # let disk coalesce
}
# Check if the tmpfs is still mounted after all that trying, log error, show debug, and give up with error
mountpoint -q "${tmpfs_path}" && {
display_alert "cleanup_tmpfs_for: umount failed" "${tmpfs_name} tmpfs still mounted after retries/lazy umount" "err"
# Show last-resort what's in there / what's using it to stderr
ls -la "${tmpfs_path}" >&2 || echo "cleanup_tmpfs_for: ls failed" >&2
lsof "${tmpfs_path}" >&2 || echo "cleanup_tmpfs_for: lsof dir failed" >&2
return 1 # sorry, we tried.
} || {
display_alert "cleanup_tmpfs_for: umount success" "${tmpfs_name}" "cleanup"
}
else
display_alert "cleanup_tmpfs_for: not umounting tmpfs" "${tmpfs_name}" "cleanup"
fi
# remove the dir if we created it
if [[ "${remove_dir}" == "remove_dir" ]]; then
display_alert "cleanup_tmpfs_for: removing dir" "${tmpfs_path}" "cleanup"
rm -rf "${tmpfs_path:?}"
else
display_alert "cleanup_tmpfs_for: not removing dir" "${tmpfs_path}" "cleanup"
fi
return 0
}
# Debugging utility.
function debug_tmpfs_show_usage() {
if [[ "${SHOW_TMPFS}" == "yes" ]]; then
display_alert "TMPFS debugging:" "${CURRENT_LOGGING_SECTION:-none} $*" "debug"
run_host_command_logged df -h -t tmpfs "||" true
run_host_command_logged free -m "||" true
fi
return 0
}