1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! Broker-owned listener handoff policy.
//!
//! Unix descriptor inheritance and validation are platform mechanics and are
//! intentionally hidden behind [`crate::platform::ipc`]. This module owns the
//! product environment contract and whether the broker elects to use it.
/// Environment variable naming the listener inherited by a daemon.
pub const INHERITED_LISTENER_FD_ENV: &str = "RUNNING_PROCESS_BROKER_LISTENER_FD";
/// Escape hatch for broker-owned bind. Set to `0` to use spawn-then-probe.
pub const LAUNCHER_OPT_IN_ENV: &str = "RUNNING_PROCESS_BROKER_OWNED_BIND";
/// Whether the launcher should bind the endpoint itself.
pub fn launcher_opt_in() -> bool {
crate::env_vars::BROKER_OWNED_BIND.is_set()
}
/// Whether this platform can hand a bound listener to a spawned daemon.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Support {
/// The broker can bind and pass the listener.
Supported,
/// It cannot, and this is why.
Unsupported { reason: &'static str },
}
impl Support {
/// Whether broker-owned bind can be used here.
pub fn is_supported(&self) -> bool {
matches!(self, Self::Supported)
}
}
/// Report the capability selected by the platform facade.
pub fn support() -> Support {
if InheritableListener::supported() {
Support::Supported
} else {
Support::Unsupported {
reason: "a Windows named-pipe listener is a single instance that becomes the connection on accept, so there is no bound listener object to hand to a child; the spawn-then-probe path applies instead",
}
}
}
/// Broker-facing opaque listener-inheritance operation.
///
/// This preserves the established broker API while the selected platform
/// implementation owns descriptor/handle details.
pub struct InheritableListener {
inner: crate::platform::ipc::InheritedListener,
}
impl InheritableListener {
/// Bind a listener that can be passed to a spawned daemon.
pub fn bind(endpoint: &str) -> std::io::Result<Self> {
let endpoint = crate::platform::ipc::Endpoint::new(endpoint.to_owned())?;
crate::platform::ipc::InheritedListener::bind(&endpoint).map(|inner| Self { inner })
}
/// Configure the child's inherited-listener environment contract.
pub fn prepare(&self, command: &mut std::process::Command) -> std::io::Result<()> {
self.inner.prepare(command, INHERITED_LISTENER_FD_ENV)
}
/// Prepare the listener and return the opaque exec-preservation token used
/// by the broker's sanitized daemon-spawn path.
pub(crate) fn prepare_for_daemon(
&self,
command: &mut std::process::Command,
) -> std::io::Result<running_process_platform_internal::platform::process::DaemonExecInheritance>
{
self.inner
.prepare_for_daemon(command, INHERITED_LISTENER_FD_ENV)
}
/// Release endpoint-name cleanup once the child owns the listener.
pub fn disown_endpoint(&mut self) {
self.inner.disown_endpoint();
}
/// Whether the selected platform supports listener inheritance.
pub fn supported() -> bool {
crate::platform::ipc::InheritedListener::supported()
}
}
/// Recover the broker listener inherited by this process, if any.
pub fn recover_from_env() -> std::io::Result<Option<crate::platform::ipc::Listener>> {
crate::platform::ipc::InheritedListener::recover_from_env(INHERITED_LISTENER_FD_ENV)
}
#[cfg(test)]
mod tests;