Skip to content

fix(gateway): run the VM compute driver in its own process group - #5068

Closed
vineethsai7 wants to merge 1 commit into
NVIDIA:mainfrom
vineethsai7:fix/4770-vm-driver-process-group/vineethsai7
Closed

vineethsai7 wants to merge 1 commit into
NVIDIA:mainfrom
vineethsai7:fix/4770-vm-driver-process-group/vineethsai7

Conversation

@vineethsai7

Copy link
Copy Markdown

Summary

Run the VM compute driver in its own process group, so Ctrl+C on a gateway running in the foreground reaches only the gateway. The gateway's shutdown sweep then stops sandboxes while the driver is still up, instead of failing with stopped=0 failed=1 and leaving the sandbox in Error after a restart.

Related Issue

Closes #4770

Downstream report: cisco-ai-defense/defenseclaw#1221

Related: #4409 changes the same shutdown sweep for a different trigger (a pending driver operation on SIGTERM). This PR does not touch the sweep.

Problem

Repro with a foreground VM gateway:

  1. Start openshell-gateway with the VM compute driver in a terminal.
  2. Run openshell sandbox create --no-keep -- sleep 600 from another terminal.
  3. Press Ctrl+C in the gateway's terminal.
  4. Start the gateway again and run openshell sandbox list.

Observed: the gateway logs gateway shutdown cleanup failed with stopped=0 failed=1, the attached CLI exits 255 with ephemeral cleanup failed, and after the restart the sandbox is listed in Error. A gateway stopped with SIGTERM from systemd or launchd is not affected.

Root cause: Ctrl+C makes the terminal send SIGINT to its whole foreground process group. vm::spawn started openshell-driver-vm in the gateway's group, and the --internal-run-vm launchers that the driver starts inherit that group, so the gateway, the driver and every launcher got SIGINT at once. The driver exited through its shutdown_signal handler, and each launcher forwarded the signal to its VM worker. When the gateway's cleanup_on_shutdown sweep then asked the driver to stop the persisted sandboxes, the driver was gone.

Changes

  • crates/openshell-gateway/src/vm.rs: move the base driver command (kill_on_drop, stdio) into compute_driver_command and add process_group(0). Tokio's Command::process_group is available in the workspace's tokio (1.53).
  • Unit test vm_driver_command_runs_driver_in_its_own_process_group: spawns a stand-in process through compute_driver_command and checks that it leads its own process group, which differs from the test process's group.

Behaviour after the change:

  • Ctrl+C: only the gateway gets SIGINT. It runs the sweep while the driver is serving, then ManagedDriverProcess::shutdown sends SIGTERM to the driver (SIGKILL after 5 seconds).
  • Launchers: they still inherit the driver's group, which is now the driver's own, so they are out of the terminal's group too and are stopped by the driver as before. No change in the driver.
  • Gateway killed with SIGKILL or crashing: unchanged. The driver arms PR_SET_PDEATHSIG(SIGKILL) on Linux and a kqueue parent-exit watcher on macOS at startup (procguard::die_with_parent), and the launchers arm their own. These track the parent process, not the process group, so the driver and its launchers still exit when the gateway dies. kill_on_drop is kept for the drop path.
  • Terminal hangup: the gateway still gets SIGHUP as part of the foreground group and exits; the driver follows through the parent-death signal, as in the crash case.
  • Other spawns checked: the VM driver is the only managed compute-driver subprocess (Docker, Podman and Kubernetes drivers run in process). The credential helper command in openshell-server/src/credentials.rs is short-lived and not part of shutdown, so it is left alone.

This PR does not change the shutdown sweep itself. Deleting rather than stopping ephemeral sandboxes during shutdown, and treating ProcessExited during shutdown as recoverable, can follow separately if maintainers want it.

Release Notes

Pressing Ctrl+C on a gateway that runs the VM driver in a terminal now stops its sandboxes cleanly. Before, the driver exited first and sandboxes were left in Error.

Testing

  • Checks appropriate to the affected code and behavior pass: cargo fmt -p openshell-gateway --check; cargo clippy -p openshell-gateway --features vendored-z3 --all-targets -- -D warnings (Linux x86_64, Rust 1.95.0).
  • Unit tests added/updated (if applicable): cargo test -p openshell-gateway --features vendored-z3 passes: 36 library tests, including the new one, plus 11 binary tests. The new test fails when process_group(0) is removed (assertion left == right failed: the driver must lead its own process group).
  • E2E tests added/updated (if applicable): not added. The live check above was run by hand; an e2e/rust/tests/ephemeral_cleanup.rs case that sends SIGINT to the gateway's process group can follow if wanted.

Before/after (live)

Tested on RHEL 9.8 x86_64 with nested KVM, with the v0.1.6 openshell CLI and openshell-driver-vm. Only the gateway binary changed. Each run used a fresh state dir and a real Ctrl+C in a tmux pane on a foreground gateway, with openshell sandbox create --no-keep -- sleep 600 attached in a second pane.

