gardesk/gar / 066e8f2

Browse files

garbar spawn: add health check and stale process cleanup

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
066e8f24c26d6e14ebe5381ecd4c4128f3f02f88
Parents
84ab76b
Tree
0614926

1 changed file

StatusFile+-
M gar/src/x11/events.rs 69 11
gar/src/x11/events.rsmodified
@@ -72,11 +72,45 @@ fn stop_graphical_session() {
7272
     }
7373
 }
7474
 
75
-/// Spawn garbar as a child process.
76
-/// Returns the child process handle if successful.
75
+/// Get garbar socket path
76
+fn garbar_socket_path() -> String {
77
+    std::env::var("XDG_RUNTIME_DIR")
78
+        .map(|dir| format!("{}/garbar.sock", dir))
79
+        .unwrap_or_else(|_| "/tmp/garbar.sock".to_string())
80
+}
81
+
82
+/// Check if garbar is healthy (socket exists)
83
+fn is_garbar_healthy() -> bool {
84
+    std::path::Path::new(&garbar_socket_path()).exists()
85
+}
86
+
87
+/// Kill any stale garbar process that isn't responding
88
+fn cleanup_stale_garbar() {
89
+    let pid_path = std::env::var("XDG_RUNTIME_DIR")
90
+        .map(|dir| format!("{}/garbar.pid", dir))
91
+        .unwrap_or_else(|_| "/tmp/garbar.pid".to_string());
92
+
93
+    if let Ok(pid_str) = std::fs::read_to_string(&pid_path) {
94
+        if let Ok(pid) = pid_str.trim().parse::<i32>() {
95
+            // Check if process exists but socket doesn't (stale process)
96
+            let proc_path = format!("/proc/{}", pid);
97
+            if std::path::Path::new(&proc_path).exists() && !is_garbar_healthy() {
98
+                tracing::warn!("Killing stale garbar process (PID {})", pid);
99
+                unsafe {
100
+                    libc::kill(pid, libc::SIGKILL);
101
+                }
102
+                std::thread::sleep(std::time::Duration::from_millis(100));
103
+            }
104
+        }
105
+    }
106
+}
107
+
77108
 fn spawn_garbar() -> Option<std::process::Child> {
78109
     tracing::info!("Spawning garbar...");
79110
 
111
+    // Clean up any stale garbar process first
112
+    cleanup_stale_garbar();
113
+
80114
     // Try to find garbar in PATH or common locations
81115
     let garbar_cmd = which_garbar().unwrap_or_else(|| "garbar".to_string());
82116
 
@@ -85,13 +119,29 @@ fn spawn_garbar() -> Option<std::process::Child> {
85119
         .map(|dir| format!("{}/gar-i3.sock", dir))
86120
         .unwrap_or_else(|_| "/tmp/gar-i3.sock".to_string());
87121
 
122
+    // Inherit DISPLAY from current environment, fallback to :0
123
+    let x_display = std::env::var("DISPLAY").unwrap_or_else(|_| ":0".to_string());
124
+
88125
     match Command::new(&garbar_cmd)
89126
         .arg("daemon")
90127
         .env("I3SOCK", &i3sock)
128
+        .env("DISPLAY", &x_display)
91129
         .spawn()
92130
     {
93131
         Ok(child) => {
94
-            tracing::info!("garbar started (PID {}), I3SOCK={}", child.id(), i3sock);
132
+            tracing::info!("garbar started (PID {}), DISPLAY={}, I3SOCK={}", child.id(), x_display, i3sock);
133
+
134
+            // Wait briefly and verify garbar becomes healthy
135
+            for attempt in 1..=10 {
136
+                std::thread::sleep(std::time::Duration::from_millis(200));
137
+                if is_garbar_healthy() {
138
+                    tracing::info!("garbar socket ready after {}ms", attempt * 200);
139
+                    return Some(child);
140
+                }
141
+            }
142
+
143
+            // Socket never appeared - garbar might be stuck
144
+            tracing::warn!("garbar socket not ready after 2s, process may be stuck");
95145
             Some(child)
96146
         }
97147
         Err(e) => {
@@ -2148,28 +2198,36 @@ impl WindowManager {
21482198
 
21492199
         // Handle garbar lifecycle based on new config
21502200
         if self.config.bar_enabled {
2151
-            // Check if garbar is still running
2152
-            let garbar_alive = if let Some(ref mut child) = self.garbar_process {
2201
+            // Check if garbar is still running AND healthy (socket exists)
2202
+            let (garbar_alive, garbar_healthy) = if let Some(ref mut child) = self.garbar_process {
21532203
                 match child.try_wait() {
2154
-                    Ok(None) => true,  // Still running
2204
+                    Ok(None) => (true, is_garbar_healthy()),  // Process running, check socket
21552205
                     Ok(Some(status)) => {
21562206
                         tracing::info!("garbar exited with status {}, will respawn", status);
2157
-                        false
2207
+                        (false, false)
21582208
                     }
21592209
                     Err(e) => {
21602210
                         tracing::warn!("Failed to check garbar status: {}", e);
2161
-                        false
2211
+                        (false, false)
21622212
                     }
21632213
                 }
21642214
             } else {
2165
-                false
2215
+                (false, false)
21662216
             };
21672217
 
2168
-            if garbar_alive {
2169
-                // garbar running, signal it to reload
2218
+            if garbar_alive && garbar_healthy {
2219
+                // garbar running and healthy, signal it to reload
21702220
                 if let Some(ref child) = self.garbar_process {
21712221
                     reload_garbar(child);
21722222
                 }
2223
+            } else if garbar_alive && !garbar_healthy {
2224
+                // garbar process exists but socket doesn't - it's stuck
2225
+                tracing::warn!("garbar process alive but socket missing, restarting...");
2226
+                if let Some(ref mut child) = self.garbar_process {
2227
+                    let _ = child.kill();
2228
+                    let _ = child.wait();
2229
+                }
2230
+                self.garbar_process = spawn_garbar();
21732231
             } else {
21742232
                 // garbar not running, spawn it
21752233
                 self.garbar_process = spawn_garbar();