# (System[d)aemon]

Building blocks for composing daemonized async applications.

## About

A daemon is an application running in the background, managed by the underlying OS. It requires
tight integration with the operating system to:

- Set up logging into the system log
- React to graceful shutdown signals
- React to reload signals
- Read and store a configuration file
- Notify the OS about the daemon's lifecycle state
- Trigger a watchdog at regular intervals

`systemdaemon` handles these tasks through concurrently running, composable building blocks.

Implement the `Service` trait with your application logic, choose a restart and reload policy,
and wire everything together with the provided handler functions.

The crate integrates with systemd out of the box. All system-facing behavior is abstracted away via
traits, making it straightforward to add custom integrations for other init systems.
Conditional compilation is heavily used to keep dependency trees as small as possible.

No features are enabled by default users need to enable only what they actually need.

## Features

| Feature    | Description                                                                      |
|------------|----------------------------------------------------------------------------------|
| `systemd`  | systemd integration                                                              |
| `toml`     | TOML configuration file support                                                  |
| `yaml`     | YAML configuration file support                                                  |
| `json`     | JSON configuration file support                                                  |
| `agnostic` | Runtime-agnostic async implementation (smol, async-std, …)                       |
| `tokio`    | Tokio async implementation. Takes precedence over `agnostic` if both are enabled |

## Minimal example

```toml
# Cargo.toml
[dependencies]
systemdaemon = { version = "1", features = ["systemd", "toml", "tokio"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
serde = { version = "1", features = ["derive"] }
tracing = "0.1"
```

```rust
#[derive(Clone, Default, serde::Serialize, serde::Deserialize, Debug)]
struct Config {
    value: String,
}

struct MyService;

impl systemdaemon::Service for MyService {
    type Config = Config;
    type Error = std::convert::Infallible;

    async fn run(&self, config: Self::Config) -> Result<(), Self::Error> {
        tracing::info!("Running with value: {}", config.value);
        std::future::pending().await
    }
}

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let daemon = systemdaemon::Daemon::default();
    daemon.setup_logger(tracing::Level::INFO);

    let config_path = std::path::Path::new("/tmp/config.toml");
    let config = systemdaemon::ServiceConfig::try_load_toml_or_default(config_path);

    let service = MyService;
    tokio::join!(
        daemon.service_handler(&service, &config, systemdaemon::RestartPolicy::Always),
        daemon.reload_handler(&config, systemdaemon::ReloadPolicy::KeepOnError),
        daemon.watchdog_handler(),
        daemon.shutdown_handler(),
    );
}
```

## Examples

Additional examples showcasing library usage alongside systemd unit deployment are located under
`examples/`. See each example's `README.md` for details.

## On watchdogs

Use the watchdog handler carefully. A watchdog can only work properly if its execution can be delayed by other tasks.
If you use a watchdog ensure that the daemon uses a single threaded async runtime or a custom watchdog handler.

Otherwise a watchdog might trigger although parts of the daemon are blocked defeating the purpose of using a watchdog in the first place.

## Author's notes

If you want this crate to support additional init systems or operating systems, feel free to build
the integration and send a PR. I can only guarantee that tests run on the systems I have access to,
so **merging such a PR requires the contributor to act as maintainer for the contributed integration.**

If you like what I'm doing, follow me on [Codeberg](https://codeberg.org/black-cat-engineering) or
[Mastodon](https://chaos.social/@GrandmasterBash). If you ever see me in real life don't hesitate to approach me.

## LLM disclaimer

LLM usage is a controversial topic in the open source scene, for many good reasons. Neither the implementation nor the tests were touched by an LLM.
I am convinced that authors of public code have the responsibility to understanding every line of their codebase. Writing it by hand is the most
reliable way to ensure that I live up to that standard.

Documentation, however, was streamlined using Claude Sonnet 4.6 via GitHub Copilot. I have a
mild form of dyslexia and LLMs are quite handy for improving and correcting plain text.

## MSRV

Stable Rust **1.88** and later.

## License

[MPL-2.0](LICENSE)
