#!/usr/bin/env bash
# Run podman from inside an agent sandbox. Passthrough when the caller is not
# confined, so this is safe to use unconditionally in place of `podman`.
#
# Rootless podman needs a subuid range to build its user namespace. Agent tool
# processes run inside a bwrap user namespace whose uid_map maps a single uid,
# and carry no_new_privs, so the setuid newuidmap helper cannot widen it. Podman
# then fails to create or join a pause namespace and reports
#   "unable to create a new pause process: cannot re-exec process to join the
#    existing user namespace ... Try running podman system migrate ... or reboot"
# Both suggestions are wrong and destructive here: `podman system migrate` stops
# every running container, and the sandbox is not a machine state a reboot fixes.
# Each such failed attempt also leaks a podman-pause-*.scope and its catatonit.
#
# systemd-run asks the user manager to spawn the process outside the sandbox,
# where the uid range is intact.
#
# usage: deck-podman <podman-args...>
set -uo pipefail

die() { echo "deck-podman: ERROR: $*" >&2; exit 1; }

SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# Resolve the real podman, skipping this wrapper's own directory so a symlink
# named `podman` on PATH can never re-enter here.
resolve_podman() {
  local d
  if [[ -n "${DECK_PODMAN_REAL:-}" ]]; then
    printf '%s' "$DECK_PODMAN_REAL"
    return 0
  fi
  local self
  self="$(readlink -f "${BASH_SOURCE[0]}")"
  IFS=':' read -ra _parts <<< "$PATH"
  for d in "${_parts[@]}"; do
    [[ -n "$d" ]] || continue
    [[ "$(cd "$d" 2>/dev/null && pwd)" == "$SELF_DIR" ]] && continue
    [[ -x "$d/podman" ]] || continue
    [[ "$(readlink -f "$d/podman")" == "$self" ]] && continue
    printf '%s' "$d/podman"
    return 0
  done
  return 1
}

# The failing condition is "this process has no subuid range to map", which is
# exactly a uid_map totalling one id. Detecting the sandbox implementation
# instead would break the moment the sandbox changes.
mapped_uid_count() {
  local total=0 _inside _outside count
  while read -r _inside _outside count; do
    [[ -n "${count:-}" ]] || continue
    total=$(( total + count ))
  done < /proc/self/uid_map
  printf '%s' "$total"
}

is_confined() {
  [[ "$(mapped_uid_count)" -le 1 ]]
}

PODMAN="$(resolve_podman)" || die "no podman binary found on PATH outside $SELF_DIR"

if ! is_confined; then
  exec "$PODMAN" "$@"
fi

[[ $# -ge 1 ]] || die "usage: deck-podman <podman-args...>"
command -v systemd-run >/dev/null 2>&1 || die "systemd-run not found; cannot reach podman from inside the sandbox"

# The sandbox overlays /tmp with a private upperdir that does not exist outside
# it. Handing such a path to a podman running outside would silently resolve to
# a different directory, so refuse instead of producing a wrong result.
jail_private() {
  local p="$1"
  [[ "$p" == /tmp || "$p" == /tmp/* ]]
}

CWD="$PWD"
if jail_private "$CWD"; then
  die "refusing to run from $CWD: the sandbox /tmp is private and does not exist outside it. cd to a real path (\$HOME, a repo) and retry."
fi

for arg in "$@"; do
  if jail_private "$arg" || [[ "$arg" == *=/tmp/* || "$arg" == *:/tmp/* ]]; then
    die "refusing: argument '$arg' points into the sandbox-private /tmp, which does not exist outside the sandbox. Use a path under \$HOME instead."
  fi
done

# --pipe is not a pty, so an interactive session would attach to a terminal that
# is not there. Only the subcommands that can allocate one are refused; `-t` on
# `podman build` is a tag, not a tty.
subcommand=""
for arg in "$@"; do
  [[ "$arg" == -* ]] && continue
  subcommand="$arg"
  break
done

case "$subcommand" in
  run|exec|attach|start)
    for arg in "$@"; do
      case "$arg" in
        --tty|--interactive|-t|-i|-it|-ti)
          die "refusing '$subcommand $arg': an interactive terminal cannot be forwarded out of the sandbox. Run it yourself outside the agent sandbox, or drop the interactive flags."
          ;;
      esac
    done
    ;;
esac

env_args=()
for v in XDG_RUNTIME_DIR CONTAINERS_CONF CONTAINERS_STORAGE_CONF CONTAINERS_REGISTRIES_CONF PODMAN_CONNECTIONS_CONF; do
  [[ -n "${!v:-}" ]] && env_args+=( "--setenv=$v=${!v}" )
done

exec systemd-run --user --pipe --wait --collect -q \
  -p "KillMode=process" \
  -p "WorkingDirectory=$CWD" \
  --setenv=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
  "${env_args[@]}" \
  "$PODMAN" "$@"
