Fix daemon connection and service installation error handling

- Clear subscriptions map when connection pool resets to prevent stale subscription IDs after daemon restart
- Revert checkbox state when install/uninstall operations fail to keep UI in sync with actual state
- Return errors when launchctl/systemctl commands fail during service installation instead of silently continuing
- Add proper error checking for all systemctl commands on Linux
This commit is contained in:
Jamie Pine 2025-12-04 18:25:45 -08:00
parent 59d6f0d47e
commit 73b057e506
3 changed files with 35 additions and 7 deletions

View File

@ -111,6 +111,7 @@ impl DaemonConnectionPool {
let mut initialized = self.initialized.lock().await;
*initialized = false;
*self.writer.lock().await = None;
self.subscriptions.write().await.clear();
tracing::info!("Connection pool reset");
}
@ -928,7 +929,9 @@ async fn install_daemon_service(
tracing::info!("launchctl load output: {:?}", String::from_utf8_lossy(&output.stdout));
if !output.status.success() {
tracing::error!("launchctl load failed: {:?}", String::from_utf8_lossy(&output.stderr));
let stderr = String::from_utf8_lossy(&output.stderr);
tracing::error!("launchctl load failed: {:?}", stderr);
return Err(format!("Failed to load daemon service: {}", stderr));
}
// Update daemon state - we no longer own the process
@ -1008,21 +1011,36 @@ WantedBy=default.target
.map_err(|e| format!("Failed to write service file: {}", e))?;
// Enable and start the service
std::process::Command::new("systemctl")
let output = std::process::Command::new("systemctl")
.args(&["--user", "daemon-reload"])
.output()
.map_err(|e| format!("Failed to reload systemd: {}", e))?;
std::process::Command::new("systemctl")
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("Failed to reload systemd: {}", stderr));
}
let output = std::process::Command::new("systemctl")
.args(&["--user", "enable", "spacedrive-daemon.service"])
.output()
.map_err(|e| format!("Failed to enable service: {}", e))?;
std::process::Command::new("systemctl")
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("Failed to enable service: {}", stderr));
}
let output = std::process::Command::new("systemctl")
.args(&["--user", "start", "spacedrive-daemon.service"])
.output()
.map_err(|e| format!("Failed to start service: {}", e))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("Failed to start daemon service: {}", stderr));
}
// Update daemon state - we no longer own the process
let mut state = daemon_state.write().await;
state.started_by_us = false;

View File

@ -138,9 +138,17 @@ export function DaemonDisconnectedOverlay({
setInstallAsService(shouldInstall);
if (shouldInstall) {
await installAndStartDaemon();
const success = await installAndStartDaemon();
if (!success) {
setInstallAsService(false);
}
} else {
await platform.uninstallDaemonService?.();
try {
await platform.uninstallDaemonService?.();
} catch (error) {
console.error('Failed to uninstall daemon service:', error);
setInstallAsService(true);
}
}
}}
className="size-4 cursor-pointer rounded border-app-line bg-app accent-accent"

View File

@ -152,7 +152,7 @@ export function useDaemonStatus() {
}
};
const installAndStartDaemon = async () => {
const installAndStartDaemon = async (): Promise<boolean> => {
console.log('[useDaemonStatus] installAndStartDaemon called');
try {
console.log('[useDaemonStatus] Calling platform.installDaemonService()');
@ -162,12 +162,14 @@ export function useDaemonStatus() {
...prev,
isInstalled: true,
}));
return true;
} catch (error) {
console.error('[useDaemonStatus] Failed to install daemon service:', error);
setStatus(prev => ({
...prev,
isChecking: false,
}));
return false;
}
};