Skip to content

feat: handle SIGTERM with graceful shutdown sequence - #298

Merged
Hakanbaban53 merged 3 commits into
Zarestia-Dev:masterfrom
nvandamme:feat/sigterm-graceful-shutdown
Sep 13, 2026
Merged

Hakanbaban53 merged 3 commits into
Zarestia-Dev:masterfrom
nvandamme:feat/sigterm-graceful-shutdown

Conversation

@nvandamme

Copy link
Copy Markdown
Contributor

Problem

On Unix, when systemd sends SIGTERM to RClone Manager at OS shutdown or logout,
the process was killed abruptly without running its cleanup. This left FUSE
remotes mounted and the logind power-inhibitor lock held until the kernel tore
everything down, risking inconsistent remote state.

Fix

Register a SIGTERM handler (Unix only) that runs the existing handle_shutdown()
sequence before exiting — releasing the power inhibitor, unmounting all remotes,
stopping jobs/serves, and shutting down the engine. Each step already carries its
own timeout, so shutdown never stalls.

This reuses the same code path as a user-initiated quit (the shutdown_app command),
so behavior is consistent across exit triggers.

Verification

  • cargo fmt --check: clean
  • cargo clippy --features desktop --no-default-features -- -D warnings: clean
  • Live test: SIGTERM → logs "SIGTERM received — running graceful shutdown sequence",
    unmounts kDrive, shuts down engine (PID), exits cleanly.

On Unix, register a SIGTERM handler that runs the existing
handle_shutdown() cleanup (release power inhibitor, unmount all remotes,
stop jobs/serves, shut down engine) before exiting. Previously SIGTERM
(from systemd at OS shutdown/logout) killed the app abruptly without
cleaning up FUSE mounts or releasing locks.
@Hakanbaban53

Copy link
Copy Markdown
Collaborator

Hi @nvandamme,

Thank you for opening this PR! Gracefully handling SIGTERM on Unix is a great addition, it ensures that desktop session logouts, systemctl --user stop, container stops (docker stop), and process kills (pkill) trigger clean FUSE unmounting and rclone engine shutdown rather than being abruptly terminated by the kernel.

To keep the codebase modular and aligned with the rest of the project, could you please address the following points:

1. Move the signal listener from lib.rs to src-tauri/src/core/event_listener.rs

In this repository, lib.rs::setup_app is reserved strictly for high-level plugin registration and initial bootstrap. All OS signals and background event listeners are centralized in src-tauri/src/core/event_listener.rs.

There is already an existing handle_ctrl_c(app) function in event_listener.rs (around line 47). The SIGTERM handling should live there alongside Ctrl+C (SIGINT).

2. Unify termination signals

Instead of running two separate tasks in different files for Ctrl+C and SIGTERM, we can unify them in event_listener.rs (e.g. renaming handle_ctrl_c to handle_termination_signals or handling both via tokio::select!).

Here is a recommended way to structure it in src-tauri/src/core/event_listener.rs:

fn handle_termination_signals(app: &AppHandle) {
    let app_clone = app.clone();
    tauri::async_runtime::spawn(async move {
        #[cfg(unix)]
        {
            use tokio::signal::unix::{signal, SignalKind};
            let mut sigterm = signal(SignalKind::terminate()).ok();
            tokio::select! {
                _ = tokio::signal::ctrl_c() => {
                    info!("Ctrl+C / SIGINT received, initiating shutdown");
                }
                _ = async {
                    if let Some(ref mut sig) = sigterm {
                        sig.recv().await;
                    } else {
                        std::future::pending::<()>().await;
                    }
                } => {
                    info!("SIGTERM received, initiating graceful shutdown");
                }
            }
        }

        #[cfg(not(unix))]
        {
            if let Err(e) = tokio::signal::ctrl_c().await {
                error!("Failed to install Ctrl+C handler: {e}");
                return;
            }
            info!("Ctrl+C received, initiating shutdown");
        }

        let _ = shutdown_app(app_clone.clone()).await;
        app_clone.exit(0);
    });
}

And in setup_event_listener(app: &AppHandle):

pub fn setup_event_listener(app: &AppHandle) {
    handle_termination_signals(app);
    ...

3. Verification

After updating, please verify that it compiles cleanly with zero warnings:

cargo fmt -- --check
cargo clippy --features desktop --no-default-features -- -D warnings

Once updated, we will be happy to merge this in!

@nvandamme

Copy link
Copy Markdown
Contributor Author

will do!

nvandamme added a commit to nvandamme/rclone-manager that referenced this pull request Sep 13, 2026
Per review feedback on PR Zarestia-Dev#298:
- Move the SIGTERM listener out of lib.rs into core/event_listener.rs,
  where OS signals and background listeners are centralized
- Merge it with handle_ctrl_c into handle_termination_signals using
  tokio::select! so both SIGINT and SIGTERM share one shutdown path
- Both call shutdown_app(), which already runs the full graceful
  sequence (inhibitor release, unmounts, jobs/serves stop) + exit(0)

Verified live: kill -TERM and kill -INT each trigger the unified
handler, run the cleanup sequence, and exit cleanly.
Per review feedback on PR Zarestia-Dev#298:
- Move the SIGTERM listener out of lib.rs into core/event_listener.rs,
  where OS signals and background listeners are centralized
- Merge it with handle_ctrl_c into handle_termination_signals using
  tokio::select! so both SIGINT and SIGTERM share one shutdown path
- Both call shutdown_app(), which already runs the full graceful
  sequence (inhibitor release, unmounts, jobs/serves stop) + exit(0)

Verified live: kill -TERM and kill -INT each trigger the unified
handler, run the cleanup sequence, and exit cleanly.
@nvandamme
nvandamme force-pushed the feat/sigterm-graceful-shutdown branch from 0f013fc to ad41030 Compare September 13, 2026 16:22
@Hakanbaban53
Hakanbaban53 merged commit c5f28f5 into Zarestia-Dev:master Sep 13, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants