Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Install SIGTERM handler to kill typesense-server

Store spawned typesense-server PID in an AtomicI32 and install a
Unix SIGTERM/SIGINT handler that kills the child and calls _exit.
Install the handler before spawning so the PID is available. Replace
blocking wait with a poll loop (try_wait) so the signal handler can
run and terminate the child.

+44 -8
+44 -8
crates/cli/src/lib.rs
··· 6 6 use rockbox_typesense::client::*; 7 7 use rockbox_typesense::types::*; 8 8 use std::process::Stdio; 9 + use std::sync::atomic::{AtomicI32, Ordering}; 9 10 use std::thread::sleep; 10 11 use std::time::Duration; 11 12 use std::{env, ffi::CStr}; 12 13 use std::{fs, thread}; 14 + 15 + /// PID of the spawned typesense-server child, or -1 if not yet started. 16 + static TYPESENSE_PID: AtomicI32 = AtomicI32::new(-1); 17 + 18 + /// SIGTERM/SIGINT handler: kill the typesense child then _exit immediately. 19 + /// 20 + /// system-hosted.c installs a SIGTERM handler that calls system_exception_wait() 21 + /// which loops forever waiting for an SDL quit event that never arrives on a 22 + /// headless daemon. We override it here with a handler that actually exits. 23 + /// _exit is used because it is async-signal-safe (exit() is not). 24 + #[cfg(unix)] 25 + extern "C" fn handle_shutdown(_sig: libc::c_int) { 26 + let pid = TYPESENSE_PID.load(Ordering::SeqCst); 27 + if pid > 0 { 28 + unsafe { libc::kill(pid, libc::SIGTERM) }; 29 + } 30 + unsafe { libc::_exit(0) }; 31 + } 13 32 14 33 #[no_mangle] 15 34 pub extern "C" fn parse_args(argc: usize, argv: *const *const u8) -> i32 { ··· 44 63 let cli = Command::new("rockboxd").version(VERSION).about(&banner); 45 64 46 65 cli.get_matches_from(args); 66 + 67 + // Install shutdown handler before spawning typesense-server so the PID is 68 + // always available when the handler fires. 69 + #[cfg(unix)] 70 + unsafe { 71 + libc::signal(libc::SIGTERM, handle_shutdown as libc::sighandler_t); 72 + libc::signal(libc::SIGINT, handle_shutdown as libc::sighandler_t); 73 + } 47 74 48 75 thread::spawn(move || { 49 76 let home = env::var("HOME").unwrap(); ··· 161 188 .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?; 162 189 let data_dir = homedir.join(".config/rockbox.org/typesense"); 163 190 164 - let path = format!( 165 - "{}:{}/{}", 166 - std::env::var("PATH").unwrap_or_default(), 167 - homedir.display(), 168 - ".rockbox/bin" 169 - ); 170 - 171 191 let ts_bin = { 172 192 let local = homedir.join(".rockbox/bin/typesense-server"); 173 193 if local.exists() { ··· 200 220 }); 201 221 } 202 222 203 - cmd.status()?; 223 + let mut child = cmd.spawn()?; 224 + TYPESENSE_PID.store(child.id() as i32, Ordering::SeqCst); 225 + 226 + // Poll instead of blocking in waitpid so SIGTERM can reach the process. 227 + loop { 228 + match child.try_wait() { 229 + Ok(Some(status)) => { 230 + eprintln!("typesense-server exited: {status}"); 231 + break; 232 + } 233 + Ok(None) => sleep(Duration::from_millis(500)), 234 + Err(e) => { 235 + eprintln!("typesense-server monitor error: {e}"); 236 + break; 237 + } 238 + } 239 + } 204 240 205 241 Ok::<(), Error>(()) 206 242 });