Add -e/--exec to join a running -r/--run session

Validates the given pid against the same tracked-session liveness
check --list-processes/--clean-processes already use, then joins its
namespaces via nsenter and runs a command there in the foreground.

Two things discovered only by testing against a live session, not
assumed up front:

- The tracked pid is bwrap's own outer process. It sets up the
  mount/user namespaces itself, then clone()s the actual sandboxed
  command into fresh pid/uts/ipc/cgroup namespaces -- clone()'s
  namespace flags only ever affect the new child, never the caller,
  so the outer process itself never enters those namespaces at all.
  exec_in_session() resolves that real child via
  /proc/<pid>/task/<pid>/children and joins its namespaces instead,
  falling back to the outer pid if that can't be read.

- Rather than nsenter -a (which would hit a known "Invalid argument"
  failure re-entering an identical namespace -- this project already
  worked around exactly that once, for the containers-storage mount
  path), each namespace type is only joined if
  /proc/<pid>/ns/<type> actually differs from this process's own.
  nsenter also needs --preserve-credentials, or it tries to
  setuid/setgid/setgroups to the target's identity, which fails
  outright against the setgroups-denied unprivileged user namespace
  bwrap creates whenever -r/--run isn't root.

Verified end-to-end: joined shell gets the container's own hostname,
process tree (ps shows only container processes), and root
filesystem; untracked/stale pids error out cleanly without touching
nsenter; Ctrl-C during the joined command doesn't disturb the
original session; no leftover mounts after either exits.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
2026-08-22 09:41:32 +00:00
parent d4c0e8f0a1
commit 00482b6b42
6 changed files with 275 additions and 7 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ configure_file(output : 'config.h', configuration : conf_data)
slocker_lite = executable('slocker-lite',
['src/main.cpp', 'src/process.cpp', 'src/oci_image.cpp', 'src/containers_storage.cpp',
'src/bwrap.cpp', 'src/user_spec.cpp', 'src/config_file.cpp', 'src/volume_mount.cpp',
'src/pid_file.cpp'],
'src/pid_file.cpp', 'src/exec_session.cpp'],
include_directories : include_directories('.'),
dependencies : [fmt_dep, catch2_dep, yaml_dep, archive_dep, json_dep, spdlog_dep],
install : true)