v0.1.6 gateway (3 runs) this branch (2 runs)
Process groups before Ctrl+C gateway, driver, --internal-run-vm launcher, VM and supervisor all in the gateway's group, which is the terminal's foreground group driver, launcher and supervisor in their own group (the driver is the group leader); gateway alone in the terminal's group
Gateway on Ctrl+C Shutdown signal received; stopping vm compute driver, then Failed to stop sandbox during gateway shutdown ... service is currently unavailable, then stopped=0 failed=1 and gateway shutdown cleanup failed Stopped sandbox during gateway shutdown ... phase=Ready, then stopped=1 failed=0; the driver is stopped afterwards; no openshell processes left
sandbox list after restart sandbox in Error, still Error 30 s later; only a manual delete removes it restored inactive sandbox, started=1 failed=0, Provisioning then Ready; never Error
SIGTERM to the gateway PID (control) stopped=1 failed=0 stopped=1 failed=0, gateway gone in 2 s, no driver, VM or supervisor left

What this PR does not change: the attached CLI still prints ephemeral cleanup failed: connect error: transport error, because the gateway has already exited when the CLI sends its ephemeral delete. The --no-keep sandbox is stopped and comes back after the restart instead of being deleted. Removing it during shutdown is the second step in #4770 (the sweep deletes, rather than stops, sandboxes whose create session is ephemeral). It needs a maintainer decision, so it is left to a follow-up.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

🤖 Generated with Claude Code

Ctrl+C on a gateway running in the foreground sends SIGINT to the
terminal's whole foreground process group. The VM compute driver and its
VM launchers inherited the gateway's group, so they exited on that signal
before the gateway's shutdown sweep asked the driver to stop its
sandboxes. The sweep then failed (stopped=0 failed=1), an attached
`sandbox create --no-keep` reported "ephemeral cleanup failed", and the
sandbox came back in Error after a restart.

Spawn the driver with process_group(0). Terminal signals now reach only
the gateway, which runs its sweep while the driver is still serving and
then stops the driver itself with SIGTERM. The launchers inherit the
driver's new group, so they are also out of the terminal's group and
stay under the driver's control. A gateway that dies without a graceful
shutdown still takes the driver with it: the driver arms a parent-death
signal at startup, which does not depend on the process group.

Closes NVIDIA#4770

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

copy-pr-bot Bot commented Oct 30, 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 Oct 30, 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-10-30T16:42:09.867246Z 5b77258 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.

@github-actions

Copy link
Copy Markdown

Thank you for your interest in contributing to OpenShell, @vineethsai7.

This project uses a vouch system for first-time contributors. Before submitting a pull request, you need to be vouched by a maintainer.

To get vouched:

  1. Open a Vouch Request discussion.
  2. Describe what you want to change and why.
  3. Write in your own words — do not have an AI generate the request.
  4. A maintainer will comment /vouch if approved.
  5. Once vouched, open a new PR (preferred) or reopen this one after a few minutes.

See CONTRIBUTING.md for details.

@github-actions github-actions Bot closed this Oct 30, 2026
@github-actions

Copy link
Copy Markdown

Thank you for your submission! We ask that you sign our Developer Certificate of Origin before we can accept your contribution. You can sign the DCO by adding a comment below using this text:


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


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the DCO Assistant Lite bot.

@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: 5b772589e0

ℹ️ 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".

))
}

/// Base command for the VM compute-driver subprocess.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Add the required DCO trailer

The reviewed commit contains no Signed-off-by trailer even though its checklist marks DCO complete, so it does not satisfy this repository's per-commit DCO requirement and the DCO check cannot accept it; add the author's valid sign-off.

AGENTS.md reference: AGENTS.md:L191-L191

Useful? React with 👍 / 👎.


/// Base command for the VM compute-driver subprocess.
///
/// The driver gets its own process group. Ctrl+C on a foreground gateway

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove the AI attribution from the commit message

The reviewed commit message ends with a Generated with [Claude Code] attribution, which repository policy explicitly forbids in commit or PR text; remove that footer before this commit is accepted.

AGENTS.md reference: AGENTS.md:L192-L192

Useful? React with 👍 / 👎.

fn compute_driver_command(driver_bin: &Path) -> Command {
let mut command = Command::new(driver_bin);
command.kill_on_drop(true);
command.process_group(0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep the driver writable in TOSTOP terminals

When a foreground gateway is started in a terminal configured with stty tostop, this makes the driver a background process group while its stdout and stderr remain attached to that terminal. Its first log write therefore receives SIGTTOU and stops the driver, so wait_for_compute_driver times out and the gateway cannot start; route the child's output through the foreground gateway or otherwise detach it from the controlling terminal while preserving the SIGINT isolation.

Useful? React with 👍 / 👎.

@vineethsai7

Copy link
Copy Markdown
Author

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

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.

bug(gateway): Ctrl+C on a foreground gateway fails to clean up its ephemeral (--no-keep) MicroVM sandbox and leaves it in Error

1 participant