systemprompt_models/subprocess/
mod.rs1use systemprompt_identifiers::ServiceName;
26
27pub const SUBPROCESS_MARKER_ENV: &str = "SYSTEMPROMPT_SUBPROCESS";
28pub const AGENT_NAME_ENV: &str = "AGENT_NAME";
29pub const MCP_SERVICE_ID_ENV: &str = "MCP_SERVICE_ID";
30pub const API_SERVER_ENV: &str = "SYSTEMPROMPT_API_SERVER";
31pub const API_SERVER_SERVICE: &str = "api";
32
33pub const DEPLOYMENT_HOST_ENV: &str = "SYSTEMPROMPT_DEPLOYMENT_HOST";
34
35pub const FLY_APP_NAME_ENV: &str = "FLY_APP_NAME";
37
38pub const ANY_DEPLOYMENT_HOST_MARKERS: &[&str] = &[DEPLOYMENT_HOST_ENV, FLY_APP_NAME_ENV];
39
40pub fn deployment_host(
41 markers: &[&str],
42 lookup: impl Fn(&str) -> Option<String>,
43) -> Option<String> {
44 markers.iter().find_map(|name| {
45 lookup(name)
46 .map(|value| value.trim().to_owned())
47 .filter(|value| !value.is_empty())
48 })
49}
50
51pub fn is_deployment_host(markers: &[&str], lookup: impl Fn(&str) -> Option<String>) -> bool {
52 deployment_host(markers, lookup).is_some()
53}
54
55pub fn inherited_parent_env(lookup: impl Fn(&str) -> Option<String>) -> Vec<(String, String)> {
56 let mut env: Vec<(String, String)> = [
57 DEPLOYMENT_HOST_ENV,
58 FLY_APP_NAME_ENV,
59 "HOSTNAME",
60 "POD_NAME",
61 "FLY_MACHINE_ID",
62 "PATH",
63 "HOME",
64 ]
65 .iter()
66 .filter_map(|name| lookup(name).map(|value| ((*name).to_owned(), value)))
67 .collect();
68
69 if let Some(entry) = crate::net::trusted_hosts_env_entry(&lookup) {
70 env.push(entry);
71 }
72
73 env
74}
75
76#[must_use]
77pub const fn identity_verification_supported() -> bool {
78 cfg!(any(target_os = "linux", target_os = "macos"))
79}
80
81#[must_use]
82pub fn signalable_pid(pid: u32) -> Option<i32> {
83 if pid == 0 {
84 return None;
85 }
86 i32::try_from(pid).ok()
87}
88
89#[must_use]
90pub fn environ_identifies_child(
91 environ: &[u8],
92 name_key: &str,
93 service_name: &ServiceName,
94) -> bool {
95 let marker = format!("{SUBPROCESS_MARKER_ENV}=1");
96 let expected_name = format!("{name_key}={service_name}");
97
98 let mut has_marker = false;
99 let mut has_name = false;
100 for entry in environ.split(|&b| b == 0) {
101 if entry == marker.as_bytes() {
102 has_marker = true;
103 } else if entry == expected_name.as_bytes() {
104 has_name = true;
105 }
106 }
107
108 has_marker && has_name
109}
110
111#[must_use]
112pub fn environ_identifies_api_server(environ: &[u8], service_name: &ServiceName) -> bool {
113 let expected = format!("{API_SERVER_ENV}={service_name}");
114 environ
115 .split(|&b| b == 0)
116 .any(|entry| entry == expected.as_bytes())
117}
118
119#[must_use]
120pub fn environ_has_entries(environ: &[u8]) -> bool {
121 environ
122 .split(|&b| b == 0)
123 .any(|entry| entry.contains(&b'='))
124}
125
126#[must_use]
130pub fn environ_from_procargs2(blob: &[u8]) -> Option<&[u8]> {
131 const ARGC_LEN: usize = size_of::<i32>();
132
133 let argc_bytes: [u8; ARGC_LEN] = blob.get(..ARGC_LEN)?.try_into().ok()?;
134 let argc = usize::try_from(i32::from_ne_bytes(argc_bytes)).ok()?;
135
136 let mut rest = blob.get(ARGC_LEN..)?;
137 let exec_path_end = rest.iter().position(|&b| b == 0)?;
138 rest = rest.get(exec_path_end + 1..)?;
139
140 let argv_start = rest.iter().position(|&b| b != 0)?;
141 rest = rest.get(argv_start..)?;
142
143 for _ in 0..argc {
144 let entry_end = rest.iter().position(|&b| b == 0)?;
145 rest = rest.get(entry_end + 1..)?;
146 }
147
148 Some(rest)
149}