-
Notifications
You must be signed in to change notification settings - Fork 2
Add optional systemd unit for always-on recording #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # systemd unit (optional) | ||
|
|
||
| Opt-in way to keep `ltm` recording across reboots instead of running | ||
| `sudo ltm start` by hand. `ltm start`/`ltm stop` remain the default, portable | ||
| path (see [docs/recording.md](../../docs/recording.md)); nothing here is | ||
| required to use ltm. | ||
|
|
||
| ```bash | ||
| go build -o bin/ltm ./cmd/ltm | ||
| sudo install -m 0755 bin/ltm /usr/bin/ltm | ||
| sudo install -m 0644 contrib/systemd/ltm.service /etc/systemd/system/ltm.service | ||
| sudo systemctl daemon-reload | ||
| sudo systemctl enable --now ltm | ||
| ``` | ||
|
|
||
| See [docs/recording.md](../../docs/recording.md#systemd-optional) for | ||
| querying, uninstalling, and the unprivileged-user variant. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| [Unit] | ||
| Description=ltm always-on activity recorder (eBPF) | ||
| Documentation=https://github.com/Agent-Hellboy/ltm/blob/main/docs/recording.md | ||
| # Recording is Linux/x86_64 only; see docs/recording.md. | ||
| After=network.target | ||
|
|
||
| [Service] | ||
| Type=simple | ||
| ExecStart=/usr/bin/ltm daemon --foreground --db /var/lib/ltm/ltm.db --pidfile /run/ltm/ltm.pid | ||
| Restart=on-failure | ||
| RestartSec=5s | ||
|
|
||
| # Owns /var/lib/ltm (db) and /run/ltm (pidfile); created/removed by systemd | ||
| # around the service's lifetime. | ||
| StateDirectory=ltm | ||
| RuntimeDirectory=ltm | ||
|
Comment on lines
+9
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Remove unused pidfile configuration and correct The systemd configuration manages a pidfile and
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
|
|
||
| # Recording needs to attach BPF tracepoints and read kernel perf event info: | ||
| # root, or CAP_BPF + CAP_PERFMON + CAP_DAC_READ_SEARCH (see docs/security.md). | ||
| # Root is the simplest default and what `sudo ltm start` already assumes; | ||
| # switch to the capability form below to run unprivileged. | ||
| User=root | ||
|
|
||
| # --- Unprivileged alternative (comment out User=root above, uncomment these) --- | ||
| # CAP_DAC_READ_SEARCH is required too: the collector reads tracepoint ids from | ||
| # /sys/kernel/tracing/events/**/id, which are mode 440 root:root, and neither | ||
| # CAP_BPF nor CAP_PERFMON grants DAC bypass. Without it every tracepoint open | ||
| # fails with "permission denied" and the daemon exits ("no tracepoints could | ||
| # be attached"). Verified on a 6.8 kernel. | ||
| # User=ltm | ||
| # CapabilityBoundingSet=CAP_BPF CAP_PERFMON CAP_DAC_READ_SEARCH | ||
| # AmbientCapabilities=CAP_BPF CAP_PERFMON CAP_DAC_READ_SEARCH | ||
| # NoNewPrivileges=yes | ||
|
|
||
| [Install] | ||
| WantedBy=multi-user.target | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -92,3 +92,59 @@ sudo ltm stop # join producer, close ingest, final flush, then exit | |
| On shutdown the service joins the collector before closing `ingest`, then waits for | ||
| the flush loop to finish before the store is closed; otherwise the last batch can be | ||
| lost. That path is covered by daemon tests. | ||
|
|
||
| ## systemd (optional) | ||
|
|
||
| `ltm start` is the default, portable way to run the recorder. Machines that | ||
| want it always on across reboots can instead run `daemon --foreground` | ||
| directly under systemd, using the unit in | ||
| [`contrib/systemd/ltm.service`](../contrib/systemd/ltm.service). This is | ||
| opt-in and Linux-only (recording already requires Linux); it doesn't replace | ||
| `ltm start`/`ltm stop`. | ||
|
|
||
| ```bash | ||
| go build -o bin/ltm ./cmd/ltm | ||
| sudo install -m 0755 bin/ltm /usr/bin/ltm | ||
| sudo install -m 0644 contrib/systemd/ltm.service /etc/systemd/system/ltm.service | ||
| sudo systemctl daemon-reload | ||
| sudo systemctl enable --now ltm | ||
| ``` | ||
|
|
||
| ```bash | ||
| systemctl status ltm # unit + process state, journal tail | ||
| sudo journalctl -u ltm -f # follow recorder logs | ||
| ltm --db /var/lib/ltm/ltm.db status # event counts, dropped, last event | ||
| ltm --db /var/lib/ltm/ltm.db timeline --since 10m | ||
|
|
||
| sudo systemctl stop ltm # graceful: same shutdown path as `ltm stop` | ||
| sudo systemctl disable ltm | ||
| ``` | ||
|
|
||
| Notes: | ||
|
|
||
| - The unit runs `daemon --foreground` as `Type=simple`, which is what it's | ||
| designed for: no forking, no re-exec, systemd tracks the process directly | ||
| and restarts it on failure. | ||
| - Needs root, or `CAP_BPF` + `CAP_PERFMON` **and** `CAP_DAC_READ_SEARCH` — the | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This new note correctly says non-root recording needs Useful? React with 👍 / 👎. |
||
| collector also reads tracepoint ids from | ||
| `/sys/kernel/tracing/events/**/id`, which are mode `440 root:root`, so the | ||
| two BPF caps alone aren't enough for a non-root user (every tracepoint open | ||
| fails with "permission denied" and the daemon exits). The unit defaults to | ||
| `User=root` (matching `sudo ltm start`) with a commented-out unprivileged | ||
| variant carrying all three capabilities, verified on a 6.8 kernel. See | ||
| [security.md](security.md). | ||
|
Comment on lines
+128
to
+135
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Sync capability requirements. The addition of Please update the requirements list at the top of the file as well to ensure consistency and prevent users from encountering permission-denied errors when running 🤖 Prompt for AI Agents |
||
| - The `daemon` subcommand (unlike `start`) doesn't write a pidfile, so | ||
| `ltm status`'s `alive` field won't reflect a systemd-managed process — use | ||
| `systemctl status ltm` for liveness instead. Event counts/timeline/etc. via | ||
| `ltm` still work against the same `--db` path either way. | ||
| - `StateDirectory=ltm` / `RuntimeDirectory=ltm` put the db under | ||
| `/var/lib/ltm/` and the (unused) pidfile path under `/run/ltm/`; systemd | ||
| creates/owns both. | ||
| - **Don't point `ltm start` (or a second instance) at the same `--db` as an | ||
| active systemd-managed recorder.** Two writers on one SQLite db contend | ||
| until a write blows past an internal deadline, both processes exit with | ||
| `context deadline exceeded`, and `Restart=on-failure` crash-loops the | ||
| service until only one recorder remains. No data corruption results | ||
| (`PRAGMA integrity_check` stays `ok`), but it's a real outage of recording | ||
| in the meantime — use a distinct `--db` for anything ad hoc, or stop the | ||
| unit first. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The repo's CLI hard rule says global flags must precede the subcommand, and
daemonArgsforwards them as--db ... --pidfile ... daemon --foreground. This unit puts--db/--pidfileafterdaemon --foreground; those two work only becauserunDaemonduplicates them as local flags, but the checked-in unit now teaches an ordering that breaks for global-only flags such as--ignore-pathif users customize the service in the same position. Please use/usr/bin/ltm --db ... --pidfile ... daemon --foregroundhere.Useful? React with 👍 / 👎.