Skip to content

fix(gateway): keep VM driver alive during Ctrl+C shutdown - #5126

Open
vineethsai7 wants to merge 2 commits into
NVIDIA:mainfrom
vineethsai7:fix/4770-vm-driver-process-group-v2
Open

vineethsai7 wants to merge 2 commits into
NVIDIA:mainfrom
vineethsai7:fix/4770-vm-driver-process-group-v2

Conversation

@vineethsai7

@vineethsai7 vineethsai7 commented Nov 2, 2026 •

Copy link
Copy Markdown

Summary

Ctrl+C sends SIGINT to a foreground gateway's entire process group. The VM compute driver and its VM launchers inherited that group, so they could exit before the gateway's shutdown sweep stopped its sandboxes. Start the driver in its own process group and relay its output through the gateway so the driver remains available during the sweep, including in terminals with stty tostop.

Related Issue

Part of #4770 (the driver now survives Ctrl+C; --no-keep deletion on shutdown is left for a follow-up)

Supersedes #5068 (auto-closed before the vouch)

Changes

  • Start the managed VM compute driver in a separate process group. Its launchers inherit that group, while the foreground gateway alone receives terminal SIGINT and can complete its shutdown sweep before stopping the driver.
  • Pipe the driver's stdout and stderr and copy each stream to the gateway's corresponding output stream. The background driver and launchers therefore never write directly to a TOSTOP terminal; their logs remain visible through the foreground gateway.
  • Add unit tests for process-group separation and piped output.

Review findings from #5068:

  • P1, DCO: The new commit has the author's Signed-off-by trailer.
  • P1, commit text: The new commit and this description omit the prohibited attribution footer and co-author lines.
  • P2, TOSTOP: The driver and launchers write to pipes instead of the controlling terminal. The gateway forwards both streams. The output-pipe regression test fails when the command is changed back to inherited stdout and stderr.

The shutdown sweep now has a live driver to stop sandboxes. This change does not make --no-keep sandboxes delete during gateway shutdown or change the attached CLI's cleanup error if the gateway exits before the CLI can delete. Those behaviors remain for a separate change.

Release Notes

Stopping a foreground VM gateway with Ctrl+C now leaves the compute driver available for sandbox shutdown. Driver logs remain visible when terminal background-output stopping is enabled.

Testing

Run on the Linux build box through dcb up-os-1221 with nice -n 15, Rust 1.95.0, and the gateway crate only:

  • Checks appropriate to the affected code and behavior pass: cargo fmt -p openshell-gateway -- --check; cargo clippy --locked -p openshell-gateway --features vendored-z3 --all-targets -- -D warnings.
  • Unit tests added/updated: cargo test --locked -p openshell-gateway --features vendored-z3 passed (37 library tests and 11 integration tests). The new output-pipe test failed against inherited stdio with driver stdout must be piped, then passed with piped stdio.
  • E2E tests added/updated: not run; the VM sandbox runtime was not started for this branch.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable): no architecture or published documentation describes this process launch detail.

Review follow-up

  • P1, VM Ctrl+C shutdown E2E (finding): Not applicable on the build box. The full VM sandbox lifecycle needs KVM, Docker, and mise, which are unavailable there. This is a valid validation gap; the lead will run the manual E2E path on a real host before merge.
  • P2, parent-death startup race (finding): Fixed in afb89df65b24dfaca8074132d292869d99155cd0. A managed VM driver now verifies that its parent is the expected gateway before and after arming procguard, and startup fails if the parent changed or procguard cannot arm. The process-level regression test timed out on the original code and passed after the fix.

Follow-up checks passed through dcb up-os-1221 with nice -n 15: cargo fmt -p openshell-driver-vm -p openshell-gateway -- --check; cargo clippy --locked -p openshell-gateway -p openshell-driver-vm --features openshell-gateway/vendored-z3 --all-targets -- -D warnings; and cargo test --quiet --locked -p openshell-driver-vm -p openshell-gateway --features openshell-gateway/vendored-z3 --all-targets (345 passed).

Manual verification

Result: PASS

