|
@@ -22,7 +22,7 @@ Every eligible Linux ordinary or PTY spawn rechecks the exact runner entry, the
|
|
|
|
|
|
|
|
The parent creates one 0700 directory with a complete 0600 `launch-request.json` containing the final target cwd and environment. The private `DSH_SUBPROCESS_RUNNER` value locates that request while the runner starts from the provider cwd and a bootstrap-safe environment. `systemd-run --user --scope --quiet --collect --expand-environment=no` registers its process in the scope, then the one-shot bootstrap removes and validates the request, changes to the target cwd, restores the complete target environment, resolves a bare executable with the target PATH rules, clears `FD_CLOEXEC` on fd 0 through fd 2, and calls libc `execve()` with the original argv. The bootstrap becomes the target in place and preserves its inherited stdio; it does not remain as a supervisor.
|
|
The parent creates one 0700 directory with a complete 0600 `launch-request.json` containing the final target cwd and environment. The private `DSH_SUBPROCESS_RUNNER` value locates that request while the runner starts from the provider cwd and a bootstrap-safe environment. `systemd-run --user --scope --quiet --collect --expand-environment=no` registers its process in the scope, then the one-shot bootstrap removes and validates the request, changes to the target cwd, restores the complete target environment, resolves a bare executable with the target PATH rules, clears `FD_CLOEXEC` on fd 0 through fd 2, and calls libc `execve()` with the original argv. The bootstrap becomes the target in place and preserves its inherited stdio; it does not remain as a supervisor.
|
|
|
|
|
|
|
|
-Request consumption or a manager observation of the unit establishes scope ownership. Unit absence before either fact remains unresolved; direct-child exit with an unconsumed request is establishment failure. After establishment, an inactive, failed, or collected-away unit proves the range empty. Unknown states and unreadable manager results reject `waitForExit()` instead of claiming quiescence. A strict sibling `startup-error.json` carries only request/bootstrap or target pre-exec failure, and the parent removes this spawn's private paths at observable lifecycle completion.
|
|
|
|
|
|
|
+Request consumption or a manager observation of the unit establishes scope ownership. Unit absence before either fact remains unresolved; direct-child exit with an unconsumed request is establishment failure. The parent checks this unresolved interval every 50 milliseconds; after establishment, active-state queries back off exponentially to the existing 5-second systemctl bound. An inactive, failed, or collected-away unit proves the range empty. Unknown states and unreadable manager results reject `waitForExit()` instead of claiming quiescence. A strict sibling `startup-error.json` carries only request/bootstrap or target pre-exec failure, and the parent removes this spawn's private paths at observable lifecycle completion.
|
|
|
|
|
|
|
|
The ordinary target result still comes from the same child process. The PTY path uses the same request and bootstrap without a resident runner, so the `node-pty` PID, process group, session leader, controlling terminal, foreground `inputWaiting`, `/dev/tty`, readiness, and direct terminal outcome retain their existing meanings while scope membership covers `setsid` and reparented descendants.
|
|
The ordinary target result still comes from the same child process. The PTY path uses the same request and bootstrap without a resident runner, so the `node-pty` PID, process group, session leader, controlling terminal, foreground `inputWaiting`, `/dev/tty`, readiness, and direct terminal outcome retain their existing meanings while scope membership covers `setsid` and reparented descendants.
|
|
|
|
|
|
|
@@ -32,7 +32,7 @@ The Windows parent starts the provider runner from a bootstrap cwd and environme
|
|
|
|
|
|
|
|
The runner is the sole owner of the target process handle and unnamed Job handle. `spawnCurrentTokenJobProcess` creates the target suspended, assigns it to a kill-on-close Job that disallows active breakaway, and resumes it only after assignment. The runner polls the direct process for the target exit code and the Job for active-process count. It exits successfully only after the direct result has been delivered through the IPC send callback and the Job has reported zero active processes; the parent maps only that clean exit to successful `waitForExit()`.
|
|
The runner is the sole owner of the target process handle and unnamed Job handle. `spawnCurrentTokenJobProcess` creates the target suspended, assigns it to a kill-on-close Job that disallows active breakaway, and resumes it only after assignment. The runner polls the direct process for the target exit code and the Job for active-process count. It exits successfully only after the direct result has been delivered through the IPC send callback and the Job has reported zero active processes; the parent maps only that clean exit to successful `waitForExit()`.
|
|
|
|
|
|
|
|
-The parent permanently latches a validated numeric `target-exit` as soon as it arrives, before the existing stdout/stderr close or bounded-drain barrier settles. `.done` waits only for that stdio barrier and then returns the latched result. A later Job query, range-settlement failure, IPC loss, or abnormal runner exit rejects only `waitForExit()` and cannot replace the direct result. Infrastructure failure before a valid target result rejects `.done`. On disconnect or result-send failure, the runner stops protocol work, terminates and closes its only Job handle, and exits nonzero. Closing the last Job handle kills remaining members but does not convert the disconnected path into a successful quiescence proof.
|
|
|
|
|
|
|
+The parent permanently latches a validated numeric `target-exit` as soon as it arrives, before the existing stdout/stderr close or bounded-drain barrier settles. `.done` waits only for that stdio barrier and then returns the latched result. A later Job query, range-settlement failure, IPC loss, or abnormal runner exit rejects only `waitForExit()` and cannot replace the direct result. Infrastructure failure before a valid target result rejects `.done`, but that rejection does not reveal whether target execution began. On disconnect or result-send failure, the runner stops protocol work, terminates and closes its only Job handle, and exits nonzero. Closing the last Job handle kills remaining members but does not convert the disconnected path into a successful quiescence proof.
|
|
|
|
|
|
|
|
### Private dispatch and protocol
|
|
### Private dispatch and protocol
|
|
|
|
|
|
|
@@ -54,7 +54,7 @@ This note owns the current native-containment mechanism. It partially updates th
|
|
|
|
|
|
|
|
## Verification
|
|
## Verification
|
|
|
|
|
|
|
|
-- Provider and Linux protocol suites pin synchronous NUL rejection before launch side effects, strict request/error decoding, target cwd and complete environment restoration, private-variable collision, symlink-sensitive PATH traversal with preserved argv, close-on-exec removal for inherited stdio, pre-exec error ownership, the three scope-establishment states, and exactly-once PTY managed-owner cleanup.
|
|
|
|
|
|
|
+- Provider and Linux protocol suites pin synchronous NUL rejection before launch side effects, strict request/error decoding, target cwd and complete environment restoration, private-variable collision, symlink-sensitive PATH traversal with preserved argv, close-on-exec removal for inherited stdio, pre-exec error ownership, the three scope-establishment states, prompt pre-establishment polling with bounded established-scope backoff, and exactly-once PTY managed-owner cleanup.
|
|
|
- Windows protocol and Win32 suites pin exactly three result branches, numeric-only target exits, raw local cancellation reasons, `EPERM`/`-4048` access-denied mapping, explicit ordinally sorted target environment blocks with `=C:` preservation and double-NUL termination, `uv_get_osfhandle()` carrier mapping and invalid-result rejection, the null-device ignored-stdin carrier and piped non-ignored stdin, result-send and IPC-disconnect failures, direct-result latching before stdio settlement, active-process quiescence, and unique handle cleanup.
|
|
- Windows protocol and Win32 suites pin exactly three result branches, numeric-only target exits, raw local cancellation reasons, `EPERM`/`-4048` access-denied mapping, explicit ordinally sorted target environment blocks with `=C:` preservation and double-NUL termination, `uv_get_osfhandle()` carrier mapping and invalid-result rejection, the null-device ignored-stdin carrier and piped non-ignored stdin, result-send and IPC-disconnect failures, direct-result latching before stdio settlement, active-process quiescence, and unique handle cleanup.
|
|
|
- Real Linux user-systemd tests run one ordinary and one `node-pty` `setsid`/reparent scenario through the production entry. They prove scope signalling and collection, bare executable lookup, escaped-descendant termination, range settlement, and unchanged PTY PID, session, controlling-terminal, foreground-input, `/dev/tty`, readiness, and startup-failure semantics.
|
|
- Real Linux user-systemd tests run one ordinary and one `node-pty` `setsid`/reparent scenario through the production entry. They prove scope signalling and collection, bare executable lookup, escaped-descendant termination, range settlement, and unchanged PTY PID, session, controlling-terminal, foreground-input, `/dev/tty`, readiness, and startup-failure semantics.
|
|
|
- Native Windows tests prove suspended creation, Job assignment before resume, inherited stdio, default descendant inheritance, direct result, termination, active-process zero, abnormal/disconnected runner cleanup, kill-on-close, and synchronous host-exit termination. Source, built, and Python packaged smokes enter the same runner core.
|
|
- Native Windows tests prove suspended creation, Job assignment before resume, inherited stdio, default descendant inheritance, direct result, termination, active-process zero, abnormal/disconnected runner cleanup, kill-on-close, and synchronous host-exit termination. Source, built, and Python packaged smokes enter the same runner core.
|