systemprompt_loader/subprocess/
ports.rs1use std::process::Command;
15
16use super::SupervisionError;
17
18#[must_use]
19pub fn parse_lsof_pids(stdout: &str) -> Vec<u32> {
20 let mut pids: Vec<u32> = stdout
21 .lines()
22 .filter_map(|line| line.trim().parse().ok())
23 .filter(|pid| *pid != 0)
24 .collect();
25 pids.sort_unstable();
26 pids.dedup();
27 pids
28}
29
30#[must_use]
31pub fn parse_netstat_listeners(stdout: &str, port: u16) -> Vec<u32> {
32 let suffix = format!(":{port}");
33 let mut pids: Vec<u32> = stdout
34 .lines()
35 .filter_map(|line| {
36 let fields: Vec<&str> = line.split_whitespace().collect();
37 match fields.as_slice() {
38 [proto, local, _remote, state, pid]
39 if proto.eq_ignore_ascii_case("TCP")
40 && local.ends_with(&suffix)
41 && state.eq_ignore_ascii_case("LISTENING") =>
42 {
43 pid.parse().ok()
44 },
45 _ => None,
46 }
47 })
48 .filter(|pid| *pid != 0)
49 .collect();
50 pids.sort_unstable();
51 pids.dedup();
52 pids
53}
54
55#[cfg(unix)]
56pub(super) fn listening_pids(port: u16) -> Result<Vec<u32>, SupervisionError> {
57 let output = Command::new("lsof")
58 .args(["-nP", "-t", &format!("-iTCP:{port}"), "-sTCP:LISTEN"])
59 .output()
60 .map_err(|source| SupervisionError::Tool {
61 tool: "lsof",
62 source,
63 })?;
64 if !output.status.success() && !output.stdout.is_empty() {
66 return Err(SupervisionError::ToolFailed {
67 tool: "lsof",
68 status: output.status,
69 });
70 }
71 Ok(parse_lsof_pids(&String::from_utf8_lossy(&output.stdout)))
72}
73
74#[cfg(windows)]
75pub(super) fn listening_pids(port: u16) -> Result<Vec<u32>, SupervisionError> {
76 let output = Command::new("netstat")
77 .args(["-ano", "-p", "TCP"])
78 .output()
79 .map_err(|source| SupervisionError::Tool {
80 tool: "netstat",
81 source,
82 })?;
83 if !output.status.success() {
84 return Err(SupervisionError::ToolFailed {
85 tool: "netstat",
86 status: output.status,
87 });
88 }
89 Ok(parse_netstat_listeners(
90 &String::from_utf8_lossy(&output.stdout),
91 port,
92 ))
93}
94
95#[cfg(not(any(unix, windows)))]
96pub(super) fn listening_pids(_port: u16) -> Result<Vec<u32>, SupervisionError> {
97 Err(SupervisionError::Unsupported)
98}