Environment

  • Host: macOS 15.8 (Apple silicon, arm64, Hypervisor.framework available), a disposable local account, private HOME, gateway on a non-default port (28181/28182), plaintext loopback, no other OpenShell install touched.
  • Before: upstream main at be5e49499 (fix(deps): update russh and pageant security fixes (#5123)), the base of this PR. After: PR head afb89df65b (fix(vm): reject managed driver after gateway exit). Both built on the host with Rust 1.95.0, cargo build --release -p openshell-gateway -p openshell-driver-vm (-p openshell-cli for the base), same embedded VM runtime, driver signed ad hoc with the Hypervisor entitlement.
  • VM runtime: published vm-runtime darwin-aarch64 libkrun/libkrunfw, guest openshell-sandbox from v0.1.6, locally built openshell-vm-init and host supervisor. Sandbox image: ubuntu:24.04 from the registry.

Steps (same for before and after)

  1. In a tmux terminal, start the gateway in the foreground with the VM driver (compute_driver = "vm"), stdout/stderr on the terminal:
    openshell-gateway --config gateway.toml --port <port> --compute-driver vm --disable-tls
  2. In a second terminal: openshell sandbox create --name <n> --from ubuntu:24.04 -- sleep 3600 (sandbox reaches Ready, supervisor connected).
  3. List the processes (ps -o pid,ppid,pgid,tpgid,stat), then start a sampler that records the gateway, driver and VM launcher processes every 0.2 s.
  4. Send Ctrl+C to the gateway terminal (tmux send-keys C-c, so the kernel delivers SIGINT to the terminal foreground process group).
  5. Read the gateway output, then start the gateway again and read the sandbox phase.
  6. After: repeat steps 1-5 once more with stty tostop set in the gateway terminal.

Before (main, be5e49499)

The driver and both VM launchers share the gateway process group, which is the terminal foreground group (TPGID):

  PID  PPID  PGID TPGID STAT COMMAND
50022 49916 50022 50022 S+   openshell-gateway
50033 50022 50022 50022 S+   openshell-driver-vm
50312 50033 50022 50022 S+   openshell-driver-vm --internal-run-vm ...
50314 50312 50022 50022 S+   openshell-driver-vm --internal-run-vm ...

After Ctrl+C the driver gets the same SIGINT and stops itself at the same instant as the gateway; the shutdown sweep finds no driver:

18:51:19.605036Z  INFO openshell_server: Shutdown signal received; stopping gateway
18:51:19.605136Z  INFO openshell_driver_vm: Shutdown signal received; stopping vm compute driver
18:51:19.606575Z  WARN openshell_server::compute: Failed to stop sandbox during gateway shutdown sandbox_name="sb-base" error=code: 'The service is currently unavailable', message: "No such file or directory (os error 2)" ...
18:51:19.606698Z  INFO openshell_server::compute: Sandbox shutdown stop sweep complete stopped=0 failed=1
Error:   x execution error: gateway shutdown cleanup failed: failed to stop 1 sandbox(es) during gateway shutdown

The sampler shows the gateway, driver and launchers all gone within one 0.2 s sample (18:51:19.532 present, 18:51:19.765 empty). After restarting the gateway the sandbox is left in Error:

Name: sb-base
Phase: Error
Conditions:
  - Ready: False (StartFailed) - Failed to start sandbox during gateway startup: VM sandbox is already running generation 47f7c6fb...

After (PR head, afb89df65b)

The driver and its launchers lead their own process group (PGID 52488); only the gateway is in the terminal foreground group (TPGID 52473):

  PID  PPID  PGID TPGID STAT COMMAND
52473 49916 52473 52473 S+   openshell-gateway
52488 52473 52488 52473 S    openshell-driver-vm
52587 52488 52488 52473 S    openshell-driver-vm --internal-run-vm ...
52588 52587 52488 52473 S    openshell-driver-vm --internal-run-vm ...

After Ctrl+C the gateway runs its shutdown sweep against a live driver, the sandbox is stopped, and only then is the driver told to stop:

18:55:21.921054Z  INFO openshell_server: Shutdown signal received; stopping gateway
18:55:21.959550Z  INFO openshell_server::compute: Stopped sandbox during gateway shutdown sandbox_name="sb-fix" phase=Ready
18:55:21.959603Z  INFO openshell_server::compute: Sandbox shutdown stop sweep complete stopped=1 failed=0
18:55:21.959641Z  INFO openshell_driver_vm: Shutdown signal received; stopping vm compute driver

The sampler shows the driver and launchers present in every sample up to 18:55:21.849 and gone at 18:55:22.078; the log order above is what matters: the sweep stopped the sandbox (stopped=1) before the driver was told to stop. Restarting the gateway brings the same sandbox back: Phase: Ready, new main process instance, no Error.

Repeat with stty tostop (verified with stty -a, tostop set): same result, stopped=1 failed=0, the driver log lines still appear in the gateway terminal (forwarded through the gateway), and nothing was suspended by SIGTTOU:

18:56:32.866647Z  INFO openshell_server: Shutdown signal received; stopping gateway
18:56:32.905167Z  INFO openshell_server::compute: Stopped sandbox during gateway shutdown sandbox_name="sb-fix" phase=Ready
18:56:32.905196Z  INFO openshell_server::compute: Sandbox shutdown stop sweep complete stopped=1 failed=0
18:56:32.905235Z  INFO openshell_driver_vm: Shutdown signal received; stopping vm compute driver

Result

PASS. On main, Ctrl+C on a foreground VM gateway kills the driver first (stop sweep stopped=0 failed=1, sandbox left in Error after restart). On the PR head the driver survives until the sweep has stopped the sandbox (stopped=1 failed=0), the sandbox restarts Ready, and the same holds with stty tostop.

Not covered: the --no-keep delete-on-shutdown behaviour the PR description says is out of scope.

A terminal sends SIGINT to its foreground process group. The VM driver
and its launchers shared that group, so they exited before the gateway's
shutdown sweep could stop running sandboxes.

Give the driver its own group and relay its stdout and stderr through
the foreground gateway. The pipes keep driver logs visible without
suspending the background group in TOSTOP terminals.

Add unit coverage for process-group separation and piped output.

Signed-off-by: Vineeth Sai Narajala <vnarajal@cisco.com>
@copy-pr-bot

copy-pr-bot Bot commented Nov 2, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-02T17:27:27.626151Z ffc8f7d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ffc8f7dd11

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/openshell-gateway/src/vm.rs Resolved
Comment thread crates/openshell-gateway/src/vm.rs Resolved
@vineethsai7

Copy link
Copy Markdown
Author

I have read the DCO document and I hereby sign the DCO.

Signed-off-by: Vineeth Sai Narajala <vnarajal@cisco.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant