@@ -3,38 +3,93 @@ use std::time::Duration;
33
44use anyhow:: { Result , bail} ;
55
6- pub fn socket_path ( session_id : & str ) -> PathBuf {
7- crate :: config:: config_dir ( ) . join ( format ! ( "daemon-{session_id}.sock" ) )
6+ use super :: protocol:: DaemonCreateParams ;
7+
8+ /// Scan the config directory for `daemon-*.sock` files and return the session names.
9+ pub fn list_daemon_names ( ) -> Vec < String > {
10+ let dir = crate :: config:: config_dir ( ) ;
11+ let Ok ( entries) = std:: fs:: read_dir ( & dir) else {
12+ return vec ! [ ] ;
13+ } ;
14+ let mut names = Vec :: new ( ) ;
15+ for entry in entries. flatten ( ) {
16+ let name = entry. file_name ( ) ;
17+ let name = name. to_string_lossy ( ) ;
18+ if let Some ( rest) = name. strip_prefix ( "daemon-" ) {
19+ if let Some ( session_name) = rest. strip_suffix ( ".sock" ) {
20+ if !session_name. is_empty ( ) {
21+ names. push ( session_name. to_string ( ) ) ;
22+ }
23+ }
24+ }
25+ }
26+ names
27+ }
28+
29+ pub fn socket_path ( session_name : & str ) -> PathBuf {
30+ crate :: config:: config_dir ( ) . join ( format ! ( "daemon-{session_name}.sock" ) )
31+ }
32+
33+ pub fn pid_path ( session_name : & str ) -> PathBuf {
34+ crate :: config:: config_dir ( ) . join ( format ! ( "daemon-{session_name}.pid" ) )
835}
936
10- pub fn pid_path ( session_id : & str ) -> PathBuf {
11- crate :: config:: config_dir ( ) . join ( format ! ( "daemon-{session_id}.pid " ) )
37+ fn log_path ( session_name : & str ) -> PathBuf {
38+ crate :: config:: config_dir ( ) . join ( format ! ( "daemon-{session_name}.log " ) )
1239}
1340
14- fn log_path ( session_id : & str ) -> PathBuf {
15- crate :: config:: config_dir ( ) . join ( format ! ( "daemon-{session_id}.log" ) )
41+ pub fn cleanup_stale ( session_name : & str ) {
42+ let _ = std:: fs:: remove_file ( socket_path ( session_name) ) ;
43+ let _ = std:: fs:: remove_file ( pid_path ( session_name) ) ;
1644}
1745
18- pub fn cleanup_stale ( session_id : & str ) {
19- let _ = std:: fs:: remove_file ( socket_path ( session_id) ) ;
20- let _ = std:: fs:: remove_file ( pid_path ( session_id) ) ;
46+ /// Check if the daemon process for this session is still alive using its PID file.
47+ /// Returns `false` if the PID file is missing, unreadable, or the process is not running.
48+ pub fn is_daemon_alive ( session_name : & str ) -> bool {
49+ let pid_file = pid_path ( session_name) ;
50+ let Ok ( contents) = std:: fs:: read_to_string ( & pid_file) else {
51+ return false ;
52+ } ;
53+ let Ok ( pid) = contents. trim ( ) . parse :: < i32 > ( ) else {
54+ return false ;
55+ } ;
56+ // kill -0 checks process existence without sending a signal
57+ std:: process:: Command :: new ( "kill" )
58+ . args ( [ "-0" , & pid. to_string ( ) ] )
59+ . stdout ( std:: process:: Stdio :: null ( ) )
60+ . stderr ( std:: process:: Stdio :: null ( ) )
61+ . status ( )
62+ . map ( |s| s. success ( ) )
63+ . unwrap_or ( false )
2164}
2265
23- /// Spawn a daemon process for the given session. The CDP URL is passed via
66+ /// If a daemon's socket file exists but its process is dead, clean up stale files.
67+ /// Returns `true` if stale files were removed.
68+ pub fn cleanup_if_dead ( session_name : & str ) -> bool {
69+ if socket_path ( session_name) . exists ( ) && !is_daemon_alive ( session_name) {
70+ cleanup_stale ( session_name) ;
71+ return true ;
72+ }
73+ false
74+ }
75+
76+ /// Spawn a daemon process for the given session. The create params are passed via
2477/// environment variable to avoid leaking API keys in the process list.
25- pub fn spawn_daemon ( session_id : & str , cdp_url : & str ) -> Result < ( ) > {
78+ pub fn spawn_daemon ( session_name : & str , params : & DaemonCreateParams ) -> Result < ( ) > {
2679 let exe = std:: env:: current_exe ( ) ?;
2780
28- cleanup_stale ( session_id ) ;
81+ cleanup_stale ( session_name ) ;
2982
3083 let config_dir = crate :: config:: config_dir ( ) ;
3184 std:: fs:: create_dir_all ( & config_dir) ?;
3285
33- let log = std:: fs:: File :: create ( log_path ( session_id) ) ?;
86+ let log = std:: fs:: File :: create ( log_path ( session_name) ) ?;
87+
88+ let params_json = serde_json:: to_string ( params) ?;
3489
3590 std:: process:: Command :: new ( exe)
36- . args ( [ "__daemon" , "--session-id " , session_id ] )
37- . env ( "STEEL_DAEMON_CDP_URL " , cdp_url )
91+ . args ( [ "__daemon" , "--session-name " , session_name ] )
92+ . env ( "STEEL_DAEMON_PARAMS " , params_json )
3893 . stdin ( std:: process:: Stdio :: null ( ) )
3994 . stdout ( log. try_clone ( ) ?)
4095 . stderr ( log)
@@ -44,8 +99,8 @@ pub fn spawn_daemon(session_id: &str, cdp_url: &str) -> Result<()> {
4499}
45100
46101/// Wait until the daemon socket is connectable.
47- pub async fn wait_for_daemon ( session_id : & str , timeout : Duration ) -> Result < ( ) > {
48- let sock = socket_path ( session_id ) ;
102+ pub async fn wait_for_daemon ( session_name : & str , timeout : Duration ) -> Result < ( ) > {
103+ let sock = socket_path ( session_name ) ;
49104 let start = std:: time:: Instant :: now ( ) ;
50105
51106 while start. elapsed ( ) < timeout {
@@ -62,23 +117,23 @@ pub async fn wait_for_daemon(session_id: &str, timeout: Duration) -> Result<()>
62117}
63118
64119/// Send a shutdown command to a running daemon and clean up files.
65- pub async fn stop_daemon ( session_id : & str ) -> Result < ( ) > {
120+ pub async fn stop_daemon ( session_name : & str ) -> Result < ( ) > {
66121 use super :: client:: DaemonClient ;
67122 use super :: protocol:: DaemonCommand ;
68123
69- if let Ok ( mut client) = DaemonClient :: connect ( session_id ) . await {
124+ if let Ok ( mut client) = DaemonClient :: connect ( session_name ) . await {
70125 let _ = client. send ( DaemonCommand :: Shutdown ) . await ;
71126 }
72127
73128 tokio:: time:: sleep ( Duration :: from_millis ( 200 ) ) . await ;
74- cleanup_stale ( session_id ) ;
129+ cleanup_stale ( session_name ) ;
75130
76131 Ok ( ( ) )
77132}
78133
79134/// Kill a daemon process by reading its PID file, then clean up.
80- pub fn kill_daemon ( session_id : & str ) -> Result < ( ) > {
81- let pid_file = pid_path ( session_id ) ;
135+ pub fn kill_daemon ( session_name : & str ) -> Result < ( ) > {
136+ let pid_file = pid_path ( session_name ) ;
82137 if let Ok ( contents) = std:: fs:: read_to_string ( & pid_file)
83138 && let Ok ( pid) = contents. trim ( ) . parse :: < u32 > ( )
84139 {
@@ -89,6 +144,6 @@ pub fn kill_daemon(session_id: &str) -> Result<()> {
89144 . stderr ( std:: process:: Stdio :: null ( ) )
90145 . status ( ) ;
91146 }
92- cleanup_stale ( session_id ) ;
147+ cleanup_stale ( session_name ) ;
93148 Ok ( ( ) )
94149}
0 commit comments