term_session/
auto_spawn.rs1use std::io;
2use std::process::{Child, Command, Stdio};
3use std::thread;
4use std::time::{Duration, Instant};
5
6use term_session_muxio_service_definitions::{ChannelName, probe_ipc_endpoint};
7
8#[derive(Clone, Debug)]
10pub struct ServerSpawnConfig<'a> {
11 pub channel: &'a ChannelName,
12 pub cmd: &'a [String],
13}
14
15fn spawn_detached_server(cfg: &ServerSpawnConfig<'_>) -> io::Result<Child> {
16 let bin = std::env::current_exe()?;
17 let mut cmd = Command::new(bin);
18 cmd.arg("--server")
19 .arg("--channel")
20 .arg(cfg.channel.to_string());
21 if !cfg.cmd.is_empty() {
22 cmd.arg("--").args(cfg.cmd);
23 }
24 cmd.stdin(Stdio::null())
29 .stdout(Stdio::null())
30 .stderr(Stdio::null());
31 #[cfg(unix)]
32 {
33 use std::os::unix::process::CommandExt;
34 unsafe {
41 cmd.pre_exec(|| {
42 if libc::setsid() == -1 {
43 return Err(std::io::Error::last_os_error());
44 }
45 Ok(())
46 });
47 }
48 }
49 #[cfg(windows)]
50 {
51 use std::os::windows::process::CommandExt;
52 cmd.creation_flags(0x08000000);
53 }
54 cmd.spawn()
55}
56
57pub fn connect_or_spawn_server(
64 channel: &ChannelName,
65 cfg: &ServerSpawnConfig<'_>,
66) -> io::Result<String> {
67 let socket_name = channel.to_string();
68
69 if probe_ipc_endpoint(channel) {
70 return Ok(socket_name);
71 }
72
73 let mut child = spawn_detached_server(cfg)?;
74 let start = Instant::now();
75 let timeout = Duration::from_secs(3);
76 let poll_interval = Duration::from_millis(50);
77
78 while start.elapsed() < timeout {
79 if probe_ipc_endpoint(channel) {
80 return Ok(socket_name);
81 }
82 if let Ok(Some(status)) = child.try_wait() {
83 if probe_ipc_endpoint(channel) {
86 return Ok(socket_name);
87 }
88 return Err(io::Error::new(
89 io::ErrorKind::ConnectionRefused,
90 format!("Session server exited during startup with status: {status}"),
91 ));
92 }
93 thread::sleep(poll_interval);
94 }
95
96 Err(io::Error::new(
97 io::ErrorKind::TimedOut,
98 format!("Timed out waiting for server on channel '{channel}'"),
99 ))
100}