diff --git a/CHANGELOG.md b/CHANGELOG.md index bebe09e..455d7ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [v0.1.1] — 2026-05-15 ### Changed @@ -19,6 +19,17 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm that left files owned by root). Unknown files in the dir are left alone. +### Fixed + +- **CLI socket lookup as the daemon user.** `sudo -u quptime qu …` + no longer fails with `dial daemon socket /tmp/quptime-quptime/…: + no such file or directory` while the system daemon is running. + `config.SocketPath()` now probes the canonical systemd location + (`/run/quptime/quptime.sock`, then `/var/run/quptime/quptime.sock`) + regardless of euid before falling back to per-user paths, so the + CLI reaches the daemon's socket even when `sudo` has stripped + `RUNTIME_DIRECTORY` and `XDG_RUNTIME_DIR` from the environment. + ## [v0.1.0] — 2026-05-15 ### Changed @@ -127,4 +138,5 @@ Initial public release. Planned for a future release. [v0.0.1]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.0.1 -[v0.1.0]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.1.0 \ No newline at end of file +[v0.1.0]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.1.0 +[v0.1.1]: https://git.cer.sh/axodouble/quptime/releases/tag/v0.1.1 \ No newline at end of file diff --git a/internal/config/paths.go b/internal/config/paths.go index 4c1c128..02d575b 100644 --- a/internal/config/paths.go +++ b/internal/config/paths.go @@ -58,19 +58,20 @@ func DataDir() string { // SocketPath returns the unix socket used for local CLI ↔ daemon control. // // Resolution order: -// 1. $QUPTIME_SOCKET — explicit operator override +// 1. $QUPTIME_SOCKET — explicit operator override. // 2. $RUNTIME_DIRECTORY — set by systemd when the unit declares -// RuntimeDirectory=quptime. This is the path that matters in -// practice: with User=quptime + PrivateTmp=true, the daemon's -// /tmp is namespaced and invisible to the root CLI shell, so a -// /tmp fallback yields "no such file" even though the daemon is -// happily listening. Anchoring on $RUNTIME_DIRECTORY puts the -// socket at /run/quptime/quptime.sock, which is the same inode -// the root-CLI default (/var/run/quptime/…) reaches via the -// /var/run → /run symlink. -// 3. /var/run/quptime/… when euid is 0 (CLI side, packaged installs) -// 4. $XDG_RUNTIME_DIR/quptime/… for user-mode installs -// 5. /tmp/quptime-/… as a last resort +// RuntimeDirectory=quptime. This is the path the daemon uses +// when run under the packaged unit: /run/quptime/quptime.sock. +// 3. The canonical system socket path — /run/quptime/quptime.sock — +// if it exists. This catches the CLI side regardless of who is +// invoking it: `sudo -u quptime qu status` strips RUNTIME_DIRECTORY +// and XDG_RUNTIME_DIR, so without this probe the CLI falls all +// the way through to /tmp/quptime-/… and reports "no such +// file" even while the daemon is happily listening. +// 4. /var/run/quptime/… when euid is 0 (CLI side, packaged installs +// on systems where /var/run isn't a symlink to /run). +// 5. $XDG_RUNTIME_DIR/quptime/… for user-mode installs. +// 6. /tmp/quptime-/… as a last resort. func SocketPath() string { if v := os.Getenv("QUPTIME_SOCKET"); v != "" { return v @@ -84,6 +85,18 @@ func SocketPath() string { } return filepath.Join(v, SocketName) } + // If a system-managed daemon is already listening, route there + // regardless of euid. Without this, `sudo -u quptime qu …` can't + // find the socket the daemon (also running as quptime) created + // via RuntimeDirectory=. + for _, p := range []string{ + "/run/quptime/" + SocketName, + "/var/run/quptime/" + SocketName, + } { + if _, err := os.Stat(p); err == nil { + return p + } + } if os.Geteuid() == 0 { return "/var/run/quptime/" + SocketName }