#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck disable=SC1091 source "${SCRIPT_DIR}/load-env.sh" camera_device="${OMNI_CAMERA_DEVICE}" occupancy_policy="${OMNI_CAMERA_OCCUPANCY_POLICY:-check}" release_service="${OMNI_CAMERA_RELEASE_SERVICE:-}" die() { echo "[camera-preflight] $*" >&2 exit 1 } camera_pids() { fuser "${camera_device}" 2>/dev/null \ | tr ' ' '\n' \ | grep -E '^[0-9]+$' \ | sort -nu \ || true } pid_service() { local pid="$1" local service service="$(sed -nE 's#.*[/:]([^/:]+\.service)$#\1#p' "/proc/${pid}/cgroup" 2>/dev/null | head -1)" printf '%s' "${service:-unknown}" } report_owners() { local pid local comm local service local found=0 while read -r pid; do [[ -n "${pid}" ]] || continue found=1 comm="$(cat "/proc/${pid}/comm" 2>/dev/null || printf 'unknown')" service="$(pid_service "${pid}")" echo "[camera-preflight] owner pid=${pid} command=${comm} service=${service}" >&2 done < <(camera_pids) if [[ "${found}" == "1" ]]; then return 0 fi return 1 } has_proc_manager_owner() { local pid while read -r pid; do [[ -n "${pid}" ]] || continue if [[ "$(pid_service "${pid}")" == "proc_manager.service" ]]; then return 0 fi done < <(camera_pids) return 1 } if [[ ! -e "${camera_device}" ]]; then die "camera device does not exist: ${camera_device}" fi if ! command -v fuser >/dev/null 2>&1; then die "missing required command: fuser (install the psmisc package)" fi resolved_device="$(readlink -f "${camera_device}" 2>/dev/null || printf '%s' "${camera_device}")" echo "[camera-preflight] checking ${camera_device} (${resolved_device}) policy=${occupancy_policy}" >&2 if ! report_owners; then echo "[camera-preflight] ${camera_device} is free" >&2 exit 0 fi case "${occupancy_policy}" in check) die "${camera_device} is busy; no process was stopped" ;; release-known) if [[ -z "${release_service}" ]]; then die "OMNI_CAMERA_RELEASE_SERVICE is required when policy=release-known" fi echo "[camera-preflight] stopping known camera service ${release_service}" >&2 systemctl stop "${release_service}" if ! report_owners; then echo "[camera-preflight] ${camera_device} was released by ${release_service}" >&2 exit 0 fi if has_proc_manager_owner; then die "${camera_device} is still owned by proc_manager.service; refusing to stop the whole process manager. Unload the owning ROS component explicitly." fi die "${camera_device} remains busy after stopping ${release_service}" ;; *) die "unsupported OMNI_CAMERA_OCCUPANCY_POLICY=${occupancy_policy}; expected check or release-known" ;; esac