@@ -139,15 +139,17 @@ X-XDP-Autostart=com.github.AppManager
139139 */
140140 public static void kill_daemon () {
141141 try {
142- // Use pkill with SIGKILL (-9) to ensure the daemon is terminated
142+ // Send SIGTERM (pkill's default) so the daemon runs its shutdown
143+ // handler and exits cleanly, letting the AppImage runtime unmount
144+ // its dwarfs mount. SIGKILL (-9) would orphan that mount in /tmp.
143145 // Match just "--background-update" to avoid issues with path variations
144146 // Use "--" to indicate end of options since pattern starts with "-"
145- string [] argv = { " pkill" , " -9 " , " - f" , " --" , " --background-update" };
147+ string [] argv = { " pkill" , " -f" , " --" , " --background-update" };
146148 int exit_status;
147149 Process . spawn_sync(null , argv, null , GLib . SpawnFlags . SEARCH_PATH , null , null , null , out exit_status);
148- debug(" Killed background daemon (exit status: %d )" , exit_status);
150+ debug(" Stopped background daemon (exit status: %d )" , exit_status);
149151 } catch (SpawnError e) {
150- warning(" Failed to kill background daemon: %s " , e. message);
152+ warning(" Failed to stop background daemon: %s " , e. message);
151153 }
152154 }
153155
@@ -172,7 +174,17 @@ X-XDP-Autostart=com.github.AppManager
172174 Thread . usleep(100000 ); // 100ms
173175 }
174176
175- warning(" Background daemon did not terminate within 5 seconds" );
177+ // Migration must not race a live daemon. If the graceful SIGTERM
178+ // didn't land in time (e.g. daemon blocked mid update-check), escalate
179+ // to SIGKILL. This can orphan the dwarfs mount, but a safe migration
180+ // outweighs a stale /tmp symlink that cleanup_stale() reaps next launch.
181+ warning(" Background daemon did not terminate within 5 seconds; sending SIGKILL" );
182+ try {
183+ string [] argv = { " pkill" , " -9" , " -f" , " --" , " --background-update" };
184+ Process . spawn_sync(null , argv, null , GLib . SpawnFlags . SEARCH_PATH , null , null , null , null );
185+ } catch (SpawnError e) {
186+ warning(" Failed to force-kill background daemon: %s " , e. message);
187+ }
176188 return true ;
177189 }
178190
@@ -490,6 +502,24 @@ X-XDP-Autostart=com.github.AppManager
490502 public void run_daemon () {
491503 log_debug(" background daemon: starting persistent service" );
492504
505+ var loop = new MainLoop ();
506+
507+ // Quit cleanly when the session manager / systemd asks us to stop.
508+ // GApplication installs no signal handlers and this daemon runs its
509+ // own MainLoop, so without this it would ignore SIGTERM on shutdown
510+ // and be SIGKILLed after the stop timeout (issue #114). Install the
511+ // handlers first so a stop request during the initial check is honored.
512+ Unix . signal_add(Posix . Signal . TERM , () = > {
513+ log_debug(" background daemon: SIGTERM received, stopping" );
514+ loop. quit();
515+ return Source . REMOVE ;
516+ });
517+ Unix . signal_add(Posix . Signal . INT , () = > {
518+ log_debug(" background daemon: SIGINT received, stopping" );
519+ loop. quit();
520+ return Source . REMOVE ;
521+ });
522+
493523 // On login, re-notify about staged updates (notify-only mode only)
494524 check_staged_updates_on_login();
495525
@@ -503,7 +533,7 @@ X-XDP-Autostart=com.github.AppManager
503533
504534 // Check periodically whether we should perform an update check
505535 // This allows the daemon to respect interval changes without restart
506- Timeout . add_seconds(DAEMON_CHECK_INTERVAL , () = > {
536+ var check_source_id = Timeout . add_seconds(DAEMON_CHECK_INTERVAL , () = > {
507537 if (! settings. get_boolean(" auto-check-updates" )) {
508538 log_debug(" background daemon: auto-check disabled, skipping" );
509539 return Source . CONTINUE ;
@@ -517,9 +547,17 @@ X-XDP-Autostart=com.github.AppManager
517547 return Source . CONTINUE ;
518548 });
519549
520- // Run the main loop - this blocks until the session ends
521- var loop = new MainLoop ();
550+ // Run the main loop - this blocks until a stop signal quits it
522551 loop. run();
552+
553+ // Clean shutdown: drop the periodic check and any D-Bus subscription
554+ // so we exit promptly instead of leaking sources and getting killed.
555+ Source . remove(check_source_id);
556+ if (dbus_connection != null && action_signal_id != 0 ) {
557+ dbus_connection. signal_unsubscribe(action_signal_id);
558+ action_signal_id = 0 ;
559+ }
560+ log_debug(" background daemon: stopped" );
523561 }
524562
525563 private void log_debug (string message ) {
0 commit comments